IPv4 and IPv6 Header Checksum Algorithm Explained

About the IP packet header checksum algorithm, simply put, it is 16-bit ones' complement of the ones' complement sum of all 16-bit words in the header. However, not many sources show exactly how this is done. The same checksum algorithm is used by TCP segment and UDP datagram, but the data involved in the checksum computing is different from that in the IP header. In addition, the checksum operation of the IPv6 packet is different from that of IPv4. Therefore, it is necessary to make a comprehensive analysis of the checksum algorithm of IP packets.

Nothing in life is to be feared, it is only to be understood.
Marie Curie (Polish and naturalized-French physicist and chemist, twice Nobel Prize winner)

IPv4 Header Checksum

IPv4 packet header format can be seen below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Here the 16-bit Header Checksum field is used for error-checking of the IPv4 header. While computing the IPv4 header checksum, the sender first clears the checksum field to zero, then calculates the sum of each 16-bit value within the header. The sum is saved in a 32-bit value. If the total number of bytes is odd, the last byte is added separately.

After all additions, the higher 16 bits saving the carry is added to the lower 16 bits. Repeat this till all higher 16 bits are zeros. Finally, the sender takes the ones' complement of the lower 16 bits of the result and writes it to the IP header checksum field.

The following demonstrates the entire calculation process using actual captured IPv4 packets.

1
2
3
4
0x0000: 00 60 47 41 11 c9 00 09 6b 7a 5b 3b 08 00 45 00 
0x0010: 00 1c 74 68 00 00 80 11 59 8f c0 a8 64 01 ab 46
0x0020: 9c e9 0f 3a 04 05 00 08 7f c5 00 00 00 00 00 00
0x0030: 00 00 00 00 00 00 00 00 00 00 00 00

At the beginning of the above 16-bit hex dump is the Ethernet frame header. The IP packet header starts from offset 0x000e, with the first byte 0x45 and the last byte 0xe9. Based on the previous description of the algorithm, we can make the following calculations:

1
2
3
4
(1) 0x4500 + 0x001c + 0x7468 + 0x0000 + 0x8011 +
0x0000 + 0xc0a8 + 0x6401 + 0xab46 + 0x9ce9 = 0x3a66d
(2) 0xa66d + 0x3 = 0xa670
(3) 0xffff - 0xa670 = 0x598f

Notice at step (1) we replace the checksum field with 0x0000. As can be seen, the calculated header checksum 0x598f is the same as the value in the captured packet. This calculating process is only used for the sender to generate the initial checksum. In practice, for the intermediate forwarding router and the final receiver, they can just sum up all header fields of the received IP packet by the same algorithm. If the result is 0xffff, the checksum verification passes.

C Program Implementation

How to program IPv4 header checksum computing? RFC 1071 (Computing the Internet Checksum) shows a reference "C" language implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
/* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
*/
register long sum = 0;

while( count > 1 ) {
/* This is the inner loop */
sum += * (unsigned short *) addr++;
count -= 2;
}

/* Add left-over byte, if any */
if ( count > 0 )
sum += * (unsigned char *) addr;

/* Fold 32-bit sum to 16 bits */
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);

checksum = ~sum;
}

In a real network connection, the source device can call the above code to generate the initial IPv4 header checksum. This checksum is then updated at each step of the routing hop because the router must decrement the Time To Live (TTL) field. RFC 1141 (Incremental Updating of the Internet Checksum) gives a reference implementation of fast checksum update:

1
2
3
4
unsigned long sum;
ipptr->ttl--; /* decrement ttl */
sum = ipptr->Checksum + 0x100; /* increment checksum high byte*/
ipptr->Checksum = (sum + (sum>>16)); /* add carry */

TCP/UDP Header Checksum

For TCP segment and UDP datagram, both have 16-bit header checksum fields used for error-checking by the destination host. The checksum computing algorithm is the same as the IP header, except for the difference of covered data. Here the checksum is calculated over the whole TCP/UDP header and the payload, plus a pseudo-header that mimics the IPv4 header as shown below:

1
2
3
4
5
6
7
8
 0      7 8     15 16    23 24    31 
