diff options
-rw-r--r-- | sys/netinet/sctp.h | 4 | ||||
-rw-r--r-- | sys/netinet/sctp_header.h | 6 | ||||
-rw-r--r-- | sys/netinet/sctp_indata.c | 8 | ||||
-rw-r--r-- | sys/netinet/sctp_input.c | 112 | ||||
-rw-r--r-- | sys/netinet/sctp_output.c | 77 | ||||
-rw-r--r-- | sys/netinet/sctp_output.h | 2 | ||||
-rw-r--r-- | sys/netinet/sctp_pcb.c | 1 | ||||
-rw-r--r-- | sys/netinet/sctp_structs.h | 7 | ||||
-rw-r--r-- | sys/netinet/sctputil.c | 1 |
9 files changed, 165 insertions, 53 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index 8e9c0c1..135ce8d 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -417,6 +417,10 @@ struct sctp_error_unrecognized_chunk { #define SCTP_BADCRC 0x02 #define SCTP_PACKET_TRUNCATED 0x04 +/* Flag for ECN -CWR */ +#define SCTP_CWR_REDUCE_OVERRIDE 0x01 +#define SCTP_CWR_IN_SAME_WINDOW 0x02 + #define SCTP_SAT_NETWORK_MIN 400 /* min ms for RTT to set satellite * time */ #define SCTP_SAT_NETWORK_BURST_INCR 2 /* how many times to multiply maxburst diff --git a/sys/netinet/sctp_header.h b/sys/netinet/sctp_header.h index 7fc3e56..2e94fb7 100644 --- a/sys/netinet/sctp_header.h +++ b/sys/netinet/sctp_header.h @@ -360,9 +360,15 @@ struct sctp_cookie_ack_chunk { } SCTP_PACKED; /* Explicit Congestion Notification Echo (ECNE) */ +struct old_sctp_ecne_chunk { + struct sctp_chunkhdr ch; + uint32_t tsn; +} SCTP_PACKED; + struct sctp_ecne_chunk { struct sctp_chunkhdr ch; uint32_t tsn; + uint32_t num_pkts_since_cwr; } SCTP_PACKED; /* Congestion Window Reduced (CWR) */ diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index 7ede155..fce913e 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -3829,6 +3829,10 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack, } /* First setup for CC stuff */ TAILQ_FOREACH(net, &asoc->nets, sctp_next) { + if (SCTP_TSN_GT(cumack, net->cwr_window_tsn)) { + /* Drag along the window_tsn for cwr's */ + net->cwr_window_tsn = cumack; + } net->prev_cwnd = net->cwnd; net->net_ack = 0; net->net_ack2 = 0; @@ -4522,6 +4526,10 @@ sctp_handle_sack(struct mbuf *m, int offset_seg, int offset_dup, * destination address basis. */ TAILQ_FOREACH(net, &asoc->nets, sctp_next) { + if (SCTP_TSN_GT(cum_ack, net->cwr_window_tsn)) { + /* Drag along the window_tsn for cwr's */ + net->cwr_window_tsn = cum_ack; + } net->prev_cwnd = net->cwnd; net->net_ack = 0; net->net_ack2 = 0; diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index bb8722f..664ff62 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -1678,8 +1678,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 @@ -1835,7 +1833,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; @@ -2073,7 +2070,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; @@ -2915,25 +2911,38 @@ 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; + 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); + pkt_cnt = ntohl(cp->num_pkts_since_cwr); /* 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); + 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.nonce_resync_tsn = stcb->asoc.sending_seq - 1; } else { - stcb->asoc.nonce_resync_tsn = lchk->rec.data.TSN_seq; + window_data_tsn = stcb->asoc.nonce_resync_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; TAILQ_FOREACH(lchk, &stcb->asoc.sent_queue, sctp_next) { if (lchk->rec.data.TSN_seq == tsn) { @@ -2944,32 +2953,71 @@ sctp_handle_ecn_echo(struct sctp_ecne_chunk *cp, break; } } - if (net == NULL) - /* default is we use the primary */ - net = stcb->asoc.primary_destination; - - if (SCTP_TSN_GT(tsn, stcb->asoc.last_cwr_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)) { /* * JRS - Use the congestion control given in the pluggable * CC module */ + int ocwnd; + + ocwnd = net->cwnd; stcb->asoc.cc_functions.sctp_cwnd_update_after_ecn_echo(stcb, net); /* - * 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)) { + /* + * Another loss in the same window update how man + * marks we have had + */ + + if (pkt_cnt > net->lost_cnt) { + /* Should be the case */ + net->ecn_ce_pkt_cnt += (pkt_cnt - net->lost_cnt); + net->lost_cnt = pkt_cnt; + } + net->last_cwr_tsn = tsn; + } } /* * 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 @@ -2978,18 +3026,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 (SCTP_TSN_GE(ntohl(cp->tsn), ntohl(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, @@ -3000,7 +3052,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; + } } } } @@ -5041,7 +5095,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: diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 480c88e..0651054 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -7274,7 +7274,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; @@ -7327,10 +7327,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) && stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) { +nothing_to_send: *reason_code = 9; return (0); } @@ -7342,6 +7344,21 @@ 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; @@ -7685,6 +7702,28 @@ again_one_more_time: /************************/ /* Now first lets go through the control queue */ 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 @@ -7692,6 +7731,7 @@ again_one_more_time: */ continue; } + skip_net_check: if (chk->data == NULL) { continue; } @@ -10761,11 +10801,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); - SCTP_STAT_INCR(sctps_queue_upd_ecne); + ctsn = ntohl(ecne->tsn); + if (SCTP_TSN_GT(high_tsn, ctsn)) { + ecne->tsn = htonl(high_tsn); + cnt = ntohl(ecne->num_pkts_since_cwr); + cnt++; + ecne->num_pkts_since_cwr = htonl(cnt); + SCTP_STAT_INCR(sctps_queue_upd_ecne); + } return; } } @@ -10797,7 +10845,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++; } @@ -10975,7 +11024,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; @@ -10983,17 +11032,7 @@ 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 */ - cwr = mtod(chk->data, struct sctp_cwr_chunk *); - if (SCTP_TSN_GT(high_tsn, ntohl(cwr->tsn))) { - cwr->tsn = htonl(high_tsn); - } - return; - } - } - /* nope could not find one to update so we must build one */ + sctp_alloc_a_chunk(stcb, chk); if (chk == NULL) { return; @@ -11016,7 +11055,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); diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h index 70fa719..cab1f52 100644 --- a/sys/netinet/sctp_output.h +++ b/sys/netinet/sctp_output.h @@ -163,7 +163,7 @@ sctp_send_packet_dropped(struct sctp_tcb *, struct sctp_nets *, struct mbuf *, -void sctp_send_cwr(struct sctp_tcb *, struct sctp_nets *, uint32_t); +void sctp_send_cwr(struct sctp_tcb *, struct sctp_nets *, uint32_t, uint8_t); void diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index f48ed6a..246ff0d 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -3854,6 +3854,7 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr, net->RTO_measured = 0; stcb->asoc.numnets++; *(&net->ref_count) = 1; + net->cwr_window_tsn = net->last_cwr_tsn = stcb->asoc.sending_seq - 1; net->tos_flowlabel = 0; if (SCTP_BASE_SYSCTL(sctp_udp_tunneling_for_client_enable)) { net->port = htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)); diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index 62326be..4a8fa46 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -211,7 +211,10 @@ struct sctp_nets { /* mtu discovered so far */ uint32_t mtu; uint32_t ssthresh; /* not sure about this one for split */ - + uint32_t last_cwr_tsn; + uint32_t cwr_window_tsn; + uint32_t ecn_ce_pkt_cnt; + uint32_t lost_cnt; /* smoothed average things for RTT and RTO itself */ int lastsa; int lastsv; @@ -864,7 +867,6 @@ struct sctp_association { uint32_t highest_tsn_inside_nr_map; uint32_t last_echo_tsn; - uint32_t last_cwr_tsn; uint32_t fast_recovery_tsn; uint32_t sat_t3_recovery_tsn; uint32_t tsn_last_delivered; @@ -1048,7 +1050,6 @@ struct sctp_association { uint16_t ecn_echo_cnt_onq; uint16_t free_chunk_cnt; - uint8_t stream_locked; uint8_t authenticated; /* packet authenticated ok */ /* diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 617494c..5917578 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -971,7 +971,6 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, asoc->last_net_cmt_send_started = NULL; /* This will need to be adjusted */ - asoc->last_cwr_tsn = asoc->init_seq_number - 1; asoc->last_acked_seq = asoc->init_seq_number - 1; asoc->advanced_peer_ack_point = asoc->last_acked_seq; asoc->asconf_seq_in = asoc->last_acked_seq; |