diff options
| author | Edoardo Canepa <edoardo.canepa@canonical.com> | 2026-09-17 13:15:20 +0200 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-09-21 17:26:50 -0700 |
| commit | fcc1a92b7a901473ce052d34c2d1d33da15739e4 (patch) | |
| tree | 03b05e5e1009c131bec83d7f448d143d7fc4e2df /tools/testing/selftests/net | |
| parent | 1643b81c38c92674ebceb66dd61211e4ef86b8fe (diff) | |
| download | linux-next-fcc1a92b7a901473ce052d34c2d1d33da15739e4.tar.gz linux-next-fcc1a92b7a901473ce052d34c2d1d33da15739e4.zip | |
selftests/net: run tun tests in a dedicated network namespace
The tun_vnet_udptnl fixture creates a fresh tap device, assigns it a
fixed MAC address and installs the outer neighbor entry as
NUD_PERMANENT. On systems where systemd-udevd is running and a systemd
.link file sets
MACAddressPolicy=persistent
(the default shipped by systemd in 99-default.link, so this is what
most systemd-based hosts inherit), systemd-udevd's net_setup_link
builtin asynchronously sends an RTM_SETLINK to reassign the freshly
created tap device's MAC to a machine-persistent value. When that
lands after the fixture has configured the device, both directions
break:
- recv_gso_packet transmits through the tap, and the address change
flushes the neighbor entry the fixture installed:
do_setlink
-> netif_set_mac_address
-> call_netdevice_notifiers(NETDEV_CHANGEADDR)
-> ndisc_netdev_event (arp_netdev_event for an IPv4 outer)
-> neigh_changeaddr
-> neigh_flush_dev(tbl, dev, /* skip_perm = */ false)
so the packet hits __neigh_create() and waits on neighbor resolution
that never completes.
- send_gso_packet writes frames addressed to the MAC the fixture
assigned. Once the tap has a different address, eth_type_trans()
marks them PACKET_OTHERHOST and the IP receive path drops them.
Either way nothing arrives before the receive timeout, and the test
fails with, for example:
tun.c:947:send_gso_packet:Expected ret (0) == variant->data_size (1)
tun.c:948:send_gso_packet:Expected r_num_mss (0) == variant->r_num_mss (1)
tun.c:962:recv_gso_packet:Expected ret (0) == variant->data_size (1)
The failure is non-deterministic and reproduces on a plain
systemd-based VM with no containers.
Fix by calling unshare(CLONE_NEWNET) from both fixture setups. The
harness runs each test in its own forked process, so every test gets a
private network namespace that is torn down with it, and all tap and
geneve devices are created in a namespace that systemd-udevd (running
in the init netns) does not watch, so its RTM_SETLINK never fires
against them.
Creating a network namespace needs CAP_SYS_ADMIN in the current user
namespace and CONFIG_NET_NS=y, neither of which the tests required
before. Where they are unavailable the unshare() is reported with
SKIP() rather than aborting, so the binary still emits a full TAP
stream and a runner can tell "network namespaces unavailable" apart
from a real tun/tap regression.
Verified on a plain systemd-based VM running the affected kernel.
Without the fix, 1000 sequential invocations of
tun -r tun_vnet_udptnl.4in6_nogsosz_1byte.recv_gso_packet
fail 10 times, and 2 out of 20 full runs of the test binary fail. With
the fix there are no failures in either case.
Reported-by: Po-Hsu Lin <po-hsu.lin@canonical.com>
Closes: https://bugs.launchpad.net/bugs/2158217
Signed-off-by: Edoardo Canepa <edoardo.canepa@canonical.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260917111520.2614448-1-edoardo.canepa@canonical.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'tools/testing/selftests/net')
| -rw-r--r-- | tools/testing/selftests/net/tun.c | 9 |
1 files changed, 9 insertions, 0 deletions
diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c index abe488bac50b..6db21dad0efe 100644 --- a/tools/testing/selftests/net/tun.c +++ b/tools/testing/selftests/net/tun.c @@ -4,6 +4,7 @@ #include <errno.h> #include <fcntl.h> +#include <sched.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -488,6 +489,10 @@ FIXTURE(tun) FIXTURE_SETUP(tun) { + if (unshare(CLONE_NEWNET)) + SKIP(return, "Cannot create network namespace: %s", + strerror(errno)); + memset(self->ifname, 0, sizeof(self->ifname)); self->fd = tun_alloc(self->ifname); @@ -732,6 +737,10 @@ FIXTURE_SETUP(tun_vnet_udptnl) struct sockaddr_storage ssa, dsa; void *sip, *dip, *smac, *dmac; + if (unshare(CLONE_NEWNET)) + SKIP(return, "Cannot create network namespace: %s", + strerror(errno)); + flags = (variant->is_tap ? IFF_TAP : IFF_TUN) | IFF_VNET_HDR | IFF_MULTI_QUEUE | IFF_NO_PI; features = TUN_F_CSUM | TUN_F_UDP_TUNNEL_GSO | |
