Networking · IP and Routing
The IPv4 Header Fields That Forwarding Rewrites
Which of the twenty header bytes a router must read, which it decrements or recomputes in place, and where the header checksum stops protecting anything.
A packet that leaves a router is not byte-identical to the packet that entered it. At minimum one byte is decremented and the header checksum changes; on many paths the source or destination address is rewritten as well. The IPv4 header is small enough to describe completely, and nearly every field in it exists because some forwarding step needed it. Reading the header field by field shows what a router is obliged to inspect, what it may rewrite, and what it must leave alone for end-to-end integrity checks to survive.
The twenty bytes a router must parse
The fixed part of the header is twenty bytes. The version field is four bits and holds the value 4. The header length field, traditionally called IHL, is four bits counting 32-bit words, so the smallest legal value is 5 and the largest is 15, which bounds the header at sixty bytes. Everything after that bound is payload.
Total Length is sixteen bits and counts the header plus the payload, not just the payload. A router derives the payload size by subtracting IHL multiplied by four from Total Length, and it must check that the subtraction does not go negative. This is the first place where malformed packets cause trouble: a Total Length smaller than the header length, or larger than the frame that carried it, describes a packet that cannot exist. Implementations drop such packets rather than attempt repair.
Two fields are easy to overlook because they are not adjacent in the layout. Identification is sixteen bits and is meaningful only when fragmentation occurs. The protocol field is eight bits and names the next header, so a router that must generate an ICMP error knows what it was carrying. The source and destination addresses are each thirty-two bits and sit at fixed offsets 12 and 16, which is why hardware can extract them with a single load of the first twenty bytes.
Ethernet adds a subtlety to the length question. A frame carrying fewer than 46 bytes of payload is padded to the minimum frame size, so the link layer knows a true length that the padding obscures. The IP Total Length is authoritative; padding after it is discarded. A router that trusted the frame length instead would hand the next hop a payload with trailing zeros.
The hop limit, decremented in place
RFC 791 defines the time-to-live field as a number of seconds, with the recommendation that each router subtract the time it spent processing. In practice every deployed implementation treats it as a hop count and subtracts exactly one per forwarding step, which is what RFC 1812 describes for routers. The field is eight bits, so the largest initial value is 255.
A router that receives a packet with a time-to-live of 1 must not forward it. It discards the packet and, unless suppressed by policy, returns ICMP Time Exceeded with code 0, quoting the header and the first eight bytes of payload. That behaviour is the loop detector for the whole protocol: a routing loop does not circulate a packet forever, it consumes the field and then generates errors at the point of expiry. A packet that enters a loop is forwarded until the field reaches zero, so a loop of length N carries it around roughly 255/N times, and every one of those traversals consumes capacity on each link of the loop. Bounded is not cheap.
The same rule is the basis of path tracing. A probe sent with time-to-live 1 expires at the first router, whose error message reveals its address; raising the value walks the path outward. Because the error is generated by the router where the packet died, not by the destination, the technique works without any cooperation from the endpoint.
The header checksum covers the header only, never the payload. A router changing one sixteen-bit word can therefore repair the checksum with two’s-complement arithmetic instead of rescanning the header. RFC 1624 gives the exact form. It records that the earlier RFC 1141 equation assumed a distributive property that one’s-complement arithmetic does not have, and so yields negative zero where a full recomputation would produce positive zero. A checker that compares against zero accepts either, so the packet still validates, but the field holds a value a recomputation would not have produced.
/* Replace the 16-bit word m with m_prime without rescanning the header.
hc is the checksum as received, in network byte order semantics. */
static uint16_t update_checksum(uint16_t hc, uint16_t m, uint16_t m_prime)
{
uint32_t sum = (uint16_t)~hc;
sum += (uint16_t)~m;
sum += m_prime;
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (uint16_t)~sum;
}
The practical consequence is architectural. A forwarding engine never needs to touch payload bytes to validate the header, so a router can begin the next-hop lookup while the packet is still arriving, and can forward a packet whose payload it has not finished receiving. That property is what makes cut-through forwarding possible at all, and it disappears the moment a middlebox needs to inspect or rewrite transport headers.
The type-of-service byte and its two successors
The original header reserved eight bits for service hints: three precedence bits, four type-of-service bits, and one that had to be zero. That encoding was replaced. RFC 2474 redefined the byte as six bits of differentiated services codepoint plus two bits that RFC 3168 assigns to explicit congestion notification.
Routers read the codepoint to choose a per-hop behaviour, which is a forwarding treatment rather than a queue. A network that honours codepoints may re-mark packets at its boundary, resetting the field to a value it trusts rather than the one the sender chose. Re-marking is a policy decision with a real cost: once a packet’s codepoint has been rewritten, no downstream device can recover the sender’s intent, and any end-to-end admission scheme that relied on it breaks. The alternative, trusting the sender’s marking, lets any host claim the best treatment for its own traffic, which is why boundaries usually re-mark rather than trust.
The congestion notification bits work differently. A sender marks a packet as capable of notification; a router experiencing congestion sets the congestion-experienced bit instead of dropping the packet, and the receiver echoes the signal back in the transport header. The value of the scheme is that the signal arrives without a retransmission, so the sender can reduce its rate before loss occurs. The failure mode is equally specific: any device that clears the bits, or a path where the sender never sets them, reduces the mechanism to ordinary loss signalling.
Fragmentation fields and the decision to split
Identification, three flag bits, and a thirteen-bit fragment offset share the middle of the header. The offset counts eight-byte units, which caps it at 65528 and matches the 65535-byte ceiling on Total Length. When a router splits a packet, it copies the header into each piece, sets the more-fragments flag on every piece except the last, sets the offset of each piece to its position in the original payload, and adjusts Total Length. The identification field is what lets the destination group the pieces back together, so it must remain constant across all fragments of one datagram.
Routers fragment only when the outgoing link’s maximum transmission unit is smaller than the packet and the don’t-fragment bit is clear. With that bit set, the router cannot split the packet and must instead return ICMP Destination Unreachable with code 4, carrying the next-hop MTU in the unused header field. A sender that ignores the message keeps retrying a packet that can never pass, which is the mechanism behind the black holes described in fragmentation and path MTU discovery.
The eight-byte granularity has a consequence worth stating plainly: only the final fragment may have a payload length that is not a multiple of eight. A reassembling host uses that to detect a malformed fragment set, and an attacker uses overlapping offsets to make different hosts reconstruct different payloads from the same fragments. Modern stacks treat overlaps as an attack rather than a curiosity.
Addresses, and the checksums that depend on them
Rewriting an address is not a local edit. The transport pseudo-header used by TCP and UDP checksums includes both addresses and the protocol number, so a middlebox that translates addresses must also translate transport checksums. UDP adds a wrinkle: in IPv4 a checksum field of zero means the sender computed none, so there is nothing to update and the translated packet carries the same absent checksum. A device that must produce a checksum where none existed, which is the case for any translation into IPv6 where the field is mandatory, has to compute one over the whole payload rather than adjust an existing value.
IPsec’s authentication header covers the IP header fields that are declared immutable, and both addresses are among them. A router decrementing the hop limit does not break the value, because that field is excluded from the computation, but a device that rewrites an address does. That asymmetry is why the header is workable on ordinary routed paths and unworkable across address translation.
ICMP errors quote the offending packet’s header and the first eight bytes of its payload. A translating device must rewrite the quoted copy as consistently as it rewrote the original, or the sender will fail to match the error to any flow it knows about. Mismatched quoting is a common reason a path MTU discovery message is silently discarded.
Options and the forty bytes that go unused
Options occupy at most forty bytes, the difference between the fifteen-word maximum and the five-word minimum. Record route, timestamp, and loose or strict source routing were specified there. Source routing lets the sender list the addresses a packet must traverse, which requires every router on the path to parse and act on a variable-length option list. That is slow in hardware, because the option length is not known until the header is parsed, and it is a security problem, because the listed addresses can direct return traffic toward a third party.
The result is that most forwarding paths treat a packet with options as an exception. It is punted to a slower processor, dropped, or answered with ICMP Parameter Problem, and operators commonly filter option-bearing packets at boundaries. The header reserved room for extensibility and the forwarding fast path spent the next decades ignoring it, which is one of the lessons that shaped the IPv6 design discussed in IPv6 forwarding and scoped addresses.
What the header has no room to express
There is no label stack, no flow identifier, and no field that identifies a tenant, a class of service beyond six bits, or a path to take beyond the deprecated option. Every mechanism built on top of IPv4 that needs such a field either encapsulates the packet or overloads an existing field and accepts the consequences. Multiprotocol label switching pushes a separate shim header. Tunnels add an outer header whose addresses mean something only to the endpoints. Policy routing, described in policy routing with multiple tables, works around the absence of a selector by keying the lookup on fields the header does carry.
The header is therefore best read as a record of what the original designers thought forwarding needed: an address pair, a loop bound, a length, a protocol tag, and a checksum over the part that changes. Everything a modern network does beyond that is built in the gap between the format and the requirements.