Networking

Explicit Congestion Notification (ECN): Marking Packets Instead of Dropping Them

A congested router facing a full queue has traditionally had exactly one way to say "slow down": throw the packet away and force a retransmit that adds a full round-trip of delay. Explicit Congestion Notification (ECN) hands the router a gentler option — flip two bits in the IP header and let the packet arrive, its "Congestion Experienced" flag now set, so the receiver can whisper the warning back to the sender before the queue ever overflows.

ECN is an end-to-end signaling mechanism, standardized in RFC 3168 (2001), that lets ECN-aware routers mark packets to indicate incipient congestion rather than dropping them. It repurposes the two low-order bits of the IPv4 Type-of-Service octet (the IPv6 Traffic Class octet) plus two spare TCP header flags, turning a lossy, delay-inducing feedback loop into an explicit, loss-free one.

  • TypeNetwork congestion-signaling mechanism (L3/L4)
  • StandardizedRFC 3168, September 2001
  • Signal cost2 bits in IP header + 2 TCP flags
  • Header fieldLow-order 2 bits of IPv4 TOS / IPv6 Traffic Class
  • Used inLinux/Windows TCP stacks, DCTCP data centers, L4S, QUIC
  • Key ideaMark packets on incipient congestion instead of dropping them

Interactive visualization

Press play, or step through manually. The visualization is yours to drive — try it before reading on.

Open visualization fullscreen ↗

Watch the 60-second explainer

A condensed visual walkthrough — narrated, captioned, under a minute.

The Problem: Loss as the Only Congestion Signal

Classic TCP congestion control is implicit: the sender infers congestion only when a packet is lost, detected via duplicate ACKs or a retransmission timeout. A router with a filling queue therefore had to drop a packet to communicate "back off," and the sender learned about it only a full round-trip later. This couples the congestion signal to two costs no one wants: added latency (the queue was already deep before the drop) and wasted work (the dropped packet must be retransmitted).

  • Tail drop — the naive policy of discarding whatever arrives at a full buffer — causes global synchronization, where many flows lose packets at once and back off in lockstep, leaving the link underutilized.
  • Bufferbloat — oversized buffers that never drop until they are enormously deep — hides congestion until queuing delay is hundreds of milliseconds.

ECN's insight, building on the Active Queue Management (AQM) work of the late 1990s, is to decouple the signal from the loss. A router can flag a packet the moment congestion begins to build, delivering the warning without the retransmit and without waiting for the buffer to overflow.

How It Works: Two Bits and a Three-Way Conversation

ECN reuses the two low-order bits of the IPv4 Type-of-Service octet (identically, the IPv6 Traffic Class octet), giving four codepoints: 00 Not-ECT, 10 ECT(0), 01 ECT(1), and 11 CE (Congestion Experienced). The end-to-end loop has three participants.

  1. Negotiation. At connection setup the initiator sends an ECN-setup SYN with the TCP ECE and CWR flags both set. A willing peer replies with an ECN-setup SYN-ACK (ECE set, CWR clear). Only then does the flow mark its data packets ECT.
  2. Marking. An ECN-capable router running an AQM (e.g. RED) that would otherwise drop an ECT packet instead flips its codepoint to CE (11) and forwards it.
  3. Echo & response. The receiver, seeing CE, sets the ECE flag on returning ACKs. The sender treats this like a loss — it halves its congestion window (cwnd) — then sets CWR on its next data packet to confirm it reacted, which tells the receiver to stop echoing.
router:   if aqm_would_drop(pkt) and pkt.ecn in {ECT0,ECT1}:
              pkt.ecn = CE          # mark, don't drop
receiver: if pkt.ecn == CE: ack.ECE = 1
sender:   if ack.ECE: cwnd /= 2; next_pkt.CWR = 1

Cost, Complexity, and a Step Trace

ECN's overhead is essentially free. The signal is 2 header bits already present in every IP packet plus 2 pre-existing TCP flags — zero extra bytes, no new packets. Router-side marking is O(1) per packet: the AQM already computes a drop decision from the average queue length; ECN only changes the action from discard to codepoint-write. Feedback latency is one round-trip time (RTT), identical to loss-based feedback, but without the retransmission.

Trace a single congestion event on a flow with cwnd = 40:

t0  sender -> pkt#500  ECN=ECT(0)
t1  queue avg crosses RED threshold; router marks:
            pkt#500  ECN=CE
t2  receiver gets CE  -> sets ECE on ACK for #500
t3  sender sees ECE   -> cwnd 40 -> 20 (halve)
            sends pkt#541 with CWR=1
t4  receiver sees CWR -> stops setting ECE
    (no packet was lost; no retransmit occurred)

Compare the loss path: at t1 the router drops #500; the sender detects it only via 3 dup-ACKs or an RTO, then retransmits #500 and halves cwnd. ECN reaches the same window reduction with one fewer packet in flight and no head-of-line stall while the retransmit is awaited.

Where It's Used in Real Systems

ECN moved from experiment to infrastructure over two decades:

  • OS TCP stacks. Linux supports ECN and negotiates it when a peer offers it (net.ipv4.tcp_ecn; mode 1 = active, 2 = accept-only, the common default). Windows, macOS, and the BSDs all implement RFC 3168.
  • Data centers — DCTCP. Data Center TCP marks packets at a shallow queue threshold and reacts proportionally to the fraction of marked ACKs rather than halving on any mark, achieving very short queues on high-bandwidth fabrics. It underpins low-latency RPC at hyperscalers.
  • L4S. The Low Latency, Low Loss, Scalable-throughput architecture (RFC 9330/9331/9332, 2023) uses the ECT(1) codepoint to steer traffic into a separate low-latency queue via Dual-Queue Coupled AQM, targeting sub-millisecond queuing delay for cloud gaming and interactive video.
  • QUIC. The transport behind HTTP/3 carries ECN counts in ACK frames, extending explicit marking to UDP-based flows.
  • Routers/switches. Cisco, Juniper, and merchant silicon implement ECN marking in their WRED/AQM data paths.

