Part III — The Internet Layer (IPv4)
IP delivers a packet from any host to any other host, best-effort. No guarantees of delivery, order, timing, or (for the payload) integrity. Everything above IP exists to paper over those gaps.
10. The IPv4 address and header, field by field
10.1 The address
32 bits, written as four dotted-decimal octets: 192.168.1.50 =
11000000.10101000.00000001.00110010. Range 0.0.0.0–255.255.255.255, ~4.29
billion values — far too few, which is why NAT (Ch. 12) and IPv6 (Part IV) exist.
An address has two parts, split by the subnet mask:
192.168.1.50 / 24
|__________| |__|
address prefix length (bits of network portion)
netmask /24 = 255.255.255.0 = 11111111.11111111.11111111.00000000
network part: 192.168.1 (first 24 bits - identifies the link)
host part: .50 (last 8 bits - identifies the host on that link)
- Network address (all host bits 0):
192.168.1.0— names the subnet, not assignable to a host. - Broadcast address (all host bits 1):
192.168.1.255— reaches every host on the subnet. - Usable host range:
192.168.1.1–192.168.1.254→2^8 − 2 = 254hosts. - The "−2" does not apply to
/31links (RFC 3021, point-to-point, 2 usable) or/32(single host).
Classful addressing is dead. You may still hear "Class A/B/C" (the pre-1993 scheme where the first bits fixed the mask). Since CIDR (Classless Inter-Domain Routing, 1993), the mask is always explicit. Ignore classes except as historical vocabulary; the class-based default masks in some old tools are a trap.
10.2 The IPv4 header
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL | DSCP |ECN| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source IP Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination IP Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options (0-40 bytes, rare) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Field | Bits | Purpose / real-world relevance |
|---|---|---|
| Version | 4 | 4. |
| IHL | 4 | Header length in 32-bit words. 5 = 20 bytes (no options). |
| DSCP | 6 | QoS class (Ch. 51). EF = expedited forwarding for VoIP; CS0 = best effort. |
| ECN | 2 | Explicit Congestion Notification. 00 not-capable, 10/01 ECT, 11 = CE ("congestion experienced") — a router marks this instead of dropping. |
| Total Length | 16 | Whole packet incl. header. Max 65535; real max ≈ PMTU. |
| Identification | 16 | Same value on all fragments of one original packet (Ch. 15). Also abused for OS fingerprinting and idle scans. |
| Flags | 3 | bit0 reserved(0); DF Don't Fragment; MF More Fragments. |
| Fragment Offset | 13 | Position of this fragment in the original, in 8-byte units. |
| TTL | 8 | Decremented by 1 per hop. At 0 the packet is dropped and ICMP time-exceeded returned (this powers traceroute). Typical initial values: Linux/macOS 64, Windows 128, some routers 255. |
| Protocol | 8 | Payload type: 1 ICMP, 6 TCP, 17 UDP, 47 GRE, 50 ESP (IPsec), 89 OSPF. |
| Header Checksum | 16 | Covers the header only. Recomputed at every hop (because TTL changes). Does not protect the payload — that's TCP/UDP's checksum. |
| Source / Destination | 32 each | The endpoints. Rewritten only by NAT. |
10.3 What a router does with this header
- Verify header checksum; drop if bad.
- Decrement TTL; if 0, drop + ICMP time-exceeded.
- Longest-prefix-match the destination in the forwarding table → next hop + egress interface (Ch. 13).
- If egress MTU < packet size: fragment (DF=0) or drop + ICMP (DF=1).
- Recompute header checksum (TTL changed).
- Resolve next-hop MAC (ARP), build new Ethernet frame, transmit.
The payload is never touched (except by NAT/firew-fixups). No per-flow state.
10.4 War story
A CDN reported that a customer's origin was "unreachable from Asia" but fine from
Europe. traceroute from Asia died after 12 hops at a specific router. That
router had a bug decrementing TTL by 2; packets with initial TTL 64 that took 32+
hops arrived with TTL 0 and were dropped. Raising the origin's default TTL via
sysctl net.ipv4.ip_default_ttl=128 was the pragmatic workaround while the
carrier fixed the router. Lesson: TTL is a hop budget; unusually long paths
plus buggy hops can exhaust it.
Try it yourself
ip -4 addr show # your addresses and prefix lengths
ipcalc 192.168.1.50/24 # network, broadcast, host range
sudo tcpdump -i any -n -v 'ip[8]<5' # capture packets with TTL < 5 (near-expiry)
sysctl net.ipv4.ip_default_ttl