在C中计算ICMPv6数据包的校验和 [英] Calculating checksum of ICMPv6 Packet in C

查看:581
本文介绍了在C中计算ICMPv6数据包的校验和的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试计算ICMPv6消息(确切地说是邻居广告)的校验和。

I am trying to calculate the checksum of an ICMPv6 message (to be precise, a Neighbor Advertisement).

RFC 4443 将其描述为整个ICMPv6消息的补码之和的16位补码。

RFC 4443 describes it as the "16-bit one's complement of the one's complement sum of the entire ICMPv6 message"

还有一些有关如何执行此操作的示例代码(尽管我认为它来自IPv4,但唯一的区别是总和中包括了什么,而不是如何计算它):
RFC 1071

There is also some example code on how to do that (although I think it's from IPv4, but the only difference is what is included in the sum, not how to calculate it): RFC 1071

我从wireshark那里拿了一个小包,并按主机字节顺序输入了短裤。然后我打印正确的校验和,将其归零并计算出我的校验和。但是它们不匹配。根据RFC 1071的规定,字节序不应该成为问题(结果不只是字节交换,而是完全关闭)。

I have taken a packet from wireshark and entered the shorts in host byte order. Then I print the correct checksum, zero it out and calculate mine. But they do not match. According to RFC 1071, endianess shouldn't be the problem (the result isn't just byte swapped, but completely off).

根据 RFC 2460#8.1 我需要在计算中包括伪标题,仅包含Src + Dst地址,长度为32位宽字段和下一个标头类型。

According to RFC 2460 #8.1 I need to include the "pseudo-header" into the calculation, containing only Src+Dst address, length as 32 Bit wide field and next header type.

调用代码:

uint32_t payloadlen = htonl(32);
struct ip6_hdr *ip6;
struct nd_neighbor_advert *na;
size_t len, offset, tmplen;
uint8_t *tmppacket, icmp = 58;

uint8_t packet[] = {
            0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3A, 0xFF, 0x20, 0x01, 0x0D, 0xB8,
            0xDD, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
            0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x54, 0xFF,
            0xFE, 0x00, 0x22, 0x00, 0x88, 0x00, 0x54, 0xB9, 0x60, 0x00, 0x00, 0x00,
            0x20, 0x01, 0x0D, 0xB8, 0xDD, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x01, 0x00, 0x02, 0x01, 0x00, 0x03, 0x54, 0x00, 0x00, 0x13
        };

na->nd_na_hdr.icmp6_cksum = 0;

tmplen = 40+sizeof(struct nd_neighbor_advert)+ICMP_OPT_LEN;
tmppacket = malloc(tmplen);
memset(tmppacket, 0, 40);
offset = sizeof(struct in6_addr);
memcpy(tmppacket, &ip6->ip6_src, offset);
memcpy(tmppacket+offset, &ip6->ip6_dst, offset);
memcpy(tmppacket+offset*2, &payloadlen, 4);
memcpy(tmppacket+39, &icmp, 1);
memcpy(tmppacket+40, packet+sizeof(struct ip6_hdr),
        sizeof(struct nd_neighbor_advert)+ICMP_OPT_LEN);

na = (struct nd_neighbor_advert *) (tmppacket+40);
na->nd_na_hdr.icmp6_cksum = checksum((uint16_t *) tmppacket, tmplen);
printf("Checksum calc: %hX\n", na->nd_na_hdr.icmp6_cksum);

dump((unsigned char *) tmppacket, tmplen);

校验和功能:

uint16_t checksum (uint16_t *addr, size_t bytes) {
    unsigned int i;
    uint16_t *p = addr;
    uint32_t sum = 0;

    /* Sum */
    for (i=bytes; i > 1; i -= 2)
        sum += *p++;

    /* If uneven length */
    if (i > 0)
        sum += (uint16_t) *((unsigned char *) (p));

    /* Carry */
    while ((sum & 0xFFFF0000) != 0)
        sum = (sum >> 16) + (sum & 0xFFFF);

    return ~((uint16_t) sum);
}

起初它是快速又肮脏的。 main的代码省略了一些内容。校验和函数中的字节序应该不是问题,该数据包的长度也应是偶数。

This is just quick and dirty to get it working at first. The code for "main" omits some stuff. Endianess in the checksum function shouldn't be a problem, also this packet has an even length.

结果应该是B954,但我得到了DB32。
转储的输出是:

The result should be B954, but I get DB32. The output of dump is:


数据包大小:72

2001 0DB8 DDDD 0000 0000 0000 0000 0100 FE80 0000 0000
0000 0203 54FF FE00 2200 0000 0020 0000 003A 8800 32DB 6000 0000 2001
0DB8 DDDD 0000 0000 0000 0000 0100 0201 0003 5400 0013

Packet size: 72
2001 0DB8 DDDD 0000 0000 0000 0000 0100 FE80 0000 0000 0000 0203 54FF FE00 2200 0000 0020 0000 003A 8800 32DB 6000 0000 2001 0DB8 DDDD 0000 0000 0000 0000 0100 0201 0003 5400 0013

感谢到目前为止的提示。如果您有什么想法可能仍然是错误的,我们将不胜感激。

Thanks for the tips so far. If you have an idea what could be still wrong, I'd appreciate your input.

推荐答案

我认为这存在三个问题您的代码:

I think there are three problems with your code:


  1. 您对IPv6标头进行校验和,但不应该这样做。校验和应同时覆盖IPv6地址和长度,但不能覆盖其他标头字段。

  2. 如果长度不均匀,则字节顺序很重要。 在添加额外字符之前,您需要 ntohs (或者实际上,在小端平台上,将其右移8位)。 编辑:在小端平台上忽略它是可以的。

  3. 当减小到16位时,总和可能会短暂溢出。在这种情况下,您需要将此进位值反馈到计算中(即加1)。

  1. You checksum the IPv6 header, which you shouldn't. The checksum should cover both IPv6 addresses and the length, but not other header fields.
  2. Endianity matters if the length is uneven. Before adding the extra character, you need to ntohs it (or actually, on a little-endian platform, shift it right 8 bits). Ignoring it is OK on little-endian platforms. The shift is needed on big-endian.
  3. When reducing to 16 bits, the sum may overflow a short. In this case, you need to feed this carry back to the calculation (i.e. add 1).

这篇关于在C中计算ICMPv6数据包的校验和的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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