我如何验证的ICMPv6校验? (为什么我总是收到到0x3FFF的校验和?) [英] How do I validate the ICMPv6 checksum? (Why am do I keep getting a checksum of 0x3fff?)

查看:1101
本文介绍了我如何验证的ICMPv6校验? (为什么我总是收到到0x3FFF的校验和?)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在接收IPv6路由器通告报一个Linux用户空间程序。作为RFC4861的一部分,我需要验证的ICMPv6校验和。根据我的研究,其中大部分是指如果计算的那些赞扬IPv6的伪报头和数据包内容的结果应为0xFFFF的校验和是指在一般的IP校验和。但我不断收到到0x3FFF的校验和。

是不是有什么毛病我的校验和执行?请问Linux内核验证合格的报文用户空间之前的ICMPv6校验?是有已知良好ICMPv6报文来测试?一个很好的参考源

  uint16_t
校验和(const的结构in6_addr * SRC,常量结构in6_addr * DST,常量无效*数据,为size_t的len){
    uint32_t的校验= 0;
    工会{
        uint32_t的DWORD;
        uint16_t [2]字;
        uint8_t有字节[4];
    }温度;    // IPv6的伪包头的源地址,目的地址,长度,零,下一个头
    校验+ = src-> s6_addr16 [0];
    校验+ = src-> s6_addr16 [1];
    校验+ = src-> s6_addr16 [2];
    校验+ = src-> s6_addr16 [3];
    校验+ = src-> s6_addr16 [4];
    校验+ = src-> s6_addr16 [5];
    校验+ = src-> s6_addr16 [6];
    校验+ = src-> s6_addr16 [7];    校验+ = dst-> s6_addr16 [0];
    校验+ = dst-> s6_addr16 [1];
    校验+ = dst-> s6_addr16 [2];
    校验+ = dst-> s6_addr16 [3];
    校验+ = dst-> s6_addr16 [4];
    校验+ = dst-> s6_addr16 [5];
    校验+ = dst-> s6_addr16 [6];
    校验+ = dst-> s6_addr16 [7];    temp.dword = htonl(LEN);
    校验+ = temp.word [0];
    校验+ = temp.word [1];    temp.byte [0] = 0;
    temp.byte [1] = 0;
    temp.byte [2] = 0;
    temp.byte [3] = 58; //的ICMPv6
    校验+ = temp.word [0];
    校验+ = temp.word [1];    而(LEN→1){
        校验+ = *((常量uint16_t *)的数据);
        数据=(常量uint16_t *)数据+ 1;
        LEN - = 2;
    }    如果(LEN大于0)
        校验+ = *((常量uint8_t有*)的数据);    的printf(校验%X \\ n,校验码);    而(校验>>!16 = 0)
        校验=(校验和放大器; 0xFFFF的)+(校验>> 16);    校验=〜校验;    返回(uint16_t)校验;
}


解决方案

while循环是矫枉过正。该机构将只发生一次。

 而(校验>>!16 = 0)
    校验=(校验和放大器; 0xFFFF的)+(校验>> 16);校验=〜校验;返回(uint16_t)校验;

而不是

 校验+ =校验>> 16;返回(uint16_t)〜校验;

这是不必要的。 LEN始终是16位。

  temp.dword = htonl(LEN);
校验+ = temp.word [0];
校验+ = temp.word [1];

这是不必要的。不变的是永远00 00 00 58,所以只加58。

  temp.byte [0] = 0;
temp.byte [1] = 0;
temp.byte [2] = 0;
temp.byte [3] = 58; //的ICMPv6
校验+ = temp.word [0];
校验+ = temp.word [1];

您算法通常会正确的,否则,除了你处理整数的字节顺序和最后一个字节奇数字节的方式。从我怎么用这个协议中,字节是大端为了概括,即字节是0xAB 0XCD要成为PTED为16位0xABCD间$ P $。您$ C $,c取决于你的机器的顺序。

