首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >计算ICMPv6报头的16位校验和

计算ICMPv6报头的16位校验和
EN

Stack Overflow用户
提问于 2015-12-12 20:38:05
回答 1查看 3.2K关注 0票数 3

我想问一下,我根据ICMPv6协议计算16位校验和的解决方案是否正确。我试着遵循维基百科,但我不确定主要有两件事。

首先是the packet length的意思-是整个ICMPv6数据包的数据包长度而没有校验和,还是只有有效载荷?它是以八位数表示的,就像在IPv6中一样?这个ICMPv6回显请求的长度是多少?

代码语言:javascript
复制
6000                                    # beginning of IPv6 packet
0000 0000 3a00 FE80 0000 0000 0000 0202 
B3FF FE1E 8329 FE80 0000 0000 0000 0202 
B3FF FE1E 8330 

8000 xxxx                               # this is beginning of the ICMP packet - type and checksum
a088 0000 0001                          # from here including this line I compute the length   
0203 0405 0607 0809 0a0b 0c0d 0e0f 1011 
1213 1415 1617 1819 1a1b 1c1d 1e1f 2021
2223 2425 2627 2829 2a2b 2c2d 2e2f 3031
3233

这是否意味着,正如我在下面的代码中所述,上面的长度是56个八位数?

然后我很难理解这一点(同样来自wiki)。

在这个伪头之后,校验和将继续使用ICMPv6消息,其中校验和最初被设置为零。校验和计算是根据Internet协议标准,使用16位的校验和求和,然后对校验和本身进行补充,并插入校验和字段。

这是否意味着我也应该将校验和字段中包含0000的整个ICMPv6帧添加到校验和中?

我试图用Python编写一个简单的程序:

代码语言:javascript
复制
# START OF Pseudo header
# we are doing 16 bit checksum hence quadruplets
## source IP 
sip = ['FE80', '0000', '0000', '0000', '0202', 'B3FF', 'FE1E', '8329']
## destination IP
dip = ['FE80', '0000', '0000', '0000', '0202', 'B3FF', 'FE1E', '8330']
## next header - 32 bits, permanently set to (58)_dec ~ (88)_hex
nh = ['0000', '0088']    
## packet length -> see my question above: (56)_dec ~ (38)_hex
lng = ['0038']
png = "8000 0000 a088 0000 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233".split(" ")

# END OF PSEUDO HEADER

tot = sip + dip + lng + nh + png # from what the sum is going to be counted
stot = sum([int(x, 16) for x in tot]) % 65535 # we are in 16 bits world
rstot = 65535 - stot # wrap around
res = hex(rstot) # convert to hex

print(stot, rstot)
print(res)
check = bin(rstot + stot)
print(check) # all ones

这是用于以下ICMPv6 ping请求(带有IPv6头):

代码语言:javascript
复制
d392 30fb 0001 d393 30fb 0001 86dd 6000 
0000 0000 3a00 FE80 0000 0000 0000 0202 
B3FF FE1E 8329 FE80 0000 0000 0000 0202 
B3FF FE1E 8330 8000 xxxx a088 0000 0001
0203 0405 0607 0809 0a0b 0c0d 0e0f 1011
1213 1415 1617 1819 1a1b 1c1d 1e1f 2021
2223 2425 2627 2829 2a2b 2c2d 2e2f 3031
3233

它给出了输出:

代码语言:javascript
复制
27741 37794
0xe672 # correct?
0b1111111111111111

因此,我应该将xxxx替换为e672。这是正确的吗?当我试图用wireshark计算这个问题时,我得到了一个不同的答案。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-19 10:37:21

我试着用一个例子来回答你的问题。

让我们从Wireshark中获取这个样本捕获,这样我们就有了相同的包,在Wireshark中打开它,然后我们获取第一个ICMPv6包(框架3)。

注意,对于这个包,至少有一件重要的事情:有效负载长度为IPv6层的为32 (0x20)。

注意:要将数据包提取为Wireshark上的字符串,请选择数据包和所需的层(例如Ipv6),然后:right click > copy > bytes > hex stream

构建伪标头

要计算校验和,首先要做的是根据RFC 2460第8.1节构建伪头。

校验和是在伪标头( ICMPv6数据包)上计算的.

IPv6版本的ICMP ICMPv6在其校验和计算中包含了上述伪头。

要构建伪头,我们需要:

  • 源IP
  • Dest IP
  • 上层数据包长度
  • 下一个标头

源和Dest来自IPv6层。

下一个标头字段固定为58:

ICMP伪标头中的下一个标头字段包含值58,它标识ICMP的IPv6版本。

上层数据包长度:

伪报头中的上层数据包长度是上层报头和数据(例如TCP报头加上TCP数据)的长度。一些上层协议携带自己的长度信息(例如,UDP报头中的长度字段);对于这样的协议,也就是伪标头中使用的长度。其他协议(如TCP)不携带自己的长度信息,在这种情况下,伪标头中使用的长度是来自IPv6报头的有效载荷长度,减去IPv6头和上层报头之间任何扩展名头的长度。

