基于Scapy实现简单NAT时H2收到ICMP报文不回复问题排查
问题描述
- 网络拓扑:
H1(192.168.1.2)---S1---(192.168.1.1)H3(20.0.0.1)---S2---H2(20.0.0.2) - 实现目标:在H3设备上通过Scapy编写脚本实现简单ICMP NAT功能,让192.168.1.0网段的H1可以访问20.0.0.0网段的H2
- 原实现代码:
from scapy.all import * def handlePacket(pkt): if pkt.sniffed_on == "h3-eth0": if IP in pkt and ICMP in pkt: if pkt[IP].src=="192.168.1.2" and pkt[IP].dst == "20.0.0.2": pkt[IP].src="20.0.0.3" pkt[Ether].dst= None sendp(pkt,iface="h3-eth1") elif pkt.sniffed_on == "h3-eth1": if IP in pkt and ICMP in pkt: if pkt[IP].src=="20.0.0.2" and pkt[IP].dst=="20.0.0.3": pkt[IP].dst="192.168.1.2" pkt[Ether].dst= None sendp(pkt,iface="h3-eth0") if __name__ == "__main__": sniff(filter="icmp",iface=["h3-eth0","h3-eth1"],prn=handlePacket)
- 故障现象:H2的网卡可以抓到H1发过来的ICMP请求报文,但H2不会返回ICMP应答报文。h2-eth0接口抓包信息如下:
帧7:线路传输长度98字节(784比特),接口捕获长度98字节(784比特),捕获接口为h2-eth0,接口id为0
以太网II首部:源MAC地址为52:a8:79:2d:6f:cf,目的MAC地址为36:34:00:71:f8:51
IPv4首部:源IP地址为20.0.0.3,目的IP地址为20.0.0.2
根因分析
代码存在两个核心问题:
- 校验和未更新:Scapy默认不会主动覆盖已存在的校验和字段。代码修改了IP层源/目的地址后,没有删除原有IP头校验和、ICMP头校验和,发送的报文携带的是修改地址前计算的旧校验和。H2收到报文后,IP层校验和校验失败会直接丢弃报文,不会上传到ICMP层处理,因此不会返回应答。
- 代码缩进错误:原代码中处理h3-eth1接口报文的
elif分支前多了一个空格,缩进层级和上层判断h3-eth0接口的if分支不匹配,会直接触发语法错误,导致反向报文(H2返回给H1的报文)的NAT转换逻辑完全不生效。
修正方案
针对上述问题做以下修改:
- 修改IP地址字段后,主动删除IP层、ICMP层的旧校验和,Scapy发送报文时会自动计算填充正确的校验和
- 修正
elif分支的缩进错误,保证分支逻辑正常执行 - 关闭sniff的报文存储、sendp的冗余打印,降低运行资源占用
修正后的可运行代码如下:
from scapy.all import * def handlePacket(pkt): if pkt.sniffed_on == "h3-eth0": if IP in pkt and ICMP in pkt: if pkt[IP].src=="192.168.1.2" and pkt[IP].dst == "20.0.0.2": # 转换源IP pkt[IP].src="20.0.0.3" # 删除旧校验和,触发自动重算 del pkt[IP].chksum del pkt[ICMP].chksum # 清空二层目的MAC,让Scapy根据出接口自动解析 pkt[Ether].dst = None sendp(pkt, iface="h3-eth1", verbose=False) elif pkt.sniffed_on == "h3-eth1": if IP in pkt and ICMP in pkt: if pkt[IP].src=="20.0.0.2" and pkt[IP].dst=="20.0.0.3": # 转换目的IP pkt[IP].dst="192.168.1.2" # 删除旧校验和,触发自动重算 del pkt[IP].chksum del pkt[ICMP].chksum pkt[Ether].dst = None sendp(pkt, iface="h3-eth0", verbose=False) if __name__ == "__main__": sniff(filter="icmp", iface=["h3-eth0","h3-eth1"], prn=handlePacket, store=False)
补充说明:运行脚本前需要关闭H3设备自身的ICMP转发和应答功能,避免H3的内核协议栈提前处理报文干扰NAT逻辑。
内容的提问来源于stack exchange,提问作者ArmoredKitty
相关产品推荐
相关产品推荐

