When one device wants to send a packet to another, it has two options:
The first thing the sending device needs to workout is, is the recipient on the same subnet as me?
To do this it uses the IP address and netmask that were defined when the interface was configured to workout the range of addresses that are on the same subnet as it.
E.g.
eth0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500inet 10.0.0.7 netmask 255.255.255.0 broadcast 10.0.0.255ether b8:27:eb:28:f5:e0 txqueuelen 1000 (Ethernet)
Using one of the various tools we can workout that this means, any IP in the range 10.0.0.1–10.0.0.254 are on the same subnet as us
If we know that a recipient is on the same subnet as us then we need to find out its MAC address, this is done using ARP requests.
The Address Resolution Protocol (ARP) is a protocol for mapping IP addresses to MAC addresses.
A device can issue an ARP request that asks, “Who has IP address X?”, this is sent to the broadcast address, meaning it’s delivered to every device on the subnet.
All the recipients inspect the message and discard the message unless they own the IP address in question. The owner of the IP address replies saying “IP address X is at MAC address Y”.
This response is normally then cached so that next time the sender doesn’t need to perform the lookup again.
We can generate some packets using ping
and see what happens with WireShark
pi@raspberrypi:~$ ping 10.0.0.4PING 10.0.0.4 (10.0.0.4) 56(84) bytes of data.64 bytes from 10.0.0.4: icmp_seq=1 ttl=64 time=1.96 ms64 bytes from 10.0.0.4: icmp_seq=2 ttl=64 time=0.932 ms
10.0.0.4
is on the same subnet as 10.0.0.7/24
and so the first step is to issue an ARP request to find the MAC address of 10.0.0.7
10.0.0.4
sees the ARP request is for itself, and responds indicating that the MAC address corresponding to that IP is it’s own.
Now the sender knows the MAC address of the recipient it can package up the message and send it to the recipient.
If the sender determines the recipient isn’t on the same subnet, it needs to pass the message onto someone else who can get it to the destination.
Route tables provide information on where to send packets depending on their destination.
More specific routes are preferred over more general and there’s normally a default route for packets that match none of rules.
The ip route
command on Linux shows the systems route table
router:~ $ ip routedefault via 192.168.1.1 dev wlan010.0.0.0/24 via 10.0.0.8 dev eth210.0.0.4nexthop dev eth0 weight 1nexthop dev eth1 weight 1192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.85
This shows that:
192.168.1.0/24
subnet gets sent out of the wlan0
interface which is directly connected to that subnet (there’s no via keyword) and so the device will lookup the destinations MAC address and pass the packet directly onto the matching device.10.0.0.4
get sent out of either interface eth0
or eth1
, which are directly connected to the device. The route chosen depends on the Linux kernel version and how it implements Equal Cost Multipath (ECMP) Routing.10.0.0.0/24
with the exception of those destined for 10.0.0.4
(as they have a more specific route which overrides this one) gets sent to the device at 10.0.0.8
out of interface eth2
which can forward them on to their destination (the via keyword indicates this is an intermediate hop) and so an ARP request will be issued out of eth2
to lookup the MAC address of the device at 10.0.0.8
and the packet will simply be passed onto the device for forwarding to the destination.192.168.1.1
via the wlan0
interface for forwarding to their destination.We can build a simpler network, our own router and connect devices on different subnets to it, to illustrate how this works with Wireshark.
On our router (A)
pi@raspberrypi-router:~ $ sudo ip route10.0.0.0/24 dev eth2 proto kernel scope link src 10.0.1.210.0.2.0/24 dev eth1 scope link
This router connects networks B and C which feature devices in the range 10.0.0.0/24
and 10.0.2.0/24
respectively which are reachable out of eth2
and eth1
. For simplicity we’ll concentrate on talking to a single device on these networks ( 10.0.0.2
and 10.0.2.2
) but B and C could be switch connecting multiple devices.
On device B (10.0.0.2)
pi@raspberrypi:~ $ sudo ip route10.0.1.0/24 dev eth0 scope link10.0.2.0/24 via 10.0.1.2 dev eth0
This device has a direct connection to the 10.0.1.0/24
network out of the eth0
interface, and can reach the 10.0.2.0/24
network via 10.0.1.2
On device C (10.0.2.2)
pi@raspberrypi:~ $ sudo ip route10.0.0.0/24 via 10.0.1.1 dev eth010.0.1.0/24 dev eth0 scope link
This device has a direct connection to the 10.0.1.0/24
network out of the eth0
interface, and can reach the 10.0.2.0/24
network via 10.0.1.1
Now with Wireshark we can see them in action, lets send a request from Device B to C
curl 10.0.2.2:8000/
Device B see’s that it has a route for 10.0.2.0/24 via 10.0.1.2 dev eth0
so it first issues an ARP request out of eth0
to find the MAC address of 10.0.1.2
It then attempts to establish a TCP connection to the destination (10.0.2.2)
and sends the packet to the router at 10.0.1.2
by placing it’s MAC address as the destination.
The router, receives the packet and see’s that it can reach 10.0.2.2
with the route 10.0.2.0/24 dev eth1
It then issues an ARP request on eth1
to find the MAC address of 10.0.2.2
It places the MAC Address it just learnt as the destination, and forwards the packet to its destination.
Over at device C it then receives the packet and sends a reply back to device A. It consults its own route tables to find a route for 10.0.0.2
10.0.0.2
can be reached via the eth0
interface, 10.0.0.0/24 via 10.0.1.1 dev eth0
It already has the MAC address of 10.0.1.1
cached from before, so doesn’t need to issue an ARP request and simply places the cached MAC address as the destination.
The router will then repeat the process in reverse and forward the reply back to the sender!
And that’s it, with enough interconnected routers you can route packets between any networks you like.