How do you turn a Linux box into a router, and why is enabling ip_forward often not enough?
Show the answer
Say this first
By default Linux behaves as a host and drops packets that are not addressed to it. net.ipv4.ip_forward=1
lets it route IPv4 between interfaces; IPv6 has its own switch, net.ipv6.conf.all.forwarding=1.
sysctl -w changes the running value; a file in /etc/sysctl.d/ makes it survive a reboot. After that
the packets still have to pass the firewall FORWARD chain and rp_filter, and the far side needs a route back.
Go deeper
Check and set
$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0
$ sudo sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/90-router.conf
sudo sysctl --system # reload every sysctl file
cat /proc/sys/net/ipv4/ip_forwardThe checklist after forwarding is on
- Firewall FORWARD chain. Docker sets the filter FORWARD policy to DROP when it starts; hardened
images often do the same.
iptables -S FORWARDornft list ruleset. - rp_filter. With strict mode, packets that arrive on an interface the router would not use to reach their source are dropped silently.
- Return route. The next router or the server must know the way back to the source subnet, or you need NAT.
- NAT if the inside uses private addresses and the outside does not route them.
Who turns it on for you
Docker, libvirt and Kubernetes components set ip_forward=1 when they start, which is why a host can
suddenly start routing after someone installs a container runtime.
They will ask next
Why can enabling IPv6 forwarding break the router's own IPv6 default route?
With forwarding on, the kernel acts as a router and ignores Router Advertisements unless the interface has
accept_ra=2. A box that learned its default route from RAs loses it. Setnet.ipv6.conf.ens192.accept_ra=2or configure the route statically.A PC behind the box can ping both of the router's interfaces but nothing beyond. Does that prove forwarding works?
No. Both interface addresses belong to the router itself, so those pings are delivered locally and never forwarded. Linux answers for any of its addresses on any interface. Test with a host beyond the router.
Traps
- Using
sysctl -wand forgetting to persist it. The router stops routing after the next reboot. - Assuming forwarding is the whole job. The FORWARD chain policy is the next most common reason.