该命令是内置的整数会影响号码携带,其中添加到正确的校验和。但是如果你的code你的目标机器匹配,则最后一个奇数字节是错误的。是0xAB会导致0xAB00,书面不0x00AB。

I'm working on a Linux userspace program that receives IPv6 router advertisement packets. As part of RFC4861 I need to verify the ICMPv6 checksum. Based on my research, most of which refers to the refers to the IP checksum in general if you compute the ones compliment checksum of the IPv6 pseudo header and the packet contents the result should be 0xffff. But I keep getting a checksum of 0x3fff.

Is there something wrong with my checksum implementation? does the Linux kernel verify the ICMPv6 checksum before passing the packets to userspace? is there a good reference source for known good ICMPv6 packets to test with?

uint16_t
checksum(const struct in6_addr *src, const struct in6_addr *dst, const void *data, size_t len) {
    uint32_t checksum = 0;
    union {
        uint32_t dword;
        uint16_t word[2];
        uint8_t byte[4];
    } temp;

    // IPv6 Pseudo header source address, destination address, length, zeros, next header
    checksum += src->s6_addr16[0];
    checksum += src->s6_addr16[1];
    checksum += src->s6_addr16[2];
    checksum += src->s6_addr16[3];
    checksum += src->s6_addr16[4];
    checksum += src->s6_addr16[5];
    checksum += src->s6_addr16[6];
    checksum += src->s6_addr16[7];

    checksum += dst->s6_addr16[0];
    checksum += dst->s6_addr16[1];
    checksum += dst->s6_addr16[2];
    checksum += dst->s6_addr16[3];
    checksum += dst->s6_addr16[4];
    checksum += dst->s6_addr16[5];
    checksum += dst->s6_addr16[6];
    checksum += dst->s6_addr16[7];

    temp.dword = htonl(len);
    checksum += temp.word[0];
    checksum += temp.word[1];

    temp.byte[0] = 0;
    temp.byte[1] = 0;
    temp.byte[2] = 0;
    temp.byte[3] = 58; // ICMPv6
    checksum += temp.word[0];
    checksum += temp.word[1];

    while (len > 1) {
        checksum += *((const uint16_t *)data);
        data = (const uint16_t *)data + 1;
        len -= 2;
    }

    if (len > 0)
        checksum += *((const uint8_t *)data);

    printf("Checksum %x\n", checksum);

    while (checksum >> 16 != 0)
        checksum = (checksum & 0xffff) + (checksum >> 16);

    checksum = ~checksum;

    return (uint16_t)checksum;
}

解决方案

The while loop is overkill. The body will only happen once.

while (checksum >> 16 != 0)
    checksum = (checksum & 0xffff) + (checksum >> 16);

checksum = ~checksum;

return (uint16_t)checksum;

Instead

checksum += checksum >> 16;

return (uint16_t)~checksum;

This is unnecessary. len is always 16-bit

temp.dword = htonl(len);
checksum += temp.word[0];
checksum += temp.word[1];

This is unnecessary. The constant is always 00 00 00 58, so just add 58.

temp.byte[0] = 0;
temp.byte[1] = 0;
temp.byte[2] = 0;
temp.byte[3] = 58; // ICMPv6
checksum += temp.word[0];
checksum += temp.word[1];

Your algorithm looks generally right otherwise, except for the way you handle the endianness of the integers and the last byte odd-numbered byte. From how I read the protocol, the bytes are to be summed in big-endian order, i.e., the bytes 0xAB 0xCD are to be interpreted as the 16-bit 0xABCD. Your code depends on the ordering of your machine.

The order that the integers are built will affect the number of carries, which you are adding correctly into the checksum. But if your code matches your target machine, then the last odd-numbered byte is wrong. 0xAB would result in 0xAB00, not 0x00AB as written.

这篇关于我如何验证的ICMPv6校验? (为什么我总是收到到0x3FFF的校验和?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