ICMP Echo Request 报文说明
ICMP Echo Request PDU 看起来像这样:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type(8) | Code(0) | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
以下是上面 wiki 链接中各个字段的描述:
客户端可以使用标识符和序列号将回复与引起回复的请求相匹配。
在实践中,大多数 Linux 系统为每个 ping 进程使用一个唯一标识符,并且在该进程中序列号是一个递增的数字。 Windows 使用固定标识符(随 Windows 版本而异)和仅在启动时重置的序列号。
pyping代码说明
标题生成
查看send_one_ping 的完整函数体,这是您的代码的来源。我将用一些信息对其进行注释:
def send_one_ping(self, current_socket):
"""
Send one ICMP ECHO_REQUEST
"""
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
# Annotation: the Type is 8 bits, the code is 8 bits, the
# header checksum is 16 bits
# Additional Header Information is 32-bits (identifier and sequence number)
# After that is Payload, which is of arbitrary length.
所以这一行
header = struct.pack(
"!BBHHH", ICMP_ECHO, 0, checksum, self.own_id, self.seq_number
)
这一行使用struct 和布局!BBHHH 创建数据包头,这意味着:
-
B - 无符号字符(8 位)
-
B - 无符号字符(8 位)
-
H - 无符号短(16 位)
-
H - 无符号短(16 位)
-
H - 无符号短(16 位)
所以标题看起来像这样:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ICMP_ECHO | 0 | checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| self.own_id | self.seq_number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
注意这一点:
-
self.own_id 设置发送此数据的应用程序的标识符。对于此代码,它仅使用程序的程序标识符号。
-
self.seq_number 设置序列号。如果您要连续发送多个,这可以帮助您识别这是哪个 ICMP 请求数据包。它可以帮助您执行计算 ICMP 数据包丢失等操作。
客户端可以使用标识符和序列号字段组合来匹配回显回复和回显请求。
有效载荷生成
现在让我们转到有效负载部分。有效载荷的长度是任意的,但 Ping 类的这段代码默认取自 55 bytes 的总数据包有效载荷大小。
所以下面的部分只是创建了一堆任意字节来填充到有效负载部分。
padBytes = []
startVal = 0x42
# Annotation: 0x42 = 66 decimal
# This loop would go from [66, 66 + packet_size],
# which in default pyping means [66, 121)
for i in range(startVal, startVal + (self.packet_size)):
padBytes += [(i & 0xff)] # Keep chars in the 0-255 range
data = bytes(padBytes)
最后,byte(padBytes) 实际上是这样的:
>> bytes(padBytes)
b'BCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwx'
为什么选择0x42?
据我所知,0x42 作为有效载荷标识符没有实际意义,所以这似乎相当随意。这里的有效载荷实际上毫无意义。从 Payload Generation 部分可以看出,它只是生成一个连续的序列,实际上并没有任何意义。如果他们愿意,他们本可以决定用0x42 字节填充整个数据包有效负载。