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