计算ICMPv6磁头的16位校验和

2024-05-29 11:38:34 发布

您现在位置:Python中文网/ 问答频道 /正文

我想问一下我根据ICMPv6协议计算16位校验和的解决方案是否正确。我试着跟随Wikipedia,但主要有两件事我不确定。在

首先是the packet length是什么意思-它是没有校验和的整个ICMPv6包的数据包长度,还是只有有效负载?它和IPv6一样是八位字节吗?这个ICMPv6回显请求的长度是多少?在

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)。在

Following this pseudo header, the checksum is continued with the ICMPv6 message in which the checksum is initially set to zero. The checksum computation is performed according to Internet protocol standards using 16-bit ones' complement summation, followed by complementing the checksum itself and inserting it into the checksum field

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

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

^{pr2}$

对于以下ICMPv6 ping请求(具有IPv6标头):

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

它可以输出:

27741 37794
0xe672 # correct?
0b1111111111111111

所以我应该把xxxx替换为e672。对吗?当我试图用wireshark计算这个值时,我得到了不同的答案。在


Tags: thepacketisthis校验lengthipv6checksum
1条回答
网友
1楼 · 发布于 2024-05-29 11:38:34

我会用一个例子来回答你的问题。在

让我们从Wireshark wiki获取this sample capture,这样我们就有了相同的包,在Wireshark中打开它,让我们获取第一个ICMPv6包(第3帧)。在

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

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

构建伪标题

要计算校验和,首先要根据RFC 2460 section 8.1构建伪头。在

校验和是在伪报头ICMPv6数据包上计算的。在

The IPv6 version of ICMP [ICMPv6] includes the above pseudo-header in its checksum computation

我们需要构建伪标题:

  • 源IP
  • 目标IP
  • 上层包长度
  • 下一个标题

源IP和目标IP来自IPv6层。在

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

The Next Header field in the pseudo-header for ICMP contains the value 58, which identifies the IPv6 version of ICMP.

上层包长度:

The Upper-Layer Packet Length in the pseudo-header is the length of the upper-layer header and data (e.g., TCP header plus TCP data). Some upper-layer protocols carry their own length information (e.g., the Length field in the UDP header); for such protocols, that is the length used in the pseudo- header. Other protocols (such as TCP) do not carry their own length information, in which case the length used in the pseudo-header is the Payload Length from the IPv6 header, minus the length of any extension headers present between the IPv6 header and the upper-layer header.

在我们的例子中,上层(ICMPv6)不携带长度字段,所以在这种情况下,我们必须使用IPv6层的有效负载长度字段,对于这个数据包,这个字段是32(0x20)。在

让我们尝试一些代码:

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

代码应该这样调用:

^{pr2}$

构建ICMPV6数据包

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

For computing the checksum, the checksum field is first set to zero.

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

TYPE_CODE = "8700"
REMAINDER = "00000000fe80000000000000026097fffe0769ea01010000860580da"

正在为校验和计算构建数据包的ICMPv6部分:

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

名称如下:

TYPE_CODE = "8700"
REMAINDER = "00000000fe80000000000000026097fffe0769ea01010000860580da"
icmpv6_chunk = build_icmpv6_chunk(TYPE_CODE, REMAINDER)

计算校验和

根据RFC 1701计算校验和。Python中的主要困难是将和包装成16位的数量。在

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

Python示例:

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)

代码示例

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

#!/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()

相关问题 更多 >

    热门问题