在我们的示例中,上层(ICMPv6)不带长度字段,因此在本例中,我们必须使用来自IPv6层的有效负载长度字段,该数据包的有效载荷长度为32 (0x20)。

让我们尝试一些代码:

代码语言:javascript
复制
def build_pseudo_header(src_ip, dest_ip, payload_len):
    source_ip_bytes = bytearray.fromhex(src_ip)
    dest_ip_bytes = bytearray.fromhex(dest_ip)
    next_header = struct.pack(">I", 58)
    upper_layer_len = struct.pack(">I", payload_len)
    return source_ip_bytes + dest_ip_bytes + upper_layer_len + next_header

代码应该如下所示:

代码语言:javascript
复制
SOURCE_IP = "fe80000000000000020086fffe0580da"
DEST_IP = "fe80000000000000026097fffe0769ea"
pseudo_header = build_pseudo_header(SOURCE_IP, DEST_IP, 32)

构建ICMPV6数据包

正如在rfc 4443第2.3节中提到的,校验和字段必须在任何计算之前设置为0。

为了计算校验和,首先将校验和字段设置为零。

在本例中,我使用来自typecode的ICMPv6字段作为信号16位值。校验和字段被删除,数据包的其余部分被简单地称为“余数”:

代码语言:javascript
复制
TYPE_CODE = "8700"
REMAINDER = "00000000fe80000000000000026097fffe0769ea01010000860580da"

构建用于校验和计算的数据包的ICMPv6部分:

代码语言:javascript
复制
def build_icmpv6_chunk(type_and_code, other):
    type_code_bytes = bytearray.fromhex(type_and_code)
    checksum = struct.pack(">I", 0)  # make sure checksum is set to 0 here
    other_bytes = bytearray.fromhex(other)
    return type_code_bytes + checksum + other_bytes

名称如下:

代码语言:javascript
复制
TYPE_CODE = "8700"
REMAINDER = "00000000fe80000000000000026097fffe0769ea01010000860580da"
icmpv6_chunk = build_icmpv6_chunk(TYPE_CODE, REMAINDER)

计算校验和

计算校验和是根据RFC 1701进行的。Python的主要困难是将和用16位的数量包装起来。

calc_checksum()函数的输入是伪标头和包的ICMPv6部分的连接(校验和设置为0):

Python示例:

代码语言:javascript
复制
def calc_checksum(packet):
    total = 0

    # Add up 16-bit words
    num_words = len(packet) // 2
    for chunk in struct.unpack("!%sH" % num_words, packet[0:num_words*2]):
        total += chunk

    # Add any left over byte
    if len(packet) % 2:
        total += ord(packet[-1]) << 8

    # Fold 32-bits into 16-bits
    total = (total >> 16) + (total & 0xffff)
    total += total >> 16
    return (~total + 0x10000 & 0xffff)

代码示例

代码很难看,但返回正确的校验和。在我们的示例中,此代码返回根据wireshark正确的0x68db

代码语言:javascript
复制
#!/usr/local/bin/python3
# -*- coding: utf8 -*-

import struct

SOURCE_IP = "fe80000000000000020086fffe0580da"
DEST_IP = "fe80000000000000026097fffe0769ea"
TYPE_CODE = "8700"
REMAINDER = "00000000fe80000000000000026097fffe0769ea01010000860580da"


def calc_checksum(packet):
    total = 0

    # Add up 16-bit words
    num_words = len(packet) // 2
    for chunk in struct.unpack("!%sH" % num_words, packet[0:num_words*2]):
        total += chunk

    # Add any left over byte
    if len(packet) % 2:
        total += ord(packet[-1]) << 8

    # Fold 32-bits into 16-bits
    total = (total >> 16) + (total & 0xffff)
    total += total >> 16
    return (~total + 0x10000 & 0xffff)


def build_pseudo_header(src_ip, dest_ip, payload_len):
    source_ip_bytes = bytearray.fromhex(src_ip)
    dest_ip_bytes = bytearray.fromhex(dest_ip)
    next_header = struct.pack(">I", 58)
    upper_layer_len = struct.pack(">I", payload_len)
    return source_ip_bytes + dest_ip_bytes + upper_layer_len + next_header


def build_icmpv6_chunk(type_and_code, other):
    type_code_bytes = bytearray.fromhex(type_and_code)
    checksum = struct.pack(">I", 0)
    other_bytes = bytearray.fromhex(other)
    return type_code_bytes + checksum + other_bytes


def main():
    icmpv6_chunk = build_icmpv6_chunk(TYPE_CODE, REMAINDER)
    pseudo_header = build_pseudo_header(SOURCE_IP, DEST_IP, 32)
    icmpv6_packet = pseudo_header + icmpv6_chunk
    checksum = calc_checksum(icmpv6_packet)

    print("checksum: {:#x}".format(checksum))

if __name__ == '__main__':
    main()
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34244726

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档