diff options
Diffstat (limited to 'sys/netinet/sctp_output.c')
-rw-r--r-- | sys/netinet/sctp_output.c | 551 |
1 files changed, 231 insertions, 320 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index d8bc9aa..96c0393 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.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: @@ -3389,54 +3391,10 @@ static uint8_t sctp_get_ect(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk) { - uint8_t this_random; - - /* Huh? */ - if (SCTP_BASE_SYSCTL(sctp_ecn_enable) == 0) - return (0); - - if (SCTP_BASE_SYSCTL(sctp_ecn_nonce) == 0) - /* no nonce, always return ECT0 */ - return (SCTP_ECT0_BIT); - - if (stcb->asoc.peer_supports_ecn_nonce == 0) { - /* Peer does NOT support it, so we send a ECT0 only */ + if ((stcb != NULL) && (stcb->asoc.ecn_allowed == 1)) { return (SCTP_ECT0_BIT); - } - if (chk == NULL) - return (SCTP_ECT0_BIT); - - if ((stcb->asoc.hb_random_idx > 3) || - ((stcb->asoc.hb_random_idx == 3) && - (stcb->asoc.hb_ect_randombit > 7))) { - uint32_t rndval; - -warp_drive_sa: - rndval = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep); - memcpy(stcb->asoc.hb_random_values, &rndval, - sizeof(stcb->asoc.hb_random_values)); - this_random = stcb->asoc.hb_random_values[0]; - stcb->asoc.hb_random_idx = 0; - stcb->asoc.hb_ect_randombit = 0; - } else { - if (stcb->asoc.hb_ect_randombit > 7) { - stcb->asoc.hb_ect_randombit = 0; - stcb->asoc.hb_random_idx++; - if (stcb->asoc.hb_random_idx > 3) { - goto warp_drive_sa; - } - } - this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx]; - } - if ((this_random >> stcb->asoc.hb_ect_randombit) & 0x01) { - if (chk != NULL) - /* ECN Nonce stuff */ - chk->rec.data.ect_nonce = SCTP_ECT1_BIT; - stcb->asoc.hb_ect_randombit++; - return (SCTP_ECT1_BIT); } else { - stcb->asoc.hb_ect_randombit++; - return (SCTP_ECT0_BIT); + return (0); } } @@ -3461,7 +3419,8 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif - union sctp_sockstore *over_addr + union sctp_sockstore *over_addr, + struct mbuf *init ) /* nofragment_flag to tell if IP_DF should be set (IPv4 only) */ { @@ -3525,6 +3484,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, SCTP_BUF_LEN(newm) = len; SCTP_BUF_NEXT(newm) = m; m = newm; + if (net != NULL) { +#ifdef INVARIANTS + if (net->flowidset == 0) { + panic("Flow ID not set"); + } +#endif + m->m_pkthdr.flowid = net->flowid; + m->m_flags |= M_FLOWID; + } else { + if ((init != NULL) && (init->m_flags & M_FLOWID)) { + m->m_pkthdr.flowid = init->m_pkthdr.flowid; + m->m_flags |= M_FLOWID; + } + } packet_length = sctp_calculate_len(m); ip = mtod(m, struct ip *); ip->ip_v = IPVERSION; @@ -3544,17 +3517,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, ip->ip_ttl = inp->ip_inp.inp.inp_ip_ttl; ip->ip_len = packet_length; - if (stcb) { - if ((stcb->asoc.ecn_allowed) && ecn_ok) { - /* Enable ECN */ - ip->ip_tos = ((u_char)(tos_value & 0xfc) | sctp_get_ect(stcb, chk)); - } else { - /* No ECN */ - ip->ip_tos = (u_char)(tos_value & 0xfc); - } - } else { - /* no association at all */ - ip->ip_tos = (tos_value & 0xfc); + ip->ip_tos = tos_value & 0xfc; + if (ecn_ok) { + ip->ip_tos |= sctp_get_ect(stcb, chk); } if (port) { ip->ip_p = IPPROTO_UDP; @@ -3686,7 +3651,7 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, * Stop any running T3 * timers here? */ - 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; SCTPDBG(SCTP_DEBUG_OUTPUT1, "Destination %p moved from PF to unreachable.\n", @@ -3854,6 +3819,20 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, SCTP_BUF_LEN(newm) = len; SCTP_BUF_NEXT(newm) = m; m = newm; + if (net != NULL) { +#ifdef INVARIANTS + if (net->flowidset == 0) { + panic("Flow ID not set"); + } +#endif + m->m_pkthdr.flowid = net->flowid; + m->m_flags |= M_FLOWID; + } else { + if ((init != NULL) && (init->m_flags & M_FLOWID)) { + m->m_pkthdr.flowid = init->m_pkthdr.flowid; + m->m_flags |= M_FLOWID; + } + } packet_length = sctp_calculate_len(m); ip6h = mtod(m, struct ip6_hdr *); @@ -3881,18 +3860,11 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp, } else { ro = (sctp_route_t *) & net->ro; } - if (stcb != NULL) { - if ((stcb->asoc.ecn_allowed) && ecn_ok) { - /* Enable ECN */ - tosBottom = (((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) | sctp_get_ect(stcb, chk)) << 4); - } else { - /* No ECN */ - tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4); - } - } else { - /* we could get no asoc if it is a O-O-T-B packet */ - tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4); + tosBottom = (((struct in6pcb *)inp)->in6p_flowinfo & 0x0c); + if (ecn_ok) { + tosBottom |= sctp_get_ect(stcb, chk); } + tosBottom <<= 4; ip6h->ip6_flow = htonl(((tosTop << 24) | ((tosBottom | flowTop) << 16) | flowBottom)); if (port) { ip6h->ip6_nxt = IPPROTO_UDP; @@ -4167,7 +4139,6 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked struct sctp_adaptation_layer_indication *ali; struct sctp_ecn_supported_param *ecn; struct sctp_prsctp_supported_param *prsctp; - struct sctp_ecn_nonce_supported_param *ecn_nonce; struct sctp_supported_chunk_types_param *pr_supported; int cnt_inits_to = 0; int padval, ret; @@ -4290,7 +4261,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked stcb->asoc.cookie_preserve_req = 0; } /* ECN parameter */ - if (SCTP_BASE_SYSCTL(sctp_ecn_enable) == 1) { + if (stcb->asoc.ecn_allowed == 1) { ecn->ph.param_type = htons(SCTP_ECN_CAPABLE); ecn->ph.param_length = htons(sizeof(*ecn)); SCTP_BUF_LEN(m) += sizeof(*ecn); @@ -4326,14 +4297,6 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked SCTP_BUF_LEN(m) += SCTP_SIZE32(p_len); - /* ECN nonce: And now tell the peer we support ECN nonce */ - if (SCTP_BASE_SYSCTL(sctp_ecn_nonce)) { - ecn_nonce = (struct sctp_ecn_nonce_supported_param *) - ((caddr_t)pr_supported + SCTP_SIZE32(p_len)); - ecn_nonce->ph.param_type = htons(SCTP_ECN_NONCE_SUPPORTED); - ecn_nonce->ph.param_length = htons(sizeof(*ecn_nonce)); - SCTP_BUF_LEN(m) += sizeof(*ecn_nonce); - } /* add authentication parameters */ if (!SCTP_BASE_SYSCTL(sctp_auth_disable)) { struct sctp_auth_random *randp; @@ -4431,7 +4394,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked (struct sockaddr *)&net->ro._l_addr, m, 0, NULL, 0, 0, 0, NULL, 0, inp->sctp_lport, stcb->rport, htonl(0), - net->port, so_locked, NULL); + net->port, so_locked, NULL, NULL); SCTPDBG(SCTP_DEBUG_OUTPUT4, "lowlevel_output - %d\n", ret); SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks); (void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time); @@ -4555,11 +4518,10 @@ sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt, case SCTP_HAS_NAT_SUPPORT: *nat_friendly = 1; /* fall through */ - case SCTP_ECN_NONCE_SUPPORTED: case SCTP_PRSCTP_SUPPORTED: if (padded_size != sizeof(struct sctp_paramhdr)) { - SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error ecnnonce/prsctp/nat support %d\n", plen); + SCTPDBG(SCTP_DEBUG_OUTPUT1, "Invalid size - error prsctp/nat support %d\n", plen); goto invalid_size; } at += padded_size; @@ -4960,7 +4922,6 @@ sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_adaptation_layer_indication *ali; struct sctp_ecn_supported_param *ecn; struct sctp_prsctp_supported_param *prsctp; - struct sctp_ecn_nonce_supported_param *ecn_nonce; struct sctp_supported_chunk_types_param *pr_supported; union sctp_sockstore store, store1, *over_addr; struct sockaddr_in *sin, *to_sin; @@ -5403,7 +5364,8 @@ do_a_abort: ecn = (struct sctp_ecn_supported_param *)((caddr_t)ali + sizeof(*ali)); /* ECN parameter */ - if (SCTP_BASE_SYSCTL(sctp_ecn_enable) == 1) { + if (((asoc != NULL) && (asoc->ecn_allowed == 1)) || + (inp->sctp_ecn_enable == 1)) { ecn->ph.param_type = htons(SCTP_ECN_CAPABLE); ecn->ph.param_length = htons(sizeof(*ecn)); SCTP_BUF_LEN(m) += sizeof(*ecn); @@ -5444,14 +5406,6 @@ do_a_abort: bzero((caddr_t)pr_supported + p_len, SCTP_SIZE32(p_len) - p_len); SCTP_BUF_LEN(m) += SCTP_SIZE32(p_len); - /* ECN nonce: And now tell the peer we support ECN nonce */ - if (SCTP_BASE_SYSCTL(sctp_ecn_nonce)) { - ecn_nonce = (struct sctp_ecn_nonce_supported_param *) - ((caddr_t)pr_supported + SCTP_SIZE32(p_len)); - ecn_nonce->ph.param_type = htons(SCTP_ECN_NONCE_SUPPORTED); - ecn_nonce->ph.param_length = htons(sizeof(*ecn_nonce)); - SCTP_BUF_LEN(m) += sizeof(*ecn_nonce); - } /* add authentication parameters */ if (!SCTP_BASE_SYSCTL(sctp_auth_disable)) { struct sctp_auth_random *randp; @@ -5611,57 +5565,11 @@ do_a_abort: (void)sctp_lowlevel_chunk_output(inp, NULL, NULL, to, m, 0, NULL, 0, 0, 0, NULL, 0, inp->sctp_lport, sh->src_port, init_chk->init.initiate_tag, - port, SCTP_SO_NOT_LOCKED, over_addr); + port, SCTP_SO_NOT_LOCKED, over_addr, init_pkt); SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks); } -void -sctp_insert_on_wheel(struct sctp_tcb *stcb, - struct sctp_association *asoc, - struct sctp_stream_out *strq, int holds_lock) -{ - if (holds_lock == 0) { - SCTP_TCB_SEND_LOCK(stcb); - } - if ((strq->next_spoke.tqe_next == NULL) && - (strq->next_spoke.tqe_prev == NULL)) { - TAILQ_INSERT_TAIL(&asoc->out_wheel, strq, next_spoke); - } - if (holds_lock == 0) { - SCTP_TCB_SEND_UNLOCK(stcb); - } -} - -void -sctp_remove_from_wheel(struct sctp_tcb *stcb, - struct sctp_association *asoc, - struct sctp_stream_out *strq, - int holds_lock) -{ - /* take off and then setup so we know it is not on the wheel */ - if (holds_lock == 0) { - SCTP_TCB_SEND_LOCK(stcb); - } - if (TAILQ_EMPTY(&strq->outqueue)) { - if (asoc->last_out_stream == strq) { - asoc->last_out_stream = TAILQ_PREV(asoc->last_out_stream, sctpwheel_listhead, next_spoke); - if (asoc->last_out_stream == NULL) { - asoc->last_out_stream = TAILQ_LAST(&asoc->out_wheel, sctpwheel_listhead); - } - if (asoc->last_out_stream == strq) { - asoc->last_out_stream = NULL; - } - } - TAILQ_REMOVE(&asoc->out_wheel, strq, next_spoke); - strq->next_spoke.tqe_next = NULL; - strq->next_spoke.tqe_prev = NULL; - } - if (holds_lock == 0) { - SCTP_TCB_SEND_UNLOCK(stcb); - } -} - static void sctp_prune_prsctp(struct sctp_tcb *stcb, struct sctp_association *asoc, @@ -5718,9 +5626,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb, } /* if chunk has enabled */ } /* tailqforeach */ - chk = TAILQ_FIRST(&asoc->send_queue); - while (chk) { - nchk = TAILQ_NEXT(chk, sctp_next); + TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) { /* Here we must move to the sent queue and mark */ if (PR_SCTP_BUF_ENABLED(chk->flags)) { if (chk->rec.data.timetodrop.tv_sec >= (long)srcv->sinfo_timetolive) { @@ -5742,8 +5648,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb, } /* end if chk->data */ } /* end if right class */ } /* end if chk pr-sctp */ - chk = nchk; - } /* end while (chk) */ + } /* tailqforeachsafe (chk) */ } /* if enabled in asoc */ } @@ -5927,11 +5832,7 @@ sctp_msg_append(struct sctp_tcb *stcb, sp->strseq = strm->next_sequence_sent; strm->next_sequence_sent++; } - if ((strm->next_spoke.tqe_next == NULL) && - (strm->next_spoke.tqe_prev == NULL)) { - /* Not on wheel, insert */ - sctp_insert_on_wheel(stcb, &stcb->asoc, strm, 1); - } + stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc, strm, sp, 1); m = NULL; SCTP_TCB_SEND_UNLOCK(stcb); out_now: @@ -6445,9 +6346,7 @@ sctp_toss_old_cookies(struct sctp_tcb *stcb, struct sctp_association *asoc) { struct sctp_tmit_chunk *chk, *nchk; - chk = TAILQ_FIRST(&asoc->control_send_queue); - while (chk) { - nchk = TAILQ_NEXT(chk, sctp_next); + TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) { if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) { TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next); if (chk->data) { @@ -6457,7 +6356,6 @@ sctp_toss_old_cookies(struct sctp_tcb *stcb, struct sctp_association *asoc) asoc->ctrl_queue_cnt--; sctp_free_a_chunk(stcb, chk); } - chk = nchk; } } @@ -6465,19 +6363,16 @@ void sctp_toss_old_asconf(struct sctp_tcb *stcb) { struct sctp_association *asoc; - struct sctp_tmit_chunk *chk, *chk_tmp; + struct sctp_tmit_chunk *chk, *nchk; struct sctp_asconf_chunk *acp; asoc = &stcb->asoc; - for (chk = TAILQ_FIRST(&asoc->asconf_send_queue); chk != NULL; - chk = chk_tmp) { - /* get next chk */ - chk_tmp = TAILQ_NEXT(chk, sctp_next); + TAILQ_FOREACH_SAFE(chk, &asoc->asconf_send_queue, sctp_next, nchk) { /* find SCTP_ASCONF chunk in queue */ if (chk->rec.chunk_id.id == SCTP_ASCONF) { if (chk->data) { acp = mtod(chk->data, struct sctp_asconf_chunk *); - if (compare_with_wrap(ntohl(acp->serial_number), stcb->asoc.asconf_seq_out_acked, MAX_SEQ)) { + if (SCTP_TSN_GT(ntohl(acp->serial_number), asoc->asconf_seq_out_acked)) { /* Not Acked yet */ break; } @@ -6518,6 +6413,7 @@ sctp_clean_up_datalist(struct sctp_tcb *stcb, } /* record time */ data_list[i]->sent_rcv_time = net->last_sent_time; + data_list[i]->rec.data.cwnd_at_send = net->cwnd; data_list[i]->rec.data.fast_retran_tsn = data_list[i]->rec.data.TSN_seq; if (data_list[i]->whoTo == NULL) { data_list[i]->whoTo = net; @@ -6525,8 +6421,7 @@ sctp_clean_up_datalist(struct sctp_tcb *stcb, } /* on to the sent queue */ tp1 = TAILQ_LAST(&asoc->sent_queue, sctpchunk_listhead); - if ((tp1) && (compare_with_wrap(tp1->rec.data.TSN_seq, - data_list[i]->rec.data.TSN_seq, MAX_TSN))) { + if ((tp1) && SCTP_TSN_GT(tp1->rec.data.TSN_seq, data_list[i]->rec.data.TSN_seq)) { struct sctp_tmit_chunk *tpp; /* need to move back */ @@ -6537,8 +6432,7 @@ sctp_clean_up_datalist(struct sctp_tcb *stcb, goto all_done; } tp1 = tpp; - if (compare_with_wrap(tp1->rec.data.TSN_seq, - data_list[i]->rec.data.TSN_seq, MAX_TSN)) { + if (SCTP_TSN_GT(tp1->rec.data.TSN_seq, data_list[i]->rec.data.TSN_seq)) { goto back_up_more; } TAILQ_INSERT_AFTER(&asoc->sent_queue, tp1, data_list[i], sctp_next); @@ -6589,9 +6483,7 @@ sctp_clean_up_ctl(struct sctp_tcb *stcb, struct sctp_association *asoc) { struct sctp_tmit_chunk *chk, *nchk; - for (chk = TAILQ_FIRST(&asoc->control_send_queue); - chk; chk = nchk) { - nchk = TAILQ_NEXT(chk, sctp_next); + TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) { if ((chk->rec.chunk_id.id == SCTP_SELECTIVE_ACK) || (chk->rec.chunk_id.id == SCTP_NR_SELECTIVE_ACK) || /* EY */ (chk->rec.chunk_id.id == SCTP_HEARTBEAT_REQUEST) || @@ -6756,6 +6648,7 @@ one_more_time: } atomic_subtract_int(&asoc->stream_queue_cnt, 1); TAILQ_REMOVE(&strq->outqueue, sp, next); + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7071,7 +6964,6 @@ dont_do_it: chk->rec.data.payloadtype = sp->ppid; chk->rec.data.context = sp->context; chk->rec.data.doing_fast_retransmit = 0; - chk->rec.data.ect_nonce = 0; /* ECN Nonce */ chk->rec.data.timetodrop = sp->ts; chk->flags = sp->act_flags; @@ -7165,6 +7057,7 @@ dont_do_it: send_lock_up = 1; } TAILQ_REMOVE(&strq->outqueue, sp, next); + stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up); if (sp->net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7194,24 +7087,6 @@ out_of: } -static struct sctp_stream_out * -sctp_select_a_stream(struct sctp_tcb *stcb, struct sctp_association *asoc) -{ - struct sctp_stream_out *strq; - - /* Find the next stream to use */ - if (asoc->last_out_stream == NULL) { - strq = TAILQ_FIRST(&asoc->out_wheel); - } else { - strq = TAILQ_NEXT(asoc->last_out_stream, next_spoke); - if (strq == NULL) { - strq = TAILQ_FIRST(&asoc->out_wheel); - } - } - return (strq); -} - - static void sctp_fill_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net, int frag_point, int eeor_mode, int *quit_now) @@ -7220,7 +7095,6 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, struct sctp_stream_out *strq, *strqn; int goal_mtu, moved_how_much, total_moved = 0, bail = 0; int locked, giveup; - struct sctp_stream_queue_pending *sp; SCTP_TCB_LOCK_ASSERT(stcb); asoc = &stcb->asoc; @@ -7244,46 +7118,17 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, strq = asoc->locked_on_sending; locked = 1; } else { - strq = sctp_select_a_stream(stcb, asoc); + strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); locked = 0; } strqn = strq; while ((goal_mtu > 0) && strq) { - sp = TAILQ_FIRST(&strq->outqueue); - if (sp == NULL) { - break; - } - /** - * Honor the users' choice if given. If not given, - * pull it only to the primary path in case of not using - * CMT. - */ - if (((sp->net != NULL) && - (sp->net != net)) || - ((sp->net == NULL) && - (asoc->sctp_cmt_on_off == 0) && - (asoc->primary_destination != net))) { - /* Do not pull to this network */ - if (locked) { - break; - } else { - strq = sctp_select_a_stream(stcb, asoc); - if (strq == NULL) - /* none left */ - break; - if (strqn == strq) { - /* I have circled */ - break; - } - continue; - } - } giveup = 0; bail = 0; moved_how_much = sctp_move_to_outqueue(stcb, strq, goal_mtu, frag_point, &locked, &giveup, eeor_mode, &bail); if (moved_how_much) - asoc->last_out_stream = strq; + stcb->asoc.ss_functions.sctp_ss_scheduled(stcb, net, asoc, strq, moved_how_much); if (locked) { asoc->locked_on_sending = strq; @@ -7292,23 +7137,10 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, break; } else { asoc->locked_on_sending = NULL; - if (TAILQ_EMPTY(&strq->outqueue)) { - if (strq == strqn) { - /* Must move start to next one */ - strqn = TAILQ_NEXT(strq, next_spoke); - if (strqn == NULL) { - strqn = TAILQ_FIRST(&asoc->out_wheel); - if (strqn == NULL) { - break; - } - } - } - sctp_remove_from_wheel(stcb, asoc, strq, 0); - } if ((giveup) || bail) { break; } - strq = sctp_select_a_stream(stcb, asoc); + strq = stcb->asoc.ss_functions.sctp_ss_select_stream(stcb, net, asoc); if (strq == NULL) { break; } @@ -7320,12 +7152,14 @@ sctp_fill_outqueue(struct sctp_tcb *stcb, if (bail) *quit_now = 1; + stcb->asoc.ss_functions.sctp_ss_packet_done(stcb, net, asoc); + if (total_moved == 0) { if ((stcb->asoc.sctp_cmt_on_off == 0) && (net == stcb->asoc.primary_destination)) { /* ran dry for primary network net */ SCTP_STAT_INCR(sctps_primary_randry); - } else if (stcb->asoc.sctp_cmt_on_off == 1) { + } else if (stcb->asoc.sctp_cmt_on_off > 0) { /* ran dry with CMT on */ SCTP_STAT_INCR(sctps_cmt_randry); } @@ -7348,16 +7182,16 @@ void sctp_move_chunks_from_net(struct sctp_tcb *stcb, struct sctp_nets *net) { struct sctp_association *asoc; - struct sctp_stream_out *outs; struct sctp_tmit_chunk *chk; struct sctp_stream_queue_pending *sp; + unsigned int i; if (net == NULL) { return; } asoc = &stcb->asoc; - TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) { - TAILQ_FOREACH(sp, &outs->outqueue, next) { + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + TAILQ_FOREACH(sp, &stcb->asoc.strmout[i].outqueue, next) { if (sp->net == net) { sctp_free_remote_addr(sp->net); sp->net = NULL; @@ -7394,7 +7228,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, * fomulate and send the low level chunks. Making sure to combine * any control in the control chunk queue also. */ - struct sctp_nets *net, *start_at, *old_start_at = NULL; + struct sctp_nets *net, *start_at, *sack_goes_to = NULL, *old_start_at = NULL; struct mbuf *outchain, *endoutchain; struct sctp_tmit_chunk *chk, *nchk; @@ -7447,10 +7281,12 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, no_data_chunks = 0; /* Nothing to possible to send? */ - if (TAILQ_EMPTY(&asoc->control_send_queue) && + if ((TAILQ_EMPTY(&asoc->control_send_queue) || + (asoc->ctrl_queue_cnt == stcb->asoc.ecn_echo_cnt_onq)) && TAILQ_EMPTY(&asoc->asconf_send_queue) && TAILQ_EMPTY(&asoc->send_queue) && - TAILQ_EMPTY(&asoc->out_wheel)) { + stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { +nothing_to_send: *reason_code = 9; return (0); } @@ -7462,12 +7298,28 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, no_data_chunks = 1; } } + if (stcb->asoc.ecn_echo_cnt_onq) { + /* Record where a sack goes, if any */ + if (no_data_chunks && + (asoc->ctrl_queue_cnt == stcb->asoc.ecn_echo_cnt_onq)) { + /* Nothing but ECNe to send - we don't do that */ + goto nothing_to_send; + } + TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { + if ((chk->rec.chunk_id.id == SCTP_SELECTIVE_ACK) || + (chk->rec.chunk_id.id == SCTP_NR_SELECTIVE_ACK)) { + sack_goes_to = chk->whoTo; + break; + } + } + } max_rwnd_per_dest = ((asoc->peers_rwnd + asoc->total_flight) / asoc->numnets); if (stcb->sctp_socket) max_send_per_dest = SCTP_SB_LIMIT_SND(stcb->sctp_socket) / asoc->numnets; else max_send_per_dest = 0; - if ((no_data_chunks == 0) && (!TAILQ_EMPTY(&asoc->out_wheel))) { + if ((no_data_chunks == 0) && + (!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc))) { TAILQ_FOREACH(net, &asoc->nets, sctp_next) { /* * This for loop we are in takes in each net, if @@ -7526,7 +7378,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp, *reason_code = 8; return (0); } - if (asoc->sctp_cmt_on_off == 1) { + if (asoc->sctp_cmt_on_off > 0) { /* get the last start point */ start_at = asoc->last_net_cmt_send_started; if (start_at == NULL) { @@ -7627,9 +7479,7 @@ again_one_more_time: /* ASCONF transmission */ /************************/ /* Now first lets go through the asconf queue */ - for (chk = TAILQ_FIRST(&asoc->asconf_send_queue); - chk; chk = nchk) { - nchk = TAILQ_NEXT(chk, sctp_next); + TAILQ_FOREACH_SAFE(chk, &asoc->asconf_send_queue, sctp_next, nchk) { if (chk->rec.chunk_id.id != SCTP_ASCONF) { continue; } @@ -7743,7 +7593,7 @@ again_one_more_time: no_fragmentflg, 0, NULL, asconf, inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), - net->port, so_locked, NULL))) { + net->port, so_locked, NULL, NULL))) { if (error == ENOBUFS) { asoc->ifp_had_enobuf = 1; SCTP_STAT_INCR(sctps_lowlevelerr); @@ -7805,9 +7655,29 @@ again_one_more_time: /* Control transmission */ /************************/ /* Now first lets go through the control queue */ - for (chk = TAILQ_FIRST(&asoc->control_send_queue); - chk; chk = nchk) { - nchk = TAILQ_NEXT(chk, sctp_next); + TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) { + if ((sack_goes_to) && + (chk->rec.chunk_id.id == SCTP_ECN_ECHO) && + (chk->whoTo != sack_goes_to)) { + /* + * if we have a sack in queue, and we are + * looking at an ecn echo that is NOT queued + * to where the sack is going.. + */ + if (chk->whoTo == net) { + /* + * Don't transmit it to where its + * going (current net) + */ + continue; + } else if (sack_goes_to == net) { + /* + * But do transmit it to this + * address + */ + goto skip_net_check; + } + } if (chk->whoTo != net) { /* * No, not sent to the network we are @@ -7815,6 +7685,7 @@ again_one_more_time: */ continue; } + skip_net_check: if (chk->data == NULL) { continue; } @@ -7932,6 +7803,20 @@ again_one_more_time: if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) { cookie = 1; no_out_cnt = 1; + } else if (chk->rec.chunk_id.id == SCTP_ECN_ECHO) { + /* + * Increment ecne send count + * here this means we may be + * over-zealous in our + * counting if the send + * fails, but its the best + * place to do it (we used + * to do it in the queue of + * the chunk, but that did + * not tell how many times + * it was sent. + */ + SCTP_STAT_INCR(sctps_sendecne); } chk->sent = SCTP_DATAGRAM_SENT; chk->snd_count++; @@ -7964,7 +7849,7 @@ again_one_more_time: no_fragmentflg, 0, NULL, asconf, inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), - net->port, so_locked, NULL))) { + net->port, so_locked, NULL, NULL))) { if (error == ENOBUFS) { asoc->ifp_had_enobuf = 1; SCTP_STAT_INCR(sctps_lowlevelerr); @@ -8028,7 +7913,7 @@ again_one_more_time: } } /* JRI: if dest is in PF state, do not send data to it */ - if ((asoc->sctp_cmt_on_off == 1) && + if ((asoc->sctp_cmt_on_off > 0) && (asoc->sctp_cmt_pf > 0) && (net->dest_state & SCTP_ADDR_PF)) { goto no_data_fill; @@ -8036,7 +7921,7 @@ again_one_more_time: if (net->flight_size >= net->cwnd) { goto no_data_fill; } - if ((asoc->sctp_cmt_on_off == 1) && + if ((asoc->sctp_cmt_on_off > 0) && (SCTP_BASE_SYSCTL(sctp_buffer_splitting) & SCTP_RECV_BUFFER_SPLITTING) && (net->flight_size > max_rwnd_per_dest)) { goto no_data_fill; @@ -8047,7 +7932,7 @@ again_one_more_time: * net. For now, this is better than nothing and it disabled * by default... */ - if ((asoc->sctp_cmt_on_off == 1) && + if ((asoc->sctp_cmt_on_off > 0) && (SCTP_BASE_SYSCTL(sctp_buffer_splitting) & SCTP_SEND_BUFFER_SPLITTING) && (max_send_per_dest > 0) && (net->flight_size > max_send_per_dest)) { @@ -8091,7 +7976,7 @@ again_one_more_time: if ((((asoc->state & SCTP_STATE_OPEN) == SCTP_STATE_OPEN) && (skip_data_for_this_net == 0)) || (cookie)) { - for (chk = TAILQ_FIRST(&asoc->send_queue); chk; chk = nchk) { + TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) { if (no_data_chunks) { /* let only control go out */ *reason_code = 1; @@ -8102,7 +7987,6 @@ again_one_more_time: *reason_code = 2; break; } - nchk = TAILQ_NEXT(chk, sctp_next); if ((chk->whoTo != NULL) && (chk->whoTo != net)) { /* Don't send the chunk on this net */ @@ -8268,7 +8152,7 @@ no_data_fill: * restart it. */ sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net); - } else if ((asoc->sctp_cmt_on_off == 1) && + } else if ((asoc->sctp_cmt_on_off > 0) && (asoc->sctp_cmt_pf > 0) && pf_hbflag && ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF) && @@ -8296,7 +8180,7 @@ no_data_fill: asconf, inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), - net->port, so_locked, NULL))) { + net->port, so_locked, NULL, NULL))) { /* error, we could not output */ if (error == ENOBUFS) { SCTP_STAT_INCR(sctps_lowlevelerr); @@ -9006,7 +8890,7 @@ sctp_chunk_retransmission(struct sctp_inpcb *inp, auth_offset, auth, stcb->asoc.authinfo.active_keyid, no_fragmentflg, 0, NULL, 0, inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), - chk->whoTo->port, so_locked, NULL))) { + chk->whoTo->port, so_locked, NULL, NULL))) { SCTP_STAT_INCR(sctps_lowlevelerr); return (error); } @@ -9264,7 +9148,7 @@ one_chunk_around: auth_offset, auth, auth_keyid, no_fragmentflg, 0, NULL, 0, inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), - net->port, so_locked, NULL))) { + net->port, so_locked, NULL, NULL))) { /* error, we could not output */ SCTP_STAT_INCR(sctps_lowlevelerr); return (error); @@ -9445,14 +9329,15 @@ sctp_chunk_output(struct sctp_inpcb *inp, */ struct sctp_association *asoc; struct sctp_nets *net; - int error = 0, num_out = 0, tot_out = 0, ret = 0, reason_code = 0, - burst_cnt = 0, burst_limit = 0; + int error = 0, num_out = 0, tot_out = 0, ret = 0, reason_code = 0; + unsigned int burst_cnt = 0; struct timeval now; int now_filled = 0; int nagle_on = 0; int frag_point = sctp_get_frag_point(stcb, &stcb->asoc); int un_sent = 0; - int fr_done, tot_frs = 0; + int fr_done; + unsigned int tot_frs = 0; asoc = &stcb->asoc; if (from_where == SCTP_OUTPUT_FROM_USR_SEND) { @@ -9549,12 +9434,11 @@ sctp_chunk_output(struct sctp_inpcb *inp, &now, &now_filled, frag_point, so_locked); return; } - if (tot_frs > asoc->max_burst) { + if ((asoc->fr_max_burst > 0) && (tot_frs >= asoc->fr_max_burst)) { /* Hit FR burst limit */ return; } if ((num_out == 0) && (ret == 0)) { - /* No more retrans to send */ break; } @@ -9563,7 +9447,6 @@ sctp_chunk_output(struct sctp_inpcb *inp, sctp_auditing(12, inp, stcb, NULL); #endif /* Check for bad destinations, if they exist move chunks around. */ - burst_limit = asoc->max_burst; TAILQ_FOREACH(net, &asoc->nets, sctp_next) { if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) == SCTP_ADDR_NOT_REACHABLE) { @@ -9576,7 +9459,7 @@ sctp_chunk_output(struct sctp_inpcb *inp, */ if (net->ref_count > 1) sctp_move_chunks_from_net(stcb, net); - } else if ((asoc->sctp_cmt_on_off == 1) && + } else if ((asoc->sctp_cmt_on_off > 0) && (asoc->sctp_cmt_pf > 0) && ((net->dest_state & SCTP_ADDR_PF) == SCTP_ADDR_PF)) { /* @@ -9592,24 +9475,29 @@ sctp_chunk_output(struct sctp_inpcb *inp, * { burst_limit = asoc->max_burst * * SCTP_SAT_NETWORK_BURST_INCR; } */ - if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst)) { - if ((net->flight_size + (burst_limit * net->mtu)) < net->cwnd) { - /* - * JRS - Use the congestion control - * given in the congestion control - * module - */ - asoc->cc_functions.sctp_cwnd_update_after_output(stcb, net, burst_limit); - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) { - sctp_log_maxburst(stcb, net, 0, burst_limit, SCTP_MAX_BURST_APPLIED); + if (asoc->max_burst > 0) { + if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst)) { + if ((net->flight_size + (asoc->max_burst * net->mtu)) < net->cwnd) { + /* + * JRS - Use the congestion + * control given in the + * congestion control module + */ + asoc->cc_functions.sctp_cwnd_update_after_output(stcb, net, asoc->max_burst); + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) { + sctp_log_maxburst(stcb, net, 0, asoc->max_burst, SCTP_MAX_BURST_APPLIED); + } + SCTP_STAT_INCR(sctps_maxburstqueued); + } + net->fast_retran_ip = 0; + } else { + if (net->flight_size == 0) { + /* + * Should be decaying the + * cwnd here + */ + ; } - SCTP_STAT_INCR(sctps_maxburstqueued); - } - net->fast_retran_ip = 0; - } else { - if (net->flight_size == 0) { - /* Should be decaying the cwnd here */ - ; } } } @@ -9656,7 +9544,7 @@ sctp_chunk_output(struct sctp_inpcb *inp, } if (TAILQ_EMPTY(&asoc->control_send_queue) && TAILQ_EMPTY(&asoc->send_queue) && - TAILQ_EMPTY(&asoc->out_wheel)) { + stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { /* Nothing left to send */ break; } @@ -9664,11 +9552,13 @@ sctp_chunk_output(struct sctp_inpcb *inp, /* Nothing left to send */ break; } - } while (num_out && (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) || - (burst_cnt < burst_limit))); + } while (num_out && + ((asoc->max_burst == 0) || + SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) || + (burst_cnt < asoc->max_burst))); if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) == 0) { - if (burst_cnt >= burst_limit) { + if ((asoc->max_burst > 0) && (burst_cnt >= asoc->max_burst)) { SCTP_STAT_INCR(sctps_maxburstqueued); asoc->burst_limit_applied = 1; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) { @@ -10018,7 +9908,7 @@ sctp_send_sack(struct sctp_tcb *stcb) if (a_chk->whoTo) { atomic_add_int(&a_chk->whoTo->ref_count, 1); } - if (compare_with_wrap(asoc->highest_tsn_inside_map, asoc->highest_tsn_inside_nr_map, MAX_TSN)) { + if (SCTP_TSN_GT(asoc->highest_tsn_inside_map, asoc->highest_tsn_inside_nr_map)) { highest_tsn = asoc->highest_tsn_inside_map; } else { highest_tsn = asoc->highest_tsn_inside_nr_map; @@ -10065,15 +9955,9 @@ sctp_send_sack(struct sctp_tcb *stcb) limit = mtod(a_chk->data, caddr_t); limit += space; - /* 0x01 is used by nonce for ecn */ - if ((SCTP_BASE_SYSCTL(sctp_ecn_enable)) && - (SCTP_BASE_SYSCTL(sctp_ecn_nonce)) && - (asoc->peer_supports_ecn_nonce)) - flags = (asoc->receiver_nonce_sum & SCTP_SACK_NONCE_SUM); - else - flags = 0; + flags = 0; - if ((asoc->sctp_cmt_on_off == 1) && + if ((asoc->sctp_cmt_on_off > 0) && SCTP_BASE_SYSCTL(sctp_cmt_use_dac)) { /*- * CMT DAC algorithm: If 2 (i.e., 0x10) packets have been @@ -10113,15 +9997,15 @@ sctp_send_sack(struct sctp_tcb *stcb) } } - if (compare_with_wrap(asoc->mapping_array_base_tsn, asoc->cumulative_tsn, MAX_TSN)) { + if (SCTP_TSN_GT(asoc->mapping_array_base_tsn, asoc->cumulative_tsn)) { offset = 1; } else { offset = asoc->mapping_array_base_tsn - asoc->cumulative_tsn; } if (((type == SCTP_SELECTIVE_ACK) && - compare_with_wrap(highest_tsn, asoc->cumulative_tsn, MAX_TSN)) || + SCTP_TSN_GT(highest_tsn, asoc->cumulative_tsn)) || ((type == SCTP_NR_SELECTIVE_ACK) && - compare_with_wrap(asoc->highest_tsn_inside_map, asoc->cumulative_tsn, MAX_TSN))) { + SCTP_TSN_GT(asoc->highest_tsn_inside_map, asoc->cumulative_tsn))) { /* we have a gap .. maybe */ for (i = 0; i < siz; i++) { tsn_map = asoc->mapping_array[i]; @@ -10193,12 +10077,12 @@ sctp_send_sack(struct sctp_tcb *stcb) siz = (((MAX_TSN - asoc->mapping_array_base_tsn) + 1) + asoc->highest_tsn_inside_nr_map + 7) / 8; } - if (compare_with_wrap(asoc->mapping_array_base_tsn, asoc->cumulative_tsn, MAX_TSN)) { + if (SCTP_TSN_GT(asoc->mapping_array_base_tsn, asoc->cumulative_tsn)) { offset = 1; } else { offset = asoc->mapping_array_base_tsn - asoc->cumulative_tsn; } - if (compare_with_wrap(asoc->highest_tsn_inside_nr_map, asoc->cumulative_tsn, MAX_TSN)) { + if (SCTP_TSN_GT(asoc->highest_tsn_inside_nr_map, asoc->cumulative_tsn)) { /* we have a gap .. maybe */ for (i = 0; i < siz; i++) { tsn_map = asoc->nr_mapping_array[i]; @@ -10377,7 +10261,7 @@ sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr, int so_locked (struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr, m_out, auth_offset, auth, stcb->asoc.authinfo.active_keyid, 1, 0, NULL, 0, stcb->sctp_ep->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag), - stcb->asoc.primary_destination->port, so_locked, NULL); + stcb->asoc.primary_destination->port, so_locked, NULL, NULL); SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks); } @@ -10414,7 +10298,7 @@ sctp_send_shutdown_complete(struct sctp_tcb *stcb, m_shutdown_comp, 0, NULL, 0, 1, 0, NULL, 0, stcb->sctp_ep->sctp_lport, stcb->rport, htonl(vtag), - net->port, SCTP_SO_NOT_LOCKED, NULL); + net->port, SCTP_SO_NOT_LOCKED, NULL, NULL); SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks); return; } @@ -10459,6 +10343,10 @@ sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh, SCTP_BUF_RESV_UF(mout, max_linkhdr); SCTP_BUF_LEN(mout) = len; SCTP_BUF_NEXT(mout) = NULL; + if (m->m_flags & M_FLOWID) { + mout->m_pkthdr.flowid = m->m_pkthdr.flowid; + mout->m_flags |= M_FLOWID; + } iph_out = NULL; #ifdef INET6 ip6_out = NULL; @@ -10866,10 +10754,19 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net, asoc = &stcb->asoc; SCTP_TCB_LOCK_ASSERT(stcb); TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { - if (chk->rec.chunk_id.id == SCTP_ECN_ECHO) { + if ((chk->rec.chunk_id.id == SCTP_ECN_ECHO) && (net == chk->whoTo)) { /* found a previous ECN_ECHO update it if needed */ + uint32_t cnt, ctsn; + ecne = mtod(chk->data, struct sctp_ecne_chunk *); - ecne->tsn = htonl(high_tsn); + ctsn = ntohl(ecne->tsn); + if (SCTP_TSN_GT(high_tsn, ctsn)) { + ecne->tsn = htonl(high_tsn); + SCTP_STAT_INCR(sctps_queue_upd_ecne); + } + cnt = ntohl(ecne->num_pkts_since_cwr); + cnt++; + ecne->num_pkts_since_cwr = htonl(cnt); return; } } @@ -10879,7 +10776,7 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net, return; } chk->copy_by_ref = 0; - SCTP_STAT_INCR(sctps_sendecne); + SCTP_STAT_INCR(sctps_queue_upd_ecne); chk->rec.chunk_id.id = SCTP_ECN_ECHO; chk->rec.chunk_id.can_take_data = 0; chk->asoc = &stcb->asoc; @@ -10901,7 +10798,8 @@ sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net, ecne->ch.chunk_flags = 0; ecne->ch.chunk_length = htons(sizeof(struct sctp_ecne_chunk)); ecne->tsn = htonl(high_tsn); - TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next); + ecne->num_pkts_since_cwr = htonl(1); + TAILQ_INSERT_HEAD(&stcb->asoc.control_send_queue, chk, sctp_next); asoc->ctrl_queue_cnt++; } @@ -11079,7 +10977,7 @@ jump_out: } void -sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn) +sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn, uint8_t override) { struct sctp_association *asoc; struct sctp_cwr_chunk *cwr; @@ -11087,18 +10985,28 @@ sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn) asoc = &stcb->asoc; SCTP_TCB_LOCK_ASSERT(stcb); + + TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { - if (chk->rec.chunk_id.id == SCTP_ECN_CWR) { - /* found a previous ECN_CWR update it if needed */ + if ((chk->rec.chunk_id.id == SCTP_ECN_CWR) && (net == chk->whoTo)) { + /* + * found a previous CWR queued to same destination + * update it if needed + */ + uint32_t ctsn; + cwr = mtod(chk->data, struct sctp_cwr_chunk *); - if (compare_with_wrap(high_tsn, ntohl(cwr->tsn), - MAX_TSN)) { + ctsn = ntohl(cwr->tsn); + if (SCTP_TSN_GT(high_tsn, ctsn)) { cwr->tsn = htonl(high_tsn); } + if (override & SCTP_CWR_REDUCE_OVERRIDE) { + /* Make sure override is carried */ + cwr->ch.chunk_flags |= SCTP_CWR_REDUCE_OVERRIDE; + } return; } } - /* nope could not find one to update so we must build one */ sctp_alloc_a_chunk(stcb, chk); if (chk == NULL) { return; @@ -11121,7 +11029,7 @@ sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn) atomic_add_int(&chk->whoTo->ref_count, 1); cwr = mtod(chk->data, struct sctp_cwr_chunk *); cwr->ch.chunk_type = SCTP_ECN_CWR; - cwr->ch.chunk_flags = 0; + cwr->ch.chunk_flags = override; cwr->ch.chunk_length = htons(sizeof(struct sctp_cwr_chunk)); cwr->tsn = htonl(high_tsn); TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next); @@ -11503,6 +11411,10 @@ sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag, SCTP_BUF_RESV_UF(mout, max_linkhdr); SCTP_BUF_LEN(mout) = len; SCTP_BUF_NEXT(mout) = err_cause; + if (m->m_flags & M_FLOWID) { + mout->m_pkthdr.flowid = m->m_pkthdr.flowid; + mout->m_flags |= M_FLOWID; + } iph_out = NULL; #ifdef INET6 ip6_out = NULL; @@ -11748,6 +11660,10 @@ sctp_send_operr_to(struct mbuf *m, int iphlen, struct mbuf *scm, uint32_t vtag, SCTP_BUF_RESV_UF(mout, max_linkhdr); SCTP_BUF_LEN(mout) = len; SCTP_BUF_NEXT(mout) = scm; + if (m->m_flags & M_FLOWID) { + mout->m_pkthdr.flowid = m->m_pkthdr.flowid; + mout->m_flags |= M_FLOWID; + } iph_out = NULL; #ifdef INET6 ip6_out = NULL; @@ -12466,8 +12382,7 @@ sctp_lower_sosend(struct socket *so, TAILQ_INIT(&asoc->strmout[i].outqueue); asoc->strmout[i].stream_no = i; asoc->strmout[i].last_msg_incomplete = 0; - asoc->strmout[i].next_spoke.tqe_next = 0; - asoc->strmout[i].next_spoke.tqe_prev = 0; + asoc->ss_functions.sctp_ss_init_stream(&asoc->strmout[i], NULL); } } } @@ -12860,11 +12775,7 @@ skip_preblock: SCTP_STAT_INCR(sctps_sends_with_unord); } TAILQ_INSERT_TAIL(&strm->outqueue, sp, next); - if ((strm->next_spoke.tqe_next == NULL) && - (strm->next_spoke.tqe_prev == NULL)) { - /* Not on wheel, insert */ - sctp_insert_on_wheel(stcb, asoc, strm, 1); - } + stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, asoc, strm, sp, 1); SCTP_TCB_SEND_UNLOCK(stcb); } else { SCTP_TCB_SEND_LOCK(stcb); |