mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
Add userns_devmem.py, which mirrors nk_devmem.py but places the netkit guest in a netns whose owning user_ns is non-init. ncdevmem is ran there via nsenter so the bind-rx call is issued with creds that hold CAP_NET_ADMIN only in the child user_ns. Without the preceding GENL_UNS_ADMIN_PERM patch the test fails at bind-rx with EPERM, but with the patch the transfer completes and tests pass. Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com> Acked-by: Stanislav Fomichev <sdf@fomichev.me> Link: https://patch.msgid.link/20260602-nl-prov-v2-2-ad721142c641@meta.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
123 lines
3.4 KiB
Python
123 lines
3.4 KiB
Python
# 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()
|