[LTP] [PATCH v2 21/31] testcases: sysfs: Add sys_net02
Cyril Hrubis <[email protected]>
| Newsgroups | it.linux.lists.ltp |
|---|---|
| Message-ID | <[email protected]> |
A test for /sys/class/net/*/* files. This test, unlike sys_net01, creates a virtual ethernet interface so that it can change various attributes and verify that they are mirrored into the sysfs correctly. Signed-off-by: Cyril Hrubis <[email protected]> --- runtest/sysfs | 1 + testcases/kernel/sysfs/class/net/.gitignore | 1 + testcases/kernel/sysfs/class/net/sys_net02.c | 179 ++++++++++++++++++ .../kernel/sysfs/class/net/sys_net_common.h | 80 ++++++++ 4 files changed, 261 insertions(+) create mode 100644 testcases/kernel/sysfs/class/net/sys_net02.c create mode 100644 testcases/kernel/sysfs/class/net/sys_net_common.h diff --git a/runtest/sysfs b/runtest/sysfs index 90e4d2b8e..fee8a0b2b 100644 --- a/runtest/sysfs +++ b/runtest/sysfs @@ -16,3 +16,4 @@ sys_wakeup01 sys_wakeup01 sys_rtc01 sys_rtc01 sys_thermal01 sys_thermal01 sys_net01 sys_net01 +sys_net02 sys_net02 diff --git a/testcases/kernel/sysfs/class/net/.gitignore b/testcases/kernel/sysfs/class/net/.gitignore index f5ef57b75..b70d02158 100644 --- a/testcases/kernel/sysfs/class/net/.gitignore +++ b/testcases/kernel/sysfs/class/net/.gitignore @@ -1 +1,2 @@ /sys_net01 +/sys_net02 diff --git a/testcases/kernel/sysfs/class/net/sys_net02.c b/testcases/kernel/sysfs/class/net/sys_net02.c new file mode 100644 index 000000000..f8f564c1f --- /dev/null +++ b/testcases/kernel/sysfs/class/net/sys_net02.c @@ -0,0 +1,179 @@ +// 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 administrative state of a + * veth peer. + * + * A veth device's carrier is expected to be up only while its peer is up: + * veth_open()/veth_close() call netif_carrier_on()/netif_carrier_off() based + * on whether the peer is running. The test creates a veth pair and checks + * that bringing the peer down and back up is reflected in: + * + * - carrier - 1 while the peer is up, 0 while it is down + * - operstate - "up" while the peer is up, "lowerlayerdown" while it is down + * - 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 + * + * Note this only checks the values synchronously after each netlink state + * change (which is itself synchronous), it does not use poll(2)/select(2): + * unlike some other sysfs attributes, /sys/class/net/<iface>/carrier does not + * support poll() based notification on Linux, link state changes are + * reported to userspace via rtnetlink instead. + * + * The test also checks attributes that are not tied to the peer's 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) + * - address - read-only in sysfs, changed via rtnetlink instead (the + * interface is brought down first since most drivers require this); while + * doing so, the IFF_UP bit in "flags" is also cross-checked against the + * interface's own administrative state + * - netdev_group and ifalias - directly writable via sysfs, a value is + * written and read back + * + * This needs root to create the veth pair. + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <net/if.h> + +#include "tst_test.h" +#include "tst_netdevice.h" +#include "tst_sysfs_assert.h" + +#define IFNAME1 "ltp_vethp1" +#define IFNAME2 "ltp_vethp2" +#define IFACE_PATH "/sys/class/net/" IFNAME1 + +static int veth_created; + +static long read_mtu(void) +{ + return TST_SYSFS_READ_LI(IFACE_PATH "/mtu"); +} + +#include "sys_net_common.h" + +static void setup(void) +{ + CREATE_VETH_PAIR(IFNAME1, IFNAME2); + veth_created = 1; + + NETDEV_SET_STATE(IFNAME1, 1); + NETDEV_SET_STATE(IFNAME2, 1); +} + +static unsigned long read_flags(void) +{ + return TST_SYSFS_READ_LX(IFACE_PATH "/flags"); +} + +static void check_address(void) +{ + static const unsigned char new_addr[6] = {0x02, 0, 0, 0, 0, 0x01}; + char actual[32]; + long iff_up; + + tst_res(TINFO, "IFNAME1 is up before the address change"); + iff_up = read_flags() & IFF_UP; + TST_EXP_EQ_LI(iff_up, IFF_UP); + + NETDEV_SET_STATE(IFNAME1, 0); + tst_res(TINFO, "IFNAME1 is down for the address change"); + iff_up = read_flags() & IFF_UP; + TST_EXP_EQ_LI(iff_up, 0); + + NETDEV_SET_HWADDR(IFNAME1, new_addr, sizeof(new_addr)); + NETDEV_SET_STATE(IFNAME1, 1); + tst_res(TINFO, "IFNAME1 is up again after the address change"); + iff_up = read_flags() & IFF_UP; + TST_EXP_EQ_LI(iff_up, IFF_UP); + + TST_SYSFS_READ_STR(actual, sizeof(actual), IFACE_PATH "/address"); + + TST_EXP_EQ_STR(actual, "02:00:00:00:00:01"); +} + +static void check_netdev_group(void) +{ + long group; + + if (FILE_PRINTF(IFACE_PATH "/netdev_group", "%d", 7)) { + tst_res(TFAIL, "Failed to set netdev_group"); + return; + } + + group = TST_SYSFS_READ_LI(IFACE_PATH "/netdev_group"); + + TST_EXP_EQ_LI(group, 7); +} + +static void check_ifalias(void) +{ + char alias[64]; + + if (FILE_PRINTF(IFACE_PATH "/ifalias", "%s", "ltp-test-alias")) { + tst_res(TFAIL, "Failed to set ifalias"); + return; + } + + TST_SYSFS_READ_STR(alias, sizeof(alias), IFACE_PATH "/ifalias"); + TST_EXP_EQ_STR(alias, "ltp-test-alias"); +} + +static void run(void) +{ + struct netdev_state s0, s1, s2; + + read_state(&s0); + check_state(&s0, 1, "up", "both ends up"); + + NETDEV_SET_STATE(IFNAME2, 0); + read_state(&s1); + check_state(&s1, 0, "lowerlayerdown", "peer down"); + check_state_delta(&s0, &s1, 0, 1, "peer down transition"); + + NETDEV_SET_STATE(IFNAME2, 1); + read_state(&s2); + check_state(&s2, 1, "up", "peer back up"); + check_state_delta(&s1, &s2, 1, 0, "peer up 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_address(); + + check_netdev_group(); + check_ifalias(); +} + +static void cleanup(void) +{ + if (veth_created) + NETDEV_REMOVE_DEVICE(IFNAME1); +} + +static struct tst_test test = { + .test_all = run, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .needs_kconfigs = (const char *const[]){ + "CONFIG_VETH", + NULL + }, +}; diff --git a/testcases/kernel/sysfs/class/net/sys_net_common.h b/testcases/kernel/sysfs/class/net/sys_net_common.h new file mode 100644 index 000000000..7b0346c9c --- /dev/null +++ b/testcases/kernel/sysfs/class/net/sys_net_common.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 Cyril Hrubis <[email protected]> + */ + +#ifndef SYS_NET_COMMON +#define SYS_NET_COMMON + +struct netdev_state { + long carrier; + long carrier_changes; + long carrier_up_count; + long carrier_down_count; + char operstate[32]; +}; + +static void read_state(struct netdev_state *st) +{ + st->carrier = TST_SYSFS_READ_LI(IFACE_PATH "/carrier"); + st->carrier_changes = TST_SYSFS_READ_LI(IFACE_PATH "/carrier_changes"); + st->carrier_up_count = TST_SYSFS_READ_LI(IFACE_PATH "/carrier_up_count"); + st->carrier_down_count = TST_SYSFS_READ_LI(IFACE_PATH "/carrier_down_count"); + TST_SYSFS_READ_STR(st->operstate, sizeof(st->operstate), IFACE_PATH "/operstate"); +} + +static void check_state(const struct netdev_state *st, long carrier, + const char *operstate, const char *desc) +{ + tst_res(TINFO, "%s", desc); + + TST_EXP_EQ_LI(st->carrier, carrier); + TST_EXP_EQ_STR(st->operstate, operstate); + TST_EXP_EQ_LI(st->carrier_changes, + st->carrier_up_count + st->carrier_down_count); +} + +static void check_state_delta(const struct netdev_state *prev, + const struct netdev_state *cur, long up_delta, + long down_delta, const char *desc) +{ + long got_up = cur->carrier_up_count - prev->carrier_up_count; + long got_down = cur->carrier_down_count - prev->carrier_down_count; + long got_changes = cur->carrier_changes - prev->carrier_changes; + + tst_res(TINFO, "%s", desc); + + TST_EXP_EQ_LI(got_up, up_delta); + TST_EXP_EQ_LI(got_down, down_delta); + TST_EXP_EQ_LI(got_changes, up_delta + down_delta); +} + +static void check_mtu_valid(long mtu) +{ + char mtu_str[16]; + + snprintf(mtu_str, sizeof(mtu_str), "%ld", mtu); + + if (FILE_PRINTF(IFACE_PATH "/mtu", "%s", mtu_str)) { + tst_res(TFAIL, "Failed to set mtu to %ld", mtu); + return; + } + + TST_EXP_EQ_LI(read_mtu(), mtu); +} + +static void check_mtu_invalid(const char *mtu_str) +{ + long before = read_mtu(); + + if (!FILE_PRINTF(IFACE_PATH "/mtu", "%s", mtu_str)) { + tst_res(TFAIL, "Writing mtu '%s' unexpectedly succeeded", + mtu_str); + return; + } + + tst_res(TINFO, "Writing mtu '%s' was rejected as expected", mtu_str); + TST_EXP_EQ_LI(read_mtu(), before); +} + +#endif /* SYS_NET_COMMON */ -- 2.54.0 -- Mailing list info: https://lists.linux.it/listinfo/ltp