1 /*- 2 * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. 3 * Copyright (c) 2008-2011, by Randall Stewart. All rights reserved. 4 * Copyright (c) 2008-2011, 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 34 /* $KAME: sctp_peeloff.c,v 1.13 2005/03/06 16:04:18 itojun Exp $ */ 35 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 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 51 int 52 sctp_can_peel_off(struct socket *head, sctp_assoc_t assoc_id) 53 { 54 struct sctp_inpcb *inp; 55 struct sctp_tcb *stcb; 56 uint32_t state; 57 58 inp = (struct sctp_inpcb *)head->so_pcb; 59 if (inp == NULL) { 60 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT); 61 return (EFAULT); 62 } 63 stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1); 64 if (stcb == NULL) { 65 SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOENT); 66 return (ENOENT); 67 } 68 state = SCTP_GET_STATE((&stcb->asoc)); 69 if ((state == SCTP_STATE_EMPTY) || 70 (state == SCTP_STATE_INUSE) || 71 (state == SCTP_STATE_COOKIE_WAIT) || 72 (state == SCTP_STATE_COOKIE_ECHOED)) { 73 SCTP_TCB_UNLOCK(stcb); 74 SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN); 75 return (ENOTCONN); 76 } 77 SCTP_TCB_UNLOCK(stcb); 78 /* We are clear to peel this one off */ 79 return (0); 80 } 81 82 int 83 sctp_do_peeloff(struct socket *head, struct socket *so, sctp_assoc_t assoc_id) 84 { 85 struct sctp_inpcb *inp, *n_inp; 86 struct sctp_tcb *stcb; 87 uint32_t state; 88 89 inp = (struct sctp_inpcb *)head->so_pcb; 90 if (inp == NULL) { 91 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT); 92 return (EFAULT); 93 } 94 stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1); 95 if (stcb == NULL) { 96 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN); 97 return (ENOTCONN); 98 } 99 state = SCTP_GET_STATE((&stcb->asoc)); 100 if ((state == SCTP_STATE_EMPTY) || 101 (state == SCTP_STATE_INUSE) || 102 (state == SCTP_STATE_COOKIE_WAIT) || 103 (state == SCTP_STATE_COOKIE_ECHOED)) { 104 SCTP_TCB_UNLOCK(stcb); 105 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN); 106 return (ENOTCONN); 107 } 108 n_inp = (struct sctp_inpcb *)so->so_pcb; 109 n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE | 110 SCTP_PCB_FLAGS_CONNECTED | 111 SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */ 112 (SCTP_PCB_COPY_FLAGS & inp->sctp_flags)); 113 n_inp->sctp_socket = so; 114 n_inp->sctp_features = inp->sctp_features; 115 n_inp->sctp_mobility_features = inp->sctp_mobility_features; 116 n_inp->sctp_frag_point = inp->sctp_frag_point; 117 n_inp->sctp_cmt_on_off = inp->sctp_cmt_on_off; 118 n_inp->sctp_ecn_enable = inp->sctp_ecn_enable; 119 n_inp->partial_delivery_point = inp->partial_delivery_point; 120 n_inp->sctp_context = inp->sctp_context; 121 n_inp->inp_starting_point_for_iterator = NULL; 122 /* copy in the authentication parameters from the original endpoint */ 123 if (n_inp->sctp_ep.local_hmacs) 124 sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs); 125 n_inp->sctp_ep.local_hmacs = 126 sctp_copy_hmaclist(inp->sctp_ep.local_hmacs); 127 if (n_inp->sctp_ep.local_auth_chunks) 128 sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks); 129 n_inp->sctp_ep.local_auth_chunks = 130 sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks); 131 (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys, 132 &n_inp->sctp_ep.shared_keys); 133 /* 134 * Now we must move it from one hash table to another and get the 135 * stcb in the right place. 136 */ 137 sctp_move_pcb_and_assoc(inp, n_inp, stcb); 138 atomic_add_int(&stcb->asoc.refcnt, 1); 139 SCTP_TCB_UNLOCK(stcb); 140 141 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT); 142 atomic_subtract_int(&stcb->asoc.refcnt, 1); 143 144 return (0); 145 } 146 147 148 struct socket * 149 sctp_get_peeloff(struct socket *head, sctp_assoc_t assoc_id, int *error) 150 { 151 struct socket *newso; 152 struct sctp_inpcb *inp, *n_inp; 153 struct sctp_tcb *stcb; 154 155 SCTPDBG(SCTP_DEBUG_PEEL1, "SCTP peel-off called\n"); 156 inp = (struct sctp_inpcb *)head->so_pcb; 157 if (inp == NULL) { 158 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, EFAULT); 159 *error = EFAULT; 160 return (NULL); 161 } 162 stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1); 163 if (stcb == NULL) { 164 SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PEELOFF, ENOTCONN); 165 *error = ENOTCONN; 166 return (NULL); 167 } 168 atomic_add_int(&stcb->asoc.refcnt, 1); 169 SCTP_TCB_UNLOCK(stcb); 170 CURVNET_SET(head->so_vnet); 171 newso = sonewconn(head, SS_ISCONNECTED 172 ); 173 CURVNET_RESTORE(); 174 if (newso == NULL) { 175 SCTPDBG(SCTP_DEBUG_PEEL1, "sctp_peeloff:sonewconn failed\n"); 176 SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_PEELOFF, ENOMEM); 177 *error = ENOMEM; 178 atomic_subtract_int(&stcb->asoc.refcnt, 1); 179 return (NULL); 180 181 } 182 SCTP_TCB_LOCK(stcb); 183 atomic_subtract_int(&stcb->asoc.refcnt, 1); 184 n_inp = (struct sctp_inpcb *)newso->so_pcb; 185 SOCK_LOCK(head); 186 n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE | 187 SCTP_PCB_FLAGS_CONNECTED | 188 SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */ 189 (SCTP_PCB_COPY_FLAGS & inp->sctp_flags)); 190 n_inp->sctp_features = inp->sctp_features; 191 n_inp->sctp_frag_point = inp->sctp_frag_point; 192 n_inp->sctp_cmt_on_off = inp->sctp_cmt_on_off; 193 n_inp->sctp_ecn_enable = inp->sctp_ecn_enable; 194 n_inp->partial_delivery_point = inp->partial_delivery_point; 195 n_inp->sctp_context = inp->sctp_context; 196 n_inp->inp_starting_point_for_iterator = NULL; 197 198 /* copy in the authentication parameters from the original endpoint */ 199 if (n_inp->sctp_ep.local_hmacs) 200 sctp_free_hmaclist(n_inp->sctp_ep.local_hmacs); 201 n_inp->sctp_ep.local_hmacs = 202 sctp_copy_hmaclist(inp->sctp_ep.local_hmacs); 203 if (n_inp->sctp_ep.local_auth_chunks) 204 sctp_free_chunklist(n_inp->sctp_ep.local_auth_chunks); 205 n_inp->sctp_ep.local_auth_chunks = 206 sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks); 207 (void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys, 208 &n_inp->sctp_ep.shared_keys); 209 210 n_inp->sctp_socket = newso; 211 if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) { 212 sctp_feature_off(n_inp, SCTP_PCB_FLAGS_AUTOCLOSE); 213 n_inp->sctp_ep.auto_close_time = 0; 214 sctp_timer_stop(SCTP_TIMER_TYPE_AUTOCLOSE, n_inp, stcb, NULL, 215 SCTP_FROM_SCTP_PEELOFF + SCTP_LOC_1); 216 } 217 /* Turn off any non-blocking semantic. */ 218 SCTP_CLEAR_SO_NBIO(newso); 219 newso->so_state |= SS_ISCONNECTED; 220 /* We remove it right away */ 221 222 #ifdef SCTP_LOCK_LOGGING 223 if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) { 224 sctp_log_lock(inp, (struct sctp_tcb *)NULL, SCTP_LOG_LOCK_SOCK); 225 } 226 #endif 227 TAILQ_REMOVE(&head->so_comp, newso, so_list); 228 head->so_qlen--; 229 SOCK_UNLOCK(head); 230 /* 231 * Now we must move it from one hash table to another and get the 232 * stcb in the right place. 233 */ 234 sctp_move_pcb_and_assoc(inp, n_inp, stcb); 235 atomic_add_int(&stcb->asoc.refcnt, 1); 236 SCTP_TCB_UNLOCK(stcb); 237 /* 238 * And now the final hack. We move data in the pending side i.e. 239 * head to the new socket buffer. Let the GRUBBING begin :-0 240 */ 241 sctp_pull_off_control_to_new_inp(inp, n_inp, stcb, SBL_WAIT); 242 atomic_subtract_int(&stcb->asoc.refcnt, 1); 243 return (newso); 244 } 245