I’m running a scenario with a host (192.168.222.1) and a virtual machine (192.168.222.130, interface ens33). On the VM, I have a Python program that creates a TUN interface (tunX) and, upon receiving an ICMP echo request packet, generates and sends back an echo reply via tunX.
The program code looks like this:
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()
On the VM, I also set up a tc ingress filter on ens33 to redirect ICMP packets coming from the host to 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
My expectation was that this filter would work in both directions, i.e., it would handle the ping from the host to the VM (echo request) and the echo reply from the TUN interface back to ens33. However, packet captures show the following:
On ens33, only ICMP echo requests are observed:
19:16:46.496830 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1095, length 64
19:16:47.536950 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1096, length 64
19:16:48.576889 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1097, length 64
19:16:49.616997 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1098, length 64
19:16:50.667469 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1099, length 64
19:16:51.696889 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1100, length 64
19:16:52.737452 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1101, length 64
On tunX, both the echo request and the echo reply (generated by the program) are visible:
19:16:46.496845 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1095, length 64
19:16:46.497030 IP 192.168.222.130 > 192.168.222.1: ICMP echo reply, id 1003, seq 1095, length 64
19:16:47.536964 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1096, length 64
19:16:47.537184 IP 192.168.222.130 > 192.168.222.1: ICMP echo reply, id 1003, seq 1096, length 64
19:16:48.576902 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1097, length 64
19:16:48.578469 IP 192.168.222.130 > 192.168.222.1: ICMP echo reply, id 1003, seq 1097, length 64
19:16:49.617014 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1098, length 64
19:16:49.617153 IP 192.168.222.130 > 192.168.222.1: ICMP echo reply, id 1003, seq 1098, length 64
19:16:50.667486 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1099, length 64
19:16:50.667623 IP 192.168.222.130 > 192.168.222.1: ICMP echo reply, id 1003, seq 1099, length 64
19:16:51.696902 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1100, length 64
19:16:51.697047 IP 192.168.222.130 > 192.168.222.1: ICMP echo reply, id 1003, seq 1100, length 64
19:16:52.737470 IP 192.168.222.1 > 192.168.222.130: ICMP echo request, id 1003, seq 1101, length 64
This means that while the echo requests are being correctly redirected from ens33 to tunX, the echo replies headed from tunX aren’t reaching ens33.
I have the following entries in the routing table (output of ip route show
):
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
My Questions:
- Is it correct to assume that the tc filter should operate bidirectionally?
- What could be causing the echo replies to not be redirected back to ens33, and how could I solve the problem?
Thank you, everyone.