6.0 KiB
Linux Networking For Fun and Profit
Setting up "hosts" with network namespaces
Network namespaces give us a neat way to simulate networked machines. This walks through using Linux network namespaces to configure a set of "hosts" in the following configuration.
Host 3
/ \
veth1b veth2b
/ \
veth1a veth2a
Host 1 Host 2
- Host 1 - 192.168.65.1
- Host 2 - 192.168.65.2
- Host 3 - 192.168.65.3
In this configuration, even though these are all on the same subnet Host 1 only has a connection to Host 3 so can't directly reach Host 2.
Basic network namespace set up
All these steps are performed on Fedora 39 (Linux 6.7.5), but this should be possible on any modern Linux distro.
First we'll create all the network namespaces
sudo ip netns add host1
sudo ip netns add host2
sudo ip netns add host3
Then we'll create all the (virtual) interfaces we need. These paired virtual ethernets act as direct connections between the host machines.
sudo ip link add veth1a type veth peer name veth1b
sudo ip link add veth2a type veth peer name veth2b
So far we've only created the interfaces, we haven't assigned them to our network namespaces, so let's do that now:
sudo ip link set veth1a netns host1
sudo ip link set veth1b netns host3
sudo ip link set veth2a netns host2
sudo ip link set veth2b netns host3
Point to point connectivity
At this point we've got the hosts mostly configured, each host has the correct interfaces, but we have to bring them up and assign IPs. Let's start with assigning ips to just Host 1 and Host 2 to prove we can't communicate just yet.
sudo ip netns exec host1 ip addr add 192.168.65.1/24 dev veth1a
sudo ip netns exec host1 ip link set veth1a up
sudo ip netns exec host2 ip addr add 192.168.65.2/24 dev veth2a
sudo ip netns exec host2 ip link set veth2a up
sudo ip netns exec host1 ping -c1 192.168.65.2
# this should fail with 100% packet loss
We know there's a path from Host 1 to Host 2 through Host 3 though, but before we do that, let's just make sure we can communicate point-to-point from Host 1 to Host 3.
We'll do this by adding an IP to veth1b and bringing it up, and then pinging that IP from Host 1.
sudo ip netns exec host3 ip addr add 192.168.65.3/24 dev veth1b
sudo ip netns exec host3 ip link set veth1b up
sudo ip netns exec host3 ip link set veth2b up
sudo ip netns exec host1 ping -c1 192.168.65.3
Host 1 to Host 3 succeeds because our veth pair is connected directly.
Bridging across virtual ethernet interfaces
So that's easy, we can communicate point-to-point, but we still can't figure out how to get to Host 2 from Host 1. We can even check the ARP table to see why.
sudo ip netns exec host1 arp
Address HWtype HWaddress Flags Mask Iface
192.168.65.3 ether 1a:60:c6:d9:2b:a0 C veth1a
192.168.65.2 (incomplete) veth1a
ARP isn't able to figure out what mac address is for the owner of 192.168.65.2. We have an veth pair on Host 3 connected to Host1 and another veth pair connected to Host 2, but we can't get from Host 1 to Host 2.
We can solve this at layer 2 by creating a bridge interface that just sends packets along from one interface to the other.
First let's remove the IP we put on the veth1b.
sudo ip netns exec host3 ip addr del 192.168.65.3/24 dev veth1b
Now let's create that bridge interface, so we can allow the networking stack to pass packets from veth1b to veth2b.
sudo ip netns exec host3 ip link add br0 type bridge
sudo ip netns exec host3 ip link set veth1b master br0
sudo ip netns exec host3 ip link set veth2b master br0
And now, instead of assigning the IPs to the underlying interfaces, we'll just assign an IP to the bridge interface and bring it up
sudo ip netns exec host3 ip addr add 192.168.65.3/24 dev br0
sudo ip netns exec host3 ip link set up br0
Let's test our configuration from Host 3, we should now be able to reach both Host 1 and Host 2 by leveraging our underlying veth interfaces.
sudo ip netns exec host3 ping -c1 192.168.65.1
sudo ip netns exec host3 ping -c1 192.168.65.2
And now, let's try from Host 1 to Host 2, and back.
sudo ip netns exec host1 ping -c1 192.168.65.2
sudo ip netns exec host2 ping -c1 192.168.65.1
Finally, we can see that our pings reach, and if we look at the ARP table you can confirm that the addresses match the address of the veth device on Host 2.
sudo ip netns exec host1 arp
Address HWtype HWaddress Flags Mask Iface
192.168.65.2 ether 46:ca:27:82:5a:c3 C veth1a
192.168.65.3 ether b6:66:d9:d0:4d:39 C veth1a
Complete the set up with loopback interfaces
There's still something funny though, if a host tries to reach itself, it can't yet, and that's because we never brought up the loopback interface.
sudo ip netns exec host2 ip a show lo
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
We'll bring it up for all 3 hosts and we're in business.
for i in 1 2 3
do
echo "============:: Host ${i}"
sudo ip netns exec host${i} ip link set lo up
for j in 1 2 3
do
sudo ip netns exec host${i} ping -c1 192.168.65.${j}
done
echo "======================"
done
Cleaning up
All the resources should be in the network namespaces, so we should be able to easily clean up by removing the namespaces.
for i in 1 2 3
do
sudo ip netns delete host${i}
done