# SPDX-License-Identifier: GPL-2.0 import ctypes import os import random import string import subprocess import time from pathlib import Path from .utils import ip libc = ctypes.cdll.LoadLibrary('libc.so.6') class NetNS: def __init__(self, name=None): if name: self.name = name else: self.name = ''.join(random.choice(string.ascii_lowercase) for _ in range(8)) ip('netns add ' + self.name) def __del__(self): if self.name: ip('netns del ' + self.name) self.name = None def __enter__(self): return self def __exit__(self, ex_type, ex_value, ex_tb): self.__del__() def __str__(self): return self.name def __repr__(self): return f"NetNS({self.name})" class UserNetNS: """Network namespace owned by a non-init user namespace.""" def __init__(self): self.name = ''.join( random.choice(string.ascii_lowercase) for _ in range(8)) self.user_ns_path = f"/run/userns/{self.name}" self.net_ns_path = f"/run/netns/{self.name}" self._user_mounted = False self._net_mounted = False os.makedirs("/run/userns", exist_ok=True) os.makedirs("/run/netns", exist_ok=True) Path(self.user_ns_path).touch() Path(self.net_ns_path).touch() with subprocess.Popen( ["unshare", "--user", "--net", "--map-root-user", "sleep", "infinity"]) as proc: try: pid = proc.pid init_user = os.readlink("/proc/self/ns/user") for _ in range(200): try: if os.readlink(f"/proc/{pid}/ns/user") != init_user: break except OSError: pass time.sleep(0.01) else: raise RuntimeError("unshare child did not create userns") subprocess.run(["mount", "--bind", f"/proc/{pid}/ns/user", self.user_ns_path], check=True) self._user_mounted = True subprocess.run(["mount", "--bind", f"/proc/{pid}/ns/net", self.net_ns_path], check=True) self._net_mounted = True finally: proc.kill() def __del__(self): if self._net_mounted: subprocess.run(["umount", self.net_ns_path], check=False) self._net_mounted = False if self._user_mounted: subprocess.run(["umount", self.user_ns_path], check=False) self._user_mounted = False for path in (self.net_ns_path, self.user_ns_path): try: os.unlink(path) except OSError: pass def __enter__(self): return self def __exit__(self, ex_type, ex_value, ex_tb): self.__del__() def __str__(self): return self.name def __repr__(self): return f"UserNetNS({self.name})" class NetNSEnter: def __init__(self, ns_name): self.ns_path = f"/run/netns/{ns_name}" def __enter__(self): self.saved = open("/proc/thread-self/ns/net") with open(self.ns_path) as ns_file: libc.setns(ns_file.fileno(), 0) return self def __exit__(self, exc_type, exc_value, traceback): libc.setns(self.saved.fileno(), 0) self.saved.close()