/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager audit support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright 2016 Red Hat, Inc. */ #include "nm-default.h" #include #include #include #include #include "nmp-object.h" #include "nmp-netns.h" #include "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_cmpint (nm_platform_link_dummy_add ((platform), (name), NULL), ==, NM_PLATFORM_ERROR_SUCCESS) 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_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX)); 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 (!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 nm_platform_link_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BRIDGE: return nm_platform_link_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BOND: { gboolean bond0_exists = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0"); NMPlatformError plerr; plerr = nm_platform_link_bond_add (NM_PLATFORM_GET, name, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0")); return plerr == NM_PLATFORM_ERROR_SUCCESS; } case NM_LINK_TYPE_TEAM: return nm_platform_link_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; 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 (nm_platform_link_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) 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_set_up (NM_PLATFORM_GET, parent_ifindex, NULL)); 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_signal (parent_changed); free_signal (parent_changed); return nm_platform_link_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } } default: g_error ("Link type %d unhandled.", link_type); } g_assert_not_reached (); } static void test_link_changed_signal_cb (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gboolean *p_test_link_changed_signal_arg) { /* 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_set_down (NM_PLATFORM_GET, ifindex)); 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)); 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_set_up (NM_PLATFORM_GET, master, NULL)); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, master)); accept_signals (master_changed, 1, 2); 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_set_down (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); accept_signals (master_changed, 0, 2); 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 (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_set_up (NM_PLATFORM_GET, ifindex, NULL)); 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, 1, 2); /* 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); ensure_no_signal (master_changed); /* 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, 3); 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, 1, 2); 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_del (NULL, -1, ifindex, NULL); 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_set_noarp (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); accept_signals (link_changed, 1, 2); g_assert (nm_platform_link_set_arp (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); /* Set master option */ 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_del (NULL, -1, ifindex, DEVICE_NAME); accept_signal (link_removed); /* Delete again */ g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); 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_del (NULL, -1, vlan_parent, NULL); 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 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) { 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; nm_utils_hwaddr_aton ("de:ad:be:ef:00:11", addr, sizeof (addr)); g_assert_cmpint (nm_platform_link_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &plink), ==, NM_PLATFORM_ERROR_SUCCESS); g_assert (plink); link = *plink; g_assert_cmpstr (link.name, ==, DEVICE_NAME); g_assert_cmpint (link.addr.len, ==, sizeof (addr)); g_assert (!memcmp (link.addr.data, addr, sizeof (addr))); plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE) == NM_PLATFORM_ERROR_SUCCESS); g_assert (nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE); g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE) == NM_PLATFORM_ERROR_SUCCESS); g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); } g_assert_cmpint (plink->addr.len, ==, sizeof (addr)); g_assert (!memcmp (plink->addr.data, addr, sizeof (addr))); nmtstp_link_del (NULL, -1, link.ifindex, link.name); } /*****************************************************************************/ 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 (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); /* Try to add again */ g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_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_set_up (NM_PLATFORM_GET, ifindex, NULL)); 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_set_down (NM_PLATFORM_GET, ifindex)); 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_set_arp (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); accept_signal (link_changed); g_assert (nm_platform_link_set_noarp (NM_PLATFORM_GET, ifindex)); 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 (nm_platform_link_set_address (NM_PLATFORM_GET, ifindex, mac, sizeof (mac)) == NM_PLATFORM_ERROR_SUCCESS); 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 (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_del (NULL, -1, ifindex, DEVICE_NAME); 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; 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); 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); } /*****************************************************************************/ 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; guint i_step; const gboolean ext = test_data->external_command; 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_GRE: { NMPlatformLnkGre lnk_gre = { }; gboolean gracefully_skip = FALSE; 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 (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "gre0")) { /* Seems that the ip_gre module is not loaded... try to load it. */ gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip_gre", NULL) != 0; } if (!nmtstp_link_gre_add (NULL, ext, DEVICE_NAME, &lnk_gre)) { if (gracefully_skip) { 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_IPIP: { NMPlatformLnkIpIp lnk_ipip = { }; gboolean gracefully_skip = FALSE; if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "tunl0")) { /* Seems that the ipip module is not loaded... try to load it. */ gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ipip", NULL) != 0; } 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 (gracefully_skip) { 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 = { }; gboolean gracefully_skip = FALSE; if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6tnl0")) { /* Seems that the ip6_tunnel module is not loaded... try to load it. */ gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_tunnel", NULL) != 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; if (!nmtstp_link_ip6tnl_add (NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { if (gracefully_skip) { 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_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. * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=17af2bce88d31e65ed73d638bb752d2e13c66ced */ for (i = ifindex_parent + 1; i < ifindex_parent + 100; i++) { 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_del (NM_PLATFORM_GET, FALSE, dummy->ifindex, NULL); } 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: { NMPlatformLnkMacvtap 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 = { }; gboolean gracefully_skip = FALSE; 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 (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "sit0")) { /* Seems that the sit module is not loaded... try to load it. */ gracefully_skip = nm_utils_modprobe (NULL, TRUE, "sit", NULL) != 0; } if (!nmtstp_link_sit_add (NULL, ext, DEVICE_NAME, &lnk_sit)) { if (gracefully_skip) { 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: nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1242", DEVICE_NAME, PARENT_NAME); 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; } 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); g_assert (lnk); switch (test_data->link_type) { 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_cmpint (plnk->parent_ifindex, ==, ifindex_parent); g_assert_cmpint (plnk->input_flags, ==, 0); g_assert_cmpint (plnk->output_flags, ==, 0); g_assert_cmpint (plnk->input_key, ==, 0); g_assert_cmpint (plnk->output_key, ==, 0); nmtst_assert_ip4_address (plnk->local, "192.168.233.204"); nmtst_assert_ip4_address (plnk->remote, "172.168.10.25"); g_assert_cmpint (plnk->ttl, ==, 174); g_assert_cmpint (plnk->tos, ==, 37); g_assert_cmpint (plnk->path_mtu_discovery, ==, TRUE); break; } case NM_LINK_TYPE_IP6TNL: { const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; 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 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 NMPlatformLnkMacvtap *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_VLAN: { const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; g_assert (plnk == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL)); g_assert_cmpint (plnk->id, ==, 1242); 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, omiting 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; } default: g_assert_not_reached (); } } nmtstp_link_del (NULL, -1, ifindex, DEVICE_NAME); out_delete_parent: nmtstp_link_del (NULL, -1, ifindex_parent, PARENT_NAME); } 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_del (NULL, -1, ifindex, DEVICE_NAME); nmtstp_link_del (NULL, -1, ifindex_parent, PARENT_NAME); } /*****************************************************************************/ static void test_create_many_links_do (guint n_devices) { gint64 time, start_time = nm_utils_get_monotonic_timestamp_ns (); guint i; char name[64]; const NMPlatformLink *pllink; gs_unref_array GArray *ifindexes = g_array_sized_new (FALSE, FALSE, sizeof (int), n_devices); const gint EX = ((int) (nmtst_get_rand_int () % 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_del (NULL, EX, g_array_index (ifindexes, int, i), name); } _LOGI (">>> process events after deleting devices..."); nm_platform_process_events (NM_PLATFORM_GET); time = nm_utils_get_monotonic_timestamp_ns () - start_time; _LOGI (">>> finished in %ld.%09ld seconds", (long) (time / NM_UTILS_NS_PER_SECOND), (long) (time % NM_UTILS_NS_PER_SECOND)); } 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; /* create veth pair. */ nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1); ifindex_veth0 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex; ifindex_veth1 = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->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); if (pllink_veth0->parent == 0) { /* pre-4.1 kernels 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_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_del (NULL, -1, ifindex_veth0, IFACE_VETH0); 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; 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); 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); 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_del (NULL, -1, ifindex_dummy0, IFACE_DUMMY0); } /*****************************************************************************/ 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; 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); 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); pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0); g_assert (pllink); 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_del (NULL, -1, ifindex_bridge0, IFACE_BRIDGE0); nmtstp_link_del (NULL, -1, ifindex_dummy0, IFACE_DUMMY0); } /*****************************************************************************/ 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) { /* re-create platform instance */ SETUP (); } 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 (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; 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?)", g_strerror (support_errsv)); g_test_skip ("No netns support (setns failed)"); return TRUE; } return FALSE; } /*****************************************************************************/ #define _sysctl_assert_eq(plat, path, value) \ G_STMT_START { \ gs_free char *_val = NULL; \ \ _val = nm_platform_sysctl_get (plat, path); \ g_assert_cmpstr (_val, ==, value); \ } G_STMT_END 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; if (_test_netns_check_skip ()) return; platform_1 = nm_linux_platform_new (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_int () % 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_int () % 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_int () % 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_int () % 2; if (nmtst_get_rand_int () % 2) { pl = platform_1; if (nmtst_get_rand_int () % 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_int () % 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, 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); /* older kernels (Ubuntu 12.04) 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 ("dummy1_", NULL, NULL, NULL)); g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL)); g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL)); 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 ("dummy1_", NULL, NULL, NULL)); g_assert (!nmp_utils_ethtool_get_driver_info ("dummy2a", NULL, NULL, NULL)); g_assert ( nmp_utils_ethtool_get_driver_info ("dummy2b", NULL, NULL, NULL)); 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; int i; if (_test_netns_check_skip ()) return; platforms[0] = platform_0 = nm_linux_platform_new (TRUE); platforms[1] = platform_1 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform (); i = nmtst_get_rand_int () % 4; if (i != 3) g_assert (nm_platform_netns_push (platforms[i], &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; pl[0].platform = platform_0 = nm_linux_platform_new (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_int () % (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_int () % 2 ? "1" : "0"; _ADD_DUMMY (pl[i].platform, pl[i].device_name); g_assert (nm_platform_sysctl_set (pl[i].platform, 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_int () % (G_N_ELEMENTS (stack) + 1); for (i = 0; i < nstack; i++) { stack[i].pl = &pl[nmtst_get_rand_int () % G_N_ELEMENTS (pl)]; stack[i].ns_types = ns_types_list[nmtst_get_rand_int () % 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_int () % (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_HAS (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 "/var/run" #define P_VAR_RUN_NETNS "/var/run/netns" #define P_VAR_RUN_NETNS_BINDNAME "/var/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 (TRUE); platforms[1] = platform_1 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform (); i = nmtst_get_rand_int () % 4; if (i != 3) g_assert (nm_platform_netns_push (platforms[i], &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_int () % 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); } /*****************************************************************************/ void _nmtstp_init_tests (int *argc, char ***argv) { nmtst_init_with_logging (argc, argv, NULL, "ALL"); } void _nmtstp_setup_tests (void) { nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, SLAVE_NAME)); nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); 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/gre", NM_LINK_TYPE_GRE, 0); test_software_detect_add ("/link/software/detect/ip6tnl", NM_LINK_TYPE_IP6TNL, 0); 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/vlan", NM_LINK_TYPE_VLAN, 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); g_test_add_func ("/link/software/vlan/set-xgress", test_vlan_set_xgress); 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); } }