There are four RFCs to read regarding the IPv4 checksum calculation:
RFC 791, RFC 1071, RFC 1141, and RFC 1624.
I did not read them and until I bump into a really weird problem, I do not intend to do so. But there is also another great page talking about IPv4's checksum field: Wikipedia's IPv4 Header Checksum. Following the example on Wikipedia, here goes my attempt to calculate the checksum:
Step 1. Calculate the one's complement sum of all the IPv4 header's fields:
We can add all these numbers either in hex, or in binary. I will do both methods:
Step 1a.1: I will add the first two fields (4500 + 062A). I will then add the third field to the result of the previous addition (4B2A+42A1). From there on, I will be adding the next field's value to the accumulated sum.
1 1111 1 1
4500 4B2A 8DCB 10DCC 14FDC 21084 21085 2D12D
062A 42A1 8001 4210 C0A8 0001 C0A8 0003
----- ----- ---- ----- ----- ----- ----- -----
4B2A 8DCB 10DCC 14FDC 21084 21085 2D12D 2D130
Step 1b.1
In octave:
-------------------
octave:14> hex2dec("4500")+hex2dec("062a")+hex2dec("42a1")+hex2dec("8001")+hex2dec("4210")+hex2dec("c0a8")+hex2dec("0001")+hex2dec("c0a8")+hex2dec("0003")
ans = 184624
octave:15>
octave:15> dec2hex(184624)
ans = 2D130
octave:16>
Step 1a.2: The addition of step 1a.1 is a simple mathematical addition of a bunch of numbers. In the one's complement addition, on the other hand, we need to do one more thing. Since the result must fit within 16 bits (meaning that the result should be 4 hex digits), then this means we must do with the most significant digit of the result. The "2" in 0x2D130 must go somewhere, because the one's complement addition must have the same length as all the numbers we added. In one's complement addition, we add the overflowing number back into the number. So, 0xD130+ 0x2 = 0xD132
The one's complement addition of the header fields then is: 0xD132
.
Step 1b.2:
In octave:
-------------------
octave:14> hex2dec("4500")+hex2dec("062a")+hex2dec("42a1")+hex2dec("8001")+hex2dec("4210")+hex2dec("c0a8")+hex2dec("0001")+hex2dec("c0a8")+hex2dec("0003")
ans = 184624
octave:15> dec2hex(184624)
ans = 2D130
octave:16> 16^4
ans = 65536
octave:17> 184624-(2*65536)
ans = 53552
octave:18> 184624-(2*65536)+2
ans = 53554
octave:19> dec2hex(184624-(2*65536)+2)
ans = D132
octave:20>
Step 1c.1:
Convert the fields into binary:
4500: 0100 0101 0000 0000
062A: 0000 0110 0010 1010
42A1: 0100 0010 1010 0001
8001: 1000 0000 0000 0001
4210: 0100 0010 0001 0000
C0A8: 1100 0000 1010 1000
0001: 0000 0000 0000 0001
C0A8: 1100 0000 1010 1000
0003: 0000 0000 0000 0011
4500+062A:
00 0100 0101 0000 0000+
00 0000 0110 0010 1010
-----------------------
00 0100 1011 0010 1010 (04B2A)
+42A1
00 0100 1011 0010 1010+
00 0100 0010 1010 0001
-----------------------
00 1000 1101 1100 1011 (08DCB)
+8001
00 1000 1101 1100 1011+
00 1000 0000 0000 0001
------------------------
01 0000 1101 1100 1100 (10DCC)
+4210
01 0000 1101 1100 1100+
00 0100 0010 0001 0000
------------------------
01 0100 1111 1101 1100 (14FDC)
+C0A8
01 0100 1111 1101 1100+
00 1100 0000 1010 1000
-----------------------
10 0001 0000 1000 0100 (21084)
+0001
10 0001 0000 1000 0100+
00 0000 0000 0000 0001
-----------------------
10 0001 0000 1000 0101 (21085)
+C0A8
10 0001 0000 1000 0101+
00 1100 0000 1010 1000
-----------------------
10 1101 0001 0010 1101 (2D12D)
+0003
10 1101 0001 0010 1101+
00 0000 0000 0000 0011
-----------------------
10 1101 0001 0011 0000 (2D130)
Step 1c.2:
Add 10 (the left most bits) to the number:
1101 0001 0011 0000+
0000 0000 0000 0010
--------------------
1101 0001 0011 0010 (D132)
Step 2.
Regardless of how you did the one's complement addition, you must now take the one's complement of the result. The one's complement of any binary number is just a fancy name for "flip all the bits in the number":
1101 0001 0011 0010 -> 0010 1110 1100 1101 (2ECD)
If you want to take the one's complement of a hex digit without converting to binary, below is a handy table:
n |
1' |
0 |
F |
1 |
E |
2 |
D |
3 |
C |
4 |
B |
5 |
A |
6 |
9 |
7 |
8 |
8 |
7 |
9 |
6 |
A |
5 |
B |
4 |
C |
3 |
D |
2 |
E |
1 |
F |
0 |
So taking the one's complement of D132 is:
D->2
1->E
3->C
2->D
So the checksum of the IPv4 header 4500 062A 42A1 8001 4210 XXXX C0A8 0001 C0A8 0003
is 0x2ECD
.
A small go program that will demonstrate the steps:
https://go.dev/play/p/DOj28mjuqtP