Я работаю над специальным демоном маршрутизации, который много заглядывает в сетевые пространства имен. Я использую библиотеку pyroute2 для управления маршрутами, и моим первоначальным намерением было использовать NetNS facility в файле pyroute2. Их подход к пространствам имен заключается в использовании оболочки/помощника, который охватывает процесс внутри пространства имен и связывается с основным процессом.

В моем случае это очень неэффективно, потому что мне нужно сканировать множество пространств имен при каждом запуске цикла управления, при этом ожидаемое время выполнения нескольких сотен из них составляет менее секунды. Мне нужна была лучшая версия.

После некоторых размышлений и сомнительных галлюцинаций от ChatGPT я смог написать свой код для этого. Это довольно сложно, но действительно решает мою проблему «быстрого обхода пространства имен»:

  1. Он переходит непосредственно из одного пространства имен в другое, например. не тратьте время на возвращение в корневое пространство имен.
  2. Он не охватывает процессы, поэтому работает быстро (извините, в масштабе Python, а не в Rust). Тем не менее, даже «python fast» намного быстрее, чем охват 300+ процессов каждые несколько секунд.

В этом примере я использую библиотеку netifaces, чтобы доказать, что я переключился на пространство имен. Его использование не является обязательным.

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()

Галлюцинация, о которой я говорю, — это использование /proc/self/ns/net для получения fd исходного пространства имен. ChatGPT предложил использовать open(‘/’), что абсурдно, но это разожгло мое воображение, чтобы найти подходящий источник для FD, и «/proc/self» действительно хороший источник.

Техника здесь заключается в прямом вызове функции setns из libc. Для этого нам нужна константа (man 2 setns) и обычный ctypes cruff.