4

Fun with Linux Network Namespaces

 3 years ago
source link: http://www.linux-admins.net/2015/07/fun-with-linux-network-namespaces.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Fun with Linux Network Namespaces

A feature of the Linux kernel called namespace isolation [1] defines groups of separated processes such that each process cannot "see" resources in the other groups. This is a form of lightweight process virtualization used in some container implementations such as LXC.

There are currently six namespaces implementations:
- mnt: mount points and filesystems isolation
- pid: process isolation
- net: network stack isolation
- ipc: System V IPC isolation
- uts: hostname isolation
- user: user isolation by means of UIDs

In this post I'll show few examples of how to create network namespaces and use them with Open vSwitch.

A network namespace is a separate copy of the network stack - it contains its own routes, network devices and iptables rules. It is defined in include/net/net_namespace.h where each device belongs to only one network namespace. 

There's always the default network namespace, called the root namespace where all network interfaces are assigned to:

root@ovs-tests:~# ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether bc:76:4e:11:b5:87 brd ff:ff:ff:ff:ff:ff 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether bc:76:4e:11:b7:85 brd ff:ff:ff:ff:ff:ff root@ovs-tests:~# ip a s 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether bc:76:4e:11:b5:87 brd ff:ff:ff:ff:ff:ff inet 104.130.212.227/24 brd 104.130.212.255 scope global eth0 valid_lft forever preferred_lft forever inet6 2001:4801:7827:101:be76:4eff:fe11:b587/64 scope global valid_lft forever preferred_lft forever inet6 fe80::be76:4eff:fe11:b587/64 scope link valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether bc:76:4e:11:b7:85 brd ff:ff:ff:ff:ff:ff inet 10.210.102.116/19 brd 10.210.127.255 scope global eth1 valid_lft forever preferred_lft forever inet6 fe80::be76:4eff:fe11:b785/64 scope link valid_lft forever preferred_lft forever root@ovs-tests:~# ip r default via 104.130.212.1 dev eth0 10.176.0.0/12 via 10.210.96.1 dev eth1 10.208.0.0/12 via 10.210.96.1 dev eth1 10.210.96.0/19 dev eth1 proto kernel scope link src 10.210.102.116 104.130.212.0/24 dev eth0 proto kernel scope link src 104.130.212.227

In this case you can see 3 network interfaces - lo, eth0 and eth1.

Now lets create 2 network namespaces called ns1 and ns2 and list them:

root@ovs-tests:~# ip netns add ns1 root@ovs-tests:~# ip netns add ns2 root@ovs-tests:~# ip netns ns2 ns1 root@ovs-tests:~# ls -la /var/run/netns/ total 0 drwxr-xr-x 2 root root 80 Jul 20 14:54 . drwxr-xr-x 19 root root 600 Jul 20 14:54 .. -r--r--r-- 1 root root 0 Jul 20 14:54 ns1 -r--r--r-- 1 root root 0 Jul 20 14:54 ns2 root@ovs-tests:~#

To execute a command inside the namespace:

root@ovs-tests:~# ip netns exec ns1 ip link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 root@ovs-tests:~# ip netns exec ns1 bash root@ovs-tests:~# ip link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 root@ovs-tests:~# exit exit root@ovs-tests:~#

We can use "ip netns exec ns1 command" where command is either the actual command we want to execute or bash, which puts us in the namespace's shell, where we can run commands as usual without the need of prefixing them with "ip netns exec ns1".

As you can see the ns1 namespace only contains the loopback interface in a DOWN state. The same is true for the ns2 network namespace.

To connect the two lets first create a software switch using Open vSwitch:

root@ovs-tests:~# apt-get update && apt-get install openvswitch-switch root@ovs-tests:~# ovs-vsctl add-br OVS-1 root@ovs-tests:~# ovs-vsctl show cad47b00-2d1d-4149-9031-d369592d6d88 Bridge "OVS-1" Port "OVS-1" Interface "OVS-1" type: internal ovs_version: "2.3.0" root@ovs-tests:~# ip a s OVS-1 5: OVS-1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default link/ether c6:e3:a0:8b:47:49 brd ff:ff:ff:ff:ff:ff

