Medium: https://itnext.io/create-your-own-network-namespace-90aaebc745d
This project explores the Linux network namespace, using tools from the iproute2
utility.
In particular, it uses commands like:
ip netns
to manage network namespacesip link
to configure virtual network interfaces (i.e.veth
)ip addr
to add address range to new network interfacesip route
to manipulate route entries in the kernel routing table
For testing purposes, a TCP and UDP servers have been included in the cmd
folder.
The following commands have been tested on Ubuntu 16.04.6 LTS.
Note that sudo
privileges are required.
To create a pair of veth
interfaces named veth0
and veth1
on the localhost:
ip link add veth0 type veth peer name veth1
ip link show veth0
134: veth0@veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 0a:c7:eb:d4:ab:2a brd ff:ff:ff:ff:ff:ff
ip link show veth1
133: veth1@veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fe:4a:e5:5b:52:fd brd ff:ff:ff:ff:ff:ff
Configure the veth0
interface with IP address range 10.0.1.0/24:
ip addr add 10.0.1.0/24 dev veth0
ip link set veth0 up
ip addr show veth0
134: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 0a:c7:eb:d4:ab:2a brd ff:ff:ff:ff:ff:ff
inet 10.0.1.0/24 scope global veth0
valid_lft forever preferred_lft forever
Create a new network namespace named vnet
:
ip netns add vnet
ip netns show vnet
vnet
Move the veth1
to the vnet
network namespace:
ip link set veth1 netns vnet
Notice that veth1
is no longer in the default host network namespace:
ip link show veth1
Device "veth1" does not exist.
It can be viewed in the vnet
network namespace using the ip netns exec
command:
ip netns exec vnet ip link show veth1
133: veth1@if134: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether fe:4a:e5:5b:52:fd brd ff:ff:ff:ff:ff:ff link-netnsid 0
There are two parts to the above command:
ip netns exec vnet
allows us to execute a command in thevnet
network namespaceip link show veth1
is the command to be executed
💡 Tips: The
ip netns exec vnet <command>
command can be shortened toip -n vnet <command>
.
Configure the veth1
interface by assigning it the IP address range 10.0.2.0/24:
ip netns exec vnet ip addr add 10.0.2.0/24 dev veth1
ip netns exec vnet ip link set veth1 up
ip netns exec vnet ip link show veth1
133: veth1@if134: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether fe:4a:e5:5b:52:fd brd ff:ff:ff:ff:ff:ff link-netnsid 0
Notice that we deliberately assign different subnet address to the veth
pair, so that we can explore routing later
Let's try to ping the veth1
interface:
ping -c10 10.0.2.1
PING 10.0.2.1 (10.0.2.1) 56(84) bytes of data.
From 96.1.221.111 icmp_seq=8 Destination Host Unreachable
--- 10.0.2.1 ping statistics ---
10 packets transmitted, 0 received, +1 errors, 100% packet loss, time 9210ms
pipe 3
It doesn't work - let's use the ip route
to do some diagnosis:
ip route get 10.0.2.1
10.0.2.1 via 192.168.1.254 dev wlp110s0 src 192.168.1.71
cache
The packets are being routed to my wlp110s0
interface, instead of the veth0
interface.
Let's examine the route table:
ip route
default via 192.168.1.254 dev wlp110s0 proto static metric 600
10.0.1.0/24 dev veth0 proto kernel scope link src 10.0.1.0
There are no route entries for the 10.0.2.0/24 range. Hence, the default
route is used!
We need to add an entry for the 10.0.2.0/24 range, so that all packets destined for IP address in that range are routed to the veth0
interface:
ip route add 10.0.2.0/24 dev veth0 scope link
ip route
default via 192.168.1.254 dev wlp110s0 proto static metric 600
10.0.1.0/24 dev veth0 proto kernel scope link src 10.0.1.0
10.0.2.0/24 dev veth0 scope link
# great! looks like it's detecting the right interface to use
ip route get 10.0.2.0
10.0.2.0 dev veth0 src 10.0.1.0
cache
Let's try the ping command again:
ping -c 10 10.0.2.0
PING 10.0.2.0 (10.0.2.0) 56(84) bytes of data.
From 10.0.1.0 icmp_seq=1 Destination Host Unreachable
From 10.0.1.0 icmp_seq=2 Destination Host Unreachable
From 10.0.1.0 icmp_seq=3 Destination Host Unreachable
From 10.0.1.0 icmp_seq=4 Destination Host Unreachable
From 10.0.1.0 icmp_seq=5 Destination Host Unreachable
From 10.0.1.0 icmp_seq=6 Destination Host Unreachable
From 10.0.1.0 icmp_seq=7 Destination Host Unreachable
From 10.0.1.0 icmp_seq=8 Destination Host Unreachable
From 10.0.1.0 icmp_seq=9 Destination Host Unreachable
From 10.0.1.0 icmp_seq=10 Destination Host Unreachable
--- 10.0.2.0 ping statistics ---
10 packets transmitted, 0 received, +10 errors, 100% packet loss, time 9217ms
pipe 4
Still no luck...
Let's investigate the route tables of the vnet
network namespace:
ip netns exec vnet ip route
10.0.2.0/24 dev veth1 proto kernel scope link src 10.0.2.0
Ah.. so it's missing the route to the 10.0.1.0/24 IP address range.
Let's add it:
ip netns exec vnet ip route add 10.0.1.0 dev veth1 scope link
ip netns exec vnet ip route
10.0.1.0 dev veth1 scope link
10.0.2.0/24 dev veth1 proto kernel scope link src 10.0.2.0
Let's try the ping again:
ping -c 10 10.0.2.0
PING 10.0.2.0 (10.0.2.0) 56(84) bytes of data.
64 bytes from 10.0.2.0: icmp_seq=1 ttl=64 time=0.134 ms
64 bytes from 10.0.2.0: icmp_seq=2 ttl=64 time=0.074 ms
64 bytes from 10.0.2.0: icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from 10.0.2.0: icmp_seq=4 ttl=64 time=0.074 ms
^C
--- 10.0.2.0 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3072ms
rtt min/avg/max/mdev = 0.074/0.089/0.134/0.026 ms
And it works! 🎉🎉
We can use tcpdump
to confirm that the packets are routed to the expected interfaces:
tcpdump -i veth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:57:27.192552 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 16, length 64
10:57:27.192595 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 16, length 64
10:57:28.220390 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 17, length 64
10:57:28.220429 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 17, length 64
10:57:29.240463 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 18, length 64
10:57:29.240506 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 18, length 64
10:57:30.264403 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 19, length 64
10:57:30.264442 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 19, length 64
10:57:31.292395 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 20, length 64
10:57:31.292440 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 20, length 64
10:57:32.312438 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 21, length 64
10:57:32.312478 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 21, length 64
ip netns exec vnet tcpdump -i veth1 icmp -l
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), capture size 262144 bytes
^C10:57:49.720446 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 38, length 64
10:57:49.720484 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 38, length 64
10:57:50.744444 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 39, length 64
10:57:50.744483 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 39, length 64
10:57:51.768468 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 40, length 64
10:57:51.768508 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 40, length 64
10:57:52.792426 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 41, length 64
10:57:52.792473 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 41, length 64
10:57:53.816467 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 42, length 64
10:57:53.816512 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 42, length 64
10:57:54.840403 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 43, length 64
10:57:54.840418 IP 10.0.2.0 > 10.0.1.0: ICMP echo reply, id 9557, seq 43, length 64
10:57:55.864453 IP 10.0.1.0 > 10.0.2.0: ICMP echo request, id 9557, seq 44, length 64
Let's test with our TCP server in the vnet
namespace:
ip netns exec vnet /usr/local/go/bin/go run ./cmd/tcp/...
2020/06/10 11:06:11 listening at 0.0.0.0:4078 (tcp)...
Open a TCP connection to it using netcat
from the host network namespace, and send it a hello
message:
echo "hello" | nc -4 -q1 10.0.2.0 4078
[2020-06-10 11:06:50] hello
The server responds with a message containing the original payload and a timestamp.
The server also logs the request that it receives:
ip netns exec vnet /usr/local/go/bin/go run ./cmd/tcp/...
2020/06/10 11:06:11 listening at 0.0.0.0:4078 (tcp)...
2020/06/10 11:06:50 received: "hello" (size_bytes=6)
To view the processes running in a network namespace, we can use the ip netns pids
command:
ip netns pids vnet
14091
14179
ps aux | grep 14091
root 14091 0.0 0.0 1287368 18588 pts/2 Sl+ 11:06 0:00 /usr/local/go/bin/go run ./cmd/tcp/...