]> ruderich.org/simon Gitweb - linux-network-namespace-labs/linux-network-namespace-labs.git/commitdiff
Add README and basic examples
authorSimon Ruderich <simon@ruderich.org>
Wed, 6 Nov 2024 11:21:01 +0000 (12:21 +0100)
committerSimon Ruderich <simon@ruderich.org>
Sun, 10 Nov 2024 22:39:39 +0000 (23:39 +0100)
17 files changed:
README.adoc [new file with mode: 0644]
examples/bird-ospf/lab.conf [new file with mode: 0644]
examples/bird-ospf/lab.png [new file with mode: 0644]
examples/bird-ospf/r1/bird.conf [new file with mode: 0644]
examples/bird-ospf/r2/bird.conf [new file with mode: 0644]
examples/bird-ospf/r3/bird.conf [new file with mode: 0644]
examples/podman-frr/lab.conf [new file with mode: 0644]
examples/podman-frr/r1/frr/daemons [new file with mode: 0644]
examples/podman-frr/r1/frr/frr.conf [new file with mode: 0644]
examples/podman-frr/r1/frr/vtysh.conf [new file with mode: 0644]
examples/podman-frr/r2/frr/daemons [new file with mode: 0644]
examples/podman-frr/r2/frr/frr.conf [new file with mode: 0644]
examples/podman-frr/r2/frr/vtysh.conf [new file with mode: 0644]
examples/podman-frr/run.sh [new file with mode: 0755]
examples/readme/lab.conf [new file with mode: 0644]
examples/readme/lab.png [new file with mode: 0644]
examples/readme/setup.sh [new file with mode: 0755]

