|
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 243565 2012-11-26 16:44:03Z 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 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 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 |
|
92 int |
|
93 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 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->sctp_ecn_enable = inp->sctp_ecn_enable; |
|
129 n_inp->partial_delivery_point = inp->partial_delivery_point; |
|
130 n_inp->sctp_context = inp->sctp_context; |
|
131 n_inp->local_strreset_support = inp->local_strreset_support; |
|
132 n_inp->inp_starting_point_for_iterator = NULL; |
|
133 /* copy in the authentication parameters from the original endpoint */ |
|
134 if (n_inp->sctp_ep.local_hmacs) |
|
135 sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs); |
|
136 n_inp->sctp_ep.local_hmacs = |
|
137 sctp_copy_hmaclist(inp->sctp_ep.local_hmacs); |
|
138 if (n_inp->sctp_ep.local_auth_chunks) |
|
139 sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks); |
|
140 n_inp->sctp_ep.local_auth_chunks = |
|
141 sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks); |
|
142 (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys, |
|
143 &n_inp->sctp_ep.shared_keys); |
|
144 #if defined(__Userspace__) |
|
145 n_inp->ulp_info = inp->ulp_info; |
|
146 n_inp->recv_callback = inp->recv_callback; |
|
147 n_inp->send_callback = inp->send_callback; |
|
148 n_inp->send_sb_threshold = inp->send_sb_threshold; |
|
149 #endif |
|
150 /* |
|
151 * Now we must move it from one hash table to another and get the |
|
152 * stcb in the right place. |
|
153 */ |
|
154 sctp_move_pcb_and_assoc(inp, n_inp, stcb); |
|
155 atomic_add_int(&stcb->asoc.refcnt, 1); |
|
156 SCTP_TCB_UNLOCK(stcb); |
|
157 |
|
158 #if defined(__FreeBSD__) |
|
159 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT); |
|
160 #else |
|
161 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK); |
|
162 #endif |
|
163 atomic_subtract_int(&stcb->asoc.refcnt, 1); |
|
164 |
|
165 return (0); |
|
166 } |
|
167 |
|
168 #if defined(HAVE_SCTP_PEELOFF_SOCKOPT) |
|
169 struct socket * |
|
170 sctp_get_peeloff(struct socket *head, sctp_assoc_t assoc_id, int *error) |
|
171 { |
|
172 #if defined(__Userspace__) |
|
173 /* if __Userspace__ chooses to originally not support peeloff, put it here... */ |
|
174 #endif |
|
175 #if defined(__Panda__) |
|
176 SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EINVAL); |
|
177 *error = EINVAL; |
|
178 return (NULL); |
|
179 #else |
|
180 struct socket *newso; |
|
181 struct sctp_inpcb *inp, *n_inp; |
|
182 struct sctp_tcb *stcb; |
|
183 |
|
184 SCTPDBG(SCTP_DEBUG_PEEL1, "SCTP peel-off called\n"); |
|
185 inp = (struct sctp_inpcb *)head->so_pcb; |
|
186 if (inp == NULL) { |
|
187 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT); |
|
188 *error = EFAULT; |
|
189 return (NULL); |
|
190 } |
|
191 stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1); |
|
192 if (stcb == NULL) { |
|
193 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN); |
|
194 *error = ENOTCONN; |
|
195 return (NULL); |
|
196 } |
|
197 atomic_add_int(&stcb->asoc.refcnt, 1); |
|
198 SCTP_TCB_UNLOCK(stcb); |
|
199 #if defined(__FreeBSD__) && __FreeBSD_version >= 801000 |
|
200 CURVNET_SET(head->so_vnet); |
|
201 #endif |
|
202 newso = sonewconn(head, SS_ISCONNECTED |
|
203 #if defined(__APPLE__) |
|
204 , NULL |
|
205 #elif defined(__Panda__) |
|
206 /* place this socket in the assoc's vrf id */ |
|
207 , NULL, stcb->asoc.vrf_id |
|
208 #endif |
|
209 ); |
|
210 #if defined(__FreeBSD__) && __FreeBSD_version >= 801000 |
|
211 CURVNET_RESTORE(); |
|
212 #endif |
|
213 if (newso == NULL) { |
|
214 SCTPDBG(SCTP_DEBUG_PEEL1, "sctp_peeloff:sonewconn failed\n"); |
|
215 SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOMEM); |
|
216 *error = ENOMEM; |
|
217 atomic_subtract_int(&stcb->asoc.refcnt, 1); |
|
218 return (NULL); |
|
219 |
|
220 } |
|
221 #if defined(__APPLE__) |
|
222 else { |
|
223 SCTP_SOCKET_LOCK(newso, 1); |
|
224 } |
|
225 #endif |
|
226 SCTP_TCB_LOCK(stcb); |
|
227 atomic_subtract_int(&stcb->asoc.refcnt, 1); |
|
228 n_inp = (struct sctp_inpcb *)newso->so_pcb; |
|
229 SOCK_LOCK(head); |
|
230 n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE | |
|
231 SCTP_PCB_FLAGS_CONNECTED | |
|
232 SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */ |
|
233 (SCTP_PCB_COPY_FLAGS & inp->sctp_flags)); |
|
234 n_inp->sctp_features = inp->sctp_features; |
|
235 n_inp->sctp_frag_point = inp->sctp_frag_point; |
|
236 n_inp->sctp_cmt_on_off = inp->sctp_cmt_on_off; |
|
237 n_inp->sctp_ecn_enable = inp->sctp_ecn_enable; |
|
238 n_inp->partial_delivery_point = inp->partial_delivery_point; |
|
239 n_inp->sctp_context = inp->sctp_context; |
|
240 n_inp->local_strreset_support = inp->local_strreset_support; |
|
241 n_inp->inp_starting_point_for_iterator = NULL; |
|
242 #if defined(__Userspace__) |
|
243 n_inp->ulp_info = inp->ulp_info; |
|
244 n_inp->recv_callback = inp->recv_callback; |
|
245 n_inp->send_callback = inp->send_callback; |
|
246 n_inp->send_sb_threshold = inp->send_sb_threshold; |
|
247 #endif |
|
248 |
|
249 /* copy in the authentication parameters from the original endpoint */ |
|
250 if (n_inp->sctp_ep.local_hmacs) |
|
251 sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs); |
|
252 n_inp->sctp_ep.local_hmacs = |
|
253 sctp_copy_hmaclist(inp->sctp_ep.local_hmacs); |
|
254 if (n_inp->sctp_ep.local_auth_chunks) |
|
255 sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks); |
|
256 n_inp->sctp_ep.local_auth_chunks = |
|
257 sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks); |
|
258 (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys, |
|
259 &n_inp->sctp_ep.shared_keys); |
|
260 |
|
261 n_inp->sctp_socket = newso; |
|
262 if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) { |
|
263 sctp_feature_off(n_inp, SCTP_PCB_FLAGS_AUTOCLOSE); |
|
264 n_inp->sctp_ep.auto_close_time = 0; |
|
265 sctp_timer_stop(SCTP_TIMER_TYPE_AUTOCLOSE, n_inp, stcb, NULL, |
|
266 SCTP_FROM_SCTP_PEELOFF+SCTP_LOC_1); |
|
267 } |
|
268 /* Turn off any non-blocking semantic. */ |
|
269 SCTP_CLEAR_SO_NBIO(newso); |
|
270 newso->so_state |= SS_ISCONNECTED; |
|
271 /* We remove it right away */ |
|
272 |
|
273 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) || defined(__Userspace__) |
|
274 #ifdef SCTP_LOCK_LOGGING |
|
275 if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) { |
|
276 sctp_log_lock(inp, (struct sctp_tcb *)NULL, SCTP_LOG_LOCK_SOCK); |
|
277 } |
|
278 #endif |
|
279 TAILQ_REMOVE(&head->so_comp, newso, so_list); |
|
280 head->so_qlen--; |
|
281 SOCK_UNLOCK(head); |
|
282 #else |
|
283 newso = TAILQ_FIRST(&head->so_q); |
|
284 if (soqremque(newso, 1) == 0) { |
|
285 SCTP_PRINTF("soremque failed, peeloff-fails (invarients would panic)\n"); |
|
286 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN); |
|
287 *error = ENOTCONN; |
|
288 return (NULL); |
|
289 |
|
290 } |
|
291 #endif |
|
292 /* |
|
293 * Now we must move it from one hash table to another and get the |
|
294 * stcb in the right place. |
|
295 */ |
|
296 sctp_move_pcb_and_assoc(inp, n_inp, stcb); |
|
297 atomic_add_int(&stcb->asoc.refcnt, 1); |
|
298 SCTP_TCB_UNLOCK(stcb); |
|
299 /* |
|
300 * And now the final hack. We move data in the pending side i.e. |
|
301 * head to the new socket buffer. Let the GRUBBING begin :-0 |
|
302 */ |
|
303 #if defined(__FreeBSD__) |
|
304 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT); |
|
305 #else |
|
306 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, M_WAITOK); |
|
307 #endif |
|
308 atomic_subtract_int(&stcb->asoc.refcnt, 1); |
|
309 return (newso); |
|
310 #endif |
|
311 } |
|
312 #endif |