Coverage Report

Created: 2025-07-11 06:39

/src/usrsctp/usrsctplib/netinet/sctp_peeloff.c
Line
Count
Source (jump to first uncovered line)
1
/*-
2
 * SPDX-License-Identifier: BSD-3-Clause
3
 *
4
 * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved.
5
 * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
6
 * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions are met:
10
 *
11
 * a) Redistributions of source code must retain the above copyright notice,
12
 *    this list of conditions and the following disclaimer.
13
 *
14
 * b) Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in
16
 *    the documentation and/or other materials provided with the distribution.
17
 *
18
 * c) Neither the name of Cisco Systems, Inc. nor the names of its
19
 *    contributors may be used to endorse or promote products derived
20
 *    from this software without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32
 * THE POSSIBILITY OF SUCH DAMAGE.
33
 */
34
35
#include <netinet/sctp_os.h>
36
#include <netinet/sctp_pcb.h>
37
#include <netinet/sctputil.h>
38
#include <netinet/sctp_var.h>
39
#include <netinet/sctp_var.h>
40
#include <netinet/sctp_sysctl.h>
41
#include <netinet/sctp.h>
42
#include <netinet/sctp_uio.h>
43
#include <netinet/sctp_peeloff.h>
44
#include <netinet/sctputil.h>
45
#include <netinet/sctp_auth.h>
46
47
int
48
sctp_can_peel_off(struct socket *head, sctp_assoc_t assoc_id)
49
0
{
50
0
  struct sctp_inpcb *inp;
51
0
  struct sctp_tcb *stcb;
52
0
  uint32_t state;
53
54
0
  if (head == NULL) {
55
0
    SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EBADF);
56
0
    return (EBADF);
57
0
  }
58
0
  inp = (struct sctp_inpcb *)head->so_pcb;
59
0
  if (inp == NULL) {
60
0
    SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT);
61
0
    return (EFAULT);
62
0
  }
63
0
  if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
64
0
      (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
65
0
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EOPNOTSUPP);
66
0
    return (EOPNOTSUPP);
67
0
  }
68
0
  stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1);
69
0
  if (stcb == NULL) {
70
0
    SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOENT);
71
0
    return (ENOENT);
72
0
  }
73
0
  state = SCTP_GET_STATE(stcb);
74
0
  if ((state == SCTP_STATE_EMPTY) ||
75
0
      (state == SCTP_STATE_INUSE)) {
76
0
    SCTP_TCB_UNLOCK(stcb);
77
0
    SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
78
0
    return (ENOTCONN);
79
0
  }
80
0
  SCTP_TCB_UNLOCK(stcb);
81
  /* We are clear to peel this one off */
82
0
  return (0);
83
0
}
84
85
int
86
sctp_do_peeloff(struct socket *head, struct socket *so, sctp_assoc_t assoc_id)
87
0
{
88
0
  struct sctp_inpcb *inp, *n_inp;
89
0
  struct sctp_tcb *stcb;
90
0
  uint32_t state;
91
92
0
  inp = (struct sctp_inpcb *)head->so_pcb;
93
0
  if (inp == NULL) {
94
0
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT);
95
0
    return (EFAULT);
96
0
  }
97
0
  stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1);
98
0
  if (stcb == NULL) {
99
0
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
100
0
    return (ENOTCONN);
101
0
  }
102
103
0
  state = SCTP_GET_STATE(stcb);
104
0
  if ((state == SCTP_STATE_EMPTY) ||
105
0
      (state == SCTP_STATE_INUSE)) {
106
0
    SCTP_TCB_UNLOCK(stcb);
107
0
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
108
0
    return (ENOTCONN);
109
0
  }
110
111
0
  n_inp = (struct sctp_inpcb *)so->so_pcb;
112
0
  n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
113
0
      SCTP_PCB_FLAGS_CONNECTED |
114
0
      SCTP_PCB_FLAGS_IN_TCPPOOL |  /* Turn on Blocking IO */
