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 @@ -1171,15 +1171,15 @@ #endif ip->ip_tos |= IPTOS_ECN_ECT0; TCPSTAT_INC(tcps_ecn_ect0); + /* + * 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; + } } - - /* - * Reply with proper ECN notifications. - */ - 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 @@ -1799,9 +1799,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; } @@ -9488,13 +9494,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;