In this post I’m going to introduce the concept of Linux network namespaces. The main goal of this post is to show how to use network namespaces in Linux, undestand how to kernel partition system resources to achieve a form of process isolation, how to use the command to connect processes in two different network namespaces and its relationships with container virtualization and orchestration software like Docker and Kubernetes. Container virtualization and orchestration software use network namespace on your base. If you would like to read a introduction about Linux namespaces you can see a brief introduction here
Now, I am going to introduce about network namespaces. Fundamentally Linux OS has a set of network interfaces and routing tables entries shared across the entire OS, you can have different and separate instances of network interfaces and routing tables that operate independent of each other. A network namespace is logically another copy of the network stack, with its own routes, firewall rules, and network devices. A process inherits its network namespace from its parent. Initially all the processes share the same default network namespace from the init process.
Network namespace, in particular, virtualizes the network stack. Each network namespace has its own set of resources like network interfaces, IP addresses, routing tables, tunnels, firewalls etc. For example, iptables rules added to a network namespace will only affect traffic entering and leaving that namespace.
In particular, container runtime uses the namespace kernel feature to partition system resources to achieve a form of process isolation, such that changes to the resources in one namespace do not affect that in other namespaces. Example of such resources include process IDs, hostnames, user IDs, file names, and network interfaces.
In a network namespace, the scoped ‘identifiers’ are network devices; so a given network device, such as eth0, exists in a particular namespace. Linux starts up with a default network namespace, so if your operating system does not do anything special, that is where all the network devices will be located.
So, lets create two namespaces.
ip netns add ns1 ip netns add ns1
Once the new namespaces were created, we can listed by this command:
ip netns list
You are now be able to see the namespaces created early. The follow command shows the content of the follow directory:
The same way when we added a new network namespace.
Note that if you run arp command in the host you will see the arp table in the host, but if you run that inside the namespace you will see nothing (at the moment). Other, you can try to see too the route table in the host and the route table inside the container. Note the isolation level of the namespaces. You can try these commands:
arp (inside the host) ip netns exec ns1 arp (inside namespace) route (inside the host) ip netns exec ns1 route (inside namespace)
Now you can deduce that we have no connection between namespaces created previously.
To acomplish it we have to create a virtual ethernet cable for each namespace assigning interfaces to the namespaces, and then configure those interfaces for network connectivity. A veth device is a virtual ethernet device that you can think of as a real ethernet cable connecting two other devices. Virtual ethernet devices act as tunnels between network namespaces creating a bridge to a physical network device in another namespace. When we created Network Intefaces, its all come in pair, so , first, lets create veth pairs:
ip link add veth1 type veth peer name veth2
You can choose the name of the interface you wanted, but choose the name that have easy indentify. Now you can verify the virtual interfaces created before showing the device, the link status, and the characteristics.
ip link list
When created, they belong to host (default) namespace. Now to connected these interfaces to namespaces created early you should to move the veth cables to respective namespace.
ip link set veth1 netns ns1 ip link set veth2 netns ns2
If you listed the veth interfaces again you should not able to view. It occurs because when you move the interface to the non default namespace you have to specify it before execute the command. So, you can use this command:
ip netns exec ns1 ip link list ip netns exec ns2 ip link list
It is possible to create the veth pairs and assign one of the pairs to a network namespace all in one command. So, if we wanted, instead create the veth pairs and after connect it to the namespace, we could to do this only one command like this:
ip link add veth1 netns ns1 type veth peer name veth2 netns ns2
Assign IP Addresses
Now, after create namespaces, virtual ethernet cable and connect the devices to a cable, its time to assign IP address to devices.
First lets create IP address to our devices into respective namespaces:
ip netns exec ns1 ip address add 192.168.1.1/24 dev veth1 ip netns exec ns2 ip address add 192.168.1.2/24 dev veth2
and up the interfaces:
ip netns exec ns1 ip link set veth1 up ip netns exec ns2 ip link set veth2 up
You can now check the interfaces and IPs that have created:
ip netns exec ns1 ip addr show ip netns exec ns2 ip addr show
As you can see, now it is possible ping to interface into the another namespace. Ex.:
ip netns exec ns1 ping 192.168.1.2
Now, if you execute the arp command that have tried before, you can now check only the arp table into the namespace ns1. In this case 192.168.1.2. The arp table of the host table has no idea about these interfaces for now.
ip netns exec ns1 arp
Setup your Bridge
Now think you have multiple namespaces and multiple interfaces, how do you will enable communication with them? In physical world, obviously that you will put all of them in a switch. In the same way we have to establish communication with them trought switch. With a virtual switch you can add many namespaces and interfaces you would want. Once you have both IPs and namespaces set its time to create a bridge to establish communication with them. Previouslly we have configured the interfaces veth1 and veth2 to establish the communication between them directly. To connect the namespace we’ll need to create virtual switch in host, as a native solution we have Linux Bridge , OpenVSwitch, etc… In this example, I will show a Linux Bridge to do this connection between namespaces.
We also only configured one side of each interface in the namespace. We then need to create the bridge and configure addresses to the other side of the veth pairs.
If you tried the steps above without use bridge, before you connect the cables to the bridge, you’ve to delete the cable created early directly between veth1 and veth2. You only need to delete on of them.
ip netns exec ns1 ip link del veth1
After you’ve set the name spaces, you’ve to create cables to connect to the virtual switch and add the respectives interfaces to the respectives namespaces.
ip netns add ns1 ip netns add ns2
Create the virtual cables between namespace interface and bridge interface:
ip link add veth1 type veth peer name veth1-ns1-bdg ip link add veth2 type veth peer name veth2-ns2-bdg
For convention, it is interesting to name the bridge like this VETH_NAMESPACE_BRIDGE.
Now set the interface to respective namespace
ip link set veth1 netns ns1 ip link set veth2 netns ns2
Now set the IP address to these links and up the respectives interfaces:
ip netns exec ns1 ip address add 192.168.1.1/24 dev veth1 ip netns exec ns1 ip link set veth1 up ip netns exec ns2 ip address add 192.168.1.2/24 dev veth2 ip netns exec ns2 ip link set veth2 up
You now have both the IPs and interfaces set, but you can’t establish communication between them. That’s because there’s no interface in the default namespace that can send the traffic to those namespaces, and we didn’t configure addresses to the other side of the veth pairs or configure a bridge device. But with the creation of the bridge device, we’ll be able to provide the necessary routing to properly form the network.
So, create the bridge and up it.
ip link add name bdg type bridge ip link set bdg up
Lets up the interface bridge side:
ip link set veth1-ns1-bdg up ip link set veth2-ns2-bdg up
Now lets connect the cable created early abd connect to the bridge
ip link set veth1-ns1-bdg master bdg ip link set veth2-ns2-bdg master bdg
You can check the bridge created:
bridge link show bdg
Finally lets try to ping from the namespace ns1 to IP in another namespace ns2. You should now get connection between two namespaces.
ip netns exec ns1 ping 192.168.1.2
Now, if you try to ping 192.168.1.2 directly from host? Whats happen? It does not works because they are isolated on their namespace, it doesn’t have visibility to the global scope. In order to access directly from host you should set the address of the bridge device bread.
ip addr add 192.168.1.10/24 brd + dev bdg
Now, you should ping 192.168.1.2 directly from host.
Its not possible to access the internet from the namespaces yet, but it is possible. In order to acomplish it, tou have to set default gateway to which namespace you would like to access the internet. In this case, lets set too ns1 default gateway our bridge. After to do this, we’ve to configure NAT to send and receive packets and finally, enable packet forwarding.
ip netns exec ns1 ip route add default via 192.168.1.10 iptables -t nat -A POSTROUTING -s 192.168.1.10/24 -j MASQUERADE sysctl -w net.ipv4.ip_forward=1
Now you can access the internet from namespace.
ip netns exec ns1 ping 220.127.116.11
Now you should have complete connectivity.
Behind the scenes, you learned how to containers and virtual networks works from scratch. It is interesting to understanding how containers and Linux networking concepts, tools, and commands happen behind the scenes. Fundamentally, containers are based on Linux networking and have understanding about how it works in depth helps troubleshooting more efficiently.
Summary, you learned how to create two namespaces and two virtual ethernet cables. You learned that each cable should be attached to a namespace on one side, and on the opposite end be united by a bridge, to complete the network.
In the future posts, I intend to talk about other Linux namespaces and its relationship with containers, like cgroups and others.
I hope this post was useful.