Русский flag Русский

Routing traffic from a local subnet through a remote server (IPIP + Policy Routing)

Published on 2025-10-29

This guide will show how to configure two Linux servers so that all the Internet traffic from a specific local subnet (for example, 10.100.10.0/24) is routed not via its default gateway but through an IPIP tunnel to a remote server, which will then put that traffic onto the Internet.

This is useful if you need services in one subnet to go out to the world with the IP address of another server — for example, to bypass restrictions, centralize NAT, or hide the source.


Actors (example)

RoleNameExternal (WAN) IPWAN InterfaceInternal (Tunnel) IPNote
Server ALocal Gateway198.51.100.10eth010.254.0.1/30Gateway for subnet 10.100.10.0/24
Server BRemote Egress Node203.0.113.20eth010.254.0.2/30Egresses traffic to the Internet
  • Subnet to route: 10.100.10.0/24
  • Tunnel name: ipip0
  • Tunnel network: 10.254.0.0/30

Step 1: Configure Server B (Remote Egress Node)

Goal: Accept an IPIP tunnel from Server A and egress traffic from subnet 10.100.10.0/24 to the Internet via NAT using external IP 203.0.113.20.

1.1. Enable IP Forwarding

sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf

1.2. Tunnel, firewall and NAT setup

#!/bin/sh
set -e

TUN_NAME="ipip0"
REMOTE_IP="198.51.100.10"
LOCAL_IP="203.0.113.20"
TUN_NET="10.254.0.2/30"
TUN_PEER="10.254.0.1"
WAN_IFACE="eth0"
CLIENT_NET="10.100.10.0/24"

echo "[*] Создаю туннель $TUN_NAME"
ip tunnel del "$TUN_NAME" 2>/dev/null || true
ip tunnel add "$TUN_NAME" mode ipip remote "$REMOTE_IP" local "$LOCAL_IP" ttl 64
ip addr add "$TUN_NET" dev "$TUN_NAME"
ip link set "$TUN_NAME" up mtu 1480

echo "[*] Настраиваю iptables"

iptables -I INPUT -p 4 -s "$REMOTE_IP" -j ACCEPT
iptables -A FORWARD -i "$TUN_NAME" -s "$CLIENT_NET" -o "$WAN_IFACE" -j ACCEPT
iptables -A FORWARD -i "$WAN_IFACE" -d "$CLIENT_NET" -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -t nat -A POSTROUTING -s "$CLIENT_NET" -o "$WAN_IFACE" -j SNAT --to-source "$LOCAL_IP"

iptables -t mangle -A FORWARD -o "$WAN_IFACE" -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

echo "[OK] Сервер Б готов."

MTU is set to 1480 and TCPMSS clamp is enabled to avoid PMTU Discovery issues.


Step 2: Configure Server A (Local Gateway)

Goal: Redirect traffic from 10.100.10.0/24 that is destined to the Internet into the tunnel to Server B.

2.1. Enable IP Forwarding and adjust rp_filter

sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.conf.default.rp_filter=2

cat <<EOF >> /etc/sysctl.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
EOF

rp_filter=2loose mode, required for asymmetric routing.


2.2. Create IPIP tunnel

#!/bin/sh
set -e

TUN_NAME="ipip0"
REMOTE_IP="203.0.113.20"
LOCAL_IP="198.51.100.10"
TUN_NET="10.254.0.1/30"
TUN_PEER="10.254.0.2"

echo "[*] Поднимаю туннель $TUN_NAME"
ip tunnel del "$TUN_NAME" 2>/dev/null || true
ip tunnel add "$TUN_NAME" mode ipip remote "$REMOTE_IP" local "$LOCAL_IP" ttl 64
ip addr add "$TUN_NET" dev "$TUN_NAME"
ip link set "$TUN_NAME" up mtu 1480

echo "[OK] Туннель поднят."

2.3. Policy Routing

Add a routing table:

grep -q "100[[:space:]]tunnel_route" /etc/iproute2/rt_tables || echo "100 tunnel_route" >> /etc/iproute2/rt_tables

Add a default route:

ip route add default via 10.254.0.2 dev ipip0 table tunnel_route

Create the rule:

ip rule add from 10.100.10.0/24 table tunnel_route pref 1000
ip route flush cache

2.4. Configure Firewall

CLIENT_NET="10.100.10.0/24"
TUN_NAME="ipip0"

iptables -A FORWARD -s "$CLIENT_NET" -o "$TUN_NAME" -j ACCEPT
iptables -A FORWARD -i "$TUN_NAME" -d "$CLIENT_NET" -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t mangle -A FORWARD -o "$TUN_NAME" -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

echo "[OK] Firewall и policy routing настроены."

Step 3: Verification

Check the normal route:

ip route get 8.8.8.8

Expected:

8.8.8.8 via <your_gateway> dev eth0 src 198.51.100.10 ...

Check the route from the subnet:

ip route get 8.8.8.8 from 10.100.10.50

Expected:

8.8.8.8 from 10.100.10.50 via 10.254.0.2 dev ipip0 src 198.51.100.10 ...

🧩 Troubleshooting

1. Check the tunnel

ip addr show dev ipip0
ip -s tunnel show
ping -I ipip0 10.254.0.2

If ping fails — check that IPIP (protocol 4) is allowed in the firewall.


2. Check policy routing

ip rule show
ip route show table tunnel_route

There should be a line:

1000: from 10.100.10.0/24 lookup tunnel_route

3. Check NAT

iptables -t nat -L POSTROUTING -v -n

SNAT counters should increase when traffic is active.


4. Check external IP from inside the subnet

curl -4 ifconfig.me

Expected — 203.0.113.20.


5. MTU and MSS

If TCP stalls:

ip link show ipip0
ping -M do -s 1472 8.8.8.8

Set mtu 1480 and use --clamp-mss-to-pmtu.


6. tcpdump

tcpdump -ni ipip0
tcpdump -ni eth0 host 8.8.8.8

7. Temporary logging

iptables -I FORWARD 1 -j LOG --log-prefix "[FORWARD DROP] "
tail -f /var/log/kern.log

8. Check return path

traceroute -s 10.100.10.10 8.8.8.8

and on Server B:

traceroute -s 10.254.0.2 10.100.10.10

9. Save state

iptables-save > /etc/iptables/rules.v4
ip rule show > /etc/iprules.conf

⚙️ Additional recommendations

ItemRecommendation
MTUSet to 1480 and enable TCPMSS clamp
FirewallPrefer nftables on newer systems
PersistencyAutomate tunnel start with systemd
Monitoringtcpdump -i ipip0 shows tunnel traffic
SecurityRestrict IPIP access and add IPSec/WireGuard if needed

✅ Done

Now all Internet traffic from 10.100.10.0/24 egresses via Server B’s IP (203.0.113.20), while Server A continues to use its normal route.

This is a simple and reliable setup for centralized NAT, bypassing restrictions, or building an internal VPN on plain Linux.

Need help?

Get in touch with me and I'll help solve the problem

Related Posts