You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

根因分析

代码存在两个核心问题:

  1. 校验和未更新:Scapy默认不会主动覆盖已存在的校验和字段。代码修改了IP层源/目的地址后,没有删除原有IP头校验和、ICMP头校验和,发送的报文携带的是修改地址前计算的旧校验和。H2收到报文后,IP层校验和校验失败会直接丢弃报文,不会上传到ICMP层处理,因此不会返回应答。
  2. 代码缩进错误:原代码中处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 04:33:19