TCP checksum calculation return 0 even when I already set checksum equals to 0 before calculating

440 views Asked by At

I'm writing a simple program to send/receive TCP packets. I got stuck at calculating checksum for TCP packet.

For checksum function, I re-use the code as below:

static int
in_cksum(u_short *addr, int len)
{
register int nleft = len;
register u_short *w = addr;
register int sum = 0;
u_short answer = 0;

/*
 * Our algorithm is simple, using a 32 bit accumulator (sum), we add
 * sequential 16 bit words to it, and at the end, fold back all the
 * carry bits from the top 16 bits into the lower 16 bits.
 */
while (nleft > 1)  {
    sum += *w++;
    nleft -= 2;
}

/* mop up an odd byte, if necessary */
if (nleft == 1) {
    *(u_char *)(&answer) = *(u_char *)w ;
    sum += answer;
}

/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16);         /* add carry */
answer = ~sum;              /* truncate to 16 bits */
return(answer);

}

I received a packet and store in char buffer[2048]. In order to get IP header, I do:

struct iphdr* ip;
ip = (struct iphdr*) buffer;

Below is how I get tcp header:

tcp=(struct tcphdr*) (buffer+(4*ip->ihl));

Here is my pseudo TCP struct

struct tcp_pseudohdr{
   uint32_t tcp_ip_src, tcp_ip_dst;
   uint8_t tcp_reserved;
   uint8_t tcp_ip_protocol;
   uint16_t tcp_length;
   struct tcphdr tcp_hdr;
}
struct tcp_pseudohdr pseudo_tcp;
memset(&pseudo_tcp,0,sizeof(struct tcp_pseudohdr));

Then I fill up the TCP pseudo struct and calculate the tcp checksum as follow:

pseudo_tcp.tcp_ip_src = ip->saddr;
pseudo_tcp.tcp_ip_dst = ip->daddr;
pseudo_tcp.tcp_ip_protocol = ip->protocol;
pseudo_tcp.tcp_reserved = 0;
pseudo_tco.tcp_length = htons(ntohs(ip->tot_length)-(4*ip->ihl));
memcpy(&pseudo_tcp,tcp,ntohs(ip->tot_length)-(4*ip->ihl));

After this, I'm able to ready back correctly the info in pseudo_tcp.tcp_hdr.source, pseudo_tcp.tcp_hdr.check, etc..

Then I finally calculate checksum as below:

tcp->check=0;
tcp->check=in_cksum((unsigned short*)&pseudo_tcp, ntohs(ip->tot_length)-(4*ip->ihl)+12);

The output from this function ntohs(tcp->check) = 0. I feel that I'm really close, but might be missing something. Usually, the cksum function returns 0 when I did not set the checksum = 0 before calculating. However, in this case I did and am not sure what is happening.

One thing I notice is that the size of struct tcphdr = 20, but when I memcpy, this ntohs(ip->tot_length)-(4*ip->ihl) = 40 since it includes option. Don't know if this causes the issue or there are other issues.

Any help would be greatly appreciated. Thank you in advance!

1

There are 1 answers

1
JS1 On BEST ANSWER

Setting tcp->check = 0 doesn't do anything because you are checksumming pseudo_tcp, which is a copy of tcp. Either set tcp->check = 0 before you copy tcp to pseudo_tcp, or set the checksum in pseudo_tcp to 0.