115
0
      (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
116
0
  n_inp->sctp_socket = so;
117
0
  n_inp->sctp_features = inp->sctp_features;
118
0
  n_inp->sctp_mobility_features = inp->sctp_mobility_features;
119
0
  n_inp->sctp_frag_point = inp->sctp_frag_point;
120
0
  n_inp->sctp_cmt_on_off = inp->sctp_cmt_on_off;
121
0
  n_inp->ecn_supported = inp->ecn_supported;
122
0
  n_inp->prsctp_supported = inp->prsctp_supported;
123
0
  n_inp->auth_supported = inp->auth_supported;
124
0
  n_inp->asconf_supported = inp->asconf_supported;
125
0
  n_inp->reconfig_supported = inp->reconfig_supported;
126
0
  n_inp->nrsack_supported = inp->nrsack_supported;
127
0
  n_inp->pktdrop_supported = inp->pktdrop_supported;
128
0
  n_inp->partial_delivery_point = inp->partial_delivery_point;
129
0
  n_inp->sctp_context = inp->sctp_context;
130
0
  n_inp->max_cwnd = inp->max_cwnd;
131
0
  n_inp->local_strreset_support = inp->local_strreset_support;
132
  /* copy in the authentication parameters from the original endpoint */
133
0
  if (n_inp->sctp_ep.local_hmacs)
134
0
    sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs);
135
0
  n_inp->sctp_ep.local_hmacs =
136
0
      sctp_copy_hmaclist(inp->sctp_ep.local_hmacs);
137
0
  if (n_inp->sctp_ep.local_auth_chunks)
138
0
    sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks);
139
0
  n_inp->sctp_ep.local_auth_chunks =
140
0
      sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks);
141
0
  (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys,
142
0
      &n_inp->sctp_ep.shared_keys);
143
0
#if defined(__Userspace__)
144
0
  n_inp->ulp_info = inp->ulp_info;
145
0
  n_inp->recv_callback = inp->recv_callback;
146
0
  n_inp->send_callback = inp->send_callback;
147
0
  n_inp->send_sb_threshold = inp->send_sb_threshold;
148
0
#endif
149
  /*
150
   * Now we must move it from one hash table to another and get the
151
   * stcb in the right place.
152
   */
153
0
  sctp_move_pcb_and_assoc(inp, n_inp, stcb);
154
0
  atomic_add_int(&stcb->asoc.refcnt, 1);
155
0
  SCTP_TCB_UNLOCK(stcb);
156
157
#if defined(__FreeBSD__) && !defined(__Userspace__)
158
  sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT);
159
#else
160
0
  sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK);
161
0
#endif
162
0
  atomic_subtract_int(&stcb->asoc.refcnt, 1);
163
164
0
  return (0);
165
0
}
166
167
#if defined(HAVE_SCTP_PEELOFF_SOCKOPT)
168
struct socket *
169
sctp_get_peeloff(struct socket *head, sctp_assoc_t assoc_id, int *error)
170
{
171
  struct socket *newso;
172
  struct sctp_inpcb *inp, *n_inp;
173
  struct sctp_tcb *stcb;
174
175
  SCTPDBG(SCTP_DEBUG_PEEL1, "SCTP peel-off called\n");
176
  inp = (struct sctp_inpcb *)head->so_pcb;
177
  if (inp == NULL) {
178
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT);
179
    *error = EFAULT;
180
    return (NULL);
181
  }
182
  stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1);
183
  if (stcb == NULL) {
184
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
185
    *error = ENOTCONN;
186
    return (NULL);
187
  }
188
  atomic_add_int(&stcb->asoc.refcnt, 1);
189
  SCTP_TCB_UNLOCK(stcb);
190
#if defined(__FreeBSD__) && !defined(__Userspace__)
191
  CURVNET_SET(head->so_vnet);
192
#endif
193
  newso = sonewconn(head, SS_ISCONNECTED
194
#if defined(__APPLE__) && !defined(__Userspace__)
195
      , NULL
196
#endif
197
    );
198
#if defined(__FreeBSD__) && !defined(__Userspace__)
199
  CURVNET_RESTORE();
200
#endif
201
  if (newso == NULL) {
202
    SCTPDBG(SCTP_DEBUG_PEEL1, "sctp_peeloff:sonewconn failed\n");
203
    SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOMEM);
204
    *error = ENOMEM;
205
    atomic_subtract_int(&stcb->asoc.refcnt, 1);
206
    return (NULL);
207
208
  }
209
#if defined(__APPLE__) && !defined(__Userspace__)
210
    else {
211
    SCTP_SOCKET_LOCK(newso, 1);
212
  }
