【问题标题】:IP and TCP header checksum calculate in cIP和TCP报头校验和在c中计算
【发布时间】:2015-01-02 16:21:29
【问题描述】:

我正在编写一个简单的程序来发送/接收 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 标头,我这样做:

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 函数中传递的长度,但不太确定如何解决它。

任何帮助将不胜感激。提前谢谢!

【问题讨论】:

  • 注意关闭选民,这个问题是关于 IP 校验和,而不是 TCP 校验和。
  • @Alnitak 抱歉,我实际上对 TCP 校验和有疑问,希望您能再次提供帮助。谢谢!
  • 您应该将其作为一个单独的问题提出,或者参考其他已经解决 TCP 的问题。注意:TCP 校验和需要通过“伪 IP 标头”计算校验和。
  • 也:tcp = (struct tcphdr *)(ip + 4 * ip->ihl) - 不要使用sizeof(struct iphdr)
  • 感谢@alnitak 的建议。我尝试了伪 IP 标头并非常接近我想要的最终结果,但仍然看到问题。我会把它作为另一个问题发布。

标签: c tcp network-programming ip checksum


【解决方案1】:

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

校验和字段是一个的 16 位反码 报头中所有 16 位字的补码之和。出于以下目的 计算校验和,校验和字段的值为零。

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

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

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

另外,除了传递sizeof(struct iphdr),您应该传递4 * ip->ihl 来说明可能在标头中的任何IP 选项,首先确保您实际上至少有那么多字节在你的缓冲区中。

【讨论】:

  • 感谢@Alnitak 的回复。根据您提供的信息,我能够计算出正确的校验和。抱歉我的回复晚了。
【解决方案2】:

总和 += (总和 >> 16); /* 添加进位 / 答案=〜总和; / 截断为 16 位 */

如果你将进位添加到低 2 个字节并负它。

显然会为零。

【讨论】:

  • 不,只需添加进位 not 会导致零。创建零的原因是在计算中包含最初计算的校验和值
  • 其实我对这个说法感到困惑。答案=〜总和; /* 截断为 16 位 */
猜你喜欢
  • 2014-03-09
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-21
  • 2015-01-04
  • 2014-03-12
  • 1970-01-01
相关资源
最近更新 更多