The TCP/IP ECN signaling loop looks like this
-CE-> (latches ECE)
<---- ACK, ECE
ACK, CWR ----> (unlatches ECE)
However, RFC3168 has a SHOULD for sending the CWR with a *new* data packet.
New data packets usually are also marked as ECN-capable transport (ECT), while
control packets, pure ACKs and retransmissions are sent without IP ECT codepoint.
Some overly restrictive clients (Linux) explicitly only accept and process
CWR, if they are received on (new) data segments, but ignore them completely when set in other packets. While such an overly restrictive behavior is also problematic, BSD *should* adhere to the guidance of RFC3168 and sent out
CWR flags only when (new) data packets are sent out (sec 6.1.2) rather than simply the very next segment (sec. 6.1):
RFC3168, section 6.1:
* The sender sets the CWR flag in the TCP header of the next
packet sent to the receiver to acknowledge its receipt of and
reaction to the ECN-Echo flag.
RFC3168, section 6.1.2:
When an ECN-Capable TCP sender reduces its congestion window for any
reason (because of a retransmit timeout, a Fast Retransmit, or in
response to an ECN Notification), the TCP sender sets the CWR flag in
the TCP header of the first new data packet sent after the window
The interaction can lead to a race to the bottom, where cwnd is continually
decreased and kept at a very small value. This results in extremely poor
performance for ECN-enabled TCP sessions.