Add some testing for linux network namespaces

This commit is contained in:
2024-04-20 10:15:10 -04:00
parent 0e9dbebd6a
commit d6afd9f472

View File

@@ -0,0 +1,202 @@
#+TITLE: Linux Networking For Fun and Profit
#+DATE: March 30, 2024
* 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.
#+begin_src
Host 3
/ \
veth1b veth2b
/ \
veth1a veth2a
Host 1 Host 2
#+end_src
- 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
#+begin_src bash
sudo ip netns add host1
sudo ip netns add host2
sudo ip netns add host3
#+end_src
Then we'll create all the (virtual) interfaces we need. These paired
virtual ethernets act as direct connections between the host machines.
#+begin_src bash
sudo ip link add veth1a type veth peer name veth1b
sudo ip link add veth2a type veth peer name veth2b
#+end_src
So far we've only created the interfaces, we haven't assigned them to
our network namespaces, so let's do that now:
#+begin_src bash
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
#+end_src
** 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.
#+begin_src bash
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
#+end_src
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.
#+begin_src bash
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
#+end_src
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.
#+begin_src bash
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
#+end_src
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.
#+begin_src bash
sudo ip netns exec host3 ip addr del 192.168.65.3/24 dev veth1b
#+end_src
Now let's create that bridge interface, so we can allow the networking
stack to pass packets from veth1b to veth2b.
#+begin_src bash
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
#+end_src
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
#+begin_src bash
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
#+end_src
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.
#+begin_src bash
sudo ip netns exec host3 ping -c1 192.168.65.1
sudo ip netns exec host3 ping -c1 192.168.65.2
#+end_src
And now, let's try from Host 1 to Host 2, and back.
#+begin_src bash
sudo ip netns exec host1 ping -c1 192.168.65.2
sudo ip netns exec host2 ping -c1 192.168.65.1
#+end_src
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.
#+begin_src bash
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
#+end_src
** 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.
#+begin_src bash
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
#+end_src
We'll bring it up for all 3 hosts and we're in business.
#+begin_src sh
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
#+end_src
** Cleaning up
All the resources should be in the network namespaces, so we should be
able to easily clean up by removing the namespaces.
#+begin_src sh
for i in 1 2 3
do
sudo ip netns delete host${i}
done
#+end_src