Lets create a link between the namespaces ns1 and ns2 through the virtual switch by specifying the two ends of the connection, called a virtual pair.

To create the two pairs run:

root@ovs-tests:~# ip link add eth1-ns1 type veth peer name veth-ns1 root@ovs-tests:~# ip link add eth1-ns2 type veth peer name veth-ns2 root@ovs-tests:~# ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether bc:76:4e:11:b5:87 brd ff:ff:ff:ff:ff:ff 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether bc:76:4e:11:b7:85 brd ff:ff:ff:ff:ff:ff 4: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default link/ether 36:ff:6e:ca:01:ae brd ff:ff:ff:ff:ff:ff 5: OVS-1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default link/ether c6:e3:a0:8b:47:49 brd ff:ff:ff:ff:ff:ff 6: veth-ns1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 2e:eb:33:7b:62:86 brd ff:ff:ff:ff:ff:ff 7: eth1-ns1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether de:0c:5f:c2:1c:0d brd ff:ff:ff:ff:ff:ff 8: veth-ns2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 56:6e:02:21:15:1f brd ff:ff:ff:ff:ff:ff 9: eth1-ns2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether b6:e3:94:fe:be:23 brd ff:ff:ff:ff:ff:ff

The commands above created two virtual links in the root namespace, each having two interface on each end - eth1-ns1/veth-ns1 and eth1-ns2/veth-ns2. Think of the eth1-ns1 and veth-ns1 interfaces as two opposite ends of the same pipe. The names are arbitrary.

First lets connect one end of the virtual connections to each name space:

root@ovs-tests:~# ip link set eth1-ns1 netns ns1 root@ovs-tests:~# ip netns exec ns1 ip link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 7: eth1-ns1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether de:0c:5f:c2:1c:0d brd ff:ff:ff:ff:ff:ff root@ovs-tests:~# root@ovs-tests:~# ip link set eth1-ns2 netns ns2 root@ovs-tests:~# ip netns exec ns2 ip link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 9: eth1-ns2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether b6:e3:94:fe:be:23 brd ff:ff:ff:ff:ff:ff root@ovs-tests:~#

Notice how the eth1-ns1 and eth1-ns2 interfaces no longer show in the root name space, but in their newly assigned ns1 and ns2 namespaces.

Then lets connect the other end to a port on the OVS:

root@ovs-tests:~# ovs-vsctl add-port OVS-1 veth-ns1 root@ovs-tests:~# ovs-vsctl add-port OVS-1 veth-ns2 root@ovs-tests:~# ovs-vsctl show cad47b00-2d1d-4149-9031-d369592d6d88 Bridge "OVS-1" Port "veth-ns2" Interface "veth-ns2" Port "OVS-1" Interface "OVS-1" type: internal Port "veth-ns1" Interface "veth-ns1" ovs_version: "2.3.0" root@ovs-tests:~#

The end result is two network namespaces ns1 and ns2 each having eth1-ns1/2 interface connected to an OVS ports veth-ns1/2. Let's bring the interfaces up and assign IPs.

In the root namespace lets bring the veth-ns1/2 interfaces up:

root@ovs-tests:~# ip link set veth-ns1 up root@ovs-tests:~# ip link set veth-ns2 up

Then in each network namespace ns1 and ns2 lets bring the lo and eth-ns1/2 interfaces up and assign IPs:

