0

I've been trying to calculate the checksum of a TCP header in C. For my purposes, the TCP header is 20 bytes long and has no extra data or options. Also, according to my knowledge, the TCP checksum is calculated as the one's complement of the one's complement sum of the 16 bit words in the header. So, I wrote a function to calculate the TCP checksum, considering the conditions I stated above. However, when I examine the packet being sent out after I calculate the checksum, I get this:

1 0.000000   10.0.2.15 -> 127.0.0.1    TCP 74 24131->8000 [SYN] Seq=0 Win=13273 [TCP CHECKSUM INCORRECT] Len=20

I examined the packet using tshark. However, I also examined the packets with Wireshark, and it also said the same thing. Except, that in parenthesis, it said, "maybe caused by TCP checksum offload?" Which, I don't fully understand.

Here's the code I used to calculate the checksum:

unsigned short tcp_checksum(struct tcphdr* tcph,struct iphdr* iph)  {

          /* Calculate TCP checksum */

          struct pseudo_hdr* pseudo_hdr;
          u_char* buffer;
          u_char* segment;
          u_char* pseudo_segment;
          unsigned int count = 32; 
          unsigned long sum = 0;
          unsigned short mask = 0xffff;
          unsigned long long* hdr;

          pseudo_hdr = malloc(sizeof(struct pseudo_hdr)); // allocate memory
          buffer = malloc(32); // allocate for 32 bytes of information 

          if (pseudo_hdr == NULL || buffer == NULL) { // if memory wasn't allocated properly

                 err();
                 if (pseudo_hdr != NULL) free(pseudo_hdr);
                 if (buffer != NULL) free(buffer);
                 return 0;

           }


           pseudo_hdr->saddr = (unsigned long)iph->saddr; // we add the cast because the fields if of type u_int_32
           pseudo_hdr->daddr = (unsigned long)iph->daddr; // same reason for adding the cast as above
           bzero(&pseudo_hdr->reserved,sizeof(pseudo_hdr->reserved)); // zero header 
           pseudo_hdr->proto = IPPROTO_TCP; // this will always be 6
           pseudo_hdr->len = htons(tcph->doff*4); // length of tcp header

           /* Place both headers into a buffer */

           segment = (u_char*)tcph; 
           pseudo_segment = (u_char*)pseudo_hdr; 

           /* Concactenate */

           memcpy((char*)buffer,(char*)pseudo_segment,sizeof(struct pseudo_hdr)); // first the pseudo header 
           memcpy((char*)buffer+12,(char*)segment,sizeof(struct tcphdr)); // then the TCP segment 

           /* Calculate checksum just like IP checksum (RFC C implementation) */

           hdr = (unsigned long long*)buffer;

           while (count > 1) {

             sum += *(unsigned short*)hdr++;
             count -= 2;
           }

           // Add left over bytes, if any 

           if (count > 0) sum += * (u_char*) hdr;

           // Fold 32-bit sum to 16 bits 

           while (sum>>16) sum = (sum & mask) + (sum >> 16);

           sum = ~sum; // bitwise NOT operation

           /* Discard headers and buffer */

           free(buffer);
           free(pseudo_hdr);

           return sum;

}

struct iphdr and struct tcphdr are defined in <netinet/ip.h> and <netinet/tcp.h>. Here's my definition of struct pseudo_hdr:

struct pseudo_hdr {

   unsigned long saddr; // 4 bytes
   unsigned long daddr; // 4 bytes
   unsigned char reserved; // 1 byte
   unsigned char proto; // 1 byte
   unsigned short len; // 2 bytes

};

/* Total = 4+4+1+1+2 = 12 bytes */

As I said, this function, for my purposes, calculates the checksum for a TCP header that is 20 bytes long.

Thank you for any help or recommendations.

