IP和TCP报头校验和在c中计算

时间:2014-11-06 08:23:00

标签: c tcp network-programming ip checksum

我正在编写一个简单的程序来发送/接收TCP数据包并将其合并到一个更大的项目中。我卡在校验和部分,我计算的数字与wireshark数字不匹配。

对于校验和功能,我重复使用Mike Muss的代码如下:

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);
}

我收到了一个数据包并存储在char缓冲区[2048]中。为了得到IP头,我做:

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

从这里开始,我可以正确地读取信息,如ip-> protocol,ip-> saddr等,甚至可以打印出wireshark中显示的正确校验和

printf("Print checksum = 0x%x\n",ntohs(ip->check));

然后我尝试使用上面的函数计算校验和并将其打印出来

printf("My calculated checksum =0x%x\n",in_cksum ((unsigned short*) ip, sizeof(struct iphdr)));

我得到的是"我的计算校验名= 0x0"似乎IP标头中没有任何内容。我想我可能没有正确传递in_cksum函数参数,但我不确定如何解决或者我是否还有其他问题。

IP cksum问题已在下面解决。但是,我在尝试计算TCP校验和时遇到了类似的问题。以下是我获取tcp标题的方法:

tcp=(struct tcphdr*) (buffer+sizeof(struct iphdr);

在此之后,我再次可以阅读有关tcp标头的正确信息,例如tcp-> source,tcp-> dest,甚至tcp-> check

然后我尝试重新计算校验和,如下所示:

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

我在这里得到的结果与我以前打印的结果不同。我认为我的问题可以在cksum函数中传递,但我不太确定如何解决它。

非常感谢任何帮助。提前谢谢!

2 个答案:

答案 0 :(得分:5)

IP校验和的计算不得包含包含该校验和的字段 - 应该为该字段使用零。来自RFC 791

  

校验和字段是该字段的16位补码         标头中所有16位字的补码和。出于...的目的         计算校验和时,校验和字段的值为零。

我还没有完成数学计算,但我怀疑得到零是在整个标题上重新计算校验和的预期结果,即它会导致计算出的总和自行取消。

[我检查过 - 维基百科同意:"如果没有损坏" ,整个IP头的总和(包括校验和)的结果应为零。因此,当验证校验和时,零实际上是正确的结果]

请注意,IP标头只能是4个字节长的倍数,因此在这种特殊情况下您的填充代码是不必要的,尽管如果想要计算其他数据的通用补码校验和,则可能有用。

此外,不是传递sizeof(struct iphdr) 应该<{1}}传递4 * ip->ihl以考虑标题中可能包含的任何IP选项,首先确保您实际拥有至少缓冲区中的那么多字节。

答案 1 :(得分:-1)

总和+ =(总和>&gt; 16); / * add carry / 回答=〜总和; / 截断为16位* /

如果将进位加到较低的2个字节并将其加负。

显然是零。