Loading learning content...
A single bit flip in an IP header can cause catastrophic misrouting. Imagine a packet destined for 192.168.1.100 where a random bit error changes the destination to 192.168.1.228. The packet arrives at the wrong host, potentially exposing sensitive data, corrupting files, or triggering security incidents. In financial systems, healthcare, or industrial control, such corruption could have severe consequences.
Network hardware is remarkably reliable, but not perfect. Cosmic rays, electromagnetic interference, faulty memory chips, and aging cables all contribute to occasional bit errors. At Internet scale—with trillions of packets traversing networks daily—even a minuscule error rate produces significant absolute numbers of corrupted packets.
The Header Checksum is IPv4's defense against this threat. By encoding a mathematical summary of the header contents, the checksum enables every router and endpoint to verify that the header arrived intact. Any corruption—even a single bit—will cause checksum validation to fail, triggering packet discard rather than misdelivery.
As a Principal Engineer, you must understand the checksum's calculation method, why routers recalculate it at every hop, how modern hardware accelerates this critical operation, and what the checksum does (and doesn't) protect against.
By the end of this page, you will understand the one's complement checksum algorithm, step-by-step calculation, incremental update techniques, hardware offload capabilities, and the checksum's scope and limitations.
The Header Checksum occupies 16 bits (2 bytes) at bytes 10-11 of the IPv4 header, immediately following the Protocol field and preceding the Source Address.
| Byte Offset | Field Name | Size | Notes |
|---|---|---|---|
| 0-1 | Version + IHL + ToS | 16 bits | Version, header length, DSCP/ECN |
| 2-3 | Total Length | 16 bits | Packet size in bytes |
| 4-5 | Identification | 16 bits | Fragment identification |
| 6-7 | Flags + Fragment Offset | 16 bits | Fragmentation control |
| 8 | Time to Live | 8 bits | Hop limit |
| 9 | Protocol | 8 bits | Upper layer protocol |
| 10-11 | Header Checksum | 16 bits | Header integrity verification |
| 12-15 | Source Address | 32 bits | Sender's IP address |
| 16-19 | Destination Address | 32 bits | Recipient's IP address |
| 20+ | Options (if IHL > 5) | Variable | Optional headers |
Critical Scope Limitation:
The IPv4 Header Checksum protects only the IP header—not the payload. This is a crucial point:
This design was intentional:
The header checksum cannot detect payload corruption. A packet with corrupted data but intact header will pass IP checksum validation. This is why transport protocols (TCP, UDP, SCTP) include their own checksums covering the payload.
IPv4 uses a 16-bit one's complement checksum algorithm. Understanding this algorithm is essential because the same method is used for TCP, UDP, ICMP, and other Internet protocols.
One's Complement Arithmetic:
One's complement is a number representation system where:
In one's complement addition, carries out of the highest bit are added back to the lowest bit (end-around carry).
Why One's Complement?
One's complement checksums have useful properties:
Byte-order independent: The checksum of bytes in any order produces the same final result (after the complement). This matters because different systems use big-endian and little-endian byte orders.
Incremental updates: Changing a field allows direct checksum adjustment without full recalculation.
Simple verification: If you sum all header words (including checksum), the result is all ones (0xFFFF) for a valid header.
12345678910111213141516171819202122232425262728
# One's Complement Arithmetic Examples # Addition with end-around carry:# Add 0xFFF0 + 0x0020 1111 1111 1111 0000 (0xFFF0) + 0000 0000 0010 0000 (0x0020) ----------------------- 1 0000 0000 0001 0000 (0x10010, 17 bits) ^ carry bit # Wrap the carry around: 0000 0000 0001 0000 (0x0010) + 0000 0000 0000 0001 (0x0001, the carry) ----------------------- 0000 0000 0001 0001 (0x0011) # Result: 0xFFF0 + 0x0020 = 0x0011 (in one's complement) # One's Complement of a number:# Invert all bits (NOT operation) 0xABCD in binary: 1010 1011 1100 1101One's complement: 0101 0100 0011 0010 = 0x5432 # Verification property:# If checksum is correct, sum of all 16-bit words = 0xFFFF# This is because: value + one's_complement(value) = 0xFFFFLet's calculate the header checksum for a real IPv4 packet. The algorithm is specified in RFC 791.
Algorithm Overview:
12345678910111213141516171819202122232425262728293031323334353637383940
# Example: Calculate checksum for this IPv4 header # Raw header bytes (checksum field at bytes 10-11, set to 0x0000):# 45 00 00 3c 1c 46 40 00 40 06 00 00 c0 a8 00 01 c0 a8 00 c7# ^^^^^ checksum (zeroed) # Step 1: Split header into 16-bit wordsWord 0: 0x4500 (Version=4, IHL=5, ToS=0)Word 1: 0x003C (Total Length = 60 bytes)Word 2: 0x1C46 (Identification)Word 3: 0x4000 (Flags + Fragment Offset, DF set)Word 4: 0x4006 (TTL=64, Protocol=6 TCP)Word 5: 0x0000 (Checksum, set to 0 for calculation)Word 6: 0xC0A8 (Source IP bytes 1-2: 192.168)Word 7: 0x0001 (Source IP bytes 3-4: 0.1)Word 8: 0xC0A8 (Dest IP bytes 1-2: 192.168)Word 9: 0x00C7 (Dest IP bytes 3-4: 0.199) # Step 2: Sum all words 0x4500+ 0x003C = 0x453C+ 0x1C46 = 0x6182+ 0x4000 = 0xA182+ 0x4006 = 0xE188+ 0x0000 = 0xE188+ 0xC0A8 = 0x1A230 (carry!) = 0xA231 (add the carry back)+ 0x0001 = 0xA232+ 0xC0A8 = 0x162DA (carry!) = 0x62DB (add the carry back)+ 0x00C7 = 0x63A2 # Step 3: One's complement (invert all bits)Sum: 0x63A2 = 0110 0011 1010 0010Inverted: 0x9C5D = 1001 1100 0101 1101 # Result: Checksum = 0x9C5D # Final header with checksum:# 45 00 00 3c 1c 46 40 00 40 06 9c 5d c0 a8 00 01 c0 a8 00 c7Verification Process:
When receiving a packet, the verification is elegantly simple:
Why does this work? Because:
1234567891011121314151617
# Verification of header with checksum 0x9C5D # Sum all words INCLUDING the checksum: 0x4500 + 0x003C + 0x1C46 + 0x4000 + 0x4006 + 0x9C5D + # Now including the checksum! 0xC0A8 + 0x0001 + 0xC0A8 + 0x00C7 # Running sum:0x4500 + 0x003C = 0x453C...continuing...+ 0x9C5D = 0xFFFF (after processing all words) # Result = 0xFFFF means header is VALID # If any bit was corrupted, result ≠ 0xFFFF# Example: If destination changed from 0x00C7 to 0x00C6# Final sum would be 0xFFFE, indicating corruptionIn practice, receivers simply sum all header words. A result of 0xFFFF means valid; any other value means corrupted. There's no need to separately extract, zero, and compare the checksum field.
Let's examine practical implementations of the checksum algorithm in different programming languages.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
#!/usr/bin/env python3"""IPv4 Header Checksum ImplementationDemonstrates both calculation and verification""" def calculate_checksum(header_bytes: bytes) -> int: """ Calculate IPv4 header checksum. Assumes checksum field (bytes 10-11) is zeroed. """ if len(header_bytes) % 2 == 1: header_bytes += b'\x00' # Pad to even length checksum = 0 # Sum all 16-bit words for i in range(0, len(header_bytes), 2): word = (header_bytes[i] << 8) + header_bytes[i + 1] checksum += word # Handle carry (fold 32-bit to 16-bit) if checksum > 0xFFFF: checksum = (checksum & 0xFFFF) + (checksum >> 16) # Final fold for any remaining carry while checksum > 0xFFFF: checksum = (checksum & 0xFFFF) + (checksum >> 16) # One's complement return ~checksum & 0xFFFF def verify_checksum(header_bytes: bytes) -> bool: """ Verify IPv4 header checksum. Returns True if valid, False if corrupted. """ if len(header_bytes) % 2 == 1: header_bytes += b'\x00' checksum = 0 for i in range(0, len(header_bytes), 2): word = (header_bytes[i] << 8) + header_bytes[i + 1] checksum += word if checksum > 0xFFFF: checksum = (checksum & 0xFFFF) + (checksum >> 16) while checksum > 0xFFFF: checksum = (checksum & 0xFFFF) + (checksum >> 16) # Valid checksum results in 0xFFFF return checksum == 0xFFFF # Example usageif __name__ == "__main__": # Sample header with checksum zeroed header_no_checksum = bytes([ 0x45, 0x00, 0x00, 0x3c, # Version, IHL, ToS, Length 0x1c, 0x46, 0x40, 0x00, # ID, Flags, Offset 0x40, 0x06, 0x00, 0x00, # TTL, Protocol, Checksum (zeroed) 0xc0, 0xa8, 0x00, 0x01, # Source: 192.168.0.1 0xc0, 0xa8, 0x00, 0xc7, # Dest: 192.168.0.199 ]) checksum = calculate_checksum(header_no_checksum) print(f"Calculated checksum: 0x{checksum:04X}") # Create header with checksum header_with_checksum = bytearray(header_no_checksum) header_with_checksum[10] = (checksum >> 8) & 0xFF header_with_checksum[11] = checksum & 0xFF is_valid = verify_checksum(bytes(header_with_checksum)) print(f"Verification: {'VALID' if is_valid else 'INVALID'}")At every hop, routers decrement TTL and must recalculate the checksum. Recalculating from scratch for every packet would waste CPU cycles on a simple, predictable change. RFC 1141 defines an incremental update technique that adjusts the existing checksum based on what changed.
The Mathematical Basis:
Let HC be the old checksum, and we're changing a 16-bit word from m to m'. The new checksum HC' is:
HC' = ~(~HC + ~m + m')
In simpler terms:
For TTL specifically, since TTL decreases by 1 each hop, the adjustment is constant:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
/* * Incremental checksum update for TTL decrement * Based on RFC 1141 */ #include <stdint.h>#include <arpa/inet.h> /* for htons/ntohs */ struct ip_header { uint8_t version_ihl; uint8_t tos; uint16_t total_length; uint16_t identification; uint16_t flags_fragment; uint8_t ttl; uint8_t protocol; uint16_t checksum; uint32_t src_addr; uint32_t dst_addr;}; /** * Decrement TTL and update checksum incrementally * This is the fast path used by routers */void decrement_ttl_update_checksum(struct ip_header *iph) { uint32_t sum; /* Decrement TTL */ iph->ttl--; /* * TTL is in the high byte of a 16-bit word (TTL | Protocol). * Decrementing TTL by 1 = subtracting 0x0100 from that word. * * In one's complement addition, subtracting X is same as * adding ~X, and for 0x0100, ~0x0100 = 0xFEFF. * * But there's a simpler approach: * Checksum was computed as ~(sum of all words). * If we decrease a word by 1 (in high byte), we need to * ADD that difference to the new sum, then re-complement. * * Shortcut: Just add 0x0100 to the existing checksum. */ /* Convert checksum to host byte order */ sum = ntohs(iph->checksum); /* Add 0x0100 (equivalent to subtracting 1 from TTL byte) */ sum += 0x0100; /* Handle carry */ if (sum > 0xFFFF) { sum = (sum & 0xFFFF) + 1; } /* Convert back to network byte order */ iph->checksum = htons(sum);} /* * General incremental update * Useful for NAT which changes addresses */uint16_t update_checksum(uint16_t old_checksum, uint16_t old_value, uint16_t new_value) { uint32_t sum; /* HC' = ~(~HC + ~m + m') */ sum = ~ntohs(old_checksum) & 0xFFFF; sum += ~old_value & 0xFFFF; sum += new_value; /* Fold carries */ while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } return htons(~sum & 0xFFFF);} /* * NAT example: Update checksum when changing source IP */void nat_update_ip_checksum(struct ip_header *iph, uint32_t new_src_addr) { uint16_t old_high = iph->src_addr >> 16; uint16_t old_low = iph->src_addr & 0xFFFF; uint16_t new_high = new_src_addr >> 16; uint16_t new_low = new_src_addr & 0xFFFF; /* Update for high 16 bits */ iph->checksum = update_checksum(iph->checksum, old_high, new_high); /* Update for low 16 bits */ iph->checksum = update_checksum(iph->checksum, old_low, new_low); /* Finally, update the actual address */ iph->src_addr = new_src_addr;}A router forwarding millions of packets per second cannot afford to recalculate checksums from scratch. The incremental update for TTL decrement involves just: one 16-bit addition, one carry check, and one conditional add. This can be done in 3-4 CPU instructions or a single cycle in hardware.
Modern Network Interface Cards (NICs) can compute checksums in hardware, freeing the CPU for other work. This capability, called checksum offload, has become essential for achieving high throughput on modern networks.
Types of Checksum Offload:
| Type | Direction | Description |
|---|---|---|
| TX IP Checksum | Transmit | NIC calculates IP header checksum for outgoing packets |
| TX TCP Checksum | Transmit | NIC calculates TCP checksum including pseudo-header |
| TX UDP Checksum | Transmit | NIC calculates UDP checksum including pseudo-header |
| RX IP Checksum | Receive | NIC verifies IP header checksum, reports result |
| RX TCP Checksum | Receive | NIC verifies TCP checksum, reports result |
| RX UDP Checksum | Receive | NIC verifies UDP checksum, reports result |
1234567891011121314151617181920212223242526272829
# View current checksum offload statusethtool -k eth0 | grep checksum# rx-checksumming: on# tx-checksumming: on# tx-checksum-ipv4: on# tx-checksum-ip-generic: off [fixed]# tx-checksum-ipv6: on# tx-checksum-fcoe-crc: off [fixed]# tx-checksum-sctp: off [fixed] # Disable TX checksum offload (for debugging/testing)sudo ethtool -K eth0 tx-checksum-ipv4 off # Re-enable TX checksum offloadsudo ethtool -K eth0 tx-checksum-ipv4 on # View all offload featuresethtool -k eth0 # Common offload features:# - rx-checksumming: Verify incoming packet checksums in hardware# - tx-checksumming: Calculate outgoing packet checksums in hardware# - scatter-gather: Allow non-contiguous memory for packets# - tcp-segmentation-offload (TSO): Hardware TCP segmentation# - generic-receive-offload (GRO): Combine received packets # Check if interface supports hardware offloadethtool -i eth0# Shows driver name and capabilitiesPacket Capture Implications:
When checksum offload is enabled, packet capture tools may show incorrect checksums. This is because:
In Wireshark, go to Edit > Preferences > Protocols > IPv4, and uncheck 'Validate checksum if possible'. Do the same for TCP and UDP. This prevents false-positive checksum error warnings when offload is enabled.
The IPv4 header checksum is intentionally simple. This simplicity enables fast processing but comes with inherent limitations.
| Error Type | Detection | Notes |
|---|---|---|
| Single-bit error | Always detected | Any single bit flip changes checksum |
| Odd number of bit errors | Always detected | Parity changes, checksum changes |
| Two-bit error (same position, different words) | May miss | Errors may cancel out |
| Transposed 16-bit words | Not detected | Checksum is order-independent within 16-bit boundaries |
| Burst errors (adjacent bits) | Usually detected | Most burst errors affect checksum |
| Malicious modification | Not detected if checksum updated | No cryptographic protection |
Key Limitations:
No Payload Protection: The checksum covers only the 20-60 byte header, not the potentially-thousands-of-bytes payload.
No Authentication: Anyone can modify the header and recalculate a valid checksum. The checksum provides no protection against intentional tampering.
Error Detection, Not Correction: The checksum tells you corruption occurred but cannot identify or fix the corrupted bits.
Weak Against Certain Errors: Some specific error patterns (byte swaps within 16-bit boundaries) go undetected.
IPv6 Omitted It: IPv6 has no header checksum, relying instead on link-layer CRCs and transport-layer checksums.
IPv6 eliminated the header checksum for performance: recalculating at every hop was deemed unnecessary given reliable modern link layers. IPv6 mandates transport-layer checksums (UDP checksum is required, not optional). This design pushes error detection to endpoints where it's most needed.
The Header Checksum is IPv4's header integrity mechanism, enabling detection of corruption during transmission. Let's consolidate the key concepts:
What's Next:
Now that you understand the Header Checksum, the final page in this module explores the Options field—the variable-length tail of the IPv4 header that provides extended functionality for routing control, timestamps, and security. While rarely used in modern networks, understanding options is essential for analyzing legacy systems and security vulnerabilities.
You now have a comprehensive understanding of the IPv4 Header Checksum—its algorithm, calculation, incremental update techniques, hardware offload, and limitations. This knowledge is essential for network programming, packet analysis, and understanding why modern protocols have evolved to use different integrity mechanisms.