Score:2

In Linux how to add a route to a prefix via a specific device with certain destinations in that prefix going via default route?

ke flag

I'm currently dealing with a VPN which connection endpoint lies within the subnet which prefix shall be tunneled via that specific VPN.

Essentially the problem thus boils down to match against a (larger) set of destination addresses (/16 mask), except for a wholly contained (small) subset of specific destinations that shall not routed that way (and instead go via the default route).

If the problem was about filtering, in Linux using this could be implemented using a ipset set up like

ipset create MyVPN hash:net
ipset add MyVPN $MYVPNNET/$MYVPNMASK
ipset add MyVPN $MYVPNENDPOINT nomatch

However such ipsets can only be used inside netfilter.

Now my question is, how I could set up something equivalent using Linux Advanced IP Routing, i.e. the ip route family of commands?

djdomi avatar
za flag
Questions seeking installation, configuration or diagnostic help must include the desired end state, the specific problem or error, sufficient information about the configuration and environment to reproduce it, and attempted solutions. Questions without a clear problem statement are not useful to other readers and are unlikely to get good answers.
datenwolf avatar
ke flag
@djdomi: This is not seeking help for a specific configuration. I'm looking for a way to define routes (in a generalized way) by removing certain subsets from route table rule. This is something that could be applied to all sorts of situations.
Score:2
cn flag

Routes are calculated on a most-specific-first approach. So if your small subnet needs the default route, put that first and then add the others:

Default Gateway in this example is 10.0.0.1 VPN is 10.0.0.2

ip route add 10.0.1.0/24 via 10.0.0.1
ip route add 10.2.0.0/16 via 10.0.0.2
ip route add default via 10.0.0.1

Would route traffic to the subnet (10.0.1.0) via default gateway, VPN via 10.0.0.2, and other default traffic via default gateway.

datenwolf avatar
ke flag
Yes, this is how most VPN scripts are doing it. But there's a problem: By adding a narrow route via a specific gateway, such a setup is incapable to seamlessly deal with changes of the default route. For example when switching networks in a roaming situation, or maybe even something as simple as docking your laptop (switching from W-LAN to wired LAN). This needlessly overcomplicates things, since if done as you suggested, there'd have to be a continuous tracking of networking state, detecting if specific routes have to be altered, and the routing tables being adjusted.
datenwolf avatar
ke flag
What I'm looking for is an equivalent of *ipset* `nomatch` to avoid all that hassle.
cn flag
Ah, okay - I understand! I'll leave the answer up to retain the comments, since they're important clarifications.
djdomi avatar
za flag
@shearn89 remind if it solves your issue, you need to accept it, else we will be remembered until the end of any decade...
cn flag
@djdomi I'm not the OP...
djdomi avatar
za flag
yupp nott scrolled enough above. @datawolf you were meaned with my comment
datenwolf avatar
ke flag
@djdomi: See the answer I wrote, regarding the method I figured out. I'll make that one the accepted answer after the grace period passed.
Score:2
ke flag

As it turns out, in Linux one can use ipset matching to select routing tables. The extra ingredient is using a netfilter rule that will match the outgoing packets and apply a fwmark to them, which then can be used to select a dedicated routing table. This dedicated routing table then will contain just a single default route through the VPN.

Here's in script form the general idea, how to set this up.

# Subnet(s) to route through the TUN

DSTNETS=.../.. .../..

# Exclude these destinations from routing through the TUN

EXCLUDE=... ...

# TUN device to use for this connection, and its parameters Those usually are supplied by the VPN daemon

TUNDEV=...
TUNADDR=.../..
TUNPEER=...

# Name for the ipset used to match destinations. Base on the TUN name

IPSET=${TUNDEV}ipset

# We need a netfilter fwmark and a iproute2 table. fwmarks and tables are 32 bit values, limited to the signed integer range, i.e. [0, 2³¹-1] fwmarks may be used as bitmasks signalling multiple flags, so depending on our needs either set a single particular bit (or few), or a very specific value that is then compared for equality.

FWMARK=0x...
TABLE=0x...

# Create the ipset and populate it with the IP address ranges and subnets to match to and not to.

ipset create $IPSET hash:net
for d in $DSTNETS ; do ipset add $IPSET $d ; done
for x in $EXCLUDE ; do ipset add $IPSET $x nomatch ; done

# Create a new iproute2 ruletable that will be used for route lookup for all packets that have our fwmark of choice set

ip rule add fwmark $FWMARK table $TABLE

# This is where the magic happens: Create a netfilter rule that will match packets originating on this host, for destinations that match the ipset we just created and mark them with our chosen fwmark. Due to the rule we just created before, those packets will then be routed using that specific table, instead the global ones.

iptables -t mangle -A OUTPUT -m set --match-set $IPSET dst -j MARK --set-mark $FWMARK

# Also add a netfilter rule to overwrite the source address for these packets since the destination network will likely reject them, if they don't match the address range used for the VPN

iptables -t nat -A POSTROUTING -m set --match-set $IPSET dst -j SNAT --to-source $INTERNAL_IP4_ADDRESS

# Now we can set up the actual TUN device. Strictly speaking those steps could have been done before, but then for a short time between the TUN coming up and establishing the routing rules some packets might have ended up in limbo

ip link set dev $TUNDEV up
ip addr add $TUNADDR peer $TUNPEER dev $TUNDEV 

# Finally establish a default route going through the TUN in our dedicated routing table which due to the fwmark rule will be hit only by packets matching to our ipset

ip route add default dev $TUNDEV table $TABLE

To tear everthing down, just follow the script in reverse, deleting stuff; the ipset can be destroyed with a single command.

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.