A Linux box with two NICs is supposed to route traffic between two subnets, but packets arriving on one interface are never seen leaving the other. What does the sysctl `net.ipv4.ip_forward` control, and how do you set it so the change survives a reboot?
answer
- host by default, router by choice
- one switch changes the personality
- /proc/sys mirrors every sysctl
- runtime change dies at reboot
- routes must exist on both sides too
basics
~20 sBy default Linux behaves as a host, not a router: it drops IP packets that are not addressed to it. Setting net.ipv4.ip_forward=1 makes the kernel forward them between interfaces. Persist it in a file under /etc/sysctl.d/ and apply with sysctl --system.
solid answer
~40 sOut of the box the kernel is a host: a packet whose destination address belongs to no local interface is dropped rather than passed along. `net.ipv4.ip_forward` flips that policy on — it is a global switch that also sets every interface's `net.ipv4.conf.<iface>.forwarding` knob, and per-interface knobs let you be selective. Turn it on at runtime with `sysctl -w net.ipv4.ip_forward=1`, which writes `/proc/sys/net/ipv4/ip_forward` and is lost on reboot; make it permanent by dropping a line into a file such as `/etc/sysctl.d/99-router.conf` and running `sysctl --system`. IPv6 has no `ip_forward` alias — you set `net.ipv6.conf.all.forwarding` instead, and doing so also stops the host from accepting router advertisements. Forwarding alone is not enough end to end: both networks still need routes back toward this box, and the firewall's forward policy must permit the traffic.
code
bash · 10 lines# inspect the current setting
sysctl net.ipv4.ip_forward
cat /proc/sys/net/ipv4/ip_forward
# enable now (runtime only, lost on reboot)
sysctl -w net.ipv4.ip_forward=1
# make it permanent and apply it immediately
echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-router.conf
sysctl --systemgo deeper
Know that Linux does not route between its interfaces by default, and that net.ipv4.ip_forward=1 turns it on. Be able to read the current value from /proc/sys/net/ipv4/ip_forward.
Explain the host-versus-router distinction, the relationship between the global switch and the per-interface forwarding knobs, and how to persist a sysctl in /etc/sysctl.d with sysctl --system.
Treat the sysctl as one item on a checklist: return routes on both subnets, the firewall's forward policy, and rp_filter dropping asymmetric traffic. Insist that persisted configuration matches the running kernel.
Decide whether general-purpose hosts should ever forward at all, and how router-role settings are delivered and audited fleet-wide, so a machine's network personality is declared in configuration rather than typed on a console.
## Host versus router Every IP stack has to decide what to do with a packet whose destination address is not one of its own. A *host* discards it. A *router* looks the destination up in its routing table and sends it onward out of another interface. Linux ships configured as a host, which is the safe default for the overwhelming majority of machines — a laptop or an application server has no business quietly relaying somebody else's traffic between networks. `net.ipv4.ip_forward` is the switch between those two personalities. With it at 0, a packet arriving on eth0 and destined for a network behind eth1 is dropped after the routing decision determines it is not for us. With it at 1, the kernel performs a normal FIB lookup on the destination, decrements the TTL, and transmits the packet out of the chosen interface. ## Reading and setting it Every sysctl is a file under `/proc/sys`, with dots in the name mapping to directory separators: ```bash cat /proc/sys/net/ipv4/ip_forward # 0 or 1 sysctl net.ipv4.ip_forward # same value, sysctl syntax sysctl -w net.ipv4.ip_forward=1 # runtime change, lost on reboot ``` For persistence, write it into a configuration file rather than into a startup script: ```bash echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-router.conf sysctl --system # reload all sysctl.d files now ``` `/etc/sysctl.conf` still works and is still read, but per-purpose drop-in files under `/etc/sysctl.d/` are the modern convention: they are easier to package, easier to override, and their numeric prefixes make ordering explicit. `sysctl --system` reloads every configuration file so the running kernel matches what a reboot would produce — a useful habit, because a value that only ever worked because someone typed `sysctl -w` on the console is a latent outage. ## The global switch and the per-interface knobs For IPv4 there are two layers. `net.ipv4.conf.<iface>.forwarding` exists for each interface, and `net.ipv4.ip_forward` is a global alias: writing it sets `conf.all.forwarding` and pushes the value onto every existing interface. That is why the coarse switch appears to "win" over anything you set per-interface beforehand. If you need one interface routing and another not, set the per-interface values after the global one, or leave the global off and enable only the interfaces you mean. IPv6 has no `net.ipv6.ip_forward`; the equivalent is `net.ipv6.conf.all.forwarding`. It carries an extra consequence worth knowing: a Linux box that forwards IPv6 stops behaving like an endpoint and by default no longer accepts router advertisements, so enabling forwarding on a machine that was autoconfiguring its own address and default route can knock out that host's own connectivity. The knob `net.ipv6.conf.<iface>.accept_ra` set to 2 restores acceptance while forwarding. ## Forwarding is necessary, not sufficient Candidates who have only ever followed a tutorial tend to stop at the sysctl. Three other things must hold before traffic actually flows end to end: - **Return routes.** Hosts on subnet A must know that subnet B is reached via this box, and vice versa — usually a static route on each side or a default route pointing here. Forwarding fixes the middle of the path, never the ends. - **Firewall policy.** Forwarded packets traverse the kernel's forward hook, and if the policy there is to drop, they die regardless of the sysctl. Configuring that policy is a separate subject, but "I would check the forward path in the firewall too" belongs in your answer. - **Address translation, if the far side does not know your networks.** Forwarding moves packets with their original addresses intact. Rewriting them is a distinct mechanism, not something `ip_forward` does. ## Reverse-path filtering, the adjacent trap A related sysctl, `net.ipv4.conf.<iface>.rp_filter`, makes the kernel validate that a packet's *source* address would route back out of the interface it arrived on. In strict mode (1) an asymmetrically routed packet is dropped silently. On a multi-homed router this produces the maddening symptom of forwarding being enabled, routes looking correct, and traffic still disappearing without a log line. Note that the kernel uses the maximum of the `all` and per-interface values, so setting only the per-interface one to 0 does not relax it if `all` is still 1. ## The check to run When forwarding "does not work", verify in this order: is the sysctl actually 1 in `/proc`; does `ip route get <dst>` on the router produce the interface you expect; does the sending host have a route to the far subnet; and is the far subnet's return route pointing back here. Most reported forwarding failures are missing return routes, not a missing sysctl.
- You set the sysctl at runtime and forwarding works, but it breaks after the next reboot. Why?`sysctl -w` writes only to the running kernel through /proc; nothing on disk changed. Persist the setting in a drop-in file such as /etc/sysctl.d/99-router.conf and apply it with `sysctl --system`, which reloads every configuration file so the live value matches what boot would produce.
- How does enabling forwarding differ for IPv6?There is no net.ipv6.ip_forward alias — you set net.ipv6.conf.all.forwarding. Enabling it also makes the host stop accepting router advertisements by default, so a box that autoconfigured its own address and default route can lose its own connectivity. Setting accept_ra to 2 on the relevant interface restores acceptance while forwarding.
- Forwarding is on and routes look right, yet packets still vanish with nothing in the logs. What else would you check?Reverse-path filtering. With net.ipv4.conf.<iface>.rp_filter in strict mode the kernel silently drops packets whose source address would not route back out of the arrival interface — common on asymmetric or multi-homed paths. The kernel takes the maximum of the `all` and per-interface values, so relaxing only one of them has no effect.
saying these in an interview costs you the question
- Thinking ip_forward also performs address translation
- Setting it with sysctl -w and calling the change permanent
- Assuming forwarding alone makes both subnets reach each other
- Looking for an ip_forward sysctl under net.ipv6
- Ignoring rp_filter when packets disappear without a log entry