Index: sys/netinet/tcp_input.c =================================================================== --- sys/netinet/tcp_input.c +++ sys/netinet/tcp_input.c @@ -445,9 +445,15 @@ } break; case CC_ECN: - if (!IN_CONGRECOVERY(tp->t_flags)) { + if (!IN_CONGRECOVERY(tp->t_flags) || + /* + * Allow ECN reaction on ACK to CWR, if + * that data segment was also CE marked. + */ + SEQ_GEQ(th->th_ack, tp->snd_recover)) { + EXIT_CONGRECOVERY(tp->t_flags); TCPSTAT_INC(tcps_ecn_rcwnd); - tp->snd_recover = tp->snd_max; + tp->snd_recover = tp->snd_max+1; if (tp->t_flags2 & TF2_ECN_PERMIT) tp->t_flags2 |= TF2_ECN_SND_CWR; } Index: sys/netinet/tcp_output.c =================================================================== --- sys/netinet/tcp_output.c +++ sys/netinet/tcp_output.c @@ -1163,7 +1163,8 @@ */ if (len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && (sack_rxmit == 0) && - !((tp->t_flags & TF_FORCEDATA) && len == 1)) { + !((tp->t_flags & TF_FORCEDATA) && len == 1 && + SEQ_LT(tp->snd_una, tp->snd_max))) { #ifdef INET6 if (isipv6) ip6->ip6_flow |= htonl(IPTOS_ECN_ECT0 << 20); @@ -1171,14 +1172,14 @@ #endif ip->ip_tos |= IPTOS_ECN_ECT0; TCPSTAT_INC(tcps_ecn_ect0); - } - - /* - * Reply with proper ECN notifications. - */ - if (tp->t_flags2 & TF2_ECN_SND_CWR) { - flags |= TH_CWR; - tp->t_flags2 &= ~TF2_ECN_SND_CWR; + /* + * Reply with proper ECN notifications. + * Only set CWR on new data segments. + */ + if (tp->t_flags2 & TF2_ECN_SND_CWR) { + flags |= TH_CWR; + tp->t_flags2 &= ~TF2_ECN_SND_CWR; + } } if (tp->t_flags2 & TF2_ECN_SND_ECE) flags |= TH_ECE; Index: sys/netinet/tcp_stacks/rack.c =================================================================== --- sys/netinet/tcp_stacks/rack.c +++ sys/netinet/tcp_stacks/rack.c @@ -1800,9 +1800,15 @@ } break; case CC_ECN: - if (!IN_CONGRECOVERY(tp->t_flags)) { + if (!IN_CONGRECOVERY(tp->t_flags) || + /* + * Allow ECN reaction on ACK to CWR, if + * that data segment was also CE marked. + */ + SEQ_GEQ(th->th_ack, tp->snd_recover)) { + EXIT_CONGRECOVERY(tp->t_flags); TCPSTAT_INC(tcps_ecn_rcwnd); - tp->snd_recover = tp->snd_max; + tp->snd_recover = tp->snd_max+1; if (tp->t_flags2 & TF2_ECN_PERMIT) tp->t_flags2 |= TF2_ECN_SND_CWR; } @@ -9481,7 +9487,8 @@ */ if (len > 0 && SEQ_GEQ(tp->snd_nxt, tp->snd_max) && (sack_rxmit == 0) && - !((tp->t_flags & TF_FORCEDATA) && len == 1)) { + !((tp->t_flags & TF_FORCEDATA) && len == 1 && + SEQ_LT(tp->snd_una, tp->snd_max))) { #ifdef INET6 if (isipv6) ip6->ip6_flow |= htonl(IPTOS_ECN_ECT0 << 20); @@ -9489,13 +9496,14 @@ #endif ip->ip_tos |= IPTOS_ECN_ECT0; TCPSTAT_INC(tcps_ecn_ect0); - } - /* - * Reply with proper ECN notifications. - */ - if (tp->t_flags2 & TF2_ECN_SND_CWR) { - flags |= TH_CWR; - tp->t_flags2 &= ~TF2_ECN_SND_CWR; + /* + * Reply with proper ECN notifications. + * Only set CWR on new data segments. + */ + if (tp->t_flags2 & TF2_ECN_SND_CWR) { + flags |= TH_CWR; + tp->t_flags2 &= ~TF2_ECN_SND_CWR; + } } if (tp->t_flags2 & TF2_ECN_SND_ECE) flags |= TH_ECE;