From f2fe5a940584826b431ef4d68f3a508d3c6d8e6b Mon Sep 17 00:00:00 2001 From: Peter Saveliev Date: Tue, 7 Jan 2025 16:03:32 +0100 Subject: [PATCH 1/3] iproute: respect the flags Bug-Url: https://github.com/svinota/pyroute2/issues/1226 --- pyroute2/netlink/core.py | 53 ++++++++++++++++++------- tests/test_linux/test_ipr/test_netns.py | 18 +++++++++ 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/pyroute2/netlink/core.py b/pyroute2/netlink/core.py index adf5e42e3..bbe2a8dfe 100644 --- a/pyroute2/netlink/core.py +++ b/pyroute2/netlink/core.py @@ -1,6 +1,8 @@ import asyncio +import builtins import collections import errno +import json import logging import multiprocessing import os @@ -114,21 +116,31 @@ def datagram_received(self, data, addr): self.enqueue(data, addr) -async def netns_main(ctl, nsname, cls): +async def netns_main(ctl, nsname, flags, cls): # A simple child process # - # 1. set network namespace - setns(nsname) - # 2. start the socket object - s = cls() - await s.ensure_socket() - # 3. send back the file descriptor - socket.send_fds(ctl, [b'test'], [s.socket.fileno()], 1) + payload = None + fds = None + try: + # 1. set network namespace + setns(nsname, flags) + # 2. start the socket object + s = cls() + await s.ensure_socket() + payload = {} + fds = [s.socket.fileno()] + except Exception as e: + # get class name + payload = {'name': e.__class__.__name__, 'args': e.args} + fds = [] + finally: + # 3. send the feedback + socket.send_fds(ctl, [json.dumps(payload).encode('utf-8')], fds, 1) # 4. exit -def netns_init(ctl, nsname, cls): - asyncio.run(netns_main(ctl, nsname, cls)) +def netns_init(ctl, nsname, flags, cls): + asyncio.run(netns_main(ctl, nsname, flags, cls)) class AsyncCoreSocket: @@ -241,12 +253,25 @@ async def ensure_socket(self): ctrl = socket.socketpair() nsproc = multiprocessing.Process( target=netns_init, - args=(ctrl[0], self.spec['netns'], type(self)), + args=( + ctrl[0], + self.spec['netns'], + self.spec['flags'], + type(self), + ), ) nsproc.start() - (_, (self.local.fileno,), _, _) = socket.recv_fds( - ctrl[1], 1024, 1 - ) + (data, fds, _, _) = socket.recv_fds(ctrl[1], 1024, 1) + # load the feedback + payload = json.loads(data.decode('utf-8')) + if payload: + if set(payload.keys()) != set(('name', 'args')): + raise TypeError('error loading netns feedback') + error_class = getattr(builtins, payload['name']) + if not issubclass(error_class, Exception): + raise TypeError('error loading netns error') + raise error_class(*payload['args']) + self.local.fileno = fds[0] nsproc.join() # 8<----------------------------------------- self.local.socket = await self.setup_socket() diff --git a/tests/test_linux/test_ipr/test_netns.py b/tests/test_linux/test_ipr/test_netns.py index 20f13473a..1fed10f18 100644 --- a/tests/test_linux/test_ipr/test_netns.py +++ b/tests/test_linux/test_ipr/test_netns.py @@ -1,3 +1,6 @@ +import errno + +import pytest from pr2test.marks import require_root from pyroute2 import NetNS @@ -5,6 +8,21 @@ pytestmark = [require_root()] +def test_flags(context): + nsname = context.new_nsname + with pytest.raises(FileNotFoundError) as e: + NetNS(nsname, flags=0) + assert e.value.args[0] == errno.ENOENT + # 8<----------------------------------------------------- + ns = NetNS(nsname, flags=64) + assert len([x.get('index') for x in ns.link('dump')]) > 0 + ns.close() + # 8<----------------------------------------------------- + ns = NetNS(nsname, flags=0) + assert len([x.get('index') for x in ns.link('dump')]) > 0 + ns.close() + + def test_get_netns_info(context): nsname = context.new_nsname peer_name = context.new_ifname From e547f0c6296f6e22058831568ee0e820b10e3cc1 Mon Sep 17 00:00:00 2001 From: Peter Saveliev Date: Tue, 7 Jan 2025 16:25:27 +0100 Subject: [PATCH 2/3] ci/iproute: avoid magic int in the test use os.O_CREAT instead Bug-Url: https://github.com/svinota/pyroute2/pull/1234 --- tests/test_linux/test_ipr/test_netns.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_linux/test_ipr/test_netns.py b/tests/test_linux/test_ipr/test_netns.py index 1fed10f18..108151b9c 100644 --- a/tests/test_linux/test_ipr/test_netns.py +++ b/tests/test_linux/test_ipr/test_netns.py @@ -1,4 +1,5 @@ import errno +import os import pytest from pr2test.marks import require_root @@ -14,7 +15,7 @@ def test_flags(context): NetNS(nsname, flags=0) assert e.value.args[0] == errno.ENOENT # 8<----------------------------------------------------- - ns = NetNS(nsname, flags=64) + ns = NetNS(nsname, flags=os.O_CREAT) assert len([x.get('index') for x in ns.link('dump')]) > 0 ns.close() # 8<----------------------------------------------------- From 1ccb582ea9df19df8ff6b80078601d28529f042e Mon Sep 17 00:00:00 2001 From: Peter Saveliev Date: Wed, 8 Jan 2025 10:37:41 +0100 Subject: [PATCH 3/3] iproute: respect the libc argument --- pyroute2/netlink/core.py | 9 +++++---- tests/test_linux/test_ipr/test_netns.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pyroute2/netlink/core.py b/pyroute2/netlink/core.py index bbe2a8dfe..64c71d5c3 100644 --- a/pyroute2/netlink/core.py +++ b/pyroute2/netlink/core.py @@ -116,14 +116,14 @@ def datagram_received(self, data, addr): self.enqueue(data, addr) -async def netns_main(ctl, nsname, flags, cls): +async def netns_main(ctl, nsname, flags, libc, cls): # A simple child process # payload = None fds = None try: # 1. set network namespace - setns(nsname, flags) + setns(nsname, flags=flags, libc=libc) # 2. start the socket object s = cls() await s.ensure_socket() @@ -139,8 +139,8 @@ async def netns_main(ctl, nsname, flags, cls): # 4. exit -def netns_init(ctl, nsname, flags, cls): - asyncio.run(netns_main(ctl, nsname, flags, cls)) +def netns_init(ctl, nsname, flags, libc, cls): + asyncio.run(netns_main(ctl, nsname, flags, libc, cls)) class AsyncCoreSocket: @@ -257,6 +257,7 @@ async def ensure_socket(self): ctrl[0], self.spec['netns'], self.spec['flags'], + self.libc, type(self), ), ) diff --git a/tests/test_linux/test_ipr/test_netns.py b/tests/test_linux/test_ipr/test_netns.py index 108151b9c..eca5e8dbd 100644 --- a/tests/test_linux/test_ipr/test_netns.py +++ b/tests/test_linux/test_ipr/test_netns.py @@ -1,3 +1,4 @@ +import ctypes import errno import os @@ -24,6 +25,22 @@ def test_flags(context): ns.close() +def test_libc_id(context): + libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) + nsname = context.new_nsname + ns = NetNS(nsname, libc=libc) + assert id(ns.asyncore.libc) == id(libc) + ns.close() + + +def test_libc_fail_string(context): + nsname = context.new_nsname + with pytest.raises(AttributeError): + # if we pass a string instead of a libc object, the + # libc.mount() must fail with AttributeError + NetNS(nsname, libc='nonsense') + + def test_get_netns_info(context): nsname = context.new_nsname peer_name = context.new_ifname