Congestion Control in Computer Networks
Congestion happens when more traffic is injected into the network than the network can handle. Routers drop packets, queues fill up, retransmissions pile on top of existing traffic, and the whole system spirals — more retransmits = more congestion = more drops. Congestion control is the set of mechanisms that prevent and recover from this state.
Why Congestion Control Exists
Without it, TCP's natural behavior is destructive:
- Packet loss triggers retransmission
- Retransmissions add more traffic to an already overloaded path
- More traffic causes more loss
- Everyone retransmits simultaneously — congestion collapse
The classic example is the early internet's collapse in 1986 when throughput on a Berkeley-to-MIT path dropped from 32 Kbps to 40 bps. Van Jacobson's 1988 paper introduced the algorithms that fixed it — the core of what TCP still uses.
The Two Problems: Flow Control vs Congestion Control
These are related but different:
| Flow Control | Congestion Control | |
|---|---|---|
| Purpose | Prevent sender overwhelming receiver | Prevent sender overwhelming the network |
| Signal | Receiver advertises window size | Packet loss / ECN / RTT increase |
| Mechanism | Receive window (rwnd) in TCP header | Congestion window (cwnd) at sender |
The sender's effective window = min(cwnd, rwnd).
Core Algorithms
1. Slow Start
When a connection opens or after timeout loss, cwnd starts small (1-10 MSS) and grows exponentially:
Each ACK received: cwnd += 1 MSS
Effect: cwnd doubles each RTT
Stop condition: cwnd reaches ssthresh (slow start threshold)
"Slow" is misleading — it's slow start relative to flooding the pipe, but exponential growth is actually fast.
2. Congestion Avoidance
Once cwnd reaches ssthresh, switch to linear growth (additive increase):
Each RTT: cwnd += 1 MSS
Each ACK: cwnd += MSS²/cwnd (approximately 1 MSS per RTT)
This is the Additive Increase part of AIMD.
3. Multiplicative Decrease
When loss is detected (timeout or triple duplicate ACK):
Timeout: ssthresh = cwnd/2, cwnd = 1 MSS, re-enter slow start
Triple dup ACK: ssthresh = cwnd/2, cwnd = ssthresh (fast recovery)
The halving is the Multiplicative Decrease part of AIMD. Together: AIMD.
4. Fast Retransmit
Don't wait for the retransmit timeout (which can be seconds). If you receive 3 duplicate ACKs for the same segment, retransmit it immediately without waiting for timeout. The 3-dup-ACK threshold suggests a single lost segment, not general congestion.
5. Fast Recovery (TCP Reno)
After fast retransmit, instead of going back to slow start:
ssthresh = cwnd/2
cwnd = ssthresh + 3 MSS (accounting for the 3 dup ACKs that triggered it)
This avoids the drastic cwnd collapse to 1 MSS and recovers faster.
Algorithm Comparison
| Algorithm | Increase | Decrease on loss | Used In |
|---|---|---|---|
| TCP Tahoe | AIMD (with slow start) | cwnd=1 always | Legacy |
| TCP Reno | AIMD + fast recovery | cwnd=ssthresh on dup ACK | Common |
| TCP CUBIC | Cubic function | Multiplicative, 0.7× factor | Linux default (kernel 2.6.19+) |
| BBR | Model-based (bandwidth + RTT) | Doesn't react to loss alone | Google, Linux 4.9+ |
ECN — Explicit Congestion Notification
Instead of waiting for packet loss, routers can set the CE bit in the IP header when a queue is building up. The receiver echoes this back in the TCP header (ECE bit), and the sender reduces cwnd proactively — before packets are dropped.
# Check ECN status on Linux
sysctl net.ipv4.tcp_ecn
# 0 = disabled, 1 = enabled on both ends, 2 = initiate ECN but accept non-ECN
# Enable ECN
sudo sysctl -w net.ipv4.tcp_ecn=1
Observing Congestion Control in Practice
# Watch TCP socket stats including cwnd in real time
ss -tin dst <target-ip>
# Look for: cwnd=<n> ssthresh=<n> rtt=<n>/<n>
# Continuous monitoring
watch -n 0.5 "ss -tin dst <target-ip>"
# netstat (older, less detail)
netstat -s | grep -i retransmit
# Check which congestion control algorithm a socket is using
ss -tin | grep cc
# Check system-wide default congestion control
sysctl net.ipv4.tcp_congestion_control
# List available algorithms
sysctl net.ipv4.tcp_available_congestion_control
# -> cubic reno bbr
# Switch to BBR (Google's model-based algorithm)
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
Relevance to DDoS
Understanding congestion control explains why DDoS is effective:
- SYN flood: Exhausts server connection state. Server sends SYN-ACKs, allocates memory for half-open connections, never gets ACK. Congestion control isn't even reached.
- Bandwidth exhaustion: Attacker sends more traffic than the link can carry. Victim's cwnd doesn't matter — the physical pipe is full.
- Slowloris: Sends HTTP headers very slowly, holding connections open. Exploits the session-level timeout, not congestion control.
- Congestion collapse amplification: Attacker induces packet loss, causing victims to retransmit, which adds to congestion, causing more loss — a feedback loop.
- ACK flooding: Sending crafted ACKs can manipulate a victim TCP stack's cwnd, causing it to send more data than the link supports.
Reference
- Van Jacobson (1988) "Congestion Avoidance and Control" — foundational paper
- RFC 5681 — TCP Congestion Control
- RFC 8312 — CUBIC for Fast Long-Distance Networks
- RFC 9002 — QUIC Loss Detection and Congestion Control (compare with TCP)