53 lines
1.7 KiB
Markdown
53 lines
1.7 KiB
Markdown
|
|
||
|
|
||
|
为了正确地计算 ICMP 报文的校验和 (checksum),我们需要遵循以下步骤:
|
||
|
|
||
|
1. **填充报文头部的校验和字段为 `0`**。
|
||
|
2. **将报文数据划分为 16 位块并求和**。
|
||
|
3. **如果报文长度为奇数,在末尾补零**。
|
||
|
4. **对总和取反,并将其结果存入校验和字段**。
|
||
|
|
||
|
以下是修改后的代码,增加了正确的 ICMP 校验和计算:
|
||
|
|
||
|
```python
|
||
|
# 计算校验和
|
||
|
def calculate_checksum(data):
|
||
|
checksum = 0
|
||
|
n = len(data)
|
||
|
|
||
|
# 按 16 位块划分
|
||
|
for i in range(0, n - 1, 2):
|
||
|
chunk = (data[i] << 8) + data[i + 1]
|
||
|
checksum += chunk
|
||
|
|
||
|
# 如果长度为奇数,补零
|
||
|
if n % 2 == 1:
|
||
|
checksum += data[-1] << 8
|
||
|
|
||
|
# 将 32 位总和折叠为 16 位
|
||
|
checksum = (checksum >> 16) + (checksum & 0xFFFF)
|
||
|
checksum += (checksum >> 16)
|
||
|
|
||
|
# 对总和取反
|
||
|
return ~checksum & 0xFFFF
|
||
|
|
||
|
|
||
|
# 构造 ICMP 报文
|
||
|
def construct_icmp():
|
||
|
# ICMP 报文头部: 类型(8), 代码(0), 校验和(0), 标识符(1), 序列号(1)
|
||
|
icmp_header = struct.pack('!BBHHH', 8, 0, 0, 1, 1)
|
||
|
data = b'Ping'
|
||
|
# 计算校验和
|
||
|
checksum = calculate_checksum(icmp_header + data)
|
||
|
# 填充校验和字段
|
||
|
icmp_header = struct.pack('!BBHHH', 8, 0, checksum, 1, 1)
|
||
|
return icmp_header + data
|
||
|
```
|
||
|
|
||
|
### 说明
|
||
|
- `calculate_checksum` 函数负责按 ICMP 的要求计算校验和。
|
||
|
- 数据部分(例如 `"Ping"`)被添加到报文头后计算校验和。
|
||
|
- 构造的 ICMP 报文将包含报文头和数据部分。
|
||
|
|
||
|
### 使用方法
|
||
|
使用这个改进后的 `construct_icmp` 函数可以确保生成的 ICMP 报文具有正确的校验和,从而能够在网络中正常解析和传输。
|