selftest: Add helper scripts to run selftest in namespaces
authorTim Beale <timbeale@catalyst.net.nz>
Tue, 26 Feb 2019 02:54:34 +0000 (15:54 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 31 May 2019 05:18:20 +0000 (05:18 +0000)
This adds the underlying scripts, but they are not actually hooked up to
the selftest code yet, and so are not actually used.

Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
selftest/ns/README [new file with mode: 0644]
selftest/ns/add_bridge_iface.sh [new file with mode: 0755]
selftest/ns/create_bridge.sh [new file with mode: 0755]
selftest/ns/start_in_ns.sh [new file with mode: 0755]

diff --git a/selftest/ns/README b/selftest/ns/README
new file mode 100644 (file)
index 0000000..e9e9d06
--- /dev/null
@@ -0,0 +1,65 @@
+The scripts in this directory are experimental and are used to create testenvs
+in separate linux namespaces. This avoids the need for socket-wrapper.
+
+What are Namespaces
+===================
+Namespaces allow the kernel to segregate its system resources (files, CPU,
+etc), so that different processes only see the set of resources they are
+allowed to use. There are several different types of namespace: network,
+user, process, file, IPC, and so on.
+
+Key points to grasp are:
+* Each type of namespace gets managed separately by the kernel, i.e. process
+namespaces are managed separately to network namespaces, which are separate
+to user namespaces. These scripts give each testenv its own network namespace,
+but otherwise they all still share the same user/process/etc namespace.
+(In future, we may want to give each testenv its own process and user
+namespace, to better mimic a production DC).
+* Namespaces are created using the 'unshare' utility. The new selftest
+namespaces are anonymous/nameless, and so the different namespaces are
+identified by the PID of the processes running within the namespace
+(typically samba).
+* Linux supports nesting namespaces within namespaces. In this case, each
+testenv DC has its own network namespace, which is a child of the overarching
+selftest namespace (which itself is a child of whatever namespace you run
+'make test' from - usually this would be the root namespace).
+
+How does it work?
+=================
+Normally when 'make test' is run, every testenv uses a 127.0.0.x IP address
+and socket-wrapper passes the packets between them.
+
+With namespaces, we can use real IP addresses and have the packets pass through
+the kernel's IP stack normally, as it forwards them between namespaces.
+
+We use veth interfaces for this. veth is a type of virtual interface supported
+by the kernel. veth interfaces come in pairs, and act as a tunnel - any packets
+sent on a veth interface simply end up as received packets on the pair veth
+interface.
+
+We create a new veth interface pair for each testenv, and use them to connect
+up the namespaces. One end of the veth pair is added to the main selftest
+namespace, and the other end is added to a new namespace that we'll run
+samba in. E.g.
+
+selftest.pl  veth21-br ------------------------ veth21 samba (ad_dc_ntvfs)
+             10.0.0.11                          10.0.0.21
+ Namespace 1                                       Namespace 2
+
+However, we need to run multiple different testenvs and have them talk to
+each other. So to do this, we need a bridge interface ('selftest0') to connect
+up the namespaces, which essentially just acts as a hub. So connecting together
+multiple testenvs looks more like this:
+
+selftest.pl     +-- veth21-br ------------------------ veth21 samba (ad_dc_ntvfs)
+                |                                      10.0.0.21
+    selftest0 --+                                        Namespace 2
+    10.0.0.11   |
+                +-- veth22-br ------------------------ veth22 samba (vampire_dc)
+                                                       10.0.0.22
+ Namespace 1                                             Namespace 3      
+
+The veth interfaces are named vethX and vethX-br, where X is the
+SOCKET_WRAPPER_DEFAULT_IFACE for the testenv. The vethX-br interface is always
+added to the selftest0 bridge interface. 
+
diff --git a/selftest/ns/add_bridge_iface.sh b/selftest/ns/add_bridge_iface.sh
new file mode 100755 (executable)
index 0000000..da9d53a
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Configures the interfaces needed for communication between namespaces.
+# This handles the bridge-end of the veth pair.
+interface=$1
+
+# the main bridge interface is called 'selftest0' (although in future we may
+# want to segregate the different domains by using different bridges)
+bridge=$2
+
+# we need to wait for the child namespace to start up and add the new
+# interface back to our new namespace
+while ! ip link show $interface > /dev/null 2>&1
+do
+    sleep 0.1
+    echo "Waiting for $interface to be created..."
+done
+
+# bring the bridge-end of the link up and add it to the bridge
+ip link set dev $interface up
+ip link set $interface master $bridge
+
diff --git a/selftest/ns/create_bridge.sh b/selftest/ns/create_bridge.sh
new file mode 100755 (executable)
index 0000000..9766cd8
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# creates a bridge interface (i.e. 'selftest0') that connects together the
+# veth interfaces for the various testenvs
+
+br_name=$1
+ip_addr=$2
+ipv6_addr=$3
+
+# make sure the loopback is up (needed for pinging between namespaces, etc)
+ip link set dev lo up
+
+# create the bridge interface and enable it
+ip link add $br_name type bridge
+ip addr add $ip_addr/24 dev $br_name
+ip addr add $ipv6_addr/112 dev $br_name
+ip link set $br_name up
+
+
diff --git a/selftest/ns/start_in_ns.sh b/selftest/ns/start_in_ns.sh
new file mode 100755 (executable)
index 0000000..5831a0b
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# Starts samba in a separate namespace. This gets passed the interface/IP
+# to use, as well as the Samba command to run. The whole script gets run
+# (via unshare) in a separate namespace.
+
+# the first 3 args are our interface-name, parent-PID, and a exports file
+# containing environment variables ($SERVER, $SERVER_IP, etc)
+interface=$1
+exports_file=$2
+parent_pid=$3
+
+# we write the testenv environment variables to file, which makes it easier
+# to work out the $SERVER, $SERVER_IP, etc
+. $exports_file
+
+# The namespaces we use are anonymous, which means other processes would need
+# to use our PID to access the new namespace
+echo "-------------------------------------------------------------"
+echo "Created namespace for $NETBIOSNAME"
+echo "To communicate with this testenv, use: nsenter -t $$ --net sh"
+echo "To copy its environment variables, use: . $exports_file"
+echo "-------------------------------------------------------------"
+
+# the rest of the args are the samba command to run
+shift 3
+SAMBA_CMD=$@
+
+# make sure namespace loopback is up (it's needed for ping, etc)
+ip link set dev lo up
+
+# Create the interfaces needed for communication between namespaces.
+# We use a veth pair, which acts as a tunnel between the namespaces.
+# One end of the veth link is added to a common bridge in the top-level (i.e.
+# selftest) namespace, and the other end is added to the testenv's namespace.
+# This means each testenv DC is in its own namespace, but they can talk to
+# each other via the common bridge interface.
+# The new veth interfaces are named "vethX" and "vethX-br", where
+# X = the testenv IP (i.e. Samba::get_interface()). E.g. ad_dc = veth30,
+# and veth30-br.
+# The "vethX" interface will live in the new testenv's namespace.
+# The "vethX-br" end is added to the bridge in the main selftest namespace.
+ip link add dev $interface-br type veth peer name $interface
+
+# move the bridge end of the link back into the parent namespace.
+ip link set $interface-br netns $parent_pid
+
+# configure our IP address and bring the interface up
+ip addr add $SERVER_IP/24 dev $interface
+# Note that samba can't bind to the IPv6 address while DAD is in progress,
+# so we use 'nodad' when configuring the address
+ip addr add $SERVER_IPV6/112 dev $interface nodad
+ip link set dev $interface up
+
+# start samba
+$SAMBA_CMD