Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /*- |
michael@0 | 2 | * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. |
michael@0 | 3 | * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. |
michael@0 | 4 | * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. |
michael@0 | 5 | * |
michael@0 | 6 | * Redistribution and use in source and binary forms, with or without |
michael@0 | 7 | * modification, are permitted provided that the following conditions are met: |
michael@0 | 8 | * |
michael@0 | 9 | * a) Redistributions of source code must retain the above copyright notice, |
michael@0 | 10 | * this list of conditions and the following disclaimer. |
michael@0 | 11 | * |
michael@0 | 12 | * b) Redistributions in binary form must reproduce the above copyright |
michael@0 | 13 | * notice, this list of conditions and the following disclaimer in |
michael@0 | 14 | * the documentation and/or other materials provided with the distribution. |
michael@0 | 15 | * |
michael@0 | 16 | * c) Neither the name of Cisco Systems, Inc. nor the names of its |
michael@0 | 17 | * contributors may be used to endorse or promote products derived |
michael@0 | 18 | * from this software without specific prior written permission. |
michael@0 | 19 | * |
michael@0 | 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
michael@0 | 22 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
michael@0 | 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
michael@0 | 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
michael@0 | 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
michael@0 | 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
michael@0 | 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
michael@0 | 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
michael@0 | 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
michael@0 | 30 | * THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 31 | */ |
michael@0 | 32 | |
michael@0 | 33 | #ifdef __FreeBSD__ |
michael@0 | 34 | #include <sys/cdefs.h> |
michael@0 | 35 | __FBSDID("$FreeBSD: head/sys/netinet/sctp_input.c 262252 2014-02-20 20:14:43Z tuexen $"); |
michael@0 | 36 | #endif |
michael@0 | 37 | |
michael@0 | 38 | #include <netinet/sctp_os.h> |
michael@0 | 39 | #include <netinet/sctp_var.h> |
michael@0 | 40 | #include <netinet/sctp_sysctl.h> |
michael@0 | 41 | #include <netinet/sctp_pcb.h> |
michael@0 | 42 | #include <netinet/sctp_header.h> |
michael@0 | 43 | #include <netinet/sctputil.h> |
michael@0 | 44 | #include <netinet/sctp_output.h> |
michael@0 | 45 | #include <netinet/sctp_input.h> |
michael@0 | 46 | #include <netinet/sctp_auth.h> |
michael@0 | 47 | #include <netinet/sctp_indata.h> |
michael@0 | 48 | #include <netinet/sctp_asconf.h> |
michael@0 | 49 | #include <netinet/sctp_bsd_addr.h> |
michael@0 | 50 | #include <netinet/sctp_timer.h> |
michael@0 | 51 | #include <netinet/sctp_crc32.h> |
michael@0 | 52 | #if !defined(__Userspace_os_Windows) |
michael@0 | 53 | #include <netinet/udp.h> |
michael@0 | 54 | #endif |
michael@0 | 55 | #if defined(__FreeBSD__) |
michael@0 | 56 | #include <sys/smp.h> |
michael@0 | 57 | #endif |
michael@0 | 58 | |
michael@0 | 59 | #if defined(__APPLE__) |
michael@0 | 60 | #define APPLE_FILE_NO 2 |
michael@0 | 61 | #endif |
michael@0 | 62 | |
michael@0 | 63 | |
michael@0 | 64 | static void |
michael@0 | 65 | sctp_stop_all_cookie_timers(struct sctp_tcb *stcb) |
michael@0 | 66 | { |
michael@0 | 67 | struct sctp_nets *net; |
michael@0 | 68 | |
michael@0 | 69 | /* This now not only stops all cookie timers |
michael@0 | 70 | * it also stops any INIT timers as well. This |
michael@0 | 71 | * will make sure that the timers are stopped in |
michael@0 | 72 | * all collision cases. |
michael@0 | 73 | */ |
michael@0 | 74 | SCTP_TCB_LOCK_ASSERT(stcb); |
michael@0 | 75 | TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { |
michael@0 | 76 | if (net->rxt_timer.type == SCTP_TIMER_TYPE_COOKIE) { |
michael@0 | 77 | sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE, |
michael@0 | 78 | stcb->sctp_ep, |
michael@0 | 79 | stcb, |
michael@0 | 80 | net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_1); |
michael@0 | 81 | } else if (net->rxt_timer.type == SCTP_TIMER_TYPE_INIT) { |
michael@0 | 82 | sctp_timer_stop(SCTP_TIMER_TYPE_INIT, |
michael@0 | 83 | stcb->sctp_ep, |
michael@0 | 84 | stcb, |
michael@0 | 85 | net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_2); |
michael@0 | 86 | } |
michael@0 | 87 | } |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | /* INIT handler */ |
michael@0 | 91 | static void |
michael@0 | 92 | sctp_handle_init(struct mbuf *m, int iphlen, int offset, |
michael@0 | 93 | struct sockaddr *src, struct sockaddr *dst, struct sctphdr *sh, |
michael@0 | 94 | struct sctp_init_chunk *cp, struct sctp_inpcb *inp, |
michael@0 | 95 | struct sctp_tcb *stcb, int *abort_no_unlock, |
michael@0 | 96 | #if defined(__FreeBSD__) |
michael@0 | 97 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 98 | #endif |
michael@0 | 99 | uint32_t vrf_id, uint16_t port) |
michael@0 | 100 | { |
michael@0 | 101 | struct sctp_init *init; |
michael@0 | 102 | struct mbuf *op_err; |
michael@0 | 103 | |
michael@0 | 104 | SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_handle_init: handling INIT tcb:%p\n", |
michael@0 | 105 | (void *)stcb); |
michael@0 | 106 | if (stcb == NULL) { |
michael@0 | 107 | SCTP_INP_RLOCK(inp); |
michael@0 | 108 | } |
michael@0 | 109 | /* validate length */ |
michael@0 | 110 | if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_init_chunk)) { |
michael@0 | 111 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 112 | sctp_abort_association(inp, stcb, m, iphlen, src, dst, sh, op_err, |
michael@0 | 113 | #if defined(__FreeBSD__) |
michael@0 | 114 | use_mflowid, mflowid, |
michael@0 | 115 | #endif |
michael@0 | 116 | vrf_id, port); |
michael@0 | 117 | if (stcb) |
michael@0 | 118 | *abort_no_unlock = 1; |
michael@0 | 119 | goto outnow; |
michael@0 | 120 | } |
michael@0 | 121 | /* validate parameters */ |
michael@0 | 122 | init = &cp->init; |
michael@0 | 123 | if (init->initiate_tag == 0) { |
michael@0 | 124 | /* protocol error... send abort */ |
michael@0 | 125 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 126 | sctp_abort_association(inp, stcb, m, iphlen, src, dst, sh, op_err, |
michael@0 | 127 | #if defined(__FreeBSD__) |
michael@0 | 128 | use_mflowid, mflowid, |
michael@0 | 129 | #endif |
michael@0 | 130 | vrf_id, port); |
michael@0 | 131 | if (stcb) |
michael@0 | 132 | *abort_no_unlock = 1; |
michael@0 | 133 | goto outnow; |
michael@0 | 134 | } |
michael@0 | 135 | if (ntohl(init->a_rwnd) < SCTP_MIN_RWND) { |
michael@0 | 136 | /* invalid parameter... send abort */ |
michael@0 | 137 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 138 | sctp_abort_association(inp, stcb, m, iphlen, src, dst, sh, op_err, |
michael@0 | 139 | #if defined(__FreeBSD__) |
michael@0 | 140 | use_mflowid, mflowid, |
michael@0 | 141 | #endif |
michael@0 | 142 | vrf_id, port); |
michael@0 | 143 | if (stcb) |
michael@0 | 144 | *abort_no_unlock = 1; |
michael@0 | 145 | goto outnow; |
michael@0 | 146 | } |
michael@0 | 147 | if (init->num_inbound_streams == 0) { |
michael@0 | 148 | /* protocol error... send abort */ |
michael@0 | 149 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 150 | sctp_abort_association(inp, stcb, m, iphlen, src, dst, sh, op_err, |
michael@0 | 151 | #if defined(__FreeBSD__) |
michael@0 | 152 | use_mflowid, mflowid, |
michael@0 | 153 | #endif |
michael@0 | 154 | vrf_id, port); |
michael@0 | 155 | if (stcb) |
michael@0 | 156 | *abort_no_unlock = 1; |
michael@0 | 157 | goto outnow; |
michael@0 | 158 | } |
michael@0 | 159 | if (init->num_outbound_streams == 0) { |
michael@0 | 160 | /* protocol error... send abort */ |
michael@0 | 161 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 162 | sctp_abort_association(inp, stcb, m, iphlen, src, dst, sh, op_err, |
michael@0 | 163 | #if defined(__FreeBSD__) |
michael@0 | 164 | use_mflowid, mflowid, |
michael@0 | 165 | #endif |
michael@0 | 166 | vrf_id, port); |
michael@0 | 167 | if (stcb) |
michael@0 | 168 | *abort_no_unlock = 1; |
michael@0 | 169 | goto outnow; |
michael@0 | 170 | } |
michael@0 | 171 | if (sctp_validate_init_auth_params(m, offset + sizeof(*cp), |
michael@0 | 172 | offset + ntohs(cp->ch.chunk_length))) { |
michael@0 | 173 | /* auth parameter(s) error... send abort */ |
michael@0 | 174 | sctp_abort_association(inp, stcb, m, iphlen, src, dst, sh, NULL, |
michael@0 | 175 | #if defined(__FreeBSD__) |
michael@0 | 176 | use_mflowid, mflowid, |
michael@0 | 177 | #endif |
michael@0 | 178 | vrf_id, port); |
michael@0 | 179 | if (stcb) |
michael@0 | 180 | *abort_no_unlock = 1; |
michael@0 | 181 | goto outnow; |
michael@0 | 182 | } |
michael@0 | 183 | /* We are only accepting if we have a socket with positive so_qlimit.*/ |
michael@0 | 184 | if ((stcb == NULL) && |
michael@0 | 185 | ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || |
michael@0 | 186 | (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) || |
michael@0 | 187 | (inp->sctp_socket == NULL) || |
michael@0 | 188 | (inp->sctp_socket->so_qlimit == 0))) { |
michael@0 | 189 | /* |
michael@0 | 190 | * FIX ME ?? What about TCP model and we have a |
michael@0 | 191 | * match/restart case? Actually no fix is needed. |
michael@0 | 192 | * the lookup will always find the existing assoc so stcb |
michael@0 | 193 | * would not be NULL. It may be questionable to do this |
michael@0 | 194 | * since we COULD just send back the INIT-ACK and hope that |
michael@0 | 195 | * the app did accept()'s by the time the COOKIE was sent. But |
michael@0 | 196 | * there is a price to pay for COOKIE generation and I don't |
michael@0 | 197 | * want to pay it on the chance that the app will actually do |
michael@0 | 198 | * some accepts(). The App just looses and should NOT be in |
michael@0 | 199 | * this state :-) |
michael@0 | 200 | */ |
michael@0 | 201 | if (SCTP_BASE_SYSCTL(sctp_blackhole) == 0) { |
michael@0 | 202 | sctp_send_abort(m, iphlen, src, dst, sh, 0, NULL, |
michael@0 | 203 | #if defined(__FreeBSD__) |
michael@0 | 204 | use_mflowid, mflowid, |
michael@0 | 205 | #endif |
michael@0 | 206 | vrf_id, port); |
michael@0 | 207 | } |
michael@0 | 208 | goto outnow; |
michael@0 | 209 | } |
michael@0 | 210 | if ((stcb != NULL) && |
michael@0 | 211 | (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT)) { |
michael@0 | 212 | SCTPDBG(SCTP_DEBUG_INPUT3, "sctp_handle_init: sending SHUTDOWN-ACK\n"); |
michael@0 | 213 | sctp_send_shutdown_ack(stcb, NULL); |
michael@0 | 214 | sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED); |
michael@0 | 215 | } else { |
michael@0 | 216 | SCTPDBG(SCTP_DEBUG_INPUT3, "sctp_handle_init: sending INIT-ACK\n"); |
michael@0 | 217 | sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, src, dst, |
michael@0 | 218 | sh, cp, |
michael@0 | 219 | #if defined(__FreeBSD__) |
michael@0 | 220 | use_mflowid, mflowid, |
michael@0 | 221 | #endif |
michael@0 | 222 | vrf_id, port, |
michael@0 | 223 | ((stcb == NULL) ? SCTP_HOLDS_LOCK : SCTP_NOT_LOCKED)); |
michael@0 | 224 | } |
michael@0 | 225 | outnow: |
michael@0 | 226 | if (stcb == NULL) { |
michael@0 | 227 | SCTP_INP_RUNLOCK(inp); |
michael@0 | 228 | } |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | /* |
michael@0 | 232 | * process peer "INIT/INIT-ACK" chunk returns value < 0 on error |
michael@0 | 233 | */ |
michael@0 | 234 | |
michael@0 | 235 | int |
michael@0 | 236 | sctp_is_there_unsent_data(struct sctp_tcb *stcb, int so_locked |
michael@0 | 237 | #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 238 | SCTP_UNUSED |
michael@0 | 239 | #endif |
michael@0 | 240 | ) |
michael@0 | 241 | { |
michael@0 | 242 | int unsent_data = 0; |
michael@0 | 243 | unsigned int i; |
michael@0 | 244 | struct sctp_stream_queue_pending *sp; |
michael@0 | 245 | struct sctp_association *asoc; |
michael@0 | 246 | |
michael@0 | 247 | /* This function returns the number of streams that have |
michael@0 | 248 | * true unsent data on them. Note that as it looks through |
michael@0 | 249 | * it will clean up any places that have old data that |
michael@0 | 250 | * has been sent but left at top of stream queue. |
michael@0 | 251 | */ |
michael@0 | 252 | asoc = &stcb->asoc; |
michael@0 | 253 | SCTP_TCB_SEND_LOCK(stcb); |
michael@0 | 254 | if (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { |
michael@0 | 255 | /* Check to see if some data queued */ |
michael@0 | 256 | for (i = 0; i < stcb->asoc.streamoutcnt; i++) { |
michael@0 | 257 | /*sa_ignore FREED_MEMORY*/ |
michael@0 | 258 | sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue); |
michael@0 | 259 | if (sp == NULL) { |
michael@0 | 260 | continue; |
michael@0 | 261 | } |
michael@0 | 262 | if ((sp->msg_is_complete) && |
michael@0 | 263 | (sp->length == 0) && |
michael@0 | 264 | (sp->sender_all_done)) { |
michael@0 | 265 | /* We are doing differed cleanup. Last |
michael@0 | 266 | * time through when we took all the data |
michael@0 | 267 | * the sender_all_done was not set. |
michael@0 | 268 | */ |
michael@0 | 269 | if (sp->put_last_out == 0) { |
michael@0 | 270 | SCTP_PRINTF("Gak, put out entire msg with NO end!-1\n"); |
michael@0 | 271 | SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d\n", |
michael@0 | 272 | sp->sender_all_done, |
michael@0 | 273 | sp->length, |
michael@0 | 274 | sp->msg_is_complete, |
michael@0 | 275 | sp->put_last_out); |
michael@0 | 276 | } |
michael@0 | 277 | atomic_subtract_int(&stcb->asoc.stream_queue_cnt, 1); |
michael@0 | 278 | TAILQ_REMOVE(&stcb->asoc.strmout[i].outqueue, sp, next); |
michael@0 | 279 | if (sp->net) { |
michael@0 | 280 | sctp_free_remote_addr(sp->net); |
michael@0 | 281 | sp->net = NULL; |
michael@0 | 282 | } |
michael@0 | 283 | if (sp->data) { |
michael@0 | 284 | sctp_m_freem(sp->data); |
michael@0 | 285 | sp->data = NULL; |
michael@0 | 286 | } |
michael@0 | 287 | sctp_free_a_strmoq(stcb, sp, so_locked); |
michael@0 | 288 | } else { |
michael@0 | 289 | unsent_data++; |
michael@0 | 290 | break; |
michael@0 | 291 | } |
michael@0 | 292 | } |
michael@0 | 293 | } |
michael@0 | 294 | SCTP_TCB_SEND_UNLOCK(stcb); |
michael@0 | 295 | return (unsent_data); |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | static int |
michael@0 | 299 | sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb) |
michael@0 | 300 | { |
michael@0 | 301 | struct sctp_init *init; |
michael@0 | 302 | struct sctp_association *asoc; |
michael@0 | 303 | struct sctp_nets *lnet; |
michael@0 | 304 | unsigned int i; |
michael@0 | 305 | |
michael@0 | 306 | init = &cp->init; |
michael@0 | 307 | asoc = &stcb->asoc; |
michael@0 | 308 | /* save off parameters */ |
michael@0 | 309 | asoc->peer_vtag = ntohl(init->initiate_tag); |
michael@0 | 310 | asoc->peers_rwnd = ntohl(init->a_rwnd); |
michael@0 | 311 | /* init tsn's */ |
michael@0 | 312 | asoc->highest_tsn_inside_map = asoc->asconf_seq_in = ntohl(init->initial_tsn) - 1; |
michael@0 | 313 | |
michael@0 | 314 | if (!TAILQ_EMPTY(&asoc->nets)) { |
michael@0 | 315 | /* update any ssthresh's that may have a default */ |
michael@0 | 316 | TAILQ_FOREACH(lnet, &asoc->nets, sctp_next) { |
michael@0 | 317 | lnet->ssthresh = asoc->peers_rwnd; |
michael@0 | 318 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_CWND_MONITOR_ENABLE|SCTP_CWND_LOGGING_ENABLE)) { |
michael@0 | 319 | sctp_log_cwnd(stcb, lnet, 0, SCTP_CWND_INITIALIZATION); |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | } |
michael@0 | 323 | } |
michael@0 | 324 | SCTP_TCB_SEND_LOCK(stcb); |
michael@0 | 325 | if (asoc->pre_open_streams > ntohs(init->num_inbound_streams)) { |
michael@0 | 326 | unsigned int newcnt; |
michael@0 | 327 | struct sctp_stream_out *outs; |
michael@0 | 328 | struct sctp_stream_queue_pending *sp, *nsp; |
michael@0 | 329 | struct sctp_tmit_chunk *chk, *nchk; |
michael@0 | 330 | |
michael@0 | 331 | /* abandon the upper streams */ |
michael@0 | 332 | newcnt = ntohs(init->num_inbound_streams); |
michael@0 | 333 | TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) { |
michael@0 | 334 | if (chk->rec.data.stream_number >= newcnt) { |
michael@0 | 335 | TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); |
michael@0 | 336 | asoc->send_queue_cnt--; |
michael@0 | 337 | if (asoc->strmout[chk->rec.data.stream_number].chunks_on_queues > 0) { |
michael@0 | 338 | asoc->strmout[chk->rec.data.stream_number].chunks_on_queues--; |
michael@0 | 339 | #ifdef INVARIANTS |
michael@0 | 340 | } else { |
michael@0 | 341 | panic("No chunks on the queues for sid %u.", chk->rec.data.stream_number); |
michael@0 | 342 | #endif |
michael@0 | 343 | } |
michael@0 | 344 | if (chk->data != NULL) { |
michael@0 | 345 | sctp_free_bufspace(stcb, asoc, chk, 1); |
michael@0 | 346 | sctp_ulp_notify(SCTP_NOTIFY_UNSENT_DG_FAIL, stcb, |
michael@0 | 347 | 0, chk, SCTP_SO_NOT_LOCKED); |
michael@0 | 348 | if (chk->data) { |
michael@0 | 349 | sctp_m_freem(chk->data); |
michael@0 | 350 | chk->data = NULL; |
michael@0 | 351 | } |
michael@0 | 352 | } |
michael@0 | 353 | sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); |
michael@0 | 354 | /*sa_ignore FREED_MEMORY*/ |
michael@0 | 355 | } |
michael@0 | 356 | } |
michael@0 | 357 | if (asoc->strmout) { |
michael@0 | 358 | for (i = newcnt; i < asoc->pre_open_streams; i++) { |
michael@0 | 359 | outs = &asoc->strmout[i]; |
michael@0 | 360 | TAILQ_FOREACH_SAFE(sp, &outs->outqueue, next, nsp) { |
michael@0 | 361 | TAILQ_REMOVE(&outs->outqueue, sp, next); |
michael@0 | 362 | asoc->stream_queue_cnt--; |
michael@0 | 363 | sctp_ulp_notify(SCTP_NOTIFY_SPECIAL_SP_FAIL, |
michael@0 | 364 | stcb, 0, sp, SCTP_SO_NOT_LOCKED); |
michael@0 | 365 | if (sp->data) { |
michael@0 | 366 | sctp_m_freem(sp->data); |
michael@0 | 367 | sp->data = NULL; |
michael@0 | 368 | } |
michael@0 | 369 | if (sp->net) { |
michael@0 | 370 | sctp_free_remote_addr(sp->net); |
michael@0 | 371 | sp->net = NULL; |
michael@0 | 372 | } |
michael@0 | 373 | /* Free the chunk */ |
michael@0 | 374 | sctp_free_a_strmoq(stcb, sp, SCTP_SO_NOT_LOCKED); |
michael@0 | 375 | /*sa_ignore FREED_MEMORY*/ |
michael@0 | 376 | } |
michael@0 | 377 | } |
michael@0 | 378 | } |
michael@0 | 379 | /* cut back the count */ |
michael@0 | 380 | asoc->pre_open_streams = newcnt; |
michael@0 | 381 | } |
michael@0 | 382 | SCTP_TCB_SEND_UNLOCK(stcb); |
michael@0 | 383 | asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams; |
michael@0 | 384 | |
michael@0 | 385 | /* EY - nr_sack: initialize highest tsn in nr_mapping_array */ |
michael@0 | 386 | asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map; |
michael@0 | 387 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { |
michael@0 | 388 | sctp_log_map(0, 5, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT); |
michael@0 | 389 | } |
michael@0 | 390 | /* This is the next one we expect */ |
michael@0 | 391 | asoc->str_reset_seq_in = asoc->asconf_seq_in + 1; |
michael@0 | 392 | |
michael@0 | 393 | asoc->mapping_array_base_tsn = ntohl(init->initial_tsn); |
michael@0 | 394 | asoc->tsn_last_delivered = asoc->cumulative_tsn = asoc->asconf_seq_in; |
michael@0 | 395 | |
michael@0 | 396 | asoc->advanced_peer_ack_point = asoc->last_acked_seq; |
michael@0 | 397 | /* open the requested streams */ |
michael@0 | 398 | |
michael@0 | 399 | if (asoc->strmin != NULL) { |
michael@0 | 400 | /* Free the old ones */ |
michael@0 | 401 | struct sctp_queued_to_read *ctl, *nctl; |
michael@0 | 402 | |
michael@0 | 403 | for (i = 0; i < asoc->streamincnt; i++) { |
michael@0 | 404 | TAILQ_FOREACH_SAFE(ctl, &asoc->strmin[i].inqueue, next, nctl) { |
michael@0 | 405 | TAILQ_REMOVE(&asoc->strmin[i].inqueue, ctl, next); |
michael@0 | 406 | sctp_free_remote_addr(ctl->whoFrom); |
michael@0 | 407 | ctl->whoFrom = NULL; |
michael@0 | 408 | sctp_m_freem(ctl->data); |
michael@0 | 409 | ctl->data = NULL; |
michael@0 | 410 | sctp_free_a_readq(stcb, ctl); |
michael@0 | 411 | } |
michael@0 | 412 | } |
michael@0 | 413 | SCTP_FREE(asoc->strmin, SCTP_M_STRMI); |
michael@0 | 414 | } |
michael@0 | 415 | if (asoc->max_inbound_streams > ntohs(init->num_outbound_streams)) { |
michael@0 | 416 | asoc->streamincnt = ntohs(init->num_outbound_streams); |
michael@0 | 417 | } else { |
michael@0 | 418 | asoc->streamincnt = asoc->max_inbound_streams; |
michael@0 | 419 | } |
michael@0 | 420 | SCTP_MALLOC(asoc->strmin, struct sctp_stream_in *, asoc->streamincnt * |
michael@0 | 421 | sizeof(struct sctp_stream_in), SCTP_M_STRMI); |
michael@0 | 422 | if (asoc->strmin == NULL) { |
michael@0 | 423 | /* we didn't get memory for the streams! */ |
michael@0 | 424 | SCTPDBG(SCTP_DEBUG_INPUT2, "process_init: couldn't get memory for the streams!\n"); |
michael@0 | 425 | return (-1); |
michael@0 | 426 | } |
michael@0 | 427 | for (i = 0; i < asoc->streamincnt; i++) { |
michael@0 | 428 | asoc->strmin[i].stream_no = i; |
michael@0 | 429 | asoc->strmin[i].last_sequence_delivered = 0xffff; |
michael@0 | 430 | TAILQ_INIT(&asoc->strmin[i].inqueue); |
michael@0 | 431 | asoc->strmin[i].delivery_started = 0; |
michael@0 | 432 | } |
michael@0 | 433 | /* |
michael@0 | 434 | * load_address_from_init will put the addresses into the |
michael@0 | 435 | * association when the COOKIE is processed or the INIT-ACK is |
michael@0 | 436 | * processed. Both types of COOKIE's existing and new call this |
michael@0 | 437 | * routine. It will remove addresses that are no longer in the |
michael@0 | 438 | * association (for the restarting case where addresses are |
michael@0 | 439 | * removed). Up front when the INIT arrives we will discard it if it |
michael@0 | 440 | * is a restart and new addresses have been added. |
michael@0 | 441 | */ |
michael@0 | 442 | /* sa_ignore MEMLEAK */ |
michael@0 | 443 | return (0); |
michael@0 | 444 | } |
michael@0 | 445 | |
michael@0 | 446 | /* |
michael@0 | 447 | * INIT-ACK message processing/consumption returns value < 0 on error |
michael@0 | 448 | */ |
michael@0 | 449 | static int |
michael@0 | 450 | sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, |
michael@0 | 451 | struct sockaddr *src, struct sockaddr *dst, struct sctphdr *sh, |
michael@0 | 452 | struct sctp_init_ack_chunk *cp, struct sctp_tcb *stcb, |
michael@0 | 453 | struct sctp_nets *net, int *abort_no_unlock, |
michael@0 | 454 | #if defined(__FreeBSD__) |
michael@0 | 455 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 456 | #endif |
michael@0 | 457 | uint32_t vrf_id) |
michael@0 | 458 | { |
michael@0 | 459 | struct sctp_association *asoc; |
michael@0 | 460 | struct mbuf *op_err; |
michael@0 | 461 | int retval, abort_flag; |
michael@0 | 462 | uint32_t initack_limit; |
michael@0 | 463 | int nat_friendly = 0; |
michael@0 | 464 | |
michael@0 | 465 | /* First verify that we have no illegal param's */ |
michael@0 | 466 | abort_flag = 0; |
michael@0 | 467 | |
michael@0 | 468 | op_err = sctp_arethere_unrecognized_parameters(m, |
michael@0 | 469 | (offset + sizeof(struct sctp_init_chunk)), |
michael@0 | 470 | &abort_flag, (struct sctp_chunkhdr *)cp, &nat_friendly); |
michael@0 | 471 | if (abort_flag) { |
michael@0 | 472 | /* Send an abort and notify peer */ |
michael@0 | 473 | sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED); |
michael@0 | 474 | *abort_no_unlock = 1; |
michael@0 | 475 | return (-1); |
michael@0 | 476 | } |
michael@0 | 477 | asoc = &stcb->asoc; |
michael@0 | 478 | asoc->peer_supports_nat = (uint8_t)nat_friendly; |
michael@0 | 479 | /* process the peer's parameters in the INIT-ACK */ |
michael@0 | 480 | retval = sctp_process_init((struct sctp_init_chunk *)cp, stcb); |
michael@0 | 481 | if (retval < 0) { |
michael@0 | 482 | return (retval); |
michael@0 | 483 | } |
michael@0 | 484 | initack_limit = offset + ntohs(cp->ch.chunk_length); |
michael@0 | 485 | /* load all addresses */ |
michael@0 | 486 | if ((retval = sctp_load_addresses_from_init(stcb, m, |
michael@0 | 487 | (offset + sizeof(struct sctp_init_chunk)), initack_limit, |
michael@0 | 488 | src, dst, NULL))) { |
michael@0 | 489 | /* Huh, we should abort */ |
michael@0 | 490 | SCTPDBG(SCTP_DEBUG_INPUT1, |
michael@0 | 491 | "Load addresses from INIT causes an abort %d\n", |
michael@0 | 492 | retval); |
michael@0 | 493 | sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, |
michael@0 | 494 | src, dst, sh, NULL, |
michael@0 | 495 | #if defined(__FreeBSD__) |
michael@0 | 496 | use_mflowid, mflowid, |
michael@0 | 497 | #endif |
michael@0 | 498 | vrf_id, net->port); |
michael@0 | 499 | *abort_no_unlock = 1; |
michael@0 | 500 | return (-1); |
michael@0 | 501 | } |
michael@0 | 502 | /* if the peer doesn't support asconf, flush the asconf queue */ |
michael@0 | 503 | if (asoc->peer_supports_asconf == 0) { |
michael@0 | 504 | struct sctp_asconf_addr *param, *nparam; |
michael@0 | 505 | |
michael@0 | 506 | TAILQ_FOREACH_SAFE(param, &asoc->asconf_queue, next, nparam) { |
michael@0 | 507 | TAILQ_REMOVE(&asoc->asconf_queue, param, next); |
michael@0 | 508 | SCTP_FREE(param, SCTP_M_ASC_ADDR); |
michael@0 | 509 | } |
michael@0 | 510 | } |
michael@0 | 511 | |
michael@0 | 512 | stcb->asoc.peer_hmac_id = sctp_negotiate_hmacid(stcb->asoc.peer_hmacs, |
michael@0 | 513 | stcb->asoc.local_hmacs); |
michael@0 | 514 | if (op_err) { |
michael@0 | 515 | sctp_queue_op_err(stcb, op_err); |
michael@0 | 516 | /* queuing will steal away the mbuf chain to the out queue */ |
michael@0 | 517 | op_err = NULL; |
michael@0 | 518 | } |
michael@0 | 519 | /* extract the cookie and queue it to "echo" it back... */ |
michael@0 | 520 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 521 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 522 | stcb->asoc.overall_error_count, |
michael@0 | 523 | 0, |
michael@0 | 524 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 525 | __LINE__); |
michael@0 | 526 | } |
michael@0 | 527 | stcb->asoc.overall_error_count = 0; |
michael@0 | 528 | net->error_count = 0; |
michael@0 | 529 | |
michael@0 | 530 | /* |
michael@0 | 531 | * Cancel the INIT timer, We do this first before queueing the |
michael@0 | 532 | * cookie. We always cancel at the primary to assue that we are |
michael@0 | 533 | * canceling the timer started by the INIT which always goes to the |
michael@0 | 534 | * primary. |
michael@0 | 535 | */ |
michael@0 | 536 | sctp_timer_stop(SCTP_TIMER_TYPE_INIT, stcb->sctp_ep, stcb, |
michael@0 | 537 | asoc->primary_destination, SCTP_FROM_SCTP_INPUT+SCTP_LOC_4); |
michael@0 | 538 | |
michael@0 | 539 | /* calculate the RTO */ |
michael@0 | 540 | net->RTO = sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered, sctp_align_safe_nocopy, |
michael@0 | 541 | SCTP_RTT_FROM_NON_DATA); |
michael@0 | 542 | |
michael@0 | 543 | retval = sctp_send_cookie_echo(m, offset, stcb, net); |
michael@0 | 544 | if (retval < 0) { |
michael@0 | 545 | /* |
michael@0 | 546 | * No cookie, we probably should send a op error. But in any |
michael@0 | 547 | * case if there is no cookie in the INIT-ACK, we can |
michael@0 | 548 | * abandon the peer, its broke. |
michael@0 | 549 | */ |
michael@0 | 550 | if (retval == -3) { |
michael@0 | 551 | /* We abort with an error of missing mandatory param */ |
michael@0 | 552 | op_err = |
michael@0 | 553 | sctp_generate_invmanparam(SCTP_CAUSE_MISSING_PARAM); |
michael@0 | 554 | if (op_err) { |
michael@0 | 555 | /* |
michael@0 | 556 | * Expand beyond to include the mandatory |
michael@0 | 557 | * param cookie |
michael@0 | 558 | */ |
michael@0 | 559 | struct sctp_inv_mandatory_param *mp; |
michael@0 | 560 | |
michael@0 | 561 | SCTP_BUF_LEN(op_err) = |
michael@0 | 562 | sizeof(struct sctp_inv_mandatory_param); |
michael@0 | 563 | mp = mtod(op_err, |
michael@0 | 564 | struct sctp_inv_mandatory_param *); |
michael@0 | 565 | /* Subtract the reserved param */ |
michael@0 | 566 | mp->length = |
michael@0 | 567 | htons(sizeof(struct sctp_inv_mandatory_param) - 2); |
michael@0 | 568 | mp->num_param = htonl(1); |
michael@0 | 569 | mp->param = htons(SCTP_STATE_COOKIE); |
michael@0 | 570 | mp->resv = 0; |
michael@0 | 571 | } |
michael@0 | 572 | sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, |
michael@0 | 573 | src, dst, sh, op_err, |
michael@0 | 574 | #if defined(__FreeBSD__) |
michael@0 | 575 | use_mflowid, mflowid, |
michael@0 | 576 | #endif |
michael@0 | 577 | vrf_id, net->port); |
michael@0 | 578 | *abort_no_unlock = 1; |
michael@0 | 579 | } |
michael@0 | 580 | return (retval); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | return (0); |
michael@0 | 584 | } |
michael@0 | 585 | |
michael@0 | 586 | static void |
michael@0 | 587 | sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp, |
michael@0 | 588 | struct sctp_tcb *stcb, struct sctp_nets *net) |
michael@0 | 589 | { |
michael@0 | 590 | struct sockaddr_storage store; |
michael@0 | 591 | struct sctp_nets *r_net, *f_net; |
michael@0 | 592 | struct timeval tv; |
michael@0 | 593 | int req_prim = 0; |
michael@0 | 594 | uint16_t old_error_counter; |
michael@0 | 595 | #ifdef INET |
michael@0 | 596 | struct sockaddr_in *sin; |
michael@0 | 597 | #endif |
michael@0 | 598 | #ifdef INET6 |
michael@0 | 599 | struct sockaddr_in6 *sin6; |
michael@0 | 600 | #endif |
michael@0 | 601 | #if defined(__Userspace__) |
michael@0 | 602 | struct sockaddr_conn *sconn; |
michael@0 | 603 | #endif |
michael@0 | 604 | |
michael@0 | 605 | if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_heartbeat_chunk)) { |
michael@0 | 606 | /* Invalid length */ |
michael@0 | 607 | return; |
michael@0 | 608 | } |
michael@0 | 609 | |
michael@0 | 610 | memset(&store, 0, sizeof(store)); |
michael@0 | 611 | switch (cp->heartbeat.hb_info.addr_family) { |
michael@0 | 612 | #ifdef INET |
michael@0 | 613 | case AF_INET: |
michael@0 | 614 | if (cp->heartbeat.hb_info.addr_len == sizeof(struct sockaddr_in)) { |
michael@0 | 615 | sin = (struct sockaddr_in *)&store; |
michael@0 | 616 | sin->sin_family = cp->heartbeat.hb_info.addr_family; |
michael@0 | 617 | #ifdef HAVE_SIN_LEN |
michael@0 | 618 | sin->sin_len = cp->heartbeat.hb_info.addr_len; |
michael@0 | 619 | #endif |
michael@0 | 620 | sin->sin_port = stcb->rport; |
michael@0 | 621 | memcpy(&sin->sin_addr, cp->heartbeat.hb_info.address, |
michael@0 | 622 | sizeof(sin->sin_addr)); |
michael@0 | 623 | } else { |
michael@0 | 624 | return; |
michael@0 | 625 | } |
michael@0 | 626 | break; |
michael@0 | 627 | #endif |
michael@0 | 628 | #ifdef INET6 |
michael@0 | 629 | case AF_INET6: |
michael@0 | 630 | if (cp->heartbeat.hb_info.addr_len == sizeof(struct sockaddr_in6)) { |
michael@0 | 631 | sin6 = (struct sockaddr_in6 *)&store; |
michael@0 | 632 | sin6->sin6_family = cp->heartbeat.hb_info.addr_family; |
michael@0 | 633 | #ifdef HAVE_SIN6_LEN |
michael@0 | 634 | sin6->sin6_len = cp->heartbeat.hb_info.addr_len; |
michael@0 | 635 | #endif |
michael@0 | 636 | sin6->sin6_port = stcb->rport; |
michael@0 | 637 | memcpy(&sin6->sin6_addr, cp->heartbeat.hb_info.address, |
michael@0 | 638 | sizeof(sin6->sin6_addr)); |
michael@0 | 639 | } else { |
michael@0 | 640 | return; |
michael@0 | 641 | } |
michael@0 | 642 | break; |
michael@0 | 643 | #endif |
michael@0 | 644 | #if defined(__Userspace__) |
michael@0 | 645 | case AF_CONN: |
michael@0 | 646 | if (cp->heartbeat.hb_info.addr_len == sizeof(struct sockaddr_conn)) { |
michael@0 | 647 | sconn = (struct sockaddr_conn *)&store; |
michael@0 | 648 | sconn->sconn_family = cp->heartbeat.hb_info.addr_family; |
michael@0 | 649 | #ifdef HAVE_SCONN_LEN |
michael@0 | 650 | sconn->sconn_len = cp->heartbeat.hb_info.addr_len; |
michael@0 | 651 | #endif |
michael@0 | 652 | sconn->sconn_port = stcb->rport; |
michael@0 | 653 | memcpy(&sconn->sconn_addr, cp->heartbeat.hb_info.address, |
michael@0 | 654 | sizeof(sconn->sconn_addr)); |
michael@0 | 655 | } else { |
michael@0 | 656 | return; |
michael@0 | 657 | } |
michael@0 | 658 | break; |
michael@0 | 659 | #endif |
michael@0 | 660 | default: |
michael@0 | 661 | return; |
michael@0 | 662 | } |
michael@0 | 663 | r_net = sctp_findnet(stcb, (struct sockaddr *)&store); |
michael@0 | 664 | if (r_net == NULL) { |
michael@0 | 665 | SCTPDBG(SCTP_DEBUG_INPUT1, "Huh? I can't find the address I sent it to, discard\n"); |
michael@0 | 666 | return; |
michael@0 | 667 | } |
michael@0 | 668 | if ((r_net && (r_net->dest_state & SCTP_ADDR_UNCONFIRMED)) && |
michael@0 | 669 | (r_net->heartbeat_random1 == cp->heartbeat.hb_info.random_value1) && |
michael@0 | 670 | (r_net->heartbeat_random2 == cp->heartbeat.hb_info.random_value2)) { |
michael@0 | 671 | /* |
michael@0 | 672 | * If the its a HB and it's random value is correct when can |
michael@0 | 673 | * confirm the destination. |
michael@0 | 674 | */ |
michael@0 | 675 | r_net->dest_state &= ~SCTP_ADDR_UNCONFIRMED; |
michael@0 | 676 | if (r_net->dest_state & SCTP_ADDR_REQ_PRIMARY) { |
michael@0 | 677 | stcb->asoc.primary_destination = r_net; |
michael@0 | 678 | r_net->dest_state &= ~SCTP_ADDR_REQ_PRIMARY; |
michael@0 | 679 | f_net = TAILQ_FIRST(&stcb->asoc.nets); |
michael@0 | 680 | if (f_net != r_net) { |
michael@0 | 681 | /* first one on the list is NOT the primary |
michael@0 | 682 | * sctp_cmpaddr() is much more efficent if |
michael@0 | 683 | * the primary is the first on the list, make it |
michael@0 | 684 | * so. |
michael@0 | 685 | */ |
michael@0 | 686 | TAILQ_REMOVE(&stcb->asoc.nets, r_net, sctp_next); |
michael@0 | 687 | TAILQ_INSERT_HEAD(&stcb->asoc.nets, r_net, sctp_next); |
michael@0 | 688 | } |
michael@0 | 689 | req_prim = 1; |
michael@0 | 690 | } |
michael@0 | 691 | sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, |
michael@0 | 692 | stcb, 0, (void *)r_net, SCTP_SO_NOT_LOCKED); |
michael@0 | 693 | sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3); |
michael@0 | 694 | sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net); |
michael@0 | 695 | } |
michael@0 | 696 | old_error_counter = r_net->error_count; |
michael@0 | 697 | r_net->error_count = 0; |
michael@0 | 698 | r_net->hb_responded = 1; |
michael@0 | 699 | tv.tv_sec = cp->heartbeat.hb_info.time_value_1; |
michael@0 | 700 | tv.tv_usec = cp->heartbeat.hb_info.time_value_2; |
michael@0 | 701 | /* Now lets do a RTO with this */ |
michael@0 | 702 | r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv, sctp_align_safe_nocopy, |
michael@0 | 703 | SCTP_RTT_FROM_NON_DATA); |
michael@0 | 704 | if (!(r_net->dest_state & SCTP_ADDR_REACHABLE)) { |
michael@0 | 705 | r_net->dest_state |= SCTP_ADDR_REACHABLE; |
michael@0 | 706 | sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, |
michael@0 | 707 | 0, (void *)r_net, SCTP_SO_NOT_LOCKED); |
michael@0 | 708 | } |
michael@0 | 709 | if (r_net->dest_state & SCTP_ADDR_PF) { |
michael@0 | 710 | r_net->dest_state &= ~SCTP_ADDR_PF; |
michael@0 | 711 | stcb->asoc.cc_functions.sctp_cwnd_update_exit_pf(stcb, net); |
michael@0 | 712 | } |
michael@0 | 713 | if (old_error_counter > 0) { |
michael@0 | 714 | sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_3); |
michael@0 | 715 | sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, r_net); |
michael@0 | 716 | } |
michael@0 | 717 | if (r_net == stcb->asoc.primary_destination) { |
michael@0 | 718 | if (stcb->asoc.alternate) { |
michael@0 | 719 | /* release the alternate, primary is good */ |
michael@0 | 720 | sctp_free_remote_addr(stcb->asoc.alternate); |
michael@0 | 721 | stcb->asoc.alternate = NULL; |
michael@0 | 722 | } |
michael@0 | 723 | } |
michael@0 | 724 | /* Mobility adaptation */ |
michael@0 | 725 | if (req_prim) { |
michael@0 | 726 | if ((sctp_is_mobility_feature_on(stcb->sctp_ep, |
michael@0 | 727 | SCTP_MOBILITY_BASE) || |
michael@0 | 728 | sctp_is_mobility_feature_on(stcb->sctp_ep, |
michael@0 | 729 | SCTP_MOBILITY_FASTHANDOFF)) && |
michael@0 | 730 | sctp_is_mobility_feature_on(stcb->sctp_ep, |
michael@0 | 731 | SCTP_MOBILITY_PRIM_DELETED)) { |
michael@0 | 732 | |
michael@0 | 733 | sctp_timer_stop(SCTP_TIMER_TYPE_PRIM_DELETED, stcb->sctp_ep, stcb, NULL, SCTP_FROM_SCTP_TIMER+SCTP_LOC_7); |
michael@0 | 734 | if (sctp_is_mobility_feature_on(stcb->sctp_ep, |
michael@0 | 735 | SCTP_MOBILITY_FASTHANDOFF)) { |
michael@0 | 736 | sctp_assoc_immediate_retrans(stcb, |
michael@0 | 737 | stcb->asoc.primary_destination); |
michael@0 | 738 | } |
michael@0 | 739 | if (sctp_is_mobility_feature_on(stcb->sctp_ep, |
michael@0 | 740 | SCTP_MOBILITY_BASE)) { |
michael@0 | 741 | sctp_move_chunks_from_net(stcb, |
michael@0 | 742 | stcb->asoc.deleted_primary); |
michael@0 | 743 | } |
michael@0 | 744 | sctp_delete_prim_timer(stcb->sctp_ep, stcb, |
michael@0 | 745 | stcb->asoc.deleted_primary); |
michael@0 | 746 | } |
michael@0 | 747 | } |
michael@0 | 748 | } |
michael@0 | 749 | |
michael@0 | 750 | static int |
michael@0 | 751 | sctp_handle_nat_colliding_state(struct sctp_tcb *stcb) |
michael@0 | 752 | { |
michael@0 | 753 | /* return 0 means we want you to proceed with the abort |
michael@0 | 754 | * non-zero means no abort processing |
michael@0 | 755 | */ |
michael@0 | 756 | struct sctpasochead *head; |
michael@0 | 757 | |
michael@0 | 758 | if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_WAIT) { |
michael@0 | 759 | /* generate a new vtag and send init */ |
michael@0 | 760 | LIST_REMOVE(stcb, sctp_asocs); |
michael@0 | 761 | stcb->asoc.my_vtag = sctp_select_a_tag(stcb->sctp_ep, stcb->sctp_ep->sctp_lport, stcb->rport, 1); |
michael@0 | 762 | head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, SCTP_BASE_INFO(hashasocmark))]; |
michael@0 | 763 | /* put it in the bucket in the vtag hash of assoc's for the system */ |
michael@0 | 764 | LIST_INSERT_HEAD(head, stcb, sctp_asocs); |
michael@0 | 765 | sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); |
michael@0 | 766 | return (1); |
michael@0 | 767 | } |
michael@0 | 768 | if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_ECHOED) { |
michael@0 | 769 | /* treat like a case where the cookie expired i.e.: |
michael@0 | 770 | * - dump current cookie. |
michael@0 | 771 | * - generate a new vtag. |
michael@0 | 772 | * - resend init. |
michael@0 | 773 | */ |
michael@0 | 774 | /* generate a new vtag and send init */ |
michael@0 | 775 | LIST_REMOVE(stcb, sctp_asocs); |
michael@0 | 776 | stcb->asoc.state &= ~SCTP_STATE_COOKIE_ECHOED; |
michael@0 | 777 | stcb->asoc.state |= SCTP_STATE_COOKIE_WAIT; |
michael@0 | 778 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 779 | sctp_toss_old_cookies(stcb, &stcb->asoc); |
michael@0 | 780 | stcb->asoc.my_vtag = sctp_select_a_tag(stcb->sctp_ep, stcb->sctp_ep->sctp_lport, stcb->rport, 1); |
michael@0 | 781 | head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, SCTP_BASE_INFO(hashasocmark))]; |
michael@0 | 782 | /* put it in the bucket in the vtag hash of assoc's for the system */ |
michael@0 | 783 | LIST_INSERT_HEAD(head, stcb, sctp_asocs); |
michael@0 | 784 | sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); |
michael@0 | 785 | return (1); |
michael@0 | 786 | } |
michael@0 | 787 | return (0); |
michael@0 | 788 | } |
michael@0 | 789 | |
michael@0 | 790 | static int |
michael@0 | 791 | sctp_handle_nat_missing_state(struct sctp_tcb *stcb, |
michael@0 | 792 | struct sctp_nets *net) |
michael@0 | 793 | |
michael@0 | 794 | { |
michael@0 | 795 | /* return 0 means we want you to proceed with the abort |
michael@0 | 796 | * non-zero means no abort processing |
michael@0 | 797 | */ |
michael@0 | 798 | if (stcb->asoc.peer_supports_auth == 0) { |
michael@0 | 799 | SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_handle_nat_missing_state: Peer does not support AUTH, cannot send an asconf\n"); |
michael@0 | 800 | return (0); |
michael@0 | 801 | } |
michael@0 | 802 | sctp_asconf_send_nat_state_update(stcb, net); |
michael@0 | 803 | return (1); |
michael@0 | 804 | } |
michael@0 | 805 | |
michael@0 | 806 | |
michael@0 | 807 | static void |
michael@0 | 808 | sctp_handle_abort(struct sctp_abort_chunk *abort, |
michael@0 | 809 | struct sctp_tcb *stcb, struct sctp_nets *net) |
michael@0 | 810 | { |
michael@0 | 811 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 812 | struct socket *so; |
michael@0 | 813 | #endif |
michael@0 | 814 | uint16_t len; |
michael@0 | 815 | uint16_t error; |
michael@0 | 816 | |
michael@0 | 817 | SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_handle_abort: handling ABORT\n"); |
michael@0 | 818 | if (stcb == NULL) |
michael@0 | 819 | return; |
michael@0 | 820 | |
michael@0 | 821 | len = ntohs(abort->ch.chunk_length); |
michael@0 | 822 | if (len > sizeof (struct sctp_chunkhdr)) { |
michael@0 | 823 | /* Need to check the cause codes for our |
michael@0 | 824 | * two magic nat aborts which don't kill the assoc |
michael@0 | 825 | * necessarily. |
michael@0 | 826 | */ |
michael@0 | 827 | struct sctp_missing_nat_state *natc; |
michael@0 | 828 | |
michael@0 | 829 | natc = (struct sctp_missing_nat_state *)(abort + 1); |
michael@0 | 830 | error = ntohs(natc->cause); |
michael@0 | 831 | if (error == SCTP_CAUSE_NAT_COLLIDING_STATE) { |
michael@0 | 832 | SCTPDBG(SCTP_DEBUG_INPUT2, "Received Colliding state abort flags:%x\n", |
michael@0 | 833 | abort->ch.chunk_flags); |
michael@0 | 834 | if (sctp_handle_nat_colliding_state(stcb)) { |
michael@0 | 835 | return; |
michael@0 | 836 | } |
michael@0 | 837 | } else if (error == SCTP_CAUSE_NAT_MISSING_STATE) { |
michael@0 | 838 | SCTPDBG(SCTP_DEBUG_INPUT2, "Received missing state abort flags:%x\n", |
michael@0 | 839 | abort->ch.chunk_flags); |
michael@0 | 840 | if (sctp_handle_nat_missing_state(stcb, net)) { |
michael@0 | 841 | return; |
michael@0 | 842 | } |
michael@0 | 843 | } |
michael@0 | 844 | } else { |
michael@0 | 845 | error = 0; |
michael@0 | 846 | } |
michael@0 | 847 | /* stop any receive timers */ |
michael@0 | 848 | sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_6); |
michael@0 | 849 | /* notify user of the abort and clean up... */ |
michael@0 | 850 | sctp_abort_notification(stcb, 1, error, abort, SCTP_SO_NOT_LOCKED); |
michael@0 | 851 | /* free the tcb */ |
michael@0 | 852 | SCTP_STAT_INCR_COUNTER32(sctps_aborted); |
michael@0 | 853 | if ((SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) || |
michael@0 | 854 | (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { |
michael@0 | 855 | SCTP_STAT_DECR_GAUGE32(sctps_currestab); |
michael@0 | 856 | } |
michael@0 | 857 | #ifdef SCTP_ASOCLOG_OF_TSNS |
michael@0 | 858 | sctp_print_out_track_log(stcb); |
michael@0 | 859 | #endif |
michael@0 | 860 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 861 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 862 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 863 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 864 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 865 | SCTP_TCB_LOCK(stcb); |
michael@0 | 866 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 867 | #endif |
michael@0 | 868 | stcb->asoc.state |= SCTP_STATE_WAS_ABORTED; |
michael@0 | 869 | (void)sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC, |
michael@0 | 870 | SCTP_FROM_SCTP_INPUT+SCTP_LOC_6); |
michael@0 | 871 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 872 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 873 | #endif |
michael@0 | 874 | SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_handle_abort: finished\n"); |
michael@0 | 875 | } |
michael@0 | 876 | |
michael@0 | 877 | static void |
michael@0 | 878 | sctp_start_net_timers(struct sctp_tcb *stcb) |
michael@0 | 879 | { |
michael@0 | 880 | uint32_t cnt_hb_sent; |
michael@0 | 881 | struct sctp_nets *net; |
michael@0 | 882 | |
michael@0 | 883 | cnt_hb_sent = 0; |
michael@0 | 884 | TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { |
michael@0 | 885 | /* For each network start: |
michael@0 | 886 | * 1) A pmtu timer. |
michael@0 | 887 | * 2) A HB timer |
michael@0 | 888 | * 3) If the dest in unconfirmed send |
michael@0 | 889 | * a hb as well if under max_hb_burst have |
michael@0 | 890 | * been sent. |
michael@0 | 891 | */ |
michael@0 | 892 | sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb, net); |
michael@0 | 893 | sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net); |
michael@0 | 894 | if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) && |
michael@0 | 895 | (cnt_hb_sent < SCTP_BASE_SYSCTL(sctp_hb_maxburst))) { |
michael@0 | 896 | sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); |
michael@0 | 897 | cnt_hb_sent++; |
michael@0 | 898 | } |
michael@0 | 899 | } |
michael@0 | 900 | if (cnt_hb_sent) { |
michael@0 | 901 | sctp_chunk_output(stcb->sctp_ep, stcb, |
michael@0 | 902 | SCTP_OUTPUT_FROM_COOKIE_ACK, |
michael@0 | 903 | SCTP_SO_NOT_LOCKED); |
michael@0 | 904 | } |
michael@0 | 905 | } |
michael@0 | 906 | |
michael@0 | 907 | |
michael@0 | 908 | static void |
michael@0 | 909 | sctp_handle_shutdown(struct sctp_shutdown_chunk *cp, |
michael@0 | 910 | struct sctp_tcb *stcb, struct sctp_nets *net, int *abort_flag) |
michael@0 | 911 | { |
michael@0 | 912 | struct sctp_association *asoc; |
michael@0 | 913 | int some_on_streamwheel; |
michael@0 | 914 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 915 | struct socket *so; |
michael@0 | 916 | #endif |
michael@0 | 917 | |
michael@0 | 918 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 919 | "sctp_handle_shutdown: handling SHUTDOWN\n"); |
michael@0 | 920 | if (stcb == NULL) |
michael@0 | 921 | return; |
michael@0 | 922 | asoc = &stcb->asoc; |
michael@0 | 923 | if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) || |
michael@0 | 924 | (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED)) { |
michael@0 | 925 | return; |
michael@0 | 926 | } |
michael@0 | 927 | if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_shutdown_chunk)) { |
michael@0 | 928 | /* Shutdown NOT the expected size */ |
michael@0 | 929 | return; |
michael@0 | 930 | } else { |
michael@0 | 931 | sctp_update_acked(stcb, cp, abort_flag); |
michael@0 | 932 | if (*abort_flag) { |
michael@0 | 933 | return; |
michael@0 | 934 | } |
michael@0 | 935 | } |
michael@0 | 936 | if (asoc->control_pdapi) { |
michael@0 | 937 | /* With a normal shutdown |
michael@0 | 938 | * we assume the end of last record. |
michael@0 | 939 | */ |
michael@0 | 940 | SCTP_INP_READ_LOCK(stcb->sctp_ep); |
michael@0 | 941 | asoc->control_pdapi->end_added = 1; |
michael@0 | 942 | asoc->control_pdapi->pdapi_aborted = 1; |
michael@0 | 943 | asoc->control_pdapi = NULL; |
michael@0 | 944 | SCTP_INP_READ_UNLOCK(stcb->sctp_ep); |
michael@0 | 945 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 946 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 947 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 948 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 949 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 950 | SCTP_TCB_LOCK(stcb); |
michael@0 | 951 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 952 | if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { |
michael@0 | 953 | /* assoc was freed while we were unlocked */ |
michael@0 | 954 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 955 | return; |
michael@0 | 956 | } |
michael@0 | 957 | #endif |
michael@0 | 958 | sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket); |
michael@0 | 959 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 960 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 961 | #endif |
michael@0 | 962 | } |
michael@0 | 963 | /* goto SHUTDOWN_RECEIVED state to block new requests */ |
michael@0 | 964 | if (stcb->sctp_socket) { |
michael@0 | 965 | if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_RECEIVED) && |
michael@0 | 966 | (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT) && |
michael@0 | 967 | (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT)) { |
michael@0 | 968 | SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_RECEIVED); |
michael@0 | 969 | SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); |
michael@0 | 970 | /* notify upper layer that peer has initiated a shutdown */ |
michael@0 | 971 | sctp_ulp_notify(SCTP_NOTIFY_PEER_SHUTDOWN, stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 972 | |
michael@0 | 973 | /* reset time */ |
michael@0 | 974 | (void)SCTP_GETTIME_TIMEVAL(&asoc->time_entered); |
michael@0 | 975 | } |
michael@0 | 976 | } |
michael@0 | 977 | if (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) { |
michael@0 | 978 | /* |
michael@0 | 979 | * stop the shutdown timer, since we WILL move to |
michael@0 | 980 | * SHUTDOWN-ACK-SENT. |
michael@0 | 981 | */ |
michael@0 | 982 | sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_8); |
michael@0 | 983 | } |
michael@0 | 984 | /* Now is there unsent data on a stream somewhere? */ |
michael@0 | 985 | some_on_streamwheel = sctp_is_there_unsent_data(stcb, SCTP_SO_NOT_LOCKED); |
michael@0 | 986 | |
michael@0 | 987 | if (!TAILQ_EMPTY(&asoc->send_queue) || |
michael@0 | 988 | !TAILQ_EMPTY(&asoc->sent_queue) || |
michael@0 | 989 | some_on_streamwheel) { |
michael@0 | 990 | /* By returning we will push more data out */ |
michael@0 | 991 | return; |
michael@0 | 992 | } else { |
michael@0 | 993 | /* no outstanding data to send, so move on... */ |
michael@0 | 994 | /* send SHUTDOWN-ACK */ |
michael@0 | 995 | /* move to SHUTDOWN-ACK-SENT state */ |
michael@0 | 996 | if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) || |
michael@0 | 997 | (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) { |
michael@0 | 998 | SCTP_STAT_DECR_GAUGE32(sctps_currestab); |
michael@0 | 999 | } |
michael@0 | 1000 | SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_ACK_SENT); |
michael@0 | 1001 | SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING); |
michael@0 | 1002 | sctp_stop_timers_for_shutdown(stcb); |
michael@0 | 1003 | sctp_send_shutdown_ack(stcb, net); |
michael@0 | 1004 | sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK, stcb->sctp_ep, |
michael@0 | 1005 | stcb, net); |
michael@0 | 1006 | } |
michael@0 | 1007 | } |
michael@0 | 1008 | |
michael@0 | 1009 | static void |
michael@0 | 1010 | sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp SCTP_UNUSED, |
michael@0 | 1011 | struct sctp_tcb *stcb, |
michael@0 | 1012 | struct sctp_nets *net) |
michael@0 | 1013 | { |
michael@0 | 1014 | struct sctp_association *asoc; |
michael@0 | 1015 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1016 | struct socket *so; |
michael@0 | 1017 | |
michael@0 | 1018 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 1019 | #endif |
michael@0 | 1020 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 1021 | "sctp_handle_shutdown_ack: handling SHUTDOWN ACK\n"); |
michael@0 | 1022 | if (stcb == NULL) |
michael@0 | 1023 | return; |
michael@0 | 1024 | |
michael@0 | 1025 | asoc = &stcb->asoc; |
michael@0 | 1026 | /* process according to association state */ |
michael@0 | 1027 | if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) || |
michael@0 | 1028 | (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED)) { |
michael@0 | 1029 | /* unexpected SHUTDOWN-ACK... do OOTB handling... */ |
michael@0 | 1030 | sctp_send_shutdown_complete(stcb, net, 1); |
michael@0 | 1031 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1032 | return; |
michael@0 | 1033 | } |
michael@0 | 1034 | if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) && |
michael@0 | 1035 | (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { |
michael@0 | 1036 | /* unexpected SHUTDOWN-ACK... so ignore... */ |
michael@0 | 1037 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1038 | return; |
michael@0 | 1039 | } |
michael@0 | 1040 | if (asoc->control_pdapi) { |
michael@0 | 1041 | /* With a normal shutdown |
michael@0 | 1042 | * we assume the end of last record. |
michael@0 | 1043 | */ |
michael@0 | 1044 | SCTP_INP_READ_LOCK(stcb->sctp_ep); |
michael@0 | 1045 | asoc->control_pdapi->end_added = 1; |
michael@0 | 1046 | asoc->control_pdapi->pdapi_aborted = 1; |
michael@0 | 1047 | asoc->control_pdapi = NULL; |
michael@0 | 1048 | SCTP_INP_READ_UNLOCK(stcb->sctp_ep); |
michael@0 | 1049 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1050 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1051 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1052 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 1053 | SCTP_TCB_LOCK(stcb); |
michael@0 | 1054 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1055 | if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { |
michael@0 | 1056 | /* assoc was freed while we were unlocked */ |
michael@0 | 1057 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1058 | return; |
michael@0 | 1059 | } |
michael@0 | 1060 | #endif |
michael@0 | 1061 | sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket); |
michael@0 | 1062 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1063 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1064 | #endif |
michael@0 | 1065 | } |
michael@0 | 1066 | #ifdef INVARIANTS |
michael@0 | 1067 | if (!TAILQ_EMPTY(&asoc->send_queue) || |
michael@0 | 1068 | !TAILQ_EMPTY(&asoc->sent_queue) || |
michael@0 | 1069 | !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { |
michael@0 | 1070 | panic("Queues are not empty when handling SHUTDOWN-ACK"); |
michael@0 | 1071 | } |
michael@0 | 1072 | #endif |
michael@0 | 1073 | /* stop the timer */ |
michael@0 | 1074 | sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_9); |
michael@0 | 1075 | /* send SHUTDOWN-COMPLETE */ |
michael@0 | 1076 | sctp_send_shutdown_complete(stcb, net, 0); |
michael@0 | 1077 | /* notify upper layer protocol */ |
michael@0 | 1078 | if (stcb->sctp_socket) { |
michael@0 | 1079 | if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || |
michael@0 | 1080 | (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { |
michael@0 | 1081 | stcb->sctp_socket->so_snd.sb_cc = 0; |
michael@0 | 1082 | } |
michael@0 | 1083 | sctp_ulp_notify(SCTP_NOTIFY_ASSOC_DOWN, stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 1084 | } |
michael@0 | 1085 | SCTP_STAT_INCR_COUNTER32(sctps_shutdown); |
michael@0 | 1086 | /* free the TCB but first save off the ep */ |
michael@0 | 1087 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1088 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1089 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1090 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 1091 | SCTP_TCB_LOCK(stcb); |
michael@0 | 1092 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1093 | #endif |
michael@0 | 1094 | (void)sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC, |
michael@0 | 1095 | SCTP_FROM_SCTP_INPUT+SCTP_LOC_10); |
michael@0 | 1096 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1097 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1098 | #endif |
michael@0 | 1099 | } |
michael@0 | 1100 | |
michael@0 | 1101 | /* |
michael@0 | 1102 | * Skip past the param header and then we will find the chunk that caused the |
michael@0 | 1103 | * problem. There are two possiblities ASCONF or FWD-TSN other than that and |
michael@0 | 1104 | * our peer must be broken. |
michael@0 | 1105 | */ |
michael@0 | 1106 | static void |
michael@0 | 1107 | sctp_process_unrecog_chunk(struct sctp_tcb *stcb, struct sctp_paramhdr *phdr, |
michael@0 | 1108 | struct sctp_nets *net) |
michael@0 | 1109 | { |
michael@0 | 1110 | struct sctp_chunkhdr *chk; |
michael@0 | 1111 | |
michael@0 | 1112 | chk = (struct sctp_chunkhdr *)((caddr_t)phdr + sizeof(*phdr)); |
michael@0 | 1113 | switch (chk->chunk_type) { |
michael@0 | 1114 | case SCTP_ASCONF_ACK: |
michael@0 | 1115 | case SCTP_ASCONF: |
michael@0 | 1116 | sctp_asconf_cleanup(stcb, net); |
michael@0 | 1117 | break; |
michael@0 | 1118 | case SCTP_FORWARD_CUM_TSN: |
michael@0 | 1119 | stcb->asoc.peer_supports_prsctp = 0; |
michael@0 | 1120 | break; |
michael@0 | 1121 | default: |
michael@0 | 1122 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 1123 | "Peer does not support chunk type %d(%x)??\n", |
michael@0 | 1124 | chk->chunk_type, (uint32_t) chk->chunk_type); |
michael@0 | 1125 | break; |
michael@0 | 1126 | } |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | /* |
michael@0 | 1130 | * Skip past the param header and then we will find the param that caused the |
michael@0 | 1131 | * problem. There are a number of param's in a ASCONF OR the prsctp param |
michael@0 | 1132 | * these will turn of specific features. |
michael@0 | 1133 | */ |
michael@0 | 1134 | static void |
michael@0 | 1135 | sctp_process_unrecog_param(struct sctp_tcb *stcb, struct sctp_paramhdr *phdr) |
michael@0 | 1136 | { |
michael@0 | 1137 | struct sctp_paramhdr *pbad; |
michael@0 | 1138 | |
michael@0 | 1139 | pbad = phdr + 1; |
michael@0 | 1140 | switch (ntohs(pbad->param_type)) { |
michael@0 | 1141 | /* pr-sctp draft */ |
michael@0 | 1142 | case SCTP_PRSCTP_SUPPORTED: |
michael@0 | 1143 | stcb->asoc.peer_supports_prsctp = 0; |
michael@0 | 1144 | break; |
michael@0 | 1145 | case SCTP_SUPPORTED_CHUNK_EXT: |
michael@0 | 1146 | break; |
michael@0 | 1147 | /* draft-ietf-tsvwg-addip-sctp */ |
michael@0 | 1148 | case SCTP_HAS_NAT_SUPPORT: |
michael@0 | 1149 | stcb->asoc.peer_supports_nat = 0; |
michael@0 | 1150 | break; |
michael@0 | 1151 | case SCTP_ADD_IP_ADDRESS: |
michael@0 | 1152 | case SCTP_DEL_IP_ADDRESS: |
michael@0 | 1153 | case SCTP_SET_PRIM_ADDR: |
michael@0 | 1154 | stcb->asoc.peer_supports_asconf = 0; |
michael@0 | 1155 | break; |
michael@0 | 1156 | case SCTP_SUCCESS_REPORT: |
michael@0 | 1157 | case SCTP_ERROR_CAUSE_IND: |
michael@0 | 1158 | SCTPDBG(SCTP_DEBUG_INPUT2, "Huh, the peer does not support success? or error cause?\n"); |
michael@0 | 1159 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 1160 | "Turning off ASCONF to this strange peer\n"); |
michael@0 | 1161 | stcb->asoc.peer_supports_asconf = 0; |
michael@0 | 1162 | break; |
michael@0 | 1163 | default: |
michael@0 | 1164 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 1165 | "Peer does not support param type %d(%x)??\n", |
michael@0 | 1166 | pbad->param_type, (uint32_t) pbad->param_type); |
michael@0 | 1167 | break; |
michael@0 | 1168 | } |
michael@0 | 1169 | } |
michael@0 | 1170 | |
michael@0 | 1171 | static int |
michael@0 | 1172 | sctp_handle_error(struct sctp_chunkhdr *ch, |
michael@0 | 1173 | struct sctp_tcb *stcb, struct sctp_nets *net) |
michael@0 | 1174 | { |
michael@0 | 1175 | int chklen; |
michael@0 | 1176 | struct sctp_paramhdr *phdr; |
michael@0 | 1177 | uint16_t error, error_type; |
michael@0 | 1178 | uint16_t error_len; |
michael@0 | 1179 | struct sctp_association *asoc; |
michael@0 | 1180 | int adjust; |
michael@0 | 1181 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1182 | struct socket *so; |
michael@0 | 1183 | #endif |
michael@0 | 1184 | |
michael@0 | 1185 | /* parse through all of the errors and process */ |
michael@0 | 1186 | asoc = &stcb->asoc; |
michael@0 | 1187 | phdr = (struct sctp_paramhdr *)((caddr_t)ch + |
michael@0 | 1188 | sizeof(struct sctp_chunkhdr)); |
michael@0 | 1189 | chklen = ntohs(ch->chunk_length) - sizeof(struct sctp_chunkhdr); |
michael@0 | 1190 | error = 0; |
michael@0 | 1191 | while ((size_t)chklen >= sizeof(struct sctp_paramhdr)) { |
michael@0 | 1192 | /* Process an Error Cause */ |
michael@0 | 1193 | error_type = ntohs(phdr->param_type); |
michael@0 | 1194 | error_len = ntohs(phdr->param_length); |
michael@0 | 1195 | if ((error_len > chklen) || (error_len == 0)) { |
michael@0 | 1196 | /* invalid param length for this param */ |
michael@0 | 1197 | SCTPDBG(SCTP_DEBUG_INPUT1, "Bogus length in error param- chunk left:%d errorlen:%d\n", |
michael@0 | 1198 | chklen, error_len); |
michael@0 | 1199 | return (0); |
michael@0 | 1200 | } |
michael@0 | 1201 | if (error == 0) { |
michael@0 | 1202 | /* report the first error cause */ |
michael@0 | 1203 | error = error_type; |
michael@0 | 1204 | } |
michael@0 | 1205 | switch (error_type) { |
michael@0 | 1206 | case SCTP_CAUSE_INVALID_STREAM: |
michael@0 | 1207 | case SCTP_CAUSE_MISSING_PARAM: |
michael@0 | 1208 | case SCTP_CAUSE_INVALID_PARAM: |
michael@0 | 1209 | case SCTP_CAUSE_NO_USER_DATA: |
michael@0 | 1210 | SCTPDBG(SCTP_DEBUG_INPUT1, "Software error we got a %d back? We have a bug :/ (or do they?)\n", |
michael@0 | 1211 | error_type); |
michael@0 | 1212 | break; |
michael@0 | 1213 | case SCTP_CAUSE_NAT_COLLIDING_STATE: |
michael@0 | 1214 | SCTPDBG(SCTP_DEBUG_INPUT2, "Received Colliding state abort flags:%x\n", |
michael@0 | 1215 | ch->chunk_flags); |
michael@0 | 1216 | if (sctp_handle_nat_colliding_state(stcb)) { |
michael@0 | 1217 | return (0); |
michael@0 | 1218 | } |
michael@0 | 1219 | break; |
michael@0 | 1220 | case SCTP_CAUSE_NAT_MISSING_STATE: |
michael@0 | 1221 | SCTPDBG(SCTP_DEBUG_INPUT2, "Received missing state abort flags:%x\n", |
michael@0 | 1222 | ch->chunk_flags); |
michael@0 | 1223 | if (sctp_handle_nat_missing_state(stcb, net)) { |
michael@0 | 1224 | return (0); |
michael@0 | 1225 | } |
michael@0 | 1226 | break; |
michael@0 | 1227 | case SCTP_CAUSE_STALE_COOKIE: |
michael@0 | 1228 | /* |
michael@0 | 1229 | * We only act if we have echoed a cookie and are |
michael@0 | 1230 | * waiting. |
michael@0 | 1231 | */ |
michael@0 | 1232 | if (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED) { |
michael@0 | 1233 | int *p; |
michael@0 | 1234 | |
michael@0 | 1235 | p = (int *)((caddr_t)phdr + sizeof(*phdr)); |
michael@0 | 1236 | /* Save the time doubled */ |
michael@0 | 1237 | asoc->cookie_preserve_req = ntohl(*p) << 1; |
michael@0 | 1238 | asoc->stale_cookie_count++; |
michael@0 | 1239 | if (asoc->stale_cookie_count > |
michael@0 | 1240 | asoc->max_init_times) { |
michael@0 | 1241 | sctp_abort_notification(stcb, 0, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 1242 | /* now free the asoc */ |
michael@0 | 1243 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1244 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 1245 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1246 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1247 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 1248 | SCTP_TCB_LOCK(stcb); |
michael@0 | 1249 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1250 | #endif |
michael@0 | 1251 | (void)sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC, |
michael@0 | 1252 | SCTP_FROM_SCTP_INPUT+SCTP_LOC_11); |
michael@0 | 1253 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1254 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1255 | #endif |
michael@0 | 1256 | return (-1); |
michael@0 | 1257 | } |
michael@0 | 1258 | /* blast back to INIT state */ |
michael@0 | 1259 | sctp_toss_old_cookies(stcb, &stcb->asoc); |
michael@0 | 1260 | asoc->state &= ~SCTP_STATE_COOKIE_ECHOED; |
michael@0 | 1261 | asoc->state |= SCTP_STATE_COOKIE_WAIT; |
michael@0 | 1262 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 1263 | sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); |
michael@0 | 1264 | } |
michael@0 | 1265 | break; |
michael@0 | 1266 | case SCTP_CAUSE_UNRESOLVABLE_ADDR: |
michael@0 | 1267 | /* |
michael@0 | 1268 | * Nothing we can do here, we don't do hostname |
michael@0 | 1269 | * addresses so if the peer does not like my IPv6 |
michael@0 | 1270 | * (or IPv4 for that matter) it does not matter. If |
michael@0 | 1271 | * they don't support that type of address, they can |
michael@0 | 1272 | * NOT possibly get that packet type... i.e. with no |
michael@0 | 1273 | * IPv6 you can't recieve a IPv6 packet. so we can |
michael@0 | 1274 | * safely ignore this one. If we ever added support |
michael@0 | 1275 | * for HOSTNAME Addresses, then we would need to do |
michael@0 | 1276 | * something here. |
michael@0 | 1277 | */ |
michael@0 | 1278 | break; |
michael@0 | 1279 | case SCTP_CAUSE_UNRECOG_CHUNK: |
michael@0 | 1280 | sctp_process_unrecog_chunk(stcb, phdr, net); |
michael@0 | 1281 | break; |
michael@0 | 1282 | case SCTP_CAUSE_UNRECOG_PARAM: |
michael@0 | 1283 | sctp_process_unrecog_param(stcb, phdr); |
michael@0 | 1284 | break; |
michael@0 | 1285 | case SCTP_CAUSE_COOKIE_IN_SHUTDOWN: |
michael@0 | 1286 | /* |
michael@0 | 1287 | * We ignore this since the timer will drive out a |
michael@0 | 1288 | * new cookie anyway and there timer will drive us |
michael@0 | 1289 | * to send a SHUTDOWN_COMPLETE. We can't send one |
michael@0 | 1290 | * here since we don't have their tag. |
michael@0 | 1291 | */ |
michael@0 | 1292 | break; |
michael@0 | 1293 | case SCTP_CAUSE_DELETING_LAST_ADDR: |
michael@0 | 1294 | case SCTP_CAUSE_RESOURCE_SHORTAGE: |
michael@0 | 1295 | case SCTP_CAUSE_DELETING_SRC_ADDR: |
michael@0 | 1296 | /* |
michael@0 | 1297 | * We should NOT get these here, but in a |
michael@0 | 1298 | * ASCONF-ACK. |
michael@0 | 1299 | */ |
michael@0 | 1300 | SCTPDBG(SCTP_DEBUG_INPUT2, "Peer sends ASCONF errors in a Operational Error?<%d>?\n", |
michael@0 | 1301 | error_type); |
michael@0 | 1302 | break; |
michael@0 | 1303 | case SCTP_CAUSE_OUT_OF_RESC: |
michael@0 | 1304 | /* |
michael@0 | 1305 | * And what, pray tell do we do with the fact that |
michael@0 | 1306 | * the peer is out of resources? Not really sure we |
michael@0 | 1307 | * could do anything but abort. I suspect this |
michael@0 | 1308 | * should have came WITH an abort instead of in a |
michael@0 | 1309 | * OP-ERROR. |
michael@0 | 1310 | */ |
michael@0 | 1311 | break; |
michael@0 | 1312 | default: |
michael@0 | 1313 | SCTPDBG(SCTP_DEBUG_INPUT1, "sctp_handle_error: unknown error type = 0x%xh\n", |
michael@0 | 1314 | error_type); |
michael@0 | 1315 | break; |
michael@0 | 1316 | } |
michael@0 | 1317 | adjust = SCTP_SIZE32(error_len); |
michael@0 | 1318 | chklen -= adjust; |
michael@0 | 1319 | phdr = (struct sctp_paramhdr *)((caddr_t)phdr + adjust); |
michael@0 | 1320 | } |
michael@0 | 1321 | sctp_ulp_notify(SCTP_NOTIFY_REMOTE_ERROR, stcb, error, ch, SCTP_SO_NOT_LOCKED); |
michael@0 | 1322 | return (0); |
michael@0 | 1323 | } |
michael@0 | 1324 | |
michael@0 | 1325 | static int |
michael@0 | 1326 | sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, |
michael@0 | 1327 | struct sockaddr *src, struct sockaddr *dst, struct sctphdr *sh, |
michael@0 | 1328 | struct sctp_init_ack_chunk *cp, struct sctp_tcb *stcb, |
michael@0 | 1329 | struct sctp_nets *net, int *abort_no_unlock, |
michael@0 | 1330 | #if defined(__FreeBSD__) |
michael@0 | 1331 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 1332 | #endif |
michael@0 | 1333 | uint32_t vrf_id) |
michael@0 | 1334 | { |
michael@0 | 1335 | struct sctp_init_ack *init_ack; |
michael@0 | 1336 | struct mbuf *op_err; |
michael@0 | 1337 | |
michael@0 | 1338 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 1339 | "sctp_handle_init_ack: handling INIT-ACK\n"); |
michael@0 | 1340 | |
michael@0 | 1341 | if (stcb == NULL) { |
michael@0 | 1342 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 1343 | "sctp_handle_init_ack: TCB is null\n"); |
michael@0 | 1344 | return (-1); |
michael@0 | 1345 | } |
michael@0 | 1346 | if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_init_ack_chunk)) { |
michael@0 | 1347 | /* Invalid length */ |
michael@0 | 1348 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 1349 | sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, |
michael@0 | 1350 | src, dst, sh, op_err, |
michael@0 | 1351 | #if defined(__FreeBSD__) |
michael@0 | 1352 | use_mflowid, mflowid, |
michael@0 | 1353 | #endif |
michael@0 | 1354 | vrf_id, net->port); |
michael@0 | 1355 | *abort_no_unlock = 1; |
michael@0 | 1356 | return (-1); |
michael@0 | 1357 | } |
michael@0 | 1358 | init_ack = &cp->init; |
michael@0 | 1359 | /* validate parameters */ |
michael@0 | 1360 | if (init_ack->initiate_tag == 0) { |
michael@0 | 1361 | /* protocol error... send an abort */ |
michael@0 | 1362 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 1363 | sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, |
michael@0 | 1364 | src, dst, sh, op_err, |
michael@0 | 1365 | #if defined(__FreeBSD__) |
michael@0 | 1366 | use_mflowid, mflowid, |
michael@0 | 1367 | #endif |
michael@0 | 1368 | vrf_id, net->port); |
michael@0 | 1369 | *abort_no_unlock = 1; |
michael@0 | 1370 | return (-1); |
michael@0 | 1371 | } |
michael@0 | 1372 | if (ntohl(init_ack->a_rwnd) < SCTP_MIN_RWND) { |
michael@0 | 1373 | /* protocol error... send an abort */ |
michael@0 | 1374 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 1375 | sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, |
michael@0 | 1376 | src, dst, sh, op_err, |
michael@0 | 1377 | #if defined(__FreeBSD__) |
michael@0 | 1378 | use_mflowid, mflowid, |
michael@0 | 1379 | #endif |
michael@0 | 1380 | vrf_id, net->port); |
michael@0 | 1381 | *abort_no_unlock = 1; |
michael@0 | 1382 | return (-1); |
michael@0 | 1383 | } |
michael@0 | 1384 | if (init_ack->num_inbound_streams == 0) { |
michael@0 | 1385 | /* protocol error... send an abort */ |
michael@0 | 1386 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 1387 | sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, |
michael@0 | 1388 | src, dst, sh, op_err, |
michael@0 | 1389 | #if defined(__FreeBSD__) |
michael@0 | 1390 | use_mflowid, mflowid, |
michael@0 | 1391 | #endif |
michael@0 | 1392 | vrf_id, net->port); |
michael@0 | 1393 | *abort_no_unlock = 1; |
michael@0 | 1394 | return (-1); |
michael@0 | 1395 | } |
michael@0 | 1396 | if (init_ack->num_outbound_streams == 0) { |
michael@0 | 1397 | /* protocol error... send an abort */ |
michael@0 | 1398 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM); |
michael@0 | 1399 | sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, |
michael@0 | 1400 | src, dst, sh, op_err, |
michael@0 | 1401 | #if defined(__FreeBSD__) |
michael@0 | 1402 | use_mflowid, mflowid, |
michael@0 | 1403 | #endif |
michael@0 | 1404 | vrf_id, net->port); |
michael@0 | 1405 | *abort_no_unlock = 1; |
michael@0 | 1406 | return (-1); |
michael@0 | 1407 | } |
michael@0 | 1408 | /* process according to association state... */ |
michael@0 | 1409 | switch (stcb->asoc.state & SCTP_STATE_MASK) { |
michael@0 | 1410 | case SCTP_STATE_COOKIE_WAIT: |
michael@0 | 1411 | /* this is the expected state for this chunk */ |
michael@0 | 1412 | /* process the INIT-ACK parameters */ |
michael@0 | 1413 | if (stcb->asoc.primary_destination->dest_state & |
michael@0 | 1414 | SCTP_ADDR_UNCONFIRMED) { |
michael@0 | 1415 | /* |
michael@0 | 1416 | * The primary is where we sent the INIT, we can |
michael@0 | 1417 | * always consider it confirmed when the INIT-ACK is |
michael@0 | 1418 | * returned. Do this before we load addresses |
michael@0 | 1419 | * though. |
michael@0 | 1420 | */ |
michael@0 | 1421 | stcb->asoc.primary_destination->dest_state &= |
michael@0 | 1422 | ~SCTP_ADDR_UNCONFIRMED; |
michael@0 | 1423 | sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, |
michael@0 | 1424 | stcb, 0, (void *)stcb->asoc.primary_destination, SCTP_SO_NOT_LOCKED); |
michael@0 | 1425 | } |
michael@0 | 1426 | if (sctp_process_init_ack(m, iphlen, offset, src, dst, sh, cp, stcb, |
michael@0 | 1427 | net, abort_no_unlock, |
michael@0 | 1428 | #if defined(__FreeBSD__) |
michael@0 | 1429 | use_mflowid, mflowid, |
michael@0 | 1430 | #endif |
michael@0 | 1431 | vrf_id) < 0) { |
michael@0 | 1432 | /* error in parsing parameters */ |
michael@0 | 1433 | return (-1); |
michael@0 | 1434 | } |
michael@0 | 1435 | /* update our state */ |
michael@0 | 1436 | SCTPDBG(SCTP_DEBUG_INPUT2, "moving to COOKIE-ECHOED state\n"); |
michael@0 | 1437 | SCTP_SET_STATE(&stcb->asoc, SCTP_STATE_COOKIE_ECHOED); |
michael@0 | 1438 | |
michael@0 | 1439 | /* reset the RTO calc */ |
michael@0 | 1440 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 1441 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 1442 | stcb->asoc.overall_error_count, |
michael@0 | 1443 | 0, |
michael@0 | 1444 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 1445 | __LINE__); |
michael@0 | 1446 | } |
michael@0 | 1447 | stcb->asoc.overall_error_count = 0; |
michael@0 | 1448 | (void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered); |
michael@0 | 1449 | /* |
michael@0 | 1450 | * collapse the init timer back in case of a exponential |
michael@0 | 1451 | * backoff |
michael@0 | 1452 | */ |
michael@0 | 1453 | sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, stcb->sctp_ep, |
michael@0 | 1454 | stcb, net); |
michael@0 | 1455 | /* |
michael@0 | 1456 | * the send at the end of the inbound data processing will |
michael@0 | 1457 | * cause the cookie to be sent |
michael@0 | 1458 | */ |
michael@0 | 1459 | break; |
michael@0 | 1460 | case SCTP_STATE_SHUTDOWN_SENT: |
michael@0 | 1461 | /* incorrect state... discard */ |
michael@0 | 1462 | break; |
michael@0 | 1463 | case SCTP_STATE_COOKIE_ECHOED: |
michael@0 | 1464 | /* incorrect state... discard */ |
michael@0 | 1465 | break; |
michael@0 | 1466 | case SCTP_STATE_OPEN: |
michael@0 | 1467 | /* incorrect state... discard */ |
michael@0 | 1468 | break; |
michael@0 | 1469 | case SCTP_STATE_EMPTY: |
michael@0 | 1470 | case SCTP_STATE_INUSE: |
michael@0 | 1471 | default: |
michael@0 | 1472 | /* incorrect state... discard */ |
michael@0 | 1473 | return (-1); |
michael@0 | 1474 | break; |
michael@0 | 1475 | } |
michael@0 | 1476 | SCTPDBG(SCTP_DEBUG_INPUT1, "Leaving handle-init-ack end\n"); |
michael@0 | 1477 | return (0); |
michael@0 | 1478 | } |
michael@0 | 1479 | |
michael@0 | 1480 | static struct sctp_tcb * |
michael@0 | 1481 | sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, |
michael@0 | 1482 | struct sockaddr *src, struct sockaddr *dst, |
michael@0 | 1483 | struct sctphdr *sh, struct sctp_state_cookie *cookie, int cookie_len, |
michael@0 | 1484 | struct sctp_inpcb *inp, struct sctp_nets **netp, |
michael@0 | 1485 | struct sockaddr *init_src, int *notification, |
michael@0 | 1486 | int auth_skipped, uint32_t auth_offset, uint32_t auth_len, |
michael@0 | 1487 | #if defined(__FreeBSD__) |
michael@0 | 1488 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 1489 | #endif |
michael@0 | 1490 | uint32_t vrf_id, uint16_t port); |
michael@0 | 1491 | |
michael@0 | 1492 | |
michael@0 | 1493 | /* |
michael@0 | 1494 | * handle a state cookie for an existing association m: input packet mbuf |
michael@0 | 1495 | * chain-- assumes a pullup on IP/SCTP/COOKIE-ECHO chunk note: this is a |
michael@0 | 1496 | * "split" mbuf and the cookie signature does not exist offset: offset into |
michael@0 | 1497 | * mbuf to the cookie-echo chunk |
michael@0 | 1498 | */ |
michael@0 | 1499 | static struct sctp_tcb * |
michael@0 | 1500 | sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, |
michael@0 | 1501 | struct sockaddr *src, struct sockaddr *dst, |
michael@0 | 1502 | struct sctphdr *sh, struct sctp_state_cookie *cookie, int cookie_len, |
michael@0 | 1503 | struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets **netp, |
michael@0 | 1504 | struct sockaddr *init_src, int *notification, |
michael@0 | 1505 | int auth_skipped, uint32_t auth_offset, uint32_t auth_len, |
michael@0 | 1506 | #if defined(__FreeBSD__) |
michael@0 | 1507 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 1508 | #endif |
michael@0 | 1509 | uint32_t vrf_id, uint16_t port) |
michael@0 | 1510 | { |
michael@0 | 1511 | struct sctp_association *asoc; |
michael@0 | 1512 | struct sctp_init_chunk *init_cp, init_buf; |
michael@0 | 1513 | struct sctp_init_ack_chunk *initack_cp, initack_buf; |
michael@0 | 1514 | struct sctp_nets *net; |
michael@0 | 1515 | struct mbuf *op_err; |
michael@0 | 1516 | struct sctp_paramhdr *ph; |
michael@0 | 1517 | int init_offset, initack_offset, i; |
michael@0 | 1518 | int retval; |
michael@0 | 1519 | int spec_flag = 0; |
michael@0 | 1520 | uint32_t how_indx; |
michael@0 | 1521 | |
michael@0 | 1522 | net = *netp; |
michael@0 | 1523 | /* I know that the TCB is non-NULL from the caller */ |
michael@0 | 1524 | asoc = &stcb->asoc; |
michael@0 | 1525 | for (how_indx = 0; how_indx < sizeof(asoc->cookie_how); how_indx++) { |
michael@0 | 1526 | if (asoc->cookie_how[how_indx] == 0) |
michael@0 | 1527 | break; |
michael@0 | 1528 | } |
michael@0 | 1529 | if (how_indx < sizeof(asoc->cookie_how)) { |
michael@0 | 1530 | asoc->cookie_how[how_indx] = 1; |
michael@0 | 1531 | } |
michael@0 | 1532 | if (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) { |
michael@0 | 1533 | /* SHUTDOWN came in after sending INIT-ACK */ |
michael@0 | 1534 | sctp_send_shutdown_ack(stcb, stcb->asoc.primary_destination); |
michael@0 | 1535 | op_err = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), |
michael@0 | 1536 | 0, M_NOWAIT, 1, MT_DATA); |
michael@0 | 1537 | if (op_err == NULL) { |
michael@0 | 1538 | /* FOOBAR */ |
michael@0 | 1539 | return (NULL); |
michael@0 | 1540 | } |
michael@0 | 1541 | /* Set the len */ |
michael@0 | 1542 | SCTP_BUF_LEN(op_err) = sizeof(struct sctp_paramhdr); |
michael@0 | 1543 | ph = mtod(op_err, struct sctp_paramhdr *); |
michael@0 | 1544 | ph->param_type = htons(SCTP_CAUSE_COOKIE_IN_SHUTDOWN); |
michael@0 | 1545 | ph->param_length = htons(sizeof(struct sctp_paramhdr)); |
michael@0 | 1546 | sctp_send_operr_to(src, dst, sh, cookie->peers_vtag, op_err, |
michael@0 | 1547 | #if defined(__FreeBSD__) |
michael@0 | 1548 | use_mflowid, mflowid, |
michael@0 | 1549 | #endif |
michael@0 | 1550 | vrf_id, net->port); |
michael@0 | 1551 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1552 | asoc->cookie_how[how_indx] = 2; |
michael@0 | 1553 | return (NULL); |
michael@0 | 1554 | } |
michael@0 | 1555 | /* |
michael@0 | 1556 | * find and validate the INIT chunk in the cookie (peer's info) the |
michael@0 | 1557 | * INIT should start after the cookie-echo header struct (chunk |
michael@0 | 1558 | * header, state cookie header struct) |
michael@0 | 1559 | */ |
michael@0 | 1560 | init_offset = offset += sizeof(struct sctp_cookie_echo_chunk); |
michael@0 | 1561 | |
michael@0 | 1562 | init_cp = (struct sctp_init_chunk *) |
michael@0 | 1563 | sctp_m_getptr(m, init_offset, sizeof(struct sctp_init_chunk), |
michael@0 | 1564 | (uint8_t *) & init_buf); |
michael@0 | 1565 | if (init_cp == NULL) { |
michael@0 | 1566 | /* could not pull a INIT chunk in cookie */ |
michael@0 | 1567 | return (NULL); |
michael@0 | 1568 | } |
michael@0 | 1569 | if (init_cp->ch.chunk_type != SCTP_INITIATION) { |
michael@0 | 1570 | return (NULL); |
michael@0 | 1571 | } |
michael@0 | 1572 | /* |
michael@0 | 1573 | * find and validate the INIT-ACK chunk in the cookie (my info) the |
michael@0 | 1574 | * INIT-ACK follows the INIT chunk |
michael@0 | 1575 | */ |
michael@0 | 1576 | initack_offset = init_offset + SCTP_SIZE32(ntohs(init_cp->ch.chunk_length)); |
michael@0 | 1577 | initack_cp = (struct sctp_init_ack_chunk *) |
michael@0 | 1578 | sctp_m_getptr(m, initack_offset, sizeof(struct sctp_init_ack_chunk), |
michael@0 | 1579 | (uint8_t *) & initack_buf); |
michael@0 | 1580 | if (initack_cp == NULL) { |
michael@0 | 1581 | /* could not pull INIT-ACK chunk in cookie */ |
michael@0 | 1582 | return (NULL); |
michael@0 | 1583 | } |
michael@0 | 1584 | if (initack_cp->ch.chunk_type != SCTP_INITIATION_ACK) { |
michael@0 | 1585 | return (NULL); |
michael@0 | 1586 | } |
michael@0 | 1587 | if ((ntohl(initack_cp->init.initiate_tag) == asoc->my_vtag) && |
michael@0 | 1588 | (ntohl(init_cp->init.initiate_tag) == asoc->peer_vtag)) { |
michael@0 | 1589 | /* |
michael@0 | 1590 | * case D in Section 5.2.4 Table 2: MMAA process accordingly |
michael@0 | 1591 | * to get into the OPEN state |
michael@0 | 1592 | */ |
michael@0 | 1593 | if (ntohl(initack_cp->init.initial_tsn) != asoc->init_seq_number) { |
michael@0 | 1594 | /*- |
michael@0 | 1595 | * Opps, this means that we somehow generated two vtag's |
michael@0 | 1596 | * the same. I.e. we did: |
michael@0 | 1597 | * Us Peer |
michael@0 | 1598 | * <---INIT(tag=a)------ |
michael@0 | 1599 | * ----INIT-ACK(tag=t)--> |
michael@0 | 1600 | * ----INIT(tag=t)------> *1 |
michael@0 | 1601 | * <---INIT-ACK(tag=a)--- |
michael@0 | 1602 | * <----CE(tag=t)------------- *2 |
michael@0 | 1603 | * |
michael@0 | 1604 | * At point *1 we should be generating a different |
michael@0 | 1605 | * tag t'. Which means we would throw away the CE and send |
michael@0 | 1606 | * ours instead. Basically this is case C (throw away side). |
michael@0 | 1607 | */ |
michael@0 | 1608 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1609 | asoc->cookie_how[how_indx] = 17; |
michael@0 | 1610 | return (NULL); |
michael@0 | 1611 | |
michael@0 | 1612 | } |
michael@0 | 1613 | switch (SCTP_GET_STATE(asoc)) { |
michael@0 | 1614 | case SCTP_STATE_COOKIE_WAIT: |
michael@0 | 1615 | case SCTP_STATE_COOKIE_ECHOED: |
michael@0 | 1616 | /* |
michael@0 | 1617 | * INIT was sent but got a COOKIE_ECHO with the |
michael@0 | 1618 | * correct tags... just accept it...but we must |
michael@0 | 1619 | * process the init so that we can make sure we |
michael@0 | 1620 | * have the right seq no's. |
michael@0 | 1621 | */ |
michael@0 | 1622 | /* First we must process the INIT !! */ |
michael@0 | 1623 | retval = sctp_process_init(init_cp, stcb); |
michael@0 | 1624 | if (retval < 0) { |
michael@0 | 1625 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1626 | asoc->cookie_how[how_indx] = 3; |
michael@0 | 1627 | return (NULL); |
michael@0 | 1628 | } |
michael@0 | 1629 | /* we have already processed the INIT so no problem */ |
michael@0 | 1630 | sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, |
michael@0 | 1631 | net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_12); |
michael@0 | 1632 | sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_13); |
michael@0 | 1633 | /* update current state */ |
michael@0 | 1634 | if (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED) |
michael@0 | 1635 | SCTP_STAT_INCR_COUNTER32(sctps_activeestab); |
michael@0 | 1636 | else |
michael@0 | 1637 | SCTP_STAT_INCR_COUNTER32(sctps_collisionestab); |
michael@0 | 1638 | |
michael@0 | 1639 | SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); |
michael@0 | 1640 | if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { |
michael@0 | 1641 | sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, |
michael@0 | 1642 | stcb->sctp_ep, stcb, asoc->primary_destination); |
michael@0 | 1643 | } |
michael@0 | 1644 | SCTP_STAT_INCR_GAUGE32(sctps_currestab); |
michael@0 | 1645 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 1646 | if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || |
michael@0 | 1647 | (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) && |
michael@0 | 1648 | (inp->sctp_socket->so_qlimit == 0) |
michael@0 | 1649 | ) { |
michael@0 | 1650 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1651 | struct socket *so; |
michael@0 | 1652 | #endif |
michael@0 | 1653 | /* |
michael@0 | 1654 | * Here is where collision would go if we |
michael@0 | 1655 | * did a connect() and instead got a |
michael@0 | 1656 | * init/init-ack/cookie done before the |
michael@0 | 1657 | * init-ack came back.. |
michael@0 | 1658 | */ |
michael@0 | 1659 | stcb->sctp_ep->sctp_flags |= |
michael@0 | 1660 | SCTP_PCB_FLAGS_CONNECTED; |
michael@0 | 1661 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1662 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 1663 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1664 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1665 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 1666 | SCTP_TCB_LOCK(stcb); |
michael@0 | 1667 | atomic_add_int(&stcb->asoc.refcnt, -1); |
michael@0 | 1668 | if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { |
michael@0 | 1669 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1670 | return (NULL); |
michael@0 | 1671 | } |
michael@0 | 1672 | #endif |
michael@0 | 1673 | soisconnected(stcb->sctp_socket); |
michael@0 | 1674 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1675 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1676 | #endif |
michael@0 | 1677 | } |
michael@0 | 1678 | /* notify upper layer */ |
michael@0 | 1679 | *notification = SCTP_NOTIFY_ASSOC_UP; |
michael@0 | 1680 | /* |
michael@0 | 1681 | * since we did not send a HB make sure we |
michael@0 | 1682 | * don't double things |
michael@0 | 1683 | */ |
michael@0 | 1684 | net->hb_responded = 1; |
michael@0 | 1685 | net->RTO = sctp_calculate_rto(stcb, asoc, net, |
michael@0 | 1686 | &cookie->time_entered, |
michael@0 | 1687 | sctp_align_unsafe_makecopy, |
michael@0 | 1688 | SCTP_RTT_FROM_NON_DATA); |
michael@0 | 1689 | |
michael@0 | 1690 | if (stcb->asoc.sctp_autoclose_ticks && |
michael@0 | 1691 | (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE))) { |
michael@0 | 1692 | sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, |
michael@0 | 1693 | inp, stcb, NULL); |
michael@0 | 1694 | } |
michael@0 | 1695 | break; |
michael@0 | 1696 | default: |
michael@0 | 1697 | /* |
michael@0 | 1698 | * we're in the OPEN state (or beyond), so |
michael@0 | 1699 | * peer must have simply lost the COOKIE-ACK |
michael@0 | 1700 | */ |
michael@0 | 1701 | break; |
michael@0 | 1702 | } /* end switch */ |
michael@0 | 1703 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 1704 | /* |
michael@0 | 1705 | * We ignore the return code here.. not sure if we should |
michael@0 | 1706 | * somehow abort.. but we do have an existing asoc. This |
michael@0 | 1707 | * really should not fail. |
michael@0 | 1708 | */ |
michael@0 | 1709 | if (sctp_load_addresses_from_init(stcb, m, |
michael@0 | 1710 | init_offset + sizeof(struct sctp_init_chunk), |
michael@0 | 1711 | initack_offset, src, dst, init_src)) { |
michael@0 | 1712 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1713 | asoc->cookie_how[how_indx] = 4; |
michael@0 | 1714 | return (NULL); |
michael@0 | 1715 | } |
michael@0 | 1716 | /* respond with a COOKIE-ACK */ |
michael@0 | 1717 | sctp_toss_old_cookies(stcb, asoc); |
michael@0 | 1718 | sctp_send_cookie_ack(stcb); |
michael@0 | 1719 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1720 | asoc->cookie_how[how_indx] = 5; |
michael@0 | 1721 | return (stcb); |
michael@0 | 1722 | } |
michael@0 | 1723 | |
michael@0 | 1724 | if (ntohl(initack_cp->init.initiate_tag) != asoc->my_vtag && |
michael@0 | 1725 | ntohl(init_cp->init.initiate_tag) == asoc->peer_vtag && |
michael@0 | 1726 | cookie->tie_tag_my_vtag == 0 && |
michael@0 | 1727 | cookie->tie_tag_peer_vtag == 0) { |
michael@0 | 1728 | /* |
michael@0 | 1729 | * case C in Section 5.2.4 Table 2: XMOO silently discard |
michael@0 | 1730 | */ |
michael@0 | 1731 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1732 | asoc->cookie_how[how_indx] = 6; |
michael@0 | 1733 | return (NULL); |
michael@0 | 1734 | } |
michael@0 | 1735 | /* If nat support, and the below and stcb is established, |
michael@0 | 1736 | * send back a ABORT(colliding state) if we are established. |
michael@0 | 1737 | */ |
michael@0 | 1738 | if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) && |
michael@0 | 1739 | (asoc->peer_supports_nat) && |
michael@0 | 1740 | ((ntohl(initack_cp->init.initiate_tag) == asoc->my_vtag) && |
michael@0 | 1741 | ((ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) || |
michael@0 | 1742 | (asoc->peer_vtag == 0)))) { |
michael@0 | 1743 | /* Special case - Peer's support nat. We may have |
michael@0 | 1744 | * two init's that we gave out the same tag on since |
michael@0 | 1745 | * one was not established.. i.e. we get INIT from host-1 |
michael@0 | 1746 | * behind the nat and we respond tag-a, we get a INIT from |
michael@0 | 1747 | * host-2 behind the nat and we get tag-a again. Then we |
michael@0 | 1748 | * bring up host-1 (or 2's) assoc, Then comes the cookie |
michael@0 | 1749 | * from hsot-2 (or 1). Now we have colliding state. We must |
michael@0 | 1750 | * send an abort here with colliding state indication. |
michael@0 | 1751 | */ |
michael@0 | 1752 | op_err = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), |
michael@0 | 1753 | 0, M_NOWAIT, 1, MT_DATA); |
michael@0 | 1754 | if (op_err == NULL) { |
michael@0 | 1755 | /* FOOBAR */ |
michael@0 | 1756 | return (NULL); |
michael@0 | 1757 | } |
michael@0 | 1758 | /* pre-reserve some space */ |
michael@0 | 1759 | #ifdef INET6 |
michael@0 | 1760 | SCTP_BUF_RESV_UF(op_err, sizeof(struct ip6_hdr)); |
michael@0 | 1761 | #else |
michael@0 | 1762 | SCTP_BUF_RESV_UF(op_err, sizeof(struct ip)); |
michael@0 | 1763 | #endif |
michael@0 | 1764 | SCTP_BUF_RESV_UF(op_err, sizeof(struct sctphdr)); |
michael@0 | 1765 | SCTP_BUF_RESV_UF(op_err, sizeof(struct sctp_chunkhdr)); |
michael@0 | 1766 | /* Set the len */ |
michael@0 | 1767 | SCTP_BUF_LEN(op_err) = sizeof(struct sctp_paramhdr); |
michael@0 | 1768 | ph = mtod(op_err, struct sctp_paramhdr *); |
michael@0 | 1769 | ph->param_type = htons(SCTP_CAUSE_NAT_COLLIDING_STATE); |
michael@0 | 1770 | ph->param_length = htons(sizeof(struct sctp_paramhdr)); |
michael@0 | 1771 | sctp_send_abort(m, iphlen, src, dst, sh, 0, op_err, |
michael@0 | 1772 | #if defined(__FreeBSD__) |
michael@0 | 1773 | use_mflowid, mflowid, |
michael@0 | 1774 | #endif |
michael@0 | 1775 | vrf_id, port); |
michael@0 | 1776 | return (NULL); |
michael@0 | 1777 | } |
michael@0 | 1778 | if ((ntohl(initack_cp->init.initiate_tag) == asoc->my_vtag) && |
michael@0 | 1779 | ((ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) || |
michael@0 | 1780 | (asoc->peer_vtag == 0))) { |
michael@0 | 1781 | /* |
michael@0 | 1782 | * case B in Section 5.2.4 Table 2: MXAA or MOAA my info |
michael@0 | 1783 | * should be ok, re-accept peer info |
michael@0 | 1784 | */ |
michael@0 | 1785 | if (ntohl(initack_cp->init.initial_tsn) != asoc->init_seq_number) { |
michael@0 | 1786 | /* Extension of case C. |
michael@0 | 1787 | * If we hit this, then the random number |
michael@0 | 1788 | * generator returned the same vtag when we |
michael@0 | 1789 | * first sent our INIT-ACK and when we later sent |
michael@0 | 1790 | * our INIT. The side with the seq numbers that are |
michael@0 | 1791 | * different will be the one that normnally would |
michael@0 | 1792 | * have hit case C. This in effect "extends" our vtags |
michael@0 | 1793 | * in this collision case to be 64 bits. The same collision |
michael@0 | 1794 | * could occur aka you get both vtag and seq number the |
michael@0 | 1795 | * same twice in a row.. but is much less likely. If it |
michael@0 | 1796 | * did happen then we would proceed through and bring |
michael@0 | 1797 | * up the assoc.. we may end up with the wrong stream |
michael@0 | 1798 | * setup however.. which would be bad.. but there is |
michael@0 | 1799 | * no way to tell.. until we send on a stream that does |
michael@0 | 1800 | * not exist :-) |
michael@0 | 1801 | */ |
michael@0 | 1802 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1803 | asoc->cookie_how[how_indx] = 7; |
michael@0 | 1804 | |
michael@0 | 1805 | return (NULL); |
michael@0 | 1806 | } |
michael@0 | 1807 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1808 | asoc->cookie_how[how_indx] = 8; |
michael@0 | 1809 | sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_14); |
michael@0 | 1810 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 1811 | /* |
michael@0 | 1812 | * since we did not send a HB make sure we don't double |
michael@0 | 1813 | * things |
michael@0 | 1814 | */ |
michael@0 | 1815 | net->hb_responded = 1; |
michael@0 | 1816 | if (stcb->asoc.sctp_autoclose_ticks && |
michael@0 | 1817 | sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) { |
michael@0 | 1818 | sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, |
michael@0 | 1819 | NULL); |
michael@0 | 1820 | } |
michael@0 | 1821 | asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd); |
michael@0 | 1822 | asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams); |
michael@0 | 1823 | |
michael@0 | 1824 | if (ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) { |
michael@0 | 1825 | /* Ok the peer probably discarded our |
michael@0 | 1826 | * data (if we echoed a cookie+data). So anything |
michael@0 | 1827 | * on the sent_queue should be marked for |
michael@0 | 1828 | * retransmit, we may not get something to |
michael@0 | 1829 | * kick us so it COULD still take a timeout |
michael@0 | 1830 | * to move these.. but it can't hurt to mark them. |
michael@0 | 1831 | */ |
michael@0 | 1832 | struct sctp_tmit_chunk *chk; |
michael@0 | 1833 | TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) { |
michael@0 | 1834 | if (chk->sent < SCTP_DATAGRAM_RESEND) { |
michael@0 | 1835 | chk->sent = SCTP_DATAGRAM_RESEND; |
michael@0 | 1836 | sctp_flight_size_decrease(chk); |
michael@0 | 1837 | sctp_total_flight_decrease(stcb, chk); |
michael@0 | 1838 | sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); |
michael@0 | 1839 | spec_flag++; |
michael@0 | 1840 | } |
michael@0 | 1841 | } |
michael@0 | 1842 | |
michael@0 | 1843 | } |
michael@0 | 1844 | /* process the INIT info (peer's info) */ |
michael@0 | 1845 | retval = sctp_process_init(init_cp, stcb); |
michael@0 | 1846 | if (retval < 0) { |
michael@0 | 1847 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1848 | asoc->cookie_how[how_indx] = 9; |
michael@0 | 1849 | return (NULL); |
michael@0 | 1850 | } |
michael@0 | 1851 | if (sctp_load_addresses_from_init(stcb, m, |
michael@0 | 1852 | init_offset + sizeof(struct sctp_init_chunk), |
michael@0 | 1853 | initack_offset, src, dst, init_src)) { |
michael@0 | 1854 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1855 | asoc->cookie_how[how_indx] = 10; |
michael@0 | 1856 | return (NULL); |
michael@0 | 1857 | } |
michael@0 | 1858 | if ((asoc->state & SCTP_STATE_COOKIE_WAIT) || |
michael@0 | 1859 | (asoc->state & SCTP_STATE_COOKIE_ECHOED)) { |
michael@0 | 1860 | *notification = SCTP_NOTIFY_ASSOC_UP; |
michael@0 | 1861 | |
michael@0 | 1862 | if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || |
michael@0 | 1863 | (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) && |
michael@0 | 1864 | (inp->sctp_socket->so_qlimit == 0)) { |
michael@0 | 1865 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1866 | struct socket *so; |
michael@0 | 1867 | #endif |
michael@0 | 1868 | stcb->sctp_ep->sctp_flags |= |
michael@0 | 1869 | SCTP_PCB_FLAGS_CONNECTED; |
michael@0 | 1870 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1871 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 1872 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1873 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1874 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 1875 | SCTP_TCB_LOCK(stcb); |
michael@0 | 1876 | atomic_add_int(&stcb->asoc.refcnt, -1); |
michael@0 | 1877 | if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { |
michael@0 | 1878 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1879 | return (NULL); |
michael@0 | 1880 | } |
michael@0 | 1881 | #endif |
michael@0 | 1882 | soisconnected(stcb->sctp_socket); |
michael@0 | 1883 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1884 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 1885 | #endif |
michael@0 | 1886 | } |
michael@0 | 1887 | if (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED) |
michael@0 | 1888 | SCTP_STAT_INCR_COUNTER32(sctps_activeestab); |
michael@0 | 1889 | else |
michael@0 | 1890 | SCTP_STAT_INCR_COUNTER32(sctps_collisionestab); |
michael@0 | 1891 | SCTP_STAT_INCR_GAUGE32(sctps_currestab); |
michael@0 | 1892 | } else if (SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) { |
michael@0 | 1893 | SCTP_STAT_INCR_COUNTER32(sctps_restartestab); |
michael@0 | 1894 | } else { |
michael@0 | 1895 | SCTP_STAT_INCR_COUNTER32(sctps_collisionestab); |
michael@0 | 1896 | } |
michael@0 | 1897 | SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); |
michael@0 | 1898 | if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { |
michael@0 | 1899 | sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, |
michael@0 | 1900 | stcb->sctp_ep, stcb, asoc->primary_destination); |
michael@0 | 1901 | } |
michael@0 | 1902 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 1903 | sctp_toss_old_cookies(stcb, asoc); |
michael@0 | 1904 | sctp_send_cookie_ack(stcb); |
michael@0 | 1905 | if (spec_flag) { |
michael@0 | 1906 | /* only if we have retrans set do we do this. What |
michael@0 | 1907 | * this call does is get only the COOKIE-ACK out |
michael@0 | 1908 | * and then when we return the normal call to |
michael@0 | 1909 | * sctp_chunk_output will get the retrans out |
michael@0 | 1910 | * behind this. |
michael@0 | 1911 | */ |
michael@0 | 1912 | sctp_chunk_output(inp,stcb, SCTP_OUTPUT_FROM_COOKIE_ACK, SCTP_SO_NOT_LOCKED); |
michael@0 | 1913 | } |
michael@0 | 1914 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1915 | asoc->cookie_how[how_indx] = 11; |
michael@0 | 1916 | |
michael@0 | 1917 | return (stcb); |
michael@0 | 1918 | } |
michael@0 | 1919 | if ((ntohl(initack_cp->init.initiate_tag) != asoc->my_vtag && |
michael@0 | 1920 | ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) && |
michael@0 | 1921 | cookie->tie_tag_my_vtag == asoc->my_vtag_nonce && |
michael@0 | 1922 | cookie->tie_tag_peer_vtag == asoc->peer_vtag_nonce && |
michael@0 | 1923 | cookie->tie_tag_peer_vtag != 0) { |
michael@0 | 1924 | struct sctpasochead *head; |
michael@0 | 1925 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1926 | struct socket *so; |
michael@0 | 1927 | #endif |
michael@0 | 1928 | |
michael@0 | 1929 | if (asoc->peer_supports_nat) { |
michael@0 | 1930 | /* This is a gross gross hack. |
michael@0 | 1931 | * Just call the cookie_new code since we |
michael@0 | 1932 | * are allowing a duplicate association. |
michael@0 | 1933 | * I hope this works... |
michael@0 | 1934 | */ |
michael@0 | 1935 | return (sctp_process_cookie_new(m, iphlen, offset, src, dst, |
michael@0 | 1936 | sh, cookie, cookie_len, |
michael@0 | 1937 | inp, netp, init_src,notification, |
michael@0 | 1938 | auth_skipped, auth_offset, auth_len, |
michael@0 | 1939 | #if defined(__FreeBSD__) |
michael@0 | 1940 | use_mflowid, mflowid, |
michael@0 | 1941 | #endif |
michael@0 | 1942 | vrf_id, port)); |
michael@0 | 1943 | } |
michael@0 | 1944 | /* |
michael@0 | 1945 | * case A in Section 5.2.4 Table 2: XXMM (peer restarted) |
michael@0 | 1946 | */ |
michael@0 | 1947 | /* temp code */ |
michael@0 | 1948 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 1949 | asoc->cookie_how[how_indx] = 12; |
michael@0 | 1950 | sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_15); |
michael@0 | 1951 | sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_16); |
michael@0 | 1952 | |
michael@0 | 1953 | /* notify upper layer */ |
michael@0 | 1954 | *notification = SCTP_NOTIFY_ASSOC_RESTART; |
michael@0 | 1955 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 1956 | if ((SCTP_GET_STATE(asoc) != SCTP_STATE_OPEN) && |
michael@0 | 1957 | (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_RECEIVED) && |
michael@0 | 1958 | (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT)) { |
michael@0 | 1959 | SCTP_STAT_INCR_GAUGE32(sctps_currestab); |
michael@0 | 1960 | } |
michael@0 | 1961 | if (SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) { |
michael@0 | 1962 | SCTP_STAT_INCR_GAUGE32(sctps_restartestab); |
michael@0 | 1963 | } else if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) { |
michael@0 | 1964 | SCTP_STAT_INCR_GAUGE32(sctps_collisionestab); |
michael@0 | 1965 | } |
michael@0 | 1966 | if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { |
michael@0 | 1967 | SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); |
michael@0 | 1968 | sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, |
michael@0 | 1969 | stcb->sctp_ep, stcb, asoc->primary_destination); |
michael@0 | 1970 | |
michael@0 | 1971 | } else if (!(asoc->state & SCTP_STATE_SHUTDOWN_SENT)) { |
michael@0 | 1972 | /* move to OPEN state, if not in SHUTDOWN_SENT */ |
michael@0 | 1973 | SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); |
michael@0 | 1974 | } |
michael@0 | 1975 | asoc->pre_open_streams = |
michael@0 | 1976 | ntohs(initack_cp->init.num_outbound_streams); |
michael@0 | 1977 | asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn); |
michael@0 | 1978 | asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number; |
michael@0 | 1979 | asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1; |
michael@0 | 1980 | |
michael@0 | 1981 | asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1; |
michael@0 | 1982 | |
michael@0 | 1983 | asoc->str_reset_seq_in = asoc->init_seq_number; |
michael@0 | 1984 | |
michael@0 | 1985 | asoc->advanced_peer_ack_point = asoc->last_acked_seq; |
michael@0 | 1986 | if (asoc->mapping_array) { |
michael@0 | 1987 | memset(asoc->mapping_array, 0, |
michael@0 | 1988 | asoc->mapping_array_size); |
michael@0 | 1989 | } |
michael@0 | 1990 | if (asoc->nr_mapping_array) { |
michael@0 | 1991 | memset(asoc->nr_mapping_array, 0, |
michael@0 | 1992 | asoc->mapping_array_size); |
michael@0 | 1993 | } |
michael@0 | 1994 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 1995 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 1996 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 1997 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 1998 | #endif |
michael@0 | 1999 | SCTP_INP_INFO_WLOCK(); |
michael@0 | 2000 | SCTP_INP_WLOCK(stcb->sctp_ep); |
michael@0 | 2001 | SCTP_TCB_LOCK(stcb); |
michael@0 | 2002 | atomic_add_int(&stcb->asoc.refcnt, -1); |
michael@0 | 2003 | /* send up all the data */ |
michael@0 | 2004 | SCTP_TCB_SEND_LOCK(stcb); |
michael@0 | 2005 | |
michael@0 | 2006 | sctp_report_all_outbound(stcb, 0, 1, SCTP_SO_LOCKED); |
michael@0 | 2007 | for (i = 0; i < stcb->asoc.streamoutcnt; i++) { |
michael@0 | 2008 | stcb->asoc.strmout[i].chunks_on_queues = 0; |
michael@0 | 2009 | stcb->asoc.strmout[i].stream_no = i; |
michael@0 | 2010 | stcb->asoc.strmout[i].next_sequence_send = 0; |
michael@0 | 2011 | stcb->asoc.strmout[i].last_msg_incomplete = 0; |
michael@0 | 2012 | } |
michael@0 | 2013 | /* process the INIT-ACK info (my info) */ |
michael@0 | 2014 | asoc->my_vtag = ntohl(initack_cp->init.initiate_tag); |
michael@0 | 2015 | asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd); |
michael@0 | 2016 | |
michael@0 | 2017 | /* pull from vtag hash */ |
michael@0 | 2018 | LIST_REMOVE(stcb, sctp_asocs); |
michael@0 | 2019 | /* re-insert to new vtag position */ |
michael@0 | 2020 | head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, |
michael@0 | 2021 | SCTP_BASE_INFO(hashasocmark))]; |
michael@0 | 2022 | /* |
michael@0 | 2023 | * put it in the bucket in the vtag hash of assoc's for the |
michael@0 | 2024 | * system |
michael@0 | 2025 | */ |
michael@0 | 2026 | LIST_INSERT_HEAD(head, stcb, sctp_asocs); |
michael@0 | 2027 | |
michael@0 | 2028 | SCTP_TCB_SEND_UNLOCK(stcb); |
michael@0 | 2029 | SCTP_INP_WUNLOCK(stcb->sctp_ep); |
michael@0 | 2030 | SCTP_INP_INFO_WUNLOCK(); |
michael@0 | 2031 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2032 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2033 | #endif |
michael@0 | 2034 | asoc->total_flight = 0; |
michael@0 | 2035 | asoc->total_flight_count = 0; |
michael@0 | 2036 | /* process the INIT info (peer's info) */ |
michael@0 | 2037 | retval = sctp_process_init(init_cp, stcb); |
michael@0 | 2038 | if (retval < 0) { |
michael@0 | 2039 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 2040 | asoc->cookie_how[how_indx] = 13; |
michael@0 | 2041 | |
michael@0 | 2042 | return (NULL); |
michael@0 | 2043 | } |
michael@0 | 2044 | /* |
michael@0 | 2045 | * since we did not send a HB make sure we don't double |
michael@0 | 2046 | * things |
michael@0 | 2047 | */ |
michael@0 | 2048 | net->hb_responded = 1; |
michael@0 | 2049 | |
michael@0 | 2050 | if (sctp_load_addresses_from_init(stcb, m, |
michael@0 | 2051 | init_offset + sizeof(struct sctp_init_chunk), |
michael@0 | 2052 | initack_offset, src, dst, init_src)) { |
michael@0 | 2053 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 2054 | asoc->cookie_how[how_indx] = 14; |
michael@0 | 2055 | |
michael@0 | 2056 | return (NULL); |
michael@0 | 2057 | } |
michael@0 | 2058 | /* respond with a COOKIE-ACK */ |
michael@0 | 2059 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 2060 | sctp_toss_old_cookies(stcb, asoc); |
michael@0 | 2061 | sctp_send_cookie_ack(stcb); |
michael@0 | 2062 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 2063 | asoc->cookie_how[how_indx] = 15; |
michael@0 | 2064 | |
michael@0 | 2065 | return (stcb); |
michael@0 | 2066 | } |
michael@0 | 2067 | if (how_indx < sizeof(asoc->cookie_how)) |
michael@0 | 2068 | asoc->cookie_how[how_indx] = 16; |
michael@0 | 2069 | /* all other cases... */ |
michael@0 | 2070 | return (NULL); |
michael@0 | 2071 | } |
michael@0 | 2072 | |
michael@0 | 2073 | |
michael@0 | 2074 | /* |
michael@0 | 2075 | * handle a state cookie for a new association m: input packet mbuf chain-- |
michael@0 | 2076 | * assumes a pullup on IP/SCTP/COOKIE-ECHO chunk note: this is a "split" mbuf |
michael@0 | 2077 | * and the cookie signature does not exist offset: offset into mbuf to the |
michael@0 | 2078 | * cookie-echo chunk length: length of the cookie chunk to: where the init |
michael@0 | 2079 | * was from returns a new TCB |
michael@0 | 2080 | */ |
michael@0 | 2081 | static struct sctp_tcb * |
michael@0 | 2082 | sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, |
michael@0 | 2083 | struct sockaddr *src, struct sockaddr *dst, |
michael@0 | 2084 | struct sctphdr *sh, struct sctp_state_cookie *cookie, int cookie_len, |
michael@0 | 2085 | struct sctp_inpcb *inp, struct sctp_nets **netp, |
michael@0 | 2086 | struct sockaddr *init_src, int *notification, |
michael@0 | 2087 | int auth_skipped, uint32_t auth_offset, uint32_t auth_len, |
michael@0 | 2088 | #if defined(__FreeBSD__) |
michael@0 | 2089 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 2090 | #endif |
michael@0 | 2091 | uint32_t vrf_id, uint16_t port) |
michael@0 | 2092 | { |
michael@0 | 2093 | struct sctp_tcb *stcb; |
michael@0 | 2094 | struct sctp_init_chunk *init_cp, init_buf; |
michael@0 | 2095 | struct sctp_init_ack_chunk *initack_cp, initack_buf; |
michael@0 | 2096 | struct sockaddr_storage sa_store; |
michael@0 | 2097 | struct sockaddr *initack_src = (struct sockaddr *)&sa_store; |
michael@0 | 2098 | struct sctp_association *asoc; |
michael@0 | 2099 | int init_offset, initack_offset, initack_limit; |
michael@0 | 2100 | int retval; |
michael@0 | 2101 | int error = 0; |
michael@0 | 2102 | uint8_t auth_chunk_buf[SCTP_PARAM_BUFFER_SIZE]; |
michael@0 | 2103 | #ifdef INET |
michael@0 | 2104 | struct sockaddr_in *sin; |
michael@0 | 2105 | #endif |
michael@0 | 2106 | #ifdef INET6 |
michael@0 | 2107 | struct sockaddr_in6 *sin6; |
michael@0 | 2108 | #endif |
michael@0 | 2109 | #if defined(__Userspace__) |
michael@0 | 2110 | struct sockaddr_conn *sconn; |
michael@0 | 2111 | #endif |
michael@0 | 2112 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2113 | struct socket *so; |
michael@0 | 2114 | |
michael@0 | 2115 | so = SCTP_INP_SO(inp); |
michael@0 | 2116 | #endif |
michael@0 | 2117 | |
michael@0 | 2118 | /* |
michael@0 | 2119 | * find and validate the INIT chunk in the cookie (peer's info) the |
michael@0 | 2120 | * INIT should start after the cookie-echo header struct (chunk |
michael@0 | 2121 | * header, state cookie header struct) |
michael@0 | 2122 | */ |
michael@0 | 2123 | init_offset = offset + sizeof(struct sctp_cookie_echo_chunk); |
michael@0 | 2124 | init_cp = (struct sctp_init_chunk *) |
michael@0 | 2125 | sctp_m_getptr(m, init_offset, sizeof(struct sctp_init_chunk), |
michael@0 | 2126 | (uint8_t *) & init_buf); |
michael@0 | 2127 | if (init_cp == NULL) { |
michael@0 | 2128 | /* could not pull a INIT chunk in cookie */ |
michael@0 | 2129 | SCTPDBG(SCTP_DEBUG_INPUT1, |
michael@0 | 2130 | "process_cookie_new: could not pull INIT chunk hdr\n"); |
michael@0 | 2131 | return (NULL); |
michael@0 | 2132 | } |
michael@0 | 2133 | if (init_cp->ch.chunk_type != SCTP_INITIATION) { |
michael@0 | 2134 | SCTPDBG(SCTP_DEBUG_INPUT1, "HUH? process_cookie_new: could not find INIT chunk!\n"); |
michael@0 | 2135 | return (NULL); |
michael@0 | 2136 | } |
michael@0 | 2137 | initack_offset = init_offset + SCTP_SIZE32(ntohs(init_cp->ch.chunk_length)); |
michael@0 | 2138 | /* |
michael@0 | 2139 | * find and validate the INIT-ACK chunk in the cookie (my info) the |
michael@0 | 2140 | * INIT-ACK follows the INIT chunk |
michael@0 | 2141 | */ |
michael@0 | 2142 | initack_cp = (struct sctp_init_ack_chunk *) |
michael@0 | 2143 | sctp_m_getptr(m, initack_offset, sizeof(struct sctp_init_ack_chunk), |
michael@0 | 2144 | (uint8_t *) & initack_buf); |
michael@0 | 2145 | if (initack_cp == NULL) { |
michael@0 | 2146 | /* could not pull INIT-ACK chunk in cookie */ |
michael@0 | 2147 | SCTPDBG(SCTP_DEBUG_INPUT1, "process_cookie_new: could not pull INIT-ACK chunk hdr\n"); |
michael@0 | 2148 | return (NULL); |
michael@0 | 2149 | } |
michael@0 | 2150 | if (initack_cp->ch.chunk_type != SCTP_INITIATION_ACK) { |
michael@0 | 2151 | return (NULL); |
michael@0 | 2152 | } |
michael@0 | 2153 | /* |
michael@0 | 2154 | * NOTE: We can't use the INIT_ACK's chk_length to determine the |
michael@0 | 2155 | * "initack_limit" value. This is because the chk_length field |
michael@0 | 2156 | * includes the length of the cookie, but the cookie is omitted when |
michael@0 | 2157 | * the INIT and INIT_ACK are tacked onto the cookie... |
michael@0 | 2158 | */ |
michael@0 | 2159 | initack_limit = offset + cookie_len; |
michael@0 | 2160 | |
michael@0 | 2161 | /* |
michael@0 | 2162 | * now that we know the INIT/INIT-ACK are in place, create a new TCB |
michael@0 | 2163 | * and popluate |
michael@0 | 2164 | */ |
michael@0 | 2165 | |
michael@0 | 2166 | /* |
michael@0 | 2167 | * Here we do a trick, we set in NULL for the proc/thread argument. We |
michael@0 | 2168 | * do this since in effect we only use the p argument when |
michael@0 | 2169 | * the socket is unbound and we must do an implicit bind. |
michael@0 | 2170 | * Since we are getting a cookie, we cannot be unbound. |
michael@0 | 2171 | */ |
michael@0 | 2172 | stcb = sctp_aloc_assoc(inp, init_src, &error, |
michael@0 | 2173 | ntohl(initack_cp->init.initiate_tag), vrf_id, |
michael@0 | 2174 | #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 |
michael@0 | 2175 | (struct thread *)NULL |
michael@0 | 2176 | #elif defined(__Windows__) |
michael@0 | 2177 | (PKTHREAD)NULL |
michael@0 | 2178 | #else |
michael@0 | 2179 | (struct proc *)NULL |
michael@0 | 2180 | #endif |
michael@0 | 2181 | ); |
michael@0 | 2182 | if (stcb == NULL) { |
michael@0 | 2183 | struct mbuf *op_err; |
michael@0 | 2184 | |
michael@0 | 2185 | /* memory problem? */ |
michael@0 | 2186 | SCTPDBG(SCTP_DEBUG_INPUT1, |
michael@0 | 2187 | "process_cookie_new: no room for another TCB!\n"); |
michael@0 | 2188 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); |
michael@0 | 2189 | |
michael@0 | 2190 | sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen, |
michael@0 | 2191 | src, dst, sh, op_err, |
michael@0 | 2192 | #if defined(__FreeBSD__) |
michael@0 | 2193 | use_mflowid, mflowid, |
michael@0 | 2194 | #endif |
michael@0 | 2195 | vrf_id, port); |
michael@0 | 2196 | return (NULL); |
michael@0 | 2197 | } |
michael@0 | 2198 | /* get the correct sctp_nets */ |
michael@0 | 2199 | if (netp) |
michael@0 | 2200 | *netp = sctp_findnet(stcb, init_src); |
michael@0 | 2201 | |
michael@0 | 2202 | asoc = &stcb->asoc; |
michael@0 | 2203 | /* get scope variables out of cookie */ |
michael@0 | 2204 | asoc->scope.ipv4_local_scope = cookie->ipv4_scope; |
michael@0 | 2205 | asoc->scope.site_scope = cookie->site_scope; |
michael@0 | 2206 | asoc->scope.local_scope = cookie->local_scope; |
michael@0 | 2207 | asoc->scope.loopback_scope = cookie->loopback_scope; |
michael@0 | 2208 | |
michael@0 | 2209 | #if defined(__Userspace__) |
michael@0 | 2210 | if ((asoc->scope.ipv4_addr_legal != cookie->ipv4_addr_legal) || |
michael@0 | 2211 | (asoc->scope.ipv6_addr_legal != cookie->ipv6_addr_legal) || |
michael@0 | 2212 | (asoc->scope.conn_addr_legal != cookie->conn_addr_legal)) { |
michael@0 | 2213 | #else |
michael@0 | 2214 | if ((asoc->scope.ipv4_addr_legal != cookie->ipv4_addr_legal) || |
michael@0 | 2215 | (asoc->scope.ipv6_addr_legal != cookie->ipv6_addr_legal)) { |
michael@0 | 2216 | #endif |
michael@0 | 2217 | struct mbuf *op_err; |
michael@0 | 2218 | |
michael@0 | 2219 | /* |
michael@0 | 2220 | * Houston we have a problem. The EP changed while the |
michael@0 | 2221 | * cookie was in flight. Only recourse is to abort the |
michael@0 | 2222 | * association. |
michael@0 | 2223 | */ |
michael@0 | 2224 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2225 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); |
michael@0 | 2226 | sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen, |
michael@0 | 2227 | src, dst, sh, op_err, |
michael@0 | 2228 | #if defined(__FreeBSD__) |
michael@0 | 2229 | use_mflowid, mflowid, |
michael@0 | 2230 | #endif |
michael@0 | 2231 | vrf_id, port); |
michael@0 | 2232 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2233 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 2234 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 2235 | SCTP_TCB_LOCK(stcb); |
michael@0 | 2236 | #endif |
michael@0 | 2237 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, |
michael@0 | 2238 | SCTP_FROM_SCTP_INPUT+SCTP_LOC_16); |
michael@0 | 2239 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2240 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2241 | #endif |
michael@0 | 2242 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2243 | return (NULL); |
michael@0 | 2244 | } |
michael@0 | 2245 | /* process the INIT-ACK info (my info) */ |
michael@0 | 2246 | asoc->my_vtag = ntohl(initack_cp->init.initiate_tag); |
michael@0 | 2247 | asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd); |
michael@0 | 2248 | asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams); |
michael@0 | 2249 | asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn); |
michael@0 | 2250 | asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number; |
michael@0 | 2251 | asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1; |
michael@0 | 2252 | asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1; |
michael@0 | 2253 | asoc->str_reset_seq_in = asoc->init_seq_number; |
michael@0 | 2254 | |
michael@0 | 2255 | asoc->advanced_peer_ack_point = asoc->last_acked_seq; |
michael@0 | 2256 | |
michael@0 | 2257 | /* process the INIT info (peer's info) */ |
michael@0 | 2258 | if (netp) |
michael@0 | 2259 | retval = sctp_process_init(init_cp, stcb); |
michael@0 | 2260 | else |
michael@0 | 2261 | retval = 0; |
michael@0 | 2262 | if (retval < 0) { |
michael@0 | 2263 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2264 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2265 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 2266 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 2267 | SCTP_TCB_LOCK(stcb); |
michael@0 | 2268 | #endif |
michael@0 | 2269 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_16); |
michael@0 | 2270 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2271 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2272 | #endif |
michael@0 | 2273 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2274 | return (NULL); |
michael@0 | 2275 | } |
michael@0 | 2276 | /* load all addresses */ |
michael@0 | 2277 | if (sctp_load_addresses_from_init(stcb, m, |
michael@0 | 2278 | init_offset + sizeof(struct sctp_init_chunk), initack_offset, |
michael@0 | 2279 | src, dst, init_src)) { |
michael@0 | 2280 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2281 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2282 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 2283 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 2284 | SCTP_TCB_LOCK(stcb); |
michael@0 | 2285 | #endif |
michael@0 | 2286 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_17); |
michael@0 | 2287 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2288 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2289 | #endif |
michael@0 | 2290 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2291 | return (NULL); |
michael@0 | 2292 | } |
michael@0 | 2293 | /* |
michael@0 | 2294 | * verify any preceding AUTH chunk that was skipped |
michael@0 | 2295 | */ |
michael@0 | 2296 | /* pull the local authentication parameters from the cookie/init-ack */ |
michael@0 | 2297 | sctp_auth_get_cookie_params(stcb, m, |
michael@0 | 2298 | initack_offset + sizeof(struct sctp_init_ack_chunk), |
michael@0 | 2299 | initack_limit - (initack_offset + sizeof(struct sctp_init_ack_chunk))); |
michael@0 | 2300 | if (auth_skipped) { |
michael@0 | 2301 | struct sctp_auth_chunk *auth; |
michael@0 | 2302 | |
michael@0 | 2303 | auth = (struct sctp_auth_chunk *) |
michael@0 | 2304 | sctp_m_getptr(m, auth_offset, auth_len, auth_chunk_buf); |
michael@0 | 2305 | if ((auth == NULL) || sctp_handle_auth(stcb, auth, m, auth_offset)) { |
michael@0 | 2306 | /* auth HMAC failed, dump the assoc and packet */ |
michael@0 | 2307 | SCTPDBG(SCTP_DEBUG_AUTH1, |
michael@0 | 2308 | "COOKIE-ECHO: AUTH failed\n"); |
michael@0 | 2309 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2310 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2311 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 2312 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 2313 | SCTP_TCB_LOCK(stcb); |
michael@0 | 2314 | #endif |
michael@0 | 2315 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_18); |
michael@0 | 2316 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2317 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2318 | #endif |
michael@0 | 2319 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2320 | return (NULL); |
michael@0 | 2321 | } else { |
michael@0 | 2322 | /* remaining chunks checked... good to go */ |
michael@0 | 2323 | stcb->asoc.authenticated = 1; |
michael@0 | 2324 | } |
michael@0 | 2325 | } |
michael@0 | 2326 | /* update current state */ |
michael@0 | 2327 | SCTPDBG(SCTP_DEBUG_INPUT2, "moving to OPEN state\n"); |
michael@0 | 2328 | SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); |
michael@0 | 2329 | if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { |
michael@0 | 2330 | sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, |
michael@0 | 2331 | stcb->sctp_ep, stcb, asoc->primary_destination); |
michael@0 | 2332 | } |
michael@0 | 2333 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 2334 | SCTP_STAT_INCR_COUNTER32(sctps_passiveestab); |
michael@0 | 2335 | SCTP_STAT_INCR_GAUGE32(sctps_currestab); |
michael@0 | 2336 | |
michael@0 | 2337 | /* |
michael@0 | 2338 | * if we're doing ASCONFs, check to see if we have any new local |
michael@0 | 2339 | * addresses that need to get added to the peer (eg. addresses |
michael@0 | 2340 | * changed while cookie echo in flight). This needs to be done |
michael@0 | 2341 | * after we go to the OPEN state to do the correct asconf |
michael@0 | 2342 | * processing. else, make sure we have the correct addresses in our |
michael@0 | 2343 | * lists |
michael@0 | 2344 | */ |
michael@0 | 2345 | |
michael@0 | 2346 | /* warning, we re-use sin, sin6, sa_store here! */ |
michael@0 | 2347 | /* pull in local_address (our "from" address) */ |
michael@0 | 2348 | switch (cookie->laddr_type) { |
michael@0 | 2349 | #ifdef INET |
michael@0 | 2350 | case SCTP_IPV4_ADDRESS: |
michael@0 | 2351 | /* source addr is IPv4 */ |
michael@0 | 2352 | sin = (struct sockaddr_in *)initack_src; |
michael@0 | 2353 | memset(sin, 0, sizeof(*sin)); |
michael@0 | 2354 | sin->sin_family = AF_INET; |
michael@0 | 2355 | #ifdef HAVE_SIN_LEN |
michael@0 | 2356 | sin->sin_len = sizeof(struct sockaddr_in); |
michael@0 | 2357 | #endif |
michael@0 | 2358 | sin->sin_addr.s_addr = cookie->laddress[0]; |
michael@0 | 2359 | break; |
michael@0 | 2360 | #endif |
michael@0 | 2361 | #ifdef INET6 |
michael@0 | 2362 | case SCTP_IPV6_ADDRESS: |
michael@0 | 2363 | /* source addr is IPv6 */ |
michael@0 | 2364 | sin6 = (struct sockaddr_in6 *)initack_src; |
michael@0 | 2365 | memset(sin6, 0, sizeof(*sin6)); |
michael@0 | 2366 | sin6->sin6_family = AF_INET6; |
michael@0 | 2367 | #ifdef HAVE_SIN6_LEN |
michael@0 | 2368 | sin6->sin6_len = sizeof(struct sockaddr_in6); |
michael@0 | 2369 | #endif |
michael@0 | 2370 | sin6->sin6_scope_id = cookie->scope_id; |
michael@0 | 2371 | memcpy(&sin6->sin6_addr, cookie->laddress, |
michael@0 | 2372 | sizeof(sin6->sin6_addr)); |
michael@0 | 2373 | break; |
michael@0 | 2374 | #endif |
michael@0 | 2375 | #if defined(__Userspace__) |
michael@0 | 2376 | case SCTP_CONN_ADDRESS: |
michael@0 | 2377 | /* source addr is IPv4 */ |
michael@0 | 2378 | sconn = (struct sockaddr_conn *)initack_src; |
michael@0 | 2379 | memset(sconn, 0, sizeof(struct sockaddr_conn)); |
michael@0 | 2380 | sconn->sconn_family = AF_CONN; |
michael@0 | 2381 | #ifdef HAVE_SCONN_LEN |
michael@0 | 2382 | sconn->sconn_len = sizeof(struct sockaddr_conn); |
michael@0 | 2383 | #endif |
michael@0 | 2384 | memcpy(&sconn->sconn_addr, cookie->laddress, sizeof(void *)); |
michael@0 | 2385 | break; |
michael@0 | 2386 | #endif |
michael@0 | 2387 | default: |
michael@0 | 2388 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2389 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2390 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 2391 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 2392 | SCTP_TCB_LOCK(stcb); |
michael@0 | 2393 | #endif |
michael@0 | 2394 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_19); |
michael@0 | 2395 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2396 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2397 | #endif |
michael@0 | 2398 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2399 | return (NULL); |
michael@0 | 2400 | } |
michael@0 | 2401 | |
michael@0 | 2402 | /* set up to notify upper layer */ |
michael@0 | 2403 | *notification = SCTP_NOTIFY_ASSOC_UP; |
michael@0 | 2404 | if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || |
michael@0 | 2405 | (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) && |
michael@0 | 2406 | (inp->sctp_socket->so_qlimit == 0)) { |
michael@0 | 2407 | /* |
michael@0 | 2408 | * This is an endpoint that called connect() how it got a |
michael@0 | 2409 | * cookie that is NEW is a bit of a mystery. It must be that |
michael@0 | 2410 | * the INIT was sent, but before it got there.. a complete |
michael@0 | 2411 | * INIT/INIT-ACK/COOKIE arrived. But of course then it |
michael@0 | 2412 | * should have went to the other code.. not here.. oh well.. |
michael@0 | 2413 | * a bit of protection is worth having.. |
michael@0 | 2414 | */ |
michael@0 | 2415 | stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED; |
michael@0 | 2416 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2417 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2418 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 2419 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 2420 | SCTP_TCB_LOCK(stcb); |
michael@0 | 2421 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 2422 | if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { |
michael@0 | 2423 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2424 | return (NULL); |
michael@0 | 2425 | } |
michael@0 | 2426 | #endif |
michael@0 | 2427 | soisconnected(stcb->sctp_socket); |
michael@0 | 2428 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2429 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 2430 | #endif |
michael@0 | 2431 | } else if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && |
michael@0 | 2432 | (inp->sctp_socket->so_qlimit)) { |
michael@0 | 2433 | /* |
michael@0 | 2434 | * We don't want to do anything with this one. Since it is |
michael@0 | 2435 | * the listening guy. The timer will get started for |
michael@0 | 2436 | * accepted connections in the caller. |
michael@0 | 2437 | */ |
michael@0 | 2438 | ; |
michael@0 | 2439 | } |
michael@0 | 2440 | /* since we did not send a HB make sure we don't double things */ |
michael@0 | 2441 | if ((netp) && (*netp)) |
michael@0 | 2442 | (*netp)->hb_responded = 1; |
michael@0 | 2443 | |
michael@0 | 2444 | if (stcb->asoc.sctp_autoclose_ticks && |
michael@0 | 2445 | sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE)) { |
michael@0 | 2446 | sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL); |
michael@0 | 2447 | } |
michael@0 | 2448 | /* calculate the RTT */ |
michael@0 | 2449 | (void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered); |
michael@0 | 2450 | if ((netp) && (*netp)) { |
michael@0 | 2451 | (*netp)->RTO = sctp_calculate_rto(stcb, asoc, *netp, |
michael@0 | 2452 | &cookie->time_entered, sctp_align_unsafe_makecopy, |
michael@0 | 2453 | SCTP_RTT_FROM_NON_DATA); |
michael@0 | 2454 | } |
michael@0 | 2455 | /* respond with a COOKIE-ACK */ |
michael@0 | 2456 | sctp_send_cookie_ack(stcb); |
michael@0 | 2457 | |
michael@0 | 2458 | /* |
michael@0 | 2459 | * check the address lists for any ASCONFs that need to be sent |
michael@0 | 2460 | * AFTER the cookie-ack is sent |
michael@0 | 2461 | */ |
michael@0 | 2462 | sctp_check_address_list(stcb, m, |
michael@0 | 2463 | initack_offset + sizeof(struct sctp_init_ack_chunk), |
michael@0 | 2464 | initack_limit - (initack_offset + sizeof(struct sctp_init_ack_chunk)), |
michael@0 | 2465 | initack_src, cookie->local_scope, cookie->site_scope, |
michael@0 | 2466 | cookie->ipv4_scope, cookie->loopback_scope); |
michael@0 | 2467 | |
michael@0 | 2468 | |
michael@0 | 2469 | return (stcb); |
michael@0 | 2470 | } |
michael@0 | 2471 | |
michael@0 | 2472 | /* |
michael@0 | 2473 | * CODE LIKE THIS NEEDS TO RUN IF the peer supports the NAT extension, i.e |
michael@0 | 2474 | * we NEED to make sure we are not already using the vtag. If so we |
michael@0 | 2475 | * need to send back an ABORT-TRY-AGAIN-WITH-NEW-TAG No middle box bit! |
michael@0 | 2476 | head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(tag, |
michael@0 | 2477 | SCTP_BASE_INFO(hashasocmark))]; |
michael@0 | 2478 | LIST_FOREACH(stcb, head, sctp_asocs) { |
michael@0 | 2479 | if ((stcb->asoc.my_vtag == tag) && (stcb->rport == rport) && (inp == stcb->sctp_ep)) { |
michael@0 | 2480 | -- SEND ABORT - TRY AGAIN -- |
michael@0 | 2481 | } |
michael@0 | 2482 | } |
michael@0 | 2483 | */ |
michael@0 | 2484 | |
michael@0 | 2485 | /* |
michael@0 | 2486 | * handles a COOKIE-ECHO message stcb: modified to either a new or left as |
michael@0 | 2487 | * existing (non-NULL) TCB |
michael@0 | 2488 | */ |
michael@0 | 2489 | static struct mbuf * |
michael@0 | 2490 | sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, |
michael@0 | 2491 | struct sockaddr *src, struct sockaddr *dst, |
michael@0 | 2492 | struct sctphdr *sh, struct sctp_cookie_echo_chunk *cp, |
michael@0 | 2493 | struct sctp_inpcb **inp_p, struct sctp_tcb **stcb, struct sctp_nets **netp, |
michael@0 | 2494 | int auth_skipped, uint32_t auth_offset, uint32_t auth_len, |
michael@0 | 2495 | struct sctp_tcb **locked_tcb, |
michael@0 | 2496 | #if defined(__FreeBSD__) |
michael@0 | 2497 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 2498 | #endif |
michael@0 | 2499 | uint32_t vrf_id, uint16_t port) |
michael@0 | 2500 | { |
michael@0 | 2501 | struct sctp_state_cookie *cookie; |
michael@0 | 2502 | struct sctp_tcb *l_stcb = *stcb; |
michael@0 | 2503 | struct sctp_inpcb *l_inp; |
michael@0 | 2504 | struct sockaddr *to; |
michael@0 | 2505 | struct sctp_pcb *ep; |
michael@0 | 2506 | struct mbuf *m_sig; |
michael@0 | 2507 | uint8_t calc_sig[SCTP_SIGNATURE_SIZE], tmp_sig[SCTP_SIGNATURE_SIZE]; |
michael@0 | 2508 | uint8_t *sig; |
michael@0 | 2509 | uint8_t cookie_ok = 0; |
michael@0 | 2510 | unsigned int sig_offset, cookie_offset; |
michael@0 | 2511 | unsigned int cookie_len; |
michael@0 | 2512 | struct timeval now; |
michael@0 | 2513 | struct timeval time_expires; |
michael@0 | 2514 | int notification = 0; |
michael@0 | 2515 | struct sctp_nets *netl; |
michael@0 | 2516 | int had_a_existing_tcb = 0; |
michael@0 | 2517 | int send_int_conf = 0; |
michael@0 | 2518 | #ifdef INET |
michael@0 | 2519 | struct sockaddr_in sin; |
michael@0 | 2520 | #endif |
michael@0 | 2521 | #ifdef INET6 |
michael@0 | 2522 | struct sockaddr_in6 sin6; |
michael@0 | 2523 | #endif |
michael@0 | 2524 | #if defined(__Userspace__) |
michael@0 | 2525 | struct sockaddr_conn sconn; |
michael@0 | 2526 | #endif |
michael@0 | 2527 | |
michael@0 | 2528 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 2529 | "sctp_handle_cookie: handling COOKIE-ECHO\n"); |
michael@0 | 2530 | |
michael@0 | 2531 | if (inp_p == NULL) { |
michael@0 | 2532 | return (NULL); |
michael@0 | 2533 | } |
michael@0 | 2534 | cookie = &cp->cookie; |
michael@0 | 2535 | cookie_offset = offset + sizeof(struct sctp_chunkhdr); |
michael@0 | 2536 | cookie_len = ntohs(cp->ch.chunk_length); |
michael@0 | 2537 | |
michael@0 | 2538 | if ((cookie->peerport != sh->src_port) && |
michael@0 | 2539 | (cookie->myport != sh->dest_port) && |
michael@0 | 2540 | (cookie->my_vtag != sh->v_tag)) { |
michael@0 | 2541 | /* |
michael@0 | 2542 | * invalid ports or bad tag. Note that we always leave the |
michael@0 | 2543 | * v_tag in the header in network order and when we stored |
michael@0 | 2544 | * it in the my_vtag slot we also left it in network order. |
michael@0 | 2545 | * This maintains the match even though it may be in the |
michael@0 | 2546 | * opposite byte order of the machine :-> |
michael@0 | 2547 | */ |
michael@0 | 2548 | return (NULL); |
michael@0 | 2549 | } |
michael@0 | 2550 | if (cookie_len < sizeof(struct sctp_cookie_echo_chunk) + |
michael@0 | 2551 | sizeof(struct sctp_init_chunk) + |
michael@0 | 2552 | sizeof(struct sctp_init_ack_chunk) + SCTP_SIGNATURE_SIZE) { |
michael@0 | 2553 | /* cookie too small */ |
michael@0 | 2554 | return (NULL); |
michael@0 | 2555 | } |
michael@0 | 2556 | /* |
michael@0 | 2557 | * split off the signature into its own mbuf (since it should not be |
michael@0 | 2558 | * calculated in the sctp_hmac_m() call). |
michael@0 | 2559 | */ |
michael@0 | 2560 | sig_offset = offset + cookie_len - SCTP_SIGNATURE_SIZE; |
michael@0 | 2561 | m_sig = m_split(m, sig_offset, M_NOWAIT); |
michael@0 | 2562 | if (m_sig == NULL) { |
michael@0 | 2563 | /* out of memory or ?? */ |
michael@0 | 2564 | return (NULL); |
michael@0 | 2565 | } |
michael@0 | 2566 | #ifdef SCTP_MBUF_LOGGING |
michael@0 | 2567 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) { |
michael@0 | 2568 | struct mbuf *mat; |
michael@0 | 2569 | |
michael@0 | 2570 | for (mat = m_sig; mat; mat = SCTP_BUF_NEXT(mat)) { |
michael@0 | 2571 | if (SCTP_BUF_IS_EXTENDED(mat)) { |
michael@0 | 2572 | sctp_log_mb(mat, SCTP_MBUF_SPLIT); |
michael@0 | 2573 | } |
michael@0 | 2574 | } |
michael@0 | 2575 | } |
michael@0 | 2576 | #endif |
michael@0 | 2577 | |
michael@0 | 2578 | /* |
michael@0 | 2579 | * compute the signature/digest for the cookie |
michael@0 | 2580 | */ |
michael@0 | 2581 | ep = &(*inp_p)->sctp_ep; |
michael@0 | 2582 | l_inp = *inp_p; |
michael@0 | 2583 | if (l_stcb) { |
michael@0 | 2584 | SCTP_TCB_UNLOCK(l_stcb); |
michael@0 | 2585 | } |
michael@0 | 2586 | SCTP_INP_RLOCK(l_inp); |
michael@0 | 2587 | if (l_stcb) { |
michael@0 | 2588 | SCTP_TCB_LOCK(l_stcb); |
michael@0 | 2589 | } |
michael@0 | 2590 | /* which cookie is it? */ |
michael@0 | 2591 | if ((cookie->time_entered.tv_sec < (long)ep->time_of_secret_change) && |
michael@0 | 2592 | (ep->current_secret_number != ep->last_secret_number)) { |
michael@0 | 2593 | /* it's the old cookie */ |
michael@0 | 2594 | (void)sctp_hmac_m(SCTP_HMAC, |
michael@0 | 2595 | (uint8_t *)ep->secret_key[(int)ep->last_secret_number], |
michael@0 | 2596 | SCTP_SECRET_SIZE, m, cookie_offset, calc_sig, 0); |
michael@0 | 2597 | } else { |
michael@0 | 2598 | /* it's the current cookie */ |
michael@0 | 2599 | (void)sctp_hmac_m(SCTP_HMAC, |
michael@0 | 2600 | (uint8_t *)ep->secret_key[(int)ep->current_secret_number], |
michael@0 | 2601 | SCTP_SECRET_SIZE, m, cookie_offset, calc_sig, 0); |
michael@0 | 2602 | } |
michael@0 | 2603 | /* get the signature */ |
michael@0 | 2604 | SCTP_INP_RUNLOCK(l_inp); |
michael@0 | 2605 | sig = (uint8_t *) sctp_m_getptr(m_sig, 0, SCTP_SIGNATURE_SIZE, (uint8_t *) & tmp_sig); |
michael@0 | 2606 | if (sig == NULL) { |
michael@0 | 2607 | /* couldn't find signature */ |
michael@0 | 2608 | sctp_m_freem(m_sig); |
michael@0 | 2609 | return (NULL); |
michael@0 | 2610 | } |
michael@0 | 2611 | /* compare the received digest with the computed digest */ |
michael@0 | 2612 | if (memcmp(calc_sig, sig, SCTP_SIGNATURE_SIZE) != 0) { |
michael@0 | 2613 | /* try the old cookie? */ |
michael@0 | 2614 | if ((cookie->time_entered.tv_sec == (long)ep->time_of_secret_change) && |
michael@0 | 2615 | (ep->current_secret_number != ep->last_secret_number)) { |
michael@0 | 2616 | /* compute digest with old */ |
michael@0 | 2617 | (void)sctp_hmac_m(SCTP_HMAC, |
michael@0 | 2618 | (uint8_t *)ep->secret_key[(int)ep->last_secret_number], |
michael@0 | 2619 | SCTP_SECRET_SIZE, m, cookie_offset, calc_sig, 0); |
michael@0 | 2620 | /* compare */ |
michael@0 | 2621 | if (memcmp(calc_sig, sig, SCTP_SIGNATURE_SIZE) == 0) |
michael@0 | 2622 | cookie_ok = 1; |
michael@0 | 2623 | } |
michael@0 | 2624 | } else { |
michael@0 | 2625 | cookie_ok = 1; |
michael@0 | 2626 | } |
michael@0 | 2627 | |
michael@0 | 2628 | /* |
michael@0 | 2629 | * Now before we continue we must reconstruct our mbuf so that |
michael@0 | 2630 | * normal processing of any other chunks will work. |
michael@0 | 2631 | */ |
michael@0 | 2632 | { |
michael@0 | 2633 | struct mbuf *m_at; |
michael@0 | 2634 | |
michael@0 | 2635 | m_at = m; |
michael@0 | 2636 | while (SCTP_BUF_NEXT(m_at) != NULL) { |
michael@0 | 2637 | m_at = SCTP_BUF_NEXT(m_at); |
michael@0 | 2638 | } |
michael@0 | 2639 | SCTP_BUF_NEXT(m_at) = m_sig; |
michael@0 | 2640 | } |
michael@0 | 2641 | |
michael@0 | 2642 | if (cookie_ok == 0) { |
michael@0 | 2643 | SCTPDBG(SCTP_DEBUG_INPUT2, "handle_cookie_echo: cookie signature validation failed!\n"); |
michael@0 | 2644 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 2645 | "offset = %u, cookie_offset = %u, sig_offset = %u\n", |
michael@0 | 2646 | (uint32_t) offset, cookie_offset, sig_offset); |
michael@0 | 2647 | return (NULL); |
michael@0 | 2648 | } |
michael@0 | 2649 | |
michael@0 | 2650 | /* |
michael@0 | 2651 | * check the cookie timestamps to be sure it's not stale |
michael@0 | 2652 | */ |
michael@0 | 2653 | (void)SCTP_GETTIME_TIMEVAL(&now); |
michael@0 | 2654 | /* Expire time is in Ticks, so we convert to seconds */ |
michael@0 | 2655 | time_expires.tv_sec = cookie->time_entered.tv_sec + TICKS_TO_SEC(cookie->cookie_life); |
michael@0 | 2656 | time_expires.tv_usec = cookie->time_entered.tv_usec; |
michael@0 | 2657 | /* TODO sctp_constants.h needs alternative time macros when |
michael@0 | 2658 | * _KERNEL is undefined. |
michael@0 | 2659 | */ |
michael@0 | 2660 | #ifndef __FreeBSD__ |
michael@0 | 2661 | if (timercmp(&now, &time_expires, >)) |
michael@0 | 2662 | #else |
michael@0 | 2663 | if (timevalcmp(&now, &time_expires, >)) |
michael@0 | 2664 | #endif |
michael@0 | 2665 | { |
michael@0 | 2666 | /* cookie is stale! */ |
michael@0 | 2667 | struct mbuf *op_err; |
michael@0 | 2668 | struct sctp_stale_cookie_msg *scm; |
michael@0 | 2669 | uint32_t tim; |
michael@0 | 2670 | op_err = sctp_get_mbuf_for_msg(sizeof(struct sctp_stale_cookie_msg), |
michael@0 | 2671 | 0, M_NOWAIT, 1, MT_DATA); |
michael@0 | 2672 | if (op_err == NULL) { |
michael@0 | 2673 | /* FOOBAR */ |
michael@0 | 2674 | return (NULL); |
michael@0 | 2675 | } |
michael@0 | 2676 | /* Set the len */ |
michael@0 | 2677 | SCTP_BUF_LEN(op_err) = sizeof(struct sctp_stale_cookie_msg); |
michael@0 | 2678 | scm = mtod(op_err, struct sctp_stale_cookie_msg *); |
michael@0 | 2679 | scm->ph.param_type = htons(SCTP_CAUSE_STALE_COOKIE); |
michael@0 | 2680 | scm->ph.param_length = htons((sizeof(struct sctp_paramhdr) + |
michael@0 | 2681 | (sizeof(uint32_t)))); |
michael@0 | 2682 | /* seconds to usec */ |
michael@0 | 2683 | tim = (now.tv_sec - time_expires.tv_sec) * 1000000; |
michael@0 | 2684 | /* add in usec */ |
michael@0 | 2685 | if (tim == 0) |
michael@0 | 2686 | tim = now.tv_usec - cookie->time_entered.tv_usec; |
michael@0 | 2687 | scm->time_usec = htonl(tim); |
michael@0 | 2688 | sctp_send_operr_to(src, dst, sh, cookie->peers_vtag, op_err, |
michael@0 | 2689 | #if defined(__FreeBSD__) |
michael@0 | 2690 | use_mflowid, mflowid, |
michael@0 | 2691 | #endif |
michael@0 | 2692 | vrf_id, port); |
michael@0 | 2693 | return (NULL); |
michael@0 | 2694 | } |
michael@0 | 2695 | /* |
michael@0 | 2696 | * Now we must see with the lookup address if we have an existing |
michael@0 | 2697 | * asoc. This will only happen if we were in the COOKIE-WAIT state |
michael@0 | 2698 | * and a INIT collided with us and somewhere the peer sent the |
michael@0 | 2699 | * cookie on another address besides the single address our assoc |
michael@0 | 2700 | * had for him. In this case we will have one of the tie-tags set at |
michael@0 | 2701 | * least AND the address field in the cookie can be used to look it |
michael@0 | 2702 | * up. |
michael@0 | 2703 | */ |
michael@0 | 2704 | to = NULL; |
michael@0 | 2705 | switch (cookie->addr_type) { |
michael@0 | 2706 | #ifdef INET6 |
michael@0 | 2707 | case SCTP_IPV6_ADDRESS: |
michael@0 | 2708 | memset(&sin6, 0, sizeof(sin6)); |
michael@0 | 2709 | sin6.sin6_family = AF_INET6; |
michael@0 | 2710 | #ifdef HAVE_SIN6_LEN |
michael@0 | 2711 | sin6.sin6_len = sizeof(sin6); |
michael@0 | 2712 | #endif |
michael@0 | 2713 | sin6.sin6_port = sh->src_port; |
michael@0 | 2714 | sin6.sin6_scope_id = cookie->scope_id; |
michael@0 | 2715 | memcpy(&sin6.sin6_addr.s6_addr, cookie->address, |
michael@0 | 2716 | sizeof(sin6.sin6_addr.s6_addr)); |
michael@0 | 2717 | to = (struct sockaddr *)&sin6; |
michael@0 | 2718 | break; |
michael@0 | 2719 | #endif |
michael@0 | 2720 | #ifdef INET |
michael@0 | 2721 | case SCTP_IPV4_ADDRESS: |
michael@0 | 2722 | memset(&sin, 0, sizeof(sin)); |
michael@0 | 2723 | sin.sin_family = AF_INET; |
michael@0 | 2724 | #ifdef HAVE_SIN_LEN |
michael@0 | 2725 | sin.sin_len = sizeof(sin); |
michael@0 | 2726 | #endif |
michael@0 | 2727 | sin.sin_port = sh->src_port; |
michael@0 | 2728 | sin.sin_addr.s_addr = cookie->address[0]; |
michael@0 | 2729 | to = (struct sockaddr *)&sin; |
michael@0 | 2730 | break; |
michael@0 | 2731 | #endif |
michael@0 | 2732 | #if defined(__Userspace__) |
michael@0 | 2733 | case SCTP_CONN_ADDRESS: |
michael@0 | 2734 | memset(&sconn, 0, sizeof(struct sockaddr_conn)); |
michael@0 | 2735 | sconn.sconn_family = AF_CONN; |
michael@0 | 2736 | #ifdef HAVE_SCONN_LEN |
michael@0 | 2737 | sconn.sconn_len = sizeof(struct sockaddr_conn); |
michael@0 | 2738 | #endif |
michael@0 | 2739 | sconn.sconn_port = sh->src_port; |
michael@0 | 2740 | memcpy(&sconn.sconn_addr, cookie->address, sizeof(void *)); |
michael@0 | 2741 | to = (struct sockaddr *)&sconn; |
michael@0 | 2742 | break; |
michael@0 | 2743 | #endif |
michael@0 | 2744 | default: |
michael@0 | 2745 | /* This should not happen */ |
michael@0 | 2746 | return (NULL); |
michael@0 | 2747 | } |
michael@0 | 2748 | if ((*stcb == NULL) && to) { |
michael@0 | 2749 | /* Yep, lets check */ |
michael@0 | 2750 | *stcb = sctp_findassociation_ep_addr(inp_p, to, netp, dst, NULL); |
michael@0 | 2751 | if (*stcb == NULL) { |
michael@0 | 2752 | /* |
michael@0 | 2753 | * We should have only got back the same inp. If we |
michael@0 | 2754 | * got back a different ep we have a problem. The |
michael@0 | 2755 | * original findep got back l_inp and now |
michael@0 | 2756 | */ |
michael@0 | 2757 | if (l_inp != *inp_p) { |
michael@0 | 2758 | SCTP_PRINTF("Bad problem find_ep got a diff inp then special_locate?\n"); |
michael@0 | 2759 | } |
michael@0 | 2760 | } else { |
michael@0 | 2761 | if (*locked_tcb == NULL) { |
michael@0 | 2762 | /* In this case we found the assoc only |
michael@0 | 2763 | * after we locked the create lock. This means |
michael@0 | 2764 | * we are in a colliding case and we must make |
michael@0 | 2765 | * sure that we unlock the tcb if its one of the |
michael@0 | 2766 | * cases where we throw away the incoming packets. |
michael@0 | 2767 | */ |
michael@0 | 2768 | *locked_tcb = *stcb; |
michael@0 | 2769 | |
michael@0 | 2770 | /* We must also increment the inp ref count |
michael@0 | 2771 | * since the ref_count flags was set when we |
michael@0 | 2772 | * did not find the TCB, now we found it which |
michael@0 | 2773 | * reduces the refcount.. we must raise it back |
michael@0 | 2774 | * out to balance it all :-) |
michael@0 | 2775 | */ |
michael@0 | 2776 | SCTP_INP_INCR_REF((*stcb)->sctp_ep); |
michael@0 | 2777 | if ((*stcb)->sctp_ep != l_inp) { |
michael@0 | 2778 | SCTP_PRINTF("Huh? ep:%p diff then l_inp:%p?\n", |
michael@0 | 2779 | (void *)(*stcb)->sctp_ep, (void *)l_inp); |
michael@0 | 2780 | } |
michael@0 | 2781 | } |
michael@0 | 2782 | } |
michael@0 | 2783 | } |
michael@0 | 2784 | if (to == NULL) { |
michael@0 | 2785 | return (NULL); |
michael@0 | 2786 | } |
michael@0 | 2787 | |
michael@0 | 2788 | cookie_len -= SCTP_SIGNATURE_SIZE; |
michael@0 | 2789 | if (*stcb == NULL) { |
michael@0 | 2790 | /* this is the "normal" case... get a new TCB */ |
michael@0 | 2791 | *stcb = sctp_process_cookie_new(m, iphlen, offset, src, dst, sh, |
michael@0 | 2792 | cookie, cookie_len, *inp_p, |
michael@0 | 2793 | netp, to, ¬ification, |
michael@0 | 2794 | auth_skipped, auth_offset, auth_len, |
michael@0 | 2795 | #if defined(__FreeBSD__) |
michael@0 | 2796 | use_mflowid, mflowid, |
michael@0 | 2797 | #endif |
michael@0 | 2798 | vrf_id, port); |
michael@0 | 2799 | } else { |
michael@0 | 2800 | /* this is abnormal... cookie-echo on existing TCB */ |
michael@0 | 2801 | had_a_existing_tcb = 1; |
michael@0 | 2802 | *stcb = sctp_process_cookie_existing(m, iphlen, offset, |
michael@0 | 2803 | src, dst, sh, |
michael@0 | 2804 | cookie, cookie_len, *inp_p, *stcb, netp, to, |
michael@0 | 2805 | ¬ification, auth_skipped, auth_offset, auth_len, |
michael@0 | 2806 | #if defined(__FreeBSD__) |
michael@0 | 2807 | use_mflowid, mflowid, |
michael@0 | 2808 | #endif |
michael@0 | 2809 | vrf_id, port); |
michael@0 | 2810 | } |
michael@0 | 2811 | |
michael@0 | 2812 | if (*stcb == NULL) { |
michael@0 | 2813 | /* still no TCB... must be bad cookie-echo */ |
michael@0 | 2814 | return (NULL); |
michael@0 | 2815 | } |
michael@0 | 2816 | #if defined(__FreeBSD__) |
michael@0 | 2817 | if ((*netp != NULL) && (use_mflowid != 0)) { |
michael@0 | 2818 | (*netp)->flowid = mflowid; |
michael@0 | 2819 | #ifdef INVARIANTS |
michael@0 | 2820 | (*netp)->flowidset = 1; |
michael@0 | 2821 | #endif |
michael@0 | 2822 | } |
michael@0 | 2823 | #endif |
michael@0 | 2824 | /* |
michael@0 | 2825 | * Ok, we built an association so confirm the address we sent the |
michael@0 | 2826 | * INIT-ACK to. |
michael@0 | 2827 | */ |
michael@0 | 2828 | netl = sctp_findnet(*stcb, to); |
michael@0 | 2829 | /* |
michael@0 | 2830 | * This code should in theory NOT run but |
michael@0 | 2831 | */ |
michael@0 | 2832 | if (netl == NULL) { |
michael@0 | 2833 | /* TSNH! Huh, why do I need to add this address here? */ |
michael@0 | 2834 | if (sctp_add_remote_addr(*stcb, to, NULL, SCTP_DONOT_SETSCOPE, SCTP_IN_COOKIE_PROC)) { |
michael@0 | 2835 | return (NULL); |
michael@0 | 2836 | } |
michael@0 | 2837 | netl = sctp_findnet(*stcb, to); |
michael@0 | 2838 | } |
michael@0 | 2839 | if (netl) { |
michael@0 | 2840 | if (netl->dest_state & SCTP_ADDR_UNCONFIRMED) { |
michael@0 | 2841 | netl->dest_state &= ~SCTP_ADDR_UNCONFIRMED; |
michael@0 | 2842 | (void)sctp_set_primary_addr((*stcb), (struct sockaddr *)NULL, |
michael@0 | 2843 | netl); |
michael@0 | 2844 | send_int_conf = 1; |
michael@0 | 2845 | } |
michael@0 | 2846 | } |
michael@0 | 2847 | sctp_start_net_timers(*stcb); |
michael@0 | 2848 | if ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) { |
michael@0 | 2849 | if (!had_a_existing_tcb || |
michael@0 | 2850 | (((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) { |
michael@0 | 2851 | /* |
michael@0 | 2852 | * If we have a NEW cookie or the connect never |
michael@0 | 2853 | * reached the connected state during collision we |
michael@0 | 2854 | * must do the TCP accept thing. |
michael@0 | 2855 | */ |
michael@0 | 2856 | struct socket *so, *oso; |
michael@0 | 2857 | struct sctp_inpcb *inp; |
michael@0 | 2858 | |
michael@0 | 2859 | if (notification == SCTP_NOTIFY_ASSOC_RESTART) { |
michael@0 | 2860 | /* |
michael@0 | 2861 | * For a restart we will keep the same |
michael@0 | 2862 | * socket, no need to do anything. I THINK!! |
michael@0 | 2863 | */ |
michael@0 | 2864 | sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 2865 | if (send_int_conf) { |
michael@0 | 2866 | sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, |
michael@0 | 2867 | (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED); |
michael@0 | 2868 | } |
michael@0 | 2869 | return (m); |
michael@0 | 2870 | } |
michael@0 | 2871 | oso = (*inp_p)->sctp_socket; |
michael@0 | 2872 | #if (defined(__FreeBSD__) && __FreeBSD_version < 700000) |
michael@0 | 2873 | /* |
michael@0 | 2874 | * We do this to keep the sockets side happy during |
michael@0 | 2875 | * the sonewcon ONLY. |
michael@0 | 2876 | */ |
michael@0 | 2877 | NET_LOCK_GIANT(); |
michael@0 | 2878 | #endif |
michael@0 | 2879 | atomic_add_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 2880 | SCTP_TCB_UNLOCK((*stcb)); |
michael@0 | 2881 | #if defined(__FreeBSD__) && __FreeBSD_version >= 801000 |
michael@0 | 2882 | CURVNET_SET(oso->so_vnet); |
michael@0 | 2883 | #endif |
michael@0 | 2884 | #if defined(__APPLE__) |
michael@0 | 2885 | SCTP_SOCKET_LOCK(oso, 1); |
michael@0 | 2886 | #endif |
michael@0 | 2887 | so = sonewconn(oso, 0 |
michael@0 | 2888 | #if defined(__APPLE__) |
michael@0 | 2889 | ,NULL |
michael@0 | 2890 | #endif |
michael@0 | 2891 | #ifdef __Panda__ |
michael@0 | 2892 | ,NULL , (*inp_p)->def_vrf_id |
michael@0 | 2893 | #endif |
michael@0 | 2894 | ); |
michael@0 | 2895 | #if (defined(__FreeBSD__) && __FreeBSD_version < 700000) |
michael@0 | 2896 | NET_UNLOCK_GIANT(); |
michael@0 | 2897 | #endif |
michael@0 | 2898 | #if defined(__APPLE__) |
michael@0 | 2899 | SCTP_SOCKET_UNLOCK(oso, 1); |
michael@0 | 2900 | #endif |
michael@0 | 2901 | #if defined(__FreeBSD__) && __FreeBSD_version >= 801000 |
michael@0 | 2902 | CURVNET_RESTORE(); |
michael@0 | 2903 | #endif |
michael@0 | 2904 | SCTP_TCB_LOCK((*stcb)); |
michael@0 | 2905 | atomic_subtract_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 2906 | |
michael@0 | 2907 | if (so == NULL) { |
michael@0 | 2908 | struct mbuf *op_err; |
michael@0 | 2909 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2910 | struct socket *pcb_so; |
michael@0 | 2911 | #endif |
michael@0 | 2912 | /* Too many sockets */ |
michael@0 | 2913 | SCTPDBG(SCTP_DEBUG_INPUT1, "process_cookie_new: no room for another socket!\n"); |
michael@0 | 2914 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); |
michael@0 | 2915 | sctp_abort_association(*inp_p, NULL, m, iphlen, |
michael@0 | 2916 | src, dst, sh, op_err, |
michael@0 | 2917 | #if defined(__FreeBSD__) |
michael@0 | 2918 | use_mflowid, mflowid, |
michael@0 | 2919 | #endif |
michael@0 | 2920 | vrf_id, port); |
michael@0 | 2921 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2922 | pcb_so = SCTP_INP_SO(*inp_p); |
michael@0 | 2923 | atomic_add_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 2924 | SCTP_TCB_UNLOCK((*stcb)); |
michael@0 | 2925 | SCTP_SOCKET_LOCK(pcb_so, 1); |
michael@0 | 2926 | SCTP_TCB_LOCK((*stcb)); |
michael@0 | 2927 | atomic_subtract_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 2928 | #endif |
michael@0 | 2929 | (void)sctp_free_assoc(*inp_p, *stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_20); |
michael@0 | 2930 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 2931 | SCTP_SOCKET_UNLOCK(pcb_so, 1); |
michael@0 | 2932 | #endif |
michael@0 | 2933 | return (NULL); |
michael@0 | 2934 | } |
michael@0 | 2935 | inp = (struct sctp_inpcb *)so->so_pcb; |
michael@0 | 2936 | SCTP_INP_INCR_REF(inp); |
michael@0 | 2937 | /* |
michael@0 | 2938 | * We add the unbound flag here so that |
michael@0 | 2939 | * if we get an soabort() before we get the |
michael@0 | 2940 | * move_pcb done, we will properly cleanup. |
michael@0 | 2941 | */ |
michael@0 | 2942 | inp->sctp_flags = (SCTP_PCB_FLAGS_TCPTYPE | |
michael@0 | 2943 | SCTP_PCB_FLAGS_CONNECTED | |
michael@0 | 2944 | SCTP_PCB_FLAGS_IN_TCPPOOL | |
michael@0 | 2945 | SCTP_PCB_FLAGS_UNBOUND | |
michael@0 | 2946 | (SCTP_PCB_COPY_FLAGS & (*inp_p)->sctp_flags) | |
michael@0 | 2947 | SCTP_PCB_FLAGS_DONT_WAKE); |
michael@0 | 2948 | inp->sctp_features = (*inp_p)->sctp_features; |
michael@0 | 2949 | inp->sctp_mobility_features = (*inp_p)->sctp_mobility_features; |
michael@0 | 2950 | inp->sctp_socket = so; |
michael@0 | 2951 | inp->sctp_frag_point = (*inp_p)->sctp_frag_point; |
michael@0 | 2952 | inp->sctp_cmt_on_off = (*inp_p)->sctp_cmt_on_off; |
michael@0 | 2953 | inp->sctp_ecn_enable = (*inp_p)->sctp_ecn_enable; |
michael@0 | 2954 | inp->partial_delivery_point = (*inp_p)->partial_delivery_point; |
michael@0 | 2955 | inp->sctp_context = (*inp_p)->sctp_context; |
michael@0 | 2956 | inp->local_strreset_support = (*inp_p)->local_strreset_support; |
michael@0 | 2957 | inp->inp_starting_point_for_iterator = NULL; |
michael@0 | 2958 | #if defined(__Userspace__) |
michael@0 | 2959 | inp->ulp_info = (*inp_p)->ulp_info; |
michael@0 | 2960 | inp->recv_callback = (*inp_p)->recv_callback; |
michael@0 | 2961 | inp->send_callback = (*inp_p)->send_callback; |
michael@0 | 2962 | inp->send_sb_threshold = (*inp_p)->send_sb_threshold; |
michael@0 | 2963 | #endif |
michael@0 | 2964 | /* |
michael@0 | 2965 | * copy in the authentication parameters from the |
michael@0 | 2966 | * original endpoint |
michael@0 | 2967 | */ |
michael@0 | 2968 | if (inp->sctp_ep.local_hmacs) |
michael@0 | 2969 | sctp_free_hmaclist(inp->sctp_ep.local_hmacs); |
michael@0 | 2970 | inp->sctp_ep.local_hmacs = |
michael@0 | 2971 | sctp_copy_hmaclist((*inp_p)->sctp_ep.local_hmacs); |
michael@0 | 2972 | if (inp->sctp_ep.local_auth_chunks) |
michael@0 | 2973 | sctp_free_chunklist(inp->sctp_ep.local_auth_chunks); |
michael@0 | 2974 | inp->sctp_ep.local_auth_chunks = |
michael@0 | 2975 | sctp_copy_chunklist((*inp_p)->sctp_ep.local_auth_chunks); |
michael@0 | 2976 | |
michael@0 | 2977 | /* |
michael@0 | 2978 | * Now we must move it from one hash table to |
michael@0 | 2979 | * another and get the tcb in the right place. |
michael@0 | 2980 | */ |
michael@0 | 2981 | |
michael@0 | 2982 | /* This is where the one-2-one socket is put into |
michael@0 | 2983 | * the accept state waiting for the accept! |
michael@0 | 2984 | */ |
michael@0 | 2985 | if (*stcb) { |
michael@0 | 2986 | (*stcb)->asoc.state |= SCTP_STATE_IN_ACCEPT_QUEUE; |
michael@0 | 2987 | } |
michael@0 | 2988 | sctp_move_pcb_and_assoc(*inp_p, inp, *stcb); |
michael@0 | 2989 | |
michael@0 | 2990 | atomic_add_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 2991 | SCTP_TCB_UNLOCK((*stcb)); |
michael@0 | 2992 | |
michael@0 | 2993 | #if defined(__FreeBSD__) |
michael@0 | 2994 | sctp_pull_off_control_to_new_inp((*inp_p), inp, *stcb, |
michael@0 | 2995 | 0); |
michael@0 | 2996 | #else |
michael@0 | 2997 | sctp_pull_off_control_to_new_inp((*inp_p), inp, *stcb, M_NOWAIT); |
michael@0 | 2998 | #endif |
michael@0 | 2999 | SCTP_TCB_LOCK((*stcb)); |
michael@0 | 3000 | atomic_subtract_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 3001 | |
michael@0 | 3002 | |
michael@0 | 3003 | /* now we must check to see if we were aborted while |
michael@0 | 3004 | * the move was going on and the lock/unlock happened. |
michael@0 | 3005 | */ |
michael@0 | 3006 | if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { |
michael@0 | 3007 | /* yep it was, we leave the |
michael@0 | 3008 | * assoc attached to the socket since |
michael@0 | 3009 | * the sctp_inpcb_free() call will send |
michael@0 | 3010 | * an abort for us. |
michael@0 | 3011 | */ |
michael@0 | 3012 | SCTP_INP_DECR_REF(inp); |
michael@0 | 3013 | return (NULL); |
michael@0 | 3014 | } |
michael@0 | 3015 | SCTP_INP_DECR_REF(inp); |
michael@0 | 3016 | /* Switch over to the new guy */ |
michael@0 | 3017 | *inp_p = inp; |
michael@0 | 3018 | sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 3019 | if (send_int_conf) { |
michael@0 | 3020 | sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, |
michael@0 | 3021 | (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED); |
michael@0 | 3022 | } |
michael@0 | 3023 | |
michael@0 | 3024 | /* Pull it from the incomplete queue and wake the guy */ |
michael@0 | 3025 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3026 | atomic_add_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 3027 | SCTP_TCB_UNLOCK((*stcb)); |
michael@0 | 3028 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 3029 | #endif |
michael@0 | 3030 | soisconnected(so); |
michael@0 | 3031 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3032 | SCTP_TCB_LOCK((*stcb)); |
michael@0 | 3033 | atomic_subtract_int(&(*stcb)->asoc.refcnt, 1); |
michael@0 | 3034 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 3035 | #endif |
michael@0 | 3036 | return (m); |
michael@0 | 3037 | } |
michael@0 | 3038 | } |
michael@0 | 3039 | if (notification) { |
michael@0 | 3040 | sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 3041 | } |
michael@0 | 3042 | if (send_int_conf) { |
michael@0 | 3043 | sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, |
michael@0 | 3044 | (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED); |
michael@0 | 3045 | } |
michael@0 | 3046 | return (m); |
michael@0 | 3047 | } |
michael@0 | 3048 | |
michael@0 | 3049 | static void |
michael@0 | 3050 | sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp SCTP_UNUSED, |
michael@0 | 3051 | struct sctp_tcb *stcb, struct sctp_nets *net) |
michael@0 | 3052 | { |
michael@0 | 3053 | /* cp must not be used, others call this without a c-ack :-) */ |
michael@0 | 3054 | struct sctp_association *asoc; |
michael@0 | 3055 | |
michael@0 | 3056 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 3057 | "sctp_handle_cookie_ack: handling COOKIE-ACK\n"); |
michael@0 | 3058 | if (stcb == NULL) |
michael@0 | 3059 | return; |
michael@0 | 3060 | |
michael@0 | 3061 | asoc = &stcb->asoc; |
michael@0 | 3062 | |
michael@0 | 3063 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 3064 | /* process according to association state */ |
michael@0 | 3065 | if (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED) { |
michael@0 | 3066 | /* state change only needed when I am in right state */ |
michael@0 | 3067 | SCTPDBG(SCTP_DEBUG_INPUT2, "moving to OPEN state\n"); |
michael@0 | 3068 | SCTP_SET_STATE(asoc, SCTP_STATE_OPEN); |
michael@0 | 3069 | sctp_start_net_timers(stcb); |
michael@0 | 3070 | if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) { |
michael@0 | 3071 | sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, |
michael@0 | 3072 | stcb->sctp_ep, stcb, asoc->primary_destination); |
michael@0 | 3073 | |
michael@0 | 3074 | } |
michael@0 | 3075 | /* update RTO */ |
michael@0 | 3076 | SCTP_STAT_INCR_COUNTER32(sctps_activeestab); |
michael@0 | 3077 | SCTP_STAT_INCR_GAUGE32(sctps_currestab); |
michael@0 | 3078 | if (asoc->overall_error_count == 0) { |
michael@0 | 3079 | net->RTO = sctp_calculate_rto(stcb, asoc, net, |
michael@0 | 3080 | &asoc->time_entered, sctp_align_safe_nocopy, |
michael@0 | 3081 | SCTP_RTT_FROM_NON_DATA); |
michael@0 | 3082 | } |
michael@0 | 3083 | (void)SCTP_GETTIME_TIMEVAL(&asoc->time_entered); |
michael@0 | 3084 | sctp_ulp_notify(SCTP_NOTIFY_ASSOC_UP, stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 3085 | if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || |
michael@0 | 3086 | (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { |
michael@0 | 3087 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3088 | struct socket *so; |
michael@0 | 3089 | |
michael@0 | 3090 | #endif |
michael@0 | 3091 | stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED; |
michael@0 | 3092 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3093 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 3094 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 3095 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 3096 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 3097 | SCTP_TCB_LOCK(stcb); |
michael@0 | 3098 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 3099 | #endif |
michael@0 | 3100 | if ((stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) == 0) { |
michael@0 | 3101 | soisconnected(stcb->sctp_socket); |
michael@0 | 3102 | } |
michael@0 | 3103 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3104 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 3105 | #endif |
michael@0 | 3106 | } |
michael@0 | 3107 | /* |
michael@0 | 3108 | * since we did not send a HB make sure we don't double |
michael@0 | 3109 | * things |
michael@0 | 3110 | */ |
michael@0 | 3111 | net->hb_responded = 1; |
michael@0 | 3112 | |
michael@0 | 3113 | if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { |
michael@0 | 3114 | /* We don't need to do the asconf thing, |
michael@0 | 3115 | * nor hb or autoclose if the socket is closed. |
michael@0 | 3116 | */ |
michael@0 | 3117 | goto closed_socket; |
michael@0 | 3118 | } |
michael@0 | 3119 | |
michael@0 | 3120 | sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, |
michael@0 | 3121 | stcb, net); |
michael@0 | 3122 | |
michael@0 | 3123 | |
michael@0 | 3124 | if (stcb->asoc.sctp_autoclose_ticks && |
michael@0 | 3125 | sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_AUTOCLOSE)) { |
michael@0 | 3126 | sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, |
michael@0 | 3127 | stcb->sctp_ep, stcb, NULL); |
michael@0 | 3128 | } |
michael@0 | 3129 | /* |
michael@0 | 3130 | * send ASCONF if parameters are pending and ASCONFs are |
michael@0 | 3131 | * allowed (eg. addresses changed when init/cookie echo were |
michael@0 | 3132 | * in flight) |
michael@0 | 3133 | */ |
michael@0 | 3134 | if ((sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_DO_ASCONF)) && |
michael@0 | 3135 | (stcb->asoc.peer_supports_asconf) && |
michael@0 | 3136 | (!TAILQ_EMPTY(&stcb->asoc.asconf_queue))) { |
michael@0 | 3137 | #ifdef SCTP_TIMER_BASED_ASCONF |
michael@0 | 3138 | sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, |
michael@0 | 3139 | stcb->sctp_ep, stcb, |
michael@0 | 3140 | stcb->asoc.primary_destination); |
michael@0 | 3141 | #else |
michael@0 | 3142 | sctp_send_asconf(stcb, stcb->asoc.primary_destination, |
michael@0 | 3143 | SCTP_ADDR_NOT_LOCKED); |
michael@0 | 3144 | #endif |
michael@0 | 3145 | } |
michael@0 | 3146 | } |
michael@0 | 3147 | closed_socket: |
michael@0 | 3148 | /* Toss the cookie if I can */ |
michael@0 | 3149 | sctp_toss_old_cookies(stcb, asoc); |
michael@0 | 3150 | if (!TAILQ_EMPTY(&asoc->sent_queue)) { |
michael@0 | 3151 | /* Restart the timer if we have pending data */ |
michael@0 | 3152 | struct sctp_tmit_chunk *chk; |
michael@0 | 3153 | |
michael@0 | 3154 | chk = TAILQ_FIRST(&asoc->sent_queue); |
michael@0 | 3155 | sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, chk->whoTo); |
michael@0 | 3156 | } |
michael@0 | 3157 | } |
michael@0 | 3158 | |
michael@0 | 3159 | static void |
michael@0 | 3160 | sctp_handle_ecn_echo(struct sctp_ecne_chunk *cp, |
michael@0 | 3161 | struct sctp_tcb *stcb) |
michael@0 | 3162 | { |
michael@0 | 3163 | struct sctp_nets *net; |
michael@0 | 3164 | struct sctp_tmit_chunk *lchk; |
michael@0 | 3165 | struct sctp_ecne_chunk bkup; |
michael@0 | 3166 | uint8_t override_bit; |
michael@0 | 3167 | uint32_t tsn, window_data_tsn; |
michael@0 | 3168 | int len; |
michael@0 | 3169 | unsigned int pkt_cnt; |
michael@0 | 3170 | |
michael@0 | 3171 | len = ntohs(cp->ch.chunk_length); |
michael@0 | 3172 | if ((len != sizeof(struct sctp_ecne_chunk)) && |
michael@0 | 3173 | (len != sizeof(struct old_sctp_ecne_chunk))) { |
michael@0 | 3174 | return; |
michael@0 | 3175 | } |
michael@0 | 3176 | if (len == sizeof(struct old_sctp_ecne_chunk)) { |
michael@0 | 3177 | /* Its the old format */ |
michael@0 | 3178 | memcpy(&bkup, cp, sizeof(struct old_sctp_ecne_chunk)); |
michael@0 | 3179 | bkup.num_pkts_since_cwr = htonl(1); |
michael@0 | 3180 | cp = &bkup; |
michael@0 | 3181 | } |
michael@0 | 3182 | SCTP_STAT_INCR(sctps_recvecne); |
michael@0 | 3183 | tsn = ntohl(cp->tsn); |
michael@0 | 3184 | pkt_cnt = ntohl(cp->num_pkts_since_cwr); |
michael@0 | 3185 | lchk = TAILQ_LAST(&stcb->asoc.send_queue, sctpchunk_listhead); |
michael@0 | 3186 | if (lchk == NULL) { |
michael@0 | 3187 | window_data_tsn = stcb->asoc.sending_seq - 1; |
michael@0 | 3188 | } else { |
michael@0 | 3189 | window_data_tsn = lchk->rec.data.TSN_seq; |
michael@0 | 3190 | } |
michael@0 | 3191 | |
michael@0 | 3192 | /* Find where it was sent to if possible. */ |
michael@0 | 3193 | net = NULL; |
michael@0 | 3194 | TAILQ_FOREACH(lchk, &stcb->asoc.sent_queue, sctp_next) { |
michael@0 | 3195 | if (lchk->rec.data.TSN_seq == tsn) { |
michael@0 | 3196 | net = lchk->whoTo; |
michael@0 | 3197 | net->ecn_prev_cwnd = lchk->rec.data.cwnd_at_send; |
michael@0 | 3198 | break; |
michael@0 | 3199 | } |
michael@0 | 3200 | if (SCTP_TSN_GT(lchk->rec.data.TSN_seq, tsn)) { |
michael@0 | 3201 | break; |
michael@0 | 3202 | } |
michael@0 | 3203 | } |
michael@0 | 3204 | if (net == NULL) { |
michael@0 | 3205 | /* |
michael@0 | 3206 | * What to do. A previous send of a |
michael@0 | 3207 | * CWR was possibly lost. See how old it is, we |
michael@0 | 3208 | * may have it marked on the actual net. |
michael@0 | 3209 | */ |
michael@0 | 3210 | TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { |
michael@0 | 3211 | if (tsn == net->last_cwr_tsn) { |
michael@0 | 3212 | /* Found him, send it off */ |
michael@0 | 3213 | break; |
michael@0 | 3214 | } |
michael@0 | 3215 | } |
michael@0 | 3216 | if (net == NULL) { |
michael@0 | 3217 | /* |
michael@0 | 3218 | * If we reach here, we need to send a special |
michael@0 | 3219 | * CWR that says hey, we did this a long time |
michael@0 | 3220 | * ago and you lost the response. |
michael@0 | 3221 | */ |
michael@0 | 3222 | net = TAILQ_FIRST(&stcb->asoc.nets); |
michael@0 | 3223 | if (net == NULL) { |
michael@0 | 3224 | /* TSNH */ |
michael@0 | 3225 | return; |
michael@0 | 3226 | } |
michael@0 | 3227 | override_bit = SCTP_CWR_REDUCE_OVERRIDE; |
michael@0 | 3228 | } else { |
michael@0 | 3229 | override_bit = 0; |
michael@0 | 3230 | } |
michael@0 | 3231 | } else { |
michael@0 | 3232 | override_bit = 0; |
michael@0 | 3233 | } |
michael@0 | 3234 | if (SCTP_TSN_GT(tsn, net->cwr_window_tsn) && |
michael@0 | 3235 | ((override_bit&SCTP_CWR_REDUCE_OVERRIDE) == 0)) { |
michael@0 | 3236 | /* JRS - Use the congestion control given in the pluggable CC module */ |
michael@0 | 3237 | stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net, 0, pkt_cnt); |
michael@0 | 3238 | /* |
michael@0 | 3239 | * We reduce once every RTT. So we will only lower cwnd at |
michael@0 | 3240 | * the next sending seq i.e. the window_data_tsn |
michael@0 | 3241 | */ |
michael@0 | 3242 | net->cwr_window_tsn = window_data_tsn; |
michael@0 | 3243 | net->ecn_ce_pkt_cnt += pkt_cnt; |
michael@0 | 3244 | net->lost_cnt = pkt_cnt; |
michael@0 | 3245 | net->last_cwr_tsn = tsn; |
michael@0 | 3246 | } else { |
michael@0 | 3247 | override_bit |= SCTP_CWR_IN_SAME_WINDOW; |
michael@0 | 3248 | if (SCTP_TSN_GT(tsn, net->last_cwr_tsn) && |
michael@0 | 3249 | ((override_bit&SCTP_CWR_REDUCE_OVERRIDE) == 0)) { |
michael@0 | 3250 | /* |
michael@0 | 3251 | * Another loss in the same window update how |
michael@0 | 3252 | * many marks/packets lost we have had. |
michael@0 | 3253 | */ |
michael@0 | 3254 | int cnt = 1; |
michael@0 | 3255 | if (pkt_cnt > net->lost_cnt) { |
michael@0 | 3256 | /* Should be the case */ |
michael@0 | 3257 | cnt = (pkt_cnt - net->lost_cnt); |
michael@0 | 3258 | net->ecn_ce_pkt_cnt += cnt; |
michael@0 | 3259 | } |
michael@0 | 3260 | net->lost_cnt = pkt_cnt; |
michael@0 | 3261 | net->last_cwr_tsn = tsn; |
michael@0 | 3262 | /* |
michael@0 | 3263 | * Most CC functions will ignore this call, since we are in-window |
michael@0 | 3264 | * yet of the initial CE the peer saw. |
michael@0 | 3265 | */ |
michael@0 | 3266 | stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net, 1, cnt); |
michael@0 | 3267 | } |
michael@0 | 3268 | } |
michael@0 | 3269 | /* |
michael@0 | 3270 | * We always send a CWR this way if our previous one was lost our |
michael@0 | 3271 | * peer will get an update, or if it is not time again to reduce we |
michael@0 | 3272 | * still get the cwr to the peer. Note we set the override when we |
michael@0 | 3273 | * could not find the TSN on the chunk or the destination network. |
michael@0 | 3274 | */ |
michael@0 | 3275 | sctp_send_cwr(stcb, net, net->last_cwr_tsn, override_bit); |
michael@0 | 3276 | } |
michael@0 | 3277 | |
michael@0 | 3278 | static void |
michael@0 | 3279 | sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb, struct sctp_nets *net) |
michael@0 | 3280 | { |
michael@0 | 3281 | /* |
michael@0 | 3282 | * Here we get a CWR from the peer. We must look in the outqueue and |
michael@0 | 3283 | * make sure that we have a covered ECNE in the control chunk part. |
michael@0 | 3284 | * If so remove it. |
michael@0 | 3285 | */ |
michael@0 | 3286 | struct sctp_tmit_chunk *chk; |
michael@0 | 3287 | struct sctp_ecne_chunk *ecne; |
michael@0 | 3288 | int override; |
michael@0 | 3289 | uint32_t cwr_tsn; |
michael@0 | 3290 | cwr_tsn = ntohl(cp->tsn); |
michael@0 | 3291 | |
michael@0 | 3292 | override = cp->ch.chunk_flags & SCTP_CWR_REDUCE_OVERRIDE; |
michael@0 | 3293 | TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) { |
michael@0 | 3294 | if (chk->rec.chunk_id.id != SCTP_ECN_ECHO) { |
michael@0 | 3295 | continue; |
michael@0 | 3296 | } |
michael@0 | 3297 | if ((override == 0) && (chk->whoTo != net)) { |
michael@0 | 3298 | /* Must be from the right src unless override is set */ |
michael@0 | 3299 | continue; |
michael@0 | 3300 | } |
michael@0 | 3301 | ecne = mtod(chk->data, struct sctp_ecne_chunk *); |
michael@0 | 3302 | if (SCTP_TSN_GE(cwr_tsn, ntohl(ecne->tsn))) { |
michael@0 | 3303 | /* this covers this ECNE, we can remove it */ |
michael@0 | 3304 | stcb->asoc.ecn_echo_cnt_onq--; |
michael@0 | 3305 | TAILQ_REMOVE(&stcb->asoc.control_send_queue, chk, |
michael@0 | 3306 | sctp_next); |
michael@0 | 3307 | if (chk->data) { |
michael@0 | 3308 | sctp_m_freem(chk->data); |
michael@0 | 3309 | chk->data = NULL; |
michael@0 | 3310 | } |
michael@0 | 3311 | stcb->asoc.ctrl_queue_cnt--; |
michael@0 | 3312 | sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); |
michael@0 | 3313 | if (override == 0) { |
michael@0 | 3314 | break; |
michael@0 | 3315 | } |
michael@0 | 3316 | } |
michael@0 | 3317 | } |
michael@0 | 3318 | } |
michael@0 | 3319 | |
michael@0 | 3320 | static void |
michael@0 | 3321 | sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp SCTP_UNUSED, |
michael@0 | 3322 | struct sctp_tcb *stcb, struct sctp_nets *net) |
michael@0 | 3323 | { |
michael@0 | 3324 | struct sctp_association *asoc; |
michael@0 | 3325 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3326 | struct socket *so; |
michael@0 | 3327 | #endif |
michael@0 | 3328 | |
michael@0 | 3329 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 3330 | "sctp_handle_shutdown_complete: handling SHUTDOWN-COMPLETE\n"); |
michael@0 | 3331 | if (stcb == NULL) |
michael@0 | 3332 | return; |
michael@0 | 3333 | |
michael@0 | 3334 | asoc = &stcb->asoc; |
michael@0 | 3335 | /* process according to association state */ |
michael@0 | 3336 | if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT) { |
michael@0 | 3337 | /* unexpected SHUTDOWN-COMPLETE... so ignore... */ |
michael@0 | 3338 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 3339 | "sctp_handle_shutdown_complete: not in SCTP_STATE_SHUTDOWN_ACK_SENT --- ignore\n"); |
michael@0 | 3340 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 3341 | return; |
michael@0 | 3342 | } |
michael@0 | 3343 | /* notify upper layer protocol */ |
michael@0 | 3344 | if (stcb->sctp_socket) { |
michael@0 | 3345 | sctp_ulp_notify(SCTP_NOTIFY_ASSOC_DOWN, stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 3346 | } |
michael@0 | 3347 | #ifdef INVARIANTS |
michael@0 | 3348 | if (!TAILQ_EMPTY(&asoc->send_queue) || |
michael@0 | 3349 | !TAILQ_EMPTY(&asoc->sent_queue) || |
michael@0 | 3350 | !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { |
michael@0 | 3351 | panic("Queues are not empty when handling SHUTDOWN-COMPLETE"); |
michael@0 | 3352 | } |
michael@0 | 3353 | #endif |
michael@0 | 3354 | /* stop the timer */ |
michael@0 | 3355 | sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWNACK, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_22); |
michael@0 | 3356 | SCTP_STAT_INCR_COUNTER32(sctps_shutdown); |
michael@0 | 3357 | /* free the TCB */ |
michael@0 | 3358 | SCTPDBG(SCTP_DEBUG_INPUT2, |
michael@0 | 3359 | "sctp_handle_shutdown_complete: calls free-asoc\n"); |
michael@0 | 3360 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3361 | so = SCTP_INP_SO(stcb->sctp_ep); |
michael@0 | 3362 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 3363 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 3364 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 3365 | SCTP_TCB_LOCK(stcb); |
michael@0 | 3366 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 3367 | #endif |
michael@0 | 3368 | (void)sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_23); |
michael@0 | 3369 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 3370 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 3371 | #endif |
michael@0 | 3372 | return; |
michael@0 | 3373 | } |
michael@0 | 3374 | |
michael@0 | 3375 | static int |
michael@0 | 3376 | process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, |
michael@0 | 3377 | struct sctp_nets *net, uint8_t flg) |
michael@0 | 3378 | { |
michael@0 | 3379 | switch (desc->chunk_type) { |
michael@0 | 3380 | case SCTP_DATA: |
michael@0 | 3381 | /* find the tsn to resend (possibly */ |
michael@0 | 3382 | { |
michael@0 | 3383 | uint32_t tsn; |
michael@0 | 3384 | struct sctp_tmit_chunk *tp1; |
michael@0 | 3385 | |
michael@0 | 3386 | tsn = ntohl(desc->tsn_ifany); |
michael@0 | 3387 | TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) { |
michael@0 | 3388 | if (tp1->rec.data.TSN_seq == tsn) { |
michael@0 | 3389 | /* found it */ |
michael@0 | 3390 | break; |
michael@0 | 3391 | } |
michael@0 | 3392 | if (SCTP_TSN_GT(tp1->rec.data.TSN_seq, tsn)) { |
michael@0 | 3393 | /* not found */ |
michael@0 | 3394 | tp1 = NULL; |
michael@0 | 3395 | break; |
michael@0 | 3396 | } |
michael@0 | 3397 | } |
michael@0 | 3398 | if (tp1 == NULL) { |
michael@0 | 3399 | /* |
michael@0 | 3400 | * Do it the other way , aka without paying |
michael@0 | 3401 | * attention to queue seq order. |
michael@0 | 3402 | */ |
michael@0 | 3403 | SCTP_STAT_INCR(sctps_pdrpdnfnd); |
michael@0 | 3404 | TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) { |
michael@0 | 3405 | if (tp1->rec.data.TSN_seq == tsn) { |
michael@0 | 3406 | /* found it */ |
michael@0 | 3407 | break; |
michael@0 | 3408 | } |
michael@0 | 3409 | } |
michael@0 | 3410 | } |
michael@0 | 3411 | if (tp1 == NULL) { |
michael@0 | 3412 | SCTP_STAT_INCR(sctps_pdrptsnnf); |
michael@0 | 3413 | } |
michael@0 | 3414 | if ((tp1) && (tp1->sent < SCTP_DATAGRAM_ACKED)) { |
michael@0 | 3415 | uint8_t *ddp; |
michael@0 | 3416 | |
michael@0 | 3417 | if (((flg & SCTP_BADCRC) == 0) && |
michael@0 | 3418 | ((flg & SCTP_FROM_MIDDLE_BOX) == 0)) { |
michael@0 | 3419 | return (0); |
michael@0 | 3420 | } |
michael@0 | 3421 | if ((stcb->asoc.peers_rwnd == 0) && |
michael@0 | 3422 | ((flg & SCTP_FROM_MIDDLE_BOX) == 0)) { |
michael@0 | 3423 | SCTP_STAT_INCR(sctps_pdrpdiwnp); |
michael@0 | 3424 | return (0); |
michael@0 | 3425 | } |
michael@0 | 3426 | if (stcb->asoc.peers_rwnd == 0 && |
michael@0 | 3427 | (flg & SCTP_FROM_MIDDLE_BOX)) { |
michael@0 | 3428 | SCTP_STAT_INCR(sctps_pdrpdizrw); |
michael@0 | 3429 | return (0); |
michael@0 | 3430 | } |
michael@0 | 3431 | ddp = (uint8_t *) (mtod(tp1->data, caddr_t) + |
michael@0 | 3432 | sizeof(struct sctp_data_chunk)); |
michael@0 | 3433 | { |
michael@0 | 3434 | unsigned int iii; |
michael@0 | 3435 | |
michael@0 | 3436 | for (iii = 0; iii < sizeof(desc->data_bytes); |
michael@0 | 3437 | iii++) { |
michael@0 | 3438 | if (ddp[iii] != desc->data_bytes[iii]) { |
michael@0 | 3439 | SCTP_STAT_INCR(sctps_pdrpbadd); |
michael@0 | 3440 | return (-1); |
michael@0 | 3441 | } |
michael@0 | 3442 | } |
michael@0 | 3443 | } |
michael@0 | 3444 | |
michael@0 | 3445 | if (tp1->do_rtt) { |
michael@0 | 3446 | /* |
michael@0 | 3447 | * this guy had a RTO calculation |
michael@0 | 3448 | * pending on it, cancel it |
michael@0 | 3449 | */ |
michael@0 | 3450 | if (tp1->whoTo->rto_needed == 0) { |
michael@0 | 3451 | tp1->whoTo->rto_needed = 1; |
michael@0 | 3452 | } |
michael@0 | 3453 | tp1->do_rtt = 0; |
michael@0 | 3454 | } |
michael@0 | 3455 | SCTP_STAT_INCR(sctps_pdrpmark); |
michael@0 | 3456 | if (tp1->sent != SCTP_DATAGRAM_RESEND) |
michael@0 | 3457 | sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); |
michael@0 | 3458 | /* |
michael@0 | 3459 | * mark it as if we were doing a FR, since |
michael@0 | 3460 | * we will be getting gap ack reports behind |
michael@0 | 3461 | * the info from the router. |
michael@0 | 3462 | */ |
michael@0 | 3463 | tp1->rec.data.doing_fast_retransmit = 1; |
michael@0 | 3464 | /* |
michael@0 | 3465 | * mark the tsn with what sequences can |
michael@0 | 3466 | * cause a new FR. |
michael@0 | 3467 | */ |
michael@0 | 3468 | if (TAILQ_EMPTY(&stcb->asoc.send_queue)) { |
michael@0 | 3469 | tp1->rec.data.fast_retran_tsn = stcb->asoc.sending_seq; |
michael@0 | 3470 | } else { |
michael@0 | 3471 | tp1->rec.data.fast_retran_tsn = (TAILQ_FIRST(&stcb->asoc.send_queue))->rec.data.TSN_seq; |
michael@0 | 3472 | } |
michael@0 | 3473 | |
michael@0 | 3474 | /* restart the timer */ |
michael@0 | 3475 | sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, |
michael@0 | 3476 | stcb, tp1->whoTo, SCTP_FROM_SCTP_INPUT+SCTP_LOC_24); |
michael@0 | 3477 | sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, |
michael@0 | 3478 | stcb, tp1->whoTo); |
michael@0 | 3479 | |
michael@0 | 3480 | /* fix counts and things */ |
michael@0 | 3481 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) { |
michael@0 | 3482 | sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_PDRP, |
michael@0 | 3483 | tp1->whoTo->flight_size, |
michael@0 | 3484 | tp1->book_size, |
michael@0 | 3485 | (uintptr_t)stcb, |
michael@0 | 3486 | tp1->rec.data.TSN_seq); |
michael@0 | 3487 | } |
michael@0 | 3488 | if (tp1->sent < SCTP_DATAGRAM_RESEND) { |
michael@0 | 3489 | sctp_flight_size_decrease(tp1); |
michael@0 | 3490 | sctp_total_flight_decrease(stcb, tp1); |
michael@0 | 3491 | } |
michael@0 | 3492 | tp1->sent = SCTP_DATAGRAM_RESEND; |
michael@0 | 3493 | } { |
michael@0 | 3494 | /* audit code */ |
michael@0 | 3495 | unsigned int audit; |
michael@0 | 3496 | |
michael@0 | 3497 | audit = 0; |
michael@0 | 3498 | TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) { |
michael@0 | 3499 | if (tp1->sent == SCTP_DATAGRAM_RESEND) |
michael@0 | 3500 | audit++; |
michael@0 | 3501 | } |
michael@0 | 3502 | TAILQ_FOREACH(tp1, &stcb->asoc.control_send_queue, |
michael@0 | 3503 | sctp_next) { |
michael@0 | 3504 | if (tp1->sent == SCTP_DATAGRAM_RESEND) |
michael@0 | 3505 | audit++; |
michael@0 | 3506 | } |
michael@0 | 3507 | if (audit != stcb->asoc.sent_queue_retran_cnt) { |
michael@0 | 3508 | SCTP_PRINTF("**Local Audit finds cnt:%d asoc cnt:%d\n", |
michael@0 | 3509 | audit, stcb->asoc.sent_queue_retran_cnt); |
michael@0 | 3510 | #ifndef SCTP_AUDITING_ENABLED |
michael@0 | 3511 | stcb->asoc.sent_queue_retran_cnt = audit; |
michael@0 | 3512 | #endif |
michael@0 | 3513 | } |
michael@0 | 3514 | } |
michael@0 | 3515 | } |
michael@0 | 3516 | break; |
michael@0 | 3517 | case SCTP_ASCONF: |
michael@0 | 3518 | { |
michael@0 | 3519 | struct sctp_tmit_chunk *asconf; |
michael@0 | 3520 | |
michael@0 | 3521 | TAILQ_FOREACH(asconf, &stcb->asoc.control_send_queue, |
michael@0 | 3522 | sctp_next) { |
michael@0 | 3523 | if (asconf->rec.chunk_id.id == SCTP_ASCONF) { |
michael@0 | 3524 | break; |
michael@0 | 3525 | } |
michael@0 | 3526 | } |
michael@0 | 3527 | if (asconf) { |
michael@0 | 3528 | if (asconf->sent != SCTP_DATAGRAM_RESEND) |
michael@0 | 3529 | sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); |
michael@0 | 3530 | asconf->sent = SCTP_DATAGRAM_RESEND; |
michael@0 | 3531 | asconf->snd_count--; |
michael@0 | 3532 | } |
michael@0 | 3533 | } |
michael@0 | 3534 | break; |
michael@0 | 3535 | case SCTP_INITIATION: |
michael@0 | 3536 | /* resend the INIT */ |
michael@0 | 3537 | stcb->asoc.dropped_special_cnt++; |
michael@0 | 3538 | if (stcb->asoc.dropped_special_cnt < SCTP_RETRY_DROPPED_THRESH) { |
michael@0 | 3539 | /* |
michael@0 | 3540 | * If we can get it in, in a few attempts we do |
michael@0 | 3541 | * this, otherwise we let the timer fire. |
michael@0 | 3542 | */ |
michael@0 | 3543 | sctp_timer_stop(SCTP_TIMER_TYPE_INIT, stcb->sctp_ep, |
michael@0 | 3544 | stcb, net, SCTP_FROM_SCTP_INPUT+SCTP_LOC_25); |
michael@0 | 3545 | sctp_send_initiate(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); |
michael@0 | 3546 | } |
michael@0 | 3547 | break; |
michael@0 | 3548 | case SCTP_SELECTIVE_ACK: |
michael@0 | 3549 | case SCTP_NR_SELECTIVE_ACK: |
michael@0 | 3550 | /* resend the sack */ |
michael@0 | 3551 | sctp_send_sack(stcb, SCTP_SO_NOT_LOCKED); |
michael@0 | 3552 | break; |
michael@0 | 3553 | case SCTP_HEARTBEAT_REQUEST: |
michael@0 | 3554 | /* resend a demand HB */ |
michael@0 | 3555 | if ((stcb->asoc.overall_error_count + 3) < stcb->asoc.max_send_times) { |
michael@0 | 3556 | /* Only retransmit if we KNOW we wont destroy the tcb */ |
michael@0 | 3557 | sctp_send_hb(stcb, net, SCTP_SO_NOT_LOCKED); |
michael@0 | 3558 | } |
michael@0 | 3559 | break; |
michael@0 | 3560 | case SCTP_SHUTDOWN: |
michael@0 | 3561 | sctp_send_shutdown(stcb, net); |
michael@0 | 3562 | break; |
michael@0 | 3563 | case SCTP_SHUTDOWN_ACK: |
michael@0 | 3564 | sctp_send_shutdown_ack(stcb, net); |
michael@0 | 3565 | break; |
michael@0 | 3566 | case SCTP_COOKIE_ECHO: |
michael@0 | 3567 | { |
michael@0 | 3568 | struct sctp_tmit_chunk *cookie; |
michael@0 | 3569 | |
michael@0 | 3570 | cookie = NULL; |
michael@0 | 3571 | TAILQ_FOREACH(cookie, &stcb->asoc.control_send_queue, |
michael@0 | 3572 | sctp_next) { |
michael@0 | 3573 | if (cookie->rec.chunk_id.id == SCTP_COOKIE_ECHO) { |
michael@0 | 3574 | break; |
michael@0 | 3575 | } |
michael@0 | 3576 | } |
michael@0 | 3577 | if (cookie) { |
michael@0 | 3578 | if (cookie->sent != SCTP_DATAGRAM_RESEND) |
michael@0 | 3579 | sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); |
michael@0 | 3580 | cookie->sent = SCTP_DATAGRAM_RESEND; |
michael@0 | 3581 | sctp_stop_all_cookie_timers(stcb); |
michael@0 | 3582 | } |
michael@0 | 3583 | } |
michael@0 | 3584 | break; |
michael@0 | 3585 | case SCTP_COOKIE_ACK: |
michael@0 | 3586 | sctp_send_cookie_ack(stcb); |
michael@0 | 3587 | break; |
michael@0 | 3588 | case SCTP_ASCONF_ACK: |
michael@0 | 3589 | /* resend last asconf ack */ |
michael@0 | 3590 | sctp_send_asconf_ack(stcb); |
michael@0 | 3591 | break; |
michael@0 | 3592 | case SCTP_FORWARD_CUM_TSN: |
michael@0 | 3593 | send_forward_tsn(stcb, &stcb->asoc); |
michael@0 | 3594 | break; |
michael@0 | 3595 | /* can't do anything with these */ |
michael@0 | 3596 | case SCTP_PACKET_DROPPED: |
michael@0 | 3597 | case SCTP_INITIATION_ACK: /* this should not happen */ |
michael@0 | 3598 | case SCTP_HEARTBEAT_ACK: |
michael@0 | 3599 | case SCTP_ABORT_ASSOCIATION: |
michael@0 | 3600 | case SCTP_OPERATION_ERROR: |
michael@0 | 3601 | case SCTP_SHUTDOWN_COMPLETE: |
michael@0 | 3602 | case SCTP_ECN_ECHO: |
michael@0 | 3603 | case SCTP_ECN_CWR: |
michael@0 | 3604 | default: |
michael@0 | 3605 | break; |
michael@0 | 3606 | } |
michael@0 | 3607 | return (0); |
michael@0 | 3608 | } |
michael@0 | 3609 | |
michael@0 | 3610 | void |
michael@0 | 3611 | sctp_reset_in_stream(struct sctp_tcb *stcb, uint32_t number_entries, uint16_t *list) |
michael@0 | 3612 | { |
michael@0 | 3613 | uint32_t i; |
michael@0 | 3614 | uint16_t temp; |
michael@0 | 3615 | |
michael@0 | 3616 | /* |
michael@0 | 3617 | * We set things to 0xffff since this is the last delivered sequence |
michael@0 | 3618 | * and we will be sending in 0 after the reset. |
michael@0 | 3619 | */ |
michael@0 | 3620 | |
michael@0 | 3621 | if (number_entries) { |
michael@0 | 3622 | for (i = 0; i < number_entries; i++) { |
michael@0 | 3623 | temp = ntohs(list[i]); |
michael@0 | 3624 | if (temp >= stcb->asoc.streamincnt) { |
michael@0 | 3625 | continue; |
michael@0 | 3626 | } |
michael@0 | 3627 | stcb->asoc.strmin[temp].last_sequence_delivered = 0xffff; |
michael@0 | 3628 | } |
michael@0 | 3629 | } else { |
michael@0 | 3630 | list = NULL; |
michael@0 | 3631 | for (i = 0; i < stcb->asoc.streamincnt; i++) { |
michael@0 | 3632 | stcb->asoc.strmin[i].last_sequence_delivered = 0xffff; |
michael@0 | 3633 | } |
michael@0 | 3634 | } |
michael@0 | 3635 | sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_RECV, stcb, number_entries, (void *)list, SCTP_SO_NOT_LOCKED); |
michael@0 | 3636 | } |
michael@0 | 3637 | |
michael@0 | 3638 | static void |
michael@0 | 3639 | sctp_reset_out_streams(struct sctp_tcb *stcb, uint32_t number_entries, uint16_t *list) |
michael@0 | 3640 | { |
michael@0 | 3641 | uint32_t i; |
michael@0 | 3642 | uint16_t temp; |
michael@0 | 3643 | |
michael@0 | 3644 | if (number_entries > 0) { |
michael@0 | 3645 | for (i = 0; i < number_entries; i++) { |
michael@0 | 3646 | temp = ntohs(list[i]); |
michael@0 | 3647 | if (temp >= stcb->asoc.streamoutcnt) { |
michael@0 | 3648 | /* no such stream */ |
michael@0 | 3649 | continue; |
michael@0 | 3650 | } |
michael@0 | 3651 | stcb->asoc.strmout[temp].next_sequence_send = 0; |
michael@0 | 3652 | } |
michael@0 | 3653 | } else { |
michael@0 | 3654 | for (i = 0; i < stcb->asoc.streamoutcnt; i++) { |
michael@0 | 3655 | stcb->asoc.strmout[i].next_sequence_send = 0; |
michael@0 | 3656 | } |
michael@0 | 3657 | } |
michael@0 | 3658 | sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_SEND, stcb, number_entries, (void *)list, SCTP_SO_NOT_LOCKED); |
michael@0 | 3659 | } |
michael@0 | 3660 | |
michael@0 | 3661 | |
michael@0 | 3662 | struct sctp_stream_reset_out_request * |
michael@0 | 3663 | sctp_find_stream_reset(struct sctp_tcb *stcb, uint32_t seq, struct sctp_tmit_chunk **bchk) |
michael@0 | 3664 | { |
michael@0 | 3665 | struct sctp_association *asoc; |
michael@0 | 3666 | struct sctp_chunkhdr *ch; |
michael@0 | 3667 | struct sctp_stream_reset_out_request *r; |
michael@0 | 3668 | struct sctp_tmit_chunk *chk; |
michael@0 | 3669 | int len, clen; |
michael@0 | 3670 | |
michael@0 | 3671 | asoc = &stcb->asoc; |
michael@0 | 3672 | if (TAILQ_EMPTY(&stcb->asoc.control_send_queue)) { |
michael@0 | 3673 | asoc->stream_reset_outstanding = 0; |
michael@0 | 3674 | return (NULL); |
michael@0 | 3675 | } |
michael@0 | 3676 | if (stcb->asoc.str_reset == NULL) { |
michael@0 | 3677 | asoc->stream_reset_outstanding = 0; |
michael@0 | 3678 | return (NULL); |
michael@0 | 3679 | } |
michael@0 | 3680 | chk = stcb->asoc.str_reset; |
michael@0 | 3681 | if (chk->data == NULL) { |
michael@0 | 3682 | return (NULL); |
michael@0 | 3683 | } |
michael@0 | 3684 | if (bchk) { |
michael@0 | 3685 | /* he wants a copy of the chk pointer */ |
michael@0 | 3686 | *bchk = chk; |
michael@0 | 3687 | } |
michael@0 | 3688 | clen = chk->send_size; |
michael@0 | 3689 | ch = mtod(chk->data, struct sctp_chunkhdr *); |
michael@0 | 3690 | r = (struct sctp_stream_reset_out_request *)(ch + 1); |
michael@0 | 3691 | if (ntohl(r->request_seq) == seq) { |
michael@0 | 3692 | /* found it */ |
michael@0 | 3693 | return (r); |
michael@0 | 3694 | } |
michael@0 | 3695 | len = SCTP_SIZE32(ntohs(r->ph.param_length)); |
michael@0 | 3696 | if (clen > (len + (int)sizeof(struct sctp_chunkhdr))) { |
michael@0 | 3697 | /* move to the next one, there can only be a max of two */ |
michael@0 | 3698 | r = (struct sctp_stream_reset_out_request *)((caddr_t)r + len); |
michael@0 | 3699 | if (ntohl(r->request_seq) == seq) { |
michael@0 | 3700 | return (r); |
michael@0 | 3701 | } |
michael@0 | 3702 | } |
michael@0 | 3703 | /* that seq is not here */ |
michael@0 | 3704 | return (NULL); |
michael@0 | 3705 | } |
michael@0 | 3706 | |
michael@0 | 3707 | static void |
michael@0 | 3708 | sctp_clean_up_stream_reset(struct sctp_tcb *stcb) |
michael@0 | 3709 | { |
michael@0 | 3710 | struct sctp_association *asoc; |
michael@0 | 3711 | struct sctp_tmit_chunk *chk = stcb->asoc.str_reset; |
michael@0 | 3712 | |
michael@0 | 3713 | if (stcb->asoc.str_reset == NULL) { |
michael@0 | 3714 | return; |
michael@0 | 3715 | } |
michael@0 | 3716 | asoc = &stcb->asoc; |
michael@0 | 3717 | |
michael@0 | 3718 | sctp_timer_stop(SCTP_TIMER_TYPE_STRRESET, stcb->sctp_ep, stcb, chk->whoTo, SCTP_FROM_SCTP_INPUT+SCTP_LOC_26); |
michael@0 | 3719 | TAILQ_REMOVE(&asoc->control_send_queue, |
michael@0 | 3720 | chk, |
michael@0 | 3721 | sctp_next); |
michael@0 | 3722 | if (chk->data) { |
michael@0 | 3723 | sctp_m_freem(chk->data); |
michael@0 | 3724 | chk->data = NULL; |
michael@0 | 3725 | } |
michael@0 | 3726 | asoc->ctrl_queue_cnt--; |
michael@0 | 3727 | sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); |
michael@0 | 3728 | /*sa_ignore NO_NULL_CHK*/ |
michael@0 | 3729 | stcb->asoc.str_reset = NULL; |
michael@0 | 3730 | } |
michael@0 | 3731 | |
michael@0 | 3732 | |
michael@0 | 3733 | static int |
michael@0 | 3734 | sctp_handle_stream_reset_response(struct sctp_tcb *stcb, |
michael@0 | 3735 | uint32_t seq, uint32_t action, |
michael@0 | 3736 | struct sctp_stream_reset_response *respin) |
michael@0 | 3737 | { |
michael@0 | 3738 | uint16_t type; |
michael@0 | 3739 | int lparm_len; |
michael@0 | 3740 | struct sctp_association *asoc = &stcb->asoc; |
michael@0 | 3741 | struct sctp_tmit_chunk *chk; |
michael@0 | 3742 | struct sctp_stream_reset_out_request *srparam; |
michael@0 | 3743 | uint32_t number_entries; |
michael@0 | 3744 | |
michael@0 | 3745 | if (asoc->stream_reset_outstanding == 0) { |
michael@0 | 3746 | /* duplicate */ |
michael@0 | 3747 | return (0); |
michael@0 | 3748 | } |
michael@0 | 3749 | if (seq == stcb->asoc.str_reset_seq_out) { |
michael@0 | 3750 | srparam = sctp_find_stream_reset(stcb, seq, &chk); |
michael@0 | 3751 | if (srparam) { |
michael@0 | 3752 | stcb->asoc.str_reset_seq_out++; |
michael@0 | 3753 | type = ntohs(srparam->ph.param_type); |
michael@0 | 3754 | lparm_len = ntohs(srparam->ph.param_length); |
michael@0 | 3755 | if (type == SCTP_STR_RESET_OUT_REQUEST) { |
michael@0 | 3756 | number_entries = (lparm_len - sizeof(struct sctp_stream_reset_out_request)) / sizeof(uint16_t); |
michael@0 | 3757 | asoc->stream_reset_out_is_outstanding = 0; |
michael@0 | 3758 | if (asoc->stream_reset_outstanding) |
michael@0 | 3759 | asoc->stream_reset_outstanding--; |
michael@0 | 3760 | if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) { |
michael@0 | 3761 | /* do it */ |
michael@0 | 3762 | sctp_reset_out_streams(stcb, number_entries, srparam->list_of_streams); |
michael@0 | 3763 | } else if (action == SCTP_STREAM_RESET_RESULT_DENIED) { |
michael@0 | 3764 | sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_DENIED_OUT, stcb, number_entries, srparam->list_of_streams, SCTP_SO_NOT_LOCKED); |
michael@0 | 3765 | } else { |
michael@0 | 3766 | sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_FAILED_OUT, stcb, number_entries, srparam->list_of_streams, SCTP_SO_NOT_LOCKED); |
michael@0 | 3767 | } |
michael@0 | 3768 | } else if (type == SCTP_STR_RESET_IN_REQUEST) { |
michael@0 | 3769 | /* Answered my request */ |
michael@0 | 3770 | number_entries = (lparm_len - sizeof(struct sctp_stream_reset_in_request)) / sizeof(uint16_t); |
michael@0 | 3771 | if (asoc->stream_reset_outstanding) |
michael@0 | 3772 | asoc->stream_reset_outstanding--; |
michael@0 | 3773 | if (action == SCTP_STREAM_RESET_RESULT_DENIED) { |
michael@0 | 3774 | sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_DENIED_IN, stcb, |
michael@0 | 3775 | number_entries, srparam->list_of_streams, SCTP_SO_NOT_LOCKED); |
michael@0 | 3776 | } else if (action != SCTP_STREAM_RESET_RESULT_PERFORMED) { |
michael@0 | 3777 | sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_FAILED_IN, stcb, |
michael@0 | 3778 | number_entries, srparam->list_of_streams, SCTP_SO_NOT_LOCKED); |
michael@0 | 3779 | } |
michael@0 | 3780 | } else if (type == SCTP_STR_RESET_ADD_OUT_STREAMS) { |
michael@0 | 3781 | /* Ok we now may have more streams */ |
michael@0 | 3782 | int num_stream; |
michael@0 | 3783 | |
michael@0 | 3784 | num_stream = stcb->asoc.strm_pending_add_size; |
michael@0 | 3785 | if (num_stream > (stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt)) { |
michael@0 | 3786 | /* TSNH */ |
michael@0 | 3787 | num_stream = stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt; |
michael@0 | 3788 | } |
michael@0 | 3789 | stcb->asoc.strm_pending_add_size = 0; |
michael@0 | 3790 | if (asoc->stream_reset_outstanding) |
michael@0 | 3791 | asoc->stream_reset_outstanding--; |
michael@0 | 3792 | if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) { |
michael@0 | 3793 | /* Put the new streams into effect */ |
michael@0 | 3794 | stcb->asoc.streamoutcnt += num_stream; |
michael@0 | 3795 | sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, 0); |
michael@0 | 3796 | } else if (action == SCTP_STREAM_RESET_RESULT_DENIED) { |
michael@0 | 3797 | sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, |
michael@0 | 3798 | SCTP_STREAM_CHANGE_DENIED); |
michael@0 | 3799 | } else { |
michael@0 | 3800 | sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, |
michael@0 | 3801 | SCTP_STREAM_CHANGE_FAILED); |
michael@0 | 3802 | } |
michael@0 | 3803 | } else if (type == SCTP_STR_RESET_ADD_IN_STREAMS) { |
michael@0 | 3804 | if (asoc->stream_reset_outstanding) |
michael@0 | 3805 | asoc->stream_reset_outstanding--; |
michael@0 | 3806 | if (action == SCTP_STREAM_RESET_RESULT_DENIED) { |
michael@0 | 3807 | sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, |
michael@0 | 3808 | SCTP_STREAM_CHANGE_DENIED); |
michael@0 | 3809 | } else if (action != SCTP_STREAM_RESET_RESULT_PERFORMED) { |
michael@0 | 3810 | sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, |
michael@0 | 3811 | SCTP_STREAM_CHANGE_FAILED); |
michael@0 | 3812 | } |
michael@0 | 3813 | } else if (type == SCTP_STR_RESET_TSN_REQUEST) { |
michael@0 | 3814 | /** |
michael@0 | 3815 | * a) Adopt the new in tsn. |
michael@0 | 3816 | * b) reset the map |
michael@0 | 3817 | * c) Adopt the new out-tsn |
michael@0 | 3818 | */ |
michael@0 | 3819 | struct sctp_stream_reset_response_tsn *resp; |
michael@0 | 3820 | struct sctp_forward_tsn_chunk fwdtsn; |
michael@0 | 3821 | int abort_flag = 0; |
michael@0 | 3822 | if (respin == NULL) { |
michael@0 | 3823 | /* huh ? */ |
michael@0 | 3824 | return (0); |
michael@0 | 3825 | } |
michael@0 | 3826 | if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) { |
michael@0 | 3827 | resp = (struct sctp_stream_reset_response_tsn *)respin; |
michael@0 | 3828 | asoc->stream_reset_outstanding--; |
michael@0 | 3829 | fwdtsn.ch.chunk_length = htons(sizeof(struct sctp_forward_tsn_chunk)); |
michael@0 | 3830 | fwdtsn.ch.chunk_type = SCTP_FORWARD_CUM_TSN; |
michael@0 | 3831 | fwdtsn.new_cumulative_tsn = htonl(ntohl(resp->senders_next_tsn) - 1); |
michael@0 | 3832 | sctp_handle_forward_tsn(stcb, &fwdtsn, &abort_flag, NULL, 0); |
michael@0 | 3833 | if (abort_flag) { |
michael@0 | 3834 | return (1); |
michael@0 | 3835 | } |
michael@0 | 3836 | stcb->asoc.highest_tsn_inside_map = (ntohl(resp->senders_next_tsn) - 1); |
michael@0 | 3837 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { |
michael@0 | 3838 | sctp_log_map(0, 7, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT); |
michael@0 | 3839 | } |
michael@0 | 3840 | |
michael@0 | 3841 | stcb->asoc.tsn_last_delivered = stcb->asoc.cumulative_tsn = stcb->asoc.highest_tsn_inside_map; |
michael@0 | 3842 | stcb->asoc.mapping_array_base_tsn = ntohl(resp->senders_next_tsn); |
michael@0 | 3843 | memset(stcb->asoc.mapping_array, 0, stcb->asoc.mapping_array_size); |
michael@0 | 3844 | |
michael@0 | 3845 | stcb->asoc.highest_tsn_inside_nr_map = stcb->asoc.highest_tsn_inside_map; |
michael@0 | 3846 | memset(stcb->asoc.nr_mapping_array, 0, stcb->asoc.mapping_array_size); |
michael@0 | 3847 | |
michael@0 | 3848 | stcb->asoc.sending_seq = ntohl(resp->receivers_next_tsn); |
michael@0 | 3849 | stcb->asoc.last_acked_seq = stcb->asoc.cumulative_tsn; |
michael@0 | 3850 | |
michael@0 | 3851 | sctp_reset_out_streams(stcb, 0, (uint16_t *) NULL); |
michael@0 | 3852 | sctp_reset_in_stream(stcb, 0, (uint16_t *) NULL); |
michael@0 | 3853 | sctp_notify_stream_reset_tsn(stcb, stcb->asoc.sending_seq, (stcb->asoc.mapping_array_base_tsn + 1), 0); |
michael@0 | 3854 | } else if (action == SCTP_STREAM_RESET_RESULT_DENIED) { |
michael@0 | 3855 | sctp_notify_stream_reset_tsn(stcb, stcb->asoc.sending_seq, (stcb->asoc.mapping_array_base_tsn + 1), |
michael@0 | 3856 | SCTP_ASSOC_RESET_DENIED); |
michael@0 | 3857 | } else { |
michael@0 | 3858 | sctp_notify_stream_reset_tsn(stcb, stcb->asoc.sending_seq, (stcb->asoc.mapping_array_base_tsn + 1), |
michael@0 | 3859 | SCTP_ASSOC_RESET_FAILED); |
michael@0 | 3860 | } |
michael@0 | 3861 | } |
michael@0 | 3862 | /* get rid of the request and get the request flags */ |
michael@0 | 3863 | if (asoc->stream_reset_outstanding == 0) { |
michael@0 | 3864 | sctp_clean_up_stream_reset(stcb); |
michael@0 | 3865 | } |
michael@0 | 3866 | } |
michael@0 | 3867 | } |
michael@0 | 3868 | return (0); |
michael@0 | 3869 | } |
michael@0 | 3870 | |
michael@0 | 3871 | static void |
michael@0 | 3872 | sctp_handle_str_reset_request_in(struct sctp_tcb *stcb, |
michael@0 | 3873 | struct sctp_tmit_chunk *chk, |
michael@0 | 3874 | struct sctp_stream_reset_in_request *req, int trunc) |
michael@0 | 3875 | { |
michael@0 | 3876 | uint32_t seq; |
michael@0 | 3877 | int len, i; |
michael@0 | 3878 | int number_entries; |
michael@0 | 3879 | uint16_t temp; |
michael@0 | 3880 | |
michael@0 | 3881 | /* |
michael@0 | 3882 | * peer wants me to send a str-reset to him for my outgoing seq's if |
michael@0 | 3883 | * seq_in is right. |
michael@0 | 3884 | */ |
michael@0 | 3885 | struct sctp_association *asoc = &stcb->asoc; |
michael@0 | 3886 | |
michael@0 | 3887 | seq = ntohl(req->request_seq); |
michael@0 | 3888 | if (asoc->str_reset_seq_in == seq) { |
michael@0 | 3889 | asoc->last_reset_action[1] = asoc->last_reset_action[0]; |
michael@0 | 3890 | if (!(asoc->local_strreset_support & SCTP_ENABLE_RESET_STREAM_REQ)) { |
michael@0 | 3891 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 3892 | } else if (trunc) { |
michael@0 | 3893 | /* Can't do it, since they exceeded our buffer size */ |
michael@0 | 3894 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 3895 | } else if (stcb->asoc.stream_reset_out_is_outstanding == 0) { |
michael@0 | 3896 | len = ntohs(req->ph.param_length); |
michael@0 | 3897 | number_entries = ((len - sizeof(struct sctp_stream_reset_in_request)) / sizeof(uint16_t)); |
michael@0 | 3898 | for (i = 0; i < number_entries; i++) { |
michael@0 | 3899 | temp = ntohs(req->list_of_streams[i]); |
michael@0 | 3900 | req->list_of_streams[i] = temp; |
michael@0 | 3901 | } |
michael@0 | 3902 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_PERFORMED; |
michael@0 | 3903 | sctp_add_stream_reset_out(chk, number_entries, req->list_of_streams, |
michael@0 | 3904 | asoc->str_reset_seq_out, |
michael@0 | 3905 | seq, (asoc->sending_seq - 1)); |
michael@0 | 3906 | asoc->stream_reset_out_is_outstanding = 1; |
michael@0 | 3907 | asoc->str_reset = chk; |
michael@0 | 3908 | sctp_timer_start(SCTP_TIMER_TYPE_STRRESET, stcb->sctp_ep, stcb, chk->whoTo); |
michael@0 | 3909 | stcb->asoc.stream_reset_outstanding++; |
michael@0 | 3910 | } else { |
michael@0 | 3911 | /* Can't do it, since we have sent one out */ |
michael@0 | 3912 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_ERR_IN_PROGRESS; |
michael@0 | 3913 | } |
michael@0 | 3914 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 3915 | asoc->str_reset_seq_in++; |
michael@0 | 3916 | } else if (asoc->str_reset_seq_in - 1 == seq) { |
michael@0 | 3917 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 3918 | } else if (asoc->str_reset_seq_in - 2 == seq) { |
michael@0 | 3919 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[1]); |
michael@0 | 3920 | } else { |
michael@0 | 3921 | sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO); |
michael@0 | 3922 | } |
michael@0 | 3923 | } |
michael@0 | 3924 | |
michael@0 | 3925 | static int |
michael@0 | 3926 | sctp_handle_str_reset_request_tsn(struct sctp_tcb *stcb, |
michael@0 | 3927 | struct sctp_tmit_chunk *chk, |
michael@0 | 3928 | struct sctp_stream_reset_tsn_request *req) |
michael@0 | 3929 | { |
michael@0 | 3930 | /* reset all in and out and update the tsn */ |
michael@0 | 3931 | /* |
michael@0 | 3932 | * A) reset my str-seq's on in and out. B) Select a receive next, |
michael@0 | 3933 | * and set cum-ack to it. Also process this selected number as a |
michael@0 | 3934 | * fwd-tsn as well. C) set in the response my next sending seq. |
michael@0 | 3935 | */ |
michael@0 | 3936 | struct sctp_forward_tsn_chunk fwdtsn; |
michael@0 | 3937 | struct sctp_association *asoc = &stcb->asoc; |
michael@0 | 3938 | int abort_flag = 0; |
michael@0 | 3939 | uint32_t seq; |
michael@0 | 3940 | |
michael@0 | 3941 | seq = ntohl(req->request_seq); |
michael@0 | 3942 | if (asoc->str_reset_seq_in == seq) { |
michael@0 | 3943 | asoc->last_reset_action[1] = stcb->asoc.last_reset_action[0]; |
michael@0 | 3944 | if (!(asoc->local_strreset_support & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { |
michael@0 | 3945 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 3946 | } else { |
michael@0 | 3947 | fwdtsn.ch.chunk_length = htons(sizeof(struct sctp_forward_tsn_chunk)); |
michael@0 | 3948 | fwdtsn.ch.chunk_type = SCTP_FORWARD_CUM_TSN; |
michael@0 | 3949 | fwdtsn.ch.chunk_flags = 0; |
michael@0 | 3950 | fwdtsn.new_cumulative_tsn = htonl(stcb->asoc.highest_tsn_inside_map + 1); |
michael@0 | 3951 | sctp_handle_forward_tsn(stcb, &fwdtsn, &abort_flag, NULL, 0); |
michael@0 | 3952 | if (abort_flag) { |
michael@0 | 3953 | return (1); |
michael@0 | 3954 | } |
michael@0 | 3955 | asoc->highest_tsn_inside_map += SCTP_STREAM_RESET_TSN_DELTA; |
michael@0 | 3956 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { |
michael@0 | 3957 | sctp_log_map(0, 10, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT); |
michael@0 | 3958 | } |
michael@0 | 3959 | asoc->tsn_last_delivered = asoc->cumulative_tsn = asoc->highest_tsn_inside_map; |
michael@0 | 3960 | asoc->mapping_array_base_tsn = asoc->highest_tsn_inside_map + 1; |
michael@0 | 3961 | memset(asoc->mapping_array, 0, asoc->mapping_array_size); |
michael@0 | 3962 | asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map; |
michael@0 | 3963 | memset(asoc->nr_mapping_array, 0, asoc->mapping_array_size); |
michael@0 | 3964 | atomic_add_int(&asoc->sending_seq, 1); |
michael@0 | 3965 | /* save off historical data for retrans */ |
michael@0 | 3966 | asoc->last_sending_seq[1] = asoc->last_sending_seq[0]; |
michael@0 | 3967 | asoc->last_sending_seq[0] = asoc->sending_seq; |
michael@0 | 3968 | asoc->last_base_tsnsent[1] = asoc->last_base_tsnsent[0]; |
michael@0 | 3969 | asoc->last_base_tsnsent[0] = asoc->mapping_array_base_tsn; |
michael@0 | 3970 | sctp_reset_out_streams(stcb, 0, (uint16_t *) NULL); |
michael@0 | 3971 | sctp_reset_in_stream(stcb, 0, (uint16_t *) NULL); |
michael@0 | 3972 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_PERFORMED; |
michael@0 | 3973 | sctp_notify_stream_reset_tsn(stcb, asoc->sending_seq, (asoc->mapping_array_base_tsn + 1), 0); |
michael@0 | 3974 | } |
michael@0 | 3975 | sctp_add_stream_reset_result_tsn(chk, seq, asoc->last_reset_action[0], |
michael@0 | 3976 | asoc->last_sending_seq[0], asoc->last_base_tsnsent[0]); |
michael@0 | 3977 | asoc->str_reset_seq_in++; |
michael@0 | 3978 | } else if (asoc->str_reset_seq_in - 1 == seq) { |
michael@0 | 3979 | sctp_add_stream_reset_result_tsn(chk, seq, asoc->last_reset_action[0], |
michael@0 | 3980 | asoc->last_sending_seq[0], asoc->last_base_tsnsent[0]); |
michael@0 | 3981 | } else if (asoc->str_reset_seq_in - 2 == seq) { |
michael@0 | 3982 | sctp_add_stream_reset_result_tsn(chk, seq, asoc->last_reset_action[1], |
michael@0 | 3983 | asoc->last_sending_seq[1], asoc->last_base_tsnsent[1]); |
michael@0 | 3984 | } else { |
michael@0 | 3985 | sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO); |
michael@0 | 3986 | } |
michael@0 | 3987 | return (0); |
michael@0 | 3988 | } |
michael@0 | 3989 | |
michael@0 | 3990 | static void |
michael@0 | 3991 | sctp_handle_str_reset_request_out(struct sctp_tcb *stcb, |
michael@0 | 3992 | struct sctp_tmit_chunk *chk, |
michael@0 | 3993 | struct sctp_stream_reset_out_request *req, int trunc) |
michael@0 | 3994 | { |
michael@0 | 3995 | uint32_t seq, tsn; |
michael@0 | 3996 | int number_entries, len; |
michael@0 | 3997 | struct sctp_association *asoc = &stcb->asoc; |
michael@0 | 3998 | |
michael@0 | 3999 | seq = ntohl(req->request_seq); |
michael@0 | 4000 | |
michael@0 | 4001 | /* now if its not a duplicate we process it */ |
michael@0 | 4002 | if (asoc->str_reset_seq_in == seq) { |
michael@0 | 4003 | len = ntohs(req->ph.param_length); |
michael@0 | 4004 | number_entries = ((len - sizeof(struct sctp_stream_reset_out_request)) / sizeof(uint16_t)); |
michael@0 | 4005 | /* |
michael@0 | 4006 | * the sender is resetting, handle the list issue.. we must |
michael@0 | 4007 | * a) verify if we can do the reset, if so no problem b) If |
michael@0 | 4008 | * we can't do the reset we must copy the request. c) queue |
michael@0 | 4009 | * it, and setup the data in processor to trigger it off |
michael@0 | 4010 | * when needed and dequeue all the queued data. |
michael@0 | 4011 | */ |
michael@0 | 4012 | tsn = ntohl(req->send_reset_at_tsn); |
michael@0 | 4013 | |
michael@0 | 4014 | /* move the reset action back one */ |
michael@0 | 4015 | asoc->last_reset_action[1] = asoc->last_reset_action[0]; |
michael@0 | 4016 | if (!(asoc->local_strreset_support & SCTP_ENABLE_RESET_STREAM_REQ)) { |
michael@0 | 4017 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4018 | } else if (trunc) { |
michael@0 | 4019 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4020 | } else if (SCTP_TSN_GE(asoc->cumulative_tsn, tsn)) { |
michael@0 | 4021 | /* we can do it now */ |
michael@0 | 4022 | sctp_reset_in_stream(stcb, number_entries, req->list_of_streams); |
michael@0 | 4023 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_PERFORMED; |
michael@0 | 4024 | } else { |
michael@0 | 4025 | /* |
michael@0 | 4026 | * we must queue it up and thus wait for the TSN's |
michael@0 | 4027 | * to arrive that are at or before tsn |
michael@0 | 4028 | */ |
michael@0 | 4029 | struct sctp_stream_reset_list *liste; |
michael@0 | 4030 | int siz; |
michael@0 | 4031 | |
michael@0 | 4032 | siz = sizeof(struct sctp_stream_reset_list) + (number_entries * sizeof(uint16_t)); |
michael@0 | 4033 | SCTP_MALLOC(liste, struct sctp_stream_reset_list *, |
michael@0 | 4034 | siz, SCTP_M_STRESET); |
michael@0 | 4035 | if (liste == NULL) { |
michael@0 | 4036 | /* gak out of memory */ |
michael@0 | 4037 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4038 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 4039 | return; |
michael@0 | 4040 | } |
michael@0 | 4041 | liste->tsn = tsn; |
michael@0 | 4042 | liste->number_entries = number_entries; |
michael@0 | 4043 | memcpy(&liste->list_of_streams, req->list_of_streams, number_entries * sizeof(uint16_t)); |
michael@0 | 4044 | TAILQ_INSERT_TAIL(&asoc->resetHead, liste, next_resp); |
michael@0 | 4045 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_PERFORMED; |
michael@0 | 4046 | } |
michael@0 | 4047 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 4048 | asoc->str_reset_seq_in++; |
michael@0 | 4049 | } else if ((asoc->str_reset_seq_in - 1) == seq) { |
michael@0 | 4050 | /* |
michael@0 | 4051 | * one seq back, just echo back last action since my |
michael@0 | 4052 | * response was lost. |
michael@0 | 4053 | */ |
michael@0 | 4054 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 4055 | } else if ((asoc->str_reset_seq_in - 2) == seq) { |
michael@0 | 4056 | /* |
michael@0 | 4057 | * two seq back, just echo back last action since my |
michael@0 | 4058 | * response was lost. |
michael@0 | 4059 | */ |
michael@0 | 4060 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[1]); |
michael@0 | 4061 | } else { |
michael@0 | 4062 | sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO); |
michael@0 | 4063 | } |
michael@0 | 4064 | } |
michael@0 | 4065 | |
michael@0 | 4066 | static void |
michael@0 | 4067 | sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk, |
michael@0 | 4068 | struct sctp_stream_reset_add_strm *str_add) |
michael@0 | 4069 | { |
michael@0 | 4070 | /* |
michael@0 | 4071 | * Peer is requesting to add more streams. |
michael@0 | 4072 | * If its within our max-streams we will |
michael@0 | 4073 | * allow it. |
michael@0 | 4074 | */ |
michael@0 | 4075 | uint32_t num_stream, i; |
michael@0 | 4076 | uint32_t seq; |
michael@0 | 4077 | struct sctp_association *asoc = &stcb->asoc; |
michael@0 | 4078 | struct sctp_queued_to_read *ctl, *nctl; |
michael@0 | 4079 | |
michael@0 | 4080 | /* Get the number. */ |
michael@0 | 4081 | seq = ntohl(str_add->request_seq); |
michael@0 | 4082 | num_stream = ntohs(str_add->number_of_streams); |
michael@0 | 4083 | /* Now what would be the new total? */ |
michael@0 | 4084 | if (asoc->str_reset_seq_in == seq) { |
michael@0 | 4085 | num_stream += stcb->asoc.streamincnt; |
michael@0 | 4086 | stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; |
michael@0 | 4087 | if (!(asoc->local_strreset_support & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { |
michael@0 | 4088 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4089 | } else if ((num_stream > stcb->asoc.max_inbound_streams) || |
michael@0 | 4090 | (num_stream > 0xffff)) { |
michael@0 | 4091 | /* We must reject it they ask for to many */ |
michael@0 | 4092 | denied: |
michael@0 | 4093 | stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4094 | } else { |
michael@0 | 4095 | /* Ok, we can do that :-) */ |
michael@0 | 4096 | struct sctp_stream_in *oldstrm; |
michael@0 | 4097 | |
michael@0 | 4098 | /* save off the old */ |
michael@0 | 4099 | oldstrm = stcb->asoc.strmin; |
michael@0 | 4100 | SCTP_MALLOC(stcb->asoc.strmin, struct sctp_stream_in *, |
michael@0 | 4101 | (num_stream * sizeof(struct sctp_stream_in)), |
michael@0 | 4102 | SCTP_M_STRMI); |
michael@0 | 4103 | if (stcb->asoc.strmin == NULL) { |
michael@0 | 4104 | stcb->asoc.strmin = oldstrm; |
michael@0 | 4105 | goto denied; |
michael@0 | 4106 | } |
michael@0 | 4107 | /* copy off the old data */ |
michael@0 | 4108 | for (i = 0; i < stcb->asoc.streamincnt; i++) { |
michael@0 | 4109 | TAILQ_INIT(&stcb->asoc.strmin[i].inqueue); |
michael@0 | 4110 | stcb->asoc.strmin[i].stream_no = i; |
michael@0 | 4111 | stcb->asoc.strmin[i].last_sequence_delivered = oldstrm[i].last_sequence_delivered; |
michael@0 | 4112 | stcb->asoc.strmin[i].delivery_started = oldstrm[i].delivery_started; |
michael@0 | 4113 | /* now anything on those queues? */ |
michael@0 | 4114 | TAILQ_FOREACH_SAFE(ctl, &oldstrm[i].inqueue, next, nctl) { |
michael@0 | 4115 | TAILQ_REMOVE(&oldstrm[i].inqueue, ctl, next); |
michael@0 | 4116 | TAILQ_INSERT_TAIL(&stcb->asoc.strmin[i].inqueue, ctl, next); |
michael@0 | 4117 | } |
michael@0 | 4118 | } |
michael@0 | 4119 | /* Init the new streams */ |
michael@0 | 4120 | for (i = stcb->asoc.streamincnt; i < num_stream; i++) { |
michael@0 | 4121 | TAILQ_INIT(&stcb->asoc.strmin[i].inqueue); |
michael@0 | 4122 | stcb->asoc.strmin[i].stream_no = i; |
michael@0 | 4123 | stcb->asoc.strmin[i].last_sequence_delivered = 0xffff; |
michael@0 | 4124 | stcb->asoc.strmin[i].delivery_started = 0; |
michael@0 | 4125 | } |
michael@0 | 4126 | SCTP_FREE(oldstrm, SCTP_M_STRMI); |
michael@0 | 4127 | /* update the size */ |
michael@0 | 4128 | stcb->asoc.streamincnt = num_stream; |
michael@0 | 4129 | stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_PERFORMED; |
michael@0 | 4130 | sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, 0); |
michael@0 | 4131 | } |
michael@0 | 4132 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 4133 | asoc->str_reset_seq_in++; |
michael@0 | 4134 | } else if ((asoc->str_reset_seq_in - 1) == seq) { |
michael@0 | 4135 | /* |
michael@0 | 4136 | * one seq back, just echo back last action since my |
michael@0 | 4137 | * response was lost. |
michael@0 | 4138 | */ |
michael@0 | 4139 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 4140 | } else if ((asoc->str_reset_seq_in - 2) == seq) { |
michael@0 | 4141 | /* |
michael@0 | 4142 | * two seq back, just echo back last action since my |
michael@0 | 4143 | * response was lost. |
michael@0 | 4144 | */ |
michael@0 | 4145 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[1]); |
michael@0 | 4146 | } else { |
michael@0 | 4147 | sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO); |
michael@0 | 4148 | |
michael@0 | 4149 | } |
michael@0 | 4150 | } |
michael@0 | 4151 | |
michael@0 | 4152 | static void |
michael@0 | 4153 | sctp_handle_str_reset_add_out_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk, |
michael@0 | 4154 | struct sctp_stream_reset_add_strm *str_add) |
michael@0 | 4155 | { |
michael@0 | 4156 | /* |
michael@0 | 4157 | * Peer is requesting to add more streams. |
michael@0 | 4158 | * If its within our max-streams we will |
michael@0 | 4159 | * allow it. |
michael@0 | 4160 | */ |
michael@0 | 4161 | uint16_t num_stream; |
michael@0 | 4162 | uint32_t seq; |
michael@0 | 4163 | struct sctp_association *asoc = &stcb->asoc; |
michael@0 | 4164 | |
michael@0 | 4165 | /* Get the number. */ |
michael@0 | 4166 | seq = ntohl(str_add->request_seq); |
michael@0 | 4167 | num_stream = ntohs(str_add->number_of_streams); |
michael@0 | 4168 | /* Now what would be the new total? */ |
michael@0 | 4169 | if (asoc->str_reset_seq_in == seq) { |
michael@0 | 4170 | stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; |
michael@0 | 4171 | if (!(asoc->local_strreset_support & SCTP_ENABLE_CHANGE_ASSOC_REQ)) { |
michael@0 | 4172 | asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4173 | } else if (stcb->asoc.stream_reset_outstanding) { |
michael@0 | 4174 | /* We must reject it we have something pending */ |
michael@0 | 4175 | stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_ERR_IN_PROGRESS; |
michael@0 | 4176 | } else { |
michael@0 | 4177 | /* Ok, we can do that :-) */ |
michael@0 | 4178 | int mychk; |
michael@0 | 4179 | mychk = stcb->asoc.streamoutcnt; |
michael@0 | 4180 | mychk += num_stream; |
michael@0 | 4181 | if (mychk < 0x10000) { |
michael@0 | 4182 | stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_PERFORMED; |
michael@0 | 4183 | if (sctp_send_str_reset_req(stcb, 0, NULL, 0, 0, 0, 1, num_stream, 0, 1)) { |
michael@0 | 4184 | stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4185 | } |
michael@0 | 4186 | } else { |
michael@0 | 4187 | stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED; |
michael@0 | 4188 | } |
michael@0 | 4189 | } |
michael@0 | 4190 | sctp_add_stream_reset_result(chk, seq, stcb->asoc.last_reset_action[0]); |
michael@0 | 4191 | asoc->str_reset_seq_in++; |
michael@0 | 4192 | } else if ((asoc->str_reset_seq_in - 1) == seq) { |
michael@0 | 4193 | /* |
michael@0 | 4194 | * one seq back, just echo back last action since my |
michael@0 | 4195 | * response was lost. |
michael@0 | 4196 | */ |
michael@0 | 4197 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); |
michael@0 | 4198 | } else if ((asoc->str_reset_seq_in - 2) == seq) { |
michael@0 | 4199 | /* |
michael@0 | 4200 | * two seq back, just echo back last action since my |
michael@0 | 4201 | * response was lost. |
michael@0 | 4202 | */ |
michael@0 | 4203 | sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[1]); |
michael@0 | 4204 | } else { |
michael@0 | 4205 | sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO); |
michael@0 | 4206 | } |
michael@0 | 4207 | } |
michael@0 | 4208 | |
michael@0 | 4209 | #if !defined(__Panda__) |
michael@0 | 4210 | #ifdef __GNUC__ |
michael@0 | 4211 | __attribute__ ((noinline)) |
michael@0 | 4212 | #endif |
michael@0 | 4213 | #endif |
michael@0 | 4214 | static int |
michael@0 | 4215 | sctp_handle_stream_reset(struct sctp_tcb *stcb, struct mbuf *m, int offset, |
michael@0 | 4216 | struct sctp_chunkhdr *ch_req) |
michael@0 | 4217 | { |
michael@0 | 4218 | int chk_length, param_len, ptype; |
michael@0 | 4219 | struct sctp_paramhdr pstore; |
michael@0 | 4220 | uint8_t cstore[SCTP_CHUNK_BUFFER_SIZE]; |
michael@0 | 4221 | uint32_t seq = 0; |
michael@0 | 4222 | int num_req = 0; |
michael@0 | 4223 | int trunc = 0; |
michael@0 | 4224 | struct sctp_tmit_chunk *chk; |
michael@0 | 4225 | struct sctp_chunkhdr *ch; |
michael@0 | 4226 | struct sctp_paramhdr *ph; |
michael@0 | 4227 | int ret_code = 0; |
michael@0 | 4228 | int num_param = 0; |
michael@0 | 4229 | |
michael@0 | 4230 | /* now it may be a reset or a reset-response */ |
michael@0 | 4231 | chk_length = ntohs(ch_req->chunk_length); |
michael@0 | 4232 | |
michael@0 | 4233 | /* setup for adding the response */ |
michael@0 | 4234 | sctp_alloc_a_chunk(stcb, chk); |
michael@0 | 4235 | if (chk == NULL) { |
michael@0 | 4236 | return (ret_code); |
michael@0 | 4237 | } |
michael@0 | 4238 | chk->rec.chunk_id.id = SCTP_STREAM_RESET; |
michael@0 | 4239 | chk->rec.chunk_id.can_take_data = 0; |
michael@0 | 4240 | chk->asoc = &stcb->asoc; |
michael@0 | 4241 | chk->no_fr_allowed = 0; |
michael@0 | 4242 | chk->book_size = chk->send_size = sizeof(struct sctp_chunkhdr); |
michael@0 | 4243 | chk->book_size_scale = 0; |
michael@0 | 4244 | chk->data = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_NOWAIT, 1, MT_DATA); |
michael@0 | 4245 | if (chk->data == NULL) { |
michael@0 | 4246 | strres_nochunk: |
michael@0 | 4247 | if (chk->data) { |
michael@0 | 4248 | sctp_m_freem(chk->data); |
michael@0 | 4249 | chk->data = NULL; |
michael@0 | 4250 | } |
michael@0 | 4251 | sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED); |
michael@0 | 4252 | return (ret_code); |
michael@0 | 4253 | } |
michael@0 | 4254 | SCTP_BUF_RESV_UF(chk->data, SCTP_MIN_OVERHEAD); |
michael@0 | 4255 | |
michael@0 | 4256 | /* setup chunk parameters */ |
michael@0 | 4257 | chk->sent = SCTP_DATAGRAM_UNSENT; |
michael@0 | 4258 | chk->snd_count = 0; |
michael@0 | 4259 | chk->whoTo = NULL; |
michael@0 | 4260 | |
michael@0 | 4261 | ch = mtod(chk->data, struct sctp_chunkhdr *); |
michael@0 | 4262 | ch->chunk_type = SCTP_STREAM_RESET; |
michael@0 | 4263 | ch->chunk_flags = 0; |
michael@0 | 4264 | ch->chunk_length = htons(chk->send_size); |
michael@0 | 4265 | SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size); |
michael@0 | 4266 | offset += sizeof(struct sctp_chunkhdr); |
michael@0 | 4267 | while ((size_t)chk_length >= sizeof(struct sctp_stream_reset_tsn_request)) { |
michael@0 | 4268 | ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(pstore), (uint8_t *)&pstore); |
michael@0 | 4269 | if (ph == NULL) |
michael@0 | 4270 | break; |
michael@0 | 4271 | param_len = ntohs(ph->param_length); |
michael@0 | 4272 | if (param_len < (int)sizeof(struct sctp_stream_reset_tsn_request)) { |
michael@0 | 4273 | /* bad param */ |
michael@0 | 4274 | break; |
michael@0 | 4275 | } |
michael@0 | 4276 | ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, min(param_len, (int)sizeof(cstore)), |
michael@0 | 4277 | (uint8_t *)&cstore); |
michael@0 | 4278 | ptype = ntohs(ph->param_type); |
michael@0 | 4279 | num_param++; |
michael@0 | 4280 | if (param_len > (int)sizeof(cstore)) { |
michael@0 | 4281 | trunc = 1; |
michael@0 | 4282 | } else { |
michael@0 | 4283 | trunc = 0; |
michael@0 | 4284 | } |
michael@0 | 4285 | if (num_param > SCTP_MAX_RESET_PARAMS) { |
michael@0 | 4286 | /* hit the max of parameters already sorry.. */ |
michael@0 | 4287 | break; |
michael@0 | 4288 | } |
michael@0 | 4289 | if (ptype == SCTP_STR_RESET_OUT_REQUEST) { |
michael@0 | 4290 | struct sctp_stream_reset_out_request *req_out; |
michael@0 | 4291 | req_out = (struct sctp_stream_reset_out_request *)ph; |
michael@0 | 4292 | num_req++; |
michael@0 | 4293 | if (stcb->asoc.stream_reset_outstanding) { |
michael@0 | 4294 | seq = ntohl(req_out->response_seq); |
michael@0 | 4295 | if (seq == stcb->asoc.str_reset_seq_out) { |
michael@0 | 4296 | /* implicit ack */ |
michael@0 | 4297 | (void)sctp_handle_stream_reset_response(stcb, seq, SCTP_STREAM_RESET_RESULT_PERFORMED, NULL); |
michael@0 | 4298 | } |
michael@0 | 4299 | } |
michael@0 | 4300 | sctp_handle_str_reset_request_out(stcb, chk, req_out, trunc); |
michael@0 | 4301 | } else if (ptype == SCTP_STR_RESET_ADD_OUT_STREAMS) { |
michael@0 | 4302 | struct sctp_stream_reset_add_strm *str_add; |
michael@0 | 4303 | str_add = (struct sctp_stream_reset_add_strm *)ph; |
michael@0 | 4304 | num_req++; |
michael@0 | 4305 | sctp_handle_str_reset_add_strm(stcb, chk, str_add); |
michael@0 | 4306 | } else if (ptype == SCTP_STR_RESET_ADD_IN_STREAMS) { |
michael@0 | 4307 | struct sctp_stream_reset_add_strm *str_add; |
michael@0 | 4308 | str_add = (struct sctp_stream_reset_add_strm *)ph; |
michael@0 | 4309 | num_req++; |
michael@0 | 4310 | sctp_handle_str_reset_add_out_strm(stcb, chk, str_add); |
michael@0 | 4311 | } else if (ptype == SCTP_STR_RESET_IN_REQUEST) { |
michael@0 | 4312 | struct sctp_stream_reset_in_request *req_in; |
michael@0 | 4313 | num_req++; |
michael@0 | 4314 | req_in = (struct sctp_stream_reset_in_request *)ph; |
michael@0 | 4315 | sctp_handle_str_reset_request_in(stcb, chk, req_in, trunc); |
michael@0 | 4316 | } else if (ptype == SCTP_STR_RESET_TSN_REQUEST) { |
michael@0 | 4317 | struct sctp_stream_reset_tsn_request *req_tsn; |
michael@0 | 4318 | num_req++; |
michael@0 | 4319 | req_tsn = (struct sctp_stream_reset_tsn_request *)ph; |
michael@0 | 4320 | if (sctp_handle_str_reset_request_tsn(stcb, chk, req_tsn)) { |
michael@0 | 4321 | ret_code = 1; |
michael@0 | 4322 | goto strres_nochunk; |
michael@0 | 4323 | } |
michael@0 | 4324 | /* no more */ |
michael@0 | 4325 | break; |
michael@0 | 4326 | } else if (ptype == SCTP_STR_RESET_RESPONSE) { |
michael@0 | 4327 | struct sctp_stream_reset_response *resp; |
michael@0 | 4328 | uint32_t result; |
michael@0 | 4329 | resp = (struct sctp_stream_reset_response *)ph; |
michael@0 | 4330 | seq = ntohl(resp->response_seq); |
michael@0 | 4331 | result = ntohl(resp->result); |
michael@0 | 4332 | if (sctp_handle_stream_reset_response(stcb, seq, result, resp)) { |
michael@0 | 4333 | ret_code = 1; |
michael@0 | 4334 | goto strres_nochunk; |
michael@0 | 4335 | } |
michael@0 | 4336 | } else { |
michael@0 | 4337 | break; |
michael@0 | 4338 | } |
michael@0 | 4339 | offset += SCTP_SIZE32(param_len); |
michael@0 | 4340 | chk_length -= SCTP_SIZE32(param_len); |
michael@0 | 4341 | } |
michael@0 | 4342 | if (num_req == 0) { |
michael@0 | 4343 | /* we have no response free the stuff */ |
michael@0 | 4344 | goto strres_nochunk; |
michael@0 | 4345 | } |
michael@0 | 4346 | /* ok we have a chunk to link in */ |
michael@0 | 4347 | TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, |
michael@0 | 4348 | chk, |
michael@0 | 4349 | sctp_next); |
michael@0 | 4350 | stcb->asoc.ctrl_queue_cnt++; |
michael@0 | 4351 | return (ret_code); |
michael@0 | 4352 | } |
michael@0 | 4353 | |
michael@0 | 4354 | /* |
michael@0 | 4355 | * Handle a router or endpoints report of a packet loss, there are two ways |
michael@0 | 4356 | * to handle this, either we get the whole packet and must disect it |
michael@0 | 4357 | * ourselves (possibly with truncation and or corruption) or it is a summary |
michael@0 | 4358 | * from a middle box that did the disectting for us. |
michael@0 | 4359 | */ |
michael@0 | 4360 | static void |
michael@0 | 4361 | sctp_handle_packet_dropped(struct sctp_pktdrop_chunk *cp, |
michael@0 | 4362 | struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t limit) |
michael@0 | 4363 | { |
michael@0 | 4364 | uint32_t bottle_bw, on_queue; |
michael@0 | 4365 | uint16_t trunc_len; |
michael@0 | 4366 | unsigned int chlen; |
michael@0 | 4367 | unsigned int at; |
michael@0 | 4368 | struct sctp_chunk_desc desc; |
michael@0 | 4369 | struct sctp_chunkhdr *ch; |
michael@0 | 4370 | |
michael@0 | 4371 | chlen = ntohs(cp->ch.chunk_length); |
michael@0 | 4372 | chlen -= sizeof(struct sctp_pktdrop_chunk); |
michael@0 | 4373 | /* XXX possible chlen underflow */ |
michael@0 | 4374 | if (chlen == 0) { |
michael@0 | 4375 | ch = NULL; |
michael@0 | 4376 | if (cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX) |
michael@0 | 4377 | SCTP_STAT_INCR(sctps_pdrpbwrpt); |
michael@0 | 4378 | } else { |
michael@0 | 4379 | ch = (struct sctp_chunkhdr *)(cp->data + sizeof(struct sctphdr)); |
michael@0 | 4380 | chlen -= sizeof(struct sctphdr); |
michael@0 | 4381 | /* XXX possible chlen underflow */ |
michael@0 | 4382 | memset(&desc, 0, sizeof(desc)); |
michael@0 | 4383 | } |
michael@0 | 4384 | trunc_len = (uint16_t) ntohs(cp->trunc_len); |
michael@0 | 4385 | if (trunc_len > limit) { |
michael@0 | 4386 | trunc_len = limit; |
michael@0 | 4387 | } |
michael@0 | 4388 | |
michael@0 | 4389 | /* now the chunks themselves */ |
michael@0 | 4390 | while ((ch != NULL) && (chlen >= sizeof(struct sctp_chunkhdr))) { |
michael@0 | 4391 | desc.chunk_type = ch->chunk_type; |
michael@0 | 4392 | /* get amount we need to move */ |
michael@0 | 4393 | at = ntohs(ch->chunk_length); |
michael@0 | 4394 | if (at < sizeof(struct sctp_chunkhdr)) { |
michael@0 | 4395 | /* corrupt chunk, maybe at the end? */ |
michael@0 | 4396 | SCTP_STAT_INCR(sctps_pdrpcrupt); |
michael@0 | 4397 | break; |
michael@0 | 4398 | } |
michael@0 | 4399 | if (trunc_len == 0) { |
michael@0 | 4400 | /* we are supposed to have all of it */ |
michael@0 | 4401 | if (at > chlen) { |
michael@0 | 4402 | /* corrupt skip it */ |
michael@0 | 4403 | SCTP_STAT_INCR(sctps_pdrpcrupt); |
michael@0 | 4404 | break; |
michael@0 | 4405 | } |
michael@0 | 4406 | } else { |
michael@0 | 4407 | /* is there enough of it left ? */ |
michael@0 | 4408 | if (desc.chunk_type == SCTP_DATA) { |
michael@0 | 4409 | if (chlen < (sizeof(struct sctp_data_chunk) + |
michael@0 | 4410 | sizeof(desc.data_bytes))) { |
michael@0 | 4411 | break; |
michael@0 | 4412 | } |
michael@0 | 4413 | } else { |
michael@0 | 4414 | if (chlen < sizeof(struct sctp_chunkhdr)) { |
michael@0 | 4415 | break; |
michael@0 | 4416 | } |
michael@0 | 4417 | } |
michael@0 | 4418 | } |
michael@0 | 4419 | if (desc.chunk_type == SCTP_DATA) { |
michael@0 | 4420 | /* can we get out the tsn? */ |
michael@0 | 4421 | if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX)) |
michael@0 | 4422 | SCTP_STAT_INCR(sctps_pdrpmbda); |
michael@0 | 4423 | |
michael@0 | 4424 | if (chlen >= (sizeof(struct sctp_data_chunk) + sizeof(uint32_t))) { |
michael@0 | 4425 | /* yep */ |
michael@0 | 4426 | struct sctp_data_chunk *dcp; |
michael@0 | 4427 | uint8_t *ddp; |
michael@0 | 4428 | unsigned int iii; |
michael@0 | 4429 | |
michael@0 | 4430 | dcp = (struct sctp_data_chunk *)ch; |
michael@0 | 4431 | ddp = (uint8_t *) (dcp + 1); |
michael@0 | 4432 | for (iii = 0; iii < sizeof(desc.data_bytes); iii++) { |
michael@0 | 4433 | desc.data_bytes[iii] = ddp[iii]; |
michael@0 | 4434 | } |
michael@0 | 4435 | desc.tsn_ifany = dcp->dp.tsn; |
michael@0 | 4436 | } else { |
michael@0 | 4437 | /* nope we are done. */ |
michael@0 | 4438 | SCTP_STAT_INCR(sctps_pdrpnedat); |
michael@0 | 4439 | break; |
michael@0 | 4440 | } |
michael@0 | 4441 | } else { |
michael@0 | 4442 | if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX)) |
michael@0 | 4443 | SCTP_STAT_INCR(sctps_pdrpmbct); |
michael@0 | 4444 | } |
michael@0 | 4445 | |
michael@0 | 4446 | if (process_chunk_drop(stcb, &desc, net, cp->ch.chunk_flags)) { |
michael@0 | 4447 | SCTP_STAT_INCR(sctps_pdrppdbrk); |
michael@0 | 4448 | break; |
michael@0 | 4449 | } |
michael@0 | 4450 | if (SCTP_SIZE32(at) > chlen) { |
michael@0 | 4451 | break; |
michael@0 | 4452 | } |
michael@0 | 4453 | chlen -= SCTP_SIZE32(at); |
michael@0 | 4454 | if (chlen < sizeof(struct sctp_chunkhdr)) { |
michael@0 | 4455 | /* done, none left */ |
michael@0 | 4456 | break; |
michael@0 | 4457 | } |
michael@0 | 4458 | ch = (struct sctp_chunkhdr *)((caddr_t)ch + SCTP_SIZE32(at)); |
michael@0 | 4459 | } |
michael@0 | 4460 | /* Now update any rwnd --- possibly */ |
michael@0 | 4461 | if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX) == 0) { |
michael@0 | 4462 | /* From a peer, we get a rwnd report */ |
michael@0 | 4463 | uint32_t a_rwnd; |
michael@0 | 4464 | |
michael@0 | 4465 | SCTP_STAT_INCR(sctps_pdrpfehos); |
michael@0 | 4466 | |
michael@0 | 4467 | bottle_bw = ntohl(cp->bottle_bw); |
michael@0 | 4468 | on_queue = ntohl(cp->current_onq); |
michael@0 | 4469 | if (bottle_bw && on_queue) { |
michael@0 | 4470 | /* a rwnd report is in here */ |
michael@0 | 4471 | if (bottle_bw > on_queue) |
michael@0 | 4472 | a_rwnd = bottle_bw - on_queue; |
michael@0 | 4473 | else |
michael@0 | 4474 | a_rwnd = 0; |
michael@0 | 4475 | |
michael@0 | 4476 | if (a_rwnd == 0) |
michael@0 | 4477 | stcb->asoc.peers_rwnd = 0; |
michael@0 | 4478 | else { |
michael@0 | 4479 | if (a_rwnd > stcb->asoc.total_flight) { |
michael@0 | 4480 | stcb->asoc.peers_rwnd = |
michael@0 | 4481 | a_rwnd - stcb->asoc.total_flight; |
michael@0 | 4482 | } else { |
michael@0 | 4483 | stcb->asoc.peers_rwnd = 0; |
michael@0 | 4484 | } |
michael@0 | 4485 | if (stcb->asoc.peers_rwnd < |
michael@0 | 4486 | stcb->sctp_ep->sctp_ep.sctp_sws_sender) { |
michael@0 | 4487 | /* SWS sender side engages */ |
michael@0 | 4488 | stcb->asoc.peers_rwnd = 0; |
michael@0 | 4489 | } |
michael@0 | 4490 | } |
michael@0 | 4491 | } |
michael@0 | 4492 | } else { |
michael@0 | 4493 | SCTP_STAT_INCR(sctps_pdrpfmbox); |
michael@0 | 4494 | } |
michael@0 | 4495 | |
michael@0 | 4496 | /* now middle boxes in sat networks get a cwnd bump */ |
michael@0 | 4497 | if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX) && |
michael@0 | 4498 | (stcb->asoc.sat_t3_loss_recovery == 0) && |
michael@0 | 4499 | (stcb->asoc.sat_network)) { |
michael@0 | 4500 | /* |
michael@0 | 4501 | * This is debateable but for sat networks it makes sense |
michael@0 | 4502 | * Note if a T3 timer has went off, we will prohibit any |
michael@0 | 4503 | * changes to cwnd until we exit the t3 loss recovery. |
michael@0 | 4504 | */ |
michael@0 | 4505 | stcb->asoc.cc_functions.sctp_cwnd_update_after_packet_dropped(stcb, |
michael@0 | 4506 | net, cp, &bottle_bw, &on_queue); |
michael@0 | 4507 | } |
michael@0 | 4508 | } |
michael@0 | 4509 | |
michael@0 | 4510 | /* |
michael@0 | 4511 | * handles all control chunks in a packet inputs: - m: mbuf chain, assumed to |
michael@0 | 4512 | * still contain IP/SCTP header - stcb: is the tcb found for this packet - |
michael@0 | 4513 | * offset: offset into the mbuf chain to first chunkhdr - length: is the |
michael@0 | 4514 | * length of the complete packet outputs: - length: modified to remaining |
michael@0 | 4515 | * length after control processing - netp: modified to new sctp_nets after |
michael@0 | 4516 | * cookie-echo processing - return NULL to discard the packet (ie. no asoc, |
michael@0 | 4517 | * bad packet,...) otherwise return the tcb for this packet |
michael@0 | 4518 | */ |
michael@0 | 4519 | #if !defined(__Panda__) |
michael@0 | 4520 | #ifdef __GNUC__ |
michael@0 | 4521 | __attribute__ ((noinline)) |
michael@0 | 4522 | #endif |
michael@0 | 4523 | #endif |
michael@0 | 4524 | static struct sctp_tcb * |
michael@0 | 4525 | sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, |
michael@0 | 4526 | struct sockaddr *src, struct sockaddr *dst, |
michael@0 | 4527 | struct sctphdr *sh, struct sctp_chunkhdr *ch, struct sctp_inpcb *inp, |
michael@0 | 4528 | struct sctp_tcb *stcb, struct sctp_nets **netp, int *fwd_tsn_seen, |
michael@0 | 4529 | #if defined(__FreeBSD__) |
michael@0 | 4530 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 4531 | #endif |
michael@0 | 4532 | uint32_t vrf_id, uint16_t port) |
michael@0 | 4533 | { |
michael@0 | 4534 | struct sctp_association *asoc; |
michael@0 | 4535 | uint32_t vtag_in; |
michael@0 | 4536 | int num_chunks = 0; /* number of control chunks processed */ |
michael@0 | 4537 | uint32_t chk_length; |
michael@0 | 4538 | int ret; |
michael@0 | 4539 | int abort_no_unlock = 0; |
michael@0 | 4540 | int ecne_seen = 0; |
michael@0 | 4541 | /* |
michael@0 | 4542 | * How big should this be, and should it be alloc'd? Lets try the |
michael@0 | 4543 | * d-mtu-ceiling for now (2k) and that should hopefully work ... |
michael@0 | 4544 | * until we get into jumbo grams and such.. |
michael@0 | 4545 | */ |
michael@0 | 4546 | uint8_t chunk_buf[SCTP_CHUNK_BUFFER_SIZE]; |
michael@0 | 4547 | struct sctp_tcb *locked_tcb = stcb; |
michael@0 | 4548 | int got_auth = 0; |
michael@0 | 4549 | uint32_t auth_offset = 0, auth_len = 0; |
michael@0 | 4550 | int auth_skipped = 0; |
michael@0 | 4551 | int asconf_cnt = 0; |
michael@0 | 4552 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 4553 | struct socket *so; |
michael@0 | 4554 | #endif |
michael@0 | 4555 | |
michael@0 | 4556 | SCTPDBG(SCTP_DEBUG_INPUT1, "sctp_process_control: iphlen=%u, offset=%u, length=%u stcb:%p\n", |
michael@0 | 4557 | iphlen, *offset, length, (void *)stcb); |
michael@0 | 4558 | |
michael@0 | 4559 | /* validate chunk header length... */ |
michael@0 | 4560 | if (ntohs(ch->chunk_length) < sizeof(*ch)) { |
michael@0 | 4561 | SCTPDBG(SCTP_DEBUG_INPUT1, "Invalid header length %d\n", |
michael@0 | 4562 | ntohs(ch->chunk_length)); |
michael@0 | 4563 | if (locked_tcb) { |
michael@0 | 4564 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4565 | } |
michael@0 | 4566 | return (NULL); |
michael@0 | 4567 | } |
michael@0 | 4568 | /* |
michael@0 | 4569 | * validate the verification tag |
michael@0 | 4570 | */ |
michael@0 | 4571 | vtag_in = ntohl(sh->v_tag); |
michael@0 | 4572 | |
michael@0 | 4573 | if (locked_tcb) { |
michael@0 | 4574 | SCTP_TCB_LOCK_ASSERT(locked_tcb); |
michael@0 | 4575 | } |
michael@0 | 4576 | if (ch->chunk_type == SCTP_INITIATION) { |
michael@0 | 4577 | SCTPDBG(SCTP_DEBUG_INPUT1, "Its an INIT of len:%d vtag:%x\n", |
michael@0 | 4578 | ntohs(ch->chunk_length), vtag_in); |
michael@0 | 4579 | if (vtag_in != 0) { |
michael@0 | 4580 | /* protocol error- silently discard... */ |
michael@0 | 4581 | SCTP_STAT_INCR(sctps_badvtag); |
michael@0 | 4582 | if (locked_tcb) { |
michael@0 | 4583 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4584 | } |
michael@0 | 4585 | return (NULL); |
michael@0 | 4586 | } |
michael@0 | 4587 | } else if (ch->chunk_type != SCTP_COOKIE_ECHO) { |
michael@0 | 4588 | /* |
michael@0 | 4589 | * If there is no stcb, skip the AUTH chunk and process |
michael@0 | 4590 | * later after a stcb is found (to validate the lookup was |
michael@0 | 4591 | * valid. |
michael@0 | 4592 | */ |
michael@0 | 4593 | if ((ch->chunk_type == SCTP_AUTHENTICATION) && |
michael@0 | 4594 | (stcb == NULL) && |
michael@0 | 4595 | !SCTP_BASE_SYSCTL(sctp_auth_disable)) { |
michael@0 | 4596 | /* save this chunk for later processing */ |
michael@0 | 4597 | auth_skipped = 1; |
michael@0 | 4598 | auth_offset = *offset; |
michael@0 | 4599 | auth_len = ntohs(ch->chunk_length); |
michael@0 | 4600 | |
michael@0 | 4601 | /* (temporarily) move past this chunk */ |
michael@0 | 4602 | *offset += SCTP_SIZE32(auth_len); |
michael@0 | 4603 | if (*offset >= length) { |
michael@0 | 4604 | /* no more data left in the mbuf chain */ |
michael@0 | 4605 | *offset = length; |
michael@0 | 4606 | if (locked_tcb) { |
michael@0 | 4607 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4608 | } |
michael@0 | 4609 | return (NULL); |
michael@0 | 4610 | } |
michael@0 | 4611 | ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset, |
michael@0 | 4612 | sizeof(struct sctp_chunkhdr), chunk_buf); |
michael@0 | 4613 | } |
michael@0 | 4614 | if (ch == NULL) { |
michael@0 | 4615 | /* Help */ |
michael@0 | 4616 | *offset = length; |
michael@0 | 4617 | if (locked_tcb) { |
michael@0 | 4618 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4619 | } |
michael@0 | 4620 | return (NULL); |
michael@0 | 4621 | } |
michael@0 | 4622 | if (ch->chunk_type == SCTP_COOKIE_ECHO) { |
michael@0 | 4623 | goto process_control_chunks; |
michael@0 | 4624 | } |
michael@0 | 4625 | /* |
michael@0 | 4626 | * first check if it's an ASCONF with an unknown src addr we |
michael@0 | 4627 | * need to look inside to find the association |
michael@0 | 4628 | */ |
michael@0 | 4629 | if (ch->chunk_type == SCTP_ASCONF && stcb == NULL) { |
michael@0 | 4630 | struct sctp_chunkhdr *asconf_ch = ch; |
michael@0 | 4631 | uint32_t asconf_offset = 0, asconf_len = 0; |
michael@0 | 4632 | |
michael@0 | 4633 | /* inp's refcount may be reduced */ |
michael@0 | 4634 | SCTP_INP_INCR_REF(inp); |
michael@0 | 4635 | |
michael@0 | 4636 | asconf_offset = *offset; |
michael@0 | 4637 | do { |
michael@0 | 4638 | asconf_len = ntohs(asconf_ch->chunk_length); |
michael@0 | 4639 | if (asconf_len < sizeof(struct sctp_asconf_paramhdr)) |
michael@0 | 4640 | break; |
michael@0 | 4641 | stcb = sctp_findassociation_ep_asconf(m, |
michael@0 | 4642 | *offset, |
michael@0 | 4643 | dst, |
michael@0 | 4644 | sh, &inp, netp, vrf_id); |
michael@0 | 4645 | if (stcb != NULL) |
michael@0 | 4646 | break; |
michael@0 | 4647 | asconf_offset += SCTP_SIZE32(asconf_len); |
michael@0 | 4648 | asconf_ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, asconf_offset, |
michael@0 | 4649 | sizeof(struct sctp_chunkhdr), chunk_buf); |
michael@0 | 4650 | } while (asconf_ch != NULL && asconf_ch->chunk_type == SCTP_ASCONF); |
michael@0 | 4651 | if (stcb == NULL) { |
michael@0 | 4652 | /* |
michael@0 | 4653 | * reduce inp's refcount if not reduced in |
michael@0 | 4654 | * sctp_findassociation_ep_asconf(). |
michael@0 | 4655 | */ |
michael@0 | 4656 | SCTP_INP_DECR_REF(inp); |
michael@0 | 4657 | } else { |
michael@0 | 4658 | locked_tcb = stcb; |
michael@0 | 4659 | } |
michael@0 | 4660 | |
michael@0 | 4661 | /* now go back and verify any auth chunk to be sure */ |
michael@0 | 4662 | if (auth_skipped && (stcb != NULL)) { |
michael@0 | 4663 | struct sctp_auth_chunk *auth; |
michael@0 | 4664 | |
michael@0 | 4665 | auth = (struct sctp_auth_chunk *) |
michael@0 | 4666 | sctp_m_getptr(m, auth_offset, |
michael@0 | 4667 | auth_len, chunk_buf); |
michael@0 | 4668 | got_auth = 1; |
michael@0 | 4669 | auth_skipped = 0; |
michael@0 | 4670 | if ((auth == NULL) || sctp_handle_auth(stcb, auth, m, |
michael@0 | 4671 | auth_offset)) { |
michael@0 | 4672 | /* auth HMAC failed so dump it */ |
michael@0 | 4673 | *offset = length; |
michael@0 | 4674 | if (locked_tcb) { |
michael@0 | 4675 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4676 | } |
michael@0 | 4677 | return (NULL); |
michael@0 | 4678 | } else { |
michael@0 | 4679 | /* remaining chunks are HMAC checked */ |
michael@0 | 4680 | stcb->asoc.authenticated = 1; |
michael@0 | 4681 | } |
michael@0 | 4682 | } |
michael@0 | 4683 | } |
michael@0 | 4684 | if (stcb == NULL) { |
michael@0 | 4685 | /* no association, so it's out of the blue... */ |
michael@0 | 4686 | sctp_handle_ootb(m, iphlen, *offset, src, dst, sh, inp, |
michael@0 | 4687 | #if defined(__FreeBSD__) |
michael@0 | 4688 | use_mflowid, mflowid, |
michael@0 | 4689 | #endif |
michael@0 | 4690 | vrf_id, port); |
michael@0 | 4691 | *offset = length; |
michael@0 | 4692 | if (locked_tcb) { |
michael@0 | 4693 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4694 | } |
michael@0 | 4695 | return (NULL); |
michael@0 | 4696 | } |
michael@0 | 4697 | asoc = &stcb->asoc; |
michael@0 | 4698 | /* ABORT and SHUTDOWN can use either v_tag... */ |
michael@0 | 4699 | if ((ch->chunk_type == SCTP_ABORT_ASSOCIATION) || |
michael@0 | 4700 | (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) || |
michael@0 | 4701 | (ch->chunk_type == SCTP_PACKET_DROPPED)) { |
michael@0 | 4702 | /* Take the T-bit always into account. */ |
michael@0 | 4703 | if ((((ch->chunk_flags & SCTP_HAD_NO_TCB) == 0) && |
michael@0 | 4704 | (vtag_in == asoc->my_vtag)) || |
michael@0 | 4705 | (((ch->chunk_flags & SCTP_HAD_NO_TCB) == SCTP_HAD_NO_TCB) && |
michael@0 | 4706 | (vtag_in == asoc->peer_vtag))) { |
michael@0 | 4707 | /* this is valid */ |
michael@0 | 4708 | } else { |
michael@0 | 4709 | /* drop this packet... */ |
michael@0 | 4710 | SCTP_STAT_INCR(sctps_badvtag); |
michael@0 | 4711 | if (locked_tcb) { |
michael@0 | 4712 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4713 | } |
michael@0 | 4714 | return (NULL); |
michael@0 | 4715 | } |
michael@0 | 4716 | } else if (ch->chunk_type == SCTP_SHUTDOWN_ACK) { |
michael@0 | 4717 | if (vtag_in != asoc->my_vtag) { |
michael@0 | 4718 | /* |
michael@0 | 4719 | * this could be a stale SHUTDOWN-ACK or the |
michael@0 | 4720 | * peer never got the SHUTDOWN-COMPLETE and |
michael@0 | 4721 | * is still hung; we have started a new asoc |
michael@0 | 4722 | * but it won't complete until the shutdown |
michael@0 | 4723 | * is completed |
michael@0 | 4724 | */ |
michael@0 | 4725 | if (locked_tcb) { |
michael@0 | 4726 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4727 | } |
michael@0 | 4728 | sctp_handle_ootb(m, iphlen, *offset, src, dst, |
michael@0 | 4729 | sh, inp, |
michael@0 | 4730 | #if defined(__FreeBSD__) |
michael@0 | 4731 | use_mflowid, mflowid, |
michael@0 | 4732 | #endif |
michael@0 | 4733 | vrf_id, port); |
michael@0 | 4734 | return (NULL); |
michael@0 | 4735 | } |
michael@0 | 4736 | } else { |
michael@0 | 4737 | /* for all other chunks, vtag must match */ |
michael@0 | 4738 | if (vtag_in != asoc->my_vtag) { |
michael@0 | 4739 | /* invalid vtag... */ |
michael@0 | 4740 | SCTPDBG(SCTP_DEBUG_INPUT3, |
michael@0 | 4741 | "invalid vtag: %xh, expect %xh\n", |
michael@0 | 4742 | vtag_in, asoc->my_vtag); |
michael@0 | 4743 | SCTP_STAT_INCR(sctps_badvtag); |
michael@0 | 4744 | if (locked_tcb) { |
michael@0 | 4745 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4746 | } |
michael@0 | 4747 | *offset = length; |
michael@0 | 4748 | return (NULL); |
michael@0 | 4749 | } |
michael@0 | 4750 | } |
michael@0 | 4751 | } /* end if !SCTP_COOKIE_ECHO */ |
michael@0 | 4752 | /* |
michael@0 | 4753 | * process all control chunks... |
michael@0 | 4754 | */ |
michael@0 | 4755 | if (((ch->chunk_type == SCTP_SELECTIVE_ACK) || |
michael@0 | 4756 | (ch->chunk_type == SCTP_NR_SELECTIVE_ACK) || |
michael@0 | 4757 | (ch->chunk_type == SCTP_HEARTBEAT_REQUEST)) && |
michael@0 | 4758 | (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_ECHOED)) { |
michael@0 | 4759 | /* implied cookie-ack.. we must have lost the ack */ |
michael@0 | 4760 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 4761 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 4762 | stcb->asoc.overall_error_count, |
michael@0 | 4763 | 0, |
michael@0 | 4764 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 4765 | __LINE__); |
michael@0 | 4766 | } |
michael@0 | 4767 | stcb->asoc.overall_error_count = 0; |
michael@0 | 4768 | sctp_handle_cookie_ack((struct sctp_cookie_ack_chunk *)ch, stcb, |
michael@0 | 4769 | *netp); |
michael@0 | 4770 | } |
michael@0 | 4771 | |
michael@0 | 4772 | process_control_chunks: |
michael@0 | 4773 | while (IS_SCTP_CONTROL(ch)) { |
michael@0 | 4774 | /* validate chunk length */ |
michael@0 | 4775 | chk_length = ntohs(ch->chunk_length); |
michael@0 | 4776 | SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_process_control: processing a chunk type=%u, len=%u\n", |
michael@0 | 4777 | ch->chunk_type, chk_length); |
michael@0 | 4778 | SCTP_LTRACE_CHK(inp, stcb, ch->chunk_type, chk_length); |
michael@0 | 4779 | if (chk_length < sizeof(*ch) || |
michael@0 | 4780 | (*offset + (int)chk_length) > length) { |
michael@0 | 4781 | *offset = length; |
michael@0 | 4782 | if (locked_tcb) { |
michael@0 | 4783 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4784 | } |
michael@0 | 4785 | return (NULL); |
michael@0 | 4786 | } |
michael@0 | 4787 | SCTP_STAT_INCR_COUNTER64(sctps_incontrolchunks); |
michael@0 | 4788 | /* |
michael@0 | 4789 | * INIT-ACK only gets the init ack "header" portion only |
michael@0 | 4790 | * because we don't have to process the peer's COOKIE. All |
michael@0 | 4791 | * others get a complete chunk. |
michael@0 | 4792 | */ |
michael@0 | 4793 | if ((ch->chunk_type == SCTP_INITIATION_ACK) || |
michael@0 | 4794 | (ch->chunk_type == SCTP_INITIATION)) { |
michael@0 | 4795 | /* get an init-ack chunk */ |
michael@0 | 4796 | ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset, |
michael@0 | 4797 | sizeof(struct sctp_init_ack_chunk), chunk_buf); |
michael@0 | 4798 | if (ch == NULL) { |
michael@0 | 4799 | *offset = length; |
michael@0 | 4800 | if (locked_tcb) { |
michael@0 | 4801 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4802 | } |
michael@0 | 4803 | return (NULL); |
michael@0 | 4804 | } |
michael@0 | 4805 | } else { |
michael@0 | 4806 | /* For cookies and all other chunks. */ |
michael@0 | 4807 | if (chk_length > sizeof(chunk_buf)) { |
michael@0 | 4808 | /* |
michael@0 | 4809 | * use just the size of the chunk buffer |
michael@0 | 4810 | * so the front part of our chunks fit in |
michael@0 | 4811 | * contiguous space up to the chunk buffer |
michael@0 | 4812 | * size (508 bytes). |
michael@0 | 4813 | * For chunks that need to get more than that |
michael@0 | 4814 | * they must use the sctp_m_getptr() function |
michael@0 | 4815 | * or other means (e.g. know how to parse mbuf |
michael@0 | 4816 | * chains). Cookies do this already. |
michael@0 | 4817 | */ |
michael@0 | 4818 | ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset, |
michael@0 | 4819 | (sizeof(chunk_buf) - 4), |
michael@0 | 4820 | chunk_buf); |
michael@0 | 4821 | if (ch == NULL) { |
michael@0 | 4822 | *offset = length; |
michael@0 | 4823 | if (locked_tcb) { |
michael@0 | 4824 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4825 | } |
michael@0 | 4826 | return (NULL); |
michael@0 | 4827 | } |
michael@0 | 4828 | } else { |
michael@0 | 4829 | /* We can fit it all */ |
michael@0 | 4830 | ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset, |
michael@0 | 4831 | chk_length, chunk_buf); |
michael@0 | 4832 | if (ch == NULL) { |
michael@0 | 4833 | SCTP_PRINTF("sctp_process_control: Can't get the all data....\n"); |
michael@0 | 4834 | *offset = length; |
michael@0 | 4835 | if (locked_tcb) { |
michael@0 | 4836 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4837 | } |
michael@0 | 4838 | return (NULL); |
michael@0 | 4839 | } |
michael@0 | 4840 | } |
michael@0 | 4841 | } |
michael@0 | 4842 | num_chunks++; |
michael@0 | 4843 | /* Save off the last place we got a control from */ |
michael@0 | 4844 | if (stcb != NULL) { |
michael@0 | 4845 | if (((netp != NULL) && (*netp != NULL)) || (ch->chunk_type == SCTP_ASCONF)) { |
michael@0 | 4846 | /* |
michael@0 | 4847 | * allow last_control to be NULL if |
michael@0 | 4848 | * ASCONF... ASCONF processing will find the |
michael@0 | 4849 | * right net later |
michael@0 | 4850 | */ |
michael@0 | 4851 | if ((netp != NULL) && (*netp != NULL)) |
michael@0 | 4852 | stcb->asoc.last_control_chunk_from = *netp; |
michael@0 | 4853 | } |
michael@0 | 4854 | } |
michael@0 | 4855 | #ifdef SCTP_AUDITING_ENABLED |
michael@0 | 4856 | sctp_audit_log(0xB0, ch->chunk_type); |
michael@0 | 4857 | #endif |
michael@0 | 4858 | |
michael@0 | 4859 | /* check to see if this chunk required auth, but isn't */ |
michael@0 | 4860 | if ((stcb != NULL) && |
michael@0 | 4861 | !SCTP_BASE_SYSCTL(sctp_auth_disable) && |
michael@0 | 4862 | sctp_auth_is_required_chunk(ch->chunk_type, stcb->asoc.local_auth_chunks) && |
michael@0 | 4863 | !stcb->asoc.authenticated) { |
michael@0 | 4864 | /* "silently" ignore */ |
michael@0 | 4865 | SCTP_STAT_INCR(sctps_recvauthmissing); |
michael@0 | 4866 | goto next_chunk; |
michael@0 | 4867 | } |
michael@0 | 4868 | switch (ch->chunk_type) { |
michael@0 | 4869 | case SCTP_INITIATION: |
michael@0 | 4870 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_INIT\n"); |
michael@0 | 4871 | /* The INIT chunk must be the only chunk. */ |
michael@0 | 4872 | if ((num_chunks > 1) || |
michael@0 | 4873 | (length - *offset > (int)SCTP_SIZE32(chk_length))) { |
michael@0 | 4874 | sctp_abort_association(inp, stcb, m, iphlen, |
michael@0 | 4875 | src, dst, sh, NULL, |
michael@0 | 4876 | #if defined(__FreeBSD__) |
michael@0 | 4877 | use_mflowid, mflowid, |
michael@0 | 4878 | #endif |
michael@0 | 4879 | vrf_id, port); |
michael@0 | 4880 | *offset = length; |
michael@0 | 4881 | return (NULL); |
michael@0 | 4882 | } |
michael@0 | 4883 | /* Honor our resource limit. */ |
michael@0 | 4884 | if (chk_length > SCTP_LARGEST_INIT_ACCEPTED) { |
michael@0 | 4885 | struct mbuf *op_err; |
michael@0 | 4886 | |
michael@0 | 4887 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); |
michael@0 | 4888 | sctp_abort_association(inp, stcb, m, iphlen, |
michael@0 | 4889 | src, dst, sh, op_err, |
michael@0 | 4890 | #if defined(__FreeBSD__) |
michael@0 | 4891 | use_mflowid, mflowid, |
michael@0 | 4892 | #endif |
michael@0 | 4893 | vrf_id, port); |
michael@0 | 4894 | *offset = length; |
michael@0 | 4895 | return (NULL); |
michael@0 | 4896 | } |
michael@0 | 4897 | sctp_handle_init(m, iphlen, *offset, src, dst, sh, |
michael@0 | 4898 | (struct sctp_init_chunk *)ch, inp, |
michael@0 | 4899 | stcb, &abort_no_unlock, |
michael@0 | 4900 | #if defined(__FreeBSD__) |
michael@0 | 4901 | use_mflowid, mflowid, |
michael@0 | 4902 | #endif |
michael@0 | 4903 | vrf_id, port); |
michael@0 | 4904 | *offset = length; |
michael@0 | 4905 | if ((!abort_no_unlock) && (locked_tcb)) { |
michael@0 | 4906 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4907 | } |
michael@0 | 4908 | return (NULL); |
michael@0 | 4909 | break; |
michael@0 | 4910 | case SCTP_PAD_CHUNK: |
michael@0 | 4911 | break; |
michael@0 | 4912 | case SCTP_INITIATION_ACK: |
michael@0 | 4913 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_INIT-ACK\n"); |
michael@0 | 4914 | if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { |
michael@0 | 4915 | /* We are not interested anymore */ |
michael@0 | 4916 | if ((stcb) && (stcb->asoc.total_output_queue_size)) { |
michael@0 | 4917 | ; |
michael@0 | 4918 | } else { |
michael@0 | 4919 | if (locked_tcb != stcb) { |
michael@0 | 4920 | /* Very unlikely */ |
michael@0 | 4921 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4922 | } |
michael@0 | 4923 | *offset = length; |
michael@0 | 4924 | if (stcb) { |
michael@0 | 4925 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 4926 | so = SCTP_INP_SO(inp); |
michael@0 | 4927 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 4928 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 4929 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 4930 | SCTP_TCB_LOCK(stcb); |
michael@0 | 4931 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 4932 | #endif |
michael@0 | 4933 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_27); |
michael@0 | 4934 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 4935 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 4936 | #endif |
michael@0 | 4937 | } |
michael@0 | 4938 | return (NULL); |
michael@0 | 4939 | } |
michael@0 | 4940 | } |
michael@0 | 4941 | /* The INIT-ACK chunk must be the only chunk. */ |
michael@0 | 4942 | if ((num_chunks > 1) || |
michael@0 | 4943 | (length - *offset > (int)SCTP_SIZE32(chk_length))) { |
michael@0 | 4944 | *offset = length; |
michael@0 | 4945 | if (locked_tcb) { |
michael@0 | 4946 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4947 | } |
michael@0 | 4948 | return (NULL); |
michael@0 | 4949 | } |
michael@0 | 4950 | if ((netp) && (*netp)) { |
michael@0 | 4951 | ret = sctp_handle_init_ack(m, iphlen, *offset, |
michael@0 | 4952 | src, dst, sh, |
michael@0 | 4953 | (struct sctp_init_ack_chunk *)ch, |
michael@0 | 4954 | stcb, *netp, |
michael@0 | 4955 | &abort_no_unlock, |
michael@0 | 4956 | #if defined(__FreeBSD__) |
michael@0 | 4957 | use_mflowid, mflowid, |
michael@0 | 4958 | #endif |
michael@0 | 4959 | vrf_id); |
michael@0 | 4960 | } else { |
michael@0 | 4961 | ret = -1; |
michael@0 | 4962 | } |
michael@0 | 4963 | *offset = length; |
michael@0 | 4964 | if (abort_no_unlock) { |
michael@0 | 4965 | return (NULL); |
michael@0 | 4966 | } |
michael@0 | 4967 | /* |
michael@0 | 4968 | * Special case, I must call the output routine to |
michael@0 | 4969 | * get the cookie echoed |
michael@0 | 4970 | */ |
michael@0 | 4971 | if ((stcb != NULL) && (ret == 0)) { |
michael@0 | 4972 | sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED); |
michael@0 | 4973 | } |
michael@0 | 4974 | if (locked_tcb) { |
michael@0 | 4975 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 4976 | } |
michael@0 | 4977 | return (NULL); |
michael@0 | 4978 | break; |
michael@0 | 4979 | case SCTP_SELECTIVE_ACK: |
michael@0 | 4980 | { |
michael@0 | 4981 | struct sctp_sack_chunk *sack; |
michael@0 | 4982 | int abort_now = 0; |
michael@0 | 4983 | uint32_t a_rwnd, cum_ack; |
michael@0 | 4984 | uint16_t num_seg, num_dup; |
michael@0 | 4985 | uint8_t flags; |
michael@0 | 4986 | int offset_seg, offset_dup; |
michael@0 | 4987 | |
michael@0 | 4988 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_SACK\n"); |
michael@0 | 4989 | SCTP_STAT_INCR(sctps_recvsacks); |
michael@0 | 4990 | if (stcb == NULL) { |
michael@0 | 4991 | SCTPDBG(SCTP_DEBUG_INDATA1, "No stcb when processing SACK chunk\n"); |
michael@0 | 4992 | break; |
michael@0 | 4993 | } |
michael@0 | 4994 | if (chk_length < sizeof(struct sctp_sack_chunk)) { |
michael@0 | 4995 | SCTPDBG(SCTP_DEBUG_INDATA1, "Bad size on SACK chunk, too small\n"); |
michael@0 | 4996 | break; |
michael@0 | 4997 | } |
michael@0 | 4998 | if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) { |
michael@0 | 4999 | /*- |
michael@0 | 5000 | * If we have sent a shutdown-ack, we will pay no |
michael@0 | 5001 | * attention to a sack sent in to us since |
michael@0 | 5002 | * we don't care anymore. |
michael@0 | 5003 | */ |
michael@0 | 5004 | break; |
michael@0 | 5005 | } |
michael@0 | 5006 | sack = (struct sctp_sack_chunk *)ch; |
michael@0 | 5007 | flags = ch->chunk_flags; |
michael@0 | 5008 | cum_ack = ntohl(sack->sack.cum_tsn_ack); |
michael@0 | 5009 | num_seg = ntohs(sack->sack.num_gap_ack_blks); |
michael@0 | 5010 | num_dup = ntohs(sack->sack.num_dup_tsns); |
michael@0 | 5011 | a_rwnd = (uint32_t) ntohl(sack->sack.a_rwnd); |
michael@0 | 5012 | if (sizeof(struct sctp_sack_chunk) + |
michael@0 | 5013 | num_seg * sizeof(struct sctp_gap_ack_block) + |
michael@0 | 5014 | num_dup * sizeof(uint32_t) != chk_length) { |
michael@0 | 5015 | SCTPDBG(SCTP_DEBUG_INDATA1, "Bad size of SACK chunk\n"); |
michael@0 | 5016 | break; |
michael@0 | 5017 | } |
michael@0 | 5018 | offset_seg = *offset + sizeof(struct sctp_sack_chunk); |
michael@0 | 5019 | offset_dup = offset_seg + num_seg * sizeof(struct sctp_gap_ack_block); |
michael@0 | 5020 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_SACK process cum_ack:%x num_seg:%d a_rwnd:%d\n", |
michael@0 | 5021 | cum_ack, num_seg, a_rwnd); |
michael@0 | 5022 | stcb->asoc.seen_a_sack_this_pkt = 1; |
michael@0 | 5023 | if ((stcb->asoc.pr_sctp_cnt == 0) && |
michael@0 | 5024 | (num_seg == 0) && |
michael@0 | 5025 | SCTP_TSN_GE(cum_ack, stcb->asoc.last_acked_seq) && |
michael@0 | 5026 | (stcb->asoc.saw_sack_with_frags == 0) && |
michael@0 | 5027 | (stcb->asoc.saw_sack_with_nr_frags == 0) && |
michael@0 | 5028 | (!TAILQ_EMPTY(&stcb->asoc.sent_queue)) |
michael@0 | 5029 | ) { |
michael@0 | 5030 | /* We have a SIMPLE sack having no prior segments and |
michael@0 | 5031 | * data on sent queue to be acked.. Use the faster |
michael@0 | 5032 | * path sack processing. We also allow window update |
michael@0 | 5033 | * sacks with no missing segments to go this way too. |
michael@0 | 5034 | */ |
michael@0 | 5035 | sctp_express_handle_sack(stcb, cum_ack, a_rwnd, &abort_now, ecne_seen); |
michael@0 | 5036 | } else { |
michael@0 | 5037 | if (netp && *netp) |
michael@0 | 5038 | sctp_handle_sack(m, offset_seg, offset_dup, stcb, |
michael@0 | 5039 | num_seg, 0, num_dup, &abort_now, flags, |
michael@0 | 5040 | cum_ack, a_rwnd, ecne_seen); |
michael@0 | 5041 | } |
michael@0 | 5042 | if (abort_now) { |
michael@0 | 5043 | /* ABORT signal from sack processing */ |
michael@0 | 5044 | *offset = length; |
michael@0 | 5045 | return (NULL); |
michael@0 | 5046 | } |
michael@0 | 5047 | if (TAILQ_EMPTY(&stcb->asoc.send_queue) && |
michael@0 | 5048 | TAILQ_EMPTY(&stcb->asoc.sent_queue) && |
michael@0 | 5049 | (stcb->asoc.stream_queue_cnt == 0)) { |
michael@0 | 5050 | sctp_ulp_notify(SCTP_NOTIFY_SENDER_DRY, stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 5051 | } |
michael@0 | 5052 | } |
michael@0 | 5053 | break; |
michael@0 | 5054 | /* EY - nr_sack: If the received chunk is an nr_sack chunk */ |
michael@0 | 5055 | case SCTP_NR_SELECTIVE_ACK: |
michael@0 | 5056 | { |
michael@0 | 5057 | struct sctp_nr_sack_chunk *nr_sack; |
michael@0 | 5058 | int abort_now = 0; |
michael@0 | 5059 | uint32_t a_rwnd, cum_ack; |
michael@0 | 5060 | uint16_t num_seg, num_nr_seg, num_dup; |
michael@0 | 5061 | uint8_t flags; |
michael@0 | 5062 | int offset_seg, offset_dup; |
michael@0 | 5063 | |
michael@0 | 5064 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_NR_SACK\n"); |
michael@0 | 5065 | SCTP_STAT_INCR(sctps_recvsacks); |
michael@0 | 5066 | if (stcb == NULL) { |
michael@0 | 5067 | SCTPDBG(SCTP_DEBUG_INDATA1, "No stcb when processing NR-SACK chunk\n"); |
michael@0 | 5068 | break; |
michael@0 | 5069 | } |
michael@0 | 5070 | if ((stcb->asoc.sctp_nr_sack_on_off == 0) || |
michael@0 | 5071 | (stcb->asoc.peer_supports_nr_sack == 0)) { |
michael@0 | 5072 | goto unknown_chunk; |
michael@0 | 5073 | } |
michael@0 | 5074 | if (chk_length < sizeof(struct sctp_nr_sack_chunk)) { |
michael@0 | 5075 | SCTPDBG(SCTP_DEBUG_INDATA1, "Bad size on NR-SACK chunk, too small\n"); |
michael@0 | 5076 | break; |
michael@0 | 5077 | } |
michael@0 | 5078 | if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) { |
michael@0 | 5079 | /*- |
michael@0 | 5080 | * If we have sent a shutdown-ack, we will pay no |
michael@0 | 5081 | * attention to a sack sent in to us since |
michael@0 | 5082 | * we don't care anymore. |
michael@0 | 5083 | */ |
michael@0 | 5084 | break; |
michael@0 | 5085 | } |
michael@0 | 5086 | nr_sack = (struct sctp_nr_sack_chunk *)ch; |
michael@0 | 5087 | flags = ch->chunk_flags; |
michael@0 | 5088 | cum_ack = ntohl(nr_sack->nr_sack.cum_tsn_ack); |
michael@0 | 5089 | num_seg = ntohs(nr_sack->nr_sack.num_gap_ack_blks); |
michael@0 | 5090 | num_nr_seg = ntohs(nr_sack->nr_sack.num_nr_gap_ack_blks); |
michael@0 | 5091 | num_dup = ntohs(nr_sack->nr_sack.num_dup_tsns); |
michael@0 | 5092 | a_rwnd = (uint32_t) ntohl(nr_sack->nr_sack.a_rwnd); |
michael@0 | 5093 | if (sizeof(struct sctp_nr_sack_chunk) + |
michael@0 | 5094 | (num_seg + num_nr_seg) * sizeof(struct sctp_gap_ack_block) + |
michael@0 | 5095 | num_dup * sizeof(uint32_t) != chk_length) { |
michael@0 | 5096 | SCTPDBG(SCTP_DEBUG_INDATA1, "Bad size of NR_SACK chunk\n"); |
michael@0 | 5097 | break; |
michael@0 | 5098 | } |
michael@0 | 5099 | offset_seg = *offset + sizeof(struct sctp_nr_sack_chunk); |
michael@0 | 5100 | offset_dup = offset_seg + num_seg * sizeof(struct sctp_gap_ack_block); |
michael@0 | 5101 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_NR_SACK process cum_ack:%x num_seg:%d a_rwnd:%d\n", |
michael@0 | 5102 | cum_ack, num_seg, a_rwnd); |
michael@0 | 5103 | stcb->asoc.seen_a_sack_this_pkt = 1; |
michael@0 | 5104 | if ((stcb->asoc.pr_sctp_cnt == 0) && |
michael@0 | 5105 | (num_seg == 0) && (num_nr_seg == 0) && |
michael@0 | 5106 | SCTP_TSN_GE(cum_ack, stcb->asoc.last_acked_seq) && |
michael@0 | 5107 | (stcb->asoc.saw_sack_with_frags == 0) && |
michael@0 | 5108 | (stcb->asoc.saw_sack_with_nr_frags == 0) && |
michael@0 | 5109 | (!TAILQ_EMPTY(&stcb->asoc.sent_queue))) { |
michael@0 | 5110 | /* |
michael@0 | 5111 | * We have a SIMPLE sack having no |
michael@0 | 5112 | * prior segments and data on sent |
michael@0 | 5113 | * queue to be acked. Use the |
michael@0 | 5114 | * faster path sack processing. We |
michael@0 | 5115 | * also allow window update sacks |
michael@0 | 5116 | * with no missing segments to go |
michael@0 | 5117 | * this way too. |
michael@0 | 5118 | */ |
michael@0 | 5119 | sctp_express_handle_sack(stcb, cum_ack, a_rwnd, |
michael@0 | 5120 | &abort_now, ecne_seen); |
michael@0 | 5121 | } else { |
michael@0 | 5122 | if (netp && *netp) |
michael@0 | 5123 | sctp_handle_sack(m, offset_seg, offset_dup, stcb, |
michael@0 | 5124 | num_seg, num_nr_seg, num_dup, &abort_now, flags, |
michael@0 | 5125 | cum_ack, a_rwnd, ecne_seen); |
michael@0 | 5126 | } |
michael@0 | 5127 | if (abort_now) { |
michael@0 | 5128 | /* ABORT signal from sack processing */ |
michael@0 | 5129 | *offset = length; |
michael@0 | 5130 | return (NULL); |
michael@0 | 5131 | } |
michael@0 | 5132 | if (TAILQ_EMPTY(&stcb->asoc.send_queue) && |
michael@0 | 5133 | TAILQ_EMPTY(&stcb->asoc.sent_queue) && |
michael@0 | 5134 | (stcb->asoc.stream_queue_cnt == 0)) { |
michael@0 | 5135 | sctp_ulp_notify(SCTP_NOTIFY_SENDER_DRY, stcb, 0, NULL, SCTP_SO_NOT_LOCKED); |
michael@0 | 5136 | } |
michael@0 | 5137 | } |
michael@0 | 5138 | break; |
michael@0 | 5139 | |
michael@0 | 5140 | case SCTP_HEARTBEAT_REQUEST: |
michael@0 | 5141 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_HEARTBEAT\n"); |
michael@0 | 5142 | if ((stcb) && netp && *netp) { |
michael@0 | 5143 | SCTP_STAT_INCR(sctps_recvheartbeat); |
michael@0 | 5144 | sctp_send_heartbeat_ack(stcb, m, *offset, |
michael@0 | 5145 | chk_length, *netp); |
michael@0 | 5146 | |
michael@0 | 5147 | /* He's alive so give him credit */ |
michael@0 | 5148 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5149 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5150 | stcb->asoc.overall_error_count, |
michael@0 | 5151 | 0, |
michael@0 | 5152 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5153 | __LINE__); |
michael@0 | 5154 | } |
michael@0 | 5155 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5156 | } |
michael@0 | 5157 | break; |
michael@0 | 5158 | case SCTP_HEARTBEAT_ACK: |
michael@0 | 5159 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_HEARTBEAT-ACK\n"); |
michael@0 | 5160 | if ((stcb == NULL) || (chk_length != sizeof(struct sctp_heartbeat_chunk))) { |
michael@0 | 5161 | /* Its not ours */ |
michael@0 | 5162 | *offset = length; |
michael@0 | 5163 | if (locked_tcb) { |
michael@0 | 5164 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5165 | } |
michael@0 | 5166 | return (NULL); |
michael@0 | 5167 | } |
michael@0 | 5168 | /* He's alive so give him credit */ |
michael@0 | 5169 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5170 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5171 | stcb->asoc.overall_error_count, |
michael@0 | 5172 | 0, |
michael@0 | 5173 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5174 | __LINE__); |
michael@0 | 5175 | } |
michael@0 | 5176 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5177 | SCTP_STAT_INCR(sctps_recvheartbeatack); |
michael@0 | 5178 | if (netp && *netp) |
michael@0 | 5179 | sctp_handle_heartbeat_ack((struct sctp_heartbeat_chunk *)ch, |
michael@0 | 5180 | stcb, *netp); |
michael@0 | 5181 | break; |
michael@0 | 5182 | case SCTP_ABORT_ASSOCIATION: |
michael@0 | 5183 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_ABORT, stcb %p\n", |
michael@0 | 5184 | (void *)stcb); |
michael@0 | 5185 | if ((stcb) && netp && *netp) |
michael@0 | 5186 | sctp_handle_abort((struct sctp_abort_chunk *)ch, |
michael@0 | 5187 | stcb, *netp); |
michael@0 | 5188 | *offset = length; |
michael@0 | 5189 | return (NULL); |
michael@0 | 5190 | break; |
michael@0 | 5191 | case SCTP_SHUTDOWN: |
michael@0 | 5192 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_SHUTDOWN, stcb %p\n", |
michael@0 | 5193 | (void *)stcb); |
michael@0 | 5194 | if ((stcb == NULL) || (chk_length != sizeof(struct sctp_shutdown_chunk))) { |
michael@0 | 5195 | *offset = length; |
michael@0 | 5196 | if (locked_tcb) { |
michael@0 | 5197 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5198 | } |
michael@0 | 5199 | return (NULL); |
michael@0 | 5200 | } |
michael@0 | 5201 | if (netp && *netp) { |
michael@0 | 5202 | int abort_flag = 0; |
michael@0 | 5203 | |
michael@0 | 5204 | sctp_handle_shutdown((struct sctp_shutdown_chunk *)ch, |
michael@0 | 5205 | stcb, *netp, &abort_flag); |
michael@0 | 5206 | if (abort_flag) { |
michael@0 | 5207 | *offset = length; |
michael@0 | 5208 | return (NULL); |
michael@0 | 5209 | } |
michael@0 | 5210 | } |
michael@0 | 5211 | break; |
michael@0 | 5212 | case SCTP_SHUTDOWN_ACK: |
michael@0 | 5213 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_SHUTDOWN-ACK, stcb %p\n", (void *)stcb); |
michael@0 | 5214 | if ((stcb) && (netp) && (*netp)) |
michael@0 | 5215 | sctp_handle_shutdown_ack((struct sctp_shutdown_ack_chunk *)ch, stcb, *netp); |
michael@0 | 5216 | *offset = length; |
michael@0 | 5217 | return (NULL); |
michael@0 | 5218 | break; |
michael@0 | 5219 | |
michael@0 | 5220 | case SCTP_OPERATION_ERROR: |
michael@0 | 5221 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_OP-ERR\n"); |
michael@0 | 5222 | if ((stcb) && netp && *netp && sctp_handle_error(ch, stcb, *netp) < 0) { |
michael@0 | 5223 | *offset = length; |
michael@0 | 5224 | return (NULL); |
michael@0 | 5225 | } |
michael@0 | 5226 | break; |
michael@0 | 5227 | case SCTP_COOKIE_ECHO: |
michael@0 | 5228 | SCTPDBG(SCTP_DEBUG_INPUT3, |
michael@0 | 5229 | "SCTP_COOKIE-ECHO, stcb %p\n", (void *)stcb); |
michael@0 | 5230 | if ((stcb) && (stcb->asoc.total_output_queue_size)) { |
michael@0 | 5231 | ; |
michael@0 | 5232 | } else { |
michael@0 | 5233 | if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { |
michael@0 | 5234 | /* We are not interested anymore */ |
michael@0 | 5235 | abend: |
michael@0 | 5236 | if (stcb) { |
michael@0 | 5237 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 5238 | } |
michael@0 | 5239 | *offset = length; |
michael@0 | 5240 | return (NULL); |
michael@0 | 5241 | } |
michael@0 | 5242 | } |
michael@0 | 5243 | /* |
michael@0 | 5244 | * First are we accepting? We do this again here |
michael@0 | 5245 | * since it is possible that a previous endpoint WAS |
michael@0 | 5246 | * listening responded to a INIT-ACK and then |
michael@0 | 5247 | * closed. We opened and bound.. and are now no |
michael@0 | 5248 | * longer listening. |
michael@0 | 5249 | */ |
michael@0 | 5250 | |
michael@0 | 5251 | if ((stcb == NULL) && (inp->sctp_socket->so_qlen >= inp->sctp_socket->so_qlimit)) { |
michael@0 | 5252 | if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && |
michael@0 | 5253 | (SCTP_BASE_SYSCTL(sctp_abort_if_one_2_one_hits_limit))) { |
michael@0 | 5254 | struct mbuf *op_err; |
michael@0 | 5255 | |
michael@0 | 5256 | op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC); |
michael@0 | 5257 | sctp_abort_association(inp, stcb, m, iphlen, |
michael@0 | 5258 | src, dst, sh, op_err, |
michael@0 | 5259 | #if defined(__FreeBSD__) |
michael@0 | 5260 | use_mflowid, mflowid, |
michael@0 | 5261 | #endif |
michael@0 | 5262 | vrf_id, port); |
michael@0 | 5263 | } |
michael@0 | 5264 | *offset = length; |
michael@0 | 5265 | return (NULL); |
michael@0 | 5266 | } else { |
michael@0 | 5267 | struct mbuf *ret_buf; |
michael@0 | 5268 | struct sctp_inpcb *linp; |
michael@0 | 5269 | if (stcb) { |
michael@0 | 5270 | linp = NULL; |
michael@0 | 5271 | } else { |
michael@0 | 5272 | linp = inp; |
michael@0 | 5273 | } |
michael@0 | 5274 | |
michael@0 | 5275 | if (linp) { |
michael@0 | 5276 | SCTP_ASOC_CREATE_LOCK(linp); |
michael@0 | 5277 | if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) || |
michael@0 | 5278 | (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { |
michael@0 | 5279 | SCTP_ASOC_CREATE_UNLOCK(linp); |
michael@0 | 5280 | goto abend; |
michael@0 | 5281 | } |
michael@0 | 5282 | } |
michael@0 | 5283 | |
michael@0 | 5284 | if (netp) { |
michael@0 | 5285 | ret_buf = |
michael@0 | 5286 | sctp_handle_cookie_echo(m, iphlen, |
michael@0 | 5287 | *offset, |
michael@0 | 5288 | src, dst, |
michael@0 | 5289 | sh, |
michael@0 | 5290 | (struct sctp_cookie_echo_chunk *)ch, |
michael@0 | 5291 | &inp, &stcb, netp, |
michael@0 | 5292 | auth_skipped, |
michael@0 | 5293 | auth_offset, |
michael@0 | 5294 | auth_len, |
michael@0 | 5295 | &locked_tcb, |
michael@0 | 5296 | #if defined(__FreeBSD__) |
michael@0 | 5297 | use_mflowid, |
michael@0 | 5298 | mflowid, |
michael@0 | 5299 | #endif |
michael@0 | 5300 | vrf_id, |
michael@0 | 5301 | port); |
michael@0 | 5302 | } else { |
michael@0 | 5303 | ret_buf = NULL; |
michael@0 | 5304 | } |
michael@0 | 5305 | if (linp) { |
michael@0 | 5306 | SCTP_ASOC_CREATE_UNLOCK(linp); |
michael@0 | 5307 | } |
michael@0 | 5308 | if (ret_buf == NULL) { |
michael@0 | 5309 | if (locked_tcb) { |
michael@0 | 5310 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5311 | } |
michael@0 | 5312 | SCTPDBG(SCTP_DEBUG_INPUT3, |
michael@0 | 5313 | "GAK, null buffer\n"); |
michael@0 | 5314 | *offset = length; |
michael@0 | 5315 | return (NULL); |
michael@0 | 5316 | } |
michael@0 | 5317 | /* if AUTH skipped, see if it verified... */ |
michael@0 | 5318 | if (auth_skipped) { |
michael@0 | 5319 | got_auth = 1; |
michael@0 | 5320 | auth_skipped = 0; |
michael@0 | 5321 | } |
michael@0 | 5322 | if (!TAILQ_EMPTY(&stcb->asoc.sent_queue)) { |
michael@0 | 5323 | /* |
michael@0 | 5324 | * Restart the timer if we have |
michael@0 | 5325 | * pending data |
michael@0 | 5326 | */ |
michael@0 | 5327 | struct sctp_tmit_chunk *chk; |
michael@0 | 5328 | |
michael@0 | 5329 | chk = TAILQ_FIRST(&stcb->asoc.sent_queue); |
michael@0 | 5330 | sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, chk->whoTo); |
michael@0 | 5331 | } |
michael@0 | 5332 | } |
michael@0 | 5333 | break; |
michael@0 | 5334 | case SCTP_COOKIE_ACK: |
michael@0 | 5335 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_COOKIE-ACK, stcb %p\n", (void *)stcb); |
michael@0 | 5336 | if ((stcb == NULL) || chk_length != sizeof(struct sctp_cookie_ack_chunk)) { |
michael@0 | 5337 | if (locked_tcb) { |
michael@0 | 5338 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5339 | } |
michael@0 | 5340 | return (NULL); |
michael@0 | 5341 | } |
michael@0 | 5342 | if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { |
michael@0 | 5343 | /* We are not interested anymore */ |
michael@0 | 5344 | if ((stcb) && (stcb->asoc.total_output_queue_size)) { |
michael@0 | 5345 | ; |
michael@0 | 5346 | } else if (stcb) { |
michael@0 | 5347 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 5348 | so = SCTP_INP_SO(inp); |
michael@0 | 5349 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 5350 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 5351 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 5352 | SCTP_TCB_LOCK(stcb); |
michael@0 | 5353 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 5354 | #endif |
michael@0 | 5355 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_27); |
michael@0 | 5356 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 5357 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 5358 | #endif |
michael@0 | 5359 | *offset = length; |
michael@0 | 5360 | return (NULL); |
michael@0 | 5361 | } |
michael@0 | 5362 | } |
michael@0 | 5363 | /* He's alive so give him credit */ |
michael@0 | 5364 | if ((stcb) && netp && *netp) { |
michael@0 | 5365 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5366 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5367 | stcb->asoc.overall_error_count, |
michael@0 | 5368 | 0, |
michael@0 | 5369 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5370 | __LINE__); |
michael@0 | 5371 | } |
michael@0 | 5372 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5373 | sctp_handle_cookie_ack((struct sctp_cookie_ack_chunk *)ch,stcb, *netp); |
michael@0 | 5374 | } |
michael@0 | 5375 | break; |
michael@0 | 5376 | case SCTP_ECN_ECHO: |
michael@0 | 5377 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_ECN-ECHO\n"); |
michael@0 | 5378 | /* He's alive so give him credit */ |
michael@0 | 5379 | if ((stcb == NULL) || (chk_length != sizeof(struct sctp_ecne_chunk))) { |
michael@0 | 5380 | /* Its not ours */ |
michael@0 | 5381 | if (locked_tcb) { |
michael@0 | 5382 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5383 | } |
michael@0 | 5384 | *offset = length; |
michael@0 | 5385 | return (NULL); |
michael@0 | 5386 | } |
michael@0 | 5387 | if (stcb) { |
michael@0 | 5388 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5389 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5390 | stcb->asoc.overall_error_count, |
michael@0 | 5391 | 0, |
michael@0 | 5392 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5393 | __LINE__); |
michael@0 | 5394 | } |
michael@0 | 5395 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5396 | sctp_handle_ecn_echo((struct sctp_ecne_chunk *)ch, |
michael@0 | 5397 | stcb); |
michael@0 | 5398 | ecne_seen = 1; |
michael@0 | 5399 | } |
michael@0 | 5400 | break; |
michael@0 | 5401 | case SCTP_ECN_CWR: |
michael@0 | 5402 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_ECN-CWR\n"); |
michael@0 | 5403 | /* He's alive so give him credit */ |
michael@0 | 5404 | if ((stcb == NULL) || (chk_length != sizeof(struct sctp_cwr_chunk))) { |
michael@0 | 5405 | /* Its not ours */ |
michael@0 | 5406 | if (locked_tcb) { |
michael@0 | 5407 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5408 | } |
michael@0 | 5409 | *offset = length; |
michael@0 | 5410 | return (NULL); |
michael@0 | 5411 | } |
michael@0 | 5412 | if (stcb) { |
michael@0 | 5413 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5414 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5415 | stcb->asoc.overall_error_count, |
michael@0 | 5416 | 0, |
michael@0 | 5417 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5418 | __LINE__); |
michael@0 | 5419 | } |
michael@0 | 5420 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5421 | sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb, *netp); |
michael@0 | 5422 | } |
michael@0 | 5423 | break; |
michael@0 | 5424 | case SCTP_SHUTDOWN_COMPLETE: |
michael@0 | 5425 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_SHUTDOWN-COMPLETE, stcb %p\n", (void *)stcb); |
michael@0 | 5426 | /* must be first and only chunk */ |
michael@0 | 5427 | if ((num_chunks > 1) || |
michael@0 | 5428 | (length - *offset > (int)SCTP_SIZE32(chk_length))) { |
michael@0 | 5429 | *offset = length; |
michael@0 | 5430 | if (locked_tcb) { |
michael@0 | 5431 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5432 | } |
michael@0 | 5433 | return (NULL); |
michael@0 | 5434 | } |
michael@0 | 5435 | if ((stcb) && netp && *netp) { |
michael@0 | 5436 | sctp_handle_shutdown_complete((struct sctp_shutdown_complete_chunk *)ch, |
michael@0 | 5437 | stcb, *netp); |
michael@0 | 5438 | } |
michael@0 | 5439 | *offset = length; |
michael@0 | 5440 | return (NULL); |
michael@0 | 5441 | break; |
michael@0 | 5442 | case SCTP_ASCONF: |
michael@0 | 5443 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_ASCONF\n"); |
michael@0 | 5444 | /* He's alive so give him credit */ |
michael@0 | 5445 | if (stcb) { |
michael@0 | 5446 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5447 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5448 | stcb->asoc.overall_error_count, |
michael@0 | 5449 | 0, |
michael@0 | 5450 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5451 | __LINE__); |
michael@0 | 5452 | } |
michael@0 | 5453 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5454 | sctp_handle_asconf(m, *offset, src, |
michael@0 | 5455 | (struct sctp_asconf_chunk *)ch, stcb, asconf_cnt == 0); |
michael@0 | 5456 | asconf_cnt++; |
michael@0 | 5457 | } |
michael@0 | 5458 | break; |
michael@0 | 5459 | case SCTP_ASCONF_ACK: |
michael@0 | 5460 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_ASCONF-ACK\n"); |
michael@0 | 5461 | if (chk_length < sizeof(struct sctp_asconf_ack_chunk)) { |
michael@0 | 5462 | /* Its not ours */ |
michael@0 | 5463 | if (locked_tcb) { |
michael@0 | 5464 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5465 | } |
michael@0 | 5466 | *offset = length; |
michael@0 | 5467 | return (NULL); |
michael@0 | 5468 | } |
michael@0 | 5469 | if ((stcb) && netp && *netp) { |
michael@0 | 5470 | /* He's alive so give him credit */ |
michael@0 | 5471 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5472 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5473 | stcb->asoc.overall_error_count, |
michael@0 | 5474 | 0, |
michael@0 | 5475 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5476 | __LINE__); |
michael@0 | 5477 | } |
michael@0 | 5478 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5479 | sctp_handle_asconf_ack(m, *offset, |
michael@0 | 5480 | (struct sctp_asconf_ack_chunk *)ch, stcb, *netp, &abort_no_unlock); |
michael@0 | 5481 | if (abort_no_unlock) |
michael@0 | 5482 | return (NULL); |
michael@0 | 5483 | } |
michael@0 | 5484 | break; |
michael@0 | 5485 | case SCTP_FORWARD_CUM_TSN: |
michael@0 | 5486 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_FWD-TSN\n"); |
michael@0 | 5487 | if (chk_length < sizeof(struct sctp_forward_tsn_chunk)) { |
michael@0 | 5488 | /* Its not ours */ |
michael@0 | 5489 | if (locked_tcb) { |
michael@0 | 5490 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5491 | } |
michael@0 | 5492 | *offset = length; |
michael@0 | 5493 | return (NULL); |
michael@0 | 5494 | } |
michael@0 | 5495 | |
michael@0 | 5496 | /* He's alive so give him credit */ |
michael@0 | 5497 | if (stcb) { |
michael@0 | 5498 | int abort_flag = 0; |
michael@0 | 5499 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5500 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5501 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5502 | stcb->asoc.overall_error_count, |
michael@0 | 5503 | 0, |
michael@0 | 5504 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5505 | __LINE__); |
michael@0 | 5506 | } |
michael@0 | 5507 | *fwd_tsn_seen = 1; |
michael@0 | 5508 | if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { |
michael@0 | 5509 | /* We are not interested anymore */ |
michael@0 | 5510 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 5511 | so = SCTP_INP_SO(inp); |
michael@0 | 5512 | atomic_add_int(&stcb->asoc.refcnt, 1); |
michael@0 | 5513 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 5514 | SCTP_SOCKET_LOCK(so, 1); |
michael@0 | 5515 | SCTP_TCB_LOCK(stcb); |
michael@0 | 5516 | atomic_subtract_int(&stcb->asoc.refcnt, 1); |
michael@0 | 5517 | #endif |
michael@0 | 5518 | (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT+SCTP_LOC_29); |
michael@0 | 5519 | #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) |
michael@0 | 5520 | SCTP_SOCKET_UNLOCK(so, 1); |
michael@0 | 5521 | #endif |
michael@0 | 5522 | *offset = length; |
michael@0 | 5523 | return (NULL); |
michael@0 | 5524 | } |
michael@0 | 5525 | sctp_handle_forward_tsn(stcb, |
michael@0 | 5526 | (struct sctp_forward_tsn_chunk *)ch, &abort_flag, m, *offset); |
michael@0 | 5527 | if (abort_flag) { |
michael@0 | 5528 | *offset = length; |
michael@0 | 5529 | return (NULL); |
michael@0 | 5530 | } else { |
michael@0 | 5531 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 5532 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 5533 | stcb->asoc.overall_error_count, |
michael@0 | 5534 | 0, |
michael@0 | 5535 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 5536 | __LINE__); |
michael@0 | 5537 | } |
michael@0 | 5538 | stcb->asoc.overall_error_count = 0; |
michael@0 | 5539 | } |
michael@0 | 5540 | |
michael@0 | 5541 | } |
michael@0 | 5542 | break; |
michael@0 | 5543 | case SCTP_STREAM_RESET: |
michael@0 | 5544 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_STREAM_RESET\n"); |
michael@0 | 5545 | if (((stcb == NULL) || (ch == NULL) || (chk_length < sizeof(struct sctp_stream_reset_tsn_req)))) { |
michael@0 | 5546 | /* Its not ours */ |
michael@0 | 5547 | if (locked_tcb) { |
michael@0 | 5548 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5549 | } |
michael@0 | 5550 | *offset = length; |
michael@0 | 5551 | return (NULL); |
michael@0 | 5552 | } |
michael@0 | 5553 | if (stcb->asoc.peer_supports_strreset == 0) { |
michael@0 | 5554 | /* |
michael@0 | 5555 | * hmm, peer should have announced this, but |
michael@0 | 5556 | * we will turn it on since he is sending us |
michael@0 | 5557 | * a stream reset. |
michael@0 | 5558 | */ |
michael@0 | 5559 | stcb->asoc.peer_supports_strreset = 1; |
michael@0 | 5560 | } |
michael@0 | 5561 | if (sctp_handle_stream_reset(stcb, m, *offset, ch)) { |
michael@0 | 5562 | /* stop processing */ |
michael@0 | 5563 | *offset = length; |
michael@0 | 5564 | return (NULL); |
michael@0 | 5565 | } |
michael@0 | 5566 | break; |
michael@0 | 5567 | case SCTP_PACKET_DROPPED: |
michael@0 | 5568 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_PACKET_DROPPED\n"); |
michael@0 | 5569 | /* re-get it all please */ |
michael@0 | 5570 | if (chk_length < sizeof(struct sctp_pktdrop_chunk)) { |
michael@0 | 5571 | /* Its not ours */ |
michael@0 | 5572 | if (locked_tcb) { |
michael@0 | 5573 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5574 | } |
michael@0 | 5575 | *offset = length; |
michael@0 | 5576 | return (NULL); |
michael@0 | 5577 | } |
michael@0 | 5578 | |
michael@0 | 5579 | |
michael@0 | 5580 | if (ch && (stcb) && netp && (*netp)) { |
michael@0 | 5581 | sctp_handle_packet_dropped((struct sctp_pktdrop_chunk *)ch, |
michael@0 | 5582 | stcb, *netp, |
michael@0 | 5583 | min(chk_length, (sizeof(chunk_buf) - 4))); |
michael@0 | 5584 | |
michael@0 | 5585 | } |
michael@0 | 5586 | |
michael@0 | 5587 | break; |
michael@0 | 5588 | |
michael@0 | 5589 | case SCTP_AUTHENTICATION: |
michael@0 | 5590 | SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_AUTHENTICATION\n"); |
michael@0 | 5591 | if (SCTP_BASE_SYSCTL(sctp_auth_disable)) |
michael@0 | 5592 | goto unknown_chunk; |
michael@0 | 5593 | |
michael@0 | 5594 | if (stcb == NULL) { |
michael@0 | 5595 | /* save the first AUTH for later processing */ |
michael@0 | 5596 | if (auth_skipped == 0) { |
michael@0 | 5597 | auth_offset = *offset; |
michael@0 | 5598 | auth_len = chk_length; |
michael@0 | 5599 | auth_skipped = 1; |
michael@0 | 5600 | } |
michael@0 | 5601 | /* skip this chunk (temporarily) */ |
michael@0 | 5602 | goto next_chunk; |
michael@0 | 5603 | } |
michael@0 | 5604 | if ((chk_length < (sizeof(struct sctp_auth_chunk))) || |
michael@0 | 5605 | (chk_length > (sizeof(struct sctp_auth_chunk) + |
michael@0 | 5606 | SCTP_AUTH_DIGEST_LEN_MAX))) { |
michael@0 | 5607 | /* Its not ours */ |
michael@0 | 5608 | if (locked_tcb) { |
michael@0 | 5609 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5610 | } |
michael@0 | 5611 | *offset = length; |
michael@0 | 5612 | return (NULL); |
michael@0 | 5613 | } |
michael@0 | 5614 | if (got_auth == 1) { |
michael@0 | 5615 | /* skip this chunk... it's already auth'd */ |
michael@0 | 5616 | goto next_chunk; |
michael@0 | 5617 | } |
michael@0 | 5618 | got_auth = 1; |
michael@0 | 5619 | if ((ch == NULL) || sctp_handle_auth(stcb, (struct sctp_auth_chunk *)ch, |
michael@0 | 5620 | m, *offset)) { |
michael@0 | 5621 | /* auth HMAC failed so dump the packet */ |
michael@0 | 5622 | *offset = length; |
michael@0 | 5623 | return (stcb); |
michael@0 | 5624 | } else { |
michael@0 | 5625 | /* remaining chunks are HMAC checked */ |
michael@0 | 5626 | stcb->asoc.authenticated = 1; |
michael@0 | 5627 | } |
michael@0 | 5628 | break; |
michael@0 | 5629 | |
michael@0 | 5630 | default: |
michael@0 | 5631 | unknown_chunk: |
michael@0 | 5632 | /* it's an unknown chunk! */ |
michael@0 | 5633 | if ((ch->chunk_type & 0x40) && (stcb != NULL)) { |
michael@0 | 5634 | struct mbuf *mm; |
michael@0 | 5635 | struct sctp_paramhdr *phd; |
michael@0 | 5636 | |
michael@0 | 5637 | mm = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), |
michael@0 | 5638 | 0, M_NOWAIT, 1, MT_DATA); |
michael@0 | 5639 | if (mm) { |
michael@0 | 5640 | phd = mtod(mm, struct sctp_paramhdr *); |
michael@0 | 5641 | /* |
michael@0 | 5642 | * We cheat and use param type since |
michael@0 | 5643 | * we did not bother to define a |
michael@0 | 5644 | * error cause struct. They are the |
michael@0 | 5645 | * same basic format with different |
michael@0 | 5646 | * names. |
michael@0 | 5647 | */ |
michael@0 | 5648 | phd->param_type = htons(SCTP_CAUSE_UNRECOG_CHUNK); |
michael@0 | 5649 | phd->param_length = htons(chk_length + sizeof(*phd)); |
michael@0 | 5650 | SCTP_BUF_LEN(mm) = sizeof(*phd); |
michael@0 | 5651 | SCTP_BUF_NEXT(mm) = SCTP_M_COPYM(m, *offset, chk_length, M_NOWAIT); |
michael@0 | 5652 | if (SCTP_BUF_NEXT(mm)) { |
michael@0 | 5653 | if (sctp_pad_lastmbuf(SCTP_BUF_NEXT(mm), SCTP_SIZE32(chk_length) - chk_length, NULL)) { |
michael@0 | 5654 | sctp_m_freem(mm); |
michael@0 | 5655 | } else { |
michael@0 | 5656 | #ifdef SCTP_MBUF_LOGGING |
michael@0 | 5657 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) { |
michael@0 | 5658 | struct mbuf *mat; |
michael@0 | 5659 | |
michael@0 | 5660 | for (mat = SCTP_BUF_NEXT(mm); mat; mat = SCTP_BUF_NEXT(mat)) { |
michael@0 | 5661 | if (SCTP_BUF_IS_EXTENDED(mat)) { |
michael@0 | 5662 | sctp_log_mb(mat, SCTP_MBUF_ICOPY); |
michael@0 | 5663 | } |
michael@0 | 5664 | } |
michael@0 | 5665 | } |
michael@0 | 5666 | #endif |
michael@0 | 5667 | sctp_queue_op_err(stcb, mm); |
michael@0 | 5668 | } |
michael@0 | 5669 | } else { |
michael@0 | 5670 | sctp_m_freem(mm); |
michael@0 | 5671 | } |
michael@0 | 5672 | } |
michael@0 | 5673 | } |
michael@0 | 5674 | if ((ch->chunk_type & 0x80) == 0) { |
michael@0 | 5675 | /* discard this packet */ |
michael@0 | 5676 | *offset = length; |
michael@0 | 5677 | return (stcb); |
michael@0 | 5678 | } /* else skip this bad chunk and continue... */ |
michael@0 | 5679 | break; |
michael@0 | 5680 | } /* switch (ch->chunk_type) */ |
michael@0 | 5681 | |
michael@0 | 5682 | |
michael@0 | 5683 | next_chunk: |
michael@0 | 5684 | /* get the next chunk */ |
michael@0 | 5685 | *offset += SCTP_SIZE32(chk_length); |
michael@0 | 5686 | if (*offset >= length) { |
michael@0 | 5687 | /* no more data left in the mbuf chain */ |
michael@0 | 5688 | break; |
michael@0 | 5689 | } |
michael@0 | 5690 | ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset, |
michael@0 | 5691 | sizeof(struct sctp_chunkhdr), chunk_buf); |
michael@0 | 5692 | if (ch == NULL) { |
michael@0 | 5693 | if (locked_tcb) { |
michael@0 | 5694 | SCTP_TCB_UNLOCK(locked_tcb); |
michael@0 | 5695 | } |
michael@0 | 5696 | *offset = length; |
michael@0 | 5697 | return (NULL); |
michael@0 | 5698 | } |
michael@0 | 5699 | } /* while */ |
michael@0 | 5700 | |
michael@0 | 5701 | if (asconf_cnt > 0 && stcb != NULL) { |
michael@0 | 5702 | sctp_send_asconf_ack(stcb); |
michael@0 | 5703 | } |
michael@0 | 5704 | return (stcb); |
michael@0 | 5705 | } |
michael@0 | 5706 | |
michael@0 | 5707 | |
michael@0 | 5708 | #ifdef INVARIANTS |
michael@0 | 5709 | #ifdef __GNUC__ |
michael@0 | 5710 | __attribute__((noinline)) |
michael@0 | 5711 | #endif |
michael@0 | 5712 | void |
michael@0 | 5713 | sctp_validate_no_locks(struct sctp_inpcb *inp) |
michael@0 | 5714 | { |
michael@0 | 5715 | #ifndef __APPLE__ |
michael@0 | 5716 | struct sctp_tcb *lstcb; |
michael@0 | 5717 | |
michael@0 | 5718 | LIST_FOREACH(lstcb, &inp->sctp_asoc_list, sctp_tcblist) { |
michael@0 | 5719 | if (mtx_owned(&lstcb->tcb_mtx)) { |
michael@0 | 5720 | panic("Own lock on stcb at return from input"); |
michael@0 | 5721 | } |
michael@0 | 5722 | } |
michael@0 | 5723 | if (mtx_owned(&inp->inp_create_mtx)) { |
michael@0 | 5724 | panic("Own create lock on inp"); |
michael@0 | 5725 | } |
michael@0 | 5726 | if (mtx_owned(&inp->inp_mtx)) { |
michael@0 | 5727 | panic("Own inp lock on inp"); |
michael@0 | 5728 | } |
michael@0 | 5729 | #endif |
michael@0 | 5730 | } |
michael@0 | 5731 | #endif |
michael@0 | 5732 | |
michael@0 | 5733 | /* |
michael@0 | 5734 | * common input chunk processing (v4 and v6) |
michael@0 | 5735 | */ |
michael@0 | 5736 | void |
michael@0 | 5737 | sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int length, |
michael@0 | 5738 | struct sockaddr *src, struct sockaddr *dst, |
michael@0 | 5739 | struct sctphdr *sh, struct sctp_chunkhdr *ch, |
michael@0 | 5740 | #if !defined(SCTP_WITH_NO_CSUM) |
michael@0 | 5741 | uint8_t compute_crc, |
michael@0 | 5742 | #endif |
michael@0 | 5743 | uint8_t ecn_bits, |
michael@0 | 5744 | #if defined(__FreeBSD__) |
michael@0 | 5745 | uint8_t use_mflowid, uint32_t mflowid, |
michael@0 | 5746 | #endif |
michael@0 | 5747 | uint32_t vrf_id, uint16_t port) |
michael@0 | 5748 | { |
michael@0 | 5749 | uint32_t high_tsn; |
michael@0 | 5750 | int fwd_tsn_seen = 0, data_processed = 0; |
michael@0 | 5751 | struct mbuf *m = *mm; |
michael@0 | 5752 | int un_sent; |
michael@0 | 5753 | int cnt_ctrl_ready = 0; |
michael@0 | 5754 | struct sctp_inpcb *inp = NULL, *inp_decr = NULL; |
michael@0 | 5755 | struct sctp_tcb *stcb = NULL; |
michael@0 | 5756 | struct sctp_nets *net = NULL; |
michael@0 | 5757 | |
michael@0 | 5758 | SCTP_STAT_INCR(sctps_recvdatagrams); |
michael@0 | 5759 | #ifdef SCTP_AUDITING_ENABLED |
michael@0 | 5760 | sctp_audit_log(0xE0, 1); |
michael@0 | 5761 | sctp_auditing(0, inp, stcb, net); |
michael@0 | 5762 | #endif |
michael@0 | 5763 | #if !defined(SCTP_WITH_NO_CSUM) |
michael@0 | 5764 | if (compute_crc != 0) { |
michael@0 | 5765 | uint32_t check, calc_check; |
michael@0 | 5766 | |
michael@0 | 5767 | check = sh->checksum; |
michael@0 | 5768 | sh->checksum = 0; |
michael@0 | 5769 | calc_check = sctp_calculate_cksum(m, iphlen); |
michael@0 | 5770 | sh->checksum = check; |
michael@0 | 5771 | if (calc_check != check) { |
michael@0 | 5772 | SCTPDBG(SCTP_DEBUG_INPUT1, "Bad CSUM on SCTP packet calc_check:%x check:%x m:%p mlen:%d iphlen:%d\n", |
michael@0 | 5773 | calc_check, check, (void *)m, length, iphlen); |
michael@0 | 5774 | stcb = sctp_findassociation_addr(m, offset, src, dst, |
michael@0 | 5775 | sh, ch, &inp, &net, vrf_id); |
michael@0 | 5776 | if ((net != NULL) && (port != 0)) { |
michael@0 | 5777 | if (net->port == 0) { |
michael@0 | 5778 | sctp_pathmtu_adjustment(stcb, net->mtu - sizeof(struct udphdr)); |
michael@0 | 5779 | } |
michael@0 | 5780 | net->port = port; |
michael@0 | 5781 | } |
michael@0 | 5782 | #if defined(__FreeBSD__) |
michael@0 | 5783 | if ((net != NULL) && (use_mflowid != 0)) { |
michael@0 | 5784 | net->flowid = mflowid; |
michael@0 | 5785 | #ifdef INVARIANTS |
michael@0 | 5786 | net->flowidset = 1; |
michael@0 | 5787 | #endif |
michael@0 | 5788 | } |
michael@0 | 5789 | #endif |
michael@0 | 5790 | if ((inp != NULL) && (stcb != NULL)) { |
michael@0 | 5791 | sctp_send_packet_dropped(stcb, net, m, length, iphlen, 1); |
michael@0 | 5792 | sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED); |
michael@0 | 5793 | } else if ((inp != NULL) && (stcb == NULL)) { |
michael@0 | 5794 | inp_decr = inp; |
michael@0 | 5795 | } |
michael@0 | 5796 | SCTP_STAT_INCR(sctps_badsum); |
michael@0 | 5797 | SCTP_STAT_INCR_COUNTER32(sctps_checksumerrors); |
michael@0 | 5798 | goto out; |
michael@0 | 5799 | } |
michael@0 | 5800 | } |
michael@0 | 5801 | #endif |
michael@0 | 5802 | /* Destination port of 0 is illegal, based on RFC4960. */ |
michael@0 | 5803 | if (sh->dest_port == 0) { |
michael@0 | 5804 | SCTP_STAT_INCR(sctps_hdrops); |
michael@0 | 5805 | goto out; |
michael@0 | 5806 | } |
michael@0 | 5807 | stcb = sctp_findassociation_addr(m, offset, src, dst, |
michael@0 | 5808 | sh, ch, &inp, &net, vrf_id); |
michael@0 | 5809 | if ((net != NULL) && (port != 0)) { |
michael@0 | 5810 | if (net->port == 0) { |
michael@0 | 5811 | sctp_pathmtu_adjustment(stcb, net->mtu - sizeof(struct udphdr)); |
michael@0 | 5812 | } |
michael@0 | 5813 | net->port = port; |
michael@0 | 5814 | } |
michael@0 | 5815 | #if defined(__FreeBSD__) |
michael@0 | 5816 | if ((net != NULL) && (use_mflowid != 0)) { |
michael@0 | 5817 | net->flowid = mflowid; |
michael@0 | 5818 | #ifdef INVARIANTS |
michael@0 | 5819 | net->flowidset = 1; |
michael@0 | 5820 | #endif |
michael@0 | 5821 | } |
michael@0 | 5822 | #endif |
michael@0 | 5823 | if (inp == NULL) { |
michael@0 | 5824 | SCTP_STAT_INCR(sctps_noport); |
michael@0 | 5825 | #if defined(__FreeBSD__) && (((__FreeBSD_version < 900000) && (__FreeBSD_version >= 804000)) || (__FreeBSD_version > 900000)) |
michael@0 | 5826 | if (badport_bandlim(BANDLIM_SCTP_OOTB) < 0) { |
michael@0 | 5827 | goto out; |
michael@0 | 5828 | } |
michael@0 | 5829 | #endif |
michael@0 | 5830 | if (ch->chunk_type == SCTP_SHUTDOWN_ACK) { |
michael@0 | 5831 | sctp_send_shutdown_complete2(src, dst, sh, |
michael@0 | 5832 | #if defined(__FreeBSD__) |
michael@0 | 5833 | use_mflowid, mflowid, |
michael@0 | 5834 | #endif |
michael@0 | 5835 | vrf_id, port); |
michael@0 | 5836 | goto out; |
michael@0 | 5837 | } |
michael@0 | 5838 | if (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) { |
michael@0 | 5839 | goto out; |
michael@0 | 5840 | } |
michael@0 | 5841 | if (ch->chunk_type != SCTP_ABORT_ASSOCIATION) { |
michael@0 | 5842 | if ((SCTP_BASE_SYSCTL(sctp_blackhole) == 0) || |
michael@0 | 5843 | ((SCTP_BASE_SYSCTL(sctp_blackhole) == 1) && |
michael@0 | 5844 | (ch->chunk_type != SCTP_INIT))) { |
michael@0 | 5845 | sctp_send_abort(m, iphlen, src, dst, |
michael@0 | 5846 | sh, 0, NULL, |
michael@0 | 5847 | #if defined(__FreeBSD__) |
michael@0 | 5848 | use_mflowid, mflowid, |
michael@0 | 5849 | #endif |
michael@0 | 5850 | vrf_id, port); |
michael@0 | 5851 | } |
michael@0 | 5852 | } |
michael@0 | 5853 | goto out; |
michael@0 | 5854 | } else if (stcb == NULL) { |
michael@0 | 5855 | inp_decr = inp; |
michael@0 | 5856 | } |
michael@0 | 5857 | #ifdef IPSEC |
michael@0 | 5858 | /*- |
michael@0 | 5859 | * I very much doubt any of the IPSEC stuff will work but I have no |
michael@0 | 5860 | * idea, so I will leave it in place. |
michael@0 | 5861 | */ |
michael@0 | 5862 | if (inp != NULL) { |
michael@0 | 5863 | switch (dst->sa_family) { |
michael@0 | 5864 | #ifdef INET |
michael@0 | 5865 | case AF_INET: |
michael@0 | 5866 | if (ipsec4_in_reject(m, &inp->ip_inp.inp)) { |
michael@0 | 5867 | #if defined(__FreeBSD__) && (__FreeBSD_version > 1000036) |
michael@0 | 5868 | IPSECSTAT_INC(ips_in_polvio); |
michael@0 | 5869 | #else |
michael@0 | 5870 | MODULE_GLOBAL(ipsec4stat).in_polvio++; |
michael@0 | 5871 | #endif |
michael@0 | 5872 | SCTP_STAT_INCR(sctps_hdrops); |
michael@0 | 5873 | goto out; |
michael@0 | 5874 | } |
michael@0 | 5875 | break; |
michael@0 | 5876 | #endif |
michael@0 | 5877 | #ifdef INET6 |
michael@0 | 5878 | case AF_INET6: |
michael@0 | 5879 | if (ipsec6_in_reject(m, &inp->ip_inp.inp)) { |
michael@0 | 5880 | #if defined(__FreeBSD__) && (__FreeBSD_version > 1000036) |
michael@0 | 5881 | IPSEC6STAT_INC(ips_in_polvio); |
michael@0 | 5882 | #else |
michael@0 | 5883 | MODULE_GLOBAL(ipsec6stat).in_polvio++; |
michael@0 | 5884 | #endif |
michael@0 | 5885 | SCTP_STAT_INCR(sctps_hdrops); |
michael@0 | 5886 | goto out; |
michael@0 | 5887 | } |
michael@0 | 5888 | break; |
michael@0 | 5889 | #endif |
michael@0 | 5890 | default: |
michael@0 | 5891 | break; |
michael@0 | 5892 | } |
michael@0 | 5893 | } |
michael@0 | 5894 | #endif |
michael@0 | 5895 | SCTPDBG(SCTP_DEBUG_INPUT1, "Ok, Common input processing called, m:%p iphlen:%d offset:%d length:%d stcb:%p\n", |
michael@0 | 5896 | (void *)m, iphlen, offset, length, (void *)stcb); |
michael@0 | 5897 | if (stcb) { |
michael@0 | 5898 | /* always clear this before beginning a packet */ |
michael@0 | 5899 | stcb->asoc.authenticated = 0; |
michael@0 | 5900 | stcb->asoc.seen_a_sack_this_pkt = 0; |
michael@0 | 5901 | SCTPDBG(SCTP_DEBUG_INPUT1, "stcb:%p state:%x\n", |
michael@0 | 5902 | (void *)stcb, stcb->asoc.state); |
michael@0 | 5903 | |
michael@0 | 5904 | if ((stcb->asoc.state & SCTP_STATE_WAS_ABORTED) || |
michael@0 | 5905 | (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED)) { |
michael@0 | 5906 | /*- |
michael@0 | 5907 | * If we hit here, we had a ref count |
michael@0 | 5908 | * up when the assoc was aborted and the |
michael@0 | 5909 | * timer is clearing out the assoc, we should |
michael@0 | 5910 | * NOT respond to any packet.. its OOTB. |
michael@0 | 5911 | */ |
michael@0 | 5912 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 5913 | stcb = NULL; |
michael@0 | 5914 | sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, |
michael@0 | 5915 | #if defined(__FreeBSD__) |
michael@0 | 5916 | use_mflowid, mflowid, |
michael@0 | 5917 | #endif |
michael@0 | 5918 | vrf_id, port); |
michael@0 | 5919 | goto out; |
michael@0 | 5920 | } |
michael@0 | 5921 | |
michael@0 | 5922 | } |
michael@0 | 5923 | if (IS_SCTP_CONTROL(ch)) { |
michael@0 | 5924 | /* process the control portion of the SCTP packet */ |
michael@0 | 5925 | /* sa_ignore NO_NULL_CHK */ |
michael@0 | 5926 | stcb = sctp_process_control(m, iphlen, &offset, length, |
michael@0 | 5927 | src, dst, sh, ch, |
michael@0 | 5928 | inp, stcb, &net, &fwd_tsn_seen, |
michael@0 | 5929 | #if defined(__FreeBSD__) |
michael@0 | 5930 | use_mflowid, mflowid, |
michael@0 | 5931 | #endif |
michael@0 | 5932 | vrf_id, port); |
michael@0 | 5933 | if (stcb) { |
michael@0 | 5934 | /* This covers us if the cookie-echo was there |
michael@0 | 5935 | * and it changes our INP. |
michael@0 | 5936 | */ |
michael@0 | 5937 | inp = stcb->sctp_ep; |
michael@0 | 5938 | if ((net) && (port)) { |
michael@0 | 5939 | if (net->port == 0) { |
michael@0 | 5940 | sctp_pathmtu_adjustment(stcb, net->mtu - sizeof(struct udphdr)); |
michael@0 | 5941 | } |
michael@0 | 5942 | net->port = port; |
michael@0 | 5943 | } |
michael@0 | 5944 | } |
michael@0 | 5945 | } else { |
michael@0 | 5946 | /* |
michael@0 | 5947 | * no control chunks, so pre-process DATA chunks (these |
michael@0 | 5948 | * checks are taken care of by control processing) |
michael@0 | 5949 | */ |
michael@0 | 5950 | |
michael@0 | 5951 | /* |
michael@0 | 5952 | * if DATA only packet, and auth is required, then punt... |
michael@0 | 5953 | * can't have authenticated without any AUTH (control) |
michael@0 | 5954 | * chunks |
michael@0 | 5955 | */ |
michael@0 | 5956 | if ((stcb != NULL) && |
michael@0 | 5957 | !SCTP_BASE_SYSCTL(sctp_auth_disable) && |
michael@0 | 5958 | sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.local_auth_chunks)) { |
michael@0 | 5959 | /* "silently" ignore */ |
michael@0 | 5960 | SCTP_STAT_INCR(sctps_recvauthmissing); |
michael@0 | 5961 | goto out; |
michael@0 | 5962 | } |
michael@0 | 5963 | if (stcb == NULL) { |
michael@0 | 5964 | /* out of the blue DATA chunk */ |
michael@0 | 5965 | sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, |
michael@0 | 5966 | #if defined(__FreeBSD__) |
michael@0 | 5967 | use_mflowid, mflowid, |
michael@0 | 5968 | #endif |
michael@0 | 5969 | vrf_id, port); |
michael@0 | 5970 | goto out; |
michael@0 | 5971 | } |
michael@0 | 5972 | if (stcb->asoc.my_vtag != ntohl(sh->v_tag)) { |
michael@0 | 5973 | /* v_tag mismatch! */ |
michael@0 | 5974 | SCTP_STAT_INCR(sctps_badvtag); |
michael@0 | 5975 | goto out; |
michael@0 | 5976 | } |
michael@0 | 5977 | } |
michael@0 | 5978 | |
michael@0 | 5979 | if (stcb == NULL) { |
michael@0 | 5980 | /* |
michael@0 | 5981 | * no valid TCB for this packet, or we found it's a bad |
michael@0 | 5982 | * packet while processing control, or we're done with this |
michael@0 | 5983 | * packet (done or skip rest of data), so we drop it... |
michael@0 | 5984 | */ |
michael@0 | 5985 | goto out; |
michael@0 | 5986 | } |
michael@0 | 5987 | |
michael@0 | 5988 | /* |
michael@0 | 5989 | * DATA chunk processing |
michael@0 | 5990 | */ |
michael@0 | 5991 | /* plow through the data chunks while length > offset */ |
michael@0 | 5992 | |
michael@0 | 5993 | /* |
michael@0 | 5994 | * Rest should be DATA only. Check authentication state if AUTH for |
michael@0 | 5995 | * DATA is required. |
michael@0 | 5996 | */ |
michael@0 | 5997 | if ((length > offset) && |
michael@0 | 5998 | (stcb != NULL) && |
michael@0 | 5999 | !SCTP_BASE_SYSCTL(sctp_auth_disable) && |
michael@0 | 6000 | sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.local_auth_chunks) && |
michael@0 | 6001 | !stcb->asoc.authenticated) { |
michael@0 | 6002 | /* "silently" ignore */ |
michael@0 | 6003 | SCTP_STAT_INCR(sctps_recvauthmissing); |
michael@0 | 6004 | SCTPDBG(SCTP_DEBUG_AUTH1, |
michael@0 | 6005 | "Data chunk requires AUTH, skipped\n"); |
michael@0 | 6006 | goto trigger_send; |
michael@0 | 6007 | } |
michael@0 | 6008 | if (length > offset) { |
michael@0 | 6009 | int retval; |
michael@0 | 6010 | |
michael@0 | 6011 | /* |
michael@0 | 6012 | * First check to make sure our state is correct. We would |
michael@0 | 6013 | * not get here unless we really did have a tag, so we don't |
michael@0 | 6014 | * abort if this happens, just dump the chunk silently. |
michael@0 | 6015 | */ |
michael@0 | 6016 | switch (SCTP_GET_STATE(&stcb->asoc)) { |
michael@0 | 6017 | case SCTP_STATE_COOKIE_ECHOED: |
michael@0 | 6018 | /* |
michael@0 | 6019 | * we consider data with valid tags in this state |
michael@0 | 6020 | * shows us the cookie-ack was lost. Imply it was |
michael@0 | 6021 | * there. |
michael@0 | 6022 | */ |
michael@0 | 6023 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) { |
michael@0 | 6024 | sctp_misc_ints(SCTP_THRESHOLD_CLEAR, |
michael@0 | 6025 | stcb->asoc.overall_error_count, |
michael@0 | 6026 | 0, |
michael@0 | 6027 | SCTP_FROM_SCTP_INPUT, |
michael@0 | 6028 | __LINE__); |
michael@0 | 6029 | } |
michael@0 | 6030 | stcb->asoc.overall_error_count = 0; |
michael@0 | 6031 | sctp_handle_cookie_ack((struct sctp_cookie_ack_chunk *)ch, stcb, net); |
michael@0 | 6032 | break; |
michael@0 | 6033 | case SCTP_STATE_COOKIE_WAIT: |
michael@0 | 6034 | /* |
michael@0 | 6035 | * We consider OOTB any data sent during asoc setup. |
michael@0 | 6036 | */ |
michael@0 | 6037 | sctp_handle_ootb(m, iphlen, offset, src, dst, sh, inp, |
michael@0 | 6038 | #if defined(__FreeBSD__) |
michael@0 | 6039 | use_mflowid, mflowid, |
michael@0 | 6040 | #endif |
michael@0 | 6041 | vrf_id, port); |
michael@0 | 6042 | goto out; |
michael@0 | 6043 | /*sa_ignore NOTREACHED*/ |
michael@0 | 6044 | break; |
michael@0 | 6045 | case SCTP_STATE_EMPTY: /* should not happen */ |
michael@0 | 6046 | case SCTP_STATE_INUSE: /* should not happen */ |
michael@0 | 6047 | case SCTP_STATE_SHUTDOWN_RECEIVED: /* This is a peer error */ |
michael@0 | 6048 | case SCTP_STATE_SHUTDOWN_ACK_SENT: |
michael@0 | 6049 | default: |
michael@0 | 6050 | goto out; |
michael@0 | 6051 | /*sa_ignore NOTREACHED*/ |
michael@0 | 6052 | break; |
michael@0 | 6053 | case SCTP_STATE_OPEN: |
michael@0 | 6054 | case SCTP_STATE_SHUTDOWN_SENT: |
michael@0 | 6055 | break; |
michael@0 | 6056 | } |
michael@0 | 6057 | /* plow through the data chunks while length > offset */ |
michael@0 | 6058 | retval = sctp_process_data(mm, iphlen, &offset, length, |
michael@0 | 6059 | src, dst, sh, |
michael@0 | 6060 | inp, stcb, net, &high_tsn, |
michael@0 | 6061 | #if defined(__FreeBSD__) |
michael@0 | 6062 | use_mflowid, mflowid, |
michael@0 | 6063 | #endif |
michael@0 | 6064 | vrf_id, port); |
michael@0 | 6065 | if (retval == 2) { |
michael@0 | 6066 | /* |
michael@0 | 6067 | * The association aborted, NO UNLOCK needed since |
michael@0 | 6068 | * the association is destroyed. |
michael@0 | 6069 | */ |
michael@0 | 6070 | stcb = NULL; |
michael@0 | 6071 | goto out; |
michael@0 | 6072 | } |
michael@0 | 6073 | data_processed = 1; |
michael@0 | 6074 | /* |
michael@0 | 6075 | * Anything important needs to have been m_copy'ed in |
michael@0 | 6076 | * process_data |
michael@0 | 6077 | */ |
michael@0 | 6078 | } |
michael@0 | 6079 | |
michael@0 | 6080 | /* take care of ecn */ |
michael@0 | 6081 | if ((data_processed == 1) && |
michael@0 | 6082 | (stcb->asoc.ecn_allowed == 1) && |
michael@0 | 6083 | ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS)) { |
michael@0 | 6084 | /* Yep, we need to add a ECNE */ |
michael@0 | 6085 | sctp_send_ecn_echo(stcb, net, high_tsn); |
michael@0 | 6086 | } |
michael@0 | 6087 | |
michael@0 | 6088 | if ((data_processed == 0) && (fwd_tsn_seen)) { |
michael@0 | 6089 | int was_a_gap; |
michael@0 | 6090 | uint32_t highest_tsn; |
michael@0 | 6091 | |
michael@0 | 6092 | if (SCTP_TSN_GT(stcb->asoc.highest_tsn_inside_nr_map, stcb->asoc.highest_tsn_inside_map)) { |
michael@0 | 6093 | highest_tsn = stcb->asoc.highest_tsn_inside_nr_map; |
michael@0 | 6094 | } else { |
michael@0 | 6095 | highest_tsn = stcb->asoc.highest_tsn_inside_map; |
michael@0 | 6096 | } |
michael@0 | 6097 | was_a_gap = SCTP_TSN_GT(highest_tsn, stcb->asoc.cumulative_tsn); |
michael@0 | 6098 | stcb->asoc.send_sack = 1; |
michael@0 | 6099 | sctp_sack_check(stcb, was_a_gap); |
michael@0 | 6100 | } else if (fwd_tsn_seen) { |
michael@0 | 6101 | stcb->asoc.send_sack = 1; |
michael@0 | 6102 | } |
michael@0 | 6103 | /* trigger send of any chunks in queue... */ |
michael@0 | 6104 | trigger_send: |
michael@0 | 6105 | #ifdef SCTP_AUDITING_ENABLED |
michael@0 | 6106 | sctp_audit_log(0xE0, 2); |
michael@0 | 6107 | sctp_auditing(1, inp, stcb, net); |
michael@0 | 6108 | #endif |
michael@0 | 6109 | SCTPDBG(SCTP_DEBUG_INPUT1, |
michael@0 | 6110 | "Check for chunk output prw:%d tqe:%d tf=%d\n", |
michael@0 | 6111 | stcb->asoc.peers_rwnd, |
michael@0 | 6112 | TAILQ_EMPTY(&stcb->asoc.control_send_queue), |
michael@0 | 6113 | stcb->asoc.total_flight); |
michael@0 | 6114 | un_sent = (stcb->asoc.total_output_queue_size - stcb->asoc.total_flight); |
michael@0 | 6115 | if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue)) { |
michael@0 | 6116 | cnt_ctrl_ready = stcb->asoc.ctrl_queue_cnt - stcb->asoc.ecn_echo_cnt_onq; |
michael@0 | 6117 | } |
michael@0 | 6118 | if (cnt_ctrl_ready || |
michael@0 | 6119 | ((un_sent) && |
michael@0 | 6120 | (stcb->asoc.peers_rwnd > 0 || |
michael@0 | 6121 | (stcb->asoc.peers_rwnd <= 0 && stcb->asoc.total_flight == 0)))) { |
michael@0 | 6122 | SCTPDBG(SCTP_DEBUG_INPUT3, "Calling chunk OUTPUT\n"); |
michael@0 | 6123 | sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED); |
michael@0 | 6124 | SCTPDBG(SCTP_DEBUG_INPUT3, "chunk OUTPUT returns\n"); |
michael@0 | 6125 | } |
michael@0 | 6126 | #ifdef SCTP_AUDITING_ENABLED |
michael@0 | 6127 | sctp_audit_log(0xE0, 3); |
michael@0 | 6128 | sctp_auditing(2, inp, stcb, net); |
michael@0 | 6129 | #endif |
michael@0 | 6130 | out: |
michael@0 | 6131 | if (stcb != NULL) { |
michael@0 | 6132 | SCTP_TCB_UNLOCK(stcb); |
michael@0 | 6133 | } |
michael@0 | 6134 | if (inp_decr != NULL) { |
michael@0 | 6135 | /* reduce ref-count */ |
michael@0 | 6136 | SCTP_INP_WLOCK(inp_decr); |
michael@0 | 6137 | SCTP_INP_DECR_REF(inp_decr); |
michael@0 | 6138 | SCTP_INP_WUNLOCK(inp_decr); |
michael@0 | 6139 | } |
michael@0 | 6140 | #ifdef INVARIANTS |
michael@0 | 6141 | if (inp != NULL) { |
michael@0 | 6142 | sctp_validate_no_locks(inp); |
michael@0 | 6143 | } |
michael@0 | 6144 | #endif |
michael@0 | 6145 | return; |
michael@0 | 6146 | } |
michael@0 | 6147 | |
michael@0 | 6148 | #if 0 |
michael@0 | 6149 | static void |
michael@0 | 6150 | sctp_print_mbuf_chain(struct mbuf *m) |
michael@0 | 6151 | { |
michael@0 | 6152 | for (; m; m = SCTP_BUF_NEXT(m)) { |
michael@0 | 6153 | SCTP_PRINTF("%p: m_len = %ld\n", (void *)m, SCTP_BUF_LEN(m)); |
michael@0 | 6154 | if (SCTP_BUF_IS_EXTENDED(m)) |
michael@0 | 6155 | SCTP_PRINTF("%p: extend_size = %d\n", (void *)m, SCTP_BUF_EXTEND_SIZE(m)); |
michael@0 | 6156 | } |
michael@0 | 6157 | } |
michael@0 | 6158 | #endif |
michael@0 | 6159 | |
michael@0 | 6160 | #ifdef INET |
michael@0 | 6161 | #if !defined(__Userspace__) |
michael@0 | 6162 | #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__) |
michael@0 | 6163 | void |
michael@0 | 6164 | sctp_input_with_port(struct mbuf *i_pak, int off, uint16_t port) |
michael@0 | 6165 | #elif defined(__Panda__) |
michael@0 | 6166 | void |
michael@0 | 6167 | sctp_input(pakhandle_type i_pak) |
michael@0 | 6168 | #else |
michael@0 | 6169 | void |
michael@0 | 6170 | #if __STDC__ |
michael@0 | 6171 | sctp_input(struct mbuf *i_pak,...) |
michael@0 | 6172 | #else |
michael@0 | 6173 | sctp_input(i_pak, va_alist) |
michael@0 | 6174 | struct mbuf *i_pak; |
michael@0 | 6175 | #endif |
michael@0 | 6176 | #endif |
michael@0 | 6177 | { |
michael@0 | 6178 | struct mbuf *m; |
michael@0 | 6179 | int iphlen; |
michael@0 | 6180 | uint32_t vrf_id = 0; |
michael@0 | 6181 | uint8_t ecn_bits; |
michael@0 | 6182 | struct sockaddr_in src, dst; |
michael@0 | 6183 | struct ip *ip; |
michael@0 | 6184 | struct sctphdr *sh; |
michael@0 | 6185 | struct sctp_chunkhdr *ch; |
michael@0 | 6186 | int length, offset; |
michael@0 | 6187 | #if !defined(SCTP_WITH_NO_CSUM) |
michael@0 | 6188 | uint8_t compute_crc; |
michael@0 | 6189 | #endif |
michael@0 | 6190 | #if defined(__FreeBSD__) |
michael@0 | 6191 | uint32_t mflowid; |
michael@0 | 6192 | uint8_t use_mflowid; |
michael@0 | 6193 | #endif |
michael@0 | 6194 | #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__Windows__)) |
michael@0 | 6195 | uint16_t port = 0; |
michael@0 | 6196 | #endif |
michael@0 | 6197 | |
michael@0 | 6198 | #if defined(__Panda__) |
michael@0 | 6199 | /* This is Evil, but its the only way to make panda work right. */ |
michael@0 | 6200 | iphlen = sizeof(struct ip); |
michael@0 | 6201 | #else |
michael@0 | 6202 | iphlen = off; |
michael@0 | 6203 | #endif |
michael@0 | 6204 | if (SCTP_GET_PKT_VRFID(i_pak, vrf_id)) { |
michael@0 | 6205 | SCTP_RELEASE_PKT(i_pak); |
michael@0 | 6206 | return; |
michael@0 | 6207 | } |
michael@0 | 6208 | m = SCTP_HEADER_TO_CHAIN(i_pak); |
michael@0 | 6209 | #ifdef __Panda__ |
michael@0 | 6210 | SCTP_DETACH_HEADER_FROM_CHAIN(i_pak); |
michael@0 | 6211 | (void)SCTP_RELEASE_HEADER(i_pak); |
michael@0 | 6212 | #endif |
michael@0 | 6213 | #ifdef SCTP_MBUF_LOGGING |
michael@0 | 6214 | /* Log in any input mbufs */ |
michael@0 | 6215 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) { |
michael@0 | 6216 | struct mbuf *mat; |
michael@0 | 6217 | |
michael@0 | 6218 | for (mat = m; mat; mat = SCTP_BUF_NEXT(mat)) { |
michael@0 | 6219 | if (SCTP_BUF_IS_EXTENDED(mat)) { |
michael@0 | 6220 | sctp_log_mb(mat, SCTP_MBUF_INPUT); |
michael@0 | 6221 | } |
michael@0 | 6222 | } |
michael@0 | 6223 | } |
michael@0 | 6224 | #endif |
michael@0 | 6225 | #ifdef SCTP_PACKET_LOGGING |
michael@0 | 6226 | if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING) { |
michael@0 | 6227 | sctp_packet_log(m); |
michael@0 | 6228 | } |
michael@0 | 6229 | #endif |
michael@0 | 6230 | #if defined(__FreeBSD__) |
michael@0 | 6231 | #if __FreeBSD_version > 1000049 |
michael@0 | 6232 | SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, |
michael@0 | 6233 | "sctp_input(): Packet of length %d received on %s with csum_flags 0x%b.\n", |
michael@0 | 6234 | m->m_pkthdr.len, |
michael@0 | 6235 | if_name(m->m_pkthdr.rcvif), |
michael@0 | 6236 | (int)m->m_pkthdr.csum_flags, CSUM_BITS); |
michael@0 | 6237 | #elif __FreeBSD_version >= 800000 |
michael@0 | 6238 | SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, |
michael@0 | 6239 | "sctp_input(): Packet of length %d received on %s with csum_flags 0x%x.\n", |
michael@0 | 6240 | m->m_pkthdr.len, |
michael@0 | 6241 | if_name(m->m_pkthdr.rcvif), |
michael@0 | 6242 | m->m_pkthdr.csum_flags); |
michael@0 | 6243 | #else |
michael@0 | 6244 | SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, |
michael@0 | 6245 | "sctp_input(): Packet of length %d received on %s with csum_flags 0x%x.\n", |
michael@0 | 6246 | m->m_pkthdr.len, |
michael@0 | 6247 | m->m_pkthdr.rcvif->if_xname, |
michael@0 | 6248 | m->m_pkthdr.csum_flags); |
michael@0 | 6249 | #endif |
michael@0 | 6250 | #endif |
michael@0 | 6251 | #if defined(__APPLE__) |
michael@0 | 6252 | SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, |
michael@0 | 6253 | "sctp_input(): Packet of length %d received on %s%d with csum_flags 0x%x.\n", |
michael@0 | 6254 | m->m_pkthdr.len, |
michael@0 | 6255 | m->m_pkthdr.rcvif->if_name, |
michael@0 | 6256 | m->m_pkthdr.rcvif->if_unit, |
michael@0 | 6257 | m->m_pkthdr.csum_flags); |
michael@0 | 6258 | #endif |
michael@0 | 6259 | #if defined(__Windows__) |
michael@0 | 6260 | SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, |
michael@0 | 6261 | "sctp_input(): Packet of length %d received on %s with csum_flags 0x%x.\n", |
michael@0 | 6262 | m->m_pkthdr.len, |
michael@0 | 6263 | m->m_pkthdr.rcvif->if_xname, |
michael@0 | 6264 | m->m_pkthdr.csum_flags); |
michael@0 | 6265 | #endif |
michael@0 | 6266 | #if defined(__FreeBSD__) |
michael@0 | 6267 | if (m->m_flags & M_FLOWID) { |
michael@0 | 6268 | mflowid = m->m_pkthdr.flowid; |
michael@0 | 6269 | use_mflowid = 1; |
michael@0 | 6270 | } else { |
michael@0 | 6271 | mflowid = 0; |
michael@0 | 6272 | use_mflowid = 0; |
michael@0 | 6273 | } |
michael@0 | 6274 | #endif |
michael@0 | 6275 | SCTP_STAT_INCR(sctps_recvpackets); |
michael@0 | 6276 | SCTP_STAT_INCR_COUNTER64(sctps_inpackets); |
michael@0 | 6277 | /* Get IP, SCTP, and first chunk header together in the first mbuf. */ |
michael@0 | 6278 | offset = iphlen + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr); |
michael@0 | 6279 | if (SCTP_BUF_LEN(m) < offset) { |
michael@0 | 6280 | if ((m = m_pullup(m, offset)) == NULL) { |
michael@0 | 6281 | SCTP_STAT_INCR(sctps_hdrops); |
michael@0 | 6282 | return; |
michael@0 | 6283 | } |
michael@0 | 6284 | } |
michael@0 | 6285 | ip = mtod(m, struct ip *); |
michael@0 | 6286 | sh = (struct sctphdr *)((caddr_t)ip + iphlen); |
michael@0 | 6287 | ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(struct sctphdr)); |
michael@0 | 6288 | offset -= sizeof(struct sctp_chunkhdr); |
michael@0 | 6289 | memset(&src, 0, sizeof(struct sockaddr_in)); |
michael@0 | 6290 | src.sin_family = AF_INET; |
michael@0 | 6291 | #ifdef HAVE_SIN_LEN |
michael@0 | 6292 | src.sin_len = sizeof(struct sockaddr_in); |
michael@0 | 6293 | #endif |
michael@0 | 6294 | src.sin_port = sh->src_port; |
michael@0 | 6295 | src.sin_addr = ip->ip_src; |
michael@0 | 6296 | memset(&dst, 0, sizeof(struct sockaddr_in)); |
michael@0 | 6297 | dst.sin_family = AF_INET; |
michael@0 | 6298 | #ifdef HAVE_SIN_LEN |
michael@0 | 6299 | dst.sin_len = sizeof(struct sockaddr_in); |
michael@0 | 6300 | #endif |
michael@0 | 6301 | dst.sin_port = sh->dest_port; |
michael@0 | 6302 | dst.sin_addr = ip->ip_dst; |
michael@0 | 6303 | #if defined(__Windows__) |
michael@0 | 6304 | NTOHS(ip->ip_len); |
michael@0 | 6305 | #endif |
michael@0 | 6306 | #if defined(__Userspace_os_Linux) || defined(__Userspace_os_Windows) |
michael@0 | 6307 | ip->ip_len = ntohs(ip->ip_len); |
michael@0 | 6308 | #endif |
michael@0 | 6309 | #if defined(__FreeBSD__) |
michael@0 | 6310 | #if __FreeBSD_version >= 1000000 |
michael@0 | 6311 | length = ntohs(ip->ip_len); |
michael@0 | 6312 | #else |
michael@0 | 6313 | length = ip->ip_len + iphlen; |
michael@0 | 6314 | #endif |
michael@0 | 6315 | #elif defined(__APPLE__) |
michael@0 | 6316 | length = ip->ip_len + iphlen; |
michael@0 | 6317 | #elif defined(__Userspace__) |
michael@0 | 6318 | #if defined(__Userspace_os_Linux) || defined(__Userspace_os_Windows) |
michael@0 | 6319 | length = ip->ip_len; |
michael@0 | 6320 | #else |
michael@0 | 6321 | length = ip->ip_len + iphlen; |
michael@0 | 6322 | #endif |
michael@0 | 6323 | #else |
michael@0 | 6324 | length = ip->ip_len; |
michael@0 | 6325 | #endif |
michael@0 | 6326 | /* Validate mbuf chain length with IP payload length. */ |
michael@0 | 6327 | if (SCTP_HEADER_LEN(m) != length) { |
michael@0 | 6328 | SCTPDBG(SCTP_DEBUG_INPUT1, |
michael@0 | 6329 | "sctp_input() length:%d reported length:%d\n", length, SCTP_HEADER_LEN(m)); |
michael@0 | 6330 | SCTP_STAT_INCR(sctps_hdrops); |
michael@0 | 6331 | goto out; |
michael@0 | 6332 | } |
michael@0 | 6333 | /* SCTP does not allow broadcasts or multicasts */ |
michael@0 | 6334 | if (IN_MULTICAST(ntohl(dst.sin_addr.s_addr))) { |
michael@0 | 6335 | goto out; |
michael@0 | 6336 | } |
michael@0 | 6337 | if (SCTP_IS_IT_BROADCAST(dst.sin_addr, m)) { |
michael@0 | 6338 | goto out; |
michael@0 | 6339 | } |
michael@0 | 6340 | ecn_bits = ip->ip_tos; |
michael@0 | 6341 | #if defined(SCTP_WITH_NO_CSUM) |
michael@0 | 6342 | SCTP_STAT_INCR(sctps_recvnocrc); |
michael@0 | 6343 | #else |
michael@0 | 6344 | #if defined(__FreeBSD__) && __FreeBSD_version >= 800000 |
michael@0 | 6345 | if (m->m_pkthdr.csum_flags & CSUM_SCTP_VALID) { |
michael@0 | 6346 | SCTP_STAT_INCR(sctps_recvhwcrc); |
michael@0 | 6347 | compute_crc = 0; |
michael@0 | 6348 | } else { |
michael@0 | 6349 | #else |
michael@0 | 6350 | if (SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback) && |
michael@0 | 6351 | ((src.sin_addr.s_addr == dst.sin_addr.s_addr) || |
michael@0 | 6352 | (SCTP_IS_IT_LOOPBACK(m)))) { |
michael@0 | 6353 | SCTP_STAT_INCR(sctps_recvnocrc); |
michael@0 | 6354 | compute_crc = 0; |
michael@0 | 6355 | } else { |
michael@0 | 6356 | #endif |
michael@0 | 6357 | SCTP_STAT_INCR(sctps_recvswcrc); |
michael@0 | 6358 | compute_crc = 1; |
michael@0 | 6359 | } |
michael@0 | 6360 | #endif |
michael@0 | 6361 | sctp_common_input_processing(&m, iphlen, offset, length, |
michael@0 | 6362 | (struct sockaddr *)&src, |
michael@0 | 6363 | (struct sockaddr *)&dst, |
michael@0 | 6364 | sh, ch, |
michael@0 | 6365 | #if !defined(SCTP_WITH_NO_CSUM) |
michael@0 | 6366 | compute_crc, |
michael@0 | 6367 | #endif |
michael@0 | 6368 | ecn_bits, |
michael@0 | 6369 | #if defined(__FreeBSD__) |
michael@0 | 6370 | use_mflowid, mflowid, |
michael@0 | 6371 | #endif |
michael@0 | 6372 | vrf_id, port); |
michael@0 | 6373 | out: |
michael@0 | 6374 | if (m) { |
michael@0 | 6375 | sctp_m_freem(m); |
michael@0 | 6376 | } |
michael@0 | 6377 | return; |
michael@0 | 6378 | } |
michael@0 | 6379 | |
michael@0 | 6380 | #if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP) |
michael@0 | 6381 | extern int *sctp_cpuarry; |
michael@0 | 6382 | #endif |
michael@0 | 6383 | |
michael@0 | 6384 | void |
michael@0 | 6385 | sctp_input(struct mbuf *m, int off) |
michael@0 | 6386 | { |
michael@0 | 6387 | #if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP) |
michael@0 | 6388 | struct ip *ip; |
michael@0 | 6389 | struct sctphdr *sh; |
michael@0 | 6390 | int offset; |
michael@0 | 6391 | int cpu_to_use; |
michael@0 | 6392 | uint32_t flowid, tag; |
michael@0 | 6393 | |
michael@0 | 6394 | if (mp_ncpus > 1) { |
michael@0 | 6395 | if (m->m_flags & M_FLOWID) { |
michael@0 | 6396 | flowid = m->m_pkthdr.flowid; |
michael@0 | 6397 | } else { |
michael@0 | 6398 | /* No flow id built by lower layers |
michael@0 | 6399 | * fix it so we create one. |
michael@0 | 6400 | */ |
michael@0 | 6401 | offset = off + sizeof(struct sctphdr); |
michael@0 | 6402 | if (SCTP_BUF_LEN(m) < offset) { |
michael@0 | 6403 | if ((m = m_pullup(m, offset)) == NULL) { |
michael@0 | 6404 | SCTP_STAT_INCR(sctps_hdrops); |
michael@0 | 6405 | return; |
michael@0 | 6406 | } |
michael@0 | 6407 | } |
michael@0 | 6408 | ip = mtod(m, struct ip *); |
michael@0 | 6409 | sh = (struct sctphdr *)((caddr_t)ip + off); |
michael@0 | 6410 | tag = htonl(sh->v_tag); |
michael@0 | 6411 | flowid = tag ^ ntohs(sh->dest_port) ^ ntohs(sh->src_port); |
michael@0 | 6412 | m->m_pkthdr.flowid = flowid; |
michael@0 | 6413 | m->m_flags |= M_FLOWID; |
michael@0 | 6414 | } |
michael@0 | 6415 | cpu_to_use = sctp_cpuarry[flowid % mp_ncpus]; |
michael@0 | 6416 | sctp_queue_to_mcore(m, off, cpu_to_use); |
michael@0 | 6417 | return; |
michael@0 | 6418 | } |
michael@0 | 6419 | #endif |
michael@0 | 6420 | sctp_input_with_port(m, off, 0); |
michael@0 | 6421 | } |
michael@0 | 6422 | #endif |
michael@0 | 6423 | #endif |