+--------+--------+--------+--------+
| source address |
+--------+--------+--------+--------+
| destination address |
+--------+--------+--------+--------+
| zero |protocol| TCP/UDP length |
+--------+--------+--------+--------+

It consists of the source and destination IP addresses, the protocol number (TCP:6/UDP:17), and the total length of the TCP/UDP header and payload (in bytes). The purpose of including the pseudo-header in the checksum computing is to confirm the packet reaches the expected destination and avoid IP spoofing attacks. Besides, for IPv4 UDP header checksum is optional, it carries all-zeros if unused.

IPv6 Difference

IPv6 is IP protocol version 6, and its main design goal was to resolve the problem of IPv4 address exhaustion. Of course, it provides many benefits in other aspects. Although IPv6 usage is growing slowly, the trend is unstoppable. The latest IPv6 standard is published in RFC 8200(Internet Protocol, Version 6 (IPv6) Specification).

IPv6 packet header format can be seen below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| Traffic Class | Flow Label |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload Length | Next Header | Hop Limit |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Source Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Destination Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Notice that the IPv6 header does not include a checksum field, a significant difference from IPv4. The absence of a checksum in the IPv6 header furthers the end-to-end principle of Internet design, to simplify router processing and speed up the packet transmission. Protection for data integrity can be accomplished by error detection at the link layer or the higher-layer protocols between endpoints (such as TCP/UDP on the transport layer). This is why IPv6 forces the UDP layer to set the header checksum.

For IPv6 TCP segment and UDP datagram header checksum computing, the pseudo-header that mimics the IPv6 header is shown below

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Source Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Destination Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Upper-Layer Packet Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| zero | Next Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

UDP-Lite Application

In actual IPv6 network applications, UDP-Lite (Lightweight UDP) can be used to balance error detection and transmission efficiency. UDP-Lite has its own protocol number 136, and its standard is described in RFC 3828 (The Lightweight User Datagram Protocol (UDP-Lite)).

Referring to the following header format, UDP-Lite uses the same set of port number values assigned by the IANA for use by UDP. But it redefines the Length field in the UDP header to a Checksum Coverage, which allows the application layer to control the length of checksummed data. This is useful for the application that can be tolerant of the potentially lossy transmission of the uncovered portion of the data.

1
2
3
4
5
6
7
8
9
10
11
12
 0              15 16             31
+--------+--------+--------+--------+
| Source | Destination |
| Port | Port |
+--------+--------+--------+--------+
| Checksum | |
| Coverage | Checksum |
+--------+--------+--------+--------+
| |
: Payload :
| |
+-----------------------------------+

UDP-Lite protocol defines the values of "Checksum Coverage" (in bytes) as shown in the following table:

Checksum Coverage Coverage Area Description
0 entire UDP-Lites datagram Calculation covers IP pseudo-header
1-7 (invalid) The receiver has to drop the datagram
8 UDP-Lites header Calculation covers IP pseudo-header
> 8 UDP-Lites header + portion of payload data Calculation covers IP pseudo-header
> IP datagram length (invalid) The receiver has to drop the datagram

For multimedia applications running VoIP or streaming video data transmission protocols, it'd better receive data with some degree of corruption than not receiving any data at all. Another example is the CAPWAP protocol used to connect Cisco wireless controller and access points. It specifies UDP-Lite as the default transport protocol for the CAPWAP Data channel, while the connection is established over the IPv6 network.

At last, share a C program snippet to present how to initialize a Berkeley socket to establish an IPv6 UDP-Lite connection:

1
2
3
4
5
6
7
8
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/udplite.h>

int udplite_conn = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);
int val = 8; /* checksum only covers 8-byte UDP-Lite header */
(void)setsockopt(udplite_conn, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof val);
(void)setsockopt(udplite_conn, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, &val, sizeof val);

Here IPPROTO_UDPLITE is protocol number 136, which is used together with AF_INET6 address family parameter in socket() function call for IPv6 socket creation. The UDPLITE_SEND_CSCOV(10) and UDPLITE_RECV_CSCOV(11) are the control parameters of socket options configuration function setsockopt(), used for setting the Checksum Coverage value in the sender and the receiver respectively. Remember that both the sender and the receiver must set the same value, otherwise, the receiver will not be able to verify the checksum properly.