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

时间:2016-05-20 16:35:34

标签: c linux sockets networking tcp

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

我从linux kernel窃取的函数来执行此操作:

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

示例标头(字节): 45009d4326400406af6cd052ed12ac10a51

调用上述函数的程序部分将校验和分配给标题并打印标题和校验和:

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

debug()只是一个printf()包装器,下面是示例输出:

newpacket checksum set to 6caf
45009d4326400406af6cd052ed12ac10a51

来自wireshark告诉我的屏幕截图:

wiresharkcap

你能帮助我并告诉我我做错了吗?

这是我通常使用的功能:

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

提前致谢。

3 个答案:

答案 0 :(得分:0)

只是从评论和我自己多一点扩展@SergeyA。如果您查看引用above的文件,您会注意到该功能的评论

  

ip_fast_csum - 有效地计算IPv4标头校验和。

您不希望计算您想要TCP校验和的IPv4标头校验和。这些是OSI模型here中的单独层。要计算TCP校验和,您需要按照these好的人描述。

如前所述,只使用内核代码,这里有一个函数可以计算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%肯定)你可以使用你之前显示的函数计算,如果你填充前面描述的伪标题。

另外,我没有检查过这段代码,但从初看起来看起来非常准确,所以here是计算TCP校验和的C /汇编方式。

答案 1 :(得分:0)

只是想发布并解决这个问题,我在尝试了很多实现后解决了这个问题,唯一有效的是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;
}

https://github.com/inliniac/suricata/blob/master/src/decode-ipv4.h

我希望这也有助于其他人,suricata也有出色的代码库,编写良好的工作代码。

为那些试图提供帮助的人欢呼并感谢你。

答案 2 :(得分:0)

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

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