Our computers can have multiple network devices: Ethernet, WiFi cards, virtual network devices, etc. On Linux when the network packets arrive to one of these devices, these packets are handled by device drivers and then put to networking stack. Then packets are dispatched to appropriate application to handle.
When the packets are sent, they are put to networking stack, e.g. via BSD sockets API. Then Linux networking stack uses a routing table to decide which network interface the packet will be sent to.
Routing table can be viewed with:
$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.1.1 0.0.0.0 UG 600 0 0 wlp3s0 0.0.0.0 0.0.0.0 0.0.0.0 U 1002 0 0 enp0s25 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 tun0 192.168.1.0 0.0.0.0 255.255.255.0 U 600 0 0 wlp3s0
$ ip route show default via 192.168.1.1 dev wlp3s0 proto static metric 600 default dev enp0s25 scope link metric 1002 linkdown 10.0.0.0/24 dev tun0 proto kernel scope link src 10.0.0.1 192.168.1.0/24 dev wlp3s0 proto kernel scope link src 192.168.1.125 metric 600
Although route command is deprecated , I still prefer it because of nicer output.
Let's take the output of route -n. Now what happens when we execute ping 22.214.171.124?
Well, Linux kernel
- matches address 126.96.36.199 with 0.0.0.0 (all addresses),
- selects route with the lowest Metric (600) because there are multiple routes to 0.0.0.0,
- looks up the gateway which is 192.168.1.1,
- matches gateway address with 192.168.1.0,
- looks up that destination 192.168.1.0 does not have gateway meaning that the network is directly connected somehow,
- does NAT (network address translation),
- writes packet to wlp3s0 interface,
- device driver sends packet to WiFi card.
Understanding routing table
Again, let's look at route -n output.
Genmask column is a mask applied (bitwise AND) to IP packet destination address. After Genmask is applied, Destination column is used to match packet destination. If the matched destination has Gateway, packet is forwarded there, otherwise it's written to network interface described by column Iface.
When packet destination address matches with multiple Destination entries, the one with the longest prefix wins. For example address 192.168.1.100 matches 0.0.0.0/0 and 192.168.1.0/24. But 192.168.1.0 prefix is 24 bits long, so this rule takes priority.
Flags column holds some metainfo about routes: U - route is up, G route has gateway.
Metric column is used to determine priority when multiple routes match packet destination address.
Ref column is not used in Linux kernel.
Manipulating routing table
We can use ip command to add, delete, modify routes. E.g. we can redirect packets to desired network interfaces:
# ip route add 188.8.131.52 via 10.0.0.2
This makes all packets with destination 184.108.40.206 be written to tun0 interface.
To delete route issue command:
# ip route del 220.127.116.11 via 10.0.0.2
We can test which Destination entry will be matched for given address:
$ ip route get 10.0.0.2 10.0.0.2 dev tun0 src 10.0.0.1 $ ip route get 18.104.22.168 22.214.171.124 via 192.168.1.1 dev wlp3s0 src 192.168.1.125 $ ip route get 192.168.1.100 192.168.1.100 dev wlp3s0 src 192.168.1.125
The difference between the last two routes is that NAT will be executed for packets destined to 126.96.36.199, while packets with destination 192.168.1.100 will remain intact.
We can change the route metric by adding new route with different metric and then deleting the old one:
# ip route add default via 192.168.1.1 dev wlp3s0 proto static metric 500 # ip route del default via 192.168.1.1 dev wlp3s0 proto static metric 600