How to traverse network namespaces

George Shuklin
OpsOps
Published in
2 min readMar 29, 2023

I’m working on a special routing daemon which peeks into network namespaces a lot. I use pyroute2 library to manage routes, and my initial intention was to use NetNS facility in the pyroute2. Their approach to namespaces is to use a wrapper/helper, which spans a process inside a namespace and communicates back to the main process.

It’s very inefficient for my case, because I need to scan many namespaces on each run of a control loop, with expected run time for few hundreds of them in less then a second. I’ve needed a better version.

After some thinking and dubious hallucinations from ChatGPT I was able to write my code to do so. It’s a rather involved, but it really solve my problem of ‘fast namespace traversal’:

  1. It goes directly from one namespace into another, e.g. do not waste time on returning back to root namespace.
  2. It do not span processes, therefore run fast (at Python scale of fast, not the Rust one, sorry). Nevertheless, even ‘python fast’ is much faster than spanning 300+ processes every few seconds.

In this example I use netifaces library to prove that I’ve switched to namespace. Its use is not mandatory.

import os
import ctypes
import netifaces

CLONE_NEWNET = 0x40000000

libc = ctypes.CDLL("libc.so.6", use_errno=True)

def ifaces():
for iface in netifaces.interfaces():
print(netifaces.ifaddresses(iface))

def ns(fd):
libc.setns(fd, CLONE_NEWNET)

root_fd = os.open('/proc/self/ns/net', os.O_RDONLY)
fd1 = os.open('/run/netns/qrouter-2d719c54-55f7-4ed7-98cd-3d08637dbb76', os.O_RDONLY)
fd2 = os.open('/run/netns/qrouter-1d7b1125-9954-4782-bf3d-70ef4ebf3277', os.O_RDONLY)

ifaces()

print("\n\n\nIn 1")
ns(fd1)

ifaces()

print("\n\n\nIn 2")
ns(fd2)
ifaces()

print("\n\nDone")
ns(root_fd)
ifaces()

The hallucination I’m talking about is use of /proc/self/ns/net to obtain fd to original namespace. ChatGPT proposed to use open(‘/’), which is absurd, but it sparked my imagination to find a suitable source for FD, and ‘/proc/self’ is really good source.

The technique here is to call ‘setns’ function from libc directly. We need a constant for that (man 2 setns), and a usual ctypes cruft.

--

--

George Shuklin
OpsOps

I work at Servers.com, most of my stories are about Ansible, Ceph, Python, Openstack and Linux. My hobby is Rust.