ECN vs. Its Alternatives: The Tradeoffs

ECN doesn't replace congestion control — it changes how the signal is delivered. Its cousins differ in cost and precision.

  • vs. Tail-drop / loss-based signaling. Loss needs no cooperation and works with any router, but each signal costs a retransmit and extra delay. ECN removes both costs — but only if every hop and both endpoints support it. One non-ECN router mid-path falls back to dropping.
  • vs. RED without ECN. Random Early Detection still drops early to break global synchronization. ECN is best thought of as "RED that marks instead of drops" — same queue math, loss-free action.
  • vs. Delay-based control (Vegas, BBR). These infer congestion from RTT growth, needing no router support at all, but a noisy RTT is a fuzzier signal than an explicit CE mark.
  • vs. Accurate ECN (AccECN, RFC 9768). Classic ECN's single ECE bit conveys at most one congestion event per RTT. AccECN adds a counter so senders (and L4S) learn the exact number of marked packets — essential for DCTCP-style proportional response.

Pitfalls, Failure Modes, and Significance

ECN's history is a case study in incremental deployment on a network no one controls.

  • Middlebox mangling. Early firewalls and load balancers dropped or reset connections that set the ECN SYN flags, so operating systems shipped ECN disabled by default for years. "Fall-back" logic that disables ECN on a failed handshake was the pragmatic fix.
  • Codepoint bleaching. Some routers rewrite the ECN bits to 00, silently erasing marks and CE signals so the sender never reacts.
  • Tunnels. Naive IP-in-IP encapsulation could hide or lose the inner CE mark; RFC 6040 defines correct copy/propagation of ECN across tunnel ingress and egress.
  • Reordering the two ECT points. ECT(0) vs ECT(1) once carried an anti-cheating nonce; that scheme was abandoned, and ECT(1) was repurposed as the L4S identifier — a reminder that spare bits get re-used.

The significance is large: ECN is the mechanism that makes loss-free, low-latency congestion control possible. Every effort to tame bufferbloat and hit sub-millisecond queuing — DCTCP, CoDel-plus-ECN, L4S — rests on the same two-bit idea: mark the packet, don't drop it.

The four ECN codepoints in the 2-bit IP header field (RFC 3168), and how each is set and interpreted.
CodepointBit patternMeaningWho sets it
Not-ECT00Not ECN-Capable TransportSender (legacy / ECN-off flow)
ECT(0)10ECN-Capable Transport, nonce 0ECN-capable sender (Classic)
ECT(1)01ECN-Capable Transport, nonce 1ECN-capable sender; used to mark L4S traffic
CE11Congestion ExperiencedRouter / AQM when queue builds
(TCP ECE)flagEcho the CE back to the senderReceiver, in ACKs
(TCP CWR)flagCongestion Window Reduced (ack'd the echo)Sender, after halving cwnd

Frequently asked questions

Which bits does ECN use, and where are they in the packet?

ECN uses the two least-significant bits of the IPv4 Type-of-Service octet, which is the IPv6 Traffic Class octet. Those two bits form four codepoints: 00 (Not-ECT), 10 (ECT(0)), 01 (ECT(1)), and 11 (CE, Congestion Experienced). The feedback also uses two TCP header flags, ECE and CWR, to carry the signal back to the sender.

What is the difference between ECT(0) and ECT(1)?

Both mean the sender is ECN-capable. In Classic ECN they were nominally interchangeable, with ECT(0) recommended as the default; the pair originally supported an ECN nonce for cheat detection that was later abandoned. Modern L4S (RFC 9331) repurposes ECT(1) as an identifier so routers can steer that traffic into a separate low-latency queue.

Does ECN reduce latency by itself?

Not directly — ECN is a signaling mechanism, not a congestion controller. It reduces latency indirectly by letting routers signal congestion before the queue overflows and by avoiding retransmissions. The actual latency gains come from congestion controllers like DCTCP or the L4S dual-queue AQM that react to marks by keeping queues short.

Why was ECN disabled by default for so long?

Broken middleboxes. Some firewalls and load balancers treated the ECN bits in the TCP SYN as invalid and dropped or reset the connection, breaking connectivity outright. Stacks therefore shipped ECN off, or added fall-back logic that retries without ECN if the ECN-enabled handshake fails. Deployment has since become much safer and it is now widely negotiated when offered.

How does a sender react to a CE mark differently from a packet drop?

The end result is similar — reduce the congestion window — but the path differs. On CE the receiver sets ECE on its ACKs, the sender halves cwnd (in Classic ECN) and sets CWR to acknowledge it, and no packet is lost. On a drop, the sender must detect the loss via duplicate ACKs or a timeout, halve cwnd, and also retransmit the missing segment, adding delay and wasted bandwidth.

What is L4S and how does it relate to ECN?

L4S (Low Latency, Low Loss, Scalable throughput), specified in RFCs 9330-9332 in 2023, is an architecture that uses ECN's ECT(1) codepoint to identify scalable-congestion-control traffic and route it through a separate low-latency queue with a Dual-Queue Coupled AQM. It builds directly on ECN marking to target sub-millisecond queuing delay for interactive applications like cloud gaming.