213
#endif
214
  SCTP_TCB_LOCK(stcb);
215
  atomic_subtract_int(&stcb->asoc.refcnt, 1);
216
  n_inp = (struct sctp_inpcb *)newso->so_pcb;
217
  SOCK_LOCK(head);
218
  n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
219
      SCTP_PCB_FLAGS_CONNECTED |
220
      SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */
221
      (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
222
  n_inp->sctp_features = inp->sctp_features;
223
  n_inp->sctp_frag_point = inp->sctp_frag_point;
224
  n_inp->sctp_cmt_on_off = inp->sctp_cmt_on_off;
225
  n_inp->ecn_supported = inp->ecn_supported;
226
  n_inp->prsctp_supported = inp->prsctp_supported;
227
  n_inp->auth_supported = inp->auth_supported;
228
  n_inp->asconf_supported = inp->asconf_supported;
229
  n_inp->reconfig_supported = inp->reconfig_supported;
230
  n_inp->nrsack_supported = inp->nrsack_supported;
231
  n_inp->pktdrop_supported = inp->pktdrop_supported;
232
  n_inp->partial_delivery_point = inp->partial_delivery_point;
233
  n_inp->sctp_context = inp->sctp_context;
234
  n_inp->max_cwnd = inp->max_cwnd;
235
  n_inp->local_strreset_support = inp->local_strreset_support;
236
  n_inp->inp_starting_point_for_iterator = NULL;
237
#if defined(__Userspace__)
238
  n_inp->ulp_info = inp->ulp_info;
239
  n_inp->recv_callback = inp->recv_callback;
240
  n_inp->send_callback = inp->send_callback;
241
  n_inp->send_sb_threshold = inp->send_sb_threshold;
242
#endif
243
244
  /* copy in the authentication parameters from the original endpoint */
245
  if (n_inp->sctp_ep.local_hmacs)
246
    sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs);
247
  n_inp->sctp_ep.local_hmacs =
248
      sctp_copy_hmaclist(inp->sctp_ep.local_hmacs);
249
  if (n_inp->sctp_ep.local_auth_chunks)
250
    sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks);
251
  n_inp->sctp_ep.local_auth_chunks =
252
      sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks);
253
  (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys,
254
      &n_inp->sctp_ep.shared_keys);
255
256
  n_inp->sctp_socket = newso;
257
  if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) {
258
    sctp_feature_off(n_inp, SCTP_PCB_FLAGS_AUTOCLOSE);
259
    n_inp->sctp_ep.auto_close_time = 0;
260
    sctp_timer_stop(SCTP_TIMER_TYPE_AUTOCLOSE, n_inp, stcb, NULL,
261
        SCTP_FROM_SCTP_PEELOFF + SCTP_LOC_1);
262
  }
263
  /* Turn off any non-blocking semantic. */
264
  SOCK_LOCK(newso);
265
  SCTP_CLEAR_SO_NBIO(newso);
266
  newso->so_state |= SS_ISCONNECTED;
267
  SOCK_UNLOCK(newso);
268
  /* We remove it right away */
269
270
#ifdef SCTP_LOCK_LOGGING
271
  if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) {
272
    sctp_log_lock(inp, (struct sctp_tcb *)NULL, SCTP_LOG_LOCK_SOCK);
273
  }
274
#endif
275
  TAILQ_REMOVE(&head->so_comp, newso, so_list);
276
  head->so_qlen--;
277
  SOCK_UNLOCK(head);
278
  /*
279
   * Now we must move it from one hash table to another and get the
280
   * stcb in the right place.
281
   */
282
  sctp_move_pcb_and_assoc(inp, n_inp, stcb);
283
  atomic_add_int(&stcb->asoc.refcnt, 1);
284
  SCTP_TCB_UNLOCK(stcb);
285
  /*
286
   * And now the final hack. We move data in the pending side i.e.
287
   * head to the new socket buffer. Let the GRUBBING begin :-0
288
   */
289
#if defined(__FreeBSD__) && !defined(__Userspace__)
290
  sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT);
291
#else
292
  sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK);
293
#endif
294
  atomic_subtract_int(&stcb->asoc.refcnt, 1);
295
  return (newso);
296
}
297
#endif