How to calculate Internet checksum?

Viewed 103459

I have a question regarding how the Internet checksum is calculated. I couldn't find any good explanation from the book, so I ask it here.

Have a look at the following example.

The following two messages are sent: 10101001 and 00111001. The checksum is calculated with 1's complement. So far I understand. But how is the sum calculated? At first I thought it maybe is XOR, but it seems not to be the case.

              10101001
              00111001
              --------
   Sum        11100010
   Checksum:  00011101

And then when they calculate if the message arrived OK. And once again how is the sum calculated?

               10101001
               00111001
               00011101
               --------
   Sum         11111111
   Complement  00000000  means that the pattern is O.K.
5 Answers

Here's a complete example with a real header of an IPv4 packet.

In the following example, I use bc, printf and here strings to calculate the header checksum and verify it. Consequently, it should be easy to reproduce the results on Linux by copy-pasting the commands.

These are the twenty bytes of our example packet header:

45 00 00 34 5F 7C 40 00 40 06 [00 00] C0 A8 B2 14 C6 FC CE 19

The sender hasn't calculated the checksum yet. The two bytes in square brackets is where the checksum will go. The checksum's value is initially set to zero.

We can mentally split up this header as a sequence of ten 16-bit values: 0x4500, 0x0034, 0x5F7C, etc.

Let's see how the sender of the packet calculates the header checksum:

  1. Add all 16-bit values to get 0x42C87: bc <<< 'obase=16;ibase=16;4500 + 0034 + 5F7C + 4000 + 4006 + 0000 + C0A8 + B214 + C6FC + CE19'
  2. The leading digit 4 is the carry count, we add this to the rest of the number to get 0x2C8B: bc <<< 'obase=16;ibase=16;2C87 + 4'
  3. Invert¹ 0x2C8B to get the checksum: 0xD374

Finally, insert the checksum into the header:

45 00 00 34 5F 7C 40 00 40 06 [D3 74] C0 A8 B2 14 C6 FC CE 19

Now the header is ready to be sent.

The recipient of the IPv4 packet then creates the checksum of the received header in the same way:

  1. Add all 16-bit values to get 0x4FFFB: bc <<< 'obase=16;ibase=16;4500 + 0034 + 5F7C + 4000 + 4006 + D374 + C0A8 + B214 + C6FC + CE19'
  2. Again, there's a carry count so we add that to the rest to get 0xFFFF: bc <<< 'obase=16;ibase=16;FFFB + 4'
  3. If the checksum is 0xFFFF, as in our case, the IPv4 header is intact.

See the Wikipedia entry for more information.


¹Inverting the hexadecimal number means converting it to binary, flipping the bits, and converting it to hexadecimal again. You can do this online or with Bash: hex_nr=0x2C8B; hex_len=$(( ${#hex_nr} - 2 )); inverted=$(printf '%X' "$(( ~ hex_nr ))"); trunc_inverted=${inverted: -hex_len}; echo $trunc_inverted

Related