diff options
Diffstat (limited to 'sys/netinet/sctp_input.c')
-rw-r--r-- | sys/netinet/sctp_input.c | 519 |
1 files changed, 280 insertions, 239 deletions
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 88d67a3..1329c40 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -1,5 +1,7 @@ /*- * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2008-2011, by Randall Stewart. All rights reserved. + * Copyright (c) 2008-2011, by Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -48,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include <netinet/sctp_timer.h> #include <netinet/sctp_crc32.h> #include <netinet/udp.h> +#include <sys/smp.h> @@ -193,8 +196,8 @@ int sctp_is_there_unsent_data(struct sctp_tcb *stcb) { int unsent_data = 0; + unsigned int i; struct sctp_stream_queue_pending *sp; - struct sctp_stream_out *strq; struct sctp_association *asoc; /* @@ -205,12 +208,11 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb) */ asoc = &stcb->asoc; SCTP_TCB_SEND_LOCK(stcb); - if (!TAILQ_EMPTY(&asoc->out_wheel)) { + if (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { /* Check to see if some data queued */ - TAILQ_FOREACH(strq, &asoc->out_wheel, next_spoke) { - is_there_another: + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { /* sa_ignore FREED_MEMORY */ - sp = TAILQ_FIRST(&strq->outqueue); + sp = TAILQ_FIRST(&stcb->asoc.strmout[i].outqueue); if (sp == NULL) { continue; } @@ -231,7 +233,7 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb) sp->put_last_out); } atomic_subtract_int(&stcb->asoc.stream_queue_cnt, 1); - TAILQ_REMOVE(&strq->outqueue, sp, next); + TAILQ_REMOVE(&stcb->asoc.strmout[i].outqueue, sp, next); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -241,10 +243,9 @@ sctp_is_there_unsent_data(struct sctp_tcb *stcb) sp->data = NULL; } sctp_free_a_strmoq(stcb, sp); - goto is_there_another; } else { unsent_data++; - continue; + break; } } } @@ -266,11 +267,13 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb, /* save off parameters */ asoc->peer_vtag = ntohl(init->initiate_tag); asoc->peers_rwnd = ntohl(init->a_rwnd); + /* init tsn's */ + asoc->highest_tsn_inside_map = asoc->asconf_seq_in = ntohl(init->initial_tsn) - 1; + if (!TAILQ_EMPTY(&asoc->nets)) { /* update any ssthresh's that may have a default */ TAILQ_FOREACH(lnet, &asoc->nets, sctp_next) { lnet->ssthresh = asoc->peers_rwnd; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & (SCTP_CWND_MONITOR_ENABLE | SCTP_CWND_LOGGING_ENABLE)) { sctp_log_cwnd(stcb, lnet, 0, SCTP_CWND_INITIALIZATION); } @@ -280,38 +283,32 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb, if (asoc->pre_open_streams > ntohs(init->num_inbound_streams)) { unsigned int newcnt; struct sctp_stream_out *outs; - struct sctp_stream_queue_pending *sp; - struct sctp_tmit_chunk *chk, *chk_next; + struct sctp_stream_queue_pending *sp, *nsp; + struct sctp_tmit_chunk *chk, *nchk; /* abandon the upper streams */ newcnt = ntohs(init->num_inbound_streams); - if (!TAILQ_EMPTY(&asoc->send_queue)) { - chk = TAILQ_FIRST(&asoc->send_queue); - while (chk) { - chk_next = TAILQ_NEXT(chk, sctp_next); - if (chk->rec.data.stream_number >= newcnt) { - TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); - asoc->send_queue_cnt--; - if (chk->data != NULL) { - sctp_free_bufspace(stcb, asoc, chk, 1); - sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, - SCTP_NOTIFY_DATAGRAM_UNSENT, chk, SCTP_SO_NOT_LOCKED); - if (chk->data) { - sctp_m_freem(chk->data); - chk->data = NULL; - } + TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) { + if (chk->rec.data.stream_number >= newcnt) { + TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); + asoc->send_queue_cnt--; + if (chk->data != NULL) { + sctp_free_bufspace(stcb, asoc, chk, 1); + sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, + SCTP_NOTIFY_DATAGRAM_UNSENT, chk, SCTP_SO_NOT_LOCKED); + if (chk->data) { + sctp_m_freem(chk->data); + chk->data = NULL; } - sctp_free_a_chunk(stcb, chk); - /* sa_ignore FREED_MEMORY */ } - chk = chk_next; + sctp_free_a_chunk(stcb, chk); + /* sa_ignore FREED_MEMORY */ } } if (asoc->strmout) { for (i = newcnt; i < asoc->pre_open_streams; i++) { outs = &asoc->strmout[i]; - sp = TAILQ_FIRST(&outs->outqueue); - while (sp) { + TAILQ_FOREACH_SAFE(sp, &outs->outqueue, next, nsp) { TAILQ_REMOVE(&outs->outqueue, sp, next); asoc->stream_queue_cnt--; sctp_ulp_notify(SCTP_NOTIFY_SPECIAL_SP_FAIL, @@ -328,7 +325,6 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb, /* Free the chunk */ sctp_free_a_strmoq(stcb, sp); /* sa_ignore FREED_MEMORY */ - sp = TAILQ_FIRST(&outs->outqueue); } } } @@ -337,8 +333,7 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb, } SCTP_TCB_SEND_UNLOCK(stcb); asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams; - /* init tsn's */ - asoc->highest_tsn_inside_map = asoc->asconf_seq_in = ntohl(init->initial_tsn) - 1; + /* EY - nr_sack: initialize highest tsn in nr_mapping_array */ asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { @@ -349,24 +344,22 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb, asoc->mapping_array_base_tsn = ntohl(init->initial_tsn); asoc->tsn_last_delivered = asoc->cumulative_tsn = asoc->asconf_seq_in; - asoc->last_echo_tsn = asoc->asconf_seq_in; + asoc->advanced_peer_ack_point = asoc->last_acked_seq; /* open the requested streams */ if (asoc->strmin != NULL) { /* Free the old ones */ - struct sctp_queued_to_read *ctl; + struct sctp_queued_to_read *ctl, *nctl; for (i = 0; i < asoc->streamincnt; i++) { - ctl = TAILQ_FIRST(&asoc->strmin[i].inqueue); - while (ctl) { + TAILQ_FOREACH_SAFE(ctl, &asoc->strmin[i].inqueue, next, nctl) { TAILQ_REMOVE(&asoc->strmin[i].inqueue, ctl, next); sctp_free_remote_addr(ctl->whoFrom); ctl->whoFrom = NULL; sctp_m_freem(ctl->data); ctl->data = NULL; sctp_free_a_readq(stcb, ctl); - ctl = TAILQ_FIRST(&asoc->strmin[i].inqueue); } } SCTP_FREE(asoc->strmin, SCTP_M_STRMI); @@ -456,13 +449,11 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, } /* if the peer doesn't support asconf, flush the asconf queue */ if (asoc->peer_supports_asconf == 0) { - struct sctp_asconf_addr *aparam; + struct sctp_asconf_addr *param, *nparam; - while (!TAILQ_EMPTY(&asoc->asconf_queue)) { - /* sa_ignore FREED_MEMORY */ - aparam = TAILQ_FIRST(&asoc->asconf_queue); - TAILQ_REMOVE(&asoc->asconf_queue, aparam, next); - SCTP_FREE(aparam, SCTP_M_ASC_ADDR); + TAILQ_FOREACH_SAFE(param, &asoc->asconf_queue, next, nparam) { + TAILQ_REMOVE(&asoc->asconf_queue, param, next); + SCTP_FREE(param, SCTP_M_ASC_ADDR); } } stcb->asoc.peer_hmac_id = sctp_negotiate_hmacid(stcb->asoc.peer_hmacs, @@ -493,7 +484,8 @@ sctp_process_init_ack(struct mbuf *m, int iphlen, int offset, asoc->primary_destination, SCTP_FROM_SCTP_INPUT + SCTP_LOC_4); /* calculate the RTO */ - net->RTO = sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered, sctp_align_safe_nocopy); + net->RTO = sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered, sctp_align_safe_nocopy, + SCTP_DETERMINE_LL_NOTOK); retval = sctp_send_cookie_echo(m, offset, stcb, net); if (retval < 0) { @@ -623,7 +615,7 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp, * timer is running, for the destination, stop the timer because a * PF-heartbeat was received. */ - if ((stcb->asoc.sctp_cmt_on_off == 1) && + if ((stcb->asoc.sctp_cmt_on_off > 0) && (stcb->asoc.sctp_cmt_pf > 0) && ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { if (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) { @@ -637,7 +629,8 @@ sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp, net, net->cwnd); } /* Now lets do a RTO with this */ - r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv, sctp_align_safe_nocopy); + r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv, sctp_align_safe_nocopy, + SCTP_DETERMINE_LL_OK); /* Mobility adaptation */ if (req_prim) { if ((sctp_is_mobility_feature_on(stcb->sctp_ep, @@ -975,7 +968,7 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp, /* are the queues empty? */ if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || - !TAILQ_EMPTY(&asoc->out_wheel)) { + !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { sctp_report_all_outbound(stcb, 0, SCTP_SO_NOT_LOCKED); } /* stop the timer */ @@ -1057,11 +1050,6 @@ sctp_process_unrecog_param(struct sctp_tcb *stcb, struct sctp_paramhdr *phdr) case SCTP_HAS_NAT_SUPPORT: stcb->asoc.peer_supports_nat = 0; break; - case SCTP_ECN_NONCE_SUPPORTED: - stcb->asoc.peer_supports_ecn_nonce = 0; - stcb->asoc.ecn_nonce_allowed = 0; - stcb->asoc.ecn_allowed = 0; - break; case SCTP_ADD_IP_ADDRESS: case SCTP_DEL_IP_ADDRESS: case SCTP_SET_PRIM_ADDR: @@ -1557,7 +1545,9 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, */ net->hb_responded = 1; net->RTO = sctp_calculate_rto(stcb, asoc, net, - &cookie->time_entered, sctp_align_unsafe_makecopy); + &cookie->time_entered, + sctp_align_unsafe_makecopy, + SCTP_DETERMINE_LL_NOTOK); if (stcb->asoc.sctp_autoclose_ticks && (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE))) { @@ -1691,8 +1681,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd); asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams); - /* Note last_cwr_tsn? where is this used? */ - asoc->last_cwr_tsn = asoc->init_seq_number - 1; if (ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) { /* * Ok the peer probably discarded our data (if we @@ -1848,7 +1836,6 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number; asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1; - asoc->last_cwr_tsn = asoc->init_seq_number - 1; asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1; asoc->str_reset_seq_in = asoc->init_seq_number; @@ -2086,7 +2073,6 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn); asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number; asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1; - asoc->last_cwr_tsn = asoc->init_seq_number - 1; asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1; asoc->str_reset_seq_in = asoc->init_seq_number; @@ -2264,7 +2250,8 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, (void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered); if ((netp) && (*netp)) { (*netp)->RTO = sctp_calculate_rto(stcb, asoc, *netp, - &cookie->time_entered, sctp_align_unsafe_makecopy); + &cookie->time_entered, sctp_align_unsafe_makecopy, + SCTP_DETERMINE_LL_NOTOK); } /* respond with a COOKIE-ACK */ sctp_send_cookie_ack(stcb); @@ -2329,6 +2316,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, int notification = 0; struct sctp_nets *netl; int had_a_existing_tcb = 0; + int send_int_conf = 0; SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_handle_cookie: handling COOKIE-ECHO\n"); @@ -2627,6 +2615,12 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, /* still no TCB... must be bad cookie-echo */ return (NULL); } + if ((*netp != NULL) && (m->m_flags & M_FLOWID)) { + (*netp)->flowid = m->m_pkthdr.flowid; +#ifdef INVARIANTS + (*netp)->flowidset = 1; +#endif + } /* * Ok, we built an association so confirm the address we sent the * INIT-ACK to. @@ -2648,8 +2642,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, netl->dest_state &= ~SCTP_ADDR_UNCONFIRMED; (void)sctp_set_primary_addr((*stcb), (struct sockaddr *)NULL, netl); - sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, - (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED); + send_int_conf = 1; } } if (*stcb) { @@ -2673,13 +2666,19 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, * socket, no need to do anything. I THINK!! */ sctp_ulp_notify(notification, *stcb, 0, (void *)&sac_restart_id, SCTP_SO_NOT_LOCKED); + if (send_int_conf) { + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, + (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED); + } return (m); } oso = (*inp_p)->sctp_socket; atomic_add_int(&(*stcb)->asoc.refcnt, 1); SCTP_TCB_UNLOCK((*stcb)); + CURVNET_SET(oso->so_vnet); so = sonewconn(oso, 0 ); + CURVNET_RESTORE(); SCTP_TCB_LOCK((*stcb)); atomic_subtract_int(&(*stcb)->asoc.refcnt, 1); @@ -2727,6 +2726,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, inp->sctp_socket = so; inp->sctp_frag_point = (*inp_p)->sctp_frag_point; inp->sctp_cmt_on_off = (*inp_p)->sctp_cmt_on_off; + inp->sctp_ecn_enable = (*inp_p)->sctp_ecn_enable; inp->partial_delivery_point = (*inp_p)->partial_delivery_point; inp->sctp_context = (*inp_p)->sctp_context; inp->inp_starting_point_for_iterator = NULL; @@ -2784,7 +2784,10 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, /* Switch over to the new guy */ *inp_p = inp; sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED); - + if (send_int_conf) { + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, + (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED); + } /* * Pull it from the incomplete queue and wake the * guy @@ -2803,8 +2806,14 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, return (m); } } - if ((notification) && ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) { - sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED); + if ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) { + if (notification) { + sctp_ulp_notify(notification, *stcb, 0, NULL, SCTP_SO_NOT_LOCKED); + } + if (send_int_conf) { + sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED, + (*stcb), 0, (void *)netl, SCTP_SO_NOT_LOCKED); + } } return (m); } @@ -2839,7 +2848,8 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp, SCTP_STAT_INCR_GAUGE32(sctps_currestab); if (asoc->overall_error_count == 0) { net->RTO = sctp_calculate_rto(stcb, asoc, net, - &asoc->time_entered, sctp_align_safe_nocopy); + &asoc->time_entered, sctp_align_safe_nocopy, + SCTP_DETERMINE_LL_NOTOK); } (void)SCTP_GETTIME_TIMEVAL(&asoc->time_entered); sctp_ulp_notify(SCTP_NOTIFY_ASSOC_UP, stcb, 0, NULL, SCTP_SO_NOT_LOCKED); @@ -2857,24 +2867,31 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp, SCTP_SOCKET_LOCK(so, 1); SCTP_TCB_LOCK(stcb); atomic_subtract_int(&stcb->asoc.refcnt, 1); - if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { - SCTP_SOCKET_UNLOCK(so, 1); - return; - } #endif - soisconnected(stcb->sctp_socket); + if ((stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) == 0) { + soisconnected(stcb->sctp_socket); + } #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); #endif } - sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, - stcb, net); /* * since we did not send a HB make sure we don't double * things */ net->hb_responded = 1; + if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { + /* + * We don't need to do the asconf thing, nor hb or + * autoclose if the socket is closed. + */ + goto closed_socket; + } + sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, + stcb, net); + + if (stcb->asoc.sctp_autoclose_ticks && sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_AUTOCLOSE)) { sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, @@ -2898,6 +2915,7 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp, #endif } } +closed_socket: /* Toss the cookie if I can */ sctp_toss_old_cookies(stcb, asoc); if (!TAILQ_EMPTY(&asoc->sent_queue)) { @@ -2905,10 +2923,7 @@ sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp, struct sctp_tmit_chunk *chk; chk = TAILQ_FIRST(&asoc->sent_queue); - if (chk) { - sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, - stcb, chk->whoTo); - } + sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, chk->whoTo); } } @@ -2918,62 +2933,119 @@ sctp_handle_ecn_echo(struct sctp_ecne_chunk *cp, { struct sctp_nets *net; struct sctp_tmit_chunk *lchk; - uint32_t tsn; + struct sctp_ecne_chunk bkup; + uint8_t override_bit = 0; + uint32_t tsn, window_data_tsn; + int len; + unsigned int pkt_cnt; - if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_ecne_chunk)) { + len = ntohs(cp->ch.chunk_length); + if ((len != sizeof(struct sctp_ecne_chunk)) && + (len != sizeof(struct old_sctp_ecne_chunk))) { return; } + if (len == sizeof(struct old_sctp_ecne_chunk)) { + /* Its the old format */ + memcpy(&bkup, cp, sizeof(struct old_sctp_ecne_chunk)); + bkup.num_pkts_since_cwr = htonl(1); + cp = &bkup; + } SCTP_STAT_INCR(sctps_recvecne); tsn = ntohl(cp->tsn); - /* ECN Nonce stuff: need a resync and disable the nonce sum check */ - /* Also we make sure we disable the nonce_wait */ - lchk = TAILQ_FIRST(&stcb->asoc.send_queue); + pkt_cnt = ntohl(cp->num_pkts_since_cwr); + lchk = TAILQ_LAST(&stcb->asoc.send_queue, sctpchunk_listhead); if (lchk == NULL) { - stcb->asoc.nonce_resync_tsn = stcb->asoc.sending_seq; + window_data_tsn = stcb->asoc.sending_seq - 1; } else { - stcb->asoc.nonce_resync_tsn = lchk->rec.data.TSN_seq; + window_data_tsn = lchk->rec.data.TSN_seq; } - stcb->asoc.nonce_wait_for_ecne = 0; - stcb->asoc.nonce_sum_check = 0; - /* Find where it was sent, if possible */ + /* Find where it was sent to if possible. */ net = NULL; - lchk = TAILQ_FIRST(&stcb->asoc.sent_queue); - while (lchk) { + TAILQ_FOREACH(lchk, &stcb->asoc.sent_queue, sctp_next) { if (lchk->rec.data.TSN_seq == tsn) { net = lchk->whoTo; + net->ecn_prev_cwnd = lchk->rec.data.cwnd_at_send; break; } - if (compare_with_wrap(lchk->rec.data.TSN_seq, tsn, MAX_SEQ)) + if (SCTP_TSN_GT(lchk->rec.data.TSN_seq, tsn)) { break; - lchk = TAILQ_NEXT(lchk, sctp_next); + } } - if (net == NULL) - /* default is we use the primary */ - net = stcb->asoc.primary_destination; - - if (compare_with_wrap(tsn, stcb->asoc.last_cwr_tsn, MAX_TSN)) { + if (net == NULL) { + /* + * What to do. A previous send of a CWR was possibly lost. + * See how old it is, we may have it marked on the actual + * net. + */ + TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { + if (tsn == net->last_cwr_tsn) { + /* Found him, send it off */ + goto out; + } + } + /* + * If we reach here, we need to send a special CWR that says + * hey, we did this a long time ago and you lost the + * response. + */ + net = TAILQ_FIRST(&stcb->asoc.nets); + override_bit = SCTP_CWR_REDUCE_OVERRIDE; + } +out: + if (SCTP_TSN_GT(tsn, net->cwr_window_tsn) && + ((override_bit & SCTP_CWR_REDUCE_OVERRIDE) == 0)) { /* * JRS - Use the congestion control given in the pluggable * CC module */ - stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net); + int ocwnd; + + ocwnd = net->cwnd; + stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net, 0, pkt_cnt); /* - * we reduce once every RTT. So we will only lower cwnd at - * the next sending seq i.e. the resync_tsn. + * We reduce once every RTT. So we will only lower cwnd at + * the next sending seq i.e. the window_data_tsn */ - stcb->asoc.last_cwr_tsn = stcb->asoc.nonce_resync_tsn; + net->cwr_window_tsn = window_data_tsn; + net->ecn_ce_pkt_cnt += pkt_cnt; + net->lost_cnt = pkt_cnt; + net->last_cwr_tsn = tsn; + } else { + override_bit |= SCTP_CWR_IN_SAME_WINDOW; + if (SCTP_TSN_GT(tsn, net->last_cwr_tsn) && + ((override_bit & SCTP_CWR_REDUCE_OVERRIDE) == 0)) { + /* + * Another loss in the same window update how many + * marks/packets lost we have had. + */ + int cnt = 1; + + if (pkt_cnt > net->lost_cnt) { + /* Should be the case */ + cnt = (pkt_cnt - net->lost_cnt); + net->ecn_ce_pkt_cnt += cnt; + } + net->lost_cnt = pkt_cnt; + net->last_cwr_tsn = tsn; + /* + * Most CC functions will ignore this call, since we + * are in-window yet of the initial CE the peer saw. + */ + stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net, 1, cnt); + } } /* * We always send a CWR this way if our previous one was lost our * peer will get an update, or if it is not time again to reduce we - * still get the cwr to the peer. + * still get the cwr to the peer. Note we set the override when we + * could not find the TSN on the chunk or the destination network. */ - sctp_send_cwr(stcb, net, tsn); + sctp_send_cwr(stcb, net, net->last_cwr_tsn, override_bit); } static void -sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb) +sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb, struct sctp_nets *net) { /* * Here we get a CWR from the peer. We must look in the outqueue and @@ -2982,19 +3054,22 @@ sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb) */ struct sctp_tmit_chunk *chk; struct sctp_ecne_chunk *ecne; + int override; + uint32_t cwr_tsn; + cwr_tsn = ntohl(cp->tsn); + + override = cp->ch.chunk_flags & SCTP_CWR_REDUCE_OVERRIDE; TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) { if (chk->rec.chunk_id.id != SCTP_ECN_ECHO) { continue; } - /* - * Look for and remove if it is the right TSN. Since there - * is only ONE ECNE on the control queue at any one time we - * don't need to worry about more than one! - */ + if ((override == 0) && (chk->whoTo != net)) { + /* Must be from the right src unless override is set */ + continue; + } ecne = mtod(chk->data, struct sctp_ecne_chunk *); - if (compare_with_wrap(ntohl(cp->tsn), ntohl(ecne->tsn), - MAX_TSN) || (cp->tsn == ecne->tsn)) { + if (SCTP_TSN_GE(cwr_tsn, ntohl(ecne->tsn))) { /* this covers this ECNE, we can remove it */ stcb->asoc.ecn_echo_cnt_onq--; TAILQ_REMOVE(&stcb->asoc.control_send_queue, chk, @@ -3005,7 +3080,9 @@ sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb) } stcb->asoc.ctrl_queue_cnt--; sctp_free_a_chunk(stcb, chk); - break; + if (override == 0) { + break; + } } } } @@ -3041,7 +3118,7 @@ sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp, /* are the queues empty? they should be */ if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || - !TAILQ_EMPTY(&asoc->out_wheel)) { + !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { sctp_report_all_outbound(stcb, 0, SCTP_SO_NOT_LOCKED); } } @@ -3078,19 +3155,16 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, struct sctp_tmit_chunk *tp1; tsn = ntohl(desc->tsn_ifany); - tp1 = TAILQ_FIRST(&stcb->asoc.sent_queue); - while (tp1) { + TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) { if (tp1->rec.data.TSN_seq == tsn) { /* found it */ break; } - if (compare_with_wrap(tp1->rec.data.TSN_seq, tsn, - MAX_TSN)) { + if (SCTP_TSN_GT(tp1->rec.data.TSN_seq, tsn)) { /* not found */ tp1 = NULL; break; } - tp1 = TAILQ_NEXT(tp1, sctp_next); } if (tp1 == NULL) { /* @@ -3098,13 +3172,11 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, * attention to queue seq order. */ SCTP_STAT_INCR(sctps_pdrpdnfnd); - tp1 = TAILQ_FIRST(&stcb->asoc.sent_queue); - while (tp1) { + TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) { if (tp1->rec.data.TSN_seq == tsn) { /* found it */ break; } - tp1 = TAILQ_NEXT(tp1, sctp_next); } } if (tp1 == NULL) { @@ -3140,11 +3212,6 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, } } } - /* - * We zero out the nonce so resync not - * needed - */ - tp1->rec.data.ect_nonce = 0; if (tp1->do_rtt) { /* @@ -3156,7 +3223,6 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, SCTP_STAT_INCR(sctps_pdrpmark); if (tp1->sent != SCTP_DATAGRAM_RESEND) sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); - tp1->sent = SCTP_DATAGRAM_RESEND; /* * mark it as if we were doing a FR, since * we will be getting gap ack reports behind @@ -3191,6 +3257,7 @@ process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc, sctp_flight_size_decrease(tp1); sctp_total_flight_decrease(stcb, tp1); } + tp1->sent = SCTP_DATAGRAM_RESEND; } { /* audit code */ unsigned int audit; @@ -3695,8 +3762,7 @@ sctp_handle_str_reset_request_out(struct sctp_tcb *stcb, if (trunc) { sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED); asoc->last_reset_action[0] = SCTP_STREAM_RESET_DENIED; - } else if ((tsn == asoc->cumulative_tsn) || - (compare_with_wrap(asoc->cumulative_tsn, tsn, MAX_TSN))) { + } else if (SCTP_TSN_GE(asoc->cumulative_tsn, tsn)) { /* we can do it now */ sctp_reset_in_stream(stcb, number_entries, req->list_of_streams); sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED); @@ -3755,7 +3821,7 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch uint16_t num_stream, i; uint32_t seq; struct sctp_association *asoc = &stcb->asoc; - struct sctp_queued_to_read *ctl; + struct sctp_queued_to_read *ctl, *nctl; /* Get the number. */ seq = ntohl(str_add->request_seq); @@ -3789,8 +3855,7 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch stcb->asoc.strmin[i].last_sequence_delivered = oldstrm[i].last_sequence_delivered; stcb->asoc.strmin[i].delivery_started = oldstrm[i].delivery_started; /* now anything on those queues? */ - while (TAILQ_EMPTY(&oldstrm[i].inqueue) == 0) { - ctl = TAILQ_FIRST(&oldstrm[i].inqueue); + TAILQ_FOREACH_SAFE(ctl, &oldstrm[i].inqueue, next, nctl) { TAILQ_REMOVE(&oldstrm[i].inqueue, ctl, next); TAILQ_INSERT_TAIL(&stcb->asoc.strmin[i].inqueue, ctl, next); } @@ -4158,6 +4223,7 @@ __attribute__((noinline)) uint32_t chk_length; int ret; int abort_no_unlock = 0; + int ecne_seen = 0; /* * How big should this be, and should it be alloc'd? Lets try the @@ -4601,7 +4667,6 @@ process_control_chunks: uint16_t num_seg, num_dup; uint8_t flags; int offset_seg, offset_dup; - int nonce_sum_flag; SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_SACK\n"); SCTP_STAT_INCR(sctps_recvsacks); @@ -4623,7 +4688,6 @@ process_control_chunks: } sack = (struct sctp_sack_chunk *)ch; flags = ch->chunk_flags; - nonce_sum_flag = flags & SCTP_SACK_NONCE_SUM; cum_ack = ntohl(sack->sack.cum_tsn_ack); num_seg = ntohs(sack->sack.num_gap_ack_blks); num_dup = ntohs(sack->sack.num_dup_tsns); @@ -4641,8 +4705,7 @@ process_control_chunks: stcb->asoc.seen_a_sack_this_pkt = 1; if ((stcb->asoc.pr_sctp_cnt == 0) && (num_seg == 0) && - ((compare_with_wrap(cum_ack, stcb->asoc.last_acked_seq, MAX_TSN)) || - (cum_ack == stcb->asoc.last_acked_seq)) && + SCTP_TSN_GE(cum_ack, stcb->asoc.last_acked_seq) && (stcb->asoc.saw_sack_with_frags == 0) && (stcb->asoc.saw_sack_with_nr_frags == 0) && (!TAILQ_EMPTY(&stcb->asoc.sent_queue)) @@ -4656,14 +4719,13 @@ process_control_chunks: * with no missing segments to go * this way too. */ - sctp_express_handle_sack(stcb, cum_ack, a_rwnd, nonce_sum_flag, - &abort_now); + sctp_express_handle_sack(stcb, cum_ack, a_rwnd, &abort_now, ecne_seen); } else { if (netp && *netp) sctp_handle_sack(m, offset_seg, offset_dup, stcb, *netp, num_seg, 0, num_dup, &abort_now, flags, - cum_ack, a_rwnd); + cum_ack, a_rwnd, ecne_seen); } if (abort_now) { /* ABORT signal from sack processing */ @@ -4689,7 +4751,6 @@ process_control_chunks: uint16_t num_seg, num_nr_seg, num_dup; uint8_t flags; int offset_seg, offset_dup; - int nonce_sum_flag; SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_NR_SACK\n"); SCTP_STAT_INCR(sctps_recvsacks); @@ -4715,8 +4776,6 @@ process_control_chunks: } nr_sack = (struct sctp_nr_sack_chunk *)ch; flags = ch->chunk_flags; - nonce_sum_flag = flags & SCTP_SACK_NONCE_SUM; - cum_ack = ntohl(nr_sack->nr_sack.cum_tsn_ack); num_seg = ntohs(nr_sack->nr_sack.num_gap_ack_blks); num_nr_seg = ntohs(nr_sack->nr_sack.num_nr_gap_ack_blks); @@ -4735,8 +4794,7 @@ process_control_chunks: stcb->asoc.seen_a_sack_this_pkt = 1; if ((stcb->asoc.pr_sctp_cnt == 0) && (num_seg == 0) && (num_nr_seg == 0) && - ((compare_with_wrap(cum_ack, stcb->asoc.last_acked_seq, MAX_TSN)) || - (cum_ack == stcb->asoc.last_acked_seq)) && + SCTP_TSN_GE(cum_ack, stcb->asoc.last_acked_seq) && (stcb->asoc.saw_sack_with_frags == 0) && (stcb->asoc.saw_sack_with_nr_frags == 0) && (!TAILQ_EMPTY(&stcb->asoc.sent_queue))) { @@ -4749,14 +4807,14 @@ process_control_chunks: * missing segments to go this way * too. */ - sctp_express_handle_sack(stcb, cum_ack, a_rwnd, nonce_sum_flag, - &abort_now); + sctp_express_handle_sack(stcb, cum_ack, a_rwnd, + &abort_now, ecne_seen); } else { if (netp && *netp) sctp_handle_sack(m, offset_seg, offset_dup, stcb, *netp, num_seg, num_nr_seg, num_dup, &abort_now, flags, - cum_ack, a_rwnd); + cum_ack, a_rwnd, ecne_seen); } if (abort_now) { /* ABORT signal from sack processing */ @@ -4965,11 +5023,7 @@ process_control_chunks: struct sctp_tmit_chunk *chk; chk = TAILQ_FIRST(&stcb->asoc.sent_queue); - if (chk) { - sctp_timer_start(SCTP_TIMER_TYPE_SEND, - stcb->sctp_ep, stcb, - chk->whoTo); - } + sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, chk->whoTo); } } break; @@ -5037,6 +5091,7 @@ process_control_chunks: stcb->asoc.overall_error_count = 0; sctp_handle_ecn_echo((struct sctp_ecne_chunk *)ch, stcb); + ecne_seen = 1; } break; case SCTP_ECN_CWR: @@ -5059,7 +5114,7 @@ process_control_chunks: __LINE__); } stcb->asoc.overall_error_count = 0; - sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb); + sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb, *netp); } break; case SCTP_SHUTDOWN_COMPLETE: @@ -5359,69 +5414,6 @@ next_chunk: } -/* - * Process the ECN bits we have something set so we must look to see if it is - * ECN(0) or ECN(1) or CE - */ -static void -sctp_process_ecn_marked_a(struct sctp_tcb *stcb, struct sctp_nets *net, - uint8_t ecn_bits) -{ - if ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS) { - ; - } else if ((ecn_bits & SCTP_ECT1_BIT) == SCTP_ECT1_BIT) { - /* - * we only add to the nonce sum for ECT1, ECT0 does not - * change the NS bit (that we have yet to find a way to send - * it yet). - */ - - /* ECN Nonce stuff */ - stcb->asoc.receiver_nonce_sum++; - stcb->asoc.receiver_nonce_sum &= SCTP_SACK_NONCE_SUM; - - /* - * Drag up the last_echo point if cumack is larger since we - * don't want the point falling way behind by more than - * 2^^31 and then having it be incorrect. - */ - if (compare_with_wrap(stcb->asoc.cumulative_tsn, - stcb->asoc.last_echo_tsn, MAX_TSN)) { - stcb->asoc.last_echo_tsn = stcb->asoc.cumulative_tsn; - } - } else if ((ecn_bits & SCTP_ECT0_BIT) == SCTP_ECT0_BIT) { - /* - * Drag up the last_echo point if cumack is larger since we - * don't want the point falling way behind by more than - * 2^^31 and then having it be incorrect. - */ - if (compare_with_wrap(stcb->asoc.cumulative_tsn, - stcb->asoc.last_echo_tsn, MAX_TSN)) { - stcb->asoc.last_echo_tsn = stcb->asoc.cumulative_tsn; - } - } -} - -static void -sctp_process_ecn_marked_b(struct sctp_tcb *stcb, struct sctp_nets *net, - uint32_t high_tsn, uint8_t ecn_bits) -{ - if ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS) { - /* - * we possibly must notify the sender that a congestion - * window reduction is in order. We do this by adding a ECNE - * chunk to the output chunk queue. The incoming CWR will - * remove this chunk. - */ - if (compare_with_wrap(high_tsn, stcb->asoc.last_echo_tsn, - MAX_TSN)) { - /* Yep, we need to add a ECNE */ - sctp_send_ecn_echo(stcb, net, high_tsn); - stcb->asoc.last_echo_tsn = high_tsn; - } - } -} - #ifdef INVARIANTS #ifdef __GNUC__ __attribute__((noinline)) @@ -5463,6 +5455,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, struct mbuf *m = *mm; int abort_flag = 0; int un_sent; + int cnt_ctrl_ready = 0; SCTP_STAT_INCR(sctps_recvdatagrams); #ifdef SCTP_AUDITING_ENABLED @@ -5620,11 +5613,6 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, case SCTP_STATE_SHUTDOWN_SENT: break; } - /* take care of ECN, part 1. */ - if (stcb->asoc.ecn_allowed && - (ecn_bits & (SCTP_ECT0_BIT | SCTP_ECT1_BIT))) { - sctp_process_ecn_marked_a(stcb, net, ecn_bits); - } /* plow through the data chunks while length > offset */ retval = sctp_process_data(mm, iphlen, &offset, length, sh, inp, stcb, net, &high_tsn); @@ -5636,27 +5624,27 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, goto out_now; } data_processed = 1; - if (retval == 0) { - /* take care of ecn part 2. */ - if (stcb->asoc.ecn_allowed && - (ecn_bits & (SCTP_ECT0_BIT | SCTP_ECT1_BIT))) { - sctp_process_ecn_marked_b(stcb, net, high_tsn, - ecn_bits); - } - } /* * Anything important needs to have been m_copy'ed in * process_data */ } + /* take care of ecn */ + if ((stcb->asoc.ecn_allowed == 1) && + ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS)) { + /* Yep, we need to add a ECNE */ + sctp_send_ecn_echo(stcb, net, high_tsn); + } if ((data_processed == 0) && (fwd_tsn_seen)) { - int was_a_gap = 0; + int was_a_gap; + uint32_t highest_tsn; - if (compare_with_wrap(stcb->asoc.highest_tsn_inside_map, - stcb->asoc.cumulative_tsn, MAX_TSN)) { - /* there was a gap before this data was processed */ - was_a_gap = 1; + if (SCTP_TSN_GT(stcb->asoc.highest_tsn_inside_nr_map, stcb->asoc.highest_tsn_inside_map)) { + highest_tsn = stcb->asoc.highest_tsn_inside_nr_map; + } else { + highest_tsn = stcb->asoc.highest_tsn_inside_map; } + was_a_gap = SCTP_TSN_GT(highest_tsn, stcb->asoc.cumulative_tsn); stcb->asoc.send_sack = 1; sctp_sack_check(stcb, was_a_gap, &abort_flag); if (abort_flag) { @@ -5678,8 +5666,10 @@ trigger_send: TAILQ_EMPTY(&stcb->asoc.control_send_queue), stcb->asoc.total_flight); un_sent = (stcb->asoc.total_output_queue_size - stcb->asoc.total_flight); - - if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue) || + if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue)) { + cnt_ctrl_ready = stcb->asoc.ctrl_queue_cnt - stcb->asoc.ecn_echo_cnt_onq; + } + if (cnt_ctrl_ready || ((un_sent) && (stcb->asoc.peers_rwnd > 0 || (stcb->asoc.peers_rwnd <= 0 && stcb->asoc.total_flight == 0)))) { @@ -5837,6 +5827,12 @@ sctp_input_with_port(struct mbuf *i_pak, int off, uint16_t port) } net->port = port; } + if ((net != NULL) && (m->m_flags & M_FLOWID)) { + net->flowid = m->m_pkthdr.flowid; +#ifdef INVARIANTS + net->flowidset = 1; +#endif + } if ((inp) && (stcb)) { sctp_send_packet_dropped(stcb, net, m, iphlen, 1); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED); @@ -5866,6 +5862,12 @@ sctp_skip_csum_4: } net->port = port; } + if ((net != NULL) && (m->m_flags & M_FLOWID)) { + net->flowid = m->m_pkthdr.flowid; +#ifdef INVARIANTS + net->flowidset = 1; +#endif + } /* inp's ref-count increased && stcb locked */ if (inp == NULL) { struct sctp_init_chunk *init_chk, chunk_buf; @@ -5952,10 +5954,49 @@ bad: } return; } + +#if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP) +extern int *sctp_cpuarry; + +#endif + void -sctp_input(i_pak, off) - struct mbuf *i_pak; - int off; +sctp_input(struct mbuf *m, int off) { - sctp_input_with_port(i_pak, off, 0); +#if defined(__FreeBSD__) && defined(SCTP_MCORE_INPUT) && defined(SMP) + struct ip *ip; + struct sctphdr *sh; + int offset; + int cpu_to_use; + uint32_t flowid, tag; + + if (mp_ncpus > 1) { + if (m->m_flags & M_FLOWID) { + flowid = m->m_pkthdr.flowid; + } else { + /* + * No flow id built by lower layers fix it so we + * create one. + */ + ip = mtod(m, struct ip *); + offset = off + sizeof(*sh); + if (SCTP_BUF_LEN(m) < offset) { + if ((m = m_pullup(m, offset)) == 0) { + SCTP_STAT_INCR(sctps_hdrops); + return; + } + ip = mtod(m, struct ip *); + } + sh = (struct sctphdr *)((caddr_t)ip + off); + tag = htonl(sh->v_tag); + flowid = tag ^ ntohs(sh->dest_port) ^ ntohs(sh->src_port); + m->m_pkthdr.flowid = flowid; + m->m_flags |= M_FLOWID; + } + cpu_to_use = sctp_cpuarry[flowid % mp_ncpus]; + sctp_queue_to_mcore(m, off, cpu_to_use); + return; + } +#endif + sctp_input_with_port(m, off, 0); } |