Networking & Protocols
Reading the IP header
Every time a router forwards a packet, it reads and modifies the IP header. It decrements the TTL by one, recomputes the checksum, and makes a forwarding decision based on the destination address. Knowing which field does what lets you diagnose network problems and understand why IPv6 dropped some fields entirely.
The IPv4 header (20 bytes minimum)
IPv4 packets begin with a 20-byte fixed header (more if options are present):
| Field | Size | Purpose |
|---|---|---|
| Version | 4 bits | Always 4 for IPv4. |
| IHL (header length) | 4 bits | Number of 32-bit words in the header; minimum 5 (= 20 bytes). |
| ToS / DSCP + ECN | 8 bits | Originally a precedence field; redefined as Differentiated Services Code Point (6 bits) for QoS marking + ECN (2 bits). |
| Total length | 16 bits | Header + payload in bytes; maximum 65,535. |
| Identification | 16 bits | Tags fragments from the same original packet. |
| Flags + Fragment offset | 16 bits | ”Don’t fragment” flag, “more fragments” flag, and offset within original packet. |
| TTL | 8 bits | Hop limit; each router decrements by 1; drop if 0. |
| Protocol | 8 bits | Next-layer protocol: 6 = TCP, 17 = UDP, 1 = ICMP, 58 = ICMPv6. |
| Header checksum | 16 bits | CRC over the header only (not payload); recomputed at every hop because TTL changes. |
| Source IP | 32 bits | Sender’s address. |
| Destination IP | 32 bits | Recipient’s address. |
Why the header checksum is only over the header. The payload is protected by the L4 checksum (TCP/UDP). Computing a checksum over the full packet at every router would be prohibitively expensive at line rate; only the header checksum is needed because TTL changes.
TTL — time to live
TTL is an 8-bit hop counter, not a time measurement (despite the name). Every router decrements it by 1. When it reaches 0 the router discards the packet and sends an ICMP “time exceeded” back to the source. This prevents routing loops from keeping packets circulating forever.
traceroute exploits TTL: it sends probes with TTL = 1, 2, 3, … Each router that drops one (because TTL hit zero) replies with “time exceeded,” revealing its address and round-trip time — the path traces itself hop by hop.
DSCP and ECN — the quality knobs
The top 8 bits of the third byte carry two orthogonal quality signals:
- DSCP (6 bits). Differentiated Services Code Point. Routers use it to prioritise traffic in their queues. Common values: EF (Expedited Forwarding — voice/real-time), AF (Assured Forwarding — video), CS (Class Selector — legacy compatibility). End-to-end DSCP rarely survives the public Internet because each AS re-marks at ingress; it reliably influences queueing only within a single network.
- ECN (2 bits). Explicit Congestion Notification. A congested router marks a packet with CE (Congestion Experienced) instead of dropping it. The receiving host echoes the signal back to the sender via TCP, which reduces its congestion window — lower latency without the loss. ECN is enabled by default on modern Linux. L4S (RFCs 9330–9332) extends ECN to achieve ultra-low latency via dual-queue traffic shaping.
- Minimum header size
- 20 bytes
- TTL initial value (Linux default)
- 64
- Protocol: TCP
- 6
- Protocol: UDP
- 17
- Protocol: ICMP
- 1
- Max packet size (total length field)
- 65,535 bytes
The IPv6 header — simplified on purpose
IPv6 has a fixed 40-byte header with no checksum and no fragmentation fields:
- Version (4 bits): Always 6.
- Traffic Class (8 bits): Same DSCP + ECN semantics as IPv4.
- Flow Label (20 bits): Identifies a flow (e.g. a TCP connection) for consistent ECMP treatment without re-hashing every packet.
- Payload Length (16 bits): Size of the payload only (not the header itself).
- Next Header (8 bits): Same role as IPv4’s Protocol field — identifies the extension header or L4 protocol that follows.
- Hop Limit (8 bits): Same as IPv4’s TTL.
- Source / Destination (128 bits each): The 16-byte addresses.
Why no checksum. IPv6 relies on L2 (Ethernet FCS) and L4 (TCP/UDP checksum) for integrity. Eliminating per-hop checksum recomputation reduces per-router work and matches modern hardware capabilities.
Extension headers chain after the fixed header via the Next Header field — analogous to IPv4 options but cleaner. Common extensions: Hop-by-Hop Options, Routing (used by Segment Routing v6), Fragment, Destination Options.
Why this works
Why IPv6 routers never fragment. IPv4 allowed routers to split an oversized packet into smaller fragments — useful but it added CPU work at every hop and created security holes (some firewalls inspected only the first fragment, letting attackers hide malicious payloads in subsequent fragments). IPv6 forbids router fragmentation. If a packet is too large, the router sends an ICMP “packet too big” back to the sender, which must retransmit at a smaller size. Complexity moves to the sender, router stays simple.
What does the TTL field protect against?
Why does IPv6 drop the IP-level header checksum?
Order the IPv4 header fields (after Version + IHL):
- 1 ToS / DSCP + ECN (QoS and congestion marking)
- 2 Total length (header + payload)
- 3 Identification + Flags + Fragment offset
- 4 TTL (hop limit)
- 5 Protocol (TCP=6, UDP=17, ICMP=1)
- 6 Header checksum
- 7 Source IP, Destination IP
- 01Why does a router recompute the IPv4 header checksum on every hop?
- 02What does the DSCP field do, and why does it rarely work end-to-end on the public Internet?
- 03How does traceroute reveal the path using TTL?
The IPv4 header packs 20 bytes of routing machinery into every packet. TTL (hop limit) prevents routing loops by decrementing at each router and triggering a discard at zero. DSCP marks QoS class; ECN signals congestion without dropping. The Protocol field routes the payload to TCP, UDP, or ICMP. The header checksum covers only the header and must be recomputed at every hop because TTL changes — which is exactly why IPv6 eliminated it, relying on L2 and L4 for integrity. IPv6 also fixed the header to 40 bytes, banned router fragmentation, and added a 20-bit Flow Label for ECMP consistency.
appears again in47
- Federation and lookahead: batching beyond DataLoadermiddle
- Senior GraphQL API: scheduling contract, tenant isolation, observabilitysenior
- Invalidation, dirty bits, and containmiddle
- Compositor layers: promotion, overlap, and GPU memorymiddle
- Production observability: LoAF, INP, and the full attack surfacesenior
- Hidden classes, transition trees, and memory layoutmiddle
- V8 in production: isolates, pointer compression, and real failuressenior
- What workers are and why they existjunior
- Web worker mechanics: dedicated, shared, and OffscreenCanvasmiddle
- Structured clone and transferablesmiddle
- SharedArrayBuffer, Atomics, and cross-origin isolationsenior
- Worker pools, Comlink, and production observabilitysenior
- Eight layers traced: from the service worker to the second navigationmiddle
- Five canonical breaks: where production reliably diessenior
- The three-track method: reading traces and building a monitored systemsenior
- Lock and single-flight: bounding concurrent rebuildsmiddle
- Stale-while-revalidate and CDN request coalescingmiddle
- Detecting stampedes and designing TTL for productionmiddle
- Metastable failure, fencing tokens, and production postmortemssenior
- What a relation is: tables, rows, keys, and constraintsjunior
- Constraints, keys, and Postgres data typesmiddle
- JSONB, arrays, and when a side table winsmiddle
- Schema integrity: deferral, versioning, and production failure modessenior
- Where data fetching happens — and why it decides LCPjunior
- React Server Components and Suspense streamingmiddle
- Senior internals: RSC payload, caching layers, and production failure modessenior
- What is OpenTelemetry: API, SDK, Collector, OTLPjunior
- OTel signals, Semantic Conventions, and the OTLP wire formatmiddle
- The OTel Collector: receivers, processors, exporters, and deployment patternsmiddle
- Vendor neutrality, eBPF instrumentation, the Operator, and browser/serverless OTelsenior
- Operating the OTel Collector: reliability, version skew, failure modes, and governancesenior
- What is trace propagation and why broken propagation is worse than nonejunior
- traceparent and tracestate: the W3C header format in fullmiddle
- Baggage and async boundaries: carrying context across queues and callbacksmiddle
- Async context per language, service mesh, B3 migration, and securitysenior
- Production propagation failures, span links, and platform designsenior
- The debugging funnel: SLO → RED → trace → profilejunior
- OTel architecture: one SDK, four signals, one wire formatmiddle
- The incident loop: from pager to postmortem to preventionmiddle
- Scale, security, and the ROI of observable systemssenior
- At-most-once, at-least-once, exactly-once: the three delivery contractsjunior
- Consumer-side dedup: the cheapest path to exactly-once processingmiddle
- Exactly-once in production: impossibility proof, hybrid patterns, and real incidentssenior
- What OAuth is and why passwords are not the answerjunior
- Authorization code flow with PKCEmiddle
- Sender-constrained tokens: DPoP and mTLSsenior
- OAuth in production: audience attacks, observability, and real failuressenior