TCP, UDP and applications
Core14
What is the difference between TCP and UDP, and when would you pick each one?
Say this first
TCP gives the application a connection and a reliable, ordered byte stream, with flow control and congestion control. It pays for that with a handshake and with waiting for retransmissions. UDP adds only ports, a length and a checksum to IP: no connection, no retransmission, no ordering, and it keeps message boundaries. I pick TCP when every byte must arrive in order (web, SSH, BGP, databases, file transfer) and UDP when late data is useless or the application wants its own reliability (voice and video over RTP, DNS queries, DHCP, NTP, QUIC).
Go deeper
Side by side
TCP UDP Setup Three-way handshake None Delivery Reliable, in order, duplicates removed Best effort, may be lost, duplicated or reordered Unit Byte stream (no message boundaries) Datagram (one send, one receive) Flow control Receive window None Congestion control Yes (CUBIC, BBR and others) None, the application must behave Header 20 to 60 bytes 8 bytes Multicast and broadcast No Yes IP protocol number 6 17 How I decide
- Does late data still have value? A retransmitted voice sample arrives after the jitter buffer has played the gap. For real-time media, a lost packet is better than a late one, so RTP runs on UDP.
- Is the exchange one small question and answer? DNS, NTP and SNMP fit in one datagram each way. A handshake would double the latency for no gain. DHCP has another reason: the client has no IP address yet and must broadcast.
- Do I need one-to-many? Multicast and broadcast only work with UDP.
- Do I want reliability but not TCP's behaviour? QUIC builds streams, retransmission and congestion control on top of UDP in user space, so it can evolve without kernel or middlebox changes.
- Everything else that must be complete and correct goes on TCP.
They will ask next
Does DNS only use UDP?
No. DNS uses UDP 53 for normal queries and TCP 53 when a response is too big (the server sets the TC bit and the client retries over TCP), for zone transfers (AXFR and IXFR), and for DNS over TLS on TCP 853. A firewall that allows only UDP 53 breaks large answers, often DNSSEC ones.
How does a firewall or NAT keep state for UDP if there is no connection?
It creates a flow entry on the first packet and removes it after an idle timeout. There is no FIN to watch, so the timeout is the only end. On IOS NAT the default UDP translation timeout is 300 seconds. Applications that go quiet for longer than the timeout send their own keepalives.
If UDP has no congestion control, what stops a UDP application from flooding the network?
Only the application. Well-behaved UDP protocols add their own rate control: QUIC has full congestion control, RTP media adapts its bitrate from RTCP feedback. Badly behaved ones are why operators police UDP and why QoS designs put real-time traffic in a strict priority queue with a policer.
Traps
- Saying UDP is "worse" because it is unreliable. It is a different tool: lower latency, no state, multicast.
- Saying TCP guarantees delivery. It guarantees that the application gets the data in order or gets an error.
Walk me through the TCP header. What does each field do?
Say this first
The TCP header is 20 bytes without options and up to 60 with them. It carries source and destination ports, a 32-bit sequence number, a 32-bit acknowledgment number, the data offset (header length in 4-byte words), the flags, a 16-bit receive window, a checksum, the urgent pointer, and then options such as MSS, window scale, SACK and timestamps.
Go deeper
Fields in order
Field Bits What it does Source port 16 Sender's port, usually ephemeral on the client Destination port 16 Receiver's port, for example 443 Sequence number 32 Byte number of the first data byte in this segment (or the ISN in a SYN) Acknowledgment number 32 Next byte the sender of this segment expects to receive, valid when ACK is set Data offset 4 Header length in 32-bit words, 5 to 15, so 20 to 60 bytes Reserved 4 Must be zero Flags 8 CWR, ECE, URG, ACK, PSH, RST, SYN, FIN Window 16 Receive window in bytes, shifted left by the window scale factor if one was agreed Checksum 16 Covers a pseudo-header (source IP, destination IP, protocol, TCP length), the header and the data Urgent pointer 16 Only meaningful with URG, almost never used Options 0 to 320 Up to 40 bytes Options worth knowing by number
Kind Option Where 0 End of option list Padding 1 NOP Alignment padding 2 MSS (4 bytes) SYN and SYN-ACK only 3 Window scale (3 bytes) SYN and SYN-ACK only 4 SACK permitted (2 bytes) SYN and SYN-ACK only 5 SACK blocks (variable) Any segment after the handshake 8 Timestamps (10 bytes) SYN, then every segment if both agreed TCP has no length field of its own: the segment length is the IP total length minus the IP header minus the TCP data offset.
They will ask next
What is the pseudo-header and why does TCP checksum it?
It is the source IP, destination IP, protocol number and TCP length, included in the checksum but never sent. It lets the receiver detect a segment delivered to the wrong address. It is also why NAT must update the TCP and UDP checksums when it rewrites an IP address.
Why can a segment carry only three SACK blocks when timestamps are on?
Options are capped at 40 bytes. Timestamps take 10 bytes plus 2 NOPs for alignment, leaving 28. A SACK option is 2 bytes plus 8 per block, so 3 blocks fit. Without timestamps, 4 blocks fit.
What do timestamps give you besides RTT measurement?
PAWS, protection against wrapped sequence numbers. At 10 Gbit/s the 32-bit sequence space wraps in about 3.4 seconds, so an old delayed segment could look valid. The timestamp tells old from new.
Traps
- Calling the window field "the congestion window". It is the receive window. The congestion window is never sent on the wire.
- Forgetting that the header length field counts 4-byte words, so a value of 5 means 20 bytes.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageWhat are the TCP flags and when is each one set?
Say this first
There are eight in common use. SYN opens a connection and carries the initial sequence number. ACK says the acknowledgment field is valid and is set on every segment after the first SYN. FIN closes one direction gracefully. RST aborts the connection immediately. PSH asks the receiver to hand data to the application without waiting. URG marks urgent data and is almost unused. ECE and CWR carry explicit congestion notification signals.
Go deeper
What each flag means
Flag tcpdump Meaning SYN SSynchronize sequence numbers. Consumes one sequence number. ACK .Acknowledgment number is valid FIN FNo more data from this side. Consumes one sequence number. RST RAbort. No ACK is expected and the receiver drops the connection state. PSH PPush buffered data up to the application now URG UUrgent pointer is valid ECE EECN echo: a packet was marked CE (or, in a SYN, "I support ECN") CWR WCongestion window reduced: sender has reacted to ECE Common combinations in a capture
[S]client SYN,[S.]SYN-ACK,[.]pure ACK.[P.]data with ACK, the normal data segment.[F.]FIN with ACK, a graceful close.[R]or[R.]reset.[R.]in reply to a SYN means the port is closed.
Capturing only control segments
Shell tcpdump -ni eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'They will ask next
What are NULL, FIN and Xmas scans?
Port scans that send segments with no flags, only FIN, or FIN, PSH and URG together. RFC-compliant stacks answer a closed port with RST and stay silent for an open one, so silence hints that a port is open. Stacks that answer RST to everything defeat the trick. A stateful firewall drops these packets because they do not belong to any connection.
Is a SYN with data legal?
Yes. TCP Fast Open (RFC 7413) puts data in the SYN using a cookie from a previous connection, saving one round trip. The server only hands that data to the application if the cookie is valid.
Why does every segment after the SYN have ACK set?
Once a side knows the peer's initial sequence number there is always something valid to acknowledge. Setting ACK on every segment keeps the acknowledgment flowing at no cost.
Traps
- Saying PSH is required for data to be delivered. Receivers deliver data without it. It is a hint.
- Thinking a RST gets acknowledged. It does not; the receiver validates it and tears the connection down.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageWhat is in a UDP header, and what does UDP leave to the application?
Say this first
Eight bytes: source port, destination port, length (header plus data) and checksum, 16 bits each. UDP leaves everything else to the application: no handshake, no acknowledgment, no retransmission, no ordering, no flow control and no congestion control. The checksum is optional in IPv4 (zero means none) and mandatory in IPv6, apart from narrow tunnel exceptions.
Go deeper
Fields
Field Bits Notes Source port 16 May be 0 if no reply is expected, rarely seen Destination port 16 Selects the application Length 16 Header plus payload, minimum 8 Checksum 16 Covers a pseudo-header, the header and the data Sizes
The largest UDP payload over IPv4 is 65,507 bytes (65,535 minus 20 for IP and 8 for UDP). Anything over the path MTU is fragmented by IP, and losing one fragment loses the whole datagram. That is why DNS over UDP now aims for answers of about 1,232 bytes and QUIC sizes its own packets.
What runs on UDP
DNS 53, DHCP 67 and 68, TFTP 69, NTP 123, SNMP 161 and 162, syslog 514, IKE 500 and NAT-T 4500, RIP 520, BFD 3784, VXLAN 4789, QUIC and HTTP/3 on 443, RTP media on dynamic ports.
What the application must add
Timeouts and retries (DNS, TFTP), sequence numbers (RTP), its own rate control (RFC 8085 sets out guidelines for UDP applications), and keepalives to hold NAT and firewall state open.
They will ask next
Why does TFTP work without TCP?
It is lock-step: every 512-byte data block (the default) is acknowledged before the next is sent, and the sender retransmits on timeout. It is reliable but slow, one block per round trip.
Why do tunnels like VXLAN use UDP rather than raw IP?
A UDP header gives a source port the encapsulating device can set from a hash of the inner flow. Routers that do ECMP on the five-tuple then spread tunnel traffic across paths. UDP also crosses NAT and firewalls more easily than a new IP protocol.
Traps
- Saying UDP has no checksum. It has one; IPv4 lets it be zero, IPv6 normally requires it.
How did you do?Book 5.1 Simple Demultiplexor (UDP)Own pageExplain the TCP three-way handshake. What is exchanged besides SYN and ACK?
Say this first
The client sends a SYN with a random initial sequence number and its options. The server answers with a SYN-ACK carrying its own ISN and acknowledging the client's ISN plus one. The client sends an ACK for the server's ISN plus one. Along the way both sides announce their MSS, window scale, SACK support, timestamps and ECN support. The client goes SYN_SENT to ESTABLISHED; the server goes LISTEN to SYN_RCVD to ESTABLISHED.
Go deeper
The three segments
Output Client (SYN_SENT) Server (LISTEN) SYN seq=x MSS 1460, SACK ok, TS, wscale 7 -> (SYN_RCVD) <- SYN,ACK seq=y ack=x+1 MSS 1460, SACK ok, TS, wscale 7 (ESTABLISHED) ACK seq=x+1 ack=y+1 -> (ESTABLISHED)The SYN and the SYN-ACK each use up one sequence number, which is why the acknowledgments are ISN plus one.
Why three and not two
Each side has to learn the other's ISN and know that its own ISN was received. Two ISNs need two acknowledgments; the server's SYN and its ACK share one segment, so three segments are enough. The exchange also defeats old duplicate SYNs: if a stale SYN reaches the server, the client answers the unexpected SYN-ACK with a RST instead of an ACK, and no connection opens.
Why the ISN is random
A predictable ISN lets an attacker who cannot see the traffic forge segments into a connection. RFC 6528 describes generating it from a keyed hash of the four-tuple plus a clock.
Options that only exist in the handshake
MSS, window scale and SACK permitted are only valid in SYN and SYN-ACK. If one side does not offer window scaling or SACK, neither side uses it. The third ACK may already carry data.
They will ask next
What happens if the SYN-ACK is lost?
The client retransmits the SYN when its timer fires: 1 second initially, doubling each time. Linux retries 6 times by default (
net.ipv4.tcp_syn_retries), about 127 seconds in total, before connect() fails with a timeout. The server also retransmits its SYN-ACK from SYN_RCVD.How do you tell a closed port from a firewall drop from the client side?
A closed port answers the SYN with RST-ACK and the client gets "connection refused" at once. A silent firewall drop makes the client retry SYNs until it times out. Some firewalls reject with a RST or an ICMP administratively prohibited message instead, which fails fast but tells the attacker a filter exists.
What is a simultaneous open?
Both sides send a SYN at the same time. Each moves from SYN_SENT to SYN_RCVD on receiving the other's SYN, and one connection forms with four segments. It is rare, but NAT traversal techniques use it.
Traps
- Saying the acknowledgment number is the next byte the sender will send. It is the next byte it expects to receive.
- Reading Wireshark's relative sequence numbers and claiming the ISN is 0.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageHow does a TCP connection close? Walk through the four-way close and the states on each side.
Say this first
Each direction closes on its own with a FIN. The side that closes first sends FIN and enters FIN_WAIT_1. The peer ACKs it and enters CLOSE_WAIT; the first side moves to FIN_WAIT_2. When the peer's application closes, the peer sends its own FIN and enters LAST_ACK. The first side ACKs that FIN and sits in TIME_WAIT for two maximum segment lifetimes; the peer goes to CLOSED when that ACK arrives.
Go deeper
The sequence
Output Active closer (A) Passive closer (B) ESTABLISHED ESTABLISHED FIN -> FIN_WAIT_1 CLOSE_WAIT (B's app is told: end of stream) <- ACK FIN_WAIT_2 ... B may keep sending data ... <- FIN LAST_ACK (B's app called close()) TIME_WAIT ACK -> CLOSED (after 2 x MSL) CLOSEDThree segments instead of four
If B's application closes straight away, B sends its FIN in the same segment as the ACK. A capture then shows
[F.],[F.],[.]. That is still the same state machine, just with the ACK and FIN combined.Half-close
Between FIN_WAIT_2 and LAST_ACK, B can still send data and A must still receive and acknowledge it.
shutdown(SHUT_WR)uses this on purpose: "I have finished sending, now give me the result".Simultaneous close
If both sides send FIN at the same time, each goes FIN_WAIT_1, then CLOSING when it receives the other's FIN, then TIME_WAIT when its own FIN is acknowledged. Both sides end in TIME_WAIT.
They will ask next
What happens if the final ACK is lost?
B, in LAST_ACK, retransmits its FIN. A is still in TIME_WAIT, so it recognises the FIN and sends the ACK again. That is one of the two reasons TIME_WAIT exists.
How long can a connection stay in FIN_WAIT_2?
The protocol has no limit, because the peer may still be sending. Linux limits sockets the application has already closed (orphans) to
net.ipv4.tcp_fin_timeout, 60 seconds by default, then drops them.Which side ends up in TIME_WAIT in a normal web request?
Whoever sends the first FIN. With HTTP keep-alive the server often closes idle connections first, so TIME_WAIT builds up on the server. A client like curl that exits after one request closes first and holds the TIME_WAIT itself.
Traps
- Mixing up CLOSE_WAIT and TIME_WAIT. CLOSE_WAIT is on the passive side waiting for its own app; TIME_WAIT is on the active side after the close.
- Saying a FIN closes the connection in both directions. It only ends the sender's direction.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageWhat is a socket, and how can one web server on port 443 serve thousands of clients at once?
Say this first
A socket is the operating system's handle for one end of a conversation: protocol, local IP and port, and for a connected TCP socket the remote IP and port too. A TCP connection is identified by the five-tuple, so the server's listening socket on 443 can accept any number of connections: each new one has the same local address and port and a different client address or client port. Clients pick their source port from the ephemeral range.
Go deeper
Port ranges
Range Name Notes 0 to 1023 Well-known (system) Binding needs root or CAP_NET_BIND_SERVICE on Linux 1024 to 49151 Registered (user) Assigned by IANA to applications 49152 to 65535 Dynamic (private) IANA's suggested ephemeral range Defaults differ: Linux uses 32768 to 60999 (
net.ipv4.ip_local_port_range), Windows uses 49152 to 65535.What really limits connections
On the server: memory and file descriptors, not ports. One port can carry millions of connections. On the client: the number of free source ports for each destination IP and port, because the four-tuple must be unique. Behind PAT, every inside host shares the outside address's ports, which is where port exhaustion usually shows up.
Listening versus accepted sockets
Shell ss -tn state established '( sport = :443 )'Every line has local port 443 and a different peer. The single
LISTENsocket is separate and only queues new connections for the application to accept().They will ask next
What is the difference between SO_REUSEADDR and SO_REUSEPORT?
SO_REUSEADDR lets a server bind its port again while old connections on it sit in TIME_WAIT, so a restart does not fail with "address already in use". SO_REUSEPORT (Linux 3.9 and later) lets several sockets listen on the same port and the kernel spreads new connections across them.
Is there a 65,535 connection limit on a server?
No. 65,535 is the number of ports, and a server uses one. The limit per client IP talking to one server port is about the size of the client's ephemeral range. Total connections are bounded by memory.
Traps
- Saying a server "runs out of ports" because it has many clients. Clients run out of source ports; servers run out of memory or file descriptors.
How did you do?Book 1.4 SoftwareBook 5.1 Simple Demultiplexor (UDP)Own pageRattle off the well-known ports an interviewer expects you to know, with TCP or UDP for each.
Say this first
FTP 20 and 21, SSH 22, Telnet 23, SMTP 25, DNS 53 on UDP and TCP, DHCP 67 and 68 UDP, TFTP 69 UDP, HTTP 80, POP3 110, NTP 123 UDP, IMAP 143, SNMP 161 and 162 UDP, BGP 179 TCP, LDAP 389, HTTPS 443 on TCP and on UDP for QUIC, SMB 445, IKE 500 UDP, syslog 514 UDP, RDP 3389, RADIUS 1812 and 1813 UDP, TACACS+ 49 TCP.
Go deeper
Network engineer's list
Port Proto Service 20, 21 TCP FTP data, FTP control 22 TCP SSH, SCP, SFTP 23 TCP Telnet 25, 587, 465 TCP SMTP relay, mail submission, submission over implicit TLS 49 TCP TACACS+ 53 UDP, TCP DNS (853 TCP for DNS over TLS) 67, 68 UDP DHCP server, DHCP client 69 UDP TFTP 80, 443 TCP HTTP, HTTPS 443 UDP QUIC and HTTP/3 110, 995 TCP POP3, POP3S 123 UDP NTP 143, 993 TCP IMAP, IMAPS 161, 162 UDP SNMP queries, SNMP traps and informs 179 TCP BGP 389, 636 TCP LDAP, LDAPS 445 TCP SMB 500, 4500 UDP IKE, IPsec NAT traversal 514 UDP Syslog (6514 TCP for syslog over TLS) 520 UDP RIP 646 TCP, UDP LDP sessions, LDP discovery 830 TCP NETCONF over SSH 1812, 1813 UDP RADIUS authentication, accounting (old: 1645, 1646) 1985 UDP HSRP 3389 TCP RDP 3784 UDP BFD single-hop control 4789 UDP VXLAN 5060, 5061 UDP, TCP SIP, SIP over TLS Protocols that are not ports
These ride directly on IP and are matched by protocol number: ICMP 1, TCP 6, UDP 17, GRE 47, ESP 50, AH 51, EIGRP 88, OSPF 89, VRRP 112.
They will ask next
What is the difference between active and passive FTP?
Both use TCP 21 for control. In active mode the client sends a PORT command and the server opens the data connection back to the client, from port 20. In passive mode the client asks with PASV and opens the data connection itself to a high port the server names. Passive works through client-side NAT and firewalls, which is why it is the default in most clients.
Why is OSPF not in an ACL as a port number?
OSPF is IP protocol 89, with no TCP or UDP header. You match it with
permit ospf any anyorpermit 89 any any, not with a port.
Traps
- Giving a port number for OSPF, EIGRP, GRE or ESP. They are IP protocol numbers.
- Saying SNMP traps go to 161. Agents listen on 161; managers receive traps and informs on 162.
What is the TCP sliding window, and how is flow control different from congestion control?
Say this first
The receiver advertises a receive window in every segment: how many more bytes it has buffer space for. The sender may have at most the smaller of that receive window and its own congestion window sent but not yet acknowledged. As ACKs arrive the window slides forward and more data can go. Flow control protects the receiver and is driven by the receiver's advertised window. Congestion control protects the network and is the sender's private estimate, the congestion window, which never appears in a packet.
Go deeper
The picture
Output bytes: | acked | sent, not acked | can send now | cannot send yet | ^ SND.UNA ^ SND.NXT ^ SND.UNA + windowUsable window = min(rwnd, cwnd) minus bytes in flight. When the receiver's application reads data, the receiver advertises a bigger window. When the sender gets ACKs, SND.UNA moves right.
Why it bounds throughput
A sender can put at most one window of data on the wire per round trip, so one flow's throughput is at most window / RTT. That single formula explains most "fast link, slow transfer" tickets.
Who sets what
Control Variable Set by Visible in packets Flow control rwnd Receiver, from free buffer Yes, Window field Congestion control cwnd Sender's algorithm No They will ask next
How do you see the congestion window if it is never on the wire?
On the sender:
ss -tishowscwnd:in segments along withrtt:andssthresh:. In Wireshark you estimate it from "bytes in flight" in the TCP analysis on a capture taken near the sender.What limits a single TCP flow's throughput?
The smallest of: receive window / RTT, congestion window / RTT, the sender's socket buffer, the bottleneck link, and the application's own speed reading or writing.
Traps
- Saying the window is counted in segments. The advertised window is in bytes (after scaling).
Explain slow start and congestion avoidance. What is ssthresh?
Say this first
A new connection does not know the path's capacity, so it starts with a small congestion window, 10 segments on modern stacks. In slow start cwnd grows by one segment per ACK, which doubles it every round trip. When cwnd reaches ssthresh, TCP switches to congestion avoidance and grows by about one segment per round trip. On loss detected by duplicate ACKs, ssthresh is set to about half the data in flight and cwnd drops to it. On a timeout, cwnd goes back to one segment and slow start begins again. ssthresh is the line between the two phases.
Go deeper
Round by round
RTT Phase cwnd (segments) 1 Slow start 10 2 Slow start 20 3 Slow start 40 4 Slow start 80, loss detected 5 Recovery ssthresh = 40, cwnd = 40 6 Congestion avoidance 41 7 Congestion avoidance 42 This additive increase and multiplicative decrease (AIMD) makes the familiar sawtooth.
Why the initial window matters
An initial window of 10 segments (RFC 6928) is about 14.6 KB. A small web response fits in the first flight; a large one needs several round trips of slow start, which is why latency, not bandwidth, dominates small transfers.
Slow start after idle
Linux shrinks cwnd back toward the initial window when a connection has been idle for longer than one RTO (
net.ipv4.tcp_slow_start_after_idle=1). Long-lived connections that send in bursts, like HTTP/2 or database links, sometimes turn it off.They will ask next
Why is it called slow start if it grows exponentially?
It is slow compared with what came before it: early TCP sent a whole receive window at once and caused congestion collapse. Starting small and doubling was the slow, careful option.
Where does ssthresh start?
Very high, effectively unlimited, so the first slow start runs until the first loss or until the receive window caps it. Linux can cache metrics per destination, including ssthresh, between connections.
Traps
- Saying cwnd starts at one segment on modern systems. Linux and most stacks start at 10.
How did you do?Book 6.3 TCP Congestion ControlOwn pageWhat is the difference between MTU and MSS, and why is the usual MSS 1460?
Say this first
The MTU is the largest IP packet a link can carry without fragmenting it: 1,500 bytes on standard Ethernet. The MSS is the largest TCP payload a host is willing to receive in one segment, announced in its SYN. On a 1,500-byte link it is 1,500 minus 20 bytes of IPv4 header minus 20 bytes of TCP header, so 1,460; with IPv6's 40-byte header it is 1,440. MSS is about TCP data only, MTU is about whole IP packets.
Go deeper
The layers of a full-size packet
Piece Bytes TCP payload (MSS) 1,460 TCP header 20 IPv4 header 20 IP packet = MTU 1,500 Ethernet header + FCS 14 + 4 Frame on the wire 1,518 (1,522 with an 802.1Q tag) With TCP timestamps on, every segment carries 12 bytes of options, so the data per packet is 1,448 even though the SYN still advertises 1,460.
ss -tishows this asmss:1448.Common MTUs and matching MSS
Link IP MTU MSS Ethernet 1,500 1,460 PPPoE (8-byte header) 1,492 1,452 GRE over 1,500 (24 bytes overhead) 1,476 1,436 GRE over IPsec, common safe value 1,400 1,360 Jumbo frames 9,000 8,960 MSS is announced, not negotiated
Each side tells the other what it can receive. The two directions can use different sizes. If a SYN has no MSS option, the default is 536 bytes for IPv4 and 1,220 for IPv6.
They will ask next
On a Cisco router, what is the difference between mtu and ip mtu?
mtusets the interface's layer 2 payload size for every protocol.ip mtusets the largest IPv4 packet on that interface and must not exceedmtu. On tunnels you normally setip mtu.Does UDP have an MSS?
No. There is no handshake to announce one. The application must size its datagrams, or IP fragments them. That is why DNS, QUIC and VPNs pick conservative packet sizes.
Traps
- Saying MSS includes the TCP header. It is payload only.
- Saying the two hosts negotiate one shared MSS. Each announces its own receive limit.
What changed between HTTP/1.1, HTTP/2 and HTTP/3?
Say this first
HTTP/1.1 is text over TCP with persistent connections, but one request at a time per connection, so browsers open about six connections per host. HTTP/2 keeps TCP but switches to binary frames and multiplexes many streams over one connection, with HPACK header compression; one lost TCP segment still stalls every stream. HTTP/3 runs over QUIC on UDP 443, with streams that are independent at the transport layer, TLS 1.3 built in and QPACK compression.
Go deeper
Side by side
HTTP/1.1 HTTP/2 HTTP/3 Transport TCP TCP QUIC over UDP Format Text Binary frames Binary frames Requests per connection One at a time (pipelining unused) Many concurrent streams Many concurrent streams Header compression None HPACK QPACK Encryption Optional (HTTPS) Browsers require TLS Always TLS 1.3 Lost packet blocks That connection All streams on the connection Only the affected stream Setup round trips with TLS 1.3 2 (TCP + TLS) 2 (TCP + TLS) 1, or 0 on resumption How the client chooses
- HTTP/2: ALPN in the TLS handshake. The client offers
h2,http/1.1and the server picks. - HTTP/3: the server advertises it, either with an
Alt-Svc: h3=":443"response header or a DNS HTTPS record. The browser then tries QUIC and falls back to TCP if UDP 443 is blocked.
RFCs
HTTP semantics RFC 9110, HTTP/1.1 RFC 9112, HTTP/2 RFC 9113 (originally 7540), HTTP/3 RFC 9114, QUIC RFC 9000.
They will ask next
Is HTTP/2 always faster than HTTP/1.1?
Not on lossy links. Six HTTP/1.1 connections lose only one connection's progress on a drop, and six congestion windows recover separately. HTTP/2's single connection stalls every stream. HTTP/3 fixes this.
What happens when an enterprise firewall blocks UDP 443?
Browsers fall back to HTTP/2 over TCP after the QUIC attempt fails, usually with a short delay. Many enterprises block QUIC on purpose so their TLS inspection and URL filtering still see the traffic.
What was the HTTP/2 Rapid Reset attack?
A 2023 denial of service where clients opened streams and cancelled them at once with RST_STREAM, at huge rates. The server did work for every stream while the attacker stayed under the concurrent stream limit. Servers patched it by limiting resets per connection.
Traps
- Saying HTTP/2 needs TLS by specification. The spec allows cleartext h2c; browsers only use h2 over TLS.
- Saying HTTP/3 uses TCP 443.
- HTTP/2: ALPN in the TLS handshake. The client offers
What is a certificate chain of trust, and what does a client check before it trusts a server certificate?
Say this first
A server certificate (the leaf) binds a public key to names and is signed by an intermediate CA, which is signed by a root CA that the client already trusts in its store. The server sends the leaf plus the intermediates; the client builds a path to a trusted root and checks each signature, the validity dates, that the hostname matches a Subject Alternative Name, that the key usage allows server authentication, that each issuer is really a CA, and revocation and certificate transparency where its policy requires. A missing intermediate is the classic outage.
Go deeper
The chain
Output Root CA (self-signed, in the OS or browser trust store, not sent by the server) signs -> Intermediate CA (sent by the server) signs -> Leaf: shop.example.com (sent by the server, first)Roots stay offline; intermediates do daily issuing so a compromise can be contained.
Checks, in the order a client roughly applies them
- Build a path from leaf to a trusted root using what the server sent and the local store.
- Each certificate's signature verifies with its issuer's public key.
- Every certificate is within its notBefore and notAfter dates.
- Issuers have Basic Constraints CA:TRUE, and path length and name constraints are respected.
- The leaf's Extended Key Usage includes serverAuth.
- The hostname matches a DNS name in the SAN. Browsers ignore the Common Name.
- Revocation (CRL or OCSP, often stapled by the server) and, in browsers, certificate transparency proofs.
Wildcards
*.example.commatchesshop.example.com, but notexample.comand nota.shop.example.com. The wildcard covers exactly one label.Lifetimes are shrinking
The CA/Browser Forum has voted to cut the maximum lifetime of public TLS certificates in steps, down to 47 days by 2029. Renewal has to be automated, usually with ACME.
They will ask next
Why does a site with a missing intermediate work in Chrome but fail in curl?
Many browsers fill the gap: they cache intermediates seen before, ship preloaded ones, or download the issuer from the Authority Information Access URL in the leaf. curl, Python, Java, Go and most API clients do not, so they fail with "unable to get local issuer certificate".
Should the server send the root too?
It is not needed: the client must already have the root to trust it. Sending it only adds bytes to the handshake. Send the leaf first, then each intermediate in order.
How do you see the chain a server actually sends?
openssl s_client -connect shop.example.com:443 -servername shop.example.com -showcerts. The "Certificate chain" section lists each certificate with s: (subject) and i: (issuer). Each i: must match the next s:.
Traps
- Saying the browser checks the Common Name. Modern clients only match the SAN.
- Fixing a chain error by installing the intermediate on one client. Fix the server so every client works.
How did you do?Book 8.3 Key PredistributionBook 8.5 Example SystemsOwn pageWhat does HTTPS protect, and what can someone watching the network still see?
Say this first
HTTPS protects the content of the HTTP exchange: the URL path and query, headers, cookies and bodies are encrypted and tamper-proof, and the certificate proves the client is talking to the named server. An observer still sees the IP addresses and ports, the timing and size of the traffic, the DNS lookup unless it is encrypted, and the hostname in the SNI field of the ClientHello unless Encrypted Client Hello is used. With TLS 1.2 the server certificate is also visible.
Go deeper
Visible versus hidden
Item Visible on the wire Client and server IP, ports Yes DNS query for the name Yes, unless DNS over HTTPS or TLS Hostname in SNI Yes, unless ECH Server certificate TLS 1.2 yes, TLS 1.3 no Path, query string, headers, cookies, body No Size and timing of requests Yes, which allows traffic analysis How enterprise firewalls filter HTTPS
- Without decryption: by destination IP, SNI and, for TLS 1.2, the certificate.
- With decryption: the firewall acts as a proxy and signs a substitute certificate with an enterprise CA that managed devices trust. Pinned or mutually authenticated applications break and are usually exempted.
HSTS
A
Strict-Transport-Securityheader tells the browser to use HTTPS only for that site for a period, so a later typedhttp://link is upgraded before any packet is sent. Preload lists ship that rule in the browser.They will ask next
Does HTTPS hide which website I visit?
Not by itself. The SNI and the DNS lookup reveal the hostname, and the IP often does too. Encrypted DNS plus ECH hide the name, but the IP address and traffic pattern remain.
What does the padlock prove?
That the connection is encrypted and the server holds a valid certificate for that hostname. It says nothing about whether the site is honest.
Traps
- Saying HTTPS hides the domain name. The SNI is plaintext without ECH.
Working16
Why does TIME_WAIT exist, how long does it last, and is a server with 30,000 sockets in TIME_WAIT a problem?
Say this first
The side that closes first waits two maximum segment lifetimes for two reasons: to re-send the last ACK if the peer's FIN is retransmitted, and to let stray segments from this connection expire before the same four-tuple can be reused. RFC 793 set MSL at 2 minutes, so 4 minutes; Linux uses a fixed 60 seconds. Thirty thousand TIME_WAIT sockets on a server cost little memory and are normal. The real risk is on a client or proxy opening many short connections to one destination IP and port, where TIME_WAIT can use up the ephemeral ports.
Go deeper
Why it is cheap on a server
A server's TIME_WAIT entries all share its listening port and differ by client address and port, so they never block new connections. Linux keeps them in a small structure without buffers.
Where it hurts
A proxy, load balancer or application server connecting to one backend
10.0.0.50:8080needs a unique source port per connection. Linux's default range is 32768 to 60999, so 28,232 ports. If each connection is closed by the client and waits 60 seconds, the ceiling is about 28,232 / 60, roughly 470 new connections per second to that one backend address and port. Past that, connect() fails withEADDRNOTAVAIL("Cannot assign requested address").Fixes, best first
- Reuse connections: HTTP keep-alive, connection pools, gRPC.
- Let the server close first, so TIME_WAIT lands on the side that is not short of ports.
- Add source IPs or backend IPs, since the limit is per four-tuple.
- Widen
net.ipv4.ip_local_port_range. net.ipv4.tcp_tw_reuse=1lets new outgoing connections reuse a TIME_WAIT four-tuple when timestamps show it is safe. Recent kernels default to 2, which means loopback only.
What not to do
tcp_tw_recyclewas removed in Linux 4.12. It dropped SYNs from clients behind NAT whose timestamps did not increase across hosts. Closing with SO_LINGER set to 0 avoids TIME_WAIT by sending RST, and throws away unsent data.They will ask next
Does net.ipv4.tcp_fin_timeout shorten TIME_WAIT on Linux?
No. Despite the name, it limits how long an orphaned socket stays in FIN_WAIT_2. TIME_WAIT is a compile-time 60 seconds on Linux.
How do you see TIME_WAIT sockets and their remaining time?
ss -tano state time-waitlists them with a timer such astimer:(timewait,41sec,0). Usess -sfor a summary count.Why is 2 x MSL enough to protect a new connection?
MSL is the assumed longest time a segment can survive in the network. Waiting twice that covers a segment travelling one way plus a reply travelling back, so nothing from the old connection can still be in flight when the four-tuple is reused.
Traps
- Tuning servers to "fix" TIME_WAIT counts that cause no errors. Measure port exhaustion first.
- Recommending tcp_tw_recycle. It no longer exists and broke clients behind NAT.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageName the TCP states and tell me what a pile-up in each one usually means when you run ss or netstat.
Say this first
The states are LISTEN, SYN_SENT, SYN_RCVD, ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, CLOSE_WAIT, CLOSING, LAST_ACK, TIME_WAIT and CLOSED. Many SYN_SENT means SYNs are not answered; many SYN_RCVD points at a SYN flood or lost final ACKs; many CLOSE_WAIT is almost always an application that does not close its sockets; many TIME_WAIT is normal on the side that closes first.
Go deeper
What each pile-up tells you
State Side Many of them usually means SYN_SENT Client Peer down, route missing, or a firewall dropping SYNs silently SYN_RCVD Server SYN flood, or the client's final ACK not arriving (asymmetric path through a stateful firewall) ESTABLISHED Both Normal. Steady growth is a connection leak or a slow backend FIN_WAIT_1 Active closer Our FIN is not acknowledged: peer gone, or unsent data stuck behind a zero window FIN_WAIT_2 Active closer Peer acknowledged our FIN but its application never closed CLOSE_WAIT Passive closer Our application received end of stream and never called close() LAST_ACK Passive closer Our FIN is not acknowledged, peer probably gone CLOSING Both Simultaneous close, rare and short-lived TIME_WAIT Active closer Normal. A problem only if ephemeral ports run out Naming differs by tool
netstatprintsSYN_SENT,ESTABLISHED,CLOSE_WAIT.ssprintsSYN-SENT,SYN-RECV,ESTAB,CLOSE-WAIT,TIME-WAIT, andUNCONNfor unconnected UDP sockets.Quick count on Linux
Shell ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rnThey will ask next
Which states have no timer at all?
ESTABLISHED (unless keepalive is on) and CLOSE_WAIT. A socket in CLOSE_WAIT stays there until the process closes it or exits, which is why a leak grows until the process hits its file descriptor limit.
You see thousands of SYN-RECV on a server behind a firewall, but traffic is low. What else could it be?
Asymmetric routing. The SYN-ACK leaves by a different path, the client's ACK comes back through a stateful firewall that never saw the SYN-ACK, and the firewall drops it. The server keeps retrying SYN-ACKs from SYN_RCVD.
Traps
- Blaming the network for CLOSE_WAIT. The kernel has done its part; the local application has not.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageWalk me through sequence and acknowledgment numbers for a short exchange, with real numbers.
Say this first
The sequence number is the number of the first data byte in a segment; the acknowledgment number is the next byte the receiver expects, and it is cumulative. SYN and FIN each use one number; pure ACKs use none. If the client starts at 1000 and sends 200 bytes after the handshake, its data segment is seq 1001 and the server acknowledges 1201.
Go deeper
Worked example
Real ISNs are random 32-bit numbers. Small ones keep the arithmetic visible. Client ISN 1000, server ISN 5000.
# Direction Flags Seq Ack Len Why 1 C to S SYN 1000 0 Client ISN 2 S to C SYN, ACK 5000 1001 0 Server ISN; SYN used byte 1000 3 C to S ACK 1001 5001 0 Pure ACK uses no number 4 C to S PSH, ACK 1001 5001 200 Request, bytes 1001 to 1200 5 S to C ACK 5001 1201 1460 Response part 1, bytes 5001 to 6460 6 S to C PSH, ACK 6461 1201 500 Response part 2, bytes 6461 to 6960 7 C to S ACK 1201 6961 0 One ACK covers both segments 8 C to S FIN, ACK 1201 6961 0 FIN uses byte 1201 9 S to C FIN, ACK 6961 1202 0 Acks the FIN; FIN uses byte 6961 10 C to S ACK 1202 6962 0 Final ACK, client enters TIME_WAIT Rules to say out loud
- Next seq from a sender = this seq + payload length (+1 for SYN or FIN).
- Ack = highest in-order byte received + 1. It never jumps over a hole.
- One ACK can cover many segments (segment 7), and with delayed ACK it usually covers two.
When segment 5 is lost
The client receives segment 6 first. It cannot advance past the hole, so it sends ack 5001 again (a duplicate ACK), with a SACK block
6461-6961if SACK was agreed. The server sees exactly which bytes to resend.They will ask next
Why does Wireshark show sequence numbers starting at 0?
It shows relative sequence numbers by default: the ISN is displayed as 0. The real ISN is still in the packet. You can turn relative numbers off in the TCP protocol preferences. tcpdump shows the absolute ISN on the SYN and relative numbers after that unless you add
-S.What happens when the sequence number reaches 2^32?
It wraps to zero. Comparisons are done modulo 2^32, and at high speed timestamps (PAWS) stop old segments from a previous wrap being accepted.
Traps
- Adding 1 for a pure ACK. Only SYN, FIN and data bytes use sequence numbers.
What is the difference between a connection closed with FIN and one reset with RST, and what causes resets?
Say this first
FIN is a graceful close of one direction: all data sent before it is delivered and acknowledged, and the other side can keep sending. RST aborts the whole connection at once: unsent and unread data is thrown away and the application gets "connection reset by peer". Resets come from a SYN to a closed port, an application closing with unread data or with linger set to zero, a segment that matches no connection (for example after a reboot), and middleboxes such as firewalls and load balancers that kill sessions.
Go deeper
Common sources of RST
Cause What you see SYN to a port with no listener [R.]right after the SYN, client error "connection refused"Peer rebooted or lost state Our next data segment gets a RST (the half-open case) App closed with data still unread in its receive buffer Linux sends RST instead of FIN SO_LINGER with timeout 0 Deliberate abortive close Firewall or IPS policy RST injected toward one or both ends Load balancer or firewall idle timeout Next packet after idle hits no state; some devices send RST Who really sent it
Compare the RST with the real peer's packets. A RST forged by a middlebox often has a different IP TTL, a different IP ID pattern, or arrives faster than the peer's RTT allows. Capturing on both sides of the firewall settles it.
RST validation
A RST is only accepted if its sequence number matches exactly what the receiver expects next. If it is merely inside the window, the receiver sends a challenge ACK instead (RFC 5961). That makes blind reset attacks much harder.
They will ask next
What is the difference between "connection refused" and "connection timed out"?
Refused means a RST came back to the SYN: the host is reachable and nothing listens on that port, or a device rejected it. Timed out means nothing came back at all: a silent drop, a dead host or a broken route.
A long-lived database connection dies with "connection reset" every morning. Where do you look?
At idle timeouts on firewalls, NAT and load balancers in the path. The connection sat idle overnight, the middlebox removed its state, and the first query in the morning hit nothing. Enabling TCP keepalive at an interval shorter than the shortest idle timeout, or a pool that validates connections, fixes it.
Traps
- Assuming every RST came from the server. Middleboxes send them too, and the capture must prove who did.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageWhy was TCP window scaling needed, and how does it work?
Say this first
The window field is 16 bits, so without help the receive window tops out at 65,535 bytes. At 100 ms RTT that caps one flow at about 5.2 Mbit/s whatever the link speed. The window scale option (RFC 7323) is sent in the SYN and SYN-ACK with a shift count from 0 to 14; after the handshake each side multiplies the window field by 2 to the power of the peer's shift, up to about 1 GiB. Both sides must offer it, or neither side scales.
Go deeper
Example
A Linux host with default buffers sends
wscale 7in its SYN. Later its segments showwin 502. The real window is 502 x 128 = 64,256 bytes. Each side picks its own shift from its maximum receive buffer, so the two values can differ.Rules worth saying
- Only in SYN and SYN-ACK. The factor cannot change during the connection.
- If the client sends it and the SYN-ACK does not, scaling is off in both directions.
- The maximum shift is 14, so the largest window is 65,535 x 16,384, about 1 GiB.
Where it goes wrong
- A capture that starts mid-connection. Wireshark cannot know the factor and shows the raw field;
it labels the scaling factor
-1 (unknown). Capture from the SYN. - A middlebox that strips or rewrites the option. The connection works but every flow is capped at 64 KB per RTT. Captures on both sides of the device show the option disappearing.
- An application that sets SO_RCVBUF. On Linux that fixes the buffer and turns off receive autotuning for that socket, which can leave the window far smaller than the path needs.
They will ask next
Why can the window scale only be agreed in the handshake?
Both sides must interpret every later window field the same way. Changing the shift mid-stream could not be synchronised safely, so RFC 7323 fixes it at connection setup.
What sets the receive buffer on Linux?
Autotuning between the three values of
net.ipv4.tcp_rmem(minimum, default, maximum), unless the application sets SO_RCVBUF, in which casenet.core.rmem_maxcaps the request. The send side usesnet.ipv4.tcp_wmemandnet.core.wmem_maxthe same way.
Traps
- Reading
win 502in tcpdump as 502 bytes. tcpdump prints the raw field; multiply by the agreed scale.
What is the bandwidth-delay product? Work out the window you need for a 1 Gbit/s path with 80 ms RTT.
Say this first
The bandwidth-delay product is how much data must be in flight to keep a path full: bandwidth times round-trip time. For 1 Gbit/s and 80 ms that is 1,000,000,000 x 0.08 = 80,000,000 bits, which is 10 MB. One TCP flow needs a receive window, a congestion window and a send buffer of at least 10 MB to fill it. With an unscaled 64 KB window the same flow tops out at 65,535 x 8 / 0.08, about 6.5 Mbit/s.
Go deeper
The formulas
- BDP (bytes) = bandwidth (bit/s) x RTT (s) / 8
- Max throughput of one flow = window (bytes) x 8 / RTT (s)
Worked examples
Path BDP 64 KB window gives 1 Gbit/s, 1 ms (inside a data center) 125 KB about 524 Mbit/s 100 Mbit/s, 20 ms 250 KB about 26 Mbit/s 1 Gbit/s, 80 ms 10 MB about 6.5 Mbit/s 10 Gbit/s, 100 ms 125 MB about 5.2 Mbit/s Loss matters too
Even with big enough windows, loss-based congestion control limits a flow on a lossy long path. The Mathis approximation for Reno-style TCP is throughput = (MSS / RTT) x (1.22 / sqrt(loss)). With MSS 1460, RTT 80 ms and 0.01% loss: (1460 x 8 / 0.08) x (1.22 / 0.01) = 146 kbit/s x 122, about 17.8 Mbit/s. That is why long-haul transfers also need a clean path, or a less loss-sensitive algorithm such as BBR.
What I check on a slow long-distance transfer
- RTT with ping, and the observed rate. If rate x RTT is close to 64 KB, the window is the limit.
ss -tion the sender:wscale,cwnd,rtt,retrans, andrwnd_limitedtime.- Buffer maximums on both hosts (
tcp_rmem,tcp_wmem) against the BDP.
They will ask next
Why do parallel streams make a slow transfer faster?
Each stream has its own window. If one flow is window-limited at 6.5 Mbit/s, eight flows get about 52 Mbit/s. That is a workaround that proves the diagnosis. The real fix is a window that covers the BDP.
Does a bigger buffer on the receiver always help?
Only up to the BDP. Beyond that the sender just fills router queues, which adds latency (bufferbloat). The sender's send buffer also has to hold a full window of unacknowledged data.
Traps
- Forgetting to convert bits to bytes. 1 Gbit/s x 80 ms is 80 Mbit, which is 10 MB, not 80 MB.
- Using one-way delay instead of round-trip time.
How does TCP decide when to retransmit, and how is the retransmission timeout calculated?
Say this first
TCP retransmits either when the retransmission timer fires or earlier, when duplicate ACKs or SACK show a hole (fast retransmit). The timer, RTO, comes from measured round-trip times (RFC 6298): RTO = smoothed RTT + 4 x RTT variance, starting at 1 second before any sample. Each time the timer fires, the RTO doubles. A timeout is expensive: the congestion window drops to one segment and the sender goes back to slow start. Linux uses a 200 ms minimum RTO, lower than the RFC's 1 second.
Go deeper
The calculation
- First sample R: SRTT = R, RTTVAR = R / 2.
- Later samples R': RTTVAR = 3/4 RTTVAR + 1/4 abs(SRTT - R'), then SRTT = 7/8 SRTT + 1/8 R'.
- RTO = SRTT + max(G, 4 x RTTVAR), where G is the clock granularity.
- Bounds: the RFC says at least 1 second; Linux uses 200 ms minimum and 120 seconds maximum.
Karn's algorithm
An ACK for a retransmitted segment is ambiguous: it may acknowledge the first copy or the second. TCP takes no RTT sample from it, and keeps the backed-off RTO until a fresh sample arrives. Timestamps remove the ambiguity because the echoed value says which copy is being acknowledged.
When the timer fires
ssthresh = max(flight size / 2, 2 x MSS), cwnd = 1 segment, retransmit the oldest unacknowledged segment, double the RTO. On Linux an established connection gives up after
net.ipv4.tcp_retries2(15) retransmissions, at least about 15 minutes.Avoiding the timer
Most losses should be repaired by fast retransmit. Linux also sends a tail loss probe about two RTTs after the last segment if nothing has been acknowledged, so a lost final segment triggers recovery without waiting for the full RTO.
They will ask next
What does "TCP Spurious Retransmission" mean in Wireshark?
The capture already saw the receiver acknowledge that data, yet the sender sent it again. Often the RTO was too short for a sudden RTT increase, or the ACK was delayed or lost. It wastes bandwidth and shrinks cwnd for nothing.
Why is a burst of RTOs worse than the same number of fast retransmits?
Each RTO waits at least 200 ms on Linux (much more after backoff) with nothing sent, then restarts from one segment. Fast retransmit repairs the hole within about one RTT and only halves the window or less.
Traps
- Saying the RTO is a fixed value. It adapts to the measured RTT and backs off exponentially.
What is fast retransmit, why three duplicate ACKs, and what happens to the congestion window afterwards?
Say this first
When one segment is lost, every later segment that arrives makes the receiver repeat the same ACK for the missing byte. After three duplicate ACKs, the fourth identical ACK in a row, the sender retransmits the missing segment at once instead of waiting for the RTO. Three is the threshold because reordering commonly causes one or two duplicates. Then fast recovery cuts ssthresh to about half the flight size (to 0.7 times with CUBIC) and continues from there instead of dropping to one segment.
Go deeper
Worked example
The sender sends segments 1 to 6, each 1,000 bytes, starting at byte 1. Segment 3 (bytes 2001 to 3000) is lost.
Receiver gets It sends Sender sees Seg 1 ACK 1001 Seg 2 ACK 2001 Seg 4 ACK 2001 (SACK 3001-4001) Dup ACK 1 Seg 5 ACK 2001 (SACK 3001-5001) Dup ACK 2 Seg 6 ACK 2001 (SACK 3001-6001) Dup ACK 3: retransmit seg 3 Seg 3 (again) ACK 6001 Hole filled, everything acknowledged Wireshark labels these
TCP Dup ACKandTCP Fast Retransmission.Fast recovery (RFC 5681, Reno style)
- ssthresh = max(flight size / 2, 2 x MSS).
- Retransmit the lost segment.
- Keep sending new data as duplicate ACKs show segments leaving the network.
- When new data is acknowledged, set cwnd to ssthresh and continue in congestion avoidance.
NewReno handles several losses in one window. SACK tells the sender exactly which blocks are missing. Linux now detects loss by time with RACK-TLP (RFC 8985) rather than only counting duplicates, which copes better with reordering, and paces the reduction with proportional rate reduction.
They will ask next
Why is per-packet load balancing bad for TCP?
It reorders segments within a flow. Reordering creates duplicate ACKs, which trigger spurious fast retransmits and needless window cuts. Per-flow hashing keeps a flow on one path.
What happens if there are not enough segments in flight to produce three duplicates?
With a small window or at the end of a transfer there may be fewer than three later segments, so only the RTO or a tail loss probe can recover. Limited transmit (RFC 3042) lets the sender send new segments on the first two duplicate ACKs to help generate the third.
Traps
- Saying "three ACKs" trigger it. It is three duplicates, the fourth identical ACK in a row.
What does selective acknowledgment add on top of cumulative ACKs?
Say this first
A cumulative ACK only says "everything up to here". With several losses in one window the sender learns about one hole per round trip. SACK (RFC 2018), agreed with SACK-permitted in the handshake, lets the receiver list up to three or four blocks of data it holds beyond the hole. The sender resends only the gaps and can repair several losses in about one RTT.
Go deeper
What it looks like
Output 10.2.20.30.5201 > 10.1.10.20.51544: Flags [.], ack 1001, win 501, options [nop,nop,TS val 2154883120 ecr 1209364471,nop,nop,sack 1 {2461:5381}], length 0The receiver has everything up to byte 1000 and also bytes 2461 to 5380. Bytes 1001 to 2460, one 1,460-byte segment, are missing.
Details that come up
- SACK blocks are advisory. The receiver may discard SACKed data, so the sender keeps it until it is cumulatively acknowledged.
- D-SACK (RFC 2883) reports data received twice, which lets the sender detect spurious retransmissions.
- Any device that rewrites sequence numbers, for example a firewall that randomises ISNs, must rewrite SACK edges as well. When one did not, SACK-based recovery stalled.
They will ask next
Why only three or four blocks?
TCP options are limited to 40 bytes. Each block is 8 bytes plus 2 for the option header, so 4 blocks fit, or 3 when the timestamp option takes its 12 bytes.
Is SACK enabled by default?
On every mainstream OS, yes. On Linux it is
net.ipv4.tcp_sack=1. It is only used when both sides offer SACK-permitted in the handshake.
Traps
- Thinking a SACKed segment can be forgotten by the sender. Only the cumulative ACK releases data.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageWhat are TCP keepalives, what are the Linux defaults, and how are they different from HTTP keep-alive and BGP keepalives?
Say this first
A TCP keepalive is an empty probe the stack sends on an idle connection, carrying a sequence number one below what the peer expects, so the peer must answer with an ACK. If enough probes go unanswered, the connection is reset and the application gets an error. It is off unless the application sets SO_KEEPALIVE. Linux defaults are 7,200 seconds idle, 75 seconds between probes and 9 probes, so a dead peer is found after about 2 hours 11 minutes. HTTP keep-alive just means reusing a connection for more requests. BGP keepalives are BGP messages, every 60 seconds with a 180-second hold time on Cisco.
Go deeper
Two jobs
- Find dead peers. A peer that crashed or lost its state leaves a half-open connection: our side thinks it is ESTABLISHED forever if we never send. A probe gets a RST or no answer.
- Keep middlebox state alive. Firewalls and NAT remove idle flows after their idle timeout, often an hour or less. A 2-hour default keepalive is too slow to help; the application's next packet then hits a device with no state and is dropped or reset.
Tuning
System-wide on Linux:
Shell sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probesPer socket, which is better: TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT. A common rule is an idle time shorter than the shortest idle timeout in the path, for example 300 seconds.
Related
TCP_USER_TIMEOUT caps how long sent data may stay unacknowledged before the connection is dropped, which catches dead peers on busy connections where keepalive never triggers.
They will ask next
What is a half-open connection, and how is it different from half-closed?
Half-open: one side has state and the other does not, because of a crash, reboot or lost NAT state. Half-closed: one side sent FIN and the other is still legitimately sending. Half-closed is normal TCP; half-open is a failure that keepalives or the next send reveal.
Why do applications often add their own heartbeat instead?
TCP keepalive only proves the peer's kernel is alive. An application heartbeat (BGP keepalive, HTTP/2 PING, WebSocket ping) proves the process is working, and its timers are under the application's control.
Traps
- Confusing HTTP keep-alive (connection reuse) with TCP keepalive (idle probes).
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageYou see TCP ZeroWindow in a capture. What does it mean and whose fault is it?
Say this first
The receiver advertised a window of zero: its receive buffer is full because its application is not reading fast enough. The sender stops, starts the persist timer and sends small zero-window probes until the receiver advertises space again with a window update. It points at the receiving host or its application (CPU, disk, a slow consumer), not at the network.
Go deeper
Wireshark labels and who sends them
Label Sent by Meaning TCP Window Full Sender It has sent everything the receiver's window allows TCP ZeroWindow Receiver Window field is 0 TCP ZeroWindowProbe Sender Persist timer probe TCP ZeroWindowProbeAck Receiver Answer, usually still 0 TCP Window Update Receiver Window opened again Proving it on the hosts
- Receiver:
ss -tnshows a large Recv-Q on that connection: data the kernel holds that the application has not read. - Sender:
ss -tnshows a large Send-Q: data waiting for window.
Why the persist timer exists
If the receiver's window update were lost, both sides would wait forever. The sender's probes force a fresh ACK carrying the current window.
Silly window syndrome
A receiver that advertises every few freed bytes makes the sender send tiny segments. Receivers wait until they can open a meaningful amount (an MSS or half the buffer) before advertising.
They will ask next
How is "Window Full" different from "ZeroWindow"?
Window Full is the sender using all the window it was given, which can be normal at high speed with a small window. ZeroWindow is the receiver saying it has no space at all.
The network team gets blamed for a slow file copy and the capture is full of ZeroWindow. What do you say?
The network delivered data faster than the receiving application consumed it. Look at the receiver's disk, CPU and application, and show the Recv-Q from
ssas evidence.
Traps
- Treating ZeroWindow as packet loss or congestion. It is flow control doing its job.
- Receiver:
How does Path MTU Discovery work, and what is a PMTUD black hole?
Say this first
The sender sets the Don't Fragment bit on its packets. A router that cannot forward a packet because the next link's MTU is smaller drops it and sends back ICMP type 3 code 4, "fragmentation needed and DF set", with the next-hop MTU. The sender lowers its packet size for that destination and resends. In IPv6 the message is ICMPv6 Packet Too Big, and routers never fragment. A black hole happens when that ICMP message never reaches the sender, usually because a firewall drops all ICMP. Small packets work, large ones vanish, and connections hang as soon as a full-size segment is needed.
Go deeper
Symptoms that shout MTU
- Ping works, the TCP handshake works, small requests work.
- Large transfers stall: web pages load partly, SSH logs in but a long directory listing hangs, file copies freeze at the first big read.
- TLS handshakes stall when the server sends its certificate chain, the first large packet.
- One direction works and the other does not (uploads fine, downloads hang).
How I test from Linux
Shell ping -M do -s 1472 -c 3 10.30.0.10 ping -M do -s 1448 -c 3 10.30.0.10 tracepath -n 10.30.0.101472 + 8 ICMP + 20 IP = 1,500. If 1,472 fails with no "Frag needed" message and a smaller size works, something is dropping packets above the real path MTU and the ICMP is not coming back.
Fixes, best first
- Permit ICMP type 3 code 4 (and ICMPv6 type 2) through every firewall and ACL in the path.
- Clamp the TCP MSS where the MTU shrinks, such as tunnel interfaces (
ip tcp adjust-mss). - Set a correct
ip mtuon tunnels so the router itself sends a correct ICMP message. - Enable packetization layer PMTUD on hosts (
net.ipv4.tcp_mtu_probing=1on Linux), which probes with smaller segments when it detects a black hole instead of relying on ICMP.
Clearing the DF bit on a router makes TCP work by forcing fragmentation, which costs CPU and can break under load. It also cannot help IPv6.
They will ask next
Why does the TCP handshake succeed when the path MTU is broken?
SYN, SYN-ACK and ACK are around 60 bytes. Only full-size data segments exceed the smaller MTU, so the failure shows up after the connection is established.
Where does the sender keep what it learned?
In a per-destination cache. On Linux,
ip route get 10.30.0.10showscache expires ... mtu 1476after an ICMP message has been received; entries expire after 10 minutes by default.Why must IPv6 firewalls never drop Packet Too Big?
IPv6 routers do not fragment, so PMTUD is the only way a sender learns a smaller MTU. Blocking ICMPv6 type 2 creates black holes for every path with a smaller link.
Traps
- Blocking all ICMP "for security". Type 3 code 4 and ICMPv6 type 2 are needed for the network to work.
How did you do?Book 3.3 Internet (IP)Own pageWhat is MSS clamping, how do you configure it on IOS and Linux, and what does it not fix?
Say this first
MSS clamping makes a router rewrite the MSS option in TCP SYN and SYN-ACK segments passing through it, so both hosts agree to segments that fit the smallest link without relying on ICMP. On IOS it is
ip tcp adjust-mss 1360on the interface, typically a tunnel; on Linux it is an iptables or nftables rule. It only works for TCP whose handshake passes through the device, so it does nothing for UDP traffic such as QUIC, DNS or other VPNs.Go deeper
IOS-XE
Cisco IOS interface Tunnel0 ip mtu 1400 ip tcp adjust-mss 1360 ipv6 mtu 1400 ipv6 tcp adjust-mss 1340ip tcp adjust-mssacts on SYNs entering and leaving the interface, only ever lowers the value, and accepts 500 to 1460. 1360 = 1400 - 20 (IP) - 20 (TCP). For IPv6 subtract 60.Linux router
Shell iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtunftables:
Shell nft add rule inet filter forward tcp flags syn tcp option maxseg size set rt mtuJunos SRX
Junos set security flow tcp-mss ipsec-vpn mss 1350What clamping does not cover
- UDP: QUIC, DNS, IPsec from hosts, WireGuard. They need PMTUD or conservative sizes of their own.
- TCP whose SYN takes a different path (asymmetric routing) than the data.
- TCP inside an encrypted tunnel the router only sees from outside.
- Packets that are already large for other reasons, such as non-TCP protocols.
So clamping is a strong safety net, but ICMP should still be allowed.
They will ask next
Why is 1360 such a common number?
It fits GRE over IPsec with headroom: an
ip mtuof 1400 covers GRE's 24 bytes plus IPsec ESP overhead in tunnel mode with common ciphers. 1400 minus 40 bytes of IPv4 and TCP headers gives 1360.Does adjust-mss raise an MSS that is lower than the configured value?
No. It only rewrites a SYN whose MSS is larger than the configured value.
Which direction should you apply it on a PPPoE link?
On the dialer or the inside interface, either works because the command handles SYNs in both directions through that interface. The usual value is 1452 for a 1,492-byte PPPoE MTU.
Traps
- Claiming MSS clamping fixes all MTU problems. It only helps TCP whose handshake it sees.
What is QUIC, why does it run over UDP, and what does it change for network engineers?
Say this first
QUIC (RFC 9000) is a reliable, multiplexed, encrypted transport built on UDP, normally on port 443 under HTTP/3. It has its own streams, retransmission, flow control and congestion control, with TLS 1.3 built into the handshake, so a new connection is ready in one round trip. It uses UDP because a new IP protocol would be blocked by NAT and firewalls and would need kernel changes everywhere; UDP passes through and QUIC can live in user space. For network teams it means almost everything is encrypted, including ACKs, so firewalls see far less, MSS clamping does not apply, and UDP idle timeouts matter.
Go deeper
Features worth naming
- Connection IDs instead of the four-tuple identify a connection, so a phone moving from Wi-Fi to LTE keeps its connection (connection migration).
- Streams are independent: a loss stalls only its own stream.
- Packet numbers never repeat, so retransmissions are not ambiguous for RTT measurement.
- 0-RTT on resumption, with the same replay caveats as TLS 1.3.
- Header protection encrypts packet numbers. On-path devices see little more than the destination connection ID and a few flag bits, including an optional spin bit that lets them estimate RTT.
Sizes and safety
- The client pads its first Initial datagram to at least 1,200 bytes, and QUIC assumes the path carries at least 1,200-byte UDP payloads.
- Before the client's address is validated, a server may send at most three times the bytes it received, which limits amplification attacks.
What the network can and cannot see
Initial packets are encrypted with keys derived from the connection ID that is sent in clear, so any middlebox can decrypt them and read the ClientHello, including the SNI. Everything after the handshake is opaque. A firewall that wants TLS inspection or URL filtering has to block UDP 443 and force a TCP fallback.
They will ask next
How does a load balancer keep QUIC packets going to the right server after a client changes address?
It cannot use the four-tuple, because that changes. It routes on the connection ID, which servers generate so that it encodes or maps to the right backend.
Should an enterprise block QUIC?
It is a policy trade-off. Blocking UDP 443 restores visibility for inspection tools and the browser falls back to TCP. Allowing it gives users better performance on lossy and mobile networks.
Traps
- Saying QUIC is unreliable because it runs over UDP. It provides its own reliability.
Walk me through a TLS 1.3 handshake. Why is it one round trip instead of two?
Say this first
The client sends a ClientHello with its supported versions, cipher suites, signature algorithms, SNI, ALPN and, the key change, a key share: its ephemeral Diffie-Hellman public key for the group it guesses the server will pick. The server replies with ServerHello and its own key share, and both sides can now compute the handshake keys. Everything after ServerHello is encrypted: EncryptedExtensions, Certificate, CertificateVerify and Finished. The client checks them and sends its Finished, already able to send its request. TLS 1.2 needed a separate round trip to agree on parameters before exchanging keys; TLS 1.3 sends the key share up front.
Go deeper
The messages
Output Client Server ClientHello + supported_versions, key_share, signature_algorithms, server_name (SNI), ALPN, psk_key_exchange_modes -> ServerHello + key_share {EncryptedExtensions} (ALPN result) {Certificate} {CertificateVerify} (signature over the transcript) <- {Finished} {Finished} [Application data: GET /] ->Braces mean encrypted with handshake keys. The server can send data right after its Finished.
Round trips before the first request byte
Stack Round trips TCP + TLS 1.2 3 TCP + TLS 1.3 2 QUIC (TLS 1.3 inside) 1 TLS 1.3 or QUIC resumption with 0-RTT data in the first flight What 1.3 removed
RSA key transport (no forward secrecy), static Diffie-Hellman, CBC and RC4 ciphers, SHA-1 in signatures, compression and renegotiation. The cipher suites are AEAD only, for example
TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384andTLS_CHACHA20_POLY1305_SHA256.Things that look odd in captures
- The version field says TLS 1.2; the real version is in the supported_versions extension. This middlebox compatibility mode also sends a dummy ChangeCipherSpec.
- If the server does not like the client's key share group it sends a HelloRetryRequest, which costs one more round trip.
- Hybrid post-quantum key shares such as X25519MLKEM768 make the ClientHello larger than one packet, which has exposed middleboxes that assumed a ClientHello fits in one segment.
They will ask next
What can a firewall read in a TLS 1.3 handshake?
The ClientHello in clear: SNI, ALPN, offered versions and cipher suites (often fingerprinted, for example with JA3 or JA4), and the ServerHello. The server certificate is encrypted in 1.3, unlike 1.2.
What is forward secrecy and why does 1.3 force it?
Session keys come from ephemeral Diffie-Hellman values that are thrown away, so stealing the server's private key later does not decrypt recorded traffic. With RSA key transport it did, so 1.3 removed it.
What does CertificateVerify prove?
That the server holds the private key for the certificate. It is a signature over the handshake transcript, which also protects the negotiation from tampering.
Traps
- Saying the server certificate is visible to observers in TLS 1.3. It is encrypted.
How did you do?Book 8.5 Example SystemsBook 8.3 Key PredistributionOwn pageWhat are SNI and ALPN in the TLS ClientHello, and why do network engineers care about them?
Say this first
SNI (Server Name Indication) is the hostname the client wants, sent in the ClientHello so a server hosting many sites on one IP can pick the right certificate. ALPN (Application-Layer Protocol Negotiation) lists the application protocols the client speaks, such as
h2andhttp/1.1, and the server picks one inside the handshake, avoiding an extra round trip. Network engineers care because firewalls filter on SNI, load balancers route on it, and a missing SNI or a proxy that does not offerh2causes odd failures.Go deeper
SNI in practice
- Without SNI the server returns its default certificate, and the client reports a name mismatch. Typical culprits: health checks or scripts connecting by IP, and old clients.
- Load balancers and reverse proxies use SNI to choose a certificate and a backend pool, even when they pass the TLS through without terminating it.
- Firewalls use it for URL categories and application identification. Encrypted Client Hello hides the real SNI behind an outer, generic one.
ALPN in practice
- HTTP/2 over TLS is selected only through ALPN
h2. A proxy in the middle that offers onlyhttp/1.1downgrades the connection. - gRPC needs HTTP/2. A TLS-terminating load balancer that does not negotiate
h2toward the client or the backend breaks gRPC calls. - QUIC uses ALPN too; HTTP/3 is
h3.
Testing
Shell openssl s_client -connect 203.0.113.80:443 -servername shop.example.com -alpn h2,http/1.1The output shows the certificate the server chose for that name and
ALPN protocol: h2when HTTP/2 was selected.They will ask next
A health check against a TLS backend fails with a certificate name error, but browsers are fine. Why?
The health check connects by IP without SNI, so the backend returns its default certificate. Configure the check to send the hostname as SNI, or relax name verification for health checks only.
What is domain fronting?
Using one hostname in the SNI and a different one in the encrypted HTTP Host header, so a censor sees an allowed domain while a shared CDN routes to another. Major CDNs now reject mismatches.
Traps
- Saying SNI is encrypted by TLS 1.3. It is not; only ECH hides it.
How did you do?Book 8.5 Example SystemsOwn page
Expert7
How does a SYN flood work, and how do SYN cookies defend against it without breaking TCP?
Say this first
A SYN flood sends huge numbers of SYNs, usually from spoofed addresses, so the server allocates a half-open entry for each and retransmits SYN-ACKs that are never answered. The SYN queue fills and real clients are refused. With SYN cookies the server stores nothing: it encodes the connection's details and a keyed hash into its own initial sequence number. When the client's ACK returns, the server checks the hash in ack minus one and rebuilds the connection from it.
Go deeper
Two queues on a Linux listener
- SYN queue: half-open connections in SYN_RCVD, waiting for the final ACK.
- Accept queue: completed connections waiting for the application to call accept(). Its size is the
listen() backlog, capped by
net.core.somaxconn(4096 by default since kernel 5.4).
A SYN flood fills the first. A slow application fills the second, which shows as
ListenOverflowsinnstatand as Recv-Q at or near Send-Q on the LISTEN line ofss -lnt.How cookies work
The server sets its ISN from a time counter, a small index for the client's MSS, and a cryptographic hash of the four-tuple with a secret. It keeps no state. A real client's ACK carries that ISN plus one; the server checks the hash and the time counter, then creates the connection. Spoofed sources never send the ACK, so they cost the server only an outgoing SYN-ACK.
The trade-off
Only a few MSS values fit in the cookie, and window scale and SACK would be lost. Linux stores those options in the TCP timestamp when timestamps are on, which avoids most of the loss. Linux only switches to cookies when the SYN queue overflows (
net.ipv4.tcp_syncookies=1, the default), and the kernel log then shows a "Possible SYN flooding on port ... Sending cookies" message.Other layers of defence
SYN proxy on a firewall or load balancer, which completes the handshake itself before opening the server side; rate limits; and upstream scrubbing for volumetric floods that fill the link itself.
They will ask next
How do you tell a SYN flood from an application that is just slow to accept?
A flood shows many SYN-RECV sockets and rising
TcpExtSyncookiesSent. A slow application shows few SYN-RECV, a full accept queue on the LISTEN socket inss -lnt, and risingTcpExtListenOverflows.Why do victims of spoofed SYN floods sometimes complain about traffic they never sent?
The attacked server sends SYN-ACKs to the spoofed source addresses. Those hosts receive unexpected SYN-ACKs (backscatter) and answer with RST.
Traps
- Saying SYN cookies are always on. Linux uses them only when the queue overflows.
Compare loss-based congestion control like Reno and CUBIC with BBR. When would you switch a server to BBR?
Say this first
Reno and CUBIC treat packet loss as the congestion signal: they grow the window until a queue overflows, then cut it. CUBIC, the Linux default, grows as a cubic function of time since the last loss, so it recovers quickly on long fat paths, and cuts to 0.7 of the window on loss. BBR does not react to loss by design: it measures the bottleneck bandwidth and the minimum RTT, paces at that rate and keeps about one to two BDPs in flight. I would try BBR on servers sending over long, slightly lossy paths, such as video or downloads to the internet, after testing fairness with existing traffic.
Go deeper
CUBIC (RFC 9438)
- Window as a function of time t since the last reduction: W(t) = C x (t - K)^3 + Wmax, with C = 0.4.
- Concave as it approaches the old maximum Wmax, flat near it, then convex as it probes beyond it.
- Multiplicative decrease factor 0.7. Growth does not depend on RTT, so long-RTT flows compete better than with Reno.
- Still loss-based: it fills buffers before it backs off, so it adds queueing delay.
BBR (Google, 2016)
- Builds a model: BtlBw, the maximum recent delivery rate, and RTprop, the minimum recent RTT.
- Paces sending at about BtlBw, with cwnd around 2 x BtlBw x RTprop.
- Phases: Startup (gain about 2.89), Drain, ProbeBW (cycles pacing gain 1.25, 0.75, then 1), and ProbeRTT (drops to 4 packets for at least 200 ms if the minimum RTT has not been refreshed for 10 s).
- Random loss that is not caused by congestion barely slows it, which is a big win on long lossy paths.
Cautions
- BBR v1 can take more than its share from CUBIC flows when buffers are shallow and less when buffers are deep, and can cause high retransmission rates. Later versions add a response to loss and ECN.
- Congestion control is sender-side only. Changing a client does not change how a server sends to it.
- BBR relies on pacing. Older kernels needed the
fqqdisc; newer kernels pace inside TCP, thoughfqis still commonly set.
Switching on Linux
Shell sysctl net.ipv4.tcp_available_congestion_control sysctl -w net.core.default_qdisc=fq sysctl -w net.ipv4.tcp_congestion_control=bbrThey will ask next
What is bufferbloat and how do these algorithms relate to it?
Bufferbloat is excess latency from oversized queues. Loss-based algorithms fill any queue before they see a drop, so latency rises under load. BBR aims to keep queues near empty. AQM such as fq_codel on the bottleneck attacks the same problem from the network side.
What does DCTCP do differently in a data center?
Switches mark packets with ECN at a low queue threshold. DCTCP cuts its window in proportion to the fraction of marked packets instead of halving, keeping queues short with high throughput. It needs ECN on every switch and host, so it suits a controlled data center, not the internet.
Traps
- Saying BBR fixes a receiver-side window limit. If rwnd or the send buffer is the limit, no congestion control helps.
What are Nagle's algorithm and delayed ACK, and why do they sometimes add 40 or 200 ms of latency together?
Say this first
Nagle's algorithm (RFC 896) holds back a small segment while earlier data is still unacknowledged, so many tiny writes become fewer full segments. Delayed ACK has the receiver wait a short time before acknowledging, hoping to combine the ACK with a reply or with the next segment. When a sender does two small writes and then waits for a reply, Nagle holds the second write until the first is acknowledged, and the receiver delays that ACK because it has no reply yet. Both wait until the delayed ACK timer fires: about 40 ms on Linux, 200 ms on Windows.
Go deeper
The stall step by step
- Client writes a 100-byte header. No data is outstanding, so it goes immediately.
- Client writes a 50-byte body. Data is outstanding and the segment is small, so Nagle holds it.
- Server gets the header. It cannot answer until it has the body, so there is no reply to piggyback on, and it delays its ACK.
- The delayed ACK timer fires, the ACK goes, Nagle releases the body, the server answers. Every request pays the timer.
Fixes
- Write the whole message in one call (or use writev), so there is no second small write.
- Set TCP_NODELAY on the sender for request and response or interactive protocols. SSH and many RPC libraries do this; Go sets it by default.
- On Linux the receiver can use TCP_QUICKACK, which is temporary and needs resetting.
Delayed ACK rules
A receiver acknowledges at least every second full-sized segment, and must not delay more than 500 ms (RFC 1122). Linux adapts its delay between about 40 ms and 200 ms.
They will ask next
Does this affect bulk transfers?
No. Bulk transfers send full-sized segments, which Nagle never holds, and the receiver ACKs every second segment. It hurts small request and response exchanges.
What is TCP_CORK?
A Linux option that is the opposite of NODELAY: hold all partial segments until the cork is removed or a full segment is ready. Web servers use it to send headers and file data in full segments.
Traps
- Disabling delayed ACK everywhere. It roughly halves ACK traffic and is not the problem in bulk flows.
How did you do?Book 5.2 Reliable Byte Stream (TCP)Own pageHow does ECN work in IP and TCP, and why is it not used everywhere?
Say this first
Explicit Congestion Notification (RFC 3168) lets a router with active queue management mark a packet as congestion experienced instead of dropping it. ECN uses the two low bits of the old IP ToS byte. The receiver echoes the mark with the ECE flag; the sender reduces cwnd as if a loss had happened, without any retransmission, and confirms with CWR. It needs support at both ends and marking routers. Linux by default only accepts ECN when the peer asks for it and does not request it itself, and old middleboxes that mishandled the bits slowed deployment.
Go deeper
IP codepoints (2 bits after the 6-bit DSCP)
Bits Name Meaning 00 Not-ECT Sender does not support ECN 10 ECT(0) ECN-capable transport 01 ECT(1) ECN-capable transport, used by L4S 11 CE Congestion experienced, set by a router TCP negotiation
- SYN with ECE and CWR set: "I support ECN".
- SYN-ACK with ECE set: "So do I".
- Data packets then carry ECT. A router marks CE; the receiver sets ECE on its ACKs until the sender replies with CWR.
Where it matters
- Data centers: DCTCP and RoCEv2 congestion control rely on ECN marking at low queue thresholds.
- L4S (RFC 9330 to 9332) uses ECT(1) for very low queueing delay.
- Linux:
net.ipv4.tcp_ecn=2by default (accept, do not initiate);1requests ECN on outgoing connections.
Network gotcha
QoS policies that rewrite the whole ToS byte instead of only DSCP wipe ECN bits. Remarking DSCP should leave the two ECN bits alone.
They will ask next
What is the difference between ECE and CWR?
ECE goes from receiver to sender: "a packet you sent was marked". CWR goes from sender to receiver: "I have reduced my window, you can stop echoing". The receiver keeps setting ECE until it sees CWR.
Does a router mark every packet when the queue fills?
No. AQM such as RED or CoDel marks probabilistically or by delay before the queue is full. If the queue does overflow, packets are still dropped, ECN or not.
Traps
- Saying ECN uses DSCP bits. It uses the two bits after DSCP in the same byte.
Explain head-of-line blocking in HTTP/1.1, HTTP/2 and HTTP/3. Which layer does each version fix?
Say this first
Head-of-line blocking is when one slow or lost item holds up everything queued behind it. HTTP/1.1 has it at the application layer: responses on a connection come back in request order. HTTP/2 fixes that by multiplexing streams, but moves the problem down to TCP: TCP delivers bytes strictly in order, so one lost segment stalls every stream on the connection until it is retransmitted. HTTP/3 fixes the transport layer too, because QUIC orders data per stream: a loss only stalls the stream it belongs to.
Go deeper
HTTP/1.1
Pipelining lets a client send several requests without waiting, but responses must return in order, so a slow first response blocks the rest. Browsers never enabled it and open about six parallel connections instead.
HTTP/2
Frames from many streams interleave on one TCP connection. The application is no longer blocked, but the kernel will not hand over bytes after a gap. With 1% loss on a busy page, one gap freezes every image, script and API call on the connection for a round trip or more.
HTTP/3 and QUIC
QUIC numbers packets, not bytes of one stream. Each stream has its own offsets, so data for stream 8 can be delivered while stream 4 waits for a retransmission. Order is only kept inside a stream.
The header compression detail
HPACK's dynamic table assumes in-order delivery: every header block may refer to earlier ones. QPACK was designed so that header decoding does not reintroduce cross-stream blocking, using separate encoder and decoder streams and letting the encoder limit how much a stream may depend on unacknowledged table entries.
They will ask next
Does QUIC remove head-of-line blocking completely?
No. Within one stream, data is still delivered in order, so a large download on one stream still waits for its own lost packets. It removes blocking between streams.
Why not just run HTTP/2 over several TCP connections?
You can, and it reduces cross-stream stalls, but you lose header compression across connections, pay more handshakes, and get multiple congestion controllers competing. QUIC gets per-stream delivery with one connection and one congestion controller.
Traps
- Saying HTTP/2 solved head-of-line blocking. It solved the HTTP-level kind and exposed the TCP-level kind.
What is TLS 1.3 0-RTT, and why is it risky?
Say this first
0-RTT lets a client that has connected before send application data in its very first flight, encrypted with keys from a pre-shared key the server gave it in a session ticket. It saves a full round trip. The risk is replay: an attacker who captures the ClientHello and early data can send it again, and the server may process the request twice. Early data also lacks forward secrecy, because its keys come only from the ticket. So 0-RTT is only safe for idempotent requests, and many servers turn it off.
Go deeper
How it works
- In a previous connection, the server sent a NewSessionTicket.
- The new ClientHello carries
pre_shared_keyandearly_data, followed by application data encrypted with the early traffic key. - The server accepts by echoing early_data in EncryptedExtensions, or rejects it, in which case the client resends the data after the normal 1-RTT handshake.
Why replay is hard to stop
Within one server, single-use tickets or a cache of recent ClientHellos can catch duplicates. Across a cluster or many data centers, that state would have to be shared, so most deployments accept some replay window and limit what early data may do.
How applications protect themselves
- Only allow safe, idempotent requests in early data, such as GET without side effects.
- A server or CDN that forwards early data to a backend marks it with
Early-Data: 1; the backend can answer425 Too Earlyto make the client retry after the handshake (RFC 8470). - nginx has
ssl_early_data offby default.
They will ask next
Is session resumption without 0-RTT risky in the same way?
No. 1-RTT resumption is not replayable, because the server's fresh contribution is part of the keys. With the psk_dhe_ke mode it also does a new Diffie-Hellman exchange and keeps forward secrecy.
How would you test 0-RTT against a server?
Save a session with
openssl s_client -sess_out sess.pem, then reconnect with-sess_in sess.pem -early_data req.txtand check whether the output reports the early data as accepted.
Traps
- Saying TLS 1.3 handshakes are always 0-RTT. A first connection is 1-RTT; 0-RTT needs a prior ticket.
What happens, in as much depth as you can, when you type https://shop.example.com into a browser and press Enter?
Say this first
The browser checks HSTS and its caches, then resolves the name: its own cache, the OS stub resolver, then a recursive resolver that walks root, TLD and authoritative servers for A, AAAA and HTTPS records. It races IPv6 and IPv4 connections (Happy Eyeballs). The host sees the address is off-subnet, ARPs for its default gateway's MAC and sends the SYN there; NAT at the edge rewrites the source, and BGP-routed paths carry it to the nearest edge, often a CDN. TCP and TLS 1.3 handshakes (or one QUIC handshake) follow, with SNI, ALPN and certificate validation. Then the HTTP request goes through load balancers to the application, the response comes back under slow start, and the browser parses, fetches subresources and renders.
Go deeper
1. Before any packet
- Parse the URL. HSTS or the preload list may force HTTPS for
http://links. - Check the HTTP cache and service worker; reuse an open connection to the same origin if one exists.
2. Name resolution
- Browser DNS cache, then the OS (hosts file, then the stub resolver), then the configured recursive resolver over UDP 53 or encrypted DNS.
- On a cache miss the resolver asks a root server for
.com, a.comTLD server forexample.com, and the authoritative server for the A, AAAA and HTTPS records. TTLs govern caching at each step. - An HTTPS record can announce
alpn="h3,h2"and keys for Encrypted Client Hello.
3. Getting the first packet out
- The destination is off-subnet, so the frame goes to the default gateway. ARP (IPv4) or neighbor discovery (IPv6) finds its MAC if not cached.
- Happy Eyeballs (RFC 8305) starts IPv6 first and IPv4 after a short delay, about 250 ms, and keeps whichever connects first.
- The edge firewall does PAT: a private source like
192.168.1.20:51544becomes a public address and port. - Routers forward hop by hop on longest prefix match; across providers, BGP chose the path. The service is often anycast, so the nearest CDN site answers.
4. Transport and security
- TCP: SYN with MSS, window scale, SACK and timestamps, then SYN-ACK and ACK: one RTT.
- TLS 1.3: ClientHello with SNI
shop.example.com, ALPNh2,http/1.1and a key share; the server returns its certificate chain encrypted; the client validates the chain and the name: one more RTT. - Or, if HTTP/3 was advertised: one QUIC handshake on UDP 443 covers both.
5. The request and the server side
- HTTP/2
GET /with:authority, cookies andAccept-Encoding, compressed with HPACK. - A CDN may answer from cache. Otherwise a layer 7 load balancer terminates TLS, picks a healthy backend, and the application talks to caches and databases.
6. The response and rendering
- Status and headers (Cache-Control, Set-Cookie, Strict-Transport-Security, Alt-Svc), then a compressed body.
- The first flight is limited by the initial congestion window of 10 segments, about 14.6 KB.
- The browser parses HTML, finds CSS, JavaScript and images, fetches them over the same multiplexed connection, builds the DOM and CSSOM, lays out and paints.
7. Afterwards
The connection stays open for reuse. When it finally closes, whichever side sends the first FIN holds TIME_WAIT.
They will ask next
Where would you look if the page is slow but only for the first visit?
Cold caches on every layer: DNS lookups that walk the hierarchy, new TCP and TLS handshakes, slow start on a fresh connection, and an empty browser and CDN cache.
curl -wwith time_namelookup, time_connect, time_appconnect and time_starttransfer splits the delay.Where does the MAC address of the web server come into this?
It does not. The client only learns its gateway's MAC. Each router hop rewrites the layer 2 header; the IP addresses stay the same end to end apart from NAT.
What changes if the browser already has an HTTP/2 connection to the site?
No DNS lookup, no TCP or TLS handshake, and a warm congestion window: the request goes out at once as a new stream on the existing connection.
Traps
- Saying the client ARPs for the web server's IP. It ARPs for the default gateway.
- Skipping certificate validation and congestion control. That is where a deep answer stands out.
- Parse the URL. HSTS or the preload list may force HTTPS for