How to Route Packets to Specific Interfaces Using iptables MARK and Policy-Based Routing


1 views

When dealing with multiple network interfaces on a Linux system, we often need to route traffic from specific applications through designated interfaces. In this case, we want to:

  • Route traffic from UID 1002 through tun0 (OpenVPN tunnel)
  • Send all other traffic through eth1 (default internet)

Before implementing any changes, let's examine the existing network configuration:

# Current routing table
ip route show
default via 192.168.1.254 dev eth1 metric 203
10.32.0.49 dev tun0 proto kernel scope link src 10.32.0.50
85.17.27.71 via 192.168.1.254 dev eth1
192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.73 metric 203

Here's the complete solution that actually works:

1. Packet Marking in Mangle Table

iptables -t mangle -A OUTPUT -m owner --uid-owner 1002 -j MARK --set-mark 11

2. Create Custom Routing Table

echo "11 vpn_route" >> /etc/iproute2/rt_tables

3. Configure Routing Rules

# Add route for VPN traffic
ip route add default via 10.32.0.49 dev tun0 table vpn_route

# Add policy routing rule
ip rule add fwmark 11 lookup vpn_route

4. NAT Configuration (Essential for VPN)

iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

To ensure everything works as expected:

# Check marked packets
iptables -t mangle -L -v -n

# Verify routing rules
ip rule show

# Test with specific user
sudo -u user1002 curl ifconfig.me

If traffic still goes through the wrong interface:

  1. Verify the VPN tunnel is active (ip addr show tun0)
  2. Check packet counters in iptables
  3. Test routing with: ip route get to 8.8.8.8 mark 11
  4. Ensure reverse path filtering is disabled: sysctl -w net.ipv4.conf.all.rp_filter=2

For persistence across reboots, add these commands to your network initialization scripts (e.g., /etc/rc.local or network manager hooks). The exact method depends on your Linux distribution.

# For Debian/Ubuntu systems, consider adding to /etc/network/interfaces
post-up ip route add default via 10.32.0.49 dev tun0 table vpn_route
post-up ip rule add fwmark 11 lookup vpn_route

When dealing with multiple network interfaces on Linux systems, proper traffic routing becomes crucial. In this scenario, we have:

eth1 - Primary internet interface (192.168.1.73/24)
tun0 - OpenVPN tunnel interface (10.32.0.50)

We need to ensure:

  • Processes owned by UID 1002 route through tun0
  • All other traffic uses eth1
  • Must maintain existing default routes

Step 1: Marking Packets

First, we mark packets from UID 1002 in the OUTPUT chain:

iptables -t mangle -A OUTPUT -m owner --uid-owner 1002 -j MARK --set-mark 11

Step 2: Policy Routing Setup

Create a new routing table and add rules:

# Create new routing table
echo "11 vpntable" >> /etc/iproute2/rt_tables

# Add route to VPN interface
ip route add default via 10.32.0.49 dev tun0 table vpntable

# Add policy rule for marked packets
ip rule add fwmark 11 table vpntable priority 1000

Step 3: NAT Configuration

For proper masquerading through the VPN:

iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

To test the configuration:

# As normal user
sudo -u user1000 wget -qO- ifconfig.me
# Should show eth1 IP

# As VPN user
sudo -u user1002 wget -qO- ifconfig.me
# Should show VPN endpoint IP

If traffic isn't routing correctly:

  1. Check packet marking: iptables -t mangle -vL
  2. Verify routing rules: ip rule list
  3. Inspect routing tables: ip route show table vpntable
  4. Enable logging for debugging: iptables -t mangle -A OUTPUT -m owner --uid-owner 1002 -j LOG --log-prefix "VPN-ROUTE: "

For a complete solution, you might want to:

  • Add persistent configuration in /etc/network/interfaces or equivalent
  • Consider DNS routing through the VPN
  • Implement connection state tracking for established sessions