TCP Checksum calculation doesn't match with the wireshark calculation TCP Checksum calculation doesn't match with the wireshark calculation unix unix

TCP Checksum calculation doesn't match with the wireshark calculation


Manual Checksum Calculation

The checksum calculation for TCP/UDP/IP is rather trivial. What is called "16 bit one's complement" arithmetic is just a notion that during addition of two 16-bit numbers whatever was carried over 16 bit is added back from bit 0. E.g.

0x8000 + 0x8000 = 0x10000 => 0x1 + 0x0000 = 0x0001.

One of the properties of this arithmetic is that negative value is produced by simple binary inversion. And 0 in this arithmetic has 2 binary values: 0x0000 and 0xffff

-0x0001 = ~0x0001 = 0xfffe;0xfffe + 0x8000 + 0x8000 = 0x1fffe => 0x1 + 0xfffe = 0xffff = 0x0000

Another nice thing about 16 bit one's complement is that you don't have to worry about endianness while doing 16-bit additions, you have to properly convert only the final result. This happens because carry bit always travels from one byte to another and is never lost. Here's the same example as if data was read in little endian machine:

0x0080 + 0x0080 = 0x0100 => htons(0x0100) = 0x0001

That's why all algorithms of checksum calculation doesn't bother converting each and every 16-bit value from network to host byte order.

Considering all these, you simply break your data block into 16-bit works, add them all together the regular way, and then add higher 16 bits to the lower 16 bits and invert the result before writing it back to the packet.

In your example, TCP header checksum would be calculated as:

0x0422 + 0x0050 + 0x0001 + 0xe0dd + 0x0001 + 0x4274 + 0x5014 + 0x2238 +0x0000 + 0x0000 = 0x19a11 = 0x1 + 0x9a11 = 0x9a12^^^^^^ // <- this is the place for the TCP checksum

As described in TCP checksum calculation you need to add a pseudo header to your TCP packet so that source and destination IP addresses and ports also took part in checksum calculation. This pseudo header is different for IPv4 and IPv6. In your example for IPv6 its going to be:

0x1080 + 0xa2b1 + 0x0000 + 0x0000 + // source IPv6 address0x0000 + 0x0000 + 0x001e + 0x0000 +0xff00 + 0x0000 + 0x0000 + 0x0000 + // destination IPv6 address0x0000 + 0x0000 + 0x0000 + 0x0024 +0x0016 +                            // IP payload (TCP packet) lenght0x0006                              // Next Header value for TCP= 0x1b28f = 0x1 + 0xb28f = 0xb290

Now TCP and IP-pseudo-header checksums combined would be:

0x9a12 + 0xb290 = 0x14ca2 = 0x1 + 0x4ca2 = 0x4ca3

Negating checksum before writing it back to the header:

~0x4ca3 = 0xb35c

Note: this checksum still differ from what you claim Wireshark calculates mostly because the packet you provided as example has 20 bytes of TCP payload data according to IP header, and TCP payload is also used in checksum calculation. In my example I used just TCP header without any other payload.

Problems in Your Code

There's a number of problems spotted in the code provided.

tcp_checksum()

  1. This function calculates IPv4 checksum. To modify it for IPv6 you need to extend IP addresses sizes used in calculation from 4 bytes to 16.

  2. The code around ip_src and ip_dst initialization is wrong and should be:

    uint16_t *ip_src=(uint16_t *)&src_addr->in_addr;    uint16_t *ip_dst=(uint16_t *)&dest_addr->in_addr;

get_ipv6_udptcp_checksum()

l4_len is not converted from network byte order. It should be:

l4_len = ntohs(ipv6_hdr->ip6_plen);

main()

Calculated checksum is not converted into network byte order, as it should be:

tcphdr.th_sum = htons(get_ipv6_udptcp_checksum(&iphdr, (uint16_t *)&tcphdr));


Checksum calculation for UDP frame with Pseudo header and : SOLVED! (Including C-Code, example output and Wireshark dataframe for verification)

Hello, i have been looking into the same problem:Please find a C program that generates the correct UDP CHECK based on the pseudo header and the frame content. Included is the program output and the wireshark result for the same frame in real life.

