I know this question is kind of old, but I just spent hours piecing this together, so in case it helps others who find their way here, I'll take a stab at a not-too-technical answer.
There are a few things you need to understand in order to make sense of these commands and what they do.
First off, if you have a VPN client running on your machine capturing traffic and directing it through the VPN, you do have custom routing rules already. If you're connecting to a VPN service, as opposed to a VPN server under your own control, the remote service will typically push routing rules to your machine via openvpn when you connect. You can see them by issuing ip route show
while the vpn is connected. All the lines that contain tun0
are referring to your VPN connection.
That tun0
is key. From the kernel's point of view, your network card (eth0
or enp1s0
or whatever) is one network interface, and the VPN connection is a second, 'virtual', network interface. If you issue ip address
while the VPN is up, you can see both of them are defined with their own separate IP addresses. Any traffic that gets routed over the tun0
virtual network interface goes out over the VPN, and any traffic that gets routed over the eth0
interface goes out directly, bypassing the VPN.
As mentioned in the openvpn wiki, traffic generated on your machine will have its 'from' IP address set to the device you're routing traffic to by default, which means the VPN, which means the IP address assigned to tun0
. But! When connections come in on the regular physical network device (ie not over the VPN), the replies will have that device's IP address as their 'from' address. Now, the routing rules pushed to you by the VPN provider don't care, they just see outgoing traffic and redirect it to the VPN over tun0
and that's that. So you try to ssh in to the box, it gets the request and answers it, but the answer goes out through the VPN. Then your ssh client goes "whoa now, that reply is coming from a totally different IP address than where I'm trying to connect to" and ignores it. Thus you can't establish a connection inbound to the machine running the VPN client, which is where the ip rule
and ip route
commands come in.
What these commands are setting up is policy-based routing, which is a complex topic. As simply as possible, the kernel uses a 'routing table' to differentiate what traffic stays here on this machine (this is the network device lo
, short for 'loopback', which has IP address 127.0.0.1), what traffic is on the same LAN and thus gets sent direct to the destination without going through a router (also known as a gateway), and traffic going elsewhere. In order to have internet access, your machine has to have a 'default gateway' routing entry that directs all 'elsewhere' traffic to a router that is connected to the outside world. All of this takes place in the default routing table, which is usually the only one you need to be concerned about.
But the VPN routing setup has rewritten your default routing table to set the VPN connection (the tun0
device) as your default route for all traffic headed out to 'elsewhere'. Luckily, Linux allows us to have multiple routing tables, and pick which routing table will apply to what traffic based on various rules—aka policies—hence "policy-based routing".
Now let's look at the specific ip
commands in question, given the context above:
ip rule add table 128 from 192.168.10.123
This creates a policy-based-routing rule saying that any traffic with a 'from' address of 192.168.10.123 shall be routed according to routing table 128 instead of the default routing table. [1]
ip route add table 128 to 192.168.10.0/24 dev eth0
This adds a route in table 128 that directs all traffic with a 'to' address in the local 192.168.10.* subnet to eth0
.
ip route add table 128 default via 192.168.10.1
This sets the default route in table 128 to use your regular local gateway instead of the VPN's gateway.
Now any traffic that's in reply to a connection on eth0
(and thus automatically has a 'from' address of 192.168.10.123
) will be looked up in table 128 first. Table 128 says, in effect, if the destination is here on the LAN just route directly via eth0
, otherwise route via the normal non-VPN gateway 192.168.10.1
. But the overall default for traffic originating on this machine is to get its 'from' address from tun0
because that's the default interface in your default routing table. All of that traffic won't match the from 192.168.10.123
rule, so table 128 doesn't apply and it'll fall through to table 'default', which says to route it via the VPN.
Note: There's nothing special about 128 here, you're simply creating a new routing table with an arbitrarily chosen number. The default routing table is number 253, and you're free to use any number from 1 to 252 for your custom table(s).
- Technically, it just checks packets against each table's policy rules in numerical order and routes them according to the first match, so 128 gets consulted before 252, and packets that get sent to a custom routing table and don't match any of the routes in it will fall through and get checked against subsequent rules, and ultimately find their way to the default routing table if nothing else matches, but since table 128 has a default route in it, all packets that get directed to that table are guaranteed not to fall through.