diff --git a/README.adoc b/README.adoc
new file mode 100644 (file)
index 0000000..f33f7ff
--- /dev/null
@@ -0,0 +1,181 @@
+= README
+
+linux-network-namespace-labs is a small program to create networking lab
+setups based on Linux network namespaces in a simple manner. It provides a
+quick way to setup labs using a text file describing the architecture
+(networks, nodes, links, commands). This is much simpler than running each
+node in a separate virtual machine. It is licensed under GPLv3+.
+
+
+== Requirements
+
+- Linux compiled with `CONFIG_NET_NS` (available on most modern systems)
+- iproute2 (`ip`)
+- Go, no external dependencies
+
+
+== Build
+
+    $ go build
+
+Then copy the binary `linux-network-namespace-labs` to somewhere in your
+`$PATH`.
+
+
+== Usage
+
+The following configuration from `examples/readme/` setups a lab with three
+nodes (routers) and configures static routes.
+
+----
+# Network range for loopback addresses of the nodes
+net loops 192.0.2.0/24 3fff::/20
+# Network range for interfaces between nodes
+net addrs 198.51.100.0/24 2001:db8::/32
+
+# Setup three nodes (routers) and assign IPv4 and IPv6 loopback addresses
+node r1 loops
+node r2 loops
+node r3 loops
+
+# Links between routers: /31 or /127 is used for each interface
+link r1 r2 addrs
+link r2 r3 addrs
+
+# Commands to run on each node; $1 is the node name (= name of network
+# namespace). Here it's used to setup static routes but can also be used to
+# start routing daemons like bird.
+cmd ./setup.sh $1
+----
+
+Then setup the lab by running (as root):
+
+    # cd examples/readme
+    # linux-network-namespace-labs up lab.conf
+    [...]
+
+Now you can enter the network namespace of each router and test a simple
+traceroute:
+
+    # ip netns exec r1 sh
+    # ip -br a
+    lo               UNKNOWN        127.0.0.1/8 ::1/128
+    lo2              UNKNOWN        192.0.2.0/32 3fff::/128 fe80::438:67ff:fe0a:4e58/64
+    r2@if287         UP             198.51.100.0/31 2001:db8::/127 fe80::a896:cdff:fe58:5c23/64
+    # traceroute 192.0.2.2
+    traceroute to 192.0.2.2 (192.0.2.2), 30 hops max, 60 byte packets
+     1  r2 (198.51.100.1)  0.937 ms  0.818 ms  0.768 ms
+     2  r3-loop (192.0.2.2)  0.722 ms  0.650 ms  0.603 ms
+
+Use `exit` to leave the network namespace.
+
+`/etc/hosts` of each node (via `/etc/netns/<ns>/hosts`) is automatically
+filled with all known addresses so you can use hostnames as well:
+
+    # ping -c1 r3-loop
+    PING r3-loop(r3-loop (3fff::2)) 56 data bytes
+    64 bytes from r3-loop (3fff::2): icmp_seq=1 ttl=63 time=0.266 ms
+    --- r3-loop ping statistics ---
+    1 packets transmitted, 1 received, 0% packet loss, time 0ms
+    rtt min/avg/max/mdev = 0.266/0.266/0.266/0.000 ms
+
+When you change the configuration you can simply rerun "up" and it will remove
+the lab (and killing all processes inside it) before starting it up again:
+
+    # linux-network-namespace-labs up lab.conf
+    [...]
+
+When you're done you can remove the lab with:
+
+    # linux-network-namespace-labs down lab.conf
+    [...]
+
+To get an overview of the network you can create a
+https://en.wikipedia.org/wiki/DOT_(graph_description_language)[DOT] file:
+
+    $ linux-network-namespace-labs dot lab.conf lab.dot
+    $ dot -Tpng lab.dot > lab.png
+
+image::examples/readme/lab.png[DOT style diagram of network nodes and links]
+
+
+== Examples
+
+Have a look at `examples/` for a few small examples which also include running
+https://bird.network.cz/[Bird] on each node and spawning a Podman container
+inside the created nodes running https://frrouting.org/[FRR].
+
+
+== Syntax of configuration file
+
+One option per line, empty lines are permitted as well as comments at the
+beginning of the line with `#`.
+
+The config files is parsed from top to bottom and the options "net", "node",
+"link" must be given in this order. "cmd" can be put anywhere (but is only
+executed at the end).
+
+=== Option "net"
+
+    "net" <name> <prefix>...
+
+"net" creates a new prefix which is used to allocate addresses for loopback
+and link interfaces. Multiple IPv4 and IPv6 prefixes can be specified. When
+the network is used one address of each prefix is assigned to the interface.
+
+=== Option "node"
+
+    "node" <name> [<loopback-net-name>...]
+
+"node" creates a new node (router) and (if specified) assigns loopback
+addresses from the given networks. The loopback addresses are assigned to a
+new interface "lo2".
+
+=== Option "link"
+
+    "link" <node> <node> <net-name>
+
+"link" creates (veth-)links between nodes. The IP addresses are taken from the
+given network name. /31 is used for IPv4- and /127 for IPv6-prefixes. The MAC
+address is not static and allocated by the kernel. The link is named after the
+node "on the other side". Multiple links can be created between two nodes. In
+this case "_2", "_3", etc. is appended to the interface name.
+
+=== Option "cmd"
+
+    "cmd" <string-passed-to-sh-c>
+
+"cmd" runs the given command (by passing it as is to `sh -c`) on each node.
+The first argument is the name of the node (which is also the name of the
+network namespace). This can be used to run setup commands or routing daemons
+on the nodes.
+
+If "cmd" is not flexible enough you can simply use `ip netns exec` to manually
+run commands on specific nodes.
+
+
+== Authors
+
+Written by Simon Ruderich <simon@ruderich.org>.
+
+Please report bugs, feature requests and patches via email.
+
+
+== License
+
+This program is licensed under GPL version 3 or later.
+
+Copyright (C) 2024  Simon Ruderich
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
diff --git a/examples/bird-ospf/lab.conf b/examples/bird-ospf/lab.conf
new file mode 100644 (file)
index 0000000..d486108
--- /dev/null
@@ -0,0 +1,18 @@
+# Start lab with: linux-network-namespace-labs up lab.conf
+
+# r1 <-> r2 <-> r3
+
+
+net loops 192.0.2.0/24 3fff::/20
+net addrs 198.51.100.0/24 2001:db8::/32
+
+node r1 loops
+node r2 loops
+node r3 loops
+
+link r1 r2 addrs
+link r2 r3 addrs
+
+# Start bird on each node, using a different config file for each
+cmd bird -u bird -g bird -c $1/bird.conf -s /run/bird/$1.ctl
+# Use with birdc() { command birdc -s "/run/bird/$(ip netns identify).ctl" "$@" }
diff --git a/examples/bird-ospf/lab.png b/examples/bird-ospf/lab.png
new file mode 100644 (file)
index 0000000..6cf20a0
Binary files /dev/null and b/examples/bird-ospf/lab.png differ
diff --git a/examples/bird-ospf/r1/bird.conf b/examples/bird-ospf/r1/bird.conf
new file mode 100644 (file)
index 0000000..2d4ebc2
--- /dev/null
@@ -0,0 +1,38 @@
+router id from "lo2";
+log syslog name "bird-r1" all;
+
+protocol ospf {
+    area 0 {
+        interface "lo2" {
+            stub;
+        };
+        interface "r2" {
+            type ptp;
+        };
+    };
+}
+protocol ospf v3 {
+    area 0 {
+        interface "lo2" {
+            stub;
+        };
+        interface "r2" {
+            type ptp;
+        };
+    };
+}
+
+protocol kernel {
+    ipv4 {
+        export all;
+    };
+}
+protocol kernel {
+    ipv6 {
+        export all;
+    };
+}
+
+protocol device {}
+
+# vim: ft=conf
diff --git a/examples/bird-ospf/r2/bird.conf b/examples/bird-ospf/r2/bird.conf
new file mode 100644 (file)
index 0000000..c46a353
--- /dev/null
@@ -0,0 +1,38 @@
+router id from "lo2";
+log syslog name "bird-r2" all;
+
+protocol ospf {
+    area 0 {
+        interface "lo2" {
+            stub;
+        };
+        interface "r1", "r3" {
+            type ptp;
+        };
+    };
+}
+protocol ospf v3 {
+    area 0 {
+        interface "lo2" {
+            stub;
+        };
+        interface "r1", "r3" {
+            type ptp;
+        };
+    };
+}
+
+protocol kernel {
+    ipv4 {
+        export all;
+    };
+}
+protocol kernel {
+    ipv6 {
+        export all;
+    };
+}
+
+protocol device {}
+
+# vim: ft=conf
diff --git a/examples/bird-ospf/r3/bird.conf b/examples/bird-ospf/r3/bird.conf
new file mode 100644 (file)
index 0000000..82d11f5
--- /dev/null
@@ -0,0 +1,38 @@
+router id from "lo2";
+log syslog name "bird-r3" all;
+
+protocol ospf {
+    area 0 {
+        interface "lo2" {
+            stub;
+        };
+        interface "r2" {
+            type ptp;
+        };
+    };
+}
+protocol ospf v3 {
+    area 0 {
+        interface "lo2" {
+            stub;
+        };
+        interface "r2" {
+            type ptp;
+        };
+    };
+}
+
+protocol kernel {
+    ipv4 {
+        export all;
+    };
+}
+protocol kernel {
+    ipv6 {
+        export all;
+    };
+}
+
+protocol device {}
+
+# vim: ft=conf
diff --git a/examples/podman-frr/lab.conf b/examples/podman-frr/lab.conf
new file mode 100644 (file)
index 0000000..180f41d
--- /dev/null
@@ -0,0 +1,12 @@
+# Start lab with: ./run.sh
+
+# r1 <-> r2
+
+
+net loops 3fff::/20
+net addrs 2001:db8::/32
+
+node r1 loops
+node r2 loops
+
+link r1 r2 addrs
diff --git a/examples/podman-frr/r1/frr/daemons b/examples/podman-frr/r1/frr/daemons
new file mode 100644 (file)
index 0000000..8b7a0d0
--- /dev/null
@@ -0,0 +1,7 @@
+ospf6d=yes
+
+vtysh_enable=yes
+zebra_options="  -A 127.0.0.1 -s 90000000"
+mgmtd_options="  -A 127.0.0.1"
+ospf6d_options=" -A ::1"
+staticd_options="-A 127.0.0.1"
diff --git a/examples/podman-frr/r1/frr/frr.conf b/examples/podman-frr/r1/frr/frr.conf
new file mode 100644 (file)
index 0000000..c096eb2
--- /dev/null
@@ -0,0 +1,19 @@
+frr defaults traditional
+log syslog
+!
+ip router-id 192.0.2.0
+!
+interface lo2
+ ipv6 ospf6 area 0
+ ipv6 ospf6 passive
+exit
+!
+interface r2
+ ipv6 ospf6 area 0
+ ipv6 ospf6 network point-to-point
+exit
+!
+router ospf6
+ log-adjacency-changes
+exit
+!
diff --git a/examples/podman-frr/r1/frr/vtysh.conf b/examples/podman-frr/r1/frr/vtysh.conf
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/podman-frr/r2/frr/daemons b/examples/podman-frr/r2/frr/daemons
new file mode 100644 (file)
index 0000000..8b7a0d0
--- /dev/null
@@ -0,0 +1,7 @@
+ospf6d=yes
+
+vtysh_enable=yes
+zebra_options="  -A 127.0.0.1 -s 90000000"
+mgmtd_options="  -A 127.0.0.1"
+ospf6d_options=" -A ::1"
+staticd_options="-A 127.0.0.1"
diff --git a/examples/podman-frr/r2/frr/frr.conf b/examples/podman-frr/r2/frr/frr.conf
new file mode 100644 (file)
index 0000000..bace1e9
--- /dev/null
@@ -0,0 +1,19 @@
+frr defaults traditional
+log syslog
+!
+ip router-id 192.0.2.1
+!
+interface lo2
+ ipv6 ospf6 area 0
+ ipv6 ospf6 passive
+exit
+!
+interface r1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 network point-to-point
+exit
+!
+router ospf6
+ log-adjacency-changes
+exit
+!
diff --git a/examples/podman-frr/r2/frr/vtysh.conf b/examples/podman-frr/r2/frr/vtysh.conf
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/podman-frr/run.sh b/examples/podman-frr/run.sh
new file mode 100755 (executable)
index 0000000..bda1830
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+set -eu
+
+
+podman_run() {
+    # ulimit to prevent warning from frr: "FD Limit set: 1048576 is stupidly
+    # large."; "/dev/log" to forward syslog to host, "ns" to run in network
+    # namespace
+    podman run --detach --rm \
+        --cap-add cap_net_admin,cap_net_raw,cap_sys_admin \
+        --ulimit nofile=1024 \
+        --name "$1" \
+        --volume "./$1/frr":/etc/frr \
+        --volume /dev/log:/dev/log \
+        --network ns:/run/netns/"$1" \
+        quay.io/frrouting/frr:10.1.1
+}
+
+podman_stop() {
+    podman stop "$1" 2>/dev/null
+}
+
+
+podman_stop r1 || true
+podman_stop r2 || true
+
+linux-network-namespace-labs up lab.conf
+
+podman_run r1
+podman_run r2
+
+# Connect to podman container with `podman exec -it r1 bash` and then run
+# `vtysh` to interact with frr.
diff --git a/examples/readme/lab.conf b/examples/readme/lab.conf
new file mode 100644 (file)
index 0000000..75d1248
--- /dev/null
@@ -0,0 +1,21 @@
+# Start lab with: linux-network-namespace-labs up lab.conf
+
+
+# Network range for loopback addresses of the nodes
+net loops 192.0.2.0/24 3fff::/20
+# Network range for interfaces between nodes
+net addrs 198.51.100.0/24 2001:db8::/32
+
+# Setup three nodes (routers) and assign IPv4 and IPv6 loopback addresses
+node r1 loops
+node r2 loops
+node r3 loops
+
+# Links between routers: /31 or /127 is used for each interface
+link r1 r2 addrs
+link r2 r3 addrs
+
+# Commands to run on each node; $1 is the node name (= name of network
+# namespace). Here it's used to setup static routes but can also be used to
+# start routing daemons like bird.
+cmd ./setup.sh $1
diff --git a/examples/readme/lab.png b/examples/readme/lab.png
new file mode 100644 (file)
index 0000000..6cf20a0
Binary files /dev/null and b/examples/readme/lab.png differ
diff --git a/examples/readme/setup.sh b/examples/readme/setup.sh
new file mode 100755 (executable)
index 0000000..a56a109
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -eu
+
+if test "$1" = r1; then
+    # Loopbacks
+    ip r a 192.0.2.1/32 via 198.51.100.1
+    ip r a 192.0.2.2/32 via 198.51.100.1
+    ip r a 3fff::1/128 via 2001:db8::1
+    ip r a 3fff::2/128 via 2001:db8::1
+    # Link networks
+    ip r a 198.51.100.2/31 via 198.51.100.1
+    ip r a 2001:db8::2/127 via 2001:db8::1
+
+elif test "$1" = r2; then
+    # Loopbacks
+    ip r a 192.0.2.0/32 via 198.51.100.0
+    ip r a 192.0.2.2/32 via 198.51.100.3
+    ip r a 3fff::0/128 via 2001:db8::0
+    ip r a 3fff::2/128 via 2001:db8::3
+
+elif test "$1" = r3; then
+    # Loopbacks
+    ip r a 192.0.2.1/32 via 198.51.100.2
+    ip r a 192.0.2.2/32 via 198.51.100.2
+    ip r a 3fff::0/128 via 2001:db8::2
+    ip r a 3fff::1/128 via 2001:db8::2
+    # Link networks
+    ip r a 198.51.100.0/31 via 198.51.100.2
+    ip r a 2001:db8::0/127 via 2001:db8::2
+
+fi