The FORWARD chain in iptables doesn’t actually route traffic; it decides what to do with packets that are already being routed by the kernel.
Let’s see it in action. Imagine you have a server acting as a gateway for two subnets: 192.168.1.0/24 on eth0 and 192.168.2.0/24 on eth1. You want hosts on 192.168.1.0/24 to reach hosts on 192.168.2.0/24, and vice-versa.
First, ensure IP forwarding is enabled on your kernel:
sysctl net.ipv4.ip_forward
# Expected output: net.ipv4.ip_forward = 1
If it’s 0, enable it temporarily:
sudo sysctl -w net.ipv4.ip_forward=1
And make it permanent by editing /etc/sysctl.conf and uncommenting or adding net.ipv4.ip_forward=1, then running sudo sysctl -p.
Now, let’s set up iptables. The FORWARD chain is where we intercept packets that have arrived on one interface and are destined for another.
# Allow established connections to pass through
sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow traffic from subnet 1 to subnet 2
sudo iptables -A FORWARD -i eth0 -o eth1 -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT
# Allow traffic from subnet 2 to subnet 1
sudo iptables -A FORWARD -i eth1 -o eth0 -s 192.168.2.0/24 -d 192.168.1.0/24 -j ACCEPT
# Optional: If you want to drop all other forwarded traffic
sudo iptables -P FORWARD DROP
Here’s what’s happening:
-
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT: This is crucial. When a connection is initiated from subnet 1 to subnet 2, the reply packet will come back from subnet 2. Without this rule, the reply would be dropped by the default policy or other rules. This rule says, "If this packet is part of an existing connection we’ve already allowed, or it’s a related connection (like ICMP error messages), let it through." It matches packets based on their connection tracking state. -
iptables -A FORWARD -i eth0 -o eth1 -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT: This rule explicitly permits packets originating from the192.168.1.0/24network (-s) that arrive oneth0(-i) and are destined for the192.168.2.0/24network (-d) and will exit viaeth1(-o). -
iptables -A FORWARD -i eth1 -o eth0 -s 192.168.2.0/24 -d 192.168.1.0/24 -j ACCEPT: This is the symmetric rule, allowing traffic in the opposite direction. -
iptables -P FORWARD DROP: This sets the default policy for theFORWARDchain toDROP. This means any packet that doesn’t match an explicitACCEPTrule in theFORWARDchain will be silently discarded. This is a good security practice for a gateway.
The kernel’s routing table determines if a packet can be routed from one interface to another. iptables on the FORWARD chain decides whether it should be allowed to pass through. Think of the routing table as the map and iptables as the border control.
A common misconception is that iptables is the router. It’s not. It’s a packet filtering and manipulation tool that sits alongside the kernel’s routing engine. The kernel’s routing decisions happen before the packet hits the FORWARD chain. If the kernel’s routing table doesn’t know how to reach the destination IP, the packet won’t even get to the FORWARD chain for iptables to inspect.
If you need to translate private IP addresses to public ones (Network Address Translation or NAT), you’d use the POSTROUTING chain in the nat table. The FORWARD chain only deals with packets that are already destined for a different network segment that the kernel knows how to reach.
You’ll next need to consider how to handle traffic originating from the gateway itself, which is governed by the OUTPUT chain.