/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Copyright (C) 2016 Red Hat, Inc. */ #include "src/core/nm-default-daemon.h" #include #include #include #include #include #include "libnm-glib-aux/nm-io-utils.h" #include "libnm-base/nm-ethtool-base.h" #include "libnm-platform/nmp-object.h" #include "libnm-platform/nmp-netns.h" #include "libnm-platform/nm-platform-utils.h" #include "test-common.h" #include "nm-test-utils-core.h" #define LO_INDEX 1 #define LO_NAME "lo" #define LO_TYPEDESC "loopback" #define DUMMY_TYPEDESC "dummy" #define BOGUS_NAME "nm-bogus-device" #define BOGUS_IFINDEX INT_MAX #define SLAVE_NAME "nm-test-slave" #define PARENT_NAME "nm-test-parent" #define VLAN_ID 4077 #define VLAN_FLAGS 0 #define MTU 1357 #define _ADD_DUMMY(platform, name) \ g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add((platform), (name), NULL))) #define _sysctl_assert_eq(plat, path, value) \ G_STMT_START \ { \ gs_free char *_val = NULL; \ \ _val = nm_platform_sysctl_get(plat, NMP_SYSCTL_PATHID_ABSOLUTE(path)); \ g_assert_cmpstr(_val, ==, value); \ } \ G_STMT_END static void test_bogus(void) { size_t addrlen; g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, BOGUS_NAME)); g_assert(!nm_platform_link_delete(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_get_ifindex(NM_PLATFORM_GET, BOGUS_NAME)); g_assert(!nm_platform_link_get_name(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_get_type(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_get_type_name(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_UP, TRUE) >= 0)); g_assert(!(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_UP, FALSE) >= 0)); g_assert( !(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_NOARP, TRUE) >= 0)); g_assert( !(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_NOARP, FALSE) >= 0)); g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_get_address(NM_PLATFORM_GET, BOGUS_IFINDEX, &addrlen)); g_assert(!addrlen); g_assert(!nm_platform_link_get_address(NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); g_assert(!NMTST_NM_ERR_SUCCESS(nm_platform_link_set_mtu(NM_PLATFORM_GET, BOGUS_IFINDEX, MTU))); g_assert(!nm_platform_link_get_mtu(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_supports_carrier_detect(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_supports_vlans(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_get_lnk_vlan(NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); g_assert(!nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); g_assert(!nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); } static void test_loopback(void) { g_assert(nm_platform_link_get_by_ifname(NM_PLATFORM_GET, LO_NAME)); g_assert_cmpint(nm_platform_link_get_type(NM_PLATFORM_GET, LO_INDEX), ==, NM_LINK_TYPE_LOOPBACK); g_assert_cmpint(nm_platform_link_get_ifindex(NM_PLATFORM_GET, LO_NAME), ==, LO_INDEX); g_assert_cmpstr(nm_platform_link_get_name(NM_PLATFORM_GET, LO_INDEX), ==, LO_NAME); g_assert_cmpstr(nm_platform_link_get_type_name(NM_PLATFORM_GET, LO_INDEX), ==, LO_TYPEDESC); g_assert(nm_platform_link_supports_carrier_detect(NM_PLATFORM_GET, LO_INDEX)); g_assert(!nm_platform_link_supports_vlans(NM_PLATFORM_GET, LO_INDEX)); } static gboolean software_add(NMLinkType link_type, const char *name) { switch (link_type) { case NM_LINK_TYPE_DUMMY: return NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, name, NULL)); case NM_LINK_TYPE_BRIDGE: return NMTST_NM_ERR_SUCCESS(nm_platform_link_bridge_add(NM_PLATFORM_GET, name, NULL, 0, 0, &nm_platform_lnk_bridge_default, NULL)); case NM_LINK_TYPE_BOND: { gboolean bond0_exists = !!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0"); int r; const NMPlatformLnkBond nm_platform_lnk_bond_default = { .mode = nmtst_rand_select(3, 1), }; r = nm_platform_link_bond_add(NM_PLATFORM_GET, name, &nm_platform_lnk_bond_default, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "bond0")); return NMTST_NM_ERR_SUCCESS(r); } case NM_LINK_TYPE_TEAM: return NMTST_NM_ERR_SUCCESS(nm_platform_link_team_add(NM_PLATFORM_GET, name, NULL)); case NM_LINK_TYPE_VLAN: { SignalData *parent_added; SignalData *parent_changed; /* Don't call link_callback for the bridge interface */ parent_added = add_signal_ifname(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME); if (NMTST_NM_ERR_SUCCESS(nm_platform_link_bridge_add(NM_PLATFORM_GET, PARENT_NAME, NULL, 0, 0, &nm_platform_lnk_bridge_default, NULL))) accept_signal(parent_added); free_signal(parent_added); { int parent_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, PARENT_NAME); gboolean was_up = nm_platform_link_is_up(NM_PLATFORM_GET, parent_ifindex); parent_changed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, parent_ifindex); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, parent_ifindex, IFF_UP, TRUE) >= 0); if (was_up) { /* when NM is running in the background, it will mess with addrgenmode which might cause additional signals. */ accept_signals(parent_changed, 0, 1); } else accept_signals(parent_changed, 1, 2); free_signal(parent_changed); return NMTST_NM_ERR_SUCCESS(nm_platform_link_vlan_add(NM_PLATFORM_GET, name, parent_ifindex, &((NMPlatformLnkVlan){ .id = VLAN_ID, .protocol = ETH_P_8021Q, }), NULL)); } } default: g_error("Link type %d unhandled.", link_type); } g_assert_not_reached(); } static void test_link_changed_signal_cb(NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformIP4Route *route, int change_type_i, gboolean *p_test_link_changed_signal_arg) { const NMPObjectType obj_type = obj_type_i; const NMPlatformSignalChangeType change_type = change_type_i; /* test invocation of platform signals with multiple listeners * connected to the signal. Platform signals have enum-typed * arguments and there seem to be an issue with invoking such * signals on s390x and ppc64 archs. * https://bugzilla.redhat.com/show_bug.cgi?id=1260577 * * As the test shows, the failure is not reproducible for * platform signals. */ g_assert(NM_IS_PLATFORM(platform)); g_assert(platform == NM_PLATFORM_GET); g_assert(ifindex > 0); g_assert(route); g_assert_cmpint(obj_type, ==, NMP_OBJECT_TYPE_LINK); g_assert_cmpint((gint64) change_type, !=, (gint64) 0); g_assert_cmpint(change_type, !=, NM_PLATFORM_SIGNAL_NONE); *p_test_link_changed_signal_arg = TRUE; } static void test_slave(int master, int type, SignalData *master_changed) { int ifindex; SignalData *link_added = add_signal_ifname(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, SLAVE_NAME); SignalData *link_changed, *link_removed; char *value; NMLinkType link_type = nm_platform_link_get_type(NM_PLATFORM_GET, master); gboolean test_link_changed_signal_arg1; gboolean test_link_changed_signal_arg2; g_assert(NM_IN_SET(link_type, NM_LINK_TYPE_TEAM, NM_LINK_TYPE_BOND, NM_LINK_TYPE_BRIDGE)); g_assert(software_add(type, SLAVE_NAME)); ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, SLAVE_NAME); g_assert(ifindex > 0); link_changed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); accept_signal(link_added); /* Set the slave up to see whether master's IFF_LOWER_UP is set correctly. * * See https://bugzilla.redhat.com/show_bug.cgi?id=910348 */ g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0); g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); ensure_no_signal(link_changed); /* Enslave */ link_changed->ifindex = ifindex; g_assert(nm_platform_link_enslave(NM_PLATFORM_GET, master, ifindex)); g_assert_cmpint(nm_platform_link_get_master(NM_PLATFORM_GET, ifindex), ==, master); accept_signals(link_changed, 1, 3); accept_signals(master_changed, 0, 2); /* enslaveing brings put the slave */ if (NM_IN_SET(link_type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); else g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); if (NM_IN_SET(link_type, NM_LINK_TYPE_BOND)) { NMPlatformLinkBondPort bond_port; gboolean prio_has; gboolean prio_supported; const NMPlatformLink *link; const NMPlatformLnkBond *lnk; link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, SLAVE_NAME, NM_LINK_TYPE_DUMMY); g_assert(link); lnk = nm_platform_link_get_lnk_bond(NM_PLATFORM_GET, master, NULL); g_assert(lnk); g_assert(NM_IN_SET(lnk->mode, 3, 1)); prio_supported = (lnk->mode == 1); prio_has = nmtst_get_rand_bool() && prio_supported; bond_port = (NMPlatformLinkBondPort){ .queue_id = 5, .prio_has = prio_has, .prio = prio_has ? 6 : 0, }; g_assert(nm_platform_link_change(NM_PLATFORM_GET, ifindex, NULL, &bond_port, 0)); accept_signals(link_changed, 1, 3); link = nmtstp_link_get(NM_PLATFORM_GET, ifindex, SLAVE_NAME); g_assert(link); g_assert_cmpint(link->port_data.bond.queue_id, ==, 5); g_assert(link->port_data.bond.prio_has || link->port_data.bond.prio == 0); } test_link_changed_signal_arg1 = FALSE; test_link_changed_signal_arg2 = FALSE; g_signal_connect(NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK(test_link_changed_signal_cb), &test_link_changed_signal_arg1); g_signal_connect(NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK(test_link_changed_signal_cb), &test_link_changed_signal_arg2); /* Set master up */ g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, master, IFF_UP, TRUE) >= 0); g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, master)); accept_signals(master_changed, 1, 3); g_signal_handlers_disconnect_by_func(NM_PLATFORM_GET, G_CALLBACK(test_link_changed_signal_cb), &test_link_changed_signal_arg1); g_signal_handlers_disconnect_by_func(NM_PLATFORM_GET, G_CALLBACK(test_link_changed_signal_cb), &test_link_changed_signal_arg2); g_assert(test_link_changed_signal_arg1); g_assert(test_link_changed_signal_arg2); /* Master with a disconnected slave is disconnected * * For some reason, bonding and teaming slaves are automatically set up. We * need to set them back down for this test. */ switch (nm_platform_link_get_type(NM_PLATFORM_GET, master)) { case NM_LINK_TYPE_BOND: case NM_LINK_TYPE_TEAM: g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0); accept_signal(link_changed); accept_signals(master_changed, 0, 3); break; default: break; } g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); if (nmtstp_is_root_test() && nm_platform_link_is_connected(NM_PLATFORM_GET, master)) { if (nm_platform_link_get_type(NM_PLATFORM_GET, master) == NM_LINK_TYPE_TEAM) { /* Older team versions (e.g. Fedora 17) have a bug that team master stays * IFF_LOWER_UP even if its slave is down. Double check it with iproute2 and if * `ip link` also claims master to be up, accept it. */ char *stdout_str = NULL; nmtst_spawn_sync(NULL, &stdout_str, NULL, 0, "/sbin/ip", "link", "show", "dev", nm_platform_link_get_name(NM_PLATFORM_GET, master)); g_assert(strstr(stdout_str, "LOWER_UP")); g_free(stdout_str); } else g_assert_not_reached(); } /* Set slave up and see if master gets up too */ g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0); g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, master)); accept_signals(link_changed, 1, 3); /* NM running, can cause additional change of addrgenmode */ accept_signals(master_changed, 0, 3); /* Enslave again * * Gracefully succeed if already enslaved. */ ensure_no_signal(link_changed); g_assert(nm_platform_link_enslave(NM_PLATFORM_GET, master, ifindex)); accept_signals(link_changed, 0, 2); accept_signals(master_changed, 0, 2); /* Set slave option */ switch (type) { case NM_LINK_TYPE_BRIDGE: if (nmtstp_is_sysfs_writable()) { g_assert( nm_platform_sysctl_slave_set_option(NM_PLATFORM_GET, ifindex, "priority", "614")); value = nm_platform_sysctl_slave_get_option(NM_PLATFORM_GET, ifindex, "priority"); g_assert_cmpstr(value, ==, "614"); g_free(value); } break; default: break; } /* Release */ ensure_no_signal(link_added); ensure_no_signal(link_changed); ensure_no_signal(link_removed); g_assert(nm_platform_link_release(NM_PLATFORM_GET, master, ifindex)); g_assert_cmpint(nm_platform_link_get_master(NM_PLATFORM_GET, ifindex), ==, 0); if (link_changed->received_count > 0) { accept_signals(link_added, 0, 1); accept_signals(link_changed, 1, 5); accept_signals(link_removed, 0, 1); } else { /* Due to https://bugzilla.redhat.com/show_bug.cgi?id=1285719 , kernel might send a * wrong RTM_DELLINK message so that we instead see an removed+added signal. */ accept_signal(link_added); ensure_no_signal(link_changed); accept_signal(link_removed); } accept_signals(master_changed, 0, 3); ensure_no_signal(master_changed); /* Release again */ ensure_no_signal(link_changed); g_assert(!nm_platform_link_release(NM_PLATFORM_GET, master, ifindex)); ensure_no_signal(master_changed); /* Remove */ ensure_no_signal(link_added); ensure_no_signal(link_changed); ensure_no_signal(link_removed); nmtstp_link_delete(NULL, -1, ifindex, NULL, TRUE); accept_signals(master_changed, 0, 1); accept_signals(link_changed, 0, 1); accept_signal(link_removed); free_signal(link_added); free_signal(link_changed); free_signal(link_removed); } static void test_software(NMLinkType link_type, const char *link_typename) { int ifindex; char *value; int vlan_parent = -1, vlan_id; SignalData *link_added, *link_changed, *link_removed; /* Add */ link_added = add_signal_ifname(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); g_assert(software_add(link_type, DEVICE_NAME)); accept_signal(link_added); g_assert(nm_platform_link_get_by_ifname(NM_PLATFORM_GET, DEVICE_NAME)); ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); g_assert(ifindex >= 0); g_assert_cmpint(nm_platform_link_get_type(NM_PLATFORM_GET, ifindex), ==, link_type); g_assert_cmpstr(nm_platform_link_get_type_name(NM_PLATFORM_GET, ifindex), ==, link_typename); link_changed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); if (link_type == NM_LINK_TYPE_VLAN) { const NMPlatformLink *plink; const NMPlatformLnkVlan *plnk; plnk = nm_platform_link_get_lnk_vlan(NM_PLATFORM_GET, ifindex, &plink); g_assert(plnk); g_assert(plink); vlan_parent = plink->parent; vlan_id = plnk->id; g_assert_cmpint(vlan_parent, ==, nm_platform_link_get_ifindex(NM_PLATFORM_GET, PARENT_NAME)); g_assert_cmpint(vlan_id, ==, VLAN_ID); } /* Add again */ g_assert(!software_add(link_type, DEVICE_NAME)); /* Set ARP/NOARP */ g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, TRUE) >= 0); g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signals(link_changed, 1, 2); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, FALSE) >= 0); g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signal(link_changed); /* Set master option */ if (nmtstp_is_root_test()) { switch (link_type) { case NM_LINK_TYPE_BRIDGE: if (nmtstp_is_sysfs_writable()) { g_assert(nm_platform_sysctl_master_set_option(NM_PLATFORM_GET, ifindex, "forward_delay", "628")); value = nm_platform_sysctl_master_get_option(NM_PLATFORM_GET, ifindex, "forward_delay"); g_assert_cmpstr(value, ==, "628"); g_free(value); } break; case NM_LINK_TYPE_BOND: if (nmtstp_is_sysfs_writable()) { g_assert(nm_platform_sysctl_master_set_option(NM_PLATFORM_GET, ifindex, "mode", "active-backup")); value = nm_platform_sysctl_master_get_option(NM_PLATFORM_GET, ifindex, "mode"); /* When reading back, the output looks slightly different. */ g_assert(g_str_has_prefix(value, "active-backup")); g_free(value); } break; default: break; } } /* Enslave and release */ switch (link_type) { case NM_LINK_TYPE_BRIDGE: case NM_LINK_TYPE_BOND: case NM_LINK_TYPE_TEAM: link_changed->ifindex = ifindex; test_slave(ifindex, NM_LINK_TYPE_DUMMY, link_changed); link_changed->ifindex = 0; break; default: break; } free_signal(link_changed); /* Delete */ nmtstp_link_delete(NULL, -1, ifindex, DEVICE_NAME, TRUE); accept_signal(link_removed); /* Delete again */ g_assert(nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME) <= 0); g_assert(!nm_platform_link_delete(NM_PLATFORM_GET, ifindex)); /* VLAN: Delete parent */ if (link_type == NM_LINK_TYPE_VLAN) { SignalData *link_removed_parent = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, vlan_parent); nmtstp_link_delete(NULL, -1, vlan_parent, NULL, TRUE); accept_signal(link_removed_parent); free_signal(link_removed_parent); } /* No pending signal */ free_signal(link_added); free_signal(link_removed); } static void test_bridge(void) { test_software(NM_LINK_TYPE_BRIDGE, "bridge"); } static int _system(const char *cmd) { /* some gcc version really want to warn on -Werror=unused-result. Add a bogus wrapper * function. */ return system(cmd); } static void test_bond(void) { if (nmtstp_is_root_test() && !g_file_test("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) && _system("modprobe --show bonding") != 0) { g_test_skip("Skipping test for bonding: bonding module not available"); return; } test_software(NM_LINK_TYPE_BOND, "bond"); } static void test_team(void) { int r; if (nmtstp_is_root_test()) { r = nm_platform_link_team_add(NM_PLATFORM_GET, "nm-team-check", NULL); if (r < 0) { g_assert_cmpint(r, ==, -EOPNOTSUPP); g_test_skip("Skipping test for teaming: team module not functioning"); return; } nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, "nm-team-check", FALSE); } test_software(NM_LINK_TYPE_TEAM, "team"); } static void test_vlan(void) { test_software(NM_LINK_TYPE_VLAN, "vlan"); } /*****************************************************************************/ static void test_bridge_addr(void) { char addr[ETH_ALEN]; NMPlatformLink link; const NMPlatformLink *plink = NULL; NMPLinkAddress hw_perm_addr; gboolean b; char sbuf[100]; gs_free char *str = NULL; NMPlatformLinkSetBridgeInfoData info_data; nm_utils_hwaddr_aton("de:ad:be:ef:00:11", addr, sizeof(addr)); g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_bridge_add(NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof(addr), 0, &nm_platform_lnk_bridge_default, &plink))); g_assert(plink); link = *plink; g_assert_cmpstr(link.name, ==, DEVICE_NAME); g_assert_cmpint(link.l_address.len, ==, sizeof(addr)); g_assert(!memcmp(link.l_address.data, addr, sizeof(addr))); plink = nm_platform_link_get(NM_PLATFORM_GET, link.ifindex); g_assert(plink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, plink, &hw_perm_addr)); if (nmtstp_is_root_test()) { g_assert_cmpint(nm_platform_link_get_inet6_addr_gen_mode(NM_PLATFORM_GET, link.ifindex), ==, NM_IN6_ADDR_GEN_MODE_EUI64); g_assert_cmpint(_nm_platform_link_get_inet6_addr_gen_mode(plink), ==, NM_IN6_ADDR_GEN_MODE_EUI64); g_assert_cmpint(_nm_platform_uint8_inv(plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); g_assert(NMTST_NM_ERR_SUCCESS( nm_platform_link_set_inet6_addr_gen_mode(NM_PLATFORM_GET, link.ifindex, NM_IN6_ADDR_GEN_MODE_NONE))); g_assert_cmpint(nm_platform_link_get_inet6_addr_gen_mode(NM_PLATFORM_GET, link.ifindex), ==, NM_IN6_ADDR_GEN_MODE_NONE); plink = nm_platform_link_get(NM_PLATFORM_GET, link.ifindex); g_assert(plink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, plink, &hw_perm_addr)); g_assert_cmpint(_nm_platform_link_get_inet6_addr_gen_mode(plink), ==, NM_IN6_ADDR_GEN_MODE_NONE); g_assert_cmpint(_nm_platform_uint8_inv(plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE); g_assert(NMTST_NM_ERR_SUCCESS( nm_platform_link_set_inet6_addr_gen_mode(NM_PLATFORM_GET, link.ifindex, NM_IN6_ADDR_GEN_MODE_EUI64))); g_assert_cmpint(nm_platform_link_get_inet6_addr_gen_mode(NM_PLATFORM_GET, link.ifindex), ==, NM_IN6_ADDR_GEN_MODE_EUI64); plink = nm_platform_link_get(NM_PLATFORM_GET, link.ifindex); g_assert(plink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, plink, &hw_perm_addr)); g_assert_cmpint(_nm_platform_link_get_inet6_addr_gen_mode(plink), ==, NM_IN6_ADDR_GEN_MODE_EUI64); g_assert_cmpint(_nm_platform_uint8_inv(plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); } g_assert_cmpint(plink->l_address.len, ==, sizeof(addr)); g_assert(!memcmp(plink->l_address.data, addr, sizeof(addr))); info_data = (const NMPlatformLinkSetBridgeInfoData){ .vlan_default_pvid_val = nmtst_rand_select(0, 5, 42, 1048), .vlan_default_pvid_has = nmtst_get_rand_bool(), .vlan_filtering_val = nmtst_get_rand_bool(), .vlan_filtering_has = nmtst_get_rand_bool(), }; b = nm_platform_link_set_bridge_info(NM_PLATFORM_GET, link.ifindex, &info_data); g_assert(b); _sysctl_assert_eq(NM_PLATFORM_GET, "/sys/class/net/" DEVICE_NAME "/bridge/default_pvid", info_data.vlan_default_pvid_has ? nm_sprintf_buf(sbuf, "%u", info_data.vlan_default_pvid_val) : "1"); _sysctl_assert_eq(NM_PLATFORM_GET, "/sys/class/net/" DEVICE_NAME "/bridge/vlan_filtering", info_data.vlan_filtering_val && info_data.vlan_filtering_has ? "1" : "0"); info_data = (const NMPlatformLinkSetBridgeInfoData){ .vlan_default_pvid_val = 55, .vlan_default_pvid_has = TRUE, .vlan_filtering_val = !info_data.vlan_filtering_val, .vlan_filtering_has = TRUE, }; b = nm_platform_link_set_bridge_info(NM_PLATFORM_GET, link.ifindex, &info_data); g_assert(b); _sysctl_assert_eq(NM_PLATFORM_GET, "/sys/class/net/" DEVICE_NAME "/bridge/default_pvid", nm_sprintf_buf(sbuf, "%u", info_data.vlan_default_pvid_val)); _sysctl_assert_eq(NM_PLATFORM_GET, "/sys/class/net/" DEVICE_NAME "/bridge/vlan_filtering", info_data.vlan_filtering_val ? "1" : "0"); nmtstp_link_delete(NULL, -1, link.ifindex, link.name, TRUE); } /*****************************************************************************/ static void test_internal(void) { SignalData *link_added = add_signal_ifname(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); SignalData *link_changed, *link_removed; const char mac[6] = {0x00, 0xff, 0x11, 0xee, 0x22, 0xdd}; const char *address; size_t addrlen; int ifindex; /* Check the functions for non-existent devices */ g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, DEVICE_NAME)); g_assert(!nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME)); /* Add device */ g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL))); accept_signal(link_added); /* Try to add again */ g_assert(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL) == -NME_PL_EXISTS); /* Check device index, name and type */ ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); g_assert(ifindex > 0); g_assert_cmpstr(nm_platform_link_get_name(NM_PLATFORM_GET, ifindex), ==, DEVICE_NAME); g_assert_cmpint(nm_platform_link_get_type(NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_DUMMY); g_assert_cmpstr(nm_platform_link_get_type_name(NM_PLATFORM_GET, ifindex), ==, DUMMY_TYPEDESC); link_changed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); /* Up/connected */ g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0); g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); accept_signals(link_changed, 1, 2); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0); g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); accept_signal(link_changed); /* arp/noarp */ g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, FALSE) >= 0); g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signal(link_changed); g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, TRUE) >= 0); g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signal(link_changed); /* Features */ g_assert(!nm_platform_link_supports_carrier_detect(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_supports_vlans(NM_PLATFORM_GET, ifindex)); /* Set MAC address */ g_assert(NMTST_NM_ERR_SUCCESS( nm_platform_link_set_address(NM_PLATFORM_GET, ifindex, mac, sizeof(mac)))); address = nm_platform_link_get_address(NM_PLATFORM_GET, ifindex, &addrlen); g_assert(addrlen == sizeof(mac)); g_assert(!memcmp(address, mac, addrlen)); address = nm_platform_link_get_address(NM_PLATFORM_GET, ifindex, NULL); g_assert(!memcmp(address, mac, addrlen)); accept_signal(link_changed); /* Set MTU */ g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_set_mtu(NM_PLATFORM_GET, ifindex, MTU))); g_assert_cmpint(nm_platform_link_get_mtu(NM_PLATFORM_GET, ifindex), ==, MTU); accept_signal(link_changed); /* Delete device */ nmtstp_link_delete(NULL, -1, ifindex, DEVICE_NAME, TRUE); accept_signal(link_removed); /* Try to delete again */ g_assert(!nm_platform_link_delete(NM_PLATFORM_GET, ifindex)); free_signal(link_added); free_signal(link_changed); free_signal(link_removed); } /*****************************************************************************/ static void test_external(void) { const NMPlatformLink *pllink; NMPLinkAddress hw_perm_addr; SignalData *link_added, *link_changed, *link_removed; int ifindex; link_added = add_signal_ifname(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); nmtstp_run_command_check("ip link add %s type %s", DEVICE_NAME, "dummy"); wait_signal(link_added); g_assert(nm_platform_link_get_by_ifname(NM_PLATFORM_GET, DEVICE_NAME)); ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); g_assert(ifindex > 0); g_assert_cmpstr(nm_platform_link_get_name(NM_PLATFORM_GET, ifindex), ==, DEVICE_NAME); g_assert_cmpint(nm_platform_link_get_type(NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_DUMMY); g_assert_cmpstr(nm_platform_link_get_type_name(NM_PLATFORM_GET, ifindex), ==, DUMMY_TYPEDESC); link_changed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex(NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex); g_assert(pllink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (!pllink->initialized) { /* we still lack the notification via UDEV. Expect another link changed signal. */ wait_signal(link_changed); } /* Up/connected/arp */ g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); nmtstp_run_command_check("ip link set %s up", DEVICE_NAME); wait_signal(link_changed); g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); nmtstp_run_command_check("ip link set %s down", DEVICE_NAME); wait_signal(link_changed); g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); nmtstp_run_command_check("ip link set %s arp on", DEVICE_NAME); wait_signal(link_changed); g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); nmtstp_run_command_check("ip link set %s arp off", DEVICE_NAME); wait_signal(link_changed); g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); nmtstp_run_command_check("ip link del %s", DEVICE_NAME); wait_signal(link_removed); accept_signals(link_changed, 0, 1); g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, DEVICE_NAME)); free_signal(link_added); free_signal(link_changed); free_signal(link_removed); } /*****************************************************************************/ static guint8 * _copy_base64(guint8 *dst, gsize dst_len, const char *base64_src) { g_assert(dst); g_assert(dst_len > 0); if (!base64_src) memset(dst, 0, dst_len); else { gs_free guint8 *b = NULL; gsize l; b = g_base64_decode(base64_src, &l); g_assert(b); g_assert(l == dst_len); memcpy(dst, b, dst_len); } return dst; } typedef struct { const char *pri; const char *pub; const char *pre; } KeyPair; static void _test_wireguard_change(NMPlatform *platform, int ifindex, int test_mode) { const KeyPair self_key = {"yOWEsaXFxX9/DOkQPzqB9RufZOpfSP4LZZCErP0N0Xo=", "s6pVT2xPwktor9O5bVOSzcPqBu9uzQOUzPQHXLU2jmk="}; const KeyPair keys[100] = { {"+BDHMh11bkheGfvlQpqt8P/H7N1sPXtVi05XraZS0E8=", "QItu7PJadBVXFXGv55CMtVnbRHdrI6E2CGlu2N5oGx4=", "2IvZnKTzbF1UlChznWSsEYtGbPjhYSTT41GXO6zLxvk="}, {"qGZyV2BO1nyY/FGYd6elBPirwJC9QyZwqbm2OJAgLkY=", "v8L1FEitO0xo+wW/CVVUnALlw0zGveApSFdlITi/5lI=", "/R2c0JmBNGJzT594NQ0mBJ2XJjxt2QUSo+ZiqeY0EQA="}, {"YDgsIb0oe+9NcxIx2r0HEEPQpRMxmRN0ALoLm9Sh40Q=", "nFPs1HaU7uFBvE9xZCMF8oOAjzLpZ49AzDHOluY1O2E=", "zsYED2Ef7zIHJoRBPcen+w4ktrRsLPEfYwZhWIuXfds="}, {"kHkosM503LWu43tdYXbNwVOpRrtPgd9XFqcN7k4t6G4=", "b8e092WT+eNmnxCr5WE2QC/MXAjDagfG1g03cs2mBC0=", "VXz0ShGWT7H0CBCg2awfatmOJF15ZtaSMPhMsp+hc3A="}, {"4C2w5CEnxH59Y2aa6CJXgLdDtWoNMS2UJounRCM1Jkk=", "gC/R9umlnEQL+Qsz/Y64AlsdMge4ECe5/u/JHZCMWSs=", "2bmL5ISr+V5a7l7xFJ695BLIJyBgx8xnxzybkxiRHOg="}, {"KHJSzFGkXcZf/wbH2rr99SYtGKWbL680wA2AcDE94lo=", "BsN23h4aOi458Q3EgMQsodsWQxd9h/RxqskAUpsXfgg=", "nK4Zv34YKEhjuexhq1SgK4oTd4MZJT5gcpYvEuPjc7Q="}, {"QGMulXJ9e3AVxtpi8+UUVqPOr/YBWvCNFVsWS9IUnUA=", "kjVclP5Ifi6og2BBCEHKS/aa/WktArB4+ig06lYaVlg=", "0+mmceDPcSRK3vFnYqHd9iAfY+Nyjzf/1KgDeYGlRkQ="}, {"AOJiDD4y6GA7P7gugjjQG9Cctvc5Y27fajHz6aU3gU4=", "gEnHn6euHtcMEwZBlX6HANPeN9Of+voBDtltS38xDUw=", "wIH1OxgX6GLxx/bnR+t3fbmjGZDTU3WMxp7t1XGezqM="}, {"COsls2BlCltaIrrq1+FU51cWddlmoPPppSeIDunOxGA=", "+n6WuV8Tb1/iZArTrHsyNqkRHABbavBQt9Me72K2KEc=", "t4baiprSO9ZbKD2/RutOY9cr+yCajQWZGCTnQdrFQj0="}, {"uHawQq2BRyJlsTPoCa+MfBVnv4MwtRoS+S9FEpFOEVg=", "8lcmr27afeb6iI3BQCaDtvalF2Cl7gxRZgs+nyJ/fEg=", "Eh9o/6W60iujBLIHuRfNrPhOWAn7PambT2JRln9iwA0="}, {"yL7hmoE/JfRGAotRzx9xOpjfrDA3BFlPEemFiQw40Wk=", "BHK0PHi5kp7rOfQ46oc9xlVpB+pZeZYXTtH7EXr5TwU=", "BS2h2ZZyW0BlYMmLR29xyHEcZ4jtO7rgj1jkz/EEaxU="}, {"ON8YrTHQgoC5e0mAR9FakZ8hZ/9I7ysuE21sG546J1Y=", "Bm3l5I6iH1tDrv6EgdZU9PzHqp0H26Z6ggmmIypwxy8=", "qKVfbCnK1EI3eC28SsL+jyBnEnIF/nRJQJzDevBFdNQ="}, {"KGLO0RI0VlQFCG+ETUqk25HdaKUnUPXKOKkTh/w8BXU=", "sBDBwQFC7k8UMdMzuavKBkBjYYKigzvYmGF5rm6muQc=", "BNfZF9d7540pUGGOQCh3iDxAZcPBqX4qqM00GSLPBek="}, {"KGdWyEodIn7KKI2e4EFK9tT6Bt3bNMFBRFVTKjjJm2E=", "lrbjU/Xn9wDCZQiU8E5fY6igTSozghvo47QeVIIWaUk=", "LczqW48MW8qDQIZYRELsz/VCzVDc5niROvk7OqrTxR0="}, {"wO3xinUGABgEmX+RJZbBbOtAmBuPPFG6oVdimvoo90w=", "dCIvTzR6EerOOsRKnWly1a9WGzbJ4qc+6t3SSzLgWFk=", "wFj0zpr5PadBoBy0couLuZ1qudZbXLbV/j3UT+AyKeo="}, {"+JNOBlO4tp9vvQk6UO4r3sILyEgjl+BBoWketZufyn0=", "Q6LSv9y7YQkJEzQ/1mpjJEgOrO8GYPUTgcizjh7Cm34=", "kg7AG9MuN04xPJ5Z0IcNZ4a8d1b/n4GsGIeyA4FaaSE="}, {"+EJcThLRwjZ+h1CNNFu15HWzznf4u/lPVw8hifTm2Ec=", "Kkn2jFqwBzyIFQfD0OpmePFSmBYmhKagv4pGgkqsWgE=", "jYpxojj8WKYe/XIXMP+uv1Fv0+TKrs83tqfzP0AGdcI="}, {"+DKFqSMNFmxriEFj3qatuzYeTJ9+xWYspZ4ydL3eC0Q=", "3o37bsg6HhRg/M9+fTlLcFYc2w/Bz9rLQySvvYCKbRE=", "Jb9qoDIBat1EexlgfpRbXa7OflptME8/zt93bldkiVE="}, {"EH3MjFOMqRoDFQz+hSlJpntWBeH3lTk6WPjTIQjr42o=", "PbPewED/nxSBLdM7AXMj7uS3bCgAAg8M6F4iPLd0b1U=", "pj4+UgOGkpJwlRvX5BRXZzmzAUnDtUtJsS7LzbcJWzw="}, {"kL6M2KvO+vPBLEc/a0DpEHTibQ1bwaMRT9b9SkzeP0Y=", "pS3G2bHHlkOE6UHP0qVitDuxXgjEaZTviTjNc55RbVs=", "ZVZpWOtYqhX3CpF1kATg/38J6pvUJo8AS1sVYjs3rUE="}, {"sFlcRLDn36fnew2Ld92IHJwnKifdS3aF1MWRPs6K3Wg=", "OpjUOTiWExaDYULTINB4yQFqc3mnU3RjQzGRV+KtdFY=", "of0V/uoFRNljv/XTt/tXgoquLRH93Ty0KNiaPUpEi2w="}, {"UJ8hjDsg3jfsnnfPH8Gw9FnCb6taTuviurAfZu+kEFg=", "3byjHksUOv8CNjGGKvHvvrJDURBhCIL5UtfZbgyVWCE=", "9f7dbWif51gGrE7R9LeuewQSrvGFGTOB3ceJC67jSkI="}, {"iFFPKGIfqeUKY/w72KAZSjd/PGTqCakHYBV10xMDfnI=", "ehneHATNSXtsJTOiPjVSc0QARkihgcfcvoXKFWKfQnI=", "yKdqDBcRwA7RCg4GiY/b5IsWcExPleOBde/hjxc36a4="}, {"GDGocdPJTPUAllxQo7SpXZKqMPn7lpxQELQUX9ETHmE=", "n0ScNEou4ekfrXRRXvcADLu2Afj8g5D3TuDP/I4KrnY=", "8QhswqAhi/ehhcmCwQF5aSh80TvIGC/gRL5jBn5wOH8="}, {"UCcrlN8fX2ZdNdhaEBNwktwL+H0ZO7fhaj7rgdUutmw=", "LF8J728ilXs4TphnrgR6r0p7W3912DYnsXkGMEPQnRE=", "cYfMjxl2REYir7frGeB0u+NHAaFYF02ysgpBhOL5ygc="}, {"SH0657XuIiHidVmViXZF30RUWtkuWXcWWHmKZHTiOG8=", "7k6j49W1u5qgLE5MQUc1osPVW1oPPhzjrGvJ7o9YamY=", "dV2+7rNk/3LR2IcwYg/c+Wvzep1yjY7/u1I+nnlTQ00="}, {"qFQWs6jzrscV42pGISQyA7JDvFFAvEQCWJi584VHD2g=", "AT63nHKLC17yUvkR4lOVPxCr4DD3QhXmcmecOTn+Amc=", "2Qe2fwJbFcu1CmKpktElOFkSQMqlyvlV3ZUIAd/Dcts="}, {"6J+yLxgPwWtUbk9I3zbeD8RuK6XkQjJ0wTJ1zSVhflQ=", "NJzMBYyPZjk3eLmgdKaOeWyNER5YZF1mR8Umeiu9f28=", "pp+4+XHw5ZmHGJ7WbZ1xLYRsnTI17QIbb0bzHzYZrBs="}, {"KJGoWYNVDUWcEMg+E4tljv1LiWAbdRw2QVapYqdFa1Y=", "M2SGk9WVnzYNGnT777G/JE8uUsY2f7mszTwlue73UDE=", "Jg8N7GbhbYB400foFP+OH0v+hCaL1jW61bajSA6EZqc="}, {"uAgrgppPyIvk8S1CHUmaCaORsgFFfBreB8pxXmbSdXw=", "dJ22bER4fD3qF2/yIGWQ7SgmZ990sy/SbANjmUMkzws=", "mKkd0OoAR9sClmD+k3fL9weBsoCy5GQGz9BP0kAQIjc="}, {"EI3Q8gePNPrtFyoMcv7hOihQgroF/dfjzPn8yvpGPWQ=", "Y9QhJFeiyuuIZNPU56B6U//ZK+XTTe4EP7h/p3Q+dE8=", "qBVUYw9rTWaxn55nUd55NpCWtxOUSWLt4WJGusiDS8Q="}, {"oEQvdRm/yHkx+JvlhHGT5RUFrFEUleKb9DCT55EqZ30=", "8hsh/UHwWADTHntOJq4dy0o7ahcNAAlo2rDpjzzrVXk=", "/GF2inW2mPtA26IgFdgOEBbBEerT740wWuP/8NyANdc="}, {"6OWrGKZKNsgfRezSUw29EnHymcgKyEKvX5/pZQlLmWs=", "oaJeO6YSS2dodNEf97DvgWrYnFFelG5daEdN84jVVGw=", "T5wvTdyVxK0LY96kouLjs06oUfhGChfty8OUL1Mddro="}, {"mPmzQbh2R+r1DC5hSquzKM1SDrxUdfnBPRPJrpqrgkE=", "BtUHAnYWjDjBI42qBf9dJqezTUikYsF96o6PKEWPrVo=", "MxU8EMmq+vVHpuK/AkFZrZDF2b+VbSqukZLPbNsCcgo="}, {"8GVxuoo1Veyr+nqxr5Q4vmsMf5qfiXwSlQ4q3+BU60g=", "uSOLe/E9/OjIgUOk0NMBHB45E0+q4Rd+IUO2UOxmKlM=", "+30F+56OE0Sr3wY2clKw4kgE+2XiucMg7xjK6EemXuk="}, {"qORPKb+qFuU/9TpFbRUupHsqm9iyk9pa6cpik+EVDkg=", "bMZxxd+Z9I0XA1h9U8JEY+/mRxWGnvbXDZ5Dxz7YzS0=", "SmkkqOz4OhHuSL6cxuRm9+Mlt50Sfd0sMDFTC78gqOE="}, {"2Ko3IYhXKcdOMIJGNpASk9saNSZsI64lyJPOoxpQ2kI=", "xVOc9PxY1VFaZfemmKi+Ei2liHhmeTu+JMa+rS00gnA=", "+398DlW8kWeI2aRaC4QfcrEjwqPKCohyDeWdaI1wvv0="}, {"CPioGCVpxnym62nH/QoCt1RiiaaxUcuFjvh5kRhjqHA=", "W0XxlBLrZgFKhggMvvv6oFf/RJbfs92qv8JK9e+5i2o=", "bsK2U6CRAUv1uVgYQ7NpqjWWswFIDiDPDEtU1XQygSA="}, {"AF17siKaeiO85hikYN0IWCMGWqPm1UOoCkXMltJGUVk=", "B+PFos9aN2S5bLxzGZHljRZj41j3rIx8RWu0vDUzq1w=", "Qb8d6iDYv3m1h7PE8j0Exl2cSwpHkim/fJ1S4P7MYvY="}, {"wMFDBTJzx5tDCBhMkrptYJ8w9EeURjc4xeDQpevxAWo=", "4ec439EXE5WQzvtV9reSX+aMmdq5k7o9Ayt8oQp+RhQ=", "jwQlvdNH5WtSSU10H+fh/JisOlaBaohDPEp/BYnTt1Q="}, {"4GaJpIFigNDwd31O84pLIMM/o2qhp0ydlI/ydD/2a2Q=", "r/LdkCoK5/BPGdq2+XJO8sCRhI+8ULFmg0887V43PAI=", "Da+3ZZvEJdx4TYFMIDUlbkmytILnSTNxTKX+sQdjMd4="}, {"gMnojGqCLmMfGp2m31xlKZ/rIV2b8ockw9DPahRyu0c=", "H6tKCTosnM4BXKqflXrkTdJyNlCIZhQ3ZRxfrvSdrDk=", "4Z6K3LKIMV89plcjMb9CzSzJl03SWRe/++geBMZcOtY="}, {"wKCg22aNNoHnDJ0oAKE46FcSSsREW4AaGn5WxCSeXUs=", "9NDTFC0iPt4HbbWepLHhN5poNTN2fdxJKNadsNT7qzY=", "GSVTOCnfLpJ1VCOLHaKSjCMv7/OlcnQiP5+5woqkud8="}, {"oCoykq7pcJcg2X2V5TBRzGwn8hzzHC05WUreuotdznY=", "DxfwnbMqr5Wn5SAyFolfEmNQT6l84Oq69ngpg6H6Iio=", "p1RHBuqhuDa1MAQ2lbqmUQFu0CTwYlf73fWZSj9tQhA="}, {"UO7YVyRUVkcKr7c63VWWV2zj36XD3HyDfLZCqvrZmFc=", "360lzYtIyHq5lv/QXSCe4bL4G2J1jBXFJ8yS+Ycr7Bc=", "RRPQ1XWF1HN8Us4dtfn2eemdjgtWm7U8r7mM3y00NOk="}, {"eCGFYV/NuGP4H552E8Of1xU+3IvZxGyX+p5UFGW8iHI=", "LqhZ4AS9dQ/MhsQnE5Oy7Q8INXY+P+mrfGY5dtg9SlE=", "SICfqs8T5wP6IzATDCT4ovamBKPdkZ7JP4Cfsb3izec="}, {"oI3HBZknoIMMZw1BuYMkTBylt25reX7AbCqtWQv8cno=", "B7dUgLgvQhi0RGmvaMrmf26WdjEVrhaiBclVkCKd4AA=", "5O9K9pLXwxFAt5lfMWh4qGbwX1BM7sz0QGYxAnR77dk="}, {"sBfYn14EFVIS2M/E1aahP7mOmRNbNtyDChDMS1s6aGs=", "FLYv0ZvzxMkc9A7OzhC4P1ZRu1aKIQd7u6gqfdekC3c=", "kaYLcNCXnCLgiB8fleMQuboUJsj5u3YAmXL9x3ywV0M="}, {"qFwZESU/XYZXUtxwrGsFU9qPAFTzjm7EhTS1Q6ajGlM=", "hraQQaqJCkS2yQXv+ccMOVh9V9a/qgSZJgdMAhrt8ms=", "72oDfnWOn39gdk/ncw8Lv267I0I+m73SwxrpYojpWYk="}, {"YHhp6Zf/miuc2QXeI2lTezy0lL0pTv2b+nWNkmjYQWI=", "UhNO5arLzF0WlZSgNOx7+IjWN+GSxDdQxZRp8uIwsyI=", "1Q39Nzv2NGI9zWKWMpYLURAMZUg+FP+OboHHzFU8Anc="}, {"mLBBXKaCJ+7qeBZpS3wxGi/SQ4kLzun+K+QwwdwfJVQ=", "gIq/nh7NwCJ36MvRnyrWHaRWu8lTmwfN2NvsjVl6SXQ=", "AahcNR91GDyJBIP+vC8ZuIV8ukqjSGtd8s+cmjVC2Ao="}, {"OJG4LZlNNngFtAEQdbVVWVm6QAjOOauGcMZGbQrb40M=", "pFHAC6HaWAOtvTRRVfSHvzG05mp4SJZXKsN/tkSF0kM=", "IoXT3wIqWNxQhYuHWl12ODq/P7RM9LwaqglhmjKg+0g="}, {"OOwBFOQNhepiqDf04DehQLh1gpBNOluDF1ia752Yfng=", "u715uJ/XhdjXjThCTJ9w6zzXnIhp3VCxhtso1wk+oBQ=", "5x1Ip3Ym0KzDjGhiYjmpeWWr+dgrZlYwfr02GngPOTM="}, {"eNPFnwkQy1qw+IjFAlrDA6+sIsxbWDlzbNSsBW8R1UA=", "OaOXaAfPb1MRpWadawFje8YZ0oxJgdCIDIP8c5X+r1U=", "NtfaRRD0GqujnaQQoNoBbtovgO4dfVwEmEQx/YgnDpw="}, {"OLdaZItbtxH3mGqItkibIJp7KV27FrQavjhd5zq6s3Q=", "SLSmAYxkMCGj0DO35cMLkC3NVAqK2VmVFndbOZEdA24=", "SnBO68XQTDjxYbmYaAeEHgLwD2u4D+BPT86raRuUQZM="}, {"UBQOEz08izwr4eEK/SnQUpkt+TxCjo6Sya/XOGMLOE0=", "wQwrwezI9LzKevGsJJCBHDG8noR0yIEtOK5Rig97SSo=", "DpyS+0d7lrlFWkztsniG2v/j44vcuvWz3sPeghRyb5A="}, {"mN98iuqUKh67ggUdq9ZIQNZCZM90fgycTVqYKEo+DkY=", "GYdXVW1jpS0dN1q9zMehubP7LfYqs34kszN0bXQqxxA=", "AJPIHffB4uvvJJki4xCG0VORVBbF6bc2mZQqUx+idPc="}, {"OEd/1it8C3o+NOWxDI3DfLMXVBHJQg15N3E8F8d99l8=", "QL1NcuUkoXxDy7M9VjGslCejcUlnUDHRghFVnr+8fmA=", "nven9Dicl8U6QXuDO8rRNtjd4NYaa90SU+Gmv435XKY="}, {"AFMCGDu2oAP68miucsi4fmYX2KeRZnsEGv8tQm8JEng=", "1sNxvk8uZhFsBUgxOXmuCMjDAgBbjVeWe9oaFk5Osy0=", "t5iI5XXd56S5q0Y9HC91gzgF9uGjL9FIy6NUaKqkydo="}, {"CIAwfJghQHHr4YlztN9at6/iWkrEVCGFAxNVuQCuT3I=", "zpUOF1h17g7RpBzrVlN7oTRz6e+dxcDL8OsAtHwgLC8=", "kOSwC1p6Uoti9E9Eg7ViPZwCytuvp5Fr5Buw677aogU="}, {"sC8vrAVBU0zvhWDRfzfySjvopXm2/cTMkTLmioyO3Es=", "p6H7GWm8NfgyO5OCX/COjvVT4MAnTs9ZUj4uZMK8XHA=", "9Tzqo57V/h7+6nSNAHSBKdmU7ultlvZbAnNKSRlrLi4="}, {"eI63gjxCZGnzqZxPEi/ifYphXhxIRI2ZxK8jzqo3mmU=", "XyNzEuU9x37fxFCnrZH89Krs5/UqGVx5wNkGfQCAYz0=", "vZ2fTlRPnJQ+q33YdS5p1aweqPGj/kTMc4Uq80FtFjI="}, {"aNJlGtm79/RS4SQ/PC4YM6LFo9zAqDr2/RjLqk/z/1A=", "lgZ9akPrABmfHQMlfNFnnpAJzGtcsaU9mUjEYKfzZHc=", "d0Xt1Bcgphd1HMI0RneA4VdBbMZL1qNGJAvFhb080eA="}, {"oONSnHirNh3cuH93Ty0C9AXKebGY+cdF3R0DtPzIQlo=", "TuREKfA8EVQiYWsPx8veUzjN2cz/b72limSLWlrCWxw=", "vEqqKbpZf0EM6EApMUaUH65r3Zr81Y/DSODhE4H7U3Y="}, {"+D6RyLEaHJ9YF9WDyOlwh87KaNJcc6lqX8Arp6yqHF0=", "EpecjfIo1/EEbmsgUtzEDqLu2ut+SMmzqaBL9Z/MlCA=", "oYfO6/7XQgEYT9zmr4sqFrk0muK/fEv3FfD8MzZzjkE="}, {"OCmW5KQql2PRMJnsMYQjXlr6TSYUbxJBknqZtXJPSHM=", "ZZR2ghHlCwAJu/XlsEZuNS6XiGPwuXzyMPPywYFapVM=", "fqSCXq+pKJJ6yNvlOi+tyQ9E4Y6kc4kblGrVqN2WuXA="}, {"APWXDAe8d2ia1CUbf/IzSPXOUjR8TVuJgmISiWw0/EY=", "jrT5P5YCkG+U7cfNTvCKy0GSEgsjwmJtHg+8HBP6ZCA=", "t75aUjZXMPir8Ao0yhVClh9/BdWxSL+11CjK3iELNWk="}, {"8Nw4sRis7M/6Om+3w5YHXthyMzLGuP48teqdzbHNPlA=", "lj3q3ZYij3ZJ/QunK8n9I00cv/Z+O1TU1kFFl8x3DTE=", "adqB79P7gbXEYYnSd1/UPCwFffTAPXa9kHWynRBYcGo="}, {"yOupps5XbjV0fIZKnGhrpcxB7yDQzbBILC0UMJyVS1c=", "+MDV/t9UCIdgm3IkH3BZlxaRPJ3lejRmrm4UPApq4mk=", "AOJPmQxsU6hjOd+9mHnF0VL7Afih3P1Fr625xFT4FtY="}, {"cD2DEl4MBwONuTV0db5XreoVjQAUZFNXqIeFEU3KFkE=", "2CeHrjN7tBX48k4Sgv5fIHG06e57q/ucCL+8DIRmfXw=", "3EUo6MRzs6rSoY/7AFs8wiBiTXPcHzerLh6Xp3aMGKo="}, {"EA+S3a9ZeOLiRbhTxaT2wkpyDheAmai+UJa6SFGzSm8=", "GGByUKZx/FPa2OkJoqVTHXx+6jrIpIw5rf0rp43MHko=", "BXoDA3yn0JcMV7hHVzEqhlwAORvhToFO1qG00nas92A="}, {"eBeJi/imBiV52WEqrwAprUQggqdQmvTTmWtLq8pDDkM=", "zCX26ZTOZHLpq5x5aIUL1XhIVoXJLp/zcXwnmFA3jBo=", "Dm/DCxXWYXEsmQgxAD3KREK2PF0bUSnV5WRAaya8s1I="}, {"8C7p+EQO+CnWUSjHVu3PpeWpUIbLy48zpftZu021plM=", "DxpnF/IbKAh6kmWC5Jpj8iw387EDkrvjsjOb9fbTSng=", "bGrk0OshJB+0oQOK0QGKU8+lotnIDz3oeUnMZGienyM="}, {"COIez7YcBJiOJCLxxWV5UGLW5/o009YI0aszlD/PiUc=", "eD9USWV37LFIOxlDSHyOmfFqNJFpORRlzEI+HoF/czI=", "n+/ra86gUSF2pNZS51nt2JgrzXnQJl+dWswOq/Ahs94="}, {"iBBSTG9VLC+T9+ahNaQ4umZoig8o7w1DaeOw+cD2BGc=", "XnAxqDlvGnQ6aakv8ABGHVj07qVQfk4NChZbstTMBxg=", "7KKSwu/4yWr1UzFmNMGtiaSwdYMhP/HKbrQLlABL4UE="}, {"eNmwattflehr9+KsVqTuwt1YaAc5ONkaIaTQt9Gkhn8=", "1NNVvm++YTTGMKyAXfGOCZ4aDDdFFH5Um3vAg4XimDo=", "bXNrnDTP0pBay9ytZe7xpiKoSi12F7WUXqoIeI++Xvo="}, {"QJotpmZINx9eptKpkh9j3JlEDcHdWnjEbicdBS7gPXM=", "3gLYKeoruVZ/AYjym0gciDvRHj45UIWyHhNjWj2Wj28=", "NwuUkkE5yOWT7wed7bltgAk71miz3cSooiIDAdv6kKw="}, {"OAYGuxH70OPQvhVIX4BhSCWUyzAI5H2IkYxKgC/AO0M=", "Rj9iNF/FagkXfdLPqc9LHfaoGR8GlvY3gun2FilE508=", "hkNXLVMlBRsMEaQKkSzevcEK2sMu0AShGKQJMNqdzWM="}, {"uBrgZ7wLHrOV/0dNiEqo7FjY9VnJqL5eUDHJWAc9QXg=", "3Hnln8ZHfSaK4OzESJe5U6NcLaW56wzfZICzvzefnSo=", "DhXsehe5FAmbUidXT5ZpZIAuu1eF9rkU6cF3FBoKwOE="}, {"sJknn3CHvx/812EWU3ddLdLLZFBKsc+wx35GXyiRsHY=", "qqa2dNSt0jWozGyqpokP392H5/DOAUUZmUpyZDaUEEE=", "1Dyz8CvmF17oKT/wG2fu3vRzPzgQv8/OY9GJYew4FG8="}, {"yOumS9HN68ZwIm+5hZol3jFQ0DB4SKuW/ld3y8wioGk=", "6PowsbKj/fKSzXZMAfaSkP3fE+4AThL9xm6ysQzMDxg=", "vF8cKu9X9FxgCjyVZ5RG7nuue8RelF5Qsb8Efme4M4A="}, {"mISm5vQfPdK72SsaHh6O1/ARvaWCtm+KZNcpTsyt500=", "YCORDQDpb1U8vADdstBgXkg0N7QaAc5VoXJ4QFuA/UY=", "JlrBmfaCgbfEVD9YQq3c03WwwsHWc1nBwp1JkFORC3A="}, {"cPyu3Qry6qbsiOJKFGRziZ9LJWJ47k3ZSXiGkQXuQm0=", "/cmBZTbqEp8sababPAxGb3OvDAEE7MlwPOwEFHE+7yM=", "lp0Hhc/rVtpT5FtLLccChqDl3El48XtP6Wm6JwjI7jo="}, {"YKsMYU0SINbPwWw4RDCJV6GnzDlSp1ZNwUw2euGWi0A=", "/KmBReATQbFnLg8YKV0jwhqKeishRoWvlVtMX3550Vk=", "7fXpPSMo1Fw2sOXtjTtvFU+DbZvS/FWB9wAsywWx6R4="}, {"WD0YI3y71eIp/GXw9i+7scEiQKSBkGihZWE+s6fGmWo=", "RdthAL/qPnAZFb3xBgRMiAtGHNjgokzoKX9iO2K5qhc=", "4dk4HGkT9dBmomGNorDE/hLr/HEFhljtl4zz3M4sG58="}, {"oD8KWhJYZVhutJRb0kZlZnB22QUXzi2FfPRD0ll65UM=", "MYTBGHh4Ukj97pKj6qcfWmxGNQzmU3/aBOX2f1tfhG0=", "VT1gC+a9nRJzYMi/TPvRVnn3IQlaop/jKmmxZePEME0="}, {"0Ns/1SOiqR2CpHRG03QNzJJd5gxTm1XJmSkFlugjQ3k=", "KvQAI+ekNOa2xfEvfyc9JGcS+CTUrnnhsKrlyJGJixg=", "J8LmSX6zElX3S9q4PNvh2NKUtAiQ3oHiYjSJ7yErPlY="}, {"mD6TeF4ezSPXN/csN1OhoAREFSXllI+zl4DUOInVq2Q=", "WmLJ9ep2EqFcSftnYFJsmWyUxqL0zzuSzVEv94PcISM=", "U2+ILy2NDmmfgSW78C8dl8GyHESUc1lXPHPpg5F+gr8="}, {"SMJoXOYgHz8HSzY+ByeWLcSP5qFwv7YjRe0bcKesRnM=", "DEsNSOY3TEs9J2YgqroQ4xKq8T8xNJQjvvE4UrTItQw=", "Bws1Hk2+lO+JQ7ME16EbwAdsBkWsGvti0Gb6LY2Lrms="}, {"iGhXF0Hg0tqZmpwAMiolxvbvTPClQ7LlBAspSSyFEHE=", "7Xxzpwl7yRWehHNWTYVtFkdChJdXhtY4Mtw1fA9QcCg=", "Swjfv0PjuaE8Oq3a17BVno5I+q49dZlPwKK1bPUoKNI="}, {"MIazjx3qTi6Qz3WzhtCPw3i4Q2uZBHcuMoh++ZGFYUk=", "oni8pbFqk9Ya+Fx+911Nl1SN0FD/hR1jwb2RH3t/pRk=", "ZYAFcj67LkbNURYbSnCCWGxAG8QLDGWwbl968mA6ZA4="}, {"MAadYdiFM2cPuJF19q20Yoo5KJabuR9TUQ9jG5nvA1w=", "5OcE8XV+UPoBVbgqBQdVF62GZCW9DOQEdxrQsktPPBA=", "FCZsEFXouy+xtxv8X7VroXtvPG1Z1HFHL724tz1jcUI="}, {"+GwxMmD2dee5+QmvXNI0NdP+rNWoSXTN42otbp0aZ24=", "Y0N44baz9ihclCUnv6rRbDqCYu4BxQlBfNnTz3NNe2A=", "/LqSgkVQNkQ/oBiZSgpM9Rw7BJv0RvRpEQpvlizvHy0="}, {"8FREpCtncOcT7+W2nW4aYSjmSbADtVSH9rIliQZZUH4=", "fTNSd0JeREhXmPfjrmrAu6Lu/yHkB9GyxR3SyO4kZ28=", "262KN/iG/iJEaZeerFm1yVtvhFVGgQFwSvtxTcjZzeg="}, {"EDhaRQGtscjoSE9wJOnSXoQVtVruIqyzknty+x/vDWo=", "eMmMgws6ZxDIxZ6QSwGjZO1Mx/r5T+fJjSTKGMBk/BU=", "0CyaJV6AG9bZ0C4yeZ/RDsOs9BdNqZpUxAsD30WmJO4="}, {"CJV0UB2YdvVDG1cs4oiJgHAS+f1FocGr/vGCfiovsWQ=", "9/O9GWZEOXVm7On8lftL27PffRORju8OKl6gZd/74CA=", "A+kXRVNOwIrA5DUa+3v7dpRC+Gbxm23LTiYmOUAXUyY="}, {"gCjDsJUwZGA7BjYVoCQsvdIgN9Q4lBHlSyKwUrl751A=", "HRwS8T9y2qPYk7JVU/8Y+6cS+Bk8XCLCXxwN/ttbQiI=", "iFotjA6rhUfkDv4S/wspJgEWunEmrlGSGsXcJ0+8laQ="}, {"6N5pL4gsuK+shHpDxirTnAGdyKXIlYHyfIhtB0njJGA=", "CVZvW7NaN2XMEEKHodghBA9hLCwee/jrmttiWh/CmEg=", "OpPEd3Sp8r6KdjNDTN4bVHETlGJ92BCK74FCdEaDe9g="}, {"UIPPTUdvhlg8qEDv6JRxM4/8F5ORjJz4ud82QZrgeEY=", "7Nd13z5EpB3ChytvQC1CxvDY7n0H8r2Y7lzLEY8hdEk=", "b22PvgU0M2QfNC7ZGN+RXNe5fjOzMsY32IcHTwLNIqw="}, {"oBn53Q5fmxKX02PgI6F47Rb+XoLeFQO07ok2tYhk0lE=", "e0gtPDKXCZSoNW1uHqBPQXLfiYgyeqPMU2zZJgPXACI=", "wmjW2wDT2EzFkyaGui7YWNLTRu8Q4eD/GVKM2utZkEs="}, }; gs_unref_ptrarray GPtrArray *allowed_ips_keep_alive = NULL; gs_unref_array GArray *peers = NULL; NMPlatformLnkWireGuard lnk_wireguard; int r; guint i; allowed_ips_keep_alive = g_ptr_array_new_with_free_func(g_free); peers = g_array_new(FALSE, TRUE, sizeof(NMPWireGuardPeer)); lnk_wireguard = (NMPlatformLnkWireGuard){ .listen_port = 50754, .fwmark = 0x1102, }; _copy_base64(lnk_wireguard.private_key, sizeof(lnk_wireguard.private_key), self_key.pri); _copy_base64(lnk_wireguard.public_key, sizeof(lnk_wireguard.public_key), self_key.pub); if (test_mode == 0) { /* no peers. */ } else if (NM_IN_SET(test_mode, 1, 2)) { guint num_peers = (test_mode == 1) ? 1 : G_N_ELEMENTS(keys); for (i = 0; i < num_peers; i++) { NMPWireGuardPeer peer; char s_addr[NM_INET_ADDRSTRLEN]; NMSockAddrUnion endpoint; guint i_allowed_ips, n_allowed_ips; NMPWireGuardAllowedIP *allowed_ips; if ((i % 2) == 1) { endpoint = (NMSockAddrUnion){ .in = { .sin_family = AF_INET, .sin_addr.s_addr = nmtst_inet4_from_string(nm_sprintf_buf(s_addr, "192.168.7.%d", i)), .sin_port = htons(14000 + i), }, }; } else { endpoint = (NMSockAddrUnion){ .in6 = { .sin6_family = AF_INET6, .sin6_addr = nmtst_inet6_from_string(nm_sprintf_buf(s_addr, "a:b:c:e::1:%d", i)), .sin6_port = htons(16000 + i), }, }; } if (test_mode == 1) n_allowed_ips = 1; else n_allowed_ips = i % 10; allowed_ips = g_new0(NMPWireGuardAllowedIP, n_allowed_ips); g_ptr_array_add(allowed_ips_keep_alive, allowed_ips); for (i_allowed_ips = 0; i_allowed_ips < n_allowed_ips; i_allowed_ips++) { NMPWireGuardAllowedIP *aip = &allowed_ips[i_allowed_ips]; aip->family = (i_allowed_ips % 2) ? AF_INET : AF_INET6; if (aip->family == AF_INET) { aip->addr.addr4 = nmtst_inet4_from_string( nm_sprintf_buf(s_addr, "10.%u.%u.0", i, i_allowed_ips)); aip->mask = 32 - (i_allowed_ips % 8); } else { aip->addr.addr6 = nmtst_inet6_from_string( nm_sprintf_buf(s_addr, "a:d:f:%02x:%02x::", i, i_allowed_ips)); aip->mask = 128 - (i_allowed_ips % 10); } } peer = (NMPWireGuardPeer){ .persistent_keepalive_interval = 60 + i, .endpoint = endpoint, .allowed_ips = n_allowed_ips > 0 ? allowed_ips : NULL, .allowed_ips_len = n_allowed_ips, }; _copy_base64(peer.public_key, sizeof(peer.public_key), keys[i].pub); _copy_base64(peer.preshared_key, sizeof(peer.preshared_key), (i % 3) ? NULL : keys[i].pre); g_array_append_val(peers, peer); } } else g_assert_not_reached(); r = nm_platform_link_wireguard_change(platform, ifindex, &lnk_wireguard, nm_g_array_first_p(peers, const NMPWireGuardPeer), NULL, peers->len, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY | NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT | NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK | NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS); g_assert(NMTST_NM_ERR_SUCCESS(r)); } /*****************************************************************************/ typedef struct { NMLinkType link_type; int test_mode; gboolean external_command; } TestAddSoftwareDetectData; static void test_software_detect(gconstpointer user_data) { const TestAddSoftwareDetectData *test_data = user_data; int ifindex, ifindex_parent; const NMPlatformLink *plink; const NMPObject *lnk; int r; guint i_step; const gboolean ext = test_data->external_command; NMPlatformLnkBridge lnk_bridge = {}; NMPlatformLnkTun lnk_tun; NMPlatformLnkGre lnk_gre = {}; NMPlatformLnkVti lnk_vti = {}; NMPlatformLnkVti6 lnk_vti6 = {}; nm_auto_close int tun_fd = -1; gboolean module_loaded; nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME); ifindex_parent = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; switch (test_data->link_type) { case NM_LINK_TYPE_BRIDGE: lnk_bridge.forward_delay = 1560; lnk_bridge.hello_time = 150; lnk_bridge.max_age = 2100; lnk_bridge.ageing_time = 2200; lnk_bridge.stp_state = TRUE; lnk_bridge.priority = 22; lnk_bridge.vlan_protocol = ETH_P_8021Q; lnk_bridge.vlan_stats_enabled = nmtstp_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED) ? TRUE : FALSE; lnk_bridge.group_fwd_mask = 8; lnk_bridge.group_addr = NM_ETHER_ADDR_INIT(0x01, 0x80, 0xC2, 0x00, 0x00, 0x08); lnk_bridge.mcast_snooping = TRUE; lnk_bridge.mcast_router = 1; lnk_bridge.mcast_query_use_ifaddr = TRUE; lnk_bridge.mcast_querier = TRUE; lnk_bridge.mcast_hash_max = 1024; lnk_bridge.mcast_last_member_count = 2; lnk_bridge.mcast_startup_query_count = 3; lnk_bridge.mcast_last_member_interval = 5000; lnk_bridge.mcast_membership_interval = 25000; lnk_bridge.mcast_querier_interval = 26000; lnk_bridge.mcast_query_interval = 12000; lnk_bridge.mcast_query_response_interval = 5200; lnk_bridge.mcast_startup_query_interval = 3000; if (!nmtstp_link_bridge_add(NULL, ext, DEVICE_NAME, &lnk_bridge)) g_error("Failed adding Bridge interface"); break; case NM_LINK_TYPE_GRE: module_loaded = nmtstp_ensure_module("ip_gre"); lnk_gre.local = nmtst_inet4_from_string("192.168.233.204"); lnk_gre.remote = nmtst_inet4_from_string("172.168.10.25"); lnk_gre.parent_ifindex = ifindex_parent; lnk_gre.ttl = 174; lnk_gre.tos = 37; lnk_gre.path_mtu_discovery = TRUE; if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) { if (!module_loaded) { g_test_skip( "Cannot create gre tunnel because of missing ip_gre module (modprobe ip_gre)"); goto out_delete_parent; } g_error("Failed adding GRE tunnel"); } break; case NM_LINK_TYPE_GRETAP: module_loaded = nmtstp_ensure_module("ip_gre"); lnk_gre.local = nmtst_inet4_from_string("192.168.1.133"); lnk_gre.remote = nmtst_inet4_from_string("172.168.101.2"); lnk_gre.parent_ifindex = ifindex_parent; lnk_gre.ttl = 39; lnk_gre.tos = 12; lnk_gre.path_mtu_discovery = FALSE; lnk_gre.is_tap = TRUE; if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) { if (!module_loaded) { g_test_skip("Cannot create gretap tunnel because of missing ip_gre module " "(modprobe ip_gre)"); goto out_delete_parent; } g_error("Failed adding GRETAP tunnel"); } break; case NM_LINK_TYPE_IPIP: { NMPlatformLnkIpIp lnk_ipip = {}; module_loaded = nmtstp_ensure_module("ipip"); lnk_ipip.local = nmtst_inet4_from_string("1.2.3.4"); lnk_ipip.remote = nmtst_inet4_from_string("5.6.7.8"); lnk_ipip.parent_ifindex = ifindex_parent; lnk_ipip.tos = 32; lnk_ipip.path_mtu_discovery = FALSE; if (!nmtstp_link_ipip_add(NULL, ext, DEVICE_NAME, &lnk_ipip)) { if (!module_loaded) { g_test_skip( "Cannot create ipip tunnel because of missing ipip module (modprobe ipip)"); goto out_delete_parent; } g_error("Failed adding IPIP tunnel"); } break; } case NM_LINK_TYPE_IP6TNL: { NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; module_loaded = nmtstp_ensure_module("ip6_tunnel"); switch (test_data->test_mode) { case 0: lnk_ip6tnl.local = nmtst_inet6_from_string("fd01::15"); lnk_ip6tnl.remote = nmtst_inet6_from_string("fd01::16"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.tclass = 20; lnk_ip6tnl.encap_limit = 6; lnk_ip6tnl.flow_label = 1337; lnk_ip6tnl.proto = IPPROTO_IPV6; break; case 1: lnk_ip6tnl.local = nmtst_inet6_from_string("fd01::17"); lnk_ip6tnl.remote = nmtst_inet6_from_string("fd01::18"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.tclass = 0; lnk_ip6tnl.encap_limit = 0; lnk_ip6tnl.flow_label = 1338; lnk_ip6tnl.proto = IPPROTO_IPV6; lnk_ip6tnl.flags = IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS; break; } if (!nmtstp_link_ip6tnl_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { if (!module_loaded) { g_test_skip("Cannot create ip6tnl tunnel because of missing ip6_tunnel module " "(modprobe ip6_tunnel)"); goto out_delete_parent; } g_error("Failed adding IP6TNL tunnel"); } break; } case NM_LINK_TYPE_IP6GRE: { NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; module_loaded = nmtstp_ensure_module("ip6_gre"); switch (test_data->test_mode) { case 0: lnk_ip6tnl.local = nmtst_inet6_from_string("fd01::43"); lnk_ip6tnl.remote = nmtst_inet6_from_string("fd01::aaaa"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.tclass = 21; lnk_ip6tnl.flow_label = 1338; lnk_ip6tnl.is_gre = TRUE; break; case 1: lnk_ip6tnl.local = nmtst_inet6_from_string("fd01::44"); lnk_ip6tnl.remote = nmtst_inet6_from_string("fd01::aaab"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.tclass = 0; lnk_ip6tnl.flow_label = 1339; lnk_ip6tnl.is_gre = TRUE; lnk_ip6tnl.flags = IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS; break; } if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { if (!module_loaded) { g_test_skip("Cannot create ip6gre tunnel because of missing ip6_gre module " "(modprobe ip6_gre)"); goto out_delete_parent; } g_error("Failed adding IP6GRE tunnel"); } break; } case NM_LINK_TYPE_IP6GRETAP: { NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; module_loaded = nmtstp_ensure_module("ip6_gre"); switch (test_data->test_mode) { case 0: lnk_ip6tnl.local = nmtst_inet6_from_string("fe80::abcd"); lnk_ip6tnl.remote = nmtst_inet6_from_string("fc01::bbbb"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.ttl = 10; lnk_ip6tnl.tclass = 23; lnk_ip6tnl.flow_label = 1340; lnk_ip6tnl.is_gre = TRUE; lnk_ip6tnl.is_tap = TRUE; break; case 1: lnk_ip6tnl.local = nmtst_inet6_from_string("fe80::abce"); lnk_ip6tnl.remote = nmtst_inet6_from_string("fc01::bbbc"); lnk_ip6tnl.parent_ifindex = ifindex_parent; lnk_ip6tnl.ttl = 10; lnk_ip6tnl.tclass = 0; lnk_ip6tnl.flow_label = 1341; lnk_ip6tnl.is_gre = TRUE; lnk_ip6tnl.is_tap = TRUE; lnk_ip6tnl.encap_limit = 4; lnk_ip6tnl.flags = IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS; break; } if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { if (!module_loaded) { g_test_skip("Cannot create ip6gretap tunnel because of missing ip6_gre module " "(modprobe ip6_gre)"); goto out_delete_parent; } g_error("Failed adding IP6GRETAP tunnel"); } break; } case NM_LINK_TYPE_MACVLAN: { NMPlatformLnkMacvlan lnk_macvlan = {}; const NMPlatformLink *dummy; char buf[256]; int i; lnk_macvlan.mode = MACVLAN_MODE_BRIDGE; lnk_macvlan.no_promisc = FALSE; lnk_macvlan.tap = FALSE; /* Since in old kernel versions sysfs files for macvtaps are not * namespaced, the creation can fail if a macvtap in another namespace * has the same index. Try to detect this situation and skip already * used indexes. * The fix (17af2bce) is included kernel 4.7, dated 24 July, 2016. */ for (i = ifindex_parent + 1; i < ifindex_parent + 100; i++) { g_snprintf(buf, sizeof(buf), "/sys/class/macvtap/tap%d", i); if (!g_file_test(buf, G_FILE_TEST_IS_SYMLINK)) break; _LOGD("skipping ifindex %d as already used by a macvtap", i); dummy = nmtstp_link_dummy_add(NM_PLATFORM_GET, FALSE, "dummy-tmp"); g_assert_cmpint(dummy->ifindex, ==, i); nmtstp_link_delete(NM_PLATFORM_GET, FALSE, dummy->ifindex, NULL, TRUE); } if (!nmtstp_link_macvlan_add(NULL, ext, DEVICE_NAME, ifindex_parent, &lnk_macvlan)) g_error("Failed adding MACVLAN interface"); break; } case NM_LINK_TYPE_MACVTAP: { NMPlatformLnkMacvlan lnk_macvtap = {}; lnk_macvtap.mode = MACVLAN_MODE_PRIVATE; lnk_macvtap.no_promisc = FALSE; lnk_macvtap.tap = TRUE; if (!nmtstp_link_macvlan_add(NULL, ext, DEVICE_NAME, ifindex_parent, &lnk_macvtap)) g_error("Failed adding MACVTAP interface"); break; } case NM_LINK_TYPE_SIT: { NMPlatformLnkSit lnk_sit = {}; module_loaded = nmtstp_ensure_module("sit"); lnk_sit.local = nmtst_inet4_from_string("192.168.200.1"); lnk_sit.remote = nmtst_inet4_from_string("172.25.100.14"); lnk_sit.parent_ifindex = ifindex_parent; lnk_sit.ttl = 0; lnk_sit.tos = 31; lnk_sit.path_mtu_discovery = FALSE; if (!nmtstp_link_sit_add(NULL, ext, DEVICE_NAME, &lnk_sit)) { if (!module_loaded) { g_test_skip( "Cannot create sit tunnel because of missing sit module (modprobe sit)"); goto out_delete_parent; } g_error("Failed adding SIT tunnel"); } break; } case NM_LINK_TYPE_VLAN: { NMPlatformLnkVlan lnk_vlan = {}; switch (test_data->test_mode) { case 0: lnk_vlan.id = 1242; lnk_vlan.protocol = ETH_P_8021Q; lnk_vlan.flags = _NM_VLAN_FLAG_REORDER_HEADERS; break; case 1: lnk_vlan.id = 4094; lnk_vlan.protocol = ETH_P_8021AD; lnk_vlan.flags = _NM_VLAN_FLAG_GVRP | _NM_VLAN_FLAG_MVRP; break; default: nm_assert_not_reached(); } if (!nmtstp_link_vlan_add(NULL, ext, DEVICE_NAME, ifindex_parent, &lnk_vlan)) g_error("Failed adding VLAN interface"); break; } case NM_LINK_TYPE_VRF: { NMPlatformLnkVrf lnk_vrf = {}; gboolean not_supported; lnk_vrf.table = 9876; if (!nmtstp_link_vrf_add(NULL, ext, DEVICE_NAME, &lnk_vrf, ¬_supported)) { if (not_supported) { g_test_skip("Cannot create VRF interface because of missing kernel support"); goto out_delete_parent; } g_error("Failed adding VRF interface"); } break; } case NM_LINK_TYPE_VTI: module_loaded = nmtstp_ensure_module("ip_vti"); lnk_vti.local = nmtst_inet4_from_string("192.168.212.204"); lnk_vti.remote = nmtst_inet4_from_string("172.168.11.25"); lnk_vti.parent_ifindex = ifindex_parent; lnk_vti.ikey = 12; lnk_vti.okey = 13; if (!nmtstp_link_vti_add(NULL, ext, DEVICE_NAME, &lnk_vti)) { if (!module_loaded) { g_test_skip( "Cannot create vti tunnel because of missing vti module (modprobe ip_vti)"); goto out_delete_parent; } g_error("Failed adding VTI tunnel"); } break; case NM_LINK_TYPE_VTI6: module_loaded = nmtstp_ensure_module("ip6_vti"); lnk_vti6.local = nmtst_inet6_from_string("fd01::1"); lnk_vti6.remote = nmtst_inet6_from_string("fd02::2"); lnk_vti6.parent_ifindex = ifindex_parent; lnk_vti6.ikey = 13; lnk_vti6.okey = 14; if (!nmtstp_link_vti6_add(NULL, ext, DEVICE_NAME, &lnk_vti6)) { if (!module_loaded) { g_test_skip( "Cannot create vti6 tunnel because of missing vti module (modprobe ip_vti)"); goto out_delete_parent; } g_error("Failed adding VTI6 tunnel"); } break; case NM_LINK_TYPE_VXLAN: { NMPlatformLnkVxlan lnk_vxlan = {}; switch (test_data->test_mode) { case 0: lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, PARENT_NAME); lnk_vxlan.id = 42; lnk_vxlan.local = nmtst_inet4_from_string("23.1.2.164"); lnk_vxlan.group = nmtst_inet4_from_string("239.1.2.134"); lnk_vxlan.dst_port = 4789; lnk_vxlan.learning = TRUE; lnk_vxlan.ageing = 1245; break; case 1: lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, PARENT_NAME); lnk_vxlan.id = 11214423; lnk_vxlan.local6 = nmtst_inet6_from_string("1:2:3:4:334:23::23"); lnk_vxlan.group6 = nmtst_inet6_from_string("ff0e::115"); lnk_vxlan.ttl = 32; lnk_vxlan.dst_port = 57412; lnk_vxlan.src_port_min = 1000; lnk_vxlan.src_port_max = 1003; lnk_vxlan.learning = TRUE; lnk_vxlan.ageing = 3245; break; } g_assert(nmtstp_link_vxlan_add(NULL, ext, DEVICE_NAME, &lnk_vxlan)); break; } case NM_LINK_TYPE_TUN: { gboolean owner_valid = nmtst_get_rand_bool(); gboolean group_valid = nmtst_get_rand_bool(); switch (test_data->test_mode) { case 0: lnk_tun = (NMPlatformLnkTun){ .type = nmtst_get_rand_bool() ? IFF_TUN : IFF_TAP, .owner = owner_valid ? getuid() : 0, .owner_valid = owner_valid, .group = group_valid ? getgid() : 0, .group_valid = group_valid, .pi = nmtst_get_rand_bool(), .vnet_hdr = nmtst_get_rand_bool(), .multi_queue = nmtst_get_rand_bool(), /* if we add the device via iproute2 (external), we can only * create persistent devices. */ .persist = (ext == 1) ? TRUE : nmtst_get_rand_bool(), }; break; default: g_assert_not_reached(); break; } g_assert(nmtstp_link_tun_add(NULL, ext, DEVICE_NAME, &lnk_tun, (!lnk_tun.persist || nmtst_get_rand_bool()) ? &tun_fd : NULL)); break; } case NM_LINK_TYPE_WIREGUARD: { const NMPlatformLink *link; r = nm_platform_link_wireguard_add(NM_PLATFORM_GET, DEVICE_NAME, &link); if (r == -EOPNOTSUPP) { g_test_skip("wireguard not supported (modprobe wireguard?)"); goto out_delete_parent; } g_assert(NMTST_NM_ERR_SUCCESS(r)); g_assert(NMP_OBJECT_GET_TYPE(NMP_OBJECT_UP_CAST(link)) == NMP_OBJECT_TYPE_LINK); break; } default: g_assert_not_reached(); } ifindex = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, DEVICE_NAME, test_data->link_type, 100) ->ifindex; nmtstp_link_set_updown(NULL, -1, ifindex_parent, TRUE); for (i_step = 0; i_step < 5; i_step++) { _LOGD("test-software-detect: step %u", i_step); if (nmtst_is_debug()) nmtstp_run_command_check("ip -d link show %s", DEVICE_NAME); if (i_step > 0) { gboolean set_up = (i_step % 2) == 1; if (test_data->link_type == NM_LINK_TYPE_VXLAN && set_up) { /* On RHEL-7, we need to add a tiny sleep here, otherwise, * upping the vxlan device fails with EADDRINUSE. * https://bugzilla.redhat.com/show_bug.cgi?id=1277131 */ g_usleep(1); } nmtstp_link_set_updown(NULL, -1, ifindex, set_up); } lnk = nm_platform_link_get_lnk(NM_PLATFORM_GET, ifindex, test_data->link_type, &plink); g_assert(plink); g_assert_cmpint(plink->ifindex, ==, ifindex); if (!lnk && test_data->link_type == NM_LINK_TYPE_TUN) { /* this is ok. Kernel apparently does not support tun properties via netlink. We * fetch them from sysfs below. */ } else g_assert(lnk); switch (test_data->link_type) { case NM_LINK_TYPE_BRIDGE: { const NMPlatformLnkBridge *plnk = &lnk->lnk_bridge; NMPlatformLnkBridge lnk_bridge_norm_stack; const NMPlatformLnkBridge *lnk_bridge_norm; g_assert(plnk == nm_platform_link_get_lnk_bridge(NM_PLATFORM_GET, ifindex, NULL)); lnk_bridge_norm = nmtstp_link_bridge_normalize_jiffies_time(&lnk_bridge, plnk, &lnk_bridge_norm_stack); g_assert_cmpint(nm_platform_lnk_bridge_cmp(lnk_bridge_norm, plnk), ==, 0); g_assert_cmpint(plnk->forward_delay, ==, lnk_bridge_norm->forward_delay); g_assert_cmpint(plnk->hello_time, ==, lnk_bridge_norm->hello_time); g_assert_cmpint(plnk->max_age, ==, lnk_bridge_norm->max_age); g_assert_cmpint(plnk->ageing_time, ==, lnk_bridge_norm->ageing_time); g_assert_cmpint(plnk->stp_state, ==, TRUE); g_assert_cmpint(plnk->priority, ==, 22); g_assert_cmpint(plnk->vlan_protocol, ==, ETH_P_8021Q); g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge_norm->vlan_stats_enabled); g_assert_cmpint(plnk->group_fwd_mask, ==, 8); g_assert_cmpint(plnk->mcast_snooping, ==, TRUE); g_assert_cmpint(plnk->mcast_router, ==, 1); g_assert_cmpint(plnk->mcast_query_use_ifaddr, ==, TRUE); g_assert_cmpint(plnk->mcast_querier, ==, TRUE); g_assert_cmpint(plnk->mcast_hash_max, ==, 1024); g_assert_cmpint(plnk->mcast_last_member_count, ==, 2); g_assert_cmpint(plnk->mcast_startup_query_count, ==, 3); g_assert_cmpint(plnk->mcast_last_member_interval, ==, lnk_bridge_norm->mcast_last_member_interval); g_assert_cmpint(plnk->mcast_membership_interval, ==, lnk_bridge_norm->mcast_membership_interval); g_assert_cmpint(plnk->mcast_querier_interval, ==, lnk_bridge_norm->mcast_querier_interval); g_assert_cmpint(plnk->mcast_query_interval, ==, lnk_bridge_norm->mcast_query_interval); g_assert_cmpint(plnk->mcast_query_response_interval, ==, lnk_bridge_norm->mcast_query_response_interval); g_assert_cmpint(plnk->mcast_startup_query_interval, ==, lnk_bridge_norm->mcast_startup_query_interval); break; } case NM_LINK_TYPE_GRE: { const NMPlatformLnkGre *plnk = &lnk->lnk_gre; g_assert(plnk == nm_platform_link_get_lnk_gre(NM_PLATFORM_GET, ifindex, NULL)); g_assert(nm_platform_lnk_gre_cmp(plnk, &lnk_gre) == 0); break; } case NM_LINK_TYPE_GRETAP: { const NMPlatformLnkGre *plnk = &lnk->lnk_gre; g_assert(plnk == nm_platform_link_get_lnk_gretap(NM_PLATFORM_GET, ifindex, NULL)); g_assert(nm_platform_lnk_gre_cmp(plnk, &lnk_gre) == 0); break; } case NM_LINK_TYPE_IP6TNL: { const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; switch (test_data->test_mode) { case 0: g_assert(plnk == nm_platform_link_get_lnk_ip6tnl(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip6_address(&plnk->local, "fd01::15"); nmtst_assert_ip6_address(&plnk->remote, "fd01::16"); g_assert_cmpint(plnk->ttl, ==, 0); g_assert_cmpint(plnk->tclass, ==, 20); g_assert_cmpint(plnk->encap_limit, ==, 6); g_assert_cmpint(plnk->flow_label, ==, 1337); g_assert_cmpint(plnk->proto, ==, IPPROTO_IPV6); break; case 1: g_assert(plnk == nm_platform_link_get_lnk_ip6tnl(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip6_address(&plnk->local, "fd01::17"); nmtst_assert_ip6_address(&plnk->remote, "fd01::18"); g_assert_cmpint(plnk->ttl, ==, 0); g_assert_cmpint(plnk->flow_label, ==, 1338); g_assert_cmpint(plnk->proto, ==, IPPROTO_IPV6); g_assert_cmpint(plnk->flags & 0xFFFF, /* ignore kernel internal flags */ ==, IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS); break; } break; } case NM_LINK_TYPE_IP6GRE: { const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; switch (test_data->test_mode) { case 0: g_assert(plnk == nm_platform_link_get_lnk_ip6gre(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip6_address(&plnk->local, "fd01::43"); nmtst_assert_ip6_address(&plnk->remote, "fd01::aaaa"); g_assert_cmpint(plnk->tclass, ==, 21); g_assert_cmpint(plnk->flow_label, ==, 1338); g_assert_cmpint(plnk->is_gre, ==, TRUE); g_assert_cmpint(plnk->is_tap, ==, FALSE); break; case 1: g_assert(plnk == nm_platform_link_get_lnk_ip6gre(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip6_address(&plnk->local, "fd01::44"); nmtst_assert_ip6_address(&plnk->remote, "fd01::aaab"); g_assert_cmpint(plnk->flow_label, ==, 1339); g_assert_cmpint(plnk->is_gre, ==, TRUE); g_assert_cmpint(plnk->is_tap, ==, FALSE); g_assert_cmpint(plnk->flags & 0xFFFF, /* ignore kernel internal flags */ ==, IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS); break; } break; } case NM_LINK_TYPE_IP6GRETAP: { const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; switch (test_data->test_mode) { case 0: g_assert(plnk == nm_platform_link_get_lnk_ip6gretap(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip6_address(&plnk->local, "fe80::abcd"); nmtst_assert_ip6_address(&plnk->remote, "fc01::bbbb"); g_assert_cmpint(plnk->ttl, ==, 10); g_assert_cmpint(plnk->tclass, ==, 23); g_assert_cmpint(plnk->flow_label, ==, 1340); g_assert_cmpint(plnk->is_gre, ==, TRUE); g_assert_cmpint(plnk->is_tap, ==, TRUE); break; case 1: g_assert(plnk == nm_platform_link_get_lnk_ip6gretap(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip6_address(&plnk->local, "fe80::abce"); nmtst_assert_ip6_address(&plnk->remote, "fc01::bbbc"); g_assert_cmpint(plnk->ttl, ==, 10); g_assert_cmpint(plnk->flow_label, ==, 1341); g_assert_cmpint(plnk->is_gre, ==, TRUE); g_assert_cmpint(plnk->is_tap, ==, TRUE); g_assert_cmpint(plnk->flags & 0xFFFF, /* ignore kernel internal flags */ ==, IP6_TNL_F_IGN_ENCAP_LIMIT | IP6_TNL_F_USE_ORIG_TCLASS); break; } break; } case NM_LINK_TYPE_IPIP: { const NMPlatformLnkIpIp *plnk = &lnk->lnk_ipip; g_assert(plnk == nm_platform_link_get_lnk_ipip(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip4_address(plnk->local, "1.2.3.4"); nmtst_assert_ip4_address(plnk->remote, "5.6.7.8"); g_assert_cmpint(plnk->ttl, ==, 0); g_assert_cmpint(plnk->tos, ==, 32); g_assert_cmpint(plnk->path_mtu_discovery, ==, FALSE); break; } case NM_LINK_TYPE_MACVLAN: { const NMPlatformLnkMacvlan *plnk = &lnk->lnk_macvlan; g_assert(plnk == nm_platform_link_get_lnk_macvlan(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->no_promisc, ==, FALSE); g_assert_cmpint(plnk->mode, ==, MACVLAN_MODE_BRIDGE); break; } case NM_LINK_TYPE_MACVTAP: { const NMPlatformLnkMacvlan *plnk = &lnk->lnk_macvlan; g_assert(plnk == nm_platform_link_get_lnk_macvtap(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->no_promisc, ==, FALSE); g_assert_cmpint(plnk->mode, ==, MACVLAN_MODE_PRIVATE); break; } case NM_LINK_TYPE_SIT: { const NMPlatformLnkSit *plnk = &lnk->lnk_sit; g_assert(plnk == nm_platform_link_get_lnk_sit(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, ==, ifindex_parent); nmtst_assert_ip4_address(plnk->local, "192.168.200.1"); nmtst_assert_ip4_address(plnk->remote, "172.25.100.14"); g_assert_cmpint(plnk->ttl, ==, 0); g_assert_cmpint(plnk->tos, ==, 31); g_assert_cmpint(plnk->path_mtu_discovery, ==, FALSE); break; } case NM_LINK_TYPE_TUN: { const NMPlatformLnkTun *plnk; NMPlatformLnkTun lnk_tun2; g_assert((lnk ? &lnk->lnk_tun : NULL) == nm_platform_link_get_lnk_tun(NM_PLATFORM_GET, ifindex, NULL)); /* kernel might not expose tun options via netlink. Either way, try * to read them (either from platform cache, or fallback to sysfs). * See also: rh#1547213. */ if (!nm_platform_link_tun_get_properties(NM_PLATFORM_GET, ifindex, &lnk_tun2)) g_assert_not_reached(); plnk = lnk ? &lnk->lnk_tun : &lnk_tun2; if (lnk) g_assert(memcmp(plnk, &lnk_tun2, sizeof(NMPlatformLnkTun)) == 0); if (i_step == 0) { /* Before we upped the device for the first time the kernel didn't notify * us of the owner set after the link creation: * https://bugzilla.redhat.com/show_bug.cgi?id=1566062 */ break; } g_assert(nm_platform_lnk_tun_cmp(plnk, &lnk_tun) == 0); break; } case NM_LINK_TYPE_VLAN: { const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; g_assert(plnk == nm_platform_link_get_lnk_vlan(NM_PLATFORM_GET, ifindex, NULL)); switch (test_data->test_mode) { case 0: g_assert_cmpint(plnk->id, ==, 1242); g_assert_cmpint(plnk->protocol, ==, ETH_P_8021Q); g_assert_cmpint(plnk->flags, ==, _NM_VLAN_FLAG_REORDER_HEADERS); break; case 1: g_assert_cmpint(plnk->id, ==, 4094); g_assert_cmpint(plnk->protocol, ==, ETH_P_8021AD); g_assert_cmpint(plnk->flags, ==, _NM_VLAN_FLAG_GVRP | _NM_VLAN_FLAG_MVRP); break; } break; } case NM_LINK_TYPE_VRF: { const NMPlatformLnkVrf *plnk = &lnk->lnk_vrf; g_assert(plnk == nm_platform_link_get_lnk_vrf(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->table, ==, 9876); break; } case NM_LINK_TYPE_VTI: { const NMPlatformLnkVti *plnk = &lnk->lnk_vti; g_assert(plnk == nm_platform_link_get_lnk_vti(NM_PLATFORM_GET, ifindex, NULL)); g_assert(nm_platform_lnk_vti_cmp(plnk, &lnk_vti) == 0); break; } case NM_LINK_TYPE_VTI6: { const NMPlatformLnkVti6 *plnk = &lnk->lnk_vti6; g_assert(plnk == nm_platform_link_get_lnk_vti6(NM_PLATFORM_GET, ifindex, NULL)); g_assert(nm_platform_lnk_vti6_cmp(plnk, &lnk_vti6) == 0); break; } case NM_LINK_TYPE_VXLAN: { const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan; g_assert(plnk == nm_platform_link_get_lnk_vxlan(NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint(plnk->parent_ifindex, !=, 0); g_assert_cmpint(plnk->tos, ==, 0); g_assert_cmpint(plnk->learning, ==, TRUE); g_assert_cmpint(plnk->limit, ==, 0); g_assert_cmpint(plnk->proxy, ==, FALSE); g_assert_cmpint(plnk->rsc, ==, FALSE); g_assert_cmpint(plnk->l2miss, ==, FALSE); g_assert_cmpint(plnk->l3miss, ==, FALSE); switch (test_data->test_mode) { case 0: g_assert_cmpint(plnk->id, ==, 42); nmtst_assert_ip4_address(plnk->local, "23.1.2.164"); nmtst_assert_ip4_address(plnk->group, "239.1.2.134"); nmtst_assert_ip6_address(&plnk->group6, "::"); nmtst_assert_ip6_address(&plnk->local6, "::"); g_assert_cmpint(plnk->ttl, ==, 0); g_assert_cmpint(plnk->ageing, ==, 1245); g_assert_cmpint(plnk->dst_port, ==, 4789); if (plnk->src_port_min != 0 || plnk->src_port_max != 0) { /* on some kernels, omitting the port range results in setting * following default port range. */ g_assert_cmpint(plnk->src_port_min, ==, 32768); g_assert_cmpint(plnk->src_port_max, ==, 61000); } break; case 1: g_assert_cmpint(plnk->id, ==, 11214423); nmtst_assert_ip4_address(plnk->local, "0.0.0.0"); nmtst_assert_ip4_address(plnk->group, "0.0.0.0"); nmtst_assert_ip6_address(&plnk->group6, "ff0e::115"); nmtst_assert_ip6_address(&plnk->local6, "1:2:3:4:334:23::23"); g_assert_cmpint(plnk->ageing, ==, 3245); g_assert_cmpint(plnk->dst_port, ==, 57412); g_assert_cmpint(plnk->ttl, ==, 32); g_assert_cmpint(plnk->src_port_min, ==, 1000); g_assert_cmpint(plnk->src_port_max, ==, 1003); break; } break; } case NM_LINK_TYPE_WIREGUARD: { const NMPlatformLnkWireGuard *plnk = &lnk->lnk_wireguard; g_assert(plnk == nm_platform_link_get_lnk_wireguard(NM_PLATFORM_GET, ifindex, NULL)); if (plink->n_ifi_flags & IFF_UP) { _test_wireguard_change(NM_PLATFORM_GET, plink->ifindex, test_data->test_mode); if (_LOGD_ENABLED()) _system("WG_HIDE_KEYS=never wg show all"); } break; } default: g_assert_not_reached(); } } nmtstp_link_delete(NULL, -1, ifindex, DEVICE_NAME, TRUE); out_delete_parent: nmtstp_link_delete(NULL, -1, ifindex_parent, PARENT_NAME, TRUE); } static void test_software_detect_add(const char *testpath, NMLinkType link_type, int test_mode) { TestAddSoftwareDetectData *test_data; char *path; test_data = g_new0(TestAddSoftwareDetectData, 1); test_data->link_type = link_type; test_data->test_mode = test_mode; test_data->external_command = TRUE; path = g_strdup_printf("%s/external", testpath); g_test_add_data_func_full(path, test_data, test_software_detect, g_free); g_free(path); test_data = g_new0(TestAddSoftwareDetectData, 1); test_data->link_type = link_type; test_data->test_mode = test_mode; test_data->external_command = FALSE; path = g_strdup_printf("%s/platform", testpath); g_test_add_data_func_full(path, test_data, test_software_detect, g_free); g_free(path); test_data = g_new0(TestAddSoftwareDetectData, 1); test_data->link_type = link_type; test_data->test_mode = test_mode; test_data->external_command = -1; path = g_strdup_printf("%s/random", testpath); g_test_add_data_func_full(path, test_data, test_software_detect, g_free); g_free(path); } /*****************************************************************************/ static void _assert_xgress_qos_mappings_impl(int ifindex, gboolean is_ingress_map, int n_entries, int n, ...) { const NMPlatformLink *plink; const NMPObject *lnk; guint n_map; const NMVlanQosMapping *map; va_list ap; guint i; lnk = nm_platform_link_get_lnk(NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink); g_assert(plink); g_assert_cmpint(plink->ifindex, ==, ifindex); g_assert(lnk); g_assert(&lnk->lnk_vlan == nm_platform_link_get_lnk_vlan(NM_PLATFORM_GET, ifindex, NULL)); if (nmtst_is_debug()) nmtstp_run_command_check("ip -d link show %s", plink->name); if (is_ingress_map) { map = lnk->_lnk_vlan.ingress_qos_map; n_map = lnk->_lnk_vlan.n_ingress_qos_map; } else { map = lnk->_lnk_vlan.egress_qos_map; n_map = lnk->_lnk_vlan.n_egress_qos_map; } if (n_entries != -1) g_assert_cmpint(n_map, ==, n_entries); for (i = 0; i < n_map; i++) { if (is_ingress_map) { g_assert_cmpint(map[i].from, >=, 0); g_assert_cmpint(map[i].from, <=, 7); } if (i > 0) g_assert_cmpint(map[i - 1].from, <, map[i].from); } va_start(ap, n); for (; n > 0; n--) { gboolean found = FALSE; guint from = va_arg(ap, guint); guint to = va_arg(ap, guint); for (i = 0; i < n_map; i++) { if (map[i].from == from) { g_assert(!found); found = TRUE; g_assert(map[i].to == to); } } g_assert(found); } va_end(ap); } #define _assert_xgress_qos_mappings(ifindex, is_ingress_map, n_entries, ...) \ _assert_xgress_qos_mappings_impl( \ (ifindex), \ (is_ingress_map), \ (n_entries), \ (G_STATIC_ASSERT_EXPR((NM_NARG(__VA_ARGS__) % 2) == 0), NM_NARG(__VA_ARGS__) / 2), \ __VA_ARGS__) #define _assert_ingress_qos_mappings(ifindex, n_entries, ...) \ _assert_xgress_qos_mappings(ifindex, TRUE, n_entries, __VA_ARGS__) #define _assert_egress_qos_mappings(ifindex, n_entries, ...) \ _assert_xgress_qos_mappings(ifindex, FALSE, n_entries, __VA_ARGS__) static void _assert_vlan_flags(int ifindex, _NMVlanFlags flags) { const NMPlatformLnkVlan *plnk; plnk = nm_platform_link_get_lnk_vlan(NM_PLATFORM_GET, ifindex, NULL); g_assert(plnk); g_assert_cmpint(plnk->flags, ==, flags); } static void test_vlan_set_xgress(void) { int ifindex, ifindex_parent; nmtstp_run_command_check("ip link add %s type dummy", PARENT_NAME); ifindex_parent = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; nmtstp_run_command_check("ip link add name %s link %s type vlan id 1245", DEVICE_NAME, PARENT_NAME); ifindex = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex; /* ingress-qos-map */ g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 4, 5)); _assert_ingress_qos_mappings(ifindex, 1, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 3, 7)); _assert_ingress_qos_mappings(ifindex, 2, 3, 7, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 3, 8)); _assert_ingress_qos_mappings(ifindex, 2, 3, 8, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 0, 4)); _assert_ingress_qos_mappings(ifindex, 3, 0, 4, 3, 8, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32)); _assert_ingress_qos_mappings(ifindex, 3, 0, G_MAXUINT32, 3, 8, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32 - 1)); _assert_ingress_qos_mappings(ifindex, 3, 0, G_MAXUINT32 - 1, 3, 8, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 0, 5)); _assert_ingress_qos_mappings(ifindex, 3, 0, 5, 3, 8, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 0, 5)); _assert_ingress_qos_mappings(ifindex, 3, 0, 5, 3, 8, 4, 5); /* Set invalid values: */ g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 8, 3)); _assert_ingress_qos_mappings(ifindex, 3, 0, 5, 3, 8, 4, 5); g_assert(nm_platform_link_vlan_set_ingress_map(NM_PLATFORM_GET, ifindex, 9, 4)); _assert_ingress_qos_mappings(ifindex, 3, 0, 5, 3, 8, 4, 5); /* egress-qos-map */ g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 7, 3)); _assert_egress_qos_mappings(ifindex, 1, 7, 3); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 8, 4)); _assert_egress_qos_mappings(ifindex, 2, 7, 3, 8, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 0, 4)); _assert_egress_qos_mappings(ifindex, 3, 0, 4, 7, 3, 8, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 1, 4)); _assert_egress_qos_mappings(ifindex, 4, 0, 4, 1, 4, 7, 3, 8, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 1, 5)); _assert_egress_qos_mappings(ifindex, 4, 0, 4, 1, 5, 7, 3, 8, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 9, 5)); _assert_egress_qos_mappings(ifindex, 5, 0, 4, 1, 5, 7, 3, 8, 4, 9, 5); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 8, 5)); _assert_egress_qos_mappings(ifindex, 5, 0, 4, 1, 5, 7, 3, 8, 5, 9, 5); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 8, 0)); _assert_egress_qos_mappings(ifindex, 4, 0, 4, 1, 5, 7, 3, 9, 5); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 0, 0)); _assert_egress_qos_mappings(ifindex, 3, 1, 5, 7, 3, 9, 5); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 100, 4)); _assert_egress_qos_mappings(ifindex, 4, 1, 5, 7, 3, 9, 5, 100, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, G_MAXUINT32, 4)); _assert_egress_qos_mappings(ifindex, 5, 1, 5, 7, 3, 9, 5, 100, 4, G_MAXUINT32, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, G_MAXUINT32, 8)); _assert_egress_qos_mappings(ifindex, 5, 1, 5, 7, 3, 9, 5, 100, 4, G_MAXUINT32, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, G_MAXUINT32, 0)); _assert_egress_qos_mappings(ifindex, 4, 1, 5, 7, 3, 9, 5, 100, 4); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 100, 0)); _assert_egress_qos_mappings(ifindex, 3, 1, 5, 7, 3, 9, 5); g_assert(nm_platform_link_vlan_set_egress_map(NM_PLATFORM_GET, ifindex, 1, 0)); _assert_egress_qos_mappings(ifindex, 2, 7, 3, 9, 5); { const NMVlanQosMapping ingress_map[] = { {.from = 1, .to = 5}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, TRUE, ingress_map, G_N_ELEMENTS(ingress_map), FALSE, NULL, 0)); _assert_ingress_qos_mappings(ifindex, 1, 1, 5); } { const NMVlanQosMapping ingress_map[] = { {.from = 3, .to = 5}, {.from = 7, .to = 1655}, {.from = 7, .to = 17655}, {.from = 5, .to = 754}, {.from = 4, .to = 12}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, TRUE, ingress_map, G_N_ELEMENTS(ingress_map), FALSE, NULL, 0)); _assert_ingress_qos_mappings(ifindex, 4, 3, 5, 4, 12, 7, 17655, 5, 754); } { const NMVlanQosMapping ingress_map[] = { {.from = 3, .to = 18}, {.from = 6, .to = 121}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, FALSE, ingress_map, G_N_ELEMENTS(ingress_map), FALSE, NULL, 0)); _assert_ingress_qos_mappings(ifindex, 5, 3, 18, 4, 12, 6, 121, 7, 17655, 5, 754); } { const NMVlanQosMapping ingress_map[] = { {.from = 3, .to = 0}, {.from = 6, .to = 7}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, TRUE, ingress_map, G_N_ELEMENTS(ingress_map), FALSE, NULL, 0)); _assert_ingress_qos_mappings(ifindex, 1, 6, 7); } { const NMVlanQosMapping ingress_map[] = { {.from = 1, .to = 5}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, TRUE, ingress_map, G_N_ELEMENTS(ingress_map), FALSE, NULL, 0)); _assert_ingress_qos_mappings(ifindex, 1, 1, 5); } { const NMVlanQosMapping egress_map[] = { {.from = 5, .to = 1}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, FALSE, NULL, 0, TRUE, egress_map, G_N_ELEMENTS(egress_map))); _assert_egress_qos_mappings(ifindex, 1, 5, 1); } { const NMVlanQosMapping egress_map[] = { {.from = 5, .to = 3}, {.from = 1655, .to = 5}, {.from = 1655, .to = 7}, {.from = G_MAXUINT32, .to = 6}, {.from = G_MAXUINT32, .to = 8}, {.from = 754, .to = 4}, {.from = 3, .to = 2}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, FALSE, NULL, 0, TRUE, egress_map, G_N_ELEMENTS(egress_map))); _assert_egress_qos_mappings(ifindex, 5, 3, 2, 5, 3, 754, 4, 1655, 7, G_MAXUINT32, 6); } { const NMVlanQosMapping egress_map[] = { {.from = 754, .to = 3}, {.from = 755, .to = 8}, {.from = 1655, .to = 0}, {.from = 6, .to = 1}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, FALSE, NULL, 0, FALSE, egress_map, G_N_ELEMENTS(egress_map))); _assert_egress_qos_mappings(ifindex, 5, 3, 2, 5, 3, 6, 1, 754, 3, G_MAXUINT32, 6); } { const NMVlanQosMapping egress_map[] = { {.from = 6, .to = 0}, {.from = 3, .to = 4}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, FALSE, NULL, 0, TRUE, egress_map, G_N_ELEMENTS(egress_map))); _assert_egress_qos_mappings(ifindex, 1, 3, 4); } { const NMVlanQosMapping egress_map[] = { {.from = 1, .to = 5}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, 0, 0, FALSE, NULL, 0, TRUE, egress_map, G_N_ELEMENTS(egress_map))); _assert_egress_qos_mappings(ifindex, 1, 1, 5); } { const NMVlanQosMapping ingress_map[] = { {.from = 6, .to = 145}, {.from = 4, .to = 1}, {.from = 6, .to = 12}, }; const NMVlanQosMapping egress_map[] = { {.from = 1, .to = 5}, {.from = 3232, .to = 7}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, _NM_VLAN_FLAG_REORDER_HEADERS | _NM_VLAN_FLAG_GVRP, _NM_VLAN_FLAG_REORDER_HEADERS, TRUE, ingress_map, G_N_ELEMENTS(ingress_map), TRUE, egress_map, G_N_ELEMENTS(egress_map))); _assert_ingress_qos_mappings(ifindex, 2, 4, 1, 6, 12); _assert_egress_qos_mappings(ifindex, 2, 1, 5, 3232, 7); _assert_vlan_flags(ifindex, _NM_VLAN_FLAG_REORDER_HEADERS); } { const NMVlanQosMapping ingress_map[] = { {.from = 6, .to = 145}, {.from = 4, .to = 1}, {.from = 6, .to = 12}, }; const NMVlanQosMapping egress_map[] = { {.from = 1, .to = 7}, {.from = 64, .to = 10}, {.from = 64, .to = 10}, {.from = 64, .to = 10}, {.from = 64, .to = 10}, {.from = 3232, .to = 0}, {.from = 64, .to = 4}, }; g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, _NM_VLAN_FLAG_GVRP, _NM_VLAN_FLAG_GVRP, FALSE, ingress_map, G_N_ELEMENTS(ingress_map), FALSE, egress_map, G_N_ELEMENTS(egress_map))); _assert_ingress_qos_mappings(ifindex, 2, 4, 1, 6, 12); _assert_egress_qos_mappings(ifindex, 2, 1, 7, 64, 4); _assert_vlan_flags(ifindex, _NM_VLAN_FLAG_REORDER_HEADERS | _NM_VLAN_FLAG_GVRP); } nmtstp_link_delete(NULL, -1, ifindex, DEVICE_NAME, TRUE); nmtstp_link_delete(NULL, -1, ifindex_parent, PARENT_NAME, TRUE); } /*****************************************************************************/ static void test_link_set_properties(void) { const NMPlatformLink *link; NMPlatformLinkProps props; NMPlatformLinkChangeFlags flags; int ifindex; props = (NMPlatformLinkProps){ .tx_queue_length = 599, .gso_max_size = 10001, .gso_max_segments = 512, }; flags = NM_PLATFORM_LINK_CHANGE_TX_QUEUE_LENGTH | NM_PLATFORM_LINK_CHANGE_GSO_MAX_SIZE | NM_PLATFORM_LINK_CHANGE_GSO_MAX_SEGMENTS; ifindex = nmtstp_link_dummy_add(NM_PLATFORM_GET, FALSE, "dummy1")->ifindex; g_assert(nm_platform_link_change(NM_PLATFORM_GET, ifindex, &props, NULL, flags)); link = nmtstp_link_get(NM_PLATFORM_GET, ifindex, "dummy1"); g_assert(link); g_assert_cmpint(link->link_props.tx_queue_length, ==, 599); g_assert_cmpint(link->link_props.gso_max_size, ==, 10001); g_assert_cmpint(link->link_props.gso_max_segments, ==, 512); nmtstp_link_delete(NULL, -1, link->ifindex, "dummy1", TRUE); } /*****************************************************************************/ static void test_create_many_links_do(guint n_devices) { gint64 time, start_time = nm_utils_get_monotonic_timestamp_nsec(); guint i; char name[64]; const NMPlatformLink *pllink; gs_unref_array GArray *ifindexes = g_array_sized_new(FALSE, FALSE, sizeof(int), n_devices); const int EX = ((int) (nmtst_get_rand_uint32() % 4)) - 1; g_assert(EX >= -1 && EX <= 2); _LOGI(">>> create devices (EX=%d)...", EX); for (i = 0; i < n_devices; i++) { nm_sprintf_buf(name, "t-%05u", i); if (EX == 2) { /* This mode is different from letting nmtstp_link_dummy_add() * because in this case we don't process any platform events * while adding all the links. */ nmtstp_run_command_check("ip link add %s type dummy", name); } else nmtstp_link_dummy_add(NULL, EX, name); } _LOGI(">>> process events after creating devices..."); nm_platform_process_events(NM_PLATFORM_GET); _LOGI(">>> check devices..."); for (i = 0; i < n_devices; i++) { nm_sprintf_buf(name, "t-%05u", i); pllink = nm_platform_link_get_by_ifname(NM_PLATFORM_GET, name); g_assert(pllink); g_assert_cmpint(pllink->type, ==, NM_LINK_TYPE_DUMMY); g_assert_cmpstr(pllink->name, ==, name); g_array_append_val(ifindexes, pllink->ifindex); } _LOGI(">>> delete devices..."); g_assert_cmpint(ifindexes->len, ==, n_devices); for (i = 0; i < n_devices; i++) { nm_sprintf_buf(name, "t-%05u", i); if (EX == 2) nmtstp_run_command_check("ip link delete %s", name); else nmtstp_link_delete(NULL, EX, nm_g_array_index(ifindexes, int, i), name, TRUE); } _LOGI(">>> process events after deleting devices..."); nm_platform_process_events(NM_PLATFORM_GET); time = nm_utils_get_monotonic_timestamp_nsec() - start_time; _LOGI(">>> finished in %ld.%09ld seconds", (long) (time / NM_UTILS_NSEC_PER_SEC), (long) (time % NM_UTILS_NSEC_PER_SEC)); } static void test_create_many_links(gconstpointer user_data) { guint n_devices = GPOINTER_TO_UINT(user_data); if (n_devices > 100 && nmtst_test_quick()) { g_print("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", g_get_prgname() ?: "test-link-linux"); g_test_skip("Skip long running test"); return; } test_create_many_links_do(n_devices); } /*****************************************************************************/ static void test_nl_bugs_veth(void) { const char *IFACE_VETH0 = "nm-test-veth0"; const char *IFACE_VETH1 = "nm-test-veth1"; int ifindex_veth0, ifindex_veth1; int i; const NMPlatformLink *pllink_veth0, *pllink_veth1; gs_free_error GError *error = NULL; NMTstpNamespaceHandle *ns_handle = NULL; NMPLinkAddress hw_perm_addr; /* create veth pair. */ ifindex_veth0 = nmtstp_link_veth_add(NM_PLATFORM_GET, -1, IFACE_VETH0, IFACE_VETH1)->ifindex; ifindex_veth1 = nmtstp_link_get_typed(NM_PLATFORM_GET, -1, IFACE_VETH1, NM_LINK_TYPE_VETH)->ifindex; /* assert that nm_platform_link_veth_get_properties() returns the expected peer ifindexes. */ g_assert(nm_platform_link_veth_get_properties(NM_PLATFORM_GET, ifindex_veth0, &i)); g_assert_cmpint(i, ==, ifindex_veth1); g_assert(nm_platform_link_veth_get_properties(NM_PLATFORM_GET, ifindex_veth1, &i)); g_assert_cmpint(i, ==, ifindex_veth0); /* assert that NMPlatformLink.parent is the peer-ifindex. */ pllink_veth0 = nm_platform_link_get(NM_PLATFORM_GET, ifindex_veth0); g_assert(pllink_veth0); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink_veth0, &hw_perm_addr)); if (pllink_veth0->parent == 0) { /* Kernels prior to 4.1 dated 21 June, 2015 don't support exposing the veth peer * as IFA_LINK. skip the remainder of the test. */ goto out; } g_assert_cmpint(pllink_veth0->parent, ==, ifindex_veth1); /* The following tests whether we have a workaround for kernel bug * https://bugzilla.redhat.com/show_bug.cgi?id=1285827 in place. */ pllink_veth1 = nm_platform_link_get(NM_PLATFORM_GET, ifindex_veth1); g_assert(pllink_veth1); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink_veth1, &hw_perm_addr)); g_assert_cmpint(pllink_veth1->parent, ==, ifindex_veth0); /* move one veth peer to another namespace and check that the * parent/IFLA_LINK of the remaining peer properly updates * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ ns_handle = nmtstp_namespace_create(CLONE_NEWNET, &error); g_assert_no_error(error); g_assert(ns_handle); nmtstp_run_command_check("ip link set %s netns %ld", IFACE_VETH1, (long) nmtstp_namespace_handle_get_pid(ns_handle)); NMTST_WAIT_ASSERT(100, { nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); nm_platform_process_events(NM_PLATFORM_GET); pllink_veth1 = nm_platform_link_get(NM_PLATFORM_GET, ifindex_veth1); pllink_veth0 = nm_platform_link_get(NM_PLATFORM_GET, ifindex_veth0); if (!pllink_veth1 && pllink_veth0 && pllink_veth0->parent == NM_PLATFORM_LINK_OTHER_NETNS) { break; } }); out: nmtstp_link_delete(NULL, -1, ifindex_veth0, IFACE_VETH0, TRUE); g_assert(!nmtstp_link_get(NM_PLATFORM_GET, ifindex_veth0, IFACE_VETH0)); g_assert(!nmtstp_link_get(NM_PLATFORM_GET, ifindex_veth1, IFACE_VETH1)); nmtstp_namespace_handle_release(ns_handle); } /*****************************************************************************/ static void test_nl_bugs_spuroius_newlink(void) { const char *IFACE_BOND0 = "nm-test-bond0"; const char *IFACE_DUMMY0 = "nm-test-dummy0"; int ifindex_bond0, ifindex_dummy0; const NMPlatformLink *pllink; NMPLinkAddress hw_perm_addr; gboolean wait_for_settle; /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ nmtstp_run_command_check("ip link add %s type dummy", IFACE_DUMMY0); ifindex_dummy0 = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100) ->ifindex; nmtstp_run_command_check("ip link add %s type bond", IFACE_BOND0); ifindex_bond0 = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex; nmtstp_link_set_updown(NULL, -1, ifindex_bond0, TRUE); nmtstp_run_command_check("ip link set %s master %s", IFACE_DUMMY0, IFACE_BOND0); NMTST_WAIT_ASSERT(100, { nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_dummy0); g_assert(pllink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (pllink->master == ifindex_bond0) break; }); nmtstp_run_command_check("ip link del %s", IFACE_BOND0); wait_for_settle = TRUE; nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); again: nm_platform_process_events(NM_PLATFORM_GET); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_bond0); g_assert(!pllink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (wait_for_settle) { wait_for_settle = FALSE; NMTST_WAIT(300, { nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); }); goto again; } g_assert(!nmtstp_link_get(NM_PLATFORM_GET, ifindex_bond0, IFACE_BOND0)); nmtstp_link_delete(NULL, -1, ifindex_dummy0, IFACE_DUMMY0, TRUE); } /*****************************************************************************/ static void test_nl_bugs_spuroius_dellink(void) { const char *IFACE_BRIDGE0 = "nm-test-bridge0"; const char *IFACE_DUMMY0 = "nm-test-dummy0"; int ifindex_bridge0, ifindex_dummy0; const NMPlatformLink *pllink; NMPLinkAddress hw_perm_addr; gboolean wait_for_settle; /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ nmtstp_run_command_check("ip link add %s type dummy", IFACE_DUMMY0); ifindex_dummy0 = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100) ->ifindex; nmtstp_run_command_check("ip link add %s type bridge", IFACE_BRIDGE0); ifindex_bridge0 = nmtstp_assert_wait_for_link(NM_PLATFORM_GET, IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100) ->ifindex; nmtstp_link_set_updown(NULL, -1, ifindex_bridge0, TRUE); nmtstp_run_command_check("ip link set %s master %s", IFACE_DUMMY0, IFACE_BRIDGE0); NMTST_WAIT_ASSERT(100, { nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_dummy0); g_assert(pllink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (pllink->master == ifindex_bridge0) break; }); nm_platform_process_events(NM_PLATFORM_GET); nmtstp_run_command_check("ip link set %s nomaster", IFACE_DUMMY0); wait_for_settle = TRUE; nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); again: nm_platform_process_events(NM_PLATFORM_GET); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_bridge0); g_assert(pllink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_dummy0); g_assert(pllink); g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); g_assert_cmpint(pllink->parent, ==, 0); if (wait_for_settle) { wait_for_settle = FALSE; NMTST_WAIT(300, { nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); }); goto again; } nmtstp_link_delete(NULL, -1, ifindex_bridge0, IFACE_BRIDGE0, TRUE); nmtstp_link_delete(NULL, -1, ifindex_dummy0, IFACE_DUMMY0, TRUE); } /*****************************************************************************/ static void _test_netns_setup(gpointer fixture, gconstpointer test_data) { /* the singleton platform instance has netns support disabled. * Destroy the instance before the test and re-create it afterwards. */ g_object_unref(NM_PLATFORM_GET); } static void _test_netns_teardown(gpointer fixture, gconstpointer test_data) { nmtstp_setup_platform(); } static NMPlatform * _test_netns_create_platform(void) { NMPNetns *netns; NMPlatform *platform; netns = nmp_netns_new(); g_assert(NMP_IS_NETNS(netns)); platform = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); g_assert(NM_IS_LINUX_PLATFORM(platform)); nmp_netns_pop(netns); g_object_unref(netns); return platform; } static gboolean _test_netns_check_skip(void) { static int support = -1; static int support_errsv = 0; NMPNetns *netns; gs_unref_object NMPNetns *netns2 = NULL; netns = nmp_netns_get_current(); if (!netns) { g_test_skip("No netns support"); return TRUE; } g_assert(nmp_netns_get_fd_net(netns) > 0); if (support == -1) { support = (setns(nmp_netns_get_fd_net(netns), CLONE_NEWNET) == 0); if (!support) support_errsv = errno; } if (!support) { _LOGD("setns() failed with \"%s\". This indicates missing support (valgrind?)", nm_strerror_native(support_errsv)); g_test_skip("No netns support (setns failed)"); return TRUE; } netns2 = nmp_netns_new(); if (!netns2) { /* skip tests for https://bugzilla.gnome.org/show_bug.cgi?id=790214 */ g_assert_cmpint(errno, ==, EINVAL); g_test_skip("No netns support to create another netns"); return TRUE; } nmp_netns_pop(netns2); return FALSE; } static gboolean _check_sysctl_skip(void) { if (access("/proc/sys/net/ipv4/ip_forward", W_OK) == -1) { g_test_skip("Can not write sysctls"); return TRUE; } return FALSE; } /*****************************************************************************/ static void test_netns_general(gpointer fixture, gconstpointer test_data) { gs_unref_object NMPlatform *platform_1 = NULL; gs_unref_object NMPlatform *platform_2 = NULL; NMPNetns *netns_tmp; char sbuf[100]; int i, j, k; gboolean ethtool_support; NMPUtilsEthtoolDriverInfo driver_info; if (_test_netns_check_skip()) return; if (_check_sysctl_skip()) return; platform_1 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platform_2 = _test_netns_create_platform(); /* add some dummy devices. The "other-*" devices are there to bump the ifindex */ for (k = 0; k < 2; k++) { NMPlatform *p = (k == 0 ? platform_1 : platform_2); const char *id = (k == 0 ? "a" : "b"); for (i = 0, j = nmtst_get_rand_uint32() % 5; i < j; i++) _ADD_DUMMY(p, nm_sprintf_buf(sbuf, "other-a-%s-%02d", id, i)); _ADD_DUMMY(p, "dummy1_"); for (i = 0, j = nmtst_get_rand_uint32() % 5; i < j; i++) _ADD_DUMMY(p, nm_sprintf_buf(sbuf, "other-b-%s-%02d", id, i)); _ADD_DUMMY(p, nm_sprintf_buf(sbuf, "dummy2%s", id)); for (i = 0, j = nmtst_get_rand_uint32() % 5; i < j; i++) _ADD_DUMMY(p, nm_sprintf_buf(sbuf, "other-c-%s-%02d", id, i)); } _sysctl_assert_eq( platform_1, "/sys/devices/virtual/net/dummy1_/ifindex", nm_sprintf_buf( sbuf, "%d", nmtstp_link_get_typed(platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex)); _sysctl_assert_eq( platform_1, "/sys/devices/virtual/net/dummy2a/ifindex", nm_sprintf_buf( sbuf, "%d", nmtstp_link_get_typed(platform_1, 0, "dummy2a", NM_LINK_TYPE_DUMMY)->ifindex)); _sysctl_assert_eq(platform_1, "/sys/devices/virtual/net/dummy2b/ifindex", NULL); _sysctl_assert_eq( platform_2, "/sys/devices/virtual/net/dummy1_/ifindex", nm_sprintf_buf( sbuf, "%d", nmtstp_link_get_typed(platform_2, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex)); _sysctl_assert_eq(platform_2, "/sys/devices/virtual/net/dummy2a/ifindex", NULL); _sysctl_assert_eq( platform_2, "/sys/devices/virtual/net/dummy2b/ifindex", nm_sprintf_buf( sbuf, "%d", nmtstp_link_get_typed(platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex)); for (i = 0; i < 10; i++) { NMPlatform *pl; const char *path; j = nmtst_get_rand_uint32() % 2; if (nmtst_get_rand_uint32() % 2) { pl = platform_1; if (nmtst_get_rand_uint32() % 2) path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6"; else path = "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6"; } else { pl = platform_2; if (nmtst_get_rand_uint32() % 2) path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6"; else path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"; } g_assert(nm_platform_sysctl_set(pl, NMP_SYSCTL_PATHID_ABSOLUTE(path), nm_sprintf_buf(sbuf, "%d", j))); _sysctl_assert_eq(pl, path, nm_sprintf_buf(sbuf, "%d", j)); } _sysctl_assert_eq(platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6", NULL); _sysctl_assert_eq(platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6", NULL); /* Kernels prior to 3.19 dated 8 February, 2015 don't support ethtool -i for dummy devices. * Work around that and skip asserts that are known to fail. */ ethtool_support = nmtstp_run_command("ethtool -i dummy1_ > /dev/null") == 0; if (ethtool_support) { g_assert(nmp_utils_ethtool_get_driver_info( nmtstp_link_get_typed(platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); g_assert(nmp_utils_ethtool_get_driver_info( nmtstp_link_get_typed(platform_1, 0, "dummy2a", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); g_assert_cmpint(nmtstp_run_command("ethtool -i dummy1_ > /dev/null"), ==, 0); g_assert_cmpint(nmtstp_run_command("ethtool -i dummy2a > /dev/null"), ==, 0); g_assert_cmpint(nmtstp_run_command("ethtool -i dummy2b 2> /dev/null"), !=, 0); } g_assert(nm_platform_netns_push(platform_2, &netns_tmp)); if (ethtool_support) { g_assert(nmp_utils_ethtool_get_driver_info( nmtstp_link_get_typed(platform_2, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); g_assert(nmp_utils_ethtool_get_driver_info( nmtstp_link_get_typed(platform_2, 0, "dummy2b", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); g_assert_cmpint(nmtstp_run_command("ethtool -i dummy1_ > /dev/null"), ==, 0); g_assert_cmpint(nmtstp_run_command("ethtool -i dummy2a 2> /dev/null"), !=, 0); g_assert_cmpint(nmtstp_run_command("ethtool -i dummy2b > /dev/null"), ==, 0); } nmp_netns_pop(netns_tmp); } /*****************************************************************************/ static void test_netns_set_netns(gpointer fixture, gconstpointer test_data) { NMPlatform *platforms[3]; gs_unref_object NMPlatform *platform_0 = NULL; gs_unref_object NMPlatform *platform_1 = NULL; gs_unref_object NMPlatform *platform_2 = NULL; nm_auto_pop_netns NMPNetns *netns_pop = NULL; if (_test_netns_check_skip()) return; platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform(); platforms[2] = platform_2 = _test_netns_create_platform(); nmtstp_netns_select_random(platforms, G_N_ELEMENTS(platforms), &netns_pop); #define LINK_MOVE_NAME "link-move" g_assert(!nm_platform_link_get_by_ifname(platform_1, LINK_MOVE_NAME)); g_assert(!nm_platform_link_get_by_ifname(platform_2, LINK_MOVE_NAME)); _ADD_DUMMY(platform_1, LINK_MOVE_NAME); g_assert(nm_platform_link_get_by_ifname(platform_1, LINK_MOVE_NAME)); g_assert(!nm_platform_link_get_by_ifname(platform_2, LINK_MOVE_NAME)); g_assert(nm_platform_link_set_netns( platform_1, nm_platform_link_get_by_ifname(platform_1, LINK_MOVE_NAME)->ifindex, nmp_netns_get_fd_net(nm_platform_netns_get(platform_2)))); g_assert(!nm_platform_link_get_by_ifname(platform_1, LINK_MOVE_NAME)); g_assert(!nm_platform_link_get_by_ifname(platform_2, LINK_MOVE_NAME)); nmtstp_assert_wait_for_link(platform_2, LINK_MOVE_NAME, NM_LINK_TYPE_DUMMY, 100); g_assert(!nm_platform_link_get_by_ifname(platform_1, LINK_MOVE_NAME)); g_assert(nm_platform_link_get_by_ifname(platform_2, LINK_MOVE_NAME)); } /*****************************************************************************/ static char * _get_current_namespace_id(int ns_type) { const char *p; GError *error = NULL; char *id; switch (ns_type) { case CLONE_NEWNET: p = "/proc/self/ns/net"; break; case CLONE_NEWNS: p = "/proc/self/ns/mnt"; break; default: g_assert_not_reached(); } id = g_file_read_link(p, &error); g_assert_no_error(error); g_assert(id); return id; } static char * _get_sysctl_value(const char *path) { char *data = NULL; gs_free_error GError *error = NULL; if (!g_file_get_contents(path, &data, NULL, &error)) { nmtst_assert_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT, NULL); g_assert(!data); } else { g_assert_no_error(error); g_assert(data); g_strstrip(data); } return data; } static void test_netns_push(gpointer fixture, gconstpointer test_data) { gs_unref_object NMPlatform *platform_0 = NULL; gs_unref_object NMPlatform *platform_1 = NULL; gs_unref_object NMPlatform *platform_2 = NULL; nm_auto_pop_netns NMPNetns *netns_pop = NULL; gs_unref_ptrarray GPtrArray *device_names = g_ptr_array_new_with_free_func(g_free); int i, j; const int ns_types_list[] = {CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWNET | CLONE_NEWNS}; const int ns_types_test[] = {CLONE_NEWNET, CLONE_NEWNS}; typedef struct { NMPlatform *platform; const char *device_name; const char *sysctl_path; const char *sysctl_value; const char *ns_net; const char *ns_mnt; } PlatformData; PlatformData pl[3] = {}; PlatformData *pl_base; struct { PlatformData *pl; int ns_types; } stack[6] = {}; int nstack; if (_test_netns_check_skip()) return; if (_check_sysctl_skip()) return; pl[0].platform = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); pl[1].platform = platform_1 = _test_netns_create_platform(); pl[2].platform = platform_2 = _test_netns_create_platform(); pl_base = &pl[0]; i = nmtst_get_rand_uint32() % (G_N_ELEMENTS(pl) + 1); if (i < G_N_ELEMENTS(pl)) { pl_base = &pl[i]; g_assert(nm_platform_netns_push(pl[i].platform, &netns_pop)); } for (i = 0; i < G_N_ELEMENTS(pl); i++) { nm_auto_pop_netns NMPNetns *netns_free = NULL; char *tmp; g_assert(nm_platform_netns_push(pl[i].platform, &netns_free)); tmp = g_strdup_printf("nmtst-dev-%d", i); g_ptr_array_add(device_names, tmp); pl[i].device_name = tmp; tmp = g_strdup_printf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", pl[i].device_name); g_ptr_array_add(device_names, tmp); pl[i].sysctl_path = tmp; pl[i].sysctl_value = nmtst_get_rand_uint32() % 2 ? "1" : "0"; _ADD_DUMMY(pl[i].platform, pl[i].device_name); g_assert(nm_platform_sysctl_set(pl[i].platform, NMP_SYSCTL_PATHID_ABSOLUTE(pl[i].sysctl_path), pl[i].sysctl_value)); tmp = _get_current_namespace_id(CLONE_NEWNET); g_ptr_array_add(device_names, tmp); pl[i].ns_net = tmp; tmp = _get_current_namespace_id(CLONE_NEWNS); g_ptr_array_add(device_names, tmp); pl[i].ns_mnt = tmp; } nstack = nmtst_get_rand_uint32() % (G_N_ELEMENTS(stack) + 1); for (i = 0; i < nstack; i++) { stack[i].pl = &pl[nmtst_get_rand_uint32() % G_N_ELEMENTS(pl)]; stack[i].ns_types = ns_types_list[nmtst_get_rand_uint32() % G_N_ELEMENTS(ns_types_list)]; nmp_netns_push_type(nm_platform_netns_get(stack[i].pl->platform), stack[i].ns_types); } /* pop some again. */ for (i = nmtst_get_rand_uint32() % (nstack + 1); i > 0; i--) { g_assert(nstack > 0); nstack--; nmp_netns_pop(nm_platform_netns_get(stack[nstack].pl->platform)); } for (i = 0; i < G_N_ELEMENTS(ns_types_test); i++) { int ns_type = ns_types_test[i]; PlatformData *p; gs_free char *current_namespace_id = NULL; p = pl_base; for (j = nstack; j >= 1;) { j--; if (NM_FLAGS_ANY(stack[j].ns_types, ns_type)) { p = stack[j].pl; break; } } current_namespace_id = _get_current_namespace_id(ns_type); if (ns_type == CLONE_NEWNET) { g_assert_cmpstr(current_namespace_id, ==, p->ns_net); for (j = 0; j < G_N_ELEMENTS(pl); j++) { gs_free char *data = NULL; if (p == &pl[j]) g_assert_cmpint( nmtstp_run_command("ip link show %s 1>/dev/null", pl[j].device_name), ==, 0); else g_assert_cmpint( nmtstp_run_command("ip link show %s 2>/dev/null", pl[j].device_name), !=, 0); data = _get_sysctl_value(pl[j].sysctl_path); if (p == &pl[j]) g_assert_cmpstr(data, ==, pl[j].sysctl_value); else g_assert(!data); } } else if (ns_type == CLONE_NEWNS) { g_assert_cmpstr(current_namespace_id, ==, p->ns_mnt); for (j = 0; j < G_N_ELEMENTS(pl); j++) { char path[600]; gs_free char *data = NULL; nm_sprintf_buf(path, "/sys/devices/virtual/net/%s/ifindex", pl[j].device_name); data = _get_sysctl_value(path); if (p == &pl[j]) g_assert_cmpstr(data, ==, nm_sprintf_buf(path, "%d", nmtstp_link_get_typed(p->platform, 0, p->device_name, NM_LINK_TYPE_DUMMY) ->ifindex)); else g_assert(!data); } } else g_assert_not_reached(); } for (i = nstack; i >= 1;) { i--; nmp_netns_pop(nm_platform_netns_get(stack[i].pl->platform)); } } /*****************************************************************************/ static void test_netns_bind_to_path(gpointer fixture, gconstpointer test_data) { #define P_VAR_RUN "/run" #define P_VAR_RUN_NETNS "/run/netns" #define P_VAR_RUN_NETNS_BINDNAME "/run/netns/" P_NETNS_BINDNAME #define P_NETNS_BINDNAME "nmtst-iproute2-netns" gs_unref_object NMPlatform *platform_0 = NULL; gs_unref_object NMPlatform *platform_1 = NULL; gs_unref_object NMPlatform *platform_2 = NULL; nm_auto_pop_netns NMPNetns *netns_pop = NULL; NMPlatform *platforms[3]; NMPNetns *netns; int i; if (_test_netns_check_skip()) return; platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform(); platforms[2] = platform_2 = _test_netns_create_platform(); nmtstp_netns_select_random(platforms, G_N_ELEMENTS(platforms), &netns_pop); g_assert_cmpint( mount("tmpfs", P_VAR_RUN, "tmpfs", MS_NOATIME | MS_NODEV | MS_NOSUID, "mode=0755,size=32K"), ==, 0); g_assert_cmpint(mkdir(P_VAR_RUN_NETNS, 755), ==, 0); i = (nmtst_get_rand_uint32() % 2) + 1; netns = nm_platform_netns_get(platforms[i]); _ADD_DUMMY(platforms[i], "dummy2b"); g_assert(!g_file_test(P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS)); g_assert_cmpint(nmtstp_run_command("ip netns exec " P_NETNS_BINDNAME " true 2>/dev/null"), !=, 0); g_assert(nmp_netns_bind_to_path(netns, P_VAR_RUN_NETNS_BINDNAME, NULL)); g_assert(g_file_test(P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS)); g_assert_cmpint(nmtstp_run_command("ip netns exec " P_NETNS_BINDNAME " true"), ==, 0); g_assert_cmpint( nmtstp_run_command("ip netns exec " P_NETNS_BINDNAME " ip link show dummy2b 1>/dev/null"), ==, 0); g_assert(nmp_netns_bind_to_path_destroy(netns, P_VAR_RUN_NETNS_BINDNAME)); g_assert(!g_file_test(P_VAR_RUN_NETNS_BINDNAME, G_FILE_TEST_EXISTS)); g_assert_cmpint(nmtstp_run_command("ip netns exec " P_NETNS_BINDNAME " true 2>/dev/null"), !=, 0); g_assert_cmpint(umount(P_VAR_RUN), ==, 0); } /*****************************************************************************/ static void test_sysctl_rename(void) { NMPlatform *const PL = NM_PLATFORM_GET; const char *const IFNAME[3] = { "nm-dummy-0", "nm-dummy-1", "nm-dummy-2", }; int ifindex[G_N_ELEMENTS(IFNAME)] = {0}; nm_auto_close int dirfd = -1; int i; char ifname_buf[IFNAMSIZ]; char *s; const NMPlatformLink *pllink; ifindex[0] = nmtstp_link_dummy_add(PL, -1, IFNAME[0])->ifindex; ifindex[1] = nmtstp_link_dummy_add(PL, -1, IFNAME[1])->ifindex; s = (nmtst_get_rand_uint32() % 2) ? NULL : ifname_buf; if (nmtst_get_rand_uint32() % 2) { /* bring the platform cache out of sync */ nmtstp_run_command_check("ip link set %s name %s", IFNAME[0], IFNAME[2]); nm_platform_process_events(PL); nmtstp_run_command_check("ip link set %s name %s", IFNAME[2], IFNAME[0]); pllink = nm_platform_link_get_by_ifname(PL, IFNAME[2]); g_assert(pllink && pllink->ifindex == ifindex[0]); pllink = nm_platform_link_get_by_ifname(PL, IFNAME[0]); g_assert(!pllink); } /* open dirfd for IFNAME[0] */ i = nmtst_get_rand_uint32() % (2 + G_N_ELEMENTS(IFNAME)); if (i == 0) { dirfd = nm_platform_sysctl_open_netdir(PL, ifindex[0], s); } else { const char *ifname_guess; /* provide a wrong or no guess. */ ifname_guess = i == 1 ? NULL : IFNAME[i - 2]; dirfd = nmp_utils_sysctl_open_netdir(ifindex[0], ifname_guess, s); } g_assert(dirfd >= 0); if (s) g_assert_cmpstr(s, ==, IFNAME[0]); /* possibly rename the interfaces. */ switch (nmtst_get_rand_uint32() % 4) { case 0: break; case 1: nmtstp_run_command_check("ip link set %s name %s", IFNAME[0], IFNAME[2]); break; case 2: nmtstp_run_command_check("ip link set %s name %s", IFNAME[0], IFNAME[2]); nmtstp_run_command_check("ip link set %s name %s", IFNAME[1], IFNAME[0]); break; } /* possibly, resync platform cache (should make no difference). */ if (nmtst_get_rand_uint32() % 2) nm_platform_process_events(PL); /* check that we still read the same file. */ switch (nmtst_get_rand_uint32() % 2) { case 0: { gs_free char *c = NULL; if (!nm_utils_file_get_contents(dirfd, "ifindex", 1 * 1024 * 1024, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &c, NULL, NULL, NULL)) g_assert_not_reached(); g_assert_cmpint(ifindex[0], ==, (int) _nm_utils_ascii_str_to_int64(c, 10, 0, G_MAXINT, -1)); break; } case 1: { g_assert_cmpint(ifindex[0], ==, (gint32) nm_platform_sysctl_get_int32( PL, NMP_SYSCTL_PATHID_NETDIR(dirfd, s ?: "", "ifindex"), -1)); break; } } nm_platform_process_events(PL); nmtstp_link_delete(PL, -1, ifindex[0], NULL, TRUE); nmtstp_link_delete(PL, -1, ifindex[1], NULL, TRUE); } /*****************************************************************************/ static void test_sysctl_netns_switch(void) { const char *const IFNAME = "nm-dummy-0"; int ifindex, ifindex_tmp; nm_auto_close int dirfd = -1; char ifname_buf[IFNAMSIZ]; char *s; gs_unref_object NMPlatform *platform_0 = NULL; gs_unref_object NMPlatform *platform_1 = NULL; gs_unref_object NMPlatform *platform_2 = NULL; nm_auto_pop_netns NMPNetns *netns_pop_1 = NULL; nm_auto_pop_netns NMPNetns *netns_pop_2 = NULL; nm_auto_pop_netns NMPNetns *netns_pop_3 = NULL; NMPlatform *PL; NMPlatform *platforms[3]; if (_test_netns_check_skip()) return; platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform(); platforms[2] = platform_2 = _test_netns_create_platform(); PL = platforms[nmtst_get_rand_uint32() % 3]; nmtstp_netns_select_random(platforms, G_N_ELEMENTS(platforms), &netns_pop_1); ifindex = nmtstp_link_dummy_add(PL, FALSE, IFNAME)->ifindex; nmtstp_netns_select_random(platforms, G_N_ELEMENTS(platforms), &netns_pop_2); s = (nmtst_get_rand_uint32() % 2) ? NULL : ifname_buf; dirfd = nm_platform_sysctl_open_netdir(PL, ifindex, s); g_assert(dirfd >= 0); if (s) g_assert_cmpstr(s, ==, IFNAME); nmtstp_netns_select_random(platforms, G_N_ELEMENTS(platforms), &netns_pop_3); /* even if we switch to other namespaces, we can still lookup the path correctly, * either using dirfd or via the platform instance (which switches namespace as needed). */ { gs_free char *c = NULL; if (!nm_utils_file_get_contents(dirfd, "ifindex", 0, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &c, NULL, NULL, NULL)) g_assert_not_reached(); g_assert_cmpint(ifindex, ==, (int) _nm_utils_ascii_str_to_int64(c, 10, 0, G_MAXINT, -1)); } g_assert_cmpint(ifindex, ==, (gint32) nm_platform_sysctl_get_int32( PL, NMP_SYSCTL_PATHID_NETDIR(dirfd, s ?: "", "ifindex"), -1)); g_assert_cmpint( ifindex, ==, (gint32) nm_platform_sysctl_get_int32( PL, NMP_SYSCTL_PATHID_ABSOLUTE(nm_sprintf_bufa(100, "/sys/class/net/%s/ifindex", IFNAME)), -1)); /* also test that nm_platform_sysctl_get() sets errno to ENOENT for non-existing paths. */ { gint64 i64; int errsv; char *v; errno = ESRCH; v = nm_platform_sysctl_get( PL, NMP_SYSCTL_PATHID_ABSOLUTE("/sys/devices/virtual/net/not-existing/ifindex")); errsv = errno; g_assert(!v); g_assert_cmpint(errsv, ==, ENOENT); errno = ESRCH; i64 = nm_platform_sysctl_get_int_checked( PL, NMP_SYSCTL_PATHID_ABSOLUTE("/sys/devices/virtual/net/not-existing/ifindex"), 10, 1, G_MAXINT, -1); errsv = errno; g_assert_cmpint(i64, ==, -1); g_assert_cmpint(errsv, ==, ENOENT); errno = ESRCH; v = nm_platform_sysctl_get( PL, NMP_SYSCTL_PATHID_ABSOLUTE("/sys/devices/virtual/net/lo/not-existing")); errsv = errno; g_assert(!v); g_assert_cmpint(errsv, ==, ENOENT); errno = ESRCH; i64 = nm_platform_sysctl_get_int_checked( PL, NMP_SYSCTL_PATHID_ABSOLUTE("/sys/devices/virtual/net/lo/not-existing"), 10, 1, G_MAXINT, -1); errsv = errno; g_assert_cmpint(i64, ==, -1); g_assert_cmpint(errsv, ==, ENOENT); } /* accessing the path directly, only succeeds iff the current namespace happens to be the namespace * in which we created the link. */ { gs_free char *c = NULL; if (!nm_utils_file_get_contents(-1, nm_sprintf_bufa(100, "/sys/class/net/%s/ifindex", IFNAME), 0, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &c, NULL, NULL, NULL)) ifindex_tmp = -1; else ifindex_tmp = _nm_utils_ascii_str_to_int64(c, 10, 0, G_MAXINT, -2); } if (nmp_netns_get_current() == nm_platform_netns_get(PL)) g_assert_cmpint(ifindex_tmp, ==, ifindex); else g_assert_cmpint(ifindex_tmp, ==, -1); nmtstp_link_delete(PL, FALSE, ifindex, NULL, TRUE); } typedef struct { GMainLoop *loop; const char *path; gboolean expected_success; gint32 expected_value; } SetAsyncData; static void sysctl_set_async_cb(GError *error, gpointer user_data) { SetAsyncData *data = user_data; if (data->expected_success) { g_assert_no_error(error); g_assert_cmpint(nm_platform_sysctl_get_int32(NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE(data->path), -1), ==, data->expected_value); } else g_assert(error); g_main_loop_quit(data->loop); } static void test_sysctl_set_async(void) { NMPlatform *const PL = NM_PLATFORM_GET; const char *const IFNAME = "nm-dummy-0"; const char *const PATH = "/proc/sys/net/ipv4/conf/nm-dummy-0/rp_filter"; GMainLoop *loop; gs_unref_object GCancellable *cancellable = NULL; gboolean proc_writable; SetAsyncData data; int ifindex; ifindex = nmtstp_link_dummy_add(PL, -1, IFNAME)->ifindex; loop = g_main_loop_new(NULL, FALSE); cancellable = g_cancellable_new(); proc_writable = access(PATH, W_OK) == 0; data = (SetAsyncData){ .loop = loop, .path = PATH, .expected_success = proc_writable, .expected_value = 2, }; nm_platform_sysctl_set_async(PL, NMP_SYSCTL_PATHID_ABSOLUTE(PATH), (const char *[]){"2", NULL}, sysctl_set_async_cb, &data, cancellable); if (!nmtst_main_loop_run(loop, 1000)) g_assert_not_reached(); data = (SetAsyncData){ .loop = loop, .path = PATH, .expected_success = proc_writable, .expected_value = 1, }; nm_platform_sysctl_set_async(PL, NMP_SYSCTL_PATHID_ABSOLUTE(PATH), (const char *[]){"2", "0", "1", "0", "1", NULL}, sysctl_set_async_cb, &data, cancellable); if (!nmtst_main_loop_run(loop, 2000)) g_assert_not_reached(); nmtstp_link_delete(NULL, -1, ifindex, IFNAME, TRUE); g_main_loop_unref(loop); } static void test_sysctl_set_async_fail(void) { NMPlatform *const PL = NM_PLATFORM_GET; const char *const IFNAME = "nm-dummy-0"; const char *const PATH = "/proc/sys/net/ipv4/conf/nm-dummy-0/does-not-exist"; GMainLoop *loop; gs_unref_object GCancellable *cancellable = NULL; SetAsyncData data; int ifindex; ifindex = nmtstp_link_dummy_add(PL, -1, IFNAME)->ifindex; loop = g_main_loop_new(NULL, FALSE); cancellable = g_cancellable_new(); data = (SetAsyncData){ .loop = loop, .path = PATH, .expected_success = FALSE, }; nm_platform_sysctl_set_async(PL, NMP_SYSCTL_PATHID_ABSOLUTE(PATH), (const char *[]){"2", NULL}, sysctl_set_async_cb, &data, cancellable); if (!nmtst_main_loop_run(loop, 1000)) g_assert_not_reached(); nmtstp_link_delete(NULL, -1, ifindex, IFNAME, TRUE); g_main_loop_unref(loop); } /*****************************************************************************/ static gpointer _test_netns_mt_thread(gpointer data) { NMPNetns *netns1 = data; gs_unref_object NMPNetns *netns2 = NULL; NMPNetns *netns_bottom; NMPNetns *initial; netns_bottom = nmp_netns_get_initial(); g_assert(netns_bottom); /* I don't know why, but we need to create a new netns here at least once. * Otherwise, setns(, CLONE_NEWNS) below fails with EINVAL (???). * * Something is not right here, but what? */ netns2 = nmp_netns_new(); nmp_netns_pop(netns2); g_clear_object(&netns2); nmp_netns_push(netns1); nmp_netns_push_type(netns_bottom, CLONE_NEWNET); nmp_netns_push_type(netns_bottom, CLONE_NEWNS); nmp_netns_push_type(netns1, CLONE_NEWNS); nmp_netns_pop(netns1); nmp_netns_pop(netns_bottom); nmp_netns_pop(netns_bottom); nmp_netns_pop(netns1); initial = nmp_netns_get_initial(); g_assert(NMP_IS_NETNS(initial)); return g_object_ref(initial); } static void test_netns_mt(void) { gs_unref_object NMPNetns *netns1 = NULL; NMPNetns *initial_from_other_thread; GThread *th; if (_test_netns_check_skip()) return; netns1 = nmp_netns_new(); g_assert(NMP_NETNS(netns1)); nmp_netns_pop(netns1); th = g_thread_new("nm-test-netns-mt", _test_netns_mt_thread, netns1); initial_from_other_thread = g_thread_join(th); g_assert(NMP_IS_NETNS(initial_from_other_thread)); if (nmtst_get_rand_bool()) { nmp_netns_push(initial_from_other_thread); nmp_netns_pop(initial_from_other_thread); } g_object_add_weak_pointer(G_OBJECT(initial_from_other_thread), (gpointer *) &initial_from_other_thread); g_object_unref(initial_from_other_thread); g_assert(initial_from_other_thread == NULL); } /*****************************************************************************/ static void ethtool_features_dump(const NMEthtoolFeatureStates *features) { guint i, j; g_assert(features); _LOGT(">>> %u features (%u ss-features)", features->n_states, features->n_ss_features); for (i = 0; i < features->n_states; i++) { const NMEthtoolFeatureState *s = &features->states_list[i]; _LOGT(">>> feature-list[%3u]: %3d = %-32s (%3u) | %s %s %s %s", i, (int) s->info->ethtool_id, s->info->kernel_names[s->idx_kernel_name], s->idx_ss_features, s->active ? "ACT" : "act", s->available ? "AVA" : "ava", s->never_changed ? "NCH" : "nch", s->requested ? "REQ" : "req"); } for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { _LOGT(">>> feature-idx [%3u]: %-32s = %u features", i + (guint) _NM_ETHTOOL_ID_FEATURE_FIRST, nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, (guint) NM_PTRARRAY_LEN(features->states_indexed[i])); for (j = 0; features->states_indexed[i] && features->states_indexed[i][j]; j++) { const NMEthtoolFeatureState *s = features->states_indexed[i][j]; _LOGT(">>> %3u: %-32s | %s %s %s %s", j, s->info->kernel_names[s->idx_kernel_name], s->active ? "ACT" : "act", s->available ? "AVA" : "ava", s->never_changed ? "NCH" : "nch", s->requested ? "REQ" : "req"); } } } static void test_ethtool_features_get(void) { gs_unref_ptrarray GPtrArray *gfree_keeper = g_ptr_array_new_with_free_func(g_free); const int IFINDEX = 1; guint i; guint i_run; for (i_run = 0; i_run < 5; i_run++) { NMEthtoolFeatureStates *features; NMOptionBool *requested; gboolean do_set = TRUE; requested = g_new(NMOptionBool, _NM_ETHTOOL_ID_FEATURE_NUM); for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) requested[i] = NM_OPTION_BOOL_DEFAULT; g_ptr_array_add(gfree_keeper, requested); if (i_run == 0) { requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_RX)] = NM_OPTION_BOOL_FALSE; requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_TSO)] = NM_OPTION_BOOL_FALSE; requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION)] = NM_OPTION_BOOL_FALSE; } else if (i_run == 1) do_set = FALSE; else if (i_run == 2) { requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_TSO)] = NM_OPTION_BOOL_FALSE; requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION)] = NM_OPTION_BOOL_TRUE; } else if (i_run == 3) do_set = FALSE; _LOGT(">>> ethtool-features-get RUN %u (do-set=%s", i_run, do_set ? "set" : "reset"); features = nmp_utils_ethtool_get_features(IFINDEX); g_ptr_array_add(gfree_keeper, features); ethtool_features_dump(features); if (_LOGT_ENABLED()) _system("ethtool -k lo"); if (!do_set) { requested = gfree_keeper->pdata[i_run * 2 - 2]; features = gfree_keeper->pdata[i_run * 2 - 1]; } nmp_utils_ethtool_set_features(IFINDEX, features, requested, do_set); } } /*****************************************************************************/ NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void _nmtstp_init_tests(int *argc, char ***argv) { nmtst_init_with_logging(argc, argv, NULL, "ALL"); } void _nmtstp_setup_tests(void) { nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, SLAVE_NAME, FALSE); nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, PARENT_NAME, FALSE); g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, DEVICE_NAME)); g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, SLAVE_NAME)); g_assert(!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, PARENT_NAME)); g_test_add_func("/link/bogus", test_bogus); g_test_add_func("/link/loopback", test_loopback); g_test_add_func("/link/internal", test_internal); g_test_add_func("/link/software/bridge", test_bridge); g_test_add_func("/link/software/bond", test_bond); g_test_add_func("/link/software/team", test_team); g_test_add_func("/link/software/vlan", test_vlan); g_test_add_func("/link/software/bridge/addr", test_bridge_addr); if (nmtstp_is_root_test()) { g_test_add_func("/link/external", test_external); test_software_detect_add("/link/software/detect/bridge", NM_LINK_TYPE_BRIDGE, 0); test_software_detect_add("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0); test_software_detect_add("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0); test_software_detect_add("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0); test_software_detect_add("/link/software/detect/ip6tnl/1", NM_LINK_TYPE_IP6TNL, 1); test_software_detect_add("/link/software/detect/ip6gre/0", NM_LINK_TYPE_IP6GRE, 0); test_software_detect_add("/link/software/detect/ip6gre/1", NM_LINK_TYPE_IP6GRE, 1); test_software_detect_add("/link/software/detect/ip6gretap/0", NM_LINK_TYPE_IP6GRETAP, 0); test_software_detect_add("/link/software/detect/ip6gretap/1", NM_LINK_TYPE_IP6GRETAP, 1); test_software_detect_add("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0); test_software_detect_add("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0); test_software_detect_add("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0); test_software_detect_add("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0); test_software_detect_add("/link/software/detect/tun", NM_LINK_TYPE_TUN, 0); test_software_detect_add("/link/software/detect/vlan/0", NM_LINK_TYPE_VLAN, 0); test_software_detect_add("/link/software/detect/vlan/1", NM_LINK_TYPE_VLAN, 1); test_software_detect_add("/link/software/detect/vti", NM_LINK_TYPE_VTI, 0); test_software_detect_add("/link/software/detect/vti6", NM_LINK_TYPE_VTI6, 0); test_software_detect_add("/link/software/detect/vrf", NM_LINK_TYPE_VRF, 0); test_software_detect_add("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0); test_software_detect_add("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1); test_software_detect_add("/link/software/detect/wireguard/0", NM_LINK_TYPE_WIREGUARD, 0); test_software_detect_add("/link/software/detect/wireguard/1", NM_LINK_TYPE_WIREGUARD, 1); test_software_detect_add("/link/software/detect/wireguard/2", NM_LINK_TYPE_WIREGUARD, 2); g_test_add_func("/link/software/vlan/set-xgress", test_vlan_set_xgress); g_test_add_func("/link/set-properties", test_link_set_properties); g_test_add_data_func("/link/create-many-links/20", GUINT_TO_POINTER(20), test_create_many_links); g_test_add_data_func("/link/create-many-links/1000", GUINT_TO_POINTER(1000), test_create_many_links); g_test_add_func("/link/nl-bugs/veth", test_nl_bugs_veth); g_test_add_func("/link/nl-bugs/spurious-newlink", test_nl_bugs_spuroius_newlink); g_test_add_func("/link/nl-bugs/spurious-dellink", test_nl_bugs_spuroius_dellink); g_test_add_vtable("/general/netns/general", 0, NULL, _test_netns_setup, test_netns_general, _test_netns_teardown); g_test_add_vtable("/general/netns/set-netns", 0, NULL, _test_netns_setup, test_netns_set_netns, _test_netns_teardown); g_test_add_vtable("/general/netns/push", 0, NULL, _test_netns_setup, test_netns_push, _test_netns_teardown); g_test_add_vtable("/general/netns/bind-to-path", 0, NULL, _test_netns_setup, test_netns_bind_to_path, _test_netns_teardown); g_test_add_func("/general/netns/mt", test_netns_mt); g_test_add_func("/general/sysctl/rename", test_sysctl_rename); g_test_add_func("/general/sysctl/netns-switch", test_sysctl_netns_switch); g_test_add_func("/general/sysctl/set-async", test_sysctl_set_async); g_test_add_func("/general/sysctl/set-async-fail", test_sysctl_set_async_fail); g_test_add_func("/link/ethtool/features/get", test_ethtool_features_get); } }