unsigned long sum=0, sum2=0, term=0;CString s;#define DATABYTES 3// pseudo header bytes:unsigned char ip1[]={192,168,11,25};   // ip 1unsigned char ip2[]={192,168,11,20};  // ip 2unsigned char pr []= {0,17};         // udp protocol unsigned char len[]= {0,8+DATABYTES};         // UDP entire message length entire // acutal message bytes:unsigned char port1[] ={0x22,0xb8};   // port 1unsigned char port2[] ={0x22,0xb8};   // port 1unsigned char msglen[]={0,8+DATABYTES};         // UDP entire message length entire unsigned char check[] = {0,0};        // check set to 0 unsigned char msg[DATABYTES]= {0x11,0x22,0x33};         // 10 bytes message length// add artificial pseudo header bytes to sumterm = ((unsigned short) ip1[0] << 8) + ip1 [1];sum+=term;s.Format("Add ip1 = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);term = ((unsigned short) ip1[2] << 8) + ip1 [3];sum+=term;s.Format("Add ip1 = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);term = ((unsigned short) ip2[0] << 8) + ip2 [1];sum+=term;s.Format("Add ip2 = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);term = ((unsigned short) ip2[2] << 8) + ip2 [3];sum+=term;s.Format("Add ip2 = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);term = ((unsigned short) pr[0] << 8) + pr [1];sum+=term;s.Format("Add pr = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);term = ((unsigned short) len[0] << 8) + len [1];sum+=term;s.Format("Add len = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);// ----------------------------------------------------------------// add real udp header bytes to sum (ports ....)term = ((unsigned short) port1[0] << 8) + port1[1];sum+=term;s.Format("Add port1 = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);term = ((unsigned short) port2[0] << 8) + port2[1];sum+=term;s.Format("Add port2 = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);term = ((unsigned short) msglen[0] << 8) + msglen [1];sum+=term;s.Format("Add msglen = 0x%04x Sum = 0x%04x\n",term,sum);  AfxTrace(s);// ----------------------------------------------------------------// add data bytes info:for (int i=0;i<DATABYTES/2;i++){    // add message data bytes to sum sum (ports ....)    term = ((unsigned short) msg[i*2] << 8) + msg [1+i*2];    sum+=term;    s.Format("Add msg = 0x%04x sum = 0x%04x\n",term,sum);  AfxTrace(s);}if (DATABYTES % 2 != 0){    term = ((unsigned short) msg[DATABYTES-1] << 8);    sum+=term;    s.Format("Add msg = 0x%04x sum = 0x%04x\n",term,sum);  AfxTrace(s);}sum= (sum >> 16) + (sum & 0xFFFF);s.Format("Clean sum = 0x%04x \n",sum);  AfxTrace(s);sum= (sum >> 16) + (sum & 0xFFFF);s.Format("Clean sum = 0x%04x \n",sum);  AfxTrace(s);sum= (~sum & 0xFFFF);s.Format("not sum = 0x%04x \n",sum);  AfxTrace(s);

Output of the program:

Add ip1 = 0xc0a8 Sum = 0xc0a8Add ip1 = 0x0b19 Sum = 0xcbc1Add ip2 = 0xc0a8 Sum = 0x18c69Add ip2 = 0x0b14 Sum = 0x1977dAdd pr = 0x0011 Sum = 0x1978eAdd len = 0x000b Sum = 0x19799Add port1 = 0x22b8 Sum = 0x1ba51Add port2 = 0x22b8 Sum = 0x1dd09Add msglen = 0x000b Sum = 0x1dd14Add msg = 0x1122 sum = 0x1ee36Add msg = 0x3300 sum = 0x22136Add msg = 0x0000 sum = 0x22136Add msg = 0x0000 sum = 0x22136Clean sum = 0x2138 Clean sum = 0x2138 not sum = 0xdec7  

And the wireshark frame:

Ethernet II, Src: Intel_af:ef:6f (00:03:47:af:ef:6f), Dst: aa:aa:bb:bb:cc:cd (aa:aa:bb:bb:cc:cd)    Destination: aa:aa:bb:bb:cc:cd (aa:aa:bb:bb:cc:cd)        Address: aa:aa:bb:bb:cc:cd (aa:aa:bb:bb:cc:cd)        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)    Source: Intel_af:ef:6f (00:03:47:af:ef:6f)        Address: Intel_af:ef:6f (00:03:47:af:ef:6f)        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)    Type: IP (0x0800)Internet Protocol Version 4, Src: 192.168.11.25 (192.168.11.25), Dst: 192.168.11.20 (192.168.11.20)    Version: 4    Header length: 20 bytes    Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))        0000 00.. = Differentiated Services Codepoint: Default (0x00)        .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)    Total Length: 31    Identification: 0xebe3 (60387)    Flags: 0x00        0... .... = Reserved bit: Not set        .0.. .... = Don't fragment: Not set        ..0. .... = More fragments: Not set    Fragment offset: 0    Time to live: 128    Protocol: UDP (17)    Header checksum: 0xb76c [correct]        [Good: True]        [Bad: False]    Source: 192.168.11.25 (192.168.11.25)    Destination: 192.168.11.20 (192.168.11.20)    [Source GeoIP: Unknown]    [Destination GeoIP: Unknown]User Datagram Protocol, Src Port: ddi-udp-1 (8888), Dst Port: ddi-udp-1 (8888)    Source port: ddi-udp-1 (8888)    Destination port: ddi-udp-1 (8888)    Length: 11    Checksum: 0xdec7 [correct]        [Good Checksum: True]        [Bad Checksum: False]Data (3 bytes)    Data: 112233    [Length: 3]0000  aa aa bb bb cc cd 00 03 47 af ef 6f 08 00 45 00   ........G..o..E.0010  00 1f eb e3 00 00 80 11 b7 6c c0 a8 0b 19 c0 a8   .........l......0020  0b 14 22 b8 22 b8 00 0b de c7 11 22 33            .."."......"3

One thing is missing: if output is 0000, you need to invert it to FFFF to signal for a calculated check instead of a check not present.Good Luck, JOHI.