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

标签 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 buffer[2048] 中。为了获得 IP header ,我这样做:

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

我得到的是“My calculated checksym = 0x0”,似乎 IP header 中没有任何内容。我想我可能没有正确传递 in_cksum 函数参数,但我不确定如何修复或者我正在做的方式是否存在其他问题。

IP cksum 问题在下面得到解决。但是,我在尝试计算 TCP 校验和时遇到了类似的问题。下面是我如何获取 tcp header :

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

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

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

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

我这里得到的结果和之前打印出来的不一样。我认为我的问题可能在于我在 cksum 函数中传递的长度,但我不太确定如何解决它。

如有任何帮助,我们将不胜感激。提前致谢!

最佳答案

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

The checksum field is the 16 bit one's complement of the one's complement sum of all 16 bit words in the header. For purposes of computing the checksum, the value of the checksum field is zero.

我没有计算过,但我怀疑得到零是重新计算整个 header 的校验和的预期结果,即它会导致计算出的总和自行抵消。

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

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

此外,您应该传递4 * ip->ihl 来考虑任何IP 选项,而不是传递sizeof(struct iphdr)可能在 header 中,首先确保您的缓冲区中实际上至少有那么多字节。

关于c - IP和TCP头校验和在c中计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26774761/

相关文章:

c - 绑定(bind)到两个本地 IP 失败

tcp - 获取 tcpstream - wireshark 与 tshark

tcp - iperf TCP 比 UDP 快得多,为什么?

c - 使用 libpcap 同时监听两个设备

c - 从 SSH 服务器发送 channel 请求,使 `ssh_channel_accept_forward()` 在 SSH 客户端上返回?

c - 如何从文件类型中读取特定的字节选择?

c - 如何使用void*?

c - 为什么声明的原型(prototype)的函数类型不需要与实际函数的类型匹配?

network-programming - 我们可以在 2 对特定的 IP 和端口之间同时有两个 udp 流吗?

裸机可移植库中的 C99 "atomic"加载