diff options
author | rrs <rrs@FreeBSD.org> | 2007-08-27 05:19:48 +0000 |
---|---|---|
committer | rrs <rrs@FreeBSD.org> | 2007-08-27 05:19:48 +0000 |
commit | e335457f91efbba1e9ccc24574c0ffc73235bd94 (patch) | |
tree | 5c37015ad6945c94641b6d5d3a589608e280afe1 /sys/netinet/sctp_input.c | |
parent | 3eb0fa1342d0b24e14ff06361e7b8a71c4441b06 (diff) | |
download | FreeBSD-src-e335457f91efbba1e9ccc24574c0ffc73235bd94.zip FreeBSD-src-e335457f91efbba1e9ccc24574c0ffc73235bd94.tar.gz |
- During shutdown pending, when the last sack came in and
the last message on the send stream was "null" but still
there, a state we allow, we could get hung and not clean
it up and wait for the shutdown guard timer to clear the
association without a graceful close. Fix this so that
that we properly clean up.
- Added support for Multiple ASCONF per new RFC. We only
(so far) accept input of these and cannot yet generate
a multi-asconf.
- Sysctl'd support for experimental Fast Handover feature. Always
disabled unless sysctl or socket option changes to enable.
- Error case in add-ip where the peer supports AUTH and ADD-IP
but does NOT require AUTH of ASCONF/ASCONF-ACK. We need to
ABORT in this case.
- According to the Kyoto summit of socket api developers
(Solaris, Linux, BSD). We need to have:
o non-eeor mode messages be atomic - Fixed
o Allow implicit setup of an assoc in 1-2-1 model if
using the sctp_**() send calls - Fixed
o Get rid of HAVE_XXX declarations - Done
o add a sctp_pr_policy in hole in sndrcvinfo structure - Done
o add a PR_SCTP_POLICY_VALID type flag - yet to-do in a future patch!
- Optimize sctp6 calls to reuse code in sctp_usrreq. Also optimize
when we close sending out the data and disabling Nagle.
- Change key concatenation order to match the auth RFC
- When sending OOTB shutdown_complete always do csum.
- Don't send PKT-DROP to a PKT-DROP
- For abort chunks just always checksums same for
shutdown-complete.
- inpcb_free front state had a bug where in queue
data could wedge an assoc. We need to just abandon
ones in front states (free_assoc).
- If a peer sends us a 64k abort, we would try to
assemble a response packet which may be larger than
64k. This then would be dropped by IP. Instead make
a "minimum" size for us 64k-2k (we want at least
2k for our initack). If we receive such an init
discard it early without all the processing.
- When we peel off we must increment the tcb ref count
to keep it from being freed from underneath us.
- handling fwd-tsn had bugs that caused memory overwrites
when given faulty data, fixed so can't happen and we
also stop at the first bad stream no.
- Fixed so comm-up generates the adaption indication.
- peeloff did not get the hmac params copied.
- fix it so we lock the addr list when doing src-addr selection
(in future we need to use a multi-reader/one writer lock here)
- During lowlevel output, we could end up with a _l_addr set
to null if the iterator is calling the output routine. This
means we would possibly crash when we gather the MTU info.
Fix so we only do the gather where we have a src address
cached.
- we need to be sure to set abort flag on conn state when
we receive an abort.
- peeloff could leak a socket. Moved code so the close will
find the socket if the peeloff fails (uipc_syscalls.c)
Approved by: re@freebsd.org(Ken Smith)
Diffstat (limited to 'sys/netinet/sctp_input.c')
-rw-r--r-- | sys/netinet/sctp_input.c | 124 |
1 files changed, 81 insertions, 43 deletions
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 32adf9b..be3602a 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -610,6 +610,7 @@ sctp_handle_abort(struct sctp_abort_chunk *cp, #ifdef SCTP_ASOCLOG_OF_TSNS sctp_print_out_track_log(stcb); #endif + stcb->asoc.state |= SCTP_STATE_WAS_ABORTED; (void)sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_6); SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_handle_abort: finished\n"); @@ -690,6 +691,8 @@ sctp_handle_shutdown(struct sctp_shutdown_chunk *cp, } SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_ACK_SENT); + sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, net, + SCTP_FROM_SCTP_INPUT + SCTP_LOC_7); /* start SHUTDOWN timer */ sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK, stcb->sctp_ep, stcb, net); @@ -2219,6 +2222,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, (SCTP_PCB_COPY_FLAGS & (*inp_p)->sctp_flags) | SCTP_PCB_FLAGS_DONT_WAKE); inp->sctp_features = (*inp_p)->sctp_features; + inp->sctp_socket = so; inp->sctp_frag_point = (*inp_p)->sctp_frag_point; inp->partial_delivery_point = (*inp_p)->partial_delivery_point; @@ -2482,6 +2486,8 @@ sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp, /* process according to association state */ if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT) { /* unexpected SHUTDOWN-COMPLETE... so ignore... */ + SCTPDBG(SCTP_DEBUG_INPUT2, + "sctp_handle_shutdown_complete: not in SCTP_STATE_SHUTDOWN_ACK_SENT --- ignore\n"); SCTP_TCB_UNLOCK(stcb); return; } @@ -2499,6 +2505,9 @@ sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp, sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_22); SCTP_STAT_INCR_COUNTER32(sctps_shutdown); /* free the TCB */ + SCTPDBG(SCTP_DEBUG_INPUT2, + "sctp_handle_shutdown_complete: calls free-asoc\n"); + (void)sctp_free_assoc(stcb->sctp_ep, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_INPUT + SCTP_LOC_23); return; } @@ -2721,7 +2730,7 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, break; case SCTP_ASCONF_ACK: /* resend last asconf ack */ - sctp_send_asconf_ack(stcb, 1); + sctp_send_asconf_ack(stcb); break; case SCTP_FORWARD_CUM_TSN: send_forward_tsn(stcb, &stcb->asoc); @@ -3481,6 +3490,7 @@ __attribute__((noinline)) int got_auth = 0; uint32_t auth_offset = 0, auth_len = 0; int auth_skipped = 0; + int asconf_cnt = 0; SCTPDBG(SCTP_DEBUG_INPUT1, "sctp_process_control: iphlen=%u, offset=%u, length=%u stcb:%p\n", iphlen, *offset, length, stcb); @@ -3555,18 +3565,35 @@ __attribute__((noinline)) * need to look inside to find the association */ if (ch->chunk_type == SCTP_ASCONF && stcb == NULL) { + struct sctp_chunkhdr *asconf_ch = ch; + uint32_t asconf_offset = 0, asconf_len = 0; + /* inp's refcount may be reduced */ SCTP_INP_INCR_REF(inp); - stcb = sctp_findassociation_ep_asconf(m, iphlen, - *offset, sh, &inp, netp); + asconf_offset = *offset; + do { + asconf_len = ntohs(asconf_ch->chunk_length); + if (asconf_len < sizeof(struct sctp_asconf_paramhdr)) + break; + stcb = sctp_findassociation_ep_asconf(m, iphlen, + *offset, sh, &inp, netp); + if (stcb != NULL) + break; + asconf_offset += SCTP_SIZE32(asconf_len); + asconf_ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, asconf_offset, + sizeof(struct sctp_chunkhdr), chunk_buf); + } while (asconf_ch != NULL && asconf_ch->chunk_type == SCTP_ASCONF); if (stcb == NULL) { /* * reduce inp's refcount if not reduced in * sctp_findassociation_ep_asconf(). */ SCTP_INP_DECR_REF(inp); + } else { + locked_tcb = stcb; } + /* now go back and verify any auth chunk to be sure */ if (auth_skipped && (stcb != NULL)) { struct sctp_auth_chunk *auth; @@ -3783,7 +3810,8 @@ process_control_chunks: return (NULL); } } - if ((num_chunks > 1) || + if ((chk_length > SCTP_LARGEST_INIT_ACCEPTED) || + (num_chunks > 1) || (sctp_strict_init && (length - *offset > (int)SCTP_SIZE32(chk_length)))) { *offset = length; if (locked_tcb) { @@ -3878,12 +3906,21 @@ process_control_chunks: if ((stcb == NULL) || (chk_length < sizeof(struct sctp_sack_chunk))) { SCTPDBG(SCTP_DEBUG_INDATA1, "Bad size on sack chunk, too small\n"); + ignore_sack: *offset = length; if (locked_tcb) { SCTP_TCB_UNLOCK(locked_tcb); } return (NULL); } + if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) { + /*- + * If we have sent a shutdown-ack, we will pay no + * attention to a sack sent in to us since + * we don't care anymore. + */ + goto ignore_sack; + } sack = (struct sctp_sack_chunk *)ch; nonce_sum_flag = ch->chunk_flags & SCTP_SACK_NONCE_SUM; cum_ack = ntohl(sack->sack.cum_tsn_ack); @@ -4246,7 +4283,8 @@ process_control_chunks: } stcb->asoc.overall_error_count = 0; sctp_handle_asconf(m, *offset, - (struct sctp_asconf_chunk *)ch, stcb); + (struct sctp_asconf_chunk *)ch, stcb, asconf_cnt == 0); + asconf_cnt++; } break; case SCTP_ASCONF_ACK: @@ -4466,6 +4504,10 @@ next_chunk: return (NULL); } } /* while */ + + if (asconf_cnt > 0 && stcb != NULL) { + sctp_send_asconf_ack(stcb); + } return (stcb); } @@ -4572,13 +4614,15 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, sctp_auditing(0, inp, stcb, net); #endif - SCTPDBG(SCTP_DEBUG_INPUT1, "Ok, Common input processing called, m:%p iphlen:%d offset:%d\n", - m, iphlen, offset); - + SCTPDBG(SCTP_DEBUG_INPUT1, "Ok, Common input processing called, m:%p iphlen:%d offset:%d stcb:%p\n", + m, iphlen, offset, stcb); if (stcb) { /* always clear this before beginning a packet */ stcb->asoc.authenticated = 0; stcb->asoc.seen_a_sack_this_pkt = 0; + SCTPDBG(SCTP_DEBUG_INPUT1, "stcb:%p state:%x\n", + stcb, stcb->asoc.state); + if ((stcb->asoc.state & SCTP_STATE_WAS_ABORTED) || (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED)) { /*- @@ -4770,9 +4814,12 @@ trigger_send: un_sent = (stcb->asoc.total_output_queue_size - stcb->asoc.total_flight); if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue) || + /* For retransmission to new primary destination (by micchie) */ + sctp_is_mobility_feature_on(inp, SCTP_MOBILITY_DO_FASTHANDOFF) || ((un_sent) && (stcb->asoc.peers_rwnd > 0 || (stcb->asoc.peers_rwnd <= 0 && stcb->asoc.total_flight == 0)))) { + sctp_mobility_feature_off(inp, SCTP_MOBILITY_DO_FASTHANDOFF); SCTPDBG(SCTP_DEBUG_INPUT3, "Calling chunk OUTPUT\n"); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC); SCTPDBG(SCTP_DEBUG_INPUT3, "chunk OUTPUT returns\n"); @@ -4881,43 +4928,34 @@ sctp_input(i_pak, off) goto bad; } /* validate SCTP checksum */ - if ((sctp_no_csum_on_loopback == 0) || !SCTP_IS_IT_LOOPBACK(m)) { - /* - * we do NOT validate things from the loopback if the sysctl - * is set to 1. - */ - check = sh->checksum; /* save incoming checksum */ - if ((check == 0) && (sctp_no_csum_on_loopback)) { - /* - * special hook for where we got a local address - * somehow routed across a non IFT_LOOP type - * interface - */ - if (ip->ip_src.s_addr == ip->ip_dst.s_addr) - goto sctp_skip_csum_4; - } - sh->checksum = 0; /* prepare for calc */ - calc_check = sctp_calculate_sum(m, &mlen, iphlen); - if (calc_check != check) { - SCTPDBG(SCTP_DEBUG_INPUT1, "Bad CSUM on SCTP packet calc_check:%x check:%x m:%p mlen:%d iphlen:%d\n", - calc_check, check, m, mlen, iphlen); - - stcb = sctp_findassociation_addr(m, iphlen, - offset - sizeof(*ch), - sh, ch, &inp, &net, - vrf_id); - if ((inp) && (stcb)) { - sctp_send_packet_dropped(stcb, net, m, iphlen, 1); - sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR); - } else if ((inp != NULL) && (stcb == NULL)) { - refcount_up = 1; - } - SCTP_STAT_INCR(sctps_badsum); - SCTP_STAT_INCR_COUNTER32(sctps_checksumerrors); - goto bad; + check = sh->checksum; /* save incoming checksum */ + if ((check == 0) && (sctp_no_csum_on_loopback) && + ((ip->ip_src.s_addr == ip->ip_dst.s_addr) || + (SCTP_IS_IT_LOOPBACK(m))) + ) { + goto sctp_skip_csum_4; + } + sh->checksum = 0; /* prepare for calc */ + calc_check = sctp_calculate_sum(m, &mlen, iphlen); + if (calc_check != check) { + SCTPDBG(SCTP_DEBUG_INPUT1, "Bad CSUM on SCTP packet calc_check:%x check:%x m:%p mlen:%d iphlen:%d\n", + calc_check, check, m, mlen, iphlen); + + stcb = sctp_findassociation_addr(m, iphlen, + offset - sizeof(*ch), + sh, ch, &inp, &net, + vrf_id); + if ((inp) && (stcb)) { + sctp_send_packet_dropped(stcb, net, m, iphlen, 1); + sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR); + } else if ((inp != NULL) && (stcb == NULL)) { + refcount_up = 1; } - sh->checksum = calc_check; + SCTP_STAT_INCR(sctps_badsum); + SCTP_STAT_INCR_COUNTER32(sctps_checksumerrors); + goto bad; } + sh->checksum = calc_check; sctp_skip_csum_4: /* destination port of 0 is illegal, based on RFC2960. */ if (sh->dest_port == 0) { |