linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Simulator
  • Knowledge base
  • Interview prep
home/linux/how/tcp-congestion

how/network

TCP congestion control

How does TCP "know" how fast to send data? It does not. It tries faster and faster until it hits a wall. Then it cuts the rate in half and tries again.

When you download a file, your TCP does not know the bandwidth of the link. Nobody handed it a slip saying "you have 100 Mbit/s". To find out, it has to try.

TCP starts with a small window (cwnd = congestion window: how many segments are "in flight" at once), grows it gradually, and when it hits packet loss it figures "this is my ceiling", cuts the rate, and keeps going more carefully.

This algorithm is called AIMD (Additive Increase, Multiplicative Decrease): linear growth and halving on loss. On a graph it draws the distinctive "sawtooth", which is the "voice of TCP".

Press ▶ to watch cwnd grow from 1 segment to its maximum, catch a loss, and continue.

step 1/6·00 · connection established, cwnd = 1
Congestion window (cwnd) над временем148162432время (RTT)cwnd (segments)idle: cwnd ещё не растёт. tcp-стек только что установил соединение

§ steps

  1. After the three-way handshake the TCP stack gets to work. The initial cwnd (congestion window) = 1 segment (or 10 on modern Linux with initcwnd=10).

    This means: "the sender may have no more than 1 packet in flight without an ACK". Next we will increase it.

    The main question: by how much, and how fast, do we increase it?

recap

What to remember:

  • Slow start is the initial phase with exponential growth (cwnd doubles every RTT). It sounds like a paradox, "fast start" versus "slow", but the industry settled on "slow start" (compared to "send at the maximum right away")
  • ssthresh (slow start threshold) is the cwnd threshold value after which slow start ends and congestion avoidance begins (linear growth)
  • AIMD = Additive Increase Multiplicative Decrease. Linear growth (+1 segment per RTT) and halving on loss. It is mathematically proven that this gives fair sharing of the link between competing flows
  • Packet loss = a congestion signal. TCP cannot tell "the link is full" from "Wi-Fi blipped", so it reacts the same way. On Wi-Fi losses this is bad (you waste bandwidth for nothing), which is why Google's BBR algorithm exists as an alternative
  • Modern Linux uses CUBIC by default, a modification of AIMD that works better on high-speed links. BBR is even more aggressive and is heavily used in YouTube/Netflix
  • Check the current algorithm: sysctl net.ipv4.tcp_congestion_control. Available ones: cat /proc/sys/net/ipv4/tcp_available_congestion_control

This is closely tied to [[tcp-retransmission|retransmission]]: fast-retransmit triggers halving of cwnd (those are the "teeth of the sawtooth" on the graph).

§ dig into the knowledge base

  • tcp-handshakeTCP handshake - what comes before
  • tcp-statesTCP states - connection states
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies