diff --git a/sys/netinet/tcp_ecn.h b/sys/netinet/tcp_ecn.h --- a/sys/netinet/tcp_ecn.h +++ b/sys/netinet/tcp_ecn.h @@ -39,6 +39,7 @@ #include #define TH_ACE_SHIFT 6 +#define MAX_ACE_DELTA 3 void tcp_ecn_input_syn_sent(struct tcpcb *, uint16_t, int); void tcp_ecn_input_parallel_syn(struct tcpcb *, uint16_t, int); diff --git a/sys/netinet/tcp_ecn.c b/sys/netinet/tcp_ecn.c --- a/sys/netinet/tcp_ecn.c +++ b/sys/netinet/tcp_ecn.c @@ -309,8 +309,14 @@ if (tp->t_flags2 & (TF2_ECN_PERMIT | TF2_ACE_PERMIT)) { if (tp->t_flags2 & TF2_ACE_PERMIT) { - if ((iptos & IPTOS_ECN_MASK) == IPTOS_ECN_CE) + if ((iptos & IPTOS_ECN_MASK) == IPTOS_ECN_CE) { tp->t_rcep += 1; + if (tp->t_rcepdelta < UCHAR_MAX) { + tp->t_rcepdelta++; + } + if (tp->t_rcepdelta >= MAX_ACE_DELTA) + tp->t_flags |= TF_ACKNOW; + } if (tp->t_flags2 & TF2_ECN_PERMIT) { delta_cep = (tcp_ecn_get_ace(thflags) + 8 - (tp->t_scep & 7)) & 7; @@ -430,6 +436,7 @@ */ if (tp->t_flags2 & TF2_ACE_PERMIT) { tcp_ecn_set_ace(thflags, tp->t_rcep); + tp->t_rcepdelta = 0; if (!(tp->t_flags2 & TF2_ECN_PERMIT)) { /* * here we process the final diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1522,7 +1522,7 @@ uint16_t thflags; int acked, ourfinisacked, needoutput = 0; sackstatus_t sack_changed; - int rstreason, todrop, win, incforsyn = 0; + int rstreason, todrop, win, incforsyn = 0, delta_ce; uint32_t tiwin; uint16_t nsegs; char *s; @@ -1600,10 +1600,11 @@ /* * TCP ECN processing. */ - if (tcp_ecn_input_segment(tp, thflags, tlen, + if ((delta_ce = tcp_ecn_input_segment(tp, thflags, tlen, tcp_packets_this_ack(tp, th->th_ack), - iptos)) + iptos))) { cc_cong_signal(tp, th, CC_ECN); + } /* * Parse options on any incoming segment. @@ -1619,6 +1620,23 @@ /* XXX: should drop? */ } #endif + + /* + * For ACKs conveying new ACE information only, + * stop processing them here before plain ACK + * DupACK processing would take place. + */ + if (__predict_false(delta_ce >= MAX_ACE_DELTA) && + !(to.to_flags & TOF_SACK) && + (tlen == 0) && + (th->th_seq == tp->rcv_nxt) && + !(tp->t_flags & TF_ACKNOW) && + ((to.to_flags & TOF_TS) == 0 || + TSTMP_GEQ(to.to_tsval, tp->ts_recent)) ) { + TCPSTAT_INC(tcps_predack); + goto drop; + } + /* * If echoed timestamp is later than the current time, * fall back to non RFC1323 RTT calculation. Normalize diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -455,6 +455,7 @@ uint32_t t_dsack_pack; /* dsack packets we have eceived */ uint8_t t_tmr_granularity; /* Granularity of all timers srtt etc */ uint8_t t_rttupdated; /* number of times rtt sampled */ + uint8_t t_rcepdelta; /* Number of received CE marks not sent */ /* TCP Fast Open */ uint8_t t_tfo_client_cookie_len; /* TFO client cookie length */ uint32_t t_end_info_status; /* Status flag of end info */