TCP checksum incorrect for packets with payload

590 views Asked by At

I'm working on an Ubuntu box in C.
The checksum calculation code is as follows:

unsigned short csum(unsigned short *buf, int nwords)
{
  unsigned long sum;
  for(sum=0; nwords>0; nwords=nwords-2){
    sum += *buf;
    //printf("%04x\n", *buf);
    buf++;
  }
  if(nwords>0)
    sum += *buf++;
  while(sum >> 16)
    sum = (sum >> 16) + (sum &0xffff);
  /* sum += (sum >> 16);*/
  return (unsigned short)(~sum);
}

This has been working fine for IP and ICMP segments, though, so I highly doubt this is the problem.
In an attempt to discern the problem, I am currently capturing random packets, constructing the pseudo header + deepcopy of the tcp header portion, then printing out the original checksum and calculated checksum.

  struct tcphdr *tcph = (struct tcphdr *)(buffer + sizeof(struct ethhdr) + iphdrlen);
  int tcphdrlen = size - sizeof(struct ethhdr) - iphdrlen;
  int pseudo_len = sizeof(iph->saddr) + sizeof(iph->daddr) + 1 + sizeof(iph->protocol) + 2 + tcphdrlen;
  test = (u_char *) malloc(pseudo_len);
  memset(test, 0, pseudo_len);
  memcpy(test, &(iph->saddr), sizeof(iph->saddr));
  int pos = sizeof(iph->saddr);
  memcpy(test + pos, &(iph->daddr), sizeof(iph->daddr));
  pos += sizeof(iph->daddr);
  memset(test + pos, 0, 1);
  pos += 1;
  memcpy(test + pos, &(iph->protocol), sizeof(iph->protocol));
  int tcphdrlenhtons = htons(tcphdrlen);
  pos += sizeof(iph->protocol);
  memcpy(test + pos, &tcphdrlenhtons, 2);
  pos += 2;
  memcpy(test + pos, tcph, tcphdrlen);

  struct tcphdr *t_tcph = (struct tcphdr *)(test + pos);
  memset(&(t_tcph->check), 0, sizeof(t_tcph->check));

  printf("correct tcp checksum: %d\n", ntohs(tcph->check));
  printf("my tcp checksum: %d\n", ntohs((unsigned short) csum((unsigned short *)test, pseudo_len)));

In testing, I've found that the calculated checksum will be correct, but only if the packet had no payload.
If anyone could give me an idea what I might be doing wrong, I'd appreciate it.

2

There are 2 answers

0
Cong Hui On BEST ANSWER

As David Schwartz had pointed out, the program was capturing the packets before the checksum was being calculated, making the captured packets' checksums to be incorrect. The program was actually giving out correct checksums.

0
nategoose On

This isn't really an answer so much as a suggestion. Your test code is a bit difficult to follow. Variables like test don't make it easy for others to read and understand the code.

Try making an actual struct for the pseudo-header and do regular assignment rather than memcpy for things like IPv4 addresses. They're only 4 bytes long and it will make your code much easier to read.

This line:

memset(test + pos, 0, 1);

bothers me a bit since it isn't obvious what you're setting to 0. I also wonder if you're setting the right number of bytes to 0.

I tried to figure out if you might have an endian issue, but that was difficult since I had trouble following your test code.