From d6afd9f47269dcf42befc00aefb3fa7a1cce8a75 Mon Sep 17 00:00:00 2001 From: Adam Mohammed Date: Sat, 20 Apr 2024 10:15:10 -0400 Subject: [PATCH] Add some testing for linux network namespaces --- design/network-namespace-testing.org | 202 +++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 design/network-namespace-testing.org diff --git a/design/network-namespace-testing.org b/design/network-namespace-testing.org new file mode 100644 index 0000000..9c299c3 --- /dev/null +++ b/design/network-namespace-testing.org @@ -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: 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