C IPv4报头校验和计算,我缺少什么

C IPv4报头校验和计算,我缺少什么,c,linux,sockets,networking,tcp,C,Linux,Sockets,Networking,Tcp,尝试修复此代码已有一段时间了,但运气不佳,尝试了计算IPv4报头校验和的不同实现,但是它们的输出与我的程序输出大不相同: 我从linux中偷来的函数可以做到这一点: static inline uint16_t ip_fast_csum(const void *iph, unsigned int ihl){ unsigned int sum; asm(" movl (%1), %0\n" " subl $4, %2\n" " jb

尝试修复此代码已有一段时间了,但运气不佳,尝试了计算IPv4报头校验和的不同实现,但是它们的输出与我的程序输出大不相同:

我从linux中偷来的函数可以做到这一点:

static inline uint16_t ip_fast_csum(const void *iph, unsigned int ihl){

     unsigned int sum;

     asm("  movl (%1), %0\n"
         "  subl $4, %2\n"
         "  jbe 2f\n"
         "  addl 4(%1), %0\n"
         "  adcl 8(%1), %0\n"
         "  adcl 12(%1), %0\n"
         "1: adcl 16(%1), %0\n"
         "  lea 4(%1), %1\n"
         "  decl %2\n"
         "  jne      1b\n"
         "  adcl $0, %0\n"
         "  movl %0, %2\n"
         "  shrl $16, %0\n"
         "  addw %w2, %w0\n"
         "  adcl $0, %0\n"
         "  notl %0\n"
         "2:"
     /* Since the input registers which are loaded with iph and ihl
        are modified, we must also specify them as outputs, or gcc
        will assume they contain their original values. */
         : "=r" (sum), "=r" (iph), "=r" (ihl)
         : "1" (iph), "2" (ihl)
         : "memory");
     return ( uint16_t)sum; }
示例标题(字节): 45009D432460406AF6CD052ED12AC10A51

我的程序中调用上述函数的部分将校验和分配给报头,并打印报头和校验和:

  newpacket.ipheader->check = ip_fast_csum ((unsigned short *) newpacket.ipheader, IP4_HDRLEN);
  debug(5,"newpacket checksum set to %0x\r\n",newpacket.ipheader->check);
  uint8_t *ipbuf=(uint8_t *)newpacket.ipheader;
  for(i=0;i<IP4_HDRLEN;i++){
   debug(5,"%0x",ipbuf[i]); 
  }debug(5,"\n");
wireshark告诉我的屏幕盖:

你能帮我告诉我我做错了什么吗

这是我通常使用的功能:

inline unsigned short csum (unsigned short *buf, int nwords) {
     unsigned long sum;

     for (sum = 0; nwords > 0; nwords--)
          sum += *buf++;
     sum = (sum >> 16) + (sum & 0xffff);
     sum += (sum >> 16);
     return (unsigned short) (~sum);
}

提前谢谢。

只是想从评论和我自己中进一步扩展@SergeyA。如果查看引用的文件,您会在该函数的注释中注意到

ip_fast_csum-高效地计算IPv4报头校验和

您不想计算IPv4报头校验和您想计算TCP校验和。这些是OSI模型中的独立层。要计算TCP校验和,您需要按照人们所描述的那样做

正如您之前所希望的那样,只使用内核代码,这里有一个函数可以计算TCP校验和。下面我将复制它的描述和标题

static inline __sum16 tcp_v4_check(int len, __be32 saddr,
                                    __be32 daddr, __wsum base)
  • 计算(/检查)TCP校验和
如果你想用艰难的方式去做的话。查看您引用的同一个文件,您可以使用函数计算TCP数据包的伪头

csum_tcpudp_nofold(__be32 saddr, __be32 daddr, __u32 len,
                 __u8 proto, __wsum sum)
返回输入数据的伪标头校验和。结果是32位 展开

这只是psuedo报头,但是从那里你需要计算总校验和,我相信(不是100%确定),如果你把前面描述的伪报头放到它上面,你可以使用前面展示的函数来计算


顺便说一句,我没有检查过这段代码,但乍一看,这段代码似乎非常准确,计算TCP校验和的C/assembly方法也是如此。

我只是想发布这段代码并解决这个问题,在尝试了许多实现之后,我已经解决了这个问题,唯一有效的是Suricata IDPS的代码

    static inline uint16_t IPV4CalculateChecksum(uint16_t *pkt, uint16_t hlen)
{
    uint32_t csum = pkt[0];

    csum += pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[6] + pkt[7] + pkt[8] +
        pkt[9];

    hlen -= 20;
    pkt += 10;

    if (hlen == 0) {
        ;
    } else if (hlen == 4) {
        csum += pkt[0] + pkt[1];
    } else if (hlen == 8) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
    } else if (hlen == 12) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5];
    } else if (hlen == 16) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
            pkt[7];
    } else if (hlen == 20) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
            pkt[7] + pkt[8] + pkt[9];
    } else if (hlen == 24) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
            pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11];
    } else if (hlen == 28) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
            pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13];
    } else if (hlen == 32) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
            pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
            pkt[14] + pkt[15];
    } else if (hlen == 36) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
            pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
            pkt[14] + pkt[15] + pkt[16] + pkt[17];
    } else if (hlen == 40) {
        csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
            pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
            pkt[14] + pkt[15] + pkt[16] + pkt[17] + pkt[18] + pkt[19];
    }

    csum = (csum >> 16) + (csum & 0x0000FFFF);
    csum += (csum >> 16);

    return (uint16_t) ~csum;
}

我希望这也能帮助其他人,suricata拥有优秀的代码库,编写良好的工作代码


干杯并感谢所有试图帮助我的人。

我最近遇到了同样的问题,我发现我错了。计算前将校验和字段设置为零,如下所示:

newpacket.ipheader->check = 0;
newpacket.ipheader->check = ip_fast_csum ((unsigned short *) newpacket.ipheader, IP4_HDRLEN);

,和是提供有关如何计算IPv4标头校验和的算法的源。看起来您正在计算IP标头校验和。Wireshark似乎显示了TCP报头校验和。仅在@SergeyA上展开,您似乎已将IP层校验和放入TCP层。或者用更“官方”的术语。你混淆了第3层和第4层。嗨,TCP校验和在wireshark中很好,如果你看图片,只有ip校验和是红色的。我在维基百科上读了几次,似乎还是遗漏了一些东西。嗨,TCP校验和很好,我有一个单独的函数来计算它。wireshark显示了良好的TCP校验和,所以我不担心,TCP校验和需要TCP heder+数据+ip伪头。这是针对ipv4报头校验和的,该校验和是独立的,并且严格适用于ipv4报头。啊,好吧,从你提供的图片和我认为你把IP校验和放在TCP字段中的文本来看,我犯了一个错误。很抱歉。
newpacket.ipheader->check = 0;
newpacket.ipheader->check = ip_fast_csum ((unsigned short *) newpacket.ipheader, IP4_HDRLEN);