| // SPDX-License-Identifier: GPL-2.0-or-later | |
| /* | |
| * Copyright (C) 2019 Cyril Hrubis <[email protected]> | |
| * Copyright (c) Linux Test Project, 2019-2023 | |
| */ | |
| /*\ | |
| * Very simple uevent netlink socket test. | |
| * | |
| * We fork a child that listens for a kernel events while parents creates and | |
| * removes a tun network device which should produce two several add and remove | |
| * events. | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <sys/wait.h> | |
| #include <sys/socket.h> | |
| #include <linux/if.h> | |
| #include <linux/if_tun.h> | |
| #include "tst_kconfig.h" | |
| #include "tst_test.h" | |
| #include "uevent.h" | |
| #define TUN_PATH "/dev/net/tun" | |
| #define MAX_ADD_UEVENTS 4 | |
| #define MAX_REM_UEVENTS 4 | |
| static struct uevent_desc add = { | |
| .msg = "add@/devices/virtual/net/ltp-tun0", | |
| .value_cnt = 4, | |
| .values = (const char*[]) { | |
| "ACTION=add", | |
| "DEVPATH=/devices/virtual/net/ltp-tun0", | |
| "SUBSYSTEM=net", | |
| "INTERFACE=ltp-tun0", | |
| } | |
| }; | |
| static struct uevent_desc add_rx = { | |
| .msg = "add@/devices/virtual/net/ltp-tun0/queues/rx-0", | |
| .value_cnt = 3, | |
| .values = (const char*[]) { | |
| "ACTION=add", | |
| "DEVPATH=/devices/virtual/net/ltp-tun0/queues/rx-0", | |
| "SUBSYSTEM=queues", | |
| } | |
| }; | |
| static struct uevent_desc add_tx = { | |
| .msg = "add@/devices/virtual/net/ltp-tun0/queues/tx-0", | |
| .value_cnt = 3, | |
| .values = (const char*[]) { | |
| "ACTION=add", | |
| "DEVPATH=/devices/virtual/net/ltp-tun0/queues/tx-0", | |
| "SUBSYSTEM=queues", | |
| } | |
| }; | |
| static struct uevent_desc rem_rx = { | |
| .msg = "remove@/devices/virtual/net/ltp-tun0/queues/rx-0", | |
| .value_cnt = 3, | |
| .values = (const char*[]) { | |
| "ACTION=remove", | |
| "DEVPATH=/devices/virtual/net/ltp-tun0/queues/rx-0", | |
| "SUBSYSTEM=queues", | |
| } | |
| }; | |
| static struct uevent_desc rem_tx = { | |
| .msg = "remove@/devices/virtual/net/ltp-tun0/queues/tx-0", | |
| .value_cnt = 3, | |
| .values = (const char*[]) { | |
| "ACTION=remove", | |
| "DEVPATH=/devices/virtual/net/ltp-tun0/queues/tx-0", | |
| "SUBSYSTEM=queues", | |
| } | |
| }; | |
| static struct uevent_desc rem = { | |
| .msg = "remove@/devices/virtual/net/ltp-tun0", | |
| .value_cnt = 4, | |
| .values = (const char*[]) { | |
| "ACTION=remove", | |
| "DEVPATH=/devices/virtual/net/ltp-tun0", | |
| "SUBSYSTEM=net", | |
| "INTERFACE=ltp-tun0", | |
| } | |
| }; | |
| static const struct uevent_desc *add_uevents[MAX_ADD_UEVENTS]; | |
| static const struct uevent_desc *rem_uevents[MAX_REM_UEVENTS]; | |
| static void generate_tun_uevents(void) | |
| { | |
| int fd = SAFE_OPEN(TUN_PATH, O_RDWR); | |
| struct ifreq ifr = { | |
| .ifr_flags = IFF_TUN, | |
| .ifr_name = "ltp-tun0", | |
| }; | |
| SAFE_IOCTL(fd, TUNSETIFF, (void *)&ifr); | |
| SAFE_IOCTL(fd, TUNSETPERSIST, 0); | |
| SAFE_CLOSE(fd); | |
| } | |
| static void verify_uevent(void) | |
| { | |
| int pid, fd; | |
| pid = SAFE_FORK(); | |
| if (!pid) { | |
| fd = open_uevent_netlink(); | |
| TST_CHECKPOINT_WAKE(0); | |
| wait_for_uevents_unordered(fd, add_uevents); | |
| wait_for_uevents_unordered(fd, rem_uevents); | |
| close(fd); | |
| exit(0); | |
| } | |
| TST_CHECKPOINT_WAIT(0); | |
| generate_tun_uevents(); | |
| wait_for_pid(pid); | |
| } | |
| static void setup(void) | |
| { | |
| struct tst_kconfig_var kconfig = TST_KCONFIG_INIT("CONFIG_RPS"); | |
| int i = 0, j = 0; | |
| tst_kconfig_read(&kconfig, 1); | |
| add_uevents[i++] = &add; | |
| if (kconfig.choice == 'y') | |
| add_uevents[i++] = &add_rx; | |
| add_uevents[i++] = &add_tx; | |
| add_uevents[i++] = NULL; | |
| if (kconfig.choice == 'y') | |
| rem_uevents[j++] = &rem_rx; | |
| rem_uevents[j++] = &rem_tx; | |
| rem_uevents[j++] = &rem; | |
| rem_uevents[j++] = NULL; | |
| } | |
| static struct tst_test test = { | |
| .setup = setup, | |
| .test_all = verify_uevent, | |
| .forks_child = 1, | |
| .needs_checkpoints = 1, | |
| .needs_kconfigs = (const char *const []) { | |
| "CONFIG_TUN", | |
| NULL | |
| }, | |
| .needs_root = 1 | |
| }; |