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