[LTP] [PATCH v2 23/31] testcases: sysfs: Add sys_net04

Cyril Hrubis <[email protected]>
Newsgroups it.linux.lists.ltp
Message-ID <[email protected]>
A test for /sys/class/net/*/* files.

Similar to sys_net02 but this time for TUN/TAP devices.

Signed-off-by: Cyril Hrubis <[email protected]>
---
 runtest/sysfs                                |   1 +
 testcases/kernel/sysfs/class/net/.gitignore  |   1 +
 testcases/kernel/sysfs/class/net/sys_net04.c | 217 +++++++++++++++++++
 3 files changed, 219 insertions(+)
 create mode 100644 testcases/kernel/sysfs/class/net/sys_net04.c

diff --git a/runtest/sysfs b/runtest/sysfs
index b2cc0005c..290866349 100644
--- a/runtest/sysfs
+++ b/runtest/sysfs
@@ -18,3 +18,4 @@ sys_thermal01 sys_thermal01
 sys_net01 sys_net01
 sys_net02 sys_net02
 sys_net03 sys_net03
+sys_net04 sys_net04
diff --git a/testcases/kernel/sysfs/class/net/.gitignore b/testcases/kernel/sysfs/class/net/.gitignore
index 8c3782a3b..6e934b7c1 100644
--- a/testcases/kernel/sysfs/class/net/.gitignore
+++ b/testcases/kernel/sysfs/class/net/.gitignore
@@ -1,3 +1,4 @@
 /sys_net01
 /sys_net02
 /sys_net03
+/sys_net04
diff --git a/testcases/kernel/sysfs/class/net/sys_net04.c b/testcases/kernel/sysfs/class/net/sys_net04.c
new file mode 100644
index 000000000..c1eda1329
--- /dev/null
+++ b/testcases/kernel/sysfs/class/net/sys_net04.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Cyril Hrubis <[email protected]>
+ */
+
+/*\
+ * Verifies that the link state attributes exported under
+ * /sys/class/net/<iface>/ correctly mirror the attach/detach state of a TUN
+ * device's file descriptor, and that TUN/TAP-specific sysfs attributes hold
+ * sane values.
+ *
+ * Unlike veth, a TUN device has no peer network device: instead, its carrier
+ * is controlled by whether a userspace process currently has the device's
+ * /dev/net/tun file descriptor attached to it (tun_attach()/tun_detach() in
+ * the kernel call netif_carrier_on()/netif_carrier_off() respectively). The
+ * device is created persistent (TUNSETPERSIST) so that it survives the file
+ * descriptor being closed, which is what allows the attach/detach cycle to
+ * be exercised here without the interface disappearing. The test checks
+ * that closing and reopening the attachment is reflected in:
+ *
+ * - carrier - 1 while a file descriptor is attached, 0 while detached
+ * - operstate - "up" while attached, "down" while detached. Unlike veth,
+ *   this is plain "down" rather than "lowerlayerdown": the latter is only
+ *   reported when the carrier-down state is attributable to a registered
+ *   upper/lower netdevice relationship (a veth peer, a bond slave, a VLAN's
+ *   parent, ...), whereas a TUN device's carrier is driven by a userspace
+ *   file descriptor attachment, which is not a netdevice dependency at all.
+ * - carrier_up_count / carrier_down_count - incremented by exactly one on
+ *   each corresponding transition
+ * - carrier_changes - always equal to carrier_up_count + carrier_down_count
+ *
+ * These four attributes are generic net_device core statistics updated by
+ * netif_carrier_on()/off() regardless of the underlying driver, so the
+ * expected behavior mirrors the veth peer test in sys_net02.c, aside from
+ * the "down" vs "lowerlayerdown" difference above.
+ *
+ * The very first attach happens as part of the same TUNSETIFF call that
+ * registers the netdevice, before which netif_carrier_on() skips updating
+ * carrier_up_count/operstate for it. setup() therefore performs a throwaway
+ * detach/reattach cycle so that run() starts from a state where carrier
+ * tracking already behaves normally.
+ *
+ * The test also checks attributes that are not tied to the attach state:
+ *
+ * - mtu - directly writable via sysfs; a few valid values are written and
+ *   read back, and one syntactically invalid value is rejected (with the mtu
+ *   left unchanged)
+ * - tun_flags - reflects (at least) the IFF_TUN, IFF_NO_PI and IFF_PERSIST
+ *   bits the device was created/configured with, and never has IFF_TAP set
+ * - owner / group - default to -1 (no restriction) since neither
+ *   TUNSETOWNER nor TUNSETGROUP was used
+ * - a second, TAP-mode device is created solely to cross-check that type and
+ *   addr_len correctly differ between the two: a TUN device has no
+ *   link-layer header (type == ARPHRD_NONE, addr_len == 0), while a TAP
+ *   device behaves like Ethernet (type == ARPHRD_ETHER, addr_len == 6)
+ *
+ * This needs root to create the TUN/TAP devices.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#include "tst_test.h"
+#include "tst_netdevice.h"
+#include "tst_safe_file_ops.h"
+#include "tst_sysfs_assert.h"
+
+#define TUN_PATH "/dev/net/tun"
+
+#define IFNAME_TUN "ltp_tun0"
+#define IFNAME_TAP "ltp_tap0"
+#define IFACE_PATH "/sys/class/net/" IFNAME_TUN
+
+/* include/uapi/linux/if_arp.h, avoided pulling in the whole header. */
+#define ARPHRD_ETHER 1
+#define ARPHRD_NONE 0xFFFE
+
+static int tun_fd = -1;
+static int tap_fd = -1;
+static int tun_created;
+static int tap_created;
+
+static long read_mtu(void)
+{
+	return TST_SYSFS_READ_LI(IFACE_PATH "/mtu");
+}
+
+#include "sys_net_common.h"
+
+static int open_tun(const char *ifname, short flags)
+{
+	struct ifreq ifr = { .ifr_flags = flags };
+	int fd = SAFE_OPEN(TUN_PATH, O_RDWR);
+
+	strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
+	SAFE_IOCTL(fd, TUNSETIFF, (void *)&ifr);
+
+	return fd;
+}
+
+static void setup(void)
+{
+	tun_fd = open_tun(IFNAME_TUN, IFF_TUN | IFF_NO_PI);
+	SAFE_IOCTL(tun_fd, TUNSETPERSIST, 1);
+	tun_created = 1;
+
+	/*
+	 * The initial attach above happens as part of the same TUNSETIFF call
+	 * that registers the netdevice, before which netif_carrier_on() skips
+	 * updating carrier_up_count/operstate for it. Detach and reattach
+	 * once here so run() starts from a state where carrier tracking
+	 * already behaves normally, see the top comment.
+	 */
+	SAFE_CLOSE(tun_fd);
+	NETDEV_SET_STATE(IFNAME_TUN, 1);
+	tun_fd = open_tun(IFNAME_TUN, IFF_TUN | IFF_NO_PI);
+
+	tap_fd = open_tun(IFNAME_TAP, IFF_TAP | IFF_NO_PI);
+	SAFE_IOCTL(tap_fd, TUNSETPERSIST, 1);
+	tap_created = 1;
+}
+
+static void check_tun_flags(void)
+{
+	unsigned long flags;
+
+	flags = TST_SYSFS_READ_LX(IFACE_PATH "/tun_flags");
+
+	tst_res(TINFO, "%s/tun_flags = 0x%lx", IFACE_PATH, flags);
+
+	TST_EXP_EQ_LI(flags & IFF_TUN, IFF_TUN);
+	TST_EXP_EQ_LI(flags & IFF_TAP, 0);
+	TST_EXP_EQ_LI(flags & IFF_NO_PI, IFF_NO_PI);
+	TST_EXP_EQ_LI(flags & IFF_PERSIST, IFF_PERSIST);
+}
+
+static void check_owner_group(void)
+{
+	TST_SYSFS_EXP_EQ_LI(-1, IFACE_PATH "/owner");
+	TST_SYSFS_EXP_EQ_LI(-1, IFACE_PATH "/group");
+}
+
+static void check_tun_vs_tap_type(void)
+{
+	TST_SYSFS_EXP_EQ_LI(ARPHRD_NONE, "/sys/class/net/" IFNAME_TUN "/type");
+	TST_SYSFS_EXP_EQ_LI(0, "/sys/class/net/" IFNAME_TUN "/addr_len");
+
+	TST_SYSFS_EXP_EQ_LI(ARPHRD_ETHER, "/sys/class/net/" IFNAME_TAP "/type");
+	TST_SYSFS_EXP_EQ_LI(6, "/sys/class/net/" IFNAME_TAP "/addr_len");
+}
+
+static int check_operstate(struct netdev_state *st, const char *expected_state)
+{
+	read_state(st);
+	return strcmp(st->operstate, expected_state);
+}
+
+static void run(void)
+{
+	struct netdev_state s0, s1, s2;
+
+	read_state(&s0);
+	check_state(&s0, 1, "up", "tun attached and up");
+
+	SAFE_CLOSE(tun_fd);
+	TST_RETRY_FUNC(check_operstate(&s1, "down"), TST_RETVAL_EQ0);
+	check_state(&s1, 0, "down", "tun detached");
+	check_state_delta(&s0, &s1, 0, 1, "detach transition");
+
+	tun_fd = open_tun(IFNAME_TUN, IFF_TUN | IFF_NO_PI);
+	TST_RETRY_FUNC(check_operstate(&s2, "up"), TST_RETVAL_EQ0);
+	check_state(&s2, 1, "up", "tun reattached");
+	check_state_delta(&s1, &s2, 1, 0, "reattach transition");
+
+	check_mtu_valid(68);
+	check_mtu_valid(1500);
+	check_mtu_valid(9000);
+	check_mtu_valid(65535);
+	check_mtu_invalid("-1");
+	check_mtu_invalid("0");
+	check_mtu_invalid("67");
+	check_mtu_invalid("70000");
+
+	check_tun_flags();
+	check_owner_group();
+	check_tun_vs_tap_type();
+}
+
+static void cleanup(void)
+{
+	if (tun_fd >= 0)
+		SAFE_CLOSE(tun_fd);
+
+	if (tap_fd >= 0)
+		SAFE_CLOSE(tap_fd);
+
+	if (tun_created)
+		NETDEV_REMOVE_DEVICE(IFNAME_TUN);
+
+	if (tap_created)
+		NETDEV_REMOVE_DEVICE(IFNAME_TAP);
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.needs_root = 1,
+	.needs_kconfigs = (const char *const[]){
+		"CONFIG_TUN",
+		NULL
+	},
+};
-- 
2.54.0


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.