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