/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "libnm-glib-aux/nm-default-glib-i18n-prog.h" #include "libnm-log-core/nm-logging.h" #include "libnm-platform/nm-netlink.h" #include "libnm-platform/nmp-netns.h" #include "libnm-platform/nm-platform-utils.h" #include "libnm-platform/nmp-object.h" #include "libnm-glib-aux/nm-test-utils.h" /*****************************************************************************/ G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP4Address)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP4Route)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Address)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectIP6Route)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLink)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBond)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkBridge)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGeneve)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkGre)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkInfiniband)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIp6Tnl)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkIpIp)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkMacsec)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkMacvlan)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkSit)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkTun)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVlan)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVrf)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVti6)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkVxlan)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectLnkWireGuard)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectQdisc)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectRoutingRule)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPObjectTfilter)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP4Address)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP4Route)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP6Address)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIP6Route)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPAddress)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPAddress)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPRoute)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXAddress)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformIPXRoute)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLink)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBond)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkBridge)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGeneve)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkGre)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkInfiniband)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIp6Tnl)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkIpIp)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkMacsec)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkMacvlan)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkSit)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkTun)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVlan)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVrf)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVti6)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkVxlan)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformLnkWireGuard)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformObjWithIfindex)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformQdisc)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformRoutingRule)); G_STATIC_ASSERT(_nm_alignof(NMPlatformObject) == _nm_alignof(NMPlatformTfilter)); /*****************************************************************************/ static void test_use_symbols(void) { static void (*const SYMBOLS[])(void) = { (void (*)(void)) nl_nlmsghdr_to_str, (void (*)(void)) nlmsg_hdr, (void (*)(void)) nlmsg_reserve, (void (*)(void)) nla_reserve, (void (*)(void)) nlmsg_alloc_convert, (void (*)(void)) nlmsg_alloc_new, (void (*)(void)) nlmsg_alloc, (void (*)(void)) nlmsg_free, (void (*)(void)) nlmsg_append, (void (*)(void)) nlmsg_parse, (void (*)(void)) nlmsg_put, (void (*)(void)) nla_strlcpy, (void (*)(void)) nla_memcpy, (void (*)(void)) nla_put, (void (*)(void)) nla_find, (void (*)(void)) nla_nest_cancel, (void (*)(void)) nla_nest_start, (void (*)(void)) nla_nest_end, (void (*)(void)) nla_parse, (void (*)(void)) nlmsg_get_proto, (void (*)(void)) nlmsg_set_proto, (void (*)(void)) nlmsg_set_src, (void (*)(void)) nlmsg_get_creds, (void (*)(void)) nlmsg_set_creds, (void (*)(void)) genlmsg_put, (void (*)(void)) genlmsg_data, (void (*)(void)) genlmsg_user_hdr, (void (*)(void)) genlmsg_hdr, (void (*)(void)) genlmsg_user_data, (void (*)(void)) genlmsg_attrdata, (void (*)(void)) genlmsg_len, (void (*)(void)) genlmsg_attrlen, (void (*)(void)) genlmsg_valid_hdr, (void (*)(void)) genlmsg_parse, (void (*)(void)) genl_ctrl_resolve, (void (*)(void)) nl_socket_new, (void (*)(void)) nl_socket_free, (void (*)(void)) nl_socket_get_fd, (void (*)(void)) nl_socket_get_local_port, (void (*)(void)) nl_socket_get_msg_buf_size, (void (*)(void)) nl_socket_set_passcred, (void (*)(void)) nl_socket_set_msg_buf_size, (void (*)(void)) nlmsg_get_dst, (void (*)(void)) nl_socket_set_buffer_size, (void (*)(void)) nl_socket_add_memberships, (void (*)(void)) nl_wait_for_ack, (void (*)(void)) nl_recvmsgs, (void (*)(void)) nl_sendmsg, (void (*)(void)) nl_send_iovec, (void (*)(void)) nl_complete_msg, (void (*)(void)) nl_send, (void (*)(void)) nl_send_auto, (void (*)(void)) nl_recv, (void (*)(void)) nmp_netns_bind_to_path, (void (*)(void)) nmp_netns_bind_to_path_destroy, (void (*)(void)) nmp_netns_get_current, (void (*)(void)) nmp_netns_get_fd_mnt, (void (*)(void)) nmp_netns_get_fd_net, (void (*)(void)) nmp_netns_get_initial, (void (*)(void)) nmp_netns_is_initial, (void (*)(void)) nmp_netns_new, (void (*)(void)) nmp_netns_pop, (void (*)(void)) nmp_netns_push, (void (*)(void)) nmp_netns_push_type, NULL, }; /* The only (not very exciting) purpose of this test is to see that * we can use various symbols and don't get a linker error. */ assert(G_N_ELEMENTS(SYMBOLS) == NM_PTRARRAY_LEN(SYMBOLS) + 1); } /*****************************************************************************/ static void test_nmp_link_mode_all_advertised_modes_bits(void) { guint32 flags[(SCHAR_MAX + 1) / 32]; guint max_bit; int i; memset(flags, 0, sizeof(flags)); max_bit = 0; for (i = 0; i < (int) G_N_ELEMENTS(_nmp_link_mode_all_advertised_modes_bits); i++) { if (i > 0) { g_assert_cmpint(_nmp_link_mode_all_advertised_modes_bits[i - 1], <, _nmp_link_mode_all_advertised_modes_bits[i]); } g_assert_cmpint(_nmp_link_mode_all_advertised_modes_bits[i], <, SCHAR_MAX); g_assert_cmpint(_nmp_link_mode_all_advertised_modes_bits[i] / 32, <, G_N_ELEMENTS(flags)); flags[_nmp_link_mode_all_advertised_modes_bits[i] / 32] |= (1u << (_nmp_link_mode_all_advertised_modes_bits[i] % 32u)); max_bit = NM_MAX(max_bit, _nmp_link_mode_all_advertised_modes_bits[i]); } g_assert_cmpint((max_bit + 31u) / 32u, ==, G_N_ELEMENTS(_nmp_link_mode_all_advertised_modes)); for (i = 0; i < (int) G_N_ELEMENTS(_nmp_link_mode_all_advertised_modes); i++) { if (flags[i] != _nmp_link_mode_all_advertised_modes[i]) { NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("_nmp_link_mode_all_advertised_modes[%d] should be 0x%0x but is 0x%0x " "(according to the bits in _nmp_link_mode_all_advertised_modes_bits)", i, flags[i], _nmp_link_mode_all_advertised_modes[i]); NM_PRAGMA_WARNING_REENABLE } } } /*****************************************************************************/ static void test_nmp_utils_bridge_vlans_normalize(void) { NMPlatformBridgeVlan vlans[10]; NMPlatformBridgeVlan expect[10]; guint vlans_len; /* Single one is unmodified */ vlans[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; expect[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; vlans_len = 1; nmp_utils_bridge_vlan_normalize(vlans, &vlans_len); g_assert(vlans_len == 1); g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len)); /* Not merged if flags are different */ vlans[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; vlans[1] = (NMPlatformBridgeVlan) { .vid_start = 11, .vid_end = 11, .pvid = TRUE, }; vlans[2] = (NMPlatformBridgeVlan) { .vid_start = 20, .vid_end = 25, }; vlans[3] = (NMPlatformBridgeVlan) { .vid_start = 26, .vid_end = 30, .untagged = TRUE, }; vlans[4] = (NMPlatformBridgeVlan) { .vid_start = 40, .vid_end = 40, .untagged = TRUE, }; vlans[5] = (NMPlatformBridgeVlan) { .vid_start = 40, .vid_end = 40, .untagged = TRUE, .pvid = TRUE, }; expect[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; expect[1] = (NMPlatformBridgeVlan) { .vid_start = 11, .vid_end = 11, .pvid = TRUE, }; expect[2] = (NMPlatformBridgeVlan) { .vid_start = 20, .vid_end = 25, }; expect[3] = (NMPlatformBridgeVlan) { .vid_start = 26, .vid_end = 30, .untagged = TRUE, }; expect[4] = (NMPlatformBridgeVlan) { .vid_start = 40, .vid_end = 40, .untagged = TRUE, }; expect[5] = (NMPlatformBridgeVlan) { .vid_start = 40, .vid_end = 40, .untagged = TRUE, .pvid = TRUE, }; vlans_len = 6; nmp_utils_bridge_vlan_normalize(vlans, &vlans_len); g_assert(vlans_len == 6); g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len)); /* Overlapping and contiguous ranges are merged */ vlans[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; vlans[1] = (NMPlatformBridgeVlan) { .vid_start = 11, .vid_end = 20, .untagged = TRUE, }; vlans[2] = (NMPlatformBridgeVlan) { .vid_start = 19, .vid_end = 30, .untagged = TRUE, }; expect[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 30, .untagged = TRUE, }; vlans_len = 3; nmp_utils_bridge_vlan_normalize(vlans, &vlans_len); g_assert(vlans_len == 1); g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len)); vlans[0] = (NMPlatformBridgeVlan) { .vid_start = 20, .vid_end = 20, }; vlans[1] = (NMPlatformBridgeVlan) { .vid_start = 4, .vid_end = 4, .pvid = TRUE, }; vlans[2] = (NMPlatformBridgeVlan) { .vid_start = 33, .vid_end = 33, }; vlans[3] = (NMPlatformBridgeVlan) { .vid_start = 100, .vid_end = 100, .untagged = TRUE, }; vlans[4] = (NMPlatformBridgeVlan) { .vid_start = 34, .vid_end = 40, }; vlans[5] = (NMPlatformBridgeVlan) { .vid_start = 21, .vid_end = 32, }; expect[0] = (NMPlatformBridgeVlan) { .vid_start = 4, .vid_end = 4, .pvid = TRUE, }; expect[1] = (NMPlatformBridgeVlan) { .vid_start = 20, .vid_end = 40, }; expect[2] = (NMPlatformBridgeVlan) { .vid_start = 100, .vid_end = 100, .untagged = TRUE, }; vlans_len = 6; nmp_utils_bridge_vlan_normalize(vlans, &vlans_len); g_assert(vlans_len == 3); g_assert(nmp_utils_bridge_normalized_vlans_equal(vlans, vlans_len, expect, vlans_len)); } static void test_nmp_utils_bridge_normalized_vlans_equal(void) { NMPlatformBridgeVlan a[10]; NMPlatformBridgeVlan b[10]; /* Both empty */ g_assert(nmp_utils_bridge_normalized_vlans_equal(NULL, 0, NULL, 0)); g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 0, b, 0)); g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 0, NULL, 0)); g_assert(nmp_utils_bridge_normalized_vlans_equal(NULL, 0, b, 0)); /* One empty, other not */ a[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, NULL, 0)); g_assert(!nmp_utils_bridge_normalized_vlans_equal(NULL, 0, a, 1)); /* Equal range + VLAN */ a[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; a[1] = (NMPlatformBridgeVlan) { .vid_start = 11, .vid_end = 11, .pvid = TRUE, }; b[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 10, .untagged = TRUE, }; b[1] = (NMPlatformBridgeVlan) { .vid_start = 11, .vid_end = 11, .pvid = TRUE, }; g_assert(nmp_utils_bridge_normalized_vlans_equal(a, 2, b, 2)); g_assert(nmp_utils_bridge_normalized_vlans_equal(b, 2, a, 2)); /* Different flag */ b[1].pvid = FALSE; g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 2, b, 2)); g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 2, a, 2)); /* Different ranges */ a[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 30, .untagged = TRUE, }; b[0] = (NMPlatformBridgeVlan) { .vid_start = 1, .vid_end = 29, .untagged = TRUE, }; g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, b, 1)); g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 1, a, 1)); b[0].vid_start = 2; b[0].vid_end = 30; g_assert(!nmp_utils_bridge_normalized_vlans_equal(a, 1, b, 1)); g_assert(!nmp_utils_bridge_normalized_vlans_equal(b, 1, a, 1)); } /*****************************************************************************/ static void test_nmpclass_consistency(void) { NMPObjectType obj_type; NMPObjectType obj_type2; G_STATIC_ASSERT(G_N_ELEMENTS(_nmp_classes) == NMP_OBJECT_TYPE_MAX); for (obj_type = 1; obj_type <= NMP_OBJECT_TYPE_MAX; obj_type++) { const NMPClass *klass = nmp_class_from_type(obj_type); gboolean is_lnk; g_assert(klass); g_assert(klass == &_nmp_classes[obj_type - 1]); g_assert_cmpint(klass->obj_type, ==, obj_type); g_assert(klass->obj_type_name); g_assert((!!klass->cmd_obj_cmp) == (!!klass->cmd_obj_hash_update)); g_assert((!!klass->cmd_plobj_cmp) == (!!klass->cmd_plobj_hash_update)); g_assert((!!klass->cmd_plobj_id_cmp) == (!!klass->cmd_plobj_id_hash_update)); g_assert((!!klass->cmd_obj_cmp) != (!!klass->cmd_plobj_cmp)); g_assert((!!klass->cmd_obj_hash_update) != (!!klass->cmd_plobj_hash_update)); g_assert((!klass->cmd_obj_cmp) || (!klass->cmd_plobj_id_cmp)); g_assert((!klass->cmd_obj_hash_update) || (!klass->cmd_plobj_id_hash_update)); g_assert_cmpint(klass->sizeof_public, >, 0); g_assert_cmpint(klass->sizeof_data, >=, klass->sizeof_public); g_assert((!!klass->cmd_obj_to_string) != (!!klass->cmd_plobj_to_string)); g_assert(!klass->cmd_plobj_to_string_id || klass->cmd_plobj_to_string); is_lnk = (obj_type >= NMP_OBJECT_TYPE_LNK_BRIDGE && obj_type <= NMP_OBJECT_TYPE_LNK_BOND); if (klass->lnk_link_type == NM_LINK_TYPE_NONE) { G_STATIC_ASSERT(NM_LINK_TYPE_NONE == 0); g_assert(!is_lnk); } else g_assert(is_lnk); for (obj_type2 = 1; obj_type2 < obj_type; obj_type2++) { const NMPClass *klass2 = nmp_class_from_type(obj_type2); g_assert_cmpstr(klass->obj_type_name, !=, klass2->obj_type_name); } } } /*****************************************************************************/ NMTST_DEFINE(); int main(int argc, char **argv) { nmtst_init(&argc, &argv, TRUE); g_test_add_func("/nm-platform/test_use_symbols", test_use_symbols); g_test_add_func("/nm-platform/test_nmp_link_mode_all_advertised_modes_bits", test_nmp_link_mode_all_advertised_modes_bits); g_test_add_func("/nm-platform/test_nmpclass_consistency", test_nmpclass_consistency); g_test_add_func("/nm-platform/test_nmp_utils_bridge_vlans_normalize", test_nmp_utils_bridge_vlans_normalize); g_test_add_func("/nm-platform/nmp-utils-bridge-vlans-equal", test_nmp_utils_bridge_normalized_vlans_equal); return g_test_run(); }