ssharma
  • 153
  • 2
  • 13
  • Did you check https://stackoverflow.com/questions/8845178/c-programming-tcp-checksum – MCG Aug 17 '17 at 17:47
  • unrelated, but I see no reason to `malloc` memory for `pseudo_hdr` or `buffer` .. just put them in automatic storage. Plus you're not `free`ing them or stashing them for later, so this looks like a memory leak to me. – yano Aug 17 '17 at 17:53
  • 'hdr >>= 16;' that's a really big shift! – Martin James Aug 17 '17 at 18:05
  • AKA 'did you mean to ADD 16 to the pointer?' – Martin James Aug 17 '17 at 18:09
  • @MartinJames The logic behind the shift is to get the next 16 bits in the header. – ssharma Aug 17 '17 at 18:51
  • 1
    The checksum must also include the pseudo header. See pages 16 and 17 of _[RFC 793, Transmission Control Protocol](https://tools.ietf.org/html/rfc793)_. – Ron Maupin Aug 17 '17 at 18:52
  • @RonMaupin I do include the pseudo-header in the buffer when I use `memcpy()` to concatenate the pseudo-header and then the TCP segment. – ssharma Aug 17 '17 at 18:55
  • 2
    _Section 4, Implementation Examples_ of _[RFC 1071, Computing the Internet Checksum](https://tools.ietf.org/html/rfc1071)_ give you the C code. – Ron Maupin Aug 17 '17 at 18:55
  • @ssharma How big is 'unsigned long long' on your system? You are shiting it by 16 bits 16 times: a 256-bit shift? – Martin James Aug 17 '17 at 18:57
  • Interesting...I'll try it out. – ssharma Aug 17 '17 at 18:58
  • @MartinJames Yes, `unsigned long long` on my system is 16 bits. – ssharma Aug 17 '17 at 19:00
  • @MartinJames Is there a problem shifting that much? – ssharma Aug 17 '17 at 19:01
  • There is if 'hdr' is less than 256 bits, (and yes, I meant 'shifting', not what I typo'd:). – Martin James Aug 17 '17 at 19:15
  • @MartinJames For my purposes, the TCP header is 20 bytes. You see, I'm trying to send a `SYN` packet to a server, and then see if I get a `SYN ACK`. That means that my TCP segment will consist of just the TCP header and no extra data, which, therefore, means the TCP header is 20 bytes. Considering that we're calculating the checksum over the pseudo-header and the TCP header combined, that means that we're looking at 32 bytes of data (12 byte pseudo-header and 20 bytes TCP header). 32 bytes is 256 bits which is 16 16-bit words. So, to cover each of those words, we loop 16 times. – ssharma Aug 17 '17 at 19:24
  • @MartinJames However, if this was more general-purpose, your point would make complete sense. – ssharma Aug 17 '17 at 19:25
  • @RonMaupin I think perhaps your answer is the most fitting for my situation. However, the line `sum += * (unsigned short) addr++;` doesn't make sense to me. Could you or someone else explain what this line is doing please? – ssharma Aug 17 '17 at 19:28
  • 2
    that's not how bit shifting works. `hdr` is an `unsigned long long`, which on my machine is 64 bits. That means you can shift 16 bits in one direction 3 (maybe 4) times (I forget what happens when you shift the entire bit-width of the field, it's undefined, implementation defined, or 0,,, someone here will know). If you want to loop over 20 bytes, you'll need to use pointer arithmetic to access each of those 20 bytes. The type of your pointer determines how you will do that. – yano Aug 17 '17 at 20:06
  • 1
    @yano I think that we can forget the language-lawyers for an attempted 256-bit shift - it's bad on all current environments, (that means I don't know undefined/implementation defined/ 0 either:). – Martin James Aug 17 '17 at 20:13
  • @yano I guess you learn something every day. I didn't realized that's how it worked. – ssharma Aug 17 '17 at 23:35
  • That also explains the line `sum += * (unsigned short) addr++;`. – ssharma Aug 17 '17 at 23:38
  • Though I still can't seem to solve my checksum problem – ssharma Aug 17 '17 at 23:40
  • yeah, you can learn a lot on SO! I certainly have, I mostly just come here to read from people who know way more than me. But bit shifting occurs in a self-contained entity. It isn't a sliding "window" of memory. AFAIK a value is loaded into a shifter circuit and it operates on that value, very quickly, with no notion of other memory surrounding where that value was loaded from. If you haven't already this is worth a look: https://stackoverflow.com/questions/141525/what-are-bitwise-shift-bit-shift-operators-and-how-do-they-work – yano Aug 18 '17 at 03:55
  • Alright, I'll take a look, Thank you. – ssharma Aug 18 '17 at 04:04

1 Answers1

0

@RonMaupin's suggestions about checking out the RFC's and the contribution of everyone else to further my understanding of TCP (and C in general!) helped me to solve my problem. Thank you!

ssharma
  • 153
  • 2
  • 13