root@ovs-tests:~# ip netns exec ns1 ip link set dev lo up root@ovs-tests:~# ip netns exec ns1 ip link set dev eth1-ns1 up root@ovs-tests:~# ip netns exec ns1 ip address add 192.168.0.1/24 dev eth1-ns1 root@ovs-tests:~# ip netns exec ns1 ip a s 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 7: eth1-ns1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether de:0c:5f:c2:1c:0d brd ff:ff:ff:ff:ff:ff inet 192.168.0.1/24 scope global eth1-ns1 valid_lft forever preferred_lft forever inet6 fe80::dc0c:5fff:fec2:1c0d/64 scope link valid_lft forever preferred_lft forever root@ovs-tests:~# root@ovs-tests:~# ip netns exec ns2 ip link set dev lo up root@ovs-tests:~# ip netns exec ns2 ip link set dev eth1-ns2 up root@ovs-tests:~# ip netns exec ns2 ip address add 192.168.0.2/24 dev eth1-ns2 root@ovs-tests:~# ip netns exec ns2 ip a s 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 9: eth1-ns2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether b6:e3:94:fe:be:23 brd ff:ff:ff:ff:ff:ff inet 192.168.0.2/24 scope global eth1-ns2 valid_lft forever preferred_lft forever inet6 fe80::b4e3:94ff:fefe:be23/64 scope link valid_lft forever preferred_lft forever root@ovs-tests:~#

We should now have network connectivity between ns1 and ns2. Let's verify:

root@ovs-tests:~# ip netns exec ns1 ping -c 3 192.168.0.2 PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data. 64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.237 ms 64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.066 ms 64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=0.059 ms

--- 192.168.0.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 1999ms rtt min/avg/max/mdev = 0.059/0.120/0.237/0.083 ms root@ovs-tests:~# ip netns exec ns2 ping -c 3 192.168.0.1 PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data. 64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=0.215 ms 64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.051 ms 64 bytes from 192.168.0.1: icmp_seq=3 ttl=64 time=0.061 ms

--- 192.168.0.1 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2000ms rtt min/avg/max/mdev = 0.051/0.109/0.215/0.075 ms root@ovs-tests:~#

Now lets start a dnsmasq server in ns1 and try to get an IP lease from ns2:

root@ovs-tests:~# ip netns exec ns1 bash root@ovs-tests:~# ip a s 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 7: eth1-ns1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether de:0c:5f:c2:1c:0d brd ff:ff:ff:ff:ff:ff inet 192.168.0.1/24 scope global eth1-ns1 valid_lft forever preferred_lft forever inet6 fe80::dc0c:5fff:fec2:1c0d/64 scope link valid_lft forever preferred_lft forever root@ovs-tests:~# dnsmasq --interface=eth1-ns1 --dhcp-range=192.168.0.100,192.168.0.200,255.255.255.0 root@ovs-tests:~# pgrep -lf dnsmasq 13219 dnsmasq root@ovs-tests:~# exit exit root@ovs-tests:~# ip netns exec ns2 bash root@ovs-tests:~# dhclient -r Killed old client process root@ovs-tests:~# ip a s 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 9: eth1-ns2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether b6:e3:94:fe:be:23 brd ff:ff:ff:ff:ff:ff inet6 fe80::b4e3:94ff:fefe:be23/64 scope link valid_lft forever preferred_lft forever root@ovs-tests:~# dhclient eth1-ns2 root@ovs-tests:~# ip a s 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 9: eth1-ns2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether b6:e3:94:fe:be:23 brd ff:ff:ff:ff:ff:ff inet 192.168.0.195/24 brd 192.168.0.255 scope global eth1-ns2 valid_lft forever preferred_lft forever inet6 fe80::b4e3:94ff:fefe:be23/64 scope link valid_lft forever preferred_lft forever root@ovs-tests:~# exit exit root@ovs-tests:~#

The logs also show that ns2 got a new IP lease from the ns1 dnsmasq process:

root@ovs-tests:~# tail -f /var/log/syslog Jul 20 16:25:31 localhost dnsmasq[13219]: started, version 2.72 cachesize 150 Jul 20 16:25:31 localhost dnsmasq[13219]: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC loop-detect Jul 20 16:25:31 localhost dnsmasq-dhcp[13219]: DHCP, IP range 192.168.0.100 -- 192.168.0.200, lease time 1h Jul 20 16:25:31 localhost dnsmasq[13219]: reading /etc/resolv.conf Jul 20 16:25:31 localhost dnsmasq[13219]: using nameserver 173.203.4.9#53 Jul 20 16:25:31 localhost dnsmasq[13219]: using nameserver 173.203.4.8#53 Jul 20 16:25:31 localhost dnsmasq[13219]: ignoring nameserver 192.168.0.1 - local interface Jul 20 16:25:31 localhost dnsmasq[13219]: read /etc/hosts - 4 addresses Jul 20 16:26:03 localhost dhclient: Killed old client process Jul 20 16:26:04 localhost dhclient: DHCPRELEASE on eth1-ns2 to 192.168.0.1 port 67 Jul 20 16:26:04 localhost dnsmasq-dhcp[13219]: DHCPRELEASE(eth1-ns1) 192.168.0.195 b6:e3:94:fe:be:23 Jul 20 16:26:25 localhost dhclient: DHCPDISCOVER on eth1-ns2 to 255.255.255.255 port 67 interval 4 Jul 20 16:26:25 localhost dnsmasq[13219]: reading /etc/resolv.conf Jul 20 16:26:25 localhost dnsmasq[13219]: using nameserver 173.203.4.9#53 Jul 20 16:26:25 localhost dnsmasq[13219]: using nameserver 173.203.4.8#53 Jul 20 16:26:25 localhost dnsmasq-dhcp[13219]: DHCPDISCOVER(eth1-ns1) 192.168.0.195 b6:e3:94:fe:be:23 Jul 20 16:26:25 localhost dnsmasq-dhcp[13219]: DHCPOFFER(eth1-ns1) 192.168.0.195 b6:e3:94:fe:be:23 Jul 20 16:26:25 localhost dhclient: DHCPREQUEST on eth1-ns2 to 255.255.255.255 port 67 Jul 20 16:26:25 localhost dhclient: DHCPOFFER from 192.168.0.1 Jul 20 16:26:25 localhost dnsmasq-dhcp[13219]: DHCPREQUEST(eth1-ns1) 192.168.0.195 b6:e3:94:fe:be:23 Jul 20 16:26:25 localhost dnsmasq-dhcp[13219]: DHCPACK(eth1-ns1) 192.168.0.195 b6:e3:94:fe:be:23 ovs-tests Jul 20 16:26:28 localhost dhclient: DHCPREQUEST on eth1-ns2 to 255.255.255.255 port 67 Jul 20 16:26:28 localhost dnsmasq-dhcp[13219]: DHCPREQUEST(eth1-ns1) 192.168.0.195 b6:e3:94:fe:be:23 Jul 20 16:26:28 localhost dnsmasq-dhcp[13219]: DHCPACK(eth1-ns1) 192.168.0.195 b6:e3:94:fe:be:23 ovs-tests Jul 20 16:26:28 localhost dhclient: DHCPACK from 192.168.0.1 Jul 20 16:26:28 localhost dhclient: bound to 192.168.0.195 -- renewal in 1499 seconds.

To isolate both name spaces into their own VLANs:

root@ovs-tests:~# ovs-vsctl set port veth-ns1 tag=100 root@ovs-tests:~# ovs-vsctl set port veth-ns2 tag=200 root@ovs-tests:~# ovs-vsctl show cad47b00-2d1d-4149-9031-d369592d6d88 Bridge "OVS-1" Port "veth-ns2" tag: 200 Interface "veth-ns2" Port "OVS-1" Interface "OVS-1" type: internal Port "veth-ns1" tag: 100 Interface "veth-ns1" ovs_version: "2.3.0" root@ovs-tests:~#

Now connectivity between both ns1 and ns2 should be lost.

Resources:
[1]. http://man7.org/linux/man-pages/man7/namespaces.7.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK