tc过滤器重定向故障:ICMP Ping回复单向不通的技术问询
问题背景
我搭建了主机(192.168.222.1)与虚拟机(192.168.222.130,网卡ens33)的测试环境。在虚拟机上,用Python程序创建TUN接口tunX,该程序接收ICMP回显请求后生成回显回复并通过tunX发送。
Python程序代码
import os import fcntl import struct from pyroute2 import IPRoute # Constants for TUN interface TUNSETIFF = 0x400454ca IFF_TUN = 0x0001 IFF_NO_PI = 0x1000 def create_tun_interface(name='tunX'): # Open the TUN device file using os.open tun_fd = os.open('/dev/net/tun', os.O_RDWR) # Create the TUN interface ifr = struct.pack('16sH', name.encode('utf-8'), IFF_TUN | IFF_NO_PI) fcntl.ioctl(tun_fd, TUNSETIFF, ifr) return tun_fd def configure_interface(name='tunX', address='10.0.0.10/24'): ip = IPRoute() idx = ip.link_lookup(ifname=name)[0] ip.addr('add', index=idx, address=address.split('/')[0], mask=int(address.split('/')[1])) ip.link('set', index=idx, state='up') def respond_to_ping(tun_fd): while True: # Read a packet from the TUN interface packet = os.read(tun_fd, 2048) # Check if it's an ICMP echo request (ping) if packet[20] == 8: # ICMP type 8 is Echo Request # Create an ICMP Echo Reply response = packet[:20] + b'\x00' + packet[21:] # Swap source and destination IP addresses response = response[:12] + response[16:20] + response[12:16] + response[20:] # Write the response packet back to the TUN interface os.write(tun_fd, response) def main(): tun_fd = create_tun_interface() configure_interface() try: print("tunX interface created and configured. Listening for ICMP packets...") respond_to_ping(tun_fd) except KeyboardInterrupt: print("Stopping...") finally: os.close(tun_fd) if __name__ == '__main__': main()
tc入口过滤器配置
在虚拟机的ens33网卡上配置了tc入口过滤器,将来自主机的ICMP数据包重定向至tunX:
sudo tc qdisc add dev ens33 handle ffff: ingress sudo tc filter add dev ens33 parent ffff: protocol ip prio 1 flower ip_proto icmp src_ip 192.168.222.1 action mirred egress redirect dev tunX
现象与路由表
抓包结果显示:
- ens33上仅能观测到ICMP回显请求;
- tunX上可观测到回显请求和程序生成的回显回复。
虚拟机路由表条目:
default via 192.168.222.2 dev ens33 proto dhcp src 192.168.222.130 metric 100 10.0.0.0/24 dev tunX proto kernel scope link src 10.0.0.10 192.168.222.0/24 dev ens33 proto kernel scope link src 192.168.222.130 metric 100
问题
- 是否可以认为tc过滤器应当双向工作?
- 导致回显回复无法从tunX重定向至ens33的原因是什么?如何解决该问题?
解答
问题1:tc过滤器是否应当双向工作?
不能。你配置的是**ingress方向(入口)**的tc过滤器,仅针对从ens33网卡进入虚拟机的数据包生效,完全不处理从虚拟机内部向外发送(egress方向)的数据包。tc的队列规则(qdisc)和过滤器是严格区分方向的,ingress只处理入站流量,egress只处理出站流量,不存在默认的“双向工作”逻辑。
问题2:回显回复无法重定向的原因及解决方法
原因
- 路由与IP不匹配:程序生成的回复数据包源IP是
tunX的10.0.0.10,目标IP是主机的192.168.222.1。主机没有到10.0.0.0/24网段的路由,即使数据包发送到主机,主机也无法回包;同时内核根据路由表会将回复包从ens33出站,但你的tc过滤器仅在ens33的ingress方向,不会处理这个出站流量。 - 缺少反向过滤规则:没有配置针对
tunX出站或ens33出站方向的过滤器,无法确保回复数据包正确从ens33发送回主机。
解决方法
方案一:修改Python程序+添加出站tc规则(推荐)
调整回复数据包的源IP
将回复包的源IP改为虚拟机ens33的IP(192.168.222.130),这样主机收到后可以直接回包。修改respond_to_ping函数中的IP替换逻辑:def respond_to_ping(tun_fd): while True: packet = os.read(tun_fd, 2048) if packet[20] == 8: response = packet[:20] + b'\x00' + packet[21:] # 将源IP替换为ens33的192.168.222.130(网络字节序) src_ip = struct.pack('!I', 0xc0a8de82) # 目标IP保留主机的192.168.222.1(从原包中提取) dst_ip = packet[12:16] response = response[:12] + src_ip + dst_ip + response[20:] os.write(tun_fd, response)添加ens33出站方向的tc规则
先添加egress方向的队列规则,再配置过滤器匹配来自tunX的ICMP回复:# 为ens33添加出站qdisc sudo tc qdisc add dev ens33 root handle 1: prio # 添加过滤器,匹配源IP为10.0.0.10的ICMP包,重定向到ens33出站 sudo tc filter add dev ens33 parent 1: protocol ip prio 1 flower ip_proto icmp src_ip 10.0.0.10 action mirred egress redirect dev ens33
方案二:不修改程序,添加主机路由(需主机配合)
如果不想修改Python程序,可以在主机上添加到10.0.0.0/24网段的路由,让主机知道通过虚拟机IP回包:
sudo ip route add 10.0.0.0/24 via 192.168.222.130
同时同样需要添加方案一中的ens33出站tc规则,确保回复包能从ens33发送出去。
内容的提问来源于stack exchange,提问作者Andy R
相关产品推荐
相关产品推荐

