IPv4 Protocol

On this page

A 4000 B IPv4 datagram encountering a 1500 B egress MTU may be split into three fragments or dropped with an error feedback triggered; the key factors are the DF (Don't Fragment) bit, the path, and endpoint handling. IPv4 provides best-effort datagram delivery; the address, length, TTL, and fragmentation fields jointly determine how this specific packet is forwarded, while reliability, ordering, and congestion recovery require corresponding upper-layer mechanisms.

Question to ExploreEntry Point in This ArticleMost Common Calculation Error
How to distinguish header length from packet lengthField-by-field analysis and checksumIHL × 4, Total Length includes header
Which route to choose when multiple matchCIDR and next hopLongest prefix match, policy, and neighbor resolution
Why is the fragment offset 185?Complete fragmentation exampleIP fragment offset is in 8-byte units
Can the packet be delivered upon receiving the last fragment?Reassembly conditionsTotal length, gaps, overlaps, and timeouts
Small packets pass, large packets failPMTUDSize-related phenomena and causal evidence
How do DSCP and ECN coexist?Service and congestion markingSix-bit DSCP and two-bit ECN are independent

Addresses and Routing: 32-bit Addresses Are Not Direct Physical Locations

CIDR prefixes divide the address into high-order bits used for route aggregation and the remaining bits. The network to which 192.0.2.130/26 belongs is 192.0.2.128/26, with a mask of 255.255.255.192; under standard broadcast subnet semantics, the address range extends to 192.0.2.191, where network and broadcast addresses serve different purposes than ordinary host addresses. One cannot generalize the "subtract two per subnet" rule to /31 point-to-point links or /32 host routes.

In a standard destination routing table, packets matching 198.51.100.0/24 will preferentially use that route rather than the equally matching 0.0.0.0/0. Policy routing, VRFs, and multipath can add context to the selection, but ARP does not replace routing decisions: only after the route selects the egress interface/next hop is neighbor mapping for that link processed.

Addresses do not provide sender authentication; NAT can rewrite addresses and related fields, and this does not transform IPv4 into a reliable transport protocol. The 32-bit address space does not equate to all publicly allocatable addresses, and the exhaustion of historical allocation pools does not mean that all regions, institutions, and use cases lost access to any address on the same day.

Understanding the Basic Header Field by Field

IPv4 Basic Header: 32 bits per row, 20 B total; field widths proportional to bits V=4 IHL DSCP ECN Total Length Identification Flags Fragment Offset TTL Protocol Header Checksum Source Address Destination Address IHL is in 4-byte units; when IHL > 5, options and necessary padding follow Diagram is arranged left-to-right in network byte order; code parsing Flags/Offset should use masks like 0x4000, 0x2000, and 0x1fff.
FieldSemanticsReading Boundaries
Version / IHLIPv4; header length is IHL×4 BMinimum 20 B, maximum 60 B; cannot be less than 5
Total LengthHeader plus data for this IP packetMaximum 65535 B, excluding Ethernet padding/FCS
IdentificationProvides an identifier for datagrams requiring reassemblyNot a globally unique sequence number for all IPv4 packets
Flags / Fragment OffsetDon't Fragment, More Fragments, and data positionOffset does not start from the Ethernet header or IP header
TTLLimits forwarding lifetimeDecrements at least once per route hop; cannot precisely determine the system based on initial value alone
ProtocolIndicates upper-layer protocol, e.g., TCP 6, UDP 17Not a port number
Header ChecksumChecksum for the current IPv4 headerDoes not cover IP payload, nor authenticate the source

When parsing, first confirm that the actual available bytes are sufficient, then verify the relationship between Version, IHL, and Total Length; extra padding received from Ethernet should not be counted as IP data. IP Options have their own formats and copy attributes; do not assume every fragment simply copies an identical header. RFC 791: IPv4 Format and Fragmentation

Why TTL and Header Checksum Are Per-Hop Relevant

Routers update the TTL when forwarding; since the header changes, the checksum must also be updated; the source and destination remain unchanged in standard non-NAT forwarding, which does not mean the entire IP header remains unchanged. When TTL expires, the packet is dropped, and ICMP Time Exceeded messages are generated under protocol-allowed and resource/policy conditions; error feedback has exceptions and rate limiting, so one cannot require visible ICMP for every drop.

The IPv4 header checksum uses one's complement addition on 16-bit words, followed by inversion, with carries wrapped back to the lower 16 bits. When verifying the complete header, the one's complement sum should be all 1s, or equivalently, the "inverted result" is zero. It only protects the IPv4 header; TCP/UDP checksums have separate pseudo-headers and payload coverage rules, and IPv4 UDP has specific semantics for zero checksums; one cannot claim that all upper layers provide the same guarantee.

The code at the end constructs a complete header, verifies the checksum, changes the TTL from 64 to 63, confirms the old checksum is no longer valid, and recalculates it. This illustrates field dependencies better than simply remembering "routers decrement by one per hop."

Why does decrementing TTL invalidate the checksum?

Gold marks the TTL/Protocol word and blue the checksum word. Retaining the original value reveals exactly what recomputation repairs. The payload toggle deliberately leaves the header untouched to expose the coverage boundary.

Preparing the visual
Why does decrementing TTL invalidate the checksum?

The IPv4 checksum covers the header. TTL changes invalidate it; payload changes do not. Header validity proves neither payload integrity nor identity.

Fragmentation: Aligned by Data Length, Not Total Fragment Length

Assume the original datagram Total Length=4000, IHL=5, no options, then the IP data is 3980 B. Egress MTU=1500, each fragment must reserve at least 20 B for the header; the data length of non-final fragments must be a multiple of 8 B, taking the maximum floor((1500−20)/8)×8=1480 B.

4000 B IP datagram passing through MTU 1500: fragmenting 3980 B of data Original Packet: 20 B IP Header + 3980 B IP Data Data indices 0 … 3979, assuming no IP options, DF=0 Fragment 1: 1480 B Data offset=0, MF=1 Fragment 2: 1480 B Data offset=185, MF=1 Fragment 3: 1020 B Data offset=370, MF=0 Total Length = 1500 Total Length = 1500 Total Length = 1040 Offset is calculated from the original IP data part, in 8-byte units; each fragment carries its own IP header, and only the first fragment contains the transport layer start header.
FragmentOriginal Data RangeData LengthOffset FieldMFFragment Total Length
10~14791480011500
21480~2959148018511500
32960~3979102037001040

1480/8=185, 2960/8=370. Non-final fragments align the data length to 8; the total length of a 1500 B fragment is not itself a multiple of 8. Each fragment has its own Total Length, offset, MF, and checksum; the Identification field is used to link fragments belonging to the same datagram; the TCP/UDP start header is only located at the beginning of the original data and is not copied into each fragment.

DF is bit 0x4000 in the 16-bit Flags/Offset word, MF is 0x2000, and the offset mask is 0x1fff; this prevents misinterpreting the flag bit number from the left in the diagram as the least significant bit number. Setting DF does not automatically reduce data size; when the egress cannot fit it, it usually requires dropping and providing appropriate feedback.

Identification constraints must be considered in conjunction with source, destination, protocol, and reassembly time windows. RFC 6864 relaxes ID usage requirements for non-fragmented atomic datagrams, so "every IP packet must have a unique ID" is not true; high packet rates also make the limited ID space and reassembly more noteworthy. RFC 6864: IPv4 ID Semantics

Reassembly Requires Completing Intervals, Not Just Waiting for the Last Fragment

Upon receiving the last fragment (MF=0), the original IP data total length can be obtained by 8×Offset + this fragment's data length: in this example, 370×8+1020=3980 B, plus the original first fragment header length to get 4000 B. One cannot directly add the Offset value to the last fragment's Total Length, nor can one count the last fragment's IP header again into the data range.

Knowing the total length is not enough; the preceding intervals must be gap-free, and appropriate overlap, boundary, and resource checks must pass; fragments can arrive out of order. Standard IP forwarding paths do not require reassembly at every hop, but intermediate processing such as firewalls and NATs may reassemble for their functionality. If reassembly fails due to timeout or resource limits, the IP layer itself does not request retransmission of a specific fragment; whether and how the upper layer retransmits depends on the protocol.

Reassembly timeout depends on implementation and configuration; it is not a universal constant. Linux 7.2's IP_FRAG_TIME is 30×HZ and is used to initialize the configurable ipfrag_time; this is still just the baseline for that implementation and does not represent all devices on the actual network. Fragmentation also makes intermediate device processing more complex, and modern applications usually prefer to avoid it through reasonable packet sizing.

Why is the last fragment insufficient for delivery?

Deliver the last fragment first, then fill interior gaps. Button offsets are wire fields; plotted byte positions already multiply them by8. Repeating a fragment reuses existing coverage without inflating the received count.

Preparing the visual
Why is the last fragment insufficient for delivery?

MF=0 reveals the endpoint, not missing ranges. Offsets count8-byte units; duplicate delivery cannot fill another gap.

PMTUD: Error Feedback Helps Endpoints Adjust Packet Size

Classic IPv4 PMTUD allows endpoints to send DF packets; when the intermediate egress MTU is insufficient, the router drops the packet and returns ICMP Destination Unreachable Type 3 Code 4 under specified conditions, carrying MTU information. The endpoint uses this to update path estimates, and the transport/application layer adjusts subsequent packet sizing; it does not automatically clear the DF bit on the original packet and resend it. RFC 1191

If necessary ICMP is filtered, the endpoint may continue to retransmit oversized packets, creating a black hole. However, "small ping works, large ping fails" only hints at size-related issues; it could also be filtering, rate limiting, or different paths. Verification requires controlling probe size, protocol, and direction, and observing egress/ingress or ICMP evidence; one cannot draw conclusions based on a single probe.

TCP MSS advertisement constrains the amount of TCP data the peer sends, but it does not equal the total packet MTU, nor does it cover UDP. MSS clamping can alleviate certain TCP path issues, but the PMTU it relies on may be incomplete, and the path may change, so it cannot be treated as a definitive fix for all scenarios where ICMP is blocked. PLPMTUD/DPLPMTUD uses probing and acknowledgment mechanisms at the datagram layer to help discover available sizes, and their state machines must still be correctly implemented. RFC 8899: DPLPMTUD for Datagram Transport

DSCP: Markings Must Be Honored by Network Policy

The high six bits of the second byte of IPv4 are DSCP, and the low two bits are ECN; ECN is not "the low two bits of DSCP." DSCP selects per-hop forwarding behavior; whether devices trust, remark, shape, or map it to a specific queue is determined by network policy. RFC 2474: DS Field

CS classes, AF classes, and EF express different service designs. AF's different drop priorities are processing dimensions within their respective classes; one cannot sort all code points into a uniform high-low order based on numerical size; EF's common code point is decimal 46, but it does not equal "infinitely highest priority" on any device. Low-latency behavior requires configuring service rates, admission, and scheduling constraints. RFC 3246: EF PHB

For example, DSCP=46, ECN=ECT(0)=2, the complete byte is (46<<2)|2=0xBA. When modifying DSCP, one should preserve the required ECN bits and not casually overwrite the entire byte, destroying congestion signals.

ECN: Negotiation First, Then Interpret Congestion Feedback

ECN Two BitsMeaning
00Not-ECT
10ECT(0)
01ECT(1)
11CE, experienced congestion

Taking classic TCP ECN in RFC 3168 as an example, negotiation is expressed using TCP ECE/CWR flags in the SYN, and the corresponding response in the SYN-ACK, rather than by writing ECT into the initial SYN's IP header. Subsequently, appropriate data packets can be marked ECT, and network nodes supporting ECN can mark CE under corresponding congestion conditions; the receiver reports back via ACK's ECE, and the sender performs congestion response and uses CWR to indicate the response has been made. RFC 3168: Classic ECN

CE does not guarantee that packets will never be dropped from then on; queues, severe congestion, and unsupported paths can still drop packets. The sender's window response depends on the congestion algorithm used and cannot be uniformly called "halving every time"; DCTCP and others have different feedback handling. Subsequent specifications have also relaxed some experimental restrictions, so the above process should be explicitly viewed as a classic baseline, not used to cover all modern ECN variants. RFC 8311: ECN Experimental Boundary Updates

Local Construction, Fragmentation, and Checksum

import struct
from ipaddress import IPv4Address, IPv4Network

def checksum(data):
    assert len(data)%2 == 0
    total = sum(struct.unpack('!%dH' % (len(data)//2), data))
    while total >> 16:
        total = (total&0xffff)+(total>>16)
    return (~total)&0xffff

header = bytearray(struct.pack('!BBHHHBBH4s4s',
    0x45, (46<<2)|2, 4000, 0x1234, 0, 64, 17, 0,
    IPv4Address('192.0.2.10').packed, IPv4Address('198.51.100.20').packed))
header[10:12] = struct.pack('!H', checksum(header))
assert checksum(header) == 0
assert header[1] == 0xBA
header[8] = 63
assert checksum(header) != 0
header[10:12] = bytes(2)
header[10:12] = struct.pack('!H', checksum(header))
assert checksum(header) == 0

payload = bytes(i%251 for i in range(3980))
chunk = ((1500-20)//8)*8
fragments = []
for start in range(0,len(payload),chunk):
    data = payload[start:start+chunk]
    fragments.append((start//8, int(start+len(data)<len(payload)), len(data)+20, data))
assert [f[:3] for f in fragments] == [(0,1,1500),(185,1,1500),(370,0,1040)]
assert b''.join(f[3] for f in fragments) == payload
assert fragments[-1][0]*8 + len(fragments[-1][3]) + 20 == 4000
assert str(IPv4Network('192.0.2.130/26', strict=False)) == '192.0.2.128/26'
print('IPv4 header checksum, TTL update, DS byte, fragment intervals, and CIDR checks passed')

This is a local verification for a teaching datagram without options; it does not send probe traffic, modify MTU/MSS, or adjust congestion control configurations.

Continue Reading