UDP隧道解封装后TCP/UDP数据包无法被应用处理的问题排查
我需要将数据包封装到UDP隧道中,当前在Ubuntu 22.04上使用Python实现。隧道末端解封装后的数据包无法被UDP或TCP应用识别。
发送端代码
iface_name = "dummy1" # interface name ip_dst = "192.168.1.2" # IP destination s1 = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3)) s1.bind((iface_name, 0)) s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True: packet, _ = s1.recvfrom(65565) s2.sendto((packet), (ip_dst, 5556))
接收端代码
entering_ip = "192.168.1.2" # Should be the same as IP destination of encapsulation s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s1.bind((entering_ip, 5556)) tun = open("/dev/net/tun", "r+b", buffering=0) LINUX_IFF_TUN = 0x0001 LINUX_IFF_NO_PI = 0x1000 LINUX_TUNSETIFF = 0x400454CA flags = LINUX_IFF_TUN | LINUX_IFF_NO_PI ifs = struct.pack("16sH22s", b"tun0", flags, b"") ioctl(tun, LINUX_TUNSETIFF, ifs) while True: packet, _ = s1.recvfrom(65565) tun.write(packet[14:]) # the [14:] is to remove the ethernet header
验证情况
经Wireshark验证,dummy1接口流入的数据包与TUN接口流出的数据包L3(IP)、L4(TCP、UDP、ICMP)头及payload完全一致,仅L2(以太网)头存在差异。反向通信的类似配置可实现发送端ping通TUN后的IP,但无法发送TCP或UDP流量,TUN流出的数据包无法被接收应用处理。
已尝试的操作
- 确认两端ip_forward已启用,多次检查路由,Wireshark显示IP和TCP校验值正常;
- 接收端使用
socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)替代TUN:数据包出现在lo接口,MAC地址为00:00:00:00:00:00,无法被绑定到其他IP/接口的应用识别; - 接收端使用packet socket并修改以太网头的源/目标MAC地址:此时ping也无法正常工作;
- 接收端使用TAP替代TUN:无论保留或修改原以太网头均无效,ping也停止工作;
- 使用veth将接收的数据包转发到netns:Wireshark可在veth接口看到数据包,但netns内监听应用无法识别,也无法将应用绑定到veth。
接收端TCP应用对SYN包无响应,既无RST也无SYN/ACK;UDP应用无法接收到消息。
问题原因及解决办法
核心原因
Linux内核的**反向路径过滤(rp_filter)**是关键诱因。当解封装后的数据包从tun0接口进入内核时,内核会校验数据包源IP对应的路由入口是否与数据包进入的接口一致。若不一致,内核会判定为异常数据包并直接丢弃,导致上层TCP/UDP应用无法接收。而ping能正常工作,是因为ICMP的处理逻辑在部分场景下绕过了rp_filter的严格校验。
解决步骤
关闭tun0接口的反向路径过滤
临时生效命令:sysctl -w net.ipv4.conf.tun0.rp_filter=0永久生效:编辑
/etc/sysctl.conf文件,添加以下内容后执行sysctl -p加载配置:net.ipv4.conf.tun0.rp_filter=0配置正确的回程路由
确保接收端存在指向隧道另一端子网的路由,让回程流量能通过tun0接口返回隧道。例如,若隧道另一端子网为192.168.0.0/24,执行:ip route add 192.168.0.0/24 dev tun0检查tun0接口IP配置
确认tun0接口的IP与解封装后数据包的目标IP处于同一网段,或路由规则能正确将目标IP流量导向tun0接口。
内容的提问来源于stack exchange,提问作者Matt P

