C中ICMPv6校验和计算返回错误结果

我一直在尝试为IPv6实现我自己的简化TCP/IP协议栈,目前我的目标是能够回答ICMPv6回应请求。
我使用以下结构,用于存储在稍后的计算所需要的数据:C中ICMPv6校验和计算返回错误结果

typedef uint16_t n_uint16_t;  //network byte order 

typedef uint32_t n_uint32_t;

n_uint16_t htons(uint16_t n);

n_uint32_t htonl(uint32_t n);

struct ipv6hdr {

n_uint32_t vtcfl; //version, traffic class, flow label

n_uint16_t payload_len;

unsigned char nexthdr;

unsigned char hop_limit;

unsigned char saddr[IP6_ALEN]; //IP6_ALEN = 16

unsigned char daddr[IP6_ALEN];

};

struct icmp6hdr {

unsigned char type;

unsigned char code;

n_uint16_t cksum;

union {

n_uint32_t un_data32[1]; /* type-specific field */

n_uint16_t un_data16[2]; /* type-specific field */

unsigned char un_data8[4]; /* type-specific field */

} dataun;

};

(也有定义的类型,用于处理endianity)

我使用以下函数来计算的ICMPv6校验和。前两个是相应地创建ICMPv6数据包和IPv6伪头字段的缓冲区。然后我计算缓冲区的16位字段之和的余数。

n_uint16_t icmpv6_chksum(struct ipv6hdr *ip6, struct icmp6hdr *icmp) { 

unsigned char buf[65535];

unsigned char *ptr = &(buf[0]);

int chksumlen = 0;

//ICMPv6 type

memcpy(ptr, &icmp->type, sizeof(icmp->type));

ptr += sizeof(icmp->type);

chksumlen += sizeof(icmp->type);

//ICMPv6 code

memcpy(ptr, &icmp->code, sizeof(icmp->code));

ptr += sizeof(icmp->code);

chksumlen += sizeof(icmp->code);

//ICMPv6 payload

memcpy(ptr, &icmp->dataun.un_data32, sizeof(icmp->dataun.un_data32));

ptr += sizeof(icmp->dataun.un_data32);

chksumlen += sizeof(icmp->dataun);

return pseudoheaderchksum(buf, ptr, ip6, chksumlen);

}

n_uint16_t pseudoheaderchksum(unsigned char *buf, unsigned char *ptr, struct ipv6hdr *ip6, int chksumlen) {

//source address

memcpy(ptr, &ip6->saddr, sizeof(ip6->saddr));

ptr += sizeof(ip6->saddr);

chksumlen += sizeof(ip6->saddr);

//dest address

memcpy(ptr, &ip6->daddr, sizeof(ip6->daddr));

ptr += sizeof(ip6->daddr);

chksumlen += sizeof(ip6->daddr);

//upper layer length

n_uint32_t upprlen = 0;

upprlen += sizeof(ip6->payload_len);

memcpy(ptr, &upprlen, sizeof(upprlen));

ptr += 4;

chksumlen += 4;

//3 bytes of zeros, then next header byte

*ptr = 0;

ptr++;

*ptr = 0;

ptr++;

*ptr = 0;

ptr++;

chksumlen += 3;

memcpy(ptr, &ip6->nexthdr, sizeof(ip6->nexthdr));

ptr += sizeof(ip6->nexthdr);

chksumlen += sizeof(ip6->nexthdr);

return chksum((uint16_t *) buf, chksumlen);

}

//counting internet checksum

n_uint16_t chksum(uint16_t *buf, int len) {

int count = len;

n_uint32_t sum = 0;

n_uint16_t res = 0;

while (count > 1) {

sum += (*(buf));

buf++;

count -= 2;

}

//if number of bytes was odd

if (count > 0) {

sum += *(unsigned char *) buf;

}

while (sum >> 16) {

sum = sum + (sum >> 16);

}

res = (n_uint16_t) sum;

return ~res;

}

不幸的是,我测试了现有的数据包,捕获的ICMPv6的校验和比我的计算结果不同。我做错了什么?

PS。我正在使用libpcap来捕获和/或发送原始的以太网数据包。


编辑:什么功能做

icmpv6_chksum更详细的说明 - 获得ICMPv6报文加在它的封装的IPv6数据包的报头的结构。为后面的计算创建一个空缓冲区,并复制ICMPv6类型,ICMPv6代码,ICMPv6消息(基本上是整个ICMPv6数据包,除校验和字段外,在计算过程中无论如何都为零)的值。
将缓冲区,其第一个空位指针和IPv6头部传递到伪首标校验块

pseudoheaderchecksum - 获取缓冲区,其第一个空位置指针和IPv6头部。复制IPv6源地址,IPv6目标地址,数据长度(在这种情况下,它是我们的ICMPv6数据包长度),然后是3个零字节和IPv6下一个标头值(= ICMPv6标头)。
该功能将添加到所谓的“IPv6伪首部”的缓冲器,其由前述的字段组成,如在例如RFC 2460.
现在填充的缓冲区被传递到chksum函数 - 一个应该为缓冲区统计Internet Checksum的函数。

chksum - 获取缓冲区及其以字节为单位的长度。它将缓冲区的16位碎片汇总在一起(如果需要,最后添加一个奇数字节)。溢出被添加到16位和。最后,函数返回计算结果的补码(二进制反转)。
在这个函数中,我试图计算Internet Checksum,如RFC 1071中所述。查看示例实现,它应该(虽然我不是100%确定,算法描述通常是模糊的)正确。


编辑2
好吧,我注意到,我并没有包括ICMPv6的消息体(即跟随标题的内容),所以现在

n_uint16_t icmpv6_chksum(struct ipv6hdr *ip6, struct icmp6hdr *icmp, unsigned char* data, int len) { 

unsigned char buf[65535];

unsigned char *ptr = &(buf[0]);

int chksumlen = 0;

//ICMPv6 type

memcpy(ptr, &icmp->type, sizeof(icmp->type));

ptr += sizeof(icmp->type);

chksumlen += sizeof(icmp->type);

//ICMPv6 code

memcpy(ptr, &icmp->code, sizeof(icmp->code));

ptr += sizeof(icmp->code);

chksumlen += sizeof(icmp->code);

//ICMPv6 rest of header

memcpy(ptr, &icmp->dataun.un_data32, sizeof(icmp->dataun.un_data32));

ptr += sizeof(icmp->dataun.un_data32);

chksumlen += sizeof(icmp->dataun);

unsigned char *tmp = data;

for(int i=0; i<len; i++){

memcpy(ptr, &tmp, sizeof(unsigned char));

ptr += sizeof(unsigned char);

tmp += sizeof(unsigned char);

chksumlen += sizeof(unsigned char);

}

return pseudoheader_chksum(buf, ptr, ip6, chksumlen);

}

哪里unsigned char *data的是,一切都来了在ICMPv6头后(不包括以太网FCS)。
不幸的是,校验和计数仍然无法正常工作。

回答:

我怀疑你正在失去跟踪数字应该在主机顺序,以及他们应该在网络顺序。仅仅在n_uint16_tuint16_t之间来回转换不会做任何事情。您需要使用htons() and ntohs()和其他相关功能。

以上是 C中ICMPv6校验和计算返回错误结果 的全部内容, 来源链接: utcz.com/qa/266580.html

回到顶部