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 #ifdef __FreeBSD__
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD: head/sys/netinet/sctp_peeloff.c 337708 2018-08-13 13:58:45Z tuexen $");
38 #endif
39
40 #include <netinet/sctp_os.h>
41 #include <netinet/sctp_pcb.h>
42 #include <netinet/sctputil.h>
43 #include <netinet/sctp_var.h>
44 #include <netinet/sctp_var.h>
45 #include <netinet/sctp_sysctl.h>
46 #include <netinet/sctp.h>
47 #include <netinet/sctp_uio.h>
48 #include <netinet/sctp_peeloff.h>
49 #include <netinet/sctputil.h>
50 #include <netinet/sctp_auth.h>
51
52 #if defined(__APPLE__)
53 #define APPLE_FILE_NO 5
54 #endif
55
56 int
sctp_can_peel_off(struct socket * head,sctp_assoc_t assoc_id)57 sctp_can_peel_off(struct socket *head, sctp_assoc_t assoc_id)
58 {
59 struct sctp_inpcb *inp;
60 struct sctp_tcb *stcb;
61 uint32_t state;
62
63 if (head == NULL) {
64 SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EBADF);
65 return (EBADF);
66 }
67 inp = (struct sctp_inpcb *)head->so_pcb;
68 if (inp == NULL) {
69 SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT);
70 return (EFAULT);
71 }
72 if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
73 (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
74 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EOPNOTSUPP);
75 return (EOPNOTSUPP);
76 }
77 stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1);
78 if (stcb == NULL) {
79 SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOENT);
80 return (ENOENT);
81 }
82 state = SCTP_GET_STATE(stcb);
83 if ((state == SCTP_STATE_EMPTY) ||
84 (state == SCTP_STATE_INUSE)) {
85 SCTP_TCB_UNLOCK(stcb);
86 SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
87 return (ENOTCONN);
88 }
89 SCTP_TCB_UNLOCK(stcb);
90 /* We are clear to peel this one off */
91 return (0);
92 }
93
94 int
sctp_do_peeloff(struct socket * head,struct socket * so,sctp_assoc_t assoc_id)95 sctp_do_peeloff(struct socket *head, struct socket *so, sctp_assoc_t assoc_id)
96 {
97 struct sctp_inpcb *inp, *n_inp;
98 struct sctp_tcb *stcb;
99 uint32_t state;
100
101 inp = (struct sctp_inpcb *)head->so_pcb;
102 if (inp == NULL) {
103 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT);
104 return (EFAULT);
105 }
106 stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1);
107 if (stcb == NULL) {
108 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
109 return (ENOTCONN);
110 }
111
112 state = SCTP_GET_STATE(stcb);
113 if ((state == SCTP_STATE_EMPTY) ||
114 (state == SCTP_STATE_INUSE)) {
115 SCTP_TCB_UNLOCK(stcb);
116 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
117 return (ENOTCONN);
118 }
119
120 n_inp = (struct sctp_inpcb *)so->so_pcb;
121 n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
122 SCTP_PCB_FLAGS_CONNECTED |
123 SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */
124 (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
125 n_inp->sctp_socket = so;
126 n_inp->sctp_features = inp->sctp_features;
127 n_inp->sctp_mobility_features = inp->sctp_mobility_features;
128 n_inp->sctp_frag_point = inp->sctp_frag_point;
129 n_inp->sctp_cmt_on_off = inp->sctp_cmt_on_off;
130 n_inp->ecn_supported = inp->ecn_supported;
131 n_inp->prsctp_supported = inp->prsctp_supported;
132 n_inp->auth_supported = inp->auth_supported;
133 n_inp->asconf_supported = inp->asconf_supported;
134 n_inp->reconfig_supported = inp->reconfig_supported;
135 n_inp->nrsack_supported = inp->nrsack_supported;
136 n_inp->pktdrop_supported = inp->pktdrop_supported;
137 n_inp->partial_delivery_point = inp->partial_delivery_point;
138 n_inp->sctp_context = inp->sctp_context;
139 n_inp->max_cwnd = inp->max_cwnd;
140 n_inp->local_strreset_support = inp->local_strreset_support;
141 n_inp->inp_starting_point_for_iterator = NULL;
142 /* copy in the authentication parameters from the original endpoint */
143 if (n_inp->sctp_ep.local_hmacs)
144 sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs);
145 n_inp->sctp_ep.local_hmacs =
146 sctp_copy_hmaclist(inp->sctp_ep.local_hmacs);
147 if (n_inp->sctp_ep.local_auth_chunks)
148 sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks);
149 n_inp->sctp_ep.local_auth_chunks =
150 sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks);
151 (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys,
152 &n_inp->sctp_ep.shared_keys);
153 #if defined(__Userspace__)
154 n_inp->ulp_info = inp->ulp_info;
155 n_inp->recv_callback = inp->recv_callback;
156 n_inp->send_callback = inp->send_callback;
157 n_inp->send_sb_threshold = inp->send_sb_threshold;
158 #endif
159 /*
160 * Now we must move it from one hash table to another and get the
161 * stcb in the right place.
162 */
163 sctp_move_pcb_and_assoc(inp, n_inp, stcb);
164 atomic_add_int(&stcb->asoc.refcnt, 1);
165 SCTP_TCB_UNLOCK(stcb);
166
167 #if defined(__FreeBSD__)
168 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT);
169 #else
170 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK);
171 #endif
172 atomic_subtract_int(&stcb->asoc.refcnt, 1);
173
174 return (0);
175 }
176
177 #if defined(HAVE_SCTP_PEELOFF_SOCKOPT)
178 struct socket *
sctp_get_peeloff(struct socket * head,sctp_assoc_t assoc_id,int * error)179 sctp_get_peeloff(struct socket *head, sctp_assoc_t assoc_id, int *error)
180 {
181 #if defined(__Userspace__)
182 /* if __Userspace__ chooses to originally not support peeloff, put it here... */
183 #endif
184 #if defined(__Panda__)
185 SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EINVAL);
186 *error = EINVAL;
187 return (NULL);
188 #else
189 struct socket *newso;
190 struct sctp_inpcb *inp, *n_inp;
191 struct sctp_tcb *stcb;
192
193 SCTPDBG(SCTP_DEBUG_PEEL1, "SCTP peel-off called\n");
194 inp = (struct sctp_inpcb *)head->so_pcb;
195 if (inp == NULL) {
196 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT);
197 *error = EFAULT;
198 return (NULL);
199 }
200 stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1);
201 if (stcb == NULL) {
202 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
203 *error = ENOTCONN;
204 return (NULL);
205 }
206 atomic_add_int(&stcb->asoc.refcnt, 1);
207 SCTP_TCB_UNLOCK(stcb);
208 #if defined(__FreeBSD__) && __FreeBSD_version >= 801000
209 CURVNET_SET(head->so_vnet);
210 #endif
211 newso = sonewconn(head, SS_ISCONNECTED
212 #if defined(__APPLE__)
213 , NULL
214 #elif defined(__Panda__)
215 /* place this socket in the assoc's vrf id */
216 , NULL, stcb->asoc.vrf_id
217 #endif
218 );
219 #if defined(__FreeBSD__) && __FreeBSD_version >= 801000
220 CURVNET_RESTORE();
221 #endif
222 if (newso == NULL) {
223 SCTPDBG(SCTP_DEBUG_PEEL1, "sctp_peeloff:sonewconn failed\n");
224 SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOMEM);
225 *error = ENOMEM;
226 atomic_subtract_int(&stcb->asoc.refcnt, 1);
227 return (NULL);
228
229 }
230 #if defined(__APPLE__)
231 else {
232 SCTP_SOCKET_LOCK(newso, 1);
233 }
234 #endif
235 SCTP_TCB_LOCK(stcb);
236 atomic_subtract_int(&stcb->asoc.refcnt, 1);
237 n_inp = (struct sctp_inpcb *)newso->so_pcb;
238 SOCK_LOCK(head);
239 n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
240 SCTP_PCB_FLAGS_CONNECTED |
241 SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */
242 (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
243 n_inp->sctp_features = inp->sctp_features;
244 n_inp->sctp_frag_point = inp->sctp_frag_point;
245 n_inp->sctp_cmt_on_off = inp->sctp_cmt_on_off;
246 n_inp->ecn_supported = inp->ecn_supported;
247 n_inp->prsctp_supported = inp->prsctp_supported;
248 n_inp->auth_supported = inp->auth_supported;
249 n_inp->asconf_supported = inp->asconf_supported;
250 n_inp->reconfig_supported = inp->reconfig_supported;
251 n_inp->nrsack_supported = inp->nrsack_supported;
252 n_inp->pktdrop_supported = inp->pktdrop_supported;
253 n_inp->partial_delivery_point = inp->partial_delivery_point;
254 n_inp->sctp_context = inp->sctp_context;
255 n_inp->max_cwnd = inp->max_cwnd;
256 n_inp->local_strreset_support = inp->local_strreset_support;
257 n_inp->inp_starting_point_for_iterator = NULL;
258 #if defined(__Userspace__)
259 n_inp->ulp_info = inp->ulp_info;
260 n_inp->recv_callback = inp->recv_callback;
261 n_inp->send_callback = inp->send_callback;
262 n_inp->send_sb_threshold = inp->send_sb_threshold;
263 #endif
264
265 /* copy in the authentication parameters from the original endpoint */
266 if (n_inp->sctp_ep.local_hmacs)
267 sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs);
268 n_inp->sctp_ep.local_hmacs =
269 sctp_copy_hmaclist(inp->sctp_ep.local_hmacs);
270 if (n_inp->sctp_ep.local_auth_chunks)
271 sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks);
272 n_inp->sctp_ep.local_auth_chunks =
273 sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks);
274 (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys,
275 &n_inp->sctp_ep.shared_keys);
276
277 n_inp->sctp_socket = newso;
278 if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) {
279 sctp_feature_off(n_inp, SCTP_PCB_FLAGS_AUTOCLOSE);
280 n_inp->sctp_ep.auto_close_time = 0;
281 sctp_timer_stop(SCTP_TIMER_TYPE_AUTOCLOSE, n_inp, stcb, NULL,
282 SCTP_FROM_SCTP_PEELOFF + SCTP_LOC_1);
283 }
284 /* Turn off any non-blocking semantic. */
285 SOCK_LOCK(newso);
286 SCTP_CLEAR_SO_NBIO(newso);
287 newso->so_state |= SS_ISCONNECTED;
288 SOCK_UNLOCK(newso);
289 /* We remove it right away */
290
291 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) || defined(__Userspace__)
292 #ifdef SCTP_LOCK_LOGGING
293 if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) {
294 sctp_log_lock(inp, (struct sctp_tcb *)NULL, SCTP_LOG_LOCK_SOCK);
295 }
296 #endif
297 TAILQ_REMOVE(&head->so_comp, newso, so_list);
298 head->so_qlen--;
299 SOCK_UNLOCK(head);
300 #else
301 newso = TAILQ_FIRST(&head->so_q);
302 if (soqremque(newso, 1) == 0) {
303 SCTP_PRINTF("soremque failed, peeloff-fails (invarients would panic)\n");
304 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN);
305 *error = ENOTCONN;
306 return (NULL);
307
308 }
309 #endif
310 /*
311 * Now we must move it from one hash table to another and get the
312 * stcb in the right place.
313 */
314 sctp_move_pcb_and_assoc(inp, n_inp, stcb);
315 atomic_add_int(&stcb->asoc.refcnt, 1);
316 SCTP_TCB_UNLOCK(stcb);
317 /*
318 * And now the final hack. We move data in the pending side i.e.
319 * head to the new socket buffer. Let the GRUBBING begin :-0
320 */
321 #if defined(__FreeBSD__)
322 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT);
323 #else
324 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK);
325 #endif
326 atomic_subtract_int(&stcb->asoc.refcnt, 1);
327 return (newso);
328 #endif
329 }
330 #endif
331