* [PATCH net-next 0/2] net: devmem: allow bind-rx from non-init user namespaces
@ 2026-06-01 19:24 Bobby Eshleman
2026-06-01 19:24 ` [PATCH net-next 1/2] " Bobby Eshleman
2026-06-01 19:24 ` [PATCH net-next 2/2] selftests: drv-net: add userns devmem RX test Bobby Eshleman
0 siblings, 2 replies; 4+ messages in thread
From: Bobby Eshleman @ 2026-06-01 19:24 UTC (permalink / raw)
To: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, Shuah Khan
Cc: netdev, linux-kernel, linux-kselftest, Stanislav Fomichev,
Mina Almasry, Bobby Eshleman
NETDEV_CMD_BIND_RX is GENL_ADMIN_PERM, which checks CAP_NET_ADMIN
against init_user_ns. With netkit and netns support for devmem, it is
now useful to let workloads holding CAP_NET_ADMIN only in their own
user_ns issue bind-rx for a netns owned by that user_ns.
The first patch switches the flag to GENL_UNS_ADMIN_PERM so the check uses
the target netns's owning user_ns. Init remains permitted.
The second patch just adds test cases. They are identical to
nk_devmem.py tests, but using a non-init userns.
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Bobby Eshleman (2):
net: devmem: allow bind-rx from non-init user namespaces
selftests: drv-net: add userns devmem RX test
Documentation/netlink/specs/netdev.yaml | 2 +-
net/core/netdev-genl-gen.c | 2 +-
tools/testing/selftests/drivers/net/hw/Makefile | 1 +
tools/testing/selftests/drivers/net/hw/config | 1 +
.../selftests/drivers/net/hw/lib/py/__init__.py | 4 +-
.../selftests/drivers/net/hw/userns_devmem.py | 48 +++++++++++++
tools/testing/selftests/drivers/net/lib/py/env.py | 8 ++-
tools/testing/selftests/net/lib/py/__init__.py | 4 +-
tools/testing/selftests/net/lib/py/netns.py | 79 ++++++++++++++++++++++
tools/testing/selftests/net/lib/py/utils.py | 7 +-
10 files changed, 146 insertions(+), 10 deletions(-)
---
base-commit: 0906c117f81c2ae6e6dbfa82719f79c75e1c9325
change-id: 20260529-nl-prov-491a85c020b0
Best regards,
--
Bobby Eshleman <bobbyeshleman@meta.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net-next 1/2] net: devmem: allow bind-rx from non-init user namespaces
2026-06-01 19:24 [PATCH net-next 0/2] net: devmem: allow bind-rx from non-init user namespaces Bobby Eshleman
@ 2026-06-01 19:24 ` Bobby Eshleman
2026-06-01 19:24 ` [PATCH net-next 2/2] selftests: drv-net: add userns devmem RX test Bobby Eshleman
1 sibling, 0 replies; 4+ messages in thread
From: Bobby Eshleman @ 2026-06-01 19:24 UTC (permalink / raw)
To: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, Shuah Khan
Cc: netdev, linux-kernel, linux-kselftest, Stanislav Fomichev,
Mina Almasry, Bobby Eshleman
From: Bobby Eshleman <bobbyeshleman@meta.com>
NETDEV_CMD_BIND_RX is currently GENL_ADMIN_PERM, which checks
CAP_NET_ADMIN against init userns. With recent container/netkit/ns
support for devmem, other userns/netns use cases come online and require
bind-rx to allow CAP_NET_ADMIN in non-init user ns as well.
Switch the flag to GENL_UNS_ADMIN_PERM to allow bind-rx for
CAP_NET_ADMIN in the netns's owning userns as well.
Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
---
Documentation/netlink/specs/netdev.yaml | 2 +-
net/core/netdev-genl-gen.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
index a1f4c5a561e9..49862b666d7d 100644
--- a/Documentation/netlink/specs/netdev.yaml
+++ b/Documentation/netlink/specs/netdev.yaml
@@ -798,7 +798,7 @@ operations:
name: bind-rx
doc: Bind dmabuf to netdev
attribute-set: dmabuf
- flags: [admin-perm]
+ flags: [uns-admin-perm]
do:
request:
attributes:
diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c
index c7e138bfe345..d18c89b5a6c7 100644
--- a/net/core/netdev-genl-gen.c
+++ b/net/core/netdev-genl-gen.c
@@ -220,7 +220,7 @@ static const struct genl_split_ops netdev_nl_ops[] = {
.doit = netdev_nl_bind_rx_doit,
.policy = netdev_bind_rx_nl_policy,
.maxattr = NETDEV_A_DMABUF_FD,
- .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = NETDEV_CMD_NAPI_SET,
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH net-next 2/2] selftests: drv-net: add userns devmem RX test
2026-06-01 19:24 [PATCH net-next 0/2] net: devmem: allow bind-rx from non-init user namespaces Bobby Eshleman
2026-06-01 19:24 ` [PATCH net-next 1/2] " Bobby Eshleman
@ 2026-06-01 19:24 ` Bobby Eshleman
2026-06-01 23:36 ` Jakub Kicinski
1 sibling, 1 reply; 4+ messages in thread
From: Bobby Eshleman @ 2026-06-01 19:24 UTC (permalink / raw)
To: Donald Hunter, Jakub Kicinski, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Andrew Lunn, Shuah Khan
Cc: netdev, linux-kernel, linux-kselftest, Stanislav Fomichev,
Mina Almasry, Bobby Eshleman
From: Bobby Eshleman <bobbyeshleman@meta.com>
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>
---
tools/testing/selftests/drivers/net/hw/Makefile | 1 +
tools/testing/selftests/drivers/net/hw/config | 1 +
.../selftests/drivers/net/hw/lib/py/__init__.py | 4 +-
.../selftests/drivers/net/hw/userns_devmem.py | 48 +++++++++++++
tools/testing/selftests/drivers/net/lib/py/env.py | 8 ++-
tools/testing/selftests/net/lib/py/__init__.py | 4 +-
tools/testing/selftests/net/lib/py/netns.py | 79 ++++++++++++++++++++++
tools/testing/selftests/net/lib/py/utils.py | 7 +-
8 files changed, 144 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/hw/Makefile b/tools/testing/selftests/drivers/net/hw/Makefile
index c7a1206880ea..fd0535a96d84 100644
--- a/tools/testing/selftests/drivers/net/hw/Makefile
+++ b/tools/testing/selftests/drivers/net/hw/Makefile
@@ -47,6 +47,7 @@ TEST_PROGS = \
rss_input_xfrm.py \
toeplitz.py \
tso.py \
+ userns_devmem.py \
uso.py \
xdp_metadata.py \
xsk_reconfig.py \
diff --git a/tools/testing/selftests/drivers/net/hw/config b/tools/testing/selftests/drivers/net/hw/config
index 8c132ace2b8d..3da6d3e39960 100644
--- a/tools/testing/selftests/drivers/net/hw/config
+++ b/tools/testing/selftests/drivers/net/hw/config
@@ -17,5 +17,6 @@ CONFIG_NET_IPGRE_DEMUX=y
CONFIG_NETKIT=y
CONFIG_NET_SCH_INGRESS=y
CONFIG_UDMABUF=y
+CONFIG_USER_NS=y
CONFIG_VXLAN=y
CONFIG_XFRM_USER=y
diff --git a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
index 84a4dab6c649..8a58cb17cc06 100644
--- a/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
+++ b/tools/testing/selftests/drivers/net/hw/lib/py/__init__.py
@@ -18,7 +18,7 @@ try:
sys.path.append(KSFT_DIR.as_posix())
# Import one by one to avoid pylint false positives
- from net.lib.py import NetNS, NetNSEnter, NetdevSimDev
+ from net.lib.py import NetNS, NetNSEnter, NetdevSimDev, UserNetNS
from net.lib.py import EthtoolFamily, NetdevFamily, NetshaperFamily, \
NlError, RtnlFamily, DevlinkFamily, PSPFamily, Netlink
from net.lib.py import CmdExitFailure
@@ -34,7 +34,7 @@ try:
from drivers.net.lib.py import GenerateTraffic, Remote, Iperf3Runner
from drivers.net.lib.py import NetDrvEnv, NetDrvEpEnv, NetDrvContEnv
- __all__ = ["NetNS", "NetNSEnter", "NetdevSimDev",
+ __all__ = ["NetNS", "NetNSEnter", "NetdevSimDev", "UserNetNS",
"EthtoolFamily", "NetdevFamily", "NetshaperFamily",
"NlError", "RtnlFamily", "DevlinkFamily", "PSPFamily", "Netlink",
"CmdExitFailure",
diff --git a/tools/testing/selftests/drivers/net/hw/userns_devmem.py b/tools/testing/selftests/drivers/net/hw/userns_devmem.py
new file mode 100755
index 000000000000..f824cc9ed3c6
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/hw/userns_devmem.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+"""
+Devmem tests for non-init userns.
+"""
+
+import os
+
+from devmem_lib import run_rx, run_rx_hds, run_tx, run_tx_chunks, setup_test
+from lib.py import NetDrvContEnv, ksft_disruptive, ksft_exit, ksft_run
+
+
+@ksft_disruptive
+def check_userns_rx(cfg) -> None:
+ """Run the devmem RX test through non-init userns netkit."""
+ run_rx(cfg)
+
+
+@ksft_disruptive
+def check_userns_tx(cfg) -> None:
+ """Run the devmem TX test through non-init userns netkit."""
+ run_tx(cfg)
+
+
+@ksft_disruptive
+def check_userns_tx_chunks(cfg) -> None:
+ """Run the devmem TX chunking test through non-init userns netkit."""
+ run_tx_chunks(cfg)
+
+
+def check_userns_rx_hds(cfg) -> None:
+ """Run the HDS test through non-init userns netkit."""
+ run_rx_hds(cfg)
+
+
+def main() -> None:
+ with NetDrvContEnv(__file__, userns=True, rxqueues=2,
+ primary_rx_redirect=True) as cfg:
+ setup_test(cfg,
+ os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ "ncdevmem"))
+ ksft_run([check_userns_rx, check_userns_tx, check_userns_tx_chunks,
+ check_userns_rx_hds], args=(cfg,))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/testing/selftests/drivers/net/lib/py/env.py b/tools/testing/selftests/drivers/net/lib/py/env.py
index ef317aef3a0a..2cc78b8a2152 100644
--- a/tools/testing/selftests/drivers/net/lib/py/env.py
+++ b/tools/testing/selftests/drivers/net/lib/py/env.py
@@ -9,7 +9,7 @@ from pathlib import Path
from lib.py import KsftSkipEx, KsftXfailEx
from lib.py import ksft_setup, wait_file
from lib.py import cmd, ethtool, ip, CmdExitFailure
-from lib.py import NetNS, NetdevSimDev
+from lib.py import NetNS, NetdevSimDev, UserNetNS
from .remote import Remote
from . import bpftool, RtnlFamily, Netlink
@@ -337,8 +337,10 @@ class NetDrvContEnv(NetDrvEpEnv):
+---------------+
"""
- def __init__(self, src_path, rxqueues=1, primary_rx_redirect=False, **kwargs):
+ def __init__(self, src_path, rxqueues=1, primary_rx_redirect=False,
+ userns=False, **kwargs):
self.netns = None
+ self._userns = userns
self._nk_host_ifname = None
self.nk_guest_ifname = None
self._tc_clsact_added = False
@@ -463,7 +465,7 @@ class NetDrvContEnv(NetDrvEpEnv):
with open(ra_path, "w", encoding="utf-8") as f:
f.write("2")
- self.netns = NetNS()
+ self.netns = UserNetNS() if self._userns else NetNS()
cmd("ip netns attach init 1")
self._init_ns_attached = True
ip("netns set init 0", ns=self.netns)
diff --git a/tools/testing/selftests/net/lib/py/__init__.py b/tools/testing/selftests/net/lib/py/__init__.py
index 64a8c1ed4950..e58bdbdc58ee 100644
--- a/tools/testing/selftests/net/lib/py/__init__.py
+++ b/tools/testing/selftests/net/lib/py/__init__.py
@@ -10,7 +10,7 @@ from .ksft import KsftFailEx, KsftSkipEx, KsftXfailEx, ksft_pr, ksft_eq, \
ksft_ge, ksft_gt, ksft_lt, ksft_raises, ksft_busy_wait, \
ktap_result, ksft_disruptive, ksft_setup, ksft_run, ksft_exit, \
ksft_variants, KsftNamedVariant
-from .netns import NetNS, NetNSEnter
+from .netns import NetNS, NetNSEnter, UserNetNS
from .nsim import NetdevSim, NetdevSimDev
from .utils import CmdExitFailure, fd_read_timeout, cmd, bkg, defer, \
bpftool, ip, ethtool, bpftrace, rand_port, rand_ports, wait_port_listen, \
@@ -26,7 +26,7 @@ __all__ = ["KSRC",
"ksft_is", "ksft_ge", "ksft_gt", "ksft_lt", "ksft_raises",
"ksft_busy_wait", "ktap_result", "ksft_disruptive", "ksft_setup",
"ksft_run", "ksft_exit", "ksft_variants", "KsftNamedVariant",
- "NetNS", "NetNSEnter",
+ "NetNS", "NetNSEnter", "UserNetNS",
"CmdExitFailure", "fd_read_timeout", "cmd", "bkg", "defer",
"bpftool", "ip", "ethtool", "bpftrace", "rand_port", "rand_ports",
"wait_port_listen", "wait_file", "tool", "tc",
diff --git a/tools/testing/selftests/net/lib/py/netns.py b/tools/testing/selftests/net/lib/py/netns.py
index 8e9317044eef..965f5802bef2 100644
--- a/tools/testing/selftests/net/lib/py/netns.py
+++ b/tools/testing/selftests/net/lib/py/netns.py
@@ -2,8 +2,12 @@
from .utils import ip
import ctypes
+import os
import random
import string
+import subprocess
+import time
+from pathlib import Path
libc = ctypes.cdll.LoadLibrary('libc.so.6')
@@ -34,6 +38,81 @@ class NetNS:
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
+ self._unshare = None
+
+ 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()
+
+ self._unshare = subprocess.Popen(
+ ["unshare", "--user", "--net", "--map-root-user",
+ "sleep", "infinity"])
+
+ try:
+ pid = self._unshare.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
+ except (OSError, RuntimeError, subprocess.CalledProcessError):
+ self._unshare.kill()
+ self._unshare.wait()
+ raise
+
+ 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
+ if self._unshare and self._unshare.poll() is None:
+ self._unshare.kill()
+ self._unshare.wait()
+ 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}"
diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py
index be9408a77168..87eae79d01c1 100644
--- a/tools/testing/selftests/net/lib/py/utils.py
+++ b/tools/testing/selftests/net/lib/py/utils.py
@@ -47,7 +47,12 @@ class cmd:
background=False, host=None, timeout=5, ksft_ready=None,
ksft_wait=None):
if ns:
- comm = f'ip netns exec {ns} ' + comm
+ if hasattr(ns, 'user_ns_path'):
+ comm = (f'nsenter --user={ns.user_ns_path} '
+ f'--net={ns.net_ns_path} --setuid=0 --setgid=0 -- '
+ + comm)
+ else:
+ comm = f'ip netns exec {ns} ' + comm
self.stdout = None
self.stderr = None
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net-next 2/2] selftests: drv-net: add userns devmem RX test
2026-06-01 19:24 ` [PATCH net-next 2/2] selftests: drv-net: add userns devmem RX test Bobby Eshleman
@ 2026-06-01 23:36 ` Jakub Kicinski
0 siblings, 0 replies; 4+ messages in thread
From: Jakub Kicinski @ 2026-06-01 23:36 UTC (permalink / raw)
To: Bobby Eshleman
Cc: Donald Hunter, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Andrew Lunn, Shuah Khan, netdev, linux-kernel,
linux-kselftest, Stanislav Fomichev, Mina Almasry, Bobby Eshleman
On Mon, 01 Jun 2026 12:24:28 -0700 Bobby Eshleman wrote:
> From: Bobby Eshleman <bobbyeshleman@meta.com>
>
> 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.
# Traceback (most recent call last):
# File "/srv/vmksft/testing/wt-17/tools/testing/selftests/drivers/net/./hds.py", line 8, in <module>
# from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_raises, KsftSkipEx
# File "/srv/vmksft/testing/wt-17/tools/testing/selftests/drivers/net/lib/py/__init__.py", line 50, in <module>
# from .env import NetDrvEnv, NetDrvEpEnv, NetDrvContEnv
# File "/srv/vmksft/testing/wt-17/tools/testing/selftests/drivers/net/lib/py/env.py", line 12, in <module>
# from lib.py import NetNS, NetdevSimDev, UserNetNS
# ImportError: cannot import name 'UserNetNS' from partially initialized module 'lib.py' (most likely due to a circular import) (/srv/vmksft/testing/wt-17/tools/testing/selftests/drivers/net/lib/py/__init__.py)
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-01 23:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-01 19:24 [PATCH net-next 0/2] net: devmem: allow bind-rx from non-init user namespaces Bobby Eshleman
2026-06-01 19:24 ` [PATCH net-next 1/2] " Bobby Eshleman
2026-06-01 19:24 ` [PATCH net-next 2/2] selftests: drv-net: add userns devmem RX test Bobby Eshleman
2026-06-01 23:36 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox