Boxes that route for a LAN and also dial a VPN end up in the same argument with themselves: the tunnel wants to own the default route, and the LAN needs it back. When OpenVPN connects it may install a default through the tunnel or a pair of half-defaults, which is right for a laptop and wrong for a gateway whose neighbours expect replies from its LAN address. The answer is not to skip the VPN. It is to give the two decisions different tables.

One default route, two owners

Forwarded traffic and locally originated traffic do not have to take the same path. LAN clients need the tunnel only for the destinations you choose, while the host's own management sessions and its replies to those clients need the LAN default. Mixing both into the main table forces a single answer onto two different questions.

OpenVPN has a switch for exactly this. route-noexec stops the client from touching the main table at all, and the values it would have used are exported to a route-up script as route_vpn_gateway, dev and the interface addresses. You then decide which table receives which route, in code you can read later.

Policy table versus rewriting main

ApproachWhat it buysWhat it costs
Separate table plus source ruleLAN traffic and host traffic keep independent defaultsEvery lookup now depends on rule order
Editing the main table onlyFewer moving parts on a single-purpose hostOne mistake takes the gateway offline entirely
Two half-default routesWorks for a full-tunnel clientAmbiguous on a host that must answer on its LAN address

The separate table is the only option that scales past one subnet. Name it once, then refer to it by name so nobody has to remember a number:

echo "10 vpn" >> /etc/iproute2/rt_tables
ip rule add from 192.168.50.0/24 table vpn
ip route add default via 10.8.0.1 dev tun0 table vpn

The addresses here are placeholders. Substitute the LAN prefix, the tunnel gateway and the device name from your own deployment before running anything.

The rule has to match the host too

A rule that matches only the LAN prefix covers forwarded packets. It does not cover traffic the gateway itself originates, and it does not cover the replies it sends back to LAN clients, which carry the gateway's LAN address as their source. Those lookups fall through to main and can still leave through the tunnel.

Add a second rule for the gateway's own LAN address, or widen the first rule to a set that includes both. The order matters: put the more specific rule first, and check the result with a lookup that names the source explicitly.

ip route get 203.0.113.9 from 192.168.50.1

If that reports the tunnel device for a destination that should stay local, the rule set is wrong regardless of what the table contains.

NAT or a route on the far side

The VPN server knows the tunnel subnet and nothing else. Packets arriving with a LAN source address have no return path unless someone provides one, and there are exactly two ways to provide it. Adding a route for the LAN prefix on the server keeps the original source visible end to end, which is what an audited design usually wants. Masquerading on the gateway rewrites the source to the tunnel address and needs no cooperation from the server.

iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o tun0 -j MASQUERADE

Choose NAT when you do not control the server and cannot add routes there. Choose the server-side route when logs, access control or return-path symmetry matter, because masquerading erases the original address from everything the far side records.

Either way, forwarding has to be enabled, and strict reverse-path filtering can drop the reply when main would not have chosen the tunnel for that source. Check the effective value before blaming the tunnel.

Making the split survive a reconnect

Hand-added rules and routes vanish when the interface is recreated, and a stale table entry can outlive the session that justified it. Put the table rule, the tunnel route and the masquerade entry inside the client's route-up script, and flush the table at the top of the script so an old gateway cannot survive into the new session.

Verify from both ends: a lookup on the gateway with the LAN source, and a real request from a LAN client to a destination that should traverse the tunnel. A route that exists but is never selected behaves exactly like no route at all until you read the counters.

Skyldar greinar