Add some testing for linux network namespaces
This commit is contained in:
202
design/network-namespace-testing.org
Normal file
202
design/network-namespace-testing.org
Normal 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
|
||||
Reference in New Issue
Block a user