diff options
| author | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
| commit | 80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch) | |
| tree | e3b229aa94e8dcf0590f2317664176e7b8f7607b /src/platform | |
| parent | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff) | |
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/platform')
38 files changed, 0 insertions, 47200 deletions
diff --git a/src/platform/linux/nl802154.h b/src/platform/linux/nl802154.h deleted file mode 100644 index c21bd079..00000000 --- a/src/platform/linux/nl802154.h +++ /dev/null @@ -1,450 +0,0 @@ -#ifndef __NL802154_H -#define __NL802154_H -/* - * 802.15.4 netlink interface public header - * - * Copyright (C) 2014 Alexander Aring <aar@pengutronix.de> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#define NL802154_GENL_NAME "nl802154" - -enum nl802154_commands { - /* don't change the order or add anything between, this is ABI! */ - /* currently we don't shipping this file via uapi, ignore the above one */ - NL802154_CMD_UNSPEC, - - NL802154_CMD_GET_WPAN_PHY, /* can dump */ - NL802154_CMD_SET_WPAN_PHY, - NL802154_CMD_NEW_WPAN_PHY, - NL802154_CMD_DEL_WPAN_PHY, - - NL802154_CMD_GET_INTERFACE, /* can dump */ - NL802154_CMD_SET_INTERFACE, - NL802154_CMD_NEW_INTERFACE, - NL802154_CMD_DEL_INTERFACE, - - NL802154_CMD_SET_CHANNEL, - - NL802154_CMD_SET_PAN_ID, - NL802154_CMD_SET_SHORT_ADDR, - - NL802154_CMD_SET_TX_POWER, - NL802154_CMD_SET_CCA_MODE, - NL802154_CMD_SET_CCA_ED_LEVEL, - - NL802154_CMD_SET_MAX_FRAME_RETRIES, - - NL802154_CMD_SET_BACKOFF_EXPONENT, - NL802154_CMD_SET_MAX_CSMA_BACKOFFS, - - NL802154_CMD_SET_LBT_MODE, - - NL802154_CMD_SET_ACKREQ_DEFAULT, - - NL802154_CMD_SET_WPAN_PHY_NETNS, - -/* add new commands above here */ - -#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL - NL802154_CMD_SET_SEC_PARAMS, - NL802154_CMD_GET_SEC_KEY, /* can dump */ - NL802154_CMD_NEW_SEC_KEY, - NL802154_CMD_DEL_SEC_KEY, - NL802154_CMD_GET_SEC_DEV, /* can dump */ - NL802154_CMD_NEW_SEC_DEV, - NL802154_CMD_DEL_SEC_DEV, - NL802154_CMD_GET_SEC_DEVKEY, /* can dump */ - NL802154_CMD_NEW_SEC_DEVKEY, - NL802154_CMD_DEL_SEC_DEVKEY, - NL802154_CMD_GET_SEC_LEVEL, /* can dump */ - NL802154_CMD_NEW_SEC_LEVEL, - NL802154_CMD_DEL_SEC_LEVEL, -#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ - - /* used to define NL802154_CMD_MAX below */ - __NL802154_CMD_AFTER_LAST, - NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1 -}; - -enum nl802154_attrs { - /* don't change the order or add anything between, this is ABI! */ - /* currently we don't shipping this file via uapi, ignore the above one */ - NL802154_ATTR_UNSPEC, - - NL802154_ATTR_WPAN_PHY, - NL802154_ATTR_WPAN_PHY_NAME, - - NL802154_ATTR_IFINDEX, - NL802154_ATTR_IFNAME, - NL802154_ATTR_IFTYPE, - - NL802154_ATTR_WPAN_DEV, - - NL802154_ATTR_PAGE, - NL802154_ATTR_CHANNEL, - - NL802154_ATTR_PAN_ID, - NL802154_ATTR_SHORT_ADDR, - - NL802154_ATTR_TX_POWER, - - NL802154_ATTR_CCA_MODE, - NL802154_ATTR_CCA_OPT, - NL802154_ATTR_CCA_ED_LEVEL, - - NL802154_ATTR_MAX_FRAME_RETRIES, - - NL802154_ATTR_MAX_BE, - NL802154_ATTR_MIN_BE, - NL802154_ATTR_MAX_CSMA_BACKOFFS, - - NL802154_ATTR_LBT_MODE, - - NL802154_ATTR_GENERATION, - - NL802154_ATTR_CHANNELS_SUPPORTED, - NL802154_ATTR_SUPPORTED_CHANNEL, - - NL802154_ATTR_EXTENDED_ADDR, - - NL802154_ATTR_WPAN_PHY_CAPS, - - NL802154_ATTR_SUPPORTED_COMMANDS, - - NL802154_ATTR_ACKREQ_DEFAULT, - - NL802154_ATTR_PAD, - - NL802154_ATTR_PID, - NL802154_ATTR_NETNS_FD, - -/* add attributes here, update the policy in nl802154.c */ - -#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL - NL802154_ATTR_SEC_ENABLED, - NL802154_ATTR_SEC_OUT_LEVEL, - NL802154_ATTR_SEC_OUT_KEY_ID, - NL802154_ATTR_SEC_FRAME_COUNTER, - - NL802154_ATTR_SEC_LEVEL, - NL802154_ATTR_SEC_DEVICE, - NL802154_ATTR_SEC_DEVKEY, - NL802154_ATTR_SEC_KEY, -#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ - - __NL802154_ATTR_AFTER_LAST, - NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1 -}; - -enum nl802154_iftype { - /* for backwards compatibility TODO */ - NL802154_IFTYPE_UNSPEC = -1, - - NL802154_IFTYPE_NODE, - NL802154_IFTYPE_MONITOR, - NL802154_IFTYPE_COORD, - - /* keep last */ - NUM_NL802154_IFTYPES, - NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1 -}; - -/** - * enum nl802154_wpan_phy_capability_attr - wpan phy capability attributes - * - * @__NL802154_CAP_ATTR_INVALID: attribute number 0 is reserved - * @NL802154_CAP_ATTR_CHANNELS: a nested attribute for nl802154_channel_attr - * @NL802154_CAP_ATTR_TX_POWERS: a nested attribute for nl802154_wpan_phy_tx_power - * @NL802154_CAP_ATTR_MIN_CCA_ED_LEVEL: minimum value for cca_ed_level - * @NL802154_CAP_ATTR_MAX_CCA_ED_LEVEL: maxmimum value for cca_ed_level - * @NL802154_CAP_ATTR_CCA_MODES: nl802154_cca_modes flags - * @NL802154_CAP_ATTR_CCA_OPTS: nl802154_cca_opts flags - * @NL802154_CAP_ATTR_MIN_MINBE: minimum of minbe value - * @NL802154_CAP_ATTR_MAX_MINBE: maximum of minbe value - * @NL802154_CAP_ATTR_MIN_MAXBE: minimum of maxbe value - * @NL802154_CAP_ATTR_MAX_MINBE: maximum of maxbe value - * @NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS: minimum of csma backoff value - * @NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS: maximum of csma backoffs value - * @NL802154_CAP_ATTR_MIN_FRAME_RETRIES: minimum of frame retries value - * @NL802154_CAP_ATTR_MAX_FRAME_RETRIES: maximum of frame retries value - * @NL802154_CAP_ATTR_IFTYPES: nl802154_iftype flags - * @NL802154_CAP_ATTR_LBT: nl802154_supported_bool_states flags - * @NL802154_CAP_ATTR_MAX: highest cap attribute currently defined - * @__NL802154_CAP_ATTR_AFTER_LAST: internal use - */ -enum nl802154_wpan_phy_capability_attr { - __NL802154_CAP_ATTR_INVALID, - - NL802154_CAP_ATTR_IFTYPES, - - NL802154_CAP_ATTR_CHANNELS, - NL802154_CAP_ATTR_TX_POWERS, - - NL802154_CAP_ATTR_CCA_ED_LEVELS, - NL802154_CAP_ATTR_CCA_MODES, - NL802154_CAP_ATTR_CCA_OPTS, - - NL802154_CAP_ATTR_MIN_MINBE, - NL802154_CAP_ATTR_MAX_MINBE, - - NL802154_CAP_ATTR_MIN_MAXBE, - NL802154_CAP_ATTR_MAX_MAXBE, - - NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS, - NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS, - - NL802154_CAP_ATTR_MIN_FRAME_RETRIES, - NL802154_CAP_ATTR_MAX_FRAME_RETRIES, - - NL802154_CAP_ATTR_LBT, - - /* keep last */ - __NL802154_CAP_ATTR_AFTER_LAST, - NL802154_CAP_ATTR_MAX = __NL802154_CAP_ATTR_AFTER_LAST - 1 -}; - -/** - * enum nl802154_cca_modes - cca modes - * - * @__NL802154_CCA_INVALID: cca mode number 0 is reserved - * @NL802154_CCA_ENERGY: Energy above threshold - * @NL802154_CCA_CARRIER: Carrier sense only - * @NL802154_CCA_ENERGY_CARRIER: Carrier sense with energy above threshold - * @NL802154_CCA_ALOHA: CCA shall always report an idle medium - * @NL802154_CCA_UWB_SHR: UWB preamble sense based on the SHR of a frame - * @NL802154_CCA_UWB_MULTIPLEXED: UWB preamble sense based on the packet with the multiplexed preamble - * @__NL802154_CCA_ATTR_AFTER_LAST: Internal - * @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number - */ -enum nl802154_cca_modes { - __NL802154_CCA_INVALID, - NL802154_CCA_ENERGY, - NL802154_CCA_CARRIER, - NL802154_CCA_ENERGY_CARRIER, - NL802154_CCA_ALOHA, - NL802154_CCA_UWB_SHR, - NL802154_CCA_UWB_MULTIPLEXED, - - /* keep last */ - __NL802154_CCA_ATTR_AFTER_LAST, - NL802154_CCA_ATTR_MAX = __NL802154_CCA_ATTR_AFTER_LAST - 1 -}; - -/** - * enum nl802154_cca_opts - additional options for cca modes - * - * @NL802154_CCA_OPT_ENERGY_CARRIER_OR: NL802154_CCA_ENERGY_CARRIER with OR - * @NL802154_CCA_OPT_ENERGY_CARRIER_AND: NL802154_CCA_ENERGY_CARRIER with AND - */ -enum nl802154_cca_opts { - NL802154_CCA_OPT_ENERGY_CARRIER_AND, - NL802154_CCA_OPT_ENERGY_CARRIER_OR, - - /* keep last */ - __NL802154_CCA_OPT_ATTR_AFTER_LAST, - NL802154_CCA_OPT_ATTR_MAX = __NL802154_CCA_OPT_ATTR_AFTER_LAST - 1 -}; - -/** - * enum nl802154_supported_bool_states - bool states for bool capability entry - * - * @NL802154_SUPPORTED_BOOL_FALSE: indicates to set false - * @NL802154_SUPPORTED_BOOL_TRUE: indicates to set true - * @__NL802154_SUPPORTED_BOOL_INVALD: reserved - * @NL802154_SUPPORTED_BOOL_BOTH: indicates to set true and false - * @__NL802154_SUPPORTED_BOOL_AFTER_LAST: Internal - * @NL802154_SUPPORTED_BOOL_MAX: highest value for bool states - */ -enum nl802154_supported_bool_states { - NL802154_SUPPORTED_BOOL_FALSE, - NL802154_SUPPORTED_BOOL_TRUE, - /* to handle them in a mask */ - __NL802154_SUPPORTED_BOOL_INVALD, - NL802154_SUPPORTED_BOOL_BOTH, - - /* keep last */ - __NL802154_SUPPORTED_BOOL_AFTER_LAST, - NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1 -}; - -#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL - -enum nl802154_dev_addr_modes { - NL802154_DEV_ADDR_NONE, - __NL802154_DEV_ADDR_INVALID, - NL802154_DEV_ADDR_SHORT, - NL802154_DEV_ADDR_EXTENDED, - - /* keep last */ - __NL802154_DEV_ADDR_AFTER_LAST, - NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1 -}; - -enum nl802154_dev_addr_attrs { - NL802154_DEV_ADDR_ATTR_UNSPEC, - - NL802154_DEV_ADDR_ATTR_PAN_ID, - NL802154_DEV_ADDR_ATTR_MODE, - NL802154_DEV_ADDR_ATTR_SHORT, - NL802154_DEV_ADDR_ATTR_EXTENDED, - NL802154_DEV_ADDR_ATTR_PAD, - - /* keep last */ - __NL802154_DEV_ADDR_ATTR_AFTER_LAST, - NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1 -}; - -enum nl802154_key_id_modes { - NL802154_KEY_ID_MODE_IMPLICIT, - NL802154_KEY_ID_MODE_INDEX, - NL802154_KEY_ID_MODE_INDEX_SHORT, - NL802154_KEY_ID_MODE_INDEX_EXTENDED, - - /* keep last */ - __NL802154_KEY_ID_MODE_AFTER_LAST, - NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1 -}; - -enum nl802154_key_id_attrs { - NL802154_KEY_ID_ATTR_UNSPEC, - - NL802154_KEY_ID_ATTR_MODE, - NL802154_KEY_ID_ATTR_INDEX, - NL802154_KEY_ID_ATTR_IMPLICIT, - NL802154_KEY_ID_ATTR_SOURCE_SHORT, - NL802154_KEY_ID_ATTR_SOURCE_EXTENDED, - NL802154_KEY_ID_ATTR_PAD, - - /* keep last */ - __NL802154_KEY_ID_ATTR_AFTER_LAST, - NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1 -}; - -enum nl802154_seclevels { - NL802154_SECLEVEL_NONE, - NL802154_SECLEVEL_MIC32, - NL802154_SECLEVEL_MIC64, - NL802154_SECLEVEL_MIC128, - NL802154_SECLEVEL_ENC, - NL802154_SECLEVEL_ENC_MIC32, - NL802154_SECLEVEL_ENC_MIC64, - NL802154_SECLEVEL_ENC_MIC128, - - /* keep last */ - __NL802154_SECLEVEL_AFTER_LAST, - NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1 -}; - -enum nl802154_frames { - NL802154_FRAME_BEACON, - NL802154_FRAME_DATA, - NL802154_FRAME_ACK, - NL802154_FRAME_CMD, - - /* keep last */ - __NL802154_FRAME_AFTER_LAST, - NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1 -}; - -enum nl802154_cmd_frames { - __NL802154_CMD_FRAME_INVALID, - NL802154_CMD_FRAME_ASSOC_REQUEST, - NL802154_CMD_FRAME_ASSOC_RESPONSE, - NL802154_CMD_FRAME_DISASSOC_NOTIFY, - NL802154_CMD_FRAME_DATA_REQUEST, - NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY, - NL802154_CMD_FRAME_ORPHAN_NOTIFY, - NL802154_CMD_FRAME_BEACON_REQUEST, - NL802154_CMD_FRAME_COORD_REALIGNMENT, - NL802154_CMD_FRAME_GTS_REQUEST, - - /* keep last */ - __NL802154_CMD_FRAME_AFTER_LAST, - NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1 -}; - -enum nl802154_seclevel_attrs { - NL802154_SECLEVEL_ATTR_UNSPEC, - - NL802154_SECLEVEL_ATTR_LEVELS, - NL802154_SECLEVEL_ATTR_FRAME, - NL802154_SECLEVEL_ATTR_CMD_FRAME, - NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, - - /* keep last */ - __NL802154_SECLEVEL_ATTR_AFTER_LAST, - NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1 -}; - -/* TODO what is this? couldn't find in mib */ -enum { - NL802154_DEVKEY_IGNORE, - NL802154_DEVKEY_RESTRICT, - NL802154_DEVKEY_RECORD, - - /* keep last */ - __NL802154_DEVKEY_AFTER_LAST, - NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1 -}; - -enum nl802154_dev { - NL802154_DEV_ATTR_UNSPEC, - - NL802154_DEV_ATTR_FRAME_COUNTER, - NL802154_DEV_ATTR_PAN_ID, - NL802154_DEV_ATTR_SHORT_ADDR, - NL802154_DEV_ATTR_EXTENDED_ADDR, - NL802154_DEV_ATTR_SECLEVEL_EXEMPT, - NL802154_DEV_ATTR_KEY_MODE, - NL802154_DEV_ATTR_PAD, - - /* keep last */ - __NL802154_DEV_ATTR_AFTER_LAST, - NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1 -}; - -enum nl802154_devkey { - NL802154_DEVKEY_ATTR_UNSPEC, - - NL802154_DEVKEY_ATTR_FRAME_COUNTER, - NL802154_DEVKEY_ATTR_EXTENDED_ADDR, - NL802154_DEVKEY_ATTR_ID, - NL802154_DEVKEY_ATTR_PAD, - - /* keep last */ - __NL802154_DEVKEY_ATTR_AFTER_LAST, - NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1 -}; - -enum nl802154_key { - NL802154_KEY_ATTR_UNSPEC, - - NL802154_KEY_ATTR_ID, - NL802154_KEY_ATTR_USAGE_FRAMES, - NL802154_KEY_ATTR_USAGE_CMDS, - NL802154_KEY_ATTR_BYTES, - - /* keep last */ - __NL802154_KEY_ATTR_AFTER_LAST, - NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1 -}; - - #define NL802154_KEY_SIZE 16 - #define NL802154_CMD_FRAME_NR_IDS 256 - -#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ - -#endif /* __NL802154_H */ diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c deleted file mode 100644 index 5bb0b426..00000000 --- a/src/platform/nm-fake-platform.c +++ /dev/null @@ -1,1403 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2012 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-fake-platform.h" - -#include <unistd.h> -#include <netinet/icmp6.h> -#include <netinet/in.h> -#include <linux/if.h> -#include <linux/rtnetlink.h> - -#include "nm-utils.h" - -#include "nm-core-utils.h" -#include "nm-platform-utils.h" -#include "nm-platform-private.h" -#include "nmp-object.h" - -#include "nm-test-utils-core.h" - -/*****************************************************************************/ - -typedef struct { - const NMPObject *obj; - char * udi; - struct in6_addr ip6_lladdr; -} NMFakePlatformLink; - -typedef struct { - GHashTable *options; - GArray * links; -} NMFakePlatformPrivate; - -struct _NMFakePlatform { - NMPlatform parent; - NMFakePlatformPrivate _priv; -}; - -struct _NMFakePlatformClass { - NMPlatformClass parent; -}; - -G_DEFINE_TYPE(NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM) - -#define NM_FAKE_PLATFORM_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMFakePlatform, NM_IS_FAKE_PLATFORM, NMPlatform) - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "platform-fake" -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__) - -#define _LOG(level, domain, self, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = (domain); \ - \ - if (nm_logging_enabled(__level, __domain)) { \ - char __prefix[32]; \ - const char * __p_prefix = _NMLOG_PREFIX_NAME; \ - NMPlatform *const __self = (self); \ - \ - if (__self && nm_platform_get_log_with_ptr(self)) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log(__level, \ - __domain, \ - 0, \ - NULL, \ - NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static void link_changed(NMPlatform * platform, - NMFakePlatformLink *device, - NMPCacheOpsType cache_op, - const NMPObject * obj_old); - -static gboolean ipx_address_delete(NMPlatform * platform, - int addr_family, - int ifindex, - gconstpointer addr, - const guint8 *plen, - gconstpointer peer_addr); - -static gboolean -ipx_route_delete(NMPlatform *platform, int addr_family, int ifindex, const NMPObject *obj); - -static gboolean ip6_address_add(NMPlatform * platform, - int ifindex, - struct in6_addr addr, - guint8 plen, - struct in6_addr peer_addr, - guint32 lifetime, - guint32 preferred, - guint flags); -static gboolean -ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen); - -/*****************************************************************************/ - -#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ - G_STMT_START \ - { \ - const char *const _pathid = (pathid); \ - const int _dirfd = (dirfd); \ - const char *const _path = (path); \ - \ - g_assert(_path &&_path[0]); \ - g_assert(!strstr(_path, "/../")); \ - if (_dirfd < 0) { \ - g_assert(!_pathid); \ - g_assert(_path[0] == '/'); \ - g_assert(g_str_has_prefix(_path, "/proc/sys/") || g_str_has_prefix(_path, "/sys/")); \ - } else { \ - g_assert_not_reached(); \ - } \ - } \ - G_STMT_END - -static gboolean -sysctl_set(NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE(platform); - - ASSERT_SYSCTL_ARGS(pathid, dirfd, path); - - g_hash_table_insert(priv->options, g_strdup(path), g_strdup(value)); - - return TRUE; -} - -static char * -sysctl_get(NMPlatform *platform, const char *pathid, int dirfd, const char *path) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE(platform); - const char * v; - - ASSERT_SYSCTL_ARGS(pathid, dirfd, path); - - v = g_hash_table_lookup(priv->options, path); - if (!v) { - errno = ENOENT; - return NULL; - } - - return g_strdup(v); -} - -static NMFakePlatformLink * -link_get(NMPlatform *platform, int ifindex) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE(platform); - NMFakePlatformLink * device; - int idx; - - if (ifindex <= 0) - g_return_val_if_reached(NULL); - - idx = ifindex - 1; - if (idx >= priv->links->len) - goto not_found; - - device = &g_array_index(priv->links, NMFakePlatformLink, idx); - if (!device->obj) - goto not_found; - - g_assert(ifindex == NMP_OBJECT_CAST_LINK(device->obj)->ifindex); - g_assert(device->obj == nm_platform_link_get_obj(platform, ifindex, FALSE)); - - return device; -not_found: - _LOGD("link not found: %d", ifindex); - return NULL; -} - -static void -link_add_prepare(NMPlatform *platform, NMFakePlatformLink *device, NMPObject *obj_tmp) -{ - gboolean connected; - - /* we must clear the driver, because platform cache wants to set it */ - g_assert(obj_tmp->link.driver == g_intern_string(obj_tmp->link.driver)); - obj_tmp->link.driver = NULL; - - if (NM_IN_SET(obj_tmp->link.type, NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND)) { - connected = FALSE; - if (NM_FLAGS_HAS(obj_tmp->link.n_ifi_flags, IFF_UP)) { - NMPLookup lookup; - NMDedupMultiIter iter; - const NMPObject *slave_candidate = NULL; - - nmp_cache_iter_for_each ( - &iter, - nmp_cache_lookup(nm_platform_get_cache(platform), - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK)), - &slave_candidate) { - if (nmp_cache_link_connected_for_slave(obj_tmp->link.ifindex, slave_candidate)) { - connected = TRUE; - break; - } - } - } - } else - connected = NM_FLAGS_HAS(obj_tmp->link.n_ifi_flags, IFF_UP); - - obj_tmp->link.n_ifi_flags = NM_FLAGS_ASSIGN(obj_tmp->link.n_ifi_flags, IFF_LOWER_UP, connected); - obj_tmp->link.connected = connected; -} - -static NMFakePlatformLink * -link_add_pre(NMPlatform *platform, - const char *name, - NMLinkType type, - const void *address, - size_t address_len, - guint32 mtu) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE(platform); - NMFakePlatformLink * device; - int ifindex; - NMPObject * o; - NMPlatformLink * link; - gs_free char * ip6_lladdr = NULL; - - g_assert(!name || strlen(name) < IFNAMSIZ); - - g_array_set_size(priv->links, priv->links->len + 1); - device = &g_array_index(priv->links, NMFakePlatformLink, priv->links->len - 1); - ifindex = priv->links->len; - - memset(device, 0, sizeof(*device)); - - o = nmp_object_new_link(ifindex); - link = NMP_OBJECT_CAST_LINK(o); - - ip6_lladdr = - ifindex > 0 ? g_strdup_printf("fe80::fa1e:%0x:%0x", ifindex / 256, ifindex % 256) : NULL; - - link->ifindex = name ? ifindex : 0; - link->type = type; - link->kind = g_intern_string(nm_link_type_to_string(type)); - link->mtu = mtu; - link->initialized = TRUE; - if (name) - strcpy(link->name, name); - switch (link->type) { - case NM_LINK_TYPE_DUMMY: - link->n_ifi_flags = NM_FLAGS_SET(link->n_ifi_flags, IFF_NOARP); - break; - default: - link->n_ifi_flags = NM_FLAGS_UNSET(link->n_ifi_flags, IFF_NOARP); - break; - } - - o->_link.netlink.is_in_netlink = TRUE; - - if (address) { - g_assert(address_len > 0 && address_len <= sizeof(link->l_address.data)); - memcpy(link->l_address.data, address, address_len); - link->l_address.len = address_len; - } else - g_assert(address_len == 0); - - device->obj = o; - device->ip6_lladdr = *nmtst_inet6_from_string(ip6_lladdr); - - return device; -} - -static int -link_add(NMPlatform * platform, - NMLinkType type, - const char * name, - int parent, - const void * address, - size_t address_len, - guint32 mtu, - gconstpointer extra_data, - const NMPlatformLink **out_link) -{ - NMFakePlatformLink * device; - NMFakePlatformLink * device_veth = NULL; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - nm_auto_nmpobj const NMPObject *obj_old_veth = NULL; - nm_auto_nmpobj const NMPObject *obj_new_veth = NULL; - NMPCacheOpsType cache_op; - NMPCacheOpsType cache_op_veth = NMP_CACHE_OPS_UNCHANGED; - const char * veth_peer = NULL; - NMPObject * dev_obj; - NMPObject * dev_lnk = NULL; - - device = link_add_pre(platform, name, type, address, address_len, mtu); - - g_assert(device); - - dev_obj = (NMPObject *) device->obj; - - if (parent > 0) - dev_obj->link.parent = parent; - else - g_assert(parent == 0); - - g_assert((parent != 0) == NM_IN_SET(type, NM_LINK_TYPE_VLAN)); - - switch (type) { - case NM_LINK_TYPE_BRIDGE: - { - const NMPlatformLnkBridge *props = extra_data; - - g_assert(props); - - dev_lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_BRIDGE, props); - break; - } - case NM_LINK_TYPE_VETH: - veth_peer = extra_data; - g_assert(veth_peer); - device_veth = link_add_pre(platform, veth_peer, type, NULL, 0, 0); - break; - case NM_LINK_TYPE_VLAN: - { - const NMPlatformLnkVlan *props = extra_data; - - g_assert(props); - - dev_lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_VLAN, props); - break; - } - case NM_LINK_TYPE_VXLAN: - { - const NMPlatformLnkVxlan *props = extra_data; - - g_assert(props); - - dev_lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_VXLAN, props); - break; - } - default: - g_assert(!extra_data); - break; - } - - if (dev_lnk) - dev_obj->_link.netlink.lnk = dev_lnk; - - link_add_prepare(platform, device, (NMPObject *) device->obj); - cache_op = nmp_cache_update_netlink(nm_platform_get_cache(platform), - (NMPObject *) device->obj, - FALSE, - &obj_old, - &obj_new); - g_assert(cache_op == NMP_CACHE_OPS_ADDED); - nmp_object_unref(device->obj); - device->obj = nmp_object_ref(obj_new); - if (veth_peer) { - link_add_prepare(platform, device_veth, (NMPObject *) device_veth->obj); - cache_op_veth = nmp_cache_update_netlink(nm_platform_get_cache(platform), - (NMPObject *) device_veth->obj, - FALSE, - &obj_old_veth, - &obj_new_veth); - g_assert(cache_op == NMP_CACHE_OPS_ADDED); - nmp_object_unref(device->obj); - device->obj = nmp_object_ref(obj_new); - } - - if (out_link) - *out_link = NMP_OBJECT_CAST_LINK(device->obj); - - link_changed(platform, device, cache_op, NULL); - if (veth_peer) - link_changed(platform, device_veth, cache_op_veth, NULL); - - return 0; -} - -static NMFakePlatformLink * -link_add_one(NMPlatform *platform, - const char *name, - NMLinkType link_type, - void (*prepare_fcn)(NMPlatform * platform, - NMFakePlatformLink *device, - gconstpointer user_data), - gconstpointer user_data, - const NMPlatformLink **out_link) -{ - NMFakePlatformLink * device; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - NMPCacheOpsType cache_op; - int ifindex; - - device = link_add_pre(platform, name, NM_LINK_TYPE_VLAN, NULL, 0, 0); - - ifindex = NMP_OBJECT_CAST_LINK(device->obj)->ifindex; - - if (prepare_fcn) - prepare_fcn(platform, device, user_data); - - link_add_prepare(platform, device, (NMPObject *) device->obj); - cache_op = nmp_cache_update_netlink(nm_platform_get_cache(platform), - (NMPObject *) device->obj, - FALSE, - &obj_old, - &obj_new); - g_assert(cache_op == NMP_CACHE_OPS_ADDED); - nmp_object_unref(device->obj); - device->obj = nmp_object_ref(obj_new); - - link_changed(platform, device, cache_op, obj_old); - - device = link_get(platform, ifindex); - if (!device) - g_assert_not_reached(); - - NM_SET_OUT(out_link, NMP_OBJECT_CAST_LINK(device->obj)); - return device; -} - -static gboolean -link_delete(NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink * device = link_get(platform, ifindex); - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_old2 = NULL; - NMPCacheOpsType cache_op; - - if (!device) - return FALSE; - - obj_old = g_steal_pointer(&device->obj); - - cache_op = nmp_cache_remove(nm_platform_get_cache(platform), obj_old, FALSE, FALSE, &obj_old2); - g_assert(cache_op == NMP_CACHE_OPS_REMOVED); - g_assert(obj_old2); - g_assert(obj_old == obj_old2); - - /* Remove addresses and routes which belong to the deleted interface */ - ipx_address_delete(platform, AF_INET, ifindex, NULL, NULL, NULL); - ipx_address_delete(platform, AF_INET6, ifindex, NULL, NULL, NULL); - ipx_route_delete(platform, AF_INET, ifindex, NULL); - ipx_route_delete(platform, AF_INET6, ifindex, NULL); - - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old2, NULL); - return TRUE; -} - -static void -link_set_obj(NMPlatform *platform, NMFakePlatformLink *device, NMPObject *obj_tmp) -{ - nm_auto_nmpobj const NMPObject *obj_new = NULL; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj NMPObject *obj_tmp_tmp = NULL; - NMPCacheOpsType cache_op; - - g_assert(device); - g_assert(NMP_OBJECT_GET_TYPE(device->obj) == NMP_OBJECT_TYPE_LINK); - - if (!obj_tmp) { - obj_tmp_tmp = nmp_object_clone(device->obj, FALSE); - obj_tmp = obj_tmp_tmp; - } - - g_assert(NMP_OBJECT_GET_TYPE(obj_tmp) == NMP_OBJECT_TYPE_LINK); - - link_add_prepare(platform, device, obj_tmp); - cache_op = nmp_cache_update_netlink(nm_platform_get_cache(platform), - obj_tmp, - FALSE, - &obj_old, - &obj_new); - g_assert(NM_IN_SET(cache_op, NMP_CACHE_OPS_UNCHANGED, NMP_CACHE_OPS_UPDATED)); - g_assert(obj_old == device->obj); - g_assert(obj_new); - - nmp_object_unref(device->obj); - device->obj = nmp_object_ref(obj_new); - - link_changed(platform, device, cache_op, obj_old); -} - -static void -link_set_flags(NMPlatform *platform, NMFakePlatformLink *device, guint n_ifi_flags) -{ - nm_auto_nmpobj NMPObject *obj_tmp = NULL; - - g_assert(device); - g_assert(NMP_OBJECT_GET_TYPE(device->obj) == NMP_OBJECT_TYPE_LINK); - - obj_tmp = nmp_object_clone(device->obj, FALSE); - obj_tmp->link.n_ifi_flags = n_ifi_flags; - link_set_obj(platform, device, obj_tmp); -} - -static void -link_changed(NMPlatform * platform, - NMFakePlatformLink *device, - NMPCacheOpsType cache_op, - const NMPObject * obj_old) -{ - g_assert(device->obj); - - g_assert(!nmp_cache_link_connected_needs_toggle(nm_platform_get_cache(platform), - device->obj, - NULL, - NULL)); - - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, device->obj); - - if (!IN6_IS_ADDR_UNSPECIFIED(&device->ip6_lladdr)) { - if (device->obj->link.connected) - ip6_address_add(platform, - device->obj->link.ifindex, - device->ip6_lladdr, - 64, - in6addr_any, - NM_PLATFORM_LIFETIME_PERMANENT, - NM_PLATFORM_LIFETIME_PERMANENT, - 0); - else - ip6_address_delete(platform, device->obj->link.ifindex, device->ip6_lladdr, 64); - } - - if (device->obj->link.master) { - NMFakePlatformLink *master; - - master = link_get(platform, device->obj->link.master); - link_set_obj(platform, master, NULL); - } -} - -static gboolean -link_set_up(NMPlatform *platform, int ifindex, gboolean *out_no_firmware) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - if (out_no_firmware) - *out_no_firmware = FALSE; - - if (!device) { - _LOGE("failure changing link: netlink error (No such device)"); - return FALSE; - } - - link_set_flags(platform, device, NM_FLAGS_ASSIGN(device->obj->link.n_ifi_flags, IFF_UP, TRUE)); - return TRUE; -} - -static gboolean -link_set_down(NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - if (!device) { - _LOGE("failure changing link: netlink error (No such device)"); - return FALSE; - } - - link_set_flags(platform, device, NM_FLAGS_UNSET(device->obj->link.n_ifi_flags, IFF_UP)); - return TRUE; -} - -static gboolean -link_set_arp(NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - if (!device) { - _LOGE("failure changing link: netlink error (No such device)"); - return FALSE; - } - - link_set_flags(platform, device, NM_FLAGS_UNSET(device->obj->link.n_ifi_flags, IFF_NOARP)); - return TRUE; -} - -static gboolean -link_set_noarp(NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - if (!device) { - _LOGE("failure changing link: netlink error (No such device)"); - return FALSE; - } - - link_set_flags(platform, device, NM_FLAGS_SET(device->obj->link.n_ifi_flags, IFF_NOARP)); - return TRUE; -} - -static int -link_set_address(NMPlatform *platform, int ifindex, gconstpointer addr, size_t len) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - nm_auto_nmpobj NMPObject *obj_tmp = NULL; - - if (len == 0 || len > NM_UTILS_HWADDR_LEN_MAX || !addr) - g_return_val_if_reached(-NME_BUG); - - if (!device) - return -NME_PL_EXISTS; - - obj_tmp = nmp_object_clone(device->obj, FALSE); - obj_tmp->link.l_address.len = len; - memset(obj_tmp->link.l_address.data, 0, sizeof(obj_tmp->link.l_address.data)); - memcpy(obj_tmp->link.l_address.data, addr, len); - - link_set_obj(platform, device, obj_tmp); - return 0; -} - -static int -link_set_mtu(NMPlatform *platform, int ifindex, guint32 mtu) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - nm_auto_nmpobj NMPObject *obj_tmp = NULL; - - if (!device) { - _LOGE("failure changing link: netlink error (No such device)"); - return -NME_PL_EXISTS; - } - - obj_tmp = nmp_object_clone(device->obj, FALSE); - obj_tmp->link.mtu = mtu; - link_set_obj(platform, device, obj_tmp); - return 0; -} - -static gboolean -link_get_driver_info(NMPlatform *platform, - int ifindex, - char ** out_driver_name, - char ** out_driver_version, - char ** out_fw_version) -{ - if (out_driver_name) - *out_driver_name = NULL; - if (out_driver_version) - *out_driver_version = NULL; - if (out_fw_version) - *out_fw_version = NULL; - - return TRUE; -} - -static gboolean -link_supports_carrier_detect(NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - if (!device) - return FALSE; - - switch (device->obj->link.type) { - case NM_LINK_TYPE_DUMMY: - return FALSE; - default: - return TRUE; - } -} - -static gboolean -link_supports_vlans(NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - if (!device) - return FALSE; - - switch (device->obj->link.type) { - case NM_LINK_TYPE_LOOPBACK: - return FALSE; - default: - return TRUE; - } -} - -static gboolean -link_supports_sriov(NMPlatform *platform, int ifindex) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - if (!device) - return FALSE; - - switch (device->obj->link.type) { - case NM_LINK_TYPE_LOOPBACK: - return FALSE; - default: - return TRUE; - } -} - -static gboolean -link_enslave(NMPlatform *platform, int master, int slave) -{ - NMFakePlatformLink *device = link_get(platform, slave); - NMFakePlatformLink *master_device = link_get(platform, master); - - g_return_val_if_fail(device, FALSE); - g_return_val_if_fail(master_device, FALSE); - - if (device->obj->link.master != master) { - nm_auto_nmpobj NMPObject *obj_tmp = NULL; - - obj_tmp = nmp_object_clone(device->obj, FALSE); - obj_tmp->link.master = master; - if (NM_IN_SET(master_device->obj->link.type, NM_LINK_TYPE_BOND, NM_LINK_TYPE_TEAM)) - obj_tmp->link.n_ifi_flags = NM_FLAGS_SET(device->obj->link.n_ifi_flags, IFF_UP); - link_set_obj(platform, device, obj_tmp); - } - - return TRUE; -} - -static gboolean -link_release(NMPlatform *platform, int master_idx, int slave_idx) -{ - NMFakePlatformLink *master = link_get(platform, master_idx); - NMFakePlatformLink *slave = link_get(platform, slave_idx); - nm_auto_nmpobj NMPObject *obj_tmp = NULL; - - g_return_val_if_fail(master, FALSE); - g_return_val_if_fail(slave, FALSE); - - if (slave->obj->link.master != master->obj->link.ifindex) - return FALSE; - - obj_tmp = nmp_object_clone(slave->obj, FALSE); - obj_tmp->link.master = 0; - link_set_obj(platform, slave, obj_tmp); - return TRUE; -} - -static gboolean -link_vlan_change(NMPlatform * platform, - int ifindex, - NMVlanFlags flags_mask, - NMVlanFlags flags_set, - gboolean ingress_reset_all, - const NMVlanQosMapping *ingress_map, - gsize n_ingress_map, - gboolean egress_reset_all, - const NMVlanQosMapping *egress_map, - gsize n_egress_map) -{ - return FALSE; -} - -struct infiniband_add_data { - int parent; - int p_key; -}; - -static void -_infiniband_add_prepare(NMPlatform *platform, NMFakePlatformLink *device, gconstpointer user_data) -{ - const struct infiniband_add_data *d = user_data; - NMPObject * obj_tmp; - NMPObject * lnk; - - obj_tmp = (NMPObject *) device->obj; - - lnk = nmp_object_new(NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); - lnk->lnk_infiniband.p_key = d->p_key; - lnk->lnk_infiniband.mode = "datagram"; - - obj_tmp->link.parent = d->parent; - obj_tmp->_link.netlink.lnk = lnk; -} - -static gboolean -infiniband_partition_add(NMPlatform * platform, - int parent, - int p_key, - const NMPlatformLink **out_link) -{ - NMFakePlatformLink * parent_device; - char name[IFNAMSIZ]; - const struct infiniband_add_data d = { - .parent = parent, - .p_key = p_key, - }; - - parent_device = link_get(platform, parent); - g_return_val_if_fail(parent_device != NULL, FALSE); - - nm_utils_new_infiniband_name(name, parent_device->obj->link.name, p_key); - - link_add_one(platform, name, NM_LINK_TYPE_INFINIBAND, _infiniband_add_prepare, &d, out_link); - return TRUE; -} - -static gboolean -infiniband_partition_delete(NMPlatform *platform, int parent, int p_key) -{ - NMFakePlatformLink *parent_device; - gs_free char * name = NULL; - - parent_device = link_get(platform, parent); - g_return_val_if_fail(parent_device != NULL, FALSE); - - nm_utils_new_infiniband_name(name, parent_device->obj->link.name, p_key); - return link_delete(platform, nm_platform_link_get_ifindex(platform, name)); -} - -static gboolean -wifi_get_capabilities(NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps) -{ - NMFakePlatformLink *device = link_get(platform, ifindex); - - g_return_val_if_fail(device, FALSE); - - if (device->obj->link.type != NM_LINK_TYPE_WIFI) - return FALSE; - - if (caps) { - *caps = (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104 - | NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP - | NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN | NM_WIFI_DEVICE_CAP_AP - | NM_WIFI_DEVICE_CAP_ADHOC); - } - return TRUE; -} - -static gboolean -wifi_get_bssid(NMPlatform *platform, int ifindex, guint8 *bssid) -{ - return FALSE; -} - -static guint32 -wifi_get_frequency(NMPlatform *platform, int ifindex) -{ - return 0; -} - -static int -wifi_get_quality(NMPlatform *platform, int ifindex) -{ - return 0; -} - -static guint32 -wifi_get_rate(NMPlatform *platform, int ifindex) -{ - return 0; -} - -static NM80211Mode -wifi_get_mode(NMPlatform *platform, int ifindex) -{ - return NM_802_11_MODE_UNKNOWN; -} - -static void -wifi_set_mode(NMPlatform *platform, int ifindex, NM80211Mode mode) -{ - ; -} - -static guint32 -wifi_find_frequency(NMPlatform *platform, int ifindex, const guint32 *freqs) -{ - return freqs[0]; -} - -static void -wifi_indicate_addressing_running(NMPlatform *platform, int ifindex, gboolean running) -{} - -static guint32 -mesh_get_channel(NMPlatform *platform, int ifindex) -{ - return 0; -} - -static gboolean -mesh_set_channel(NMPlatform *platform, int ifindex, guint32 channel) -{ - return FALSE; -} - -static gboolean -mesh_set_ssid(NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) -{ - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -ipx_address_add(NMPlatform *platform, int addr_family, const NMPlatformObject *address) -{ - nm_auto_nmpobj NMPObject *obj = NULL; - NMPCacheOpsType cache_op; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - NMPCache * cache = nm_platform_get_cache(platform); - - g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - - obj = nmp_object_new(addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ADDRESS - : NMP_OBJECT_TYPE_IP6_ADDRESS, - address); - - cache_op = nmp_cache_update_netlink(cache, obj, FALSE, &obj_old, &obj_new); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); - return TRUE; -} - -static gboolean -ip4_address_add(NMPlatform *platform, - int ifindex, - in_addr_t addr, - guint8 plen, - in_addr_t peer_addr, - in_addr_t broadcast_address, - guint32 lifetime, - guint32 preferred, - guint32 flags, - const char *label) -{ - NMPlatformIP4Address address; - - address = (NMPlatformIP4Address){ - .addr_source = NM_IP_CONFIG_SOURCE_KERNEL, - .ifindex = ifindex, - .address = addr, - .plen = plen, - .peer_address = peer_addr, - .broadcast_address = broadcast_address, - .use_ip4_broadcast_address = TRUE, - .timestamp = nm_utils_get_monotonic_timestamp_sec(), - .lifetime = lifetime, - .preferred = preferred, - .n_ifa_flags = flags, - }; - if (label) - g_strlcpy(address.label, label, sizeof(address.label)); - - return ipx_address_add(platform, AF_INET, (const NMPlatformObject *) &address); -} - -static gboolean -ip6_address_add(NMPlatform * platform, - int ifindex, - struct in6_addr addr, - guint8 plen, - struct in6_addr peer_addr, - guint32 lifetime, - guint32 preferred, - guint32 flags) -{ - NMPlatformIP6Address address; - - memset(&address, 0, sizeof(address)); - address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; - address.ifindex = ifindex; - address.address = addr; - address.peer_address = - (IN6_IS_ADDR_UNSPECIFIED(&peer_addr) || IN6_ARE_ADDR_EQUAL(&addr, &peer_addr)) ? in6addr_any - : peer_addr; - address.plen = plen; - address.timestamp = nm_utils_get_monotonic_timestamp_sec(); - address.lifetime = lifetime; - address.preferred = preferred; - address.n_ifa_flags = flags; - - return ipx_address_add(platform, AF_INET6, (const NMPlatformObject *) &address); -} - -static gboolean -ipx_address_delete(NMPlatform * platform, - int addr_family, - int ifindex, - gconstpointer addr, - const guint8 *plen, - gconstpointer peer_addr) -{ - gs_unref_ptrarray GPtrArray *objs = - g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - NMDedupMultiIter iter; - const NMPObject *o = NULL; - guint i; - guint32 peer_addr_i; - - g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - - peer_addr_i = peer_addr ? *((guint32 *) peer_addr) : 0; - - nmp_cache_iter_for_each (&iter, - nm_platform_lookup_object(platform, - addr_family == AF_INET - ? NMP_OBJECT_TYPE_IP4_ADDRESS - : NMP_OBJECT_TYPE_IP6_ADDRESS, - 0), - &o) { - const NMPObject *obj_old = NULL; - - if (addr_family == AF_INET) { - const NMPlatformIP4Address *address = NMP_OBJECT_CAST_IP4_ADDRESS(o); - - if (address->ifindex != ifindex || (addr && address->address != *((guint32 *) addr)) - || (plen && address->plen != *plen) - || (peer_addr - && (((peer_addr_i ^ address->peer_address) - & _nm_utils_ip4_prefix_to_netmask(address->plen)) - != 0))) - continue; - } else { - const NMPlatformIP6Address *address = NMP_OBJECT_CAST_IP6_ADDRESS(o); - - g_assert(!peer_addr); - if (address->ifindex != ifindex - || (addr && !IN6_ARE_ADDR_EQUAL(&address->address, addr)) - || (plen && address->plen != *plen)) - continue; - } - - if (nmp_cache_remove(nm_platform_get_cache(platform), o, TRUE, FALSE, &obj_old) - != NMP_CACHE_OPS_REMOVED) - g_assert_not_reached(); - g_assert(obj_old); - g_ptr_array_add(objs, (gpointer) obj_old); - } - - for (i = 0; i < objs->len; i++) { - nm_platform_cache_update_emit_signal(platform, NMP_CACHE_OPS_REMOVED, objs->pdata[i], NULL); - } - return TRUE; -} - -static gboolean -ip4_address_delete(NMPlatform *platform, - int ifindex, - in_addr_t addr, - guint8 plen, - in_addr_t peer_address) -{ - return ipx_address_delete(platform, AF_INET, ifindex, &addr, &plen, &peer_address); -} - -static gboolean -ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - return ipx_address_delete(platform, AF_INET6, ifindex, &addr, &plen, NULL); -} - -/*****************************************************************************/ - -static gboolean -ipx_route_delete(NMPlatform *platform, int addr_family, int ifindex, const NMPObject *obj) -{ - gs_unref_ptrarray GPtrArray *objs = - g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - NMDedupMultiIter iter; - const NMPObject *o = NULL; - guint i; - NMPObjectType obj_type; - - if (addr_family == AF_UNSPEC) { - g_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE)); - g_assert(ifindex == -1); - ifindex = NMP_OBJECT_CAST_IP_ROUTE(obj)->ifindex; - obj_type = NMP_OBJECT_GET_TYPE(obj); - } else { - g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - g_assert(!obj); - g_assert(ifindex > 0); - obj_type = addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE; - } - - nmp_cache_iter_for_each (&iter, nm_platform_lookup_object(platform, obj_type, ifindex), &o) { - const NMPObject *obj_old = NULL; - - if (obj) { - if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) { - const NMPlatformIP4Route *route = NMP_OBJECT_CAST_IP4_ROUTE(o); - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(obj); - - if (route->network != r->network || route->plen != r->plen - || route->metric != r->metric) - continue; - } else { - const NMPlatformIP6Route *route = NMP_OBJECT_CAST_IP6_ROUTE(o); - const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE(obj); - - if (!IN6_ARE_ADDR_EQUAL(&route->network, &r->network) || route->plen != r->plen - || route->metric != r->metric) - continue; - } - } - - if (nmp_cache_remove(nm_platform_get_cache(platform), o, TRUE, FALSE, &obj_old) - != NMP_CACHE_OPS_REMOVED) - g_assert_not_reached(); - g_assert(obj_old); - g_ptr_array_add(objs, (gpointer) obj_old); - } - - for (i = 0; i < objs->len; i++) { - nm_platform_cache_update_emit_signal(platform, NMP_CACHE_OPS_REMOVED, objs->pdata[i], NULL); - } - return TRUE; -} - -static gboolean -object_delete(NMPlatform *platform, const NMPObject *obj) -{ - g_assert(NM_IS_FAKE_PLATFORM(platform)); - g_assert( - NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - return ipx_route_delete(platform, AF_UNSPEC, -1, obj); -} - -static int -ip_route_add(NMPlatform * platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) -{ - NMDedupMultiIter iter; - nm_auto_nmpobj NMPObject *obj = NULL; - NMPCacheOpsType cache_op; - const NMPObject * o = NULL; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - nm_auto_nmpobj const NMPObject *obj_replace = NULL; - NMPCache * cache = nm_platform_get_cache(platform); - gboolean has_gateway = FALSE; - NMPlatformIPRoute * r = NULL; - NMPlatformIP4Route * r4 = NULL; - NMPlatformIP6Route * r6 = NULL; - gboolean has_same_weak_id; - gboolean only_dirty; - guint16 nlmsgflags; - - g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - - flags = NM_FLAGS_UNSET(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE); - - /* currently, only replace is implemented. */ - g_assert(flags == NMP_NLM_FLAG_REPLACE); - - obj = nmp_object_new(addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE - : NMP_OBJECT_TYPE_IP6_ROUTE, - (const NMPlatformObject *) route); - r = NMP_OBJECT_CAST_IP_ROUTE(obj); - nm_platform_ip_route_normalize(addr_family, r); - - switch (addr_family) { - case AF_INET: - r4 = NMP_OBJECT_CAST_IP4_ROUTE(obj); - if (r4->gateway) - has_gateway = TRUE; - break; - case AF_INET6: - r6 = NMP_OBJECT_CAST_IP6_ROUTE(obj); - if (!IN6_IS_ADDR_UNSPECIFIED(&r6->gateway)) - has_gateway = TRUE; - break; - default: - nm_assert_not_reached(); - } - - if (has_gateway) { - gboolean has_route_to_gw = FALSE; - - nmp_cache_iter_for_each (&iter, - nm_platform_lookup_object(platform, NMP_OBJECT_GET_TYPE(obj), 0), - &o) { - if (addr_family == AF_INET) { - const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE(o); - guint32 n = nm_utils_ip4_address_clear_host_address(item->network, item->plen); - guint32 g = nm_utils_ip4_address_clear_host_address(r4->gateway, item->plen); - - if (r->ifindex == item->ifindex && n == g) { - has_route_to_gw = TRUE; - break; - } - } else { - const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE(o); - - if (r->ifindex == item->ifindex - && nm_utils_ip6_address_same_prefix(&r6->gateway, &item->network, item->plen)) { - has_route_to_gw = TRUE; - break; - } - } - } - if (!has_route_to_gw) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - if (addr_family == AF_INET) { - nm_log_warn( - LOGD_PLATFORM, - "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", - r->ifindex, - _nm_utils_inet4_ntop(r4->network, sbuf), - r->plen, - r->metric); - } else { - nm_log_warn( - LOGD_PLATFORM, - "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", - r->ifindex, - _nm_utils_inet6_ntop(&r6->network, sbuf), - r->plen, - r->metric); - } - return -NME_UNSPEC; - } - } - - has_same_weak_id = FALSE; - nmp_cache_iter_for_each ( - &iter, - nm_platform_lookup_all(platform, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj), - &o) { - if (addr_family == AF_INET) { - if (nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(o), - r4, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) - == 0) - continue; - } else { - if (nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(o), - r6, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) - == 0) - continue; - } - has_same_weak_id = TRUE; - } - - nlmsgflags = 0; - if (has_same_weak_id) { - switch (flags) { - case NMP_NLM_FLAG_REPLACE: - nlmsgflags = NLM_F_REPLACE; - break; - default: - g_assert_not_reached(); - break; - } - } - - /* we manipulate the cache the same was as NMLinuxPlatform does it. */ - cache_op = nmp_cache_update_netlink_route(cache, - obj, - FALSE, - nlmsgflags, - &obj_old, - &obj_new, - &obj_replace, - NULL); - only_dirty = FALSE; - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - if (obj_replace) { - const NMDedupMultiEntry *entry_replace; - - entry_replace = nmp_cache_lookup_entry(cache, obj_replace); - nm_assert(entry_replace && entry_replace->obj == obj_replace); - nm_dedup_multi_entry_set_dirty(entry_replace, TRUE); - only_dirty = TRUE; - } - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); - } - - if (obj_replace) { - cache_op = nmp_cache_remove(cache, obj_replace, TRUE, only_dirty, NULL); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - nm_assert(cache_op == NMP_CACHE_OPS_REMOVED); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_replace, NULL); - } - } - - return 0; -} - -/*****************************************************************************/ - -static void -nm_fake_platform_init(NMFakePlatform *fake_platform) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE(fake_platform); - - priv->options = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); - priv->links = g_array_new(TRUE, TRUE, sizeof(NMFakePlatformLink)); -} - -void -nm_fake_platform_setup(void) -{ - NMPlatform *platform; - - platform = g_object_new(NM_TYPE_FAKE_PLATFORM, NM_PLATFORM_LOG_WITH_PTR, FALSE, NULL); - - nm_platform_setup(platform); - - link_add(platform, NM_LINK_TYPE_LOOPBACK, "lo", 0, NULL, 0, 0, NULL, NULL); - link_add(platform, NM_LINK_TYPE_ETHERNET, "eth0", 0, NULL, 0, 0, NULL, NULL); - link_add(platform, NM_LINK_TYPE_ETHERNET, "eth1", 0, NULL, 0, 0, NULL, NULL); - link_add(platform, NM_LINK_TYPE_ETHERNET, "eth2", 0, NULL, 0, 0, NULL, NULL); -} - -static void -finalize(GObject *object) -{ - NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE(object); - int i; - - g_hash_table_unref(priv->options); - for (i = 0; i < priv->links->len; i++) { - NMFakePlatformLink *device = &g_array_index(priv->links, NMFakePlatformLink, i); - - nm_clear_pointer(&device->obj, nmp_object_unref); - } - g_array_unref(priv->links); - - G_OBJECT_CLASS(nm_fake_platform_parent_class)->finalize(object); -} - -static void -nm_fake_platform_class_init(NMFakePlatformClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMPlatformClass * platform_class = NM_PLATFORM_CLASS(klass); - NMPlatformKernelSupportType kernel_support; - - for (kernel_support = 0; kernel_support < _NM_PLATFORM_KERNEL_SUPPORT_NUM; kernel_support++) - _nm_platform_kernel_support_init(kernel_support, -1); - - object_class->finalize = finalize; - - platform_class->sysctl_set = sysctl_set; - platform_class->sysctl_get = sysctl_get; - - platform_class->link_add = link_add; - platform_class->link_delete = link_delete; - - platform_class->link_set_up = link_set_up; - platform_class->link_set_down = link_set_down; - platform_class->link_set_arp = link_set_arp; - platform_class->link_set_noarp = link_set_noarp; - - platform_class->link_set_address = link_set_address; - platform_class->link_set_mtu = link_set_mtu; - - platform_class->link_get_driver_info = link_get_driver_info; - - platform_class->link_supports_carrier_detect = link_supports_carrier_detect; - platform_class->link_supports_vlans = link_supports_vlans; - platform_class->link_supports_sriov = link_supports_sriov; - - platform_class->link_enslave = link_enslave; - platform_class->link_release = link_release; - - platform_class->link_vlan_change = link_vlan_change; - - platform_class->infiniband_partition_add = infiniband_partition_add; - platform_class->infiniband_partition_delete = infiniband_partition_delete; - - platform_class->wifi_get_capabilities = wifi_get_capabilities; - platform_class->wifi_get_bssid = wifi_get_bssid; - platform_class->wifi_get_frequency = wifi_get_frequency; - platform_class->wifi_get_quality = wifi_get_quality; - platform_class->wifi_get_rate = wifi_get_rate; - platform_class->wifi_get_mode = wifi_get_mode; - platform_class->wifi_set_mode = wifi_set_mode; - platform_class->wifi_find_frequency = wifi_find_frequency; - platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; - - platform_class->mesh_get_channel = mesh_get_channel; - platform_class->mesh_set_channel = mesh_set_channel; - platform_class->mesh_set_ssid = mesh_set_ssid; - - platform_class->ip4_address_add = ip4_address_add; - platform_class->ip6_address_add = ip6_address_add; - platform_class->ip4_address_delete = ip4_address_delete; - platform_class->ip6_address_delete = ip6_address_delete; - - platform_class->ip_route_add = ip_route_add; - platform_class->object_delete = object_delete; -} diff --git a/src/platform/nm-fake-platform.h b/src/platform/nm-fake-platform.h deleted file mode 100644 index c3ec8921..00000000 --- a/src/platform/nm-fake-platform.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2012 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_FAKE_PLATFORM_H__ -#define __NETWORKMANAGER_FAKE_PLATFORM_H__ - -#include "nm-platform.h" - -#define NM_TYPE_FAKE_PLATFORM (nm_fake_platform_get_type()) -#define NM_FAKE_PLATFORM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatform)) -#define NM_FAKE_PLATFORM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass)) -#define NM_IS_FAKE_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_FAKE_PLATFORM)) -#define NM_IS_FAKE_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_FAKE_PLATFORM)) -#define NM_FAKE_PLATFORM_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_FAKE_PLATFORM, NMFakePlatformClass)) - -typedef struct _NMFakePlatform NMFakePlatform; -typedef struct _NMFakePlatformClass NMFakePlatformClass; - -GType nm_fake_platform_get_type(void); - -void nm_fake_platform_setup(void); - -#endif /* __NETWORKMANAGER_FAKE_PLATFORM_H__ */ diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c deleted file mode 100644 index b377c85e..00000000 --- a/src/platform/nm-linux-platform.c +++ /dev/null @@ -1,9711 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2012 - 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-linux-platform.h" - -#include <arpa/inet.h> -#include <dlfcn.h> -#include <endian.h> -#include <fcntl.h> -#include <libudev.h> -#include <linux/fib_rules.h> -#include <linux/ip.h> -#include <linux/if_arp.h> -#include <linux/if_bridge.h> -#include <linux/if_link.h> -#include <linux/if_tun.h> -#include <linux/if_tunnel.h> -#include <linux/ip6_tunnel.h> -#include <linux/tc_act/tc_mirred.h> -#include <netinet/icmp6.h> -#include <netinet/in.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/statvfs.h> -#include <unistd.h> - -#include "nm-std-aux/unaligned.h" - -#include "nm-utils.h" -#include "nm-core-internal.h" -#include "nm-setting-vlan.h" - -#include "nm-glib-aux/nm-secret-utils.h" -#include "nm-glib-aux/nm-c-list.h" -#include "nm-netlink.h" -#include "nm-core-utils.h" -#include "nmp-object.h" -#include "nmp-netns.h" -#include "nm-platform-utils.h" -#include "nm-platform-private.h" -#include "wifi/nm-wifi-utils.h" -#include "wifi/nm-wifi-utils-wext.h" -#include "wpan/nm-wpan-utils.h" -#include "nm-glib-aux/nm-io-utils.h" -#include "nm-udev-aux/nm-udev-utils.h" - -/*****************************************************************************/ - -/* re-implement <linux/tc_act/tc_defact.h> to build against kernel - * headers that lack this. */ - -#include <linux/pkt_cls.h> - -struct tc_defact { - tc_gen; -}; - -enum { TCA_DEF_UNSPEC, TCA_DEF_TM, TCA_DEF_PARMS, TCA_DEF_DATA, TCA_DEF_PAD, __TCA_DEF_MAX }; -#define TCA_DEF_MAX (__TCA_DEF_MAX - 1) - -/*****************************************************************************/ - -/* Compat with older kernels. */ - -#define TCA_FQ_CODEL_CE_THRESHOLD 7 -#define TCA_FQ_CODEL_MEMORY_LIMIT 9 - -/*****************************************************************************/ - -#define VLAN_FLAG_MVRP 0x8 - -/*****************************************************************************/ - -#define IFQDISCSIZ 32 - -/*****************************************************************************/ - -#ifndef IFLA_PROMISCUITY - #define IFLA_PROMISCUITY 30 -#endif -#define IFLA_NUM_TX_QUEUES 31 -#define IFLA_NUM_RX_QUEUES 32 -#define IFLA_CARRIER 33 -#define IFLA_PHYS_PORT_ID 34 -#define IFLA_LINK_NETNSID 37 -#define __IFLA_MAX 39 - -#define IFLA_INET6_TOKEN 7 -#define IFLA_INET6_ADDR_GEN_MODE 8 -#define __IFLA_INET6_MAX 9 - -#define IFLA_VLAN_PROTOCOL 5 -#define __IFLA_VLAN_MAX 6 - -#define IFA_FLAGS 8 -#define __IFA_MAX 9 - -#define IFLA_MACVLAN_FLAGS 2 -#define __IFLA_MACVLAN_MAX 3 - -#define IFLA_IPTUN_LINK 1 -#define IFLA_IPTUN_LOCAL 2 -#define IFLA_IPTUN_REMOTE 3 -#define IFLA_IPTUN_TTL 4 -#define IFLA_IPTUN_TOS 5 -#define IFLA_IPTUN_ENCAP_LIMIT 6 -#define IFLA_IPTUN_FLOWINFO 7 -#define IFLA_IPTUN_FLAGS 8 -#define IFLA_IPTUN_PROTO 9 -#define IFLA_IPTUN_PMTUDISC 10 -#define __IFLA_IPTUN_MAX 19 -#ifndef IFLA_IPTUN_MAX - #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) -#endif - -#define IFLA_TUN_UNSPEC 0 -#define IFLA_TUN_OWNER 1 -#define IFLA_TUN_GROUP 2 -#define IFLA_TUN_TYPE 3 -#define IFLA_TUN_PI 4 -#define IFLA_TUN_VNET_HDR 5 -#define IFLA_TUN_PERSIST 6 -#define IFLA_TUN_MULTI_QUEUE 7 -#define IFLA_TUN_NUM_QUEUES 8 -#define IFLA_TUN_NUM_DISABLED_QUEUES 9 -#define __IFLA_TUN_MAX 10 -#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1) - -G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1)); -#define RTA_PREF 20 -#undef RTA_MAX -#define RTA_MAX (MAX((__RTA_MAX - 1), RTA_PREF)) - -#ifndef MACVLAN_FLAG_NOPROMISC - #define MACVLAN_FLAG_NOPROMISC 1 -#endif - -#define IP6_FLOWINFO_TCLASS_MASK 0x0FF00000 -#define IP6_FLOWINFO_TCLASS_SHIFT 20 -#define IP6_FLOWINFO_FLOWLABEL_MASK 0x000FFFFF - -#define IFLA_BR_VLAN_STATS_ENABLED 41 - -/*****************************************************************************/ - -/* Appeared in the kernel prior to 3.13 dated 19 January, 2014 */ -#ifndef ARPHRD_6LOWPAN - #define ARPHRD_6LOWPAN 825 -#endif - -/*****************************************************************************/ - -#define FRA_TUN_ID 12 -#define FRA_SUPPRESS_IFGROUP 13 -#define FRA_SUPPRESS_PREFIXLEN 14 -#define FRA_PAD 18 -#define FRA_L3MDEV 19 -#define FRA_UID_RANGE 20 -#define FRA_PROTOCOL 21 -#define FRA_IP_PROTO 22 -#define FRA_SPORT_RANGE 23 -#define FRA_DPORT_RANGE 24 - -/*****************************************************************************/ - -#define IFLA_MACSEC_UNSPEC 0 -#define IFLA_MACSEC_SCI 1 -#define IFLA_MACSEC_PORT 2 -#define IFLA_MACSEC_ICV_LEN 3 -#define IFLA_MACSEC_CIPHER_SUITE 4 -#define IFLA_MACSEC_WINDOW 5 -#define IFLA_MACSEC_ENCODING_SA 6 -#define IFLA_MACSEC_ENCRYPT 7 -#define IFLA_MACSEC_PROTECT 8 -#define IFLA_MACSEC_INC_SCI 9 -#define IFLA_MACSEC_ES 10 -#define IFLA_MACSEC_SCB 11 -#define IFLA_MACSEC_REPLAY_PROTECT 12 -#define IFLA_MACSEC_VALIDATION 13 -#define IFLA_MACSEC_PAD 14 -#define __IFLA_MACSEC_MAX 15 - -/*****************************************************************************/ - -#define WG_CMD_GET_DEVICE 0 -#define WG_CMD_SET_DEVICE 1 - -#define WGDEVICE_F_REPLACE_PEERS ((guint32)(1U << 0)) - -#define WGPEER_F_REMOVE_ME ((guint32)(1U << 0)) -#define WGPEER_F_REPLACE_ALLOWEDIPS ((guint32)(1U << 1)) - -#define WGDEVICE_A_UNSPEC 0 -#define WGDEVICE_A_IFINDEX 1 -#define WGDEVICE_A_IFNAME 2 -#define WGDEVICE_A_PRIVATE_KEY 3 -#define WGDEVICE_A_PUBLIC_KEY 4 -#define WGDEVICE_A_FLAGS 5 -#define WGDEVICE_A_LISTEN_PORT 6 -#define WGDEVICE_A_FWMARK 7 -#define WGDEVICE_A_PEERS 8 -#define WGDEVICE_A_MAX 8 - -#define WGPEER_A_UNSPEC 0 -#define WGPEER_A_PUBLIC_KEY 1 -#define WGPEER_A_PRESHARED_KEY 2 -#define WGPEER_A_FLAGS 3 -#define WGPEER_A_ENDPOINT 4 -#define WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL 5 -#define WGPEER_A_LAST_HANDSHAKE_TIME 6 -#define WGPEER_A_RX_BYTES 7 -#define WGPEER_A_TX_BYTES 8 -#define WGPEER_A_ALLOWEDIPS 9 -#define WGPEER_A_MAX 9 - -#define WGALLOWEDIP_A_UNSPEC 0 -#define WGALLOWEDIP_A_FAMILY 1 -#define WGALLOWEDIP_A_IPADDR 2 -#define WGALLOWEDIP_A_CIDR_MASK 3 -#define WGALLOWEDIP_A_MAX 3 - -/*****************************************************************************/ - -/* Redefine VF enums and structures that are not available on older kernels. */ - -#define IFLA_VF_UNSPEC 0 -#define IFLA_VF_MAC 1 -#define IFLA_VF_VLAN 2 -#define IFLA_VF_TX_RATE 3 -#define IFLA_VF_SPOOFCHK 4 -#define IFLA_VF_LINK_STATE 5 -#define IFLA_VF_RATE 6 -#define IFLA_VF_RSS_QUERY_EN 7 -#define IFLA_VF_STATS 8 -#define IFLA_VF_TRUST 9 -#define IFLA_VF_IB_NODE_GUID 10 -#define IFLA_VF_IB_PORT_GUID 11 -#define IFLA_VF_VLAN_LIST 12 - -#define IFLA_VF_VLAN_INFO_UNSPEC 0 -#define IFLA_VF_VLAN_INFO 1 - -/* valid for TRUST, SPOOFCHK, LINK_STATE, RSS_QUERY_EN */ -struct _ifla_vf_setting { - guint32 vf; - guint32 setting; -}; - -struct _ifla_vf_rate { - guint32 vf; - guint32 min_tx_rate; - guint32 max_tx_rate; -}; - -struct _ifla_vf_vlan_info { - guint32 vf; - guint32 vlan; /* 0 - 4095, 0 disables VLAN filter */ - guint32 qos; - guint16 vlan_proto; /* VLAN protocol, either 802.1Q or 802.1ad */ -}; - -/*****************************************************************************/ - -/* Appeared in the kernel 4.0 dated April 12, 2015 */ -#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN - #define BRIDGE_VLAN_INFO_RANGE_BEGIN (1 << 3) /* VLAN is start of vlan range */ - #define BRIDGE_VLAN_INFO_RANGE_END (1 << 4) /* VLAN is end of vlan range */ -#endif - -/*****************************************************************************/ - -#define PSCHED_TIME_UNITS_PER_SEC 1000000 - -/*****************************************************************************/ - -typedef enum { - INFINIBAND_ACTION_CREATE_CHILD, - INFINIBAND_ACTION_DELETE_CHILD, -} InfinibandAction; - -typedef enum { - CHANGE_LINK_TYPE_UNSPEC, - CHANGE_LINK_TYPE_SET_MTU, - CHANGE_LINK_TYPE_SET_ADDRESS, -} ChangeLinkType; - -typedef struct { - union { - struct { - gconstpointer address; - gsize length; - } set_address; - }; -} ChangeLinkData; - -typedef enum { - _REFRESH_ALL_TYPE_FIRST = 0, - - REFRESH_ALL_TYPE_LINKS = 0, - REFRESH_ALL_TYPE_IP4_ADDRESSES = 1, - REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, - REFRESH_ALL_TYPE_IP4_ROUTES = 3, - REFRESH_ALL_TYPE_IP6_ROUTES = 4, - REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5, - REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6, - REFRESH_ALL_TYPE_QDISCS = 7, - REFRESH_ALL_TYPE_TFILTERS = 8, - - _REFRESH_ALL_TYPE_NUM, -} RefreshAllType; - -typedef struct { - NMPObjectType obj_type; - - /* for NLM_F_DUMP, which address family to request. */ - int addr_family; -} RefreshAllInfo; - -typedef enum { - DELAYED_ACTION_TYPE_NONE = 0, - -#define F(val, name) ((sizeof(char[(((val)) == (name)) ? 1 : -1]) * 0) + (val)) - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = 1 << F(0, REFRESH_ALL_TYPE_LINKS), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = 1 << F(1, REFRESH_ALL_TYPE_IP4_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F(2, REFRESH_ALL_TYPE_IP6_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F(3, REFRESH_ALL_TYPE_IP4_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F(4, REFRESH_ALL_TYPE_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1 - << F(5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1 - << F(6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F(7, REFRESH_ALL_TYPE_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F(8, REFRESH_ALL_TYPE_TFILTERS), -#undef F - - DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 9, - DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 10, - DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 11, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12, - - __DELAYED_ACTION_TYPE_MAX, - - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL = - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, - - DELAYED_ACTION_TYPE_REFRESH_ALL = - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS - | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - - DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX - 1, -} DelayedActionType; - -#define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ - for ((iflags) = (DelayedActionType) 0x1LL; ({ \ - gboolean _good = FALSE; \ - \ - nm_assert(nm_utils_is_power_of_two(iflags)); \ - \ - while ((iflags) <= DELAYED_ACTION_TYPE_MAX) { \ - if (NM_FLAGS_ANY((flags_all), (iflags))) { \ - _good = TRUE; \ - break; \ - } \ - (iflags) <<= 1; \ - } \ - _good; \ - }); \ - (iflags) <<= 1) - -typedef enum { - /* Negative values are errors from kernel. Add dummy member to - * make enum signed. */ - _WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = G_MININT, - - WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN = 0, - WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK, - WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS, -} WaitForNlResponseResult; - -typedef enum { - DELAYED_ACTION_RESPONSE_TYPE_VOID = 0, - DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1, - DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET = 2, -} DelayedActionWaitForNlResponseType; - -typedef struct { - guint32 seq_number; - WaitForNlResponseResult seq_result; - DelayedActionWaitForNlResponseType response_type; - gint64 timeout_abs_ns; - WaitForNlResponseResult * out_seq_result; - char ** out_errmsg; - union { - int * out_refresh_all_in_progress; - NMPObject **out_route_get; - gpointer out_data; - } response; -} DelayedActionWaitForNlResponseData; - -/*****************************************************************************/ - -typedef struct { - struct nl_sock *genl; - - struct nl_sock *nlh; - - GSource *event_source; - - guint32 nlh_seq_next; -#if NM_MORE_LOGGING - guint32 nlh_seq_last_handled; -#endif - guint32 nlh_seq_last_seen; - - guint32 pruning[_REFRESH_ALL_TYPE_NUM]; - - GHashTable *sysctl_get_prev_values; - CList sysctl_list; - - NMUdevClient *udev_client; - - struct { - /* which delayed actions are scheduled, as marked in @flags. - * Some types have additional arguments in the fields below. */ - DelayedActionType flags; - - /* counter that a refresh all action is in progress, separated - * by type. */ - int refresh_all_in_progress[_REFRESH_ALL_TYPE_NUM]; - - GPtrArray *list_master_connected; - GPtrArray *list_refresh_link; - GArray * list_wait_for_nl_response; - - int is_handling; - } delayed_action; -} NMLinuxPlatformPrivate; - -struct _NMLinuxPlatform { - NMPlatform parent; - NMLinuxPlatformPrivate _priv; -}; - -struct _NMLinuxPlatformClass { - NMPlatformClass parent; -}; - -G_DEFINE_TYPE(NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) - -#define NM_LINUX_PLATFORM_GET_PRIVATE(self) \ - _NM_GET_PRIVATE(self, NMLinuxPlatform, NM_IS_LINUX_PLATFORM, NMPlatform) - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "platform-linux" -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG2_DOMAIN LOGD_PLATFORM -#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, platform, __VA_ARGS__) -#define _NMLOG_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG_DOMAIN, platform, __VA_ARGS__) -#define _NMLOG2(level, ...) _LOG(level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) -#define _NMLOG2_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__) - -#define _LOG_print(__level, __domain, __errsv, self, ...) \ - G_STMT_START \ - { \ - char __prefix[32]; \ - const char * __p_prefix = _NMLOG_PREFIX_NAME; \ - NMPlatform *const __self = (self); \ - \ - if (__self && nm_platform_get_log_with_ptr(__self)) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log(__level, \ - __domain, \ - __errsv, \ - NULL, \ - NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - G_STMT_END - -#define _LOG(level, domain, self, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = (domain); \ - \ - if (nm_logging_enabled(__level, __domain)) { \ - _LOG_print(__level, __domain, 0, self, __VA_ARGS__); \ - } \ - } \ - G_STMT_END - -#define _LOG_err(errsv, level, domain, self, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = (domain); \ - \ - if (nm_logging_enabled(__level, __domain)) { \ - int __errsv = (errsv); \ - \ - /* The %m format specifier (GNU extension) would already allow you to specify the error - * message conveniently (and nm_log would get that right too). But we don't want to depend - * on that, so instead append the message at the end. - * Currently, users are expected not to use %m in the format string. */ \ - _LOG_print( \ - __level, \ - __domain, \ - __errsv, \ - self, \ - _NM_UTILS_MACRO_FIRST(__VA_ARGS__) ": %s (%d)" _NM_UTILS_MACRO_REST(__VA_ARGS__), \ - nm_strerror_native(__errsv), \ - __errsv); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static void -delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpointer user_data); -static gboolean delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink); -static void do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name); -static void do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type); -static void cache_on_change(NMPlatform * platform, - NMPCacheOpsType cache_op, - const NMPObject *obj_old, - const NMPObject *obj_new); -static void cache_prune_all(NMPlatform *platform); -static gboolean event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks); -static struct nl_sock *_genl_sock(NMLinuxPlatform *platform); - -/*****************************************************************************/ - -static int -wait_for_nl_response_to_nmerr(WaitForNlResponseResult seq_result) -{ - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; - if (seq_result < 0) - return (int) seq_result; - return -NME_PL_NETLINK; -} - -static const char * -wait_for_nl_response_to_string(WaitForNlResponseResult seq_result, - const char * errmsg, - char * buf, - gsize buf_size) -{ - char *buf0 = buf; - - switch (seq_result) { - case WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN: - nm_utils_strbuf_append_str(&buf, &buf_size, "unknown"); - break; - case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK: - nm_utils_strbuf_append_str(&buf, &buf_size, "success"); - break; - case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN: - nm_utils_strbuf_append_str(&buf, &buf_size, "failure"); - break; - default: - if (seq_result < 0) { - nm_utils_strbuf_append(&buf, - &buf_size, - "failure %d (%s%s%s)", - -((int) seq_result), - nm_strerror_native(-((int) seq_result)), - errmsg ? " - " : "", - errmsg ?: ""); - } else - nm_utils_strbuf_append(&buf, &buf_size, "internal failure %d", (int) seq_result); - break; - } - return buf0; -} - -/****************************************************************** - * Various utilities - ******************************************************************/ - -static int -_vlan_qos_mapping_cmp_from(gconstpointer a, gconstpointer b, gpointer user_data) -{ - const NMVlanQosMapping *map_a = a; - const NMVlanQosMapping *map_b = b; - - if (map_a->from != map_b->from) - return map_a->from < map_b->from ? -1 : 1; - return 0; -} - -static int -_vlan_qos_mapping_cmp_from_ptr(gconstpointer a, gconstpointer b, gpointer user_data) -{ - return _vlan_qos_mapping_cmp_from(*((const NMVlanQosMapping **) a), - *((const NMVlanQosMapping **) b), - NULL); -} - -/****************************************************************** - * NMLinkType functions - ******************************************************************/ - -typedef struct { - const char *type_string; - - /* IFLA_INFO_KIND / rtnl_link_get_type() where applicable; the rtnl type - * should only be specified if the device type can be created without - * additional parameters, and if the device type can be determined from - * the rtnl_type. eg, tun/tap should not be specified since both - * tun and tap devices use "tun", and InfiniBand should not be - * specified because a PKey is required at creation. Drivers set this - * value from their 'struct rtnl_link_ops' structure. - */ - const char *rtnl_type; - - /* uevent DEVTYPE where applicable, from /sys/class/net/<ifname>/uevent; - * drivers set this value from their SET_NETDEV_DEV() call and the - * 'struct device_type' name member. - */ - const char *devtype; -} LinkDesc; - -static const LinkDesc link_descs[] = { - [NM_LINK_TYPE_NONE] = {"none", NULL, NULL}, - [NM_LINK_TYPE_UNKNOWN] = {"unknown", NULL, NULL}, - [NM_LINK_TYPE_ANY] = {"any", NULL, NULL}, - - [NM_LINK_TYPE_ETHERNET] = {"ethernet", NULL, NULL}, - [NM_LINK_TYPE_INFINIBAND] = {"infiniband", NULL, NULL}, - [NM_LINK_TYPE_OLPC_MESH] = {"olpc-mesh", NULL, NULL}, - [NM_LINK_TYPE_WIFI] = {"wifi", NULL, "wlan"}, - [NM_LINK_TYPE_WWAN_NET] = {"wwan", NULL, "wwan"}, - [NM_LINK_TYPE_WIMAX] = {"wimax", "wimax", "wimax"}, - [NM_LINK_TYPE_WPAN] = {"wpan", NULL, NULL}, - [NM_LINK_TYPE_6LOWPAN] = {"6lowpan", NULL, NULL}, - - [NM_LINK_TYPE_BNEP] = {"bluetooth", NULL, "bluetooth"}, - [NM_LINK_TYPE_DUMMY] = {"dummy", "dummy", NULL}, - [NM_LINK_TYPE_GRE] = {"gre", "gre", NULL}, - [NM_LINK_TYPE_GRETAP] = {"gretap", "gretap", NULL}, - [NM_LINK_TYPE_IFB] = {"ifb", "ifb", NULL}, - [NM_LINK_TYPE_IP6TNL] = {"ip6tnl", "ip6tnl", NULL}, - [NM_LINK_TYPE_IP6GRE] = {"ip6gre", "ip6gre", NULL}, - [NM_LINK_TYPE_IP6GRETAP] = {"ip6gretap", "ip6gretap", NULL}, - [NM_LINK_TYPE_IPIP] = {"ipip", "ipip", NULL}, - [NM_LINK_TYPE_LOOPBACK] = {"loopback", NULL, NULL}, - [NM_LINK_TYPE_MACSEC] = {"macsec", "macsec", NULL}, - [NM_LINK_TYPE_MACVLAN] = {"macvlan", "macvlan", NULL}, - [NM_LINK_TYPE_MACVTAP] = {"macvtap", "macvtap", NULL}, - [NM_LINK_TYPE_OPENVSWITCH] = {"openvswitch", "openvswitch", NULL}, - [NM_LINK_TYPE_PPP] = {"ppp", NULL, "ppp"}, - [NM_LINK_TYPE_SIT] = {"sit", "sit", NULL}, - [NM_LINK_TYPE_TUN] = {"tun", "tun", NULL}, - [NM_LINK_TYPE_VETH] = {"veth", "veth", NULL}, - [NM_LINK_TYPE_VLAN] = {"vlan", "vlan", "vlan"}, - [NM_LINK_TYPE_VRF] = {"vrf", "vrf", "vrf"}, - [NM_LINK_TYPE_VXLAN] = {"vxlan", "vxlan", "vxlan"}, - [NM_LINK_TYPE_WIREGUARD] = {"wireguard", "wireguard", "wireguard"}, - - [NM_LINK_TYPE_BRIDGE] = {"bridge", "bridge", "bridge"}, - [NM_LINK_TYPE_BOND] = {"bond", "bond", "bond"}, - [NM_LINK_TYPE_TEAM] = {"team", "team", NULL}, -}; - -static const LinkDesc * -_link_desc_from_link_type(NMLinkType link_type) -{ - nm_assert(_NM_INT_NOT_NEGATIVE(link_type)); - nm_assert(link_type < G_N_ELEMENTS(link_descs)); - nm_assert(link_descs[link_type].type_string); - - return &link_descs[link_type]; -} - -static NMLinkType -_link_type_from_rtnl_type(const char *name) -{ - static const NMLinkType LIST[] = { - NM_LINK_TYPE_BOND, /* "bond" */ - NM_LINK_TYPE_BRIDGE, /* "bridge" */ - NM_LINK_TYPE_DUMMY, /* "dummy" */ - NM_LINK_TYPE_GRE, /* "gre" */ - NM_LINK_TYPE_GRETAP, /* "gretap" */ - NM_LINK_TYPE_IFB, /* "ifb" */ - NM_LINK_TYPE_IP6GRE, /* "ip6gre" */ - NM_LINK_TYPE_IP6GRETAP, /* "ip6gretap" */ - NM_LINK_TYPE_IP6TNL, /* "ip6tnl" */ - NM_LINK_TYPE_IPIP, /* "ipip" */ - NM_LINK_TYPE_MACSEC, /* "macsec" */ - NM_LINK_TYPE_MACVLAN, /* "macvlan" */ - NM_LINK_TYPE_MACVTAP, /* "macvtap" */ - NM_LINK_TYPE_OPENVSWITCH, /* "openvswitch" */ - NM_LINK_TYPE_SIT, /* "sit" */ - NM_LINK_TYPE_TEAM, /* "team" */ - NM_LINK_TYPE_TUN, /* "tun" */ - NM_LINK_TYPE_VETH, /* "veth" */ - NM_LINK_TYPE_VLAN, /* "vlan" */ - NM_LINK_TYPE_VRF, /* "vrf" */ - NM_LINK_TYPE_VXLAN, /* "vxlan" */ - NM_LINK_TYPE_WIMAX, /* "wimax" */ - NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ - }; - - nm_assert(name); - - if (NM_MORE_ASSERT_ONCE(5)) { - int i, j, k; - - for (i = 0; i < G_N_ELEMENTS(LIST); i++) { - nm_assert(_link_desc_from_link_type(LIST[i]) == &link_descs[LIST[i]]); - nm_assert(link_descs[LIST[i]].rtnl_type); - if (i > 0) - nm_assert(strcmp(link_descs[LIST[i - 1]].rtnl_type, link_descs[LIST[i]].rtnl_type) - < 0); - } - for (i = 0; i < G_N_ELEMENTS(link_descs); i++) { - if (!link_descs[i].rtnl_type) - continue; - for (j = 0, k = 0; j < G_N_ELEMENTS(LIST); j++) - k += (LIST[j] == i); - nm_assert(k == 1); - } - } - - { - int imin = 0; - int imax = (G_N_ELEMENTS(LIST) - 1); - int imid = (G_N_ELEMENTS(LIST) - 1) / 2; - - for (;;) { - const int cmp = strcmp(link_descs[LIST[imid]].rtnl_type, name); - - if (G_UNLIKELY(cmp == 0)) - return LIST[imid]; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - - if (G_UNLIKELY(imin > imax)) - return NM_LINK_TYPE_NONE; - - imid = (imin + imax) / 2; - } - } -} - -static NMLinkType -_link_type_from_devtype(const char *name) -{ - static const NMLinkType LIST[] = { - NM_LINK_TYPE_BNEP, /* "bluetooth" */ - NM_LINK_TYPE_BOND, /* "bond" */ - NM_LINK_TYPE_BRIDGE, /* "bridge" */ - NM_LINK_TYPE_PPP, /* "ppp" */ - NM_LINK_TYPE_VLAN, /* "vlan" */ - NM_LINK_TYPE_VRF, /* "vrf" */ - NM_LINK_TYPE_VXLAN, /* "vxlan" */ - NM_LINK_TYPE_WIMAX, /* "wimax" */ - NM_LINK_TYPE_WIREGUARD, /* "wireguard" */ - NM_LINK_TYPE_WIFI, /* "wlan" */ - NM_LINK_TYPE_WWAN_NET, /* "wwan" */ - }; - - nm_assert(name); - - if (NM_MORE_ASSERT_ONCE(5)) { - int i, j, k; - - for (i = 0; i < G_N_ELEMENTS(LIST); i++) { - nm_assert(_link_desc_from_link_type(LIST[i]) == &link_descs[LIST[i]]); - nm_assert(link_descs[LIST[i]].devtype); - if (i > 0) - nm_assert(strcmp(link_descs[LIST[i - 1]].devtype, link_descs[LIST[i]].devtype) < 0); - } - for (i = 0; i < G_N_ELEMENTS(link_descs); i++) { - if (!link_descs[i].devtype) - continue; - for (j = 0, k = 0; j < G_N_ELEMENTS(LIST); j++) - k += (LIST[j] == i); - nm_assert(k == 1); - } - } - - { - int imin = 0; - int imax = (G_N_ELEMENTS(LIST) - 1); - int imid = (G_N_ELEMENTS(LIST) - 1) / 2; - - for (;;) { - const int cmp = strcmp(link_descs[LIST[imid]].devtype, name); - - if (G_UNLIKELY(cmp == 0)) - return LIST[imid]; - - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; - - if (G_UNLIKELY(imin > imax)) - return NM_LINK_TYPE_NONE; - - imid = (imin + imax) / 2; - } - } -} - -static const char * -nm_link_type_to_rtnl_type_string(NMLinkType link_type) -{ - return _link_desc_from_link_type(link_type)->rtnl_type; -} - -const char * -nm_link_type_to_string(NMLinkType link_type) -{ - return _link_desc_from_link_type(link_type)->type_string; -} - -/****************************************************************** - * Utilities - ******************************************************************/ - -/* _timestamp_nl_to_ms: - * @timestamp_nl: a timestamp from ifa_cacheinfo. - * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current - * uptime and how often timestamp_nl wrapped. - * - * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds. - * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts - * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However, - * the uint32 counter wraps every 497 days of uptime, so we have to compensate - * for that. */ -static gint64 -_timestamp_nl_to_ms(guint32 timestamp_nl, gint64 monotonic_ms) -{ - const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); - gint64 timestamp_nl_ms; - - /* convert timestamp from 1/100th of a second to msec. */ - timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); - - /* timestamp wraps every 497 days. Try to compensate for that.*/ - if (timestamp_nl_ms > monotonic_ms) { - /* timestamp_nl_ms is in the future. Truncate it to *now* */ - timestamp_nl_ms = monotonic_ms; - } else if (monotonic_ms >= WRAP_INTERVAL) { - timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; - if (timestamp_nl_ms > monotonic_ms) - timestamp_nl_ms -= WRAP_INTERVAL; - } - - return timestamp_nl_ms; -} - -static guint32 -_addrtime_timestamp_to_nm(guint32 timestamp, gint32 *out_now_nm) -{ - gint64 now_nl; - gint64 now_nm; - gint64 result; - - /* timestamp is unset. Default to 1. */ - if (!timestamp) { - NM_SET_OUT(out_now_nm, 0); - return 1; - } - - /* do all the calculations in milliseconds scale */ - - now_nm = nm_utils_get_monotonic_timestamp_msec(); - now_nl = nm_utils_clock_gettime_msec(CLOCK_MONOTONIC); - - nm_assert(now_nm >= 1000); - nm_assert(now_nl >= 0); - - result = now_nm - (now_nl - _timestamp_nl_to_ms(timestamp, now_nl)); - - NM_SET_OUT(out_now_nm, now_nm / 1000); - - /* converting the timestamp into nm_utils_get_monotonic_timestamp_msec() scale is - * a good guess but fails in the following situations: - * - * - If the address existed before start of the process, the timestamp in nm scale would - * be negative or zero. In this case we default to 1. - * - during hibernation, the CLOCK_MONOTONIC/timestamp drifts from - * nm_utils_get_monotonic_timestamp_msec() scale. - */ - if (result <= 1000) - return 1; - - if (result > now_nm) - return now_nm / 1000; - - return result / 1000; -} - -static guint32 -_addrtime_extend_lifetime(guint32 lifetime, guint32 seconds) -{ - guint64 v; - - if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT || seconds == 0) - return lifetime; - - v = (guint64) lifetime + (guint64) seconds; - return MIN(v, NM_PLATFORM_LIFETIME_PERMANENT - 1); -} - -/* The rtnl_addr object contains relative lifetimes @valid and @preferred - * that count in seconds, starting from the moment when the kernel constructed - * the netlink message. - * - * There is also a field rtnl_addr_last_update_time(), which is the absolute - * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address - * was modified (wrapping every 497 days). - * Immediately at the time when the address was last modified, #NOW and @last_update_time - * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. - * However, this is not true in general. As time goes by, whenever kernel sends a new address - * via netlink, the lifetimes keep counting down. - **/ -static void -_addrtime_get_lifetimes(guint32 timestamp, - guint32 lifetime, - guint32 preferred, - guint32 *out_timestamp, - guint32 *out_lifetime, - guint32 *out_preferred) -{ - gint32 now; - - if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { - if (preferred > lifetime) - preferred = lifetime; - timestamp = _addrtime_timestamp_to_nm(timestamp, &now); - - if (now == 0) { - /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ - nm_assert(timestamp == 1); - now = nm_utils_get_monotonic_timestamp_sec(); - } - if (timestamp < now) { - guint32 diff = now - timestamp; - - lifetime = _addrtime_extend_lifetime(lifetime, diff); - preferred = _addrtime_extend_lifetime(preferred, diff); - } else - nm_assert(timestamp == now); - } else - timestamp = 0; - *out_timestamp = timestamp; - *out_lifetime = lifetime; - *out_preferred = preferred; -} - -/*****************************************************************************/ - -static const NMPObject * -_lookup_cached_link(const NMPCache * cache, - int ifindex, - gboolean * completed_from_cache, - const NMPObject **link_cached) -{ - const NMPObject *obj; - - nm_assert(completed_from_cache && link_cached); - - if (!*completed_from_cache) { - obj = ifindex > 0 && cache ? nmp_cache_lookup_link(cache, ifindex) : NULL; - - *link_cached = obj; - *completed_from_cache = TRUE; - } - return *link_cached; -} - -/*****************************************************************************/ - -#define DEVTYPE_PREFIX "DEVTYPE=" - -static char * -_linktype_read_devtype(int dirfd) -{ - gs_free char *contents = NULL; - char * cont, *end; - - nm_assert(dirfd >= 0); - - if (!nm_utils_file_get_contents(dirfd, - "uevent", - 1 * 1024 * 1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - NULL, - NULL, - NULL)) - return NULL; - for (cont = contents; cont; cont = end) { - end = strpbrk(cont, "\r\n"); - if (end) - *end++ = '\0'; - if (strncmp(cont, DEVTYPE_PREFIX, NM_STRLEN(DEVTYPE_PREFIX)) == 0) { - cont += NM_STRLEN(DEVTYPE_PREFIX); - memmove(contents, cont, strlen(cont) + 1); - return g_steal_pointer(&contents); - } - } - return NULL; -} - -static NMLinkType -_linktype_get_type(NMPlatform * platform, - const NMPCache * cache, - const char * kind, - int ifindex, - const char * ifname, - unsigned flags, - unsigned arptype, - gboolean * completed_from_cache, - const NMPObject **link_cached, - const char ** out_kind) -{ - NMLinkType link_type; - - NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform); - nm_assert(ifname); - nm_assert(_link_type_from_devtype("wlan") == NM_LINK_TYPE_WIFI); - nm_assert(_link_type_from_rtnl_type("bond") == NM_LINK_TYPE_BOND); - - if (completed_from_cache) { - const NMPObject *obj; - - obj = _lookup_cached_link(cache, ifindex, completed_from_cache, link_cached); - - /* If we detected the link type before, we stick to that - * decision unless the "kind" or "name" changed. If "name" changed, - * it means that their type may not have been determined correctly - * due to race conditions while accessing sysfs. - * - * This way, we save additional ethtool/sysctl lookups, but moreover, - * we keep the linktype stable and don't change it as long as the link - * exists. - * - * Note that kernel *can* reuse the ifindex (on integer overflow, and - * when moving interface to other netns). Thus here there is a tiny potential - * of messing stuff up. */ - if (obj && obj->_link.netlink.is_in_netlink - && !NM_IN_SET(obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) - && nm_streq(ifname, obj->link.name) && (!kind || nm_streq0(kind, obj->link.kind))) { - nm_assert(obj->link.kind == g_intern_string(obj->link.kind)); - *out_kind = obj->link.kind; - return obj->link.type; - } - } - - /* we intern kind to not require us to keep the pointer alive. Essentially - * leaking it in a global cache. That should be safe enough, because the - * kind comes only from kernel messages, which depend on the number of - * available drivers. So, there is not the danger that we leak uncontrolled - * many kinds. */ - *out_kind = g_intern_string(kind); - - if (kind) { - link_type = _link_type_from_rtnl_type(kind); - if (link_type != NM_LINK_TYPE_NONE) - return link_type; - } - - if (arptype == ARPHRD_LOOPBACK) - return NM_LINK_TYPE_LOOPBACK; - else if (arptype == ARPHRD_INFINIBAND) - return NM_LINK_TYPE_INFINIBAND; - else if (arptype == ARPHRD_SIT) - return NM_LINK_TYPE_SIT; - else if (arptype == ARPHRD_TUNNEL6) - return NM_LINK_TYPE_IP6TNL; - else if (arptype == ARPHRD_PPP) - return NM_LINK_TYPE_PPP; - else if (arptype == ARPHRD_IEEE802154) - return NM_LINK_TYPE_WPAN; - else if (arptype == ARPHRD_6LOWPAN) - return NM_LINK_TYPE_6LOWPAN; - - { - NMPUtilsEthtoolDriverInfo driver_info; - - /* Fallback OVS detection for kernel <= 3.16 */ - if (nmp_utils_ethtool_get_driver_info(ifindex, &driver_info)) { - if (nm_streq(driver_info.driver, "openvswitch")) - return NM_LINK_TYPE_OPENVSWITCH; - - if (arptype == 256) { - /* Some s390 CTC-type devices report 256 for the encapsulation type - * for some reason, but we need to call them Ethernet. - */ - if (nm_streq(driver_info.driver, "ctcm")) - return NM_LINK_TYPE_ETHERNET; - } - } - } - - { - nm_auto_close int dirfd = -1; - gs_free char * devtype = NULL; - char ifname_verified[IFNAMSIZ]; - - dirfd = nmp_utils_sysctl_open_netdir(ifindex, ifname, ifname_verified); - if (dirfd >= 0) { - if (faccessat(dirfd, "anycast_mask", F_OK, 0) == 0) - return NM_LINK_TYPE_OLPC_MESH; - - devtype = _linktype_read_devtype(dirfd); - if (devtype) { - link_type = _link_type_from_devtype(devtype); - if (link_type != NM_LINK_TYPE_NONE) { - if (link_type == NM_LINK_TYPE_BNEP && arptype != ARPHRD_ETHER) { - /* Both BNEP and 6lowpan use DEVTYPE=bluetooth, so we must - * use arptype to distinguish between them. - */ - } else - return link_type; - } - } - - /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ - if (nm_wifi_utils_is_wifi(dirfd, ifname_verified)) - return NM_LINK_TYPE_WIFI; - } - - if (arptype == ARPHRD_ETHER) { - /* Misc non-upstream WWAN drivers. rmnet is Qualcomm's proprietary - * modem interface, ccmni is MediaTek's. FIXME: these drivers should - * really set devtype=WWAN. - */ - if (g_str_has_prefix(ifname, "rmnet") || g_str_has_prefix(ifname, "rev_rmnet") - || g_str_has_prefix(ifname, "ccmni")) - return NM_LINK_TYPE_WWAN_NET; - - /* Standard wired ethernet interfaces don't report an rtnl_link_type, so - * only allow fallback to Ethernet if no type is given. This should - * prevent future virtual network drivers from being treated as Ethernet - * when they should be Generic instead. - */ - if (!kind && !devtype) - return NM_LINK_TYPE_ETHERNET; - - /* The USB gadget interfaces behave and look like ordinary ethernet devices - * aside from the DEVTYPE. */ - if (nm_streq0(devtype, "gadget")) - return NM_LINK_TYPE_ETHERNET; - - /* Distributed Switch Architecture switch chips */ - if (nm_streq0(devtype, "dsa")) - return NM_LINK_TYPE_ETHERNET; - } - } - - return NM_LINK_TYPE_UNKNOWN; -} - -/****************************************************************** - * libnl unility functions and wrappers - ******************************************************************/ - -#define NLMSG_TAIL(nmsg) ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) - -/* copied from iproute2's addattr_l(). */ -static gboolean -_nl_addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen) -{ - int len = RTA_LENGTH(alen); - struct rtattr *rta; - - if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) - return FALSE; - - rta = NLMSG_TAIL(n); - rta->rta_type = type; - rta->rta_len = len; - memcpy(RTA_DATA(rta), data, alen); - n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); - return TRUE; -} - -/****************************************************************** - * NMPObject/netlink functions - ******************************************************************/ - -#define _check_addr_or_return_val(tb, attr, addr_len, ret_val) \ - ({ \ - const struct nlattr *__t = (tb)[(attr)]; \ - \ - if (__t) { \ - if (nla_len(__t) != (addr_len)) { \ - return ret_val; \ - } \ - } \ - !!__t; \ - }) - -#define _check_addr_or_return_null(tb, attr, addr_len) \ - _check_addr_or_return_val(tb, attr, addr_len, NULL) - -/*****************************************************************************/ - -/* Copied and heavily modified from libnl3's inet6_parse_protinfo(). */ -static gboolean -_parse_af_inet6(NMPlatform * platform, - struct nlattr * attr, - NMUtilsIPv6IfaceId *out_token, - gboolean * out_token_valid, - guint8 * out_addr_gen_mode_inv, - gboolean * out_addr_gen_mode_valid) -{ - static const struct nla_policy policy[] = { - [IFLA_INET6_FLAGS] = {.type = NLA_U32}, - [IFLA_INET6_CACHEINFO] = {.minlen = nm_offsetofend(struct ifla_cacheinfo, retrans_time)}, - [IFLA_INET6_CONF] = {.minlen = 4}, - [IFLA_INET6_STATS] = {.minlen = 8}, - [IFLA_INET6_ICMP6STATS] = {.minlen = 8}, - [IFLA_INET6_TOKEN] = {.minlen = sizeof(struct in6_addr)}, - [IFLA_INET6_ADDR_GEN_MODE] = {.type = NLA_U8}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - struct in6_addr i6_token; - gboolean token_valid = FALSE; - gboolean addr_gen_mode_valid = FALSE; - guint8 i6_addr_gen_mode_inv = 0; - - if (nla_parse_nested_arr(tb, attr, policy) < 0) - return FALSE; - - if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) - return FALSE; - if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) - return FALSE; - if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) - return FALSE; - - if (_check_addr_or_return_val(tb, IFLA_INET6_TOKEN, sizeof(struct in6_addr), FALSE)) { - nla_memcpy(&i6_token, tb[IFLA_INET6_TOKEN], sizeof(struct in6_addr)); - token_valid = TRUE; - } - - /* Hack to detect support addrgenmode of the kernel. We only parse - * netlink messages that we receive from kernel, hence this check - * is valid. */ - if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { - /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ - _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL, - tb[IFLA_INET6_ADDR_GEN_MODE] ? 1 : -1); - } - - if (tb[IFLA_INET6_ADDR_GEN_MODE]) { - i6_addr_gen_mode_inv = _nm_platform_uint8_inv(nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE])); - if (i6_addr_gen_mode_inv == 0) { - /* an inverse addrgenmode of zero is unexpected. We need to reserve zero - * to signal "unset". */ - return FALSE; - } - addr_gen_mode_valid = TRUE; - } - - if (token_valid) { - *out_token_valid = token_valid; - nm_utils_ipv6_interface_identifier_get_from_addr(out_token, &i6_token); - } - if (addr_gen_mode_valid) { - *out_addr_gen_mode_valid = addr_gen_mode_valid; - *out_addr_gen_mode_inv = i6_addr_gen_mode_inv; - } - return TRUE; -} - -/*****************************************************************************/ - -static NMPObject * -_parse_lnk_bridge(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_BR_FORWARD_DELAY] = {.type = NLA_U32}, - [IFLA_BR_HELLO_TIME] = {.type = NLA_U32}, - [IFLA_BR_MAX_AGE] = {.type = NLA_U32}, - [IFLA_BR_AGEING_TIME] = {.type = NLA_U32}, - [IFLA_BR_STP_STATE] = {.type = NLA_U32}, - [IFLA_BR_PRIORITY] = {.type = NLA_U16}, - [IFLA_BR_VLAN_PROTOCOL] = {.type = NLA_U16}, - [IFLA_BR_VLAN_STATS_ENABLED] = {.type = NLA_U8}, - [IFLA_BR_GROUP_FWD_MASK] = {.type = NLA_U16}, - [IFLA_BR_GROUP_ADDR] = {.minlen = sizeof(NMEtherAddr)}, - [IFLA_BR_MCAST_SNOOPING] = {.type = NLA_U8}, - [IFLA_BR_MCAST_ROUTER] = {.type = NLA_U8}, - [IFLA_BR_MCAST_QUERY_USE_IFADDR] = {.type = NLA_U8}, - [IFLA_BR_MCAST_QUERIER] = {.type = NLA_U8}, - [IFLA_BR_MCAST_HASH_MAX] = {.type = NLA_U32}, - [IFLA_BR_MCAST_LAST_MEMBER_CNT] = {.type = NLA_U32}, - [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = {.type = NLA_U32}, - [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = {.type = NLA_U64}, - [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = {.type = NLA_U64}, - [IFLA_BR_MCAST_QUERIER_INTVL] = {.type = NLA_U64}, - [IFLA_BR_MCAST_QUERY_INTVL] = {.type = NLA_U64}, - [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = {.type = NLA_U64}, - [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = {.type = NLA_U64}, - }; - NMPlatformLnkBridge *props; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - - if (!info_data || !nm_streq0(kind, "bridge")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_BRIDGE, NULL); - - props = &obj->lnk_bridge; - *props = nm_platform_lnk_bridge_default; - - if (!_nm_platform_kernel_support_detected( - NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED)) { - /* IFLA_BR_VLAN_STATS_ENABLED was added in kernel 4.10 on April 30, 2016. - * See commit 6dada9b10a0818ba72c249526a742c8c41274a73. */ - _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED, - tb[IFLA_BR_VLAN_STATS_ENABLED] ? 1 : -1); - } - - if (tb[IFLA_BR_FORWARD_DELAY]) - props->forward_delay = nla_get_u32(tb[IFLA_BR_FORWARD_DELAY]); - if (tb[IFLA_BR_HELLO_TIME]) - props->hello_time = nla_get_u32(tb[IFLA_BR_HELLO_TIME]); - if (tb[IFLA_BR_MAX_AGE]) - props->max_age = nla_get_u32(tb[IFLA_BR_MAX_AGE]); - if (tb[IFLA_BR_AGEING_TIME]) - props->ageing_time = nla_get_u32(tb[IFLA_BR_AGEING_TIME]); - if (tb[IFLA_BR_STP_STATE]) - props->stp_state = !!nla_get_u32(tb[IFLA_BR_STP_STATE]); - if (tb[IFLA_BR_PRIORITY]) - props->priority = nla_get_u16(tb[IFLA_BR_PRIORITY]); - if (tb[IFLA_BR_VLAN_PROTOCOL]) - props->vlan_protocol = ntohs(nla_get_u16(tb[IFLA_BR_VLAN_PROTOCOL])); - if (tb[IFLA_BR_VLAN_STATS_ENABLED]) - props->vlan_stats_enabled = nla_get_u8(tb[IFLA_BR_VLAN_STATS_ENABLED]); - if (tb[IFLA_BR_GROUP_FWD_MASK]) - props->group_fwd_mask = nla_get_u16(tb[IFLA_BR_GROUP_FWD_MASK]); - if (tb[IFLA_BR_GROUP_ADDR]) - props->group_addr = *nla_data_as(NMEtherAddr, tb[IFLA_BR_GROUP_ADDR]); - if (tb[IFLA_BR_MCAST_SNOOPING]) - props->mcast_snooping = !!nla_get_u8(tb[IFLA_BR_MCAST_SNOOPING]); - if (tb[IFLA_BR_MCAST_ROUTER]) - props->mcast_router = nla_get_u8(tb[IFLA_BR_MCAST_ROUTER]); - if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]) - props->mcast_query_use_ifaddr = !!nla_get_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]); - if (tb[IFLA_BR_MCAST_QUERIER]) - props->mcast_querier = nla_get_u8(tb[IFLA_BR_MCAST_QUERIER]); - if (tb[IFLA_BR_MCAST_HASH_MAX]) - props->mcast_hash_max = nla_get_u32(tb[IFLA_BR_MCAST_HASH_MAX]); - if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]) - props->mcast_last_member_count = nla_get_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]); - if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) - props->mcast_startup_query_count = nla_get_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]); - if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) - props->mcast_last_member_interval = nla_get_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]); - if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) - props->mcast_membership_interval = nla_get_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]); - if (tb[IFLA_BR_MCAST_QUERIER_INTVL]) - props->mcast_querier_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL]); - if (tb[IFLA_BR_MCAST_QUERY_INTVL]) - props->mcast_query_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERY_INTVL]); - if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) - props->mcast_query_response_interval = nla_get_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]); - if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) - props->mcast_startup_query_interval = nla_get_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]); - - return obj; -} - -/***********************************************************************************/ - -static NMPObject * -_parse_lnk_gre(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_GRE_LINK] = {.type = NLA_U32}, - [IFLA_GRE_IFLAGS] = {.type = NLA_U16}, - [IFLA_GRE_OFLAGS] = {.type = NLA_U16}, - [IFLA_GRE_IKEY] = {.type = NLA_U32}, - [IFLA_GRE_OKEY] = {.type = NLA_U32}, - [IFLA_GRE_LOCAL] = {.type = NLA_U32}, - [IFLA_GRE_REMOTE] = {.type = NLA_U32}, - [IFLA_GRE_TTL] = {.type = NLA_U8}, - [IFLA_GRE_TOS] = {.type = NLA_U8}, - [IFLA_GRE_PMTUDISC] = {.type = NLA_U8}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - NMPlatformLnkGre *props; - gboolean is_tap; - - if (!info_data || !kind) - return NULL; - - if (nm_streq(kind, "gretap")) - is_tap = TRUE; - else if (nm_streq(kind, "gre")) - is_tap = FALSE; - else - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(is_tap ? NMP_OBJECT_TYPE_LNK_GRETAP : NMP_OBJECT_TYPE_LNK_GRE, NULL); - props = &obj->lnk_gre; - - props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32(tb[IFLA_GRE_LINK]) : 0; - props->input_flags = tb[IFLA_GRE_IFLAGS] ? ntohs(nla_get_u16(tb[IFLA_GRE_IFLAGS])) : 0; - props->output_flags = tb[IFLA_GRE_OFLAGS] ? ntohs(nla_get_u16(tb[IFLA_GRE_OFLAGS])) : 0; - props->input_key = tb[IFLA_GRE_IKEY] ? ntohl(nla_get_u32(tb[IFLA_GRE_IKEY])) : 0; - props->output_key = tb[IFLA_GRE_OKEY] ? ntohl(nla_get_u32(tb[IFLA_GRE_OKEY])) : 0; - props->local = tb[IFLA_GRE_LOCAL] ? nla_get_u32(tb[IFLA_GRE_LOCAL]) : 0; - props->remote = tb[IFLA_GRE_REMOTE] ? nla_get_u32(tb[IFLA_GRE_REMOTE]) : 0; - props->tos = tb[IFLA_GRE_TOS] ? nla_get_u8(tb[IFLA_GRE_TOS]) : 0; - props->ttl = tb[IFLA_GRE_TTL] ? nla_get_u8(tb[IFLA_GRE_TTL]) : 0; - props->path_mtu_discovery = !tb[IFLA_GRE_PMTUDISC] || !!nla_get_u8(tb[IFLA_GRE_PMTUDISC]); - props->is_tap = is_tap; - - return obj; -} - -/*****************************************************************************/ - -/* IFLA_IPOIB_* were introduced in the 3.7 kernel, but the kernel headers - * we're building against might not have those properties even though the - * running kernel might. - */ -#define IFLA_IPOIB_UNSPEC 0 -#define IFLA_IPOIB_PKEY 1 -#define IFLA_IPOIB_MODE 2 -#define IFLA_IPOIB_UMCAST 3 -#undef IFLA_IPOIB_MAX -#define IFLA_IPOIB_MAX IFLA_IPOIB_UMCAST - -#define IPOIB_MODE_DATAGRAM 0 /* using unreliable datagram QPs */ -#define IPOIB_MODE_CONNECTED 1 /* using connected QPs */ - -static NMPObject * -_parse_lnk_infiniband(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_IPOIB_PKEY] = {.type = NLA_U16}, - [IFLA_IPOIB_MODE] = {.type = NLA_U16}, - [IFLA_IPOIB_UMCAST] = {.type = NLA_U16}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPlatformLnkInfiniband *info; - NMPObject * obj; - const char * mode; - - if (!info_data || !nm_streq0(kind, "ipoib")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE]) - return NULL; - - switch (nla_get_u16(tb[IFLA_IPOIB_MODE])) { - case IPOIB_MODE_DATAGRAM: - mode = "datagram"; - break; - case IPOIB_MODE_CONNECTED: - mode = "connected"; - break; - default: - return NULL; - } - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); - info = &obj->lnk_infiniband; - - info->p_key = nla_get_u16(tb[IFLA_IPOIB_PKEY]); - info->mode = mode; - - return obj; -} - -/*****************************************************************************/ - -static NMPObject * -_parse_lnk_ip6tnl(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_IPTUN_LINK] = {.type = NLA_U32}, - [IFLA_IPTUN_LOCAL] = {.minlen = sizeof(struct in6_addr)}, - [IFLA_IPTUN_REMOTE] = {.minlen = sizeof(struct in6_addr)}, - [IFLA_IPTUN_TTL] = {.type = NLA_U8}, - [IFLA_IPTUN_ENCAP_LIMIT] = {.type = NLA_U8}, - [IFLA_IPTUN_FLOWINFO] = {.type = NLA_U32}, - [IFLA_IPTUN_PROTO] = {.type = NLA_U8}, - [IFLA_IPTUN_FLAGS] = {.type = NLA_U32}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - NMPlatformLnkIp6Tnl *props; - guint32 flowinfo; - - if (!info_data || !nm_streq0(kind, "ip6tnl")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_IP6TNL, NULL); - props = &obj->lnk_ip6tnl; - - if (tb[IFLA_IPTUN_LINK]) - props->parent_ifindex = nla_get_u32(tb[IFLA_IPTUN_LINK]); - if (tb[IFLA_IPTUN_LOCAL]) - props->local = *nla_data_as(struct in6_addr, tb[IFLA_IPTUN_LOCAL]); - if (tb[IFLA_IPTUN_REMOTE]) - props->remote = *nla_data_as(struct in6_addr, tb[IFLA_IPTUN_REMOTE]); - if (tb[IFLA_IPTUN_TTL]) - props->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]); - if (tb[IFLA_IPTUN_ENCAP_LIMIT]) - props->encap_limit = nla_get_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]); - if (tb[IFLA_IPTUN_FLOWINFO]) { - flowinfo = ntohl(nla_get_u32(tb[IFLA_IPTUN_FLOWINFO])); - props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK; - props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT; - } - if (tb[IFLA_IPTUN_PROTO]) - props->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]); - if (tb[IFLA_IPTUN_FLAGS]) - props->flags = nla_get_u32(tb[IFLA_IPTUN_FLAGS]); - - return obj; -} - -static NMPObject * -_parse_lnk_ip6gre(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_GRE_LINK] = {.type = NLA_U32}, - [IFLA_GRE_IFLAGS] = {.type = NLA_U16}, - [IFLA_GRE_OFLAGS] = {.type = NLA_U16}, - [IFLA_GRE_IKEY] = {.type = NLA_U32}, - [IFLA_GRE_OKEY] = {.type = NLA_U32}, - [IFLA_GRE_LOCAL] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, - [IFLA_GRE_REMOTE] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, - [IFLA_GRE_TTL] = {.type = NLA_U8}, - [IFLA_GRE_ENCAP_LIMIT] = {.type = NLA_U8}, - [IFLA_GRE_FLOWINFO] = {.type = NLA_U32}, - [IFLA_GRE_FLAGS] = {.type = NLA_U32}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - NMPlatformLnkIp6Tnl *props; - guint32 flowinfo; - gboolean is_tap; - - if (!info_data || !kind) - return NULL; - - if (nm_streq(kind, "ip6gre")) - is_tap = FALSE; - else if (nm_streq(kind, "ip6gretap")) - is_tap = TRUE; - else - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(is_tap ? NMP_OBJECT_TYPE_LNK_IP6GRETAP : NMP_OBJECT_TYPE_LNK_IP6GRE, NULL); - props = &obj->lnk_ip6tnl; - props->is_gre = TRUE; - props->is_tap = is_tap; - - if (tb[IFLA_GRE_LINK]) - props->parent_ifindex = nla_get_u32(tb[IFLA_GRE_LINK]); - if (tb[IFLA_GRE_IFLAGS]) - props->input_flags = ntohs(nla_get_u16(tb[IFLA_GRE_IFLAGS])); - if (tb[IFLA_GRE_OFLAGS]) - props->output_flags = ntohs(nla_get_u16(tb[IFLA_GRE_OFLAGS])); - if (tb[IFLA_GRE_IKEY]) - props->input_key = ntohl(nla_get_u32(tb[IFLA_GRE_IKEY])); - if (tb[IFLA_GRE_OKEY]) - props->output_key = ntohl(nla_get_u32(tb[IFLA_GRE_OKEY])); - if (tb[IFLA_GRE_LOCAL]) - props->local = *nla_data_as(struct in6_addr, tb[IFLA_GRE_LOCAL]); - if (tb[IFLA_GRE_REMOTE]) - props->remote = *nla_data_as(struct in6_addr, tb[IFLA_GRE_REMOTE]); - if (tb[IFLA_GRE_TTL]) - props->ttl = nla_get_u8(tb[IFLA_GRE_TTL]); - if (tb[IFLA_GRE_ENCAP_LIMIT]) - props->encap_limit = nla_get_u8(tb[IFLA_GRE_ENCAP_LIMIT]); - if (tb[IFLA_GRE_FLOWINFO]) { - flowinfo = ntohl(nla_get_u32(tb[IFLA_GRE_FLOWINFO])); - props->flow_label = flowinfo & IP6_FLOWINFO_FLOWLABEL_MASK; - props->tclass = (flowinfo & IP6_FLOWINFO_TCLASS_MASK) >> IP6_FLOWINFO_TCLASS_SHIFT; - } - if (tb[IFLA_GRE_FLAGS]) - props->flags = nla_get_u32(tb[IFLA_GRE_FLAGS]); - - return obj; -} - -/*****************************************************************************/ - -static NMPObject * -_parse_lnk_ipip(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_IPTUN_LINK] = {.type = NLA_U32}, - [IFLA_IPTUN_LOCAL] = {.type = NLA_U32}, - [IFLA_IPTUN_REMOTE] = {.type = NLA_U32}, - [IFLA_IPTUN_TTL] = {.type = NLA_U8}, - [IFLA_IPTUN_TOS] = {.type = NLA_U8}, - [IFLA_IPTUN_PMTUDISC] = {.type = NLA_U8}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - NMPlatformLnkIpIp *props; - - if (!info_data || !nm_streq0(kind, "ipip")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_IPIP, NULL); - props = &obj->lnk_ipip; - - props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32(tb[IFLA_IPTUN_LINK]) : 0; - props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32(tb[IFLA_IPTUN_LOCAL]) : 0; - props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32(tb[IFLA_IPTUN_REMOTE]) : 0; - props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8(tb[IFLA_IPTUN_TOS]) : 0; - props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8(tb[IFLA_IPTUN_TTL]) : 0; - props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); - - return obj; -} - -/*****************************************************************************/ - -static NMPObject * -_parse_lnk_macvlan(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_MACVLAN_MODE] = {.type = NLA_U32}, - [IFLA_MACVLAN_FLAGS] = {.type = NLA_U16}, - }; - NMPlatformLnkMacvlan *props; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - gboolean tap; - - if (!info_data || !kind) - return NULL; - - if (nm_streq(kind, "macvlan")) - tap = FALSE; - else if (nm_streq(kind, "macvtap")) - tap = TRUE; - else - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - if (!tb[IFLA_MACVLAN_MODE]) - return NULL; - - obj = nmp_object_new(tap ? NMP_OBJECT_TYPE_LNK_MACVTAP : NMP_OBJECT_TYPE_LNK_MACVLAN, NULL); - props = &obj->lnk_macvlan; - props->mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]); - props->tap = tap; - - if (tb[IFLA_MACVLAN_FLAGS]) - props->no_promisc = - NM_FLAGS_HAS(nla_get_u16(tb[IFLA_MACVLAN_FLAGS]), MACVLAN_FLAG_NOPROMISC); - - return obj; -} - -/*****************************************************************************/ - -static NMPObject * -_parse_lnk_macsec(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_MACSEC_SCI] = {.type = NLA_U64}, - [IFLA_MACSEC_ICV_LEN] = {.type = NLA_U8}, - [IFLA_MACSEC_CIPHER_SUITE] = {.type = NLA_U64}, - [IFLA_MACSEC_WINDOW] = {.type = NLA_U32}, - [IFLA_MACSEC_ENCODING_SA] = {.type = NLA_U8}, - [IFLA_MACSEC_ENCRYPT] = {.type = NLA_U8}, - [IFLA_MACSEC_PROTECT] = {.type = NLA_U8}, - [IFLA_MACSEC_INC_SCI] = {.type = NLA_U8}, - [IFLA_MACSEC_ES] = {.type = NLA_U8}, - [IFLA_MACSEC_SCB] = {.type = NLA_U8}, - [IFLA_MACSEC_REPLAY_PROTECT] = {.type = NLA_U8}, - [IFLA_MACSEC_VALIDATION] = {.type = NLA_U8}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - NMPlatformLnkMacsec *props; - - if (!info_data || !nm_streq0(kind, "macsec")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_MACSEC, NULL); - props = &obj->lnk_macsec; - - if (tb[IFLA_MACSEC_SCI]) { - props->sci = nla_get_be64(tb[IFLA_MACSEC_SCI]); - } - if (tb[IFLA_MACSEC_ICV_LEN]) { - props->icv_length = nla_get_u8(tb[IFLA_MACSEC_ICV_LEN]); - } - if (tb[IFLA_MACSEC_CIPHER_SUITE]) { - props->cipher_suite = nla_get_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); - } - if (tb[IFLA_MACSEC_WINDOW]) { - props->window = nla_get_u32(tb[IFLA_MACSEC_WINDOW]); - } - if (tb[IFLA_MACSEC_ENCODING_SA]) { - props->encoding_sa = !!nla_get_u8(tb[IFLA_MACSEC_ENCODING_SA]); - } - if (tb[IFLA_MACSEC_ENCRYPT]) { - props->encrypt = !!nla_get_u8(tb[IFLA_MACSEC_ENCRYPT]); - } - if (tb[IFLA_MACSEC_PROTECT]) { - props->protect = !!nla_get_u8(tb[IFLA_MACSEC_PROTECT]); - } - if (tb[IFLA_MACSEC_INC_SCI]) { - props->include_sci = !!nla_get_u8(tb[IFLA_MACSEC_INC_SCI]); - } - if (tb[IFLA_MACSEC_ES]) { - props->es = !!nla_get_u8(tb[IFLA_MACSEC_ES]); - } - if (tb[IFLA_MACSEC_SCB]) { - props->scb = !!nla_get_u8(tb[IFLA_MACSEC_SCB]); - } - if (tb[IFLA_MACSEC_REPLAY_PROTECT]) { - props->replay_protect = !!nla_get_u8(tb[IFLA_MACSEC_REPLAY_PROTECT]); - } - if (tb[IFLA_MACSEC_VALIDATION]) { - props->validation = nla_get_u8(tb[IFLA_MACSEC_VALIDATION]); - } - - return obj; -} - -/*****************************************************************************/ - -static NMPObject * -_parse_lnk_sit(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_IPTUN_LINK] = {.type = NLA_U32}, - [IFLA_IPTUN_LOCAL] = {.type = NLA_U32}, - [IFLA_IPTUN_REMOTE] = {.type = NLA_U32}, - [IFLA_IPTUN_TTL] = {.type = NLA_U8}, - [IFLA_IPTUN_TOS] = {.type = NLA_U8}, - [IFLA_IPTUN_PMTUDISC] = {.type = NLA_U8}, - [IFLA_IPTUN_FLAGS] = {.type = NLA_U16}, - [IFLA_IPTUN_PROTO] = {.type = NLA_U8}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - NMPlatformLnkSit *props; - - if (!info_data || !nm_streq0(kind, "sit")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_SIT, NULL); - props = &obj->lnk_sit; - - props->parent_ifindex = tb[IFLA_IPTUN_LINK] ? nla_get_u32(tb[IFLA_IPTUN_LINK]) : 0; - props->local = tb[IFLA_IPTUN_LOCAL] ? nla_get_u32(tb[IFLA_IPTUN_LOCAL]) : 0; - props->remote = tb[IFLA_IPTUN_REMOTE] ? nla_get_u32(tb[IFLA_IPTUN_REMOTE]) : 0; - props->tos = tb[IFLA_IPTUN_TOS] ? nla_get_u8(tb[IFLA_IPTUN_TOS]) : 0; - props->ttl = tb[IFLA_IPTUN_TTL] ? nla_get_u8(tb[IFLA_IPTUN_TTL]) : 0; - props->path_mtu_discovery = !tb[IFLA_IPTUN_PMTUDISC] || !!nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]); - props->flags = tb[IFLA_IPTUN_FLAGS] ? nla_get_u16(tb[IFLA_IPTUN_FLAGS]) : 0; - props->proto = tb[IFLA_IPTUN_PROTO] ? nla_get_u8(tb[IFLA_IPTUN_PROTO]) : 0; - - return obj; -} - -/*****************************************************************************/ - -static NMPObject * -_parse_lnk_tun(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_TUN_OWNER] = {.type = NLA_U32}, - [IFLA_TUN_GROUP] = {.type = NLA_U32}, - [IFLA_TUN_TYPE] = {.type = NLA_U8}, - [IFLA_TUN_PI] = {.type = NLA_U8}, - [IFLA_TUN_VNET_HDR] = {.type = NLA_U8}, - [IFLA_TUN_PERSIST] = {.type = NLA_U8}, - [IFLA_TUN_MULTI_QUEUE] = {.type = NLA_U8}, - [IFLA_TUN_NUM_QUEUES] = {.type = NLA_U32}, - [IFLA_TUN_NUM_DISABLED_QUEUES] = {.type = NLA_U32}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - NMPlatformLnkTun *props; - - if (!info_data || !nm_streq0(kind, "tun")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - if (!tb[IFLA_TUN_TYPE]) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_TUN, NULL); - props = &obj->lnk_tun; - - props->type = nla_get_u8(tb[IFLA_TUN_TYPE]); - - props->pi = !!nla_get_u8_cond(tb, IFLA_TUN_PI, FALSE); - props->vnet_hdr = !!nla_get_u8_cond(tb, IFLA_TUN_VNET_HDR, FALSE); - props->multi_queue = !!nla_get_u8_cond(tb, IFLA_TUN_MULTI_QUEUE, FALSE); - props->persist = !!nla_get_u8_cond(tb, IFLA_TUN_PERSIST, FALSE); - - if (tb[IFLA_TUN_OWNER]) { - props->owner_valid = TRUE; - props->owner = nla_get_u32(tb[IFLA_TUN_OWNER]); - } - if (tb[IFLA_TUN_GROUP]) { - props->group_valid = TRUE; - props->group = nla_get_u32(tb[IFLA_TUN_GROUP]); - } - return obj; -} - -/*****************************************************************************/ - -static gboolean -_vlan_qos_mapping_from_nla(struct nlattr * nlattr, - const NMVlanQosMapping **out_map, - guint * out_n_map) -{ - struct nlattr * nla; - int remaining; - gs_unref_ptrarray GPtrArray *array = NULL; - - G_STATIC_ASSERT(sizeof(NMVlanQosMapping) == sizeof(struct ifla_vlan_qos_mapping)); - G_STATIC_ASSERT(sizeof(((NMVlanQosMapping *) 0)->to) - == sizeof(((struct ifla_vlan_qos_mapping *) 0)->to)); - G_STATIC_ASSERT(sizeof(((NMVlanQosMapping *) 0)->from) - == sizeof(((struct ifla_vlan_qos_mapping *) 0)->from)); - G_STATIC_ASSERT(sizeof(NMVlanQosMapping) - == sizeof(((NMVlanQosMapping *) 0)->from) - + sizeof(((NMVlanQosMapping *) 0)->to)); - - nm_assert(out_map && !*out_map); - nm_assert(out_n_map && !*out_n_map); - - if (!nlattr) - return TRUE; - - array = g_ptr_array_new(); - nla_for_each_nested (nla, nlattr, remaining) { - if (nla_len(nla) < sizeof(NMVlanQosMapping)) - return FALSE; - g_ptr_array_add(array, nla_data(nla)); - } - - if (array->len > 0) { - NMVlanQosMapping *list; - guint i, j; - - /* The sorting is necessary, because for egress mapping, kernel - * doesn't sent the items strictly sorted by the from field. */ - g_ptr_array_sort_with_data(array, _vlan_qos_mapping_cmp_from_ptr, NULL); - - list = g_new(NMVlanQosMapping, array->len); - - for (i = 0, j = 0; i < array->len; i++) { - NMVlanQosMapping *map; - - map = array->pdata[i]; - - /* kernel doesn't really send us duplicates. Just be extra cautious - * because we want strong guarantees about the sort order and uniqueness - * of our mapping list (for simpler equality comparison). */ - if (j > 0 && list[j - 1].from == map->from) - list[j - 1] = *map; - else - list[j++] = *map; - } - - *out_n_map = j; - *out_map = list; - } - - return TRUE; -} - -/* Copied and heavily modified from libnl3's vlan_parse() */ -static NMPObject * -_parse_lnk_vlan(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_VLAN_ID] = {.type = NLA_U16}, - [IFLA_VLAN_FLAGS] = {.minlen = nm_offsetofend(struct ifla_vlan_flags, flags)}, - [IFLA_VLAN_INGRESS_QOS] = {.type = NLA_NESTED}, - [IFLA_VLAN_EGRESS_QOS] = {.type = NLA_NESTED}, - [IFLA_VLAN_PROTOCOL] = {.type = NLA_U16}, - }; - struct nlattr *tb[G_N_ELEMENTS(policy)]; - nm_auto_nmpobj NMPObject *obj = NULL; - NMPObject * obj_result; - - if (!info_data || !nm_streq0(kind, "vlan")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - if (!tb[IFLA_VLAN_ID]) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VLAN, NULL); - obj->lnk_vlan.id = nla_get_u16(tb[IFLA_VLAN_ID]); - - if (tb[IFLA_VLAN_FLAGS]) { - struct ifla_vlan_flags flags; - - nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags)); - - obj->lnk_vlan.flags = flags.flags; - } - - if (!_vlan_qos_mapping_from_nla(tb[IFLA_VLAN_INGRESS_QOS], - &obj->_lnk_vlan.ingress_qos_map, - &obj->_lnk_vlan.n_ingress_qos_map)) - return NULL; - - if (!_vlan_qos_mapping_from_nla(tb[IFLA_VLAN_EGRESS_QOS], - &obj->_lnk_vlan.egress_qos_map, - &obj->_lnk_vlan.n_egress_qos_map)) - return NULL; - - obj_result = obj; - obj = NULL; - return obj_result; -} - -/*****************************************************************************/ - -/* The installed kernel headers might not have VXLAN stuff at all, or - * they might have the original properties, but not PORT, GROUP6, or LOCAL6. - * So until we depend on kernel >= 3.11, we just ignore the actual enum - * in if_link.h and define the values ourselves. - */ -#define IFLA_VXLAN_UNSPEC 0 -#define IFLA_VXLAN_ID 1 -#define IFLA_VXLAN_GROUP 2 -#define IFLA_VXLAN_LINK 3 -#define IFLA_VXLAN_LOCAL 4 -#define IFLA_VXLAN_TTL 5 -#define IFLA_VXLAN_TOS 6 -#define IFLA_VXLAN_LEARNING 7 -#define IFLA_VXLAN_AGEING 8 -#define IFLA_VXLAN_LIMIT 9 -#define IFLA_VXLAN_PORT_RANGE 10 -#define IFLA_VXLAN_PROXY 11 -#define IFLA_VXLAN_RSC 12 -#define IFLA_VXLAN_L2MISS 13 -#define IFLA_VXLAN_L3MISS 14 -#define IFLA_VXLAN_PORT 15 -#define IFLA_VXLAN_GROUP6 16 -#define IFLA_VXLAN_LOCAL6 17 -#undef IFLA_VXLAN_MAX -#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6 - -#define IFLA_VRF_TABLE 1 - -/* older kernel header might not contain 'struct ifla_vxlan_port_range'. - * Redefine it. */ -struct nm_ifla_vxlan_port_range { - guint16 low; - guint16 high; -}; - -static NMPObject * -_parse_lnk_vxlan(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_VXLAN_ID] = {.type = NLA_U32}, - [IFLA_VXLAN_GROUP] = {.type = NLA_U32}, - [IFLA_VXLAN_GROUP6] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, - [IFLA_VXLAN_LINK] = {.type = NLA_U32}, - [IFLA_VXLAN_LOCAL] = {.type = NLA_U32}, - [IFLA_VXLAN_LOCAL6] = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)}, - [IFLA_VXLAN_TOS] = {.type = NLA_U8}, - [IFLA_VXLAN_TTL] = {.type = NLA_U8}, - [IFLA_VXLAN_LEARNING] = {.type = NLA_U8}, - [IFLA_VXLAN_AGEING] = {.type = NLA_U32}, - [IFLA_VXLAN_LIMIT] = {.type = NLA_U32}, - [IFLA_VXLAN_PORT_RANGE] = {.type = NLA_UNSPEC, - .minlen = sizeof(struct nm_ifla_vxlan_port_range)}, - [IFLA_VXLAN_PROXY] = {.type = NLA_U8}, - [IFLA_VXLAN_RSC] = {.type = NLA_U8}, - [IFLA_VXLAN_L2MISS] = {.type = NLA_U8}, - [IFLA_VXLAN_L3MISS] = {.type = NLA_U8}, - [IFLA_VXLAN_PORT] = {.type = NLA_U16}, - }; - NMPlatformLnkVxlan *props; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - - if (!info_data || !nm_streq0(kind, "vxlan")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VXLAN, NULL); - - props = &obj->lnk_vxlan; - - if (tb[IFLA_VXLAN_LINK]) - props->parent_ifindex = nla_get_u32(tb[IFLA_VXLAN_LINK]); - if (tb[IFLA_VXLAN_ID]) - props->id = nla_get_u32(tb[IFLA_VXLAN_ID]); - if (tb[IFLA_VXLAN_GROUP]) - props->group = nla_get_u32(tb[IFLA_VXLAN_GROUP]); - if (tb[IFLA_VXLAN_LOCAL]) - props->local = nla_get_u32(tb[IFLA_VXLAN_LOCAL]); - if (tb[IFLA_VXLAN_LOCAL6]) - props->local6 = *nla_data_as(struct in6_addr, tb[IFLA_VXLAN_LOCAL6]); - if (tb[IFLA_VXLAN_GROUP6]) - props->group6 = *nla_data_as(struct in6_addr, tb[IFLA_VXLAN_GROUP6]); - - if (tb[IFLA_VXLAN_AGEING]) - props->ageing = nla_get_u32(tb[IFLA_VXLAN_AGEING]); - if (tb[IFLA_VXLAN_LIMIT]) - props->limit = nla_get_u32(tb[IFLA_VXLAN_LIMIT]); - if (tb[IFLA_VXLAN_TOS]) - props->tos = nla_get_u8(tb[IFLA_VXLAN_TOS]); - if (tb[IFLA_VXLAN_TTL]) - props->ttl = nla_get_u8(tb[IFLA_VXLAN_TTL]); - - if (tb[IFLA_VXLAN_PORT]) - props->dst_port = ntohs(nla_get_u16(tb[IFLA_VXLAN_PORT])); - - if (tb[IFLA_VXLAN_PORT_RANGE]) { - struct nm_ifla_vxlan_port_range *range; - - range = nla_data_as(struct nm_ifla_vxlan_port_range, tb[IFLA_VXLAN_PORT_RANGE]); - props->src_port_min = ntohs(range->low); - props->src_port_max = ntohs(range->high); - } - - if (tb[IFLA_VXLAN_LEARNING]) - props->learning = !!nla_get_u8(tb[IFLA_VXLAN_LEARNING]); - if (tb[IFLA_VXLAN_PROXY]) - props->proxy = !!nla_get_u8(tb[IFLA_VXLAN_PROXY]); - if (tb[IFLA_VXLAN_RSC]) - props->rsc = !!nla_get_u8(tb[IFLA_VXLAN_RSC]); - if (tb[IFLA_VXLAN_L2MISS]) - props->l2miss = !!nla_get_u8(tb[IFLA_VXLAN_L2MISS]); - if (tb[IFLA_VXLAN_L3MISS]) - props->l3miss = !!nla_get_u8(tb[IFLA_VXLAN_L3MISS]); - - return obj; -} - -static NMPObject * -_parse_lnk_vrf(const char *kind, struct nlattr *info_data) -{ - static const struct nla_policy policy[] = { - [IFLA_VRF_TABLE] = {.type = NLA_U32}, - }; - NMPlatformLnkVrf *props; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj; - - if (!info_data || !nm_streq0(kind, "vrf")) - return NULL; - - if (nla_parse_nested_arr(tb, info_data, policy) < 0) - return NULL; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_VRF, NULL); - - props = &obj->lnk_vrf; - - if (tb[IFLA_VRF_TABLE]) - props->table = nla_get_u32(tb[IFLA_VRF_TABLE]); - - return obj; -} - -/*****************************************************************************/ - -static gboolean -_wireguard_update_from_allowed_ips_nla(NMPWireGuardAllowedIP *allowed_ip, struct nlattr *nlattr) -{ - static const struct nla_policy policy[] = { - [WGALLOWEDIP_A_FAMILY] = {.type = NLA_U16}, - [WGALLOWEDIP_A_IPADDR] = {.minlen = sizeof(struct in_addr)}, - [WGALLOWEDIP_A_CIDR_MASK] = {.type = NLA_U8}, - }; - struct nlattr *tb[G_N_ELEMENTS(policy)]; - int family; - int addr_len; - - if (nla_parse_nested_arr(tb, nlattr, policy) < 0) - return FALSE; - - if (!tb[WGALLOWEDIP_A_FAMILY]) - return FALSE; - - family = nla_get_u16(tb[WGALLOWEDIP_A_FAMILY]); - if (family == AF_INET) - addr_len = sizeof(in_addr_t); - else if (family == AF_INET6) - addr_len = sizeof(struct in6_addr); - else - return FALSE; - - _check_addr_or_return_val(tb, WGALLOWEDIP_A_IPADDR, addr_len, FALSE); - - *allowed_ip = (NMPWireGuardAllowedIP){ - .family = family, - }; - - nm_assert((int) allowed_ip->family == family); - - if (tb[WGALLOWEDIP_A_IPADDR]) - nla_memcpy(&allowed_ip->addr, tb[WGALLOWEDIP_A_IPADDR], addr_len); - if (tb[WGALLOWEDIP_A_CIDR_MASK]) - allowed_ip->mask = nla_get_u8(tb[WGALLOWEDIP_A_CIDR_MASK]); - - return TRUE; -} - -typedef struct { - CList lst; - NMPWireGuardPeer data; -} WireGuardPeerConstruct; - -static gboolean -_wireguard_update_from_peers_nla(CList *peers, GArray **p_allowed_ips, struct nlattr *peer_attr) -{ - static const struct nla_policy policy[] = { - [WGPEER_A_PUBLIC_KEY] = {.minlen = NMP_WIREGUARD_PUBLIC_KEY_LEN}, - [WGPEER_A_PRESHARED_KEY] = {}, - [WGPEER_A_FLAGS] = {.type = NLA_U32}, - [WGPEER_A_ENDPOINT] = {}, - [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = {.type = NLA_U16}, - [WGPEER_A_LAST_HANDSHAKE_TIME] = {}, - [WGPEER_A_RX_BYTES] = {.type = NLA_U64}, - [WGPEER_A_TX_BYTES] = {.type = NLA_U64}, - [WGPEER_A_ALLOWEDIPS] = {.type = NLA_NESTED}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - WireGuardPeerConstruct *peer_c; - - if (nla_parse_nested_arr(tb, peer_attr, policy) < 0) - return FALSE; - - if (!tb[WGPEER_A_PUBLIC_KEY]) - return FALSE; - - /* a peer with the same public key as last peer is just a continuation for extra AllowedIPs */ - peer_c = c_list_last_entry(peers, WireGuardPeerConstruct, lst); - if (peer_c - && !memcmp(nla_data(tb[WGPEER_A_PUBLIC_KEY]), - peer_c->data.public_key, - NMP_WIREGUARD_PUBLIC_KEY_LEN)) { - G_STATIC_ASSERT_EXPR(NMP_WIREGUARD_PUBLIC_KEY_LEN == sizeof(peer_c->data.public_key)); - /* this message is a continuation of the previous peer. - * Only parse WGPEER_A_ALLOWEDIPS below. */ - } else { - /* otherwise, start a new peer */ - peer_c = g_slice_new0(WireGuardPeerConstruct); - c_list_link_tail(peers, &peer_c->lst); - - nla_memcpy(&peer_c->data.public_key, - tb[WGPEER_A_PUBLIC_KEY], - sizeof(peer_c->data.public_key)); - - if (tb[WGPEER_A_PRESHARED_KEY]) { - nla_memcpy(&peer_c->data.preshared_key, - tb[WGPEER_A_PRESHARED_KEY], - sizeof(peer_c->data.preshared_key)); - /* FIXME(netlink-bzero-secret) */ - nm_explicit_bzero(nla_data(tb[WGPEER_A_PRESHARED_KEY]), - nla_len(tb[WGPEER_A_PRESHARED_KEY])); - } - - nm_sock_addr_union_cpy_untrusted( - &peer_c->data.endpoint, - tb[WGPEER_A_ENDPOINT] ? nla_data(tb[WGPEER_A_ENDPOINT]) : NULL, - tb[WGPEER_A_ENDPOINT] ? nla_len(tb[WGPEER_A_ENDPOINT]) : 0); - - if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) - peer_c->data.persistent_keepalive_interval = - nla_get_u16(tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); - if (tb[WGPEER_A_LAST_HANDSHAKE_TIME]) { - if (nla_len(tb[WGPEER_A_LAST_HANDSHAKE_TIME]) - >= sizeof(peer_c->data.last_handshake_time)) - nla_memcpy(&peer_c->data.last_handshake_time, - tb[WGPEER_A_LAST_HANDSHAKE_TIME], - sizeof(peer_c->data.last_handshake_time)); - } - if (tb[WGPEER_A_RX_BYTES]) - peer_c->data.rx_bytes = nla_get_u64(tb[WGPEER_A_RX_BYTES]); - if (tb[WGPEER_A_TX_BYTES]) - peer_c->data.tx_bytes = nla_get_u64(tb[WGPEER_A_TX_BYTES]); - } - - if (tb[WGPEER_A_ALLOWEDIPS]) { - struct nlattr *attr; - int rem; - GArray * allowed_ips = *p_allowed_ips; - - nla_for_each_nested (attr, tb[WGPEER_A_ALLOWEDIPS], rem) { - if (!allowed_ips) { - allowed_ips = g_array_new(FALSE, FALSE, sizeof(NMPWireGuardAllowedIP)); - *p_allowed_ips = allowed_ips; - g_array_set_size(allowed_ips, 1); - } else - g_array_set_size(allowed_ips, allowed_ips->len + 1); - - if (!_wireguard_update_from_allowed_ips_nla( - &g_array_index(allowed_ips, NMPWireGuardAllowedIP, allowed_ips->len - 1), - attr)) { - /* we ignore the error of parsing one allowed-ip. */ - g_array_set_size(allowed_ips, allowed_ips->len - 1); - continue; - } - - if (!peer_c->data._construct_idx_end) - peer_c->data._construct_idx_start = allowed_ips->len - 1; - peer_c->data._construct_idx_end = allowed_ips->len; - } - } - - return TRUE; -} - -typedef struct { - const int ifindex; - NMPObject *obj; - CList peers; - GArray * allowed_ips; -} WireGuardParseData; - -static int -_wireguard_get_device_cb(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy policy[] = { - [WGDEVICE_A_IFINDEX] = {.type = NLA_U32}, - [WGDEVICE_A_IFNAME] = {.type = NLA_NUL_STRING, .maxlen = IFNAMSIZ}, - [WGDEVICE_A_PRIVATE_KEY] = {}, - [WGDEVICE_A_PUBLIC_KEY] = {}, - [WGDEVICE_A_FLAGS] = {.type = NLA_U32}, - [WGDEVICE_A_LISTEN_PORT] = {.type = NLA_U16}, - [WGDEVICE_A_FWMARK] = {.type = NLA_U32}, - [WGDEVICE_A_PEERS] = {.type = NLA_NESTED}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - WireGuardParseData *parse_data = arg; - - if (genlmsg_parse_arr(nlmsg_hdr(msg), 0, tb, policy) < 0) - return NL_SKIP; - - if (tb[WGDEVICE_A_IFINDEX]) { - int ifindex; - - ifindex = (int) nla_get_u32(tb[WGDEVICE_A_IFINDEX]); - if (ifindex <= 0 || parse_data->ifindex != ifindex) - return NL_SKIP; - } else { - if (!parse_data->obj) - return NL_SKIP; - } - - if (parse_data->obj) { - /* we already have an object instance. This means the netlink message - * is a continuation, only providing more WGDEVICE_A_PEERS data below. */ - } else { - NMPObject * obj; - NMPlatformLnkWireGuard *props; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_WIREGUARD, NULL); - props = &obj->lnk_wireguard; - - if (tb[WGDEVICE_A_PRIVATE_KEY]) { - nla_memcpy(props->private_key, tb[WGDEVICE_A_PRIVATE_KEY], sizeof(props->private_key)); - /* FIXME(netlink-bzero-secret): extend netlink library to wipe memory. For now, - * just hack it here (yes, this does not cover all places where the - * private key was copied). */ - nm_explicit_bzero(nla_data(tb[WGDEVICE_A_PRIVATE_KEY]), - nla_len(tb[WGDEVICE_A_PRIVATE_KEY])); - } - if (tb[WGDEVICE_A_PUBLIC_KEY]) - nla_memcpy(props->public_key, tb[WGDEVICE_A_PUBLIC_KEY], sizeof(props->public_key)); - if (tb[WGDEVICE_A_LISTEN_PORT]) - props->listen_port = nla_get_u16(tb[WGDEVICE_A_LISTEN_PORT]); - if (tb[WGDEVICE_A_FWMARK]) - props->fwmark = nla_get_u32(tb[WGDEVICE_A_FWMARK]); - - parse_data->obj = obj; - } - - if (tb[WGDEVICE_A_PEERS]) { - struct nlattr *attr; - int rem; - - nla_for_each_nested (attr, tb[WGDEVICE_A_PEERS], rem) { - if (!_wireguard_update_from_peers_nla(&parse_data->peers, - &parse_data->allowed_ips, - attr)) { - /* we ignore the error of parsing one peer. - * _wireguard_update_from_peers_nla() leaves the @peers array in the - * desired state. */ - } - } - } - - return NL_OK; -} - -static const NMPObject * -_wireguard_read_info(NMPlatform * platform /* used only as logging context */, - struct nl_sock *genl, - int wireguard_family_id, - int ifindex) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - NMPObject * obj = NULL; - WireGuardPeerConstruct * peer_c; - WireGuardPeerConstruct * peer_c_safe; - gs_unref_array GArray *allowed_ips = NULL; - WireGuardParseData parse_data = { - .ifindex = ifindex, - }; - guint i; - - nm_assert(genl); - nm_assert(wireguard_family_id >= 0); - nm_assert(ifindex > 0); - - _LOGT("wireguard: fetching information for ifindex %d (genl-id %d)...", - ifindex, - wireguard_family_id); - - msg = nlmsg_alloc(); - - if (!genlmsg_put(msg, - NL_AUTO_PORT, - NL_AUTO_SEQ, - wireguard_family_id, - 0, - NLM_F_DUMP, - WG_CMD_GET_DEVICE, - 1)) - return NULL; - - NLA_PUT_U32(msg, WGDEVICE_A_IFINDEX, (guint32) ifindex); - - if (nl_send_auto(genl, msg) < 0) - return NULL; - - c_list_init(&parse_data.peers); - - /* we ignore errors, and return whatever we could successfully - * parse. */ - nl_recvmsgs(genl, - &((const struct nl_cb){ - .valid_cb = _wireguard_get_device_cb, - .valid_arg = (gpointer) &parse_data, - })); - - /* unpack: transfer ownership */ - obj = parse_data.obj; - allowed_ips = parse_data.allowed_ips; - - if (!obj) { - while ((peer_c = c_list_first_entry(&parse_data.peers, WireGuardPeerConstruct, lst))) { - c_list_unlink_stale(&peer_c->lst); - nm_explicit_bzero(&peer_c->data.preshared_key, sizeof(peer_c->data.preshared_key)); - g_slice_free(WireGuardPeerConstruct, peer_c); - } - return NULL; - } - - /* we receive peers/allowed-ips possibly in separate netlink messages. Hence, while - * parsing the dump, we don't know upfront how many peers/allowed-ips we will receive. - * - * We solve that, by collecting all peers with a CList. It's done this way, - * because a GArray would require growing the array, but we want to bzero() - * the preshared-key of each peer while reallocating. The CList apprach avoids - * that. - * - * For allowed-ips, we instead track one GArray, which are all appended - * there. The realloc/resize of the GArray is fine there. However, - * while we build the GArray, we don't yet have the final pointers. - * Hence, while constructing, we track the indexes with peer->_construct_idx_* - * fields. These indexes must be converted to actual pointers blow. - * - * This is all done during parsing. In the final NMPObjectLnkWireGuard we - * don't want the CList anymore and repackage the NMPObject tightly. The - * reason is, that NMPObject instances are immutable and long-living. Spend - * a bit effort below during construction to obtain a most suitable representation - * in this regard. */ - obj->_lnk_wireguard.peers_len = c_list_length(&parse_data.peers); - obj->_lnk_wireguard.peers = obj->_lnk_wireguard.peers_len > 0 - ? g_new(NMPWireGuardPeer, obj->_lnk_wireguard.peers_len) - : NULL; - - /* duplicate allowed_ips instead of using the pointer. The GArray possibly has more - * space allocated then we need, and we want to get rid of this excess buffer. - * Note that NMPObject instance is possibly put into the cache and long-living. */ - obj->_lnk_wireguard._allowed_ips_buf_len = allowed_ips ? allowed_ips->len : 0u; - obj->_lnk_wireguard._allowed_ips_buf = - obj->_lnk_wireguard._allowed_ips_buf_len > 0 - ? (NMPWireGuardAllowedIP *) nm_memdup(allowed_ips->data, - sizeof(NMPWireGuardAllowedIP) * allowed_ips->len) - : NULL; - - i = 0; - c_list_for_each_entry_safe (peer_c, peer_c_safe, &parse_data.peers, lst) { - NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &obj->_lnk_wireguard.peers[i++]; - - *peer = peer_c->data; - - c_list_unlink_stale(&peer_c->lst); - nm_explicit_bzero(&peer_c->data.preshared_key, sizeof(peer_c->data.preshared_key)); - g_slice_free(WireGuardPeerConstruct, peer_c); - - if (peer->_construct_idx_end != 0) { - guint len; - - nm_assert(obj->_lnk_wireguard._allowed_ips_buf); - nm_assert(peer->_construct_idx_end > peer->_construct_idx_start); - nm_assert(peer->_construct_idx_start < obj->_lnk_wireguard._allowed_ips_buf_len); - nm_assert(peer->_construct_idx_end <= obj->_lnk_wireguard._allowed_ips_buf_len); - - len = peer->_construct_idx_end - peer->_construct_idx_start; - peer->allowed_ips = &obj->_lnk_wireguard._allowed_ips_buf[peer->_construct_idx_start]; - peer->allowed_ips_len = len; - } else { - nm_assert(!peer->_construct_idx_start); - nm_assert(!peer->_construct_idx_end); - peer->allowed_ips = NULL; - peer->allowed_ips_len = 0; - } - } - - return obj; - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static int -_wireguard_get_family_id(NMPlatform *platform, int ifindex_try) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - int wireguard_family_id = -1; - - if (ifindex_try > 0) { - const NMPlatformLink *plink; - - if (nm_platform_link_get_lnk_wireguard(platform, ifindex_try, &plink)) - wireguard_family_id = NMP_OBJECT_UP_CAST(plink)->_link.wireguard_family_id; - } - if (wireguard_family_id < 0) - wireguard_family_id = genl_ctrl_resolve(priv->genl, "wireguard"); - return wireguard_family_id; -} - -static const NMPObject * -_wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifindex) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - nm_auto_nmpobj const NMPObject *lnk_new = NULL; - NMPCacheOpsType cache_op; - const NMPObject * plink = NULL; - nm_auto_nmpobj NMPObject *obj = NULL; - - nm_assert(wireguard_family_id >= 0); - nm_assert(ifindex > 0); - - nm_platform_process_events(platform); - - plink = nm_platform_link_get_obj(platform, ifindex, TRUE); - - if (!plink || plink->link.type != NM_LINK_TYPE_WIREGUARD) { - nm_platform_link_refresh(platform, ifindex); - plink = nm_platform_link_get_obj(platform, ifindex, TRUE); - if (!plink || plink->link.type != NM_LINK_TYPE_WIREGUARD) - return NULL; - if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) - lnk_new = nmp_object_ref(plink->_link.netlink.lnk); - } else { - lnk_new = _wireguard_read_info(platform, priv->genl, wireguard_family_id, ifindex); - if (!lnk_new) { - if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) - lnk_new = nmp_object_ref(plink->_link.netlink.lnk); - } else if (nmp_object_equal(plink->_link.netlink.lnk, lnk_new)) { - nmp_object_unref(lnk_new); - lnk_new = nmp_object_ref(plink->_link.netlink.lnk); - } - } - - if (plink->_link.wireguard_family_id == wireguard_family_id - && plink->_link.netlink.lnk == lnk_new) - return plink; - - /* we use nmp_cache_update_netlink() to re-inject the new object into the cache. - * For that, we need to clone it, and tweak it so that it's suitable. It's a bit - * of a hack, in particular that we need to clear driver and udev-device. */ - obj = nmp_object_clone(plink, FALSE); - obj->_link.wireguard_family_id = wireguard_family_id; - nmp_object_unref(obj->_link.netlink.lnk); - obj->_link.netlink.lnk = g_steal_pointer(&lnk_new); - obj->link.driver = NULL; - nm_clear_pointer(&obj->_link.udev.device, udev_device_unref); - - cache_op = - nmp_cache_update_netlink(nm_platform_get_cache(platform), obj, FALSE, &obj_old, &obj_new); - nm_assert(NM_IN_SET(cache_op, NMP_CACHE_OPS_UPDATED)); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - cache_on_change(platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); - } - - nm_assert(!obj_new - || (NMP_OBJECT_GET_TYPE(obj_new) == NMP_OBJECT_TYPE_LINK - && obj_new->link.type == NM_LINK_TYPE_WIREGUARD - && (!obj_new->_link.netlink.lnk - || NMP_OBJECT_GET_TYPE(obj_new->_link.netlink.lnk) - == NMP_OBJECT_TYPE_LNK_WIREGUARD))); - return obj_new; -} - -static int -_wireguard_create_change_nlmsgs(NMPlatform * platform, - int ifindex, - int wireguard_family_id, - const NMPlatformLnkWireGuard * lnk_wireguard, - const NMPWireGuardPeer * peers, - const NMPlatformWireGuardChangePeerFlags *peer_flags, - guint peers_len, - NMPlatformWireGuardChangeFlags change_flags, - GPtrArray ** out_msgs) -{ - gs_unref_ptrarray GPtrArray * msgs = NULL; - nm_auto_nlmsg struct nl_msg * msg = NULL; - const guint IDX_NIL = G_MAXUINT; - guint idx_peer_curr; - guint idx_allowed_ips_curr; - struct nlattr * nest_peers; - struct nlattr * nest_curr_peer; - struct nlattr * nest_allowed_ips; - struct nlattr * nest_curr_allowed_ip; - NMPlatformWireGuardChangePeerFlags p_flags = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT; - -#define _nla_nest_end(msg, nest_start) \ - G_STMT_START \ - { \ - if (nla_nest_end((msg), (nest_start)) < 0) \ - g_return_val_if_reached(-NME_BUG); \ - } \ - G_STMT_END - - /* Adapted from LGPL-2.1+ code [1]. - * - * [1] https://git.zx2c4.com/WireGuard/tree/contrib/examples/embeddable-wg-library/wireguard.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n1073 */ - - idx_peer_curr = IDX_NIL; - idx_allowed_ips_curr = IDX_NIL; - - /* TODO: for the moment, we always reset all peers and allowed-ips (WGDEVICE_F_REPLACE_PEERS, WGPEER_F_REPLACE_ALLOWEDIPS). - * The platform API should be extended to also support partial updates. In particular, configuring the same configuration - * multiple times, should not clear and re-add all settings, but rather sync the existing settings with the desired configuration. */ - -again: - - msg = nlmsg_alloc(); - if (!genlmsg_put(msg, - NL_AUTO_PORT, - NL_AUTO_SEQ, - wireguard_family_id, - 0, - NLM_F_REQUEST, - WG_CMD_SET_DEVICE, - 1)) - g_return_val_if_reached(-NME_BUG); - - NLA_PUT_U32(msg, WGDEVICE_A_IFINDEX, (guint32) ifindex); - - if (idx_peer_curr == IDX_NIL) { - guint32 flags; - - if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY)) - NLA_PUT(msg, - WGDEVICE_A_PRIVATE_KEY, - sizeof(lnk_wireguard->private_key), - lnk_wireguard->private_key); - if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT)) - NLA_PUT_U16(msg, WGDEVICE_A_LISTEN_PORT, lnk_wireguard->listen_port); - if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK)) - NLA_PUT_U32(msg, WGDEVICE_A_FWMARK, lnk_wireguard->fwmark); - - flags = 0; - if (NM_FLAGS_HAS(change_flags, NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS)) - flags |= WGDEVICE_F_REPLACE_PEERS; - NLA_PUT_U32(msg, WGDEVICE_A_FLAGS, flags); - } - - if (peers_len == 0) - goto send; - - nest_curr_peer = NULL; - nest_allowed_ips = NULL; - nest_curr_allowed_ip = NULL; - - nest_peers = nla_nest_start(msg, WGDEVICE_A_PEERS); - if (!nest_peers) - g_return_val_if_reached(-NME_BUG); - - if (idx_peer_curr == IDX_NIL) - idx_peer_curr = 0; - for (; idx_peer_curr < peers_len; idx_peer_curr++) { - const NMPWireGuardPeer *p = &peers[idx_peer_curr]; - - if (peer_flags) { - p_flags = peer_flags[idx_peer_curr]; - if (!NM_FLAGS_ANY(p_flags, - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS)) { - /* no flags set. We take that as indication to skip configuring the peer - * entirely. */ - nm_assert(p_flags == NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE); - continue; - } - } - - nest_curr_peer = nla_nest_start(msg, 0); - if (!nest_curr_peer) - goto toobig_peers; - - if (nla_put(msg, WGPEER_A_PUBLIC_KEY, NMP_WIREGUARD_PUBLIC_KEY_LEN, p->public_key) < 0) - goto toobig_peers; - - if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME)) { - /* all other p_flags are silently ignored. */ - if (nla_put_uint32(msg, WGPEER_A_FLAGS, WGPEER_F_REMOVE_ME) < 0) - goto toobig_peers; - } else { - if (idx_allowed_ips_curr == IDX_NIL) { - if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY) - && nla_put(msg, - WGPEER_A_PRESHARED_KEY, - sizeof(p->preshared_key), - p->preshared_key) - < 0) - goto toobig_peers; - - if (NM_FLAGS_HAS(p_flags, - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL) - && nla_put_uint16(msg, - WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, - p->persistent_keepalive_interval) - < 0) - goto toobig_peers; - - if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS) - && nla_put_uint32(msg, WGPEER_A_FLAGS, WGPEER_F_REPLACE_ALLOWEDIPS) < 0) - goto toobig_peers; - - if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT)) { - if (NM_IN_SET(p->endpoint.sa.sa_family, AF_INET, AF_INET6)) { - if (nla_put(msg, - WGPEER_A_ENDPOINT, - p->endpoint.sa.sa_family == AF_INET ? sizeof(p->endpoint.in) - : sizeof(p->endpoint.in6), - &p->endpoint) - < 0) - goto toobig_peers; - } else { - /* I think there is no way to clear an endpoint, though there should be. */ - nm_assert(p->endpoint.sa.sa_family == AF_UNSPEC); - } - } - } - - if (NM_FLAGS_HAS(p_flags, NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS) - && p->allowed_ips_len > 0) { - if (idx_allowed_ips_curr == IDX_NIL) - idx_allowed_ips_curr = 0; - - nest_allowed_ips = nla_nest_start(msg, WGPEER_A_ALLOWEDIPS); - if (!nest_allowed_ips) - goto toobig_allowedips; - - for (; idx_allowed_ips_curr < p->allowed_ips_len; idx_allowed_ips_curr++) { - const NMPWireGuardAllowedIP *aip = &p->allowed_ips[idx_allowed_ips_curr]; - - nest_curr_allowed_ip = nla_nest_start(msg, 0); - if (!nest_curr_allowed_ip) - goto toobig_allowedips; - - g_return_val_if_fail(NM_IN_SET(aip->family, AF_INET, AF_INET6), -NME_BUG); - - if (nla_put_uint16(msg, WGALLOWEDIP_A_FAMILY, aip->family) < 0) - goto toobig_allowedips; - if (nla_put(msg, - WGALLOWEDIP_A_IPADDR, - nm_utils_addr_family_to_size(aip->family), - &aip->addr) - < 0) - goto toobig_allowedips; - if (nla_put_uint8(msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0) - goto toobig_allowedips; - - _nla_nest_end(msg, nest_curr_allowed_ip); - nest_curr_allowed_ip = NULL; - } - idx_allowed_ips_curr = IDX_NIL; - - _nla_nest_end(msg, nest_allowed_ips); - nest_allowed_ips = NULL; - } - } - - _nla_nest_end(msg, nest_curr_peer); - nest_curr_peer = NULL; - } - - _nla_nest_end(msg, nest_peers); - goto send; - -toobig_allowedips: - if (nest_curr_allowed_ip) - nla_nest_cancel(msg, nest_curr_allowed_ip); - if (nest_allowed_ips) - nla_nest_cancel(msg, nest_allowed_ips); - _nla_nest_end(msg, nest_curr_peer); - _nla_nest_end(msg, nest_peers); - goto send; - -toobig_peers: - if (nest_curr_peer) - nla_nest_cancel(msg, nest_curr_peer); - _nla_nest_end(msg, nest_peers); - goto send; - -send: - if (!msgs) - msgs = g_ptr_array_new_with_free_func((GDestroyNotify) nlmsg_free); - g_ptr_array_add(msgs, g_steal_pointer(&msg)); - - if (idx_peer_curr != IDX_NIL && idx_peer_curr < peers_len) - goto again; - - NM_SET_OUT(out_msgs, g_steal_pointer(&msgs)); - return 0; - -nla_put_failure: - g_return_val_if_reached(-NME_BUG); - -#undef _nla_nest_end -} - -static int -link_wireguard_change(NMPlatform * platform, - int ifindex, - const NMPlatformLnkWireGuard * lnk_wireguard, - const NMPWireGuardPeer * peers, - const NMPlatformWireGuardChangePeerFlags *peer_flags, - guint peers_len, - NMPlatformWireGuardChangeFlags change_flags) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - gs_unref_ptrarray GPtrArray *msgs = NULL; - int wireguard_family_id; - guint i; - int r; - - wireguard_family_id = _wireguard_get_family_id(platform, ifindex); - if (wireguard_family_id < 0) - return -NME_PL_NO_FIRMWARE; - - r = _wireguard_create_change_nlmsgs(platform, - ifindex, - wireguard_family_id, - lnk_wireguard, - peers, - peer_flags, - peers_len, - change_flags, - &msgs); - if (r < 0) { - _LOGW("wireguard: set-device, cannot construct netlink message: %s", nm_strerror(r)); - return r; - } - - for (i = 0; i < msgs->len; i++) { - r = nl_send_auto(priv->genl, msgs->pdata[i]); - if (r < 0) { - _LOGW("wireguard: set-device, send netlink message #%u failed: %s", i, nm_strerror(r)); - return r; - } - - do { - r = nl_recvmsgs(priv->genl, NULL); - } while (r == -EAGAIN); - if (r < 0) { - _LOGW("wireguard: set-device, message #%u was rejected: %s", i, nm_strerror(r)); - return r; - } - - _LOGT("wireguard: set-device, message #%u sent and confirmed", i); - } - - _wireguard_refresh_link(platform, wireguard_family_id, ifindex); - - return 0; -} - -/*****************************************************************************/ - -static void -_nmp_link_address_set(NMPLinkAddress *dst, const struct nlattr *nla) -{ - *dst = (NMPLinkAddress){ - .len = 0, - }; - if (nla) { - int l = nla_len(nla); - - if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { - G_STATIC_ASSERT_EXPR(sizeof(dst->data) == NM_UTILS_HWADDR_LEN_MAX); - memcpy(dst->data, nla_data(nla), l); - dst->len = l; - } - } -} - -/* Copied and heavily modified from libnl3's link_msg_parser(). */ -static NMPObject * -_new_from_nl_link(NMPlatform * platform, - const NMPCache * cache, - struct nlmsghdr *nlh, - gboolean id_only) -{ - static const struct nla_policy policy[] = { - [IFLA_IFNAME] = {.type = NLA_STRING, .maxlen = IFNAMSIZ}, - [IFLA_MTU] = {.type = NLA_U32}, - [IFLA_TXQLEN] = {.type = NLA_U32}, - [IFLA_LINK] = {.type = NLA_U32}, - [IFLA_WEIGHT] = {.type = NLA_U32}, - [IFLA_MASTER] = {.type = NLA_U32}, - [IFLA_OPERSTATE] = {.type = NLA_U8}, - [IFLA_LINKMODE] = {.type = NLA_U8}, - [IFLA_LINKINFO] = {.type = NLA_NESTED}, - [IFLA_QDISC] = {.type = NLA_STRING, .maxlen = IFQDISCSIZ}, - [IFLA_STATS] = {.minlen = nm_offsetofend(struct rtnl_link_stats, tx_compressed)}, - [IFLA_STATS64] = {.minlen = nm_offsetofend(struct rtnl_link_stats64, tx_compressed)}, - [IFLA_MAP] = {.minlen = nm_offsetofend(struct rtnl_link_ifmap, port)}, - [IFLA_IFALIAS] = {.type = NLA_STRING, .maxlen = IFALIASZ}, - [IFLA_NUM_VF] = {.type = NLA_U32}, - [IFLA_AF_SPEC] = {.type = NLA_NESTED}, - [IFLA_PROMISCUITY] = {.type = NLA_U32}, - [IFLA_NUM_TX_QUEUES] = {.type = NLA_U32}, - [IFLA_NUM_RX_QUEUES] = {.type = NLA_U32}, - [IFLA_GROUP] = {.type = NLA_U32}, - [IFLA_CARRIER] = {.type = NLA_U8}, - [IFLA_PHYS_PORT_ID] = {.type = NLA_UNSPEC}, - [IFLA_NET_NS_PID] = {.type = NLA_U32}, - [IFLA_NET_NS_FD] = {.type = NLA_U32}, - [IFLA_LINK_NETNSID] = {}, - }; - const struct ifinfomsg *ifi; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - struct nlattr * nl_info_data = NULL; - const char * nl_info_kind = NULL; - nm_auto_nmpobj NMPObject *obj = NULL; - gboolean completed_from_cache_val = FALSE; - gboolean * completed_from_cache = cache ? &completed_from_cache_val : NULL; - const NMPObject * link_cached = NULL; - const NMPObject * lnk_data = NULL; - gboolean address_complete_from_cache = TRUE; - gboolean broadcast_complete_from_cache = TRUE; - gboolean lnk_data_complete_from_cache = TRUE; - gboolean need_ext_data = FALSE; - gboolean af_inet6_token_valid = FALSE; - gboolean af_inet6_addr_gen_mode_valid = FALSE; - - if (!nlmsg_valid_hdr(nlh, sizeof(*ifi))) - return NULL; - - ifi = nlmsg_data(nlh); - - if (ifi->ifi_family != AF_UNSPEC) - return NULL; - if (ifi->ifi_index <= 0) - return NULL; - - obj = nmp_object_new_link(ifi->ifi_index); - - if (id_only) - return g_steal_pointer(&obj); - - if (nlmsg_parse_arr(nlh, sizeof(*ifi), tb, policy) < 0) - return NULL; - - if (!tb[IFLA_IFNAME]) - return NULL; - nla_strlcpy(obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ); - if (!obj->link.name[0]) - return NULL; - - if (!tb[IFLA_MTU]) { - /* Kernel has two places that send RTM_GETLINK messages: - * net/core/rtnetlink.c and net/wireless/ext-core.c. - * Unfortunately ext-core.c sets only IFLA_WIRELESS and - * IFLA_IFNAME. This confuses code in this function, because - * it cannot get complete set of data for the interface and - * later incomplete object this function creates is used to - * overwrite existing data in NM's cache. - * Since ext-core.c doesn't set IFLA_MTU we can use it as a - * signal to ignore incoming message. - * To some extent this is a hack and correct approach is to - * merge objects per-field. - */ - return NULL; - } - obj->link.mtu = nla_get_u32(tb[IFLA_MTU]); - - if (tb[IFLA_LINKINFO]) { - static const struct nla_policy policy_link_info[] = { - [IFLA_INFO_KIND] = {.type = NLA_STRING}, - [IFLA_INFO_DATA] = {.type = NLA_NESTED}, - [IFLA_INFO_XSTATS] = {.type = NLA_NESTED}, - }; - struct nlattr *li[G_N_ELEMENTS(policy_link_info)]; - - if (nla_parse_nested_arr(li, tb[IFLA_LINKINFO], policy_link_info) < 0) - return NULL; - - if (li[IFLA_INFO_KIND]) - nl_info_kind = nla_get_string(li[IFLA_INFO_KIND]); - - nl_info_data = li[IFLA_INFO_DATA]; - } - - if (tb[IFLA_STATS64]) { - const char *stats = nla_data(tb[IFLA_STATS64]); - - obj->link.rx_packets = - unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, rx_packets)]); - obj->link.rx_bytes = - unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, rx_bytes)]); - obj->link.tx_packets = - unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, tx_packets)]); - obj->link.tx_bytes = - unaligned_read_ne64(&stats[G_STRUCT_OFFSET(struct rtnl_link_stats64, tx_bytes)]); - } - - obj->link.n_ifi_flags = ifi->ifi_flags; - obj->link.connected = NM_FLAGS_HAS(obj->link.n_ifi_flags, IFF_LOWER_UP); - obj->link.arptype = ifi->ifi_type; - - obj->link.type = _linktype_get_type(platform, - cache, - nl_info_kind, - obj->link.ifindex, - obj->link.name, - obj->link.n_ifi_flags, - obj->link.arptype, - completed_from_cache, - &link_cached, - &obj->link.kind); - - if (tb[IFLA_MASTER]) - obj->link.master = nla_get_u32(tb[IFLA_MASTER]); - - if (tb[IFLA_LINK]) { - if (!tb[IFLA_LINK_NETNSID]) - obj->link.parent = nla_get_u32(tb[IFLA_LINK]); - else - obj->link.parent = NM_PLATFORM_LINK_OTHER_NETNS; - } - - if (tb[IFLA_ADDRESS]) { - _nmp_link_address_set(&obj->link.l_address, tb[IFLA_ADDRESS]); - address_complete_from_cache = FALSE; - } - - if (tb[IFLA_BROADCAST]) { - _nmp_link_address_set(&obj->link.l_broadcast, tb[IFLA_BROADCAST]); - broadcast_complete_from_cache = FALSE; - } - - if (tb[IFLA_AF_SPEC]) { - struct nlattr *af_attr; - int remaining; - - nla_for_each_nested (af_attr, tb[IFLA_AF_SPEC], remaining) { - switch (nla_type(af_attr)) { - case AF_INET6: - _parse_af_inet6(platform, - af_attr, - &obj->link.inet6_token, - &af_inet6_token_valid, - &obj->link.inet6_addr_gen_mode_inv, - &af_inet6_addr_gen_mode_valid); - break; - } - } - } - - switch (obj->link.type) { - case NM_LINK_TYPE_BRIDGE: - lnk_data = _parse_lnk_bridge(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_GRE: - case NM_LINK_TYPE_GRETAP: - lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_INFINIBAND: - lnk_data = _parse_lnk_infiniband(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_IP6TNL: - lnk_data = _parse_lnk_ip6tnl(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_IP6GRE: - case NM_LINK_TYPE_IP6GRETAP: - lnk_data = _parse_lnk_ip6gre(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_IPIP: - lnk_data = _parse_lnk_ipip(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_MACSEC: - lnk_data = _parse_lnk_macsec(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_MACVLAN: - case NM_LINK_TYPE_MACVTAP: - lnk_data = _parse_lnk_macvlan(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_SIT: - lnk_data = _parse_lnk_sit(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_TUN: - lnk_data = _parse_lnk_tun(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_VLAN: - lnk_data = _parse_lnk_vlan(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_VRF: - lnk_data = _parse_lnk_vrf(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_VXLAN: - lnk_data = _parse_lnk_vxlan(nl_info_kind, nl_info_data); - break; - case NM_LINK_TYPE_WIFI: - case NM_LINK_TYPE_OLPC_MESH: - case NM_LINK_TYPE_WPAN: - need_ext_data = TRUE; - lnk_data_complete_from_cache = FALSE; - break; - case NM_LINK_TYPE_WIREGUARD: - lnk_data_complete_from_cache = TRUE; - break; - default: - lnk_data_complete_from_cache = FALSE; - break; - } - - if (completed_from_cache - && (lnk_data_complete_from_cache || need_ext_data || address_complete_from_cache - || broadcast_complete_from_cache || !af_inet6_token_valid - || !af_inet6_addr_gen_mode_valid || !tb[IFLA_STATS64])) { - _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached); - if (link_cached && link_cached->_link.netlink.is_in_netlink) { - if (lnk_data_complete_from_cache && link_cached->link.type == obj->link.type - && link_cached->_link.netlink.lnk - && (!lnk_data || nmp_object_equal(lnk_data, link_cached->_link.netlink.lnk))) { - /* We always try to look into the cache and reuse the object there. - * We do that, because we consider the lnk object as immutable and don't - * modify it after creating. Hence we can share it and reuse. - * - * Also, sometimes the info-data is missing for updates. In this case - * we want to keep the previously received lnk_data. */ - nmp_object_unref(lnk_data); - lnk_data = nmp_object_ref(link_cached->_link.netlink.lnk); - } - - if (need_ext_data && link_cached->link.type == obj->link.type - && link_cached->_link.ext_data) { - /* Prefer reuse of existing ext_data object */ - obj->_link.ext_data = g_object_ref(link_cached->_link.ext_data); - } - - if (address_complete_from_cache) - obj->link.l_address = link_cached->link.l_address; - if (broadcast_complete_from_cache) - obj->link.l_broadcast = link_cached->link.l_broadcast; - if (!af_inet6_token_valid) - obj->link.inet6_token = link_cached->link.inet6_token; - if (!af_inet6_addr_gen_mode_valid) - obj->link.inet6_addr_gen_mode_inv = link_cached->link.inet6_addr_gen_mode_inv; - if (!tb[IFLA_STATS64]) { - obj->link.rx_packets = link_cached->link.rx_packets; - obj->link.rx_bytes = link_cached->link.rx_bytes; - obj->link.tx_packets = link_cached->link.tx_packets; - obj->link.tx_bytes = link_cached->link.tx_bytes; - } - } - } - - obj->_link.netlink.lnk = lnk_data; - - if (need_ext_data && obj->_link.ext_data == NULL) { - switch (obj->link.type) { - case NM_LINK_TYPE_WIFI: - case NM_LINK_TYPE_OLPC_MESH: - obj->_link.ext_data = - (GObject *) nm_wifi_utils_new(ifi->ifi_index, - _genl_sock(NM_LINUX_PLATFORM(platform)), - TRUE); - break; - case NM_LINK_TYPE_WPAN: - obj->_link.ext_data = - (GObject *) nm_wpan_utils_new(ifi->ifi_index, - _genl_sock(NM_LINUX_PLATFORM(platform)), - TRUE); - break; - default: - g_assert_not_reached(); - } - } - - if (obj->link.type == NM_LINK_TYPE_WIREGUARD) { - const NMPObject *lnk_data_new = NULL; - struct nl_sock * genl = NM_LINUX_PLATFORM_GET_PRIVATE(platform)->genl; - - /* The WireGuard kernel module does not yet send link update - * notifications, so we don't actually update the cache. For - * now, always refetch link data here. */ - - _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached); - if (link_cached && link_cached->_link.netlink.is_in_netlink - && link_cached->link.type == NM_LINK_TYPE_WIREGUARD) - obj->_link.wireguard_family_id = link_cached->_link.wireguard_family_id; - else - obj->_link.wireguard_family_id = -1; - - if (obj->_link.wireguard_family_id < 0) - obj->_link.wireguard_family_id = genl_ctrl_resolve(genl, "wireguard"); - - if (obj->_link.wireguard_family_id >= 0) { - lnk_data_new = _wireguard_read_info(platform, - genl, - obj->_link.wireguard_family_id, - obj->link.ifindex); - } - - if (lnk_data_new && obj->_link.netlink.lnk - && nmp_object_equal(obj->_link.netlink.lnk, lnk_data_new)) - nmp_object_unref(lnk_data_new); - else { - nmp_object_unref(obj->_link.netlink.lnk); - obj->_link.netlink.lnk = lnk_data_new; - } - } - - obj->_link.netlink.is_in_netlink = TRUE; - return g_steal_pointer(&obj); -} - -/* Copied and heavily modified from libnl3's addr_msg_parser(). */ -static NMPObject * -_new_from_nl_addr(struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [IFA_LABEL] = {.type = NLA_STRING, .maxlen = IFNAMSIZ}, - [IFA_CACHEINFO] = {.minlen = nm_offsetofend(struct ifa_cacheinfo, tstamp)}, - [IFA_FLAGS] = {}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - const struct ifaddrmsg *ifa; - gboolean is_v4; - nm_auto_nmpobj NMPObject *obj = NULL; - int addr_len; - guint32 lifetime, preferred, timestamp; - - if (!nlmsg_valid_hdr(nlh, sizeof(*ifa))) - return NULL; - - ifa = nlmsg_data(nlh); - - if (!NM_IN_SET(ifa->ifa_family, AF_INET, AF_INET6)) - return NULL; - - is_v4 = ifa->ifa_family == AF_INET; - - if (nlmsg_parse_arr(nlh, sizeof(*ifa), tb, policy) < 0) - return NULL; - - addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr); - - if (ifa->ifa_prefixlen > (is_v4 ? 32 : 128)) - return NULL; - - /*****************************************************************/ - - obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); - - obj->ip_address.ifindex = ifa->ifa_index; - obj->ip_address.plen = ifa->ifa_prefixlen; - - _check_addr_or_return_null(tb, IFA_ADDRESS, addr_len); - _check_addr_or_return_null(tb, IFA_LOCAL, addr_len); - if (is_v4) { - /* For IPv4, kernel omits IFA_LOCAL/IFA_ADDRESS if (and only if) they - * are effectively 0.0.0.0 (all-zero). */ - if (tb[IFA_LOCAL]) - memcpy(&obj->ip4_address.address, nla_data(tb[IFA_LOCAL]), addr_len); - if (tb[IFA_ADDRESS]) - memcpy(&obj->ip4_address.peer_address, nla_data(tb[IFA_ADDRESS]), addr_len); - - _check_addr_or_return_null(tb, IFA_BROADCAST, addr_len); - obj->ip4_address.broadcast_address = - tb[IFA_BROADCAST] ? nla_get_u32(tb[IFA_BROADCAST]) : 0u; - obj->ip4_address.use_ip4_broadcast_address = TRUE; - } else { - /* For IPv6, IFA_ADDRESS is always present. - * - * If IFA_LOCAL is missing, IFA_ADDRESS is @address and @peer_address - * is :: (all-zero). - * - * If unexpectedly IFA_ADDRESS is missing, make the best of it -- but it _should_ - * actually be there. */ - if (tb[IFA_ADDRESS] || tb[IFA_LOCAL]) { - if (tb[IFA_LOCAL]) { - memcpy(&obj->ip6_address.address, nla_data(tb[IFA_LOCAL]), addr_len); - if (tb[IFA_ADDRESS]) - memcpy(&obj->ip6_address.peer_address, nla_data(tb[IFA_ADDRESS]), addr_len); - else - obj->ip6_address.peer_address = obj->ip6_address.address; - } else - memcpy(&obj->ip6_address.address, nla_data(tb[IFA_ADDRESS]), addr_len); - } - } - - obj->ip_address.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; - - obj->ip_address.n_ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifa->ifa_flags; - - if (is_v4) { - if (tb[IFA_LABEL]) { - char label[IFNAMSIZ]; - - nla_strlcpy(label, tb[IFA_LABEL], IFNAMSIZ); - - /* Check for ':'; we're only interested in labels used as interface aliases */ - if (strchr(label, ':')) - g_strlcpy(obj->ip4_address.label, label, sizeof(obj->ip4_address.label)); - } - } - - lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - preferred = NM_PLATFORM_LIFETIME_PERMANENT; - timestamp = 0; - /* IPv6 only */ - if (tb[IFA_CACHEINFO]) { - const struct ifa_cacheinfo *ca; - - ca = nla_data_as(struct ifa_cacheinfo, tb[IFA_CACHEINFO]); - lifetime = ca->ifa_valid; - preferred = ca->ifa_prefered; - timestamp = ca->tstamp; - } - _addrtime_get_lifetimes(timestamp, - lifetime, - preferred, - &obj->ip_address.timestamp, - &obj->ip_address.lifetime, - &obj->ip_address.preferred); - - return g_steal_pointer(&obj); -} - -/* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */ -static NMPObject * -_new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [RTA_TABLE] = {.type = NLA_U32}, - [RTA_IIF] = {.type = NLA_U32}, - [RTA_OIF] = {.type = NLA_U32}, - [RTA_PRIORITY] = {.type = NLA_U32}, - [RTA_PREF] = {.type = NLA_U8}, - [RTA_FLOW] = {.type = NLA_U32}, - [RTA_CACHEINFO] = {.minlen = nm_offsetofend(struct rta_cacheinfo, rta_tsage)}, - [RTA_METRICS] = {.type = NLA_NESTED}, - [RTA_MULTIPATH] = {.type = NLA_NESTED}, - }; - const struct rtmsg *rtm; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - gboolean is_v4; - nm_auto_nmpobj NMPObject *obj = NULL; - int addr_len; - struct { - gboolean is_present; - int ifindex; - NMIPAddr gateway; - } nh = { - .is_present = FALSE, - }; - guint32 mss; - guint32 window = 0; - guint32 cwnd = 0; - guint32 initcwnd = 0; - guint32 initrwnd = 0; - guint32 mtu = 0; - guint32 lock = 0; - - if (!nlmsg_valid_hdr(nlh, sizeof(*rtm))) - return NULL; - - rtm = nlmsg_data(nlh); - - /***************************************************************** - * only handle ~supported~ routes. - *****************************************************************/ - - if (!NM_IN_SET(rtm->rtm_family, AF_INET, AF_INET6)) - return NULL; - - if (!NM_IN_SET(rtm->rtm_type, RTN_UNICAST, RTN_LOCAL)) - return NULL; - - if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0) - return NULL; - - /*****************************************************************/ - - is_v4 = rtm->rtm_family == AF_INET; - addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr); - - if (rtm->rtm_dst_len > (is_v4 ? 32 : 128)) - return NULL; - - /***************************************************************** - * parse nexthops. Only handle routes with one nh. - *****************************************************************/ - - if (tb[RTA_MULTIPATH]) { - size_t tlen = nla_len(tb[RTA_MULTIPATH]); - struct rtnexthop *rtnh; - - if (tlen < sizeof(*rtnh)) - goto rta_multipath_done; - - rtnh = nla_data_as(struct rtnexthop, tb[RTA_MULTIPATH]); - - if (tlen < rtnh->rtnh_len) - goto rta_multipath_done; - - while (TRUE) { - if (nh.is_present) { - /* we don't support multipath routes. */ - return NULL; - } - - nh.is_present = TRUE; - nh.ifindex = rtnh->rtnh_ifindex; - - if (rtnh->rtnh_len > sizeof(*rtnh)) { - struct nlattr *ntb[G_N_ELEMENTS(policy)]; - - if (nla_parse_arr(ntb, - (struct nlattr *) RTNH_DATA(rtnh), - rtnh->rtnh_len - sizeof(*rtnh), - policy) - < 0) - return NULL; - - if (_check_addr_or_return_null(ntb, RTA_GATEWAY, addr_len)) - memcpy(&nh.gateway, nla_data(ntb[RTA_GATEWAY]), addr_len); - } - - if (tlen < RTNH_ALIGN(rtnh->rtnh_len) + sizeof(*rtnh)) - goto rta_multipath_done; - - tlen -= RTNH_ALIGN(rtnh->rtnh_len); - rtnh = RTNH_NEXT(rtnh); - } -rta_multipath_done:; - } - - if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW]) { - int ifindex = 0; - NMIPAddr gateway = {}; - - if (tb[RTA_OIF]) - ifindex = nla_get_u32(tb[RTA_OIF]); - if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len)) - memcpy(&gateway, nla_data(tb[RTA_GATEWAY]), addr_len); - - if (!nh.is_present) { - /* If no nexthops have been provided via RTA_MULTIPATH - * we add it as regular nexthop to maintain backwards - * compatibility */ - nh.ifindex = ifindex; - nh.gateway = gateway; - } else { - /* Kernel supports new style nexthop configuration, - * verify that it is a duplicate and ignore old-style nexthop. */ - if (nh.ifindex != ifindex || memcmp(&nh.gateway, &gateway, addr_len) != 0) - return NULL; - } - } else if (!nh.is_present) - return NULL; - - /*****************************************************************/ - - mss = 0; - if (tb[RTA_METRICS]) { - static const struct nla_policy rtax_policy[] = { - [RTAX_LOCK] = {.type = NLA_U32}, - [RTAX_ADVMSS] = {.type = NLA_U32}, - [RTAX_WINDOW] = {.type = NLA_U32}, - [RTAX_CWND] = {.type = NLA_U32}, - [RTAX_INITCWND] = {.type = NLA_U32}, - [RTAX_INITRWND] = {.type = NLA_U32}, - [RTAX_MTU] = {.type = NLA_U32}, - }; - struct nlattr *mtb[G_N_ELEMENTS(rtax_policy)]; - - if (nla_parse_nested_arr(mtb, tb[RTA_METRICS], rtax_policy) < 0) - return NULL; - - if (mtb[RTAX_LOCK]) - lock = nla_get_u32(mtb[RTAX_LOCK]); - if (mtb[RTAX_ADVMSS]) - mss = nla_get_u32(mtb[RTAX_ADVMSS]); - if (mtb[RTAX_WINDOW]) - window = nla_get_u32(mtb[RTAX_WINDOW]); - if (mtb[RTAX_CWND]) - cwnd = nla_get_u32(mtb[RTAX_CWND]); - if (mtb[RTAX_INITCWND]) - initcwnd = nla_get_u32(mtb[RTAX_INITCWND]); - if (mtb[RTAX_INITRWND]) - initrwnd = nla_get_u32(mtb[RTAX_INITRWND]); - if (mtb[RTAX_MTU]) - mtu = nla_get_u32(mtb[RTAX_MTU]); - } - - /*****************************************************************/ - - obj = nmp_object_new(is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - - obj->ip_route.type_coerced = nm_platform_route_type_coerce(rtm->rtm_type); - obj->ip_route.table_coerced = nm_platform_route_table_coerce( - tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : (guint32) rtm->rtm_table); - - obj->ip_route.ifindex = nh.ifindex; - - if (_check_addr_or_return_null(tb, RTA_DST, addr_len)) - memcpy(obj->ip_route.network_ptr, nla_data(tb[RTA_DST]), addr_len); - - obj->ip_route.plen = rtm->rtm_dst_len; - - if (tb[RTA_PRIORITY]) - obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]); - - if (is_v4) - obj->ip4_route.gateway = nh.gateway.addr4; - else - obj->ip6_route.gateway = nh.gateway.addr6; - - if (is_v4) - obj->ip4_route.scope_inv = nm_platform_route_scope_inv(rtm->rtm_scope); - - if (_check_addr_or_return_null(tb, RTA_PREFSRC, addr_len)) { - if (is_v4) - memcpy(&obj->ip4_route.pref_src, nla_data(tb[RTA_PREFSRC]), addr_len); - else - memcpy(&obj->ip6_route.pref_src, nla_data(tb[RTA_PREFSRC]), addr_len); - } - - if (is_v4) - obj->ip4_route.tos = rtm->rtm_tos; - else { - if (tb[RTA_SRC]) { - _check_addr_or_return_null(tb, RTA_SRC, addr_len); - memcpy(&obj->ip6_route.src, nla_data(tb[RTA_SRC]), addr_len); - } - obj->ip6_route.src_plen = rtm->rtm_src_len; - } - - obj->ip_route.mss = mss; - obj->ip_route.window = window; - obj->ip_route.cwnd = cwnd; - obj->ip_route.initcwnd = initcwnd; - obj->ip_route.initrwnd = initrwnd; - obj->ip_route.mtu = mtu; - obj->ip_route.lock_window = NM_FLAGS_HAS(lock, 1 << RTAX_WINDOW); - obj->ip_route.lock_cwnd = NM_FLAGS_HAS(lock, 1 << RTAX_CWND); - obj->ip_route.lock_initcwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITCWND); - obj->ip_route.lock_initrwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITRWND); - obj->ip_route.lock_mtu = NM_FLAGS_HAS(lock, 1 << RTAX_MTU); - - if (!is_v4) { - if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF)) { - /* Detect support for RTA_PREF by inspecting the netlink message. - * RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ - _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF, - tb[RTA_PREF] ? 1 : -1); - } - - if (tb[RTA_PREF]) - obj->ip6_route.rt_pref = nla_get_u8(tb[RTA_PREF]); - } - - obj->ip_route.r_rtm_flags = rtm->rtm_flags; - obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot(rtm->rtm_protocol); - - return g_steal_pointer(&obj); -} - -static NMPObject * -_new_from_nl_routing_rule(struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [FRA_UNSPEC] = {}, - [FRA_DST] = {/* struct in_addr, struct in6_addr */}, - [FRA_SRC] = {/* struct in_addr, struct in6_addr */}, - [FRA_IIFNAME] = - { - .type = NLA_STRING, - .maxlen = IFNAMSIZ, - }, - [FRA_GOTO] = - { - .type = NLA_U32, - }, - [FRA_UNUSED2] = {}, - [FRA_PRIORITY] = - { - .type = NLA_U32, - }, - [FRA_UNUSED3] = {}, - [FRA_UNUSED4] = {}, - [FRA_UNUSED5] = {}, - [FRA_FWMARK] = - { - .type = NLA_U32, - }, - [FRA_FLOW] = - { - .type = NLA_U32, - }, - [FRA_TUN_ID] = - { - .type = NLA_U64, - }, - [FRA_SUPPRESS_IFGROUP] = - { - .type = NLA_U32, - }, - [FRA_SUPPRESS_PREFIXLEN] = - { - .type = NLA_U32, - }, - [FRA_TABLE] = - { - .type = NLA_U32, - }, - [FRA_FWMASK] = - { - .type = NLA_U32, - }, - [FRA_OIFNAME] = - { - .type = NLA_STRING, - .maxlen = IFNAMSIZ, - }, - [FRA_PAD] = - { - .type = NLA_U32, - }, - [FRA_L3MDEV] = - { - .type = NLA_U8, - }, - [FRA_UID_RANGE] = - { - .minlen = sizeof(NMFibRuleUidRange), - .maxlen = sizeof(NMFibRuleUidRange), - }, - [FRA_PROTOCOL] = - { - .type = NLA_U8, - }, - [FRA_IP_PROTO] = - { - .type = NLA_U8, - }, - [FRA_SPORT_RANGE] = - { - .minlen = sizeof(NMFibRulePortRange), - .maxlen = sizeof(NMFibRulePortRange), - }, - [FRA_DPORT_RANGE] = - { - .minlen = sizeof(NMFibRulePortRange), - .maxlen = sizeof(NMFibRulePortRange), - }, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - const struct fib_rule_hdr *frh; - NMPlatformRoutingRule * props; - nm_auto_nmpobj NMPObject *obj = NULL; - int addr_family; - guint8 addr_size; - - if (nlmsg_parse_arr(nlh, sizeof(*frh), tb, policy) < 0) - return NULL; - - frh = nlmsg_data(nlh); - - addr_family = frh->family; - - if (!NM_IN_SET(addr_family, AF_INET, AF_INET6)) { - /* we don't care about other address families. */ - return NULL; - } - - addr_size = nm_utils_addr_family_to_size(addr_family); - - obj = nmp_object_new(NMP_OBJECT_TYPE_ROUTING_RULE, NULL); - props = &obj->routing_rule; - - props->addr_family = addr_family; - props->action = frh->action; - props->flags = frh->flags; - props->tos = frh->tos; - - props->table = tb[FRA_TABLE] ? nla_get_u32(tb[FRA_TABLE]) : frh->table; - - if (tb[FRA_SUPPRESS_PREFIXLEN]) - props->suppress_prefixlen_inverse = ~nla_get_u32(tb[FRA_SUPPRESS_PREFIXLEN]); - - if (tb[FRA_SUPPRESS_IFGROUP]) - props->suppress_ifgroup_inverse = ~nla_get_u32(tb[FRA_SUPPRESS_IFGROUP]); - - if (tb[FRA_IIFNAME]) - nla_strlcpy(props->iifname, tb[FRA_IIFNAME], sizeof(props->iifname)); - - if (tb[FRA_OIFNAME]) - nla_strlcpy(props->oifname, tb[FRA_OIFNAME], sizeof(props->oifname)); - - if (tb[FRA_PRIORITY]) - props->priority = nla_get_u32(tb[FRA_PRIORITY]); - - if (tb[FRA_FWMARK]) - props->fwmark = nla_get_u32(tb[FRA_FWMARK]); - - if (tb[FRA_FWMASK]) - props->fwmask = nla_get_u32(tb[FRA_FWMASK]); - - if (tb[FRA_GOTO]) - props->goto_target = nla_get_u32(tb[FRA_GOTO]); - - props->src_len = frh->src_len; - if (props->src_len > addr_size * 8) - return NULL; - if (!tb[FRA_SRC]) { - if (props->src_len > 0) - return NULL; - } else if (!nm_ip_addr_set_from_untrusted(addr_family, - &props->src, - nla_data(tb[FRA_SRC]), - nla_len(tb[FRA_SRC]), - NULL)) - return NULL; - - props->dst_len = frh->dst_len; - if (props->dst_len > addr_size * 8) - return NULL; - if (!tb[FRA_DST]) { - if (props->dst_len > 0) - return NULL; - } else if (!nm_ip_addr_set_from_untrusted(addr_family, - &props->dst, - nla_data(tb[FRA_DST]), - nla_len(tb[FRA_DST]), - NULL)) - return NULL; - - if (tb[FRA_FLOW]) - props->flow = nla_get_u32(tb[FRA_FLOW]); - - if (tb[FRA_TUN_ID]) - props->tun_id = nla_get_be64(tb[FRA_TUN_ID]); - - if (tb[FRA_L3MDEV]) { - if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) { - /* support for FRA_L3MDEV was added in 96c63fa7393d0a346acfe5a91e0c7d4c7782641b, - * kernel 4.8, 3 October 2017. - * - * We can only detect support if the attribute is present. A missing attribute - * is not conclusive. */ - _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, 1); - } - - /* actually, kernel only allows this attribute to be missing or - * "1". Still, encode it as full uint8. - * - * Note that FRA_L3MDEV and FRA_TABLE are mutally exclusive. */ - props->l3mdev = nla_get_u8(tb[FRA_L3MDEV]); - } - - if (tb[FRA_PROTOCOL]) - props->protocol = nla_get_u8(tb[FRA_PROTOCOL]); - else - nm_assert(props->protocol == RTPROT_UNSPEC); - - if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)) { - /* FRA_PROTOCOL was added in kernel 4.17, dated 3 June, 2018. - * See commit 1b71af6053af1bd2f849e9fda4f71c1e3f145dcf. */ - _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL, - tb[FRA_PROTOCOL] ? 1 : -1); - } - - if (tb[FRA_IP_PROTO]) - props->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]); - - G_STATIC_ASSERT_EXPR(sizeof(NMFibRulePortRange) == 4); - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRulePortRange, start) == 0); - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRulePortRange, end) == 2); - - nla_memcpy_checked_size(&props->sport_range, tb[FRA_SPORT_RANGE], sizeof(props->sport_range)); - nla_memcpy_checked_size(&props->dport_range, tb[FRA_DPORT_RANGE], sizeof(props->dport_range)); - - if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { - /* support for FRA_IP_PROTO, FRA_SPORT_RANGE, and FRA_DPORT_RANGE was added together - * by bfff4862653bb96001ab57c1edd6d03f48e5f035, kernel 4.17, 4 June 2018. - * - * Unfortunately, a missing attribute does not tell us anything about support. - * We can only tell for sure when we have support, but not when we don't have. */ - if (tb[FRA_IP_PROTO] || tb[FRA_SPORT_RANGE] || tb[FRA_DPORT_RANGE]) - _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO, 1); - } - - G_STATIC_ASSERT_EXPR(sizeof(NMFibRuleUidRange) == 8); - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRuleUidRange, start) == 0); - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(NMFibRuleUidRange, end) == 4); - - if (tb[FRA_UID_RANGE]) { - if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { - /* support for FRA_UID_RANGE was added in 622ec2c9d52405973c9f1ca5116eb1c393adfc7d, - * kernel 4.10, 19 February 2017. - * - * We can only detect support if the attribute is present. A missing attribute - * is not conclusive. */ - _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, 1); - } - - nla_memcpy_checked_size(&props->uid_range, tb[FRA_UID_RANGE], sizeof(props->uid_range)); - props->uid_range_has = TRUE; - } - - return g_steal_pointer(&obj); -} - -static guint32 -psched_tick_to_time(NMPlatform *platform, guint32 tick) -{ - static gboolean initialized; - static double tick_in_usec = 1; - - if (!initialized) { - gs_free char *params = NULL; - double clock_factor = 1; - guint32 clock_res; - guint32 t2us; - guint32 us2t; - - initialized = TRUE; - params = nm_platform_sysctl_get(platform, NMP_SYSCTL_PATHID_ABSOLUTE("/proc/net/psched")); - if (!params || sscanf(params, "%08x%08x%08x", &t2us, &us2t, &clock_res) != 3) { - _LOGW("packet scheduler parameters not available"); - } else { - /* See tc_core_init() in iproute2 */ - if (clock_res == 1000000000) - t2us = us2t; - - clock_factor = (double) clock_res / PSCHED_TIME_UNITS_PER_SEC; - tick_in_usec = (double) t2us / us2t * clock_factor; - } - } - - return tick / tick_in_usec; -} - -static NMPObject * -_new_from_nl_qdisc(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [TCA_KIND] = {.type = NLA_STRING}, - [TCA_OPTIONS] = {.type = NLA_NESTED}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - const struct tcmsg *tcm; - nm_auto_nmpobj NMPObject *obj = NULL; - - if (nlmsg_parse_arr(nlh, sizeof(*tcm), tb, policy) < 0) - return NULL; - - if (!tb[TCA_KIND]) - return NULL; - - tcm = nlmsg_data(nlh); - - obj = nmp_object_new(NMP_OBJECT_TYPE_QDISC, NULL); - - obj->qdisc.kind = g_intern_string(nla_get_string(tb[TCA_KIND])); - obj->qdisc.ifindex = tcm->tcm_ifindex; - obj->qdisc.addr_family = tcm->tcm_family; - obj->qdisc.handle = tcm->tcm_handle; - obj->qdisc.parent = tcm->tcm_parent; - obj->qdisc.info = tcm->tcm_info; - - if (nm_streq0(obj->qdisc.kind, "fq_codel")) { - obj->qdisc.fq_codel.memory_limit = NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET; - obj->qdisc.fq_codel.ce_threshold = NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED; - } - - if (tb[TCA_OPTIONS]) { - struct nlattr *options_attr; - int remaining; - - if (nm_streq0(obj->qdisc.kind, "sfq")) { - struct tc_sfq_qopt_v1 opt; - - if (tb[TCA_OPTIONS]->nla_len >= nla_attr_size(sizeof(opt))) { - memcpy(&opt, nla_data(tb[TCA_OPTIONS]), sizeof(opt)); - obj->qdisc.sfq.quantum = opt.v0.quantum; - obj->qdisc.sfq.perturb_period = opt.v0.perturb_period; - obj->qdisc.sfq.limit = opt.v0.limit; - obj->qdisc.sfq.divisor = opt.v0.divisor; - obj->qdisc.sfq.flows = opt.v0.flows; - obj->qdisc.sfq.depth = opt.depth; - } - } else if (nm_streq0(obj->qdisc.kind, "tbf")) { - static const struct nla_policy tbf_policy[] = { - [TCA_TBF_PARMS] = {.minlen = sizeof(struct tc_tbf_qopt)}, - [TCA_TBF_RATE64] = {.type = NLA_U64}, - }; - struct nlattr * tbf_tb[G_N_ELEMENTS(tbf_policy)]; - struct tc_tbf_qopt opt; - - if (nla_parse_nested_arr(tbf_tb, tb[TCA_OPTIONS], tbf_policy) < 0) - return NULL; - if (!tbf_tb[TCA_TBF_PARMS]) - return NULL; - - nla_memcpy_checked_size(&opt, tbf_tb[TCA_TBF_PARMS], sizeof(opt)); - obj->qdisc.tbf.rate = opt.rate.rate; - if (tbf_tb[TCA_TBF_RATE64]) - obj->qdisc.tbf.rate = nla_get_u64(tbf_tb[TCA_TBF_RATE64]); - obj->qdisc.tbf.burst = - ((double) obj->qdisc.tbf.rate * psched_tick_to_time(platform, opt.buffer)) - / PSCHED_TIME_UNITS_PER_SEC; - obj->qdisc.tbf.limit = opt.limit; - } else { - nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) { - if (nla_len(options_attr) < sizeof(uint32_t)) - continue; - - if (nm_streq0(obj->qdisc.kind, "fq_codel")) { - switch (nla_type(options_attr)) { - case TCA_FQ_CODEL_LIMIT: - obj->qdisc.fq_codel.limit = nla_get_u32(options_attr); - break; - case TCA_FQ_CODEL_FLOWS: - obj->qdisc.fq_codel.flows = nla_get_u32(options_attr); - break; - case TCA_FQ_CODEL_TARGET: - obj->qdisc.fq_codel.target = nla_get_u32(options_attr); - break; - case TCA_FQ_CODEL_INTERVAL: - obj->qdisc.fq_codel.interval = nla_get_u32(options_attr); - break; - case TCA_FQ_CODEL_QUANTUM: - obj->qdisc.fq_codel.quantum = nla_get_u32(options_attr); - break; - case TCA_FQ_CODEL_CE_THRESHOLD: - obj->qdisc.fq_codel.ce_threshold = nla_get_u32(options_attr); - break; - case TCA_FQ_CODEL_MEMORY_LIMIT: - obj->qdisc.fq_codel.memory_limit = nla_get_u32(options_attr); - break; - case TCA_FQ_CODEL_ECN: - obj->qdisc.fq_codel.ecn = !!nla_get_u32(options_attr); - break; - } - } - } - } - } - - return g_steal_pointer(&obj); -} - -static NMPObject * -_new_from_nl_tfilter(struct nlmsghdr *nlh, gboolean id_only) -{ - static const struct nla_policy policy[] = { - [TCA_KIND] = {.type = NLA_STRING}, - }; - struct nlattr * tb[G_N_ELEMENTS(policy)]; - NMPObject * obj = NULL; - const struct tcmsg *tcm; - - if (nlmsg_parse_arr(nlh, sizeof(*tcm), tb, policy) < 0) - return NULL; - - if (!tb[TCA_KIND]) - return NULL; - - tcm = nlmsg_data(nlh); - - obj = nmp_object_new(NMP_OBJECT_TYPE_TFILTER, NULL); - - obj->tfilter.kind = g_intern_string(nla_get_string(tb[TCA_KIND])); - obj->tfilter.ifindex = tcm->tcm_ifindex; - obj->tfilter.addr_family = tcm->tcm_family; - obj->tfilter.handle = tcm->tcm_handle; - obj->tfilter.parent = tcm->tcm_parent; - obj->tfilter.info = tcm->tcm_info; - - return obj; -} - -/** - * nmp_object_new_from_nl: - * @platform: (allow-none): for creating certain objects, the constructor wants to check - * sysfs. For this the platform instance is needed. If missing, the object might not - * be correctly detected. - * @cache: (allow-none): for certain objects, the netlink message doesn't contain all the information. - * If a cache is given, the object is completed with information from the cache. - * @nlh: the netlink message header - * @id_only: whether only to create an empty object with only the ID fields set. - * - * Returns: %NULL or a newly created NMPObject instance. - **/ -static NMPObject * -nmp_object_new_from_nl(NMPlatform * platform, - const NMPCache *cache, - struct nl_msg * msg, - gboolean id_only) -{ - struct nlmsghdr *msghdr; - - if (nlmsg_get_proto(msg) != NETLINK_ROUTE) - return NULL; - - msghdr = nlmsg_hdr(msg); - - switch (msghdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_DELLINK: - case RTM_GETLINK: - case RTM_SETLINK: - return _new_from_nl_link(platform, cache, msghdr, id_only); - case RTM_NEWADDR: - case RTM_DELADDR: - case RTM_GETADDR: - return _new_from_nl_addr(msghdr, id_only); - case RTM_NEWROUTE: - case RTM_DELROUTE: - case RTM_GETROUTE: - return _new_from_nl_route(msghdr, id_only); - case RTM_NEWRULE: - case RTM_DELRULE: - case RTM_GETRULE: - return _new_from_nl_routing_rule(msghdr, id_only); - case RTM_NEWQDISC: - case RTM_DELQDISC: - case RTM_GETQDISC: - return _new_from_nl_qdisc(platform, msghdr, id_only); - case RTM_NEWTFILTER: - case RTM_DELTFILTER: - case RTM_GETTFILTER: - return _new_from_nl_tfilter(msghdr, id_only); - default: - return NULL; - } -} - -/*****************************************************************************/ - -static gboolean -_nl_msg_new_link_set_afspec(struct nl_msg *msg, int addr_gen_mode, NMUtilsIPv6IfaceId *iid) -{ - struct nlattr *af_spec; - struct nlattr *af_attr; - - nm_assert(msg); - - if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC))) - goto nla_put_failure; - - if (addr_gen_mode >= 0 || iid) { - if (!(af_attr = nla_nest_start(msg, AF_INET6))) - goto nla_put_failure; - - if (addr_gen_mode >= 0) - NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode); - - if (iid) { - struct in6_addr i6_token = {.s6_addr = { - 0, - }}; - - nm_utils_ipv6_addr_set_interface_identifier(&i6_token, *iid); - NLA_PUT(msg, IFLA_INET6_TOKEN, sizeof(struct in6_addr), &i6_token); - } - - nla_nest_end(msg, af_attr); - } - - nla_nest_end(msg, af_spec); - - return TRUE; -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static gboolean -_nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpointer extra_data) -{ - struct nlattr *info; - struct nlattr *data = NULL; - const char * kind; - - nm_assert(msg); - - kind = nm_link_type_to_rtnl_type_string(link_type); - if (!kind) - goto nla_put_failure; - - if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) - goto nla_put_failure; - - NLA_PUT_STRING(msg, IFLA_INFO_KIND, kind); - - switch (link_type) { - case NM_LINK_TYPE_BRIDGE: - { - const NMPlatformLnkBridge *props = extra_data; - - nm_assert(extra_data); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U32(msg, IFLA_BR_FORWARD_DELAY, props->forward_delay); - NLA_PUT_U32(msg, IFLA_BR_HELLO_TIME, props->hello_time); - NLA_PUT_U32(msg, IFLA_BR_MAX_AGE, props->max_age); - NLA_PUT_U32(msg, IFLA_BR_AGEING_TIME, props->ageing_time); - NLA_PUT_U32(msg, IFLA_BR_STP_STATE, !!props->stp_state); - NLA_PUT_U16(msg, IFLA_BR_PRIORITY, props->priority); - NLA_PUT_U16(msg, IFLA_BR_VLAN_PROTOCOL, htons(props->vlan_protocol)); - if (props->vlan_stats_enabled) - NLA_PUT_U8(msg, IFLA_BR_VLAN_STATS_ENABLED, !!props->vlan_stats_enabled); - NLA_PUT_U16(msg, IFLA_BR_GROUP_FWD_MASK, props->group_fwd_mask); - NLA_PUT(msg, IFLA_BR_GROUP_ADDR, sizeof(props->group_addr), &props->group_addr); - NLA_PUT_U8(msg, IFLA_BR_MCAST_SNOOPING, !!props->mcast_snooping); - NLA_PUT_U8(msg, IFLA_BR_MCAST_ROUTER, props->mcast_router); - NLA_PUT_U8(msg, IFLA_BR_MCAST_QUERY_USE_IFADDR, !!props->mcast_query_use_ifaddr); - NLA_PUT_U8(msg, IFLA_BR_MCAST_QUERIER, !!props->mcast_querier); - NLA_PUT_U32(msg, IFLA_BR_MCAST_HASH_MAX, props->mcast_hash_max); - NLA_PUT_U32(msg, IFLA_BR_MCAST_LAST_MEMBER_CNT, props->mcast_last_member_count); - NLA_PUT_U32(msg, IFLA_BR_MCAST_STARTUP_QUERY_CNT, props->mcast_startup_query_count); - NLA_PUT_U64(msg, IFLA_BR_MCAST_LAST_MEMBER_INTVL, props->mcast_last_member_interval); - NLA_PUT_U64(msg, IFLA_BR_MCAST_MEMBERSHIP_INTVL, props->mcast_membership_interval); - NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERIER_INTVL, props->mcast_querier_interval); - NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERY_INTVL, props->mcast_query_interval); - NLA_PUT_U64(msg, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, props->mcast_query_response_interval); - NLA_PUT_U64(msg, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, props->mcast_startup_query_interval); - break; - } - case NM_LINK_TYPE_VLAN: - { - const NMPlatformLnkVlan *props = extra_data; - - nm_assert(extra_data); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U16(msg, IFLA_VLAN_ID, props->id); - - { - struct ifla_vlan_flags flags = { - .flags = props->flags & NM_VLAN_FLAGS_ALL, - .mask = NM_VLAN_FLAGS_ALL, - }; - - NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); - } - break; - } - case NM_LINK_TYPE_VRF: - { - const NMPlatformLnkVrf *props = extra_data; - - nm_assert(extra_data); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U32(msg, IFLA_VRF_TABLE, props->table); - break; - } - case NM_LINK_TYPE_VXLAN: - { - const NMPlatformLnkVxlan *props = extra_data; - - nm_assert(extra_data); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U32(msg, IFLA_VXLAN_ID, props->id); - - if (props->group) - NLA_PUT(msg, IFLA_VXLAN_GROUP, sizeof(props->group), &props->group); - else if (!IN6_IS_ADDR_UNSPECIFIED(&props->group6)) - NLA_PUT(msg, IFLA_VXLAN_GROUP6, sizeof(props->group6), &props->group6); - - if (props->local) - NLA_PUT(msg, IFLA_VXLAN_LOCAL, sizeof(props->local), &props->local); - else if (!IN6_IS_ADDR_UNSPECIFIED(&props->local6)) - NLA_PUT(msg, IFLA_VXLAN_LOCAL6, sizeof(props->local6), &props->local6); - - if (props->parent_ifindex >= 0) - NLA_PUT_U32(msg, IFLA_VXLAN_LINK, props->parent_ifindex); - - if (props->src_port_min || props->src_port_max) { - struct nm_ifla_vxlan_port_range port_range = { - .low = htons(props->src_port_min), - .high = htons(props->src_port_max), - }; - - NLA_PUT(msg, IFLA_VXLAN_PORT_RANGE, sizeof(port_range), &port_range); - } - - NLA_PUT_U16(msg, IFLA_VXLAN_PORT, htons(props->dst_port)); - NLA_PUT_U8(msg, IFLA_VXLAN_TOS, props->tos); - NLA_PUT_U8(msg, IFLA_VXLAN_TTL, props->ttl); - NLA_PUT_U32(msg, IFLA_VXLAN_AGEING, props->ageing); - NLA_PUT_U32(msg, IFLA_VXLAN_LIMIT, props->limit); - NLA_PUT_U8(msg, IFLA_VXLAN_LEARNING, !!props->learning); - NLA_PUT_U8(msg, IFLA_VXLAN_PROXY, !!props->proxy); - NLA_PUT_U8(msg, IFLA_VXLAN_RSC, !!props->rsc); - NLA_PUT_U8(msg, IFLA_VXLAN_L2MISS, !!props->l2miss); - NLA_PUT_U8(msg, IFLA_VXLAN_L3MISS, !!props->l3miss); - break; - } - case NM_LINK_TYPE_VETH: - { - const char * veth_peer = extra_data; - const struct ifinfomsg ifi = {}; - struct nlattr * info_peer; - - nm_assert(veth_peer); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - if (!(info_peer = nla_nest_start(msg, 1 /*VETH_INFO_PEER*/))) - goto nla_put_failure; - if (nlmsg_append_struct(msg, &ifi) < 0) - goto nla_put_failure; - NLA_PUT_STRING(msg, IFLA_IFNAME, veth_peer); - nla_nest_end(msg, info_peer); - break; - } - case NM_LINK_TYPE_GRE: - case NM_LINK_TYPE_GRETAP: - { - const NMPlatformLnkGre *props = extra_data; - - nm_assert(props); - nm_assert(props->is_tap == (link_type == NM_LINK_TYPE_GRETAP)); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32(msg, IFLA_GRE_LINK, props->parent_ifindex); - NLA_PUT_U32(msg, IFLA_GRE_LOCAL, props->local); - NLA_PUT_U32(msg, IFLA_GRE_REMOTE, props->remote); - NLA_PUT_U8(msg, IFLA_GRE_TTL, props->ttl); - NLA_PUT_U8(msg, IFLA_GRE_TOS, props->tos); - NLA_PUT_U8(msg, IFLA_GRE_PMTUDISC, !!props->path_mtu_discovery); - NLA_PUT_U32(msg, IFLA_GRE_IKEY, htonl(props->input_key)); - NLA_PUT_U32(msg, IFLA_GRE_OKEY, htonl(props->output_key)); - NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, htons(props->input_flags)); - NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, htons(props->output_flags)); - break; - } - case NM_LINK_TYPE_SIT: - { - const NMPlatformLnkSit *props = extra_data; - - nm_assert(props); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex); - NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, props->local); - NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, props->remote); - NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl); - NLA_PUT_U8(msg, IFLA_IPTUN_TOS, props->tos); - NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); - break; - } - case NM_LINK_TYPE_IP6TNL: - { - const NMPlatformLnkIp6Tnl *props = extra_data; - guint32 flowinfo; - - nm_assert(props); - nm_assert(!props->is_gre); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex); - - if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) - NLA_PUT(msg, IFLA_IPTUN_LOCAL, sizeof(props->local), &props->local); - if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote)) - NLA_PUT(msg, IFLA_IPTUN_REMOTE, sizeof(props->remote), &props->remote); - - NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl); - NLA_PUT_U8(msg, IFLA_IPTUN_ENCAP_LIMIT, props->encap_limit); - - flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK; - flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) & IP6_FLOWINFO_TCLASS_MASK; - NLA_PUT_U32(msg, IFLA_IPTUN_FLOWINFO, htonl(flowinfo)); - NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, props->proto); - NLA_PUT_U32(msg, IFLA_IPTUN_FLAGS, props->flags); - break; - } - case NM_LINK_TYPE_IP6GRE: - case NM_LINK_TYPE_IP6GRETAP: - { - const NMPlatformLnkIp6Tnl *props = extra_data; - guint32 flowinfo; - - nm_assert(props); - nm_assert(props->is_gre); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32(msg, IFLA_GRE_LINK, props->parent_ifindex); - - NLA_PUT_U32(msg, IFLA_GRE_IKEY, htonl(props->input_key)); - NLA_PUT_U32(msg, IFLA_GRE_OKEY, htonl(props->output_key)); - NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, htons(props->input_flags)); - NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, htons(props->output_flags)); - - if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) - NLA_PUT(msg, IFLA_GRE_LOCAL, sizeof(props->local), &props->local); - if (!IN6_IS_ADDR_UNSPECIFIED(&props->local)) - NLA_PUT(msg, IFLA_GRE_REMOTE, sizeof(props->remote), &props->remote); - - NLA_PUT_U8(msg, IFLA_GRE_TTL, props->ttl); - NLA_PUT_U8(msg, IFLA_GRE_ENCAP_LIMIT, props->encap_limit); - - flowinfo = props->flow_label & IP6_FLOWINFO_FLOWLABEL_MASK; - flowinfo |= (props->tclass << IP6_FLOWINFO_TCLASS_SHIFT) & IP6_FLOWINFO_TCLASS_MASK; - NLA_PUT_U32(msg, IFLA_GRE_FLOWINFO, htonl(flowinfo)); - NLA_PUT_U32(msg, IFLA_GRE_FLAGS, props->flags); - break; - } - case NM_LINK_TYPE_IPIP: - { - const NMPlatformLnkIpIp *props = extra_data; - - nm_assert(props); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->parent_ifindex) - NLA_PUT_U32(msg, IFLA_IPTUN_LINK, props->parent_ifindex); - NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, props->local); - NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, props->remote); - NLA_PUT_U8(msg, IFLA_IPTUN_TTL, props->ttl); - NLA_PUT_U8(msg, IFLA_IPTUN_TOS, props->tos); - NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, !!props->path_mtu_discovery); - break; - } - case NM_LINK_TYPE_MACSEC: - { - const NMPlatformLnkMacsec *props = extra_data; - - nm_assert(props); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (props->icv_length) - NLA_PUT_U8(msg, IFLA_MACSEC_ICV_LEN, 16); - if (props->cipher_suite) - NLA_PUT_U64(msg, IFLA_MACSEC_CIPHER_SUITE, props->cipher_suite); - if (props->replay_protect) - NLA_PUT_U32(msg, IFLA_MACSEC_WINDOW, props->window); - - NLA_PUT_U64(msg, IFLA_MACSEC_SCI, htobe64(props->sci)); - NLA_PUT_U8(msg, IFLA_MACSEC_ENCODING_SA, props->encoding_sa); - NLA_PUT_U8(msg, IFLA_MACSEC_ENCRYPT, props->encrypt); - NLA_PUT_U8(msg, IFLA_MACSEC_PROTECT, props->protect); - NLA_PUT_U8(msg, IFLA_MACSEC_INC_SCI, props->include_sci); - NLA_PUT_U8(msg, IFLA_MACSEC_ES, props->es); - NLA_PUT_U8(msg, IFLA_MACSEC_SCB, props->scb); - NLA_PUT_U8(msg, IFLA_MACSEC_REPLAY_PROTECT, props->replay_protect); - NLA_PUT_U8(msg, IFLA_MACSEC_VALIDATION, props->validation); - break; - }; - case NM_LINK_TYPE_MACVTAP: - case NM_LINK_TYPE_MACVLAN: - { - const NMPlatformLnkMacvlan *props = extra_data; - - nm_assert(props); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, props->mode); - NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0); - break; - } - default: - nm_assert(!extra_data); - break; - } - - if (data) - nla_nest_end(msg, data); - - nla_nest_end(msg, info); - - return TRUE; -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static gboolean -_nl_msg_new_link_set_linkinfo_vlan(struct nl_msg * msg, - int vlan_id, - guint32 flags_mask, - guint32 flags_set, - const NMVlanQosMapping *ingress_qos, - int ingress_qos_len, - const NMVlanQosMapping *egress_qos, - int egress_qos_len) -{ - struct nlattr *info; - struct nlattr *data; - guint i; - gboolean has_any_vlan_properties = FALSE; - - G_STATIC_ASSERT(NM_VLAN_FLAG_REORDER_HEADERS == (guint32) VLAN_FLAG_REORDER_HDR); - G_STATIC_ASSERT(NM_VLAN_FLAG_GVRP == (guint32) VLAN_FLAG_GVRP); - G_STATIC_ASSERT(NM_VLAN_FLAG_LOOSE_BINDING == (guint32) VLAN_FLAG_LOOSE_BINDING); - G_STATIC_ASSERT(NM_VLAN_FLAG_MVRP == (guint32) VLAN_FLAG_MVRP); - -#define VLAN_XGRESS_PRIO_VALID(from) (((from) & ~(guint32) 0x07) == 0) - - nm_assert(msg); - - /* We must not create an empty IFLA_LINKINFO section. Otherwise, kernel - * rejects the request as invalid. */ - if (flags_mask != 0 || vlan_id >= 0) - has_any_vlan_properties = TRUE; - if (!has_any_vlan_properties && ingress_qos && ingress_qos_len > 0) { - for (i = 0; i < ingress_qos_len; i++) { - if (VLAN_XGRESS_PRIO_VALID(ingress_qos[i].from)) { - has_any_vlan_properties = TRUE; - break; - } - } - } - if (!has_any_vlan_properties && egress_qos && egress_qos_len > 0) { - for (i = 0; i < egress_qos_len; i++) { - if (VLAN_XGRESS_PRIO_VALID(egress_qos[i].to)) { - has_any_vlan_properties = TRUE; - break; - } - } - } - if (!has_any_vlan_properties) - return TRUE; - - if (!(info = nla_nest_start(msg, IFLA_LINKINFO))) - goto nla_put_failure; - - NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan"); - - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; - - if (vlan_id >= 0) - NLA_PUT_U16(msg, IFLA_VLAN_ID, vlan_id); - - if (flags_mask != 0) { - struct ifla_vlan_flags flags = { - .flags = flags_mask & flags_set, - .mask = flags_mask, - }; - - NLA_PUT(msg, IFLA_VLAN_FLAGS, sizeof(flags), &flags); - } - - if (ingress_qos && ingress_qos_len > 0) { - struct nlattr *qos = NULL; - - for (i = 0; i < ingress_qos_len; i++) { - /* Silently ignore invalid mappings. Kernel would truncate - * them and modify the wrong mapping. */ - if (VLAN_XGRESS_PRIO_VALID(ingress_qos[i].from)) { - if (!qos) { - if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS))) - goto nla_put_failure; - } - NLA_PUT(msg, i, sizeof(ingress_qos[i]), &ingress_qos[i]); - } - } - - if (qos) - nla_nest_end(msg, qos); - } - - if (egress_qos && egress_qos_len > 0) { - struct nlattr *qos = NULL; - - for (i = 0; i < egress_qos_len; i++) { - if (VLAN_XGRESS_PRIO_VALID(egress_qos[i].to)) { - if (!qos) { - if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) - goto nla_put_failure; - } - NLA_PUT(msg, i, sizeof(egress_qos[i]), &egress_qos[i]); - } - } - - if (qos) - nla_nest_end(msg, qos); - } - - nla_nest_end(msg, data); - nla_nest_end(msg, info); - - return TRUE; -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static struct nl_msg * -_nl_msg_new_link_full(int nlmsg_type, - int nlmsg_flags, - int ifindex, - const char *ifname, - guint8 family, - unsigned flags_mask, - unsigned flags_set) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - const struct ifinfomsg ifi = { - .ifi_family = family, - .ifi_change = flags_mask, - .ifi_flags = flags_set, - .ifi_index = ifindex, - }; - - nm_assert(NM_IN_SET(nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK, RTM_SETLINK)); - - msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); - - if (nlmsg_append_struct(msg, &ifi) < 0) - goto nla_put_failure; - - if (ifname) - NLA_PUT_STRING(msg, IFLA_IFNAME, ifname); - - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static struct nl_msg * -_nl_msg_new_link(int nlmsg_type, int nlmsg_flags, int ifindex, const char *ifname) -{ - return _nl_msg_new_link_full(nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0); -} - -/* Copied and modified from libnl3's build_addr_msg(). */ -static struct nl_msg * -_nl_msg_new_address(int nlmsg_type, - int nlmsg_flags, - int family, - int ifindex, - gconstpointer address, - guint8 plen, - gconstpointer peer_address, - guint32 flags, - int scope, - guint32 lifetime, - guint32 preferred, - in_addr_t ip4_broadcast_address, - const char * label) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct ifaddrmsg am = { - .ifa_family = family, - .ifa_index = ifindex, - .ifa_prefixlen = plen, - .ifa_flags = flags, - }; - gsize addr_len; - - nm_assert(NM_IN_SET(family, AF_INET, AF_INET6)); - nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWADDR, RTM_DELADDR)); - - msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); - - if (scope == -1) { - /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ - if (family == AF_INET && address && *((char *) address) == 127) - scope = RT_SCOPE_HOST; - else - scope = RT_SCOPE_UNIVERSE; - } - am.ifa_scope = scope, - - addr_len = family == AF_INET ? sizeof(in_addr_t) : sizeof(struct in6_addr); - - if (nlmsg_append_struct(msg, &am) < 0) - goto nla_put_failure; - - if (address) - NLA_PUT(msg, IFA_LOCAL, addr_len, address); - - if (peer_address) - NLA_PUT(msg, IFA_ADDRESS, addr_len, peer_address); - else if (address) - NLA_PUT(msg, IFA_ADDRESS, addr_len, address); - - if (label && label[0]) - NLA_PUT_STRING(msg, IFA_LABEL, label); - - if (ip4_broadcast_address != 0) - NLA_PUT(msg, IFA_BROADCAST, sizeof(in_addr_t), &ip4_broadcast_address); - - if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { - struct ifa_cacheinfo ca = { - .ifa_valid = lifetime, - .ifa_prefered = preferred, - }; - - NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca); - } - - if (flags & ~((guint32) 0xFF)) { - /* only set the IFA_FLAGS attribute, if they actually contain additional - * flags that are not already set to am.ifa_flags. - * - * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL - * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which - * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. */ - NLA_PUT_U32(msg, IFA_FLAGS, flags); - } - - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static guint32 -ip_route_get_lock_flag(const NMPlatformIPRoute *route) -{ - return (((guint32) route->lock_window) << RTAX_WINDOW) - | (((guint32) route->lock_cwnd) << RTAX_CWND) - | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) - | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) - | (((guint32) route->lock_mtu) << RTAX_MTU); -} - -/* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ -static struct nl_msg * -_nl_msg_new_route(int nlmsg_type, guint16 nlmsgflags, const NMPObject *obj) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - const NMPClass * klass = NMP_OBJECT_GET_CLASS(obj); - gboolean is_v4 = klass->addr_family == AF_INET; - const guint32 lock = ip_route_get_lock_flag(NMP_OBJECT_CAST_IP_ROUTE(obj)); - const guint32 table = - nm_platform_route_table_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->table_coerced, TRUE); - const struct rtmsg rtmsg = { - .rtm_family = klass->addr_family, - .rtm_tos = is_v4 ? obj->ip4_route.tos : 0, - .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, - .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot(obj->ip_route.rt_source), - .rtm_scope = - is_v4 ? nm_platform_route_scope_inv(obj->ip4_route.scope_inv) : RT_SCOPE_NOWHERE, - .rtm_type = nm_platform_route_type_uncoerce(NMP_OBJECT_CAST_IP_ROUTE(obj)->type_coerced), - .rtm_flags = obj->ip_route.r_rtm_flags & ((unsigned) (RTNH_F_ONLINK)), - .rtm_dst_len = obj->ip_route.plen, - .rtm_src_len = is_v4 ? 0 : NMP_OBJECT_CAST_IP6_ROUTE(obj)->src_plen, - }; - - gsize addr_len; - - nm_assert( - NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); - - msg = nlmsg_alloc_simple(nlmsg_type, (int) nlmsgflags); - - if (nlmsg_append_struct(msg, &rtmsg) < 0) - goto nla_put_failure; - - addr_len = is_v4 ? sizeof(in_addr_t) : sizeof(struct in6_addr); - - NLA_PUT(msg, - RTA_DST, - addr_len, - is_v4 ? (gconstpointer) &obj->ip4_route.network - : (gconstpointer) &obj->ip6_route.network); - - if (!is_v4) { - if (!IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(obj)->src)) - NLA_PUT(msg, RTA_SRC, addr_len, &obj->ip6_route.src); - } - - NLA_PUT_U32(msg, - RTA_PRIORITY, - is_v4 ? nm_platform_ip4_route_get_effective_metric(&obj->ip4_route) - : nm_platform_ip6_route_get_effective_metric(&obj->ip6_route)); - - if (table > 0xFF) - NLA_PUT_U32(msg, RTA_TABLE, table); - - if (is_v4) { - if (NMP_OBJECT_CAST_IP4_ROUTE(obj)->pref_src) - NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src); - } else { - if (!IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(obj)->pref_src)) - NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); - } - - if (obj->ip_route.mss || obj->ip_route.window || obj->ip_route.cwnd || obj->ip_route.initcwnd - || obj->ip_route.initrwnd || obj->ip_route.mtu || lock) { - struct nlattr *metrics; - - metrics = nla_nest_start(msg, RTA_METRICS); - if (!metrics) - goto nla_put_failure; - - if (obj->ip_route.mss) - NLA_PUT_U32(msg, RTAX_ADVMSS, obj->ip_route.mss); - if (obj->ip_route.window) - NLA_PUT_U32(msg, RTAX_WINDOW, obj->ip_route.window); - if (obj->ip_route.cwnd) - NLA_PUT_U32(msg, RTAX_CWND, obj->ip_route.cwnd); - if (obj->ip_route.initcwnd) - NLA_PUT_U32(msg, RTAX_INITCWND, obj->ip_route.initcwnd); - if (obj->ip_route.initrwnd) - NLA_PUT_U32(msg, RTAX_INITRWND, obj->ip_route.initrwnd); - if (obj->ip_route.mtu) - NLA_PUT_U32(msg, RTAX_MTU, obj->ip_route.mtu); - if (lock) - NLA_PUT_U32(msg, RTAX_LOCK, lock); - - nla_nest_end(msg, metrics); - } - - /* We currently don't have need for multi-hop routes... */ - if (is_v4) { - NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway); - } else { - if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway)) - NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway); - } - NLA_PUT_U32(msg, RTA_OIF, obj->ip_route.ifindex); - - if (!is_v4 && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM) - NLA_PUT_U8(msg, RTA_PREF, obj->ip6_route.rt_pref); - - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static struct nl_msg * -_nl_msg_new_routing_rule(int nlmsg_type, int nlmsg_flags, const NMPlatformRoutingRule *routing_rule) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - const guint8 addr_size = nm_utils_addr_family_to_size(routing_rule->addr_family); - guint32 table; - - msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); - - table = routing_rule->table; - - if (NM_IN_SET(routing_rule->addr_family, AF_INET, AF_INET6) - && routing_rule->action == FR_ACT_TO_TBL && routing_rule->l3mdev == 0 - && table == RT_TABLE_UNSPEC) { - /* for IPv6, this setting is invalid and rejected by kernel. That's fine. - * - * for IPv4, kernel will automatically assign an unused table. That's not - * fine, because we don't know what we will get. - * - * The caller must not allow that to happen. */ - nm_assert_not_reached(); - } - - { - const struct fib_rule_hdr frh = { - .family = routing_rule->addr_family, - .src_len = routing_rule->src_len, - .dst_len = routing_rule->dst_len, - .tos = routing_rule->tos, - .table = table < 0x100u ? (guint8) table : (guint8) RT_TABLE_UNSPEC, - .action = routing_rule->action, - - /* we only allow setting the "not" flag. */ - .flags = routing_rule->flags & ((guint32) FIB_RULE_INVERT), - }; - - if (nlmsg_append_struct(msg, &frh) < 0) - goto nla_put_failure; - } - - if (table > G_MAXINT8) - NLA_PUT_U32(msg, FRA_TABLE, table); - - if (routing_rule->suppress_prefixlen_inverse != 0) - NLA_PUT_U32(msg, FRA_SUPPRESS_PREFIXLEN, ~routing_rule->suppress_prefixlen_inverse); - - if (routing_rule->suppress_ifgroup_inverse != 0) - NLA_PUT_U32(msg, FRA_SUPPRESS_IFGROUP, ~routing_rule->suppress_ifgroup_inverse); - - if (routing_rule->iifname[0] != '\0') - NLA_PUT_STRING(msg, FRA_IIFNAME, routing_rule->iifname); - - if (routing_rule->oifname[0] != '\0') - NLA_PUT_STRING(msg, FRA_OIFNAME, routing_rule->oifname); - - /* we always set the priority and don't support letting kernel pick one. */ - NLA_PUT_U32(msg, FRA_PRIORITY, routing_rule->priority); - - if (routing_rule->fwmark != 0 || routing_rule->fwmask != 0) { - NLA_PUT_U32(msg, FRA_FWMARK, routing_rule->fwmark); - NLA_PUT_U32(msg, FRA_FWMASK, routing_rule->fwmask); - } - - if (routing_rule->src_len > 0) - NLA_PUT(msg, FRA_SRC, addr_size, &routing_rule->src); - - if (routing_rule->dst_len > 0) - NLA_PUT(msg, FRA_DST, addr_size, &routing_rule->dst); - - if (routing_rule->flow != 0) { - /* only relevant for IPv4. */ - NLA_PUT_U32(msg, FRA_FLOW, routing_rule->flow); - } - - if (routing_rule->tun_id != 0) - NLA_PUT_U64(msg, FRA_TUN_ID, htobe64(routing_rule->tun_id)); - - if (routing_rule->l3mdev) - NLA_PUT_U8(msg, FRA_L3MDEV, routing_rule->l3mdev); - - if (routing_rule->protocol != RTPROT_UNSPEC) - NLA_PUT_U8(msg, FRA_PROTOCOL, routing_rule->protocol); - - if (routing_rule->ip_proto != 0) - NLA_PUT_U8(msg, FRA_IP_PROTO, routing_rule->ip_proto); - - if (routing_rule->sport_range.start || routing_rule->sport_range.end) - NLA_PUT(msg, - FRA_SPORT_RANGE, - sizeof(routing_rule->sport_range), - &routing_rule->sport_range); - - if (routing_rule->dport_range.start || routing_rule->dport_range.end) - NLA_PUT(msg, - FRA_DPORT_RANGE, - sizeof(routing_rule->dport_range), - &routing_rule->dport_range); - - if (routing_rule->uid_range_has) - NLA_PUT(msg, FRA_UID_RANGE, sizeof(routing_rule->uid_range), &routing_rule->uid_range); - - switch (routing_rule->action) { - case FR_ACT_GOTO: - NLA_PUT_U32(msg, FRA_GOTO, routing_rule->goto_target); - break; - } - - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static struct nl_msg * -_nl_msg_new_qdisc(int nlmsg_type, int nlmsg_flags, const NMPlatformQdisc *qdisc) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nlattr * tc_options; - const struct tcmsg tcm = { - .tcm_family = qdisc->addr_family, - .tcm_ifindex = qdisc->ifindex, - .tcm_handle = qdisc->handle, - .tcm_parent = qdisc->parent, - .tcm_info = qdisc->info, - }; - - msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); - - if (nlmsg_append_struct(msg, &tcm) < 0) - goto nla_put_failure; - - NLA_PUT_STRING(msg, TCA_KIND, qdisc->kind); - - if (nm_streq(qdisc->kind, "sfq")) { - struct tc_sfq_qopt_v1 opt = {}; - - opt.v0.quantum = qdisc->sfq.quantum; - opt.v0.limit = qdisc->sfq.limit; - opt.v0.perturb_period = qdisc->sfq.perturb_period; - opt.v0.flows = qdisc->sfq.flows; - opt.v0.divisor = qdisc->sfq.divisor; - opt.depth = qdisc->sfq.depth; - - NLA_PUT(msg, TCA_OPTIONS, sizeof(opt), &opt); - } else if (nm_streq(qdisc->kind, "tbf")) { - struct tc_tbf_qopt opt = {}; - - if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS))) - goto nla_put_failure; - - opt.rate.rate = (qdisc->tbf.rate >= (1ULL << 32)) ? ~0U : (guint32) qdisc->tbf.rate; - if (qdisc->tbf.limit) - opt.limit = qdisc->tbf.limit; - else if (qdisc->tbf.latency) { - opt.limit = qdisc->tbf.rate * (double) qdisc->tbf.latency / PSCHED_TIME_UNITS_PER_SEC - + qdisc->tbf.burst; - } - - NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opt), &opt); - if (qdisc->tbf.rate >= (1ULL << 32)) - NLA_PUT_U64(msg, TCA_TBF_RATE64, qdisc->tbf.rate); - NLA_PUT_U32(msg, TCA_TBF_BURST, qdisc->tbf.burst); - - nla_nest_end(msg, tc_options); - } else if (nm_streq(qdisc->kind, "prio")) { - struct tc_prio_qopt opt = {3, {1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}}; - - NLA_PUT(msg, TCA_OPTIONS, sizeof(opt), &opt); - } else { - if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS))) - goto nla_put_failure; - - if (nm_streq(qdisc->kind, "fq_codel")) { - if (qdisc->fq_codel.limit) - NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit); - if (qdisc->fq_codel.flows) - NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows); - if (qdisc->fq_codel.target) - NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target); - if (qdisc->fq_codel.interval) - NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval); - if (qdisc->fq_codel.quantum) - NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum); - if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) - NLA_PUT_U32(msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold); - if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) - NLA_PUT_U32(msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit); - if (qdisc->fq_codel.ecn) - NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn); - } - - nla_nest_end(msg, tc_options); - } - - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static struct nl_msg * -_nl_msg_new_tfilter(int nlmsg_type, int nlmsg_flags, const NMPlatformTfilter *tfilter) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nlattr * tc_options; - struct nlattr * act_tab; - const struct tcmsg tcm = { - .tcm_family = tfilter->addr_family, - .tcm_ifindex = tfilter->ifindex, - .tcm_handle = tfilter->handle, - .tcm_parent = tfilter->parent, - .tcm_info = tfilter->info, - }; - - msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags | NMP_NLM_FLAG_F_ECHO); - - if (nlmsg_append_struct(msg, &tcm) < 0) - goto nla_put_failure; - - NLA_PUT_STRING(msg, TCA_KIND, tfilter->kind); - - if (!(tc_options = nla_nest_start(msg, TCA_OPTIONS))) - goto nla_put_failure; - - if (!(act_tab = nla_nest_start(msg, TCA_OPTIONS))) // 3 TCA_ACT_KIND TCA_ACT_KIND - goto nla_put_failure; - - if (tfilter->action.kind) { - const NMPlatformAction *action = &tfilter->action; - struct nlattr * prio; - struct nlattr * act_options; - - if (!(prio = nla_nest_start(msg, 1 /* priority */))) - goto nla_put_failure; - - NLA_PUT_STRING(msg, TCA_ACT_KIND, action->kind); - - if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { - const NMPlatformActionSimple *simple = &action->simple; - struct tc_defact sel = { - 0, - }; - - if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) - goto nla_put_failure; - - NLA_PUT(msg, TCA_DEF_PARMS, sizeof(sel), &sel); - NLA_PUT(msg, TCA_DEF_DATA, sizeof(simple->sdata), simple->sdata); - - nla_nest_end(msg, act_options); - - } else if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { - const NMPlatformActionMirred *mirred = &action->mirred; - struct tc_mirred sel = { - 0, - }; - - if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) - goto nla_put_failure; - - if (mirred->egress && mirred->redirect) - sel.eaction = TCA_EGRESS_REDIR; - else if (mirred->egress && mirred->mirror) - sel.eaction = TCA_EGRESS_MIRROR; - else if (mirred->ingress && mirred->redirect) - sel.eaction = TCA_INGRESS_REDIR; - else if (mirred->ingress && mirred->mirror) - sel.eaction = TCA_INGRESS_MIRROR; - sel.ifindex = mirred->ifindex; - - NLA_PUT(msg, TCA_MIRRED_PARMS, sizeof(sel), &sel); - - nla_nest_end(msg, act_options); - } - - nla_nest_end(msg, prio); - } - - nla_nest_end(msg, tc_options); - - nla_nest_end(msg, act_tab); - - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -/*****************************************************************************/ - -static struct nl_sock * -_genl_sock(NMLinuxPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - - return priv->genl; -} - -#define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ - G_STMT_START \ - { \ - const char *const _pathid = (pathid); \ - const int _dirfd = (dirfd); \ - const char *const _path = (path); \ - \ - nm_assert(_path &&_path[0]); \ - g_assert(!strstr(_path, "/../")); \ - if (_dirfd < 0) { \ - nm_assert(!_pathid); \ - nm_assert(_path[0] == '/'); \ - nm_assert(g_str_has_prefix(_path, "/proc/sys/") || g_str_has_prefix(_path, "/sys/") \ - || g_str_has_prefix(_path, "/proc/net")); \ - } else { \ - nm_assert(_pathid &&_pathid[0] && _pathid[0] != '/'); \ - nm_assert(_path[0] != '/'); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -/* core sysctl-set functions can be called from a non-main thread. - * Hence, we require locking from nm-logging. Indicate that by - * setting NM_THREAD_SAFE_ON_MAIN_THREAD to zero. */ -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 - -static void -_log_dbg_sysctl_set_impl(NMPlatform *platform, - const char *pathid, - int dirfd, - const char *path, - const char *value) -{ - GError * error = NULL; - gs_free char *contents = NULL; - gs_free char *value_escaped = g_strescape(value, NULL); - - if (!nm_utils_file_get_contents(dirfd, - path, - 1 * 1024 * 1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - NULL, - NULL, - &error)) { - _LOGD("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", - pathid ?: path, - value_escaped, - error->message); - g_clear_error(&error); - return; - } - - g_strstrip(contents); - if (nm_streq(contents, value)) - _LOGD("sysctl: setting '%s' to '%s' (current value is identical)", - pathid ?: path, - value_escaped); - else { - gs_free char *contents_escaped = g_strescape(contents, NULL); - - _LOGD("sysctl: setting '%s' to '%s' (current value is '%s')", - pathid ?: path, - value_escaped, - contents_escaped); - } -} - -#define _log_dbg_sysctl_set(platform, pathid, dirfd, path, value) \ - G_STMT_START \ - { \ - if (_LOGD_ENABLED()) { \ - _log_dbg_sysctl_set_impl(platform, pathid, dirfd, path, value); \ - } \ - } \ - G_STMT_END - -static gboolean -sysctl_set_internal(NMPlatform *platform, - const char *pathid, - int dirfd, - const char *path, - const char *value) -{ - int fd, tries; - gssize nwrote; - gssize len; - char * actual; - gs_free char *actual_free = NULL; - int errsv; - - if (dirfd < 0) { - pathid = path; - - fd = open(path, O_WRONLY | O_TRUNC | O_CLOEXEC); - if (fd == -1) { - errsv = errno; - if (errsv == ENOENT) { - _LOGD("sysctl: failed to open '%s': (%d) %s", - pathid, - errsv, - nm_strerror_native(errsv)); - } else { - _LOGE("sysctl: failed to open '%s': (%d) %s", - pathid, - errsv, - nm_strerror_native(errsv)); - } - errno = errsv; - return FALSE; - } - } else { - fd = openat(dirfd, path, O_WRONLY | O_TRUNC | O_CLOEXEC); - if (fd == -1) { - errsv = errno; - if (errsv == ENOENT) { - _LOGD("sysctl: failed to openat '%s': (%d) %s", - pathid, - errsv, - nm_strerror_native(errsv)); - } else { - _LOGE("sysctl: failed to openat '%s': (%d) %s", - pathid, - errsv, - nm_strerror_native(errsv)); - } - errno = errsv; - return FALSE; - } - } - - _log_dbg_sysctl_set(platform, pathid, dirfd, path, value); - - /* Most sysfs and sysctl options don't care about a trailing LF, while some - * (like infiniband) do. So always add the LF. Also, neither sysfs nor - * sysctl support partial writes so the LF must be added to the string we're - * about to write. - */ - len = strlen(value) + 1; - nm_assert(len > 0); - if (len > 512) - actual = actual_free = g_malloc(len + 1); - else - actual = g_alloca(len + 1); - memcpy(actual, value, len - 1); - actual[len - 1] = '\n'; - actual[len] = '\0'; - - /* Try to write the entire value three times if a partial write occurs */ - errsv = 0; - for (tries = 0, nwrote = 0; tries < 3 && nwrote < len - 1; tries++) { - nwrote = write(fd, actual, len); - if (nwrote == -1) { - errsv = errno; - if (errsv == EINTR) { - _LOGD("sysctl: interrupted, will try again"); - continue; - } - break; - } - } - if (nwrote == -1) { - NMLogLevel level = LOGL_ERR; - - if (errsv == EEXIST) { - level = LOGL_DEBUG; - } else if (errsv == EINVAL - && nm_utils_sysctl_ip_conf_is_path(AF_INET6, path, NULL, "mtu")) { - /* setting the MTU can fail under regular conditions. Suppress - * logging a warning. */ - level = LOGL_DEBUG; - } - - _NMLOG(level, - "sysctl: failed to set '%s' to '%s': (%d) %s", - path, - value, - errsv, - nm_strerror_native(errsv)); - } else if (nwrote < len - 1) { - _LOGE("sysctl: failed to set '%s' to '%s' after three attempts", path, value); - } - - if (nwrote < len - 1) { - if (nm_close(fd) != 0) { - if (errsv != 0) - errno = errsv; - } else if (errsv != 0) - errno = errsv; - else - errno = EIO; - return FALSE; - } - if (nm_close(fd) != 0) { - /* errno is already properly set. */ - return FALSE; - } - - /* success. errno is undefined (no need to set). */ - return TRUE; -} - -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 - -/*****************************************************************************/ - -static gboolean -sysctl_set(NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - - g_return_val_if_fail(path, FALSE); - g_return_val_if_fail(value, FALSE); - - ASSERT_SYSCTL_ARGS(pathid, dirfd, path); - - if (dirfd < 0 && !nm_platform_netns_push(platform, &netns)) { - errno = ENETDOWN; - return FALSE; - } - - return sysctl_set_internal(platform, pathid, dirfd, path, value); -} - -typedef struct { - NMPlatform * platform; - char * pathid; - int dirfd; - char * path; - char ** values; - GCancellable * cancellable; - NMPlatformAsyncCallback callback; - gpointer callback_data; -} SysctlAsyncInfo; - -static void -sysctl_async_info_free(SysctlAsyncInfo *info) -{ - g_object_unref(info->platform); - g_free(info->pathid); - if (info->dirfd >= 0) - nm_close(info->dirfd); - g_free(info->path); - g_strfreev(info->values); - g_object_unref(info->cancellable); - g_slice_free(SysctlAsyncInfo, info); -} - -static void -sysctl_async_cb(GObject *object, GAsyncResult *res, gpointer user_data) -{ - NMPlatform * platform; - GTask * task = G_TASK(res); - SysctlAsyncInfo *info; - gs_free_error GError *error = NULL; - gs_free char * values_str = NULL; - - info = g_task_get_task_data(task); - - if (g_task_propagate_boolean(task, &error)) { - platform = info->platform; - _LOGD("sysctl: successfully set-async '%s' to values '%s'", - info->pathid ?: info->path, - (values_str = g_strjoinv(", ", info->values))); - } - - if (info->callback) - info->callback(error, info->callback_data); -} - -static void -sysctl_async_thread_fn(GTask * task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - SysctlAsyncInfo * info = task_data; - GError * error = NULL; - char ** value; - - if (g_task_return_error_if_cancelled(task)) - return; - - if (info->dirfd < 0 && !nm_platform_netns_push(info->platform, &netns)) { - g_set_error_literal(&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "sysctl: failed changing namespace"); - g_task_return_error(task, error); - return; - } - - for (value = info->values; *value; value++) { - if (!sysctl_set_internal(info->platform, info->pathid, info->dirfd, info->path, *value)) { - g_set_error(&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "sysctl: failed setting '%s' to value '%s': %s", - info->pathid ?: info->path, - *value, - nm_strerror_native(errno)); - g_task_return_error(task, error); - return; - } - if (g_task_return_error_if_cancelled(task)) - return; - } - g_task_return_boolean(task, TRUE); -} - -static void -sysctl_set_async_return_idle(gpointer user_data, GCancellable *cancellable) -{ - gs_unref_object NMPlatform *platform = NULL; - gs_free_error GError *cancelled_error = NULL; - gs_free_error GError * error = NULL; - NMPlatformAsyncCallback callback; - gpointer callback_data; - - nm_utils_user_data_unpack(user_data, &platform, &callback, &callback_data, &error); - g_cancellable_set_error_if_cancelled(cancellable, &cancelled_error); - callback(cancelled_error ?: error, callback_data); -} - -static void -sysctl_set_async(NMPlatform * platform, - const char * pathid, - int dirfd, - const char * path, - const char *const * values, - NMPlatformAsyncCallback callback, - gpointer data, - GCancellable * cancellable) -{ - SysctlAsyncInfo *info; - GTask * task; - int dirfd_dup, errsv; - gpointer packed; - GError * error = NULL; - - g_return_if_fail(platform); - g_return_if_fail(path); - g_return_if_fail(values && values[0]); - g_return_if_fail(cancellable); - g_return_if_fail(!data || callback); - - ASSERT_SYSCTL_ARGS(pathid, dirfd, path); - - if (dirfd >= 0) { - dirfd_dup = fcntl(dirfd, F_DUPFD_CLOEXEC, 0); - if (dirfd_dup < 0) { - if (!callback) - return; - errsv = errno; - g_set_error(&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "sysctl: failure duplicating directory fd: %s", - nm_strerror_native(errsv)); - packed = nm_utils_user_data_pack(g_object_ref(platform), callback, data, error); - nm_utils_invoke_on_idle(cancellable, sysctl_set_async_return_idle, packed); - return; - } - } else - dirfd_dup = -1; - - info = g_slice_new0(SysctlAsyncInfo); - info->platform = g_object_ref(platform); - info->pathid = g_strdup(pathid); - info->dirfd = dirfd_dup; - info->path = g_strdup(path); - info->values = g_strdupv((char **) values); - info->callback = callback; - info->callback_data = data; - info->cancellable = g_object_ref(cancellable); - - task = g_task_new(platform, cancellable, sysctl_async_cb, NULL); - g_task_set_task_data(task, info, (GDestroyNotify) sysctl_async_info_free); - g_task_set_return_on_cancel(task, FALSE); - g_task_run_in_thread(task, sysctl_async_thread_fn); - g_object_unref(task); -} - -static GSList *sysctl_clear_cache_list; - -void -_nm_logging_clear_platform_logging_cache(void) -{ - while (sysctl_clear_cache_list) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(sysctl_clear_cache_list->data); - - sysctl_clear_cache_list = - g_slist_delete_link(sysctl_clear_cache_list, sysctl_clear_cache_list); - - g_hash_table_destroy(priv->sysctl_get_prev_values); - priv->sysctl_get_prev_values = NULL; - } -} - -typedef struct { - const char *path; - CList lst; - char * value; - char path_data[]; -} SysctlCacheEntry; - -static void -sysctl_cache_entry_free(SysctlCacheEntry *entry) -{ - c_list_unlink_stale(&entry->lst); - g_free(entry->value); - g_free(entry); -} - -static void -_log_dbg_sysctl_get_impl(NMPlatform *platform, const char *pathid, const char *contents) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - SysctlCacheEntry * entry = NULL; - - if (!priv->sysctl_get_prev_values) { - sysctl_clear_cache_list = g_slist_prepend(sysctl_clear_cache_list, platform); - c_list_init(&priv->sysctl_list); - priv->sysctl_get_prev_values = - g_hash_table_new_full(nm_pstr_hash, - nm_pstr_equal, - (GDestroyNotify) sysctl_cache_entry_free, - NULL); - } else - entry = g_hash_table_lookup(priv->sysctl_get_prev_values, &pathid); - - if (entry) { - if (!nm_streq(entry->value, contents)) { - gs_free char *contents_escaped = g_strescape(contents, NULL); - gs_free char *prev_value_escaped = g_strescape(entry->value, NULL); - - _LOGD("sysctl: reading '%s': '%s' (changed from '%s' on last read)", - pathid, - contents_escaped, - prev_value_escaped); - g_free(entry->value); - entry->value = g_strdup(contents); - } - nm_c_list_move_front(&priv->sysctl_list, &entry->lst); - } else { - gs_free char * contents_escaped = g_strescape(contents, NULL); - SysctlCacheEntry *old; - size_t len; - - len = strlen(pathid); - entry = g_malloc(sizeof(SysctlCacheEntry) + len + 1); - entry->value = g_strdup(contents); - entry->path = entry->path_data; - memcpy(entry->path_data, pathid, len + 1); - - /* Remove oldest entry when the cache becomes too big */ - if (g_hash_table_size(priv->sysctl_get_prev_values) > 1000) { - old = c_list_last_entry(&priv->sysctl_list, SysctlCacheEntry, lst); - g_hash_table_remove(priv->sysctl_get_prev_values, old); - } - - _LOGD("sysctl: reading '%s': '%s'", pathid, contents_escaped); - - g_hash_table_add(priv->sysctl_get_prev_values, entry); - c_list_link_front(&priv->sysctl_list, &entry->lst); - } -} - -#define _log_dbg_sysctl_get(platform, pathid, contents) \ - G_STMT_START \ - { \ - if (_LOGD_ENABLED()) \ - _log_dbg_sysctl_get_impl(platform, pathid, contents); \ - } \ - G_STMT_END - -static char * -sysctl_get(NMPlatform *platform, const char *pathid, int dirfd, const char *path) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - GError * error = NULL; - gs_free char * contents = NULL; - - ASSERT_SYSCTL_ARGS(pathid, dirfd, path); - - if (dirfd < 0) { - if (!nm_platform_netns_push(platform, &netns)) { - errno = EBUSY; - return NULL; - } - pathid = path; - } - - if (!nm_utils_file_get_contents(dirfd, - path, - 1 * 1024 * 1024, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - NULL, - NULL, - &error)) { - NMLogLevel log_level = LOGL_ERR; - int errsv = EBUSY; - - if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - errsv = ENOENT; - log_level = LOGL_DEBUG; - } else if (g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NODEV) - || g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_FAILED)) { - /* We assume FAILED means EOPNOTSUP and don't log a error message. */ - log_level = LOGL_DEBUG; - } - - _NMLOG(log_level, "error reading %s: %s", pathid, error->message); - g_clear_error(&error); - errno = errsv; - return NULL; - } - - g_strstrip(contents); - - _log_dbg_sysctl_get(platform, pathid, contents); - - /* errno is left undefined (as we don't return NULL). */ - return g_steal_pointer(&contents); -} - -/*****************************************************************************/ - -static void -process_events(NMPlatform *platform) -{ - delayed_action_handle_all(platform, TRUE); -} - -/*****************************************************************************/ - -static const RefreshAllInfo * -refresh_all_type_get_info(RefreshAllType refresh_all_type) -{ - static const RefreshAllInfo infos[] = { -#define R(_refresh_all_type, _obj_type, _addr_family) \ - [_refresh_all_type] = { \ - .obj_type = _obj_type, \ - .addr_family = _addr_family, \ - } - R(REFRESH_ALL_TYPE_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), - R(REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), - R(REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), - R(REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), - R(REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), -#undef R - }; - - nm_assert(_NM_INT_NOT_NEGATIVE(refresh_all_type)); - nm_assert(refresh_all_type < G_N_ELEMENTS(infos)); - nm_assert(nmp_class_from_type(infos[refresh_all_type].obj_type)); - - return &infos[refresh_all_type]; -} - -static NM_UTILS_LOOKUP_DEFINE( - delayed_action_type_to_refresh_all_type, - DelayedActionType, - RefreshAllType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(0), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, - REFRESH_ALL_TYPE_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, - REFRESH_ALL_TYPE_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, - REFRESH_ALL_TYPE_ROUTING_RULES_IP4), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, - REFRESH_ALL_TYPE_ROUTING_RULES_IP6), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), - NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), ); - -static DelayedActionType -delayed_action_type_from_refresh_all_type(RefreshAllType refresh_all_type) -{ - DelayedActionType t; - - nm_assert(refresh_all_type_get_info(refresh_all_type)); - - t = (((DelayedActionType) 1) << refresh_all_type); - - nm_assert(refresh_all_type == delayed_action_type_to_refresh_all_type(t)); - - return t; -} - -static RefreshAllType -refresh_all_type_from_needle_object(const NMPObject *obj_needle) -{ - switch (NMP_OBJECT_GET_TYPE(obj_needle)) { - case NMP_OBJECT_TYPE_LINK: - return REFRESH_ALL_TYPE_LINKS; - case NMP_OBJECT_TYPE_IP4_ADDRESS: - return REFRESH_ALL_TYPE_IP4_ADDRESSES; - case NMP_OBJECT_TYPE_IP6_ADDRESS: - return REFRESH_ALL_TYPE_IP6_ADDRESSES; - case NMP_OBJECT_TYPE_IP4_ROUTE: - return REFRESH_ALL_TYPE_IP4_ROUTES; - case NMP_OBJECT_TYPE_IP6_ROUTE: - return REFRESH_ALL_TYPE_IP6_ROUTES; - case NMP_OBJECT_TYPE_QDISC: - return REFRESH_ALL_TYPE_QDISCS; - case NMP_OBJECT_TYPE_TFILTER: - return REFRESH_ALL_TYPE_TFILTERS; - case NMP_OBJECT_TYPE_ROUTING_RULE: - switch (NMP_OBJECT_CAST_ROUTING_RULE(obj_needle)->addr_family) { - case AF_INET: - return REFRESH_ALL_TYPE_ROUTING_RULES_IP4; - case AF_INET6: - return REFRESH_ALL_TYPE_ROUTING_RULES_IP6; - } - nm_assert_not_reached(); - return 0; - default: - nm_assert_not_reached(); - return 0; - } -} - -static const NMPLookup * -refresh_all_type_init_lookup(RefreshAllType refresh_all_type, NMPLookup *lookup) -{ - const RefreshAllInfo *refresh_all_info; - - nm_assert(lookup); - - refresh_all_info = refresh_all_type_get_info(refresh_all_type); - - nm_assert(refresh_all_info); - - if (NM_IN_SET(refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) { - return nmp_lookup_init_object_by_addr_family(lookup, - refresh_all_info->obj_type, - refresh_all_info->addr_family); - } - - /* not yet implemented. */ - nm_assert(refresh_all_info->addr_family == AF_UNSPEC); - - return nmp_lookup_init_obj_type(lookup, refresh_all_info->obj_type); -} - -static DelayedActionType -delayed_action_refresh_from_needle_object(const NMPObject *obj_needle) -{ - return delayed_action_type_from_refresh_all_type( - refresh_all_type_from_needle_object(obj_needle)); -} - -static NM_UTILS_LOOKUP_STR_DEFINE( - delayed_action_to_string, - DelayedActionType, - NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, - "refresh-all-ip4-addresses"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, - "refresh-all-ip6-addresses"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, - "refresh-all-routing-rules-ip4"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, - "refresh-all-routing-rules-ip6"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), - NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_NONE), - NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL), - NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL), - NM_UTILS_LOOKUP_ITEM_IGNORE(__DELAYED_ACTION_TYPE_MAX), ); - -static const char * -delayed_action_to_string_full(DelayedActionType action_type, - gpointer user_data, - char * buf, - gsize buf_size) -{ - char * buf0 = buf; - const DelayedActionWaitForNlResponseData *data; - - nm_utils_strbuf_append_str(&buf, &buf_size, delayed_action_to_string(action_type)); - switch (action_type) { - case DELAYED_ACTION_TYPE_MASTER_CONNECTED: - nm_utils_strbuf_append(&buf, &buf_size, " (master-ifindex %d)", GPOINTER_TO_INT(user_data)); - break; - case DELAYED_ACTION_TYPE_REFRESH_LINK: - nm_utils_strbuf_append(&buf, &buf_size, " (ifindex %d)", GPOINTER_TO_INT(user_data)); - break; - case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: - data = user_data; - - if (data) { - gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_nsec(); - char b[255]; - - nm_utils_strbuf_append( - &buf, - &buf_size, - " (seq %u, timeout in %s%" G_GINT64_FORMAT ".%09" G_GINT64_FORMAT - ", response-type %d%s%s)", - data->seq_number, - timeout < 0 ? "-" : "", - (timeout < 0 ? -timeout : timeout) / NM_UTILS_NSEC_PER_SEC, - (timeout < 0 ? -timeout : timeout) % NM_UTILS_NSEC_PER_SEC, - (int) data->response_type, - data->seq_result ? ", " : "", - data->seq_result - ? wait_for_nl_response_to_string(data->seq_result, NULL, b, sizeof(b)) - : ""); - } else - nm_utils_strbuf_append_str(&buf, &buf_size, " (any)"); - break; - default: - nm_assert(!user_data); - break; - } - return buf0; -} - -#define _LOGt_delayed_action(action_type, user_data, operation) \ - G_STMT_START \ - { \ - char _buf[255]; \ - \ - _LOGt("delayed-action: %s %s", \ - "" operation, \ - delayed_action_to_string_full(action_type, user_data, _buf, sizeof(_buf))); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static gboolean -delayed_action_refresh_all_in_progress(NMPlatform *platform, DelayedActionType action_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - RefreshAllType refresh_all_type; - - nm_assert(nm_utils_is_power_of_two(action_type)); - nm_assert(NM_FLAGS_ANY(action_type, DELAYED_ACTION_TYPE_REFRESH_ALL)); - nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); - - if (NM_FLAGS_ANY(priv->delayed_action.flags, action_type)) - return TRUE; - - refresh_all_type = delayed_action_type_to_refresh_all_type(action_type); - return (priv->delayed_action.refresh_all_in_progress[refresh_all_type] > 0); -} - -static void -delayed_action_wait_for_nl_response_complete(NMPlatform * platform, - guint idx, - WaitForNlResponseResult seq_result) -{ - NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - DelayedActionWaitForNlResponseData *data; - - nm_assert(NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert(idx < priv->delayed_action.list_wait_for_nl_response->len); - nm_assert(seq_result); - - data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - idx); - - _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete"); - - if (priv->delayed_action.list_wait_for_nl_response->len <= 1) - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; - if (data->out_seq_result) - *data->out_seq_result = seq_result; - switch (data->response_type) { - case DELAYED_ACTION_RESPONSE_TYPE_VOID: - break; - case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: - if (data->response.out_refresh_all_in_progress) { - nm_assert(*data->response.out_refresh_all_in_progress > 0); - *data->response.out_refresh_all_in_progress -= 1; - data->response.out_refresh_all_in_progress = NULL; - } - break; - case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET: - if (data->response.out_route_get) { - nm_assert(!*data->response.out_route_get); - data->response.out_route_get = NULL; - } - break; - } - - g_array_remove_index_fast(priv->delayed_action.list_wait_for_nl_response, idx); -} - -static void -delayed_action_wait_for_nl_response_complete_check(NMPlatform * platform, - WaitForNlResponseResult force_result, - guint32 * out_next_seq_number, - gint64 * out_next_timeout_abs_ns, - gint64 * p_now_ns) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - guint i; - guint32 next_seq_number = 0; - gint64 next_timeout_abs_ns = 0; - int now_ns = 0; - - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len;) { - const DelayedActionWaitForNlResponseData *data = - &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); - - if (data->seq_result) - delayed_action_wait_for_nl_response_complete(platform, i, data->seq_result); - else if (p_now_ns - && ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_nsec())) - >= data->timeout_abs_ns)) { - /* the caller can optionally check for timeout by providing a p_now_ns argument. */ - delayed_action_wait_for_nl_response_complete( - platform, - i, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); - } else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) - delayed_action_wait_for_nl_response_complete(platform, i, force_result); - else { - if (next_seq_number == 0 || next_timeout_abs_ns > data->timeout_abs_ns) { - next_seq_number = data->seq_number; - next_timeout_abs_ns = data->timeout_abs_ns; - } - i++; - } - } - - if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) { - nm_assert( - !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert(priv->delayed_action.list_wait_for_nl_response->len == 0); - } - - NM_SET_OUT(out_next_seq_number, next_seq_number); - NM_SET_OUT(out_next_timeout_abs_ns, next_timeout_abs_ns); - NM_SET_OUT(p_now_ns, now_ns); -} - -static void -delayed_action_wait_for_nl_response_complete_all(NMPlatform * platform, - WaitForNlResponseResult fallback_result) -{ - delayed_action_wait_for_nl_response_complete_check(platform, fallback_result, NULL, NULL, NULL); -} - -/*****************************************************************************/ - -static void -delayed_action_handle_MASTER_CONNECTED(NMPlatform *platform, int master_ifindex) -{ - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - NMPCacheOpsType cache_op; - - cache_op = nmp_cache_update_link_master_connected(nm_platform_get_cache(platform), - master_ifindex, - &obj_old, - &obj_new); - if (cache_op == NMP_CACHE_OPS_UNCHANGED) - return; - cache_on_change(platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); -} - -static void -delayed_action_handle_REFRESH_LINK(NMPlatform *platform, int ifindex) -{ - do_request_link_no_delayed_actions(platform, ifindex, NULL); -} - -static void -delayed_action_handle_REFRESH_ALL(NMPlatform *platform, DelayedActionType flags) -{ - do_request_all_no_delayed_actions(platform, flags); -} - -static void -delayed_action_handle_READ_NETLINK(NMPlatform *platform) -{ - event_handler_read_netlink(platform, FALSE); -} - -static void -delayed_action_handle_WAIT_FOR_NL_RESPONSE(NMPlatform *platform) -{ - event_handler_read_netlink(platform, TRUE); -} - -static gboolean -delayed_action_handle_one(NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - gpointer user_data; - - if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) - return FALSE; - - /* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions. - * This type of action is entirely cache-internal and is here to resolve a - * cache inconsistency. It should be fixed right away. */ - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) { - nm_assert(priv->delayed_action.list_master_connected->len > 0); - - user_data = priv->delayed_action.list_master_connected->pdata[0]; - g_ptr_array_remove_index_fast(priv->delayed_action.list_master_connected, 0); - if (priv->delayed_action.list_master_connected->len == 0) - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_MASTER_CONNECTED; - nm_assert(_nm_utils_ptrarray_find_first( - (gconstpointer *) priv->delayed_action.list_master_connected->pdata, - priv->delayed_action.list_master_connected->len, - user_data) - < 0); - - _LOGt_delayed_action(DELAYED_ACTION_TYPE_MASTER_CONNECTED, user_data, "handle"); - delayed_action_handle_MASTER_CONNECTED(platform, GPOINTER_TO_INT(user_data)); - return TRUE; - } - nm_assert(priv->delayed_action.list_master_connected->len == 0); - - /* Next we prefer read-netlink, because the buffer size is limited and we want to process events - * from netlink early. */ - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_READ_NETLINK)) { - _LOGt_delayed_action(DELAYED_ACTION_TYPE_READ_NETLINK, NULL, "handle"); - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_READ_NETLINK; - delayed_action_handle_READ_NETLINK(platform); - return TRUE; - } - - if (NM_FLAGS_ANY(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) { - DelayedActionType flags, iflags; - - flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL; - - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL; - - if (_LOGt_ENABLED()) { - FOR_EACH_DELAYED_ACTION(iflags, flags) - _LOGt_delayed_action(iflags, NULL, "handle"); - } - - delayed_action_handle_REFRESH_ALL(platform, flags); - return TRUE; - } - - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { - nm_assert(priv->delayed_action.list_refresh_link->len > 0); - - user_data = priv->delayed_action.list_refresh_link->pdata[0]; - g_ptr_array_remove_index_fast(priv->delayed_action.list_refresh_link, 0); - if (priv->delayed_action.list_refresh_link->len == 0) - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; - nm_assert(_nm_utils_ptrarray_find_first( - (gconstpointer *) priv->delayed_action.list_refresh_link->pdata, - priv->delayed_action.list_refresh_link->len, - user_data) - < 0); - - _LOGt_delayed_action(DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle"); - - delayed_action_handle_REFRESH_LINK(platform, GPOINTER_TO_INT(user_data)); - - return TRUE; - } - - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); - _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, NULL, "handle"); - delayed_action_handle_WAIT_FOR_NL_RESPONSE(platform); - return TRUE; - } - - return FALSE; -} - -static gboolean -delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - gboolean any = FALSE; - - g_return_val_if_fail(priv->delayed_action.is_handling == 0, FALSE); - - priv->delayed_action.is_handling++; - if (read_netlink) - delayed_action_schedule(platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL); - while (delayed_action_handle_one(platform)) - any = TRUE; - priv->delayed_action.is_handling--; - - cache_prune_all(platform); - - return any; -} - -static void -delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpointer user_data) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - DelayedActionType iflags; - - nm_assert(action_type != DELAYED_ACTION_TYPE_NONE); - - switch (action_type) { - case DELAYED_ACTION_TYPE_REFRESH_LINK: - if (_nm_utils_ptrarray_find_first( - (gconstpointer *) priv->delayed_action.list_refresh_link->pdata, - priv->delayed_action.list_refresh_link->len, - user_data) - < 0) - g_ptr_array_add(priv->delayed_action.list_refresh_link, user_data); - break; - case DELAYED_ACTION_TYPE_MASTER_CONNECTED: - if (_nm_utils_ptrarray_find_first( - (gconstpointer *) priv->delayed_action.list_master_connected->pdata, - priv->delayed_action.list_master_connected->len, - user_data) - < 0) - g_ptr_array_add(priv->delayed_action.list_master_connected, user_data); - break; - case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: - g_array_append_vals(priv->delayed_action.list_wait_for_nl_response, user_data, 1); - break; - default: - nm_assert(!user_data); - nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)); - nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)); - nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - break; - } - - priv->delayed_action.flags |= action_type; - - if (_LOGt_ENABLED()) { - FOR_EACH_DELAYED_ACTION(iflags, action_type) - _LOGt_delayed_action(iflags, user_data, "schedule"); - } -} - -static void -delayed_action_schedule_WAIT_FOR_NL_RESPONSE(NMPlatform * platform, - guint32 seq_number, - WaitForNlResponseResult * out_seq_result, - char ** out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) -{ - DelayedActionWaitForNlResponseData data = { - .seq_number = seq_number, - .timeout_abs_ns = - nm_utils_get_monotonic_timestamp_nsec() + (200 * (NM_UTILS_NSEC_PER_SEC / 1000)), - .out_seq_result = out_seq_result, - .out_errmsg = out_errmsg, - .response_type = response_type, - .response.out_data = response_out_data, - }; - - delayed_action_schedule(platform, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, &data); -} - -/*****************************************************************************/ - -static void -cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) -{ - NMDedupMultiIter iter; - const NMPObject *obj; - NMPCacheOpsType cache_op; - NMPCache * cache = nm_platform_get_cache(platform); - - nm_dedup_multi_iter_init(&iter, nmp_cache_lookup(cache, lookup)); - while (nm_dedup_multi_iter_next(&iter)) { - const NMDedupMultiEntry *main_entry; - - /* we only track the dirty flag for the OBJECT-TYPE index. That means, - * for other lookup types we need to check the dirty flag of the main-entry. */ - main_entry = nmp_cache_reresolve_main_entry(cache, iter.current, lookup); - if (!main_entry->dirty) - continue; - - obj = main_entry->obj; - - _LOGt("cache-prune: prune %s", - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - - { - nm_auto_nmpobj const NMPObject *obj_old = NULL; - - cache_op = nmp_cache_remove(cache, obj, TRUE, TRUE, &obj_old); - nm_assert(cache_op == NMP_CACHE_OPS_REMOVED); - cache_on_change(platform, cache_op, obj_old, NULL); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, NULL); - } - } -} - -static void -cache_prune_all(NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - RefreshAllType refresh_all_type; - - for (refresh_all_type = _REFRESH_ALL_TYPE_FIRST; refresh_all_type < _REFRESH_ALL_TYPE_NUM; - refresh_all_type++) { - NMPLookup lookup; - - if (priv->pruning[refresh_all_type] == 0) - continue; - priv->pruning[refresh_all_type] -= 1; - if (priv->pruning[refresh_all_type] > 0) - continue; - refresh_all_type_init_lookup(refresh_all_type, &lookup); - cache_prune_one_type(platform, &lookup); - } -} - -static void -cache_on_change(NMPlatform * platform, - NMPCacheOpsType cache_op, - const NMPObject *obj_old, - const NMPObject *obj_new) -{ - const NMPClass *klass; - char str_buf[sizeof(_nm_utils_to_string_buffer)]; - char str_buf2[sizeof(_nm_utils_to_string_buffer)]; - NMPCache * cache = nm_platform_get_cache(platform); - - ASSERT_nmp_cache_ops(cache, cache_op, obj_old, obj_new); - nm_assert(cache_op != NMP_CACHE_OPS_UNCHANGED); - - klass = obj_old ? NMP_OBJECT_GET_CLASS(obj_old) : NMP_OBJECT_GET_CLASS(obj_new); - - _LOGt( - "update-cache-%s: %s: %s%s%s", - klass->obj_type_name, - (cache_op == NMP_CACHE_OPS_UPDATED ? "UPDATE" - : (cache_op == NMP_CACHE_OPS_REMOVED ? "REMOVE" - : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD" - : "???")), - (cache_op != NMP_CACHE_OPS_ADDED - ? nmp_object_to_string(obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof(str_buf2)) - : nmp_object_to_string(obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof(str_buf2))), - (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "", - (cache_op == NMP_CACHE_OPS_UPDATED - ? nmp_object_to_string(obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof(str_buf)) - : "")); - - switch (klass->obj_type) { - case NMP_OBJECT_TYPE_LINK: - { - /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ - if (obj_old - && nmp_cache_link_connected_needs_toggle_by_ifindex(cache, - obj_old->link.master, - obj_new, - obj_old)) - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_MASTER_CONNECTED, - GINT_TO_POINTER(obj_old->link.master)); - if (obj_new && (!obj_old || obj_old->link.master != obj_new->link.master) - && nmp_cache_link_connected_needs_toggle_by_ifindex(cache, - obj_new->link.master, - obj_new, - obj_old)) - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_MASTER_CONNECTED, - GINT_TO_POINTER(obj_new->link.master)); - } - { - /* check whether we are about to change a master link that needs toggling connected state. */ - if (obj_new /* <-- nonsensical, make coverity happy */ - && nmp_cache_link_connected_needs_toggle(cache, obj_new, obj_new, obj_old)) - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_MASTER_CONNECTED, - GINT_TO_POINTER(obj_new->link.ifindex)); - } - { - int ifindex = 0; - - /* if we remove a link (from netlink), we must refresh the addresses, routes, qdiscs and tfilters */ - if (cache_op == NMP_CACHE_OPS_REMOVED - && obj_old /* <-- nonsensical, make coverity happy */) - ifindex = obj_old->link.ifindex; - else if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old - && obj_new /* <-- nonsensical, make coverity happy */ - && !obj_new->_link.netlink.is_in_netlink - && obj_new->_link.netlink.is_in_netlink - != obj_old->_link.netlink.is_in_netlink) - ifindex = obj_new->link.ifindex; - - if (ifindex > 0) { - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL - | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS - | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); - } - } - { - int ifindex = -1; - - /* removal of a link could be caused by moving the link to another netns. - * In this case, we potentially have to update other links that have this link as parent. - * Currently, kernel misses to sent us a notification in this case - * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ - - if (cache_op == NMP_CACHE_OPS_REMOVED - && obj_old /* <-- nonsensical, make coverity happy */ - && obj_old->_link.netlink.is_in_netlink) - ifindex = obj_old->link.ifindex; - else if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old - && obj_new /* <-- nonsensical, make coverity happy */ - && obj_old->_link.netlink.is_in_netlink - && !obj_new->_link.netlink.is_in_netlink) - ifindex = obj_new->link.ifindex; - - if (ifindex > 0) { - NMPLookup lookup; - NMDedupMultiIter iter; - const NMPlatformLink *l; - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK); - nmp_cache_iter_for_each_link (&iter, nmp_cache_lookup(cache, &lookup), &l) { - if (l->parent == ifindex) - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER(l->ifindex)); - } - } - } - { - /* if a link goes down, we must refresh routes */ - if (cache_op == NMP_CACHE_OPS_UPDATED && obj_old - && obj_new /* <-- nonsensical, make coverity happy */ - && obj_old->_link.netlink.is_in_netlink && obj_new->_link.netlink.is_in_netlink - && ((NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_UP) - && !NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_UP)) - || (NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_LOWER_UP) - && !NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) { - /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I - * think kernel does send RTM_DELROUTE events for IPv6 routes, so - * we might not need to refresh IPv6 routes. */ - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, - NULL); - } - } - if (NM_IN_SET(cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) - && (obj_new && obj_new->_link.netlink.is_in_netlink) - && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) { - gboolean re_request_link = FALSE; - const NMPlatformLnkTun *lnk_tun; - - if (!obj_new->_link.netlink.lnk - && NM_IN_SET(obj_new->link.type, - NM_LINK_TYPE_GRE, - NM_LINK_TYPE_GRETAP, - NM_LINK_TYPE_IP6TNL, - NM_LINK_TYPE_IP6GRE, - NM_LINK_TYPE_IP6GRETAP, - NM_LINK_TYPE_INFINIBAND, - NM_LINK_TYPE_MACVLAN, - NM_LINK_TYPE_MACVLAN, - NM_LINK_TYPE_SIT, - NM_LINK_TYPE_TUN, - NM_LINK_TYPE_VLAN, - NM_LINK_TYPE_VXLAN)) { - /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that - * kernel didn't send this notification, thus when we first learn about a link - * that lacks an lnk_data we re-request it again. - * - * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ - re_request_link = TRUE; - } else if (obj_new->link.type == NM_LINK_TYPE_TUN && obj_new->_link.netlink.lnk - && (lnk_tun = &(obj_new->_link.netlink.lnk)->lnk_tun) && !lnk_tun->persist - && lnk_tun->pi && !lnk_tun->vnet_hdr && !lnk_tun->multi_queue - && !lnk_tun->owner_valid && !lnk_tun->group_valid) { - /* kernel has/had a know issue that the first notification for TUN device would - * be sent with invalid parameters. The message looks like that kind, so refetch - * it. */ - re_request_link = TRUE; - } else if (obj_new->link.type == NM_LINK_TYPE_VETH && obj_new->link.parent == 0) { - /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK - * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). - * Request it again. */ - re_request_link = TRUE; - } else if (obj_new->link.type == NM_LINK_TYPE_ETHERNET - && obj_new->link.l_address.len == 0) { - /* Due to a kernel bug, we sometimes receive spurious NEWLINK - * messages after a wifi interface has disappeared. Since the - * link is not present anymore we can't determine its type and - * thus it will show up as a Ethernet one, with no address - * specified. Request the link again to check if it really - * exists. https://bugzilla.redhat.com/show_bug.cgi?id=1302037 - */ - re_request_link = TRUE; - } - if (re_request_link) { - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER(obj_new->link.ifindex)); - } - } - { - /* on enslave/release, we also refresh the master. */ - int ifindex1 = 0, ifindex2 = 0; - gboolean changed_master, changed_connected; - - changed_master = - (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0 - ? obj_new->link.master - : 0) - != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0 - ? obj_old->link.master - : 0); - changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink - ? NM_FLAGS_HAS(obj_new->link.n_ifi_flags, IFF_LOWER_UP) - : 2) - != (obj_old && obj_old->_link.netlink.is_in_netlink - ? NM_FLAGS_HAS(obj_old->link.n_ifi_flags, IFF_LOWER_UP) - : 2); - - if (changed_master || changed_connected) { - ifindex1 = - (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0) - ? obj_old->link.master - : 0; - ifindex2 = - (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0) - ? obj_new->link.master - : 0; - - if (ifindex1 > 0) - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER(ifindex1)); - if (ifindex2 > 0 && ifindex1 != ifindex2) - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER(ifindex2)); - } - } - break; - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - { - /* Address deletion is sometimes accompanied by route deletion. We need to - * check all routes belonging to the same interface. */ - if (cache_op == NMP_CACHE_OPS_REMOVED) { - delayed_action_schedule(platform, - (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) - ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, - NULL); - } - } break; - default: - break; - } -} - -/*****************************************************************************/ - -static guint32 -_nlh_seq_next_get(NMLinuxPlatformPrivate *priv) -{ - /* generate a new sequence number, but never return zero. - * Wrapping numbers are not a problem, because we don't rely - * on strictly increasing sequence numbers. */ - return (++priv->nlh_seq_next) ?: (++priv->nlh_seq_next); -} - -/** - * _nl_send_nlmsghdr: - * @platform: - * @nlhdr: - * @out_seq_result: - * @response_type: - * @response_out_data: - * - * Returns: 0 on success or a negative errno. - */ -static int -_nl_send_nlmsghdr(NMPlatform * platform, - struct nlmsghdr * nlhdr, - WaitForNlResponseResult * out_seq_result, - char ** out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - guint32 seq; - int errsv; - - nm_assert(nlhdr); - - seq = _nlh_seq_next_get(priv); - nlhdr->nlmsg_seq = seq; - - { - struct sockaddr_nl nladdr = { - .nl_family = AF_NETLINK, - }; - struct iovec iov = {.iov_base = nlhdr, .iov_len = nlhdr->nlmsg_len}; - struct msghdr msg = { - .msg_name = &nladdr, - .msg_namelen = sizeof(nladdr), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - int try_count; - - if (!nlhdr->nlmsg_pid) - nlhdr->nlmsg_pid = nl_socket_get_local_port(priv->nlh); - nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); - - try_count = 0; -again: - errsv = sendmsg(nl_socket_get_fd(priv->nlh), &msg, 0); - if (errsv < 0) { - errsv = errno; - if (errsv == EINTR && try_count++ < 100) - goto again; - _LOGD("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", - nm_strerror_native(errsv), - errsv); - return -nm_errno_from_native(errsv); - } - } - - delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform, - seq, - out_seq_result, - out_errmsg, - response_type, - response_out_data); - return 0; -} - -/** - * _nl_send_nlmsg: - * @platform: - * @nlmsg: - * @out_seq_result: - * @response_type: - * @response_out_data: - * - * Returns: 0 on success, or a negative libnl3 error code (beware, it's not an errno). - */ -static int -_nl_send_nlmsg(NMPlatform * platform, - struct nl_msg * nlmsg, - WaitForNlResponseResult * out_seq_result, - char ** out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - struct nlmsghdr * nlhdr; - guint32 seq; - int nle; - - nlhdr = nlmsg_hdr(nlmsg); - seq = _nlh_seq_next_get(priv); - nlhdr->nlmsg_seq = seq; - - nle = nl_send_auto(priv->nlh, nlmsg); - if (nle < 0) { - _LOGD("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nm_strerror(nle), nle); - return nle; - } - - delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform, - seq, - out_seq_result, - out_errmsg, - response_type, - response_out_data); - return 0; -} - -static void -do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name) -{ - NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - int nle; - - if (name && !name[0]) - name = NULL; - - g_return_if_fail(ifindex > 0 || name); - - _LOGD("do-request-link: %d %s", ifindex, name ?: ""); - - if (ifindex > 0) { - const NMDedupMultiEntry *entry; - - entry = nmp_cache_lookup_entry_link(nm_platform_get_cache(platform), ifindex); - if (entry) { - priv->pruning[REFRESH_ALL_TYPE_LINKS] += 1; - nm_dedup_multi_entry_set_dirty(entry, TRUE); - } - } - - event_handler_read_netlink(platform, FALSE); - - nlmsg = _nl_msg_new_link(RTM_GETLINK, 0, ifindex, name); - if (nlmsg) { - nle = _nl_send_nlmsg(platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); - if (nle < 0) { - _LOGE("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", - ifindex, - name ?: "", - nm_strerror(nle), - -nle); - return; - } - } -} - -static void -do_request_link(NMPlatform *platform, int ifindex, const char *name) -{ - do_request_link_no_delayed_actions(platform, ifindex, name); - delayed_action_handle_all(platform, FALSE); -} - -static struct nl_msg * -_nl_msg_new_dump(NMPObjectType obj_type, int preferred_addr_family) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - const NMPClass * klass; - - klass = nmp_class_from_type(obj_type); - - nm_assert(klass); - nm_assert(klass->rtm_gettype > 0); - - nlmsg = nlmsg_alloc_simple(klass->rtm_gettype, NLM_F_DUMP); - - if (klass->addr_family != AF_UNSPEC) { - /* if the class specifies a particular address family, then it is preferred. */ - nm_assert(NM_IN_SET(preferred_addr_family, AF_UNSPEC, klass->addr_family)); - preferred_addr_family = klass->addr_family; - } - - switch (klass->obj_type) { - case NMP_OBJECT_TYPE_QDISC: - case NMP_OBJECT_TYPE_TFILTER: - { - const struct tcmsg tcmsg = { - .tcm_family = preferred_addr_family, - }; - - if (nlmsg_append_struct(nlmsg, &tcmsg) < 0) - g_return_val_if_reached(NULL); - } break; - case NMP_OBJECT_TYPE_LINK: - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - case NMP_OBJECT_TYPE_ROUTING_RULE: - { - const struct rtgenmsg gmsg = { - .rtgen_family = preferred_addr_family, - }; - - if (nlmsg_append_struct(nlmsg, &gmsg) < 0) - g_return_val_if_reached(NULL); - } break; - default: - g_return_val_if_reached(NULL); - } - - return g_steal_pointer(&nlmsg); -} - -static void -do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - DelayedActionType action_type_prune; - DelayedActionType iflags; - - nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); - action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; - - action_type_prune = action_type; - - /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra - * cache lookup for every entry. - * - * Avoid that, by special casing routing-rules here. */ - if (NM_FLAGS_ALL(action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) { - NMPLookup lookup; - - priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1; - priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1; - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup); - action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL; - } - - FOR_EACH_DELAYED_ACTION(iflags, action_type_prune) - { - RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type(iflags); - NMPLookup lookup; - - priv->pruning[refresh_all_type] += 1; - refresh_all_type_init_lookup(refresh_all_type, &lookup); - nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup); - } - - FOR_EACH_DELAYED_ACTION(iflags, action_type) - { - RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type(iflags); - const RefreshAllInfo *refresh_all_info = refresh_all_type_get_info(refresh_all_type); - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - int * out_refresh_all_in_progress; - - out_refresh_all_in_progress = - &priv->delayed_action.refresh_all_in_progress[refresh_all_type]; - nm_assert(*out_refresh_all_in_progress >= 0); - *out_refresh_all_in_progress += 1; - - /* clear any delayed action that request a refresh of this object type. */ - priv->delayed_action.flags &= ~iflags; - _LOGt_delayed_action(iflags, NULL, "handle (do-request-all)"); - - if (refresh_all_type == REFRESH_ALL_TYPE_LINKS) { - nm_assert( - (priv->delayed_action.list_refresh_link->len > 0) - == NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)); - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) { - _LOGt_delayed_action(DELAYED_ACTION_TYPE_REFRESH_LINK, - NULL, - "clear (do-request-all)"); - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK; - g_ptr_array_set_size(priv->delayed_action.list_refresh_link, 0); - } - } - - event_handler_read_netlink(platform, FALSE); - - nlmsg = _nl_msg_new_dump(refresh_all_info->obj_type, refresh_all_info->addr_family); - if (!nlmsg) - goto next_after_fail; - - if (_nl_send_nlmsg(platform, - nlmsg, - NULL, - NULL, - DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, - out_refresh_all_in_progress) - < 0) - goto next_after_fail; - - continue; - -next_after_fail: - nm_assert(*out_refresh_all_in_progress > 0); - *out_refresh_all_in_progress -= 1; - } -} - -static void -do_request_one_type_by_needle_object(NMPlatform *platform, const NMPObject *obj_needle) -{ - do_request_all_no_delayed_actions(platform, - delayed_action_refresh_from_needle_object(obj_needle)); - delayed_action_handle_all(platform, FALSE); -} - -static void -event_seq_check_refresh_all(NMPlatform *platform, guint32 seq_number) -{ - NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - DelayedActionWaitForNlResponseData *data; - guint i; - - if (NM_IN_SET(seq_number, 0, priv->nlh_seq_last_seen)) - return; - - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); - - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); - - if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS - && data->response.out_refresh_all_in_progress - && data->seq_number == priv->nlh_seq_last_seen) { - *data->response.out_refresh_all_in_progress -= 1; - data->response.out_refresh_all_in_progress = NULL; - break; - } - } - } - - priv->nlh_seq_last_seen = seq_number; -} - -static void -event_seq_check(NMPlatform * platform, - guint32 seq_number, - WaitForNlResponseResult seq_result, - const char * msg) -{ - NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - DelayedActionWaitForNlResponseData *data; - guint i; - - if (seq_number == 0) - return; - - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); - - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); - - if (data->seq_number == seq_number) { - /* We potentially receive many parts partial responses for the same sequence number. - * Thus, we only remember the result, and collect it later. */ - if (data->seq_result < 0) { - /* we already saw an error for this sequence number. - * Preserve it. */ - } else if (seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN - || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) - data->seq_result = seq_result; - if (data->out_errmsg && !*data->out_errmsg) - *data->out_errmsg = g_strdup(msg); - return; - } - } - } - -#if NM_MORE_LOGGING - if (seq_number != priv->nlh_seq_last_handled) - _LOGt("netlink: recvmsg: unwaited sequence number %u", seq_number); - priv->nlh_seq_last_handled = seq_number; -#endif -} - -static void -event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) -{ - NMLinuxPlatformPrivate *priv; - nm_auto_nmpobj NMPObject *obj = NULL; - NMPCacheOpsType cache_op; - struct nlmsghdr * msghdr; - char buf_nlmsghdr[400]; - gboolean is_del = FALSE; - gboolean is_dump = FALSE; - NMPCache * cache = nm_platform_get_cache(platform); - - msghdr = nlmsg_hdr(msg); - - if (!_nm_platform_kernel_support_detected(NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) - && msghdr->nlmsg_type == RTM_NEWADDR) { - /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, - * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ - if (nlmsg_valid_hdr(msghdr, sizeof(struct ifaddrmsg)) - && NM_IN_SET(((struct ifaddrmsg *) nlmsg_data(msghdr))->ifa_family, - AF_INET, - AF_INET6)) { - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, - * dated 30 March, 2014. - * - * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy - * way to detect kernel support. */ - _nm_platform_kernel_support_init( - NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS, - !!nlmsg_find_attr(msghdr, sizeof(struct ifaddrmsg), IFA_FLAGS) ? 1 : -1); - } - } - - if (!handle_events) - return; - - if (NM_IN_SET(msghdr->nlmsg_type, - RTM_DELLINK, - RTM_DELADDR, - RTM_DELROUTE, - RTM_DELRULE, - RTM_DELQDISC, - RTM_DELTFILTER)) { - /* The event notifies about a deleted object. We don't need to initialize all - * fields of the object. */ - is_del = TRUE; - } - - obj = nmp_object_new_from_nl(platform, cache, msg, is_del); - if (!obj) { - _LOGT("event-notification: %s: ignore", - nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); - return; - } - - if (!is_del - && NM_IN_SET(msghdr->nlmsg_type, - RTM_NEWADDR, - RTM_NEWLINK, - RTM_NEWROUTE, - RTM_NEWRULE, - RTM_NEWQDISC, - RTM_NEWTFILTER)) { - is_dump = - delayed_action_refresh_all_in_progress(platform, - delayed_action_refresh_from_needle_object(obj)); - } - - _LOGT("event-notification: %s%s: %s", - nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)), - is_dump ? ", in-dump" : "", - nmp_object_to_string(obj, - is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, - NULL, - 0)); - - { - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - - switch (msghdr->nlmsg_type) { - case RTM_GETLINK: - case RTM_NEWADDR: - case RTM_NEWLINK: - case RTM_NEWQDISC: - case RTM_NEWRULE: - case RTM_NEWTFILTER: - cache_op = nmp_cache_update_netlink(cache, obj, is_dump, &obj_old, &obj_new); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - cache_on_change(platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); - } - break; - - case RTM_NEWROUTE: - { - nm_auto_nmpobj const NMPObject *obj_replace = NULL; - gboolean resync_required = FALSE; - gboolean only_dirty = FALSE; - gboolean is_ipv6; - - /* IPv4 routes that are a response to RTM_GETROUTE must have - * the cloned flag while IPv6 routes don't have to. */ - is_ipv6 = NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP6_ROUTE; - if (is_ipv6 || NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, RTM_F_CLONED)) { - nm_assert(is_ipv6 || !nmp_object_is_alive(obj)); - priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - if (NM_FLAGS_HAS(priv->delayed_action.flags, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - guint i; - - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - DelayedActionWaitForNlResponseData *data = - &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); - - if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET - && data->response.out_route_get) { - nm_assert(!*data->response.out_route_get); - if (data->seq_number == nlmsg_hdr(msg)->nlmsg_seq) { - *data->response.out_route_get = nmp_object_clone(obj, FALSE); - data->response.out_route_get = NULL; - break; - } - } - } - } - } - - cache_op = nmp_cache_update_netlink_route(cache, - obj, - is_dump, - msghdr->nlmsg_flags, - &obj_old, - &obj_new, - &obj_replace, - &resync_required); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - if (obj_replace) { - const NMDedupMultiEntry *entry_replace; - - /* we found an object that is to be replaced by the RTM_NEWROUTE message. - * While we invoke the signal, the platform cache might change and invalidate - * the findings. Mitigate that (for the most part), by marking the entry as - * dirty and only delete @obj_replace if it is still dirty afterwards. - * - * Yes, there is a tiny tiny chance for still getting it wrong. But in practice, - * the signal handlers do not cause to call the platform again, so the cache - * is not really changing. -- if they would, it would anyway be dangerous to overflow - * the stack and it's not ensured that the processing of netlink messages is - * reentrant (maybe it is). - */ - entry_replace = nmp_cache_lookup_entry(cache, obj_replace); - nm_assert(entry_replace && entry_replace->obj == obj_replace); - nm_dedup_multi_entry_set_dirty(entry_replace, TRUE); - only_dirty = TRUE; - } - cache_on_change(platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); - } - - if (obj_replace) { - /* the RTM_NEWROUTE message indicates that another route was replaced. - * Remove it now. */ - cache_op = nmp_cache_remove(cache, obj_replace, TRUE, only_dirty, NULL); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - nm_assert(cache_op == NMP_CACHE_OPS_REMOVED); - cache_on_change(platform, cache_op, obj_replace, NULL); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_replace, NULL); - } - } - - if (resync_required) { - /* we'd like to avoid such resyncs as they are expensive and we should only rely on the - * netlink events. This needs investigation. */ - _LOGT("schedule resync of routes after RTM_NEWROUTE"); - delayed_action_schedule(platform, - delayed_action_refresh_from_needle_object(obj), - NULL); - } - break; - } - - case RTM_DELADDR: - case RTM_DELLINK: - case RTM_DELQDISC: - case RTM_DELROUTE: - case RTM_DELRULE: - case RTM_DELTFILTER: - cache_op = nmp_cache_remove_netlink(cache, obj, &obj_old, &obj_new); - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - cache_on_change(platform, cache_op, obj_old, obj_new); - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); - } - break; - default: - break; - } - } -} - -/*****************************************************************************/ - -static int -do_add_link_with_lookup(NMPlatform * platform, - NMLinkType link_type, - const char * name, - struct nl_msg * nlmsg, - const NMPlatformLink **out_link) -{ - const NMPObject * obj = NULL; - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char * errmsg = NULL; - int nle; - char s_buf[256]; - NMPCache * cache = nm_platform_get_cache(platform); - - event_handler_read_netlink(platform, FALSE); - - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); - if (nle < 0) { - _LOGE("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", - name, - nm_link_type_to_string(link_type), - nm_strerror(nle), - -nle); - NM_SET_OUT(out_link, NULL); - return nle; - } - - delayed_action_handle_all(platform, FALSE); - - nm_assert(seq_result); - - _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, - "do-add-link[%s/%s]: %s", - name, - nm_link_type_to_string(link_type), - wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - - if (out_link) { - obj = nmp_cache_lookup_link_full(cache, 0, name, FALSE, link_type, NULL, NULL); - *out_link = NMP_OBJECT_CAST_LINK(obj); - } - - return wait_for_nl_response_to_nmerr(seq_result); -} - -static int -do_add_addrroute(NMPlatform * platform, - const NMPObject *obj_id, - struct nl_msg * nlmsg, - gboolean suppress_netlink_failure) -{ - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char * errmsg = NULL; - int nle; - char s_buf[256]; - - nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), - NMP_OBJECT_TYPE_IP4_ADDRESS, - NMP_OBJECT_TYPE_IP6_ADDRESS, - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE)); - - event_handler_read_netlink(platform, FALSE); - - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); - if (nle < 0) { - _LOGE("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", - NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - nm_strerror(nle), - -nle); - return -NME_PL_NETLINK; - } - - delayed_action_handle_all(platform, FALSE); - - nm_assert(seq_result); - - _NMLOG((seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK - || (suppress_netlink_failure && seq_result < 0)) - ? LOGL_DEBUG - : LOGL_WARN, - "do-add-%s[%s]: %s", - NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - - if (NMP_OBJECT_GET_TYPE(obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { - /* In rare cases, the object is not yet ready as we received the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * rh#1484434 */ - if (!nmp_cache_lookup_obj(nm_platform_get_cache(platform), obj_id)) - do_request_one_type_by_needle_object(platform, obj_id); - } - - return wait_for_nl_response_to_nmerr(seq_result); -} - -static gboolean -do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) -{ - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char * errmsg = NULL; - int nle; - char s_buf[256]; - gboolean success; - const char * log_detail = ""; - - event_handler_read_netlink(platform, FALSE); - - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); - if (nle < 0) { - _LOGE("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", - NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - nm_strerror(nle), - -nle); - return FALSE; - } - - delayed_action_handle_all(platform, FALSE); - - nm_assert(seq_result); - - success = TRUE; - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { - /* ok */ - } else if (NM_IN_SET(-((int) seq_result), ESRCH, ENOENT)) - log_detail = ", meaning the object was already removed"; - else if (NM_IN_SET(-((int) seq_result), ENXIO) - && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) { - /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */ - log_detail = ", meaning the address was already removed"; - } else if (NM_IN_SET(-((int) seq_result), EADDRNOTAVAIL) - && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), - NMP_OBJECT_TYPE_IP4_ADDRESS, - NMP_OBJECT_TYPE_IP6_ADDRESS)) - log_detail = ", meaning the address was already removed"; - else - success = FALSE; - - _NMLOG(success ? LOGL_DEBUG : LOGL_WARN, - "do-delete-%s[%s]: %s%s", - NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, - nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), - wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)), - log_detail); - - if (NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), - NMP_OBJECT_TYPE_IP6_ADDRESS, - NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER)) { - /* In rare cases, the object is still there after we receive the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * rh#1484434 */ - if (nmp_cache_lookup_obj(nm_platform_get_cache(platform), obj_id)) - do_request_one_type_by_needle_object(platform, obj_id); - } - - return success; -} - -static int -do_change_link(NMPlatform * platform, - ChangeLinkType change_link_type, - int ifindex, - struct nl_msg * nlmsg, - const ChangeLinkData *data) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - int nle; - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char * errmsg = NULL; - char s_buf[256]; - int result = 0; - NMLogLevel log_level = LOGL_DEBUG; - const char * log_result = "failure"; - const char * log_detail = ""; - gs_free char * log_detail_free = NULL; - const NMPObject * obj_cache; - - if (!nm_platform_netns_push(platform, &netns)) { - log_level = LOGL_ERR; - log_detail = ", failure to change network namespace"; - goto out; - } - -retry: - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); - if (nle < 0) { - log_level = LOGL_ERR; - log_detail_free = - g_strdup_printf(", failure sending netlink request: %s (%d)", nm_strerror(nle), -nle); - log_detail = log_detail_free; - goto out; - } - - /* always refetch the link after changing it. There seems to be issues - * and we sometimes lack events. Nuke it from the orbit... */ - delayed_action_schedule(platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER(ifindex)); - - delayed_action_handle_all(platform, FALSE); - - nm_assert(seq_result); - - if (NM_IN_SET(-((int) seq_result), EOPNOTSUPP) && nlmsg_hdr(nlmsg)->nlmsg_type == RTM_NEWLINK) { - nlmsg_hdr(nlmsg)->nlmsg_type = RTM_SETLINK; - goto retry; - } - - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { - log_result = "success"; - } else if (NM_IN_SET(-((int) seq_result), EEXIST, EADDRINUSE)) { - /* */ - } else if (NM_IN_SET(-((int) seq_result), ESRCH, ENOENT)) { - log_detail = ", firmware not found"; - result = -NME_PL_NO_FIRMWARE; - } else if (NM_IN_SET(-((int) seq_result), ERANGE) - && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { - log_detail = ", setting MTU to requested size is not possible"; - result = -NME_PL_CANT_SET_MTU; - } else if (NM_IN_SET(-((int) seq_result), ENFILE) - && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS - && (obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex)) - && obj_cache->link.l_address.len == data->set_address.length - && memcmp(obj_cache->link.l_address.data, - data->set_address.address, - data->set_address.length) - == 0) { - /* workaround ENFILE which may be wrongly returned (bgo #770456). - * If the MAC address is as expected, assume success? */ - log_result = "success"; - log_detail = " (assume success changing address)"; - result = 0; - } else if (NM_IN_SET(-((int) seq_result), ENODEV)) { - log_level = LOGL_DEBUG; - result = -NME_PL_NOT_FOUND; - } else if (-((int) seq_result) == EAFNOSUPPORT) { - log_level = LOGL_DEBUG; - result = -NME_PL_OPNOTSUPP; - } else { - log_level = LOGL_WARN; - result = -NME_UNSPEC; - } - -out: - _NMLOG(log_level, - "do-change-link[%d]: %s changing link: %s%s", - ifindex, - log_result, - wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)), - log_detail); - return result; -} - -static int -link_add(NMPlatform * platform, - NMLinkType type, - const char * name, - int parent, - const void * address, - size_t address_len, - guint32 mtu, - gconstpointer extra_data, - const NMPlatformLink **out_link) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - if (type == NM_LINK_TYPE_BOND) { - /* When the kernel loads the bond module, either via explicit modprobe - * or automatically in response to creating a bond master, it will also - * create a 'bond0' interface. Since the bond we're about to create may - * or may not be named 'bond0' prevent potential confusion about a bond - * that the user didn't want by telling the bonding module not to create - * bond0 automatically. - */ - if (!g_file_test("/sys/class/net/bonding_masters", G_FILE_TEST_EXISTS)) - (void) nm_utils_modprobe(NULL, TRUE, "bonding", "max_bonds=0", NULL); - } - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, name); - if (!nlmsg) - return -NME_UNSPEC; - - if (parent > 0) - NLA_PUT_U32(nlmsg, IFLA_LINK, parent); - - if (address && address_len) - NLA_PUT(nlmsg, IFLA_ADDRESS, address_len, address); - - if (mtu) - NLA_PUT_U32(nlmsg, IFLA_MTU, mtu); - - if (!_nl_msg_new_link_set_linkinfo(nlmsg, type, extra_data)) - return -NME_UNSPEC; - - return do_add_link_with_lookup(platform, type, name, nlmsg, out_link); -nla_put_failure: - g_return_val_if_reached(-NME_BUG); -} - -static gboolean -link_delete(NMPlatform *platform, int ifindex) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; - const NMPObject * obj; - - obj = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex); - if (!obj || !obj->_link.netlink.is_in_netlink) - return FALSE; - - nlmsg = _nl_msg_new_link(RTM_DELLINK, 0, ifindex, NULL); - - nmp_object_stackinit_id_link(&obj_id, ifindex); - return do_delete_object(platform, &obj_id, nlmsg); -} - -static gboolean -link_refresh(NMPlatform *platform, int ifindex) -{ - do_request_link(platform, ifindex, NULL); - return !!nm_platform_link_get_obj(platform, ifindex, TRUE); -} - -static gboolean -link_set_netns(NMPlatform *platform, int ifindex, int netns_fd) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg) - return FALSE; - - NLA_PUT(nlmsg, IFLA_NET_NS_FD, 4, &netns_fd); - return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static int -link_change_flags(NMPlatform *platform, int ifindex, unsigned flags_mask, unsigned flags_set) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - char s_flags[100]; - - _LOGD("link: change %d: flags: set 0x%x/0x%x ([%s] / [%s])", - ifindex, - flags_set, - flags_mask, - nm_platform_link_flags2str(flags_set, s_flags, sizeof(s_flags)), - nm_platform_link_flags2str(flags_mask, NULL, 0)); - - nlmsg = _nl_msg_new_link_full(RTM_NEWLINK, 0, ifindex, NULL, AF_UNSPEC, flags_mask, flags_set); - if (!nlmsg) - return -NME_UNSPEC; - return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); -} - -static gboolean -link_set_up(NMPlatform *platform, int ifindex, gboolean *out_no_firmware) -{ - int r; - - r = link_change_flags(platform, ifindex, IFF_UP, IFF_UP); - NM_SET_OUT(out_no_firmware, (r == -NME_PL_NO_FIRMWARE)); - return r >= 0; -} - -static gboolean -link_set_down(NMPlatform *platform, int ifindex) -{ - return (link_change_flags(platform, ifindex, IFF_UP, 0) >= 0); -} - -static gboolean -link_set_arp(NMPlatform *platform, int ifindex) -{ - return (link_change_flags(platform, ifindex, IFF_NOARP, 0) >= 0); -} - -static gboolean -link_set_noarp(NMPlatform *platform, int ifindex) -{ - return (link_change_flags(platform, ifindex, IFF_NOARP, IFF_NOARP) >= 0); -} - -static int -link_set_user_ipv6ll_enabled(NMPlatform *platform, int ifindex, gboolean enabled) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; - - _LOGD("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", - ifindex, - nm_platform_link_inet6_addrgenmode2str(mode, NULL, 0)); - - if (!nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { - _LOGD("link: change %d: user-ipv6ll: not supported", ifindex); - return -NME_PL_OPNOTSUPP; - } - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg || !_nl_msg_new_link_set_afspec(nlmsg, mode, NULL)) - g_return_val_if_reached(-NME_BUG); - - return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); -} - -static gboolean -link_set_token(NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - _LOGD("link: change %d: token: set IPv6 address generation token to %s", - ifindex, - nm_utils_inet6_interface_identifier_to_token(iid, sbuf)); - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg || !_nl_msg_new_link_set_afspec(nlmsg, -1, &iid)) - g_return_val_if_reached(FALSE); - - return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); -} - -static gboolean -link_supports_carrier_detect(NMPlatform *platform, int ifindex) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - - if (!nm_platform_netns_push(platform, &netns)) - return FALSE; - - /* We use netlink for the actual carrier detection, but netlink can't tell - * us whether the device actually supports carrier detection in the first - * place. We assume any device that does implements one of these two APIs. - */ - return nmp_utils_ethtool_supports_carrier_detect(ifindex) - || nmp_utils_mii_supports_carrier_detect(ifindex); -} - -static gboolean -link_supports_vlans(NMPlatform *platform, int ifindex) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - const NMPObject * obj; - - obj = nm_platform_link_get_obj(platform, ifindex, TRUE); - - /* Only ARPHRD_ETHER links can possibly support VLANs. */ - if (!obj || obj->link.arptype != ARPHRD_ETHER) - return FALSE; - - if (!nm_platform_netns_push(platform, &netns)) - return FALSE; - - return nmp_utils_ethtool_supports_vlans(ifindex); -} - -static gboolean -link_supports_sriov(NMPlatform *platform, int ifindex) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - nm_auto_close int dirfd = -1; - char ifname[IFNAMSIZ]; - int num = -1; - - if (!nm_platform_netns_push(platform, &netns)) - return FALSE; - - dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname); - if (dirfd < 0) - return FALSE; - - num = - nm_platform_sysctl_get_int32(platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"), - -1); - - return num != -1; -} - -static int -link_set_address(NMPlatform *platform, int ifindex, gconstpointer address, size_t length) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - const ChangeLinkData d = { - .set_address = - { - .address = address, - .length = length, - }, - }; - - if (!address || !length) - g_return_val_if_reached(-NME_BUG); - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg) - g_return_val_if_reached(-NME_BUG); - - NLA_PUT(nlmsg, IFLA_ADDRESS, length, address); - - return do_change_link(platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d); -nla_put_failure: - g_return_val_if_reached(-NME_BUG); -} - -static int -link_set_name(NMPlatform *platform, int ifindex, const char *name) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg) - g_return_val_if_reached(-NME_BUG); - - NLA_PUT(nlmsg, IFLA_IFNAME, strlen(name) + 1, name); - - return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static gboolean -link_get_permanent_address(NMPlatform *platform, int ifindex, guint8 *buf, size_t *length) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - - if (!nm_platform_netns_push(platform, &netns)) - return FALSE; - - return nmp_utils_ethtool_get_permanent_address(ifindex, buf, length); -} - -static int -link_set_mtu(NMPlatform *platform, int ifindex, guint32 mtu) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg) - return FALSE; - - NLA_PUT_U32(nlmsg, IFLA_MTU, mtu); - - return do_change_link(platform, CHANGE_LINK_TYPE_SET_MTU, ifindex, nlmsg, NULL); -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static void -sriov_idle_cb(gpointer user_data, GCancellable *cancellable) -{ - gs_unref_object NMPlatform *platform = NULL; - gs_free_error GError *cancelled_error = NULL; - gs_free_error GError * error = NULL; - NMPlatformAsyncCallback callback; - gpointer callback_data; - - g_cancellable_set_error_if_cancelled(cancellable, &cancelled_error); - nm_utils_user_data_unpack(user_data, &platform, &error, &callback, &callback_data); - callback(cancelled_error ?: error, callback_data); -} - -static void -link_set_sriov_params_async(NMPlatform * platform, - int ifindex, - guint num_vfs, - NMTernary autoprobe, - NMPlatformAsyncCallback callback, - gpointer data, - GCancellable * cancellable) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - gs_free_error GError *error = NULL; - nm_auto_close int dirfd = -1; - int current_autoprobe; - guint i, total; - gint64 current_num; - char ifname[IFNAMSIZ]; - gpointer packed; - const char * values[3]; - char buf[64]; - - g_return_if_fail(callback || !data); - g_return_if_fail(cancellable); - - if (!nm_platform_netns_push(platform, &netns)) { - g_set_error_literal(&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "couldn't change namespace"); - goto out_idle; - } - - dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname); - if (!dirfd) { - g_set_error_literal(&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "couldn't open netdir"); - goto out_idle; - } - - total = nm_platform_sysctl_get_int_checked( - platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_totalvfs"), - 10, - 0, - G_MAXUINT, - 0); - if (!errno && num_vfs > total) { - _LOGW("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs); - num_vfs = total; - } - - /* - * Take special care when setting new values: - * - don't touch anything if the right values are already set - * - to change the number of VFs or autoprobe we need to destroy existing VFs - * - the autoprobe setting is irrelevant when numvfs is zero - */ - current_num = nm_platform_sysctl_get_int_checked( - platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"), - 10, - 0, - G_MAXUINT, - -1); - current_autoprobe = nm_platform_sysctl_get_int_checked( - platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_drivers_autoprobe"), - 10, - 0, - 1, - -1); - - if (current_autoprobe == -1 && errno == ENOENT) { - /* older kernel versions don't have this sysctl. Assume the value is - * "1". */ - current_autoprobe = 1; - } - - if (current_num == num_vfs - && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) - goto out_idle; - - if (NM_IN_SET(autoprobe, NM_TERNARY_TRUE, NM_TERNARY_FALSE) && current_autoprobe != autoprobe - && !nm_platform_sysctl_set( - NM_PLATFORM_GET, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_drivers_autoprobe"), - nm_sprintf_buf(buf, "%d", (int) autoprobe))) { - g_set_error(&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "couldn't set SR-IOV drivers-autoprobe to %d: %s", - (int) autoprobe, - nm_strerror_native(errno)); - goto out_idle; - } - - if (current_num == 0 && num_vfs == 0) - goto out_idle; - - i = 0; - if (current_num != 0) - values[i++] = "0"; - if (num_vfs != 0) - values[i++] = nm_sprintf_bufa(32, "%u", num_vfs); - values[i++] = NULL; - - sysctl_set_async(platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "device/sriov_numvfs"), - values, - callback, - data, - cancellable); - return; - -out_idle: - if (callback) { - packed = nm_utils_user_data_pack(g_object_ref(platform), - g_steal_pointer(&error), - callback, - data); - nm_utils_invoke_on_idle(cancellable, sriov_idle_cb, packed); - } -} - -static gboolean -link_set_sriov_vfs(NMPlatform *platform, int ifindex, const NMPlatformVF *const *vfs) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - struct nlattr * list, *info, *vlan_list; - guint i; - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg) - g_return_val_if_reached(-NME_BUG); - - if (!(list = nla_nest_start(nlmsg, IFLA_VFINFO_LIST))) - goto nla_put_failure; - - for (i = 0; vfs[i]; i++) { - const NMPlatformVF *vf = vfs[i]; - - if (!(info = nla_nest_start(nlmsg, IFLA_VF_INFO))) - goto nla_put_failure; - - if (vf->spoofchk >= 0) { - struct _ifla_vf_setting ivs = {0}; - - ivs.vf = vf->index; - ivs.setting = vf->spoofchk; - NLA_PUT(nlmsg, IFLA_VF_SPOOFCHK, sizeof(ivs), &ivs); - } - - if (vf->trust >= 0) { - struct _ifla_vf_setting ivs = {0}; - - ivs.vf = vf->index; - ivs.setting = vf->trust; - NLA_PUT(nlmsg, IFLA_VF_TRUST, sizeof(ivs), &ivs); - } - - if (vf->mac.len) { - struct ifla_vf_mac ivm = {0}; - - ivm.vf = vf->index; - memcpy(ivm.mac, vf->mac.data, vf->mac.len); - NLA_PUT(nlmsg, IFLA_VF_MAC, sizeof(ivm), &ivm); - } - - if (vf->min_tx_rate || vf->max_tx_rate) { - struct _ifla_vf_rate ivr = {0}; - - ivr.vf = vf->index; - ivr.min_tx_rate = vf->min_tx_rate; - ivr.max_tx_rate = vf->max_tx_rate; - NLA_PUT(nlmsg, IFLA_VF_RATE, sizeof(ivr), &ivr); - } - - /* Kernel only supports one VLAN per VF now. If this - * changes in the future, we need to figure out how to - * clear existing VLANs and set new ones in one message - * with the new API.*/ - if (vf->num_vlans > 1) { - _LOGW("multiple VLANs per VF are not supported at the moment"); - return FALSE; - } else { - struct _ifla_vf_vlan_info ivvi = {0}; - - if (!(vlan_list = nla_nest_start(nlmsg, IFLA_VF_VLAN_LIST))) - goto nla_put_failure; - - ivvi.vf = vf->index; - if (vf->num_vlans == 1) { - ivvi.vlan = vf->vlans[0].id; - ivvi.qos = vf->vlans[0].qos; - ivvi.vlan_proto = htons(vf->vlans[0].proto_ad ? ETH_P_8021AD : ETH_P_8021Q); - } else { - /* Clear existing VLAN */ - ivvi.vlan = 0; - ivvi.qos = 0; - ivvi.vlan_proto = htons(ETH_P_8021Q); - } - - NLA_PUT(nlmsg, IFLA_VF_VLAN_INFO, sizeof(ivvi), &ivvi); - nla_nest_end(nlmsg, vlan_list); - } - nla_nest_end(nlmsg, info); - } - nla_nest_end(nlmsg, list); - - return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static gboolean -link_set_bridge_vlans(NMPlatform * platform, - int ifindex, - gboolean on_master, - const NMPlatformBridgeVlan *const *vlans) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - struct nlattr * list; - struct bridge_vlan_info vinfo = {}; - guint i; - - nlmsg = - _nl_msg_new_link_full(vlans ? RTM_SETLINK : RTM_DELLINK, 0, ifindex, NULL, AF_BRIDGE, 0, 0); - if (!nlmsg) - g_return_val_if_reached(-NME_BUG); - - if (!(list = nla_nest_start(nlmsg, IFLA_AF_SPEC))) - goto nla_put_failure; - - NLA_PUT_U16(nlmsg, IFLA_BRIDGE_FLAGS, on_master ? BRIDGE_FLAGS_MASTER : BRIDGE_FLAGS_SELF); - - if (vlans) { - /* Add VLANs */ - for (i = 0; vlans[i]; i++) { - const NMPlatformBridgeVlan *vlan = vlans[i]; - gboolean is_range = vlan->vid_start != vlan->vid_end; - - vinfo.vid = vlan->vid_start; - vinfo.flags = is_range ? BRIDGE_VLAN_INFO_RANGE_BEGIN : 0; - - if (vlan->untagged) - vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; - if (vlan->pvid) - vinfo.flags |= BRIDGE_VLAN_INFO_PVID; - - NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); - - if (is_range) { - vinfo.vid = vlan->vid_end; - vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; - NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); - } - } - } else { - /* Flush existing VLANs */ - vinfo.vid = 1; - vinfo.flags = BRIDGE_VLAN_INFO_RANGE_BEGIN; - NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); - - vinfo.vid = 4094; - vinfo.flags = BRIDGE_VLAN_INFO_RANGE_END; - NLA_PUT(nlmsg, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); - } - - nla_nest_end(nlmsg, list); - - return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static char * -link_get_physical_port_id(NMPlatform *platform, int ifindex) -{ - nm_auto_close int dirfd = -1; - char ifname_verified[IFNAMSIZ]; - - dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname_verified); - if (dirfd < 0) - return NULL; - return sysctl_get(platform, NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "phys_port_id")); -} - -static guint -link_get_dev_id(NMPlatform *platform, int ifindex) -{ - nm_auto_close int dirfd = -1; - char ifname_verified[IFNAMSIZ]; - - dirfd = nm_platform_sysctl_open_netdir(platform, ifindex, ifname_verified); - if (dirfd < 0) - return 0; - return nm_platform_sysctl_get_int_checked( - platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "dev_id"), - 16, - 0, - G_MAXUINT16, - 0); -} - -static gboolean -link_tun_add(NMPlatform * platform, - const char * name, - const NMPlatformLnkTun *props, - const NMPlatformLink ** out_link, - int * out_fd) -{ - const NMPObject * obj; - struct ifreq ifr = {}; - nm_auto_close int fd = -1; - - nm_assert(NM_IN_SET(props->type, IFF_TAP, IFF_TUN)); - nm_assert(props->persist || out_fd); - - fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC); - if (fd < 0) - return FALSE; - - nm_utils_ifname_cpy(ifr.ifr_name, name); - ifr.ifr_flags = ((short) props->type) | ((short) IFF_TUN_EXCL) - | (!props->pi ? (short) IFF_NO_PI : (short) 0) - | (props->vnet_hdr ? (short) IFF_VNET_HDR : (short) 0) - | (props->multi_queue ? (short) NM_IFF_MULTI_QUEUE : (short) 0); - if (ioctl(fd, TUNSETIFF, &ifr)) - return FALSE; - - if (props->owner_valid) { - if (ioctl(fd, TUNSETOWNER, (uid_t) props->owner)) - return FALSE; - } - - if (props->group_valid) { - if (ioctl(fd, TUNSETGROUP, (gid_t) props->group)) - return FALSE; - } - - if (props->persist) { - if (ioctl(fd, TUNSETPERSIST, 1)) - return FALSE; - } - - do_request_link(platform, 0, name); - obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform), - 0, - name, - FALSE, - NM_LINK_TYPE_TUN, - NULL, - NULL); - - if (!obj) - return FALSE; - - NM_SET_OUT(out_link, &obj->link); - NM_SET_OUT(out_fd, nm_steal_fd(&fd)); - return TRUE; -} - -static void -_vlan_change_vlan_qos_mapping_create(gboolean is_ingress_map, - gboolean reset_all, - const NMVlanQosMapping *current_map, - guint current_n_map, - const NMVlanQosMapping *set_map, - guint set_n_map, - NMVlanQosMapping ** out_map, - guint * out_n_map) -{ - NMVlanQosMapping *map; - guint i, j, len; - const guint INGRESS_RANGE_LEN = 8; - - nm_assert(out_map && !*out_map); - nm_assert(out_n_map && !*out_n_map); - - if (!reset_all) - current_n_map = 0; - else if (is_ingress_map) - current_n_map = INGRESS_RANGE_LEN; - - len = current_n_map + set_n_map; - - if (len == 0) - return; - - map = g_new(NMVlanQosMapping, len); - - if (current_n_map) { - if (is_ingress_map) { - /* For the ingress-map, there are only 8 entries (0 to 7). - * When the user requests to reset all entries, we don't actually - * need the cached entries, we can just explicitly clear all possible - * ones. - * - * That makes only a real difference in case our cache is out-of-date. - * - * For the egress map we cannot do that, because there are far too - * many. There we can only clear the entries that we know about. */ - for (i = 0; i < INGRESS_RANGE_LEN; i++) { - map[i].from = i; - map[i].to = 0; - } - } else { - for (i = 0; i < current_n_map; i++) { - map[i].from = current_map[i].from; - map[i].to = 0; - } - } - } - if (set_n_map) - memcpy(&map[current_n_map], set_map, sizeof(*set_map) * set_n_map); - - g_qsort_with_data(map, len, sizeof(*map), _vlan_qos_mapping_cmp_from, NULL); - - for (i = 0, j = 0; i < len; i++) { - if ((is_ingress_map && !VLAN_XGRESS_PRIO_VALID(map[i].from)) - || (!is_ingress_map && !VLAN_XGRESS_PRIO_VALID(map[i].to))) - continue; - if (j > 0 && map[j - 1].from == map[i].from) - map[j - 1] = map[i]; - else - map[j++] = map[i]; - } - - *out_map = map; - *out_n_map = j; -} - -static gboolean -link_vlan_change(NMPlatform * platform, - int ifindex, - NMVlanFlags flags_mask, - NMVlanFlags flags_set, - gboolean ingress_reset_all, - const NMVlanQosMapping *ingress_map, - gsize n_ingress_map, - gboolean egress_reset_all, - const NMVlanQosMapping *egress_map, - gsize n_egress_map) -{ - const NMPObject * obj_cache; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - const NMPObjectLnkVlan * lnk; - guint new_n_ingress_map = 0; - guint new_n_egress_map = 0; - gs_free NMVlanQosMapping *new_ingress_map = NULL; - gs_free NMVlanQosMapping *new_egress_map = NULL; - - obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex); - if (!obj_cache || !obj_cache->_link.netlink.is_in_netlink) { - _LOGD("link: change %d: %s: link does not exist", ifindex, "vlan"); - return FALSE; - } - - lnk = obj_cache->_link.netlink.lnk ? &obj_cache->_link.netlink.lnk->_lnk_vlan : NULL; - - flags_set &= flags_mask; - - _vlan_change_vlan_qos_mapping_create(TRUE, - ingress_reset_all, - lnk ? lnk->ingress_qos_map : NULL, - lnk ? lnk->n_ingress_qos_map : 0, - ingress_map, - n_ingress_map, - &new_ingress_map, - &new_n_ingress_map); - - _vlan_change_vlan_qos_mapping_create(FALSE, - egress_reset_all, - lnk ? lnk->egress_qos_map : NULL, - lnk ? lnk->n_egress_qos_map : 0, - egress_map, - n_egress_map, - &new_egress_map, - &new_n_egress_map); - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg - || !_nl_msg_new_link_set_linkinfo_vlan(nlmsg, - -1, - flags_mask, - flags_set, - new_ingress_map, - new_n_ingress_map, - new_egress_map, - new_n_egress_map)) - g_return_val_if_reached(FALSE); - - return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); -} - -static gboolean -link_enslave(NMPlatform *platform, int master, int slave) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - int ifindex = slave; - - nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL); - if (!nlmsg) - return FALSE; - - NLA_PUT_U32(nlmsg, IFLA_MASTER, master); - - return (do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) >= 0); -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static gboolean -link_release(NMPlatform *platform, int master, int slave) -{ - return link_enslave(platform, 0, slave); -} - -/*****************************************************************************/ - -static gboolean -_infiniband_partition_action(NMPlatform * platform, - InfinibandAction action, - int parent, - int p_key, - const NMPlatformLink **out_link) -{ - nm_auto_close int dirfd = -1; - char ifname_parent[IFNAMSIZ]; - const NMPObject * obj; - char id[20]; - char name[IFNAMSIZ]; - gboolean success; - - nm_assert(NM_IN_SET(action, INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD)); - nm_assert(p_key > 0 && p_key <= 0xffff && p_key != 0x8000); - - dirfd = nm_platform_sysctl_open_netdir(platform, parent, ifname_parent); - if (dirfd < 0) { - errno = ENOENT; - return FALSE; - } - - nm_sprintf_buf(id, "0x%04x", p_key); - if (action == INFINIBAND_ACTION_CREATE_CHILD) - success = - nm_platform_sysctl_set(platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_parent, "create_child"), - id); - else - success = - nm_platform_sysctl_set(platform, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_parent, "delete_child"), - id); - - if (!success) { - if (action == INFINIBAND_ACTION_DELETE_CHILD && errno == ENODEV) - return TRUE; - return FALSE; - } - - nm_utils_new_infiniband_name(name, ifname_parent, p_key); - do_request_link(platform, 0, name); - - if (action == INFINIBAND_ACTION_DELETE_CHILD) - return TRUE; - - obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform), - 0, - name, - FALSE, - NM_LINK_TYPE_INFINIBAND, - NULL, - NULL); - if (out_link) - *out_link = obj ? &obj->link : NULL; - return !!obj; -} - -static gboolean -infiniband_partition_add(NMPlatform * platform, - int parent, - int p_key, - const NMPlatformLink **out_link) -{ - return _infiniband_partition_action(platform, - INFINIBAND_ACTION_CREATE_CHILD, - parent, - p_key, - out_link); -} - -static gboolean -infiniband_partition_delete(NMPlatform *platform, int parent, int p_key) -{ - return _infiniband_partition_action(platform, - INFINIBAND_ACTION_DELETE_CHILD, - parent, - p_key, - NULL); -} - -/*****************************************************************************/ - -static GObject * -get_ext_data(NMPlatform *platform, int ifindex) -{ - const NMPObject *obj; - - obj = nmp_cache_lookup_link(nm_platform_get_cache(platform), ifindex); - if (!obj) - return NULL; - - return obj->_link.ext_data; -} - -/*****************************************************************************/ - -#define WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, retval) \ - nm_auto_pop_netns NMPNetns *netns = NULL; \ - NMWifiUtils * wifi_data; \ - if (!nm_platform_netns_push(platform, &netns)) \ - return retval; \ - wifi_data = NM_WIFI_UTILS(get_ext_data(platform, ifindex)); \ - if (!wifi_data) \ - return retval; - -static gboolean -wifi_get_capabilities(NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - if (caps) - *caps = nm_wifi_utils_get_caps(wifi_data); - return TRUE; -} - -static gboolean -wifi_get_bssid(NMPlatform *platform, int ifindex, guint8 *bssid) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_bssid(wifi_data, bssid); -} - -static guint32 -wifi_get_frequency(NMPlatform *platform, int ifindex) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); - return nm_wifi_utils_get_freq(wifi_data); -} - -static gboolean -wifi_get_quality(NMPlatform *platform, int ifindex) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_qual(wifi_data); -} - -static guint32 -wifi_get_rate(NMPlatform *platform, int ifindex) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_rate(wifi_data); -} - -static NM80211Mode -wifi_get_mode(NMPlatform *platform, int ifindex) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, NM_802_11_MODE_UNKNOWN); - return nm_wifi_utils_get_mode(wifi_data); -} - -static void -wifi_set_mode(NMPlatform *platform, int ifindex, NM80211Mode mode) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); - nm_wifi_utils_set_mode(wifi_data, mode); -} - -static void -wifi_set_powersave(NMPlatform *platform, int ifindex, guint32 powersave) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); - nm_wifi_utils_set_powersave(wifi_data, powersave); -} - -static guint32 -wifi_find_frequency(NMPlatform *platform, int ifindex, const guint32 *freqs) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); - return nm_wifi_utils_find_freq(wifi_data, freqs); -} - -static void -wifi_indicate_addressing_running(NMPlatform *platform, int ifindex, gboolean running) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); - nm_wifi_utils_indicate_addressing_running(wifi_data, running); -} - -static NMSettingWirelessWakeOnWLan -wifi_get_wake_on_wlan(NMPlatform *platform, int ifindex) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_wake_on_wlan(wifi_data); -} - -static gboolean -wifi_set_wake_on_wlan(NMPlatform *platform, int ifindex, NMSettingWirelessWakeOnWLan wowl) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_set_wake_on_wlan(wifi_data, wowl); -} - -/*****************************************************************************/ - -static gboolean -link_can_assume(NMPlatform *platform, int ifindex) -{ - NMPLookup lookup; - const NMPObject *link, *o; - NMDedupMultiIter iter; - NMPCache * cache = nm_platform_get_cache(platform); - - if (ifindex <= 0) - return FALSE; - - link = nm_platform_link_get_obj(platform, ifindex, TRUE); - if (!link) - return FALSE; - - if (!NM_FLAGS_HAS(link->link.n_ifi_flags, IFF_UP)) - return FALSE; - - if (link->link.master > 0) - return TRUE; - - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); - if (nmp_cache_lookup(cache, &lookup)) - return TRUE; - - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); - nmp_cache_iter_for_each (&iter, nmp_cache_lookup(cache, &lookup), &o) { - nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP6_ADDRESS); - if (!IN6_IS_ADDR_LINKLOCAL(&o->ip6_address.address)) - return TRUE; - } - return FALSE; -} - -/*****************************************************************************/ - -static guint32 -mesh_get_channel(NMPlatform *platform, int ifindex) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); - return nm_wifi_utils_get_mesh_channel(wifi_data); -} - -static gboolean -mesh_set_channel(NMPlatform *platform, int ifindex, guint32 channel) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_set_mesh_channel(wifi_data, channel); -} - -static gboolean -mesh_set_ssid(NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_set_mesh_ssid(wifi_data, ssid, len); -} - -/*****************************************************************************/ - -#define WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, retval) \ - NMWpanUtils *wpan_data = NM_WPAN_UTILS(get_ext_data(platform, ifindex)); \ - if (!wpan_data) \ - return retval; - -static guint16 -wpan_get_pan_id(NMPlatform *platform, int ifindex) -{ - WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, G_MAXINT16); - return nm_wpan_utils_get_pan_id(wpan_data); -} - -static gboolean -wpan_set_pan_id(NMPlatform *platform, int ifindex, guint16 pan_id) -{ - WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE); - return nm_wpan_utils_set_pan_id(wpan_data, pan_id); -} - -static guint16 -wpan_get_short_addr(NMPlatform *platform, int ifindex) -{ - WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, G_MAXINT16); - return nm_wpan_utils_get_short_addr(wpan_data); -} - -static gboolean -wpan_set_short_addr(NMPlatform *platform, int ifindex, guint16 short_addr) -{ - WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE); - return nm_wpan_utils_set_short_addr(wpan_data, short_addr); -} - -static gboolean -wpan_set_channel(NMPlatform *platform, int ifindex, guint8 page, guint8 channel) -{ - WPAN_GET_WPAN_DATA(wpan_data, platform, ifindex, FALSE); - return nm_wpan_utils_set_channel(wpan_data, page, channel); -} - -/*****************************************************************************/ - -static gboolean -link_get_wake_on_lan(NMPlatform *platform, int ifindex) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - NMLinkType type = nm_platform_link_get_type(platform, ifindex); - - if (!nm_platform_netns_push(platform, &netns)) - return FALSE; - - if (type == NM_LINK_TYPE_ETHERNET) - return nmp_utils_ethtool_get_wake_on_lan(ifindex); - else if (type == NM_LINK_TYPE_WIFI) { - NMWifiUtils *wifi_data = NM_WIFI_UTILS(get_ext_data(platform, ifindex)); - - if (!wifi_data) - return FALSE; - - return !NM_IN_SET(nm_wifi_utils_get_wake_on_wlan(wifi_data), - NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE, - NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE); - - } else - return FALSE; -} - -static gboolean -link_get_driver_info(NMPlatform *platform, - int ifindex, - char ** out_driver_name, - char ** out_driver_version, - char ** out_fw_version) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - NMPUtilsEthtoolDriverInfo driver_info; - - if (!nm_platform_netns_push(platform, &netns)) - return FALSE; - - if (!nmp_utils_ethtool_get_driver_info(ifindex, &driver_info)) - return FALSE; - NM_SET_OUT(out_driver_name, g_strdup(driver_info.driver)); - NM_SET_OUT(out_driver_version, g_strdup(driver_info.version)); - NM_SET_OUT(out_fw_version, g_strdup(driver_info.fw_version)); - return TRUE; -} - -/*****************************************************************************/ - -static gboolean -ip4_address_add(NMPlatform *platform, - int ifindex, - in_addr_t addr, - guint8 plen, - in_addr_t peer_addr, - in_addr_t broadcast_address, - guint32 lifetime, - guint32 preferred, - guint32 flags, - const char *label) -{ - NMPObject obj_id; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - nlmsg = _nl_msg_new_address(RTM_NEWADDR, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET, - ifindex, - &addr, - plen, - &peer_addr, - flags, - nm_utils_ip4_address_is_link_local(addr) ? RT_SCOPE_LINK - : RT_SCOPE_UNIVERSE, - lifetime, - preferred, - broadcast_address, - label); - - nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, addr, plen, peer_addr); - return (do_add_addrroute(platform, &obj_id, nlmsg, FALSE) >= 0); -} - -static gboolean -ip6_address_add(NMPlatform * platform, - int ifindex, - struct in6_addr addr, - guint8 plen, - struct in6_addr peer_addr, - guint32 lifetime, - guint32 preferred, - guint32 flags) -{ - NMPObject obj_id; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - - nlmsg = _nl_msg_new_address(RTM_NEWADDR, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET6, - ifindex, - &addr, - plen, - IN6_IS_ADDR_UNSPECIFIED(&peer_addr) ? NULL : &peer_addr, - flags, - RT_SCOPE_UNIVERSE, - lifetime, - preferred, - 0, - NULL); - - nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr); - return (do_add_addrroute(platform, &obj_id, nlmsg, FALSE) >= 0); -} - -static gboolean -ip4_address_delete(NMPlatform *platform, - int ifindex, - in_addr_t addr, - guint8 plen, - in_addr_t peer_address) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; - - nlmsg = _nl_msg_new_address(RTM_DELADDR, - 0, - AF_INET, - ifindex, - &addr, - plen, - &peer_address, - 0, - RT_SCOPE_NOWHERE, - NM_PLATFORM_LIFETIME_PERMANENT, - NM_PLATFORM_LIFETIME_PERMANENT, - 0, - NULL); - if (!nlmsg) - g_return_val_if_reached(FALSE); - - nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, addr, plen, peer_address); - return do_delete_object(platform, &obj_id, nlmsg); -} - -static gboolean -ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; - - nlmsg = _nl_msg_new_address(RTM_DELADDR, - 0, - AF_INET6, - ifindex, - &addr, - plen, - NULL, - 0, - RT_SCOPE_NOWHERE, - NM_PLATFORM_LIFETIME_PERMANENT, - NM_PLATFORM_LIFETIME_PERMANENT, - 0, - NULL); - if (!nlmsg) - g_return_val_if_reached(FALSE); - - nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr); - return do_delete_object(platform, &obj_id, nlmsg); -} - -/*****************************************************************************/ - -static int -ip_route_add(NMPlatform * platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj; - - switch (addr_family) { - case AF_INET: - nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); - break; - case AF_INET6: - nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); - break; - default: - nm_assert_not_reached(); - } - - nm_platform_ip_route_normalize(addr_family, NMP_OBJECT_CAST_IP_ROUTE(&obj)); - - nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); - if (!nlmsg) - g_return_val_if_reached(-NME_BUG); - return do_add_addrroute(platform, - &obj, - nlmsg, - NM_FLAGS_HAS(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); -} - -static gboolean -object_delete(NMPlatform *platform, const NMPObject *obj) -{ - nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; - nm_auto_nlmsg struct nl_msg * nlmsg = NULL; - - if (!NMP_OBJECT_IS_STACKINIT(obj)) - obj_keep_alive = nmp_object_ref(obj); - - switch (NMP_OBJECT_GET_TYPE(obj)) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - nlmsg = _nl_msg_new_route(RTM_DELROUTE, 0, obj); - break; - case NMP_OBJECT_TYPE_ROUTING_RULE: - nlmsg = _nl_msg_new_routing_rule(RTM_DELRULE, 0, NMP_OBJECT_CAST_ROUTING_RULE(obj)); - break; - case NMP_OBJECT_TYPE_QDISC: - nlmsg = _nl_msg_new_qdisc(RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC(obj)); - break; - case NMP_OBJECT_TYPE_TFILTER: - nlmsg = _nl_msg_new_tfilter(RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER(obj)); - break; - default: - break; - } - - if (!nlmsg) - g_return_val_if_reached(FALSE); - return do_delete_object(platform, obj, nlmsg); -} - -/*****************************************************************************/ - -static int -ip_route_get(NMPlatform * platform, - int addr_family, - gconstpointer address, - int oif_ifindex, - NMPObject ** out_route) -{ - const gboolean is_v4 = (addr_family == AF_INET); - const int addr_len = is_v4 ? 4 : 16; - int try_count = 0; - WaitForNlResponseResult seq_result; - int nle; - nm_auto_nmpobj NMPObject *route = NULL; - - nm_assert(NM_IS_LINUX_PLATFORM(platform)); - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - nm_assert(address); - - do { - struct { - struct nlmsghdr n; - struct rtmsg r; - char buf[64]; - } req = { - .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), - .n.nlmsg_flags = NLM_F_REQUEST, - .n.nlmsg_type = RTM_GETROUTE, - .r.rtm_family = addr_family, - .r.rtm_tos = 0, - .r.rtm_dst_len = is_v4 ? 32 : 128, - .r.rtm_flags = 0x1000 /* RTM_F_LOOKUP_TABLE */, - }; - - nm_clear_pointer(&route, nmp_object_unref); - - if (!_nl_addattr_l(&req.n, sizeof(req), RTA_DST, address, addr_len)) - nm_assert_not_reached(); - - if (oif_ifindex > 0) { - gint32 ii = oif_ifindex; - - if (!_nl_addattr_l(&req.n, sizeof(req), RTA_OIF, &ii, sizeof(ii))) - nm_assert_not_reached(); - } - - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - nle = _nl_send_nlmsghdr(platform, - &req.n, - &seq_result, - NULL, - DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, - &route); - if (nle < 0) { - _LOGE("get-route: failure sending netlink request \"%s\" (%d)", - nm_strerror_native(-nle), - -nle); - return -NME_UNSPEC; - } - - delayed_action_handle_all(platform, FALSE); - - /* Retry, if we failed due to a cache resync. That can happen when the netlink - * socket fills up and we lost the response. */ - } while (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC && ++try_count < 10); - - if (seq_result < 0) { - /* negative seq_result is an errno from kernel. Map it to negative - * int (which are also errno). */ - return (int) seq_result; - } - - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { - if (route) { - NM_SET_OUT(out_route, g_steal_pointer(&route)); - return 0; - } - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; - } - - return -NME_UNSPEC; -} - -/*****************************************************************************/ - -static int -routing_rule_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformRoutingRule *routing_rule) -{ - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - nm_auto_nlmsg struct nl_msg *msg = NULL; - gs_free char * errmsg = NULL; - char s_buf[256]; - int nle; - - msg = _nl_msg_new_routing_rule(RTM_NEWRULE, flags, routing_rule); - - event_handler_read_netlink(platform, FALSE); - - nle = _nl_send_nlmsg(platform, - msg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); - if (nle < 0) { - _LOGE("do-add-rule: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); - return -NME_PL_NETLINK; - } - - delayed_action_handle_all(platform, FALSE); - - nm_assert(seq_result); - - _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, - "do-add-rule: %s", - wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; - if (seq_result < 0) - return seq_result; - return -NME_UNSPEC; -} - -/*****************************************************************************/ - -static int -qdisc_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) -{ - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char * errmsg = NULL; - int nle; - char s_buf[256]; - nm_auto_nlmsg struct nl_msg *msg = NULL; - - /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind - * is undefined. */ - - msg = _nl_msg_new_qdisc(RTM_NEWQDISC, flags, qdisc); - - event_handler_read_netlink(platform, FALSE); - - nle = _nl_send_nlmsg(platform, - msg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); - if (nle < 0) { - _LOGE("do-add-qdisc: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); - return -NME_PL_NETLINK; - } - - delayed_action_handle_all(platform, FALSE); - - nm_assert(seq_result); - - _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, - "do-add-qdisc: %s", - wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; - if (seq_result < 0) - return seq_result; - return -NME_UNSPEC; -} - -/*****************************************************************************/ - -static int -tfilter_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformTfilter *tfilter) -{ - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; - gs_free char * errmsg = NULL; - int nle; - char s_buf[256]; - nm_auto_nlmsg struct nl_msg *msg = NULL; - - /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind - * and tfilter.action.kind is undefined. */ - - msg = _nl_msg_new_tfilter(RTM_NEWTFILTER, flags, tfilter); - - event_handler_read_netlink(platform, FALSE); - - nle = _nl_send_nlmsg(platform, - msg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); - if (nle < 0) { - _LOGE("do-add-tfilter: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); - return -NME_PL_NETLINK; - } - - delayed_action_handle_all(platform, FALSE); - - nm_assert(seq_result); - - _NMLOG(seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG : LOGL_WARN, - "do-add-tfilter: %s", - wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); - - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - return 0; - - return -NME_UNSPEC; -} - -/*****************************************************************************/ - -static gboolean -event_handler(int fd, GIOCondition io_condition, gpointer user_data) -{ - delayed_action_handle_all(NM_PLATFORM(user_data), TRUE); - return TRUE; -} - -/*****************************************************************************/ - -/* copied from libnl3's recvmsgs() */ -static int -event_handler_recvmsgs(NMPlatform *platform, gboolean handle_events) -{ - NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - struct nl_sock * sk = priv->nlh; - int n; - int err = 0; - gboolean multipart = 0; - gboolean interrupted = FALSE; - struct nlmsghdr * hdr; - WaitForNlResponseResult seq_result; - struct sockaddr_nl nla = {0}; - struct ucred creds; - gboolean creds_has; - nm_auto_free unsigned char *buf = NULL; - -continue_reading: - nm_clear_pointer(&buf, free); - n = nl_recv(sk, &nla, &buf, &creds, &creds_has); - - if (n <= 0) { - if (n == -NME_NL_MSG_TRUNC) { - int buf_size; - - /* the message receive buffer was too small. We lost one message, which - * is unfortunate. Try to double the buffer size for the next time. */ - buf_size = nl_socket_get_msg_buf_size(sk); - if (buf_size < 512 * 1024) { - buf_size *= 2; - _LOGT("netlink: recvmsg: increase message buffer size for recvmsg() to %d bytes", - buf_size); - if (nl_socket_set_msg_buf_size(sk, buf_size) < 0) - nm_assert_not_reached(); - if (!handle_events) - goto continue_reading; - } - } - - return n; - } - - hdr = (struct nlmsghdr *) buf; - while (nlmsg_ok(hdr, n)) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - gboolean abort_parsing = FALSE; - gboolean process_valid_msg = FALSE; - guint32 seq_number; - char buf_nlmsghdr[400]; - const char * extack_msg = NULL; - - msg = nlmsg_alloc_convert(hdr); - - nlmsg_set_proto(msg, NETLINK_ROUTE); - nlmsg_set_src(msg, &nla); - - if (!creds_has || creds.pid) { - if (!creds_has) - _LOGT("netlink: recvmsg: received message without credentials"); - else - _LOGT("netlink: recvmsg: received non-kernel message (pid %d)", creds.pid); - err = 0; - goto stop; - } - - _LOGt("netlink: recvmsg: new message %s", - nl_nlmsghdr_to_str(hdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); - - nlmsg_set_creds(msg, &creds); - - if (hdr->nlmsg_flags & NLM_F_MULTI) - multipart = TRUE; - - if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { - /* - * We have to continue reading to clear - * all messages until a NLMSG_DONE is - * received and report the inconsistency. - */ - interrupted = TRUE; - } - - /* Other side wishes to see an ack for this message */ - if (hdr->nlmsg_flags & NLM_F_ACK) { - /* FIXME: implement */ - } - - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; - - if (hdr->nlmsg_type == NLMSG_DONE) { - /* messages terminates a multipart message, this is - * usually the end of a message and therefore we slip - * out of the loop by default. the user may overrule - * this action by skipping this packet. */ - multipart = FALSE; - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; - } else if (hdr->nlmsg_type == NLMSG_NOOP) { - /* Message to be ignored, the default action is to - * skip this message if no callback is specified. The - * user may overrule this action by returning - * NL_PROCEED. */ - } else if (hdr->nlmsg_type == NLMSG_OVERRUN) { - /* Data got lost, report back to user. The default action is to - * quit parsing. The user may overrule this action by returning - * NL_SKIP or NL_PROCEED (dangerous) */ - err = -NME_NL_MSG_OVERFLOW; - abort_parsing = TRUE; - } else if (hdr->nlmsg_type == NLMSG_ERROR) { - /* Message carries a nlmsgerr */ - struct nlmsgerr *e = nlmsg_data(hdr); - - if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { - /* Truncated error message, the default action - * is to stop parsing. The user may overrule - * this action by returning NL_SKIP or - * NL_PROCEED (dangerous) */ - err = -NME_NL_MSG_TRUNC; - abort_parsing = TRUE; - } else if (e->error) { - int errsv = nm_errno_native(e->error); - - if (NM_FLAGS_HAS(hdr->nlmsg_flags, NLM_F_ACK_TLVS) - && hdr->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) { - static const struct nla_policy policy[] = { - [NLMSGERR_ATTR_MSG] = {.type = NLA_STRING}, - [NLMSGERR_ATTR_OFFS] = {.type = NLA_U32}, - }; - struct nlattr *tb[G_N_ELEMENTS(policy)]; - struct nlattr *tlvs; - - tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len - - NLMSG_HDRLEN); - if (nla_parse_arr(tb, - tlvs, - hdr->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len, - policy) - >= 0) { - if (tb[NLMSGERR_ATTR_MSG]) - extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]); - } - } - - /* Error message reported back from kernel. */ - _LOGD("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", - nm_strerror_native(errsv), - errsv, - NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""), - nlmsg_hdr(msg)->nlmsg_seq); - seq_result = -NM_ERRNO_NATIVE(errsv); - } else - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; - } else - process_valid_msg = TRUE; - - seq_number = nlmsg_hdr(msg)->nlmsg_seq; - - /* check whether the seq number is different from before, and - * whether the previous number (@nlh_seq_last_seen) is a pending - * refresh-all request. In that case, the pending request is thereby - * completed. - * - * We must do that before processing the message with event_valid_msg(), - * because we must track the completion of the pending request before that. */ - event_seq_check_refresh_all(platform, seq_number); - - if (process_valid_msg) { - /* Valid message (not checking for MULTIPART bit to - * get along with broken kernels. NL_SKIP has no - * effect on this. */ - - event_valid_msg(platform, msg, handle_events); - - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; - } - - event_seq_check(platform, seq_number, seq_result, extack_msg); - - if (abort_parsing) - goto stop; - - err = 0; - hdr = nlmsg_next(hdr, &n); - } - - if (multipart) { - /* Multipart message not yet complete, continue reading */ - goto continue_reading; - } -stop: - if (!handle_events) { - /* when we don't handle events, we want to drain all messages from the socket - * without handling the messages (but still check for sequence numbers). - * Repeat reading. */ - goto continue_reading; - } - - if (interrupted) - return -NME_NL_DUMP_INTR; - return err; -} - -/*****************************************************************************/ - -static gboolean -event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - NMLinuxPlatformPrivate * priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - int r; - struct pollfd pfd; - gboolean any = FALSE; - int timeout_msec; - struct { - guint32 seq_number; - gint64 timeout_abs_ns; - gint64 now_ns; - } next; - - if (!nm_platform_netns_push(platform, &netns)) { - delayed_action_wait_for_nl_response_complete_all(platform, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS); - return FALSE; - } - - for (;;) { - for (;;) { - int nle; - - nle = event_handler_recvmsgs(platform, TRUE); - - if (nle < 0) { - switch (nle) { - case -EAGAIN: - goto after_read; - case -NME_NL_DUMP_INTR: - _LOGD("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", - nm_strerror(nle), - nle); - break; - case -NME_NL_MSG_TRUNC: - case -ENOBUFS: - _LOGI("netlink: read: %s. Need to resynchronize platform cache", ({ - const char *_reason = "unknown"; - switch (nle) { - case -NME_NL_MSG_TRUNC: - _reason = "message truncated"; - break; - case -ENOBUFS: - _reason = "too many netlink events"; - break; - } - _reason; - })); - event_handler_recvmsgs(platform, FALSE); - delayed_action_wait_for_nl_response_complete_all( - platform, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); - - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL - | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS - | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); - break; - default: - _LOGE("netlink: read: failed to retrieve incoming events: %s (%d)", - nm_strerror(nle), - nle); - break; - } - } - any = TRUE; - } - -after_read: - - if (!NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) - return any; - - delayed_action_wait_for_nl_response_complete_check(platform, - WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN, - &next.seq_number, - &next.timeout_abs_ns, - &next.now_ns); - - if (!wait_for_acks - || !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) - return any; - - nm_assert(next.seq_number); - nm_assert(next.now_ns > 0); - nm_assert(next.timeout_abs_ns > next.now_ns); - - _LOGT("netlink: read: wait for ACK for sequence number %u...", next.seq_number); - - timeout_msec = (next.timeout_abs_ns - next.now_ns) / (NM_UTILS_NSEC_PER_SEC / 1000); - - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = nl_socket_get_fd(priv->nlh); - pfd.events = POLLIN; - r = poll(&pfd, 1, MAX(1, timeout_msec)); - - if (r == 0) { - /* timeout and there is nothing to read. */ - goto after_read; - } - - if (r < 0) { - int errsv = errno; - - if (errsv != EINTR) { - _LOGE("netlink: read: poll failed with %s", nm_strerror_native(errsv)); - delayed_action_wait_for_nl_response_complete_all( - platform, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL); - return any; - } - /* Continue to read again, even if there might be nothing to read after EINTR. */ - } - } -} - -/*****************************************************************************/ - -static void -cache_update_link_udev(NMPlatform *platform, int ifindex, struct udev_device *udevice) -{ - nm_auto_nmpobj const NMPObject *obj_old = NULL; - nm_auto_nmpobj const NMPObject *obj_new = NULL; - NMPCacheOpsType cache_op; - - cache_op = nmp_cache_update_link_udev(nm_platform_get_cache(platform), - ifindex, - udevice, - &obj_old, - &obj_new); - - if (cache_op != NMP_CACHE_OPS_UNCHANGED) { - nm_auto_pop_netns NMPNetns *netns = NULL; - - cache_on_change(platform, cache_op, obj_old, obj_new); - if (!nm_platform_netns_push(platform, &netns)) - return; - nm_platform_cache_update_emit_signal(platform, cache_op, obj_old, obj_new); - } -} - -static void -udev_device_added(NMPlatform *platform, struct udev_device *udevice) -{ - const char *ifname; - const char *ifindex_s; - int ifindex; - - ifname = udev_device_get_sysname(udevice); - if (!ifname) { - _LOGD("udev-add: failed to get device's interface"); - return; - } - - ifindex_s = udev_device_get_property_value(udevice, "IFINDEX"); - if (!ifindex_s) { - _LOGW("udev-add[%s]failed to get device's ifindex", ifname); - return; - } - ifindex = _nm_utils_ascii_str_to_int64(ifindex_s, 10, 1, G_MAXINT, 0); - if (ifindex <= 0) { - _LOGW("udev-add[%s]: retrieved invalid IFINDEX=%d", ifname, ifindex); - return; - } - - if (!udev_device_get_syspath(udevice)) { - _LOGD("udev-add[%s,%d]: couldn't determine device path; ignoring...", ifname, ifindex); - return; - } - - _LOGT("udev-add[%s,%d]: device added", ifname, ifindex); - cache_update_link_udev(platform, ifindex, udevice); -} - -static gboolean -_udev_device_removed_match_link(const NMPObject *obj, gpointer udevice) -{ - return obj->_link.udev.device == udevice; -} - -static void -udev_device_removed(NMPlatform *platform, struct udev_device *udevice) -{ - const char *ifindex_s; - int ifindex = 0; - - ifindex_s = udev_device_get_property_value(udevice, "IFINDEX"); - ifindex = _nm_utils_ascii_str_to_int64(ifindex_s, 10, 1, G_MAXINT, 0); - if (ifindex <= 0) { - const NMPObject *obj; - - obj = nmp_cache_lookup_link_full(nm_platform_get_cache(platform), - 0, - NULL, - FALSE, - NM_LINK_TYPE_NONE, - _udev_device_removed_match_link, - udevice); - if (obj) - ifindex = obj->link.ifindex; - } - - _LOGD("udev-remove: IFINDEX=%d", ifindex); - if (ifindex <= 0) - return; - - cache_update_link_udev(platform, ifindex, NULL); -} - -static void -handle_udev_event(NMUdevClient *udev_client, struct udev_device *udevice, gpointer user_data) -{ - nm_auto_pop_netns NMPNetns *netns = NULL; - NMPlatform * platform = NM_PLATFORM(user_data); - const char * subsys; - const char * ifindex; - guint64 seqnum; - const char * action; - - action = udev_device_get_action(udevice); - g_return_if_fail(action); - - subsys = udev_device_get_subsystem(udevice); - g_return_if_fail(nm_streq0(subsys, "net")); - - if (!nm_platform_netns_push(platform, &netns)) - return; - - ifindex = udev_device_get_property_value(udevice, "IFINDEX"); - seqnum = udev_device_get_seqnum(udevice); - _LOGD("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT, - action, - subsys, - udev_device_get_sysname(udevice), - ifindex ?: "unknown", - seqnum); - - if (NM_IN_STRSET(action, "add", "move")) - udev_device_added(platform, udevice); - else if (NM_IN_STRSET(action, "remove")) - udev_device_removed(platform, udevice); -} - -/*****************************************************************************/ - -void -nm_linux_platform_setup(void) -{ - nm_platform_setup(nm_linux_platform_new(FALSE, FALSE)); -} - -/*****************************************************************************/ - -static void -nm_linux_platform_init(NMLinuxPlatform *self) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(self); - - priv->delayed_action.list_master_connected = g_ptr_array_new(); - priv->delayed_action.list_refresh_link = g_ptr_array_new(); - priv->delayed_action.list_wait_for_nl_response = - g_array_new(FALSE, TRUE, sizeof(DelayedActionWaitForNlResponseData)); -} - -static void -constructed(GObject *_object) -{ - NMPlatform * platform = NM_PLATFORM(_object); - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - int nle; - int fd; - - nm_assert(!platform->_netns || platform->_netns == nmp_netns_get_current()); - - if (nm_platform_get_use_udev(platform)) { - priv->udev_client = nm_udev_client_new(NM_MAKE_STRV("net"), handle_udev_event, platform); - } - - _LOGD("create (%s netns, %s, %s udev)", - !platform->_netns ? "ignore" : "use", - !platform->_netns && nmp_netns_is_initial() - ? "initial netns" - : (!nmp_netns_get_current() - ? "no netns support" - : nm_sprintf_bufa(100, - "in netns[%p]%s", - nmp_netns_get_current(), - nmp_netns_get_current() == nmp_netns_get_initial() ? "/main" - : "")), - nm_platform_get_use_udev(platform) ? "use" : "no"); - - priv->genl = nl_socket_alloc(); - g_assert(priv->genl); - - nle = nl_connect(priv->genl, NETLINK_GENERIC); - if (nle) { - _LOGE("unable to connect the generic netlink socket \"%s\" (%d)", nm_strerror(nle), -nle); - nl_socket_free(priv->genl); - priv->genl = NULL; - } - - priv->nlh = nl_socket_alloc(); - g_assert(priv->nlh); - - nle = nl_connect(priv->nlh, NETLINK_ROUTE); - g_assert(!nle); - nle = nl_socket_set_passcred(priv->nlh, 1); - g_assert(!nle); - - /* No blocking for event socket, so that we can drain it safely. */ - nle = nl_socket_set_nonblocking(priv->nlh); - g_assert(!nle); - - /* use 8 MB for receive socket kernel queue. */ - nle = nl_socket_set_buffer_size(priv->nlh, 8 * 1024 * 1024, 0); - g_assert(!nle); - - nle = nl_socket_set_ext_ack(priv->nlh, TRUE); - if (nle) - _LOGD("could not enable extended acks on netlink socket"); - - /* explicitly set the msg buffer size and disable MSG_PEEK. - * If we later encounter NME_NL_MSG_TRUNC, we will adjust the buffer size. */ - nl_socket_disable_msg_peek(priv->nlh); - nle = nl_socket_set_msg_buf_size(priv->nlh, 32 * 1024); - g_assert(!nle); - - nle = nl_socket_add_memberships(priv->nlh, - RTNLGRP_IPV4_IFADDR, - RTNLGRP_IPV4_ROUTE, - RTNLGRP_IPV4_RULE, - RTNLGRP_IPV6_RULE, - RTNLGRP_IPV6_IFADDR, - RTNLGRP_IPV6_ROUTE, - RTNLGRP_LINK, - RTNLGRP_TC, - 0); - g_assert(!nle); - - fd = nl_socket_get_fd(priv->nlh); - - _LOGD("Netlink socket for events established: port=%u, fd=%d", - nl_socket_get_local_port(priv->nlh), - fd); - - priv->event_source = - nm_g_unix_fd_source_new(fd, - G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP, - G_PRIORITY_DEFAULT, - event_handler, - platform, - NULL); - g_source_attach(priv->event_source, NULL); - - /* complete construction of the GObject instance before populating the cache. */ - G_OBJECT_CLASS(nm_linux_platform_parent_class)->constructed(_object); - - _LOGD("populate platform cache"); - delayed_action_schedule( - platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL - | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); - - delayed_action_handle_all(platform, FALSE); - - /* Set up udev monitoring */ - if (priv->udev_client) { - struct udev_enumerate * enumerator; - struct udev_list_entry *devices, *l; - - /* And read initial device list */ - enumerator = nm_udev_client_enumerate_new(priv->udev_client); - - udev_enumerate_add_match_is_initialized(enumerator); - - udev_enumerate_scan_devices(enumerator); - - devices = udev_enumerate_get_list_entry(enumerator); - for (l = devices; l; l = udev_list_entry_get_next(l)) { - struct udev_device *udevice; - - udevice = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerator), - udev_list_entry_get_name(l)); - if (!udevice) - continue; - - udev_device_added(platform, udevice); - udev_device_unref(udevice); - } - - udev_enumerate_unref(enumerator); - } -} - -/* Similar to systemd's path_is_read_only_fs(), at - * https://github.com/systemd/systemd/blob/v246/src/basic/stat-util.c#L132 */ -static int -path_is_read_only_fs(const char *path) -{ - struct statvfs st; - - if (statvfs(path, &st) < 0) - return -errno; - - if (st.f_flag & ST_RDONLY) - return TRUE; - - /* On NFS, statvfs() might not reflect whether we can actually - * write to the remote share. Let's try again with - * access(W_OK) which is more reliable, at least sometimes. */ - if (access(path, W_OK) < 0 && errno == EROFS) - return TRUE; - - return FALSE; -} - -NMPlatform * -nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support) -{ - gboolean use_udev = FALSE; - - if (nmp_netns_is_initial() && path_is_read_only_fs("/sys") == FALSE) - use_udev = TRUE; - - return g_object_new(NM_TYPE_LINUX_PLATFORM, - NM_PLATFORM_LOG_WITH_PTR, - log_with_ptr, - NM_PLATFORM_USE_UDEV, - use_udev, - NM_PLATFORM_NETNS_SUPPORT, - netns_support, - NULL); -} - -static void -dispose(GObject *object) -{ - NMPlatform * platform = NM_PLATFORM(object); - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - - _LOGD("dispose"); - - delayed_action_wait_for_nl_response_complete_all(platform, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); - - priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; - g_ptr_array_set_size(priv->delayed_action.list_master_connected, 0); - g_ptr_array_set_size(priv->delayed_action.list_refresh_link, 0); - - G_OBJECT_CLASS(nm_linux_platform_parent_class)->dispose(object); -} - -static void -finalize(GObject *object) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(object); - - g_ptr_array_unref(priv->delayed_action.list_master_connected); - g_ptr_array_unref(priv->delayed_action.list_refresh_link); - g_array_unref(priv->delayed_action.list_wait_for_nl_response); - - nl_socket_free(priv->genl); - - nm_clear_g_source_inst(&priv->event_source); - - nl_socket_free(priv->nlh); - - if (priv->sysctl_get_prev_values) { - sysctl_clear_cache_list = g_slist_remove(sysctl_clear_cache_list, object); - g_hash_table_destroy(priv->sysctl_get_prev_values); - } - - priv->udev_client = nm_udev_client_unref(priv->udev_client); - - G_OBJECT_CLASS(nm_linux_platform_parent_class)->finalize(object); -} - -static void -nm_linux_platform_class_init(NMLinuxPlatformClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMPlatformClass *platform_class = NM_PLATFORM_CLASS(klass); - - object_class->constructed = constructed; - object_class->dispose = dispose; - object_class->finalize = finalize; - - platform_class->sysctl_set = sysctl_set; - platform_class->sysctl_set_async = sysctl_set_async; - platform_class->sysctl_get = sysctl_get; - - platform_class->link_add = link_add; - platform_class->link_delete = link_delete; - - platform_class->link_refresh = link_refresh; - - platform_class->link_set_netns = link_set_netns; - - platform_class->link_set_up = link_set_up; - platform_class->link_set_down = link_set_down; - platform_class->link_set_arp = link_set_arp; - platform_class->link_set_noarp = link_set_noarp; - - platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; - platform_class->link_set_token = link_set_token; - - platform_class->link_set_address = link_set_address; - platform_class->link_get_permanent_address = link_get_permanent_address; - platform_class->link_set_mtu = link_set_mtu; - platform_class->link_set_name = link_set_name; - platform_class->link_set_sriov_params_async = link_set_sriov_params_async; - platform_class->link_set_sriov_vfs = link_set_sriov_vfs; - platform_class->link_set_bridge_vlans = link_set_bridge_vlans; - - platform_class->link_get_physical_port_id = link_get_physical_port_id; - platform_class->link_get_dev_id = link_get_dev_id; - platform_class->link_get_wake_on_lan = link_get_wake_on_lan; - platform_class->link_get_driver_info = link_get_driver_info; - - platform_class->link_supports_carrier_detect = link_supports_carrier_detect; - platform_class->link_supports_vlans = link_supports_vlans; - platform_class->link_supports_sriov = link_supports_sriov; - - platform_class->link_enslave = link_enslave; - platform_class->link_release = link_release; - - platform_class->link_can_assume = link_can_assume; - - platform_class->link_vlan_change = link_vlan_change; - platform_class->link_wireguard_change = link_wireguard_change; - - platform_class->infiniband_partition_add = infiniband_partition_add; - platform_class->infiniband_partition_delete = infiniband_partition_delete; - - platform_class->wifi_get_capabilities = wifi_get_capabilities; - platform_class->wifi_get_bssid = wifi_get_bssid; - platform_class->wifi_get_frequency = wifi_get_frequency; - platform_class->wifi_get_quality = wifi_get_quality; - platform_class->wifi_get_rate = wifi_get_rate; - platform_class->wifi_get_mode = wifi_get_mode; - platform_class->wifi_set_mode = wifi_set_mode; - platform_class->wifi_set_powersave = wifi_set_powersave; - platform_class->wifi_find_frequency = wifi_find_frequency; - platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; - platform_class->wifi_get_wake_on_wlan = wifi_get_wake_on_wlan; - platform_class->wifi_set_wake_on_wlan = wifi_set_wake_on_wlan; - - platform_class->mesh_get_channel = mesh_get_channel; - platform_class->mesh_set_channel = mesh_set_channel; - platform_class->mesh_set_ssid = mesh_set_ssid; - - platform_class->wpan_get_pan_id = wpan_get_pan_id; - platform_class->wpan_set_pan_id = wpan_set_pan_id; - platform_class->wpan_get_short_addr = wpan_get_short_addr; - platform_class->wpan_set_short_addr = wpan_set_short_addr; - platform_class->wpan_set_channel = wpan_set_channel; - - platform_class->link_tun_add = link_tun_add; - - platform_class->object_delete = object_delete; - platform_class->ip4_address_add = ip4_address_add; - platform_class->ip6_address_add = ip6_address_add; - platform_class->ip4_address_delete = ip4_address_delete; - platform_class->ip6_address_delete = ip6_address_delete; - - platform_class->ip_route_add = ip_route_add; - platform_class->ip_route_get = ip_route_get; - - platform_class->routing_rule_add = routing_rule_add; - - platform_class->qdisc_add = qdisc_add; - platform_class->tfilter_add = tfilter_add; - - platform_class->process_events = process_events; -} diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h deleted file mode 100644 index 98935b98..00000000 --- a/src/platform/nm-linux-platform.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2012 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_LINUX_PLATFORM_H__ -#define __NETWORKMANAGER_LINUX_PLATFORM_H__ - -#include "nm-platform.h" - -#define NM_TYPE_LINUX_PLATFORM (nm_linux_platform_get_type()) -#define NM_LINUX_PLATFORM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatform)) -#define NM_LINUX_PLATFORM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass)) -#define NM_IS_LINUX_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_LINUX_PLATFORM)) -#define NM_IS_LINUX_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_LINUX_PLATFORM)) -#define NM_LINUX_PLATFORM_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_LINUX_PLATFORM, NMLinuxPlatformClass)) - -typedef struct _NMLinuxPlatform NMLinuxPlatform; -typedef struct _NMLinuxPlatformClass NMLinuxPlatformClass; - -GType nm_linux_platform_get_type(void); - -NMPlatform *nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support); - -void nm_linux_platform_setup(void); - -#endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */ diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c deleted file mode 100644 index 965a73bc..00000000 --- a/src/platform/nm-netlink.c +++ /dev/null @@ -1,1518 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-netlink.h" - -#include <unistd.h> -#include <fcntl.h> - -/*****************************************************************************/ - -#ifndef SOL_NETLINK - #define SOL_NETLINK 270 -#endif - -/*****************************************************************************/ - -#define NL_SOCK_PASSCRED (1 << 1) -#define NL_MSG_PEEK (1 << 3) -#define NL_MSG_PEEK_EXPLICIT (1 << 4) -#define NL_NO_AUTO_ACK (1 << 5) - -#ifndef NETLINK_EXT_ACK - #define NETLINK_EXT_ACK 11 -#endif - -struct nl_msg { - int nm_protocol; - struct sockaddr_nl nm_src; - struct sockaddr_nl nm_dst; - struct ucred nm_creds; - struct nlmsghdr * nm_nlh; - size_t nm_size; - bool nm_creds_has : 1; -}; - -struct nl_sock { - struct sockaddr_nl s_local; - struct sockaddr_nl s_peer; - int s_fd; - int s_proto; - unsigned int s_seq_next; - unsigned int s_seq_expect; - int s_flags; - size_t s_bufsize; -}; - -/*****************************************************************************/ - -NM_UTILS_ENUM2STR_DEFINE(nl_nlmsgtype2str, - int, - NM_UTILS_ENUM2STR(NLMSG_NOOP, "NOOP"), - NM_UTILS_ENUM2STR(NLMSG_ERROR, "ERROR"), - NM_UTILS_ENUM2STR(NLMSG_DONE, "DONE"), - NM_UTILS_ENUM2STR(NLMSG_OVERRUN, "OVERRUN"), ); - -NM_UTILS_FLAGS2STR_DEFINE(nl_nlmsg_flags2str, - int, - NM_UTILS_FLAGS2STR(NLM_F_REQUEST, "REQUEST"), - NM_UTILS_FLAGS2STR(NLM_F_MULTI, "MULTI"), - NM_UTILS_FLAGS2STR(NLM_F_ACK, "ACK"), - NM_UTILS_FLAGS2STR(NLM_F_ECHO, "ECHO"), - NM_UTILS_FLAGS2STR(NLM_F_ROOT, "ROOT"), - NM_UTILS_FLAGS2STR(NLM_F_MATCH, "MATCH"), - NM_UTILS_FLAGS2STR(NLM_F_ATOMIC, "ATOMIC"), - NM_UTILS_FLAGS2STR(NLM_F_REPLACE, "REPLACE"), - NM_UTILS_FLAGS2STR(NLM_F_EXCL, "EXCL"), - NM_UTILS_FLAGS2STR(NLM_F_CREATE, "CREATE"), - NM_UTILS_FLAGS2STR(NLM_F_APPEND, "APPEND"), ); - -/*****************************************************************************/ - -const char * -nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len) -{ - const char *b; - const char *s; - guint flags, flags_before; - const char *prefix; - - if (!nm_utils_to_string_buffer_init_null(hdr, &buf, &len)) - return buf; - - b = buf; - - switch (hdr->nlmsg_type) { - case RTM_GETLINK: - s = "RTM_GETLINK"; - break; - case RTM_NEWLINK: - s = "RTM_NEWLINK"; - break; - case RTM_DELLINK: - s = "RTM_DELLINK"; - break; - case RTM_SETLINK: - s = "RTM_SETLINK"; - break; - case RTM_GETADDR: - s = "RTM_GETADDR"; - break; - case RTM_NEWADDR: - s = "RTM_NEWADDR"; - break; - case RTM_DELADDR: - s = "RTM_DELADDR"; - break; - case RTM_GETROUTE: - s = "RTM_GETROUTE"; - break; - case RTM_NEWROUTE: - s = "RTM_NEWROUTE"; - break; - case RTM_DELROUTE: - s = "RTM_DELROUTE"; - break; - case RTM_GETRULE: - s = "RTM_GETRULE"; - break; - case RTM_NEWRULE: - s = "RTM_NEWRULE"; - break; - case RTM_DELRULE: - s = "RTM_DELRULE"; - break; - case RTM_GETQDISC: - s = "RTM_GETQDISC"; - break; - case RTM_NEWQDISC: - s = "RTM_NEWQDISC"; - break; - case RTM_DELQDISC: - s = "RTM_DELQDISC"; - break; - case RTM_GETTFILTER: - s = "RTM_GETTFILTER"; - break; - case RTM_NEWTFILTER: - s = "RTM_NEWTFILTER"; - break; - case RTM_DELTFILTER: - s = "RTM_DELTFILTER"; - break; - case NLMSG_NOOP: - s = "NLMSG_NOOP"; - break; - case NLMSG_ERROR: - s = "NLMSG_ERROR"; - break; - case NLMSG_DONE: - s = "NLMSG_DONE"; - break; - case NLMSG_OVERRUN: - s = "NLMSG_OVERRUN"; - break; - default: - s = NULL; - break; - } - - if (s) - nm_utils_strbuf_append_str(&buf, &len, s); - else - nm_utils_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); - - flags = hdr->nlmsg_flags; - - if (!flags) { - nm_utils_strbuf_append_str(&buf, &len, ", flags 0"); - goto flags_done; - } - -#define _F(f, n) \ - G_STMT_START \ - { \ - if (NM_FLAGS_ALL(flags, f)) { \ - flags &= ~(f); \ - nm_utils_strbuf_append(&buf, &len, "%s%s", prefix, n); \ - if (!flags) \ - goto flags_done; \ - prefix = ","; \ - } \ - } \ - G_STMT_END - - prefix = ", flags "; - flags_before = flags; - _F(NLM_F_REQUEST, "request"); - _F(NLM_F_MULTI, "multi"); - _F(NLM_F_ACK, "ack"); - _F(NLM_F_ECHO, "echo"); - _F(NLM_F_DUMP_INTR, "dump_intr"); - _F(0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); - - if (flags_before != flags) - prefix = ";"; - - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - case RTM_NEWQDISC: - case RTM_NEWTFILTER: - _F(NLM_F_REPLACE, "replace"); - _F(NLM_F_EXCL, "excl"); - _F(NLM_F_CREATE, "create"); - _F(NLM_F_APPEND, "append"); - break; - case RTM_GETLINK: - case RTM_GETADDR: - case RTM_GETROUTE: - case RTM_DELQDISC: - case RTM_DELTFILTER: - _F(NLM_F_DUMP, "dump"); - _F(NLM_F_ROOT, "root"); - _F(NLM_F_MATCH, "match"); - _F(NLM_F_ATOMIC, "atomic"); - break; - } - -#undef _F - - if (flags_before != flags) - prefix = ";"; - nm_utils_strbuf_append(&buf, &len, "%s0x%04x", prefix, flags); - -flags_done: - - nm_utils_strbuf_append(&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); - - return b; -} - -/*****************************************************************************/ - -struct nlmsghdr * -nlmsg_hdr(struct nl_msg *n) -{ - return n->nm_nlh; -} - -void * -nlmsg_reserve(struct nl_msg *n, size_t len, int pad) -{ - char * buf = (char *) n->nm_nlh; - size_t nlmsg_len = n->nm_nlh->nlmsg_len; - size_t tlen; - - nm_assert(pad >= 0); - - if (len > n->nm_size) - return NULL; - - tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; - - if ((tlen + nlmsg_len) > n->nm_size) - return NULL; - - buf += nlmsg_len; - n->nm_nlh->nlmsg_len += tlen; - - if (tlen > len) - memset(buf + len, 0, tlen - len); - - return buf; -} - -/*****************************************************************************/ - -struct nlattr * -nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) -{ - struct nlattr *nla; - int tlen; - - if (attrlen < 0) - return NULL; - - tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); - - if (tlen > msg->nm_size) - return NULL; - - nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); - nla->nla_type = attrtype; - nla->nla_len = nla_attr_size(attrlen); - - if (attrlen) - memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); - msg->nm_nlh->nlmsg_len = tlen; - - return nla; -} - -/*****************************************************************************/ - -struct nl_msg * -nlmsg_alloc_size(size_t len) -{ - struct nl_msg *nm; - - if (len < sizeof(struct nlmsghdr)) - len = sizeof(struct nlmsghdr); - - nm = g_slice_new(struct nl_msg); - *nm = (struct nl_msg){ - .nm_protocol = -1, - .nm_size = len, - .nm_nlh = g_malloc0(len), - }; - nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); - return nm; -} - -/** - * Allocate a new netlink message with the default maximum payload size. - * - * Allocates a new netlink message without any further payload. The - * maximum payload size defaults to PAGESIZE or as otherwise specified - * with nlmsg_set_default_size(). - * - * @return Newly allocated netlink message or NULL. - */ -struct nl_msg * -nlmsg_alloc(void) -{ - return nlmsg_alloc_size(nm_utils_getpagesize()); -} - -struct nl_msg * -nlmsg_alloc_convert(struct nlmsghdr *hdr) -{ - struct nl_msg *nm; - - nm = nlmsg_alloc_size(NLMSG_ALIGN(hdr->nlmsg_len)); - memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); - return nm; -} - -struct nl_msg * -nlmsg_alloc_simple(int nlmsgtype, int flags) -{ - struct nl_msg *nm; - struct nlmsghdr *new; - - nm = nlmsg_alloc(); - new = nm->nm_nlh; - new->nlmsg_type = nlmsgtype; - new->nlmsg_flags = flags; - return nm; -} - -void -nlmsg_free(struct nl_msg *msg) -{ - if (!msg) - return; - - g_free(msg->nm_nlh); - g_slice_free(struct nl_msg, msg); -} - -/*****************************************************************************/ - -int -nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad) -{ - void *tmp; - - nm_assert(n); - nm_assert(data); - nm_assert(len > 0); - nm_assert(pad >= 0); - - tmp = nlmsg_reserve(n, len, pad); - if (tmp == NULL) - return -ENOMEM; - - memcpy(tmp, data, len); - return 0; -} - -/*****************************************************************************/ - -int -nlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy) -{ - if (!nlmsg_valid_hdr(nlh, hdrlen)) - return -NME_NL_MSG_TOOSHORT; - - return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), policy); -} - -struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags) -{ - struct nlmsghdr *nlh; - - if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) - g_return_val_if_reached(NULL); - - nlh = (struct nlmsghdr *) n->nm_nlh; - nlh->nlmsg_type = type; - nlh->nlmsg_flags = flags; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = seq; - - if (payload > 0 && nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) - return NULL; - - return nlh; -} - -size_t -nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) -{ - const char *src; - size_t srclen; - size_t len; - - /* - Always writes @dstsize bytes to @dst - * - Copies the first non-NUL characters to @dst. - * Any characters after the first NUL bytes in @nla are ignored. - * - If the string @nla is longer than @dstsize, the string - * gets truncated. @dst will always be NUL terminated. */ - - if (G_UNLIKELY(dstsize <= 1)) { - if (dstsize == 1) - dst[0] = '\0'; - if (nla && (srclen = nla_len(nla)) > 0) - return strnlen(nla_data(nla), srclen); - return 0; - } - - nm_assert(dst); - - if (nla) { - srclen = nla_len(nla); - if (srclen > 0) { - src = nla_data(nla); - srclen = strnlen(src, srclen); - if (srclen > 0) { - len = NM_MIN(dstsize - 1, srclen); - memcpy(dst, src, len); - memset(&dst[len], 0, dstsize - len); - return srclen; - } - } - } - - memset(dst, 0, dstsize); - return 0; -} - -size_t -nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize) -{ - size_t len; - int srclen; - - if (!nla) - return 0; - - srclen = nla_len(nla); - - if (srclen <= 0) { - nm_assert(srclen == 0); - return 0; - } - - len = NM_MIN((size_t) srclen, dstsize); - if (len > 0) { - /* there is a crucial difference between nla_strlcpy() and nla_memcpy(). - * The former always write @dstsize bytes (akin to strncpy()), here, we only - * write the bytes that we actually have (leaving the remainder undefined). */ - memcpy(dst, nla_data(nla), len); - } - - return srclen; -} - -int -nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) -{ - struct nlattr *nla; - - nla = nla_reserve(msg, attrtype, datalen); - if (!nla) { - if (datalen < 0) - g_return_val_if_reached(-NME_BUG); - - return -ENOMEM; - } - - if (datalen > 0) - memcpy(nla_data(nla), data, datalen); - - return 0; -} - -struct nlattr * -nla_find(const struct nlattr *head, int len, int attrtype) -{ - const struct nlattr *nla; - int rem; - - nla_for_each_attr (nla, head, len, rem) { - if (nla_type(nla) == attrtype) - return (struct nlattr *) nla; - } - - return NULL; -} - -void -nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr) -{ - ssize_t len; - - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; - if (len < 0) - g_return_if_reached(); - else if (len > 0) { - msg->nm_nlh->nlmsg_len -= len; - memset(nlmsg_tail(msg->nm_nlh), 0, len); - } -} - -struct nlattr * -nla_nest_start(struct nl_msg *msg, int attrtype) -{ - struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); - - if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) - return NULL; - - return start; -} - -static int -_nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) -{ - size_t pad, len; - - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; - - if (len > USHRT_MAX || (!keep_empty && len == NLA_HDRLEN)) { - /* - * Max nlattr size exceeded or empty nested attribute, trim the - * attribute header again - */ - nla_nest_cancel(msg, start); - - /* Return error only if nlattr size was exceeded */ - return (len == NLA_HDRLEN) ? 0 : -NME_NL_ATTRSIZE; - } - - start->nla_len = len; - - pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; - if (pad > 0) { - /* - * Data inside attribute does not end at a alignment boundary. - * Pad accordingly and account for the additional space in - * the message. nlmsg_reserve() may never fail in this situation, - * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. - */ - if (!nlmsg_reserve(msg, pad, 0)) - g_return_val_if_reached(-NME_BUG); - } - - return 0; -} - -int -nla_nest_end(struct nl_msg *msg, struct nlattr *start) -{ - return _nest_end(msg, start, 0); -} - -static const uint16_t nla_attr_minlen[NLA_TYPE_MAX + 1] = { - [NLA_U8] = sizeof(uint8_t), - [NLA_U16] = sizeof(uint16_t), - [NLA_U32] = sizeof(uint32_t), - [NLA_U64] = sizeof(uint64_t), - [NLA_STRING] = 1, - [NLA_FLAG] = 0, -}; - -static int -validate_nla(const struct nlattr *nla, int maxtype, const struct nla_policy *policy) -{ - const struct nla_policy *pt; - unsigned int minlen = 0; - int type = nla_type(nla); - - if (type < 0 || type > maxtype) - return 0; - - pt = &policy[type]; - - if (pt->type > NLA_TYPE_MAX) - g_return_val_if_reached(-NME_BUG); - - if (pt->minlen) - minlen = pt->minlen; - else if (pt->type != NLA_UNSPEC) - minlen = nla_attr_minlen[pt->type]; - - if (nla_len(nla) < minlen) - return -NME_UNSPEC; - - if (pt->maxlen && nla_len(nla) > pt->maxlen) - return -NME_UNSPEC; - - if (pt->type == NLA_STRING) { - const char *data; - - nm_assert(minlen > 0); - - data = nla_data(nla); - if (data[nla_len(nla) - 1] != '\0') - return -NME_UNSPEC; - } - - return 0; -} - -int -nla_parse(struct nlattr * tb[], - int maxtype, - struct nlattr * head, - int len, - const struct nla_policy *policy) -{ - struct nlattr *nla; - int rem, nmerr; - - memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); - - nla_for_each_attr (nla, head, len, rem) { - int type = nla_type(nla); - - if (type > maxtype) - continue; - - if (policy) { - nmerr = validate_nla(nla, maxtype, policy); - if (nmerr < 0) - return nmerr; - } - - tb[type] = nla; - } - - return 0; -} - -/*****************************************************************************/ - -int -nlmsg_get_proto(struct nl_msg *msg) -{ - return msg->nm_protocol; -} - -void -nlmsg_set_proto(struct nl_msg *msg, int protocol) -{ - msg->nm_protocol = protocol; -} - -void -nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) -{ - memcpy(&msg->nm_src, addr, sizeof(*addr)); -} - -struct ucred * -nlmsg_get_creds(struct nl_msg *msg) -{ - if (msg->nm_creds_has) - return &msg->nm_creds; - return NULL; -} - -void -nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) -{ - if (creds) { - memcpy(&msg->nm_creds, creds, sizeof(*creds)); - msg->nm_creds_has = TRUE; - } else - msg->nm_creds_has = FALSE; -} - -/*****************************************************************************/ - -void * -genlmsg_put(struct nl_msg *msg, - uint32_t port, - uint32_t seq, - int family, - int hdrlen, - int flags, - uint8_t cmd, - uint8_t version) -{ - struct nlmsghdr * nlh; - struct genlmsghdr hdr = { - .cmd = cmd, - .version = version, - }; - - nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); - if (nlh == NULL) - return NULL; - - memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); - - return (char *) nlmsg_data(nlh) + GENL_HDRLEN; -} - -void * -genlmsg_data(const struct genlmsghdr *gnlh) -{ - return ((unsigned char *) gnlh + GENL_HDRLEN); -} - -void * -genlmsg_user_hdr(const struct genlmsghdr *gnlh) -{ - return genlmsg_data(gnlh); -} - -struct genlmsghdr * -genlmsg_hdr(struct nlmsghdr *nlh) -{ - return nlmsg_data(nlh); -} - -void * -genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) -{ - return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); -} - -struct nlattr * -genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_user_data(gnlh, hdrlen); -} - -int -genlmsg_len(const struct genlmsghdr *gnlh) -{ - const struct nlmsghdr *nlh; - - nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); - return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); -} - -int -genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); -} - -int -genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) -{ - struct genlmsghdr *ghdr; - - if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN)) - return 0; - - ghdr = nlmsg_data(nlh); - if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen)) - return 0; - - return 1; -} - -int -genlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy) -{ - struct genlmsghdr *ghdr; - - if (!genlmsg_valid_hdr(nlh, hdrlen)) - return -NME_NL_MSG_TOOSHORT; - - ghdr = nlmsg_data(nlh); - return nla_parse(tb, - maxtype, - genlmsg_attrdata(ghdr, hdrlen), - genlmsg_attrlen(ghdr, hdrlen), - policy); -} - -static int -_genl_parse_getfamily(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy ctrl_policy[] = { - [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16}, - [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ}, - [CTRL_ATTR_VERSION] = {.type = NLA_U32}, - [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32}, - [CTRL_ATTR_MAXATTR] = {.type = NLA_U32}, - [CTRL_ATTR_OPS] = {.type = NLA_NESTED}, - [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED}, - }; - struct nlattr * tb[G_N_ELEMENTS(ctrl_policy)]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - gint32 * response_data = arg; - - if (genlmsg_parse_arr(nlh, 0, tb, ctrl_policy) < 0) - return NL_SKIP; - - if (tb[CTRL_ATTR_FAMILY_ID]) - *response_data = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); - - return NL_STOP; -} - -int -genl_ctrl_resolve(struct nl_sock *sk, const char *name) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - int nmerr; - gint32 response_data = -1; - const struct nl_cb cb = { - .valid_cb = _genl_parse_getfamily, - .valid_arg = &response_data, - }; - - msg = nlmsg_alloc(); - - if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1)) - return -ENOMEM; - - nmerr = nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name); - if (nmerr < 0) - return nmerr; - - nmerr = nl_send_auto(sk, msg); - if (nmerr < 0) - return nmerr; - - nmerr = nl_recvmsgs(sk, &cb); - if (nmerr < 0) - return nmerr; - - /* If search was successful, request may be ACKed after data */ - nmerr = nl_wait_for_ack(sk, NULL); - if (nmerr < 0) - return nmerr; - - if (response_data < 0) - return -NME_UNSPEC; - - return response_data; -} - -/*****************************************************************************/ - -struct nl_sock * -nl_socket_alloc(void) -{ - struct nl_sock *sk; - - sk = g_slice_new0(struct nl_sock); - - sk->s_fd = -1; - sk->s_local.nl_family = AF_NETLINK; - sk->s_peer.nl_family = AF_NETLINK; - sk->s_seq_expect = sk->s_seq_next = time(NULL); - - return sk; -} - -void -nl_socket_free(struct nl_sock *sk) -{ - if (!sk) - return; - - if (sk->s_fd >= 0) - nm_close(sk->s_fd); - g_slice_free(struct nl_sock, sk); -} - -int -nl_socket_get_fd(const struct nl_sock *sk) -{ - return sk->s_fd; -} - -uint32_t -nl_socket_get_local_port(const struct nl_sock *sk) -{ - return sk->s_local.nl_pid; -} - -size_t -nl_socket_get_msg_buf_size(struct nl_sock *sk) -{ - return sk->s_bufsize; -} - -int -nl_socket_set_passcred(struct nl_sock *sk, int state) -{ - int err; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state)); - if (err < 0) - return -nm_errno_from_native(errno); - - if (state) - sk->s_flags |= NL_SOCK_PASSCRED; - else - sk->s_flags &= ~NL_SOCK_PASSCRED; - - return 0; -} - -int -nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize) -{ - sk->s_bufsize = bufsize; - - return 0; -} - -struct sockaddr_nl * -nlmsg_get_dst(struct nl_msg *msg) -{ - return &msg->nm_dst; -} - -int -nl_socket_set_nonblocking(const struct nl_sock *sk) -{ - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) - return -nm_errno_from_native(errno); - - return 0; -} - -int -nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) -{ - int err; - - if (rxbuf <= 0) - rxbuf = 32768; - - if (txbuf <= 0) - txbuf = 32768; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof(txbuf)); - if (err < 0) { - return -nm_errno_from_native(errno); - } - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, sizeof(rxbuf)); - if (err < 0) { - return -nm_errno_from_native(errno); - } - - return 0; -} - -int -nl_socket_add_memberships(struct nl_sock *sk, int group, ...) -{ - int err; - va_list ap; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - va_start(ap, group); - - while (group != 0) { - if (group < 0) { - va_end(ap); - g_return_val_if_reached(-NME_BUG); - } - - err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); - if (err < 0) { - int errsv = errno; - - va_end(ap); - return -nm_errno_from_native(errsv); - } - - group = va_arg(ap, int); - } - - va_end(ap); - - return 0; -} - -int -nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable) -{ - int err, val; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - val = !!enable; - err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof(val)); - if (err < 0) - return -nm_errno_from_native(errno); - - return 0; -} - -void -nl_socket_disable_msg_peek(struct nl_sock *sk) -{ - sk->s_flags |= NL_MSG_PEEK_EXPLICIT; - sk->s_flags &= ~NL_MSG_PEEK; -} - -int -nl_connect(struct nl_sock *sk, int protocol) -{ - int err, nmerr; - socklen_t addrlen; - struct sockaddr_nl local = {0}; - - if (sk->s_fd != -1) - return -NME_NL_BAD_SOCK; - - sk->s_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); - if (sk->s_fd < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - nmerr = nl_socket_set_buffer_size(sk, 0, 0); - if (nmerr < 0) - goto errout; - - nm_assert(sk->s_local.nl_pid == 0); - - err = bind(sk->s_fd, (struct sockaddr *) &sk->s_local, sizeof(sk->s_local)); - if (err != 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - addrlen = sizeof(local); - err = getsockname(sk->s_fd, (struct sockaddr *) &local, &addrlen); - if (err < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - if (addrlen != sizeof(local)) { - nmerr = -NME_UNSPEC; - goto errout; - } - - if (local.nl_family != AF_NETLINK) { - nmerr = -NME_UNSPEC; - goto errout; - } - - sk->s_local = local; - sk->s_proto = protocol; - - return 0; - -errout: - if (sk->s_fd != -1) { - close(sk->s_fd); - sk->s_fd = -1; - } - return nmerr; -} - -/*****************************************************************************/ - -static void -_cb_init(struct nl_cb *dst, const struct nl_cb *src) -{ - nm_assert(dst); - - if (src) - *dst = *src; - else - memset(dst, 0, sizeof(*dst)); -} - -static int -ack_wait_handler(struct nl_msg *msg, void *arg) -{ - return NL_STOP; -} - -int -nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb) -{ - struct nl_cb cb2; - - _cb_init(&cb2, cb); - cb2.ack_cb = ack_wait_handler; - return nl_recvmsgs(sk, &cb2); -} - -#define NL_CB_CALL(cb, type, msg) \ - do { \ - const struct nl_cb *_cb = (cb); \ - \ - if (_cb && _cb->type##_cb) { \ - /* the returned value here must be either a negative - * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ \ - nmerr = _cb->type##_cb((msg), _cb->type##_arg); \ - switch (nmerr) { \ - case NL_OK: \ - nm_assert(nmerr == 0); \ - break; \ - case NL_SKIP: \ - goto skip; \ - case NL_STOP: \ - goto stop; \ - default: \ - if (nmerr >= 0) { \ - nm_assert_not_reached(); \ - nmerr = -NME_BUG; \ - } \ - goto out; \ - } \ - } \ - } while (0) - -int -nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb) -{ - int n, nmerr = 0, multipart = 0, interrupted = 0, nrecv = 0; - gs_free unsigned char *buf = NULL; - struct nlmsghdr * hdr; - struct sockaddr_nl nla = {0}; - struct ucred creds; - gboolean creds_has; - -continue_reading: - n = nl_recv(sk, &nla, &buf, &creds, &creds_has); - if (n <= 0) - return n; - - hdr = (struct nlmsghdr *) buf; - while (nlmsg_ok(hdr, n)) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - - msg = nlmsg_alloc_convert(hdr); - - nlmsg_set_proto(msg, sk->s_proto); - nlmsg_set_src(msg, &nla); - nlmsg_set_creds(msg, creds_has ? &creds : NULL); - - nrecv++; - - /* Only do sequence checking if auto-ack mode is enabled */ - if (!(sk->s_flags & NL_NO_AUTO_ACK)) { - if (hdr->nlmsg_seq != sk->s_seq_expect) { - nmerr = -NME_NL_SEQ_MISMATCH; - goto out; - } - } - - if (hdr->nlmsg_type == NLMSG_DONE || hdr->nlmsg_type == NLMSG_ERROR - || hdr->nlmsg_type == NLMSG_NOOP || hdr->nlmsg_type == NLMSG_OVERRUN) { - /* We can't check for !NLM_F_MULTI since some netlink - * users in the kernel are broken. */ - sk->s_seq_expect++; - } - - if (hdr->nlmsg_flags & NLM_F_MULTI) - multipart = 1; - - if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { - /* - * We have to continue reading to clear - * all messages until a NLMSG_DONE is - * received and report the inconsistency. - */ - interrupted = 1; - } - - /* messages terminates a multipart message, this is - * usually the end of a message and therefore we slip - * out of the loop by default. the user may overrule - * this action by skipping this packet. */ - if (hdr->nlmsg_type == NLMSG_DONE) { - multipart = 0; - NL_CB_CALL(cb, finish, msg); - } - - /* Message to be ignored, the default action is to - * skip this message if no callback is specified. The - * user may overrule this action by returning - * NL_PROCEED. */ - else if (hdr->nlmsg_type == NLMSG_NOOP) - goto skip; - - /* Data got lost, report back to user. The default action is to - * quit parsing. The user may overrule this action by returning - * NL_SKIP or NL_PROCEED (dangerous) */ - else if (hdr->nlmsg_type == NLMSG_OVERRUN) { - nmerr = -NME_NL_MSG_OVERFLOW; - goto out; - } - - /* Message carries a nlmsgerr */ - else if (hdr->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *e = nlmsg_data(hdr); - - if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { - /* Truncated error message, the default action - * is to stop parsing. The user may overrule - * this action by returning NL_SKIP or - * NL_PROCEED (dangerous) */ - nmerr = -NME_NL_MSG_TRUNC; - goto out; - } - if (e->error) { - /* Error message reported back from kernel. */ - if (cb && cb->err_cb) { - /* the returned value here must be either a negative - * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ - nmerr = cb->err_cb(&nla, e, cb->err_arg); - if (nmerr < 0) - goto out; - else if (nmerr == NL_SKIP) - goto skip; - else if (nmerr == NL_STOP) { - nmerr = -nm_errno_from_native(e->error); - goto out; - } - nm_assert(nmerr == NL_OK); - } else { - nmerr = -nm_errno_from_native(e->error); - goto out; - } - } else - NL_CB_CALL(cb, ack, msg); - } else { - /* Valid message (not checking for MULTIPART bit to - * get along with broken kernels. NL_SKIP has no - * effect on this. */ - NL_CB_CALL(cb, valid, msg); - } -skip: - nmerr = 0; - hdr = nlmsg_next(hdr, &n); - } - - if (multipart) { - /* Multipart message not yet complete, continue reading */ - nm_clear_g_free(&buf); - - nmerr = 0; - goto continue_reading; - } - -stop: - nmerr = 0; - -out: - if (interrupted) - nmerr = -NME_NL_DUMP_INTR; - - nm_assert(nmerr <= 0); - return nmerr ?: nrecv; -} - -int -nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) -{ - int ret; - - if (sk->s_fd < 0) - return -NME_NL_BAD_SOCK; - - nlmsg_set_src(msg, &sk->s_local); - - ret = sendmsg(sk->s_fd, hdr, 0); - if (ret < 0) - return -nm_errno_from_native(errno); - - return ret; -} - -int -nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) -{ - struct sockaddr_nl *dst; - struct ucred * creds; - struct msghdr hdr = { - .msg_name = (void *) &sk->s_peer, - .msg_namelen = sizeof(struct sockaddr_nl), - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - char buf[CMSG_SPACE(sizeof(struct ucred))]; - - /* Overwrite destination if specified in the message itself, defaults - * to the peer address of the socket. - */ - dst = nlmsg_get_dst(msg); - if (dst->nl_family == AF_NETLINK) - hdr.msg_name = dst; - - /* Add credentials if present. */ - creds = nlmsg_get_creds(msg); - if (creds != NULL) { - struct cmsghdr *cmsg; - - hdr.msg_control = buf; - hdr.msg_controllen = sizeof(buf); - - cmsg = CMSG_FIRSTHDR(&hdr); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); - } - - return nl_sendmsg(sk, msg, &hdr); -} - -void -nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) -{ - struct nlmsghdr *nlh; - - nlh = nlmsg_hdr(msg); - if (nlh->nlmsg_pid == NL_AUTO_PORT) - nlh->nlmsg_pid = nl_socket_get_local_port(sk); - - if (nlh->nlmsg_seq == NL_AUTO_SEQ) - nlh->nlmsg_seq = sk->s_seq_next++; - - if (msg->nm_protocol == -1) - msg->nm_protocol = sk->s_proto; - - nlh->nlmsg_flags |= NLM_F_REQUEST; - - if (!(sk->s_flags & NL_NO_AUTO_ACK)) - nlh->nlmsg_flags |= NLM_F_ACK; -} - -int -nl_send(struct nl_sock *sk, struct nl_msg *msg) -{ - struct iovec iov = { - .iov_base = (void *) nlmsg_hdr(msg), - .iov_len = nlmsg_hdr(msg)->nlmsg_len, - }; - - return nl_send_iovec(sk, msg, &iov, 1); -} - -int -nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) -{ - nl_complete_msg(sk, msg); - - return nl_send(sk, msg); -} - -int -nl_recv(struct nl_sock * sk, - struct sockaddr_nl *nla, - unsigned char ** buf, - struct ucred * out_creds, - gboolean * out_creds_has) -{ - ssize_t n; - int flags = 0; - struct iovec iov; - struct msghdr msg = { - .msg_name = (void *) nla, - .msg_namelen = sizeof(struct sockaddr_nl), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - struct ucred tmpcreds; - gboolean tmpcreds_has = FALSE; - int retval; - int errsv; - - nm_assert(nla); - nm_assert(buf && !*buf); - nm_assert(!out_creds_has == !out_creds); - - if ((sk->s_flags & NL_MSG_PEEK) - || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) - flags |= MSG_PEEK | MSG_TRUNC; - - iov.iov_len = sk->s_bufsize ?: (((size_t) nm_utils_getpagesize()) * 4u); - iov.iov_base = g_malloc(iov.iov_len); - - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { - msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); - msg.msg_control = g_malloc(msg.msg_controllen); - } - -retry: - n = recvmsg(sk->s_fd, &msg, flags); - if (!n) { - retval = 0; - goto abort; - } - - if (n < 0) { - errsv = errno; - if (errsv == EINTR) - goto retry; - retval = -nm_errno_from_native(errsv); - goto abort; - } - - if (msg.msg_flags & MSG_CTRUNC) { - if (msg.msg_controllen == 0) { - retval = -NME_NL_MSG_TRUNC; - goto abort; - } - - msg.msg_controllen *= 2; - msg.msg_control = g_realloc(msg.msg_control, msg.msg_controllen); - goto retry; - } - - if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) { - /* respond with error to an incomplete message */ - if (flags == 0) { - retval = -NME_NL_MSG_TRUNC; - goto abort; - } - - /* Provided buffer is not long enough, enlarge it - * to size of n (which should be total length of the message) - * and try again. */ - iov.iov_base = g_realloc(iov.iov_base, n); - iov.iov_len = n; - flags = 0; - goto retry; - } - - if (flags != 0) { - /* Buffer is big enough, do the actual reading */ - flags = 0; - goto retry; - } - - if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { - retval = -NME_UNSPEC; - goto abort; - } - - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { - struct cmsghdr *cmsg; - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level != SOL_SOCKET) - continue; - if (cmsg->cmsg_type != SCM_CREDENTIALS) - continue; - memcpy(&tmpcreds, CMSG_DATA(cmsg), sizeof(tmpcreds)); - tmpcreds_has = TRUE; - break; - } - } - - retval = n; - -abort: - g_free(msg.msg_control); - - if (retval <= 0) { - g_free(iov.iov_base); - return retval; - } - - *buf = iov.iov_base; - if (out_creds && tmpcreds_has) - *out_creds = tmpcreds; - NM_SET_OUT(out_creds_has, tmpcreds_has); - return retval; -} diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h deleted file mode 100644 index 52283e9f..00000000 --- a/src/platform/nm-netlink.h +++ /dev/null @@ -1,616 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_NETLINK_H__ -#define __NM_NETLINK_H__ - -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <linux/genetlink.h> - -#include "nm-std-aux/unaligned.h" - -/*****************************************************************************/ - -#define NLMSGERR_ATTR_UNUSED 0 -#define NLMSGERR_ATTR_MSG 1 -#define NLMSGERR_ATTR_OFFS 2 -#define NLMSGERR_ATTR_COOKIE 3 -#define NLMSGERR_ATTR_MAX 3 - -#ifndef NLM_F_ACK_TLVS - #define NLM_F_ACK_TLVS 0x200 -#endif - -/*****************************************************************************/ - -/* Basic attribute data types */ -enum { - NLA_UNSPEC, /* Unspecified type, binary data chunk */ - NLA_U8, /* 8 bit integer */ - NLA_U16, /* 16 bit integer */ - NLA_U32, /* 32 bit integer */ - NLA_U64, /* 64 bit integer */ - NLA_STRING, /* NUL terminated character string */ - NLA_FLAG, /* Flag */ - NLA_MSECS, /* Micro seconds (64bit) */ - NLA_NESTED, /* Nested attributes */ - NLA_NESTED_COMPAT, - NLA_NUL_STRING, - NLA_BINARY, - NLA_S8, - NLA_S16, - NLA_S32, - NLA_S64, - __NLA_TYPE_MAX, -}; - -#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) - -struct nl_msg; - -/*****************************************************************************/ - -const char *nl_nlmsgtype2str(int type, char *buf, size_t size); - -const char *nl_nlmsg_flags2str(int flags, char *buf, size_t len); - -const char *nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len); - -/*****************************************************************************/ - -struct nla_policy { - /* Type of attribute or NLA_UNSPEC */ - uint16_t type; - - /* Minimal length of payload required */ - uint16_t minlen; - - /* Maximal length of payload allowed */ - uint16_t maxlen; -}; - -/*****************************************************************************/ - -/* static asserts that @tb and @policy are suitable arguments to nla_parse(). */ -#define _nl_static_assert_tb(tb, policy) \ - G_STMT_START \ - { \ - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(tb) > 0); \ - \ - /* We allow @policy to be either a C array or NULL. The sizeof() - * must either match the expected array size or the sizeof(NULL), - * but not both. */ \ - G_STATIC_ASSERT_EXPR((sizeof(policy) == G_N_ELEMENTS(tb) * sizeof(struct nla_policy)) \ - ^ (sizeof(policy) == sizeof(NULL))); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static inline int -nla_attr_size(int payload) -{ - nm_assert(payload >= 0); - - return NLA_HDRLEN + payload; -} - -static inline int -nla_total_size(int payload) -{ - return NLA_ALIGN(nla_attr_size(payload)); -} - -static inline int -nla_padlen(int payload) -{ - return nla_total_size(payload) - nla_attr_size(payload); -} - -struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen); - -static inline int -nla_len(const struct nlattr *nla) -{ - nm_assert(nla); - nm_assert(nla->nla_len >= NLA_HDRLEN); - - return ((int) nla->nla_len) - NLA_HDRLEN; -} - -static inline int -nla_type(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return nla->nla_type & NLA_TYPE_MASK; -} - -static inline void * -nla_data(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return &(((char *) nla)[NLA_HDRLEN]); -} - -#define nla_data_as(type, nla) \ - ({ \ - const struct nlattr *_nla = (nla); \ - \ - nm_assert(nla_len(_nla) >= sizeof(type)); \ - \ - /* note that casting the pointer is undefined behavior in C, if - * the data has wrong alignment. Netlink data is aligned to 4 bytes, - * that means, if the alignment is larger than 4, this is invalid. */ \ - G_STATIC_ASSERT_EXPR(_nm_alignof(type) <= NLA_ALIGNTO); \ - \ - (type *) nla_data(_nla); \ - }) - -static inline uint8_t -nla_get_u8(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint8_t)); - - return *((const uint8_t *) nla_data(nla)); -} - -static inline int8_t -nla_get_s8(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(int8_t)); - - return *((const int8_t *) nla_data(nla)); -} - -static inline uint8_t -nla_get_u8_cond(/*const*/ struct nlattr *const *tb, int attr, uint8_t default_val) -{ - nm_assert(tb); - nm_assert(attr >= 0); - - return tb[attr] ? nla_get_u8(tb[attr]) : default_val; -} - -static inline uint16_t -nla_get_u16(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint16_t)); - - return *((const uint16_t *) nla_data(nla)); -} - -static inline uint32_t -nla_get_u32(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint32_t)); - - return *((const uint32_t *) nla_data(nla)); -} - -static inline int32_t -nla_get_s32(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(int32_t)); - - return *((const int32_t *) nla_data(nla)); -} - -static inline uint64_t -nla_get_u64(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint64_t)); - - return unaligned_read_ne64(nla_data(nla)); -} - -static inline uint64_t -nla_get_be64(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint64_t)); - - return unaligned_read_be64(nla_data(nla)); -} - -static inline char * -nla_get_string(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return (char *) nla_data(nla); -} - -size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); - -size_t nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize); - -#define nla_memcpy_checked_size(dst, nla, dstsize) \ - G_STMT_START \ - { \ - void *const _dst = (dst); \ - const struct nlattr *const _nla = (nla); \ - const size_t _dstsize = (dstsize); \ - size_t _srcsize; \ - \ - /* assert that, if @nla is given, that it has the exact expected - * size. This implies that the caller previously verified the length - * of the attribute (via minlen/maxlen at nla_parse()). */ \ - \ - if (_nla) { \ - _srcsize = nla_memcpy(_dst, _nla, _dstsize); \ - nm_assert(_srcsize == _dstsize); \ - } \ - } \ - G_STMT_END - -int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data); - -static inline int -nla_put_string(struct nl_msg *msg, int attrtype, const char *str) -{ - nm_assert(str); - - return nla_put(msg, attrtype, strlen(str) + 1, str); -} - -static inline int -nla_put_uint8(struct nl_msg *msg, int attrtype, uint8_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -static inline int -nla_put_uint16(struct nl_msg *msg, int attrtype, uint16_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -static inline int -nla_put_uint32(struct nl_msg *msg, int attrtype, uint32_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -#define NLA_PUT(msg, attrtype, attrlen, data) \ - G_STMT_START \ - { \ - if (nla_put(msg, attrtype, attrlen, data) < 0) \ - goto nla_put_failure; \ - } \ - G_STMT_END - -#define NLA_PUT_TYPE(msg, type, attrtype, value) \ - G_STMT_START \ - { \ - type __nla_tmp = value; \ - NLA_PUT(msg, attrtype, sizeof(type), &__nla_tmp); \ - } \ - G_STMT_END - -#define NLA_PUT_U8(msg, attrtype, value) NLA_PUT_TYPE(msg, uint8_t, attrtype, value) - -#define NLA_PUT_S8(msg, attrtype, value) NLA_PUT_TYPE(msg, int8_t, attrtype, value) - -#define NLA_PUT_U16(msg, attrtype, value) NLA_PUT_TYPE(msg, uint16_t, attrtype, value) - -#define NLA_PUT_U32(msg, attrtype, value) NLA_PUT_TYPE(msg, uint32_t, attrtype, value) - -#define NLA_PUT_S32(msg, attrtype, value) NLA_PUT_TYPE(msg, int32_t, attrtype, value) - -#define NLA_PUT_U64(msg, attrtype, value) NLA_PUT_TYPE(msg, uint64_t, attrtype, value) - -#define NLA_PUT_STRING(msg, attrtype, value) NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) - -#define NLA_PUT_FLAG(msg, attrtype) NLA_PUT(msg, attrtype, 0, NULL) - -struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype); - -static inline int -nla_ok(const struct nlattr *nla, int remaining) -{ - return remaining >= (int) sizeof(*nla) && nla->nla_len >= sizeof(*nla) - && nla->nla_len <= remaining; -} - -static inline struct nlattr * -nla_next(const struct nlattr *nla, int *remaining) -{ - int totlen = NLA_ALIGN(nla->nla_len); - - *remaining -= totlen; - return (struct nlattr *) ((char *) nla + totlen); -} - -#define nla_for_each_attr(pos, head, len, rem) \ - for (pos = head, rem = len; nla_ok(pos, rem); pos = nla_next(pos, &(rem))) - -#define nla_for_each_nested(pos, nla, rem) \ - for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); nla_ok(pos, rem); \ - pos = nla_next(pos, &(rem))) - -void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr); -struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype); -int nla_nest_end(struct nl_msg *msg, struct nlattr *start); - -int nla_parse(struct nlattr * tb[], - int maxtype, - struct nlattr * head, - int len, - const struct nla_policy *policy); - -#define nla_parse_arr(tb, head, len, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - \ - nla_parse((tb), G_N_ELEMENTS(tb) - 1, (head), (len), (policy)); \ - }) - -static inline int -nla_parse_nested(struct nlattr * tb[], - int maxtype, - struct nlattr * nla, - const struct nla_policy *policy) -{ - return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); -} - -#define nla_parse_nested_arr(tb, nla, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - \ - nla_parse_nested((tb), G_N_ELEMENTS(tb) - 1, (nla), (policy)); \ - }) - -/*****************************************************************************/ - -struct nl_msg *nlmsg_alloc(void); - -struct nl_msg *nlmsg_alloc_size(size_t max); - -struct nl_msg *nlmsg_alloc_convert(struct nlmsghdr *hdr); - -struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags); - -void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad); - -int nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad); - -#define nlmsg_append_struct(n, data) nlmsg_append(n, (data), sizeof(*(data)), NLMSG_ALIGNTO) - -void nlmsg_free(struct nl_msg *msg); - -static inline int -nlmsg_size(int payload) -{ - nm_assert(payload >= 0 && payload < G_MAXINT - NLMSG_HDRLEN - 4); - return NLMSG_HDRLEN + payload; -} - -static inline int -nlmsg_total_size(int payload) -{ - return NLMSG_ALIGN(nlmsg_size(payload)); -} - -static inline int -nlmsg_ok(const struct nlmsghdr *nlh, int remaining) -{ - return (remaining >= (int) sizeof(struct nlmsghdr) && nlh->nlmsg_len >= sizeof(struct nlmsghdr) - && nlh->nlmsg_len <= remaining); -} - -static inline struct nlmsghdr * -nlmsg_next(struct nlmsghdr *nlh, int *remaining) -{ - int totlen = NLMSG_ALIGN(nlh->nlmsg_len); - - *remaining -= totlen; - - return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); -} - -int nlmsg_get_proto(struct nl_msg *msg); -void nlmsg_set_proto(struct nl_msg *msg, int protocol); - -void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr); - -struct ucred *nlmsg_get_creds(struct nl_msg *msg); -void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds); - -static inline void -_nm_auto_nl_msg_cleanup(struct nl_msg **ptr) -{ - nlmsg_free(*ptr); -} -#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) - -static inline void * -nlmsg_data(const struct nlmsghdr *nlh) -{ - return (unsigned char *) nlh + NLMSG_HDRLEN; -} - -static inline void * -nlmsg_tail(const struct nlmsghdr *nlh) -{ - return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); -} - -struct nlmsghdr *nlmsg_hdr(struct nl_msg *n); - -static inline int -nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) -{ - if (nlh->nlmsg_len < nlmsg_size(hdrlen)) - return 0; - - return 1; -} - -static inline int -nlmsg_datalen(const struct nlmsghdr *nlh) -{ - return nlh->nlmsg_len - NLMSG_HDRLEN; -} - -static inline int -nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) -{ - return NM_MAX((int) (nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen)), 0); -} - -static inline struct nlattr * -nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) -{ - unsigned char *data = nlmsg_data(nlh); - return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); -} - -static inline struct nlattr * -nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) -{ - return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype); -} - -int nlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy); - -#define nlmsg_parse_arr(nlh, hdrlen, tb, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ - \ - nlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ - }) - -struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags); - -/*****************************************************************************/ - -#define NL_AUTO_PORT 0 -#define NL_AUTO_SEQ 0 - -struct nl_sock; - -struct nl_sock *nl_socket_alloc(void); - -void nl_socket_free(struct nl_sock *sk); - -int nl_socket_get_fd(const struct nl_sock *sk); - -struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg); - -size_t nl_socket_get_msg_buf_size(struct nl_sock *sk); -int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize); - -int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf); - -int nl_socket_set_passcred(struct nl_sock *sk, int state); - -int nl_socket_set_nonblocking(const struct nl_sock *sk); - -void nl_socket_disable_msg_peek(struct nl_sock *sk); - -uint32_t nl_socket_get_local_port(const struct nl_sock *sk); - -int nl_socket_add_memberships(struct nl_sock *sk, int group, ...); - -int nl_connect(struct nl_sock *sk, int protocol); - -int nl_recv(struct nl_sock * sk, - struct sockaddr_nl *nla, - unsigned char ** buf, - struct ucred * out_creds, - gboolean * out_creds_has); - -int nl_send(struct nl_sock *sk, struct nl_msg *msg); - -int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg); - -/*****************************************************************************/ - -enum nl_cb_action { - /* Proceed with wathever would come next */ - NL_OK, - /* Skip this message */ - NL_SKIP, - /* Stop parsing altogether and discard remaining messages */ - NL_STOP, -}; - -typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); - -typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); - -struct nl_cb { - nl_recvmsg_msg_cb_t valid_cb; - void * valid_arg; - - nl_recvmsg_msg_cb_t finish_cb; - void * finish_arg; - - nl_recvmsg_msg_cb_t ack_cb; - void * ack_arg; - - nl_recvmsg_err_cb_t err_cb; - void * err_arg; -}; - -int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr); - -int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen); - -void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg); - -int nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb); - -int nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb); - -int nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable); - -/*****************************************************************************/ - -void * genlmsg_put(struct nl_msg *msg, - uint32_t port, - uint32_t seq, - int family, - int hdrlen, - int flags, - uint8_t cmd, - uint8_t version); -void * genlmsg_data(const struct genlmsghdr *gnlh); -void * genlmsg_user_hdr(const struct genlmsghdr *gnlh); -struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh); -void * genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen); -struct nlattr * genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_len(const struct genlmsghdr *gnlh); -int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen); - -int genlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy); - -#define genlmsg_parse_arr(nlh, hdrlen, tb, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ - \ - genlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ - }) - -int genl_ctrl_resolve(struct nl_sock *sk, const char *name); - -/*****************************************************************************/ - -#endif /* __NM_NETLINK_H__ */ diff --git a/src/platform/nm-platform-private.h b/src/platform/nm-platform-private.h deleted file mode 100644 index d99e330b..00000000 --- a/src/platform/nm-platform-private.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2017 Red Hat, Inc. - */ - -#ifndef __NM_PLATFORM_PRIVATE_H__ -#define __NM_PLATFORM_PRIVATE_H__ - -#include "nm-platform.h" -#include "nmp-object.h" - -NMPCache *nm_platform_get_cache(NMPlatform *self); - -#define NMTST_ASSERT_PLATFORM_NETNS_CURRENT(platform) \ - G_STMT_START \ - { \ - NMPlatform *_platform = (platform); \ - \ - nm_assert(NM_IS_PLATFORM(_platform)); \ - nm_assert(NM_IN_SET(nm_platform_netns_get(_platform), NULL, nmp_netns_get_current())); \ - } \ - G_STMT_END - -void nm_platform_cache_update_emit_signal(NMPlatform * platform, - NMPCacheOpsType cache_op, - const NMPObject *obj_old, - const NMPObject *obj_new); - -#endif /* __NM_PLATFORM_PRIVATE_H__ */ diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c deleted file mode 100644 index ea45050d..00000000 --- a/src/platform/nm-platform-utils.c +++ /dev/null @@ -1,1785 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-platform-utils.h" - -#include <unistd.h> -#include <sys/ioctl.h> -#include <linux/ethtool.h> -#include <linux/sockios.h> -#include <linux/mii.h> -#include <linux/if.h> -#include <linux/version.h> -#include <linux/rtnetlink.h> -#include <fcntl.h> -#include <libudev.h> - -#include "nm-utils.h" -#include "nm-setting-wired.h" -#include "nm-libnm-core-intern/nm-ethtool-utils.h" - -#include "nm-core-utils.h" - -#define ONOFF(bool_val) ((bool_val) ? "on" : "off") - -/****************************************************************************** - * utils - *****************************************************************************/ - -extern char *if_indextoname(unsigned __ifindex, char *__ifname); -unsigned if_nametoindex(const char *__ifname); - -const char * -nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/) -{ - g_return_val_if_fail(ifindex > 0, NULL); - g_return_val_if_fail(out_ifname, NULL); - - return if_indextoname(ifindex, out_ifname); -} - -int -nmp_utils_if_nametoindex(const char *ifname) -{ - g_return_val_if_fail(ifname, 0); - - return if_nametoindex(ifname); -} - -/*****************************************************************************/ - -typedef struct { - int fd; - const int ifindex; - char ifname[IFNAMSIZ]; -} SocketHandle; - -#define SOCKET_HANDLE_INIT(_ifindex) \ - { \ - .fd = -1, .ifindex = (_ifindex), \ - } - -static void -_nm_auto_socket_handle(SocketHandle *shandle) -{ - if (shandle->fd >= 0) - nm_close(shandle->fd); -} - -#define nm_auto_socket_handle nm_auto(_nm_auto_socket_handle) - -/*****************************************************************************/ - -typedef enum { - IOCTL_CALL_DATA_TYPE_NONE, - IOCTL_CALL_DATA_TYPE_IFRDATA, - IOCTL_CALL_DATA_TYPE_IFRU, -} IoctlCallDataType; - -static int -_ioctl_call(const char * log_ioctl_type, - const char * log_subtype, - unsigned long int ioctl_request, - int ifindex, - int * inout_fd, - char * inout_ifname, - IoctlCallDataType edata_type, - gpointer edata, - gsize edata_size, - struct ifreq * out_ifreq) -{ - nm_auto_close int fd_close = -1; - int fd; - int r; - gpointer edata_backup = NULL; - gs_free gpointer edata_backup_free = NULL; - guint try_count; - char known_ifnames[2][IFNAMSIZ]; - const char * failure_reason = NULL; - struct ifreq ifr; - - nm_assert(ifindex > 0); - nm_assert(NM_IN_SET(edata_type, - IOCTL_CALL_DATA_TYPE_NONE, - IOCTL_CALL_DATA_TYPE_IFRDATA, - IOCTL_CALL_DATA_TYPE_IFRU)); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_NONE || edata_size == 0); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRDATA || edata_size > 0); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRU - || (edata_size > 0 && edata_size <= sizeof(ifr.ifr_ifru))); - nm_assert(edata_size == 0 || edata); - - /* open a file descriptor (or use the one provided). */ - if (inout_fd && *inout_fd >= 0) - fd = *inout_fd; - else { - fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - r = -NM_ERRNO_NATIVE(errno); - failure_reason = "failed creating socket or ioctl"; - goto out; - } - if (inout_fd) - *inout_fd = fd; - else - fd_close = fd; - } - - /* resolve the ifindex to name (or use the one provided). */ - if (inout_ifname && inout_ifname[0]) - nm_utils_ifname_cpy(known_ifnames[0], inout_ifname); - else { - if (!nmp_utils_if_indextoname(ifindex, known_ifnames[0])) { - failure_reason = "cannot resolve ifindex"; - r = -ENODEV; - goto out; - } - if (inout_ifname) - nm_utils_ifname_cpy(inout_ifname, known_ifnames[0]); - } - - /* we might need to retry the request. Backup edata so that we can - * restore it on retry. */ - if (edata_size > 0) - edata_backup = nm_memdup_maybe_a(500, edata, edata_size, &edata_backup_free); - - try_count = 0; - -again: -{ - const char *ifname = known_ifnames[try_count % 2]; - - nm_assert(ifindex > 0); - nm_assert(ifname && nm_utils_ifname_valid_kernel(ifname, NULL)); - nm_assert(fd >= 0); - - memset(&ifr, 0, sizeof(ifr)); - nm_utils_ifname_cpy(ifr.ifr_name, ifname); - if (edata_type == IOCTL_CALL_DATA_TYPE_IFRDATA) - ifr.ifr_data = edata; - else if (edata_type == IOCTL_CALL_DATA_TYPE_IFRU) - memcpy(&ifr.ifr_ifru, edata, NM_MIN(edata_size, sizeof(ifr.ifr_ifru))); - - if (ioctl(fd, ioctl_request, &ifr) < 0) { - r = -NM_ERRNO_NATIVE(errno); - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s, %s: failed: %s", - log_ioctl_type, - ifindex, - log_subtype, - ifname, - nm_strerror_native(-r)); - } else { - r = 0; - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s, %s: success", - log_ioctl_type, - ifindex, - log_subtype, - ifname); - } -} - - try_count++; - - /* resolve the name again to see whether the ifindex still has the same name. */ - if (!nmp_utils_if_indextoname(ifindex, known_ifnames[try_count % 2])) { - /* we could not find the ifindex again. Probably the device just got - * removed. - * - * In both cases we return the error code we got from ioctl above. - * Either it failed because the device was gone already or it still - * managed to complete the call. In both cases, the error code is good. */ - failure_reason = - "cannot resolve ifindex after ioctl call. Probably the device was just removed"; - goto out; - } - - /* check whether the ifname changed in the meantime. If yes, would render the result - * invalid. Note that this cannot detect every race regarding renames, for example: - * - * - if_indextoname(#10) gives eth0 - * - rename(#10) => eth0_tmp - * - rename(#11) => eth0 - * - ioctl(eth0) (wrongly fetching #11, formerly eth1) - * - rename(#11) => eth_something - * - rename(#10) => eth0 - * - if_indextoname(#10) gives eth0 - */ - if (!nm_streq(known_ifnames[0], known_ifnames[1])) { - gboolean retry; - - /* we detected a possible(!) rename. - * - * For getters it's straight forward to just retry the call. - * - * For setters we also always retry. If our previous call operated on the right device, - * calling it again should have no bad effect (just setting the same thing more than once). - * - * The only potential bad thing is if there was a race involving swapping names, and we just - * set the ioctl option on the wrong device. But then the bad thing already happenned and - * we cannot detect it (nor do anything about it). At least, we can retry and set the - * option on the right interface. */ - retry = (try_count < 5); - - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s: rename detected from \"%s\" to \"%s\". %s", - log_ioctl_type, - ifindex, - log_subtype, - known_ifnames[(try_count - 1) % 2], - known_ifnames[try_count % 2], - retry ? "Retry" : "No retry"); - if (inout_ifname) - nm_utils_ifname_cpy(inout_ifname, known_ifnames[try_count % 2]); - if (retry) { - if (edata_size > 0) - memcpy(edata, edata_backup, edata_size); - goto again; - } - } - -out: - if (failure_reason) { - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s: %s: %s", - log_ioctl_type, - ifindex, - log_subtype, - failure_reason, - r < 0 ? nm_strerror_native(-r) : "assume success"); - } - if (r >= 0) - NM_SET_OUT(out_ifreq, ifr); - return r; -} - -/****************************************************************************** - * ethtool - *****************************************************************************/ - -static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string, - guint32, - NM_UTILS_ENUM2STR(ETHTOOL_GCOALESCE, "ETHTOOL_GCOALESCE"), - NM_UTILS_ENUM2STR(ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), - NM_UTILS_ENUM2STR(ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES"), - NM_UTILS_ENUM2STR(ETHTOOL_GLINK, "ETHTOOL_GLINK"), - NM_UTILS_ENUM2STR(ETHTOOL_GPERMADDR, "ETHTOOL_GPERMADDR"), - NM_UTILS_ENUM2STR(ETHTOOL_GRINGPARAM, "ETHTOOL_GRINGPARAM"), - NM_UTILS_ENUM2STR(ETHTOOL_GSET, "ETHTOOL_GSET"), - NM_UTILS_ENUM2STR(ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO"), - NM_UTILS_ENUM2STR(ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), - NM_UTILS_ENUM2STR(ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), - NM_UTILS_ENUM2STR(ETHTOOL_GWOL, "ETHTOOL_GWOL"), - NM_UTILS_ENUM2STR(ETHTOOL_SCOALESCE, "ETHTOOL_SCOALESCE"), - NM_UTILS_ENUM2STR(ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"), - NM_UTILS_ENUM2STR(ETHTOOL_SRINGPARAM, "ETHTOOL_SRINGPARAM"), - NM_UTILS_ENUM2STR(ETHTOOL_SSET, "ETHTOOL_SSET"), - NM_UTILS_ENUM2STR(ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); - -static const char * -_ethtool_edata_to_string(gpointer edata, gsize edata_size, char *sbuf, gsize sbuf_len) -{ - nm_assert(edata); - nm_assert(edata_size >= sizeof(guint32)); - nm_assert((((intptr_t) edata) % _nm_alignof(guint32)) == 0); - - return _ethtool_cmd_to_string(*((guint32 *) edata), sbuf, sbuf_len); -} - -/*****************************************************************************/ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) - #define ethtool_cmd_speed(pedata) ((pedata)->speed) - - #define ethtool_cmd_speed_set(pedata, speed) \ - G_STMT_START \ - { \ - (pedata)->speed = (guint16)(speed); \ - } \ - G_STMT_END -#endif - -static int -_ethtool_call_handle(SocketHandle *shandle, gpointer edata, gsize edata_size) -{ - char sbuf[50]; - - return _ioctl_call("ethtool", - _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), - SIOCETHTOOL, - shandle->ifindex, - &shandle->fd, - shandle->ifname, - IOCTL_CALL_DATA_TYPE_IFRDATA, - edata, - edata_size, - NULL); -} - -static int -_ethtool_call_once(int ifindex, gpointer edata, gsize edata_size) -{ - char sbuf[50]; - - return _ioctl_call("ethtool", - _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), - SIOCETHTOOL, - ifindex, - NULL, - NULL, - IOCTL_CALL_DATA_TYPE_IFRDATA, - edata, - edata_size, - NULL); -} - -/*****************************************************************************/ - -static struct ethtool_gstrings * -ethtool_get_stringset(SocketHandle *shandle, int stringset_id) -{ - struct { - struct ethtool_sset_info info; - guint32 sentinel; - } sset_info = { - .info.cmd = ETHTOOL_GSSET_INFO, - .info.reserved = 0, - .info.sset_mask = (1ULL << stringset_id), - }; - const guint32 * pdata; - gs_free struct ethtool_gstrings *gstrings = NULL; - gsize gstrings_len; - guint32 i, len; - - if (_ethtool_call_handle(shandle, &sset_info, sizeof(sset_info)) < 0) - return NULL; - if (!sset_info.info.sset_mask) - return NULL; - - pdata = (guint32 *) sset_info.info.data; - - len = *pdata; - - gstrings_len = sizeof(*gstrings) + (len * ETH_GSTRING_LEN); - gstrings = g_malloc0(gstrings_len); - gstrings->cmd = ETHTOOL_GSTRINGS; - gstrings->string_set = stringset_id; - gstrings->len = len; - if (gstrings->len > 0) { - if (_ethtool_call_handle(shandle, gstrings, gstrings_len) < 0) - return NULL; - for (i = 0; i < gstrings->len; i++) { - /* ensure NUL terminated */ - gstrings->data[i * ETH_GSTRING_LEN + (ETH_GSTRING_LEN - 1)] = '\0'; - } - } - - return g_steal_pointer(&gstrings); -} - -static int -ethtool_gstrings_find(const struct ethtool_gstrings *gstrings, const char *needle) -{ - guint32 i; - - /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. - * that means, we cannot possibly request longer names. */ - nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); - - for (i = 0; i < gstrings->len; i++) { - if (nm_streq((char *) &gstrings->data[i * ETH_GSTRING_LEN], needle)) - return i; - } - return -1; -} - -static int -ethtool_get_stringset_index(SocketHandle *shandle, int stringset_id, const char *needle) -{ - gs_free struct ethtool_gstrings *gstrings = NULL; - - /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. - * that means, we cannot possibly request longer names. */ - nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); - - gstrings = ethtool_get_stringset(shandle, stringset_id); - if (gstrings) - return ethtool_gstrings_find(gstrings, needle); - return -1; -} - -/*****************************************************************************/ - -static const NMEthtoolFeatureInfo _ethtool_feature_infos[_NM_ETHTOOL_ID_FEATURE_NUM] = { -#define ETHT_FEAT(eid, ...) \ - { \ - .ethtool_id = eid, .n_kernel_names = NM_NARG(__VA_ARGS__), \ - .kernel_names = ((const char *const[]){__VA_ARGS__}), \ - } - - /* the order does only matter for one thing: if it happens that more than one NMEthtoolID - * reference the same kernel-name, then the one that is mentioned *later* will win in - * case these NMEthtoolIDs are set. That mostly only makes sense for ethtool-ids which - * refer to multiple features ("feature-tso"), while also having more specific ids - * ("feature-tx-tcp-segmentation"). */ - - /* names from ethtool utility, which are aliases for multiple features. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_SG, "tx-scatter-gather", "tx-scatter-gather-fraglist"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TSO, - "tx-tcp-segmentation", - "tx-tcp-ecn-segmentation", - "tx-tcp-mangleid-segmentation", - "tx-tcp6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX, - "tx-checksum-ipv4", - "tx-checksum-ip-generic", - "tx-checksum-ipv6", - "tx-checksum-fcoe-crc", - "tx-checksum-sctp"), - - /* names from ethtool utility, which are aliases for one feature. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert"), - - /* names of features, as known by kernel. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, "esp-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, "esp-tx-csum-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_FCOE_MTU, "fcoe-mtu"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HIGHDMA, "highdma"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, "hw-tc-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, "l2-fwd-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LOOPBACK, "loopback"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_ALL, "rx-all"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_FCS, "rx-fcs"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, "rx-gro-hw"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, "rx-udp_tunnel-port-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, "rx-vlan-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, "rx-vlan-stag-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, "rx-vlan-stag-hw-parse"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, "tls-hw-record"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, "tls-hw-tx-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, "tx-checksum-fcoe-crc"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, "tx-checksum-ipv4"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, "tx-checksum-ipv6"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, "tx-checksum-ip-generic"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, "tx-checksum-sctp"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, "tx-esp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, "tx-fcoe-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, "tx-gre-csum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, "tx-gre-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, "tx-gso-partial"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, "tx-gso-robust"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, "tx-ipxip4-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, "tx-ipxip6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, "tx-nocache-copy"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, "tx-scatter-gather"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, "tx-scatter-gather-fraglist"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, "tx-sctp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, "tx-tcp-ecn-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, "tx-udp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"), -}; - -/* the number of kernel features that we handle. It essentially is the sum of all - * kernel_names. So, all ethtool-ids that reference exactly one kernel-name - * (_NM_ETHTOOL_ID_FEATURE_NUM) + some extra, for ethtool-ids that are aliases - * for multiple kernel-names. */ -#define N_ETHTOOL_KERNEL_FEATURES (((guint) _NM_ETHTOOL_ID_FEATURE_NUM) + 8u) - -static void -_ASSERT_ethtool_feature_infos(void) -{ -#if NM_MORE_ASSERTS > 10 - guint i, k, n; - bool found[_NM_ETHTOOL_ID_FEATURE_NUM] = {}; - - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(_ethtool_feature_infos) == _NM_ETHTOOL_ID_FEATURE_NUM); - - n = 0; - for (i = 0; i < G_N_ELEMENTS(_ethtool_feature_infos); i++) { - NMEthtoolFeatureState kstate; - const NMEthtoolFeatureInfo *inf = &_ethtool_feature_infos[i]; - - g_assert(inf->ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST); - g_assert(inf->ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST); - g_assert(inf->n_kernel_names > 0); - - for (k = 0; k < i; k++) - g_assert(inf->ethtool_id != _ethtool_feature_infos[k].ethtool_id); - - g_assert(!found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)]); - found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)] = TRUE; - - kstate.idx_kernel_name = inf->n_kernel_names - 1; - g_assert((guint) kstate.idx_kernel_name == (guint)(inf->n_kernel_names - 1)); - - n += inf->n_kernel_names; - for (k = 0; k < inf->n_kernel_names; k++) { - g_assert(nm_utils_strv_find_first((char **) inf->kernel_names, k, inf->kernel_names[k]) - < 0); - } - } - - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) - g_assert(found[i]); - - g_assert(n == N_ETHTOOL_KERNEL_FEATURES); -#endif -} - -static NMEthtoolFeatureStates * -ethtool_get_features(SocketHandle *shandle) -{ - gs_free NMEthtoolFeatureStates * states = NULL; - gs_free struct ethtool_gstrings *ss_features = NULL; - - _ASSERT_ethtool_feature_infos(); - - ss_features = ethtool_get_stringset(shandle, ETH_SS_FEATURES); - if (!ss_features) - return NULL; - - if (ss_features->len > 0) { - gs_free struct ethtool_gfeatures * gfeatures_free = NULL; - struct ethtool_gfeatures * gfeatures; - gsize gfeatures_len; - guint idx; - const NMEthtoolFeatureState * states_list0 = NULL; - const NMEthtoolFeatureState *const *states_plist0 = NULL; - guint states_plist_n = 0; - - gfeatures_len = sizeof(struct ethtool_gfeatures) - + (NM_DIV_ROUND_UP(ss_features->len, 32u) * sizeof(gfeatures->features[0])); - gfeatures = nm_malloc0_maybe_a(300, gfeatures_len, &gfeatures_free); - gfeatures->cmd = ETHTOOL_GFEATURES; - gfeatures->size = NM_DIV_ROUND_UP(ss_features->len, 32u); - if (_ethtool_call_handle(shandle, gfeatures, gfeatures_len) < 0) - return NULL; - - for (idx = 0; idx < G_N_ELEMENTS(_ethtool_feature_infos); idx++) { - const NMEthtoolFeatureInfo *info = &_ethtool_feature_infos[idx]; - guint idx_kernel_name; - - for (idx_kernel_name = 0; idx_kernel_name < info->n_kernel_names; idx_kernel_name++) { - NMEthtoolFeatureState *kstate; - const char * kernel_name = info->kernel_names[idx_kernel_name]; - int i_feature; - guint i_block; - guint32 i_flag; - - i_feature = ethtool_gstrings_find(ss_features, kernel_name); - if (i_feature < 0) - continue; - - i_block = ((guint) i_feature) / 32u; - i_flag = (guint32)(1u << (((guint) i_feature) % 32u)); - - if (!states) { - states = g_malloc0( - sizeof(NMEthtoolFeatureStates) - + (N_ETHTOOL_KERNEL_FEATURES * sizeof(NMEthtoolFeatureState)) - + ((N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)) - * sizeof(NMEthtoolFeatureState *))); - states_list0 = &states->states_list[0]; - states_plist0 = (gpointer) &states_list0[N_ETHTOOL_KERNEL_FEATURES]; - states->n_ss_features = ss_features->len; - } - - nm_assert(states->n_states < N_ETHTOOL_KERNEL_FEATURES); - kstate = (NMEthtoolFeatureState *) &states_list0[states->n_states]; - states->n_states++; - - kstate->info = info; - kstate->idx_ss_features = i_feature; - kstate->idx_kernel_name = idx_kernel_name; - kstate->available = !!(gfeatures->features[i_block].available & i_flag); - kstate->requested = !!(gfeatures->features[i_block].requested & i_flag); - kstate->active = !!(gfeatures->features[i_block].active & i_flag); - kstate->never_changed = !!(gfeatures->features[i_block].never_changed & i_flag); - - nm_assert(states_plist_n - < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); - - if (!states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) - states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)] = - &states_plist0[states_plist_n]; - ((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate; - states_plist_n++; - } - - if (states && states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) { - nm_assert(states_plist_n - < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); - nm_assert(!states_plist0[states_plist_n]); - states_plist_n++; - } - } - } - - return g_steal_pointer(&states); -} - -NMEthtoolFeatureStates * -nmp_utils_ethtool_get_features(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - NMEthtoolFeatureStates * features; - - g_return_val_if_fail(ifindex > 0, 0); - - features = ethtool_get_features(&shandle); - - if (!features) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting features", - ifindex, - "get-features"); - return NULL; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel features", - ifindex, - "get-features"); - return features; -} - -static const char * -_ethtool_feature_state_to_string(char * buf, - gsize buf_size, - const NMEthtoolFeatureState *s, - const char * prefix) -{ - int l; - - l = g_snprintf(buf, - buf_size, - "%s %s%s", - prefix ?: "", - ONOFF(s->active), - (!s->available || s->never_changed) - ? ", [fixed]" - : ((s->requested != s->active) - ? (s->requested ? ", [requested on]" : ", [requested off]") - : "")); - nm_assert(l < buf_size); - return buf; -} - -gboolean -nmp_utils_ethtool_set_features( - int ifindex, - const NMEthtoolFeatureStates *features, - const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gs_free struct ethtool_sfeatures * sfeatures_free = NULL; - struct ethtool_sfeatures * sfeatures; - gsize sfeatures_len; - int r; - guint i, j; - struct { - const NMEthtoolFeatureState *f_state; - NMTernary requested; - } set_states[N_ETHTOOL_KERNEL_FEATURES]; - guint set_states_n = 0; - gboolean success = TRUE; - - g_return_val_if_fail(ifindex > 0, 0); - g_return_val_if_fail(features, 0); - g_return_val_if_fail(requested, 0); - - nm_assert(features->n_states <= N_ETHTOOL_KERNEL_FEATURES); - - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { - const NMEthtoolFeatureState *const *states_indexed; - - if (requested[i] == NM_TERNARY_DEFAULT) - continue; - - if (!(states_indexed = features->states_indexed[i])) { - if (do_set) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: set feature %s: skip (not found)", - ifindex, - "set-features", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname); - success = FALSE; - } - continue; - } - - for (j = 0; states_indexed[j]; j++) { - const NMEthtoolFeatureState *s = states_indexed[j]; - char sbuf[255]; - - if (set_states_n >= G_N_ELEMENTS(set_states)) - g_return_val_if_reached(FALSE); - - if (s->never_changed) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s feature %s (%s): %s, %s (skip feature marked as " - "never changed)", - ifindex, - "set-features", - do_set ? "set" : "reset", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, - s->info->kernel_names[s->idx_kernel_name], - ONOFF(do_set ? requested[i] == NM_TERNARY_TRUE : s->active), - _ethtool_feature_state_to_string(sbuf, - sizeof(sbuf), - s, - do_set ? " currently:" : " before:")); - continue; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s feature %s (%s): %s, %s", - ifindex, - "set-features", - do_set ? "set" : "reset", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, - s->info->kernel_names[s->idx_kernel_name], - ONOFF(do_set ? requested[i] == NM_TERNARY_TRUE : s->active), - _ethtool_feature_state_to_string(sbuf, - sizeof(sbuf), - s, - do_set ? " currently:" : " before:")); - - if (do_set && (!s->available || s->never_changed) - && (s->active != (requested[i] == NM_TERNARY_TRUE))) { - /* we request to change a flag which kernel reported as fixed. - * While the ethtool operation will silently succeed, mark the request - * as failure. */ - success = FALSE; - } - - set_states[set_states_n].f_state = s; - set_states[set_states_n].requested = requested[i]; - set_states_n++; - } - } - - if (set_states_n == 0) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: no feature requested", - ifindex, - "set-features"); - return TRUE; - } - - sfeatures_len = - sizeof(struct ethtool_sfeatures) - + (NM_DIV_ROUND_UP(features->n_ss_features, 32U) * sizeof(sfeatures->features[0])); - sfeatures = nm_malloc0_maybe_a(300, sfeatures_len, &sfeatures_free); - sfeatures->cmd = ETHTOOL_SFEATURES; - sfeatures->size = NM_DIV_ROUND_UP(features->n_ss_features, 32U); - - for (i = 0; i < set_states_n; i++) { - const NMEthtoolFeatureState *s = set_states[i].f_state; - guint i_block; - guint32 i_flag; - gboolean is_requested; - - i_block = s->idx_ss_features / 32u; - i_flag = (guint32)(1u << (s->idx_ss_features % 32u)); - - sfeatures->features[i_block].valid |= i_flag; - - if (do_set) - is_requested = (set_states[i].requested == NM_TERNARY_TRUE); - else - is_requested = s->active; - - if (is_requested) - sfeatures->features[i_block].requested |= i_flag; - else - sfeatures->features[i_block].requested &= ~i_flag; - } - - r = _ethtool_call_handle(&shandle, sfeatures, sfeatures_len); - if (r < 0) { - success = FALSE; - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting features (%s)", - ifindex, - "set-features", - nm_strerror_native(-r)); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s", - ifindex, - "set-features", - success ? "successfully setting features" - : "at least some of the features were not successfully set"); - return success; -} - -static gboolean -ethtool_get_coalesce(SocketHandle *shandle, NMEthtoolCoalesceState *coalesce) -{ - struct ethtool_coalesce eth_data; - - eth_data.cmd = ETHTOOL_GCOALESCE; - - if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) != 0) - return FALSE; - - *coalesce = (NMEthtoolCoalesceState){ - .s = { - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)] = - eth_data.rx_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)] = - eth_data.rx_max_coalesced_frames, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)] = - eth_data.rx_coalesce_usecs_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)] = - eth_data.rx_max_coalesced_frames_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)] = - eth_data.tx_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)] = - eth_data.tx_max_coalesced_frames, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)] = - eth_data.tx_coalesce_usecs_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)] = - eth_data.tx_max_coalesced_frames_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)] = - eth_data.stats_block_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)] = - eth_data.use_adaptive_rx_coalesce, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)] = - eth_data.use_adaptive_tx_coalesce, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)] = - eth_data.pkt_rate_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)] = - eth_data.rx_coalesce_usecs_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)] = - eth_data.rx_max_coalesced_frames_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)] = - eth_data.tx_coalesce_usecs_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)] = - eth_data.tx_max_coalesced_frames_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)] = - eth_data.pkt_rate_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)] = - eth_data.rx_coalesce_usecs_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)] = - eth_data.rx_max_coalesced_frames_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)] = - eth_data.tx_coalesce_usecs_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)] = - eth_data.tx_max_coalesced_frames_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)] = - eth_data.rate_sample_interval, - }}; - return TRUE; -} - -gboolean -nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(coalesce, FALSE); - - if (!ethtool_get_coalesce(&shandle, coalesce)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting coalesce settings", - ifindex, - "get-coalesce"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel coalesce settings", - ifindex, - "get-coalesce"); - return TRUE; -} - -static gboolean -ethtool_set_coalesce(SocketHandle *shandle, const NMEthtoolCoalesceState *coalesce) -{ - struct ethtool_coalesce eth_data; - gboolean success; - - nm_assert(shandle); - nm_assert(coalesce); - - eth_data = (struct ethtool_coalesce){ - .cmd = ETHTOOL_SCOALESCE, - .rx_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)], - .rx_max_coalesced_frames = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)], - .rx_coalesce_usecs_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)], - .rx_max_coalesced_frames_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)], - .tx_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)], - .tx_max_coalesced_frames = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)], - .tx_coalesce_usecs_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)], - .tx_max_coalesced_frames_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)], - .stats_block_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)], - .use_adaptive_rx_coalesce = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)], - .use_adaptive_tx_coalesce = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)], - .pkt_rate_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)], - .rx_coalesce_usecs_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)], - .rx_max_coalesced_frames_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)], - .tx_coalesce_usecs_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)], - .tx_max_coalesced_frames_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)], - .pkt_rate_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)], - .rx_coalesce_usecs_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)], - .rx_max_coalesced_frames_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)], - .tx_coalesce_usecs_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)], - .tx_max_coalesced_frames_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)], - .rate_sample_interval = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)], - }; - - success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) == 0); - return success; -} - -gboolean -nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(coalesce, FALSE); - - if (!ethtool_set_coalesce(&shandle, coalesce)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting coalesce settings", - ifindex, - "set-coalesce"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: set kernel coalesce settings", - ifindex, - "set-coalesce"); - return TRUE; -} - -static gboolean -ethtool_get_ring(SocketHandle *shandle, NMEthtoolRingState *ring) -{ - struct ethtool_ringparam eth_data; - - eth_data.cmd = ETHTOOL_GRINGPARAM; - - if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) != 0) - return FALSE; - - ring->rx_pending = eth_data.rx_pending; - ring->rx_jumbo_pending = eth_data.rx_jumbo_pending; - ring->rx_mini_pending = eth_data.rx_mini_pending; - ring->tx_pending = eth_data.tx_pending; - - return TRUE; -} - -gboolean -nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ring, FALSE); - - if (!ethtool_get_ring(&shandle, ring)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting ring settings", - ifindex, - "get-ring"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel ring settings", - ifindex, - "get-ring"); - return TRUE; -} - -static gboolean -ethtool_set_ring(SocketHandle *shandle, const NMEthtoolRingState *ring) -{ - gboolean success; - struct ethtool_ringparam eth_data; - - g_return_val_if_fail(shandle, FALSE); - g_return_val_if_fail(ring, FALSE); - - eth_data = (struct ethtool_ringparam){ - .cmd = ETHTOOL_SRINGPARAM, - .rx_pending = ring->rx_pending, - .rx_jumbo_pending = ring->rx_jumbo_pending, - .rx_mini_pending = ring->rx_mini_pending, - .tx_pending = ring->tx_pending, - }; - - success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) == 0); - return success; -} - -gboolean -nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ring, FALSE); - - if (!ethtool_set_ring(&shandle, ring)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting ring settings", - ifindex, - "set-ring"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, "ethtool[%d]: %s: set kernel ring settings", ifindex, "set-ring"); - return TRUE; -} - -/*****************************************************************************/ - -gboolean -nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data) -{ - struct ethtool_drvinfo *drvinfo; - - G_STATIC_ASSERT_EXPR(sizeof(*data) == sizeof(*drvinfo)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, driver) - == offsetof(struct ethtool_drvinfo, driver)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, version) - == offsetof(struct ethtool_drvinfo, version)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, fw_version) - == offsetof(struct ethtool_drvinfo, fw_version)); - G_STATIC_ASSERT_EXPR(sizeof(data->driver) == sizeof(drvinfo->driver)); - G_STATIC_ASSERT_EXPR(sizeof(data->version) == sizeof(drvinfo->version)); - G_STATIC_ASSERT_EXPR(sizeof(data->fw_version) == sizeof(drvinfo->fw_version)); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(data, FALSE); - - drvinfo = (struct ethtool_drvinfo *) data; - *drvinfo = (struct ethtool_drvinfo){ - .cmd = ETHTOOL_GDRVINFO, - }; - return _ethtool_call_once(ifindex, drvinfo, sizeof(*drvinfo)) >= 0; -} - -gboolean -nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length) -{ - struct { - struct ethtool_perm_addr e; - guint8 _extra_data[NM_UTILS_HWADDR_LEN_MAX + 1]; - } edata = { - .e.cmd = ETHTOOL_GPERMADDR, - .e.size = NM_UTILS_HWADDR_LEN_MAX, - }; - const guint8 *pdata; - - guint i; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) - return FALSE; - - if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX) - return FALSE; - if (edata.e.size < 1) - return FALSE; - - pdata = (const guint8 *) edata.e.data; - - if (NM_IN_SET(pdata[0], 0, 0xFF)) { - /* Some drivers might return a permanent address of all zeros. - * Reject that (rh#1264024) - * - * Some drivers return a permanent address of all ones. Reject that too */ - for (i = 1; i < edata.e.size; i++) { - if (pdata[0] != pdata[i]) - goto not_all_0or1; - } - return FALSE; - } - -not_all_0or1: - memcpy(buf, pdata, edata.e.size); - *length = edata.e.size; - return TRUE; -} - -gboolean -nmp_utils_ethtool_supports_carrier_detect(int ifindex) -{ - struct ethtool_cmd edata = {.cmd = ETHTOOL_GLINK}; - - g_return_val_if_fail(ifindex > 0, FALSE); - - /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we - * assume the device supports carrier-detect, otherwise we assume it - * doesn't. - */ - return _ethtool_call_once(ifindex, &edata, sizeof(edata)) >= 0; -} - -gboolean -nmp_utils_ethtool_supports_vlans(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gs_free struct ethtool_gfeatures * features_free = NULL; - struct ethtool_gfeatures * features; - gsize features_len; - int idx, block, bit, size; - - g_return_val_if_fail(ifindex > 0, FALSE); - - idx = ethtool_get_stringset_index(&shandle, ETH_SS_FEATURES, "vlan-challenged"); - if (idx < 0) { - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: vlan-challenged ethtool feature does not exist?", - ifindex); - return FALSE; - } - - block = idx / 32; - bit = idx % 32; - size = block + 1; - - features_len = sizeof(*features) + (size * sizeof(struct ethtool_get_features_block)); - features = nm_malloc0_maybe_a(300, features_len, &features_free); - features->cmd = ETHTOOL_GFEATURES; - features->size = size; - - if (_ethtool_call_handle(&shandle, features, features_len) < 0) - return FALSE; - - return !(features->features[block].active & (1 << bit)); -} - -int -nmp_utils_ethtool_get_peer_ifindex(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gsize stats_len; - gs_free struct ethtool_stats * stats_free = NULL; - struct ethtool_stats * stats; - int peer_ifindex_stat; - - g_return_val_if_fail(ifindex > 0, 0); - - peer_ifindex_stat = ethtool_get_stringset_index(&shandle, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat < 0) { - nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); - return FALSE; - } - - stats_len = sizeof(*stats) + (peer_ifindex_stat + 1) * sizeof(guint64); - stats = nm_malloc0_maybe_a(300, stats_len, &stats_free); - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = peer_ifindex_stat + 1; - if (_ethtool_call_handle(&shandle, stats, stats_len) < 0) - return 0; - - return stats->data[peer_ifindex_stat]; -} - -gboolean -nmp_utils_ethtool_get_wake_on_lan(int ifindex) -{ - struct ethtool_wolinfo wol = { - .cmd = ETHTOOL_GWOL, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &wol, sizeof(wol)) < 0) - return FALSE; - - return wol.wolopts != 0; -} - -gboolean -nmp_utils_ethtool_get_link_settings(int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex) -{ - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) - return FALSE; - - NM_SET_OUT(out_autoneg, (edata.autoneg == AUTONEG_ENABLE)); - - if (out_speed) { - guint32 speed; - - speed = ethtool_cmd_speed(&edata); - if (speed == G_MAXUINT16 || speed == G_MAXUINT32) - speed = 0; - - *out_speed = speed; - } - - if (out_duplex) { - switch (edata.duplex) { - case DUPLEX_HALF: - *out_duplex = NM_PLATFORM_LINK_DUPLEX_HALF; - break; - case DUPLEX_FULL: - *out_duplex = NM_PLATFORM_LINK_DUPLEX_FULL; - break; - default: /* DUPLEX_UNKNOWN */ - *out_duplex = NM_PLATFORM_LINK_DUPLEX_UNKNOWN; - break; - } - } - - return TRUE; -} - -#define ADVERTISED_INVALID 0 -#define BASET_ALL_MODES \ - (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half \ - | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full \ - | ADVERTISED_10000baseT_Full) - -static guint32 -get_baset_mode(guint32 speed, NMPlatformLinkDuplexType duplex) -{ - if (duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN) - return ADVERTISED_INVALID; - - if (duplex == NM_PLATFORM_LINK_DUPLEX_HALF) { - switch (speed) { - case 10: - return ADVERTISED_10baseT_Half; - case 100: - return ADVERTISED_100baseT_Half; - case 1000: - return ADVERTISED_1000baseT_Half; - default: - return ADVERTISED_INVALID; - } - } else { - switch (speed) { - case 10: - return ADVERTISED_10baseT_Full; - case 100: - return ADVERTISED_100baseT_Full; - case 1000: - return ADVERTISED_1000baseT_Full; - case 10000: - return ADVERTISED_10000baseT_Full; - default: - return ADVERTISED_INVALID; - } - } -} - -gboolean -nmp_utils_ethtool_set_link_settings(int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail((speed && duplex != NM_PLATFORM_LINK_DUPLEX_UNKNOWN) - || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), - FALSE); - - /* retrieve first current settings */ - if (_ethtool_call_handle(&shandle, &edata, sizeof(edata)) < 0) - return FALSE; - - /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API - * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf - */ - - /* then change the needed ones */ - edata.cmd = ETHTOOL_SSET; - if (autoneg) { - edata.autoneg = AUTONEG_ENABLE; - if (!speed) - edata.advertising = edata.supported; - else { - guint32 mode; - - mode = get_baset_mode(speed, duplex); - - if (!mode) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised", - ifindex, - speed, - nm_platform_link_duplex_type_to_string(duplex)); - return FALSE; - } - if (!(edata.supported & mode)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: device does not support %uBASE-T %s duplex mode", - ifindex, - speed, - nm_platform_link_duplex_type_to_string(duplex)); - return FALSE; - } - edata.advertising = (edata.supported & ~BASET_ALL_MODES) | mode; - } - } else { - edata.autoneg = AUTONEG_DISABLE; - - if (speed) - ethtool_cmd_speed_set(&edata, speed); - - switch (duplex) { - case NM_PLATFORM_LINK_DUPLEX_HALF: - edata.duplex = DUPLEX_HALF; - break; - case NM_PLATFORM_LINK_DUPLEX_FULL: - edata.duplex = DUPLEX_FULL; - break; - case NM_PLATFORM_LINK_DUPLEX_UNKNOWN: - break; - default: - g_return_val_if_reached(FALSE); - } - } - - return _ethtool_call_handle(&shandle, &edata, sizeof(edata)) >= 0; -} - -gboolean -nmp_utils_ethtool_set_wake_on_lan(int ifindex, - NMSettingWiredWakeOnLan wol, - const char * wol_password) -{ - struct ethtool_wolinfo wol_info = { - .cmd = ETHTOOL_SWOL, - .wolopts = 0, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) - return TRUE; - - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", - ifindex, - (unsigned) wol, - wol_password); - - if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) - wol_info.wolopts |= WAKE_PHY; - if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) - wol_info.wolopts |= WAKE_UCAST; - if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST)) - wol_info.wolopts |= WAKE_MCAST; - if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST)) - wol_info.wolopts |= WAKE_BCAST; - if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP)) - wol_info.wolopts |= WAKE_ARP; - if (NM_FLAGS_HAS(wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) - wol_info.wolopts |= WAKE_MAGIC; - - if (wol_password) { - if (!nm_utils_hwaddr_aton(wol_password, wol_info.sopass, ETH_ALEN)) { - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: couldn't parse Wake-on-LAN password '%s'", - ifindex, - wol_password); - return FALSE; - } - wol_info.wolopts |= WAKE_MAGICSECURE; - } - - return _ethtool_call_once(ifindex, &wol_info, sizeof(wol_info)) >= 0; -} - -/****************************************************************************** - * mii - *****************************************************************************/ - -gboolean -nmp_utils_mii_supports_carrier_detect(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - int r; - struct ifreq ifr; - struct mii_ioctl_data * mii; - - g_return_val_if_fail(ifindex > 0, FALSE); - - r = _ioctl_call("mii", - "SIOCGMIIPHY", - SIOCGMIIPHY, - shandle.ifindex, - &shandle.fd, - shandle.ifname, - IOCTL_CALL_DATA_TYPE_NONE, - NULL, - 0, - &ifr); - if (r < 0) - return FALSE; - - /* If we can read the BMSR register, we assume that the card supports MII link detection */ - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - mii->reg_num = MII_BMSR; - - r = _ioctl_call("mii", - "SIOCGMIIREG", - SIOCGMIIREG, - shandle.ifindex, - &shandle.fd, - shandle.ifname, - IOCTL_CALL_DATA_TYPE_IFRU, - mii, - sizeof(*mii), - &ifr); - if (r < 0) - return FALSE; - - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - nm_log_trace(LOGD_PLATFORM, - "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", - ifindex, - shandle.ifname, - mii->val_out); - return TRUE; -} - -/****************************************************************************** - * udev - *****************************************************************************/ - -const char * -nmp_utils_udev_get_driver(struct udev_device *udevice) -{ - struct udev_device *parent = NULL, *grandparent = NULL; - const char * driver, *subsys; - - driver = udev_device_get_driver(udevice); - if (driver) - goto out; - - /* Try the parent */ - parent = udev_device_get_parent(udevice); - if (parent) { - driver = udev_device_get_driver(parent); - if (!driver) { - /* Try the grandparent if it's an ibmebus device or if the - * subsys is NULL which usually indicates some sort of - * platform device like a 'gadget' net interface. - */ - subsys = udev_device_get_subsystem(parent); - if ((g_strcmp0(subsys, "ibmebus") == 0) || (subsys == NULL)) { - grandparent = udev_device_get_parent(parent); - if (grandparent) - driver = udev_device_get_driver(grandparent); - } - } - } - -out: - /* Intern the string so we don't have to worry about memory - * management in NMPlatformLink. */ - return g_intern_string(driver); -} - -/****************************************************************************** - * utils - *****************************************************************************/ - -NMIPConfigSource -nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) -{ - return ((int) rtprot) + 1; -} - -NMIPConfigSource -nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) -{ - /* when adding a route to kernel for a give @source, the resulting route - * will be put into the cache with a source of NM_IP_CONFIG_SOURCE_RTPROT_*. - * This function returns that. */ - return nmp_utils_ip_config_source_from_rtprot( - nmp_utils_ip_config_source_coerce_to_rtprot(source)); -} - -guint8 -nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) -{ - /* when adding a route to kernel, we coerce the @source field - * to rtm_protocol. This is not lossless as we map different - * source values to the same RTPROT uint8 value. */ - if (source <= NM_IP_CONFIG_SOURCE_UNKNOWN) - return RTPROT_UNSPEC; - - if (source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST) - return source - 1; - - switch (source) { - case NM_IP_CONFIG_SOURCE_KERNEL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_IP6LL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_DHCP: - return RTPROT_DHCP; - case NM_IP_CONFIG_SOURCE_NDISC: - return RTPROT_RA; - - default: - return RTPROT_STATIC; - } -} - -NMIPConfigSource -nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) -{ - /* When we receive a route from kernel and put it into the platform cache, - * we preserve the protocol field by converting it to a NMIPConfigSource - * via nmp_utils_ip_config_source_from_rtprot(). - * - * However, that is not the inverse of nmp_utils_ip_config_source_coerce_to_rtprot(). - * Instead, to go back to the original value, you need another step: - * nmp_utils_ip_config_source_coerce_from_rtprot (nmp_utils_ip_config_source_from_rtprot (rtprot)). - * - * This might partly restore the original source value, but of course that - * is not really possible because nmp_utils_ip_config_source_coerce_to_rtprot() - * is not injective. - * */ - switch (source) { - case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: - return NM_IP_CONFIG_SOURCE_UNKNOWN; - - case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: - case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: - return NM_IP_CONFIG_SOURCE_KERNEL; - - case NM_IP_CONFIG_SOURCE_RTPROT_RA: - return NM_IP_CONFIG_SOURCE_NDISC; - - case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: - return NM_IP_CONFIG_SOURCE_DHCP; - - default: - return NM_IP_CONFIG_SOURCE_USER; - } -} - -const char * -nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len) -{ - const char *s = NULL; - nm_utils_to_string_buffer_init(&buf, &len); - - if (!len) - return buf; - - switch (source) { - case NM_IP_CONFIG_SOURCE_UNKNOWN: - s = "unknown"; - break; - - case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: - s = "rt-unspec"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: - s = "rt-redirect"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: - s = "rt-kernel"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_BOOT: - s = "rt-boot"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_STATIC: - s = "rt-static"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: - s = "rt-dhcp"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_RA: - s = "rt-ra"; - break; - - case NM_IP_CONFIG_SOURCE_KERNEL: - s = "kernel"; - break; - case NM_IP_CONFIG_SOURCE_SHARED: - s = "shared"; - break; - case NM_IP_CONFIG_SOURCE_IP4LL: - s = "ipv4ll"; - break; - case NM_IP_CONFIG_SOURCE_IP6LL: - s = "ipv6ll"; - break; - case NM_IP_CONFIG_SOURCE_PPP: - s = "ppp"; - break; - case NM_IP_CONFIG_SOURCE_WWAN: - s = "wwan"; - break; - case NM_IP_CONFIG_SOURCE_VPN: - s = "vpn"; - break; - case NM_IP_CONFIG_SOURCE_DHCP: - s = "dhcp"; - break; - case NM_IP_CONFIG_SOURCE_NDISC: - s = "ndisc"; - break; - case NM_IP_CONFIG_SOURCE_USER: - s = "user"; - break; - default: - break; - } - - if (source >= 1 && source <= 0x100) { - if (s) - g_snprintf(buf, len, "%s", s); - else - g_snprintf(buf, len, "rt-%d", ((int) source) - 1); - } else { - if (s) - g_strlcpy(buf, s, len); - else - g_snprintf(buf, len, "(%d)", source); - } - return buf; -} - -/** - * nmp_utils_sysctl_open_netdir: - * @ifindex: the ifindex for which to open "/sys/class/net/%s" - * @ifname_guess: (allow-none): optional argument, if present used as initial - * guess as the current name for @ifindex. If guessed right, - * it saves an additional if_indextoname() call. - * @out_ifname: (allow-none): if present, must be at least IFNAMSIZ - * characters. On success, this will contain the actual ifname - * found while opening the directory. - * - * Returns: a negative value on failure, on success returns the open fd - * to the "/sys/class/net/%s" directory for @ifindex. - */ -int -nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname) -{ -#define SYS_CLASS_NET "/sys/class/net/" - const char *ifname = ifname_guess; - char ifname_buf_last_try[IFNAMSIZ]; - char ifname_buf[IFNAMSIZ]; - guint try_count = 0; - char sysdir[NM_STRLEN(SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET; - char fd_buf[256]; - ssize_t nn; - - g_return_val_if_fail(ifindex >= 0, -1); - - ifname_buf_last_try[0] = '\0'; - - for (try_count = 0; try_count < 10; try_count++, ifname = NULL) { - nm_auto_close int fd_dir = -1; - nm_auto_close int fd_ifindex = -1; - - if (!ifname) { - ifname = nmp_utils_if_indextoname(ifindex, ifname_buf); - if (!ifname) - return -1; - } - - nm_assert(nm_utils_ifname_valid_kernel(ifname, NULL)); - - if (g_strlcpy(&sysdir[NM_STRLEN(SYS_CLASS_NET)], ifname, IFNAMSIZ) >= IFNAMSIZ) - g_return_val_if_reached(-1); - - /* we only retry, if the name changed since previous attempt. - * Hence, it is extremely unlikely that this loop runes until the - * end of the @try_count. */ - if (nm_streq(ifname, ifname_buf_last_try)) - return -1; - strcpy(ifname_buf_last_try, ifname); - - fd_dir = open(sysdir, O_DIRECTORY | O_CLOEXEC); - if (fd_dir < 0) - continue; - - fd_ifindex = openat(fd_dir, "ifindex", O_CLOEXEC); - if (fd_ifindex < 0) - continue; - - nn = nm_utils_fd_read_loop(fd_ifindex, fd_buf, sizeof(fd_buf) - 2, FALSE); - if (nn <= 0) - continue; - fd_buf[nn] = '\0'; - - if (ifindex != (int) _nm_utils_ascii_str_to_int64(fd_buf, 10, 1, G_MAXINT, -1)) - continue; - - if (out_ifname) - strcpy(out_ifname, ifname); - - return nm_steal_fd(&fd_dir); - } - - return -1; -} diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h deleted file mode 100644 index 076a2c7d..00000000 --- a/src/platform/nm-platform-utils.h +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_PLATFORM_UTILS_H__ -#define __NM_PLATFORM_UTILS_H__ - -#include "nm-platform.h" -#include "nm-setting-wired.h" -#include "nm-libnm-core-intern/nm-ethtool-utils.h" - -/*****************************************************************************/ - -const char *nmp_utils_ethtool_get_driver(int ifindex); -gboolean nmp_utils_ethtool_supports_carrier_detect(int ifindex); -gboolean nmp_utils_ethtool_supports_vlans(int ifindex); -int nmp_utils_ethtool_get_peer_ifindex(int ifindex); -gboolean nmp_utils_ethtool_get_wake_on_lan(int ifindex); -gboolean nmp_utils_ethtool_set_wake_on_lan(int ifindex, - NMSettingWiredWakeOnLan wol, - const char * wol_password); - -gboolean nmp_utils_ethtool_get_link_settings(int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex); -gboolean nmp_utils_ethtool_set_link_settings(int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex); - -gboolean nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length); - -typedef struct { - /* We don't want to include <linux/ethtool.h> in header files, - * thus create a ABI compatible version of struct ethtool_drvinfo.*/ - guint32 _private_cmd; - char driver[32]; - char version[32]; - char fw_version[32]; - char _private_bus_info[32]; - char _private_erom_version[32]; - char _private_reserved2[12]; - guint32 _private_n_priv_flags; - guint32 _private_n_stats; - guint32 _private_testinfo_len; - guint32 _private_eedump_len; - guint32 _private_regdump_len; -} NMPUtilsEthtoolDriverInfo; - -gboolean nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data); - -typedef struct { - NMEthtoolID ethtool_id; - - guint8 n_kernel_names; - - /* one NMEthtoolID refers to one or more kernel_names. The reason for supporting this complexity - * (where one NMSettingEthtool option refers to multiple kernel features) is to follow what - * ethtool does, where "tx" is an alias for multiple features. */ - const char *const *kernel_names; -} NMEthtoolFeatureInfo; - -typedef struct { - const NMEthtoolFeatureInfo *info; - - guint idx_ss_features; - - /* one NMEthtoolFeatureInfo references one or more kernel_names. This is the index - * of the matching info->kernel_names */ - guint8 idx_kernel_name; - - bool available : 1; - bool requested : 1; - bool active : 1; - bool never_changed : 1; -} NMEthtoolFeatureState; - -struct _NMEthtoolFeatureStates { - guint n_states; - - guint n_ss_features; - - /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */ - const NMEthtoolFeatureState *const *states_indexed[_NM_ETHTOOL_ID_FEATURE_NUM]; - - /* the same content, here as a list of n_states entries. */ - const NMEthtoolFeatureState states_list[]; -}; - -NMEthtoolFeatureStates *nmp_utils_ethtool_get_features(int ifindex); - -gboolean nmp_utils_ethtool_set_features( - int ifindex, - const NMEthtoolFeatureStates *features, - const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */); - -struct _NMEthtoolCoalesceState { - guint32 - s[_NM_ETHTOOL_ID_COALESCE_NUM /* indexed by (NMEthtoolID - _NM_ETHTOOL_ID_COALESCE_FIRST) */ - ]; -}; - -gboolean nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce); - -gboolean nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce); - -struct _NMEthtoolRingState { - guint32 rx_pending; - guint32 rx_mini_pending; - guint32 rx_jumbo_pending; - guint32 tx_pending; -}; - -gboolean nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring); - -gboolean nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring); - -/*****************************************************************************/ - -gboolean nmp_utils_mii_supports_carrier_detect(int ifindex); - -struct udev_device; - -const char *nmp_utils_udev_get_driver(struct udev_device *udevice); - -NMIPConfigSource nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) _nm_const; -guint8 nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) _nm_const; -NMIPConfigSource nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) _nm_const; -NMIPConfigSource nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) _nm_const; -const char *nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len); - -const char *nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/); -int nmp_utils_if_nametoindex(const char *ifname); - -int nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname); - -#endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c deleted file mode 100644 index 7969206c..00000000 --- a/src/platform/nm-platform.c +++ /dev/null @@ -1,8982 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2012 - 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-platform.h" - -#include <stdlib.h> -#include <unistd.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/socket.h> -#include <netdb.h> -#include <linux/fib_rules.h> -#include <linux/ip.h> -#include <linux/if.h> -#include <linux/if_tun.h> -#include <linux/if_tunnel.h> -#include <linux/rtnetlink.h> -#include <linux/tc_act/tc_mirred.h> -#include <libudev.h> - -#include "nm-utils.h" -#include "nm-core-internal.h" -#include "nm-glib-aux/nm-dedup-multi.h" -#include "nm-udev-aux/nm-udev-utils.h" -#include "nm-glib-aux/nm-secret-utils.h" - -#include "nm-core-utils.h" -#include "nm-platform-utils.h" -#include "nm-platform-private.h" -#include "nmp-object.h" -#include "nmp-netns.h" - -/*****************************************************************************/ - -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPAddress, address_ptr) - == G_STRUCT_OFFSET(NMPlatformIP4Address, address)); -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPAddress, address_ptr) - == G_STRUCT_OFFSET(NMPlatformIP6Address, address)); -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPRoute, network_ptr) - == G_STRUCT_OFFSET(NMPlatformIP4Route, network)); -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPRoute, network_ptr) - == G_STRUCT_OFFSET(NMPlatformIP6Route, network)); - -G_STATIC_ASSERT(_nm_alignof(NMPlatformIPRoute) == _nm_alignof(NMPlatformIP4Route)); -G_STATIC_ASSERT(_nm_alignof(NMPlatformIPRoute) == _nm_alignof(NMPlatformIP6Route)); -G_STATIC_ASSERT(_nm_alignof(NMPlatformIPRoute) == _nm_alignof(NMPlatformIPXRoute)); - -G_STATIC_ASSERT(_nm_alignof(NMPlatformIPAddress) == _nm_alignof(NMPlatformIP4Address)); -G_STATIC_ASSERT(_nm_alignof(NMPlatformIPAddress) == _nm_alignof(NMPlatformIP6Address)); -G_STATIC_ASSERT(_nm_alignof(NMPlatformIPAddress) == _nm_alignof(NMPlatformIPXAddress)); - -/*****************************************************************************/ - -G_STATIC_ASSERT(sizeof(((NMPLinkAddress *) NULL)->data) == NM_UTILS_HWADDR_LEN_MAX); -G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_address.data) == NM_UTILS_HWADDR_LEN_MAX); -G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == NM_UTILS_HWADDR_LEN_MAX); - -static const char * -_nmp_link_address_to_string(const NMPLinkAddress *addr, - char buf[static(NM_UTILS_HWADDR_LEN_MAX * 3)]) -{ - nm_assert(addr); - - if (addr->len > 0) { - if (!_nm_utils_hwaddr_ntoa(addr->data, addr->len, TRUE, buf, NM_UTILS_HWADDR_LEN_MAX * 3)) { - buf[0] = '\0'; - g_return_val_if_reached(buf); - } - } else - buf[0] = '\0'; - - return buf; -} - -gconstpointer -nmp_link_address_get(const NMPLinkAddress *addr, size_t *length) -{ - if (!addr || addr->len <= 0) { - NM_SET_OUT(length, 0); - return NULL; - } - - if (addr->len > NM_UTILS_HWADDR_LEN_MAX) { - NM_SET_OUT(length, 0); - g_return_val_if_reached(NULL); - } - - NM_SET_OUT(length, addr->len); - return addr->data; -} - -GBytes * -nmp_link_address_get_as_bytes(const NMPLinkAddress *addr) -{ - gconstpointer data; - size_t length; - - data = nmp_link_address_get(addr, &length); - - return length > 0 ? g_bytes_new(data, length) : NULL; -} - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG_PREFIX_NAME "platform" - -#define NMLOG_COMMON(level, name, ...) \ - char __prefix[32]; \ - const char * __p_prefix = _NMLOG_PREFIX_NAME; \ - const NMPlatform *const __self = (self); \ - const char * __name = name; \ - \ - if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - __name, \ - NULL, \ - "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix, \ - NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); - -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - NMLOG_COMMON(level, NULL, __VA_ARGS__); \ - } \ - } \ - G_STMT_END - -#define _NMLOG2(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - NMLOG_COMMON(level, name, __VA_ARGS__); \ - } \ - } \ - G_STMT_END - -#define _NMLOG3(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - NMLOG_COMMON(level, \ - ifindex > 0 ? nm_platform_link_get_name(self, ifindex) : NULL, \ - __VA_ARGS__); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = {0}; - -enum { - PROP_0, - PROP_NETNS_SUPPORT, - PROP_USE_UDEV, - PROP_LOG_WITH_PTR, - LAST_PROP, -}; - -typedef struct _NMPlatformPrivate { - bool use_udev : 1; - bool log_with_ptr : 1; - - guint ip4_dev_route_blacklist_check_id; - guint ip4_dev_route_blacklist_gc_timeout_id; - GHashTable * ip4_dev_route_blacklist_hash; - NMDedupMultiIndex *multi_idx; - NMPCache * cache; -} NMPlatformPrivate; - -G_DEFINE_TYPE(NMPlatform, nm_platform, G_TYPE_OBJECT) - -#define NM_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMPlatform, NM_IS_PLATFORM) - -/*****************************************************************************/ - -static void _ip4_dev_route_blacklist_schedule(NMPlatform *self); - -/*****************************************************************************/ - -gboolean -nm_platform_get_use_udev(NMPlatform *self) -{ - return NM_PLATFORM_GET_PRIVATE(self)->use_udev; -} - -gboolean -nm_platform_get_log_with_ptr(NMPlatform *self) -{ - return NM_PLATFORM_GET_PRIVATE(self)->log_with_ptr; -} - -/*****************************************************************************/ - -guint -_nm_platform_signal_id_get(NMPlatformSignalIdType signal_type) -{ - nm_assert(signal_type > 0 && signal_type != NM_PLATFORM_SIGNAL_ID_NONE - && signal_type < _NM_PLATFORM_SIGNAL_ID_LAST); - - return signals[signal_type]; -} - -/*****************************************************************************/ - -/* Singleton NMPlatform subclass instance and cached class object */ -NM_DEFINE_SINGLETON_INSTANCE(NMPlatform); - -NM_DEFINE_SINGLETON_REGISTER(NMPlatform); - -/* Just always initialize a @klass instance. NM_PLATFORM_GET_CLASS() - * is only a plain read on the self instance, which the compiler - * like can optimize out. - */ -#define _CHECK_SELF_VOID(self, klass) \ - NMPlatformClass *klass; \ - do { \ - g_return_if_fail(NM_IS_PLATFORM(self)); \ - klass = NM_PLATFORM_GET_CLASS(self); \ - (void) klass; \ - } while (0) - -#define _CHECK_SELF(self, klass, err_val) \ - NMPlatformClass *klass; \ - do { \ - g_return_val_if_fail(NM_IS_PLATFORM(self), err_val); \ - klass = NM_PLATFORM_GET_CLASS(self); \ - (void) klass; \ - } while (0) - -#define _CHECK_SELF_NETNS(self, klass, netns, err_val) \ - nm_auto_pop_netns NMPNetns *netns = NULL; \ - NMPlatformClass * klass; \ - do { \ - g_return_val_if_fail(NM_IS_PLATFORM(self), err_val); \ - klass = NM_PLATFORM_GET_CLASS(self); \ - (void) klass; \ - if (!nm_platform_netns_push(self, &netns)) \ - return (err_val); \ - } while (0) - -/** - * nm_platform_setup: - * @instance: the #NMPlatform instance - * - * Failing to set up #NMPlatform singleton results in a fatal error, - * as well as trying to initialize it multiple times without freeing - * it. - * - * NetworkManager will typically use only one platform object during - * its run. Test programs might want to switch platform implementations, - * though. - */ -void -nm_platform_setup(NMPlatform *instance) -{ - g_return_if_fail(NM_IS_PLATFORM(instance)); - g_return_if_fail(!singleton_instance); - - singleton_instance = instance; - - nm_singleton_instance_register(); - - nm_log_dbg(LOGD_CORE, - "setup %s singleton (" NM_HASH_OBFUSCATE_PTR_FMT ")", - "NMPlatform", - NM_HASH_OBFUSCATE_PTR(instance)); -} - -/** - * nm_platform_get: - * @self: platform instance - * - * Retrieve #NMPlatform singleton. Use this whenever you want to connect to - * #NMPlatform signals. It is an error to call it before nm_platform_setup(). - * - * Returns: (transfer none): The #NMPlatform singleton reference. - */ -NMPlatform * -nm_platform_get() -{ - g_assert(singleton_instance); - - return singleton_instance; -} - -/*****************************************************************************/ - -NMDedupMultiIndex * -nm_platform_get_multi_idx(NMPlatform *self) -{ - g_return_val_if_fail(NM_IS_PLATFORM(self), NULL); - - return NM_PLATFORM_GET_PRIVATE(self)->multi_idx; -} - -/*****************************************************************************/ - -static NM_UTILS_LOOKUP_STR_DEFINE( - _nmp_nlm_flag_to_string_lookup, - NMPNlmFlags, - NM_UTILS_LOOKUP_DEFAULT(NULL), - NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_ADD, "add"), - NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_CHANGE, "change"), - NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_REPLACE, "replace"), - NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_PREPEND, "prepend"), - NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_APPEND, "append"), - NM_UTILS_LOOKUP_ITEM(NMP_NLM_FLAG_TEST, "test"), - NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_F_APPEND), - NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_FMASK), - NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE), - NM_UTILS_LOOKUP_ITEM_IGNORE(NMP_NLM_FLAG_F_ECHO), ); - -#define _nmp_nlm_flag_to_string(flags) \ - ({ \ - NMPNlmFlags _flags = (flags); \ - \ - _nmp_nlm_flag_to_string_lookup(flags) \ - ?: nm_sprintf_bufa(100, "new[0x%x]", (unsigned) _flags); \ - }) - -/*****************************************************************************/ - -volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPORT_NUM] = {}; - -static const struct { - bool compile_time_default; - const char *name; - const char *desc; -} _nm_platform_kernel_support_info[_NM_PLATFORM_KERNEL_SUPPORT_NUM] = { - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS] = - { - .compile_time_default = TRUE, - .name = "EXTENDED_IFA_FLAGS", - .desc = "IPv6 temporary addresses support", - }, - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL] = - { - .compile_time_default = TRUE, - .name = "USER_IPV6LL", - .desc = "IFLA_INET6_ADDR_GEN_MODE support", - }, - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF] = - { - .compile_time_default = (RTA_MAX >= 20 /* RTA_PREF */), - .name = "RTA_PREF", - .desc = "ability to set router preference for IPv6 routes", - }, - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV] = - { - .compile_time_default = (FRA_MAX >= 19 /* FRA_L3MDEV */), - .name = "FRA_L3MDEV", - .desc = "FRA_L3MDEV attribute for policy routing rules", - }, - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE] = - { - .compile_time_default = (FRA_MAX >= 20 /* FRA_UID_RANGE */), - .name = "FRA_UID_RANGE", - .desc = "FRA_UID_RANGE attribute for policy routing rules", - }, - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL] = - { - .compile_time_default = (FRA_MAX >= 21 /* FRA_PROTOCOL */), - .name = "FRA_PROTOCOL", - .desc = "FRA_PROTOCOL attribute for policy routing rules", - }, - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO] = - { - .compile_time_default = (FRA_MAX >= 22 /* FRA_IP_PROTO */), - .name = "FRA_IP_PROTO", - .desc = "FRA_IP_PROTO, FRA_SPORT_RANGE, FRA_DPORT_RANGE attributes for policy routing " - "rules", - }, - [NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED] = - { - .compile_time_default = (IFLA_BR_MAX >= 41 /* IFLA_BR_VLAN_STATS_ENABLED */), - .name = "IFLA_BR_VLAN_STATS_ENABLE", - .desc = "IFLA_BR_VLAN_STATS_ENABLE bridge link attribute", - }, -}; - -int -_nm_platform_kernel_support_init(NMPlatformKernelSupportType type, int value) -{ - volatile int *p_state; - gboolean set_default = FALSE; - - nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < G_N_ELEMENTS(_nm_platform_kernel_support_state)); - - p_state = &_nm_platform_kernel_support_state[type]; - - if (value == 0) { - set_default = TRUE; - value = _nm_platform_kernel_support_info[type].compile_time_default ? 1 : -1; - } - - nm_assert(NM_IN_SET(value, -1, 1)); - - if (!g_atomic_int_compare_and_exchange(p_state, 0, value)) { - value = g_atomic_int_get(p_state); - nm_assert(NM_IN_SET(value, -1, 1)); - return value; - } - -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 - - if (set_default) { - nm_log_dbg(LOGD_PLATFORM, - "platform: kernel-support for %s (%s) not detected: assume %ssupported", - _nm_platform_kernel_support_info[type].name, - _nm_platform_kernel_support_info[type].desc, - value >= 0 ? "" : "not "); - } else { - nm_log_dbg(LOGD_PLATFORM, - "platform: kernel-support for %s (%s) detected: %ssupported", - _nm_platform_kernel_support_info[type].name, - _nm_platform_kernel_support_info[type].desc, - value >= 0 ? "" : "not "); - } - -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 - - return value; -} - -/*****************************************************************************/ - -/** - * nm_platform_process_events: - * @self: platform instance - * - * Process pending events or handle pending delayed-actions. - * Effectively, this reads the netlink socket and processes - * new netlink messages. Possibly it will raise change signals. - */ -void -nm_platform_process_events(NMPlatform *self) -{ - _CHECK_SELF_VOID(self, klass); - - if (klass->process_events) - klass->process_events(self); -} - -const NMPlatformLink * -nm_platform_process_events_ensure_link(NMPlatform *self, int ifindex, const char *ifname) -{ - const NMPObject *obj; - gboolean refreshed = FALSE; - - g_return_val_if_fail(NM_IS_PLATFORM(self), NULL); - - if (ifindex <= 0 && !ifname) - return NULL; - - /* we look into the cache, whether a link for given ifindex/ifname - * exits. If not, we poll the netlink socket, maybe the event - * with the link is waiting. - * - * Then we try again to find the object. - * - * If the link is already cached the first time, we avoid polling - * the netlink socket. */ -again: - obj = nmp_cache_lookup_link_full( - nm_platform_get_cache(self), - ifindex, - ifname, - FALSE, /* also invisible. We don't care here whether udev is ready */ - NM_LINK_TYPE_NONE, - NULL, - NULL); - if (obj) - return NMP_OBJECT_CAST_LINK(obj); - if (!refreshed) { - refreshed = TRUE; - nm_platform_process_events(self); - goto again; - } - - return NULL; -} - -/*****************************************************************************/ - -/** - * nm_platform_sysctl_open_netdir: - * @self: platform instance - * @ifindex: the ifindex for which to open /sys/class/net/%s - * @out_ifname: optional output argument of the found ifname. - * - * Wraps nmp_utils_sysctl_open_netdir() by first changing into the right - * network-namespace. - * - * Returns: on success, the open file descriptor to the /sys/class/net/%s - * directory. - */ -int -nm_platform_sysctl_open_netdir(NMPlatform *self, int ifindex, char *out_ifname) -{ - const char *ifname_guess; - _CHECK_SELF_NETNS(self, klass, netns, -1); - - g_return_val_if_fail(ifindex > 0, -1); - - /* we don't have an @ifname_guess argument to make the API nicer. - * But still do a cache-lookup first. Chances are good that we have - * the right ifname cached and save if_indextoname() */ - ifname_guess = nm_platform_link_get_name(self, ifindex); - - return nmp_utils_sysctl_open_netdir(ifindex, ifname_guess, out_ifname); -} - -/** - * nm_platform_sysctl_set: - * @self: platform instance - * @pathid: if @dirfd is present, this must be the full path that is looked up. - * It is required for logging. - * @dirfd: optional file descriptor for parent directory for openat() - * @path: Absolute option path - * @value: Value to write - * - * This function is intended to be used for writing values to sysctl-style - * virtual runtime configuration files. This includes not only /proc/sys - * but also for example /sys/class. - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_sysctl_set(NMPlatform *self, - const char *pathid, - int dirfd, - const char *path, - const char *value) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(path, FALSE); - g_return_val_if_fail(value, FALSE); - - return klass->sysctl_set(self, pathid, dirfd, path, value); -} - -/** - * nm_platform_sysctl_set_async: - * @self: platform instance - * @pathid: if @dirfd is present, this must be the full path that is looked up - * @dirfd: optional file descriptor for parent directory for openat() - * @path: absolute option path - * @values: NULL-terminated array of strings to be written - * @callback: function called on termination - * @data: data passed to callback function - * @cancellable: to cancel the operation - * - * This function is intended to be used for writing values to sysctl-style - * virtual runtime configuration files. This includes not only /proc/sys - * but also for example /sys/class. The function does not block and returns - * immediately. The callback is always invoked, and asynchronously. The file - * is closed after writing each value and reopened to write the next one so - * that the function can be used safely on all /proc and /sys files, - * independently of how /proc/sys/kernel/sysctl_writes_strict is configured. - */ -void -nm_platform_sysctl_set_async(NMPlatform * self, - const char * pathid, - int dirfd, - const char * path, - const char *const * values, - NMPlatformAsyncCallback callback, - gpointer data, - GCancellable * cancellable) -{ - _CHECK_SELF_VOID(self, klass); - - klass->sysctl_set_async(self, pathid, dirfd, path, values, callback, data, cancellable); -} - -gboolean -nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(NMPlatform *self, const char *iface, int value) -{ - const char *path; - gint64 cur; - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - _CHECK_SELF(self, klass, FALSE); - - /* the hop-limit provided via RA is uint8. */ - if (value > 0xFF) - return FALSE; - - /* don't allow unreasonable small values */ - if (value < 10) - return FALSE; - - path = nm_utils_sysctl_ip_conf_path(AF_INET6, buf, iface, "hop_limit"); - cur = nm_platform_sysctl_get_int_checked(self, - NMP_SYSCTL_PATHID_ABSOLUTE(path), - 10, - 1, - G_MAXINT32, - -1); - - /* only allow increasing the hop-limit to avoid DOS by an attacker - * setting a low hop-limit (CVE-2015-2924, rh#1209902) */ - - if (value < cur) - return FALSE; - if (value != cur) { - char svalue[20]; - - sprintf(svalue, "%d", value); - nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), svalue); - } - - return TRUE; -} - -gboolean -nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(NMPlatform *self, - const char *iface, - guint value_ms) -{ - char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - char str[128]; - guint clamped; - - _CHECK_SELF(self, klass, FALSE); - - if (!value_ms) - return TRUE; - - /* RFC 4861 says the value can't be greater than one hour. - * Also use a reasonable lower threshold. */ - clamped = NM_CLAMP(value_ms, 100, 3600000); - nm_sprintf_buf(path, "/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms", iface); - nm_sprintf_buf(str, "%u", clamped); - if (!nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), str)) - return FALSE; - - /* Set stale time in the same way as kernel */ - nm_sprintf_buf(path, "/proc/sys/net/ipv6/neigh/%s/gc_stale_time", iface); - nm_sprintf_buf(str, "%u", clamped * 3 / 1000); - - return nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), str); -} - -gboolean -nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(NMPlatform *self, - const char *iface, - guint value_ms) -{ - char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - char str[128]; - - _CHECK_SELF(self, klass, FALSE); - - if (!value_ms) - return TRUE; - - nm_sprintf_buf(path, "/proc/sys/net/ipv6/neigh/%s/retrans_time_ms", iface); - nm_sprintf_buf(str, "%u", NM_CLAMP(value_ms, 10, 3600000)); - - return nm_platform_sysctl_set(self, NMP_SYSCTL_PATHID_ABSOLUTE(path), str); -} - -/** - * nm_platform_sysctl_get: - * @self: platform instance - * @dirfd: if non-negative, used to lookup the path via openat(). - * @pathid: if @dirfd is present, this must be the full path that is looked up. - * It is required for logging. - * @path: Absolute path to sysctl - * - * Returns: (transfer full): Contents of the virtual sysctl file. - * - * If the path does not exist, %NULL is returned and %errno set to %ENOENT. - */ -char * -nm_platform_sysctl_get(NMPlatform *self, const char *pathid, int dirfd, const char *path) -{ - _CHECK_SELF(self, klass, NULL); - - g_return_val_if_fail(path, NULL); - - return klass->sysctl_get(self, pathid, dirfd, path); -} - -/** - * nm_platform_sysctl_get_int32: - * @self: platform instance - * @pathid: if @dirfd is present, this must be the full path that is looked up. - * It is required for logging. - * @dirfd: if non-negative, used to lookup the path via openat(). - * @path: Absolute path to sysctl - * @fallback: default value, if the content of path could not be read - * as decimal integer. - * - * Returns: contents of the sysctl file parsed as s32 integer, or - * @fallback on error. On error, %errno will be set to a non-zero - * value, on success %errno will be set to zero. - */ -gint32 -nm_platform_sysctl_get_int32(NMPlatform *self, - const char *pathid, - int dirfd, - const char *path, - gint32 fallback) -{ - return nm_platform_sysctl_get_int_checked(self, - pathid, - dirfd, - path, - 10, - G_MININT32, - G_MAXINT32, - fallback); -} - -/** - * nm_platform_sysctl_get_int_checked: - * @self: platform instance - * @pathid: if @dirfd is present, this must be the full path that is looked up. - * It is required for logging. - * @dirfd: if non-negative, used to lookup the path via openat(). - * @path: Absolute path to sysctl - * @base: base of numeric conversion - * @min: minimal value that is still valid - * @max: maximal value that is still valid - * @fallback: default value, if the content of path could not be read - * as valid integer. - * - * Returns: contents of the sysctl file parsed as s64 integer, or - * @fallback on error. On error, %errno will be set to a non-zero - * value. On success, %errno will be set to zero. The returned value - * will always be in the range between @min and @max - * (inclusive) or @fallback. - * If the file does not exist, the fallback is returned and %errno - * is set to ENOENT. - */ -gint64 -nm_platform_sysctl_get_int_checked(NMPlatform *self, - const char *pathid, - int dirfd, - const char *path, - guint base, - gint64 min, - gint64 max, - gint64 fallback) -{ - char * value = NULL; - gint32 ret; - int errsv; - - _CHECK_SELF(self, klass, fallback); - - g_return_val_if_fail(path, fallback); - - if (!path) { - errno = EINVAL; - return fallback; - } - - value = nm_platform_sysctl_get(self, pathid, dirfd, path); - if (!value) { - /* nm_platform_sysctl_get() set errno to ENOENT if the file does not exist. - * Propagate/preserve that. */ - if (errno != ENOENT) - errno = EINVAL; - return fallback; - } - - ret = _nm_utils_ascii_str_to_int64(value, base, min, max, fallback); - errsv = errno; - g_free(value); - errno = errsv; - return ret; -} - -/*****************************************************************************/ - -char * -nm_platform_sysctl_ip_conf_get(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property) -{ - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - return nm_platform_sysctl_get( - self, - NMP_SYSCTL_PATHID_ABSOLUTE( - nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property))); -} - -gint64 -nm_platform_sysctl_ip_conf_get_int_checked(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property, - guint base, - gint64 min, - gint64 max, - gint64 fallback) -{ - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - return nm_platform_sysctl_get_int_checked( - self, - NMP_SYSCTL_PATHID_ABSOLUTE( - nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property)), - base, - min, - max, - fallback); -} - -gboolean -nm_platform_sysctl_ip_conf_set(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property, - const char *value) -{ - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - return nm_platform_sysctl_set( - self, - NMP_SYSCTL_PATHID_ABSOLUTE( - nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property)), - value); -} - -gboolean -nm_platform_sysctl_ip_conf_set_int64(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property, - gint64 value) -{ - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - char s[64]; - - return nm_platform_sysctl_set( - self, - NMP_SYSCTL_PATHID_ABSOLUTE( - nm_utils_sysctl_ip_conf_path(addr_family, buf, ifname, property)), - nm_sprintf_buf(s, "%" G_GINT64_FORMAT, value)); -} - -int -nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(NMPlatform *self, - const char *ifname, - gboolean consider_all, - gboolean * out_due_to_all) -{ - int val, val_all; - - NM_SET_OUT(out_due_to_all, FALSE); - - if (!ifname) - return -1; - - val = nm_platform_sysctl_ip_conf_get_int_checked(self, - AF_INET, - ifname, - "rp_filter", - 10, - 0, - 2, - -1); - if (val == -1) - return -1; - - /* the effectively used value is the rp_filter sysctl value of MAX(all,ifname). - * Note that this is the numerical MAX(), despite rp_filter "1" being more strict - * than "2". */ - if (val < 2 && consider_all && !nm_streq(ifname, "all")) { - val_all = nm_platform_sysctl_ip_conf_get_int_checked(self, - AF_INET, - "all", - "rp_filter", - 10, - 0, - 2, - val); - if (val_all > val) { - val = val_all; - NM_SET_OUT(out_due_to_all, TRUE); - } - } - - return val; -} - -/*****************************************************************************/ - -static int -_link_get_all_presort(gconstpointer p_a, gconstpointer p_b, gpointer sort_by_name) -{ - const NMPlatformLink *a = NMP_OBJECT_CAST_LINK(*((const NMPObject **) p_a)); - const NMPlatformLink *b = NMP_OBJECT_CAST_LINK(*((const NMPObject **) p_b)); - - /* Loopback always first */ - if (a->ifindex == 1) - return -1; - if (b->ifindex == 1) - return 1; - - if (GPOINTER_TO_INT(sort_by_name)) { - /* Initialized links first */ - if (a->initialized > b->initialized) - return -1; - if (a->initialized < b->initialized) - return 1; - - return strcmp(a->name, b->name); - } else - return a->ifindex - b->ifindex; -} - -/** - * nm_platform_link_get_all: - * @self: platform instance - * @sort_by_name: whether to sort by name or ifindex. - * - * Retrieve a snapshot of configuration for all links at once. The result is - * owned by the caller and should be freed with g_ptr_array_unref(). - */ -GPtrArray * -nm_platform_link_get_all(NMPlatform *self, gboolean sort_by_name) -{ - gs_unref_ptrarray GPtrArray *links = NULL; - GPtrArray * result; - guint i, nresult; - gs_unref_hashtable GHashTable *unseen = NULL; - const NMPlatformLink * item; - NMPLookup lookup; - - _CHECK_SELF(self, klass, NULL); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK); - links = nm_dedup_multi_objs_to_ptr_array_head(nm_platform_lookup(self, &lookup), NULL, NULL); - if (!links) - return NULL; - - for (i = 0; i < links->len;) { - if (!nmp_object_is_visible(links->pdata[i])) - g_ptr_array_remove_index_fast(links, i); - else - i++; - } - - if (links->len == 0) - return NULL; - - /* first sort the links by their ifindex or name. Below we will sort - * further by moving children/slaves to the end. */ - g_ptr_array_sort_with_data(links, _link_get_all_presort, GINT_TO_POINTER(sort_by_name)); - - unseen = g_hash_table_new(nm_direct_hash, NULL); - for (i = 0; i < links->len; i++) { - item = NMP_OBJECT_CAST_LINK(links->pdata[i]); - nm_assert(item->ifindex > 0); - if (!g_hash_table_insert(unseen, GINT_TO_POINTER(item->ifindex), NULL)) - nm_assert_not_reached(); - } - -#if NM_MORE_ASSERTS - /* Ensure that link_get_all returns a consistent and valid result. */ - for (i = 0; i < links->len; i++) { - item = NMP_OBJECT_CAST_LINK(links->pdata[i]); - - if (!item->ifindex) - continue; - if (item->master != 0) { - g_warn_if_fail(item->master > 0); - g_warn_if_fail(item->master != item->ifindex); - g_warn_if_fail(g_hash_table_contains(unseen, GINT_TO_POINTER(item->master))); - } - if (item->parent != 0) { - if (item->parent != NM_PLATFORM_LINK_OTHER_NETNS) { - g_warn_if_fail(item->parent > 0); - g_warn_if_fail(item->parent != item->ifindex); - g_warn_if_fail(g_hash_table_contains(unseen, GINT_TO_POINTER(item->parent))); - } - } - } -#endif - - /* Re-order the links list such that children/slaves come after all ancestors */ - nm_assert(g_hash_table_size(unseen) == links->len); - nresult = links->len; - result = g_ptr_array_new_full(nresult, (GDestroyNotify) nmp_object_unref); - - while (TRUE) { - gboolean found_something = FALSE; - guint first_idx = G_MAXUINT; - - for (i = 0; i < links->len; i++) { - item = NMP_OBJECT_CAST_LINK(links->pdata[i]); - - if (!item) - continue; - - g_assert(g_hash_table_contains(unseen, GINT_TO_POINTER(item->ifindex))); - - if (item->master > 0 && g_hash_table_contains(unseen, GINT_TO_POINTER(item->master))) - goto skip; - if (item->parent > 0 && g_hash_table_contains(unseen, GINT_TO_POINTER(item->parent))) - goto skip; - - g_hash_table_remove(unseen, GINT_TO_POINTER(item->ifindex)); - g_ptr_array_add(result, links->pdata[i]); - links->pdata[i] = NULL; - found_something = TRUE; - continue; -skip: - if (first_idx == G_MAXUINT) - first_idx = i; - } - - if (found_something) { - if (first_idx == G_MAXUINT) - break; - } else { - nm_assert(first_idx != G_MAXUINT); - /* There is a loop, pop the first (remaining) element from the list. - * This can happen for veth pairs where each peer is parent of the other end. */ - item = NMP_OBJECT_CAST_LINK(links->pdata[first_idx]); - nm_assert(item); - g_hash_table_remove(unseen, GINT_TO_POINTER(item->ifindex)); - g_ptr_array_add(result, links->pdata[first_idx]); - links->pdata[first_idx] = NULL; - } - nm_assert(result->len < nresult); - } - nm_assert(result->len == nresult); - - return result; -} - -/*****************************************************************************/ - -const NMPObject * -nm_platform_link_get_obj(NMPlatform *self, int ifindex, gboolean visible_only) -{ - const NMPObject *obj_cache; - - _CHECK_SELF(self, klass, NULL); - - obj_cache = nmp_cache_lookup_link(nm_platform_get_cache(self), ifindex); - if (!obj_cache || (visible_only && !nmp_object_is_visible(obj_cache))) - return NULL; - return obj_cache; -} - -/*****************************************************************************/ - -/** - * nm_platform_link_get: - * @self: platform instance - * @ifindex: ifindex of the link - * - * Lookup the internal NMPlatformLink object. - * - * Returns: %NULL, if such a link exists or the internal - * platform link object. Do not modify the returned value. - * Also, be aware that any subsequent platform call might - * invalidate/modify the returned instance. - **/ -const NMPlatformLink * -nm_platform_link_get(NMPlatform *self, int ifindex) -{ - return NMP_OBJECT_CAST_LINK(nm_platform_link_get_obj(self, ifindex, TRUE)); -} - -/** - * nm_platform_link_get_by_ifname: - * @self: platform instance - * @ifname: the ifname - * - * Returns: the first #NMPlatformLink instance with the given name. - **/ -const NMPlatformLink * -nm_platform_link_get_by_ifname(NMPlatform *self, const char *ifname) -{ - const NMPObject *obj; - - _CHECK_SELF(self, klass, NULL); - - if (!ifname || !*ifname) - return NULL; - - obj = nmp_cache_lookup_link_full(nm_platform_get_cache(self), - 0, - ifname, - TRUE, - NM_LINK_TYPE_NONE, - NULL, - NULL); - return NMP_OBJECT_CAST_LINK(obj); -} - -struct _nm_platform_link_get_by_address_data { - gconstpointer data; - guint8 len; -}; - -static gboolean -_nm_platform_link_get_by_address_match_link(const NMPObject * obj, - struct _nm_platform_link_get_by_address_data *d) -{ - return obj->link.l_address.len == d->len && !memcmp(obj->link.l_address.data, d->data, d->len); -} - -/** - * nm_platform_link_get_by_address: - * @self: platform instance - * @address: a pointer to the binary hardware address - * @length: the size of @address in bytes - * - * Returns: the first #NMPlatformLink object with a matching - * address. - **/ -const NMPlatformLink * -nm_platform_link_get_by_address(NMPlatform * self, - NMLinkType link_type, - gconstpointer address, - size_t length) -{ - const NMPObject * obj; - struct _nm_platform_link_get_by_address_data d = { - .data = address, - .len = length, - }; - - _CHECK_SELF(self, klass, NULL); - - if (length == 0) - return NULL; - - if (length > NM_UTILS_HWADDR_LEN_MAX) - g_return_val_if_reached(NULL); - if (!address) - g_return_val_if_reached(NULL); - - obj = nmp_cache_lookup_link_full(nm_platform_get_cache(self), - 0, - NULL, - TRUE, - link_type, - (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, - &d); - return NMP_OBJECT_CAST_LINK(obj); -} - -static int -_link_add_check_existing(NMPlatform * self, - const char * name, - NMLinkType type, - const NMPlatformLink **out_link) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get_by_ifname(self, name); - if (pllink) { - gboolean wrong_type; - - wrong_type = type != NM_LINK_TYPE_NONE && pllink->type != type; - _LOG2D("link: skip adding link due to existing interface of type %s%s%s", - nm_link_type_to_string(pllink->type), - wrong_type ? ", expected " : "", - wrong_type ? nm_link_type_to_string(type) : ""); - if (out_link) - *out_link = pllink; - if (wrong_type) - return -NME_PL_WRONG_TYPE; - return -NME_PL_EXISTS; - } - if (out_link) - *out_link = NULL; - return 0; -} - -/** - * nm_platform_link_add: - * @self: platform instance - * @type: Interface type - * @name: Interface name - * @parent: the IFLA_LINK parameter or 0. - * @address: (allow-none): set the mac address of the link - * @address_len: the length of the @address - * @extra_data: depending on @type, additional data. - * @out_link: on success, the link object - * - * Add a software interface. If the interface already exists and is of type - * @type, return -NME_PL_EXISTS and returns the link - * in @out_link. If the interface already exists and is not of type @type, - * return -NME_PL_WRONG_TYPE. - * - * Any link-changed ADDED signal will be emitted directly, before this - * function finishes. - * - * Returns: the negative nm-error on failure. - */ -int -nm_platform_link_add(NMPlatform * self, - NMLinkType type, - const char * name, - int parent, - const void * address, - size_t address_len, - guint32 mtu, - gconstpointer extra_data, - const NMPlatformLink **out_link) -{ - int r; - char addr_buf[NM_UTILS_HWADDR_LEN_MAX * 3]; - char mtu_buf[16]; - char parent_buf[64]; - char buf[512]; - - _CHECK_SELF(self, klass, -NME_BUG); - - g_return_val_if_fail(name, -NME_BUG); - g_return_val_if_fail((address != NULL) ^ (address_len == 0), -NME_BUG); - g_return_val_if_fail(address_len <= NM_UTILS_HWADDR_LEN_MAX, -NME_BUG); - g_return_val_if_fail(parent >= 0, -NME_BUG); - - r = _link_add_check_existing(self, name, type, out_link); - if (r < 0) - return r; - - _LOG2D("link: adding link: " - "%s " /* type */ - "\"%s\"" /* name */ - "%s%s" /* parent */ - "%s%s" /* address */ - "%s%s" /* mtu */ - "%s" /* extra_data */ - "", - nm_link_type_to_string(type), - name, - parent > 0 ? ", parent " : "", - parent > 0 ? nm_sprintf_buf(parent_buf, "%d", parent) : "", - address ? ", address: " : "", - address ? _nm_utils_hwaddr_ntoa(address, address_len, FALSE, addr_buf, sizeof(addr_buf)) - : "", - mtu ? ", mtu: " : "", - mtu ? nm_sprintf_buf(mtu_buf, "%u", mtu) : "", - ({ - char *buf_p = buf; - gsize buf_len = sizeof(buf); - - buf[0] = '\0'; - - switch (type) { - case NM_LINK_TYPE_BRIDGE: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_bridge_to_string((const NMPlatformLnkBridge *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_VLAN: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_vlan_to_string((const NMPlatformLnkVlan *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_VRF: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_vrf_to_string((const NMPlatformLnkVrf *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_VXLAN: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_vxlan_to_string((const NMPlatformLnkVxlan *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_VETH: - nm_sprintf_buf(buf, ", veth-peer \"%s\"", (const char *) extra_data); - break; - case NM_LINK_TYPE_GRE: - case NM_LINK_TYPE_GRETAP: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_gre_to_string((const NMPlatformLnkGre *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_SIT: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_sit_to_string((const NMPlatformLnkSit *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_IP6TNL: - case NM_LINK_TYPE_IP6GRE: - case NM_LINK_TYPE_IP6GRETAP: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_ip6tnl_to_string((const NMPlatformLnkIp6Tnl *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_IPIP: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_ipip_to_string((const NMPlatformLnkIpIp *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_MACSEC: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_macsec_to_string((const NMPlatformLnkMacsec *) extra_data, - buf_p, - buf_len); - break; - case NM_LINK_TYPE_MACVLAN: - case NM_LINK_TYPE_MACVTAP: - nm_utils_strbuf_append_str(&buf_p, &buf_len, ", "); - nm_platform_lnk_macvlan_to_string((const NMPlatformLnkMacvlan *) extra_data, - buf_p, - buf_len); - break; - default: - nm_assert(!extra_data); - break; - } - - buf; - })); - - return klass - ->link_add(self, type, name, parent, address, address_len, mtu, extra_data, out_link); -} - -/** - * nm_platform_link_delete: - * @self: platform instance - * @ifindex: Interface index - */ -gboolean -nm_platform_link_delete(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - _LOG3D("link: deleting"); - return klass->link_delete(self, ifindex); -} - -/** - * nm_platform_link_set_netns: - * @self: platform instance - * @ifindex: Interface index - * @netns_fd: the file descriptor for the new netns. - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_link_set_netns(NMPlatform *self, int ifindex, int netns_fd) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(netns_fd > 0, FALSE); - - _LOG3D("link: move link to network namespace with fd %d", netns_fd); - return klass->link_set_netns(self, ifindex, netns_fd); -} - -/** - * nm_platform_link_get_index: - * @self: platform instance - * @name: Interface name - * - * Returns: The interface index corresponding to the given interface name - * or 0. Interface name is owned by #NMPlatform, don't free it. - */ -int -nm_platform_link_get_ifindex(NMPlatform *self, const char *name) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get_by_ifname(self, name); - return pllink ? pllink->ifindex : 0; -} - -const char * -nm_platform_if_indextoname(NMPlatform *self, int ifindex, char out_ifname[static 16 /* IFNAMSIZ */]) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - return nmp_utils_if_indextoname(ifindex, out_ifname); -} - -int -nm_platform_if_nametoindex(NMPlatform *self, const char *ifname) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - return nmp_utils_if_nametoindex(ifname); -} - -/** - * nm_platform_link_get_name: - * @self: platform instance - * @name: Interface name - * - * Returns: The interface name corresponding to the given interface index - * or %NULL. - */ -const char * -nm_platform_link_get_name(NMPlatform *self, int ifindex) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get(self, ifindex); - return pllink ? pllink->name : NULL; -} - -/** - * nm_platform_link_get_type: - * @self: platform instance - * @ifindex: Interface index. - * - * Returns: Link type constant as defined in nm-platform.h. On error, - * NM_LINK_TYPE_NONE is returned. - */ -NMLinkType -nm_platform_link_get_type(NMPlatform *self, int ifindex) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get(self, ifindex); - return pllink ? pllink->type : NM_LINK_TYPE_NONE; -} - -/** - * nm_platform_link_get_type_name: - * @self: platform instance - * @ifindex: Interface index. - * - * Returns: A string describing the type of link. In some cases this - * may be more specific than nm_platform_link_get_type(), but in - * other cases it may not. On error, %NULL is returned. - */ -const char * -nm_platform_link_get_type_name(NMPlatform *self, int ifindex) -{ - const NMPObject *obj; - - obj = nm_platform_link_get_obj(self, ifindex, TRUE); - if (!obj) - return NULL; - - if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { - /* We could detect the @link_type. In this case the function returns - * our internal module names, which differs from rtnl_link_get_type(): - * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") - * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). - * Note that this functions is only used by NMDeviceGeneric to - * set type_description. */ - return nm_link_type_to_string(obj->link.type); - } - /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ - return obj->link.kind ?: "unknown"; -} - -static gboolean -link_get_udev_property(NMPlatform *self, int ifindex, const char *name, const char **out_value) -{ - struct udev_device *udevice = NULL; - const char * uproperty; - - udevice = nm_platform_link_get_udev_device(self, ifindex); - if (!udevice) - return FALSE; - - uproperty = udev_device_get_property_value(udevice, name); - if (!uproperty) - return FALSE; - - NM_SET_OUT(out_value, uproperty); - return TRUE; -} - -/** - * nm_platform_link_get_unmanaged: - * @self: platform instance - * @ifindex: interface index - * @unmanaged: management status (in case %TRUE is returned) - * - * Returns: %TRUE if platform overrides NM default-unmanaged status, - * %FALSE otherwise (with @unmanaged unmodified). - */ -gboolean -nm_platform_link_get_unmanaged(NMPlatform *self, int ifindex, gboolean *unmanaged) -{ - const char *value; - - if (link_get_udev_property(self, ifindex, "NM_UNMANAGED", &value)) { - NM_SET_OUT(unmanaged, nm_udev_utils_property_as_boolean(value)); - return TRUE; - } - - return FALSE; -} - -/** - * nm_platform_link_is_software: - * @self: platform instance - * @ifindex: Interface index. - * - * Returns: %TRUE if ifindex belongs to a software interface, not backed by - * a physical device. - */ -gboolean -nm_platform_link_is_software(NMPlatform *self, int ifindex) -{ - return nm_link_type_is_software(nm_platform_link_get_type(self, ifindex)); -} - -/** - * nm_platform_link_supports_slaves: - * @self: platform instance - * @ifindex: Interface index. - * - * Returns: %TRUE if ifindex belongs to an interface capable of enslaving - * other interfaces. - */ -gboolean -nm_platform_link_supports_slaves(NMPlatform *self, int ifindex) -{ - return nm_link_type_supports_slaves(nm_platform_link_get_type(self, ifindex)); -} - -/** - * nm_platform_link_refresh: - * @self: platform instance - * @ifindex: Interface index - * - * Reload the cache for ifindex synchronously. - */ -gboolean -nm_platform_link_refresh(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (klass->link_refresh) - return klass->link_refresh(self, ifindex); - - return TRUE; -} - -int -nm_platform_link_get_ifi_flags(NMPlatform *self, int ifindex, guint requested_flags) -{ - const NMPlatformLink *pllink; - - /* include invisible links (only in netlink, not udev). */ - pllink = NMP_OBJECT_CAST_LINK(nm_platform_link_get_obj(self, ifindex, FALSE)); - if (!pllink) - return -ENODEV; - - /* Errors are signaled as negative values. That means, you cannot request - * the most significant bit (2^31) with this API. Assert against that. */ - nm_assert((int) requested_flags >= 0); - nm_assert(requested_flags < (guint) G_MAXINT); - - return (int) (pllink->n_ifi_flags & requested_flags); -} - -/** - * nm_platform_link_is_up: - * @self: platform instance - * @ifindex: Interface index - * - * Check if the interface is up. - */ -gboolean -nm_platform_link_is_up(NMPlatform *self, int ifindex) -{ - return nm_platform_link_get_ifi_flags(self, ifindex, IFF_UP) == IFF_UP; -} - -/** - * nm_platform_link_is_connected: - * @self: platform instance - * @ifindex: Interface index - * - * Check if the interface is connected. - */ -gboolean -nm_platform_link_is_connected(NMPlatform *self, int ifindex) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get(self, ifindex); - return pllink ? pllink->connected : FALSE; -} - -/** - * nm_platform_link_uses_arp: - * @self: platform instance - * @ifindex: Interface index - * - * Check if the interface is configured to use ARP. - */ -gboolean -nm_platform_link_uses_arp(NMPlatform *self, int ifindex) -{ - int f; - - f = nm_platform_link_get_ifi_flags(self, ifindex, IFF_NOARP); - - if (f < 0) - return FALSE; - if (f == IFF_NOARP) - return FALSE; - return TRUE; -} - -/** - * nm_platform_link_set_ipv6_token: - * @self: platform instance - * @ifindex: Interface index - * @iid: Tokenized interface identifier - * - * Sets then IPv6 tokenized interface identifier. - * - * Returns: %TRUE a tokenized identifier was available - */ -gboolean -nm_platform_link_set_ipv6_token(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - if (klass->link_set_token) - return klass->link_set_token(self, ifindex, iid); - return FALSE; -} - -const char * -nm_platform_link_get_udi(NMPlatform *self, int ifindex) -{ - struct udev_device *device; - - device = nm_platform_link_get_udev_device(self, ifindex); - return device ? udev_device_get_syspath(device) : NULL; -} - -const char * -nm_platform_link_get_path(NMPlatform *self, int ifindex) -{ - const char *value = NULL; - - link_get_udev_property(self, ifindex, "ID_PATH", &value); - - return value; -} - -struct udev_device * -nm_platform_link_get_udev_device(NMPlatform *self, int ifindex) -{ - const NMPObject *obj_cache; - - obj_cache = nm_platform_link_get_obj(self, ifindex, FALSE); - return obj_cache ? obj_cache->_link.udev.device : NULL; -} - -/** - * nm_platform_link_get_user_ip6vll_enabled: - * @self: platform instance - * @ifindex: Interface index - * - * Check whether NM handles IPv6LL address creation for the link. If the - * platform or OS doesn't support changing the IPv6LL address mode, this call - * will fail and return %FALSE. - * - * Returns: %TRUE if NM handles the IPv6LL address for @ifindex - */ -gboolean -nm_platform_link_get_user_ipv6ll_enabled(NMPlatform *self, int ifindex) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get(self, ifindex); - if (pllink && pllink->inet6_addr_gen_mode_inv) - return _nm_platform_uint8_inv(pllink->inet6_addr_gen_mode_inv) == NM_IN6_ADDR_GEN_MODE_NONE; - return FALSE; -} - -/** - * nm_platform_link_set_user_ip6vll_enabled: - * @self: platform instance - * @ifindex: Interface index - * - * Set whether NM handles IPv6LL address creation for the link. If the - * platform or OS doesn't support changing the IPv6LL address mode, this call - * will fail and return %FALSE. - * - * Returns: the negative nm-error on failure. - */ -int -nm_platform_link_set_user_ipv6ll_enabled(NMPlatform *self, int ifindex, gboolean enabled) -{ - _CHECK_SELF(self, klass, -NME_BUG); - - g_return_val_if_fail(ifindex > 0, -NME_BUG); - - return klass->link_set_user_ipv6ll_enabled(self, ifindex, enabled); -} - -/** - * nm_platform_link_set_address: - * @self: platform instance - * @ifindex: Interface index - * @address: The new MAC address - * - * Set interface MAC address. - */ -int -nm_platform_link_set_address(NMPlatform *self, int ifindex, gconstpointer address, size_t length) -{ - gs_free char *mac = NULL; - - _CHECK_SELF(self, klass, -NME_BUG); - - g_return_val_if_fail(ifindex > 0, -NME_BUG); - g_return_val_if_fail(address, -NME_BUG); - g_return_val_if_fail(length > 0, -NME_BUG); - - _LOG3D("link: setting hardware address to %s", (mac = nm_utils_hwaddr_ntoa(address, length))); - return klass->link_set_address(self, ifindex, address, length); -} - -/** - * nm_platform_link_get_address: - * @self: platform instance - * @ifindex: Interface index - * @length: Pointer to a variable to store address length - * - * Returns: the interface hardware address as an array of bytes of - * length @length. - */ -gconstpointer -nm_platform_link_get_address(NMPlatform *self, int ifindex, size_t *length) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get(self, ifindex); - return nmp_link_address_get(pllink ? &pllink->l_address : NULL, length); -} - -/** - * nm_platform_link_get_permanent_address: - * @self: platform instance - * @ifindex: Interface index - * @buf: buffer of at least %NM_UTILS_HWADDR_LEN_MAX bytes, on success - * the permanent hardware address - * @length: Pointer to a variable to store address length - * - * Returns: %TRUE on success, %FALSE on failure to read the permanent hardware - * address. - */ -gboolean -nm_platform_link_get_permanent_address(NMPlatform *self, int ifindex, guint8 *buf, size_t *length) -{ - _CHECK_SELF(self, klass, FALSE); - - if (length) - *length = 0; - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(buf, FALSE); - g_return_val_if_fail(length, FALSE); - - if (klass->link_get_permanent_address) - return klass->link_get_permanent_address(self, ifindex, buf, length); - return FALSE; -} - -gboolean -nm_platform_link_supports_carrier_detect(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - return klass->link_supports_carrier_detect(self, ifindex); -} - -gboolean -nm_platform_link_supports_vlans(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - return klass->link_supports_vlans(self, ifindex); -} - -gboolean -nm_platform_link_supports_sriov(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - return klass->link_supports_sriov(self, ifindex); -} - -/** - * nm_platform_link_set_sriov_params: - * @self: platform instance - * @ifindex: the index of the interface to change - * @num_vfs: the number of VFs to create - * @autoprobe: the new autoprobe-drivers value (pass - * %NM_TERNARY_DEFAULT to keep current value) - * @callback: called when the operation finishes - * @callback_data: data passed to @callback - * @cancellable: cancellable to abort the operation - * - * Sets SR-IOV parameters asynchronously without - * blocking the main thread. The callback function is - * always invoked, and asynchronously. - */ -void -nm_platform_link_set_sriov_params_async(NMPlatform * self, - int ifindex, - guint num_vfs, - NMTernary autoprobe, - NMPlatformAsyncCallback callback, - gpointer callback_data, - GCancellable * cancellable) -{ - _CHECK_SELF_VOID(self, klass); - - g_return_if_fail(ifindex > 0); - - _LOG3D("link: setting %u total VFs and autoprobe %d", num_vfs, (int) autoprobe); - klass->link_set_sriov_params_async(self, - ifindex, - num_vfs, - autoprobe, - callback, - callback_data, - cancellable); -} - -gboolean -nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs) -{ - guint i; - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - _LOG3D("link: setting VFs"); - for (i = 0; vfs[i]; i++) { - const NMPlatformVF *vf = vfs[i]; - - _LOG3D("link: VF %s", nm_platform_vf_to_string(vf, NULL, 0)); - } - - return klass->link_set_sriov_vfs(self, ifindex, vfs); -} - -gboolean -nm_platform_link_set_bridge_vlans(NMPlatform * self, - int ifindex, - gboolean on_master, - const NMPlatformBridgeVlan *const *vlans) -{ - guint i; - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - _LOG3D("link: %s bridge VLANs on %s", - vlans ? "setting" : "clearing", - on_master ? "master" : "self"); - if (vlans) { - for (i = 0; vlans[i]; i++) { - const NMPlatformBridgeVlan *vlan = vlans[i]; - - _LOG3D("link: bridge VLAN %s", nm_platform_bridge_vlan_to_string(vlan, NULL, 0)); - } - } - - return klass->link_set_bridge_vlans(self, ifindex, on_master, vlans); -} - -/** - * nm_platform_link_set_up: - * @self: platform instance - * @ifindex: Interface index - * @out_no_firmware: (allow-none): if the failure reason is due to missing firmware. - * - * Bring the interface up. - */ -gboolean -nm_platform_link_set_up(NMPlatform *self, int ifindex, gboolean *out_no_firmware) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - _LOG3D("link: setting up"); - return klass->link_set_up(self, ifindex, out_no_firmware); -} - -/** - * nm_platform_link_set_down: - * @self: platform instance - * @ifindex: Interface index - * - * Take the interface down. - */ -gboolean -nm_platform_link_set_down(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - _LOG3D("link: setting down"); - return klass->link_set_down(self, ifindex); -} - -/** - * nm_platform_link_set_arp: - * @self: platform instance - * @ifindex: Interface index - * - * Enable ARP on the interface. - */ -gboolean -nm_platform_link_set_arp(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - _LOG3D("link: setting arp"); - return klass->link_set_arp(self, ifindex); -} - -/** - * nm_platform_link_set_noarp: - * @self: platform instance - * @ifindex: Interface index - * - * Disable ARP on the interface. - */ -gboolean -nm_platform_link_set_noarp(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - _LOG3D("link: setting noarp"); - return klass->link_set_noarp(self, ifindex); -} - -/** - * nm_platform_link_set_mtu: - * @self: platform instance - * @ifindex: Interface index - * @mtu: The new MTU value - * - * Set interface MTU. - */ -int -nm_platform_link_set_mtu(NMPlatform *self, int ifindex, guint32 mtu) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - g_return_val_if_fail(mtu > 0, FALSE); - - _LOG3D("link: setting mtu %" G_GUINT32_FORMAT, mtu); - return klass->link_set_mtu(self, ifindex, mtu); -} - -/** - * nm_platform_link_get_mtu: - * @self: platform instance - * @ifindex: Interface index - * - * Returns: MTU value for the interface or 0 on error. - */ -guint32 -nm_platform_link_get_mtu(NMPlatform *self, int ifindex) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get(self, ifindex); - return pllink ? pllink->mtu : 0; -} - -/** - * nm_platform_link_set_name: - * @self: platform instance - * @ifindex: Interface index - * @name: The new interface name - * - * Set interface name. - */ -gboolean -nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - g_return_val_if_fail(name, FALSE); - - _LOG3D("link: setting name %s", name); - - if (strlen(name) + 1 > IFNAMSIZ) - return FALSE; - - return klass->link_set_name(self, ifindex, name); -} - -/** - * nm_platform_link_get_physical_port_id: - * @self: platform instance - * @ifindex: Interface index - * - * The physical port ID, if present, indicates some unique identifier of - * the parent interface (eg, the physical port of which this link is a child). - * Two links that report the same physical port ID can be assumed to be - * children of the same physical port and may share resources that limit - * their abilities. - * - * Returns: physical port ID for the interface, or %NULL on error - * or if the interface has no physical port ID. - */ -char * -nm_platform_link_get_physical_port_id(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, NULL); - - g_return_val_if_fail(ifindex >= 0, NULL); - - if (klass->link_get_physical_port_id) - return klass->link_get_physical_port_id(self, ifindex); - return NULL; -} - -/** - * nm_platform_link_get_dev_id: - * @self: platform instance - * @ifindex: Interface index - * - * In contrast to the physical device ID (which indicates which parent a - * child has) the device ID differentiates sibling devices that may share - * the same MAC address. - * - * Returns: device ID for the interface, or 0 on error or if the - * interface has no device ID. - */ -guint -nm_platform_link_get_dev_id(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, 0); - - g_return_val_if_fail(ifindex >= 0, 0); - - if (klass->link_get_dev_id) - return klass->link_get_dev_id(self, ifindex); - return 0; -} - -/** - * nm_platform_link_get_wake_onlan: - * @self: platform instance - * @ifindex: Interface index - * - * Returns: the "Wake-on-LAN" status for @ifindex. - */ -gboolean -nm_platform_link_get_wake_on_lan(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - if (klass->link_get_wake_on_lan) - return klass->link_get_wake_on_lan(self, ifindex); - return FALSE; -} - -/** - * nm_platform_link_get_driver_info: - * @self: platform instance - * @ifindex: Interface index - * @out_driver_name: (transfer full): on success, the driver name if available - * @out_driver_version: (transfer full): on success, the driver version if available - * @out_fw_version: (transfer full): on success, the firmware version if available - * - * Returns: %TRUE on success (though @out_driver_name, @out_driver_version and - * @out_fw_version can be %NULL if no information was available), %FALSE on - * failure. - */ -gboolean -nm_platform_link_get_driver_info(NMPlatform *self, - int ifindex, - char ** out_driver_name, - char ** out_driver_version, - char ** out_fw_version) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex >= 0, FALSE); - - return klass->link_get_driver_info(self, - ifindex, - out_driver_name, - out_driver_version, - out_fw_version); -} - -/** - * nm_platform_link_enslave: - * @self: platform instance - * @master: Interface index of the master - * @ifindex: Interface index of the slave - * - * Enslave @ifindex to @master. - */ -gboolean -nm_platform_link_enslave(NMPlatform *self, int master, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(master > 0, FALSE); - g_return_val_if_fail(ifindex > 0, FALSE); - - _LOG3D("link: enslaving to master '%s'", nm_platform_link_get_name(self, master)); - return klass->link_enslave(self, master, ifindex); -} - -/** - * nm_platform_link_release: - * @self: platform instance - * @master: Interface index of the master - * @ifindex: Interface index of the slave - * - * Release @slave from @master. - */ -gboolean -nm_platform_link_release(NMPlatform *self, int master, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(master > 0, FALSE); - g_return_val_if_fail(ifindex > 0, FALSE); - - if (nm_platform_link_get_master(self, ifindex) != master) - return FALSE; - - _LOG3D("link: releasing from master '%s'", nm_platform_link_get_name(self, master)); - return klass->link_release(self, master, ifindex); -} - -/** - * nm_platform_link_get_master: - * @self: platform instance - * @slave: Interface index of the slave. - * - * Returns: Interface index of the slave's master. - */ -int -nm_platform_link_get_master(NMPlatform *self, int slave) -{ - const NMPlatformLink *pllink; - - pllink = nm_platform_link_get(self, slave); - return pllink ? pllink->master : 0; -} - -/*****************************************************************************/ - -gboolean -nm_platform_link_can_assume(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - if (klass->link_can_assume) - return klass->link_can_assume(self, ifindex); - g_return_val_if_reached(FALSE); -} - -/*****************************************************************************/ - -/** - * nm_platform_link_get_lnk: - * @self: the platform instance - * @ifindex: the link ifindex to lookup - * @link_type: filter by link-type. - * @out_link: (allow-none): returns the platform link instance - * - * If the function returns %NULL, that could mean that no such ifindex - * exists, of that the link has no lnk data. You can find that out - * by checking @out_link. @out_link will always be set if a link - * with @ifindex exists. - * - * If @link_type is %NM_LINK_TYPE_NONE, the function returns the lnk - * object if it is present. If you set link-type, you can be sure - * that only a link type of the matching type is returned (or %NULL). - * - * Returns: the internal link lnk object. The returned object - * is owned by the platform cache and must not be modified. Note - * however, that the object is guaranteed to be immutable, so - * you can safely take a reference and keep it for yourself - * (but don't modify it). - */ -const NMPObject * -nm_platform_link_get_lnk(NMPlatform * self, - int ifindex, - NMLinkType link_type, - const NMPlatformLink **out_link) -{ - const NMPObject *obj; - - obj = nm_platform_link_get_obj(self, ifindex, TRUE); - if (!obj) { - NM_SET_OUT(out_link, NULL); - return NULL; - } - - NM_SET_OUT(out_link, &obj->link); - - if (!obj->_link.netlink.lnk) - return NULL; - if (link_type != NM_LINK_TYPE_NONE - && (link_type != obj->link.type - || link_type != NMP_OBJECT_GET_CLASS(obj->_link.netlink.lnk)->lnk_link_type)) - return NULL; - - return obj->_link.netlink.lnk; -} - -static gconstpointer -_link_get_lnk(NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) -{ - const NMPObject *lnk; - - lnk = nm_platform_link_get_lnk(self, ifindex, link_type, out_link); - return lnk ? &lnk->object : NULL; -} - -const NMPlatformLnkBridge * -nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_BRIDGE, out_link); -} - -const NMPlatformLnkGre * -nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_GRE, out_link); -} - -const NMPlatformLnkGre * -nm_platform_link_get_lnk_gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_GRETAP, out_link); -} - -const NMPlatformLnkInfiniband * -nm_platform_link_get_lnk_infiniband(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_INFINIBAND, out_link); -} - -const NMPlatformLnkIp6Tnl * -nm_platform_link_get_lnk_ip6tnl(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IP6TNL, out_link); -} - -const NMPlatformLnkIp6Tnl * -nm_platform_link_get_lnk_ip6gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IP6GRE, out_link); -} - -const NMPlatformLnkIp6Tnl * -nm_platform_link_get_lnk_ip6gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IP6GRETAP, out_link); -} - -const NMPlatformLnkIpIp * -nm_platform_link_get_lnk_ipip(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_IPIP, out_link); -} - -const NMPlatformLnkMacsec * -nm_platform_link_get_lnk_macsec(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_MACSEC, out_link); -} - -const NMPlatformLnkMacvlan * -nm_platform_link_get_lnk_macvlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_MACVLAN, out_link); -} - -const NMPlatformLnkMacvlan * -nm_platform_link_get_lnk_macvtap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_MACVTAP, out_link); -} - -const NMPlatformLnkSit * -nm_platform_link_get_lnk_sit(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_SIT, out_link); -} - -const NMPlatformLnkTun * -nm_platform_link_get_lnk_tun(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_TUN, out_link); -} - -const NMPlatformLnkVlan * -nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VLAN, out_link); -} - -const NMPlatformLnkVrf * -nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VRF, out_link); -} - -const NMPlatformLnkVxlan * -nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_VXLAN, out_link); -} - -const NMPlatformLnkWireGuard * -nm_platform_link_get_lnk_wireguard(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) -{ - return _link_get_lnk(self, ifindex, NM_LINK_TYPE_WIREGUARD, out_link); -} - -/*****************************************************************************/ - -static NM_UTILS_FLAGS2STR_DEFINE( - _wireguard_change_flags_to_string, - NMPlatformWireGuardChangeFlags, - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE, "none"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS, "replace-peers"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY, "has-private-key"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT, "has-listen-port"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK, "has-fwmark"), ); - -static NM_UTILS_FLAGS2STR_DEFINE( - _wireguard_change_peer_flags_to_string, - NMPlatformWireGuardChangePeerFlags, - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE, "none"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME, "remove"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY, "psk"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL, "ka"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT, "ep"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS, "aips"), - NM_UTILS_FLAGS2STR(NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS, "remove-aips"), ); - -int -nm_platform_link_wireguard_change(NMPlatform * self, - int ifindex, - const NMPlatformLnkWireGuard * lnk_wireguard, - const NMPWireGuardPeer * peers, - const NMPlatformWireGuardChangePeerFlags *peer_flags, - guint peers_len, - NMPlatformWireGuardChangeFlags change_flags) -{ - _CHECK_SELF(self, klass, -NME_BUG); - - nm_assert(klass->link_wireguard_change); - - if (_LOGD_ENABLED()) { - char buf_lnk[256]; - char buf_peers[512]; - char buf_change_flags[100]; - - buf_peers[0] = '\0'; - if (peers_len > 0) { - char *b = buf_peers; - gsize len = sizeof(buf_peers); - guint i; - - nm_utils_strbuf_append_str(&b, &len, " { "); - for (i = 0; i < peers_len; i++) { - nm_utils_strbuf_append_str(&b, &len, " { "); - nm_platform_wireguard_peer_to_string(&peers[i], b, len); - nm_utils_strbuf_seek_end(&b, &len); - if (peer_flags) { - nm_utils_strbuf_append( - &b, - &len, - " (%s)", - _wireguard_change_peer_flags_to_string(peer_flags[i], - buf_change_flags, - sizeof(buf_change_flags))); - } - nm_utils_strbuf_append_str(&b, &len, " } "); - } - nm_utils_strbuf_append_str(&b, &len, "}"); - } - - _LOG3D("link: change wireguard ifindex %d, %s, (%s), %u peers%s", - ifindex, - nm_platform_lnk_wireguard_to_string(lnk_wireguard, buf_lnk, sizeof(buf_lnk)), - _wireguard_change_flags_to_string(change_flags, - buf_change_flags, - sizeof(buf_change_flags)), - peers_len, - buf_peers); - } - - return klass->link_wireguard_change(self, - ifindex, - lnk_wireguard, - peers, - peer_flags, - peers_len, - change_flags); -} - -/*****************************************************************************/ - -/** - * nm_platform_link_tun_add: - * @self: platform instance - * @name: new interface name - * @tap: whether the interface is a TAP - * @owner: interface owner or -1 - * @group: interface group or -1 - * @pi: whether to clear the IFF_NO_PI flag - * @vnet_hdr: whether to set the IFF_VNET_HDR flag - * @multi_queue: whether to set the IFF_MULTI_QUEUE flag - * @out_link: on success, the link object - * @out_fd: (allow-none): if give, return the file descriptor for the - * created device. Note that when creating a non-persistent device, - * this argument is mandatory, otherwise it makes no sense - * to create such an interface. - * The caller is responsible for closing this file descriptor. - * - * Create a TUN or TAP interface. - */ -int -nm_platform_link_tun_add(NMPlatform * self, - const char * name, - const NMPlatformLnkTun *props, - const NMPlatformLink ** out_link, - int * out_fd) -{ - char b[255]; - int r; - - _CHECK_SELF(self, klass, -NME_BUG); - - g_return_val_if_fail(name, -NME_BUG); - g_return_val_if_fail(props, -NME_BUG); - g_return_val_if_fail(NM_IN_SET(props->type, IFF_TUN, IFF_TAP), -NME_BUG); - - /* creating a non-persistent device requires that the caller handles - * the file descriptor. */ - g_return_val_if_fail(props->persist || out_fd, -NME_BUG); - - NM_SET_OUT(out_fd, -1); - - r = _link_add_check_existing(self, name, NM_LINK_TYPE_TUN, out_link); - if (r < 0) - return r; - - _LOG2D("link: adding link %s", nm_platform_lnk_tun_to_string(props, b, sizeof(b))); - - if (!klass->link_tun_add(self, name, props, out_link, out_fd)) - return -NME_UNSPEC; - return 0; -} - -gboolean -nm_platform_link_6lowpan_get_properties(NMPlatform *self, int ifindex, int *out_parent) -{ - const NMPlatformLink *plink; - - plink = nm_platform_link_get(self, ifindex); - if (!plink) - return FALSE; - - if (plink->type != NM_LINK_TYPE_6LOWPAN) - return FALSE; - - if (plink->parent != 0) { - NM_SET_OUT(out_parent, plink->parent); - return TRUE; - } - - /* As of 4.16 kernel does not expose the peer_ifindex as IFA_LINK. - * Find the WPAN device with the same MAC address. */ - if (out_parent) { - const NMPlatformLink *parent_plink; - - parent_plink = nm_platform_link_get_by_address(self, - NM_LINK_TYPE_WPAN, - plink->l_address.data, - plink->l_address.len); - NM_SET_OUT(out_parent, parent_plink ? parent_plink->ifindex : -1); - } - - return TRUE; -} - -/*****************************************************************************/ - -static gboolean -link_set_option(NMPlatform *self, - int ifindex, - const char *category, - const char *option, - const char *value) -{ - nm_auto_close int dirfd = -1; - char ifname_verified[IFNAMSIZ]; - const char * path; - - if (!category || !option) - return FALSE; - - dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname_verified); - if (dirfd < 0) - return FALSE; - - path = - nm_sprintf_buf_unsafe_a(strlen(category) + strlen(option) + 2, "%s/%s", category, option); - return nm_platform_sysctl_set(self, - NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname_verified, path), - value); -} - -static char * -link_get_option(NMPlatform *self, int ifindex, const char *category, const char *option) -{ - nm_auto_close int dirfd = -1; - char ifname_verified[IFNAMSIZ]; - const char * path; - - if (!category || !option) - return NULL; - - dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname_verified); - if (dirfd < 0) - return NULL; - - path = - nm_sprintf_buf_unsafe_a(strlen(category) + strlen(option) + 2, "%s/%s", category, option); - return nm_platform_sysctl_get(self, - NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname_verified, path)); -} - -static const char * -master_category(NMPlatform *self, int master) -{ - switch (nm_platform_link_get_type(self, master)) { - case NM_LINK_TYPE_BRIDGE: - return "bridge"; - case NM_LINK_TYPE_BOND: - return "bonding"; - default: - return NULL; - } -} - -static const char * -slave_category(NMPlatform *self, int slave) -{ - int master = nm_platform_link_get_master(self, slave); - - if (master <= 0) - return NULL; - - switch (nm_platform_link_get_type(self, master)) { - case NM_LINK_TYPE_BRIDGE: - return "brport"; - default: - return NULL; - } -} - -gboolean -nm_platform_sysctl_master_set_option(NMPlatform *self, - int ifindex, - const char *option, - const char *value) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(option, FALSE); - g_return_val_if_fail(value, FALSE); - - return link_set_option(self, ifindex, master_category(self, ifindex), option, value); -} - -char * -nm_platform_sysctl_master_get_option(NMPlatform *self, int ifindex, const char *option) -{ - _CHECK_SELF(self, klass, NULL); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(option, FALSE); - - return link_get_option(self, ifindex, master_category(self, ifindex), option); -} - -gboolean -nm_platform_sysctl_slave_set_option(NMPlatform *self, - int ifindex, - const char *option, - const char *value) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(option, FALSE); - g_return_val_if_fail(value, FALSE); - - return link_set_option(self, ifindex, slave_category(self, ifindex), option, value); -} - -char * -nm_platform_sysctl_slave_get_option(NMPlatform *self, int ifindex, const char *option) -{ - _CHECK_SELF(self, klass, NULL); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(option, FALSE); - - return link_get_option(self, ifindex, slave_category(self, ifindex), option); -} - -/*****************************************************************************/ - -gboolean -nm_platform_link_vlan_change(NMPlatform * self, - int ifindex, - NMVlanFlags flags_mask, - NMVlanFlags flags_set, - gboolean ingress_reset_all, - const NMVlanQosMapping *ingress_map, - gsize n_ingress_map, - gboolean egress_reset_all, - const NMVlanQosMapping *egress_map, - gsize n_egress_map) -{ - _CHECK_SELF(self, klass, FALSE); - - nm_assert(klass->link_vlan_change); - - g_return_val_if_fail(!n_ingress_map || ingress_map, FALSE); - g_return_val_if_fail(!n_egress_map || egress_map, FALSE); - - flags_set &= flags_mask; - - if (_LOGD_ENABLED()) { - char buf[512]; - char *b = buf; - gsize len, i; - - b[0] = '\0'; - len = sizeof(buf); - - if (flags_mask) - nm_utils_strbuf_append(&b, - &len, - " flags 0x%x/0x%x", - (unsigned) flags_set, - (unsigned) flags_mask); - - if (ingress_reset_all || n_ingress_map) { - nm_utils_strbuf_append_str(&b, &len, " ingress-qos-map"); - nm_platform_vlan_qos_mapping_to_string("", ingress_map, n_ingress_map, b, len); - i = strlen(b); - b += i; - len -= i; - if (ingress_reset_all) - nm_utils_strbuf_append_str(&b, &len, " (reset-all)"); - } - - if (egress_reset_all || n_egress_map) { - nm_utils_strbuf_append_str(&b, &len, " egress-qos-map"); - nm_platform_vlan_qos_mapping_to_string("", egress_map, n_egress_map, b, len); - i = strlen(b); - b += i; - len -= i; - if (egress_reset_all) - nm_utils_strbuf_append_str(&b, &len, " (reset-all)"); - } - - _LOG3D("link: change vlan %s", buf); - } - return klass->link_vlan_change(self, - ifindex, - flags_mask, - flags_set, - ingress_reset_all, - ingress_map, - n_ingress_map, - egress_reset_all, - egress_map, - n_egress_map); -} - -gboolean -nm_platform_link_vlan_set_ingress_map(NMPlatform *self, int ifindex, int from, int to) -{ - NMVlanQosMapping map = { - .from = from, - .to = to, - }; - - return nm_platform_link_vlan_change(self, ifindex, 0, 0, FALSE, &map, 1, FALSE, NULL, 0); -} - -gboolean -nm_platform_link_vlan_set_egress_map(NMPlatform *self, int ifindex, int from, int to) -{ - NMVlanQosMapping map = { - .from = from, - .to = to, - }; - - return nm_platform_link_vlan_change(self, ifindex, 0, 0, FALSE, NULL, 0, FALSE, &map, 1); -} - -static int -_infiniband_add_add_or_delete(NMPlatform * self, - int ifindex, - int p_key, - gboolean add, - const NMPlatformLink **out_link) -{ - char name[IFNAMSIZ]; - const NMPlatformLink *parent_link; - int r; - - _CHECK_SELF(self, klass, -NME_BUG); - - g_return_val_if_fail(ifindex >= 0, -NME_BUG); - g_return_val_if_fail(p_key >= 0 && p_key <= 0xffff, -NME_BUG); - - /* the special keys 0x0000 and 0x8000 are not allowed. */ - if (NM_IN_SET(p_key, 0, 0x8000)) - return -NME_UNSPEC; - - parent_link = nm_platform_link_get(self, ifindex); - if (!parent_link) - return -NME_PL_NOT_FOUND; - - if (parent_link->type != NM_LINK_TYPE_INFINIBAND) - return -NME_PL_WRONG_TYPE; - - nm_utils_new_infiniband_name(name, parent_link->name, p_key); - - if (add) { - r = _link_add_check_existing(self, name, NM_LINK_TYPE_INFINIBAND, out_link); - if (r < 0) - return r; - - _LOG3D("link: adding infiniband partition %s, key %d", name, p_key); - if (!klass->infiniband_partition_add(self, ifindex, p_key, out_link)) - return -NME_UNSPEC; - } else { - _LOG3D("link: deleting infiniband partition %s, key %d", name, p_key); - - if (!klass->infiniband_partition_delete(self, ifindex, p_key)) - return -NME_UNSPEC; - } - - return 0; -} - -int -nm_platform_link_infiniband_add(NMPlatform * self, - int parent, - int p_key, - const NMPlatformLink **out_link) -{ - return _infiniband_add_add_or_delete(self, parent, p_key, TRUE, out_link); -} - -int -nm_platform_link_infiniband_delete(NMPlatform *self, int parent, int p_key) -{ - return _infiniband_add_add_or_delete(self, parent, p_key, FALSE, NULL); -} - -gboolean -nm_platform_link_infiniband_get_properties(NMPlatform * self, - int ifindex, - int * out_parent, - int * out_p_key, - const char **out_mode) -{ - nm_auto_close int dirfd = -1; - char ifname_verified[IFNAMSIZ]; - const NMPlatformLnkInfiniband *plnk; - const NMPlatformLink * plink; - char * contents; - const char * mode; - int p_key = 0; - - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - plnk = nm_platform_link_get_lnk_infiniband(self, ifindex, &plink); - - if (!plink || plink->type != NM_LINK_TYPE_INFINIBAND) - return FALSE; - - if (plnk) { - NM_SET_OUT(out_parent, plink->parent); - NM_SET_OUT(out_p_key, plnk->p_key); - NM_SET_OUT(out_mode, plnk->mode); - return TRUE; - } - - /* Could not get the link information via netlink. To support older kernels, - * fallback to reading sysfs. */ - - dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname_verified); - if (dirfd < 0) - return FALSE; - - contents = - nm_platform_sysctl_get(self, NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "mode")); - if (!contents) - return FALSE; - if (strstr(contents, "datagram")) - mode = "datagram"; - else if (strstr(contents, "connected")) - mode = "connected"; - else - mode = NULL; - g_free(contents); - - p_key = - nm_platform_sysctl_get_int_checked(self, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "pkey"), - 16, - 0, - 0xFFFF, - -1); - if (p_key < 0) - return FALSE; - - NM_SET_OUT(out_parent, plink->parent); - NM_SET_OUT(out_p_key, p_key); - NM_SET_OUT(out_mode, mode); - return TRUE; -} - -gboolean -nm_platform_link_veth_get_properties(NMPlatform *self, int ifindex, int *out_peer_ifindex) -{ - const NMPlatformLink *plink; - int peer_ifindex; - - plink = nm_platform_link_get(self, ifindex); - if (!plink) - return FALSE; - - if (plink->type != NM_LINK_TYPE_VETH) - return FALSE; - - if (plink->parent != 0) { - NM_SET_OUT(out_peer_ifindex, plink->parent); - return TRUE; - } - - /* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */ - if (out_peer_ifindex) { - nm_auto_pop_netns NMPNetns *netns = NULL; - - if (!nm_platform_netns_push(self, &netns)) - return FALSE; - peer_ifindex = nmp_utils_ethtool_get_peer_ifindex(plink->ifindex); - if (peer_ifindex <= 0) - return FALSE; - - *out_peer_ifindex = peer_ifindex; - } - return TRUE; -} - -/** - * nm_platform_link_tun_get_properties: - * @self: the #NMPlatform instance - * @ifindex: the ifindex to look up - * @out_properties: (out) (allow-none): return the read properties - * - * Only recent versions of kernel export tun properties via netlink. - * So, if that's the case, then we have the NMPlatformLnkTun instance - * in the platform cache ready to return. Otherwise, this function - * falls back reading sysctl to obtain the tun properties. That - * is racy, because querying sysctl means that the object might - * be already removed from cache (while NM didn't yet process the - * netlink message). - * - * Hence, to lookup the tun properties, you always need to use this - * function, and use it with care knowing that it might obtain its - * data by reading sysctl. Note that we don't want to add this workaround - * to the platform cache itself, because the cache should (mainly) - * contain data from netlink. To access the sysctl side channel, the - * user needs to do explicitly. - * - * Returns: #TRUE, if the properties could be read. */ -gboolean -nm_platform_link_tun_get_properties(NMPlatform *self, int ifindex, NMPlatformLnkTun *out_properties) -{ - const NMPObject *plobj; - const NMPObject *pllnk; - char ifname[IFNAMSIZ]; - gint64 owner; - gint64 group; - gint64 flags; - - /* we consider also invisible links (those that are not yet in udev). */ - plobj = nm_platform_link_get_obj(self, ifindex, FALSE); - if (!plobj) - return FALSE; - - if (NMP_OBJECT_CAST_LINK(plobj)->type != NM_LINK_TYPE_TUN) - return FALSE; - - pllnk = plobj->_link.netlink.lnk; - if (pllnk) { - nm_assert(NMP_OBJECT_GET_TYPE(pllnk) == NMP_OBJECT_TYPE_LNK_TUN); - nm_assert(NMP_OBJECT_GET_CLASS(pllnk)->lnk_link_type == NM_LINK_TYPE_TUN); - - /* recent kernels expose tun properties via netlink and thus we have them - * in the platform cache. */ - NM_SET_OUT(out_properties, pllnk->lnk_tun); - return TRUE; - } - - /* fallback to reading sysctl. */ - { - nm_auto_close int dirfd = -1; - - dirfd = nm_platform_sysctl_open_netdir(self, ifindex, ifname); - if (dirfd < 0) - return FALSE; - - owner = nm_platform_sysctl_get_int_checked(self, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "owner"), - 10, - -1, - G_MAXUINT32, - -2); - if (owner == -2) - return FALSE; - - group = nm_platform_sysctl_get_int_checked(self, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "group"), - 10, - -1, - G_MAXUINT32, - -2); - if (group == -2) - return FALSE; - - flags = - nm_platform_sysctl_get_int_checked(self, - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, "tun_flags"), - 16, - 0, - G_MAXINT64, - -1); - if (flags == -1) - return FALSE; - } - - if (out_properties) { - memset(out_properties, 0, sizeof(*out_properties)); - if (owner != -1) { - out_properties->owner_valid = TRUE; - out_properties->owner = owner; - } - if (group != -1) { - out_properties->group_valid = TRUE; - out_properties->group = group; - } - out_properties->type = (flags & TUN_TYPE_MASK); - out_properties->pi = !(flags & IFF_NO_PI); - out_properties->vnet_hdr = !!(flags & IFF_VNET_HDR); - out_properties->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE); - out_properties->persist = !!(flags & IFF_PERSIST); - } - return TRUE; -} - -gboolean -nm_platform_wifi_get_capabilities(NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wifi_get_capabilities(self, ifindex, caps); -} - -gboolean -nm_platform_wifi_get_bssid(NMPlatform *self, int ifindex, guint8 *bssid) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wifi_get_bssid(self, ifindex, bssid); -} - -guint32 -nm_platform_wifi_get_frequency(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, 0); - - g_return_val_if_fail(ifindex > 0, 0); - - return klass->wifi_get_frequency(self, ifindex); -} - -int -nm_platform_wifi_get_quality(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, 0); - - g_return_val_if_fail(ifindex > 0, 0); - - return klass->wifi_get_quality(self, ifindex); -} - -guint32 -nm_platform_wifi_get_rate(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, 0); - - g_return_val_if_fail(ifindex > 0, 0); - - return klass->wifi_get_rate(self, ifindex); -} - -NM80211Mode -nm_platform_wifi_get_mode(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, NM_802_11_MODE_UNKNOWN); - - g_return_val_if_fail(ifindex > 0, NM_802_11_MODE_UNKNOWN); - - return klass->wifi_get_mode(self, ifindex); -} - -void -nm_platform_wifi_set_mode(NMPlatform *self, int ifindex, NM80211Mode mode) -{ - _CHECK_SELF_VOID(self, klass); - - g_return_if_fail(ifindex > 0); - - klass->wifi_set_mode(self, ifindex, mode); -} - -static void -wifi_set_powersave(NMPlatform *p, int ifindex, guint32 powersave) -{ - /* empty */ -} - -void -nm_platform_wifi_set_powersave(NMPlatform *self, int ifindex, guint32 powersave) -{ - _CHECK_SELF_VOID(self, klass); - - g_return_if_fail(ifindex > 0); - - klass->wifi_set_powersave(self, ifindex, powersave); -} - -guint32 -nm_platform_wifi_find_frequency(NMPlatform *self, int ifindex, const guint32 *freqs) -{ - _CHECK_SELF(self, klass, 0); - - g_return_val_if_fail(ifindex > 0, 0); - g_return_val_if_fail(freqs != NULL, 0); - - return klass->wifi_find_frequency(self, ifindex, freqs); -} - -void -nm_platform_wifi_indicate_addressing_running(NMPlatform *self, int ifindex, gboolean running) -{ - _CHECK_SELF_VOID(self, klass); - - g_return_if_fail(ifindex > 0); - - klass->wifi_indicate_addressing_running(self, ifindex, running); -} - -NMSettingWirelessWakeOnWLan -nm_platform_wifi_get_wake_on_wlan(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wifi_get_wake_on_wlan(self, ifindex); -} - -gboolean -nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wifi_set_wake_on_wlan(self, ifindex, wowl); -} - -guint32 -nm_platform_mesh_get_channel(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, 0); - - g_return_val_if_fail(ifindex > 0, 0); - - return klass->mesh_get_channel(self, ifindex); -} - -gboolean -nm_platform_mesh_set_channel(NMPlatform *self, int ifindex, guint32 channel) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->mesh_set_channel(self, ifindex, channel); -} - -gboolean -nm_platform_mesh_set_ssid(NMPlatform *self, int ifindex, const guint8 *ssid, gsize len) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ssid != NULL, FALSE); - - return klass->mesh_set_ssid(self, ifindex, ssid, len); -} - -guint16 -nm_platform_wpan_get_pan_id(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wpan_get_pan_id(self, ifindex); -} - -gboolean -nm_platform_wpan_set_pan_id(NMPlatform *self, int ifindex, guint16 pan_id) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wpan_set_pan_id(self, ifindex, pan_id); -} - -guint16 -nm_platform_wpan_get_short_addr(NMPlatform *self, int ifindex) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wpan_get_short_addr(self, ifindex); -} - -gboolean -nm_platform_wpan_set_short_addr(NMPlatform *self, int ifindex, guint16 short_addr) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wpan_set_short_addr(self, ifindex, short_addr); -} - -gboolean -nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 channel) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wpan_set_channel(self, ifindex, page, channel); -} - -#define TO_STRING_DEV_BUF_SIZE (5 + 15 + 1) -static const char * -_to_string_dev(NMPlatform *self, int ifindex, char *buf, size_t size) -{ - g_assert(buf && size >= TO_STRING_DEV_BUF_SIZE); - - if (ifindex) { - const char *name = ifindex > 0 && self ? nm_platform_link_get_name(self, ifindex) : NULL; - char * buf2; - - strcpy(buf, " dev "); - buf2 = buf + 5; - size -= 5; - - if (name) - g_strlcpy(buf2, name, size); - else - g_snprintf(buf2, size, "%d", ifindex); - } else - buf[0] = 0; - - return buf; -} - -#define TO_STRING_IFA_FLAGS_BUF_SIZE 256 - -static const char * -_to_string_ifa_flags(guint32 ifa_flags, char *buf, gsize size) -{ -#define S_FLAGS_PREFIX " flags " - nm_assert(buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN(S_FLAGS_PREFIX)); - - if (!ifa_flags) - buf[0] = '\0'; - else { - nm_platform_addr_flags2str(ifa_flags, - &buf[NM_STRLEN(S_FLAGS_PREFIX)], - size - NM_STRLEN(S_FLAGS_PREFIX)); - if (buf[NM_STRLEN(S_FLAGS_PREFIX)] == '\0') - buf[0] = '\0'; - else - memcpy(buf, S_FLAGS_PREFIX, NM_STRLEN(S_FLAGS_PREFIX)); - } - return buf; -} - -/*****************************************************************************/ - -gboolean -nm_platform_ethtool_set_wake_on_lan(NMPlatform * self, - int ifindex, - NMSettingWiredWakeOnLan wol, - const char * wol_password) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return nmp_utils_ethtool_set_wake_on_lan(ifindex, wol, wol_password); -} - -gboolean -nm_platform_ethtool_set_link_settings(NMPlatform * self, - int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return nmp_utils_ethtool_set_link_settings(ifindex, autoneg, speed, duplex); -} - -gboolean -nm_platform_ethtool_get_link_settings(NMPlatform * self, - int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return nmp_utils_ethtool_get_link_settings(ifindex, out_autoneg, out_speed, out_duplex); -} - -NM_UTILS_LOOKUP_STR_DEFINE(nm_platform_link_duplex_type_to_string, - NMPlatformLinkDuplexType, - NM_UTILS_LOOKUP_DEFAULT_WARN(NULL), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_FULL, "full"), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_HALF, "half"), ); - -/*****************************************************************************/ - -NMEthtoolFeatureStates * -nm_platform_ethtool_get_link_features(NMPlatform *self, int ifindex) -{ - _CHECK_SELF_NETNS(self, klass, netns, NULL); - - g_return_val_if_fail(ifindex > 0, NULL); - - return nmp_utils_ethtool_get_features(ifindex); -} - -gboolean -nm_platform_ethtool_set_features( - NMPlatform * self, - int ifindex, - const NMEthtoolFeatureStates *features, - const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return nmp_utils_ethtool_set_features(ifindex, features, requested, do_set); -} - -gboolean -nm_platform_ethtool_get_link_coalesce(NMPlatform * self, - int ifindex, - NMEthtoolCoalesceState *coalesce) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(coalesce, FALSE); - - return nmp_utils_ethtool_get_coalesce(ifindex, coalesce); -} - -gboolean -nm_platform_ethtool_set_coalesce(NMPlatform * self, - int ifindex, - const NMEthtoolCoalesceState *coalesce) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return nmp_utils_ethtool_set_coalesce(ifindex, coalesce); -} - -gboolean -nm_platform_ethtool_get_link_ring(NMPlatform *self, int ifindex, NMEthtoolRingState *ring) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ring, FALSE); - - return nmp_utils_ethtool_get_ring(ifindex, ring); -} - -gboolean -nm_platform_ethtool_set_ring(NMPlatform *self, int ifindex, const NMEthtoolRingState *ring) -{ - _CHECK_SELF_NETNS(self, klass, netns, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return nmp_utils_ethtool_set_ring(ifindex, ring); -} - -/*****************************************************************************/ - -const NMDedupMultiHeadEntry * -nm_platform_lookup_all(NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj) -{ - return nmp_cache_lookup_all(nm_platform_get_cache(self), cache_id_type, obj); -} - -const NMDedupMultiEntry * -nm_platform_lookup_entry(NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj) -{ - return nmp_cache_lookup_entry_with_idx_type(nm_platform_get_cache(self), cache_id_type, obj); -} - -const NMDedupMultiHeadEntry * -nm_platform_lookup(NMPlatform *self, const NMPLookup *lookup) -{ - return nmp_cache_lookup(nm_platform_get_cache(self), lookup); -} - -gboolean -nm_platform_lookup_predicate_routes_main(const NMPObject *obj, gpointer user_data) -{ - nm_assert( - NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - return nm_platform_route_table_is_main( - nm_platform_ip_route_get_effective_table(&obj->ip_route)); -} - -gboolean -nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel(const NMPObject *obj, - gpointer user_data) -{ - nm_assert( - NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - return nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&obj->ip_route)) - && obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; -} - -/** - * nm_platform_lookup_clone: - * @self: - * @lookup: - * @predicate: if given, only objects for which @predicate returns %TRUE are included - * in the result. - * @user_data: user data for @predicate - * - * Returns the result of lookup in a GPtrArray. The result array contains - * references objects from the cache, its destroy function will unref them. - * - * The user must unref the GPtrArray, which will also unref the NMPObject - * elements. - * - * The elements in the array *must* not be modified. - * - * Returns: the result of the lookup. - */ -GPtrArray * -nm_platform_lookup_clone(NMPlatform * self, - const NMPLookup * lookup, - NMPObjectPredicateFunc predicate, - gpointer user_data) -{ - return nm_dedup_multi_objs_to_ptr_array_head(nm_platform_lookup(self, lookup), - (NMDedupMultiFcnSelectPredicate) predicate, - user_data); -} - -void -nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen) -{ - nm_assert(plen <= 32); - - addr->address = address; - addr->peer_address = address; - addr->plen = plen; -} - -const struct in6_addr * -nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr) -{ - if (IN6_IS_ADDR_UNSPECIFIED(&addr->peer_address) - || IN6_ARE_ADDR_EQUAL(&addr->peer_address, &addr->address)) - return &addr->address; - return &addr->peer_address; -} - -gboolean -nm_platform_ip6_address_match(const NMPlatformIP6Address *addr, NMPlatformMatchFlags match_flag) -{ - nm_assert(!NM_FLAGS_ANY( - match_flag, - ~(NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY))); - nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY)); - nm_assert(NM_FLAGS_ANY(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)); - - if (IN6_IS_ADDR_LINKLOCAL(&addr->address)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL)) - return FALSE; - } - - if (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_DADFAILED)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED)) - return FALSE; - } else if (NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_TENTATIVE) - && !NM_FLAGS_HAS(addr->n_ifa_flags, IFA_F_OPTIMISTIC)) { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE)) - return FALSE; - } else { - if (!NM_FLAGS_HAS(match_flag, NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) - return FALSE; - } - - return TRUE; -} - -gboolean -nm_platform_ip4_address_add(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address, - in_addr_t broadcast_address, - guint32 lifetime, - guint32 preferred, - guint32 flags, - const char *label) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(plen <= 32, FALSE); - g_return_val_if_fail(lifetime > 0, FALSE); - g_return_val_if_fail(preferred <= lifetime, FALSE); - g_return_val_if_fail(!label || strlen(label) < sizeof(((NMPlatformIP4Address *) NULL)->label), - FALSE); - - if (_LOGD_ENABLED()) { - NMPlatformIP4Address addr; - - addr = (NMPlatformIP4Address){ - .ifindex = ifindex, - .address = address, - .peer_address = peer_address, - .plen = plen, - .timestamp = 0, /* set it at zero, which to_string will treat as *now* */ - .lifetime = lifetime, - .preferred = preferred, - .n_ifa_flags = flags, - .broadcast_address = broadcast_address, - .use_ip4_broadcast_address = TRUE, - }; - if (label) - g_strlcpy(addr.label, label, sizeof(addr.label)); - - _LOG3D("address: adding or updating IPv4 address: %s", - nm_platform_ip4_address_to_string(&addr, NULL, 0)); - } - return klass->ip4_address_add(self, - ifindex, - address, - plen, - peer_address, - broadcast_address, - lifetime, - preferred, - flags, - label); -} - -gboolean -nm_platform_ip6_address_add(NMPlatform * self, - int ifindex, - struct in6_addr address, - guint8 plen, - struct in6_addr peer_address, - guint32 lifetime, - guint32 preferred, - guint32 flags) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(plen <= 128, FALSE); - g_return_val_if_fail(lifetime > 0, FALSE); - g_return_val_if_fail(preferred <= lifetime, FALSE); - - if (_LOGD_ENABLED()) { - NMPlatformIP6Address addr = {0}; - - addr.ifindex = ifindex; - addr.address = address; - addr.peer_address = peer_address; - addr.plen = plen; - addr.timestamp = 0; /* set it to zero, which to_string will treat as *now* */ - addr.lifetime = lifetime; - addr.preferred = preferred; - addr.n_ifa_flags = flags; - - _LOG3D("address: adding or updating IPv6 address: %s", - nm_platform_ip6_address_to_string(&addr, NULL, 0)); - } - return klass - ->ip6_address_add(self, ifindex, address, plen, peer_address, lifetime, preferred, flags); -} - -gboolean -nm_platform_ip4_address_delete(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address) -{ - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; - char str_peer[INET_ADDRSTRLEN + 50]; - - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(plen <= 32, FALSE); - - _LOG3D("address: deleting IPv4 address %s/%d, %s%s", - _nm_utils_inet4_ntop(address, b1), - plen, - peer_address != address - ? nm_sprintf_buf(str_peer, "peer %s, ", _nm_utils_inet4_ntop(peer_address, b2)) - : "", - _to_string_dev(self, ifindex, str_dev, sizeof(str_dev))); - return klass->ip4_address_delete(self, ifindex, address, plen, peer_address); -} - -gboolean -nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen) -{ - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(plen <= 128, FALSE); - - _LOG3D("address: deleting IPv6 address %s/%d, %s", - _nm_utils_inet6_ntop(&address, sbuf), - plen, - _to_string_dev(self, ifindex, str_dev, sizeof(str_dev))); - return klass->ip6_address_delete(self, ifindex, address, plen); -} - -const NMPlatformIP4Address * -nm_platform_ip4_address_get(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address) -{ - NMPObject obj_id; - const NMPObject *obj; - - _CHECK_SELF(self, klass, NULL); - - g_return_val_if_fail(plen <= 32, NULL); - - nmp_object_stackinit_id_ip4_address(&obj_id, ifindex, address, plen, peer_address); - obj = nmp_cache_lookup_obj(nm_platform_get_cache(self), &obj_id); - nm_assert(!obj || nmp_object_is_visible(obj)); - return NMP_OBJECT_CAST_IP4_ADDRESS(obj); -} - -const NMPlatformIP6Address * -nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address) -{ - NMPObject obj_id; - const NMPObject *obj; - - _CHECK_SELF(self, klass, NULL); - - nm_assert(address); - - nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, address); - obj = nmp_cache_lookup_obj(nm_platform_get_cache(self), &obj_id); - nm_assert(!obj || nmp_object_is_visible(obj)); - return NMP_OBJECT_CAST_IP6_ADDRESS(obj); -} - -static gboolean -_addr_array_clean_expired(int addr_family, - int ifindex, - GPtrArray * array, - guint32 now, - GHashTable **idx) -{ - guint i; - gboolean any_addrs = FALSE; - - nm_assert_addr_family(addr_family); - nm_assert(ifindex > 0); - nm_assert(now > 0); - - if (!array) - return FALSE; - - /* remove all addresses that are already expired. */ - for (i = 0; i < array->len; i++) { - const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS(array->pdata[i]); - -#if NM_MORE_ASSERTS > 10 - nm_assert(a); - nm_assert(a->ifindex == ifindex); - { - const NMPObject *o = NMP_OBJECT_UP_CAST(a); - guint j; - - nm_assert(NMP_OBJECT_GET_CLASS(o)->addr_family == addr_family); - for (j = i + 1; j < array->len; j++) { - const NMPObject *o2 = array->pdata[j]; - - nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_GET_TYPE(o2)); - nm_assert(!nmp_object_id_equal(o, o2)); - } - } -#endif - - if (addr_family == AF_INET6 && NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_TEMPORARY)) { - /* temporary addresses are never added explicitly by NetworkManager but - * kernel adds them via mngtempaddr flag. - * - * We drop them from this list. */ - goto clear_and_next; - } - - if (!nm_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, now, NULL)) - goto clear_and_next; - - if (idx) { - if (G_UNLIKELY(!*idx)) { - *idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal); - } - if (!g_hash_table_add(*idx, (gpointer) NMP_OBJECT_UP_CAST(a))) - nm_assert_not_reached(); - } - any_addrs = TRUE; - continue; - -clear_and_next: - nmp_object_unref(g_steal_pointer(&array->pdata[i])); - } - - return any_addrs; -} - -static gboolean -ip4_addr_subnets_is_plain_address(const GPtrArray *addresses, gconstpointer needle) -{ - return needle >= (gconstpointer) &addresses->pdata[0] - && needle < (gconstpointer) &addresses->pdata[addresses->len]; -} - -static const NMPObject ** -ip4_addr_subnets_addr_list_get(const GPtrArray *addr_list, guint idx) -{ - nm_assert(addr_list); - nm_assert(addr_list->len > 1); - nm_assert(idx < addr_list->len); - nm_assert(addr_list->pdata[idx]); - nm_assert(!(*((gpointer *) addr_list->pdata[idx])) - || NMP_OBJECT_CAST_IP4_ADDRESS(*((gpointer *) addr_list->pdata[idx]))); - nm_assert(idx == 0 || ip4_addr_subnets_addr_list_get(addr_list, idx - 1)); - return addr_list->pdata[idx]; -} - -static void -ip4_addr_subnets_destroy_index(GHashTable *subnets, const GPtrArray *addresses) -{ - GHashTableIter iter; - gpointer p; - - if (!subnets) - return; - - g_hash_table_iter_init(&iter, subnets); - while (g_hash_table_iter_next(&iter, NULL, &p)) { - if (!ip4_addr_subnets_is_plain_address(addresses, p)) - g_ptr_array_free((GPtrArray *) p, TRUE); - } - - g_hash_table_unref(subnets); -} - -static GHashTable * -ip4_addr_subnets_build_index(const GPtrArray *addresses, - gboolean consider_flags, - gboolean full_index) -{ - GHashTable *subnets; - guint i; - - nm_assert(addresses && addresses->len); - - subnets = g_hash_table_new(nm_direct_hash, NULL); - - /* Build a hash table of all addresses per subnet */ - for (i = 0; i < addresses->len; i++) { - const NMPlatformIP4Address *address; - gpointer p_address; - GPtrArray * addr_list; - guint32 net; - int position; - gpointer p; - - if (!addresses->pdata[i]) - continue; - - p_address = &addresses->pdata[i]; - address = NMP_OBJECT_CAST_IP4_ADDRESS(addresses->pdata[i]); - - net = address->address & _nm_utils_ip4_prefix_to_netmask(address->plen); - if (!g_hash_table_lookup_extended(subnets, GUINT_TO_POINTER(net), NULL, &p)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); - continue; - } - nm_assert(p); - - if (full_index) { - if (ip4_addr_subnets_is_plain_address(addresses, p)) { - addr_list = g_ptr_array_new(); - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), addr_list); - g_ptr_array_add(addr_list, p); - } else - addr_list = p; - - if (!consider_flags || NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_SECONDARY)) - position = -1; /* append */ - else - position = 0; /* prepend */ - g_ptr_array_insert(addr_list, position, p_address); - } else { - /* we only care about the primary. No need to track the secondaries - * as a GPtrArray. */ - nm_assert(ip4_addr_subnets_is_plain_address(addresses, p)); - if (consider_flags && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_SECONDARY)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); - } - } - } - - return subnets; -} - -/** - * ip4_addr_subnets_is_secondary: - * @address: an address - * @subnets: the hash table mapping subnets to addresses - * @addresses: array of addresses in the hash table - * @out_addr_list: array of addresses belonging to the same subnet - * - * Checks whether @address is secondary and returns in @out_addr_list the list of addresses - * belonging to the same subnet, if it contains other elements. - * - * Returns: %TRUE if the address is secondary, %FALSE otherwise - */ -static gboolean -ip4_addr_subnets_is_secondary(const NMPObject * address, - GHashTable * subnets, - const GPtrArray * addresses, - const GPtrArray **out_addr_list) -{ - const NMPlatformIP4Address *a; - const GPtrArray * addr_list; - gconstpointer p; - guint32 net; - const NMPObject ** o; - - a = NMP_OBJECT_CAST_IP4_ADDRESS(address); - - net = a->address & _nm_utils_ip4_prefix_to_netmask(a->plen); - p = g_hash_table_lookup(subnets, GUINT_TO_POINTER(net)); - nm_assert(p); - if (!ip4_addr_subnets_is_plain_address(addresses, p)) { - addr_list = p; - nm_assert(addr_list->len > 1); - NM_SET_OUT(out_addr_list, addr_list); - o = ip4_addr_subnets_addr_list_get(addr_list, 0); - nm_assert(o && *o); - if (*o != address) - return TRUE; - } else { - NM_SET_OUT(out_addr_list, NULL); - return address != *((gconstpointer *) p); - } - return FALSE; -} - -typedef enum { - IP6_ADDR_SCOPE_LOOPBACK, - IP6_ADDR_SCOPE_LINKLOCAL, - IP6_ADDR_SCOPE_SITELOCAL, - IP6_ADDR_SCOPE_OTHER, -} IP6AddrScope; - -static IP6AddrScope -ip6_address_scope(const NMPlatformIP6Address *a) -{ - if (IN6_IS_ADDR_LOOPBACK(&a->address)) - return IP6_ADDR_SCOPE_LOOPBACK; - if (IN6_IS_ADDR_LINKLOCAL(&a->address)) - return IP6_ADDR_SCOPE_LINKLOCAL; - if (IN6_IS_ADDR_SITELOCAL(&a->address)) - return IP6_ADDR_SCOPE_SITELOCAL; - return IP6_ADDR_SCOPE_OTHER; -} - -static int -ip6_address_scope_cmp(gconstpointer p_a, gconstpointer p_b, gpointer increasing) -{ - const NMPlatformIP6Address *a; - const NMPlatformIP6Address *b; - - if (!increasing) - NM_SWAP(&p_a, &p_b); - - a = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_a); - b = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_b); - - NM_CMP_DIRECT(ip6_address_scope(a), ip6_address_scope(b)); - return 0; -} - -/** - * nm_platform_ip_address_sync: - * @self: platform instance - * @addr_family: the address family AF_INET or AF_INET6. - * @ifindex: Interface index - * @known_addresses: List of addresses. The list will be modified and only - * addresses that were successfully added will be kept in the list. - * That means, expired addresses and addresses that could not be added - * will be dropped. - * Hence, the input argument @known_addresses is also an output argument - * telling which addresses were successfully added. - * Addresses are removed by unrefing the instance via nmp_object_unref() - * and leaving a NULL tombstone. - * @addresses_prune: (allow-none): the list of addresses to delete. - * If platform has such an address configured, it will be deleted - * at the beginning of the sync. Note that the array will be modified - * by the function. - * Note that the addresses must be properly sorted, by their priority. - * Create this list with nm_platform_ip_address_get_prune_list() which - * gets the sorting right. - * - * A convenience function to synchronize addresses for a specific interface - * with the least possible disturbance. It simply removes addresses that are - * not listed and adds addresses that are. - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_ip_address_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray * known_addresses, - GPtrArray * addresses_prune) -{ - const gint32 now = nm_utils_get_monotonic_timestamp_sec(); - const gboolean IS_IPv4 = NM_IS_IPv4(addr_family); - gs_unref_hashtable GHashTable *known_addresses_idx = NULL; - GPtrArray * plat_addresses; - GHashTable * known_subnets = NULL; - guint32 ifa_flags; - guint i_plat; - guint i_know; - guint i; - guint j; - - _CHECK_SELF(self, klass, FALSE); - - /* The order we want to enforce is only among addresses with the same - * scope, as the kernel keeps addresses sorted by scope. Therefore, - * apply the same sorting to known addresses, so that we don't try to - * unnecessary change the order of addresses with different scopes. */ - if (!IS_IPv4) { - if (known_addresses) - g_ptr_array_sort_with_data(known_addresses, - ip6_address_scope_cmp, - GINT_TO_POINTER(TRUE)); - } - - if (!_addr_array_clean_expired(addr_family, - ifindex, - known_addresses, - now, - &known_addresses_idx)) - known_addresses = NULL; - - /* @plat_addresses must be sorted in decreasing priority order (highest priority addresses first), contrary to - * @known_addresses which is in increasing priority order (lowest priority addresses first). */ - plat_addresses = addresses_prune; - - if (nm_g_ptr_array_len(plat_addresses) > 0) { - /* Delete unknown addresses */ - if (IS_IPv4) { - GHashTable *plat_subnets; - - plat_subnets = ip4_addr_subnets_build_index(plat_addresses, TRUE, TRUE); - - for (i = 0; i < plat_addresses->len; i++) { - const NMPObject * plat_obj; - const NMPlatformIP4Address *plat_address; - const GPtrArray * addr_list; - - plat_obj = plat_addresses->pdata[i]; - if (!plat_obj) { - /* Already deleted */ - continue; - } - - plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); - - if (known_addresses) { - const NMPObject *o; - - o = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (o) { - gboolean secondary; - - if (!known_subnets) - known_subnets = - ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); - - secondary = - ip4_addr_subnets_is_secondary(o, known_subnets, known_addresses, NULL); - if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) { - /* if we have an existing known-address, with matching secondary role, - * do not delete the platform-address. */ - continue; - } - } - } - - nm_platform_ip4_address_delete(self, - ifindex, - plat_address->address, - plat_address->plen, - plat_address->peer_address); - - if (!ip4_addr_subnets_is_secondary(plat_obj, - plat_subnets, - plat_addresses, - &addr_list) - && addr_list) { - /* If we just deleted a primary addresses and there were - * secondary ones the kernel can do two things, depending on - * version and sysctl setting: delete also secondary addresses - * or promote a secondary to primary. Ensure that secondary - * addresses are deleted, so that we can start with a clean - * slate and add addresses in the right order. */ - for (j = 1; j < addr_list->len; j++) { - const NMPObject **o; - - o = ip4_addr_subnets_addr_list_get(addr_list, j); - nm_assert(o); - - if (*o) { - const NMPlatformIP4Address *a; - - a = NMP_OBJECT_CAST_IP4_ADDRESS(*o); - nm_platform_ip4_address_delete(self, - ifindex, - a->address, - a->plen, - a->peer_address); - nmp_object_unref(*o); - *o = NULL; - } - } - } - } - ip4_addr_subnets_destroy_index(plat_subnets, plat_addresses); - } else { - guint known_addresses_len; - IP6AddrScope cur_scope; - gboolean delete_remaining_addrs; - - g_ptr_array_sort_with_data(plat_addresses, - ip6_address_scope_cmp, - GINT_TO_POINTER(FALSE)); - - known_addresses_len = known_addresses ? known_addresses->len : 0; - - /* First, compare every address whether it is still a "known address", that is, whether - * to keep it or to delete it. - * - * If we don't find a matching valid address in @known_addresses, we will delete - * plat_addr. - * - * Certain addresses, like temporary addresses, are ignored by this function - * if not run with full_sync. These addresses are usually not managed by NetworkManager - * directly, or at least, they are not managed via nm_platform_ip6_address_sync(). - * Only in full_sync mode, we really want to get rid of them (usually, when we take - * the interface down). - * - * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ - for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { - const NMPObject * plat_obj = plat_addresses->pdata[i_plat]; - const NMPObject * know_obj; - const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); - - if (known_addresses_idx) { - know_obj = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (know_obj - && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS(know_obj)->plen) { - /* technically, plen is not part of the ID for IPv6 addresses and thus - * @plat_addr is essentially the same address as @know_addr (regrading - * its identity, not its other attributes). - * However, we cannot modify an existing addresses' plen without - * removing and readding it. Thus, only keep plat_addr, if the plen - * matches. - * - * keep this one, and continue */ - continue; - } - } - - nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen); - nmp_object_unref(g_steal_pointer(&plat_addresses->pdata[i_plat])); - } - - /* Next, we must preserve the priority of the routes. That is, source address - * selection will choose addresses in the order as they are reported by kernel. - * Note that the order in @plat_addresses of the remaining matches is highest - * priority first. - * We need to compare this to the order of addresses with same scope in - * @known_addresses (which has lowest priority first). - * - * If we find a first discrepancy, we need to delete all remaining addresses - * with same scope from that point on, because below we must re-add all the - * addresses in the right order to get their priority right. */ - cur_scope = IP6_ADDR_SCOPE_LOOPBACK; - delete_remaining_addrs = FALSE; - i_plat = plat_addresses->len; - i_know = 0; - while (i_plat > 0) { - const NMPlatformIP6Address *plat_addr = - NMP_OBJECT_CAST_IP6_ADDRESS(plat_addresses->pdata[--i_plat]); - IP6AddrScope plat_scope; - - if (!plat_addr) - continue; - - plat_scope = ip6_address_scope(plat_addr); - if (cur_scope != plat_scope) { - nm_assert(cur_scope < plat_scope); - delete_remaining_addrs = FALSE; - cur_scope = plat_scope; - } - - if (!delete_remaining_addrs) { - delete_remaining_addrs = TRUE; - for (; i_know < known_addresses_len; i_know++) { - const NMPlatformIP6Address *know_addr = - NMP_OBJECT_CAST_IP6_ADDRESS(known_addresses->pdata[i_know]); - IP6AddrScope know_scope; - - if (!know_addr) - continue; - - know_scope = ip6_address_scope(know_addr); - if (know_scope < plat_scope) - continue; - - if (IN6_ARE_ADDR_EQUAL(&plat_addr->address, &know_addr->address)) { - /* we have a match. Mark address as handled. */ - i_know++; - delete_remaining_addrs = FALSE; - goto next_plat; - } - - /* plat_address has no match. Now delete_remaining_addrs is TRUE and we will - * delete all the remaining addresses with cur_scope. */ - break; - } - } - - nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen); -next_plat:; - } - } - } - - if (!known_addresses) - return TRUE; - - if (IS_IPv4) - ip4_addr_subnets_destroy_index(known_subnets, known_addresses); - - ifa_flags = nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS) - ? IFA_F_NOPREFIXROUTE - : 0; - - /* Add missing addresses. New addresses are added by kernel with top - * priority. - */ - for (i_know = 0; i_know < known_addresses->len; i_know++) { - const NMPlatformIPXAddress *known_address; - const NMPObject * o; - guint32 lifetime; - guint32 preferred; - - o = known_addresses->pdata[i_know]; - if (!o) - continue; - - nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); - - known_address = NMP_OBJECT_CAST_IPX_ADDRESS(o); - - lifetime = nm_utils_lifetime_get(known_address->ax.timestamp, - known_address->ax.lifetime, - known_address->ax.preferred, - now, - &preferred); - nm_assert(lifetime > 0); - - if (IS_IPv4) { - if (!nm_platform_ip4_address_add( - self, - ifindex, - known_address->a4.address, - known_address->a4.plen, - known_address->a4.peer_address, - nm_platform_ip4_broadcast_address_from_addr(&known_address->a4), - lifetime, - preferred, - ifa_flags, - known_address->a4.label)) { - /* ignore error, for unclear reasons. */ - } - } else { - if (!nm_platform_ip6_address_add(self, - ifindex, - known_address->a6.address, - known_address->a6.plen, - known_address->a6.peer_address, - lifetime, - preferred, - ifa_flags | known_address->a6.n_ifa_flags)) - return FALSE; - } - } - - return TRUE; -} - -gboolean -nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex) -{ - gboolean success = TRUE; - - _CHECK_SELF(self, klass, FALSE); - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) - success &= nm_platform_ip4_address_sync(self, ifindex, NULL); - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) - success &= nm_platform_ip6_address_sync(self, ifindex, NULL, TRUE); - return success; -} - -/*****************************************************************************/ - -static gboolean -_err_inval_due_to_ipv6_tentative_pref_src(NMPlatform *self, const NMPObject *obj) -{ - const NMPlatformIP6Route * r; - const NMPlatformIP6Address *a; - - nm_assert(NM_IS_PLATFORM(self)); - nm_assert(NMP_OBJECT_IS_VALID(obj)); - - /* trying to add an IPv6 route with pref-src fails, if the address is - * still tentative (rh#1452684). We need to hack around that. - * - * Detect it, by guessing whether that's the case. */ - - if (NMP_OBJECT_GET_TYPE(obj) != NMP_OBJECT_TYPE_IP6_ROUTE) - return FALSE; - - r = NMP_OBJECT_CAST_IP6_ROUTE(obj); - - /* we only allow this workaround for routes added manually by the user. */ - if (r->rt_source != NM_IP_CONFIG_SOURCE_USER) - return FALSE; - - if (IN6_IS_ADDR_UNSPECIFIED(&r->pref_src)) - return FALSE; - - a = nm_platform_ip6_address_get(self, r->ifindex, &r->pref_src); - if (!a) - return FALSE; - if (!NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_TENTATIVE) - || NM_FLAGS_HAS(a->n_ifa_flags, IFA_F_DADFAILED)) - return FALSE; - - return TRUE; -} - -GPtrArray * -nm_platform_ip_address_get_prune_list(NMPlatform *self, - int addr_family, - int ifindex, - gboolean exclude_ipv6_temporary_addrs) -{ - const gboolean IS_IPv4 = NM_IS_IPv4(addr_family); - const NMDedupMultiHeadEntry *head_entry; - NMPLookup lookup; - GPtrArray * result; - CList * iter; - - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(NM_IS_IPv4(addr_family)), ifindex); - - head_entry = nm_platform_lookup(self, &lookup); - - if (!head_entry) - return NULL; - - result = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nmp_object_unref); - - c_list_for_each (iter, &head_entry->lst_entries_head) { - const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; - - if (!IS_IPv4) { - if (exclude_ipv6_temporary_addrs - && NM_FLAGS_HAS(NMP_OBJECT_CAST_IP_ADDRESS(obj)->n_ifa_flags, IFA_F_TEMPORARY)) - continue; - } - - g_ptr_array_add(result, (gpointer) nmp_object_ref(obj)); - } - - if (result->len == 0) { - g_ptr_array_unref(result); - return NULL; - } - return result; -} - -GPtrArray * -nm_platform_ip_route_get_prune_list(NMPlatform * self, - int addr_family, - int ifindex, - NMIPRouteTableSyncMode route_table_sync) -{ - NMPLookup lookup; - GPtrArray * routes_prune; - const NMDedupMultiHeadEntry *head_entry; - CList * iter; - - nm_assert(NM_IS_PLATFORM(self)); - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - nm_assert(NM_IN_SET(route_table_sync, - NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN, - NM_IP_ROUTE_TABLE_SYNC_MODE_FULL, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL)); - - nmp_lookup_init_object(&lookup, - addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE - : NMP_OBJECT_TYPE_IP6_ROUTE, - ifindex); - head_entry = nm_platform_lookup(self, &lookup); - if (!head_entry) - return NULL; - - routes_prune = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref); - - c_list_for_each (iter, &head_entry->lst_entries_head) { - const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; - - if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_FULL) { - if (nm_platform_ip_route_get_effective_table(NMP_OBJECT_CAST_IP_ROUTE(obj)) - == RT_TABLE_LOCAL) - continue; - } else if (route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN) { - if (!nm_platform_route_table_is_main( - nm_platform_ip_route_get_effective_table(NMP_OBJECT_CAST_IP_ROUTE(obj)))) - continue; - } else - nm_assert(route_table_sync == NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); - - g_ptr_array_add(routes_prune, (gpointer) nmp_object_ref(obj)); - } - - if (routes_prune->len == 0) { - g_ptr_array_unref(routes_prune); - return NULL; - } - return routes_prune; -} - -/** - * nm_platform_ip_route_sync: - * @self: the #NMPlatform instance. - * @addr_family: AF_INET or AF_INET6. - * @ifindex: the @ifindex for which the routes are to be added. - * @routes: (allow-none): a list of routes to configure. Must contain - * NMPObject instances of routes, according to @addr_family. - * @routes_prune: (allow-none): the list of routes to delete. - * If platform has such a route configured, it will be deleted - * at the end of the operation. Note that if @routes contains - * the same route, then it will not be deleted. @routes overrules - * @routes_prune list. - * @out_temporary_not_available: (allow-none) (out): routes that could - * currently not be synced. The caller shall keep them and try later again. - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_ip_route_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray * routes, - GPtrArray * routes_prune, - GPtrArray **out_temporary_not_available) -{ - const NMPlatformVTableRoute *vt; - gs_unref_hashtable GHashTable *routes_idx = NULL; - const NMPObject * conf_o; - const NMDedupMultiEntry * plat_entry; - guint i; - int i_type; - gboolean success = TRUE; - char sbuf1[sizeof(_nm_utils_to_string_buffer)]; - char sbuf2[sizeof(_nm_utils_to_string_buffer)]; - const gboolean IS_IPv4 = (addr_family == AF_INET); - - nm_assert(NM_IS_PLATFORM(self)); - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - nm_assert(ifindex > 0); - - vt = &nm_platform_vtable_route.vx[IS_IPv4]; - - for (i_type = 0; routes && i_type < 2; i_type++) { - for (i = 0; i < routes->len; i++) { - int r, r2; - gboolean gateway_route_added = FALSE; - - conf_o = routes->pdata[i]; - -#define VTABLE_IS_DEVICE_ROUTE(vt, o) \ - (vt->is_ip4 ? (NMP_OBJECT_CAST_IP4_ROUTE(o)->gateway == 0) \ - : IN6_IS_ADDR_UNSPECIFIED(&NMP_OBJECT_CAST_IP6_ROUTE(o)->gateway)) - - if ((i_type == 0 && !VTABLE_IS_DEVICE_ROUTE(vt, conf_o)) - || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE(vt, conf_o))) { - /* we add routes in two runs over @i_type. - * - * First device routes, then gateway routes. */ - continue; - } - - if (!routes_idx) { - routes_idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal); - } - if (!g_hash_table_insert(routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { - _LOG3D("route-sync: skip adding duplicate route %s", - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1))); - continue; - } - - if (!IS_IPv4 - && nm_platform_ip6_route_get_effective_metric(NMP_OBJECT_CAST_IP6_ROUTE(conf_o)) - == 0) { - /* User space cannot add routes with metric 0. However, kernel can, and we might track such - * routes in @route as they are present external. Skip them silently. */ - continue; - } - - plat_entry = nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, conf_o); - if (plat_entry) { - const NMPObject *plat_o; - - plat_o = plat_entry->obj; - - if (vt->route_cmp(NMP_OBJECT_CAST_IPX_ROUTE(conf_o), - NMP_OBJECT_CAST_IPX_ROUTE(plat_o), - NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - == 0) - continue; - - /* we need to replace the existing route with a (slightly) different - * one. Delete it first. */ - if (!nm_platform_object_delete(self, plat_o)) { - /* ignore error. */ - } - } - -sync_route_add: - r = nm_platform_ip_route_add(self, - NMP_NLM_FLAG_APPEND - | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, - conf_o); - if (r < 0) { - if (r == -EEXIST) { - /* Don't fail for EEXIST. It's not clear that the existing route - * is identical to the one that we were about to add. However, - * above we should have deleted conflicting (non-identical) routes. */ - if (_LOGD_ENABLED()) { - plat_entry = - nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, conf_o); - if (!plat_entry) { - _LOG3D("route-sync: adding route %s failed with EEXIST, however we " - "cannot find such a route", - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1))); - } else if (vt->route_cmp(NMP_OBJECT_CAST_IPX_ROUTE(conf_o), - NMP_OBJECT_CAST_IPX_ROUTE(plat_entry->obj), - NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - != 0) { - _LOG3D("route-sync: adding route %s failed due to existing " - "(different!) route %s", - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1)), - nmp_object_to_string(plat_entry->obj, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf2, - sizeof(sbuf2))); - } - } - } else if (NMP_OBJECT_CAST_IP_ROUTE(conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) { - _LOG3D("route-sync: ignore failure to add IPv%c route: %s: %s", - vt->is_ip4 ? '4' : '6', - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1)), - nm_strerror(r)); - } else if (r == -EINVAL && out_temporary_not_available - && _err_inval_due_to_ipv6_tentative_pref_src(self, conf_o)) { - _LOG3D("route-sync: ignore failure to add IPv6 route with tentative IPv6 " - "pref-src: %s: %s", - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1)), - nm_strerror(r)); - if (!*out_temporary_not_available) - *out_temporary_not_available = - g_ptr_array_new_full(0, (GDestroyNotify) nmp_object_unref); - g_ptr_array_add(*out_temporary_not_available, - (gpointer) nmp_object_ref(conf_o)); - } else if (!gateway_route_added - && ((r == -ENETUNREACH && vt->is_ip4 - && !!NMP_OBJECT_CAST_IP4_ROUTE(conf_o)->gateway) - || (r == -EHOSTUNREACH && !vt->is_ip4 - && !IN6_IS_ADDR_UNSPECIFIED( - &NMP_OBJECT_CAST_IP6_ROUTE(conf_o)->gateway)))) { - NMPObject oo; - - if (vt->is_ip4) { - const NMPlatformIP4Route *rt = NMP_OBJECT_CAST_IP4_ROUTE(conf_o); - - nmp_object_stackinit( - &oo, - NMP_OBJECT_TYPE_IP4_ROUTE, - &((NMPlatformIP4Route){ - .ifindex = rt->ifindex, - .network = rt->gateway, - .plen = 32, - .metric = nm_platform_ip4_route_get_effective_metric(rt), - .rt_source = rt->rt_source, - .table_coerced = nm_platform_ip_route_get_effective_table( - NM_PLATFORM_IP_ROUTE_CAST(rt)), - })); - } else { - const NMPlatformIP6Route *rt = NMP_OBJECT_CAST_IP6_ROUTE(conf_o); - - nmp_object_stackinit( - &oo, - NMP_OBJECT_TYPE_IP6_ROUTE, - &((NMPlatformIP6Route){ - .ifindex = rt->ifindex, - .network = rt->gateway, - .plen = 128, - .metric = nm_platform_ip6_route_get_effective_metric(rt), - .rt_source = rt->rt_source, - .table_coerced = nm_platform_ip_route_get_effective_table( - NM_PLATFORM_IP_ROUTE_CAST(rt)), - })); - } - - _LOG3D("route-sync: failure to add IPv%c route: %s: %s; try adding direct " - "route to gateway %s", - vt->is_ip4 ? '4' : '6', - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1)), - nm_strerror(r), - nmp_object_to_string(&oo, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf2, - sizeof(sbuf2))); - - r2 = nm_platform_ip_route_add(self, - NMP_NLM_FLAG_APPEND - | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, - &oo); - - if (r2 < 0) { - _LOG3D("route-sync: failure to add gateway IPv%c route: %s: %s", - vt->is_ip4 ? '4' : '6', - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1)), - nm_strerror(r2)); - } - - gateway_route_added = TRUE; - goto sync_route_add; - } else { - _LOG3W("route-sync: failure to add IPv%c route: %s: %s", - vt->is_ip4 ? '4' : '6', - nmp_object_to_string(conf_o, - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf1, - sizeof(sbuf1)), - nm_strerror(r)); - success = FALSE; - } - } - } - } - - if (routes_prune) { - for (i = 0; i < routes_prune->len; i++) { - const NMPObject *prune_o; - - prune_o = routes_prune->pdata[i]; - - nm_assert((addr_family == AF_INET - && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP4_ROUTE) - || (addr_family == AF_INET6 - && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP6_ROUTE)); - - if (routes_idx && g_hash_table_lookup(routes_idx, prune_o)) - continue; - - if (!nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, prune_o)) - continue; - - if (!nm_platform_object_delete(self, prune_o)) { - /* ignore error... */ - } - } - } - - return success; -} - -gboolean -nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex) -{ - gboolean success = TRUE; - - _CHECK_SELF(self, klass, FALSE); - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) { - gs_unref_ptrarray GPtrArray *routes_prune = NULL; - - routes_prune = nm_platform_ip_route_get_prune_list(self, - AF_INET, - ifindex, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); - success &= nm_platform_ip_route_sync(self, AF_INET, ifindex, NULL, routes_prune, NULL); - } - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) { - gs_unref_ptrarray GPtrArray *routes_prune = NULL; - - routes_prune = nm_platform_ip_route_get_prune_list(self, - AF_INET6, - ifindex, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL); - success &= nm_platform_ip_route_sync(self, AF_INET6, ifindex, NULL, routes_prune, NULL); - } - return success; -} - -/*****************************************************************************/ - -static guint8 -_ip_route_scope_inv_get_normalized(const NMPlatformIP4Route *route) -{ - /* in kernel, you cannot set scope to RT_SCOPE_NOWHERE (255). - * That means, in NM, we treat RT_SCOPE_NOWHERE as unset, and detect - * it based on the presence of the gateway. In other words, when adding - * a route with scope RT_SCOPE_NOWHERE (in NetworkManager) to kernel, - * the resulting scope will be either "link" or "universe" (depending - * on the gateway). - * - * Note that internally, we track @scope_inv is the inverse of scope, - * so that the default equals zero (~(RT_SCOPE_NOWHERE)). - **/ - if (route->scope_inv == 0) { - if (route->type_coerced == nm_platform_route_type_coerce(RTN_LOCAL)) - return nm_platform_route_scope_inv(RT_SCOPE_HOST); - else { - return nm_platform_route_scope_inv(!route->gateway ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); - } - } - return route->scope_inv; -} - -static guint8 -_route_pref_normalize(guint8 pref) -{ - /* for kernel (and ICMPv6) pref can only have one of 3 values. Normalize. */ - return NM_IN_SET(pref, NM_ICMPV6_ROUTER_PREF_LOW, NM_ICMPV6_ROUTER_PREF_HIGH) - ? pref - : NM_ICMPV6_ROUTER_PREF_MEDIUM; -} - -/** - * nm_platform_ip_route_normalize: - * @addr_family: AF_INET or AF_INET6 - * @route: an NMPlatformIP4Route or NMPlatformIP6Route instance, depending on @addr_family. - * - * Adding a route to kernel via nm_platform_ip_route_add() will normalize/coerce some - * properties of the route. This function modifies (normalizes) the route like it - * would be done by adding the route in kernel. - * - * Note that this function is related to NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY - * in that if two routes compare semantically equal, after normalizing they also shall - * compare equal with NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL. - */ -void -nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) -{ - NMPlatformIP4Route *r4; - NMPlatformIP6Route *r6; - - route->table_coerced = - nm_platform_route_table_coerce(nm_platform_ip_route_get_effective_table(route)); - route->table_any = FALSE; - - route->rt_source = nmp_utils_ip_config_source_round_trip_rtprot(route->rt_source); - - switch (addr_family) { - case AF_INET: - r4 = (NMPlatformIP4Route *) route; - route->metric = nm_platform_ip4_route_get_effective_metric(r4); - route->metric_any = FALSE; - r4->network = nm_utils_ip4_address_clear_host_address(r4->network, r4->plen); - r4->scope_inv = _ip_route_scope_inv_get_normalized(r4); - break; - case AF_INET6: - r6 = (NMPlatformIP6Route *) route; - route->metric = nm_platform_ip6_route_get_effective_metric(r6); - route->metric_any = FALSE; - nm_utils_ip6_address_clear_host_address(&r6->network, &r6->network, r6->plen); - nm_utils_ip6_address_clear_host_address(&r6->src, &r6->src, r6->src_plen); - break; - default: - nm_assert_not_reached(); - break; - } -} - -static int -_ip_route_add(NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route) -{ - char sbuf[sizeof(_nm_utils_to_string_buffer)]; - int ifindex; - - _CHECK_SELF(self, klass, FALSE); - - nm_assert(route); - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); - - ifindex = ((const NMPlatformIPRoute *) route)->ifindex; - _LOG3D("route: %-10s IPv%c route: %s", - _nmp_nlm_flag_to_string(flags & NMP_NLM_FLAG_FMASK), - nm_utils_addr_family_to_char(addr_family), - addr_family == AF_INET ? nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf)) - : nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); - - return klass->ip_route_add(self, flags, addr_family, route); -} - -int -nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route) -{ - int addr_family; - - switch (NMP_OBJECT_GET_TYPE(route)) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - addr_family = AF_INET; - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - addr_family = AF_INET6; - break; - default: - g_return_val_if_reached(FALSE); - } - - return _ip_route_add(self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE(route)); -} - -int -nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) -{ - return _ip_route_add(self, flags, AF_INET, route); -} - -int -nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route) -{ - return _ip_route_add(self, flags, AF_INET6, route); -} - -gboolean -nm_platform_object_delete(NMPlatform *self, const NMPObject *obj) -{ - int ifindex; - - _CHECK_SELF(self, klass, FALSE); - - switch (NMP_OBJECT_GET_TYPE(obj)) { - case NMP_OBJECT_TYPE_ROUTING_RULE: - _LOGD("%s: delete %s", - NMP_OBJECT_GET_CLASS(obj)->obj_type_name, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - case NMP_OBJECT_TYPE_QDISC: - case NMP_OBJECT_TYPE_TFILTER: - ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex; - _LOG3D("%s: delete %s", - NMP_OBJECT_GET_CLASS(obj)->obj_type_name, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - break; - default: - g_return_val_if_reached(FALSE); - } - - return klass->object_delete(self, obj); -} - -/*****************************************************************************/ - -int -nm_platform_ip_route_get(NMPlatform * self, - int addr_family, - gconstpointer address /* in_addr_t or struct in6_addr */, - int oif_ifindex, - NMPObject ** out_route) -{ - nm_auto_nmpobj NMPObject *route = NULL; - int result; - char buf[NM_UTILS_INET_ADDRSTRLEN]; - char buf_oif[64]; - - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(address, -NME_BUG); - g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), -NME_BUG); - - _LOGT("route: get IPv%c route for: %s%s", - nm_utils_addr_family_to_char(addr_family), - inet_ntop(addr_family, address, buf, sizeof(buf)), - oif_ifindex > 0 ? nm_sprintf_buf(buf_oif, " oif %d", oif_ifindex) : ""); - - if (!klass->ip_route_get) - result = -NME_PL_OPNOTSUPP; - else { - result = klass->ip_route_get(self, addr_family, address, oif_ifindex, &route); - } - - if (result < 0) { - nm_assert(!route); - _LOGW("route: get IPv%c route for: %s failed with %s", - nm_utils_addr_family_to_char(addr_family), - inet_ntop(addr_family, address, buf, sizeof(buf)), - nm_strerror(result)); - } else { - nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(route), - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE)); - nm_assert(!NMP_OBJECT_IS_STACKINIT(route)); - nm_assert(route->parent._ref_count == 1); - _LOGD("route: get IPv%c route for: %s succeeded: %s", - nm_utils_addr_family_to_char(addr_family), - inet_ntop(addr_family, address, buf, sizeof(buf)), - nmp_object_to_string(route, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - NM_SET_OUT(out_route, g_steal_pointer(&route)); - } - return result; -} - -/*****************************************************************************/ - -#define IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS ((int) 1500) -#define IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S \ - ((int) (((IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS + 999) * 3) / 1000)) - -static gint64 -_ip4_dev_route_blacklist_timeout_ms_get(gint64 timeout_msec) -{ - return timeout_msec >> 1; -} - -static gint64 -_ip4_dev_route_blacklist_timeout_ms_marked(gint64 timeout_msec) -{ - return !!(timeout_msec & ((gint64) 1)); -} - -static gboolean -_ip4_dev_route_blacklist_check_cb(gpointer user_data) -{ - NMPlatform * self = user_data; - NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); - GHashTableIter iter; - const NMPObject * p_obj; - gint64 * p_timeout_ms; - gint64 now_ms; - - priv->ip4_dev_route_blacklist_check_id = 0; - -again: - if (!priv->ip4_dev_route_blacklist_hash) - goto out; - - now_ms = nm_utils_get_monotonic_timestamp_msec(); - - g_hash_table_iter_init(&iter, priv->ip4_dev_route_blacklist_hash); - while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { - if (!_ip4_dev_route_blacklist_timeout_ms_marked(*p_timeout_ms)) - continue; - - /* unmark because we checked it. */ - *p_timeout_ms = *p_timeout_ms & ~((gint64) 1); - - if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms)) - continue; - - if (!nm_platform_lookup_entry(self, NMP_CACHE_ID_TYPE_OBJECT_TYPE, p_obj)) - continue; - - _LOGT("ip4-dev-route: delete %s", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - nm_platform_object_delete(self, p_obj); - goto again; - } - -out: - return G_SOURCE_REMOVE; -} - -static void -_ip4_dev_route_blacklist_check_schedule(NMPlatform *self) -{ - NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); - - if (!priv->ip4_dev_route_blacklist_check_id) { - priv->ip4_dev_route_blacklist_check_id = - g_idle_add_full(G_PRIORITY_HIGH, _ip4_dev_route_blacklist_check_cb, self, NULL); - } -} - -static void -_ip4_dev_route_blacklist_notify_route(NMPlatform *self, const NMPObject *obj) -{ - NMPlatformPrivate *priv; - const NMPObject * p_obj; - gint64 * p_timeout_ms; - gint64 now_ms; - - nm_assert(NM_IS_PLATFORM(self)); - nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE); - - priv = NM_PLATFORM_GET_PRIVATE(self); - - nm_assert(priv->ip4_dev_route_blacklist_gc_timeout_id); - - if (!g_hash_table_lookup_extended(priv->ip4_dev_route_blacklist_hash, - obj, - (gpointer *) &p_obj, - (gpointer *) &p_timeout_ms)) - return; - - now_ms = nm_utils_get_monotonic_timestamp_msec(); - if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms)) { - /* already expired. Wait for gc. */ - return; - } - - if (_ip4_dev_route_blacklist_timeout_ms_marked(*p_timeout_ms)) { - nm_assert(priv->ip4_dev_route_blacklist_check_id); - return; - } - - /* We cannot delete it right away because we are in the process of receiving netlink messages. - * It may be possible to do so, but complicated and error prone. - * - * Instead, we mark the entry and schedule an idle action (with high priority). */ - *p_timeout_ms = (*p_timeout_ms) | ((gint64) 1); - _ip4_dev_route_blacklist_check_schedule(self); -} - -static gboolean -_ip4_dev_route_blacklist_gc_timeout_handle(gpointer user_data) -{ - NMPlatform * self = user_data; - NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); - GHashTableIter iter; - const NMPObject * p_obj; - gint64 * p_timeout_ms; - gint64 now_ms; - - nm_assert(priv->ip4_dev_route_blacklist_gc_timeout_id); - - now_ms = nm_utils_get_monotonic_timestamp_msec(); - - g_hash_table_iter_init(&iter, priv->ip4_dev_route_blacklist_hash); - while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { - if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms)) { - _LOGT("ip4-dev-route: cleanup %s", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_iter_remove(&iter); - } - } - - _ip4_dev_route_blacklist_schedule(self); - return G_SOURCE_CONTINUE; -} - -static void -_ip4_dev_route_blacklist_schedule(NMPlatform *self) -{ - NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); - - if (!priv->ip4_dev_route_blacklist_hash - || g_hash_table_size(priv->ip4_dev_route_blacklist_hash) == 0) { - nm_clear_pointer(&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); - nm_clear_g_source(&priv->ip4_dev_route_blacklist_gc_timeout_id); - } else { - if (!priv->ip4_dev_route_blacklist_gc_timeout_id) { - /* this timeout is only to garbage collect the expired entries from priv->ip4_dev_route_blacklist_hash. - * It can run infrequently, and it doesn't hurt if expired entries linger around a bit - * longer then necessary. */ - priv->ip4_dev_route_blacklist_gc_timeout_id = - g_timeout_add_seconds(IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S, - _ip4_dev_route_blacklist_gc_timeout_handle, - self); - } - } -} - -/** - * nm_platform_ip4_dev_route_blacklist_set: - * @self: - * @ifindex: - * @ip4_dev_route_blacklist: - * - * When adding an IP address, kernel automatically adds a device route. - * This can be suppressed via the IFA_F_NOPREFIXROUTE address flag. For proper - * IPv6 support, we require kernel support for IFA_F_NOPREFIXROUTE and always - * add the device route manually. - * - * For IPv4, this flag is rather new and we don't rely on it yet. We want to use - * it (but currently still don't). So, for IPv4, kernel possibly adds a device - * route, however it has a wrong metric of zero. We add our own device route (with - * proper metric), but need to delete the route that kernel adds. - * - * The problem is, that kernel does not immediately add the route, when adding - * the address. It only shows up some time later. So, we register here a list - * of blacklisted routes, and when they show up within a time out, we assume it's - * the kernel generated one, and we delete it. - * - * Eventually, we want to get rid of this and use IFA_F_NOPREFIXROUTE for IPv4 - * routes as well. - */ -void -nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self, - int ifindex, - GPtrArray * ip4_dev_route_blacklist) -{ - NMPlatformPrivate *priv; - GHashTableIter iter; - const NMPObject * p_obj; - guint i; - gint64 timeout_msec; - gint64 timeout_msec_val; - gint64 * p_timeout_ms; - gboolean needs_check = FALSE; - - nm_assert(NM_IS_PLATFORM(self)); - nm_assert(ifindex > 0); - - /* TODO: the blacklist should be maintained by NML3Cfg. */ - - priv = NM_PLATFORM_GET_PRIVATE(self); - - /* first, expire all for current ifindex... */ - if (priv->ip4_dev_route_blacklist_hash) { - g_hash_table_iter_init(&iter, priv->ip4_dev_route_blacklist_hash); - while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { - if (NMP_OBJECT_CAST_IP4_ROUTE(p_obj)->ifindex == ifindex) { - /* we could g_hash_table_iter_remove(&iter) the current entry. - * Instead, just expire it and let _ip4_dev_route_blacklist_gc_timeout_handle() - * handle it. - * - * The assumption is, that ip4_dev_route_blacklist contains the very same entry - * again, with a new timeout. So, we can un-expire it below. */ - *p_timeout_ms = 0; - } - } - } - - if (ip4_dev_route_blacklist && ip4_dev_route_blacklist->len > 0) { - if (!priv->ip4_dev_route_blacklist_hash) { - priv->ip4_dev_route_blacklist_hash = - g_hash_table_new_full((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - nm_g_slice_free_fcn_gint64); - } - - timeout_msec = nm_utils_get_monotonic_timestamp_msec() + IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS; - timeout_msec_val = (timeout_msec << 1) | ((gint64) 1); - for (i = 0; i < ip4_dev_route_blacklist->len; i++) { - const NMPObject *o; - - needs_check = TRUE; - o = ip4_dev_route_blacklist->pdata[i]; - if (g_hash_table_lookup_extended(priv->ip4_dev_route_blacklist_hash, - o, - (gpointer *) &p_obj, - (gpointer *) &p_timeout_ms)) { - if (nmp_object_equal(p_obj, o)) { - /* un-expire and reuse the entry. */ - _LOGT("ip4-dev-route: register %s (update)", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - *p_timeout_ms = timeout_msec_val; - continue; - } - } - - _LOGT("ip4-dev-route: register %s", - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - p_timeout_ms = g_slice_new(gint64); - *p_timeout_ms = timeout_msec_val; - g_hash_table_replace(priv->ip4_dev_route_blacklist_hash, - (gpointer) nmp_object_ref(o), - p_timeout_ms); - } - } - - _ip4_dev_route_blacklist_schedule(self); - - if (needs_check) - _ip4_dev_route_blacklist_check_schedule(self); -} - -/*****************************************************************************/ - -int -nm_platform_routing_rule_add(NMPlatform * self, - NMPNlmFlags flags, - const NMPlatformRoutingRule *routing_rule) -{ - _CHECK_SELF(self, klass, -NME_BUG); - - g_return_val_if_fail(routing_rule, -NME_BUG); - - _LOGD("routing-rule: adding or updating: %s", - nm_platform_routing_rule_to_string(routing_rule, NULL, 0)); - return klass->routing_rule_add(self, flags, routing_rule); -} - -/*****************************************************************************/ - -int -nm_platform_qdisc_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) -{ - int ifindex = qdisc->ifindex; - _CHECK_SELF(self, klass, -NME_BUG); - - /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind - * is undefined. */ - - _LOG3D("adding or updating a qdisc: %s", nm_platform_qdisc_to_string(qdisc, NULL, 0)); - return klass->qdisc_add(self, flags, qdisc); -} - -/** - * nm_platform_qdisc_sync: - * @self: the #NMPlatform instance - * @ifindex: the ifindex where to configure the qdiscs. - * @known_qdiscs: the list of qdiscs (#NMPObject). - * - * The function promises not to take any reference to the qdisc - * instances from @known_qdiscs, nor to keep them around after - * the function returns. This is important, because it allows the - * caller to pass NMPlatformQdisc instances which "kind" string - * have a limited lifetime. - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_qdisc_sync(NMPlatform *self, int ifindex, GPtrArray *known_qdiscs) -{ - gs_unref_ptrarray GPtrArray *plat_qdiscs = NULL; - NMPLookup lookup; - guint i; - gboolean success = TRUE; - gs_unref_hashtable GHashTable *known_qdiscs_idx = NULL; - - nm_assert(NM_IS_PLATFORM(self)); - nm_assert(ifindex > 0); - - known_qdiscs_idx = - g_hash_table_new((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal); - if (known_qdiscs) { - for (i = 0; i < known_qdiscs->len; i++) { - const NMPObject *q = g_ptr_array_index(known_qdiscs, i); - - if (!g_hash_table_insert(known_qdiscs_idx, (gpointer) q, (gpointer) q)) { - _LOGW("duplicate qdisc %s", nm_platform_qdisc_to_string(&q->qdisc, NULL, 0)); - return FALSE; - } - } - } - - plat_qdiscs = - nm_platform_lookup_clone(self, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_QDISC, ifindex), - NULL, - NULL); - if (plat_qdiscs) { - for (i = 0; i < plat_qdiscs->len; i++) { - const NMPObject *p = g_ptr_array_index(plat_qdiscs, i); - const NMPObject *k; - - /* look up known qdisc with same parent */ - k = g_hash_table_lookup(known_qdiscs_idx, p); - - if (k) { - const NMPlatformQdisc *qdisc_k = NMP_OBJECT_CAST_QDISC(k); - const NMPlatformQdisc *qdisc_p = NMP_OBJECT_CAST_QDISC(p); - - /* check other fields */ - if (nm_platform_qdisc_cmp_full(qdisc_k, qdisc_p, FALSE) != 0 - || (qdisc_k->handle != qdisc_p->handle && qdisc_k != 0)) { - k = NULL; - } - } - - if (k) { - g_hash_table_remove(known_qdiscs_idx, k); - } else { - /* can't delete qdisc with zero handle */ - if (TC_H_MAJ(p->qdisc.handle) != 0) { - success &= nm_platform_object_delete(self, p); - } - } - } - } - - if (known_qdiscs) { - for (i = 0; i < known_qdiscs->len; i++) { - const NMPObject *q = g_ptr_array_index(known_qdiscs, i); - - if (g_hash_table_contains(known_qdiscs_idx, q)) { - success &= - (nm_platform_qdisc_add(self, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_QDISC(q)) >= 0); - } - } - } - - return success; -} - -/*****************************************************************************/ - -int -nm_platform_tfilter_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter) -{ - int ifindex = tfilter->ifindex; - _CHECK_SELF(self, klass, -NME_BUG); - - /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind - * and tfilter.action.kind is undefined. */ - - _LOG3D("adding or updating a tfilter: %s", nm_platform_tfilter_to_string(tfilter, NULL, 0)); - return klass->tfilter_add(self, flags, tfilter); -} - -/** - * nm_platform_qdisc_sync: - * @self: the #NMPlatform instance - * @ifindex: the ifindex where to configure the qdiscs. - * @known_tfilters: the list of tfilters (#NMPObject). - * - * The function promises not to take any reference to the tfilter - * instances from @known_tfilters, nor to keep them around after - * the function returns. This is important, because it allows the - * caller to pass NMPlatformTfilter instances which "kind" string - * have a limited lifetime. - * - * Returns: %TRUE on success. - */ -gboolean -nm_platform_tfilter_sync(NMPlatform *self, int ifindex, GPtrArray *known_tfilters) -{ - gs_unref_ptrarray GPtrArray *plat_tfilters = NULL; - NMPLookup lookup; - guint i; - gboolean success = TRUE; - gs_unref_hashtable GHashTable *known_tfilters_idx = NULL; - - nm_assert(NM_IS_PLATFORM(self)); - nm_assert(ifindex > 0); - - known_tfilters_idx = - g_hash_table_new((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal); - - if (known_tfilters) { - for (i = 0; i < known_tfilters->len; i++) { - const NMPObject *q = g_ptr_array_index(known_tfilters, i); - - g_hash_table_insert(known_tfilters_idx, (gpointer) q, (gpointer) q); - } - } - - plat_tfilters = - nm_platform_lookup_clone(self, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_TFILTER, ifindex), - NULL, - NULL); - - if (plat_tfilters) { - for (i = 0; i < plat_tfilters->len; i++) { - const NMPObject *q = g_ptr_array_index(plat_tfilters, i); - - if (!g_hash_table_lookup(known_tfilters_idx, q)) - success &= nm_platform_object_delete(self, q); - } - } - - if (known_tfilters) { - for (i = 0; i < known_tfilters->len; i++) { - const NMPObject *q = g_ptr_array_index(known_tfilters, i); - - success &= - (nm_platform_tfilter_add(self, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_TFILTER(q)) >= 0); - } - } - - return success; -} - -/*****************************************************************************/ - -const char * -nm_platform_vlan_qos_mapping_to_string(const char * name, - const NMVlanQosMapping *map, - gsize n_map, - char * buf, - gsize len) -{ - gsize i; - char *b; - - nm_utils_to_string_buffer_init(&buf, &len); - - if (!n_map) { - nm_utils_strbuf_append_str(&buf, &len, ""); - return buf; - } - - if (!map) - g_return_val_if_reached(""); - - b = buf; - - if (name) { - nm_utils_strbuf_append_str(&b, &len, name); - nm_utils_strbuf_append_str(&b, &len, " {"); - } else - nm_utils_strbuf_append_c(&b, &len, '{'); - - for (i = 0; i < n_map; i++) - nm_utils_strbuf_append(&b, &len, " %u:%u", map[i].from, map[i].to); - nm_utils_strbuf_append_str(&b, &len, " }"); - return buf; -} - -static const char * -_lifetime_to_string(guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size) -{ - if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT) - return "forever"; - - g_snprintf(buf, - buf_size, - "%usec", - nm_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now)); - return buf; -} - -static const char * -_lifetime_summary_to_string(gint32 now, - guint32 timestamp, - guint32 preferred, - guint32 lifetime, - char * buf, - size_t buf_size) -{ - g_snprintf(buf, - buf_size, - " lifetime %d-%u[%u,%u]", - (signed) now, - (unsigned) timestamp, - (unsigned) preferred, - (unsigned) lifetime); - return buf; -} - -/** - * nm_platform_link_to_string: - * @route: pointer to NMPlatformLink address structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting an link struct into a string representation. - * - * Returns: a string representation of the link. - */ -const char * -nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len) -{ - char master[20]; - char parent[20]; - char str_flags[1 + NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN + 1]; - char str_highlighted_flags[50]; - char * s; - gsize l; - char str_addrmode[30]; - char str_address[NM_UTILS_HWADDR_LEN_MAX * 3]; - char str_broadcast[NM_UTILS_HWADDR_LEN_MAX * 3]; - char str_inet6_token[NM_UTILS_INET_ADDRSTRLEN]; - const char *str_link_type; - - if (!nm_utils_to_string_buffer_init_null(link, &buf, &len)) - return buf; - - s = str_highlighted_flags; - l = sizeof(str_highlighted_flags); - if (NM_FLAGS_HAS(link->n_ifi_flags, IFF_NOARP)) - nm_utils_strbuf_append_str(&s, &l, "NOARP,"); - if (NM_FLAGS_HAS(link->n_ifi_flags, IFF_UP)) - nm_utils_strbuf_append_str(&s, &l, "UP"); - else - nm_utils_strbuf_append_str(&s, &l, "DOWN"); - if (link->connected) - nm_utils_strbuf_append_str(&s, &l, ",LOWER_UP"); - nm_assert(s > str_highlighted_flags && l > 0); - - if (link->n_ifi_flags) { - str_flags[0] = ';'; - nm_platform_link_flags2str(link->n_ifi_flags, &str_flags[1], sizeof(str_flags) - 1); - } else - str_flags[0] = '\0'; - - if (link->master) - g_snprintf(master, sizeof(master), " master %d", link->master); - else - master[0] = 0; - - if (link->parent > 0) - g_snprintf(parent, sizeof(parent), "@%d", link->parent); - else if (link->parent == NM_PLATFORM_LINK_OTHER_NETNS) - g_strlcpy(parent, "@other-netns", sizeof(parent)); - else - parent[0] = 0; - - _nmp_link_address_to_string(&link->l_address, str_address); - _nmp_link_address_to_string(&link->l_broadcast, str_broadcast); - - str_link_type = nm_link_type_to_string(link->type); - - g_snprintf( - buf, - len, - "%d: " /* ifindex */ - "%s" /* name */ - "%s" /* parent */ - " <%s%s>" /* flags */ - " mtu %d" - "%s" /* master */ - " arp %u" /* arptype */ - " %s" /* link->type */ - "%s%s" /* kind */ - "%s" /* is-in-udev */ - "%s%s" /* addr-gen-mode */ - "%s%s" /* l_address */ - "%s%s" /* l_broadcast */ - "%s%s" /* inet6_token */ - "%s%s" /* driver */ - " rx:%" G_GUINT64_FORMAT ",%" G_GUINT64_FORMAT " tx:%" G_GUINT64_FORMAT - ",%" G_GUINT64_FORMAT, - link->ifindex, - link->name, - parent, - str_highlighted_flags, - str_flags, - link->mtu, - master, - link->arptype, - str_link_type ?: "???", - link->kind ? (g_strcmp0(str_link_type, link->kind) ? "/" : "*") : "?", - link->kind && g_strcmp0(str_link_type, link->kind) ? link->kind : "", - link->initialized ? " init" : " not-init", - link->inet6_addr_gen_mode_inv ? " addrgenmode " : "", - link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str( - _nm_platform_uint8_inv(link->inet6_addr_gen_mode_inv), - str_addrmode, - sizeof(str_addrmode)) - : "", - str_address[0] ? " addr " : "", - str_address[0] ? str_address : "", - str_broadcast[0] ? " brd " : "", - str_broadcast[0] ? str_broadcast : "", - link->inet6_token.id ? " inet6token " : "", - link->inet6_token.id - ? nm_utils_inet6_interface_identifier_to_token(link->inet6_token, str_inet6_token) - : "", - link->driver ? " driver " : "", - link->driver ?: "", - link->rx_packets, - link->rx_bytes, - link->tx_packets, - link->tx_bytes); - return buf; -} - -const NMPlatformLnkBridge nm_platform_lnk_bridge_default = { - .forward_delay = NM_BRIDGE_FORWARD_DELAY_DEF_SYS, - .hello_time = NM_BRIDGE_HELLO_TIME_DEF_SYS, - .max_age = NM_BRIDGE_MAX_AGE_DEF_SYS, - .ageing_time = NM_BRIDGE_AGEING_TIME_DEF_SYS, - .stp_state = FALSE, - .priority = NM_BRIDGE_PRIORITY_DEF, - .vlan_protocol = 0x8100, - .vlan_stats_enabled = NM_BRIDGE_VLAN_STATS_ENABLED_DEF, - .group_fwd_mask = 0, - .group_addr = NM_ETHER_ADDR_INIT(NM_BRIDGE_GROUP_ADDRESS_DEF_BIN), - .mcast_snooping = NM_BRIDGE_MULTICAST_SNOOPING_DEF, - .mcast_router = 1, - .mcast_query_use_ifaddr = NM_BRIDGE_MULTICAST_QUERY_USE_IFADDR_DEF, - .mcast_querier = NM_BRIDGE_MULTICAST_QUERIER_DEF, - .mcast_hash_max = NM_BRIDGE_MULTICAST_HASH_MAX_DEF, - .mcast_last_member_count = NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_DEF, - .mcast_startup_query_count = NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_DEF, - .mcast_last_member_interval = NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_DEF, - .mcast_membership_interval = NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_DEF, - .mcast_querier_interval = NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_DEF, - .mcast_query_interval = NM_BRIDGE_MULTICAST_QUERY_INTERVAL_DEF, - .mcast_query_response_interval = NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_DEF, - .mcast_startup_query_interval = NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_DEF, -}; - -const char * -nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len) -{ - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - g_snprintf(buf, - len, - "forward_delay %u" - " hello_time %u" - " max_age %u" - " ageing_time %u" - " stp_state %d" - " priority %u" - " vlan_protocol %u" - " vlan_stats_enabled %d" - " group_fwd_mask %#x" - " group_address " NM_ETHER_ADDR_FORMAT_STR " mcast_snooping %d" - " mcast_router %u" - " mcast_query_use_ifaddr %d" - " mcast_querier %d" - " mcast_hash_max %u" - " mcast_last_member_count %u" - " mcast_startup_query_count %u" - " mcast_last_member_interval %" G_GUINT64_FORMAT - " mcast_membership_interval %" G_GUINT64_FORMAT - " mcast_querier_interval %" G_GUINT64_FORMAT - " mcast_query_interval %" G_GUINT64_FORMAT - " mcast_query_response_interval %" G_GUINT64_FORMAT - " mcast_startup_query_interval %" G_GUINT64_FORMAT "", - lnk->forward_delay, - lnk->hello_time, - lnk->max_age, - lnk->ageing_time, - (int) lnk->stp_state, - lnk->priority, - lnk->vlan_protocol, - (int) lnk->vlan_stats_enabled, - lnk->group_fwd_mask, - NM_ETHER_ADDR_FORMAT_VAL(lnk->group_addr), - (int) lnk->mcast_snooping, - lnk->mcast_router, - (int) lnk->mcast_query_use_ifaddr, - (int) lnk->mcast_querier, - lnk->mcast_hash_max, - lnk->mcast_last_member_count, - lnk->mcast_startup_query_count, - lnk->mcast_last_member_interval, - lnk->mcast_membership_interval, - lnk->mcast_querier_interval, - lnk->mcast_query_interval, - lnk->mcast_query_response_interval, - lnk->mcast_startup_query_interval); - return buf; -} - -const char * -nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) -{ - char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; - char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; - char str_ttl[30]; - char str_tos[30]; - char str_parent_ifindex[30]; - char str_input_flags[30]; - char str_output_flags[30]; - char str_input_key[30]; - char str_input_key1[NM_UTILS_INET_ADDRSTRLEN]; - char str_output_key[30]; - char str_output_key1[NM_UTILS_INET_ADDRSTRLEN]; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - g_snprintf( - buf, - len, - "gre%s" /* is_tap */ - "%s" /* remote */ - "%s" /* local */ - "%s" /* parent_ifindex */ - "%s" /* ttl */ - "%s" /* tos */ - "%s" /* path_mtu_discovery */ - "%s" /* iflags */ - "%s" /* oflags */ - "%s" /* ikey */ - "%s" /* okey */ - "", - lnk->is_tap ? "tap" : "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) - : "", - lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) - : "", - lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", - lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos)) - : "", - lnk->path_mtu_discovery ? "" : " nopmtudisc", - lnk->input_flags ? nm_sprintf_buf(str_input_flags, " iflags 0x%x", lnk->input_flags) : "", - lnk->output_flags ? nm_sprintf_buf(str_output_flags, " oflags 0x%x", lnk->output_flags) - : "", - NM_FLAGS_HAS(lnk->input_flags, GRE_KEY) || lnk->input_key - ? nm_sprintf_buf(str_input_key, - " ikey %s", - _nm_utils_inet4_ntop(lnk->input_key, str_input_key1)) - : "", - NM_FLAGS_HAS(lnk->output_flags, GRE_KEY) || lnk->output_key - ? nm_sprintf_buf(str_output_key, - " okey %s", - _nm_utils_inet4_ntop(lnk->output_key, str_output_key1)) - : ""); - return buf; -} - -const char * -nm_platform_lnk_infiniband_to_string(const NMPlatformLnkInfiniband *lnk, char *buf, gsize len) -{ - char str_p_key[64]; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - g_snprintf(buf, - len, - "infiniband" - "%s" /* p_key */ - "%s%s" /* mode */ - "", - lnk->p_key ? nm_sprintf_buf(str_p_key, " pkey %d", lnk->p_key) : "", - lnk->mode ? " mode " : "", - lnk->mode ?: ""); - return buf; -} - -const char * -nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len) -{ - char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; - char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; - char str_ttl[30]; - char str_tclass[30]; - char str_flow[30]; - char str_encap[30]; - char str_proto[30]; - char str_parent_ifindex[30]; - char *str_type; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - if (lnk->is_gre) - str_type = lnk->is_tap ? "ip6gretap" : "ip6gre"; - else - str_type = "ip6tnl"; - - g_snprintf( - buf, - len, - "%s" /* type */ - "%s" /* remote */ - "%s" /* local */ - "%s" /* parent_ifindex */ - "%s" /* ttl */ - "%s" /* tclass */ - "%s" /* encap limit */ - "%s" /* flow label */ - "%s" /* proto */ - " flags 0x%x" - "", - str_type, - nm_sprintf_buf(str_remote, " remote %s", _nm_utils_inet6_ntop(&lnk->remote, str_remote1)), - nm_sprintf_buf(str_local, " local %s", _nm_utils_inet6_ntop(&lnk->local, str_local1)), - lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) - : "", - lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", - lnk->tclass == 1 ? " tclass inherit" - : nm_sprintf_buf(str_tclass, " tclass 0x%x", lnk->tclass), - nm_sprintf_buf(str_encap, " encap-limit %u", lnk->encap_limit), - nm_sprintf_buf(str_flow, " flow-label 0x05%x", lnk->flow_label), - nm_sprintf_buf(str_proto, " proto %u", lnk->proto), - (guint) lnk->flags); - return buf; -} - -const char * -nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize len) -{ - char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; - char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; - char str_ttl[30]; - char str_tos[30]; - char str_parent_ifindex[30]; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - g_snprintf( - buf, - len, - "ipip" - "%s" /* remote */ - "%s" /* local */ - "%s" /* parent_ifindex */ - "%s" /* ttl */ - "%s" /* tos */ - "%s" /* path_mtu_discovery */ - "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) - : "", - lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) - : "", - lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", - lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos)) - : "", - lnk->path_mtu_discovery ? "" : " nopmtudisc"); - return buf; -} - -const char * -nm_platform_lnk_macsec_to_string(const NMPlatformLnkMacsec *lnk, char *buf, gsize len) -{ - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - g_snprintf(buf, - len, - "macsec " - "sci %016llx " - "protect %s " - "cipher %016llx " - "icvlen %u " - "encodingsa %u " - "validate %u " - "encrypt %s " - "send_sci %s " - "end_station %s " - "scb %s " - "replay %s", - (unsigned long long) lnk->sci, - lnk->protect ? "on" : "off", - (unsigned long long) lnk->cipher_suite, - lnk->icv_length, - lnk->encoding_sa, - lnk->validation, - lnk->encrypt ? "on" : "off", - lnk->include_sci ? "on" : "off", - lnk->es ? "on" : "off", - lnk->scb ? "on" : "off", - lnk->replay_protect ? "on" : "off"); - return buf; -} - -const char * -nm_platform_lnk_macvlan_to_string(const NMPlatformLnkMacvlan *lnk, char *buf, gsize len) -{ - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - g_snprintf(buf, - len, - "%s mode %u %s", - lnk->tap ? "macvtap" : "macvlan", - lnk->mode, - lnk->no_promisc ? "not-promisc" : "promisc"); - return buf; -} - -const char * -nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len) -{ - char str_local[30]; - char str_local1[NM_UTILS_INET_ADDRSTRLEN]; - char str_remote[30]; - char str_remote1[NM_UTILS_INET_ADDRSTRLEN]; - char str_ttl[30]; - char str_tos[30]; - char str_flags[30]; - char str_proto[30]; - char str_parent_ifindex[30]; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - g_snprintf( - buf, - len, - "sit" - "%s" /* remote */ - "%s" /* local */ - "%s" /* parent_ifindex */ - "%s" /* ttl */ - "%s" /* tos */ - "%s" /* path_mtu_discovery */ - "%s" /* flags */ - "%s" /* proto */ - "", - lnk->remote ? nm_sprintf_buf(str_remote, - " remote %s", - _nm_utils_inet4_ntop(lnk->remote, str_remote1)) - : "", - lnk->local - ? nm_sprintf_buf(str_local, " local %s", _nm_utils_inet4_ntop(lnk->local, str_local1)) - : "", - lnk->parent_ifindex ? nm_sprintf_buf(str_parent_ifindex, " dev %d", lnk->parent_ifindex) - : "", - lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : " ttl inherit", - lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos 0x%x", lnk->tos)) - : "", - lnk->path_mtu_discovery ? "" : " nopmtudisc", - lnk->flags ? nm_sprintf_buf(str_flags, " flags 0x%x", lnk->flags) : "", - lnk->proto ? nm_sprintf_buf(str_proto, " proto 0x%x", lnk->proto) : ""); - return buf; -} - -const char * -nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len) -{ - char str_owner[50]; - char str_group[50]; - char str_type[50]; - const char *type; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - if (lnk->type == IFF_TUN) - type = "tun"; - else if (lnk->type == IFF_TAP) - type = "tap"; - else - type = nm_sprintf_buf(str_type, "tun type %u", (guint) lnk->type); - - g_snprintf(buf, - len, - "%s" /* type */ - "%s" /* pi */ - "%s" /* vnet_hdr */ - "%s" /* multi_queue */ - "%s" /* persist */ - "%s" /* owner */ - "%s" /* group */ - "", - type, - lnk->pi ? " pi" : "", - lnk->vnet_hdr ? " vnet_hdr" : "", - lnk->multi_queue ? " multi_queue" : "", - lnk->persist ? " persist" : "", - lnk->owner_valid ? nm_sprintf_buf(str_owner, " owner %u", (guint) lnk->owner) : "", - lnk->group_valid ? nm_sprintf_buf(str_group, " group %u", (guint) lnk->group) : ""); - return buf; -} - -const char * -nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len) -{ - char *b; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - b = buf; - - nm_utils_strbuf_append(&b, &len, "vlan %u", lnk->id); - if (lnk->flags) - nm_utils_strbuf_append(&b, &len, " flags 0x%x", lnk->flags); - return buf; -} - -const char * -nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len) -{ - char *b; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - b = buf; - - nm_utils_strbuf_append(&b, &len, "table %u", lnk->table); - return buf; -} - -const char * -nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len) -{ - char str_group[100]; - char str_group6[100]; - char str_local[100]; - char str_local6[100]; - char str_dev[25]; - char str_limit[25]; - char str_src_port[35]; - char str_dst_port[25]; - char str_tos[25]; - char str_ttl[25]; - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - if (lnk->group == 0) - str_group[0] = '\0'; - else { - g_snprintf(str_group, - sizeof(str_group), - " %s %s", - IN_MULTICAST(ntohl(lnk->group)) ? "group" : "remote", - _nm_utils_inet4_ntop(lnk->group, sbuf)); - } - if (IN6_IS_ADDR_UNSPECIFIED(&lnk->group6)) - str_group6[0] = '\0'; - else { - g_snprintf(str_group6, - sizeof(str_group6), - " %s%s %s", - IN6_IS_ADDR_MULTICAST(&lnk->group6) ? "group" : "remote", - str_group[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - _nm_utils_inet6_ntop(&lnk->group6, sbuf)); - } - - if (lnk->local == 0) - str_local[0] = '\0'; - else { - g_snprintf(str_local, - sizeof(str_local), - " local %s", - _nm_utils_inet4_ntop(lnk->local, sbuf)); - } - if (IN6_IS_ADDR_UNSPECIFIED(&lnk->local6)) - str_local6[0] = '\0'; - else { - g_snprintf(str_local6, - sizeof(str_local6), - " local%s %s", - str_local[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */ - _nm_utils_inet6_ntop(&lnk->local6, sbuf)); - } - - g_snprintf( - buf, - len, - "vxlan" - " id %u" /* id */ - "%s%s" /* group/group6 */ - "%s%s" /* local/local6 */ - "%s" /* dev */ - "%s" /* src_port_min/src_port_max */ - "%s" /* dst_port */ - "%s" /* learning */ - "%s" /* proxy */ - "%s" /* rsc */ - "%s" /* l2miss */ - "%s" /* l3miss */ - "%s" /* tos */ - "%s" /* ttl */ - " ageing %u" /* ageing */ - "%s" /* limit */ - "", - (guint) lnk->id, - str_group, - str_group6, - str_local, - str_local6, - lnk->parent_ifindex ? nm_sprintf_buf(str_dev, " dev %d", lnk->parent_ifindex) : "", - lnk->src_port_min || lnk->src_port_max - ? nm_sprintf_buf(str_src_port, " srcport %u %u", lnk->src_port_min, lnk->src_port_max) - : "", - lnk->dst_port ? nm_sprintf_buf(str_dst_port, " dstport %u", lnk->dst_port) : "", - !lnk->learning ? " nolearning" : "", - lnk->proxy ? " proxy" : "", - lnk->rsc ? " rsc" : "", - lnk->l2miss ? " l2miss" : "", - lnk->l3miss ? " l3miss" : "", - lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf(str_tos, " tos %#x", lnk->tos), - lnk->ttl ? nm_sprintf_buf(str_ttl, " ttl %u", lnk->ttl) : "", - lnk->ageing, - lnk->limit ? nm_sprintf_buf(str_limit, " maxaddr %u", lnk->limit) : ""); - return buf; -} - -const char * -nm_platform_wireguard_peer_to_string(const NMPWireGuardPeer *peer, char *buf, gsize len) -{ - char * buf0 = buf; - gs_free char *public_key_b64 = NULL; - char s_sockaddr[NM_UTILS_INET_ADDRSTRLEN + 100]; - char s_endpoint[20 + sizeof(s_sockaddr)]; - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; - char s_keepalive[100]; - guint i; - - nm_utils_to_string_buffer_init(&buf, &len); - - public_key_b64 = g_base64_encode(peer->public_key, sizeof(peer->public_key)); - - if (peer->endpoint.sa.sa_family != AF_UNSPEC) { - nm_sprintf_buf( - s_endpoint, - " endpoint %s", - nm_sock_addr_union_to_string(&peer->endpoint, s_sockaddr, sizeof(s_sockaddr))); - } else - s_endpoint[0] = '\0'; - - nm_utils_strbuf_append( - &buf, - &len, - "public-key %s" - "%s" /* preshared-key */ - "%s" /* endpoint */ - " rx %" G_GUINT64_FORMAT " tx %" G_GUINT64_FORMAT "%s" /* persistent-keepalive */ - "%s", /* allowed-ips */ - public_key_b64, - nm_utils_memeqzero_secret(peer->preshared_key, sizeof(peer->preshared_key)) - ? "" - : " preshared-key (hidden)", - s_endpoint, - peer->rx_bytes, - peer->tx_bytes, - peer->persistent_keepalive_interval > 0 - ? nm_sprintf_buf(s_keepalive, - " keepalive %u", - (guint) peer->persistent_keepalive_interval) - : "", - peer->allowed_ips_len > 0 ? " allowed-ips" : ""); - - for (i = 0; i < peer->allowed_ips_len; i++) { - const NMPWireGuardAllowedIP *allowed_ip = &peer->allowed_ips[i]; - - nm_utils_strbuf_append(&buf, - &len, - " %s/%u", - nm_utils_inet_ntop(allowed_ip->family, &allowed_ip->addr, s_addr), - allowed_ip->mask); - } - - return buf0; -} - -const char * -nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len) -{ - gs_free char *public_b64 = NULL; - - if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) - return buf; - - if (!nm_utils_memeqzero(lnk->public_key, sizeof(lnk->public_key))) - public_b64 = g_base64_encode(lnk->public_key, sizeof(lnk->public_key)); - - g_snprintf(buf, - len, - "wireguard" - "%s%s" /* public-key */ - "%s" /* private-key */ - " listen-port %u" - " fwmark 0x%x", - public_b64 ? " public-key " : "", - public_b64 ?: "", - nm_utils_memeqzero_secret(lnk->private_key, sizeof(lnk->private_key)) - ? "" - : " private-key (hidden)", - lnk->listen_port, - lnk->fwmark); - - return buf; -} - -/** - * nm_platform_ip4_address_to_string: - * @route: pointer to NMPlatformIP4Address address structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting an address struct into a string representation. - * - * Example output: "" - * - * Returns: a string representation of the address. - */ -const char * -nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len) -{ - char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; - char s_address[INET_ADDRSTRLEN]; - char s_peer[INET_ADDRSTRLEN]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_label[32]; - char str_lft[30], str_pref[30], str_time[50], s_source[50]; - char * str_peer = NULL; - const char *str_lft_p, *str_pref_p, *str_time_p; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); - in_addr_t broadcast_address; - char str_broadcast[INET_ADDRSTRLEN]; - - if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) - return buf; - - inet_ntop(AF_INET, &address->address, s_address, sizeof(s_address)); - - if (address->peer_address != address->address) { - inet_ntop(AF_INET, &address->peer_address, s_peer, sizeof(s_peer)); - str_peer = g_strconcat(" ptp ", s_peer, NULL); - } - - _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev)); - - if (*address->label) - g_snprintf(str_label, sizeof(str_label), " label %s", address->label); - else - str_label[0] = 0; - - str_lft_p = _lifetime_to_string(address->timestamp, - address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_lft, - sizeof(str_lft)), - str_pref_p = - (address->lifetime == address->preferred) - ? str_lft_p - : (_lifetime_to_string(address->timestamp, - address->lifetime ? MIN(address->preferred, address->lifetime) - : NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_pref, - sizeof(str_pref))); - str_time_p = _lifetime_summary_to_string(now, - address->timestamp, - address->preferred, - address->lifetime, - str_time, - sizeof(str_time)); - - broadcast_address = nm_platform_ip4_broadcast_address_from_addr(address); - - g_snprintf( - buf, - len, - "%s/%d" - "%s%s" /* broadcast */ - " lft %s" - " pref %s" - "%s" /* time */ - "%s" /* peer */ - "%s" /* dev */ - "%s" /* flags */ - "%s" /* label */ - " src %s" - "%s" /* external */ - "", - s_address, - address->plen, - broadcast_address ? " brd " : "", - broadcast_address ? _nm_utils_inet4_ntop(broadcast_address, str_broadcast) : "", - str_lft_p, - str_pref_p, - str_time_p, - str_peer ?: "", - str_dev, - _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), - str_label, - nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->external ? " ext" : ""); - g_free(str_peer); - return buf; -} - -NM_UTILS_FLAGS2STR_DEFINE(nm_platform_link_flags2str, - unsigned, - NM_UTILS_FLAGS2STR(IFF_LOOPBACK, "loopback"), - NM_UTILS_FLAGS2STR(IFF_BROADCAST, "broadcast"), - NM_UTILS_FLAGS2STR(IFF_POINTOPOINT, "pointopoint"), - NM_UTILS_FLAGS2STR(IFF_MULTICAST, "multicast"), - NM_UTILS_FLAGS2STR(IFF_NOARP, "noarp"), - NM_UTILS_FLAGS2STR(IFF_ALLMULTI, "allmulti"), - NM_UTILS_FLAGS2STR(IFF_PROMISC, "promisc"), - NM_UTILS_FLAGS2STR(IFF_MASTER, "master"), - NM_UTILS_FLAGS2STR(IFF_SLAVE, "slave"), - NM_UTILS_FLAGS2STR(IFF_DEBUG, "debug"), - NM_UTILS_FLAGS2STR(IFF_DYNAMIC, "dynamic"), - NM_UTILS_FLAGS2STR(IFF_AUTOMEDIA, "automedia"), - NM_UTILS_FLAGS2STR(IFF_PORTSEL, "portsel"), - NM_UTILS_FLAGS2STR(IFF_NOTRAILERS, "notrailers"), - NM_UTILS_FLAGS2STR(IFF_UP, "up"), - NM_UTILS_FLAGS2STR(IFF_RUNNING, "running"), - NM_UTILS_FLAGS2STR(IFF_LOWER_UP, "lowerup"), - NM_UTILS_FLAGS2STR(IFF_DORMANT, "dormant"), - NM_UTILS_FLAGS2STR(IFF_ECHO, "echo"), ); - -NM_UTILS_ENUM2STR_DEFINE(nm_platform_link_inet6_addrgenmode2str, - guint8, - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_NONE, "none"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"), - NM_UTILS_ENUM2STR(NM_IN6_ADDR_GEN_MODE_RANDOM, "random"), ); - -NM_UTILS_FLAGS2STR_DEFINE(nm_platform_addr_flags2str, - unsigned, - NM_UTILS_FLAGS2STR(IFA_F_SECONDARY, "secondary"), - NM_UTILS_FLAGS2STR(IFA_F_NODAD, "nodad"), - NM_UTILS_FLAGS2STR(IFA_F_OPTIMISTIC, "optimistic"), - NM_UTILS_FLAGS2STR(IFA_F_HOMEADDRESS, "homeaddress"), - NM_UTILS_FLAGS2STR(IFA_F_DEPRECATED, "deprecated"), - NM_UTILS_FLAGS2STR(IFA_F_PERMANENT, "permanent"), - NM_UTILS_FLAGS2STR(IFA_F_MANAGETEMPADDR, "mngtmpaddr"), - NM_UTILS_FLAGS2STR(IFA_F_NOPREFIXROUTE, "noprefixroute"), - NM_UTILS_FLAGS2STR(IFA_F_TENTATIVE, "tentative"), ); - -NM_UTILS_ENUM2STR_DEFINE(nm_platform_route_scope2str, - int, - NM_UTILS_ENUM2STR(RT_SCOPE_NOWHERE, "nowhere"), - NM_UTILS_ENUM2STR(RT_SCOPE_HOST, "host"), - NM_UTILS_ENUM2STR(RT_SCOPE_LINK, "link"), - NM_UTILS_ENUM2STR(RT_SCOPE_SITE, "site"), - NM_UTILS_ENUM2STR(RT_SCOPE_UNIVERSE, "global"), ); - -/** - * nm_platform_ip6_address_to_string: - * @route: pointer to NMPlatformIP6Address address structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting an address struct into a string representation. - * - * Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1" - * - * Returns: a string representation of the address. - */ -const char * -nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len) -{ - char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE]; - char s_address[INET6_ADDRSTRLEN]; - char s_peer[INET6_ADDRSTRLEN]; - char str_lft[30], str_pref[30], str_time[50], s_source[50]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char * str_peer = NULL; - const char *str_lft_p, *str_pref_p, *str_time_p; - gint32 now = nm_utils_get_monotonic_timestamp_sec(); - - if (!nm_utils_to_string_buffer_init_null(address, &buf, &len)) - return buf; - - inet_ntop(AF_INET6, &address->address, s_address, sizeof(s_address)); - - if (!IN6_IS_ADDR_UNSPECIFIED(&address->peer_address)) { - inet_ntop(AF_INET6, &address->peer_address, s_peer, sizeof(s_peer)); - str_peer = g_strconcat(" ptp ", s_peer, NULL); - } - - _to_string_dev(NULL, address->ifindex, str_dev, sizeof(str_dev)); - - str_lft_p = _lifetime_to_string(address->timestamp, - address->lifetime ?: NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_lft, - sizeof(str_lft)), - str_pref_p = - (address->lifetime == address->preferred) - ? str_lft_p - : (_lifetime_to_string(address->timestamp, - address->lifetime ? MIN(address->preferred, address->lifetime) - : NM_PLATFORM_LIFETIME_PERMANENT, - now, - str_pref, - sizeof(str_pref))); - str_time_p = _lifetime_summary_to_string(now, - address->timestamp, - address->preferred, - address->lifetime, - str_time, - sizeof(str_time)); - - g_snprintf( - buf, - len, - "%s/%d lft %s pref %s%s%s%s%s src %s%s", - s_address, - address->plen, - str_lft_p, - str_pref_p, - str_time_p, - str_peer ?: "", - str_dev, - _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), - nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->external ? " ext" : ""); - g_free(str_peer); - return buf; -} - -static NM_UTILS_FLAGS2STR_DEFINE(_rtm_flags_to_string, - unsigned, - NM_UTILS_FLAGS2STR(RTNH_F_DEAD, "dead"), - NM_UTILS_FLAGS2STR(RTNH_F_PERVASIVE, "pervasive"), - NM_UTILS_FLAGS2STR(RTNH_F_ONLINK, "onlink"), - NM_UTILS_FLAGS2STR(8 /*RTNH_F_OFFLOAD*/, "offload"), - NM_UTILS_FLAGS2STR(16 /*RTNH_F_LINKDOWN*/, "linkdown"), - NM_UTILS_FLAGS2STR(32 /*RTNH_F_UNRESOLVED*/, "unresolved"), - - NM_UTILS_FLAGS2STR(RTM_F_NOTIFY, "notify"), - NM_UTILS_FLAGS2STR(RTM_F_CLONED, "cloned"), - NM_UTILS_FLAGS2STR(RTM_F_EQUALIZE, "equalize"), - NM_UTILS_FLAGS2STR(RTM_F_PREFIX, "prefix"), - NM_UTILS_FLAGS2STR(0x1000 /*RTM_F_LOOKUP_TABLE*/, "lookup-table"), - NM_UTILS_FLAGS2STR(0x2000 /*RTM_F_FIB_MATCH*/, "fib-match"), ); - -#define _RTM_FLAGS_TO_STRING_MAXLEN 200 - -static const char * -_rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags) -{ - const char *buf0 = buf; - - nm_assert(buf_size >= _RTM_FLAGS_TO_STRING_MAXLEN); - - if (!rtm_flags) - return ""; - - nm_utils_strbuf_append_str(&buf, &buf_size, " rtm_flags "); - _rtm_flags_to_string(rtm_flags, buf, buf_size); - nm_assert(strlen(buf) < buf_size); - return buf0; -} - -/** - * nm_platform_ip4_route_to_string: - * @route: pointer to NMPlatformIP4Route route structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting a route struct into a string representation. - * - * Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0" - * - * Returns: a string representation of the route. - */ -const char * -nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len) -{ - char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN]; - char s_pref_src[INET_ADDRSTRLEN]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_table[30]; - char str_scope[30], s_source[50]; - char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; - char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; - char str_type[30]; - char str_metric[30]; - - if (!nm_utils_to_string_buffer_init_null(route, &buf, &len)) - return buf; - - inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network)); - inet_ntop(AF_INET, &route->gateway, s_gateway, sizeof(s_gateway)); - - _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev)); - - g_snprintf( - buf, - len, - "type %s " /* type */ - "%s" /* table */ - "%s/%d" - " via %s" - "%s" - " metric %s" - " mss %" G_GUINT32_FORMAT " rt-src %s" /* protocol */ - "%s" /* rtm_flags */ - "%s%s" /* scope */ - "%s%s" /* pref-src */ - "%s" /* tos */ - "%s" /* window */ - "%s" /* cwnd */ - "%s" /* initcwnd */ - "%s" /* initrwnd */ - "%s" /* mtu */ - "", - nm_utils_route_type2str(nm_platform_route_type_uncoerce(route->type_coerced), - str_type, - sizeof(str_type)), - route->table_any - ? "table ?? " - : (route->table_coerced - ? nm_sprintf_buf(str_table, - "table %u ", - nm_platform_route_table_uncoerce(route->table_coerced, FALSE)) - : ""), - s_network, - route->plen, - s_gateway, - str_dev, - route->metric_any ? "??" : nm_sprintf_buf(str_metric, "%u", route->metric), - route->mss, - nmp_utils_ip_config_source_to_string(route->rt_source, s_source, sizeof(s_source)), - _rtm_flags_to_string_full(str_rtm_flags, sizeof(str_rtm_flags), route->r_rtm_flags), - route->scope_inv ? " scope " : "", - route->scope_inv - ? (nm_platform_route_scope2str(nm_platform_route_scope_inv(route->scope_inv), - str_scope, - sizeof(str_scope))) - : "", - route->pref_src ? " pref-src " : "", - route->pref_src ? inet_ntop(AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "", - route->tos ? nm_sprintf_buf(str_tos, " tos 0x%x", (unsigned) route->tos) : "", - route->window || route->lock_window ? nm_sprintf_buf(str_window, - " window %s%" G_GUINT32_FORMAT, - route->lock_window ? "lock " : "", - route->window) - : "", - route->cwnd || route->lock_cwnd ? nm_sprintf_buf(str_cwnd, - " cwnd %s%" G_GUINT32_FORMAT, - route->lock_cwnd ? "lock " : "", - route->cwnd) - : "", - route->initcwnd || route->lock_initcwnd - ? nm_sprintf_buf(str_initcwnd, - " initcwnd %s%" G_GUINT32_FORMAT, - route->lock_initcwnd ? "lock " : "", - route->initcwnd) - : "", - route->initrwnd || route->lock_initrwnd - ? nm_sprintf_buf(str_initrwnd, - " initrwnd %s%" G_GUINT32_FORMAT, - route->lock_initrwnd ? "lock " : "", - route->initrwnd) - : "", - route->mtu || route->lock_mtu ? nm_sprintf_buf(str_mtu, - " mtu %s%" G_GUINT32_FORMAT, - route->lock_mtu ? "lock " : "", - route->mtu) - : ""); - return buf; -} - -/** - * nm_platform_ip6_route_to_string: - * @route: pointer to NMPlatformIP6Route route structure - * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. - * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. - * - * A method for converting a route struct into a string representation. - * - * Example output: "ff02::fb/128 via :: dev em1 metric 0" - * - * Returns: a string representation of the route. - */ -const char * -nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len) -{ - char s_network[INET6_ADDRSTRLEN]; - char s_gateway[INET6_ADDRSTRLEN]; - char s_pref_src[INET6_ADDRSTRLEN]; - char s_src_all[INET6_ADDRSTRLEN + 40]; - char s_src[INET6_ADDRSTRLEN]; - char str_type[30]; - char str_table[30]; - char str_pref[40]; - char str_pref2[30]; - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char s_source[50]; - char str_window[32]; - char str_cwnd[32]; - char str_initcwnd[32]; - char str_initrwnd[32]; - char str_mtu[32]; - char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; - char str_metric[30]; - - if (!nm_utils_to_string_buffer_init_null(route, &buf, &len)) - return buf; - - inet_ntop(AF_INET6, &route->network, s_network, sizeof(s_network)); - inet_ntop(AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway)); - - if (IN6_IS_ADDR_UNSPECIFIED(&route->pref_src)) - s_pref_src[0] = 0; - else - inet_ntop(AF_INET6, &route->pref_src, s_pref_src, sizeof(s_pref_src)); - - _to_string_dev(NULL, route->ifindex, str_dev, sizeof(str_dev)); - - g_snprintf( - buf, - len, - "type %s " /* type */ - "%s" /* table */ - "%s/%d" - " via %s" - "%s" - " metric %s" - " mss %" G_GUINT32_FORMAT " rt-src %s" /* protocol */ - "%s" /* source */ - "%s" /* rtm_flags */ - "%s%s" /* pref-src */ - "%s" /* window */ - "%s" /* cwnd */ - "%s" /* initcwnd */ - "%s" /* initrwnd */ - "%s" /* mtu */ - "%s" /* pref */ - "", - nm_utils_route_type2str(nm_platform_route_type_uncoerce(route->type_coerced), - str_type, - sizeof(str_type)), - route->table_any - ? "table ?? " - : (route->table_coerced - ? nm_sprintf_buf(str_table, - "table %u ", - nm_platform_route_table_uncoerce(route->table_coerced, FALSE)) - : ""), - s_network, - route->plen, - s_gateway, - str_dev, - route->metric_any ? "??" : nm_sprintf_buf(str_metric, "%u", route->metric), - route->mss, - nmp_utils_ip_config_source_to_string(route->rt_source, s_source, sizeof(s_source)), - route->src_plen || !IN6_IS_ADDR_UNSPECIFIED(&route->src) - ? nm_sprintf_buf(s_src_all, - " src %s/%u", - _nm_utils_inet6_ntop(&route->src, s_src), - (unsigned) route->src_plen) - : "", - _rtm_flags_to_string_full(str_rtm_flags, sizeof(str_rtm_flags), route->r_rtm_flags), - s_pref_src[0] ? " pref-src " : "", - s_pref_src[0] ? s_pref_src : "", - route->window || route->lock_window ? nm_sprintf_buf(str_window, - " window %s%" G_GUINT32_FORMAT, - route->lock_window ? "lock " : "", - route->window) - : "", - route->cwnd || route->lock_cwnd ? nm_sprintf_buf(str_cwnd, - " cwnd %s%" G_GUINT32_FORMAT, - route->lock_cwnd ? "lock " : "", - route->cwnd) - : "", - route->initcwnd || route->lock_initcwnd - ? nm_sprintf_buf(str_initcwnd, - " initcwnd %s%" G_GUINT32_FORMAT, - route->lock_initcwnd ? "lock " : "", - route->initcwnd) - : "", - route->initrwnd || route->lock_initrwnd - ? nm_sprintf_buf(str_initrwnd, - " initrwnd %s%" G_GUINT32_FORMAT, - route->lock_initrwnd ? "lock " : "", - route->initrwnd) - : "", - route->mtu || route->lock_mtu ? nm_sprintf_buf(str_mtu, - " mtu %s%" G_GUINT32_FORMAT, - route->lock_mtu ? "lock " : "", - route->mtu) - : "", - route->rt_pref ? nm_sprintf_buf( - str_pref, - " pref %s", - nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2))) - : ""); - - return buf; -} - -static void -_routing_rule_addr_to_string(char ** buf, - gsize * len, - int addr_family, - const NMIPAddr *addr, - guint8 plen, - gboolean is_src) -{ - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; - gboolean is_zero; - gsize addr_size; - - nm_assert_addr_family(addr_family); - nm_assert(addr); - - addr_size = nm_utils_addr_family_to_size(addr_family); - - is_zero = nm_utils_memeqzero(addr, addr_size); - - if (plen == 0 && is_zero) { - if (is_src) - nm_utils_strbuf_append_str(buf, len, " from all"); - else - nm_utils_strbuf_append_str(buf, len, ""); - return; - } - - nm_utils_strbuf_append_str(buf, len, is_src ? " from " : " to "); - - nm_utils_strbuf_append_str(buf, len, nm_utils_inet_ntop(addr_family, addr, s_addr)); - - if (plen != (addr_size * 8)) - nm_utils_strbuf_append(buf, len, "/%u", plen); -} - -static void -_routing_rule_port_range_to_string(char ** buf, - gsize * len, - const NMFibRulePortRange *port_range, - const char * name) -{ - if (port_range->start == 0 && port_range->end == 0) - nm_utils_strbuf_append_str(buf, len, ""); - else { - nm_utils_strbuf_append(buf, len, " %s %u", name, port_range->start); - if (port_range->start != port_range->end) - nm_utils_strbuf_append(buf, len, "-%u", port_range->end); - } -} - -const char * -nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len) -{ - const char *buf0; - guint32 rr_flags; - - if (!nm_utils_to_string_buffer_init_null(routing_rule, &buf, &len)) - return buf; - - if (!NM_IN_SET(routing_rule->addr_family, AF_INET, AF_INET6)) { - /* invalid addr-family. The other fields are undefined. */ - if (routing_rule->addr_family == AF_UNSPEC) - g_snprintf(buf, len, "[routing-rule]"); - else - g_snprintf(buf, len, "[routing-rule family:%u]", routing_rule->addr_family); - return buf; - } - - buf0 = buf; - - rr_flags = routing_rule->flags; - - rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_INVERT); - nm_utils_strbuf_append(&buf, - &len, - "[%c] " /* addr-family */ - "%u:" /* priority */ - "%s", /* not/FIB_RULE_INVERT */ - nm_utils_addr_family_to_char(routing_rule->addr_family), - routing_rule->priority, - (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_INVERT) ? " not" : "")); - - _routing_rule_addr_to_string(&buf, - &len, - routing_rule->addr_family, - &routing_rule->src, - routing_rule->src_len, - TRUE); - - _routing_rule_addr_to_string(&buf, - &len, - routing_rule->addr_family, - &routing_rule->dst, - routing_rule->dst_len, - FALSE); - - if (routing_rule->tos) - nm_utils_strbuf_append(&buf, &len, " tos 0x%02x", routing_rule->tos); - - if (routing_rule->fwmark != 0 || routing_rule->fwmask != 0) { - nm_utils_strbuf_append(&buf, &len, " fwmark %#x", (unsigned) routing_rule->fwmark); - if (routing_rule->fwmark != 0xFFFFFFFFu) - nm_utils_strbuf_append(&buf, &len, "/%#x", (unsigned) routing_rule->fwmask); - } - - if (routing_rule->iifname[0]) { - nm_utils_strbuf_append(&buf, &len, " iif %s", routing_rule->iifname); - rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_IIF_DETACHED); - if (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_IIF_DETACHED)) - nm_utils_strbuf_append_str(&buf, &len, " [detached]"); - } - - if (routing_rule->oifname[0]) { - nm_utils_strbuf_append(&buf, &len, " oif %s", routing_rule->oifname); - rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_OIF_DETACHED); - if (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_OIF_DETACHED)) - nm_utils_strbuf_append_str(&buf, &len, " [detached]"); - } - - if (routing_rule->l3mdev != 0) { - if (routing_rule->l3mdev == 1) - nm_utils_strbuf_append_str(&buf, &len, " lookup [l3mdev-table]"); - else { - nm_utils_strbuf_append(&buf, - &len, - " lookup [l3mdev-table/%u]", - (unsigned) routing_rule->l3mdev); - } - } - - if (routing_rule->uid_range_has || routing_rule->uid_range.start - || routing_rule->uid_range.end) { - nm_utils_strbuf_append(&buf, - &len, - " uidrange %u-%u%s", - routing_rule->uid_range.start, - routing_rule->uid_range.end, - routing_rule->uid_range_has ? "" : "(?)"); - } - - if (routing_rule->ip_proto != 0) { - /* we don't call getprotobynumber(), just print the numeric value. - * This differs from what ip-rule prints. */ - nm_utils_strbuf_append(&buf, &len, " ipproto %u", routing_rule->ip_proto); - } - - _routing_rule_port_range_to_string(&buf, &len, &routing_rule->sport_range, "sport"); - - _routing_rule_port_range_to_string(&buf, &len, &routing_rule->dport_range, "dport"); - - if (routing_rule->tun_id != 0) { - nm_utils_strbuf_append(&buf, &len, " tun_id %" G_GUINT64_FORMAT, routing_rule->tun_id); - } - - if (routing_rule->table != 0) { - nm_utils_strbuf_append(&buf, &len, " lookup %u", routing_rule->table); - } - - if (routing_rule->suppress_prefixlen_inverse != 0) { - nm_utils_strbuf_append(&buf, - &len, - " suppress_prefixlen %d", - (int) (~routing_rule->suppress_prefixlen_inverse)); - } - - if (routing_rule->suppress_ifgroup_inverse != 0) { - nm_utils_strbuf_append(&buf, - &len, - " suppress_ifgroup %d", - (int) (~routing_rule->suppress_ifgroup_inverse)); - } - - if (routing_rule->flow) { - /* FRA_FLOW is only for IPv4, but we want to print the value for all address-families, - * to see when it is set. In practice, this should not be set except for IPv4. - * - * We don't follow the style how ip-rule prints flow/realms. It's confusing. Just - * print the value hex. */ - nm_utils_strbuf_append(&buf, &len, " realms 0x%08x", routing_rule->flow); - } - - if (routing_rule->action == RTN_NAT) { - G_STATIC_ASSERT_EXPR(RTN_NAT == 10); - - /* NAT is deprecated for many years. We don't support RTA_GATEWAY/FRA_UNUSED2 - * for the gateway, and so do recent kernels ignore that parameter. */ - nm_utils_strbuf_append_str(&buf, &len, " masquerade"); - } else if (routing_rule->action == FR_ACT_GOTO) { - if (routing_rule->goto_target != 0) - nm_utils_strbuf_append(&buf, &len, " goto %u", routing_rule->goto_target); - else - nm_utils_strbuf_append_str(&buf, &len, " goto none"); - rr_flags = NM_FLAGS_UNSET(rr_flags, FIB_RULE_UNRESOLVED); - if (NM_FLAGS_HAS(routing_rule->flags, FIB_RULE_UNRESOLVED)) - nm_utils_strbuf_append_str(&buf, &len, " unresolved"); - } else if (routing_rule->action != FR_ACT_TO_TBL) { - const char *ss; - char ss_buf[60]; - -#define _V(v1, v2) ((sizeof(char[(((int) (v1)) == ((int) (v2))) ? 1 : -1]) * 0) + (v1)) - switch (routing_rule->action) { - case _V(FR_ACT_UNSPEC, RTN_UNSPEC): - ss = "none"; - break; - case _V(FR_ACT_TO_TBL, RTN_UNICAST): - ss = "unicast"; - break; - case _V(FR_ACT_GOTO, RTN_LOCAL): - ss = "local"; - break; - case _V(FR_ACT_NOP, RTN_BROADCAST): - ss = "nop"; - break; - case _V(FR_ACT_RES3, RTN_ANYCAST): - ss = "anycast"; - break; - case _V(FR_ACT_RES4, RTN_MULTICAST): - ss = "multicast"; - break; - case _V(FR_ACT_BLACKHOLE, RTN_BLACKHOLE): - ss = "blackhole"; - break; - case _V(FR_ACT_UNREACHABLE, RTN_UNREACHABLE): - ss = "unreachable"; - break; - case _V(FR_ACT_PROHIBIT, RTN_PROHIBIT): - ss = "prohibit"; - break; - case RTN_THROW: - ss = "throw"; - break; - case RTN_NAT: - ss = "nat"; - break; - case RTN_XRESOLVE: - ss = "xresolve"; - break; - default: - ss = nm_sprintf_buf(ss_buf, "action-%u", routing_rule->action); - break; - } -#undef _V - nm_utils_strbuf_append(&buf, &len, " %s", ss); - } - - if (routing_rule->protocol != RTPROT_UNSPEC) - nm_utils_strbuf_append(&buf, &len, " protocol %u", routing_rule->protocol); - - if (routing_rule->goto_target != 0 && routing_rule->action != FR_ACT_GOTO) { - /* a trailing target is set for an unexpected action. Print it. */ - nm_utils_strbuf_append(&buf, &len, " goto-target %u", routing_rule->goto_target); - } - - if (rr_flags != 0) { - /* we have some flags we didn't print about yet. */ - nm_utils_strbuf_append(&buf, &len, " remaining-flags %x", rr_flags); - } - - return buf0; -} - -const char * -nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len) -{ - char str_dev[TO_STRING_DEV_BUF_SIZE]; - const char *buf0; - - if (!nm_utils_to_string_buffer_init_null(qdisc, &buf, &len)) - return buf; - - buf0 = buf; - - nm_utils_strbuf_append(&buf, - &len, - "%s%s family %u handle %x parent %x info %x", - qdisc->kind, - _to_string_dev(NULL, qdisc->ifindex, str_dev, sizeof(str_dev)), - qdisc->addr_family, - qdisc->handle, - qdisc->parent, - qdisc->info); - - if (nm_streq0(qdisc->kind, "fq_codel")) { - if (qdisc->fq_codel.limit) - nm_utils_strbuf_append(&buf, &len, " limit %u", qdisc->fq_codel.limit); - if (qdisc->fq_codel.flows) - nm_utils_strbuf_append(&buf, &len, " flows %u", qdisc->fq_codel.flows); - if (qdisc->fq_codel.target) - nm_utils_strbuf_append(&buf, &len, " target %u", qdisc->fq_codel.target); - if (qdisc->fq_codel.interval) - nm_utils_strbuf_append(&buf, &len, " interval %u", qdisc->fq_codel.interval); - if (qdisc->fq_codel.quantum) - nm_utils_strbuf_append(&buf, &len, " quantum %u", qdisc->fq_codel.quantum); - if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) - nm_utils_strbuf_append(&buf, &len, " ce_threshold %u", qdisc->fq_codel.ce_threshold); - if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) - nm_utils_strbuf_append(&buf, &len, " memory_limit %u", qdisc->fq_codel.memory_limit); - if (qdisc->fq_codel.ecn) - nm_utils_strbuf_append(&buf, &len, " ecn"); - } else if (nm_streq0(qdisc->kind, "sfq")) { - if (qdisc->sfq.quantum) - nm_utils_strbuf_append(&buf, &len, " quantum %u", qdisc->sfq.quantum); - if (qdisc->sfq.perturb_period) - nm_utils_strbuf_append(&buf, &len, " perturb %d", qdisc->sfq.perturb_period); - if (qdisc->sfq.limit) - nm_utils_strbuf_append(&buf, &len, " limit %u", (guint) qdisc->sfq.limit); - if (qdisc->sfq.divisor) - nm_utils_strbuf_append(&buf, &len, " divisor %u", qdisc->sfq.divisor); - if (qdisc->sfq.flows) - nm_utils_strbuf_append(&buf, &len, " flows %u", qdisc->sfq.flows); - if (qdisc->sfq.depth) - nm_utils_strbuf_append(&buf, &len, " depth %u", qdisc->sfq.depth); - } else if (nm_streq0(qdisc->kind, "tbf")) { - nm_utils_strbuf_append(&buf, &len, " rate %" G_GUINT64_FORMAT, qdisc->tbf.rate); - nm_utils_strbuf_append(&buf, &len, " burst %u", qdisc->tbf.burst); - if (qdisc->tbf.limit) - nm_utils_strbuf_append(&buf, &len, " limit %u", qdisc->tbf.limit); - if (qdisc->tbf.latency) - nm_utils_strbuf_append(&buf, &len, " latency %uns", qdisc->tbf.latency); - } - - return buf0; -} - -void -nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h) -{ - nm_hash_update_str0(h, obj->kind); - nm_hash_update_vals(h, obj->ifindex, obj->addr_family, obj->handle, obj->parent, obj->info); - if (nm_streq0(obj->kind, "fq_codel")) { - nm_hash_update_vals(h, - obj->fq_codel.limit, - obj->fq_codel.flows, - obj->fq_codel.target, - obj->fq_codel.interval, - obj->fq_codel.quantum, - obj->fq_codel.ce_threshold, - obj->fq_codel.memory_limit, - NM_HASH_COMBINE_BOOLS(guint8, obj->fq_codel.ecn)); - } else if (nm_streq0(obj->kind, "sfq")) { - nm_hash_update_vals(h, - obj->sfq.quantum, - obj->sfq.perturb_period, - obj->sfq.limit, - obj->sfq.divisor, - obj->sfq.flows, - obj->sfq.depth); - } else if (nm_streq0(obj->kind, "tbf")) { - nm_hash_update_vals(h, obj->tbf.rate, obj->tbf.burst, obj->tbf.limit, obj->tbf.latency); - } -} - -int -nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, parent); - NM_CMP_FIELD_STR_INTERNED(a, b, kind); - NM_CMP_FIELD(a, b, addr_family); - if (compare_handle) - NM_CMP_FIELD(a, b, handle); - NM_CMP_FIELD(a, b, info); - - if (nm_streq0(a->kind, "fq_codel")) { - NM_CMP_FIELD(a, b, fq_codel.limit); - NM_CMP_FIELD(a, b, fq_codel.flows); - NM_CMP_FIELD(a, b, fq_codel.target); - NM_CMP_FIELD(a, b, fq_codel.interval); - NM_CMP_FIELD(a, b, fq_codel.quantum); - NM_CMP_FIELD(a, b, fq_codel.ce_threshold); - NM_CMP_FIELD(a, b, fq_codel.memory_limit); - NM_CMP_FIELD_UNSAFE(a, b, fq_codel.ecn); - } else if (nm_streq0(a->kind, "sfq")) { - NM_CMP_FIELD(a, b, sfq.quantum); - NM_CMP_FIELD(a, b, sfq.perturb_period); - NM_CMP_FIELD(a, b, sfq.limit); - NM_CMP_FIELD(a, b, sfq.flows); - NM_CMP_FIELD(a, b, sfq.divisor); - NM_CMP_FIELD(a, b, sfq.depth); - } else if (nm_streq0(a->kind, "tbf")) { - NM_CMP_FIELD(a, b, tbf.rate); - NM_CMP_FIELD(a, b, tbf.burst); - NM_CMP_FIELD(a, b, tbf.limit); - NM_CMP_FIELD(a, b, tbf.latency); - } - - return 0; -} - -int -nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b) -{ - return nm_platform_qdisc_cmp_full(a, b, TRUE); -} - -const char * -nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len) -{ - char str_dev[TO_STRING_DEV_BUF_SIZE]; - char act_buf[300]; - char *p; - gsize l; - - if (!nm_utils_to_string_buffer_init_null(tfilter, &buf, &len)) - return buf; - - if (tfilter->action.kind) { - p = act_buf; - l = sizeof(act_buf); - - nm_utils_strbuf_append(&p, &l, " \"%s\"", tfilter->action.kind); - if (nm_streq(tfilter->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { - gs_free char *t = NULL; - - nm_utils_strbuf_append( - &p, - &l, - " (\"%s\")", - nm_utils_str_utf8safe_escape(tfilter->action.kind, - NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL - | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII, - &t)); - } else if (nm_streq(tfilter->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { - nm_utils_strbuf_append(&p, - &l, - "%s%s%s%s dev %d", - tfilter->action.mirred.ingress ? " ingress" : "", - tfilter->action.mirred.egress ? " egress" : "", - tfilter->action.mirred.mirror ? " mirror" : "", - tfilter->action.mirred.redirect ? " redirect" : "", - tfilter->action.mirred.ifindex); - } - } else - act_buf[0] = '\0'; - - g_snprintf(buf, - len, - "%s%s family %u handle %x parent %x info %x%s", - tfilter->kind, - _to_string_dev(NULL, tfilter->ifindex, str_dev, sizeof(str_dev)), - tfilter->addr_family, - tfilter->handle, - tfilter->parent, - tfilter->info, - act_buf); - - return buf; -} - -void -nm_platform_tfilter_hash_update(const NMPlatformTfilter *obj, NMHashState *h) -{ - nm_hash_update_str0(h, obj->kind); - nm_hash_update_vals(h, obj->ifindex, obj->addr_family, obj->handle, obj->parent, obj->info); - if (obj->action.kind) { - nm_hash_update_str(h, obj->action.kind); - if (nm_streq(obj->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { - nm_hash_update_strarr(h, obj->action.simple.sdata); - } else if (nm_streq(obj->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { - nm_hash_update_vals(h, - obj->action.mirred.ifindex, - NM_HASH_COMBINE_BOOLS(guint8, - obj->action.mirred.ingress, - obj->action.mirred.egress, - obj->action.mirred.mirror, - obj->action.mirred.redirect)); - } - } -} - -int -nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, parent); - NM_CMP_FIELD_STR_INTERNED(a, b, kind); - NM_CMP_FIELD(a, b, addr_family); - NM_CMP_FIELD(a, b, handle); - NM_CMP_FIELD(a, b, info); - - NM_CMP_FIELD_STR_INTERNED(a, b, action.kind); - if (a->action.kind) { - if (nm_streq(a->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { - NM_CMP_FIELD_STR(a, b, action.simple.sdata); - } else if (nm_streq(a->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { - NM_CMP_FIELD(a, b, action.mirred.ifindex); - NM_CMP_FIELD_UNSAFE(a, b, action.mirred.ingress); - NM_CMP_FIELD_UNSAFE(a, b, action.mirred.egress); - NM_CMP_FIELD_UNSAFE(a, b, action.mirred.mirror); - NM_CMP_FIELD_UNSAFE(a, b, action.mirred.redirect); - } - } - - return 0; -} - -const char * -nm_platform_vf_to_string(const NMPlatformVF *vf, char *buf, gsize len) -{ - char str_mac[128], mac[128]; - char str_spoof_check[64]; - char str_trust[64]; - char str_min_tx_rate[64]; - char str_max_tx_rate[64]; - nm_auto_free_gstring GString *gstr_vlans = NULL; - guint i; - - if (!nm_utils_to_string_buffer_init_null(vf, &buf, &len)) - return buf; - - if (vf->mac.len) { - _nm_utils_hwaddr_ntoa(vf->mac.data, vf->mac.len, TRUE, mac, sizeof(mac)); - nm_sprintf_buf(str_mac, " mac %s", mac); - } else - str_mac[0] = '\0'; - - if (vf->num_vlans) { - gstr_vlans = g_string_new(""); - for (i = 0; i < vf->num_vlans; i++) { - g_string_append_printf(gstr_vlans, " vlan %u", (unsigned) vf->vlans[i].id); - if (vf->vlans[i].qos) - g_string_append_printf(gstr_vlans, " qos %u", (unsigned) vf->vlans[i].qos); - if (vf->vlans[i].proto_ad) - g_string_append(gstr_vlans, " proto 802.1ad"); - } - } - - g_snprintf(buf, - len, - "%u" /* index */ - "%s" /* MAC */ - "%s" /* spoof check */ - "%s" /* trust */ - "%s" /* min tx rate */ - "%s" /* max tx rate */ - "%s", /* VLANs */ - vf->index, - str_mac, - vf->spoofchk >= 0 ? nm_sprintf_buf(str_spoof_check, " spoofchk %d", vf->spoofchk) - : "", - vf->trust >= 0 ? nm_sprintf_buf(str_trust, " trust %d", vf->trust) : "", - vf->min_tx_rate - ? nm_sprintf_buf(str_min_tx_rate, " min_tx_rate %u", (unsigned) vf->min_tx_rate) - : "", - vf->max_tx_rate - ? nm_sprintf_buf(str_max_tx_rate, " max_tx_rate %u", (unsigned) vf->max_tx_rate) - : "", - gstr_vlans ? gstr_vlans->str : ""); - - return buf; -} - -const char * -nm_platform_bridge_vlan_to_string(const NMPlatformBridgeVlan *vlan, char *buf, gsize len) -{ - char str_vid_end[64]; - - if (!nm_utils_to_string_buffer_init_null(vlan, &buf, &len)) - return buf; - - g_snprintf(buf, - len, - "%u" - "%s" - "%s" - "%s", - vlan->vid_start, - vlan->vid_start != vlan->vid_end ? nm_sprintf_buf(str_vid_end, "-%u", vlan->vid_end) - : "", - vlan->pvid ? " PVID" : "", - vlan->untagged ? " untagged" : ""); - - return buf; -} - -void -nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->ifindex, - obj->master, - obj->parent, - obj->n_ifi_flags, - obj->mtu, - obj->type, - obj->arptype, - obj->inet6_addr_gen_mode_inv, - obj->inet6_token, - obj->rx_packets, - obj->rx_bytes, - obj->tx_packets, - obj->tx_bytes, - NM_HASH_COMBINE_BOOLS(guint8, obj->connected, obj->initialized)); - nm_hash_update_strarr(h, obj->name); - nm_hash_update_str0(h, obj->kind); - nm_hash_update_str0(h, obj->driver); - /* nm_hash_update_mem() also hashes the length obj->addr.len */ - nm_hash_update_mem(h, - obj->l_address.data, - NM_MIN(obj->l_address.len, sizeof(obj->l_address.data))); - nm_hash_update_mem(h, - obj->l_broadcast.data, - NM_MIN(obj->l_broadcast.len, sizeof(obj->l_broadcast.data))); -} - -int -nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, type); - NM_CMP_FIELD_STR(a, b, name); - NM_CMP_FIELD(a, b, master); - NM_CMP_FIELD(a, b, parent); - NM_CMP_FIELD(a, b, n_ifi_flags); - NM_CMP_FIELD_UNSAFE(a, b, connected); - NM_CMP_FIELD(a, b, mtu); - NM_CMP_FIELD_BOOL(a, b, initialized); - NM_CMP_FIELD(a, b, arptype); - NM_CMP_FIELD(a, b, l_address.len); - NM_CMP_FIELD(a, b, l_broadcast.len); - NM_CMP_FIELD(a, b, inet6_addr_gen_mode_inv); - NM_CMP_FIELD_STR_INTERNED(a, b, kind); - NM_CMP_FIELD_STR_INTERNED(a, b, driver); - if (a->l_address.len) - NM_CMP_FIELD_MEMCMP_LEN(a, b, l_address.data, a->l_address.len); - if (a->l_broadcast.len) - NM_CMP_FIELD_MEMCMP_LEN(a, b, l_broadcast.data, a->l_broadcast.len); - NM_CMP_FIELD_MEMCMP(a, b, inet6_token); - NM_CMP_FIELD(a, b, rx_packets); - NM_CMP_FIELD(a, b, rx_bytes); - NM_CMP_FIELD(a, b, tx_packets); - NM_CMP_FIELD(a, b, tx_bytes); - return 0; -} - -void -nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->forward_delay, - obj->hello_time, - obj->max_age, - obj->ageing_time, - obj->priority, - obj->vlan_protocol, - obj->group_fwd_mask, - obj->group_addr, - obj->mcast_hash_max, - obj->mcast_last_member_count, - obj->mcast_startup_query_count, - obj->mcast_last_member_interval, - obj->mcast_membership_interval, - obj->mcast_querier_interval, - obj->mcast_query_interval, - obj->mcast_router, - obj->mcast_query_response_interval, - obj->mcast_startup_query_interval, - NM_HASH_COMBINE_BOOLS(guint8, - obj->stp_state, - obj->mcast_querier, - obj->mcast_query_use_ifaddr, - obj->mcast_snooping, - obj->vlan_stats_enabled)); -} - -int -nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, forward_delay); - NM_CMP_FIELD(a, b, hello_time); - NM_CMP_FIELD(a, b, max_age); - NM_CMP_FIELD(a, b, ageing_time); - NM_CMP_FIELD_BOOL(a, b, stp_state); - NM_CMP_FIELD(a, b, priority); - NM_CMP_FIELD(a, b, vlan_protocol); - NM_CMP_FIELD_BOOL(a, b, vlan_stats_enabled); - NM_CMP_FIELD(a, b, group_fwd_mask); - NM_CMP_FIELD_MEMCMP(a, b, group_addr); - NM_CMP_FIELD_BOOL(a, b, mcast_snooping); - NM_CMP_FIELD(a, b, mcast_router); - NM_CMP_FIELD_BOOL(a, b, mcast_query_use_ifaddr); - NM_CMP_FIELD_BOOL(a, b, mcast_querier); - NM_CMP_FIELD(a, b, mcast_hash_max); - NM_CMP_FIELD(a, b, mcast_last_member_count); - NM_CMP_FIELD(a, b, mcast_startup_query_count); - NM_CMP_FIELD(a, b, mcast_last_member_interval); - NM_CMP_FIELD(a, b, mcast_membership_interval); - NM_CMP_FIELD(a, b, mcast_querier_interval); - NM_CMP_FIELD(a, b, mcast_query_interval); - NM_CMP_FIELD(a, b, mcast_query_response_interval); - NM_CMP_FIELD(a, b, mcast_startup_query_interval); - - return 0; -} - -void -nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->local, - obj->remote, - obj->parent_ifindex, - obj->input_flags, - obj->output_flags, - obj->input_key, - obj->output_key, - obj->ttl, - obj->tos, - (bool) obj->path_mtu_discovery, - (bool) obj->is_tap); -} - -int -nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, parent_ifindex); - NM_CMP_FIELD(a, b, input_flags); - NM_CMP_FIELD(a, b, output_flags); - NM_CMP_FIELD(a, b, input_key); - NM_CMP_FIELD(a, b, output_key); - NM_CMP_FIELD(a, b, local); - NM_CMP_FIELD(a, b, remote); - NM_CMP_FIELD(a, b, ttl); - NM_CMP_FIELD(a, b, tos); - NM_CMP_FIELD_BOOL(a, b, path_mtu_discovery); - NM_CMP_FIELD_BOOL(a, b, is_tap); - return 0; -} - -void -nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h) -{ - nm_hash_update_val(h, obj->p_key); - nm_hash_update_str0(h, obj->mode); -} - -int -nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, p_key); - NM_CMP_FIELD_STR_INTERNED(a, b, mode); - return 0; -} - -void -nm_platform_lnk_ip6tnl_hash_update(const NMPlatformLnkIp6Tnl *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->local, - obj->remote, - obj->parent_ifindex, - obj->ttl, - obj->tclass, - obj->encap_limit, - obj->proto, - obj->flow_label, - obj->flags, - obj->input_flags, - obj->output_flags, - obj->input_key, - obj->output_key, - (bool) obj->is_gre, - (bool) obj->is_tap); -} - -int -nm_platform_lnk_ip6tnl_cmp(const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, parent_ifindex); - NM_CMP_FIELD_MEMCMP(a, b, local); - NM_CMP_FIELD_MEMCMP(a, b, remote); - NM_CMP_FIELD(a, b, ttl); - NM_CMP_FIELD(a, b, tclass); - NM_CMP_FIELD(a, b, encap_limit); - NM_CMP_FIELD(a, b, flow_label); - NM_CMP_FIELD(a, b, proto); - NM_CMP_FIELD(a, b, flags); - NM_CMP_FIELD(a, b, input_flags); - NM_CMP_FIELD(a, b, output_flags); - NM_CMP_FIELD(a, b, input_key); - NM_CMP_FIELD(a, b, output_key); - NM_CMP_FIELD_BOOL(a, b, is_gre); - NM_CMP_FIELD_BOOL(a, b, is_tap); - return 0; -} - -void -nm_platform_lnk_ipip_hash_update(const NMPlatformLnkIpIp *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->local, - obj->remote, - obj->parent_ifindex, - obj->ttl, - obj->tos, - (bool) obj->path_mtu_discovery); -} - -int -nm_platform_lnk_ipip_cmp(const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, parent_ifindex); - NM_CMP_FIELD(a, b, local); - NM_CMP_FIELD(a, b, remote); - NM_CMP_FIELD(a, b, ttl); - NM_CMP_FIELD(a, b, tos); - NM_CMP_FIELD_BOOL(a, b, path_mtu_discovery); - return 0; -} - -void -nm_platform_lnk_macsec_hash_update(const NMPlatformLnkMacsec *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->parent_ifindex, - obj->sci, - obj->cipher_suite, - obj->window, - obj->icv_length, - obj->encoding_sa, - obj->validation, - NM_HASH_COMBINE_BOOLS(guint8, - obj->encrypt, - obj->protect, - obj->include_sci, - obj->es, - obj->scb, - obj->replay_protect)); -} - -int -nm_platform_lnk_macsec_cmp(const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, parent_ifindex); - NM_CMP_FIELD(a, b, sci); - NM_CMP_FIELD(a, b, icv_length); - NM_CMP_FIELD(a, b, cipher_suite); - NM_CMP_FIELD(a, b, window); - NM_CMP_FIELD(a, b, encoding_sa); - NM_CMP_FIELD(a, b, validation); - NM_CMP_FIELD_UNSAFE(a, b, encrypt); - NM_CMP_FIELD_UNSAFE(a, b, protect); - NM_CMP_FIELD_UNSAFE(a, b, include_sci); - NM_CMP_FIELD_UNSAFE(a, b, es); - NM_CMP_FIELD_UNSAFE(a, b, scb); - NM_CMP_FIELD_UNSAFE(a, b, replay_protect); - return 0; -} - -void -nm_platform_lnk_macvlan_hash_update(const NMPlatformLnkMacvlan *obj, NMHashState *h) -{ - nm_hash_update_vals(h, obj->mode, NM_HASH_COMBINE_BOOLS(guint8, obj->no_promisc, obj->tap)); -} - -int -nm_platform_lnk_macvlan_cmp(const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, mode); - NM_CMP_FIELD_UNSAFE(a, b, no_promisc); - NM_CMP_FIELD_UNSAFE(a, b, tap); - return 0; -} - -void -nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->local, - obj->remote, - obj->parent_ifindex, - obj->flags, - obj->ttl, - obj->tos, - obj->proto, - (bool) obj->path_mtu_discovery); -} - -int -nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, parent_ifindex); - NM_CMP_FIELD(a, b, local); - NM_CMP_FIELD(a, b, remote); - NM_CMP_FIELD(a, b, ttl); - NM_CMP_FIELD(a, b, tos); - NM_CMP_FIELD_BOOL(a, b, path_mtu_discovery); - NM_CMP_FIELD(a, b, flags); - NM_CMP_FIELD(a, b, proto); - return 0; -} - -void -nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->type, - obj->owner, - obj->group, - NM_HASH_COMBINE_BOOLS(guint8, - obj->owner_valid, - obj->group_valid, - obj->pi, - obj->vnet_hdr, - obj->multi_queue, - obj->persist)); -} - -int -nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, type); - NM_CMP_FIELD(a, b, owner); - NM_CMP_FIELD(a, b, group); - NM_CMP_FIELD_BOOL(a, b, owner_valid); - NM_CMP_FIELD_BOOL(a, b, group_valid); - NM_CMP_FIELD_BOOL(a, b, pi); - NM_CMP_FIELD_BOOL(a, b, vnet_hdr); - NM_CMP_FIELD_BOOL(a, b, multi_queue); - NM_CMP_FIELD_BOOL(a, b, persist); - return 0; -} - -void -nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h) -{ - nm_hash_update_vals(h, obj->id, obj->flags); -} - -int -nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, id); - NM_CMP_FIELD(a, b, flags); - return 0; -} - -void -nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h) -{ - nm_hash_update_vals(h, obj->table); -} - -int -nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, table); - return 0; -} - -void -nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->group6, - obj->local6, - obj->group, - obj->local, - obj->parent_ifindex, - obj->id, - obj->ageing, - obj->limit, - obj->dst_port, - obj->src_port_min, - obj->src_port_max, - obj->tos, - obj->ttl, - NM_HASH_COMBINE_BOOLS(guint8, - obj->learning, - obj->proxy, - obj->rsc, - obj->l2miss, - obj->l3miss)); -} - -int -nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, parent_ifindex); - NM_CMP_FIELD(a, b, id); - NM_CMP_FIELD(a, b, group); - NM_CMP_FIELD(a, b, local); - NM_CMP_FIELD_MEMCMP(a, b, group6); - NM_CMP_FIELD_MEMCMP(a, b, local6); - NM_CMP_FIELD(a, b, tos); - NM_CMP_FIELD(a, b, ttl); - NM_CMP_FIELD_BOOL(a, b, learning); - NM_CMP_FIELD(a, b, ageing); - NM_CMP_FIELD(a, b, limit); - NM_CMP_FIELD(a, b, dst_port); - NM_CMP_FIELD(a, b, src_port_min); - NM_CMP_FIELD(a, b, src_port_max); - NM_CMP_FIELD_BOOL(a, b, proxy); - NM_CMP_FIELD_BOOL(a, b, rsc); - NM_CMP_FIELD_BOOL(a, b, l2miss); - NM_CMP_FIELD_BOOL(a, b, l3miss); - return 0; -} - -void -nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h) -{ - nm_hash_update_vals(h, obj->listen_port, obj->fwmark); - nm_hash_update(h, obj->private_key, sizeof(obj->private_key)); - nm_hash_update(h, obj->public_key, sizeof(obj->public_key)); -} - -int -nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, listen_port); - NM_CMP_FIELD(a, b, fwmark); - NM_CMP_FIELD_MEMCMP(a, b, private_key); - NM_CMP_FIELD_MEMCMP(a, b, public_key); - return 0; -} - -static int -_address_pretty_sort_get_prio_4(in_addr_t addr) -{ - if (nm_utils_ip4_address_is_link_local(addr)) - return 0; - return 1; -} - -int -nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, - const NMPlatformIP4Address *a2) -{ - in_addr_t n1; - in_addr_t n2; - - nm_assert(a1); - nm_assert(a2); - - /* Sort by address type. For example link local will - * be sorted *after* a global address. */ - NM_CMP_DIRECT(_address_pretty_sort_get_prio_4(a2->address), - _address_pretty_sort_get_prio_4(a1->address)); - - /* Sort the addresses based on their source. */ - NM_CMP_DIRECT(a2->addr_source, a1->addr_source); - - NM_CMP_DIRECT((a2->label[0] == '\0'), (a1->label[0] == '\0')); - - /* Finally, sort addresses lexically. We compare only the - * network part so that the order of addresses in the same - * subnet (and thus also the primary/secondary role) is - * preserved. - */ - n1 = a1->address & _nm_utils_ip4_prefix_to_netmask(a1->plen); - n2 = a2->address & _nm_utils_ip4_prefix_to_netmask(a2->plen); - NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32)); - return 0; -} - -static int -_address_pretty_sort_get_prio_6(const struct in6_addr *addr) -{ - if (IN6_IS_ADDR_V4MAPPED(addr)) - return 0; - if (IN6_IS_ADDR_V4COMPAT(addr)) - return 1; - if (IN6_IS_ADDR_UNSPECIFIED(addr)) - return 2; - if (IN6_IS_ADDR_LOOPBACK(addr)) - return 3; - if (IN6_IS_ADDR_LINKLOCAL(addr)) - return 4; - if (IN6_IS_ADDR_SITELOCAL(addr)) - return 5; - return 6; -} - -int -nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, - const NMPlatformIP6Address *a2, - gboolean prefer_temp) -{ - gboolean ipv6_privacy1; - gboolean ipv6_privacy2; - - nm_assert(a1); - nm_assert(a2); - - /* tentative addresses are always sorted back... */ - /* sort tentative addresses after non-tentative. */ - NM_CMP_DIRECT(NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_TENTATIVE), - NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_TENTATIVE)); - - /* Sort by address type. For example link local will - * be sorted *after* site local or global. */ - NM_CMP_DIRECT(_address_pretty_sort_get_prio_6(&a2->address), - _address_pretty_sort_get_prio_6(&a1->address)); - - ipv6_privacy1 = NM_FLAGS_ANY(a1->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY); - ipv6_privacy2 = NM_FLAGS_ANY(a2->n_ifa_flags, IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY); - if (ipv6_privacy1 || ipv6_privacy2) { - gboolean public1 = TRUE; - gboolean public2 = TRUE; - - if (ipv6_privacy1) { - if (a1->n_ifa_flags & IFA_F_TEMPORARY) - public1 = prefer_temp; - else - public1 = !prefer_temp; - } - if (ipv6_privacy2) { - if (a2->n_ifa_flags & IFA_F_TEMPORARY) - public2 = prefer_temp; - else - public2 = !prefer_temp; - } - - NM_CMP_DIRECT(public2, public1); - } - - /* Sort the addresses based on their source. */ - NM_CMP_DIRECT(a2->addr_source, a1->addr_source); - - /* sort permanent addresses before non-permanent. */ - NM_CMP_DIRECT(NM_FLAGS_HAS(a2->n_ifa_flags, IFA_F_PERMANENT), - NM_FLAGS_HAS(a1->n_ifa_flags, IFA_F_PERMANENT)); - - /* finally sort addresses lexically */ - NM_CMP_DIRECT_IN6ADDR(&a1->address, &a2->address); - NM_CMP_DIRECT_MEMCMP(a1, a2, sizeof(*a1)); - return 0; -} - -void -nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->ifindex, - obj->addr_source, - nm_platform_ip4_broadcast_address_from_addr(obj), - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->external)); - nm_hash_update_strarr(h, obj->label); -} - -int -nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) -{ - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, address); - NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD(a, b, peer_address); - NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), - nm_platform_ip4_broadcast_address_from_addr(b)); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD_STR(a, b, label); - NM_CMP_FIELD_UNSAFE(a, b, external); - return 0; -} - -void -nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) -{ - nm_hash_update_vals(h, - obj->ifindex, - obj->addr_source, - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->external)); -} - -int -nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) -{ - const struct in6_addr *p_a, *p_b; - - NM_CMP_SELF(a, b); - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD_MEMCMP(a, b, address); - NM_CMP_FIELD(a, b, plen); - p_a = nm_platform_ip6_address_get_peer(a); - p_b = nm_platform_ip6_address_get_peer(b); - NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD_UNSAFE(a, b, external); - return 0; -} - -void -nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, - NMPlatformIPRouteCmpType cmp_type, - NMHashState * h) -{ - switch (cmp_type) { - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: - nm_hash_update_vals( - h, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), - obj->plen, - nm_platform_ip4_route_get_effective_metric(obj), - obj->tos, - NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: - nm_hash_update_vals( - h, - obj->type_coerced, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), - obj->plen, - nm_platform_ip4_route_get_effective_metric(obj), - obj->tos, - /* on top of WEAK_ID: */ - obj->ifindex, - nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), - _ip_route_scope_inv_get_normalized(obj), - obj->gateway, - obj->mss, - obj->pref_src, - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - obj->r_rtm_flags & RTNH_F_ONLINK, - NM_HASH_COMBINE_BOOLS(guint8, - obj->metric_any, - obj->table_any, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu)); - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: - nm_hash_update_vals( - h, - obj->type_coerced, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - obj->ifindex, - nm_utils_ip4_address_clear_host_address(obj->network, obj->plen), - obj->plen, - nm_platform_ip4_route_get_effective_metric(obj), - obj->gateway, - nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), - _ip_route_scope_inv_get_normalized(obj), - obj->tos, - obj->mss, - obj->pref_src, - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - obj->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK), - NM_HASH_COMBINE_BOOLS(guint8, - obj->metric_any, - obj->table_any, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu)); - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: - nm_hash_update_vals(h, - obj->type_coerced, - obj->table_coerced, - obj->ifindex, - obj->network, - obj->plen, - nm_platform_ip4_route_get_effective_metric(obj), - obj->gateway, - obj->rt_source, - obj->scope_inv, - obj->tos, - obj->mss, - obj->pref_src, - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - obj->r_rtm_flags, - NM_HASH_COMBINE_BOOLS(guint8, - obj->metric_any, - obj->table_any, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu)); - break; - } -} - -int -nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, - const NMPlatformIP4Route *b, - NMPlatformIPRouteCmpType cmp_type) -{ - NM_CMP_SELF(a, b); - switch (cmp_type) { - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: - NM_CMP_FIELD_UNSAFE(a, b, table_any); - NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); - NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD_UNSAFE(a, b, metric_any); - if (!a->metric_any) - NM_CMP_FIELD(a, b, metric); - NM_CMP_FIELD(a, b, tos); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, type_coerced); - NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), - nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); - NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a), - _ip_route_scope_inv_get_normalized(b)); - NM_CMP_FIELD(a, b, gateway); - NM_CMP_FIELD(a, b, mss); - NM_CMP_FIELD(a, b, pref_src); - NM_CMP_FIELD(a, b, window); - NM_CMP_FIELD(a, b, cwnd); - NM_CMP_FIELD(a, b, initcwnd); - NM_CMP_FIELD(a, b, initrwnd); - NM_CMP_FIELD(a, b, mtu); - NM_CMP_DIRECT(a->r_rtm_flags & RTNH_F_ONLINK, b->r_rtm_flags & RTNH_F_ONLINK); - NM_CMP_FIELD_UNSAFE(a, b, lock_window); - NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); - } - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: - NM_CMP_FIELD(a, b, type_coerced); - NM_CMP_FIELD_UNSAFE(a, b, table_any); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { - NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - } else - NM_CMP_FIELD(a, b, table_coerced); - NM_CMP_FIELD(a, b, ifindex); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->network, b->network, MIN(a->plen, b->plen)); - else - NM_CMP_FIELD(a, b, network); - NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD_UNSAFE(a, b, metric_any); - if (!a->metric_any) - NM_CMP_FIELD(a, b, metric); - NM_CMP_FIELD(a, b, gateway); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { - NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), - nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); - NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a), - _ip_route_scope_inv_get_normalized(b)); - } else { - NM_CMP_FIELD(a, b, rt_source); - NM_CMP_FIELD(a, b, scope_inv); - } - NM_CMP_FIELD(a, b, mss); - NM_CMP_FIELD(a, b, pref_src); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { - NM_CMP_DIRECT(a->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK), - b->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK)); - } else - NM_CMP_FIELD(a, b, r_rtm_flags); - NM_CMP_FIELD(a, b, tos); - NM_CMP_FIELD_UNSAFE(a, b, lock_window); - NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); - NM_CMP_FIELD(a, b, window); - NM_CMP_FIELD(a, b, cwnd); - NM_CMP_FIELD(a, b, initcwnd); - NM_CMP_FIELD(a, b, initrwnd); - NM_CMP_FIELD(a, b, mtu); - break; - } - return 0; -} - -void -nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, - NMPlatformIPRouteCmpType cmp_type, - NMHashState * h) -{ - struct in6_addr a1, a2; - - switch (cmp_type) { - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: - nm_hash_update_vals( - h, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), - obj->plen, - nm_platform_ip6_route_get_effective_metric(obj), - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), - obj->src_plen, - NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: - nm_hash_update_vals( - h, - obj->type_coerced, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), - obj->plen, - nm_platform_ip6_route_get_effective_metric(obj), - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), - obj->src_plen, - NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any), - /* on top of WEAK_ID: */ - obj->ifindex, - obj->gateway); - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: - nm_hash_update_vals( - h, - obj->type_coerced, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - obj->ifindex, - *nm_utils_ip6_address_clear_host_address(&a1, &obj->network, obj->plen), - obj->plen, - nm_platform_ip6_route_get_effective_metric(obj), - obj->gateway, - obj->pref_src, - *nm_utils_ip6_address_clear_host_address(&a2, &obj->src, obj->src_plen), - obj->src_plen, - nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), - obj->mss, - obj->r_rtm_flags & RTM_F_CLONED, - NM_HASH_COMBINE_BOOLS(guint8, - obj->metric_any, - obj->table_any, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu), - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - _route_pref_normalize(obj->rt_pref)); - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: - nm_hash_update_vals(h, - obj->type_coerced, - obj->table_coerced, - obj->ifindex, - obj->network, - nm_platform_ip6_route_get_effective_metric(obj), - obj->gateway, - obj->pref_src, - obj->src, - obj->src_plen, - obj->rt_source, - obj->mss, - obj->r_rtm_flags, - NM_HASH_COMBINE_BOOLS(guint8, - obj->metric_any, - obj->table_any, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu), - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - obj->rt_pref); - break; - } -} - -int -nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, - const NMPlatformIP6Route *b, - NMPlatformIPRouteCmpType cmp_type) -{ - NM_CMP_SELF(a, b); - switch (cmp_type) { - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: - NM_CMP_FIELD_UNSAFE(a, b, table_any); - NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); - NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD_UNSAFE(a, b, metric_any); - if (!a->metric_any) - NM_CMP_FIELD(a, b, metric); - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); - NM_CMP_FIELD(a, b, src_plen); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { - NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, type_coerced); - NM_CMP_FIELD_IN6ADDR(a, b, gateway); - } - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: - NM_CMP_FIELD(a, b, type_coerced); - NM_CMP_FIELD_UNSAFE(a, b, table_any); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { - NM_CMP_DIRECT(nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(a)), - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b))); - } else - NM_CMP_FIELD(a, b, table_coerced); - NM_CMP_FIELD(a, b, ifindex); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->network, &b->network, MIN(a->plen, b->plen)); - else - NM_CMP_FIELD_IN6ADDR(a, b, network); - NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD_UNSAFE(a, b, metric_any); - if (!a->metric_any) - NM_CMP_FIELD(a, b, metric); - NM_CMP_FIELD_IN6ADDR(a, b, gateway); - NM_CMP_FIELD_IN6ADDR(a, b, pref_src); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { - NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(&a->src, &b->src, MIN(a->src_plen, b->src_plen)); - NM_CMP_FIELD(a, b, src_plen); - NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), - nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); - } else { - NM_CMP_FIELD_IN6ADDR(a, b, src); - NM_CMP_FIELD(a, b, src_plen); - NM_CMP_FIELD(a, b, rt_source); - } - NM_CMP_FIELD(a, b, mss); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { - NM_CMP_DIRECT(a->r_rtm_flags & RTM_F_CLONED, b->r_rtm_flags & RTM_F_CLONED); - } else - NM_CMP_FIELD(a, b, r_rtm_flags); - NM_CMP_FIELD_UNSAFE(a, b, lock_window); - NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); - NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); - NM_CMP_FIELD(a, b, window); - NM_CMP_FIELD(a, b, cwnd); - NM_CMP_FIELD(a, b, initcwnd); - NM_CMP_FIELD(a, b, initrwnd); - NM_CMP_FIELD(a, b, mtu); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref)); - else - NM_CMP_FIELD(a, b, rt_pref); - break; - } - return 0; -} - -#define _ROUTING_RULE_FLAGS_IGNORE \ - (FIB_RULE_UNRESOLVED | FIB_RULE_IIF_DETACHED | FIB_RULE_OIF_DETACHED) - -#define _routing_rule_compare(cmp_type, kernel_support_type) \ - ((cmp_type) == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL \ - || nm_platform_kernel_support_get(kernel_support_type)) - -void -nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj, - NMPlatformRoutingRuleCmpType cmp_type, - NMHashState * h) -{ - gboolean cmp_full = TRUE; - gsize addr_size; - guint32 flags_mask = G_MAXUINT32; - - if (G_UNLIKELY(!NM_IN_SET(obj->addr_family, AF_INET, AF_INET6))) { - /* the address family is not one of the supported ones. That means, the - * instance will only compare equal to itself (pointer-equality). */ - nm_hash_update_val(h, (gconstpointer) obj); - return; - } - - switch (cmp_type) { - case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: - - flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; - - /* fall-through */ - case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: - - cmp_full = FALSE; - - /* fall-through */ - case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: - - nm_hash_update_vals( - h, - obj->addr_family, - obj->tun_id, - obj->table, - obj->flags & flags_mask, - obj->priority, - obj->fwmark, - obj->fwmask, - ((cmp_full - || (cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY - && obj->action == FR_ACT_GOTO)) - ? obj->goto_target - : (guint32) 0u), - ((cmp_full || obj->addr_family == AF_INET) ? obj->flow : (guint32) 0u), - NM_HASH_COMBINE_BOOLS( - guint8, - (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE) - ? obj->uid_range_has - : FALSE)), - obj->suppress_prefixlen_inverse, - obj->suppress_ifgroup_inverse, - (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV) - ? (cmp_full ? (guint16) obj->l3mdev : (guint16) !!obj->l3mdev) - : G_MAXUINT16), - obj->action, - obj->tos, - obj->src_len, - obj->dst_len, - (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL) - ? (guint16) obj->protocol - : G_MAXUINT16)); - addr_size = nm_utils_addr_family_to_size(obj->addr_family); - if (cmp_full || obj->src_len > 0) - nm_hash_update(h, &obj->src, addr_size); - if (cmp_full || obj->dst_len > 0) - nm_hash_update(h, &obj->dst, addr_size); - if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { - if (cmp_full || obj->uid_range_has) - nm_hash_update_valp(h, &obj->uid_range); - } - if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { - nm_hash_update_val(h, obj->ip_proto); - nm_hash_update_valp(h, &obj->sport_range); - nm_hash_update_valp(h, &obj->dport_range); - } - nm_hash_update_str(h, obj->iifname); - nm_hash_update_str(h, obj->oifname); - return; - } - - nm_assert_not_reached(); -} - -int -nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a, - const NMPlatformRoutingRule *b, - NMPlatformRoutingRuleCmpType cmp_type) -{ - gboolean cmp_full = TRUE; - gsize addr_size; - bool valid; - guint32 flags_mask = G_MAXUINT32; - - NM_CMP_SELF(a, b); - - valid = NM_IN_SET(a->addr_family, AF_INET, AF_INET6); - NM_CMP_DIRECT(valid, (bool) NM_IN_SET(b->addr_family, AF_INET, AF_INET6)); - - if (G_UNLIKELY(!valid)) { - /* the address family is not one of the supported ones. That means, the - * instance will only compare equal to itself. */ - NM_CMP_DIRECT((uintptr_t) a, (uintptr_t) b); - nm_assert_not_reached(); - return 0; - } - - switch (cmp_type) { - case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID: - - flags_mask &= ~_ROUTING_RULE_FLAGS_IGNORE; - - /* fall-through */ - case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY: - - cmp_full = FALSE; - - /* fall-through */ - case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL: - NM_CMP_FIELD(a, b, addr_family); - NM_CMP_FIELD(a, b, action); - NM_CMP_FIELD(a, b, priority); - NM_CMP_FIELD(a, b, tun_id); - - if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV)) { - if (cmp_full) - NM_CMP_FIELD(a, b, l3mdev); - else - NM_CMP_FIELD_BOOL(a, b, l3mdev); - } - - NM_CMP_FIELD(a, b, table); - - NM_CMP_DIRECT(a->flags & flags_mask, b->flags & flags_mask); - - NM_CMP_FIELD(a, b, fwmark); - NM_CMP_FIELD(a, b, fwmask); - - if (cmp_full - || (cmp_type == NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY - && a->action == FR_ACT_GOTO)) - NM_CMP_FIELD(a, b, goto_target); - - NM_CMP_FIELD(a, b, suppress_prefixlen_inverse); - NM_CMP_FIELD(a, b, suppress_ifgroup_inverse); - NM_CMP_FIELD(a, b, tos); - - if (cmp_full || a->addr_family == AF_INET) - NM_CMP_FIELD(a, b, flow); - - if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL)) - NM_CMP_FIELD(a, b, protocol); - - if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO)) { - NM_CMP_FIELD(a, b, ip_proto); - NM_CMP_FIELD(a, b, sport_range.start); - NM_CMP_FIELD(a, b, sport_range.end); - NM_CMP_FIELD(a, b, dport_range.start); - NM_CMP_FIELD(a, b, dport_range.end); - } - - addr_size = nm_utils_addr_family_to_size(a->addr_family); - - NM_CMP_FIELD(a, b, src_len); - if (cmp_full || a->src_len > 0) - NM_CMP_FIELD_MEMCMP_LEN(a, b, src, addr_size); - - NM_CMP_FIELD(a, b, dst_len); - if (cmp_full || a->dst_len > 0) - NM_CMP_FIELD_MEMCMP_LEN(a, b, dst, addr_size); - - if (_routing_rule_compare(cmp_type, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE)) { - NM_CMP_FIELD_UNSAFE(a, b, uid_range_has); - if (cmp_full || a->uid_range_has) { - NM_CMP_FIELD(a, b, uid_range.start); - NM_CMP_FIELD(a, b, uid_range.end); - } - } - - NM_CMP_FIELD_STR(a, b, iifname); - NM_CMP_FIELD_STR(a, b, oifname); - return 0; - } - - nm_assert_not_reached(); - return 0; -} - -/** - * nm_platform_ip_address_cmp_expiry: - * @a: a NMPlatformIPAddress to compare - * @b: the other NMPlatformIPAddress to compare - * - * Compares two addresses and returns which one has a longer remaining lifetime. - * If both addresses have the same lifetime, look at the remaining preferred time. - * - * For comparison, only the timestamp, lifetime and preferred fields are considered. - * If they compare equal (== 0), their other fields were not considered. - * - * Returns: -1, 0, or 1 according to the comparison - **/ -int -nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) -{ - gint64 ta = 0, tb = 0; - - NM_CMP_SELF(a, b); - - if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0) - ta = G_MAXINT64; - else if (a->timestamp) - ta = ((gint64) a->timestamp) + a->lifetime; - - if (b->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || b->lifetime == 0) - tb = G_MAXINT64; - else if (b->timestamp) - tb = ((gint64) b->timestamp) + b->lifetime; - - if (ta == tb) { - /* if the lifetime is equal, compare the preferred time. */ - ta = tb = 0; - - if (a->preferred == NM_PLATFORM_LIFETIME_PERMANENT - || a->lifetime == 0 /* lifetime==0 means permanent! */) - ta = G_MAXINT64; - else if (a->timestamp) - ta = ((gint64) a->timestamp) + a->preferred; - - if (b->preferred == NM_PLATFORM_LIFETIME_PERMANENT || b->lifetime == 0) - tb = G_MAXINT64; - else if (b->timestamp) - tb = ((gint64) b->timestamp) + b->preferred; - - if (ta == tb) - return 0; - } - - return ta < tb ? -1 : 1; -} - -/*****************************************************************************/ - -GHashTable * -nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex) -{ - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject * obj; - NMPLookup lookup; - GHashTable * hash; - - g_return_val_if_fail(NM_IS_PLATFORM(self), NULL); - g_return_val_if_fail(ifindex > 0, NULL); - - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); - - head_entry = nmp_cache_lookup(NM_PLATFORM_GET_PRIVATE(self)->cache, &lookup); - - if (!head_entry) - return NULL; - - hash = g_hash_table_new(nm_direct_hash, NULL); - - nmp_cache_iter_for_each (&iter, head_entry, &obj) { - const NMPlatformIP4Address *a = NMP_OBJECT_CAST_IP4_ADDRESS(obj); - - g_hash_table_add(hash, GUINT_TO_POINTER(a->address)); - } - - return hash; -} - -/*****************************************************************************/ - -const char * -nm_platform_signal_change_type_to_string(NMPlatformSignalChangeType change_type) -{ - switch (change_type) { - case NM_PLATFORM_SIGNAL_ADDED: - return "added"; - case NM_PLATFORM_SIGNAL_CHANGED: - return "changed"; - case NM_PLATFORM_SIGNAL_REMOVED: - return "removed"; - default: - g_return_val_if_reached("UNKNOWN"); - } -} - -static void -log_link(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformLink * device, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - _LOG3D("signal: link %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_link_to_string(device, NULL, 0)); -} - -static void -log_ip4_address(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformIP4Address * address, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - _LOG3D("signal: address 4 %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_address_to_string(address, NULL, 0)); -} - -static void -log_ip6_address(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformIP6Address * address, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - _LOG3D("signal: address 6 %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_address_to_string(address, NULL, 0)); -} - -static void -log_ip4_route(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformIP4Route * route, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - _LOG3D("signal: route 4 %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_route_to_string(route, NULL, 0)); -} - -static void -log_ip6_route(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformIP6Route * route, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - _LOG3D("signal: route 6 %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_route_to_string(route, NULL, 0)); -} - -static void -log_routing_rule(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformRoutingRule * routing_rule, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - /* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */ - _LOG3D("signal: rt-rule %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_routing_rule_to_string(routing_rule, NULL, 0)); -} - -static void -log_qdisc(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformQdisc * qdisc, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - _LOG3D("signal: qdisc %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_qdisc_to_string(qdisc, NULL, 0)); -} - -static void -log_tfilter(NMPlatform * self, - NMPObjectType obj_type, - int ifindex, - NMPlatformTfilter * tfilter, - NMPlatformSignalChangeType change_type, - gpointer user_data) -{ - _LOG3D("signal: tfilter %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_tfilter_to_string(tfilter, NULL, 0)); -} - -/*****************************************************************************/ - -void -nm_platform_cache_update_emit_signal(NMPlatform * self, - NMPCacheOpsType cache_op, - const NMPObject *obj_old, - const NMPObject *obj_new) -{ - gboolean visible_new; - gboolean visible_old; - const NMPObject *o; - const NMPClass * klass; - int ifindex; - - nm_assert(NM_IN_SET((NMPlatformSignalChangeType) cache_op, - NM_PLATFORM_SIGNAL_NONE, - NM_PLATFORM_SIGNAL_ADDED, - NM_PLATFORM_SIGNAL_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED)); - - ASSERT_nmp_cache_ops(nm_platform_get_cache(self), cache_op, obj_old, obj_new); - - NMTST_ASSERT_PLATFORM_NETNS_CURRENT(self); - - switch (cache_op) { - case NMP_CACHE_OPS_ADDED: - if (!nmp_object_is_visible(obj_new)) - return; - o = obj_new; - break; - case NMP_CACHE_OPS_UPDATED: - visible_old = nmp_object_is_visible(obj_old); - visible_new = nmp_object_is_visible(obj_new); - if (!visible_old && visible_new) { - o = obj_new; - cache_op = NMP_CACHE_OPS_ADDED; - } else if (visible_old && !visible_new) { - o = obj_old; - cache_op = NMP_CACHE_OPS_REMOVED; - } else if (!visible_new) { - /* it was invisible and stayed invisible. Nothing to do. */ - return; - } else - o = obj_new; - break; - case NMP_CACHE_OPS_REMOVED: - if (!nmp_object_is_visible(obj_old)) - return; - o = obj_old; - break; - default: - nm_assert(cache_op == NMP_CACHE_OPS_UNCHANGED); - return; - } - - klass = NMP_OBJECT_GET_CLASS(o); - - if (klass->obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) - ifindex = 0; - else - ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(o)->ifindex; - - if (klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE - && NM_PLATFORM_GET_PRIVATE(self)->ip4_dev_route_blacklist_gc_timeout_id - && NM_IN_SET(cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) - _ip4_dev_route_blacklist_notify_route(self, o); - - _LOG3t("emit signal %s %s: %s", - klass->signal_type, - nm_platform_signal_change_type_to_string((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - nmp_object_ref(o); - g_signal_emit(self, - _nm_platform_signal_id_get(klass->signal_type_id), - 0, - (int) klass->obj_type, - ifindex, - &o->object, - (int) cache_op); - nmp_object_unref(o); -} - -/*****************************************************************************/ - -NMPCache * -nm_platform_get_cache(NMPlatform *self) -{ - return NM_PLATFORM_GET_PRIVATE(self)->cache; -} - -NMPNetns * -nm_platform_netns_get(NMPlatform *self) -{ - _CHECK_SELF(self, klass, NULL); - - return self->_netns; -} - -gboolean -nm_platform_netns_push(NMPlatform *self, NMPNetns **netns) -{ - g_return_val_if_fail(NM_IS_PLATFORM(self), FALSE); - - if (self->_netns && !nmp_netns_push(self->_netns)) { - NM_SET_OUT(netns, NULL); - return FALSE; - } - - NM_SET_OUT(netns, self->_netns); - return TRUE; -} - -/*****************************************************************************/ - -const _NMPlatformVTableRouteUnion nm_platform_vtable_route = { - .v4 = - { - .is_ip4 = TRUE, - .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, - .addr_family = AF_INET, - .sizeof_route = sizeof(NMPlatformIP4Route), - .route_cmp = (int (*)(const NMPlatformIPXRoute *a, - const NMPlatformIPXRoute *b, - NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, - char * buf, - gsize len)) nm_platform_ip4_route_to_string, - }, - .v6 = - { - .is_ip4 = FALSE, - .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, - .addr_family = AF_INET6, - .sizeof_route = sizeof(NMPlatformIP6Route), - .route_cmp = (int (*)(const NMPlatformIPXRoute *a, - const NMPlatformIPXRoute *b, - NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, - char * buf, - gsize len)) nm_platform_ip6_route_to_string, - }, -}; - -/*****************************************************************************/ - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMPlatform * self = NM_PLATFORM(object); - NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_NETNS_SUPPORT: - /* construct-only */ - if (g_value_get_boolean(value)) { - NMPNetns *netns; - - netns = nmp_netns_get_current(); - if (netns) - self->_netns = g_object_ref(netns); - } - break; - case PROP_USE_UDEV: - /* construct-only */ - priv->use_udev = g_value_get_boolean(value); - break; - case PROP_LOG_WITH_PTR: - /* construct-only */ - priv->log_with_ptr = g_value_get_boolean(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -nm_platform_init(NMPlatform *self) -{ - self->_priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_PLATFORM, NMPlatformPrivate); -} - -static GObject * -constructor(GType type, guint n_construct_params, GObjectConstructParam *construct_params) -{ - GObject * object; - NMPlatform * self; - NMPlatformPrivate *priv; - - object = G_OBJECT_CLASS(nm_platform_parent_class) - ->constructor(type, n_construct_params, construct_params); - self = NM_PLATFORM(object); - priv = NM_PLATFORM_GET_PRIVATE(self); - - priv->multi_idx = nm_dedup_multi_index_new(); - - priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev); - - return object; -} - -static void -finalize(GObject *object) -{ - NMPlatform * self = NM_PLATFORM(object); - NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); - - nm_clear_g_source(&priv->ip4_dev_route_blacklist_check_id); - nm_clear_g_source(&priv->ip4_dev_route_blacklist_gc_timeout_id); - nm_clear_pointer(&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); - g_clear_object(&self->_netns); - nm_dedup_multi_index_unref(priv->multi_idx); - nmp_cache_free(priv->cache); -} - -static void -nm_platform_class_init(NMPlatformClass *platform_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS(platform_class); - - g_type_class_add_private(object_class, sizeof(NMPlatformPrivate)); - - object_class->constructor = constructor; - object_class->set_property = set_property; - object_class->finalize = finalize; - - platform_class->wifi_set_powersave = wifi_set_powersave; - - g_object_class_install_property( - object_class, - PROP_NETNS_SUPPORT, - g_param_spec_boolean(NM_PLATFORM_NETNS_SUPPORT, - "", - "", - NM_PLATFORM_NETNS_SUPPORT_DEFAULT, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property( - object_class, - PROP_USE_UDEV, - g_param_spec_boolean(NM_PLATFORM_USE_UDEV, - "", - "", - FALSE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property( - object_class, - PROP_LOG_WITH_PTR, - g_param_spec_boolean(NM_PLATFORM_LOG_WITH_PTR, - "", - "", - TRUE, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - -#define SIGNAL(signal, signal_id, method) \ - G_STMT_START \ - { \ - signals[signal] = \ - g_signal_new_class_handler("" signal_id "", \ - G_OBJECT_CLASS_TYPE(object_class), \ - G_SIGNAL_RUN_FIRST, \ - G_CALLBACK(method), \ - NULL, \ - NULL, \ - NULL, \ - G_TYPE_NONE, \ - 4, \ - G_TYPE_INT, /* (int) NMPObjectType */ \ - G_TYPE_INT, /* ifindex */ \ - G_TYPE_POINTER /* const NMPObject * */, \ - G_TYPE_INT /* (int) NMPlatformSignalChangeType */ \ - ); \ - } \ - G_STMT_END - - /* Signals */ - SIGNAL(NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link); - SIGNAL(NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, - NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - log_ip4_address); - SIGNAL(NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, - NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - log_ip6_address); - SIGNAL(NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route); - SIGNAL(NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route); - SIGNAL(NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, - NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, - log_routing_rule); - SIGNAL(NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc); - SIGNAL(NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter); -} diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h deleted file mode 100644 index a1314000..00000000 --- a/src/platform/nm-platform.h +++ /dev/null @@ -1,2364 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2009 - 2018 Red Hat, Inc. - */ - -#ifndef __NETWORKMANAGER_PLATFORM_H__ -#define __NETWORKMANAGER_PLATFORM_H__ - -#include "nm-dbus-interface.h" -#include "nm-core-types-internal.h" - -#include "nm-core-utils.h" -#include "nm-setting-vlan.h" -#include "nm-setting-wired.h" -#include "nm-setting-wireless.h" -#include "nm-setting-ip-tunnel.h" - -#define NM_TYPE_PLATFORM (nm_platform_get_type()) -#define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_PLATFORM, NMPlatform)) -#define NM_PLATFORM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_PLATFORM, NMPlatformClass)) -#define NM_IS_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_PLATFORM)) -#define NM_IS_PLATFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_PLATFORM)) -#define NM_PLATFORM_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_PLATFORM, NMPlatformClass)) - -#define NM_PLATFORM_NETNS_SUPPORT_DEFAULT FALSE - -/*****************************************************************************/ - -#define NM_PLATFORM_NETNS_SUPPORT "netns-support" -#define NM_PLATFORM_USE_UDEV "use-udev" -#define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr" - -/*****************************************************************************/ - -/* IFNAMSIZ is both defined in <linux/if.h> and <net/if.h>. In the past, these - * headers conflicted, so we cannot simply include either of them in a header-file.*/ -#define NMP_IFNAMSIZ 16 - -/*****************************************************************************/ - -struct _NMPWireGuardPeer; - -struct udev_device; - -typedef gboolean (*NMPObjectPredicateFunc)(const NMPObject *obj, gpointer user_data); - -/* workaround for older libnl version, that does not define these flags. */ -#ifndef IFA_F_MANAGETEMPADDR - #define IFA_F_MANAGETEMPADDR 0x100 -#endif -#ifndef IFA_F_NOPREFIXROUTE - #define IFA_F_NOPREFIXROUTE 0x200 -#endif - -#define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */ - -/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers - * that don't define it. */ -#define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ -#define NM_IN6_ADDR_GEN_MODE_EUI64 0 /* IN6_ADDR_GEN_MODE_EUI64 */ -#define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ -#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ -#define NM_IN6_ADDR_GEN_MODE_RANDOM 3 /* IN6_ADDR_GEN_MODE_RANDOM */ - -#define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */ - -/* Redefine this in host's endianness */ -#define NM_GRE_KEY 0x2000 - -typedef enum { - NMP_NLM_FLAG_F_ECHO = 0x08, /* NLM_F_ECHO, Echo this request */ - - /* use our own platform enum for the nlmsg-flags. Otherwise, we'd have - * to include <linux/netlink.h> */ - NMP_NLM_FLAG_F_REPLACE = 0x100, /* NLM_F_REPLACE, Override existing */ - NMP_NLM_FLAG_F_EXCL = 0x200, /* NLM_F_EXCL, Do not touch, if it exists */ - NMP_NLM_FLAG_F_CREATE = 0x400, /* NLM_F_CREATE, Create, if it does not exist */ - NMP_NLM_FLAG_F_APPEND = 0x800, /* NLM_F_APPEND, Add to end of list */ - - NMP_NLM_FLAG_FMASK = 0xFFFF, /* a mask for all NMP_NLM_FLAG_F_* flags */ - - /* instructs NM to suppress logging an error message for any failures - * received from kernel. - * - * It will still log with debug-level, and it will still log - * other failures aside the kernel response. */ - NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE = 0x10000, - - /* the following aliases correspond to iproute2's `ip route CMD` for - * RTM_NEWROUTE, with CMD being one of add, change, replace, prepend, - * append and test. */ - NMP_NLM_FLAG_ADD = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_EXCL, - NMP_NLM_FLAG_CHANGE = NMP_NLM_FLAG_F_REPLACE, - NMP_NLM_FLAG_REPLACE = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_REPLACE, - NMP_NLM_FLAG_PREPEND = NMP_NLM_FLAG_F_CREATE, - NMP_NLM_FLAG_APPEND = NMP_NLM_FLAG_F_CREATE | NMP_NLM_FLAG_F_APPEND, - NMP_NLM_FLAG_TEST = NMP_NLM_FLAG_F_EXCL, -} NMPNlmFlags; - -typedef enum { - /* compare fields which kernel considers as similar routes. - * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID - * and means that `ip route add` would fail to add two routes - * that have the same NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID. - * On the other hand, `ip route append` would allow that, as - * long as NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID differs. */ - NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, - - /* compare two routes as kernel would allow to add them with - * `ip route append`. In other words, kernel does not allow you to - * add two routes (at the same time) which compare equal according - * to NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID. - * - * For the ID we can only recognize route fields that we actually implement. - * However, kernel supports more routing options, some of them also part of - * the ID. NetworkManager is oblivious to these options and will wrongly think - * that two routes are identical, while they are not. That can lead to an - * inconsistent platform cache. Not much what we can do about that, except - * implementing all options that kernel supports *sigh*. See rh#1337860. - */ - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, - - /* compare all fields as they make sense for kernel. For example, - * a route destination 192.168.1.5/24 is not accepted by kernel and - * we treat it identical to 192.168.1.0/24. Semantically these - * routes are identical, but NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL will - * report them as different. - * - * The result shall be identical to call first nm_platform_ip_route_normalize() - * on both routes and then doing a full comparison. */ - NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY, - - /* compare all fields. This should have the same effect as memcmp(), - * except allowing for undefined data in holes between field alignment. - */ - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, - -} NMPlatformIPRouteCmpType; - -typedef enum { - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, - - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY, - - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, -} NMPlatformRoutingRuleCmpType; - -typedef struct { - union { - guint8 data[20 /* NM_UTILS_HWADDR_LEN_MAX */]; - NMEtherAddr ether_addr; - }; - guint8 len; -} NMPLinkAddress; - -/* assert that NMEtherAddr does not affect the alignment of NMPLinkAddress struct. */ -G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) == 1); -G_STATIC_ASSERT(_nm_alignof(NMPLinkAddress) == 1); - -gconstpointer nmp_link_address_get(const NMPLinkAddress *addr, size_t *length); -GBytes * nmp_link_address_get_as_bytes(const NMPLinkAddress *addr); - -typedef enum { - - /* match-flags are strictly inclusive. That means, - * by default nothing is matched, but if you enable a particular - * flag, a candidate that matches passes the check. - * - * In other words: adding more flags can only extend the result - * set of matching objects. - * - * Also, the flags form partitions. Like, an address can be either of - * ADDRTYPE_NORMAL or ADDRTYPE_LINKLOCAL, but never both. Same for - * the ADDRSTATE match types. - */ - NM_PLATFORM_MATCH_WITH_NONE = 0, - - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL = (1LL << 0), - NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL = (1LL << 1), - NM_PLATFORM_MATCH_WITH_ADDRTYPE__ANY = - NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRTYPE_LINKLOCAL, - - NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL = (1LL << 2), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE = (1LL << 3), - NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED = (1LL << 4), - NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY = NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_TENTATIVE - | NM_PLATFORM_MATCH_WITH_ADDRSTATE_DADFAILED, -} NMPlatformMatchFlags; - -#define NM_PLATFORM_LINK_OTHER_NETNS (-1) - -struct _NMPlatformObject { - /* the object type has no fields of its own, it is only used to having - * a special pointer type that can be used to indicate "any" type. */ - char _dummy_don_t_use_me; -}; - -#define __NMPlatformObjWithIfindex_COMMON \ - int ifindex; \ - ; - -struct _NMPlatformObjWithIfindex { - __NMPlatformObjWithIfindex_COMMON; -}; - -struct _NMPlatformLink { - __NMPlatformObjWithIfindex_COMMON; - char name[NMP_IFNAMSIZ]; - NMLinkType type; - - /* rtnl_link_get_type(), IFLA_INFO_KIND. */ - /* NMPlatform initializes this field with a static string. */ - const char *kind; - - /* NMPlatform initializes this field with a static string. */ - const char *driver; - - int master; - - /* rtnl_link_get_link(), IFLA_LINK. - * If IFLA_LINK_NETNSID indicates that the parent is in another namespace, - * this field be set to (negative) NM_PLATFORM_LINK_OTHER_NETNS. */ - int parent; - - /* IFF_* flags. Note that the flags in 'struct ifinfomsg' are declared as 'unsigned'. */ - guint n_ifi_flags; - - guint mtu; - - /* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */ - guint32 arptype; - - /* IFLA_ADDRESS */ - NMPLinkAddress l_address; - - /* IFLA_BROADCAST */ - NMPLinkAddress l_broadcast; - - /* rtnl_link_inet6_get_token(), IFLA_INET6_TOKEN */ - NMUtilsIPv6IfaceId inet6_token; - - /* The bitwise inverse of rtnl_link_inet6_get_addr_gen_mode(). It is inverse - * to have a default of 0 -- meaning: unspecified. That way, a struct - * initialized with memset(0) has and unset value.*/ - guint8 inet6_addr_gen_mode_inv; - - /* Statistics */ - guint64 rx_packets; - guint64 rx_bytes; - guint64 tx_packets; - guint64 tx_bytes; - - /* @connected is mostly identical to (@n_ifi_flags & IFF_UP). Except for bridge/bond masters, - * where we coerce the link as disconnect if it has no slaves. */ - bool connected : 1; - - bool initialized : 1; -}; - -typedef enum { /*< skip >*/ - NM_PLATFORM_SIGNAL_ID_NONE, - NM_PLATFORM_SIGNAL_ID_LINK, - NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, - NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, - NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, - NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, - NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, - NM_PLATFORM_SIGNAL_ID_QDISC, - NM_PLATFORM_SIGNAL_ID_TFILTER, - _NM_PLATFORM_SIGNAL_ID_LAST, -} NMPlatformSignalIdType; - -guint _nm_platform_signal_id_get(NMPlatformSignalIdType signal_type); - -typedef enum { - NM_PLATFORM_SIGNAL_NONE, - NM_PLATFORM_SIGNAL_ADDED, - NM_PLATFORM_SIGNAL_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, -} NMPlatformSignalChangeType; - -#define NM_PLATFORM_IP_ADDRESS_CAST(address) \ - NM_CONSTCAST(NMPlatformIPAddress, \ - (address), \ - NMPlatformIPXAddress, \ - NMPlatformIP4Address, \ - NMPlatformIP6Address) - -#define __NMPlatformIPAddress_COMMON \ - __NMPlatformObjWithIfindex_COMMON; \ - NMIPConfigSource addr_source; \ - \ - /* Timestamp in seconds in the reference system of nm_utils_get_monotonic_timestamp_*(). - * - * The rules are: - * 1 @lifetime==0: @timestamp and @preferred is irrelevant (but mostly set to 0 too). Such addresses - * are permanent. This rule is so that unset addresses (calloc) are permanent by default. - * 2 @lifetime==@preferred==NM_PLATFORM_LIFETIME_PERMANENT: @timestamp is irrelevant (but mostly - * set to 0). Such addresses are permanent. - * 3 Non permanent addresses should (almost) always have @timestamp > 0. 0 is not a valid timestamp - * and never returned by nm_utils_get_monotonic_timestamp_sec(). In this case @valid/@preferred - * is anchored at @timestamp. - * 4 Non permanent addresses with @timestamp == 0 are implicitly anchored at *now*, thus the time - * moves as time goes by. This is usually not useful, except e.g. nm_platform_ip[46]_address_add(). - * - * Non permanent addresses from DHCP/RA might have the @timestamp set to the moment of when the - * lease was received. Addresses from kernel might have the @timestamp based on the last modification - * time of the addresses. But don't rely on this behaviour, the @timestamp is only defined for anchoring - * @lifetime and @preferred. - */ \ - guint32 timestamp; \ - guint32 lifetime; /* seconds since timestamp */ \ - guint32 preferred; /* seconds since timestamp */ \ - \ - /* ifa_flags in 'struct ifaddrmsg' from <linux/if_addr.h>, extended to 32 bit by - * IFA_FLAGS attribute. */ \ - guint32 n_ifa_flags; \ - \ - guint8 plen; \ - \ - /* FIXME(l3cfg): the external marker won't be necessary anymore, because we only - * merge addresses we care about, and ignore (don't remove) external addresses. */ \ - bool external : 1; \ - \ - bool use_ip4_broadcast_address : 1; \ - ; - -/** - * NMPlatformIPAddress: - * - * Common parts of NMPlatformIP4Address and NMPlatformIP6Address. - **/ -typedef struct { - __NMPlatformIPAddress_COMMON; - union { - guint8 address_ptr[1]; - guint32 __dummy_for_32bit_alignment; - }; -} NMPlatformIPAddress; - -/** - * NMPlatformIP4Address: - * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() - **/ -struct _NMPlatformIP4Address { - __NMPlatformIPAddress_COMMON; - - /* The local address IFA_LOCAL. */ - in_addr_t address; - - /* The IFA_ADDRESS PTP peer address. This field is rather important, because - * it constitutes the identifier for the IPv4 address (e.g. you can add two - * addresses that only differ by their peer's network-part. - * - * Beware that for most cases, NetworkManager doesn't want to set an explicit - * peer-address. However, that corresponds to setting the peer address to @address - * itself. Leaving peer-address unset/zero, means explicitly setting the peer - * address to 0.0.0.0, which you probably don't want. - * */ - in_addr_t peer_address; /* PTP peer address */ - - /* IFA_BROADCAST. - * - * This parameter is ignored unless use_ip4_broadcast_address is TRUE. - * See nm_platform_ip4_broadcast_address_from_addr(). */ - in_addr_t broadcast_address; - - char label[NMP_IFNAMSIZ]; -}; - -/** - * NMPlatformIP6Address: - * @timestamp: timestamp as returned by nm_utils_get_monotonic_timestamp_sec() - **/ -struct _NMPlatformIP6Address { - __NMPlatformIPAddress_COMMON; - struct in6_addr address; - struct in6_addr peer_address; -}; - -typedef union { - NMPlatformIPAddress ax; - NMPlatformIP4Address a4; - NMPlatformIP6Address a6; -} NMPlatformIPXAddress; - -#undef __NMPlatformIPAddress_COMMON - -#define NM_PLATFORM_IP4_ADDRESS_INIT(...) (&((const NMPlatformIP4Address){__VA_ARGS__})) - -#define NM_PLATFORM_IP6_ADDRESS_INIT(...) (&((const NMPlatformIP6Address){__VA_ARGS__})) - -/* Default value for adding an IPv4 route. This is also what iproute2 does. - * Note that contrary to IPv6, you can add routes with metric 0 and it is even - * the default. - */ -#define NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4 ((guint32) 0u) - -/* Default value for adding an IPv6 route. This is also what iproute2 does. - * Adding an IPv6 route with metric 0, kernel translates to IP6_RT_PRIO_USER (1024). - * - * Note that kernel doesn't allow adding IPv6 routes with metric zero via netlink. - * It however can itself add routes with metric zero. */ -#define NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6 ((guint32) 1024u) - -/* For IPv4, kernel adds a device route (subnet routes) with metric 0 when user - * configures addresses. */ -#define NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE ((guint32) 0u) - -#define __NMPlatformIPRoute_COMMON \ - __NMPlatformObjWithIfindex_COMMON; \ - \ - /* The NMIPConfigSource. For routes that we receive from cache this corresponds - * to the rtm_protocol field (and is one of the NM_IP_CONFIG_SOURCE_RTPROT_* values). - * When adding a route, the source will be coerced to the protocol using - * nmp_utils_ip_config_source_coerce_to_rtprot(). - * - * rtm_protocol is part of the primary key of an IPv4 route (meaning, you can add - * two IPv4 routes that only differ in their rtm_protocol. For IPv6, that is not - * the case. - * - * When deleting an IPv4/IPv6 route, the rtm_protocol field must match (even - * if it is not part of the primary key for IPv6) -- unless rtm_protocol is set - * to zero, in which case the first matching route (with proto ignored) is deleted. */ \ - NMIPConfigSource rt_source; \ - \ - guint8 plen; \ - \ - /* RTA_METRICS: - * - * For IPv4 routes, these properties are part of their - * ID (meaning: you can add otherwise identical IPv4 routes that - * only differ by the metric property). - * On the other hand, for IPv6 you cannot add two IPv6 routes that only differ - * by an RTA_METRICS property. - * - * When deleting a route, kernel seems to ignore the RTA_METRICS properties. - * That is a problem/bug for IPv4 because you cannot explicitly select which - * route to delete. Kernel just picks the first. See rh#1475642. */ \ - \ - /* RTA_METRICS.RTAX_LOCK (iproute2: "lock" arguments) */ \ - bool lock_window : 1; \ - bool lock_cwnd : 1; \ - bool lock_initcwnd : 1; \ - bool lock_initrwnd : 1; \ - bool lock_mtu : 1; \ - \ - /* if TRUE, the "metric" field gets ignored and can be overridden with settings from - * the device. This is to track routes that should be configured (e.g. from a DHCP - * lease), but where the actual metric is determined by NMDevice. */ \ - bool metric_any : 1; \ - \ - /* like "metric_any", the table is determined by other layers of the code. - * This field overrides "table_coerced" field. */ \ - bool table_any : 1; \ - \ - /* rtnh_flags - * - * Routes with rtm_flags RTM_F_CLONED are hidden by platform and - * do not exist from the point-of-view of platform users. - * Such a route is not alive, according to nmp_object_is_alive(). - * - * NOTE: currently we ignore all flags except RTM_F_CLONED - * and RTNH_F_ONLINK. - * We also may not properly consider the flags as part of the ID - * in route-cmp. */ \ - unsigned r_rtm_flags; \ - \ - /* RTA_METRICS.RTAX_ADVMSS (iproute2: advmss) */ \ - guint32 mss; \ - \ - /* RTA_METRICS.RTAX_WINDOW (iproute2: window) */ \ - guint32 window; \ - \ - /* RTA_METRICS.RTAX_CWND (iproute2: cwnd) */ \ - guint32 cwnd; \ - \ - /* RTA_METRICS.RTAX_INITCWND (iproute2: initcwnd) */ \ - guint32 initcwnd; \ - \ - /* RTA_METRICS.RTAX_INITRWND (iproute2: initrwnd) */ \ - guint32 initrwnd; \ - \ - /* RTA_METRICS.RTAX_MTU (iproute2: mtu) */ \ - guint32 mtu; \ - \ - /* RTA_PRIORITY (iproute2: metric) */ \ - guint32 metric; \ - \ - /* rtm_table, RTA_TABLE. - * - * This is not the original table ID. Instead, 254 (RT_TABLE_MAIN) and - * zero (RT_TABLE_UNSPEC) are swapped, so that the default is the main - * table. Use nm_platform_route_table_coerce()/nm_platform_route_table_uncoerce(). */ \ - guint32 table_coerced; \ - \ - /* rtm_type. - * - * This is not the original type, if type_coerced is 0 then - * it means RTN_UNSPEC otherwise the type value is preserved. - * */ \ - guint8 type_coerced; \ - \ - /*end*/ - -typedef struct { - __NMPlatformIPRoute_COMMON; - union { - guint8 network_ptr[1]; - guint32 __dummy_for_32bit_alignment; - }; -} NMPlatformIPRoute; - -#define NM_PLATFORM_IP_ROUTE_CAST(route) \ - NM_CONSTCAST(NMPlatformIPRoute, \ - (route), \ - NMPlatformIPXRoute, \ - NMPlatformIP4Route, \ - NMPlatformIP6Route) - -#define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) (NM_PLATFORM_IP_ROUTE_CAST(route)->plen <= 0) - -struct _NMPlatformIP4Route { - __NMPlatformIPRoute_COMMON; - in_addr_t network; - - /* RTA_GATEWAY. The gateway is part of the primary key for a route */ - in_addr_t gateway; - - /* RTA_PREFSRC (called "src" by iproute2). - * - * pref_src is part of the ID of an IPv4 route. When deleting a route, - * pref_src must match, unless set to 0.0.0.0 to match any. */ - in_addr_t pref_src; - - /* rtm_tos (iproute2: tos) - * - * For IPv4, tos is part of the weak-id (like metric). - * - * For IPv6, tos is ignored by kernel. */ - guint8 tos; - - /* The bitwise inverse of the route scope rtm_scope. It is inverted so that the - * default value (RT_SCOPE_NOWHERE) is zero. Use nm_platform_route_scope_inv() - * to convert back and forth between the inverse representation and the - * real value. - * - * rtm_scope is part of the primary key for IPv4 routes. When deleting a route, - * the scope must match, unless it is left at RT_SCOPE_NOWHERE, in which case the first - * matching route is deleted. - * - * For IPv6 routes, the scope is ignored and kernel always assumes global scope. - * Hence, this field is only in NMPlatformIP4Route. */ - guint8 scope_inv; -}; - -struct _NMPlatformIP6Route { - __NMPlatformIPRoute_COMMON; - struct in6_addr network; - - /* RTA_GATEWAY. The gateway is part of the primary key for a route */ - struct in6_addr gateway; - - /* RTA_PREFSRC (called "src" by iproute2). - * - * pref_src is not part of the ID for an IPv6 route. You cannot add two - * routes that only differ by pref_src. - * - * When deleting a route, pref_src is ignored by kernel. */ - struct in6_addr pref_src; - - /* RTA_SRC and rtm_src_len (called "from" by iproute2). - * - * Kernel clears the host part of src/src_plen. - * - * src/src_plen is part of the ID of a route just like network/plen. That is, - * Not only `ip route append`, but also `ip route add` allows to add routes that only - * differ in their src/src_plen. - */ - struct in6_addr src; - guint8 src_plen; - - /* RTA_PREF router preference. - * - * The type is guint8 to keep the struct size small. But the values are compatible with - * the NMIcmpv6RouterPref enum. */ - guint8 rt_pref; -}; - -typedef union { - NMPlatformIPRoute rx; - NMPlatformIP4Route r4; - NMPlatformIP6Route r6; -} NMPlatformIPXRoute; - -#undef __NMPlatformIPRoute_COMMON - -typedef struct { - /* struct fib_rule_uid_range */ - guint32 start; - guint32 end; -} NMFibRuleUidRange; - -typedef struct { - /* struct fib_rule_port_range */ - guint16 start; - guint16 end; -} NMFibRulePortRange; - -typedef struct { - NMIPAddr src; /* FRA_SRC */ - NMIPAddr dst; /* FRA_DST */ - guint64 tun_id; /* betoh64(FRA_TUN_ID) */ - guint32 table; /* (struct fib_rule_hdr).table, FRA_TABLE */ - guint32 flags; /* (struct fib_rule_hdr).flags */ - guint32 priority; /* RA_PRIORITY */ - guint32 fwmark; /* FRA_FWMARK */ - guint32 fwmask; /* FRA_FWMASK */ - guint32 goto_target; /* FRA_GOTO */ - guint32 flow; /* FRA_FLOW */ - guint32 suppress_prefixlen_inverse; /* ~(FRA_SUPPRESS_PREFIXLEN) */ - guint32 suppress_ifgroup_inverse; /* ~(FRA_SUPPRESS_IFGROUP) */ - NMFibRuleUidRange uid_range; /* FRA_UID_RANGE */ - NMFibRulePortRange sport_range; /* FRA_SPORT_RANGE */ - NMFibRulePortRange dport_range; /* FRA_DPORT_RANGE */ - char iifname[NMP_IFNAMSIZ]; /* FRA_IIFNAME */ - char oifname[NMP_IFNAMSIZ]; /* FRA_OIFNAME */ - guint8 addr_family; /* (struct fib_rule_hdr).family */ - guint8 action; /* (struct fib_rule_hdr).action */ - guint8 tos; /* (struct fib_rule_hdr).tos */ - guint8 src_len; /* (struct fib_rule_hdr).src_len */ - guint8 dst_len; /* (struct fib_rule_hdr).dst_len */ - guint8 l3mdev; /* FRA_L3MDEV */ - guint8 protocol; /* FRA_PROTOCOL */ - guint8 ip_proto; /* FRA_IP_PROTO */ - - bool uid_range_has : 1; /* has(FRA_UID_RANGE) */ -} NMPlatformRoutingRule; - -#define NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET (~((guint32) 0)) - -#define NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED ((guint32) 0x83126E97u) - -G_STATIC_ASSERT(((((guint64) NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) * 1000u) >> 10) - == (guint64) INT_MAX); - -typedef struct { - guint32 limit; - guint32 flows; - guint32 target; - guint32 interval; - guint32 quantum; - - /* TCA_FQ_CODEL_CE_THRESHOLD: kernel internally stores this value as - * ((val64 * NSEC_PER_USEC) >> CODEL_SHIFT). The default value (in - * the domain with this coercion) is CODEL_DISABLED_THRESHOLD (INT_MAX). - * That means, "disabled" is expressed on RTM_NEWQDISC netlink API by absence of the - * netlink attribute but also as the special value 0x83126E97u - * (NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED). - * Beware: zero is not the default you must always explicitly set this value. */ - guint32 ce_threshold; - - /* TCA_FQ_CODEL_MEMORY_LIMIT: note that only values <= 2^31 are accepted by kernel - * and kernel defaults to 32MB. - * Note that we use the special value NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET - * to indicate that no explicit limit is set (when we send a RTM_NEWQDISC request). - * This will cause kernel to choose the default (32MB). - * Beware: zero is not the default you must always explicitly set this value. */ - guint32 memory_limit; - - bool ecn : 1; -} NMPlatformQdiscFqCodel; - -typedef struct { - unsigned quantum; - int perturb_period; - guint32 limit; - unsigned divisor; - unsigned flows; - unsigned depth; -} NMPlatformQdiscSfq; - -typedef struct { - guint64 rate; - guint32 burst; - guint32 limit; - guint32 latency; -} NMPlatformQdiscTbf; - -typedef struct { - __NMPlatformObjWithIfindex_COMMON; - - /* beware, kind is embedded in an NMPObject, hence you must - * take care of the lifetime of the string. */ - const char *kind; - - int addr_family; - guint32 handle; - guint32 parent; - guint32 info; - union { - NMPlatformQdiscFqCodel fq_codel; - NMPlatformQdiscSfq sfq; - NMPlatformQdiscTbf tbf; - }; -} NMPlatformQdisc; - -typedef struct { - char sdata[32]; -} NMPlatformActionSimple; - -typedef struct { - int ifindex; - bool egress : 1; - bool ingress : 1; - bool mirror : 1; - bool redirect : 1; -} NMPlatformActionMirred; - -typedef struct { - /* beware, kind is embedded in an NMPObject, hence you must - * take care of the lifetime of the string. */ - const char *kind; - - union { - NMPlatformActionSimple simple; - NMPlatformActionMirred mirred; - }; -} NMPlatformAction; - -#define NM_PLATFORM_ACTION_KIND_SIMPLE "simple" -#define NM_PLATFORM_ACTION_KIND_MIRRED "mirred" - -typedef struct { - __NMPlatformObjWithIfindex_COMMON; - - /* beware, kind is embedded in an NMPObject, hence you must - * take care of the lifetime of the string. */ - const char *kind; - - int addr_family; - guint32 handle; - guint32 parent; - guint32 info; - NMPlatformAction action; -} NMPlatformTfilter; - -#undef __NMPlatformObjWithIfindex_COMMON - -typedef struct { - gboolean is_ip4; - NMPObjectType obj_type; - int addr_family; - gsize sizeof_route; - int (*route_cmp)(const NMPlatformIPXRoute *a, - const NMPlatformIPXRoute *b, - NMPlatformIPRouteCmpType cmp_type); - const char *(*route_to_string)(const NMPlatformIPXRoute *route, char *buf, gsize len); -} NMPlatformVTableRoute; - -typedef union { - struct { - NMPlatformVTableRoute v6; - NMPlatformVTableRoute v4; - }; - NMPlatformVTableRoute vx[2]; -} _NMPlatformVTableRouteUnion; - -extern const _NMPlatformVTableRouteUnion nm_platform_vtable_route; - -typedef struct { - guint16 id; - guint32 qos; - bool proto_ad : 1; -} NMPlatformVFVlan; - -typedef struct { - guint32 index; - guint32 min_tx_rate; - guint32 max_tx_rate; - guint num_vlans; - NMPlatformVFVlan *vlans; - struct { - guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */ - guint8 len; - } mac; - gint8 spoofchk; - gint8 trust; -} NMPlatformVF; - -typedef struct { - guint16 vid_start; - guint16 vid_end; - bool untagged : 1; - bool pvid : 1; -} NMPlatformBridgeVlan; - -typedef struct { - NMEtherAddr group_addr; - bool mcast_querier : 1; - bool mcast_query_use_ifaddr : 1; - bool mcast_snooping : 1; - bool stp_state : 1; - bool vlan_stats_enabled : 1; - guint16 group_fwd_mask; - guint16 priority; - guint16 vlan_protocol; - guint32 ageing_time; - guint32 forward_delay; - guint32 hello_time; - guint32 max_age; - guint32 mcast_last_member_count; - guint32 mcast_startup_query_count; - guint32 mcast_hash_max; - guint64 mcast_last_member_interval; - guint64 mcast_membership_interval; - guint64 mcast_querier_interval; - guint64 mcast_query_interval; - guint64 mcast_query_response_interval; - guint64 mcast_startup_query_interval; - guint8 mcast_router; -} NMPlatformLnkBridge; - -extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default; - -typedef struct { - in_addr_t local; - in_addr_t remote; - int parent_ifindex; - guint16 input_flags; - guint16 output_flags; - guint32 input_key; - guint32 output_key; - guint8 ttl; - guint8 tos; - bool path_mtu_discovery : 1; - bool is_tap : 1; -} NMPlatformLnkGre; - -typedef struct { - int p_key; - const char *mode; -} NMPlatformLnkInfiniband; - -typedef struct { - struct in6_addr local; - struct in6_addr remote; - int parent_ifindex; - guint8 ttl; - guint8 tclass; - guint8 encap_limit; - guint8 proto; - guint flow_label; - guint32 flags; - - /* IP6GRE only */ - guint32 input_key; - guint32 output_key; - guint16 input_flags; - guint16 output_flags; - bool is_tap : 1; - bool is_gre : 1; -} NMPlatformLnkIp6Tnl; - -typedef struct { - in_addr_t local; - in_addr_t remote; - int parent_ifindex; - guint8 ttl; - guint8 tos; - bool path_mtu_discovery : 1; -} NMPlatformLnkIpIp; - -typedef struct { - int parent_ifindex; - guint64 sci; /* host byte order */ - guint64 cipher_suite; - guint32 window; - guint8 icv_length; - guint8 encoding_sa; - guint8 validation; - bool encrypt : 1; - bool protect : 1; - bool include_sci : 1; - bool es : 1; - bool scb : 1; - bool replay_protect : 1; -} NMPlatformLnkMacsec; - -typedef struct { - guint mode; - bool no_promisc : 1; - bool tap : 1; -} NMPlatformLnkMacvlan; - -typedef struct { - in_addr_t local; - in_addr_t remote; - int parent_ifindex; - guint16 flags; - guint8 ttl; - guint8 tos; - guint8 proto; - bool path_mtu_discovery : 1; -} NMPlatformLnkSit; - -typedef struct { - guint32 owner; - guint32 group; - - guint8 type; - - bool owner_valid : 1; - bool group_valid : 1; - - bool pi : 1; - bool vnet_hdr : 1; - bool multi_queue : 1; - bool persist : 1; -} NMPlatformLnkTun; - -typedef struct { - /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ - guint16 id; - NMVlanFlags flags; -} NMPlatformLnkVlan; - -typedef struct { - guint32 table; -} NMPlatformLnkVrf; - -typedef struct { - struct in6_addr group6; - struct in6_addr local6; - in_addr_t group; - in_addr_t local; - int parent_ifindex; - guint32 id; - guint32 ageing; - guint32 limit; - guint16 dst_port; - guint16 src_port_min; - guint16 src_port_max; - guint8 tos; - guint8 ttl; - bool learning : 1; - bool proxy : 1; - bool rsc : 1; - bool l2miss : 1; - bool l3miss : 1; -} NMPlatformLnkVxlan; - -#define NMP_WIREGUARD_PUBLIC_KEY_LEN 32 -#define NMP_WIREGUARD_SYMMETRIC_KEY_LEN 32 - -typedef struct { - guint32 fwmark; - guint16 listen_port; - guint8 private_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; - guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; -} NMPlatformLnkWireGuard; - -typedef enum { - NM_PLATFORM_LINK_DUPLEX_UNKNOWN, - NM_PLATFORM_LINK_DUPLEX_HALF, - NM_PLATFORM_LINK_DUPLEX_FULL, -} NMPlatformLinkDuplexType; - -typedef enum { - NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE = 0, - NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS = (1LL << 0), - NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY = (1LL << 1), - NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT = (1LL << 2), - NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK = (1LL << 3), -} NMPlatformWireGuardChangeFlags; - -typedef enum { - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE = 0, - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REMOVE_ME = (1LL << 0), - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY = (1LL << 1), - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL = (1LL << 2), - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT = (1LL << 3), - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS = (1LL << 4), - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS = (1LL << 5), - - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT = - NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT - | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS, - -} NMPlatformWireGuardChangePeerFlags; - -typedef void (*NMPlatformAsyncCallback)(GError *error, gpointer user_data); - -/*****************************************************************************/ - -typedef enum { - NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS, - NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL, - NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF, - NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, - NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, - NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL, - NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED, - - /* this also includes FRA_SPORT_RANGE and FRA_DPORT_RANGE which - * were added at the same time. */ - NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO, - - _NM_PLATFORM_KERNEL_SUPPORT_NUM, -} NMPlatformKernelSupportType; - -extern volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPORT_NUM]; - -int _nm_platform_kernel_support_init(NMPlatformKernelSupportType type, int value); - -static inline gboolean -_nm_platform_kernel_support_detected(NMPlatformKernelSupportType type) -{ - nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < G_N_ELEMENTS(_nm_platform_kernel_support_state)); - - return G_LIKELY(g_atomic_int_get(&_nm_platform_kernel_support_state[type]) != 0); -} - -static inline NMTernary -nm_platform_kernel_support_get_full(NMPlatformKernelSupportType type, gboolean init_if_not_set) -{ - int v; - - nm_assert(_NM_INT_NOT_NEGATIVE(type) && type < G_N_ELEMENTS(_nm_platform_kernel_support_state)); - - v = g_atomic_int_get(&_nm_platform_kernel_support_state[type]); - if (G_UNLIKELY(v == 0)) { - if (!init_if_not_set) - return NM_TERNARY_DEFAULT; - v = _nm_platform_kernel_support_init(type, 0); - } - return (v >= 0); -} - -static inline gboolean -nm_platform_kernel_support_get(NMPlatformKernelSupportType type) -{ - return nm_platform_kernel_support_get_full(type, TRUE) != NM_TERNARY_FALSE; -} - -/*****************************************************************************/ - -struct _NMPlatformPrivate; - -struct _NMPlatform { - GObject parent; - NMPNetns * _netns; - struct _NMPlatformPrivate *_priv; -}; - -typedef struct { - GObjectClass parent; - - gboolean (*sysctl_set)(NMPlatform *self, - const char *pathid, - int dirfd, - const char *path, - const char *value); - void (*sysctl_set_async)(NMPlatform * self, - const char * pathid, - int dirfd, - const char * path, - const char *const * values, - NMPlatformAsyncCallback callback, - gpointer data, - GCancellable * cancellable); - char *(*sysctl_get)(NMPlatform *self, const char *pathid, int dirfd, const char *path); - - void (*refresh_all)(NMPlatform *self, NMPObjectType obj_type); - void (*process_events)(NMPlatform *self); - - int (*link_add)(NMPlatform * self, - NMLinkType type, - const char * name, - int parent, - const void * address, - size_t address_len, - guint32 mtu, - gconstpointer extra_data, - const NMPlatformLink **out_link); - gboolean (*link_delete)(NMPlatform *self, int ifindex); - gboolean (*link_refresh)(NMPlatform *self, int ifindex); - gboolean (*link_set_netns)(NMPlatform *self, int ifindex, int netns_fd); - gboolean (*link_set_up)(NMPlatform *self, int ifindex, gboolean *out_no_firmware); - gboolean (*link_set_down)(NMPlatform *self, int ifindex); - gboolean (*link_set_arp)(NMPlatform *self, int ifindex); - gboolean (*link_set_noarp)(NMPlatform *self, int ifindex); - - int (*link_set_user_ipv6ll_enabled)(NMPlatform *self, int ifindex, gboolean enabled); - gboolean (*link_set_token)(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); - - gboolean (*link_get_permanent_address)(NMPlatform *self, - int ifindex, - guint8 * buf, - size_t * length); - int (*link_set_address)(NMPlatform *self, int ifindex, gconstpointer address, size_t length); - int (*link_set_mtu)(NMPlatform *self, int ifindex, guint32 mtu); - gboolean (*link_set_name)(NMPlatform *self, int ifindex, const char *name); - void (*link_set_sriov_params_async)(NMPlatform * self, - int ifindex, - guint num_vfs, - int autoprobe, - NMPlatformAsyncCallback callback, - gpointer callback_data, - GCancellable * cancellable); - gboolean (*link_set_sriov_vfs)(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); - gboolean (*link_set_bridge_vlans)(NMPlatform * self, - int ifindex, - gboolean on_master, - const NMPlatformBridgeVlan *const *vlans); - - char *(*link_get_physical_port_id)(NMPlatform *self, int ifindex); - guint (*link_get_dev_id)(NMPlatform *self, int ifindex); - gboolean (*link_get_wake_on_lan)(NMPlatform *self, int ifindex); - gboolean (*link_get_driver_info)(NMPlatform *self, - int ifindex, - char ** out_driver_name, - char ** out_driver_version, - char ** out_fw_version); - - gboolean (*link_supports_carrier_detect)(NMPlatform *self, int ifindex); - gboolean (*link_supports_vlans)(NMPlatform *self, int ifindex); - gboolean (*link_supports_sriov)(NMPlatform *self, int ifindex); - - gboolean (*link_enslave)(NMPlatform *self, int master, int slave); - gboolean (*link_release)(NMPlatform *self, int master, int slave); - - gboolean (*link_can_assume)(NMPlatform *self, int ifindex); - - int (*link_wireguard_change)(NMPlatform * self, - int ifindex, - const NMPlatformLnkWireGuard * lnk_wireguard, - const struct _NMPWireGuardPeer * peers, - const NMPlatformWireGuardChangePeerFlags *peer_flags, - guint peers_len, - NMPlatformWireGuardChangeFlags change_flags); - - gboolean (*link_vlan_change)(NMPlatform * self, - int ifindex, - NMVlanFlags flags_mask, - NMVlanFlags flags_set, - gboolean ingress_reset_all, - const NMVlanQosMapping *ingress_map, - gsize n_ingress_map, - gboolean egress_reset_all, - const NMVlanQosMapping *egress_map, - gsize n_egress_map); - gboolean (*link_tun_add)(NMPlatform * self, - const char * name, - const NMPlatformLnkTun *props, - const NMPlatformLink ** out_link, - int * out_fd); - - gboolean (*infiniband_partition_add)(NMPlatform * self, - int parent, - int p_key, - const NMPlatformLink **out_link); - gboolean (*infiniband_partition_delete)(NMPlatform *self, int parent, int p_key); - - gboolean (*wifi_get_capabilities)(NMPlatform * self, - int ifindex, - NMDeviceWifiCapabilities *caps); - gboolean (*wifi_get_bssid)(NMPlatform *self, int ifindex, guint8 *bssid); - guint32 (*wifi_get_frequency)(NMPlatform *self, int ifindex); - int (*wifi_get_quality)(NMPlatform *self, int ifindex); - guint32 (*wifi_get_rate)(NMPlatform *self, int ifindex); - NM80211Mode (*wifi_get_mode)(NMPlatform *self, int ifindex); - void (*wifi_set_mode)(NMPlatform *self, int ifindex, NM80211Mode mode); - void (*wifi_set_powersave)(NMPlatform *self, int ifindex, guint32 powersave); - guint32 (*wifi_find_frequency)(NMPlatform *self, int ifindex, const guint32 *freqs); - void (*wifi_indicate_addressing_running)(NMPlatform *self, int ifindex, gboolean running); - NMSettingWirelessWakeOnWLan (*wifi_get_wake_on_wlan)(NMPlatform *self, int ifindex); - gboolean (*wifi_set_wake_on_wlan)(NMPlatform * self, - int ifindex, - NMSettingWirelessWakeOnWLan wowl); - - guint32 (*mesh_get_channel)(NMPlatform *self, int ifindex); - gboolean (*mesh_set_channel)(NMPlatform *self, int ifindex, guint32 channel); - gboolean (*mesh_set_ssid)(NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); - - guint16 (*wpan_get_pan_id)(NMPlatform *self, int ifindex); - gboolean (*wpan_set_pan_id)(NMPlatform *self, int ifindex, guint16 pan_id); - guint16 (*wpan_get_short_addr)(NMPlatform *self, int ifindex); - gboolean (*wpan_set_short_addr)(NMPlatform *self, int ifindex, guint16 short_addr); - gboolean (*wpan_set_channel)(NMPlatform *self, int ifindex, guint8 page, guint8 channel); - - gboolean (*object_delete)(NMPlatform *self, const NMPObject *obj); - - gboolean (*ip4_address_add)(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address, - in_addr_t broadcast_address, - guint32 lifetime, - guint32 preferred_lft, - guint32 flags, - const char *label); - gboolean (*ip6_address_add)(NMPlatform * self, - int ifindex, - struct in6_addr address, - guint8 plen, - struct in6_addr peer_address, - guint32 lifetime, - guint32 preferred_lft, - guint32 flags); - gboolean (*ip4_address_delete)(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address); - gboolean (*ip6_address_delete)(NMPlatform * self, - int ifindex, - struct in6_addr address, - guint8 plen); - - int (*ip_route_add)(NMPlatform * self, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route); - int (*ip_route_get)(NMPlatform * self, - int addr_family, - gconstpointer address, - int oif_ifindex, - NMPObject ** out_route); - - int (*routing_rule_add)(NMPlatform * self, - NMPNlmFlags flags, - const NMPlatformRoutingRule *routing_rule); - - int (*qdisc_add)(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc); - - int (*tfilter_add)(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter); -} NMPlatformClass; - -/* NMPlatform signals - * - * Each signal handler is called with a type-specific object that provides - * key attributes that constitute identity of the object. They may also - * provide additional attributes for convenience. - * - * The object only intended to be used by the signal handler to determine - * the current values. It is no longer valid after the signal handler exits - * but you are free to copy the provided information and use it for later - * reference. - */ -#define NM_PLATFORM_SIGNAL_LINK_CHANGED "link-changed" -#define NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED "ip4-address-changed" -#define NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED "ip6-address-changed" -#define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed" -#define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed" -#define NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED "routing-rule-changed" -#define NM_PLATFORM_SIGNAL_QDISC_CHANGED "qdisc-changed" -#define NM_PLATFORM_SIGNAL_TFILTER_CHANGED "tfilter-changed" - -const char *nm_platform_signal_change_type_to_string(NMPlatformSignalChangeType change_type); - -/*****************************************************************************/ - -GType nm_platform_get_type(void); - -void nm_platform_setup(NMPlatform *instance); -NMPlatform *nm_platform_get(void); - -#define NM_PLATFORM_GET (nm_platform_get()) - -/*****************************************************************************/ - -static inline in_addr_t -nm_platform_ip4_broadcast_address_create(in_addr_t address, guint8 plen) -{ - return address | ~_nm_utils_ip4_prefix_to_netmask(plen); -} - -static inline in_addr_t -nm_platform_ip4_broadcast_address_from_addr(const NMPlatformIP4Address *addr) -{ - nm_assert(addr); - - if (addr->use_ip4_broadcast_address) - return addr->broadcast_address; - - /* the set broadcast-address gets ignored, and we determine a default brd base - * on the peer IFA_ADDRESS. */ - if (addr->peer_address != 0u && addr->plen < 31 /* RFC3021 */) - return nm_platform_ip4_broadcast_address_create(addr->peer_address, addr->plen); - return 0u; -} - -/*****************************************************************************/ - -/** - * nm_platform_route_table_coerce: - * @table: the route table, in its original value as received - * from rtm_table/RTA_TABLE. - * - * Returns: returns the coerced table id, that can be stored in - * NMPlatformIPRoute.table_coerced. - */ -static inline guint32 -nm_platform_route_table_coerce(guint32 table) -{ - /* For kernel, the default table is RT_TABLE_MAIN (254). - * We want that in NMPlatformIPRoute.table_coerced a numeric - * zero is the default. Hence, @table_coerced swaps the - * value 0 and 254. Use nm_platform_route_table_coerce() - * and nm_platform_route_table_uncoerce() to convert between - * the two domains. */ - switch (table) { - case 0 /* RT_TABLE_UNSPEC */: - return 254; - case 254 /* RT_TABLE_MAIN */: - return 0; - default: - return table; - } -} - -/** - * nm_platform_route_table_uncoerce: - * @table_coerced: the route table, in its coerced value - * @normalize: whether to normalize RT_TABLE_UNSPEC to - * RT_TABLE_MAIN. For kernel, routes with a table id - * RT_TABLE_UNSPEC do not exist and are treated like - * RT_TABLE_MAIN. - * - * Returns: reverts the coerced table ID in NMPlatformIPRoute.table_coerced - * to the original value as kernel understands it. - */ -static inline guint32 -nm_platform_route_table_uncoerce(guint32 table_coerced, gboolean normalize) -{ - /* this undoes nm_platform_route_table_coerce(). */ - switch (table_coerced) { - case 0 /* RT_TABLE_UNSPEC */: - return 254; - case 254 /* RT_TABLE_MAIN */: - return normalize ? 254 : 0; - default: - return table_coerced; - } -} - -static inline gboolean -nm_platform_route_table_is_main(guint32 table) -{ - /* same as - * nm_platform_route_table_uncoerce (table, TRUE) == RT_TABLE_MAIN - * and - * nm_platform_route_table_uncoerce (nm_platform_route_table_coerce (table), TRUE) == RT_TABLE_MAIN - * - * That is, the function operates the same on @table and its coerced - * form. - */ - return table == 0 || table == 254; -} - -/** - * nm_platform_route_scope_inv: - * @scope: the route scope, either its original value, or its inverse. - * - * This function is useful, because the constants such as RT_SCOPE_NOWHERE - * are 'int', so ~scope also gives an 'int'. This function gets the type - * casts to guint8 right. - * - * Returns: the bitwise inverse of the route scope. - * */ -#define nm_platform_route_scope_inv _nm_platform_uint8_inv -static inline guint8 -_nm_platform_uint8_inv(guint8 scope) -{ - return (guint8) ~scope; -} - -/** - * nm_platform_route_type_coerce: - * @table: the route type, in its original value. - * - * Returns: returns the coerced type, that can be stored in - * NMPlatformIPRoute.type_coerced. - */ -static inline guint8 -nm_platform_route_type_coerce(guint8 type) -{ - switch (type) { - case 0 /* RTN_UNSPEC */: - return 1; - case 1 /* RTN_UNICAST */: - return 0; - default: - return type; - } -} - -/** - * nm_platform_route_type_uncoerce: - * @table: the type table, in its coerced value - * - * Returns: reverts the coerced type in NMPlatformIPRoute.type_coerced - * to the original value as kernel understands it. - */ -static inline guint8 -nm_platform_route_type_uncoerce(guint8 type_coerced) -{ - return nm_platform_route_type_coerce(type_coerced); -} - -gboolean nm_platform_get_use_udev(NMPlatform *self); -gboolean nm_platform_get_log_with_ptr(NMPlatform *self); - -NMPNetns *nm_platform_netns_get(NMPlatform *self); -gboolean nm_platform_netns_push(NMPlatform *self, NMPNetns **netns); - -const char *nm_link_type_to_string(NMLinkType link_type); - -#define NMP_SYSCTL_PATHID_ABSOLUTE(path) ((const char *) NULL), -1, (path) - -#define NMP_SYSCTL_PATHID_NETDIR_unsafe(dirfd, ifname, path) \ - nm_sprintf_buf_unsafe_a(NM_STRLEN("net:/sys/class/net//\0") + NMP_IFNAMSIZ + ({ \ - const gsize _l = strlen(path); \ - \ - nm_assert(_l < 200); \ - _l; \ - }), \ - "net:/sys/class/net/%s/%s", \ - (ifname), \ - (path)), \ - (dirfd), (path) - -#define NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname, path) \ - nm_sprintf_bufa(NM_STRLEN("net:/sys/class/net//" path "/\0") + NMP_IFNAMSIZ, \ - "net:/sys/class/net/%s/%s", \ - (ifname), \ - path), \ - (dirfd), ("" path "") - -int nm_platform_sysctl_open_netdir(NMPlatform *self, int ifindex, char *out_ifname); -gboolean nm_platform_sysctl_set(NMPlatform *self, - const char *pathid, - int dirfd, - const char *path, - const char *value); -void nm_platform_sysctl_set_async(NMPlatform * self, - const char * pathid, - int dirfd, - const char * path, - const char *const * values, - NMPlatformAsyncCallback callback, - gpointer data, - GCancellable * cancellable); -char * nm_platform_sysctl_get(NMPlatform *self, const char *pathid, int dirfd, const char *path); -gint32 nm_platform_sysctl_get_int32(NMPlatform *self, - const char *pathid, - int dirfd, - const char *path, - gint32 fallback); -gint64 nm_platform_sysctl_get_int_checked(NMPlatform *self, - const char *pathid, - int dirfd, - const char *path, - guint base, - gint64 min, - gint64 max, - gint64 fallback); - -char *nm_platform_sysctl_ip_conf_get(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property); - -gint64 nm_platform_sysctl_ip_conf_get_int_checked(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property, - guint base, - gint64 min, - gint64 max, - gint64 fallback); - -gboolean nm_platform_sysctl_ip_conf_set(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property, - const char *value); - -gboolean nm_platform_sysctl_ip_conf_set_int64(NMPlatform *self, - int addr_family, - const char *ifname, - const char *property, - gint64 value); - -gboolean -nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe(NMPlatform *self, const char *iface, int value); -gboolean nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time(NMPlatform *self, - const char *iface, - guint value_ms); -gboolean nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time(NMPlatform *self, - const char *iface, - guint value_ms); -int nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(NMPlatform *platform, - const char *iface, - gboolean consider_all, - gboolean * out_due_to_all); - -const char *nm_platform_if_indextoname(NMPlatform *self, - int ifindex, - char out_ifname[static 16 /* IFNAMSIZ */]); -int nm_platform_if_nametoindex(NMPlatform *self, const char *ifname); - -const NMPObject *nm_platform_link_get_obj(NMPlatform *self, int ifindex, gboolean visible_only); -const NMPlatformLink *nm_platform_link_get(NMPlatform *self, int ifindex); -const NMPlatformLink *nm_platform_link_get_by_ifname(NMPlatform *self, const char *ifname); -const NMPlatformLink *nm_platform_link_get_by_address(NMPlatform * self, - NMLinkType link_type, - gconstpointer address, - size_t length); - -GPtrArray *nm_platform_link_get_all(NMPlatform *self, gboolean sort_by_name); - -int nm_platform_link_add(NMPlatform * self, - NMLinkType type, - const char * name, - int parent, - const void * address, - size_t address_len, - guint32 mtu, - gconstpointer extra_data, - const NMPlatformLink **out_link); - -static inline int -nm_platform_link_veth_add(NMPlatform * self, - const char * name, - const char * peer, - const NMPlatformLink **out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_VETH, name, 0, NULL, 0, 0, peer, out_link); -} - -static inline int -nm_platform_link_dummy_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_DUMMY, name, 0, NULL, 0, 0, NULL, out_link); -} - -static inline int -nm_platform_link_bridge_add(NMPlatform * self, - const char * name, - const void * address, - size_t address_len, - guint32 mtu, - const NMPlatformLnkBridge *props, - const NMPlatformLink ** out_link) -{ - return nm_platform_link_add(self, - NM_LINK_TYPE_BRIDGE, - name, - 0, - address, - address_len, - mtu, - props, - out_link); -} - -static inline int -nm_platform_link_bond_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, NULL, out_link); -} - -static inline int -nm_platform_link_team_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_TEAM, name, 0, NULL, 0, 0, NULL, out_link); -} - -static inline int -nm_platform_link_wireguard_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_WIREGUARD, name, 0, NULL, 0, 0, NULL, out_link); -} - -static inline int -nm_platform_link_gre_add(NMPlatform * self, - const char * name, - const void * address, - size_t address_len, - const NMPlatformLnkGre *props, - const NMPlatformLink ** out_link) -{ - g_return_val_if_fail(props, -NME_BUG); - - return nm_platform_link_add(self, - props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE, - name, - 0, - address, - address_len, - 0, - props, - out_link); -} - -static inline int -nm_platform_link_sit_add(NMPlatform * self, - const char * name, - const NMPlatformLnkSit *props, - const NMPlatformLink ** out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_SIT, name, 0, NULL, 0, 0, props, out_link); -} - -static inline int -nm_platform_link_vlan_add(NMPlatform * self, - const char * name, - int parent, - int vlanid, - guint32 vlanflags, - const NMPlatformLink **out_link) -{ - g_return_val_if_fail(parent >= 0, -NME_BUG); - g_return_val_if_fail(vlanid >= 0, -NME_BUG); - - return nm_platform_link_add(self, - NM_LINK_TYPE_VLAN, - name, - parent, - NULL, - 0, - 0, - &((NMPlatformLnkVlan){ - .id = vlanid, - .flags = vlanflags, - }), - out_link); -} - -static inline int -nm_platform_link_vrf_add(NMPlatform * self, - const char * name, - const NMPlatformLnkVrf *props, - const NMPlatformLink ** out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_VRF, name, 0, NULL, 0, 0, props, out_link); -} - -static inline int -nm_platform_link_vxlan_add(NMPlatform * self, - const char * name, - const NMPlatformLnkVxlan *props, - const NMPlatformLink ** out_link) -{ - return nm_platform_link_add(self, NM_LINK_TYPE_VXLAN, name, 0, NULL, 0, 0, props, out_link); -} - -static inline int -nm_platform_link_6lowpan_add(NMPlatform * self, - const char * name, - int parent, - const NMPlatformLink **out_link) -{ - return nm_platform_link_add(self, - NM_LINK_TYPE_6LOWPAN, - name, - parent, - NULL, - 0, - 0, - NULL, - out_link); -} - -static inline int -nm_platform_link_ip6tnl_add(NMPlatform * self, - const char * name, - const NMPlatformLnkIp6Tnl *props, - const NMPlatformLink ** out_link) -{ - g_return_val_if_fail(props, -NME_BUG); - g_return_val_if_fail(!props->is_gre, -NME_BUG); - - return nm_platform_link_add(self, NM_LINK_TYPE_IP6TNL, name, 0, NULL, 0, 0, props, out_link); -} - -static inline int -nm_platform_link_ip6gre_add(NMPlatform * self, - const char * name, - const void * address, - size_t address_len, - const NMPlatformLnkIp6Tnl *props, - const NMPlatformLink ** out_link) -{ - g_return_val_if_fail(props, -NME_BUG); - g_return_val_if_fail(props->is_gre, -NME_BUG); - - return nm_platform_link_add(self, - props->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE, - name, - 0, - address, - address_len, - 0, - props, - out_link); -} - -static inline int -nm_platform_link_ipip_add(NMPlatform * self, - const char * name, - const NMPlatformLnkIpIp *props, - const NMPlatformLink ** out_link) -{ - g_return_val_if_fail(props, -NME_BUG); - - return nm_platform_link_add(self, NM_LINK_TYPE_IPIP, name, 0, NULL, 0, 0, props, out_link); -} - -static inline int -nm_platform_link_macsec_add(NMPlatform * self, - const char * name, - int parent, - const NMPlatformLnkMacsec *props, - const NMPlatformLink ** out_link) -{ - g_return_val_if_fail(props, -NME_BUG); - g_return_val_if_fail(parent > 0, -NME_BUG); - - return nm_platform_link_add(self, - NM_LINK_TYPE_MACSEC, - name, - parent, - NULL, - 0, - 0, - props, - out_link); -} - -static inline int -nm_platform_link_macvlan_add(NMPlatform * self, - const char * name, - int parent, - const NMPlatformLnkMacvlan *props, - const NMPlatformLink ** out_link) -{ - g_return_val_if_fail(props, -NME_BUG); - g_return_val_if_fail(parent > 0, -NME_BUG); - - return nm_platform_link_add(self, - props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN, - name, - parent, - NULL, - 0, - 0, - props, - out_link); -} - -gboolean nm_platform_link_delete(NMPlatform *self, int ifindex); - -gboolean nm_platform_link_set_netns(NMPlatform *self, int ifindex, int netns_fd); - -struct _NMDedupMultiHeadEntry; -struct _NMPLookup; -const struct _NMDedupMultiHeadEntry *nm_platform_lookup(NMPlatform * self, - const struct _NMPLookup *lookup); - -#define nm_platform_iter_obj_for_each(iter, self, lookup, obj) \ - for (nm_dedup_multi_iter_init((iter), nm_platform_lookup((self), (lookup))); \ - nm_platform_dedup_multi_iter_next_obj((iter), (obj), NMP_OBJECT_TYPE_UNKNOWN);) - -gboolean nm_platform_lookup_predicate_routes_main(const NMPObject *obj, gpointer user_data); -gboolean nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel(const NMPObject *obj, - gpointer user_data); - -GPtrArray *nm_platform_lookup_clone(NMPlatform * self, - const struct _NMPLookup *lookup, - NMPObjectPredicateFunc predicate, - gpointer user_data); - -/* convenience methods to lookup the link and access fields of NMPlatformLink. */ -int nm_platform_link_get_ifindex(NMPlatform *self, const char *name); -const char *nm_platform_link_get_name(NMPlatform *self, int ifindex); -NMLinkType nm_platform_link_get_type(NMPlatform *self, int ifindex); -gboolean nm_platform_link_is_software(NMPlatform *self, int ifindex); -int nm_platform_link_get_ifi_flags(NMPlatform *self, int ifindex, guint requested_flags); -gboolean nm_platform_link_is_up(NMPlatform *self, int ifindex); -gboolean nm_platform_link_is_connected(NMPlatform *self, int ifindex); -gboolean nm_platform_link_uses_arp(NMPlatform *self, int ifindex); -guint32 nm_platform_link_get_mtu(NMPlatform *self, int ifindex); -gboolean nm_platform_link_get_user_ipv6ll_enabled(NMPlatform *self, int ifindex); - -gconstpointer nm_platform_link_get_address(NMPlatform *self, int ifindex, size_t *length); - -int nm_platform_link_get_master(NMPlatform *self, int slave); - -gboolean nm_platform_link_can_assume(NMPlatform *self, int ifindex); - -gboolean nm_platform_link_get_unmanaged(NMPlatform *self, int ifindex, gboolean *unmanaged); -gboolean nm_platform_link_supports_slaves(NMPlatform *self, int ifindex); -const char *nm_platform_link_get_type_name(NMPlatform *self, int ifindex); - -gboolean nm_platform_link_refresh(NMPlatform *self, int ifindex); -void nm_platform_process_events(NMPlatform *self); - -const NMPlatformLink * -nm_platform_process_events_ensure_link(NMPlatform *self, int ifindex, const char *ifname); - -gboolean nm_platform_link_set_up(NMPlatform *self, int ifindex, gboolean *out_no_firmware); -gboolean nm_platform_link_set_down(NMPlatform *self, int ifindex); -gboolean nm_platform_link_set_arp(NMPlatform *self, int ifindex); -gboolean nm_platform_link_set_noarp(NMPlatform *self, int ifindex); - -const char *nm_platform_link_get_udi(NMPlatform *self, int ifindex); -const char *nm_platform_link_get_path(NMPlatform *self, int ifindex); - -struct udev_device *nm_platform_link_get_udev_device(NMPlatform *self, int ifindex); - -int nm_platform_link_set_user_ipv6ll_enabled(NMPlatform *self, int ifindex, gboolean enabled); -gboolean nm_platform_link_set_ipv6_token(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); - -gboolean -nm_platform_link_get_permanent_address(NMPlatform *self, int ifindex, guint8 *buf, size_t *length); -int nm_platform_link_set_address(NMPlatform *self, int ifindex, const void *address, size_t length); -int nm_platform_link_set_mtu(NMPlatform *self, int ifindex, guint32 mtu); -gboolean nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name); - -void nm_platform_link_set_sriov_params_async(NMPlatform * self, - int ifindex, - guint num_vfs, - int autoprobe, - NMPlatformAsyncCallback callback, - gpointer callback_data, - GCancellable * cancellable); - -gboolean -nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); -gboolean nm_platform_link_set_bridge_vlans(NMPlatform * self, - int ifindex, - gboolean on_master, - const NMPlatformBridgeVlan *const *vlans); - -char * nm_platform_link_get_physical_port_id(NMPlatform *self, int ifindex); -guint nm_platform_link_get_dev_id(NMPlatform *self, int ifindex); -gboolean nm_platform_link_get_wake_on_lan(NMPlatform *self, int ifindex); -gboolean nm_platform_link_get_driver_info(NMPlatform *self, - int ifindex, - char ** out_driver_name, - char ** out_driver_version, - char ** out_fw_version); - -gboolean nm_platform_link_supports_carrier_detect(NMPlatform *self, int ifindex); -gboolean nm_platform_link_supports_vlans(NMPlatform *self, int ifindex); -gboolean nm_platform_link_supports_sriov(NMPlatform *self, int ifindex); - -gboolean nm_platform_link_enslave(NMPlatform *self, int master, int slave); -gboolean nm_platform_link_release(NMPlatform *self, int master, int slave); - -gboolean nm_platform_sysctl_master_set_option(NMPlatform *self, - int ifindex, - const char *option, - const char *value); -char * nm_platform_sysctl_master_get_option(NMPlatform *self, int ifindex, const char *option); -gboolean nm_platform_sysctl_slave_set_option(NMPlatform *self, - int ifindex, - const char *option, - const char *value); -char * nm_platform_sysctl_slave_get_option(NMPlatform *self, int ifindex, const char *option); - -const NMPObject *nm_platform_link_get_lnk(NMPlatform * self, - int ifindex, - NMLinkType link_type, - const NMPlatformLink **out_link); -const NMPlatformLnkBridge * -nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkGre * -nm_platform_link_get_lnk_gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkGre * -nm_platform_link_get_lnk_gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkIp6Tnl * -nm_platform_link_get_lnk_ip6tnl(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkIp6Tnl * -nm_platform_link_get_lnk_ip6gre(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkIp6Tnl * -nm_platform_link_get_lnk_ip6gretap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkIpIp * -nm_platform_link_get_lnk_ipip(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkInfiniband * -nm_platform_link_get_lnk_infiniband(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkIpIp * -nm_platform_link_get_lnk_ipip(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkMacsec * -nm_platform_link_get_lnk_macsec(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkMacvlan * -nm_platform_link_get_lnk_macvlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkMacvlan * -nm_platform_link_get_lnk_macvtap(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkSit * -nm_platform_link_get_lnk_sit(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkTun * -nm_platform_link_get_lnk_tun(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkVlan * -nm_platform_link_get_lnk_vlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkVrf * -nm_platform_link_get_lnk_vrf(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkVxlan * -nm_platform_link_get_lnk_vxlan(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); -const NMPlatformLnkWireGuard * -nm_platform_link_get_lnk_wireguard(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); - -gboolean nm_platform_link_vlan_set_ingress_map(NMPlatform *self, int ifindex, int from, int to); -gboolean nm_platform_link_vlan_set_egress_map(NMPlatform *self, int ifindex, int from, int to); -gboolean nm_platform_link_vlan_change(NMPlatform * self, - int ifindex, - NMVlanFlags flags_mask, - NMVlanFlags flags_set, - gboolean ingress_reset_all, - const NMVlanQosMapping *ingress_map, - gsize n_ingress_map, - gboolean egress_reset_all, - const NMVlanQosMapping *egress_map, - gsize n_egress_map); - -int nm_platform_link_infiniband_add(NMPlatform * self, - int parent, - int p_key, - const NMPlatformLink **out_link); -int nm_platform_link_infiniband_delete(NMPlatform *self, int parent, int p_key); -gboolean nm_platform_link_infiniband_get_properties(NMPlatform * self, - int ifindex, - int * parent, - int * p_key, - const char **mode); - -gboolean nm_platform_link_veth_get_properties(NMPlatform *self, int ifindex, int *out_peer_ifindex); -gboolean nm_platform_link_tun_get_properties(NMPlatform * self, - int ifindex, - NMPlatformLnkTun *out_properties); - -gboolean -nm_platform_wifi_get_capabilities(NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); -gboolean nm_platform_wifi_get_bssid(NMPlatform *self, int ifindex, guint8 *bssid); -guint32 nm_platform_wifi_get_frequency(NMPlatform *self, int ifindex); -int nm_platform_wifi_get_quality(NMPlatform *self, int ifindex); -guint32 nm_platform_wifi_get_rate(NMPlatform *self, int ifindex); -NM80211Mode nm_platform_wifi_get_mode(NMPlatform *self, int ifindex); -void nm_platform_wifi_set_mode(NMPlatform *self, int ifindex, NM80211Mode mode); -void nm_platform_wifi_set_powersave(NMPlatform *self, int ifindex, guint32 powersave); -guint32 nm_platform_wifi_find_frequency(NMPlatform *self, int ifindex, const guint32 *freqs); -void nm_platform_wifi_indicate_addressing_running(NMPlatform *self, int ifindex, gboolean running); -NMSettingWirelessWakeOnWLan nm_platform_wifi_get_wake_on_wlan(NMPlatform *self, int ifindex); -gboolean -nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl); - -guint32 nm_platform_mesh_get_channel(NMPlatform *self, int ifindex); -gboolean nm_platform_mesh_set_channel(NMPlatform *self, int ifindex, guint32 channel); -gboolean nm_platform_mesh_set_ssid(NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); - -guint16 nm_platform_wpan_get_pan_id(NMPlatform *self, int ifindex); -gboolean nm_platform_wpan_set_pan_id(NMPlatform *self, int ifindex, guint16 pan_id); -guint16 nm_platform_wpan_get_short_addr(NMPlatform *self, int ifindex); -gboolean nm_platform_wpan_set_short_addr(NMPlatform *self, int ifindex, guint16 short_addr); -gboolean nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page, guint8 channel); - -void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); -const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr); - -const NMPlatformIP4Address *nm_platform_ip4_address_get(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address); - -int nm_platform_link_sit_add(NMPlatform * self, - const char * name, - const NMPlatformLnkSit *props, - const NMPlatformLink ** out_link); -int nm_platform_link_tun_add(NMPlatform * self, - const char * name, - const NMPlatformLnkTun *props, - const NMPlatformLink ** out_link, - int * out_fd); -gboolean nm_platform_link_6lowpan_get_properties(NMPlatform *self, int ifindex, int *out_parent); - -int -nm_platform_link_wireguard_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link); - -int nm_platform_link_wireguard_change(NMPlatform * self, - int ifindex, - const NMPlatformLnkWireGuard * lnk_wireguard, - const struct _NMPWireGuardPeer * peers, - const NMPlatformWireGuardChangePeerFlags *peer_flags, - guint peers_len, - NMPlatformWireGuardChangeFlags change_flags); - -const NMPlatformIP6Address * -nm_platform_ip6_address_get(NMPlatform *self, int ifindex, const struct in6_addr *address); - -gboolean nm_platform_object_delete(NMPlatform *self, const NMPObject *route); - -gboolean nm_platform_ip4_address_add(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address, - in_addr_t broadcast_address, - guint32 lifetime, - guint32 preferred_lft, - guint32 flags, - const char *label); -gboolean nm_platform_ip6_address_add(NMPlatform * self, - int ifindex, - struct in6_addr address, - guint8 plen, - struct in6_addr peer_address, - guint32 lifetime, - guint32 preferred_lft, - guint32 flags); -gboolean nm_platform_ip4_address_delete(NMPlatform *self, - int ifindex, - in_addr_t address, - guint8 plen, - in_addr_t peer_address); -gboolean -nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); - -gboolean nm_platform_ip_address_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray * known_addresses, - GPtrArray * addresses_prune); - -GPtrArray *nm_platform_ip_address_get_prune_list(NMPlatform *self, - int addr_family, - int ifindex, - gboolean exclude_ipv6_temporary_addrs); - -static inline gboolean -_nm_platform_ip_address_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray * known_addresses, - gboolean full_sync) -{ - gs_unref_ptrarray GPtrArray *addresses_prune = NULL; - - addresses_prune = nm_platform_ip_address_get_prune_list(self, addr_family, ifindex, !full_sync); - return nm_platform_ip_address_sync(self, - addr_family, - ifindex, - known_addresses, - addresses_prune); -} - -static inline gboolean -nm_platform_ip4_address_sync(NMPlatform *self, int ifindex, GPtrArray *known_addresses) -{ - return _nm_platform_ip_address_sync(self, AF_INET, ifindex, known_addresses, TRUE); -} - -static inline gboolean -nm_platform_ip6_address_sync(NMPlatform *self, - int ifindex, - GPtrArray * known_addresses, - gboolean full_sync) -{ - return _nm_platform_ip_address_sync(self, AF_INET6, ifindex, known_addresses, full_sync); -} - -gboolean nm_platform_ip_address_flush(NMPlatform *self, int addr_family, int ifindex); - -static inline gconstpointer -nm_platform_ip_address_get_peer_address(int addr_family, const NMPlatformIPAddress *addr) -{ - nm_assert_addr_family(addr_family); - nm_assert(addr); - - if (NM_IS_IPv4(addr_family)) - return &((NMPlatformIP4Address *) addr)->peer_address; - return &((NMPlatformIP6Address *) addr)->peer_address; -} - -void nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route); - -static inline guint32 -nm_platform_ip4_route_get_effective_metric(const NMPlatformIP4Route *r) -{ - nm_assert(r); - nm_assert(!r->metric_any || r->metric == 0); - - return r->metric_any ? NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4 : r->metric; -} - -static inline guint32 -nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r) -{ - nm_assert(r); - nm_assert(!r->metric_any || r->metric == 0); - - return r->metric_any ? NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6 : r->metric; -} - -static inline guint32 -nm_platform_ip_route_get_effective_table(const NMPlatformIPRoute *r) -{ - nm_assert(r); - nm_assert(!r->table_any || r->table_coerced == 0); - - return r->table_any ? 254u /* RT_TABLE_MAIN */ - : nm_platform_route_table_uncoerce(r->table_coerced, TRUE); -} - -static inline gconstpointer -nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route) -{ - nm_assert_addr_family(addr_family); - nm_assert(route); - - if (NM_IS_IPv4(addr_family)) - return &((NMPlatformIP4Route *) route)->gateway; - return &((NMPlatformIP6Route *) route)->gateway; -} - -int nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route); -int nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); -int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); - -GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform * self, - int addr_family, - int ifindex, - NMIPRouteTableSyncMode route_table_sync); - -gboolean nm_platform_ip_route_sync(NMPlatform *self, - int addr_family, - int ifindex, - GPtrArray * routes, - GPtrArray * routes_prune, - GPtrArray **out_temporary_not_available); - -gboolean nm_platform_ip_route_flush(NMPlatform *self, int addr_family, int ifindex); - -int nm_platform_ip_route_get(NMPlatform * self, - int addr_family, - gconstpointer address, - int oif_ifindex, - NMPObject ** out_route); - -int nm_platform_routing_rule_add(NMPlatform * self, - NMPNlmFlags flags, - const NMPlatformRoutingRule *routing_rule); - -int nm_platform_qdisc_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc); -gboolean nm_platform_qdisc_sync(NMPlatform *self, int ifindex, GPtrArray *known_qdiscs); - -int nm_platform_tfilter_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter); -gboolean nm_platform_tfilter_sync(NMPlatform *self, int ifindex, GPtrArray *known_tfilters); - -const char *nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len); -const char *nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len); -const char *nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len); -const char * -nm_platform_lnk_infiniband_to_string(const NMPlatformLnkInfiniband *lnk, char *buf, gsize len); -const char *nm_platform_lnk_ip6tnl_to_string(const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len); -const char *nm_platform_lnk_ipip_to_string(const NMPlatformLnkIpIp *lnk, char *buf, gsize len); -const char *nm_platform_lnk_macsec_to_string(const NMPlatformLnkMacsec *lnk, char *buf, gsize len); -const char * -nm_platform_lnk_macvlan_to_string(const NMPlatformLnkMacvlan *lnk, char *buf, gsize len); -const char *nm_platform_lnk_sit_to_string(const NMPlatformLnkSit *lnk, char *buf, gsize len); -const char *nm_platform_lnk_tun_to_string(const NMPlatformLnkTun *lnk, char *buf, gsize len); -const char *nm_platform_lnk_vlan_to_string(const NMPlatformLnkVlan *lnk, char *buf, gsize len); -const char *nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf, gsize len); -const char *nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len); -const char * -nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len); -const char * -nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf, gsize len); -const char * -nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf, gsize len); -const char *nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len); -const char *nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len); -const char * -nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len); -const char *nm_platform_qdisc_to_string(const NMPlatformQdisc *qdisc, char *buf, gsize len); -const char *nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len); -const char *nm_platform_vf_to_string(const NMPlatformVF *vf, char *buf, gsize len); -const char * -nm_platform_bridge_vlan_to_string(const NMPlatformBridgeVlan *vlan, char *buf, gsize len); - -const char *nm_platform_vlan_qos_mapping_to_string(const char * name, - const NMVlanQosMapping *map, - gsize n_map, - char * buf, - gsize len); - -const char * -nm_platform_wireguard_peer_to_string(const struct _NMPWireGuardPeer *peer, char *buf, gsize len); - -int nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b); -int nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b); -int nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b); -int nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a, - const NMPlatformLnkInfiniband *b); -int nm_platform_lnk_ip6tnl_cmp(const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b); -int nm_platform_lnk_ipip_cmp(const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b); -int nm_platform_lnk_macsec_cmp(const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b); -int nm_platform_lnk_macvlan_cmp(const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b); -int nm_platform_lnk_sit_cmp(const NMPlatformLnkSit *a, const NMPlatformLnkSit *b); -int nm_platform_lnk_tun_cmp(const NMPlatformLnkTun *a, const NMPlatformLnkTun *b); -int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b); -int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b); -int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); -int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b); -int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); -int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); - -int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, - const NMPlatformIP4Address *a2); - -int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, - const NMPlatformIP6Address *a2, - gboolean prefer_temp); - -GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex); - -int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, - const NMPlatformIP4Route *b, - NMPlatformIPRouteCmpType cmp_type); -int nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, - const NMPlatformIP6Route *b, - NMPlatformIPRouteCmpType cmp_type); - -static inline int -nm_platform_ip4_route_cmp_full(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) -{ - return nm_platform_ip4_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - -static inline int -nm_platform_ip6_route_cmp_full(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) -{ - return nm_platform_ip6_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - -int nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a, - const NMPlatformRoutingRule *b, - NMPlatformRoutingRuleCmpType cmp_type); - -static inline int -nm_platform_routing_rule_cmp_full(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b) -{ - return nm_platform_routing_rule_cmp(a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); -} - -int nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b); -int nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle); -int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b); - -void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h); -void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h); -void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h); -void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, - NMPlatformIPRouteCmpType cmp_type, - NMHashState * h); -void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, - NMPlatformIPRouteCmpType cmp_type, - NMHashState * h); -void nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj, - NMPlatformRoutingRuleCmpType cmp_type, - NMHashState * h); -void nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h); -void nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h); -void nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h); -void nm_platform_lnk_ip6tnl_hash_update(const NMPlatformLnkIp6Tnl *obj, NMHashState *h); -void nm_platform_lnk_ipip_hash_update(const NMPlatformLnkIpIp *obj, NMHashState *h); -void nm_platform_lnk_macsec_hash_update(const NMPlatformLnkMacsec *obj, NMHashState *h); -void nm_platform_lnk_macvlan_hash_update(const NMPlatformLnkMacvlan *obj, NMHashState *h); -void nm_platform_lnk_sit_hash_update(const NMPlatformLnkSit *obj, NMHashState *h); -void nm_platform_lnk_tun_hash_update(const NMPlatformLnkTun *obj, NMHashState *h); -void nm_platform_lnk_vlan_hash_update(const NMPlatformLnkVlan *obj, NMHashState *h); -void nm_platform_lnk_vrf_hash_update(const NMPlatformLnkVrf *obj, NMHashState *h); -void nm_platform_lnk_vxlan_hash_update(const NMPlatformLnkVxlan *obj, NMHashState *h); -void nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NMHashState *h); - -void nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h); -void nm_platform_tfilter_hash_update(const NMPlatformTfilter *obj, NMHashState *h); - -#define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162) - -const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len); -const char *nm_platform_link_inet6_addrgenmode2str(guint8 mode, char *buf, gsize len); -const char *nm_platform_addr_flags2str(unsigned flags, char *buf, gsize len); -const char *nm_platform_route_scope2str(int scope, char *buf, gsize len); - -int nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b); - -gboolean nm_platform_ethtool_set_wake_on_lan(NMPlatform * self, - int ifindex, - NMSettingWiredWakeOnLan wol, - const char * wol_password); -gboolean nm_platform_ethtool_set_link_settings(NMPlatform * self, - int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex); -gboolean nm_platform_ethtool_get_link_settings(NMPlatform * self, - int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex); - -typedef struct _NMEthtoolFeatureStates NMEthtoolFeatureStates; - -NMEthtoolFeatureStates *nm_platform_ethtool_get_link_features(NMPlatform *self, int ifindex); -gboolean nm_platform_ethtool_set_features( - NMPlatform * self, - int ifindex, - const NMEthtoolFeatureStates *features, - const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */); - -typedef struct _NMEthtoolCoalesceState NMEthtoolCoalesceState; - -gboolean nm_platform_ethtool_get_link_coalesce(NMPlatform * self, - int ifindex, - NMEthtoolCoalesceState *coalesce); - -gboolean nm_platform_ethtool_set_coalesce(NMPlatform * self, - int ifindex, - const NMEthtoolCoalesceState *coalesce); - -typedef struct _NMEthtoolRingState NMEthtoolRingState; - -gboolean nm_platform_ethtool_get_link_ring(NMPlatform *self, int ifindex, NMEthtoolRingState *ring); - -gboolean -nm_platform_ethtool_set_ring(NMPlatform *self, int ifindex, const NMEthtoolRingState *ring); - -const char *nm_platform_link_duplex_type_to_string(NMPlatformLinkDuplexType duplex); - -void nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self, - int ifindex, - GPtrArray * ip4_dev_route_blacklist); - -struct _NMDedupMultiIndex *nm_platform_get_multi_idx(NMPlatform *self); - -/*****************************************************************************/ - -gboolean nm_platform_ip6_address_match(const NMPlatformIP6Address *addr, - NMPlatformMatchFlags match_flag); - -#endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c deleted file mode 100644 index 07f7a7c9..00000000 --- a/src/platform/nmp-netns.c +++ /dev/null @@ -1,764 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nmp-netns.h" - -#include <fcntl.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <pthread.h> - -/*****************************************************************************/ - -/* NOTE: NMPNetns and all code used here must be thread-safe! */ - -/* we may not call logging functions from the main-thread alone. Hence, we - * require locking from nm-logging. Indicate that by setting NM_THREAD_SAFE_ON_MAIN_THREAD - * to zero. */ -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 - -/*****************************************************************************/ - -#define PROC_SELF_NS_MNT "/proc/self/ns/mnt" -#define PROC_SELF_NS_NET "/proc/self/ns/net" - -#define _CLONE_NS_ALL ((int) (CLONE_NEWNS | CLONE_NEWNET)) -#define _CLONE_NS_ALL_V CLONE_NEWNS, CLONE_NEWNET - -static NM_UTILS_FLAGS2STR_DEFINE(_clone_ns_to_str, - int, - NM_UTILS_FLAGS2STR(CLONE_NEWNS, "mnt"), - NM_UTILS_FLAGS2STR(CLONE_NEWNET, "net"), ); - -static const char * -__ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len) -{ - const char *b = buf; - char bb[200]; - - nm_utils_strbuf_append_c(&buf, &len, '['); - if (ns_types & ~ns_types_already_set) { - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ~ns_types_already_set, bb, sizeof(bb))); - } - if (ns_types & ns_types_already_set) { - if (ns_types & ~ns_types_already_set) - nm_utils_strbuf_append_c(&buf, &len, '/'); - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ns_types_already_set, bb, sizeof(bb))); - } - nm_utils_strbuf_append_c(&buf, &len, ']'); - return b; -} -#define _ns_types_to_str(ns_types, ns_types_already_set, buf) \ - __ns_types_to_str(ns_types, ns_types_already_set, buf, sizeof(buf)) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG_PREFIX_NAME "netns" -#define _NMLOG(level, netns, ...) \ - G_STMT_START \ - { \ - NMLogLevel _level = (level); \ - \ - if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ - NMPNetns *_netns = (netns); \ - char _sbuf[20]; \ - \ - _nm_log(_level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \ - : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FD_NET, PROP_FD_MNT, ); - -typedef struct { - int fd_net; - int fd_mnt; -} NMPNetnsPrivate; - -struct _NMPNetns { - GObject parent; - NMPNetnsPrivate _priv; -}; - -struct _NMPNetnsClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE(NMPNetns, nmp_netns, G_TYPE_OBJECT); - -#define NMP_NETNS_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMPNetns, NMP_IS_NETNS) - -/*****************************************************************************/ - -typedef struct { - NMPNetns *netns; - int count; - int ns_types; -} NetnsInfo; - -static void _stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types); -static NMPNetns *_netns_new(GError **error); - -/*****************************************************************************/ - -static NMPNetns * -_netns_get(NetnsInfo *info) -{ - nm_assert(!info || NMP_IS_NETNS(info->netns)); - return info ? info->netns : NULL; -} - -/*****************************************************************************/ - -static _nm_thread_local GArray *_netns_stack = NULL; - -static void -_netns_stack_clear_cb(gpointer data) -{ - NetnsInfo *info = data; - - nm_assert(NMP_IS_NETNS(info->netns)); - g_object_unref(info->netns); -} - -static GArray * -_netns_stack_get_impl(void) -{ - gs_unref_object NMPNetns *netns = NULL; - gs_free_error GError *error = NULL; - pthread_key_t key; - GArray * s; - - s = g_array_new(FALSE, FALSE, sizeof(NetnsInfo)); - g_array_set_clear_func(s, _netns_stack_clear_cb); - _netns_stack = s; - - /* at the bottom of the stack we must try to create a netns instance - * that we never pop. It's the base to which we need to return. */ - netns = _netns_new(&error); - if (!netns) { - _LOGE(NULL, "failed to create initial netns: %s", error->message); - return s; - } - - /* we leak this instance inside the stack. */ - _stack_push(s, netns, _CLONE_NS_ALL); - - /* finally, register a destructor function to cleanup the array. If we fail - * to do so, we will leak NMPNetns instances (and their file descriptor) when the - * thread exits. */ - if (pthread_key_create(&key, (void (*)(void *)) g_array_unref) != 0) - _LOGE(NULL, "failure to initialize thread-local storage"); - else if (pthread_setspecific(key, s) != 0) - _LOGE(NULL, "failure to set thread-local storage"); - - return s; -} - -#define _netns_stack_get() \ - ({ \ - GArray *_s = _netns_stack; \ - \ - if (G_UNLIKELY(!_s)) \ - _s = _netns_stack_get_impl(); \ - _s; \ - }) - -/*****************************************************************************/ - -static NMPNetns * -_stack_current_netns(GArray *netns_stack, int ns_types) -{ - guint j; - - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to find the netns that has - * all @ns_types set. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - - if (NM_FLAGS_ALL(info->ns_types, ns_types)) - return info->netns; - } - - g_return_val_if_reached(NULL); -} - -static int -_stack_current_ns_types(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - const int ns_types_check[] = {_CLONE_NS_ALL_V}; - guint i, j; - int res = 0; - - nm_assert(netns); - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to check which of @ns_types - * are already set to @netns. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - if (info->netns != netns) { - ns_types = NM_FLAGS_UNSET(ns_types, info->ns_types); - continue; - } - - for (i = 0; i < G_N_ELEMENTS(ns_types_check); i++) { - if (NM_FLAGS_ANY(ns_types, ns_types_check[i]) - && NM_FLAGS_ANY(info->ns_types, ns_types_check[i])) { - res = NM_FLAGS_SET(res, ns_types_check[i]); - ns_types = NM_FLAGS_UNSET(ns_types, ns_types_check[i]); - } - } - } - - return res; -} - -static NetnsInfo * -_stack_peek(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - return NULL; -} - -static NetnsInfo * -_stack_bottom(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, 0); - return NULL; -} - -static void -_stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(NMP_IS_NETNS(netns)); - nm_assert(NM_FLAGS_ANY(ns_types, _CLONE_NS_ALL)); - nm_assert(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL)); - - g_array_set_size(netns_stack, netns_stack->len + 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - *info = (NetnsInfo){ - .netns = g_object_ref(netns), - .ns_types = ns_types, - .count = 1, - }; -} - -static void -_stack_pop(GArray *netns_stack) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(netns_stack->len > 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - - nm_assert(NMP_IS_NETNS(info->netns)); - nm_assert(info->count == 1); - - g_array_set_size(netns_stack, netns_stack->len - 1); -} - -static guint -_stack_size(GArray *netns_stack) -{ - nm_assert(netns_stack); - - return netns_stack->len; -} - -/*****************************************************************************/ - -static NMPNetns * -_netns_new(GError **error) -{ - NMPNetns *self; - int fd_net, fd_mnt; - int errsv; - - fd_net = open(PROC_SELF_NS_NET, O_RDONLY | O_CLOEXEC); - if (fd_net == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening netns: %s", - nm_strerror_native(errsv)); - errno = errsv; - return NULL; - } - - fd_mnt = open(PROC_SELF_NS_MNT, O_RDONLY | O_CLOEXEC); - if (fd_mnt == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening mntns: %s", - nm_strerror_native(errsv)); - nm_close(fd_net); - errno = errsv; - return NULL; - } - - self = g_object_new(NMP_TYPE_NETNS, NMP_NETNS_FD_NET, fd_net, NMP_NETNS_FD_MNT, fd_mnt, NULL); - - _LOGD(self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt); - - return self; -} - -static int -_setns(NMPNetns *self, int type) -{ - char buf[100]; - int fd; - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_assert(NM_IN_SET(type, _CLONE_NS_ALL_V)); - - fd = (type == CLONE_NEWNET) ? priv->fd_net : priv->fd_mnt; - - _LOGt(self, "set netns(%s, %d)", _ns_types_to_str(type, 0, buf), fd); - - return setns(fd, type); -} - -static gboolean -_netns_switch_push(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET) - && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - return FALSE; - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS) - && _setns(self, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - - /* try to fix the mess by returning to the previous netns. */ - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET)) { - self = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (self && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to restore netns: %s", nm_strerror_native(errsv)); - } - } - return FALSE; - } - - return TRUE; -} - -static gboolean -_netns_switch_pop(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - NMPNetns *current; - int success = TRUE; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNS); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - - return success; -} - -/*****************************************************************************/ - -int -nmp_netns_get_fd_net(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_net; -} - -int -nmp_netns_get_fd_mnt(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_mnt; -} - -/*****************************************************************************/ - -static gboolean -_nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - char sbuf[100]; - - info = _stack_peek(netns_stack); - g_return_val_if_fail(info, FALSE); - - if (info->netns == self && info->ns_types == ns_types) { - info->count++; - _LOGt(self, - "push#%u* %s (increase count to %d)", - _stack_size(netns_stack) - 1, - _ns_types_to_str(ns_types, ns_types, sbuf), - info->count); - return TRUE; - } - - _LOGD(self, - "push#%u %s", - _stack_size(netns_stack), - _ns_types_to_str(ns_types, _stack_current_ns_types(netns_stack, self, ns_types), sbuf)); - - if (!_netns_switch_push(netns_stack, self, ns_types)) - return FALSE; - - _stack_push(netns_stack, self, ns_types); - return TRUE; -} - -gboolean -nmp_netns_push(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - return _nmp_netns_push_type(self, _CLONE_NS_ALL); -} - -gboolean -nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL), FALSE); - - return _nmp_netns_push_type(self, ns_types == 0 ? _CLONE_NS_ALL : ns_types); -} - -NMPNetns * -nmp_netns_new(void) -{ - GArray * netns_stack = _netns_stack_get(); - NMPNetns * self; - int errsv; - GError * error = NULL; - unsigned long mountflags = 0; - - if (!_stack_peek(netns_stack)) { - /* there are no netns instances. We cannot create a new one - * (because after unshare we couldn't return to the original one). */ - errno = ENOTSUP; - return NULL; - } - - if (unshare(_CLONE_NS_ALL) != 0) { - errsv = errno; - _LOGE(NULL, "failed to create new net and mnt namespace: %s", nm_strerror_native(errsv)); - return NULL; - } - - if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount --make-rslave: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (umount2("/sys", MNT_DETACH) != 0) { - errsv = errno; - _LOGE(NULL, "failed umount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (access("/sys", W_OK) == -1) - mountflags = MS_RDONLY; - - if (mount("sysfs", "/sys", "sysfs", mountflags, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - self = _netns_new(&error); - if (!self) { - errsv = errno; - _LOGE(NULL, "failed to create netns after unshare: %s", error->message); - g_clear_error(&error); - goto err_out; - } - - _stack_push(netns_stack, self, _CLONE_NS_ALL); - - return self; -err_out: - _netns_switch_pop(netns_stack, NULL, _CLONE_NS_ALL); - errno = errsv; - return NULL; -} - -gboolean -nmp_netns_pop(NMPNetns *self) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - int ns_types; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - info = _stack_peek(netns_stack); - - g_return_val_if_fail(info, FALSE); - g_return_val_if_fail(info->netns == self, FALSE); - - if (info->count > 1) { - info->count--; - _LOGt(self, "pop#%u* (decrease count to %d)", _stack_size(netns_stack) - 1, info->count); - return TRUE; - } - g_return_val_if_fail(info->count == 1, FALSE); - - /* cannot pop the original netns. */ - g_return_val_if_fail(_stack_size(netns_stack) > 1, FALSE); - - _LOGD(self, "pop#%u", _stack_size(netns_stack) - 1); - - ns_types = info->ns_types; - - _stack_pop(netns_stack); - - return _netns_switch_pop(netns_stack, self, ns_types); -} - -NMPNetns * -nmp_netns_get_current(void) -{ - return _netns_get(_stack_peek(_netns_stack_get())); -} - -NMPNetns * -nmp_netns_get_initial(void) -{ - return _netns_get(_stack_bottom(_netns_stack_get())); -} - -gboolean -nmp_netns_is_initial(void) -{ - GArray *netns_stack = _netns_stack_get(); - - return (_netns_get(_stack_peek(netns_stack)) == _netns_get(_stack_bottom(netns_stack))); -} - -/*****************************************************************************/ - -gboolean -nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd) -{ - gs_free char * dirname = NULL; - int errsv; - int fd; - nm_auto_pop_netns NMPNetns *netns_pop = NULL; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (!nmp_netns_push_type(self, CLONE_NEWNET)) - return FALSE; - netns_pop = self; - - dirname = g_path_get_dirname(filename); - if (mkdir(dirname, 0) != 0) { - errsv = errno; - if (errsv != EEXIST) { - _LOGE(self, - "bind: failed to create directory %s: %s", - dirname, - nm_strerror_native(errsv)); - return FALSE; - } - } - - if ((fd = creat(filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to create %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - nm_close(fd); - - if (mount(PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) { - errsv = errno; - _LOGE(self, - "bind: failed to mount %s to %s: %s", - PROC_SELF_NS_NET, - filename, - nm_strerror_native(errsv)); - unlink(filename); - return FALSE; - } - - if (out_fd) { - if ((fd = open(filename, O_RDONLY | O_CLOEXEC)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to open %s: %s", filename, nm_strerror_native(errsv)); - umount2(filename, MNT_DETACH); - unlink(filename); - return FALSE; - } - *out_fd = fd; - } - - return TRUE; -} - -gboolean -nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename) -{ - int errsv; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (umount2(filename, MNT_DETACH) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unmount2 %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - if (unlink(filename) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unlink %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_FD_NET: - /* construct-only */ - priv->fd_net = g_value_get_int(value); - g_return_if_fail(priv->fd_net > 0); - break; - case PROP_FD_MNT: - /* construct-only */ - priv->fd_mnt = g_value_get_int(value); - g_return_if_fail(priv->fd_mnt > 0); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -nmp_netns_init(NMPNetns *self) -{} - -static void -dispose(GObject *object) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_close(priv->fd_net); - priv->fd_net = -1; - - nm_close(priv->fd_mnt); - priv->fd_mnt = -1; - - G_OBJECT_CLASS(nmp_netns_parent_class)->dispose(object); -} - -static void -nmp_netns_class_init(NMPNetnsClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->set_property = set_property; - object_class->dispose = dispose; - - obj_properties[PROP_FD_NET] = - g_param_spec_int(NMP_NETNS_FD_NET, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_FD_MNT] = - g_param_spec_int(NMP_NETNS_FD_MNT, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h deleted file mode 100644 index 2516a79e..00000000 --- a/src/platform/nmp-netns.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NMP_NETNS_UTILS_H__ -#define __NMP_NETNS_UTILS_H__ - -/*****************************************************************************/ - -#define NMP_TYPE_NETNS (nmp_netns_get_type()) -#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) -#define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMP_TYPE_NETNS, NMPNetnsClass)) -#define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMP_TYPE_NETNS)) -#define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMP_TYPE_NETNS)) -#define NMP_NETNS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NMP_TYPE_NETNS, NMPNetnsClass)) - -#define NMP_NETNS_FD_NET "fd-net" -#define NMP_NETNS_FD_MNT "fd-mnt" - -typedef struct _NMPNetnsClass NMPNetnsClass; - -GType nmp_netns_get_type(void); - -NMPNetns *nmp_netns_new(void); - -gboolean nmp_netns_push(NMPNetns *self); -gboolean nmp_netns_push_type(NMPNetns *self, int ns_types); -gboolean nmp_netns_pop(NMPNetns *self); - -NMPNetns *nmp_netns_get_current(void); -NMPNetns *nmp_netns_get_initial(void); -gboolean nmp_netns_is_initial(void); - -int nmp_netns_get_fd_net(NMPNetns *self); -int nmp_netns_get_fd_mnt(NMPNetns *self); - -static inline void -_nm_auto_pop_netns(NMPNetns **p) -{ - if (*p) { - int errsv = errno; - - nmp_netns_pop(*p); - errno = errsv; - } -} -#define nm_auto_pop_netns nm_auto(_nm_auto_pop_netns) - -gboolean nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd); -gboolean nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename); - -#endif /* __NMP_NETNS_UTILS_H__ */ diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c deleted file mode 100644 index 6eea732f..00000000 --- a/src/platform/nmp-object.c +++ /dev/null @@ -1,3457 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 - 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nmp-object.h" - -#include <unistd.h> -#include <linux/rtnetlink.h> -#include <linux/if.h> -#include <libudev.h> - -#include "nm-utils.h" -#include "nm-glib-aux/nm-secret-utils.h" - -#include "nm-core-utils.h" -#include "nm-platform-utils.h" - -#include "wifi/nm-wifi-utils.h" -#include "wpan/nm-wpan-utils.h" - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG(level, obj, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - const NMPObject *const __obj = (obj); \ - \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __obj, \ - (__obj ? NMP_OBJECT_GET_CLASS(__obj)->obj_type_name \ - : "???") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -typedef struct { - NMDedupMultiIdxType parent; - NMPCacheIdType cache_id_type; -} DedupMultiIdxType; - -struct _NMPCache { - /* the cache contains only one hash table for all object types, and similarly - * it contains only one NMMultiIndex. - * This works, because different object types don't ever compare equal and - * because their index ids also don't overlap. - * - * For routes and addresses, the cache contains an address if (and only if) the - * object was reported via netlink. - * For links, the cache contain a link if it was reported by either netlink - * or udev. That means, a link object can be alive, even if it was already - * removed via netlink. - * - * This effectively merges the udev-device cache into the NMPCache. - */ - - NMDedupMultiIndex *multi_idx; - - /* an idx_type entry for each NMP_CACHE_ID_TYPE. Note that NONE (zero) - * is skipped, so the index is shifted by one: idx_type[cache_id_type - 1]. - * - * Don't bother, use _idx_type_get() instead! */ - DedupMultiIdxType idx_types[NMP_CACHE_ID_TYPE_MAX]; - - gboolean use_udev; -}; - -/*****************************************************************************/ - -int -nm_sock_addr_union_cmp(const NMSockAddrUnion *a, const NMSockAddrUnion *b) -{ - nm_assert(!a || NM_IN_SET(a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6)); - nm_assert(!b || NM_IN_SET(b->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6)); - - NM_CMP_SELF(a, b); - - NM_CMP_FIELD(a, b, sa.sa_family); - switch (a->sa.sa_family) { - case AF_INET: - NM_CMP_DIRECT(ntohl(a->in.sin_addr.s_addr), ntohl(b->in.sin_addr.s_addr)); - NM_CMP_DIRECT(htons(a->in.sin_port), htons(b->in.sin_port)); - break; - case AF_INET6: - NM_CMP_DIRECT_IN6ADDR(&a->in6.sin6_addr, &b->in6.sin6_addr); - NM_CMP_DIRECT(htons(a->in6.sin6_port), htons(b->in6.sin6_port)); - NM_CMP_FIELD(a, b, in6.sin6_scope_id); - NM_CMP_FIELD(a, b, in6.sin6_flowinfo); - break; - } - return 0; -} - -void -nm_sock_addr_union_hash_update(const NMSockAddrUnion *a, NMHashState *h) -{ - if (!a) { - nm_hash_update_val(h, 1241364739u); - return; - } - - nm_assert(NM_IN_SET(a->sa.sa_family, AF_UNSPEC, AF_INET, AF_INET6)); - - switch (a->sa.sa_family) { - case AF_INET: - nm_hash_update_vals(h, a->in.sin_family, a->in.sin_addr.s_addr, a->in.sin_port); - return; - case AF_INET6: - nm_hash_update_vals(h, - a->in6.sin6_family, - a->in6.sin6_addr, - a->in6.sin6_port, - a->in6.sin6_scope_id, - a->in6.sin6_flowinfo); - return; - default: - nm_hash_update_val(h, a->sa.sa_family); - return; - } -} - -/** - * nm_sock_addr_union_cpy: - * @dst: the destination #NMSockAddrUnion. It will always be fully initialized, - * to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of - * error). - * @src: (allow-none): the source buffer with an sockaddr to copy. It may be unaligned in - * memory. If not %NULL, the buffer must be at least large enough to contain - * sa.sa_family, and then, depending on sa.sa_family, it must be large enough - * to hold struct sockaddr_in or struct sockaddr_in6. - * - * @dst will always be fully initialized (including setting all un-used bytes to zero). - */ -void -nm_sock_addr_union_cpy(NMSockAddrUnion *dst, - gconstpointer src /* unaligned (const NMSockAddrUnion *) */) -{ - struct sockaddr sa; - gsize src_len; - - nm_assert(dst); - - *dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC; - - if (!src) - return; - - memcpy(&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof(sa.sa_family)); - - if (sa.sa_family == AF_INET) - src_len = sizeof(struct sockaddr_in); - else if (sa.sa_family == AF_INET6) - src_len = sizeof(struct sockaddr_in6); - else - return; - - memcpy(dst, src, src_len); - nm_assert(dst->sa.sa_family == sa.sa_family); -} - -/** - * nm_sock_addr_union_cpy_untrusted: - * @dst: the destination #NMSockAddrUnion. It will always be fully initialized, - * to one of the address families AF_INET, AF_INET6, or AF_UNSPEC (in case of - * error). - * @src: the source buffer with an sockaddr to copy. It may be unaligned in - * memory. - * @src_len: the length of @src in bytes. - * - * The function requires @src_len to be either sizeof(struct sockaddr_in) or sizeof (struct sockaddr_in6). - * If that's the case, then @src will be interpreted as such structure (unaligned), and - * accessed. It will check sa.sa_family to match the expected sizes, and if it does, the - * struct will be copied. - * - * On any failure, @dst will be set to sa.sa_family AF_UNSPEC. - * @dst will always be fully initialized (including setting all un-used bytes to zero). - */ -void -nm_sock_addr_union_cpy_untrusted(NMSockAddrUnion *dst, - gconstpointer src /* unaligned (const NMSockAddrUnion *) */, - gsize src_len) -{ - int f_expected; - struct sockaddr sa; - - nm_assert(dst); - - *dst = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC; - - if (src_len == sizeof(struct sockaddr_in)) - f_expected = AF_INET; - else if (src_len == sizeof(struct sockaddr_in6)) - f_expected = AF_INET6; - else - return; - - memcpy(&sa.sa_family, &((struct sockaddr *) src)->sa_family, sizeof(sa.sa_family)); - - if (sa.sa_family != f_expected) - return; - - memcpy(dst, src, src_len); - nm_assert(dst->sa.sa_family == sa.sa_family); -} - -const char * -nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len) -{ - char s_addr[NM_UTILS_INET_ADDRSTRLEN]; - char s_scope_id[40]; - - if (!nm_utils_to_string_buffer_init_null(sa, &buf, &len)) - return buf; - - /* maybe we should use getnameinfo(), but here implement it ourself. - * - * We want to see the actual bytes for debugging (as we understand them), - * and now what getnameinfo() makes of it. Also, it's simpler this way. */ - - switch (sa->sa.sa_family) { - case AF_INET: - g_snprintf(buf, - len, - "%s:%u", - _nm_utils_inet4_ntop(sa->in.sin_addr.s_addr, s_addr), - (guint) htons(sa->in.sin_port)); - break; - case AF_INET6: - g_snprintf(buf, - len, - "[%s%s]:%u", - _nm_utils_inet6_ntop(&sa->in6.sin6_addr, s_addr), - (sa->in6.sin6_scope_id != 0 - ? nm_sprintf_buf(s_scope_id, "%u", sa->in6.sin6_scope_id) - : ""), - (guint) htons(sa->in6.sin6_port)); - break; - case AF_UNSPEC: - g_snprintf(buf, len, "unspec"); - break; - default: - g_snprintf(buf, len, "{addr-family:%u}", (unsigned) sa->sa.sa_family); - break; - } - - return buf; -} - -/*****************************************************************************/ - -static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class; - -static void -_idx_obj_id_hash_update(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj, - NMHashState * h) -{ - const NMPObject *o = (NMPObject *) obj; - - nm_assert(idx_type && idx_type->klass == &_dedup_multi_idx_type_class); - nm_assert(NMP_OBJECT_GET_TYPE(o) != NMP_OBJECT_TYPE_UNKNOWN); - - nmp_object_id_hash_update(o, h); -} - -static gboolean -_idx_obj_id_equal(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj_a, - const NMDedupMultiObj * obj_b) -{ - const NMPObject *o_a = (NMPObject *) obj_a; - const NMPObject *o_b = (NMPObject *) obj_b; - - nm_assert(idx_type && idx_type->klass == &_dedup_multi_idx_type_class); - nm_assert(NMP_OBJECT_GET_TYPE(o_a) != NMP_OBJECT_TYPE_UNKNOWN); - nm_assert(NMP_OBJECT_GET_TYPE(o_b) != NMP_OBJECT_TYPE_UNKNOWN); - - return nmp_object_id_equal(o_a, o_b); -} - -static guint -_idx_obj_part(const DedupMultiIdxType *idx_type, - const NMPObject * obj_a, - const NMPObject * obj_b, - NMHashState * h) -{ - NMPObjectType obj_type; - - /* the hash/equals functions are strongly related. So, keep them - * side-by-side and do it all in _idx_obj_part(). */ - - nm_assert(idx_type); - nm_assert(idx_type->parent.klass == &_dedup_multi_idx_type_class); - nm_assert(obj_a); - nm_assert(NMP_OBJECT_GET_TYPE(obj_a) != NMP_OBJECT_TYPE_UNKNOWN); - nm_assert(!obj_b || (NMP_OBJECT_GET_TYPE(obj_b) != NMP_OBJECT_TYPE_UNKNOWN)); - nm_assert(!h || !obj_b); - - switch (idx_type->cache_id_type) { - case NMP_CACHE_ID_TYPE_OBJECT_TYPE: - if (obj_b) - return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b); - if (h) { - nm_hash_update_vals(h, idx_type->cache_id_type, NMP_OBJECT_GET_TYPE(obj_a)); - } - return 1; - - case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME: - if (NMP_OBJECT_GET_TYPE(obj_a) != NMP_OBJECT_TYPE_LINK) { - /* first check, whether obj_a is suitable for this idx_type. - * If not, return 0 (which is correct for partitionable(), hash() and equal() - * functions. */ - if (h) - nm_hash_update_val(h, obj_a); - return 0; - } - if (obj_b) { - /* we are in equal() mode. Compare obj_b with obj_a. */ - return NMP_OBJECT_GET_TYPE(obj_b) == NMP_OBJECT_TYPE_LINK - && nm_streq(obj_a->link.name, obj_b->link.name); - } - if (h) { - nm_hash_update_val(h, idx_type->cache_id_type); - nm_hash_update_strarr(h, obj_a->link.name); - } - /* just return 1, to indicate that obj_a is partitionable by this idx_type. */ - return 1; - - case NMP_CACHE_ID_TYPE_DEFAULT_ROUTES: - if (!NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_a), - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE) - || !NM_PLATFORM_IP_ROUTE_IS_DEFAULT(&obj_a->ip_route) - || !nmp_object_is_visible(obj_a)) { - if (h) - nm_hash_update_val(h, obj_a); - return 0; - } - if (obj_b) { - return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b) - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT(&obj_b->ip_route) - && nmp_object_is_visible(obj_b); - } - if (h) { - nm_hash_update_vals(h, idx_type->cache_id_type, NMP_OBJECT_GET_TYPE(obj_a)); - } - return 1; - - case NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX: - if (!NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_a), - NMP_OBJECT_TYPE_IP4_ADDRESS, - NMP_OBJECT_TYPE_IP6_ADDRESS, - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER) - || !nmp_object_is_visible(obj_a)) { - if (h) - nm_hash_update_val(h, obj_a); - return 0; - } - nm_assert(NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_a)->ifindex > 0); - if (obj_b) { - return NMP_OBJECT_GET_TYPE(obj_a) == NMP_OBJECT_GET_TYPE(obj_b) - && NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_a)->ifindex - == NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj_b)->ifindex - && nmp_object_is_visible(obj_b); - } - if (h) { - nm_hash_update_vals(h, idx_type->cache_id_type, obj_a->obj_with_ifindex.ifindex); - } - return 1; - - case NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID: - obj_type = NMP_OBJECT_GET_TYPE(obj_a); - if (!NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) - || NMP_OBJECT_CAST_IP_ROUTE(obj_a)->ifindex <= 0) { - if (h) - nm_hash_update_val(h, obj_a); - return 0; - } - if (obj_b) { - return obj_type == NMP_OBJECT_GET_TYPE(obj_b) - && NMP_OBJECT_CAST_IP_ROUTE(obj_b)->ifindex > 0 - && (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE - ? (nm_platform_ip4_route_cmp(&obj_a->ip4_route, - &obj_b->ip4_route, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) - == 0) - : (nm_platform_ip6_route_cmp(&obj_a->ip6_route, - &obj_b->ip6_route, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID) - == 0)); - } - if (h) { - nm_hash_update_val(h, idx_type->cache_id_type); - if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) - nm_platform_ip4_route_hash_update(&obj_a->ip4_route, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, - h); - else - nm_platform_ip6_route_hash_update(&obj_a->ip6_route, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, - h); - } - return 1; - - case NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY: - obj_type = NMP_OBJECT_GET_TYPE(obj_a); - /* currently, only routing rules are supported for this cache-id-type. */ - if (obj_type != NMP_OBJECT_TYPE_ROUTING_RULE - || !NM_IN_SET(obj_a->routing_rule.addr_family, AF_INET, AF_INET6)) { - if (h) - nm_hash_update_val(h, obj_a); - return 0; - } - if (obj_b) { - return NMP_OBJECT_GET_TYPE(obj_b) == NMP_OBJECT_TYPE_ROUTING_RULE - && obj_a->routing_rule.addr_family == obj_b->routing_rule.addr_family; - } - if (h) { - nm_hash_update_vals(h, idx_type->cache_id_type, obj_a->routing_rule.addr_family); - } - return 1; - - case NMP_CACHE_ID_TYPE_NONE: - case __NMP_CACHE_ID_TYPE_MAX: - break; - } - nm_assert_not_reached(); - return 0; -} - -static gboolean -_idx_obj_partitionable(const NMDedupMultiIdxType *idx_type, const NMDedupMultiObj *obj) -{ - return _idx_obj_part((DedupMultiIdxType *) idx_type, (NMPObject *) obj, NULL, NULL) != 0; -} - -static void -_idx_obj_partition_hash_update(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj, - NMHashState * h) -{ - _idx_obj_part((DedupMultiIdxType *) idx_type, (NMPObject *) obj, NULL, h); -} - -static gboolean -_idx_obj_partition_equal(const NMDedupMultiIdxType *idx_type, - const NMDedupMultiObj * obj_a, - const NMDedupMultiObj * obj_b) -{ - return _idx_obj_part((DedupMultiIdxType *) idx_type, - (NMPObject *) obj_a, - (NMPObject *) obj_b, - NULL); -} - -static const NMDedupMultiIdxTypeClass _dedup_multi_idx_type_class = { - .idx_obj_id_hash_update = _idx_obj_id_hash_update, - .idx_obj_id_equal = _idx_obj_id_equal, - .idx_obj_partitionable = _idx_obj_partitionable, - .idx_obj_partition_hash_update = _idx_obj_partition_hash_update, - .idx_obj_partition_equal = _idx_obj_partition_equal, -}; - -static void -_dedup_multi_idx_type_init(DedupMultiIdxType *idx_type, NMPCacheIdType cache_id_type) -{ - nm_dedup_multi_idx_type_init((NMDedupMultiIdxType *) idx_type, &_dedup_multi_idx_type_class); - idx_type->cache_id_type = cache_id_type; -} - -/*****************************************************************************/ - -static void -_vlan_xgress_qos_mappings_hash_update(guint n_map, const NMVlanQosMapping *map, NMHashState *h) -{ - /* ensure no padding. */ - G_STATIC_ASSERT(sizeof(NMVlanQosMapping) == 2 * sizeof(guint32)); - - nm_hash_update_val(h, n_map); - if (n_map) - nm_hash_update(h, map, n_map * sizeof(*map)); -} - -static int -_vlan_xgress_qos_mappings_cmp(guint n_map, - const NMVlanQosMapping *map1, - const NMVlanQosMapping *map2) -{ - guint i; - - for (i = 0; i < n_map; i++) { - if (map1[i].from != map2[i].from) - return map1[i].from < map2[i].from ? -1 : 1; - if (map1[i].to != map2[i].to) - return map1[i].to < map2[i].to ? -1 : 1; - } - return 0; -} - -static void -_vlan_xgress_qos_mappings_cpy(guint * dst_n_map, - NMVlanQosMapping ** dst_map, - guint src_n_map, - const NMVlanQosMapping *src_map) -{ - if (src_n_map == 0) { - nm_clear_g_free(dst_map); - *dst_n_map = 0; - } else if (src_n_map != *dst_n_map - || _vlan_xgress_qos_mappings_cmp(src_n_map, *dst_map, src_map) != 0) { - nm_clear_g_free(dst_map); - *dst_n_map = src_n_map; - if (src_n_map > 0) - *dst_map = nm_memdup(src_map, sizeof(*src_map) * src_n_map); - } -} - -/*****************************************************************************/ - -static void -_wireguard_allowed_ip_hash_update(const NMPWireGuardAllowedIP *ip, NMHashState *h) -{ - nm_hash_update_vals(h, ip->family, ip->mask); - - if (ip->family == AF_INET) - nm_hash_update_val(h, ip->addr.addr4); - else if (ip->family == AF_INET6) - nm_hash_update_val(h, ip->addr.addr6); -} - -static int -_wireguard_allowed_ip_cmp(const NMPWireGuardAllowedIP *a, const NMPWireGuardAllowedIP *b) -{ - NM_CMP_SELF(a, b); - - NM_CMP_FIELD(a, b, family); - NM_CMP_FIELD(a, b, mask); - - if (a->family == AF_INET) - NM_CMP_FIELD(a, b, addr.addr4); - else if (a->family == AF_INET6) - NM_CMP_FIELD_IN6ADDR(a, b, addr.addr6); - - return 0; -} - -static void -_wireguard_peer_hash_update(const NMPWireGuardPeer *peer, NMHashState *h) -{ - guint i; - - nm_hash_update(h, peer->public_key, sizeof(peer->public_key)); - nm_hash_update(h, peer->preshared_key, sizeof(peer->preshared_key)); - nm_hash_update_vals(h, - peer->persistent_keepalive_interval, - peer->allowed_ips_len, - peer->rx_bytes, - peer->tx_bytes, - peer->last_handshake_time.tv_sec, - peer->last_handshake_time.tv_nsec); - - nm_sock_addr_union_hash_update(&peer->endpoint, h); - - for (i = 0; i < peer->allowed_ips_len; i++) - _wireguard_allowed_ip_hash_update(&peer->allowed_ips[i], h); -} - -static int -_wireguard_peer_cmp(const NMPWireGuardPeer *a, const NMPWireGuardPeer *b) -{ - guint i; - - NM_CMP_SELF(a, b); - - NM_CMP_FIELD(a, b, last_handshake_time.tv_sec); - NM_CMP_FIELD(a, b, last_handshake_time.tv_nsec); - NM_CMP_FIELD(a, b, rx_bytes); - NM_CMP_FIELD(a, b, tx_bytes); - NM_CMP_FIELD(a, b, allowed_ips_len); - NM_CMP_FIELD(a, b, persistent_keepalive_interval); - NM_CMP_FIELD(a, b, endpoint.sa.sa_family); - NM_CMP_FIELD_MEMCMP(a, b, public_key); - NM_CMP_FIELD_MEMCMP(a, b, preshared_key); - - NM_CMP_RETURN(nm_sock_addr_union_cmp(&a->endpoint, &b->endpoint)); - - for (i = 0; i < a->allowed_ips_len; i++) { - NM_CMP_RETURN(_wireguard_allowed_ip_cmp(&a->allowed_ips[i], &b->allowed_ips[i])); - } - - return 0; -} - -/*****************************************************************************/ - -static const char * -_link_get_driver(struct udev_device *udevice, const char *kind, int ifindex) -{ - const char *driver = NULL; - - nm_assert(kind == g_intern_string(kind)); - - if (udevice) { - driver = nmp_utils_udev_get_driver(udevice); - if (driver) - return driver; - } - - if (kind) - return kind; - - if (ifindex > 0) { - NMPUtilsEthtoolDriverInfo driver_info; - - if (nmp_utils_ethtool_get_driver_info(ifindex, &driver_info)) { - if (driver_info.driver[0]) - return g_intern_string(driver_info.driver); - } - } - - return "unknown"; -} - -void -_nmp_object_fixup_link_udev_fields(NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev) -{ - const char *driver = NULL; - gboolean initialized = FALSE; - NMPObject * obj; - - nm_assert(obj_orig || *obj_new); - nm_assert(obj_new); - nm_assert(!obj_orig || NMP_OBJECT_GET_TYPE(obj_orig) == NMP_OBJECT_TYPE_LINK); - nm_assert(!*obj_new || NMP_OBJECT_GET_TYPE(*obj_new) == NMP_OBJECT_TYPE_LINK); - - obj = *obj_new ?: obj_orig; - - /* The link contains internal fields that are combined by - * properties from netlink and udev. Update those properties */ - - /* When a link is not in netlink, its udev fields don't matter. */ - if (obj->_link.netlink.is_in_netlink) { - driver = _link_get_driver(obj->_link.udev.device, obj->link.kind, obj->link.ifindex); - if (obj->_link.udev.device) - initialized = TRUE; - else if (!use_udev) { - /* If we don't use udev, we immediately mark the link as initialized. - * - * For that, we consult @use_udev argument, that is cached via - * nmp_cache_use_udev_get(). It is on purpose not to test - * for a writable /sys on every call. A minor reason for that is - * performance, but the real reason is reproducibility. - * */ - initialized = TRUE; - } - } - - if (nm_streq0(obj->link.driver, driver) && obj->link.initialized == initialized) - return; - - if (!*obj_new) - obj = *obj_new = nmp_object_clone(obj, FALSE); - - obj->link.driver = driver; - obj->link.initialized = initialized; -} - -static void -_nmp_object_fixup_link_master_connected(NMPObject ** obj_new, - NMPObject * obj_orig, - const NMPCache *cache) -{ - NMPObject *obj; - - nm_assert(obj_orig || *obj_new); - nm_assert(obj_new); - nm_assert(!obj_orig || NMP_OBJECT_GET_TYPE(obj_orig) == NMP_OBJECT_TYPE_LINK); - nm_assert(!*obj_new || NMP_OBJECT_GET_TYPE(*obj_new) == NMP_OBJECT_TYPE_LINK); - - obj = *obj_new ?: obj_orig; - - if (nmp_cache_link_connected_needs_toggle(cache, obj, NULL, NULL)) { - if (!*obj_new) - obj = *obj_new = nmp_object_clone(obj, FALSE); - obj->link.connected = !obj->link.connected; - } -} - -/*****************************************************************************/ - -static void -_vt_cmd_obj_dispose_link(NMPObject *obj) -{ - if (obj->_link.udev.device) { - udev_device_unref(obj->_link.udev.device); - obj->_link.udev.device = NULL; - } - g_clear_object(&obj->_link.ext_data); - nmp_object_unref(obj->_link.netlink.lnk); -} - -static void -_vt_cmd_obj_dispose_lnk_vlan(NMPObject *obj) -{ - g_free((gpointer) obj->_lnk_vlan.ingress_qos_map); - g_free((gpointer) obj->_lnk_vlan.egress_qos_map); -} - -static void -_wireguard_clear(NMPObjectLnkWireGuard *lnk) -{ - guint i; - - nm_explicit_bzero(lnk->_public.private_key, sizeof(lnk->_public.private_key)); - for (i = 0; i < lnk->peers_len; i++) { - NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &lnk->peers[i]; - - nm_explicit_bzero(peer->preshared_key, sizeof(peer->preshared_key)); - } - g_free((gpointer) lnk->peers); - g_free((gpointer) lnk->_allowed_ips_buf); -} - -static void -_vt_cmd_obj_dispose_lnk_wireguard(NMPObject *obj) -{ - _wireguard_clear(&obj->_lnk_wireguard); -} - -static NMPObject * -_nmp_object_new_from_class(const NMPClass *klass) -{ - NMPObject *obj; - - nm_assert(klass); - nm_assert(klass->sizeof_data > 0); - nm_assert(klass->sizeof_public > 0 && klass->sizeof_public <= klass->sizeof_data); - - obj = g_slice_alloc0(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object)); - obj->_class = klass; - obj->parent._ref_count = 1; - return obj; -} - -NMPObject * -nmp_object_new(NMPObjectType obj_type, gconstpointer plobj) -{ - const NMPClass *klass = nmp_class_from_type(obj_type); - NMPObject * obj; - - obj = _nmp_object_new_from_class(klass); - if (plobj) - memcpy(&obj->object, plobj, klass->sizeof_public); - return obj; -} - -NMPObject * -nmp_object_new_link(int ifindex) -{ - NMPObject *obj; - - obj = nmp_object_new(NMP_OBJECT_TYPE_LINK, NULL); - obj->link.ifindex = ifindex; - return obj; -} - -/*****************************************************************************/ - -static void -_nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) -{ - nm_assert(obj); - nm_assert(klass); - - *obj = (NMPObject){ - .parent = - { - .klass = (const NMDedupMultiObjClass *) klass, - ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, - }, - }; -} - -static NMPObject * -_nmp_object_stackinit_from_type(NMPObject *obj, NMPObjectType obj_type) -{ - const NMPClass *klass; - - nm_assert(obj); - klass = nmp_class_from_type(obj_type); - nm_assert(klass); - - *obj = (NMPObject){ - .parent = - { - .klass = (const NMDedupMultiObjClass *) klass, - ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, - }, - }; - return obj; -} - -const NMPObject * -nmp_object_stackinit(NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj) -{ - const NMPClass *klass = nmp_class_from_type(obj_type); - - _nmp_object_stackinit_from_class(obj, klass); - if (plobj) - memcpy(&obj->object, plobj, klass->sizeof_public); - return obj; -} - -const NMPObject * -nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src) -{ - const NMPClass *klass; - - nm_assert(NMP_OBJECT_IS_VALID(src)); - nm_assert(obj); - - klass = NMP_OBJECT_GET_CLASS(src); - _nmp_object_stackinit_from_class(obj, klass); - if (klass->cmd_plobj_id_copy) - klass->cmd_plobj_id_copy(&obj->object, &src->object); - return obj; -} - -const NMPObject * -nmp_object_stackinit_id_link(NMPObject *obj, int ifindex) -{ - _nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_LINK); - obj->link.ifindex = ifindex; - return obj; -} - -const NMPObject * -nmp_object_stackinit_id_ip4_address(NMPObject *obj, - int ifindex, - guint32 address, - guint8 plen, - guint32 peer_address) -{ - _nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP4_ADDRESS); - obj->ip4_address.ifindex = ifindex; - obj->ip4_address.address = address; - obj->ip4_address.plen = plen; - obj->ip4_address.peer_address = peer_address; - return obj; -} - -const NMPObject * -nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in6_addr *address) -{ - _nmp_object_stackinit_from_type(obj, NMP_OBJECT_TYPE_IP6_ADDRESS); - obj->ip4_address.ifindex = ifindex; - if (address) - obj->ip6_address.address = *address; - return obj; -} - -/*****************************************************************************/ - -const char * -nmp_object_to_string(const NMPObject * obj, - NMPObjectToStringMode to_string_mode, - char * buf, - gsize buf_size) -{ - const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; - - if (!nm_utils_to_string_buffer_init_null(obj, &buf, &buf_size)) - return buf; - - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj), NULL); - - klass = NMP_OBJECT_GET_CLASS(obj); - - if (klass->cmd_obj_to_string) - return klass->cmd_obj_to_string(obj, to_string_mode, buf, buf_size); - - switch (to_string_mode) { - case NMP_OBJECT_TO_STRING_ID: - if (!klass->cmd_plobj_to_string_id) { - g_snprintf(buf, buf_size, "%p", obj); - return buf; - } - return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); - case NMP_OBJECT_TO_STRING_ALL: - g_snprintf( - buf, - buf_size, - "[%s,%p,%u,%calive,%cvisible; %s]", - klass->obj_type_name, - obj, - obj->parent._ref_count, - nmp_object_is_alive(obj) ? '+' : '-', - nmp_object_is_visible(obj) ? '+' : '-', - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf2, sizeof(buf2))); - return buf; - case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); - return buf; - default: - g_return_val_if_reached("ERROR"); - } -} - -static const char * -_vt_cmd_obj_to_string_link(const NMPObject * obj, - NMPObjectToStringMode to_string_mode, - char * buf, - gsize buf_size) -{ - const NMPClass *klass = NMP_OBJECT_GET_CLASS(obj); - char * b = buf; - - switch (to_string_mode) { - case NMP_OBJECT_TO_STRING_ID: - return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); - case NMP_OBJECT_TO_STRING_ALL: - nm_utils_strbuf_append(&b, - &buf_size, - "[%s,%p,%u,%calive,%cvisible,%cin-nl,%p; ", - klass->obj_type_name, - obj, - obj->parent._ref_count, - nmp_object_is_alive(obj) ? '+' : '-', - nmp_object_is_visible(obj) ? '+' : '-', - obj->_link.netlink.is_in_netlink ? '+' : '-', - obj->_link.udev.device); - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size); - nm_utils_strbuf_seek_end(&b, &buf_size); - if (obj->_link.netlink.lnk) { - nm_utils_strbuf_append_str(&b, &buf_size, "; "); - nmp_object_to_string(obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_ALL, b, buf_size); - nm_utils_strbuf_seek_end(&b, &buf_size); - } - nm_utils_strbuf_append_c(&b, &buf_size, ']'); - return buf; - case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size); - if (obj->_link.netlink.lnk) { - nm_utils_strbuf_seek_end(&b, &buf_size); - nm_utils_strbuf_append_str(&b, &buf_size, "; "); - nmp_object_to_string(obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_PUBLIC, b, buf_size); - } - return buf; - default: - g_return_val_if_reached("ERROR"); - } -} - -static const char * -_vt_cmd_obj_to_string_lnk_vlan(const NMPObject * obj, - NMPObjectToStringMode to_string_mode, - char * buf, - gsize buf_size) -{ - const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; - char * b; - gsize l; - - klass = NMP_OBJECT_GET_CLASS(obj); - - switch (to_string_mode) { - case NMP_OBJECT_TO_STRING_ID: - g_snprintf(buf, buf_size, "%p", obj); - return buf; - case NMP_OBJECT_TO_STRING_ALL: - - g_snprintf(buf, - buf_size, - "[%s,%p,%u,%calive,%cvisible; %s]", - klass->obj_type_name, - obj, - obj->parent._ref_count, - nmp_object_is_alive(obj) ? '+' : '-', - nmp_object_is_visible(obj) ? '+' : '-', - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2))); - return buf; - case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); - - b = buf; - l = strlen(b); - b += l; - buf_size -= l; - - if (obj->_lnk_vlan.n_ingress_qos_map) { - nm_platform_vlan_qos_mapping_to_string(" ingress-qos-map", - obj->_lnk_vlan.ingress_qos_map, - obj->_lnk_vlan.n_ingress_qos_map, - b, - buf_size); - l = strlen(b); - b += l; - buf_size -= l; - } - if (obj->_lnk_vlan.n_egress_qos_map) { - nm_platform_vlan_qos_mapping_to_string(" egress-qos-map", - obj->_lnk_vlan.egress_qos_map, - obj->_lnk_vlan.n_egress_qos_map, - b, - buf_size); - l = strlen(b); - b += l; - buf_size -= l; - } - - return buf; - default: - g_return_val_if_reached("ERROR"); - } -} - -static const char * -_vt_cmd_obj_to_string_lnk_wireguard(const NMPObject * obj, - NMPObjectToStringMode to_string_mode, - char * buf, - gsize buf_size) -{ - const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; - char * b; - guint i; - - klass = NMP_OBJECT_GET_CLASS(obj); - - switch (to_string_mode) { - case NMP_OBJECT_TO_STRING_ID: - g_snprintf(buf, buf_size, "%p", obj); - return buf; - case NMP_OBJECT_TO_STRING_ALL: - b = buf; - - nm_utils_strbuf_append( - &b, - &buf_size, - "[%s,%p,%u,%calive,%cvisible; %s" - "%s", - klass->obj_type_name, - obj, - obj->parent._ref_count, - nmp_object_is_alive(obj) ? '+' : '-', - nmp_object_is_visible(obj) ? '+' : '-', - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2)), - obj->_lnk_wireguard.peers_len > 0 ? " peers {" : ""); - - for (i = 0; i < obj->_lnk_wireguard.peers_len; i++) { - const NMPWireGuardPeer *peer = &obj->_lnk_wireguard.peers[i]; - - nm_utils_strbuf_append_str(&b, &buf_size, " { "); - nm_platform_wireguard_peer_to_string(peer, b, buf_size); - nm_utils_strbuf_seek_end(&b, &buf_size); - nm_utils_strbuf_append_str(&b, &buf_size, " }"); - } - if (obj->_lnk_wireguard.peers_len) - nm_utils_strbuf_append_str(&b, &buf_size, " }"); - - return buf; - case NMP_OBJECT_TO_STRING_PUBLIC: - NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, buf, buf_size); - - return buf; - default: - g_return_val_if_reached("ERROR"); - } -} - -#define _vt_cmd_plobj_to_string_id(type, plat_type, ...) \ - static const char *_vt_cmd_plobj_to_string_id_##type(const NMPlatformObject *_obj, \ - char * buf, \ - gsize buf_len) \ - { \ - plat_type *const obj = (plat_type *) _obj; \ - _nm_unused char buf1[NM_UTILS_INET_ADDRSTRLEN]; \ - _nm_unused char buf2[NM_UTILS_INET_ADDRSTRLEN]; \ - \ - g_snprintf(buf, buf_len, __VA_ARGS__); \ - return buf; \ - } \ - struct _dummy_struct_for_tailing_semicolon - -_vt_cmd_plobj_to_string_id(link, NMPlatformLink, "%d", obj->ifindex); - -_vt_cmd_plobj_to_string_id(ip4_address, - NMPlatformIP4Address, - "%d: %s/%d%s%s", - obj->ifindex, - _nm_utils_inet4_ntop(obj->address, buf1), - obj->plen, - obj->peer_address != obj->address ? "," : "", - obj->peer_address != obj->address ? _nm_utils_inet4_ntop( - nm_utils_ip4_address_clear_host_address(obj->peer_address, - obj->plen), - buf2) - : ""); - -_vt_cmd_plobj_to_string_id(ip6_address, - NMPlatformIP6Address, - "%d: %s", - obj->ifindex, - _nm_utils_inet6_ntop(&obj->address, buf1)); - -_vt_cmd_plobj_to_string_id(qdisc, NMPlatformQdisc, "%d: %d", obj->ifindex, obj->parent); - -_vt_cmd_plobj_to_string_id(tfilter, NMPlatformTfilter, "%d: %d", obj->ifindex, obj->parent); - -void -nmp_object_hash_update(const NMPObject *obj, NMHashState *h) -{ - const NMPClass *klass; - - g_return_if_fail(NMP_OBJECT_IS_VALID(obj)); - - klass = NMP_OBJECT_GET_CLASS(obj); - - nm_hash_update_val(h, klass->obj_type); - if (klass->cmd_obj_hash_update) - klass->cmd_obj_hash_update(obj, h); - else if (klass->cmd_plobj_hash_update) - klass->cmd_plobj_hash_update(&obj->object, h); - else - nm_hash_update_val(h, obj); -} - -static void -_vt_cmd_obj_hash_update_link(const NMPObject *obj, NMHashState *h) -{ - nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK); - - nm_platform_link_hash_update(&obj->link, h); - nm_hash_update_vals(h, - obj->_link.netlink.is_in_netlink, - obj->_link.wireguard_family_id, - obj->_link.udev.device); - if (obj->_link.netlink.lnk) - nmp_object_hash_update(obj->_link.netlink.lnk, h); -} - -static void -_vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, NMHashState *h) -{ - nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_VLAN); - - nm_platform_lnk_vlan_hash_update(&obj->lnk_vlan, h); - _vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_ingress_qos_map, - obj->_lnk_vlan.ingress_qos_map, - h); - _vlan_xgress_qos_mappings_hash_update(obj->_lnk_vlan.n_egress_qos_map, - obj->_lnk_vlan.egress_qos_map, - h); -} - -static void -_vt_cmd_obj_hash_update_lnk_wireguard(const NMPObject *obj, NMHashState *h) -{ - guint i; - - nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LNK_WIREGUARD); - - nm_platform_lnk_wireguard_hash_update(&obj->lnk_wireguard, h); - - nm_hash_update_val(h, obj->_lnk_wireguard.peers_len); - for (i = 0; i < obj->_lnk_wireguard.peers_len; i++) - _wireguard_peer_hash_update(&obj->_lnk_wireguard.peers[i], h); -} - -int -nmp_object_cmp(const NMPObject *obj1, const NMPObject *obj2) -{ - const NMPClass *klass1, *klass2; - - NM_CMP_SELF(obj1, obj2); - - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), -1); - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), 1); - - klass1 = NMP_OBJECT_GET_CLASS(obj1); - klass2 = NMP_OBJECT_GET_CLASS(obj2); - - if (klass1 != klass2) { - nm_assert(klass1->obj_type != klass2->obj_type); - return klass1->obj_type < klass2->obj_type ? -1 : 1; - } - - if (klass1->cmd_obj_cmp) - return klass1->cmd_obj_cmp(obj1, obj2); - return klass1->cmd_plobj_cmp(&obj1->object, &obj2->object); -} - -static int -_vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2) -{ - NM_CMP_RETURN(nm_platform_link_cmp(&obj1->link, &obj2->link)); - NM_CMP_DIRECT(obj1->_link.netlink.is_in_netlink, obj2->_link.netlink.is_in_netlink); - NM_CMP_RETURN(nmp_object_cmp(obj1->_link.netlink.lnk, obj2->_link.netlink.lnk)); - NM_CMP_DIRECT(obj1->_link.wireguard_family_id, obj2->_link.wireguard_family_id); - - if (obj1->_link.udev.device != obj2->_link.udev.device) { - if (!obj1->_link.udev.device) - return -1; - if (!obj2->_link.udev.device) - return 1; - - /* Only compare based on pointer values. That is ugly because it's not a - * stable sort order. - * - * Have this check as very last. */ - return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1; - } - - return 0; -} - -static int -_vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2) -{ - int c; - - c = nm_platform_lnk_vlan_cmp(&obj1->lnk_vlan, &obj2->lnk_vlan); - if (c) - return c; - - if (obj1->_lnk_vlan.n_ingress_qos_map != obj2->_lnk_vlan.n_ingress_qos_map) - return obj1->_lnk_vlan.n_ingress_qos_map < obj2->_lnk_vlan.n_ingress_qos_map ? -1 : 1; - if (obj1->_lnk_vlan.n_egress_qos_map != obj2->_lnk_vlan.n_egress_qos_map) - return obj1->_lnk_vlan.n_egress_qos_map < obj2->_lnk_vlan.n_egress_qos_map ? -1 : 1; - - c = _vlan_xgress_qos_mappings_cmp(obj1->_lnk_vlan.n_ingress_qos_map, - obj1->_lnk_vlan.ingress_qos_map, - obj2->_lnk_vlan.ingress_qos_map); - if (c) - return c; - c = _vlan_xgress_qos_mappings_cmp(obj1->_lnk_vlan.n_egress_qos_map, - obj1->_lnk_vlan.egress_qos_map, - obj2->_lnk_vlan.egress_qos_map); - - return c; -} - -static int -_vt_cmd_obj_cmp_lnk_wireguard(const NMPObject *obj1, const NMPObject *obj2) -{ - guint i; - - NM_CMP_RETURN(nm_platform_lnk_wireguard_cmp(&obj1->lnk_wireguard, &obj2->lnk_wireguard)); - - NM_CMP_FIELD(obj1, obj2, _lnk_wireguard.peers_len); - - for (i = 0; i < obj1->_lnk_wireguard.peers_len; i++) - NM_CMP_RETURN( - _wireguard_peer_cmp(&obj1->_lnk_wireguard.peers[i], &obj2->_lnk_wireguard.peers[i])); - - return 0; -} - -/* @src is a const object, which is not entirely correct for link types, where - * we increase the ref count for src->_link.udev.device. - * Hence, nmp_object_copy() can violate the const promise of @src. - * */ -void -nmp_object_copy(NMPObject *dst, const NMPObject *src, gboolean id_only) -{ - g_return_if_fail(NMP_OBJECT_IS_VALID(dst)); - g_return_if_fail(NMP_OBJECT_IS_VALID(src)); - g_return_if_fail(!NMP_OBJECT_IS_STACKINIT(dst)); - - if (src != dst) { - const NMPClass *klass = NMP_OBJECT_GET_CLASS(dst); - - g_return_if_fail(klass == NMP_OBJECT_GET_CLASS(src)); - - if (id_only) { - if (klass->cmd_plobj_id_copy) - klass->cmd_plobj_id_copy(&dst->object, &src->object); - } else if (klass->cmd_obj_copy) - klass->cmd_obj_copy(dst, src); - else - memcpy(&dst->object, &src->object, klass->sizeof_data); - } -} - -static void -_vt_cmd_obj_copy_link(NMPObject *dst, const NMPObject *src) -{ - if (dst->_link.udev.device != src->_link.udev.device) { - if (src->_link.udev.device) - udev_device_ref(src->_link.udev.device); - if (dst->_link.udev.device) - udev_device_unref(dst->_link.udev.device); - dst->_link.udev.device = src->_link.udev.device; - } - if (dst->_link.netlink.lnk != src->_link.netlink.lnk) { - if (src->_link.netlink.lnk) - nmp_object_ref(src->_link.netlink.lnk); - if (dst->_link.netlink.lnk) - nmp_object_unref(dst->_link.netlink.lnk); - dst->_link.netlink.lnk = src->_link.netlink.lnk; - } - if (dst->_link.ext_data != src->_link.ext_data) { - if (dst->_link.ext_data) - g_clear_object(&dst->_link.ext_data); - if (src->_link.ext_data) - dst->_link.ext_data = g_object_ref(src->_link.ext_data); - } - dst->_link = src->_link; -} - -static void -_vt_cmd_obj_copy_lnk_vlan(NMPObject *dst, const NMPObject *src) -{ - dst->lnk_vlan = src->lnk_vlan; - _vlan_xgress_qos_mappings_cpy( - &dst->_lnk_vlan.n_ingress_qos_map, - NM_UNCONST_PPTR(NMVlanQosMapping, &dst->_lnk_vlan.ingress_qos_map), - src->_lnk_vlan.n_ingress_qos_map, - src->_lnk_vlan.ingress_qos_map); - _vlan_xgress_qos_mappings_cpy(&dst->_lnk_vlan.n_egress_qos_map, - NM_UNCONST_PPTR(NMVlanQosMapping, &dst->_lnk_vlan.egress_qos_map), - src->_lnk_vlan.n_egress_qos_map, - src->_lnk_vlan.egress_qos_map); -} - -static void -_vt_cmd_obj_copy_lnk_wireguard(NMPObject *dst, const NMPObject *src) -{ - guint i; - - nm_assert(dst != src); - - _wireguard_clear(&dst->_lnk_wireguard); - - dst->_lnk_wireguard = src->_lnk_wireguard; - - dst->_lnk_wireguard.peers = nm_memdup(dst->_lnk_wireguard.peers, - sizeof(NMPWireGuardPeer) * dst->_lnk_wireguard.peers_len); - dst->_lnk_wireguard._allowed_ips_buf = - nm_memdup(dst->_lnk_wireguard._allowed_ips_buf, - sizeof(NMPWireGuardAllowedIP) * dst->_lnk_wireguard._allowed_ips_buf_len); - - /* all the peers' pointers point into the buffer. They need to be readjusted. */ - for (i = 0; i < dst->_lnk_wireguard.peers_len; i++) { - NMPWireGuardPeer *peer = (NMPWireGuardPeer *) &dst->_lnk_wireguard.peers[i]; - - if (peer->allowed_ips_len == 0) { - nm_assert(!peer->allowed_ips); - continue; - } - nm_assert(dst->_lnk_wireguard._allowed_ips_buf_len > 0); - nm_assert(src->_lnk_wireguard._allowed_ips_buf); - nm_assert(peer->allowed_ips >= src->_lnk_wireguard._allowed_ips_buf); - nm_assert( - &peer->allowed_ips[peer->allowed_ips_len] - <= &src->_lnk_wireguard._allowed_ips_buf[src->_lnk_wireguard._allowed_ips_buf_len]); - - peer->allowed_ips = - &dst->_lnk_wireguard - ._allowed_ips_buf[peer->allowed_ips - src->_lnk_wireguard._allowed_ips_buf]; - } - - nm_assert(nmp_object_equal(src, dst)); -} - -#define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \ - static void _vt_cmd_plobj_id_copy_##type(NMPlatformObject *_dst, const NMPlatformObject *_src) \ - { \ - plat_type *const dst = (plat_type *) _dst; \ - const plat_type *const src = (const plat_type *) _src; \ - { \ - cmd \ - } \ - } \ - struct _dummy_struct_for_tailing_semicolon - -_vt_cmd_plobj_id_copy(link, NMPlatformLink, { dst->ifindex = src->ifindex; }); - -_vt_cmd_plobj_id_copy(ip4_address, NMPlatformIP4Address, { - dst->ifindex = src->ifindex; - dst->plen = src->plen; - dst->address = src->address; - dst->peer_address = src->peer_address; -}); - -_vt_cmd_plobj_id_copy(ip6_address, NMPlatformIP6Address, { - dst->ifindex = src->ifindex; - dst->address = src->address; -}); - -_vt_cmd_plobj_id_copy(ip4_route, NMPlatformIP4Route, { - *dst = *src; - nm_assert(nm_platform_ip4_route_cmp(dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); -}); - -_vt_cmd_plobj_id_copy(ip6_route, NMPlatformIP6Route, { - *dst = *src; - nm_assert(nm_platform_ip6_route_cmp(dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); -}); - -_vt_cmd_plobj_id_copy(routing_rule, NMPlatformRoutingRule, { - *dst = *src; - nm_assert(nm_platform_routing_rule_cmp(dst, src, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); -}); - -/* Uses internally nmp_object_copy(), hence it also violates the const - * promise for @obj. - * */ -NMPObject * -nmp_object_clone(const NMPObject *obj, gboolean id_only) -{ - NMPObject *dst; - - if (!obj) - return NULL; - - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj), NULL); - - dst = _nmp_object_new_from_class(NMP_OBJECT_GET_CLASS(obj)); - nmp_object_copy(dst, obj, id_only); - return dst; -} - -int -nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) -{ - const NMPClass *klass, *klass2; - - NM_CMP_SELF(obj1, obj2); - - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj1), FALSE); - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj2), FALSE); - - klass = NMP_OBJECT_GET_CLASS(obj1); - nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); - - klass2 = NMP_OBJECT_GET_CLASS(obj2); - nm_assert(klass); - if (klass != klass2) { - nm_assert(klass2); - NM_CMP_DIRECT(klass->obj_type, klass2->obj_type); - /* resort to pointer comparison */ - NM_CMP_DIRECT_PTR(klass, klass2); - return 0; - } - - if (!klass->cmd_plobj_id_cmp) { - /* the klass doesn't implement ID cmp(). That means, different objects - * never compare equal, but the cmp() according to their pointer value. */ - NM_CMP_DIRECT_PTR(obj1, obj2); - return 0; - } - - return klass->cmd_plobj_id_cmp(&obj1->object, &obj2->object); -} - -#define _vt_cmd_plobj_id_cmp(type, plat_type, cmd) \ - static int _vt_cmd_plobj_id_cmp_##type(const NMPlatformObject *_obj1, \ - const NMPlatformObject *_obj2) \ - { \ - const plat_type *const obj1 = (const plat_type *) _obj1; \ - const plat_type *const obj2 = (const plat_type *) _obj2; \ - \ - NM_CMP_SELF(obj1, obj2); \ - { \ - cmd; \ - } \ - return 0; \ - } \ - struct _dummy_struct_for_tailing_semicolon - -_vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); }); - -_vt_cmd_plobj_id_cmp(ip4_address, NMPlatformIP4Address, { - NM_CMP_FIELD(obj1, obj2, ifindex); - NM_CMP_FIELD(obj1, obj2, plen); - NM_CMP_FIELD(obj1, obj2, address); - /* for IPv4 addresses, you can add the same local address with differing peer-address - * (IFA_ADDRESS), provided that their net-part differs. */ - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(obj1->peer_address, obj2->peer_address, obj1->plen); -}); - -_vt_cmd_plobj_id_cmp(ip6_address, NMPlatformIP6Address, { - NM_CMP_FIELD(obj1, obj2, ifindex); - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - NM_CMP_FIELD_IN6ADDR(obj1, obj2, address); -}); - -_vt_cmd_plobj_id_cmp(qdisc, NMPlatformQdisc, { - NM_CMP_FIELD(obj1, obj2, ifindex); - NM_CMP_FIELD(obj1, obj2, parent); -}); - -_vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, { - NM_CMP_FIELD(obj1, obj2, ifindex); - NM_CMP_FIELD(obj1, obj2, handle); -}); - -static int -_vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) -{ - return nm_platform_ip4_route_cmp((NMPlatformIP4Route *) obj1, - (NMPlatformIP4Route *) obj2, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); -} - -static int -_vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) -{ - return nm_platform_ip6_route_cmp((NMPlatformIP6Route *) obj1, - (NMPlatformIP6Route *) obj2, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); -} - -static int -_vt_cmd_plobj_id_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2) -{ - return nm_platform_routing_rule_cmp((NMPlatformRoutingRule *) obj1, - (NMPlatformRoutingRule *) obj2, - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); -} - -void -nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h) -{ - const NMPClass *klass; - - g_return_if_fail(NMP_OBJECT_IS_VALID(obj)); - - klass = NMP_OBJECT_GET_CLASS(obj); - - nm_assert(!klass->cmd_plobj_id_hash_update == !klass->cmd_plobj_id_cmp); - - if (!klass->cmd_plobj_id_hash_update) { - /* The klass doesn't implement ID compare. It means, to use pointer - * equality. */ - nm_hash_update_val(h, obj); - return; - } - - nm_hash_update_val(h, klass->obj_type); - klass->cmd_plobj_id_hash_update(&obj->object, h); -} - -guint -nmp_object_id_hash(const NMPObject *obj) -{ - NMHashState h; - - if (!obj) - return nm_hash_static(914932607u); - - nm_hash_init(&h, 914932607u); - nmp_object_id_hash_update(obj, &h); - return nm_hash_complete(&h); -} - -#define _vt_cmd_plobj_id_hash_update(type, plat_type, cmd) \ - static void _vt_cmd_plobj_id_hash_update_##type(const NMPlatformObject *_obj, NMHashState *h) \ - { \ - const plat_type *const obj = (const plat_type *) _obj; \ - { \ - cmd; \ - } \ - } \ - struct _dummy_struct_for_tailing_semicolon - -_vt_cmd_plobj_id_hash_update(link, NMPlatformLink, { nm_hash_update_val(h, obj->ifindex); }); - -_vt_cmd_plobj_id_hash_update(ip4_address, NMPlatformIP4Address, { - nm_hash_update_vals( - h, - obj->ifindex, - obj->plen, - obj->address, - /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ - nm_utils_ip4_address_clear_host_address(obj->peer_address, obj->plen)); -}); - -_vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, { - nm_hash_update_vals( - h, - obj->ifindex, - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - obj->address); -}); - -_vt_cmd_plobj_id_hash_update(ip4_route, NMPlatformIP4Route, { - nm_platform_ip4_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); -}); - -_vt_cmd_plobj_id_hash_update(ip6_route, NMPlatformIP6Route, { - nm_platform_ip6_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); -}); - -_vt_cmd_plobj_id_hash_update(routing_rule, NMPlatformRoutingRule, { - nm_platform_routing_rule_hash_update(obj, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, h); -}); - -_vt_cmd_plobj_id_hash_update(qdisc, NMPlatformQdisc, { - nm_hash_update_vals(h, obj->ifindex, obj->parent); -}); - -_vt_cmd_plobj_id_hash_update(tfilter, NMPlatformTfilter, { - nm_hash_update_vals(h, obj->ifindex, obj->handle); -}); - -static void -_vt_cmd_plobj_hash_update_ip4_route(const NMPlatformObject *obj, NMHashState *h) -{ - return nm_platform_ip4_route_hash_update((const NMPlatformIP4Route *) obj, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, - h); -} - -static void -_vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h) -{ - return nm_platform_ip6_route_hash_update((const NMPlatformIP6Route *) obj, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, - h); -} - -static void -_vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState *h) -{ - return nm_platform_routing_rule_hash_update((const NMPlatformRoutingRule *) obj, - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, - h); -} - -guint -nmp_object_indirect_id_hash(gconstpointer a) -{ - const NMPObject *const *p_obj = a; - - return nmp_object_id_hash(*p_obj); -} - -gboolean -nmp_object_indirect_id_equal(gconstpointer a, gconstpointer b) -{ - const NMPObject *const *p_obj_a = a; - const NMPObject *const *p_obj_b = b; - - return nmp_object_id_equal(*p_obj_a, *p_obj_b); -} - -/*****************************************************************************/ - -gboolean -nmp_object_is_alive(const NMPObject *obj) -{ - const NMPClass *klass; - - /* for convenience, allow NULL. */ - if (!obj) - return FALSE; - - klass = NMP_OBJECT_GET_CLASS(obj); - return !klass->cmd_obj_is_alive || klass->cmd_obj_is_alive(obj); -} - -static gboolean -_vt_cmd_obj_is_alive_link(const NMPObject *obj) -{ - return NMP_OBJECT_CAST_LINK(obj)->ifindex > 0 - && (obj->_link.netlink.is_in_netlink || obj->_link.udev.device); -} - -static gboolean -_vt_cmd_obj_is_alive_ipx_address(const NMPObject *obj) -{ - return NMP_OBJECT_CAST_IP_ADDRESS(obj)->ifindex > 0; -} - -static gboolean -_vt_cmd_obj_is_alive_ipx_route(const NMPObject *obj) -{ - /* We want to ignore routes that are RTM_F_CLONED but we still - * let nmp_object_from_nl() create such route objects, instead of - * returning NULL right away. - * - * The idea is, that if we have the same route (according to its id) - * in the cache with !RTM_F_CLONED, an update that changes the route - * to be RTM_F_CLONED must remove the instance. - * - * If nmp_object_from_nl() would just return NULL, we couldn't look - * into the cache to see if it contains a route that now disappears - * (because it changed to be cloned). - * - * Instead we create a dead object, and nmp_cache_update_netlink() - * will remove the old version of the update. - **/ - return NMP_OBJECT_CAST_IP_ROUTE(obj)->ifindex > 0 - && !NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, RTM_F_CLONED); -} - -static gboolean -_vt_cmd_obj_is_alive_routing_rule(const NMPObject *obj) -{ - return NM_IN_SET(obj->routing_rule.addr_family, AF_INET, AF_INET6); -} - -static gboolean -_vt_cmd_obj_is_alive_qdisc(const NMPObject *obj) -{ - return NMP_OBJECT_CAST_QDISC(obj)->ifindex > 0; -} - -static gboolean -_vt_cmd_obj_is_alive_tfilter(const NMPObject *obj) -{ - return NMP_OBJECT_CAST_TFILTER(obj)->ifindex > 0; -} - -gboolean -nmp_object_is_visible(const NMPObject *obj) -{ - const NMPClass *klass; - - /* for convenience, allow NULL. */ - if (!obj) - return FALSE; - - klass = NMP_OBJECT_GET_CLASS(obj); - - /* a dead object is never visible. */ - if (klass->cmd_obj_is_alive && !klass->cmd_obj_is_alive(obj)) - return FALSE; - - return !klass->cmd_obj_is_visible || klass->cmd_obj_is_visible(obj); -} - -static gboolean -_vt_cmd_obj_is_visible_link(const NMPObject *obj) -{ - return obj->_link.netlink.is_in_netlink && obj->link.name[0]; -} - -/*****************************************************************************/ - -static const guint8 _supported_cache_ids_object[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX, - 0, -}; - -static const guint8 _supported_cache_ids_link[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, - 0, -}; - -static const guint8 _supported_cache_ids_ipx_address[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX, - 0, -}; - -static const guint8 _supported_cache_ids_ipx_route[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX, - NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, - NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, - 0, -}; - -static const guint8 _supported_cache_ids_routing_rules[] = { - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, - 0, -}; - -/*****************************************************************************/ - -static void -_vt_dedup_obj_destroy(NMDedupMultiObj *obj) -{ - NMPObject * o = (NMPObject *) obj; - const NMPClass *klass; - - nm_assert(o->parent._ref_count == 0); - nm_assert(!o->parent._multi_idx); - - klass = o->_class; - if (klass->cmd_obj_dispose) - klass->cmd_obj_dispose(o); - g_slice_free1(klass->sizeof_data + G_STRUCT_OFFSET(NMPObject, object), o); -} - -static const NMDedupMultiObj * -_vt_dedup_obj_clone(const NMDedupMultiObj *obj) -{ - return (const NMDedupMultiObj *) nmp_object_clone((const NMPObject *) obj, FALSE); -} - -#define DEDUP_MULTI_OBJ_CLASS_INIT() \ - { \ - .obj_clone = _vt_dedup_obj_clone, .obj_destroy = _vt_dedup_obj_destroy, \ - .obj_full_hash_update = \ - (void (*)(const NMDedupMultiObj *obj, NMHashState *h)) nmp_object_hash_update, \ - .obj_full_equal = (gboolean(*)(const NMDedupMultiObj *obj_a, \ - const NMDedupMultiObj *obj_b)) nmp_object_equal, \ - } - -/*****************************************************************************/ - -static NMDedupMultiIdxType * -_idx_type_get(const NMPCache *cache, NMPCacheIdType cache_id_type) -{ - nm_assert(cache); - nm_assert(cache_id_type > NMP_CACHE_ID_TYPE_NONE); - nm_assert(cache_id_type <= NMP_CACHE_ID_TYPE_MAX); - nm_assert((int) cache_id_type - 1 >= 0); - nm_assert((int) cache_id_type - 1 < G_N_ELEMENTS(cache->idx_types)); - - return (NMDedupMultiIdxType *) &cache->idx_types[cache_id_type - 1]; -} - -gboolean -nmp_cache_use_udev_get(const NMPCache *cache) -{ - g_return_val_if_fail(cache, TRUE); - - return cache->use_udev; -} - -/*****************************************************************************/ - -gboolean -nmp_cache_link_connected_for_slave(int ifindex_master, const NMPObject *slave) -{ - nm_assert(NMP_OBJECT_GET_TYPE(slave) == NMP_OBJECT_TYPE_LINK); - - return ifindex_master > 0 && slave->link.master == ifindex_master && slave->link.connected - && nmp_object_is_visible(slave); -} - -/** - * nmp_cache_link_connected_needs_toggle: - * @cache: the platform cache - * @master: the link object, that is checked whether its connected property - * needs to be toggled. - * @potential_slave: (allow-none): an additional link object that is treated - * as if it was inside @cache. If given, it shaddows a link in the cache - * with the same ifindex. - * @ignore_slave: (allow-none): if set, the check will pretend that @ignore_slave - * is not in the cache. - * - * NMPlatformLink has two connected flags: (master->link.flags&IFF_LOWER_UP) (as reported - * from netlink) and master->link.connected. For bond and bridge master, kernel reports - * those links as IFF_LOWER_UP if they have no slaves attached. We want to present instead - * a combined @connected flag that shows masters without slaves as down. - * - * Check if the connected flag of @master should be toggled according to the content - * of @cache (including @potential_slave). - * - * Returns: %TRUE, if @master->link.connected should be flipped/toggled. - **/ -gboolean -nmp_cache_link_connected_needs_toggle(const NMPCache * cache, - const NMPObject *master, - const NMPObject *potential_slave, - const NMPObject *ignore_slave) -{ - gboolean is_lower_up = FALSE; - - if (!master || NMP_OBJECT_GET_TYPE(master) != NMP_OBJECT_TYPE_LINK || master->link.ifindex <= 0 - || !nmp_object_is_visible(master) - || !NM_IN_SET(master->link.type, NM_LINK_TYPE_BRIDGE, NM_LINK_TYPE_BOND)) - return FALSE; - - /* if native IFF_LOWER_UP is down, link.connected must also be down - * regardless of the slaves. */ - if (!NM_FLAGS_HAS(master->link.n_ifi_flags, IFF_LOWER_UP)) - return !!master->link.connected; - - if (potential_slave && NMP_OBJECT_GET_TYPE(potential_slave) != NMP_OBJECT_TYPE_LINK) - potential_slave = NULL; - - if (potential_slave - && nmp_cache_link_connected_for_slave(master->link.ifindex, potential_slave)) - is_lower_up = TRUE; - else { - NMPLookup lookup; - NMDedupMultiIter iter; - const NMPlatformLink *link = NULL; - - nmp_cache_iter_for_each_link ( - &iter, - nmp_cache_lookup(cache, nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK)), - &link) { - const NMPObject *obj = NMP_OBJECT_UP_CAST((NMPlatformObject *) link); - - if ((!potential_slave || potential_slave->link.ifindex != link->ifindex) - && ignore_slave != obj - && nmp_cache_link_connected_for_slave(master->link.ifindex, obj)) { - is_lower_up = TRUE; - break; - } - } - } - return !!master->link.connected != is_lower_up; -} - -/** - * nmp_cache_link_connected_needs_toggle_by_ifindex: - * @cache: - * @master_ifindex: the ifindex of a potential master that should be checked - * whether it needs toggling. - * @potential_slave: (allow-none): passed to nmp_cache_link_connected_needs_toggle(). - * It considers @potential_slave as being inside the cache, replacing an existing - * link with the same ifindex. - * @ignore_slave: (allow-onne): passed to nmp_cache_link_connected_needs_toggle(). - * - * The flag obj->link.connected depends on the state of other links in the - * @cache. See also nmp_cache_link_connected_needs_toggle(). Given an ifindex - * of a master, check if the cache contains such a master link that needs - * toggling of the connected flag. - * - * Returns: NULL if there is no master link with ifindex @master_ifindex that should be toggled. - * Otherwise, return the link object from inside the cache with the given ifindex. - * The connected flag of that master should be toggled. - */ -const NMPObject * -nmp_cache_link_connected_needs_toggle_by_ifindex(const NMPCache * cache, - int master_ifindex, - const NMPObject *potential_slave, - const NMPObject *ignore_slave) -{ - const NMPObject *master; - - if (master_ifindex > 0) { - master = nmp_cache_lookup_link(cache, master_ifindex); - if (nmp_cache_link_connected_needs_toggle(cache, master, potential_slave, ignore_slave)) - return master; - } - return NULL; -} - -/*****************************************************************************/ - -static const NMDedupMultiEntry * -_lookup_entry_with_idx_type(const NMPCache * cache, - NMPCacheIdType cache_id_type, - const NMPObject *obj) -{ - const NMDedupMultiEntry *entry; - - nm_assert(cache); - nm_assert(NMP_OBJECT_IS_VALID(obj)); - - entry = - nm_dedup_multi_index_lookup_obj(cache->multi_idx, _idx_type_get(cache, cache_id_type), obj); - nm_assert(!entry - || (NMP_OBJECT_IS_VALID(entry->obj) - && NMP_OBJECT_GET_CLASS(entry->obj) == NMP_OBJECT_GET_CLASS(obj))); - return entry; -} - -static const NMDedupMultiEntry * -_lookup_entry(const NMPCache *cache, const NMPObject *obj) -{ - return _lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj); -} - -const NMDedupMultiEntry * -nmp_cache_lookup_entry_with_idx_type(const NMPCache * cache, - NMPCacheIdType cache_id_type, - const NMPObject *obj) -{ - g_return_val_if_fail(cache, NULL); - g_return_val_if_fail(obj, NULL); - g_return_val_if_fail(cache_id_type > NMP_CACHE_ID_TYPE_NONE - && cache_id_type <= NMP_CACHE_ID_TYPE_MAX, - NULL); - - return _lookup_entry_with_idx_type(cache, cache_id_type, obj); -} - -const NMDedupMultiEntry * -nmp_cache_lookup_entry(const NMPCache *cache, const NMPObject *obj) -{ - g_return_val_if_fail(cache, NULL); - g_return_val_if_fail(obj, NULL); - - return _lookup_entry(cache, obj); -} - -const NMDedupMultiEntry * -nmp_cache_lookup_entry_link(const NMPCache *cache, int ifindex) -{ - NMPObject obj_needle; - - g_return_val_if_fail(cache, NULL); - g_return_val_if_fail(ifindex > 0, NULL); - - nmp_object_stackinit_id_link(&obj_needle, ifindex); - return _lookup_entry(cache, &obj_needle); -} - -const NMPObject * -nmp_cache_lookup_obj(const NMPCache *cache, const NMPObject *obj) -{ - return nm_dedup_multi_entry_get_obj(nmp_cache_lookup_entry(cache, obj)); -} - -const NMPObject * -nmp_cache_lookup_link(const NMPCache *cache, int ifindex) -{ - return nm_dedup_multi_entry_get_obj(nmp_cache_lookup_entry_link(cache, ifindex)); -} - -/*****************************************************************************/ - -const NMDedupMultiHeadEntry * -nmp_cache_lookup_all(const NMPCache * cache, - NMPCacheIdType cache_id_type, - const NMPObject *select_obj) -{ - nm_assert(cache); - nm_assert(NMP_OBJECT_IS_VALID(select_obj)); - - return nm_dedup_multi_index_lookup_head(cache->multi_idx, - _idx_type_get(cache, cache_id_type), - select_obj); -} - -static const NMPLookup * -_L(const NMPLookup *lookup) -{ -#if NM_MORE_ASSERTS - DedupMultiIdxType idx_type; - - nm_assert(lookup); - _dedup_multi_idx_type_init(&idx_type, lookup->cache_id_type); - nm_assert( - idx_type.parent.klass->idx_obj_partitionable((NMDedupMultiIdxType *) &idx_type, - (NMDedupMultiObj *) &lookup->selector_obj)); -#endif - return lookup; -} - -const NMPLookup * -nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type) -{ - nm_assert(lookup); - - switch (obj_type) { - case NMP_OBJECT_TYPE_LINK: - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - case NMP_OBJECT_TYPE_ROUTING_RULE: - case NMP_OBJECT_TYPE_QDISC: - case NMP_OBJECT_TYPE_TFILTER: - _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type); - lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE; - return _L(lookup); - default: - nm_assert_not_reached(); - return NULL; - } -} - -const NMPLookup * -nmp_lookup_init_link_by_ifname(NMPLookup *lookup, const char *ifname) -{ - NMPObject *o; - - nm_assert(lookup); - nm_assert(ifname); - nm_assert(strlen(ifname) < IFNAMSIZ); - - o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_LINK); - if (g_strlcpy(o->link.name, ifname, sizeof(o->link.name)) >= sizeof(o->link.name)) - g_return_val_if_reached(NULL); - lookup->cache_id_type = NMP_CACHE_ID_TYPE_LINK_BY_IFNAME; - return _L(lookup); -} - -const NMPLookup * -nmp_lookup_init_object(NMPLookup *lookup, NMPObjectType obj_type, int ifindex) -{ - NMPObject *o; - - nm_assert(lookup); - nm_assert(NM_IN_SET(obj_type, - NMP_OBJECT_TYPE_IP4_ADDRESS, - NMP_OBJECT_TYPE_IP6_ADDRESS, - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER)); - - if (ifindex <= 0) { - return nmp_lookup_init_obj_type(lookup, obj_type); - } - - o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type); - o->obj_with_ifindex.ifindex = ifindex; - lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX; - return _L(lookup); -} - -const NMPLookup * -nmp_lookup_init_route_default(NMPLookup *lookup, NMPObjectType obj_type) -{ - NMPObject *o; - - nm_assert(lookup); - nm_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type); - o->ip_route.ifindex = 1; - lookup->cache_id_type = NMP_CACHE_ID_TYPE_DEFAULT_ROUTES; - return _L(lookup); -} - -const NMPLookup * -nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj) -{ - const NMPlatformIP4Route *r4; - const NMPlatformIP6Route *r6; - - nm_assert(lookup); - - switch (NMP_OBJECT_GET_TYPE(obj)) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - r4 = NMP_OBJECT_CAST_IP4_ROUTE(obj); - return nmp_lookup_init_ip4_route_by_weak_id(lookup, - r4->network, - r4->plen, - r4->metric, - r4->tos); - case NMP_OBJECT_TYPE_IP6_ROUTE: - r6 = NMP_OBJECT_CAST_IP6_ROUTE(obj); - return nmp_lookup_init_ip6_route_by_weak_id(lookup, - &r6->network, - r6->plen, - r6->metric, - &r6->src, - r6->src_plen); - default: - nm_assert_not_reached(); - return NULL; - } -} - -const NMPLookup * -nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup, - in_addr_t network, - guint plen, - guint32 metric, - guint8 tos) -{ - NMPObject *o; - - nm_assert(lookup); - - o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE); - o->ip4_route.ifindex = 1; - o->ip4_route.plen = plen; - o->ip4_route.metric = metric; - if (network) - o->ip4_route.network = network; - o->ip4_route.tos = tos; - lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; - return _L(lookup); -} - -const NMPLookup * -nmp_lookup_init_ip6_route_by_weak_id(NMPLookup * lookup, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen) -{ - NMPObject *o; - - nm_assert(lookup); - - o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE); - o->ip6_route.ifindex = 1; - o->ip6_route.plen = plen; - o->ip6_route.metric = metric; - if (network) - o->ip6_route.network = *network; - if (src) - o->ip6_route.src = *src; - o->ip6_route.src_plen = src_plen; - lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; - return _L(lookup); -} - -const NMPLookup * -nmp_lookup_init_object_by_addr_family(NMPLookup *lookup, NMPObjectType obj_type, int addr_family) -{ - NMPObject *o; - - nm_assert(lookup); - nm_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)); - - if (addr_family == AF_UNSPEC) - return nmp_lookup_init_obj_type(lookup, obj_type); - - nm_assert_addr_family(addr_family); - o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type); - NMP_OBJECT_CAST_ROUTING_RULE(o)->addr_family = addr_family; - lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY; - return _L(lookup); -} - -/*****************************************************************************/ - -GArray * -nmp_cache_lookup_to_array(const NMDedupMultiHeadEntry *head_entry, - NMPObjectType obj_type, - gboolean visible_only) -{ - const NMPClass * klass = nmp_class_from_type(obj_type); - NMDedupMultiIter iter; - const NMPObject *o; - GArray * array; - - g_return_val_if_fail(klass, NULL); - - array = g_array_sized_new(FALSE, FALSE, klass->sizeof_public, head_entry ? head_entry->len : 0); - nmp_cache_iter_for_each (&iter, head_entry, &o) { - nm_assert(NMP_OBJECT_GET_CLASS(o) == klass); - if (visible_only && !nmp_object_is_visible(o)) - continue; - g_array_append_vals(array, &o->object, 1); - } - return array; -} - -/*****************************************************************************/ - -const NMPObject * -nmp_cache_lookup_link_full(const NMPCache * cache, - int ifindex, - const char * ifname, - gboolean visible_only, - NMLinkType link_type, - NMPObjectMatchFn match_fn, - gpointer user_data) -{ - NMPObject obj_needle; - const NMPObject * obj; - NMDedupMultiIter iter; - const NMDedupMultiHeadEntry *head_entry; - const NMPlatformLink * link = NULL; - NMPLookup lookup; - - if (ifindex > 0) { - obj = nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&obj_needle, ifindex)); - - if (!obj || (visible_only && !nmp_object_is_visible(obj)) - || (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) - || (ifname && strcmp(obj->link.name, ifname)) - || (match_fn && !match_fn(obj, user_data))) - return NULL; - return obj; - } else if (!ifname && !match_fn) - return NULL; - else { - const NMPObject *obj_best = NULL; - - if (ifname) { - if (strlen(ifname) >= IFNAMSIZ) - return NULL; - nmp_lookup_init_link_by_ifname(&lookup, ifname); - } else - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK); - - head_entry = nmp_cache_lookup(cache, &lookup); - nmp_cache_iter_for_each_link (&iter, head_entry, &link) { - obj = NMP_OBJECT_UP_CAST(link); - - if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type) - continue; - if (visible_only && !nmp_object_is_visible(obj)) - continue; - if (match_fn && !match_fn(obj, user_data)) - continue; - - /* if there are multiple candidates, prefer the visible ones. */ - if (visible_only || nmp_object_is_visible(obj)) - return obj; - if (!obj_best) - obj_best = obj; - } - return obj_best; - } -} - -/*****************************************************************************/ - -static NMDedupMultiIdxMode -_obj_get_add_mode(const NMPObject *obj) -{ - /* new objects are usually appended to the list. Except for - * addresses, which are prepended during `ip address add`. - * - * Actually, for routes it is more complicated, because depending on - * `ip route append`, `ip route replace`, `ip route prepend`, the object - * will be added at the tail, at the front, or even replace an element - * in the list. However, that is handled separately by nmp_cache_update_netlink_route() - * and of no concern here. */ - if (NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), - NMP_OBJECT_TYPE_IP4_ADDRESS, - NMP_OBJECT_TYPE_IP6_ADDRESS)) - return NM_DEDUP_MULTI_IDX_MODE_PREPEND; - return NM_DEDUP_MULTI_IDX_MODE_APPEND; -} - -static void -_idxcache_update_order_for_dump(NMPCache *cache, const NMDedupMultiEntry *entry) -{ - const NMPClass * klass; - const guint8 * i_idx_type; - const NMDedupMultiEntry *entry2; - - nm_dedup_multi_entry_reorder(entry, NULL, TRUE); - - klass = NMP_OBJECT_GET_CLASS(entry->obj); - for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) { - NMPCacheIdType id_type = *i_idx_type; - - if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) - continue; - - entry2 = nm_dedup_multi_index_lookup_obj(cache->multi_idx, - _idx_type_get(cache, id_type), - entry->obj); - if (!entry2) - continue; - - nm_assert(entry2 != entry); - nm_assert(entry2->obj == entry->obj); - - nm_dedup_multi_entry_reorder(entry2, NULL, TRUE); - } -} - -static void -_idxcache_update_other_cache_ids(NMPCache * cache, - NMPCacheIdType cache_id_type, - const NMPObject *obj_old, - const NMPObject *obj_new, - gboolean is_dump) -{ - const NMDedupMultiEntry *entry_new; - const NMDedupMultiEntry *entry_old; - const NMDedupMultiEntry *entry_order; - NMDedupMultiIdxType * idx_type; - - nm_assert(obj_new || obj_old); - nm_assert(!obj_new || NMP_OBJECT_GET_TYPE(obj_new) != NMP_OBJECT_TYPE_UNKNOWN); - nm_assert(!obj_old || NMP_OBJECT_GET_TYPE(obj_old) != NMP_OBJECT_TYPE_UNKNOWN); - nm_assert(!obj_old || !obj_new - || NMP_OBJECT_GET_CLASS(obj_new) == NMP_OBJECT_GET_CLASS(obj_old)); - nm_assert(!obj_old || !obj_new || !nmp_object_equal(obj_new, obj_old)); - nm_assert(!obj_new || obj_new == nm_dedup_multi_index_obj_find(cache->multi_idx, obj_new)); - nm_assert(!obj_old || obj_old == nm_dedup_multi_index_obj_find(cache->multi_idx, obj_old)); - - idx_type = _idx_type_get(cache, cache_id_type); - - if (obj_old) { - entry_old = nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type, obj_old); - if (!obj_new) { - if (entry_old) - nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old); - return; - } - } else - entry_old = NULL; - - if (obj_new) { - if (obj_old && nm_dedup_multi_idx_type_id_equal(idx_type, obj_old, obj_new) - && nm_dedup_multi_idx_type_partition_equal(idx_type, obj_old, obj_new)) { - /* optimize. We just looked up the @obj_old entry and @obj_new compares equal - * according to idx_obj_id_equal(). entry_new is the same as entry_old. */ - entry_new = entry_old; - } else { - entry_new = nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type, obj_new); - } - - if (entry_new) - entry_order = entry_new; - else if (entry_old - && nm_dedup_multi_idx_type_partition_equal(idx_type, entry_old->obj, obj_new)) - entry_order = entry_old; - else - entry_order = NULL; - nm_dedup_multi_index_add_full( - cache->multi_idx, - idx_type, - obj_new, - is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE : _obj_get_add_mode(obj_new), - is_dump ? NULL : entry_order, - entry_new ?: NM_DEDUP_MULTI_ENTRY_MISSING, - entry_new ? entry_new->head : (entry_order ? entry_order->head : NULL), - &entry_new, - NULL); - -#if NM_MORE_ASSERTS - if (entry_new) { - nm_assert(idx_type->klass->idx_obj_partitionable); - nm_assert(idx_type->klass->idx_obj_partition_equal); - nm_assert(idx_type->klass->idx_obj_partitionable(idx_type, entry_new->obj)); - nm_assert(idx_type->klass->idx_obj_partition_equal(idx_type, - (gpointer) obj_new, - entry_new->obj)); - } -#endif - } else - entry_new = NULL; - - if (entry_old && entry_old != entry_new) - nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old); -} - -static void -_idxcache_update(NMPCache * cache, - const NMDedupMultiEntry * entry_old, - NMPObject * obj_new, - gboolean is_dump, - const NMDedupMultiEntry **out_entry_new) -{ - const NMPClass * klass; - const guint8 * i_idx_type; - NMDedupMultiIdxType * idx_type_o = _idx_type_get(cache, NMP_CACHE_ID_TYPE_OBJECT_TYPE); - const NMDedupMultiEntry *entry_new = NULL; - nm_auto_nmpobj const NMPObject *obj_old = NULL; - - /* we update an object in the cache. - * - * Note that @entry_old MUST be what is currently tracked in multi_idx, and it must - * have the same ID as @obj_new. */ - - nm_assert(cache); - nm_assert(entry_old || obj_new); - nm_assert(!obj_new || nmp_object_is_alive(obj_new)); - nm_assert( - !entry_old - || entry_old - == nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type_o, entry_old->obj)); - nm_assert(!obj_new - || entry_old - == nm_dedup_multi_index_lookup_obj(cache->multi_idx, idx_type_o, obj_new)); - nm_assert(!entry_old || entry_old->head->idx_type == idx_type_o); - nm_assert(!entry_old || !obj_new - || nm_dedup_multi_idx_type_partition_equal(idx_type_o, entry_old->obj, obj_new)); - nm_assert(!entry_old || !obj_new - || nm_dedup_multi_idx_type_id_equal(idx_type_o, entry_old->obj, obj_new)); - nm_assert(!entry_old || !obj_new - || (obj_new->parent.klass == ((const NMPObject *) entry_old->obj)->parent.klass - && !obj_new->parent.klass->obj_full_equal((NMDedupMultiObj *) obj_new, - entry_old->obj))); - - /* keep a reference to the pre-existing entry */ - if (entry_old) - obj_old = nmp_object_ref(entry_old->obj); - - /* first update the main index NMP_CACHE_ID_TYPE_OBJECT_TYPE. - * We already know the pre-existing @entry old, so all that - * nm_dedup_multi_index_add_full() effectively does, is update the - * obj reference. - * - * We also get the new boxed object, which we need below. */ - if (obj_new) { - nm_auto_nmpobj NMPObject *obj_old2 = NULL; - - nm_dedup_multi_index_add_full(cache->multi_idx, - idx_type_o, - obj_new, - is_dump ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE - : _obj_get_add_mode(obj_new), - NULL, - entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, - NULL, - &entry_new, - (const NMDedupMultiObj **) &obj_old2); - nm_assert(entry_new); - nm_assert(obj_old == obj_old2); - nm_assert(!entry_old || entry_old == entry_new); - } else - nm_dedup_multi_index_remove_entry(cache->multi_idx, entry_old); - - /* now update all other indexes. We know the previously boxed entry, and the - * newly boxed one. */ - klass = NMP_OBJECT_GET_CLASS(entry_new ? entry_new->obj : obj_old); - for (i_idx_type = klass->supported_cache_ids; *i_idx_type; i_idx_type++) { - NMPCacheIdType id_type = *i_idx_type; - - if (id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) - continue; - _idxcache_update_other_cache_ids(cache, - id_type, - obj_old, - entry_new ? entry_new->obj : NULL, - is_dump); - } - - NM_SET_OUT(out_entry_new, entry_new); -} - -NMPCacheOpsType -nmp_cache_remove(NMPCache * cache, - const NMPObject * obj_needle, - gboolean equals_by_ptr, - gboolean only_dirty, - const NMPObject **out_obj_old) -{ - const NMDedupMultiEntry *entry_old; - const NMPObject * obj_old; - - entry_old = _lookup_entry(cache, obj_needle); - - if (!entry_old) { - NM_SET_OUT(out_obj_old, NULL); - return NMP_CACHE_OPS_UNCHANGED; - } - - obj_old = entry_old->obj; - - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - - if (equals_by_ptr && obj_old != obj_needle) { - /* We found an identical object, but we only delete it if it's the same pointer as - * @obj_needle. */ - return NMP_CACHE_OPS_UNCHANGED; - } - if (only_dirty && !entry_old->dirty) { - /* the entry is not dirty. Skip. */ - return NMP_CACHE_OPS_UNCHANGED; - } - _idxcache_update(cache, entry_old, NULL, FALSE, NULL); - return NMP_CACHE_OPS_REMOVED; -} - -NMPCacheOpsType -nmp_cache_remove_netlink(NMPCache * cache, - const NMPObject * obj_needle, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new) -{ - const NMDedupMultiEntry *entry_old; - const NMDedupMultiEntry *entry_new = NULL; - const NMPObject * obj_old; - nm_auto_nmpobj NMPObject *obj_new = NULL; - - entry_old = _lookup_entry(cache, obj_needle); - - if (!entry_old) { - NM_SET_OUT(out_obj_old, NULL); - NM_SET_OUT(out_obj_new, NULL); - return NMP_CACHE_OPS_UNCHANGED; - } - - obj_old = entry_old->obj; - - if (NMP_OBJECT_GET_TYPE(obj_needle) == NMP_OBJECT_TYPE_LINK) { - /* For nmp_cache_remove_netlink() we have an incomplete @obj_needle instance to be - * removed from netlink. Link objects are alive without being in netlink when they - * have a udev-device. All we want to do in this case is clear the netlink.is_in_netlink - * flag. */ - - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - - if (!obj_old->_link.netlink.is_in_netlink) { - nm_assert(obj_old->_link.udev.device); - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old)); - return NMP_CACHE_OPS_UNCHANGED; - } - - if (!obj_old->_link.udev.device) { - /* the update would make @obj_old invalid. Remove it. */ - _idxcache_update(cache, entry_old, NULL, FALSE, NULL); - NM_SET_OUT(out_obj_new, NULL); - return NMP_CACHE_OPS_REMOVED; - } - - obj_new = nmp_object_clone(obj_old, FALSE); - obj_new->_link.netlink.is_in_netlink = FALSE; - - _nmp_object_fixup_link_master_connected(&obj_new, NULL, cache); - _nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev); - - _idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new); - NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj)); - return NMP_CACHE_OPS_UPDATED; - } - - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - NM_SET_OUT(out_obj_new, NULL); - _idxcache_update(cache, entry_old, NULL, FALSE, NULL); - return NMP_CACHE_OPS_REMOVED; -} - -/** - * nmp_cache_update_netlink: - * @cache: the platform cache - * @obj_hand_over: a #NMPObject instance as received from netlink and created via - * nmp_object_from_nl(). Especially for link, it must not have the udev - * replated fields set. - * This instance will be modified and might be put into the cache. When - * calling nmp_cache_update_netlink() you hand @obj over to the cache. - * Except, that the cache will increment the ref count as appropriate. You - * must still unref the obj to release your part of the ownership. - * @is_dump: whether this update comes during a dump of object of the same kind. - * kernel dumps objects in a certain order, which matters especially for routes. - * Before a dump we mark all objects as dirty, and remove all untouched objects - * afterwards. Hence, during a dump, every update should move the object to the - * end of the list, to obtain the correct order. That means, to use NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, - * instead of NM_DEDUP_MULTI_IDX_MODE_APPEND. - * @out_obj_old: (allow-none) (out): return the object with same ID as @obj_hand_over, - * that was in the cache before update. If an object is returned, the caller must - * unref it afterwards. - * @out_obj_new: (allow-none) (out): return the object from the cache after update. - * The caller must unref this object. - * - * Returns: how the cache changed. - * - * Even if there was no change in the cache (NMP_CACHE_OPS_UNCHANGED), @out_obj_old - * and @out_obj_new will be set accordingly. - **/ -NMPCacheOpsType -nmp_cache_update_netlink(NMPCache * cache, - NMPObject * obj_hand_over, - gboolean is_dump, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new) -{ - const NMDedupMultiEntry *entry_old; - const NMDedupMultiEntry *entry_new; - const NMPObject * obj_old; - gboolean is_alive; - - nm_assert(cache); - nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over)); - nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over)); - /* A link object from netlink must have the udev related fields unset. - * We could implement to handle that, but there is no need to support such - * a use-case */ - nm_assert(NMP_OBJECT_GET_TYPE(obj_hand_over) != NMP_OBJECT_TYPE_LINK - || (!obj_hand_over->_link.udev.device && !obj_hand_over->link.driver)); - nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over); - - entry_old = _lookup_entry(cache, obj_hand_over); - - if (!entry_old) { - NM_SET_OUT(out_obj_old, NULL); - - if (!nmp_object_is_alive(obj_hand_over)) { - NM_SET_OUT(out_obj_new, NULL); - return NMP_CACHE_OPS_UNCHANGED; - } - - if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_LINK) { - _nmp_object_fixup_link_master_connected(&obj_hand_over, NULL, cache); - _nmp_object_fixup_link_udev_fields(&obj_hand_over, NULL, cache->use_udev); - } - - _idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new); - NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj)); - return NMP_CACHE_OPS_ADDED; - } - - obj_old = entry_old->obj; - - if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_LINK) { - if (!obj_hand_over->_link.netlink.is_in_netlink) { - if (!obj_old->_link.netlink.is_in_netlink) { - nm_assert(obj_old->_link.udev.device); - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old)); - return NMP_CACHE_OPS_UNCHANGED; - } - if (obj_old->_link.udev.device) { - /* @obj_hand_over is not in netlink. - * - * This is similar to nmp_cache_remove_netlink(), but there we preserve the - * preexisting netlink properties. The use case of that is when kernel_get_object() - * cannot load an object (based on the id of a needle). - * - * Here we keep the data provided from @obj_hand_over. The usecase is when receiving - * a valid @obj_hand_over instance from netlink with RTM_DELROUTE. - */ - is_alive = TRUE; - } else - is_alive = FALSE; - } else - is_alive = TRUE; - - if (is_alive) { - _nmp_object_fixup_link_master_connected(&obj_hand_over, NULL, cache); - - /* Merge the netlink parts with what we have from udev. */ - udev_device_unref(obj_hand_over->_link.udev.device); - obj_hand_over->_link.udev.device = - obj_old->_link.udev.device ? udev_device_ref(obj_old->_link.udev.device) : NULL; - _nmp_object_fixup_link_udev_fields(&obj_hand_over, NULL, cache->use_udev); - - if (obj_hand_over->_link.netlink.lnk) { - nm_auto_nmpobj const NMPObject *lnk_old = obj_hand_over->_link.netlink.lnk; - - /* let's dedup/intern the lnk object. */ - obj_hand_over->_link.netlink.lnk = - nm_dedup_multi_index_obj_intern(cache->multi_idx, lnk_old); - } - } - } else - is_alive = nmp_object_is_alive(obj_hand_over); - - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - - if (!is_alive) { - /* the update would make @obj_old invalid. Remove it. */ - _idxcache_update(cache, entry_old, NULL, FALSE, NULL); - NM_SET_OUT(out_obj_new, NULL); - return NMP_CACHE_OPS_REMOVED; - } - - if (nmp_object_equal(obj_old, obj_hand_over)) { - if (is_dump) - _idxcache_update_order_for_dump(cache, entry_old); - nm_dedup_multi_entry_set_dirty(entry_old, FALSE); - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old)); - return NMP_CACHE_OPS_UNCHANGED; - } - - _idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new); - NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj)); - return NMP_CACHE_OPS_UPDATED; -} - -NMPCacheOpsType -nmp_cache_update_netlink_route(NMPCache * cache, - NMPObject * obj_hand_over, - gboolean is_dump, - guint16 nlmsgflags, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new, - const NMPObject **out_obj_replace, - gboolean * out_resync_required) -{ - NMDedupMultiIter iter; - const NMDedupMultiEntry * entry_old; - const NMDedupMultiEntry * entry_new; - const NMDedupMultiEntry * entry_cur; - const NMDedupMultiEntry * entry_replace; - const NMDedupMultiHeadEntry *head_entry; - gboolean is_alive; - NMPCacheOpsType ops_type = NMP_CACHE_OPS_UNCHANGED; - gboolean resync_required; - - nm_assert(cache); - nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over)); - nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over)); - /* A link object from netlink must have the udev related fields unset. - * We could implement to handle that, but there is no need to support such - * a use-case */ - nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_hand_over), - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE)); - nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over); - - entry_old = _lookup_entry(cache, obj_hand_over); - entry_new = NULL; - - NM_SET_OUT(out_obj_old, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_old))); - - if (!entry_old) { - if (!nmp_object_is_alive(obj_hand_over)) - goto update_done; - - _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new); - ops_type = NMP_CACHE_OPS_ADDED; - goto update_done; - } - - is_alive = nmp_object_is_alive(obj_hand_over); - - if (!is_alive) { - /* the update would make @entry_old invalid. Remove it. */ - _idxcache_update(cache, entry_old, NULL, FALSE, NULL); - ops_type = NMP_CACHE_OPS_REMOVED; - goto update_done; - } - - if (nmp_object_equal(entry_old->obj, obj_hand_over)) { - if (is_dump) - _idxcache_update_order_for_dump(cache, entry_old); - nm_dedup_multi_entry_set_dirty(entry_old, FALSE); - goto update_done; - } - - _idxcache_update(cache, entry_old, obj_hand_over, is_dump, &entry_new); - ops_type = NMP_CACHE_OPS_UPDATED; - -update_done: - NM_SET_OUT(out_obj_new, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_new))); - - /* a RTM_GETROUTE event may signal that another object was replaced. - * Find out whether that is the case and return it as @obj_replaced. - * - * Also, fixup the order of @entry_new within NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID - * index. For most parts, we don't care about the order of objects (including routes). - * But NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID we must keep in the correct order, to - * properly find @obj_replaced. */ - resync_required = FALSE; - entry_replace = NULL; - if (is_dump) - goto out; - - if (!entry_new) { - if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE) - && nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) { - /* hm. @obj_hand_over was not added, meaning it was not alive. - * However, we track some other objects with the same weak-id. - * It's unclear what that means. To be sure, resync. */ - resync_required = TRUE; - } - goto out; - } - - /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID - * index. For all other indexes their order becomes messed up. */ - entry_cur = - _lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj); - if (!entry_cur) { - nm_assert_not_reached(); - goto out; - } - nm_assert(entry_cur->obj == entry_new->obj); - - head_entry = entry_cur->head; - nm_assert(head_entry - == nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_cur->obj)); - - if (head_entry->len == 1) { - /* there is only one object, and we expect it to be @obj_new. */ - nm_assert(nm_dedup_multi_head_entry_get_idx(head_entry, 0) == entry_cur); - goto out; - } - - switch (nlmsgflags & (NLM_F_REPLACE | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND)) { - case NLM_F_REPLACE: - /* ip route change */ - - /* get the first element (but skip @obj_new). */ - nm_dedup_multi_iter_init(&iter, head_entry); - if (!nm_dedup_multi_iter_next(&iter)) - nm_assert_not_reached(); - if (iter.current == entry_cur) { - if (!nm_dedup_multi_iter_next(&iter)) - nm_assert_not_reached(); - } - entry_replace = iter.current; - - nm_assert(entry_replace && entry_cur != entry_replace); - - nm_dedup_multi_entry_reorder(entry_cur, entry_replace, FALSE); - break; - case NLM_F_CREATE | NLM_F_APPEND: - /* ip route append */ - nm_dedup_multi_entry_reorder(entry_cur, NULL, TRUE); - break; - case NLM_F_CREATE: - /* ip route prepend */ - nm_dedup_multi_entry_reorder(entry_cur, NULL, FALSE); - break; - default: - /* this is an unexpected case, probably a bug that we need to handle better. */ - resync_required = TRUE; - break; - } - -out: - NM_SET_OUT(out_obj_replace, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_replace))); - NM_SET_OUT(out_resync_required, resync_required); - return ops_type; -} - -NMPCacheOpsType -nmp_cache_update_link_udev(NMPCache * cache, - int ifindex, - struct udev_device *udevice, - const NMPObject ** out_obj_old, - const NMPObject ** out_obj_new) -{ - const NMPObject *obj_old; - nm_auto_nmpobj NMPObject *obj_new = NULL; - const NMDedupMultiEntry * entry_old; - const NMDedupMultiEntry * entry_new; - - entry_old = nmp_cache_lookup_entry_link(cache, ifindex); - - if (!entry_old) { - if (!udevice) { - NM_SET_OUT(out_obj_old, NULL); - NM_SET_OUT(out_obj_new, NULL); - return NMP_CACHE_OPS_UNCHANGED; - } - - obj_new = nmp_object_new(NMP_OBJECT_TYPE_LINK, NULL); - obj_new->link.ifindex = ifindex; - obj_new->_link.udev.device = udev_device_ref(udevice); - - _nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev); - - _idxcache_update(cache, NULL, obj_new, FALSE, &entry_new); - NM_SET_OUT(out_obj_old, NULL); - NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj)); - return NMP_CACHE_OPS_ADDED; - } else { - obj_old = entry_old->obj; - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - - if (obj_old->_link.udev.device == udevice) { - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old)); - return NMP_CACHE_OPS_UNCHANGED; - } - - if (!udevice && !obj_old->_link.netlink.is_in_netlink) { - /* the update would make @obj_old invalid. Remove it. */ - _idxcache_update(cache, entry_old, NULL, FALSE, NULL); - NM_SET_OUT(out_obj_new, NULL); - return NMP_CACHE_OPS_REMOVED; - } - - obj_new = nmp_object_clone(obj_old, FALSE); - - udev_device_unref(obj_new->_link.udev.device); - obj_new->_link.udev.device = udevice ? udev_device_ref(udevice) : NULL; - - _nmp_object_fixup_link_udev_fields(&obj_new, NULL, cache->use_udev); - - _idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new); - NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj)); - return NMP_CACHE_OPS_UPDATED; - } -} - -NMPCacheOpsType -nmp_cache_update_link_master_connected(NMPCache * cache, - int ifindex, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new) -{ - const NMDedupMultiEntry *entry_old; - const NMDedupMultiEntry *entry_new = NULL; - const NMPObject * obj_old; - nm_auto_nmpobj NMPObject *obj_new = NULL; - - entry_old = nmp_cache_lookup_entry_link(cache, ifindex); - - if (!entry_old) { - NM_SET_OUT(out_obj_old, NULL); - NM_SET_OUT(out_obj_new, NULL); - return NMP_CACHE_OPS_UNCHANGED; - } - - obj_old = entry_old->obj; - - if (!nmp_cache_link_connected_needs_toggle(cache, obj_old, NULL, NULL)) { - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - NM_SET_OUT(out_obj_new, nmp_object_ref(obj_old)); - return NMP_CACHE_OPS_UNCHANGED; - } - - obj_new = nmp_object_clone(obj_old, FALSE); - obj_new->link.connected = !obj_old->link.connected; - - NM_SET_OUT(out_obj_old, nmp_object_ref(obj_old)); - _idxcache_update(cache, entry_old, obj_new, FALSE, &entry_new); - NM_SET_OUT(out_obj_new, nmp_object_ref(entry_new->obj)); - return NMP_CACHE_OPS_UPDATED; -} - -/*****************************************************************************/ - -void -nmp_cache_dirty_set_all_main(NMPCache *cache, const NMPLookup *lookup) -{ - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - - nm_assert(cache); - nm_assert(lookup); - - head_entry = nmp_cache_lookup(cache, lookup); - - nm_dedup_multi_iter_init(&iter, head_entry); - while (nm_dedup_multi_iter_next(&iter)) { - const NMDedupMultiEntry *main_entry; - - main_entry = nmp_cache_reresolve_main_entry(cache, iter.current, lookup); - - nm_dedup_multi_entry_set_dirty(main_entry, TRUE); - } -} - -/*****************************************************************************/ - -NMPCache * -nmp_cache_new(NMDedupMultiIndex *multi_idx, gboolean use_udev) -{ - NMPCache *cache = g_slice_new0(NMPCache); - guint i; - - for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) - _dedup_multi_idx_type_init((DedupMultiIdxType *) _idx_type_get(cache, i), i); - - cache->multi_idx = nm_dedup_multi_index_ref(multi_idx); - - cache->use_udev = !!use_udev; - return cache; -} - -void -nmp_cache_free(NMPCache *cache) -{ - guint i; - - for (i = NMP_CACHE_ID_TYPE_NONE + 1; i <= NMP_CACHE_ID_TYPE_MAX; i++) - nm_dedup_multi_index_remove_idx(cache->multi_idx, _idx_type_get(cache, i)); - - nm_dedup_multi_index_unref(cache->multi_idx); - - g_slice_free(NMPCache, cache); -} - -/*****************************************************************************/ - -void -nmtst_assert_nmp_cache_is_consistent(const NMPCache *cache) -{} - -/*****************************************************************************/ - -/* below, ensure that addr_family get's automatically initialize to AF_UNSPEC. */ -G_STATIC_ASSERT(AF_UNSPEC == 0); - -typedef const char *(*CmdPlobjToStringFunc)(const NMPlatformObject *obj, char *buf, gsize len); -typedef const char *(*CmdPlobjToStringIdFunc)(const NMPlatformObject *obj, char *buf, gsize len); -typedef void (*CmdPlobjHashUpdateFunc)(const NMPlatformObject *obj, NMHashState *h); -typedef int (*CmdPlobjCmpFunc)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); - -const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { - [NMP_OBJECT_TYPE_LINK - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LINK, - .sizeof_data = sizeof(NMPObjectLink), - .sizeof_public = sizeof(NMPlatformLink), - .obj_type_name = "link", - .rtm_gettype = RTM_GETLINK, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK, - .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, - .supported_cache_ids = _supported_cache_ids_link, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_link, - .cmd_obj_cmp = _vt_cmd_obj_cmp_link, - .cmd_obj_copy = _vt_cmd_obj_copy_link, - .cmd_obj_dispose = _vt_cmd_obj_dispose_link, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, - .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, - .cmd_obj_to_string = _vt_cmd_obj_to_string_link, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_link, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_link, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_link_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_link_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_link_cmp, - }, - [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, - .sizeof_data = sizeof(NMPObjectIP4Address), - .sizeof_public = sizeof(NMPlatformIP4Address), - .obj_type_name = "ip4-address", - .addr_family = AF_INET, - .rtm_gettype = RTM_GETADDR, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, - .signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_address, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_address, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_address_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip4_address_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp, - }, - [NMP_OBJECT_TYPE_IP6_ADDRESS - - 1] = {.parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, - .sizeof_data = sizeof(NMPObjectIP6Address), - .sizeof_public = sizeof(NMPlatformIP6Address), - .obj_type_name = "ip6-address", - .addr_family = AF_INET6, - .rtm_gettype = RTM_GETADDR, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, - .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string, - .cmd_plobj_hash_update = - (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp}, - [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, - .sizeof_data = sizeof(NMPObjectIP4Route), - .sizeof_public = sizeof(NMPlatformIP4Route), - .obj_type_name = "ip4-route", - .addr_family = AF_INET, - .rtm_gettype = RTM_GETROUTE, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, - .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_route, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route, - .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip4_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_route_cmp_full, - }, - [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP6_ROUTE, - .sizeof_data = sizeof(NMPObjectIP6Route), - .sizeof_public = sizeof(NMPlatformIP6Route), - .obj_type_name = "ip6-route", - .addr_family = AF_INET6, - .rtm_gettype = RTM_GETROUTE, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, - .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_route, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_route, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_route, - .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip6_route_to_string, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_route_to_string, - .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_route_cmp_full, - }, - [NMP_OBJECT_TYPE_ROUTING_RULE - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_ROUTING_RULE, - .sizeof_data = sizeof(NMPObjectRoutingRule), - .sizeof_public = sizeof(NMPlatformRoutingRule), - .obj_type_name = "routing-rule", - .rtm_gettype = RTM_GETRULE, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, - .signal_type = NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, - .supported_cache_ids = _supported_cache_ids_routing_rules, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_routing_rule, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_routing_rule, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_routing_rule, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_routing_rule, - .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_routing_rule_to_string, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_routing_rule_to_string, - .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_routing_rule_cmp_full, - }, - [NMP_OBJECT_TYPE_QDISC - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_QDISC, - .sizeof_data = sizeof(NMPObjectQdisc), - .sizeof_public = sizeof(NMPlatformQdisc), - .obj_type_name = "qdisc", - .rtm_gettype = RTM_GETQDISC, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_QDISC, - .signal_type = NM_PLATFORM_SIGNAL_QDISC_CHANGED, - .supported_cache_ids = _supported_cache_ids_object, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_qdisc, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_qdisc, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_qdisc, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_qdisc, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_qdisc_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_qdisc_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_qdisc_cmp, - }, - [NMP_OBJECT_TYPE_TFILTER - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_TFILTER, - .sizeof_data = sizeof(NMPObjectTfilter), - .sizeof_public = sizeof(NMPlatformTfilter), - .obj_type_name = "tfilter", - .rtm_gettype = RTM_GETTFILTER, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_TFILTER, - .signal_type = NM_PLATFORM_SIGNAL_TFILTER_CHANGED, - .supported_cache_ids = _supported_cache_ids_object, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_tfilter, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_tfilter, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_tfilter, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_tfilter, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_tfilter_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_tfilter_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_tfilter_cmp, - }, - [NMP_OBJECT_TYPE_LNK_BRIDGE - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_BRIDGE, - .sizeof_data = sizeof(NMPObjectLnkBridge), - .sizeof_public = sizeof(NMPlatformLnkBridge), - .obj_type_name = "bridge", - .lnk_link_type = NM_LINK_TYPE_BRIDGE, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_bridge_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bridge_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_bridge_cmp, - }, - [NMP_OBJECT_TYPE_LNK_GRE - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_GRE, - .sizeof_data = sizeof(NMPObjectLnkGre), - .sizeof_public = sizeof(NMPlatformLnkGre), - .obj_type_name = "gre", - .lnk_link_type = NM_LINK_TYPE_GRE, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_gre_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_gre_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_gre_cmp, - }, - [NMP_OBJECT_TYPE_LNK_GRETAP - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_GRETAP, - .sizeof_data = sizeof(NMPObjectLnkGre), - .sizeof_public = sizeof(NMPlatformLnkGre), - .obj_type_name = "gretap", - .lnk_link_type = NM_LINK_TYPE_GRETAP, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_gre_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_gre_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_gre_cmp, - }, - [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND, - .sizeof_data = sizeof(NMPObjectLnkInfiniband), - .sizeof_public = sizeof(NMPlatformLnkInfiniband), - .obj_type_name = "infiniband", - .lnk_link_type = NM_LINK_TYPE_INFINIBAND, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_infiniband_to_string, - .cmd_plobj_hash_update = - (CmdPlobjHashUpdateFunc) nm_platform_lnk_infiniband_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_infiniband_cmp, - }, - [NMP_OBJECT_TYPE_LNK_IP6TNL - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_IP6TNL, - .sizeof_data = sizeof(NMPObjectLnkIp6Tnl), - .sizeof_public = sizeof(NMPlatformLnkIp6Tnl), - .obj_type_name = "ip6tnl", - .lnk_link_type = NM_LINK_TYPE_IP6TNL, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp, - }, - [NMP_OBJECT_TYPE_LNK_IP6GRE - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_IP6GRE, - .sizeof_data = sizeof(NMPObjectLnkIp6Tnl), - .sizeof_public = sizeof(NMPlatformLnkIp6Tnl), - .obj_type_name = "ip6gre", - .lnk_link_type = NM_LINK_TYPE_IP6GRE, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp, - }, - [NMP_OBJECT_TYPE_LNK_IP6GRETAP - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_IP6GRETAP, - .sizeof_data = sizeof(NMPObjectLnkIp6Tnl), - .sizeof_public = sizeof(NMPlatformLnkIp6Tnl), - .obj_type_name = "ip6gretap", - .lnk_link_type = NM_LINK_TYPE_IP6GRETAP, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ip6tnl_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ip6tnl_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ip6tnl_cmp, - }, - [NMP_OBJECT_TYPE_LNK_IPIP - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_IPIP, - .sizeof_data = sizeof(NMPObjectLnkIpIp), - .sizeof_public = sizeof(NMPlatformLnkIpIp), - .obj_type_name = "ipip", - .lnk_link_type = NM_LINK_TYPE_IPIP, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_ipip_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_ipip_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_ipip_cmp, - }, - [NMP_OBJECT_TYPE_LNK_MACSEC - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_MACSEC, - .sizeof_data = sizeof(NMPObjectLnkMacsec), - .sizeof_public = sizeof(NMPlatformLnkMacsec), - .obj_type_name = "macsec", - .lnk_link_type = NM_LINK_TYPE_MACSEC, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_macsec_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macsec_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_macsec_cmp, - }, - [NMP_OBJECT_TYPE_LNK_MACVLAN - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_MACVLAN, - .sizeof_data = sizeof(NMPObjectLnkMacvlan), - .sizeof_public = sizeof(NMPlatformLnkMacvlan), - .obj_type_name = "macvlan", - .lnk_link_type = NM_LINK_TYPE_MACVLAN, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_macvlan_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macvlan_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_macvlan_cmp, - }, - [NMP_OBJECT_TYPE_LNK_MACVTAP - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP, - .sizeof_data = sizeof(NMPObjectLnkMacvtap), - .sizeof_public = sizeof(NMPlatformLnkMacvlan), - .obj_type_name = "macvtap", - .lnk_link_type = NM_LINK_TYPE_MACVTAP, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_macvlan_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_macvlan_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_macvlan_cmp, - }, - [NMP_OBJECT_TYPE_LNK_SIT - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_SIT, - .sizeof_data = sizeof(NMPObjectLnkSit), - .sizeof_public = sizeof(NMPlatformLnkSit), - .obj_type_name = "sit", - .lnk_link_type = NM_LINK_TYPE_SIT, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_sit_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_sit_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_sit_cmp, - }, - [NMP_OBJECT_TYPE_LNK_TUN - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_TUN, - .sizeof_data = sizeof(NMPObjectLnkTun), - .sizeof_public = sizeof(NMPlatformLnkTun), - .obj_type_name = "tun", - .lnk_link_type = NM_LINK_TYPE_TUN, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_tun_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_tun_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_tun_cmp, - }, - [NMP_OBJECT_TYPE_LNK_VLAN - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, - .sizeof_data = sizeof(NMPObjectLnkVlan), - .sizeof_public = sizeof(NMPlatformLnkVlan), - .obj_type_name = "vlan", - .lnk_link_type = NM_LINK_TYPE_VLAN, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_vlan, - .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, - .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, - .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, - .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vlan_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vlan_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vlan_cmp, - }, - [NMP_OBJECT_TYPE_LNK_VRF - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_VRF, - .sizeof_data = sizeof(NMPObjectLnkVrf), - .sizeof_public = sizeof(NMPlatformLnkVrf), - .obj_type_name = "vrf", - .lnk_link_type = NM_LINK_TYPE_VRF, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vrf_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vrf_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vrf_cmp, - }, - [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_VXLAN, - .sizeof_data = sizeof(NMPObjectLnkVxlan), - .sizeof_public = sizeof(NMPlatformLnkVxlan), - .obj_type_name = "vxlan", - .lnk_link_type = NM_LINK_TYPE_VXLAN, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_vxlan_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_vxlan_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_vxlan_cmp, - }, - [NMP_OBJECT_TYPE_LNK_WIREGUARD - 1] = - { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_LNK_WIREGUARD, - .sizeof_data = sizeof(NMPObjectLnkWireGuard), - .sizeof_public = sizeof(NMPlatformLnkWireGuard), - .obj_type_name = "wireguard", - .lnk_link_type = NM_LINK_TYPE_WIREGUARD, - .cmd_obj_hash_update = _vt_cmd_obj_hash_update_lnk_wireguard, - .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_wireguard, - .cmd_obj_copy = _vt_cmd_obj_copy_lnk_wireguard, - .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_wireguard, - .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_wireguard, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_wireguard_to_string, - .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_wireguard_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_wireguard_cmp, - }, -}; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h deleted file mode 100644 index 58f54dbe..00000000 --- a/src/platform/nmp-object.h +++ /dev/null @@ -1,1128 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 - 2018 Red Hat, Inc. - */ - -#ifndef __NMP_OBJECT_H__ -#define __NMP_OBJECT_H__ - -#include <netinet/in.h> - -#include "nm-glib-aux/nm-obj.h" -#include "nm-glib-aux/nm-dedup-multi.h" -#include "nm-platform.h" - -struct udev_device; - -/*****************************************************************************/ - -/* "struct __kernel_timespec" uses "long long", but we use gint64. In practice, - * these are the same types. */ -G_STATIC_ASSERT(sizeof(long long) == sizeof(gint64)); - -typedef struct { - /* like "struct __kernel_timespec". */ - gint64 tv_sec; - gint64 tv_nsec; -} NMPTimespec64; - -/*****************************************************************************/ - -typedef union { - struct sockaddr sa; - struct sockaddr_in in; - struct sockaddr_in6 in6; -} NMSockAddrUnion; - -#define NM_SOCK_ADDR_UNION_INIT_UNSPEC \ - { \ - .sa = { \ - .sa_family = AF_UNSPEC, \ - }, \ - } - -int nm_sock_addr_union_cmp(const NMSockAddrUnion *a, const NMSockAddrUnion *b); - -void nm_sock_addr_union_hash_update(const NMSockAddrUnion *a, NMHashState *h); - -void nm_sock_addr_union_cpy(NMSockAddrUnion *dst, - gconstpointer src /* unaligned (const NMSockAddrUnion *) */); - -void nm_sock_addr_union_cpy_untrusted(NMSockAddrUnion *dst, - gconstpointer src /* unaligned (const NMSockAddrUnion *) */, - gsize src_len); - -const char *nm_sock_addr_union_to_string(const NMSockAddrUnion *sa, char *buf, gsize len); - -/*****************************************************************************/ - -typedef struct { - NMIPAddr addr; - guint8 family; - guint8 mask; -} NMPWireGuardAllowedIP; - -typedef struct _NMPWireGuardPeer { - NMSockAddrUnion endpoint; - - NMPTimespec64 last_handshake_time; - - guint64 rx_bytes; - guint64 tx_bytes; - - union { - const NMPWireGuardAllowedIP *allowed_ips; - guint _construct_idx_start; - }; - union { - guint allowed_ips_len; - guint _construct_idx_end; - }; - - guint16 persistent_keepalive_interval; - - guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; - guint8 preshared_key[NMP_WIREGUARD_SYMMETRIC_KEY_LEN]; -} NMPWireGuardPeer; - -/*****************************************************************************/ - -typedef enum { /*< skip >*/ - NMP_OBJECT_TO_STRING_ID, - NMP_OBJECT_TO_STRING_PUBLIC, - NMP_OBJECT_TO_STRING_ALL, -} NMPObjectToStringMode; - -typedef enum { /*< skip >*/ - NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE, - NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED, - NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED, - NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED, -} NMPCacheOpsType; - -/* The NMPCacheIdType are the different index types. - * - * An object of a certain object-type, can be candidate to being - * indexed by a certain NMPCacheIdType or not. For example, all - * objects are indexed via an index of type NMP_CACHE_ID_TYPE_OBJECT_TYPE, - * but only route objects can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT. - * - * Of one index type, there can be multiple indexes or not. - * For example, of the index type NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX there - * are multiple instances (for different route/addresses, v4/v6, per-ifindex). - * - * But one object, can only be indexed by one particular index of a - * type. For example, a certain address instance is only indexed by - * the index NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX with - * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible. - * */ -typedef enum { /*< skip >*/ - NMP_CACHE_ID_TYPE_NONE, - - /* all the objects of a certain type. - * - * This index is special. It is the only one that contains *all* object. - * Other indexes may consider some object as non "partitionable", hence - * they don't track all objects. - * - * Hence, this index type is used when looking at all objects (still - * partitioned by type). - * - * Also, note that links may be considered invisible. This index type - * expose all links, even invisible ones. For addresses/routes, this - * distinction doesn't exist, as all addresses/routes that are alive - * are visible as well. */ - NMP_CACHE_ID_TYPE_OBJECT_TYPE, - - /* index for the link objects by ifname. */ - NMP_CACHE_ID_TYPE_LINK_BY_IFNAME, - - /* indices for the visible default-routes, ignoring ifindex. - * This index only contains two partitions: all visible default-routes, - * separate for IPv4 and IPv6. */ - NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, - - /* all the objects that have an ifindex (by object-type) for an ifindex. */ - NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX, - - /* Consider all the destination fields of a route, that is, the ID without the ifindex - * and gateway (meaning: network/plen,metric). - * The reason for this is that `ip route change` can replace an existing route - * and modify its ifindex/gateway. Effectively, that means it deletes an existing - * route and adds a different one (as the ID of the route changes). However, it only - * sends one RTM_NEWADDR notification without notifying about the deletion. We detect - * that by having this index to contain overlapping routes which require special - * cache-resync. */ - NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, - - /* a filter for objects that track an explicit address family. - * - * Note that currently on NMPObjectRoutingRule is indexed by this filter. */ - NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY, - - __NMP_CACHE_ID_TYPE_MAX, - NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, -} NMPCacheIdType; - -typedef struct { - NMDedupMultiObjClass parent; - const char * obj_type_name; - const char * signal_type; - const guint8 * supported_cache_ids; - int sizeof_data; - int sizeof_public; - int addr_family; - int rtm_gettype; - NMPObjectType obj_type; - NMPlatformSignalIdType signal_type_id; - - /* Only for NMPObjectLnk* types. */ - NMLinkType lnk_link_type; - - void (*cmd_obj_hash_update)(const NMPObject *obj, NMHashState *h); - int (*cmd_obj_cmp)(const NMPObject *obj1, const NMPObject *obj2); - void (*cmd_obj_copy)(NMPObject *dst, const NMPObject *src); - void (*cmd_obj_dispose)(NMPObject *obj); - gboolean (*cmd_obj_is_alive)(const NMPObject *obj); - gboolean (*cmd_obj_is_visible)(const NMPObject *obj); - const char *(*cmd_obj_to_string)(const NMPObject * obj, - NMPObjectToStringMode to_string_mode, - char * buf, - gsize buf_size); - - /* functions that operate on NMPlatformObject */ - void (*cmd_plobj_id_copy)(NMPlatformObject *dst, const NMPlatformObject *src); - int (*cmd_plobj_id_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); - void (*cmd_plobj_id_hash_update)(const NMPlatformObject *obj, NMHashState *h); - const char *(*cmd_plobj_to_string_id)(const NMPlatformObject *obj, char *buf, gsize buf_size); - const char *(*cmd_plobj_to_string)(const NMPlatformObject *obj, char *buf, gsize len); - void (*cmd_plobj_hash_update)(const NMPlatformObject *obj, NMHashState *h); - int (*cmd_plobj_cmp)(const NMPlatformObject *obj1, const NMPlatformObject *obj2); -} NMPClass; - -extern const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX]; - -typedef struct { - NMPlatformLink _public; - - struct { - bool is_in_netlink; - - /* Additional data that depends on the link-type (IFLA_INFO_DATA) */ - const NMPObject *lnk; - } netlink; - - struct { - /* note that "struct udev_device" references the library context - * "struct udev", but doesn't own it. - * - * Hence, the udev.device shall not be used after the library - * context is destroyed. - * - * In case of NMPObjectLink instances that you obtained from the - * platform cache, that means that you shall no keep references - * to those instances that outlife the NMPlatform instance. - * - * In practice, the requirement is less strict and you'll be even - * fine if the platform instance (and the "struct udev" instance) - * are already destroyed while you still hold onto a reference to - * the NMPObjectLink instance. Just don't make use of udev functions - * that cause access to the udev library context. - */ - struct udev_device *device; - } udev; - - /* Auxiliary data object for Wi-Fi and WPAN */ - GObject *ext_data; - - /* FIXME: not every NMPObjectLink should pay the price for tracking - * the wireguard family id. This should be tracked via ext_data, which - * would be exactly the right place. */ - int wireguard_family_id; -} NMPObjectLink; - -typedef struct { - NMPlatformLnkBridge _public; -} NMPObjectLnkBridge; - -typedef struct { - NMPlatformLnkGre _public; -} NMPObjectLnkGre; - -typedef struct { - NMPlatformLnkInfiniband _public; -} NMPObjectLnkInfiniband; - -typedef struct { - NMPlatformLnkIp6Tnl _public; -} NMPObjectLnkIp6Tnl; - -typedef struct { - NMPlatformLnkIpIp _public; -} NMPObjectLnkIpIp; - -typedef struct { - NMPlatformLnkMacsec _public; -} NMPObjectLnkMacsec; - -typedef struct { - NMPlatformLnkMacvlan _public; -} NMPObjectLnkMacvlan; - -typedef NMPObjectLnkMacvlan NMPObjectLnkMacvtap; - -typedef struct { - NMPlatformLnkSit _public; -} NMPObjectLnkSit; - -typedef struct { - NMPlatformLnkTun _public; -} NMPObjectLnkTun; - -typedef struct { - NMPlatformLnkVlan _public; - - guint n_ingress_qos_map; - guint n_egress_qos_map; - const NMVlanQosMapping *ingress_qos_map; - const NMVlanQosMapping *egress_qos_map; -} NMPObjectLnkVlan; - -typedef struct { - NMPlatformLnkVrf _public; -} NMPObjectLnkVrf; - -typedef struct { - NMPlatformLnkVxlan _public; -} NMPObjectLnkVxlan; - -typedef struct { - NMPlatformLnkWireGuard _public; - const NMPWireGuardPeer * peers; - const NMPWireGuardAllowedIP *_allowed_ips_buf; - guint peers_len; - guint _allowed_ips_buf_len; -} NMPObjectLnkWireGuard; - -typedef struct { - NMPlatformIP4Address _public; -} NMPObjectIP4Address; - -typedef struct { - NMPlatformIP4Route _public; -} NMPObjectIP4Route; - -typedef struct { - NMPlatformIP6Address _public; -} NMPObjectIP6Address; - -typedef struct { - NMPlatformIP6Route _public; -} NMPObjectIP6Route; - -typedef struct { - NMPlatformRoutingRule _public; -} NMPObjectRoutingRule; - -typedef struct { - NMPlatformQdisc _public; -} NMPObjectQdisc; - -typedef struct { - NMPlatformTfilter _public; -} NMPObjectTfilter; - -struct _NMPObject { - union { - NMDedupMultiObj parent; - const NMPClass *_class; - }; - union { - NMPlatformObject object; - - NMPlatformObjWithIfindex obj_with_ifindex; - - NMPlatformLink link; - NMPObjectLink _link; - - NMPlatformLnkBridge lnk_bridge; - NMPObjectLnkBridge _lnk_bridge; - - NMPlatformLnkGre lnk_gre; - NMPObjectLnkGre _lnk_gre; - - NMPlatformLnkInfiniband lnk_infiniband; - NMPObjectLnkInfiniband _lnk_infiniband; - - NMPlatformLnkIpIp lnk_ipip; - NMPObjectLnkIpIp _lnk_ipip; - - NMPlatformLnkIp6Tnl lnk_ip6tnl; - NMPObjectLnkIp6Tnl _lnk_ip6tnl; - - NMPlatformLnkMacsec lnk_macsec; - NMPObjectLnkMacsec _lnk_macsec; - - NMPlatformLnkMacvlan lnk_macvlan; - NMPObjectLnkMacvlan _lnk_macvlan; - - NMPlatformLnkSit lnk_sit; - NMPObjectLnkSit _lnk_sit; - - NMPlatformLnkTun lnk_tun; - NMPObjectLnkTun _lnk_tun; - - NMPlatformLnkVlan lnk_vlan; - NMPObjectLnkVlan _lnk_vlan; - - NMPlatformLnkVrf lnk_vrf; - NMPObjectLnkVrf _lnk_vrf; - - NMPlatformLnkVxlan lnk_vxlan; - NMPObjectLnkVxlan _lnk_vxlan; - - NMPlatformLnkWireGuard lnk_wireguard; - NMPObjectLnkWireGuard _lnk_wireguard; - - NMPlatformIPAddress ip_address; - NMPlatformIPXAddress ipx_address; - NMPlatformIP4Address ip4_address; - NMPlatformIP6Address ip6_address; - NMPObjectIP4Address _ip4_address; - NMPObjectIP6Address _ip6_address; - - NMPlatformIPRoute ip_route; - NMPlatformIPXRoute ipx_route; - NMPlatformIP4Route ip4_route; - NMPlatformIP6Route ip6_route; - NMPObjectIP4Route _ip4_route; - NMPObjectIP6Route _ip6_route; - - NMPlatformRoutingRule routing_rule; - NMPObjectRoutingRule _routing_rule; - - NMPlatformQdisc qdisc; - NMPObjectQdisc _qdisc; - NMPlatformTfilter tfilter; - NMPObjectTfilter _tfilter; - }; -}; - -/*****************************************************************************/ - -static inline gboolean -NMP_CLASS_IS_VALID(const NMPClass *klass) -{ - return klass >= &_nmp_classes[0] && klass <= &_nmp_classes[G_N_ELEMENTS(_nmp_classes)] - && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof(_nmp_classes[0]))) == 0; -} - -static inline const NMPClass * -nmp_class_from_type(NMPObjectType obj_type) -{ - nm_assert(obj_type > 0); - nm_assert(obj_type <= G_N_ELEMENTS(_nmp_classes)); - nm_assert(_nmp_classes[obj_type - 1].obj_type == obj_type); - nm_assert(NMP_CLASS_IS_VALID(&_nmp_classes[obj_type - 1])); - - return &_nmp_classes[obj_type - 1]; -} - -static inline NMPObject * -NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) -{ - NMPObject *obj; - - obj = plobj ? (NMPObject *) (&(((char *) plobj)[-((int) G_STRUCT_OFFSET(NMPObject, object))])) - : NULL; - nm_assert(!obj || (obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID(obj->_class))); - return obj; -} -#define NMP_OBJECT_UP_CAST(plobj) (NMP_OBJECT_UP_CAST((const NMPlatformObject *) (plobj))) - -static inline gboolean -NMP_OBJECT_IS_VALID(const NMPObject *obj) -{ - nm_assert(!obj || (obj && obj->parent._ref_count > 0 && NMP_CLASS_IS_VALID(obj->_class))); - - /* There isn't really much to check. Either @obj is NULL, or we must - * assume that it points to valid memory. */ - return obj != NULL; -} - -static inline gboolean -NMP_OBJECT_IS_STACKINIT(const NMPObject *obj) -{ - nm_assert(!obj || NMP_OBJECT_IS_VALID(obj)); - - return obj && obj->parent._ref_count == NM_OBJ_REF_COUNT_STACKINIT; -} - -static inline const NMPClass * -NMP_OBJECT_GET_CLASS(const NMPObject *obj) -{ - nm_assert(NMP_OBJECT_IS_VALID(obj)); - - return obj->_class; -} - -static inline NMPObjectType -NMP_OBJECT_GET_TYPE(const NMPObject *obj) -{ - nm_assert(!obj || NMP_OBJECT_IS_VALID(obj)); - - return obj ? obj->_class->obj_type : NMP_OBJECT_TYPE_UNKNOWN; -} - -static inline gboolean -_NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) -{ - switch (obj_type) { - case NMP_OBJECT_TYPE_LINK: - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - - case NMP_OBJECT_TYPE_QDISC: - - case NMP_OBJECT_TYPE_TFILTER: - - case NMP_OBJECT_TYPE_LNK_BRIDGE: - case NMP_OBJECT_TYPE_LNK_GRE: - case NMP_OBJECT_TYPE_LNK_GRETAP: - case NMP_OBJECT_TYPE_LNK_INFINIBAND: - case NMP_OBJECT_TYPE_LNK_IP6TNL: - case NMP_OBJECT_TYPE_LNK_IP6GRE: - case NMP_OBJECT_TYPE_LNK_IP6GRETAP: - case NMP_OBJECT_TYPE_LNK_IPIP: - case NMP_OBJECT_TYPE_LNK_MACSEC: - case NMP_OBJECT_TYPE_LNK_MACVLAN: - case NMP_OBJECT_TYPE_LNK_MACVTAP: - case NMP_OBJECT_TYPE_LNK_SIT: - case NMP_OBJECT_TYPE_LNK_TUN: - case NMP_OBJECT_TYPE_LNK_VLAN: - case NMP_OBJECT_TYPE_LNK_VRF: - case NMP_OBJECT_TYPE_LNK_VXLAN: - case NMP_OBJECT_TYPE_LNK_WIREGUARD: - return TRUE; - - case NMP_OBJECT_TYPE_ROUTING_RULE: - return FALSE; - - case NMP_OBJECT_TYPE_UNKNOWN: - case __NMP_OBJECT_TYPE_LAST: - break; - } - nm_assert_not_reached(); - return FALSE; -} - -#define NMP_OBJECT_CAST_OBJECT(obj) \ - ({ \ - typeof(obj) _obj = (obj); \ - \ - nm_assert ( !_obj \ - || nmp_class_from_type (NMP_OBJECT_GET_TYPE (_obj)))); \ - _obj ? &NM_CONSTCAST(NMPObject, _obj)->object : NULL; \ - }) - -#define NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj) \ - ({ \ - typeof(obj) _obj = (obj); \ - \ - nm_assert(!_obj || _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMP_OBJECT_GET_TYPE(_obj))); \ - _obj ? &NM_CONSTCAST(NMPObject, _obj)->obj_with_ifindex : NULL; \ - }) - -#define _NMP_OBJECT_CAST(obj, field, ...) \ - ({ \ - typeof(obj) _obj = (obj); \ - \ - nm_assert(!_obj || NM_IN_SET(NMP_OBJECT_GET_TYPE(_obj), __VA_ARGS__)); \ - _obj ? &NM_CONSTCAST(NMPObject, _obj)->field : NULL; \ - }) - -#define NMP_OBJECT_CAST_LINK(obj) _NMP_OBJECT_CAST(obj, link, NMP_OBJECT_TYPE_LINK) -#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \ - _NMP_OBJECT_CAST(obj, ip_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) -#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \ - _NMP_OBJECT_CAST(obj, ipx_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) -#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \ - _NMP_OBJECT_CAST(obj, ip4_address, NMP_OBJECT_TYPE_IP4_ADDRESS) -#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \ - _NMP_OBJECT_CAST(obj, ip6_address, NMP_OBJECT_TYPE_IP6_ADDRESS) -#define NMP_OBJECT_CAST_IP_ROUTE(obj) \ - _NMP_OBJECT_CAST(obj, ip_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) -#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \ - _NMP_OBJECT_CAST(obj, ipx_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) -#define NMP_OBJECT_CAST_IP4_ROUTE(obj) _NMP_OBJECT_CAST(obj, ip4_route, NMP_OBJECT_TYPE_IP4_ROUTE) -#define NMP_OBJECT_CAST_IP6_ROUTE(obj) _NMP_OBJECT_CAST(obj, ip6_route, NMP_OBJECT_TYPE_IP6_ROUTE) -#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \ - _NMP_OBJECT_CAST(obj, routing_rule, NMP_OBJECT_TYPE_ROUTING_RULE) -#define NMP_OBJECT_CAST_QDISC(obj) _NMP_OBJECT_CAST(obj, qdisc, NMP_OBJECT_TYPE_QDISC) -#define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST(obj, tfilter, NMP_OBJECT_TYPE_TFILTER) -#define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) \ - _NMP_OBJECT_CAST(obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD) -#define NMP_OBJECT_CAST_LNK_BRIDGE(obj) \ - _NMP_OBJECT_CAST(obj, lnk_bridge, NMP_OBJECT_TYPE_LNK_BRIDGE) - -static inline int -NMP_OBJECT_TYPE_TO_ADDR_FAMILY(NMPObjectType obj_type) -{ - return nmp_class_from_type(obj_type)->addr_family; -} - -static inline int -NMP_OBJECT_GET_ADDR_FAMILY(const NMPObject *obj) -{ - return NMP_OBJECT_GET_CLASS(obj)->addr_family; -} - -static inline const NMPObject * -nmp_object_ref(const NMPObject *obj) -{ - if (!obj) { - /* for convenience, allow NULL. */ - return NULL; - } - - /* ref and unref accept const pointers. NMPObject is supposed to be shared - * and kept immutable. Disallowing to take/return a reference to a const - * NMPObject is cumbersome, because callers are precisely expected to - * keep a ref on the otherwise immutable object. */ - g_return_val_if_fail(NMP_OBJECT_IS_VALID(obj), NULL); - g_return_val_if_fail(obj->parent._ref_count != NM_OBJ_REF_COUNT_STACKINIT, NULL); - - return (const NMPObject *) nm_dedup_multi_obj_ref((const NMDedupMultiObj *) obj); -} - -static inline void -nmp_object_unref(const NMPObject *obj) -{ - if (obj) { - nm_assert(NMP_OBJECT_IS_VALID(obj)); - - nm_dedup_multi_obj_unref((const NMDedupMultiObj *) obj); - } -} - -#define nm_clear_nmp_object(ptr) \ - ({ \ - typeof(ptr) _ptr = (ptr); \ - typeof(*_ptr) _pptr; \ - gboolean _changed = FALSE; \ - \ - if (_ptr && (_pptr = *_ptr)) { \ - *_ptr = NULL; \ - nmp_object_unref(_pptr); \ - _changed = TRUE; \ - } \ - _changed; \ - }) - -static inline gboolean -nmp_object_ref_set(const NMPObject **pp, const NMPObject *obj) -{ - gboolean _changed = FALSE; - const NMPObject *p; - - nm_assert(!pp || !*pp || NMP_OBJECT_IS_VALID(*pp)); - nm_assert(!obj || NMP_OBJECT_IS_VALID(obj)); - - if (pp && ((p = *pp) != obj)) { - nmp_object_ref(obj); - *pp = obj; - nmp_object_unref(p); - _changed = TRUE; - } - return _changed; -} - -NMPObject *nmp_object_new(NMPObjectType obj_type, gconstpointer plobj); -NMPObject *nmp_object_new_link(int ifindex); - -const NMPObject *nmp_object_stackinit(NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj); - -static inline NMPObject * -nmp_object_stackinit_obj(NMPObject *obj, const NMPObject *src) -{ - return obj == src - ? obj - : (NMPObject *) nmp_object_stackinit(obj, NMP_OBJECT_GET_TYPE(src), &src->object); -} - -const NMPObject *nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src); -const NMPObject *nmp_object_stackinit_id_link(NMPObject *obj, int ifindex); -const NMPObject *nmp_object_stackinit_id_ip4_address(NMPObject *obj, - int ifindex, - guint32 address, - guint8 plen, - guint32 peer_address); -const NMPObject * -nmp_object_stackinit_id_ip6_address(NMPObject *obj, int ifindex, const struct in6_addr *address); - -const char *nmp_object_to_string(const NMPObject * obj, - NMPObjectToStringMode to_string_mode, - char * buf, - gsize buf_size); -void nmp_object_hash_update(const NMPObject *obj, NMHashState *h); -int nmp_object_cmp(const NMPObject *obj1, const NMPObject *obj2); - -static inline gboolean -nmp_object_equal(const NMPObject *obj1, const NMPObject *obj2) -{ - return nmp_object_cmp(obj1, obj2) == 0; -} - -void nmp_object_copy(NMPObject *dst, const NMPObject *src, gboolean id_only); -NMPObject *nmp_object_clone(const NMPObject *obj, gboolean id_only); - -int nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2); -void nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h); -guint nmp_object_id_hash(const NMPObject *obj); - -static inline gboolean -nmp_object_id_equal(const NMPObject *obj1, const NMPObject *obj2) -{ - return nmp_object_id_cmp(obj1, obj2) == 0; -} - -guint nmp_object_indirect_id_hash(gconstpointer a); -gboolean nmp_object_indirect_id_equal(gconstpointer a, gconstpointer b); - -gboolean nmp_object_is_alive(const NMPObject *obj); -gboolean nmp_object_is_visible(const NMPObject *obj); - -void -_nmp_object_fixup_link_udev_fields(NMPObject **obj_new, NMPObject *obj_orig, gboolean use_udev); - -static inline void -_nm_auto_nmpobj_cleanup(gpointer p) -{ - nmp_object_unref(*((const NMPObject **) p)); -} -#define nm_auto_nmpobj nm_auto(_nm_auto_nmpobj_cleanup) - -typedef struct _NMPCache NMPCache; - -typedef void (*NMPCachePreHook)(NMPCache * cache, - const NMPObject *old, - const NMPObject *new, - NMPCacheOpsType ops_type, - gpointer user_data); -typedef gboolean (*NMPObjectMatchFn)(const NMPObject *obj, gpointer user_data); - -const NMDedupMultiEntry *nmp_cache_lookup_entry(const NMPCache *cache, const NMPObject *obj); -const NMDedupMultiEntry *nmp_cache_lookup_entry_with_idx_type(const NMPCache * cache, - NMPCacheIdType cache_id_type, - const NMPObject *obj); -const NMDedupMultiEntry *nmp_cache_lookup_entry_link(const NMPCache *cache, int ifindex); -const NMPObject * nmp_cache_lookup_obj(const NMPCache *cache, const NMPObject *obj); -const NMPObject * nmp_cache_lookup_link(const NMPCache *cache, int ifindex); - -typedef struct _NMPLookup NMPLookup; - -struct _NMPLookup { - NMPCacheIdType cache_id_type; - NMPObject selector_obj; -}; - -const NMDedupMultiHeadEntry *nmp_cache_lookup_all(const NMPCache * cache, - NMPCacheIdType cache_id_type, - const NMPObject *select_obj); - -static inline const NMDedupMultiHeadEntry * -nmp_cache_lookup(const NMPCache *cache, const NMPLookup *lookup) -{ - return nmp_cache_lookup_all(cache, lookup->cache_id_type, &lookup->selector_obj); -} - -const NMPLookup *nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type); -const NMPLookup *nmp_lookup_init_link_by_ifname(NMPLookup *lookup, const char *ifname); -const NMPLookup *nmp_lookup_init_object(NMPLookup *lookup, NMPObjectType obj_type, int ifindex); -const NMPLookup *nmp_lookup_init_route_default(NMPLookup *lookup, NMPObjectType obj_type); -const NMPLookup *nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj); -const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup, - in_addr_t network, - guint plen, - guint32 metric, - guint8 tos); -const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id(NMPLookup * lookup, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen); -const NMPLookup * -nmp_lookup_init_object_by_addr_family(NMPLookup *lookup, NMPObjectType obj_type, int addr_family); - -GArray *nmp_cache_lookup_to_array(const NMDedupMultiHeadEntry *head_entry, - NMPObjectType obj_type, - gboolean visible_only); - -static inline gboolean -nmp_cache_iter_next(NMDedupMultiIter *iter, const NMPObject **out_obj) -{ - gboolean has_next; - - has_next = nm_dedup_multi_iter_next(iter); - nm_assert(!has_next || NMP_OBJECT_IS_VALID(iter->current->obj)); - if (out_obj) - *out_obj = has_next ? iter->current->obj : NULL; - return has_next; -} - -static inline gboolean -nmp_cache_iter_next_link(NMDedupMultiIter *iter, const NMPlatformLink **out_obj) -{ - gboolean has_next; - - has_next = nm_dedup_multi_iter_next(iter); - nm_assert(!has_next || NMP_OBJECT_GET_TYPE(iter->current->obj) == NMP_OBJECT_TYPE_LINK); - if (out_obj) - *out_obj = has_next ? &(((const NMPObject *) iter->current->obj)->link) : NULL; - return has_next; -} - -#define nmp_cache_iter_for_each(iter, head, obj) \ - for (nm_dedup_multi_iter_init((iter), (head)); nmp_cache_iter_next((iter), (obj));) - -#define nmp_cache_iter_for_each_link(iter, head, obj) \ - for (nm_dedup_multi_iter_init((iter), (head)); nmp_cache_iter_next_link((iter), (obj));) - -const NMPObject *nmp_cache_lookup_link_full(const NMPCache * cache, - int ifindex, - const char * ifname, - gboolean visible_only, - NMLinkType link_type, - NMPObjectMatchFn match_fn, - gpointer user_data); - -gboolean nmp_cache_link_connected_for_slave(int ifindex_master, const NMPObject *slave); -gboolean nmp_cache_link_connected_needs_toggle(const NMPCache * cache, - const NMPObject *master, - const NMPObject *potential_slave, - const NMPObject *ignore_slave); -const NMPObject *nmp_cache_link_connected_needs_toggle_by_ifindex(const NMPCache * cache, - int master_ifindex, - const NMPObject *potential_slave, - const NMPObject *ignore_slave); - -gboolean nmp_cache_use_udev_get(const NMPCache *cache); - -void nmtst_assert_nmp_cache_is_consistent(const NMPCache *cache); - -NMPCacheOpsType nmp_cache_remove(NMPCache * cache, - const NMPObject * obj_needle, - gboolean equals_by_ptr, - gboolean only_dirty, - const NMPObject **out_obj_old); -NMPCacheOpsType nmp_cache_remove_netlink(NMPCache * cache, - const NMPObject * obj_needle, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new); -NMPCacheOpsType nmp_cache_update_netlink(NMPCache * cache, - NMPObject * obj_hand_over, - gboolean is_dump, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new); -NMPCacheOpsType nmp_cache_update_netlink_route(NMPCache * cache, - NMPObject * obj_hand_over, - gboolean is_dump, - guint16 nlmsgflags, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new, - const NMPObject **out_obj_replace, - gboolean * out_resync_required); -NMPCacheOpsType nmp_cache_update_link_udev(NMPCache * cache, - int ifindex, - struct udev_device *udevice, - const NMPObject ** out_obj_old, - const NMPObject ** out_obj_new); -NMPCacheOpsType nmp_cache_update_link_master_connected(NMPCache * cache, - int ifindex, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new); - -static inline const NMDedupMultiEntry * -nmp_cache_reresolve_main_entry(NMPCache * cache, - const NMDedupMultiEntry *entry, - const NMPLookup * lookup) -{ - const NMDedupMultiEntry *main_entry; - - nm_assert(cache); - nm_assert(entry); - nm_assert(lookup); - - if (lookup->cache_id_type == NMP_CACHE_ID_TYPE_OBJECT_TYPE) { - nm_assert(entry == nmp_cache_lookup_entry(cache, entry->obj)); - return entry; - } - - /* we only track the dirty flag for the OBJECT-TYPE index. That means, - * for other lookup types we need to check the dirty flag of the main-entry. */ - main_entry = nmp_cache_lookup_entry(cache, entry->obj); - - nm_assert(main_entry); - nm_assert(main_entry->obj == entry->obj); - - return main_entry; -} - -void nmp_cache_dirty_set_all_main(NMPCache *cache, const NMPLookup *lookup); - -NMPCache *nmp_cache_new(NMDedupMultiIndex *multi_idx, gboolean use_udev); -void nmp_cache_free(NMPCache *cache); - -static inline void -ASSERT_nmp_cache_ops(const NMPCache * cache, - NMPCacheOpsType ops_type, - const NMPObject *obj_old, - const NMPObject *obj_new) -{ -#if NM_MORE_ASSERTS - nm_assert(cache); - nm_assert(obj_old || obj_new); - nm_assert(!obj_old - || (NMP_OBJECT_IS_VALID(obj_old) && !NMP_OBJECT_IS_STACKINIT(obj_old) - && nmp_object_is_alive(obj_old))); - nm_assert(!obj_new - || (NMP_OBJECT_IS_VALID(obj_new) && !NMP_OBJECT_IS_STACKINIT(obj_new) - && nmp_object_is_alive(obj_new))); - - switch (ops_type) { - case NMP_CACHE_OPS_UNCHANGED: - nm_assert(obj_old == obj_new); - break; - case NMP_CACHE_OPS_ADDED: - nm_assert(!obj_old && obj_new); - break; - case NMP_CACHE_OPS_UPDATED: - nm_assert(obj_old && obj_new && obj_old != obj_new); - break; - case NMP_CACHE_OPS_REMOVED: - nm_assert(obj_old && !obj_new); - break; - default: - nm_assert_not_reached(); - } - - nm_assert(obj_new == NULL || obj_old == NULL || nmp_object_id_equal(obj_new, obj_old)); - nm_assert(!obj_old || !obj_new - || NMP_OBJECT_GET_CLASS(obj_old) == NMP_OBJECT_GET_CLASS(obj_new)); - - nm_assert(obj_new == nmp_cache_lookup_obj(cache, obj_new ?: obj_old)); -#endif -} - -const NMDedupMultiHeadEntry * -nm_platform_lookup_all(NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj); - -const NMDedupMultiEntry * -nm_platform_lookup_entry(NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj); - -static inline const NMPObject * -nm_platform_lookup_obj(NMPlatform *platform, NMPCacheIdType cache_id_type, const NMPObject *obj) -{ - return nm_dedup_multi_entry_get_obj(nm_platform_lookup_entry(platform, cache_id_type, obj)); -} - -static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_obj_type(NMPlatform *platform, NMPObjectType obj_type) -{ - NMPLookup lookup; - - nmp_lookup_init_obj_type(&lookup, obj_type); - return nm_platform_lookup(platform, &lookup); -} - -static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_link_by_ifname(NMPlatform *platform, const char *ifname) -{ - NMPLookup lookup; - - nmp_lookup_init_link_by_ifname(&lookup, ifname); - return nm_platform_lookup(platform, &lookup); -} - -static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_object(NMPlatform *platform, NMPObjectType obj_type, int ifindex) -{ - NMPLookup lookup; - - nmp_lookup_init_object(&lookup, obj_type, ifindex); - return nm_platform_lookup(platform, &lookup); -} - -static inline GPtrArray * -nm_platform_lookup_object_clone(NMPlatform * platform, - NMPObjectType obj_type, - int ifindex, - NMPObjectPredicateFunc predicate, - gpointer user_data) -{ - NMPLookup lookup; - - nmp_lookup_init_object(&lookup, obj_type, ifindex); - return nm_platform_lookup_clone(platform, &lookup, predicate, user_data); -} - -static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_route_default(NMPlatform *platform, NMPObjectType obj_type) -{ - NMPLookup lookup; - - nmp_lookup_init_route_default(&lookup, obj_type); - return nm_platform_lookup(platform, &lookup); -} - -static inline GPtrArray * -nm_platform_lookup_route_default_clone(NMPlatform * platform, - NMPObjectType obj_type, - NMPObjectPredicateFunc predicate, - gpointer user_data) -{ - NMPLookup lookup; - - nmp_lookup_init_route_default(&lookup, obj_type); - return nm_platform_lookup_clone(platform, &lookup, predicate, user_data); -} - -static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_ip4_route_by_weak_id(NMPlatform *platform, - in_addr_t network, - guint plen, - guint32 metric, - guint8 tos) -{ - NMPLookup lookup; - - nmp_lookup_init_ip4_route_by_weak_id(&lookup, network, plen, metric, tos); - return nm_platform_lookup(platform, &lookup); -} - -static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_ip6_route_by_weak_id(NMPlatform * platform, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen) -{ - NMPLookup lookup; - - nmp_lookup_init_ip6_route_by_weak_id(&lookup, network, plen, metric, src, src_plen); - return nm_platform_lookup(platform, &lookup); -} - -static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_object_by_addr_family(NMPlatform * platform, - NMPObjectType obj_type, - int addr_family) -{ - NMPLookup lookup; - - nmp_lookup_init_object_by_addr_family(&lookup, obj_type, addr_family); - return nm_platform_lookup(platform, &lookup); -} - -/*****************************************************************************/ - -static inline const char * -nmp_object_link_get_ifname(const NMPObject *obj) -{ - if (!obj) - return NULL; - return NMP_OBJECT_CAST_LINK(obj)->name; -} - -static inline gboolean -nmp_object_ip_route_is_best_defaut_route(const NMPObject *obj) -{ - const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE(obj); - - /* return whether @obj is considered a default-route. - * - * NMIP4Config/NMIP6Config tracks the (best) default-route explicitly, because - * at various places we act differently depending on whether there is a default-route - * configured. - * - * Note that this only considers the main routing table. */ - return r && NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r) - && nm_platform_route_table_is_main(r->table_coerced) - && r->type_coerced == nm_platform_route_type_coerce(1 /* RTN_UNICAST */); -} - -static inline gboolean -nmp_object_ip6_address_is_not_link_local(const NMPObject *obj) -{ - return !IN6_IS_ADDR_LINKLOCAL(&NMP_OBJECT_CAST_IP6_ADDRESS(obj)->address); -} - -/*****************************************************************************/ - -static inline gboolean -nm_platform_dedup_multi_iter_next_obj(NMDedupMultiIter *ipconf_iter, - const NMPObject **out_obj, - NMPObjectType assert_obj_type) -{ - gboolean has_next; - - has_next = nm_dedup_multi_iter_next(ipconf_iter); - nm_assert(assert_obj_type == NMP_OBJECT_TYPE_UNKNOWN || !has_next - || NMP_OBJECT_GET_TYPE(ipconf_iter->current->obj) == assert_obj_type); - NM_SET_OUT(out_obj, has_next ? ipconf_iter->current->obj : NULL); - return has_next; -} - -static inline gboolean -nm_platform_dedup_multi_iter_next_ip4_address(NMDedupMultiIter * ipconf_iter, - const NMPlatformIP4Address **out_address) -{ - gboolean has_next; - - has_next = nm_dedup_multi_iter_next(ipconf_iter); - if (out_address) - *out_address = has_next ? NMP_OBJECT_CAST_IP4_ADDRESS(ipconf_iter->current->obj) : NULL; - return has_next; -} - -static inline gboolean -nm_platform_dedup_multi_iter_next_ip4_route(NMDedupMultiIter * ipconf_iter, - const NMPlatformIP4Route **out_route) -{ - gboolean has_next; - - has_next = nm_dedup_multi_iter_next(ipconf_iter); - if (out_route) - *out_route = has_next ? NMP_OBJECT_CAST_IP4_ROUTE(ipconf_iter->current->obj) : NULL; - return has_next; -} - -static inline gboolean -nm_platform_dedup_multi_iter_next_ip6_address(NMDedupMultiIter * ipconf_iter, - const NMPlatformIP6Address **out_address) -{ - gboolean has_next; - - has_next = nm_dedup_multi_iter_next(ipconf_iter); - if (out_address) - *out_address = has_next ? NMP_OBJECT_CAST_IP6_ADDRESS(ipconf_iter->current->obj) : NULL; - return has_next; -} - -static inline gboolean -nm_platform_dedup_multi_iter_next_ip6_route(NMDedupMultiIter * ipconf_iter, - const NMPlatformIP6Route **out_route) -{ - gboolean has_next; - - has_next = nm_dedup_multi_iter_next(ipconf_iter); - if (out_route) - *out_route = has_next ? NMP_OBJECT_CAST_IP6_ROUTE(ipconf_iter->current->obj) : NULL; - return has_next; -} - -#endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/nmp-rules-manager.c b/src/platform/nmp-rules-manager.c deleted file mode 100644 index d139c9a3..00000000 --- a/src/platform/nmp-rules-manager.c +++ /dev/null @@ -1,808 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-default.h" - -#include "nmp-rules-manager.h" - -#include <linux/fib_rules.h> -#include <linux/rtnetlink.h> - -#include "nm-std-aux/c-list-util.h" -#include "nmp-object.h" - -/*****************************************************************************/ - -struct _NMPRulesManager { - NMPlatform *platform; - GHashTable *by_obj; - GHashTable *by_user_tag; - GHashTable *by_data; - guint ref_count; -}; - -/*****************************************************************************/ - -static void _rules_init(NMPRulesManager *self); - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG_PREFIX_NAME "rules-manager" - -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static gboolean -NMP_IS_RULES_MANAGER(gpointer self) -{ - return self && ((NMPRulesManager *) self)->ref_count > 0 - && NM_IS_PLATFORM(((NMPRulesManager *) self)->platform); -} - -#define _USER_TAG_LOG(user_tag) nm_hash_obfuscate_ptr(1240261787u, (user_tag)) - -/*****************************************************************************/ - -typedef struct { - const NMPObject *obj; - gconstpointer user_tag; - CList obj_lst; - CList user_tag_lst; - - /* track_priority_val zero is special: those are weakly tracked rules. - * That means: NetworkManager will restore them only if it removed them earlier. - * But it will not remove or add them otherwise. - * - * Otherwise, the track_priority_val goes together with track_priority_present. - * In case of one rule being tracked multiple times (with different priorities), - * the one with higher priority wins. See _rules_obj_get_best_data(). - * Then, the winning present state either enforces that the rule is present - * or absent. - * - * If a rules is not tracked at all, it is ignored by NetworkManager. Assuming - * that it was added externally by the user. But unlike weakly tracked rules, - * NM will *not* restore such rules if NetworkManager themself removed them. */ - guint32 track_priority_val; - bool track_priority_present : 1; - - bool dirty : 1; -} RulesData; - -typedef enum { - CONFIG_STATE_NONE = 0, - CONFIG_STATE_ADDED_BY_US = 1, - CONFIG_STATE_REMOVED_BY_US = 2, - - /* ConfigState encodes whether the rule was touched by us at all (CONFIG_STATE_NONE). - * - * Maybe we would only need to track whether we touched the rule at all. But we - * track it more in detail what we did: did we add it (CONFIG_STATE_ADDED_BY_US) - * or did we remove it (CONFIG_STATE_REMOVED_BY_US)? - * Finally, we need CONFIG_STATE_OWNED_BY_US, which means that we didn't actively - * add/remove it, but whenever we are about to undo the add/remove, we need to do it. - * In that sense, CONFIG_STATE_OWNED_BY_US is really just a flag that we unconditionally - * force the state next time when necessary. */ - CONFIG_STATE_OWNED_BY_US = 3, -} ConfigState; - -typedef struct { - const NMPObject *obj; - CList obj_lst_head; - - /* indicates whether we configured/removed the rule (during sync()). We need that, so - * if the rule gets untracked, that we know to remove/restore it. - * - * This makes NMPRulesManager stateful (beyond the configuration that indicates - * which rules are tracked). - * After a restart, NetworkManager would no longer remember which rules were added - * by us. - * - * That is partially fixed by NetworkManager taking over the rules that it - * actively configures (see %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG). */ - ConfigState config_state; -} RulesObjData; - -typedef struct { - gconstpointer user_tag; - CList user_tag_lst_head; -} RulesUserTagData; - -/*****************************************************************************/ - -static void _rules_data_untrack(NMPRulesManager *self, - RulesData * rules_data, - gboolean remove_user_tag_data, - gboolean make_owned_by_us); - -/*****************************************************************************/ - -static void -_rules_data_assert(const RulesData *rules_data, gboolean linked) -{ - nm_assert(rules_data); - nm_assert(NMP_OBJECT_GET_TYPE(rules_data->obj) == NMP_OBJECT_TYPE_ROUTING_RULE); - nm_assert(nmp_object_is_visible(rules_data->obj)); - nm_assert(rules_data->user_tag); - nm_assert(!linked || !c_list_is_empty(&rules_data->obj_lst)); - nm_assert(!linked || !c_list_is_empty(&rules_data->user_tag_lst)); -} - -static guint -_rules_data_hash(gconstpointer data) -{ - const RulesData *rules_data = data; - NMHashState h; - - _rules_data_assert(rules_data, FALSE); - - nm_hash_init(&h, 269297543u); - nm_platform_routing_rule_hash_update(NMP_OBJECT_CAST_ROUTING_RULE(rules_data->obj), - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, - &h); - nm_hash_update_val(&h, rules_data->user_tag); - return nm_hash_complete(&h); -} - -static gboolean -_rules_data_equal(gconstpointer data_a, gconstpointer data_b) -{ - const RulesData *rules_data_a = data_a; - const RulesData *rules_data_b = data_b; - - _rules_data_assert(rules_data_a, FALSE); - _rules_data_assert(rules_data_b, FALSE); - - return rules_data_a->user_tag == rules_data_b->user_tag - && (nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(rules_data_a->obj), - NMP_OBJECT_CAST_ROUTING_RULE(rules_data_b->obj), - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) - == 0); -} - -static void -_rules_data_destroy(gpointer data) -{ - RulesData *rules_data = data; - - _rules_data_assert(rules_data, FALSE); - - c_list_unlink_stale(&rules_data->obj_lst); - c_list_unlink_stale(&rules_data->user_tag_lst); - nmp_object_unref(rules_data->obj); - g_slice_free(RulesData, rules_data); -} - -static const RulesData * -_rules_obj_get_best_data(RulesObjData *obj_data) -{ - RulesData * rules_data; - const RulesData *rd_best = NULL; - - c_list_for_each_entry (rules_data, &obj_data->obj_lst_head, obj_lst) { - _rules_data_assert(rules_data, TRUE); - - if (rd_best) { - if (rd_best->track_priority_val > rules_data->track_priority_val) - continue; - if (rd_best->track_priority_val == rules_data->track_priority_val) { - if (rd_best->track_priority_present || !rules_data->track_priority_present) { - /* if the priorities are identical, then "present" wins over - * "!present" (absent). */ - continue; - } - } - } - - rd_best = rules_data; - } - - return rd_best; -} - -static guint -_rules_obj_hash(gconstpointer data) -{ - const RulesObjData *obj_data = data; - NMHashState h; - - nm_hash_init(&h, 432817559u); - nm_platform_routing_rule_hash_update(NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj), - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, - &h); - return nm_hash_complete(&h); -} - -static gboolean -_rules_obj_equal(gconstpointer data_a, gconstpointer data_b) -{ - const RulesObjData *obj_data_a = data_a; - const RulesObjData *obj_data_b = data_b; - - return (nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(obj_data_a->obj), - NMP_OBJECT_CAST_ROUTING_RULE(obj_data_b->obj), - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) - == 0); -} - -static void -_rules_obj_destroy(gpointer data) -{ - RulesObjData *obj_data = data; - - c_list_unlink_stale(&obj_data->obj_lst_head); - nmp_object_unref(obj_data->obj); - g_slice_free(RulesObjData, obj_data); -} - -static guint -_rules_user_tag_hash(gconstpointer data) -{ - const RulesUserTagData *user_tag_data = data; - - return nm_hash_val(644693447u, user_tag_data->user_tag); -} - -static gboolean -_rules_user_tag_equal(gconstpointer data_a, gconstpointer data_b) -{ - const RulesUserTagData *user_tag_data_a = data_a; - const RulesUserTagData *user_tag_data_b = data_b; - - return user_tag_data_a->user_tag == user_tag_data_b->user_tag; -} - -static void -_rules_user_tag_destroy(gpointer data) -{ - RulesUserTagData *user_tag_data = data; - - c_list_unlink_stale(&user_tag_data->user_tag_lst_head); - g_slice_free(RulesUserTagData, user_tag_data); -} - -static RulesData * -_rules_data_lookup(GHashTable *by_data, const NMPObject *obj, gconstpointer user_tag) -{ - RulesData rules_data_needle = { - .obj = obj, - .user_tag = user_tag, - }; - - return g_hash_table_lookup(by_data, &rules_data_needle); -} - -/** - * nmp_rules_manager_track: - * @self: the #NMPRulesManager instance - * @routing_rule: the #NMPlatformRoutingRule to track or untrack - * @track_priority: the priority for tracking the rule. Note that - * negative values indicate a forced absence of the rule. Priorities - * are compared with their absolute values (with higher absolute - * value being more important). For example, if you track the same - * rule twice, once with priority -5 and +10, then the rule is - * present (because the positive number is more important). - * The special value 0 indicates weakly-tracked rules. - * @user_tag: the tag associated with tracking this rule. The same tag - * must be used to untrack the rule later. - * @user_tag_untrack: if not %NULL, at the same time untrack this user-tag - * for the same rule. Note that this is different from a plain nmp_rules_manager_untrack(), - * because it enforces ownership of the now tracked rule. On the other hand, - * a plain nmp_rules_manager_untrack() merely forgets about the tracking. - * The purpose here is to set this to %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG. - */ -void -nmp_rules_manager_track(NMPRulesManager * self, - const NMPlatformRoutingRule *routing_rule, - gint32 track_priority, - gconstpointer user_tag, - gconstpointer user_tag_untrack) -{ - NMPObject obj_stack; - const NMPObject * p_obj_stack; - RulesData * rules_data; - RulesObjData * obj_data; - RulesUserTagData *user_tag_data; - gboolean changed = FALSE; - guint32 track_priority_val; - gboolean track_priority_present; - - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - g_return_if_fail(routing_rule); - g_return_if_fail(user_tag); - nm_assert(track_priority != G_MININT32); - - _rules_init(self); - - p_obj_stack = nmp_object_stackinit(&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); - - nm_assert(nmp_object_is_visible(p_obj_stack)); - - if (track_priority >= 0) { - track_priority_val = track_priority; - track_priority_present = TRUE; - } else { - track_priority_val = -track_priority; - track_priority_present = FALSE; - } - - rules_data = _rules_data_lookup(self->by_data, p_obj_stack, user_tag); - - if (!rules_data) { - rules_data = g_slice_new(RulesData); - *rules_data = (RulesData){ - .obj = nm_dedup_multi_index_obj_intern(nm_platform_get_multi_idx(self->platform), - p_obj_stack), - .user_tag = user_tag, - .track_priority_val = track_priority_val, - .track_priority_present = track_priority_present, - .dirty = FALSE, - }; - g_hash_table_add(self->by_data, rules_data); - - obj_data = g_hash_table_lookup(self->by_obj, &rules_data->obj); - if (!obj_data) { - obj_data = g_slice_new(RulesObjData); - *obj_data = (RulesObjData){ - .obj = nmp_object_ref(rules_data->obj), - .obj_lst_head = C_LIST_INIT(obj_data->obj_lst_head), - .config_state = CONFIG_STATE_NONE, - }; - g_hash_table_add(self->by_obj, obj_data); - } - c_list_link_tail(&obj_data->obj_lst_head, &rules_data->obj_lst); - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &rules_data->user_tag); - if (!user_tag_data) { - user_tag_data = g_slice_new(RulesUserTagData); - *user_tag_data = (RulesUserTagData){ - .user_tag = user_tag, - .user_tag_lst_head = C_LIST_INIT(user_tag_data->user_tag_lst_head), - }; - g_hash_table_add(self->by_user_tag, user_tag_data); - } - c_list_link_tail(&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst); - changed = TRUE; - } else { - rules_data->dirty = FALSE; - if (rules_data->track_priority_val != track_priority_val - || rules_data->track_priority_present != track_priority_present) { - rules_data->track_priority_val = track_priority_val; - rules_data->track_priority_present = track_priority_present; - changed = TRUE; - } - } - - if (user_tag_untrack) { - if (user_tag != user_tag_untrack) { - RulesData *rules_data_untrack; - - rules_data_untrack = _rules_data_lookup(self->by_data, p_obj_stack, user_tag_untrack); - if (rules_data_untrack) - _rules_data_untrack(self, rules_data_untrack, FALSE, TRUE); - } else - nm_assert_not_reached(); - } - - _rules_data_assert(rules_data, TRUE); - - if (changed) { - _LOGD("routing-rule: track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] \"%s\")", - _USER_TAG_LOG(rules_data->user_tag), - (rules_data->track_priority_val == 0 - ? "" - : (rules_data->track_priority_present ? "+" : "-")), - (guint) rules_data->track_priority_val, - nmp_object_to_string(rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - } -} - -static void -_rules_data_untrack(NMPRulesManager *self, - RulesData * rules_data, - gboolean remove_user_tag_data, - gboolean make_owned_by_us) -{ - RulesObjData *obj_data; - - nm_assert(NMP_IS_RULES_MANAGER(self)); - _rules_data_assert(rules_data, TRUE); - nm_assert(self->by_data); - nm_assert(g_hash_table_lookup(self->by_data, rules_data) == rules_data); - - _LOGD("routing-rule: untrack [" NM_HASH_OBFUSCATE_PTR_FMT "] \"%s\"", - _USER_TAG_LOG(rules_data->user_tag), - nmp_object_to_string(rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - -#if NM_MORE_ASSERTS - { - RulesUserTagData *user_tag_data; - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &rules_data->user_tag); - nm_assert(user_tag_data); - nm_assert(c_list_contains(&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst)); - } -#endif - - nm_assert(!c_list_is_empty(&rules_data->user_tag_lst)); - - obj_data = g_hash_table_lookup(self->by_obj, &rules_data->obj); - nm_assert(obj_data); - nm_assert(c_list_contains(&obj_data->obj_lst_head, &rules_data->obj_lst)); - nm_assert(obj_data == g_hash_table_lookup(self->by_obj, &rules_data->obj)); - - if (make_owned_by_us) { - if (obj_data->config_state == CONFIG_STATE_NONE) { - /* we need to mark this entry that it requires a touch on the next - * sync. */ - obj_data->config_state = CONFIG_STATE_OWNED_BY_US; - } - } else if (remove_user_tag_data && c_list_length_is(&rules_data->user_tag_lst, 1)) - g_hash_table_remove(self->by_user_tag, &rules_data->user_tag); - - /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry - * around for the next sync -- so that we can undo what we did earlier. */ - if (obj_data->config_state == CONFIG_STATE_NONE && c_list_length_is(&rules_data->obj_lst, 1)) - g_hash_table_remove(self->by_obj, &rules_data->obj); - - g_hash_table_remove(self->by_data, rules_data); -} - -void -nmp_rules_manager_untrack(NMPRulesManager * self, - const NMPlatformRoutingRule *routing_rule, - gconstpointer user_tag) -{ - NMPObject obj_stack; - const NMPObject *p_obj_stack; - RulesData * rules_data; - - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - g_return_if_fail(routing_rule); - g_return_if_fail(user_tag); - - _rules_init(self); - - p_obj_stack = nmp_object_stackinit(&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); - - nm_assert(nmp_object_is_visible(p_obj_stack)); - - rules_data = _rules_data_lookup(self->by_data, p_obj_stack, user_tag); - if (rules_data) - _rules_data_untrack(self, rules_data, TRUE, FALSE); -} - -void -nmp_rules_manager_set_dirty(NMPRulesManager *self, gconstpointer user_tag) -{ - RulesData * rules_data; - RulesUserTagData *user_tag_data; - - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - g_return_if_fail(user_tag); - - if (!self->by_data) - return; - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag); - if (!user_tag_data) - return; - - c_list_for_each_entry (rules_data, &user_tag_data->user_tag_lst_head, user_tag_lst) - rules_data->dirty = TRUE; -} - -void -nmp_rules_manager_untrack_all(NMPRulesManager *self, - gconstpointer user_tag, - gboolean all /* or only dirty */) -{ - RulesData * rules_data; - RulesData * rules_data_safe; - RulesUserTagData *user_tag_data; - - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - g_return_if_fail(user_tag); - - if (!self->by_data) - return; - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag); - if (!user_tag_data) - return; - - c_list_for_each_entry_safe (rules_data, - rules_data_safe, - &user_tag_data->user_tag_lst_head, - user_tag_lst) { - if (all || rules_data->dirty) - _rules_data_untrack(self, rules_data, FALSE, FALSE); - } - if (c_list_is_empty(&user_tag_data->user_tag_lst_head)) - g_hash_table_remove(self->by_user_tag, user_tag_data); -} - -void -nmp_rules_manager_sync(NMPRulesManager *self, gboolean keep_deleted_rules) -{ - const NMDedupMultiHeadEntry *pl_head_entry; - NMDedupMultiIter pl_iter; - const NMPObject * plobj; - gs_unref_ptrarray GPtrArray *rules_to_delete = NULL; - RulesObjData * obj_data; - GHashTableIter h_iter; - guint i; - const RulesData * rd_best; - - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - - if (!self->by_data) - return; - - _LOGD("sync%s", keep_deleted_rules ? " (don't remove any rules)" : ""); - - pl_head_entry = nm_platform_lookup_obj_type(self->platform, NMP_OBJECT_TYPE_ROUTING_RULE); - if (pl_head_entry) { - nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) { - obj_data = g_hash_table_lookup(self->by_obj, &plobj); - - if (!obj_data) { - /* this rule is not tracked. It was externally added, hence we - * ignore it. */ - continue; - } - - rd_best = _rules_obj_get_best_data(obj_data); - if (rd_best) { - if (rd_best->track_priority_present) { - if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) - obj_data->config_state = CONFIG_STATE_ADDED_BY_US; - continue; - } - if (rd_best->track_priority_val == 0) { - if (!NM_IN_SET(obj_data->config_state, - CONFIG_STATE_ADDED_BY_US, - CONFIG_STATE_OWNED_BY_US)) { - obj_data->config_state = CONFIG_STATE_NONE; - continue; - } - obj_data->config_state = CONFIG_STATE_NONE; - } - } - - if (keep_deleted_rules) { - _LOGD("forget/leak rule added by us: %s", - nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - continue; - } - - if (!rules_to_delete) - rules_to_delete = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - - g_ptr_array_add(rules_to_delete, (gpointer) nmp_object_ref(plobj)); - - obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; - } - } - - if (rules_to_delete) { - for (i = 0; i < rules_to_delete->len; i++) - nm_platform_object_delete(self->platform, rules_to_delete->pdata[i]); - } - - g_hash_table_iter_init(&h_iter, self->by_obj); - while (g_hash_table_iter_next(&h_iter, (gpointer *) &obj_data, NULL)) { - rd_best = _rules_obj_get_best_data(obj_data); - - if (!rd_best) { - g_hash_table_iter_remove(&h_iter); - continue; - } - - if (!rd_best->track_priority_present) { - if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) - obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; - continue; - } - if (rd_best->track_priority_val == 0) { - if (!NM_IN_SET(obj_data->config_state, - CONFIG_STATE_REMOVED_BY_US, - CONFIG_STATE_OWNED_BY_US)) { - obj_data->config_state = CONFIG_STATE_NONE; - continue; - } - obj_data->config_state = CONFIG_STATE_NONE; - } - - plobj = - nm_platform_lookup_obj(self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj); - if (plobj) - continue; - - obj_data->config_state = CONFIG_STATE_ADDED_BY_US; - nm_platform_routing_rule_add(self->platform, - NMP_NLM_FLAG_ADD, - NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj)); - } -} - -void -nmp_rules_manager_track_from_platform(NMPRulesManager *self, - NMPlatform * platform, - int addr_family, - gint32 tracking_priority, - gconstpointer user_tag) -{ - NMPLookup lookup; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject * o; - - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - - if (!platform) - platform = self->platform; - else - g_return_if_fail(NM_IS_PLATFORM(platform)); - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - head_entry = nm_platform_lookup(platform, &lookup); - nmp_cache_iter_for_each (&iter, head_entry, &o) { - const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE(o); - - if (addr_family != AF_UNSPEC && rr->addr_family != addr_family) - continue; - - nmp_rules_manager_track(self, rr, tracking_priority, user_tag, NULL); - } -} - -/*****************************************************************************/ - -void -nmp_rules_manager_track_default(NMPRulesManager *self, - int addr_family, - gint32 track_priority, - gconstpointer user_tag) -{ - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - /* track the default rules. See also `man ip-rule`. */ - - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) { - nmp_rules_manager_track(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET, - .priority = 0, - .table = RT_TABLE_LOCAL, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - nmp_rules_manager_track(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET, - .priority = 32766, - .table = RT_TABLE_MAIN, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - nmp_rules_manager_track(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET, - .priority = 32767, - .table = RT_TABLE_DEFAULT, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - } - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) { - nmp_rules_manager_track(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET6, - .priority = 0, - .table = RT_TABLE_LOCAL, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - nmp_rules_manager_track(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET6, - .priority = 32766, - .table = RT_TABLE_MAIN, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - } -} - -static void -_rules_init(NMPRulesManager *self) -{ - if (self->by_data) - return; - - self->by_data = - g_hash_table_new_full(_rules_data_hash, _rules_data_equal, NULL, _rules_data_destroy); - self->by_obj = - g_hash_table_new_full(_rules_obj_hash, _rules_obj_equal, NULL, _rules_obj_destroy); - self->by_user_tag = g_hash_table_new_full(_rules_user_tag_hash, - _rules_user_tag_equal, - NULL, - _rules_user_tag_destroy); -} - -/*****************************************************************************/ - -NMPRulesManager * -nmp_rules_manager_new(NMPlatform *platform) -{ - NMPRulesManager *self; - - g_return_val_if_fail(NM_IS_PLATFORM(platform), NULL); - - self = g_slice_new(NMPRulesManager); - *self = (NMPRulesManager){ - .ref_count = 1, - .platform = g_object_ref(platform), - }; - return self; -} - -void -nmp_rules_manager_ref(NMPRulesManager *self) -{ - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - - self->ref_count++; -} - -void -nmp_rules_manager_unref(NMPRulesManager *self) -{ - g_return_if_fail(NMP_IS_RULES_MANAGER(self)); - - if (--self->ref_count > 0) - return; - - if (self->by_data) { - g_hash_table_destroy(self->by_user_tag); - g_hash_table_destroy(self->by_obj); - g_hash_table_destroy(self->by_data); - } - g_object_unref(self->platform); - g_slice_free(NMPRulesManager, self); -} diff --git a/src/platform/nmp-rules-manager.h b/src/platform/nmp-rules-manager.h deleted file mode 100644 index 29c1295c..00000000 --- a/src/platform/nmp-rules-manager.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#ifndef __NMP_RULES_MANAGER_H__ -#define __NMP_RULES_MANAGER_H__ - -#include "nm-platform.h" - -/*****************************************************************************/ - -#define NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG ((const void *) nmp_rules_manager_new) - -typedef struct _NMPRulesManager NMPRulesManager; - -NMPRulesManager *nmp_rules_manager_new(NMPlatform *platform); - -void nmp_rules_manager_ref(NMPRulesManager *self); -void nmp_rules_manager_unref(NMPRulesManager *self); - -#define nm_auto_unref_rules_manager nm_auto(_nmp_rules_manager_unref) -NM_AUTO_DEFINE_FCN0(NMPRulesManager *, _nmp_rules_manager_unref, nmp_rules_manager_unref); - -void nmp_rules_manager_track(NMPRulesManager * self, - const NMPlatformRoutingRule *routing_rule, - gint32 track_priority, - gconstpointer user_tag, - gconstpointer user_tag_untrack); - -void nmp_rules_manager_track_default(NMPRulesManager *self, - int addr_family, - gint32 track_priority, - gconstpointer user_tag); - -void nmp_rules_manager_track_from_platform(NMPRulesManager *self, - NMPlatform * platform, - int addr_family, - gint32 tracking_priority, - gconstpointer user_tag); - -void nmp_rules_manager_untrack(NMPRulesManager * self, - const NMPlatformRoutingRule *routing_rule, - gconstpointer user_tag); - -void nmp_rules_manager_set_dirty(NMPRulesManager *self, gconstpointer user_tag); - -void nmp_rules_manager_untrack_all(NMPRulesManager *self, - gconstpointer user_tag, - gboolean all /* or only dirty */); - -void nmp_rules_manager_sync(NMPRulesManager *self, gboolean keep_deleted_rules); - -/*****************************************************************************/ - -#endif /* __NMP_RULES_MANAGER_H__ */ diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build deleted file mode 100644 index f96850cc..00000000 --- a/src/platform/tests/meson.build +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1+ - -test_fake_c_flags = test_c_flags + ['-DSETUP=nm_fake_platform_setup'] -test_linux_c_flags = test_c_flags + ['-DSETUP=nm_linux_platform_setup'] - -test_units = [ - ['test-address-fake', 'test-address.c', test_fake_c_flags, default_test_timeout], - ['test-address-linux', 'test-address.c', test_linux_c_flags, default_test_timeout], - ['test-cleanup-fake', 'test-cleanup.c', test_fake_c_flags, default_test_timeout], - ['test-cleanup-linux', 'test-cleanup.c', test_linux_c_flags, default_test_timeout], - ['test-link-fake', 'test-link.c', test_fake_c_flags, default_test_timeout], - ['test-link-linux', 'test-link.c', test_linux_c_flags, 900], - ['test-nmp-object', 'test-nmp-object.c', test_c_flags, default_test_timeout], - ['test-platform-general', 'test-platform-general.c', test_c_flags, default_test_timeout], - ['test-route-fake', 'test-route.c', test_fake_c_flags, default_test_timeout], - ['test-route-linux', 'test-route.c', test_linux_c_flags, default_test_timeout], - ['test-tc-fake', 'test-tc.c', test_fake_c_flags, default_test_timeout], - ['test-tc-linux', 'test-tc.c', test_linux_c_flags, default_test_timeout], -] - -foreach test_unit: test_units - exe = executable( - test_unit[0], - test_unit[1], - dependencies: libnetwork_manager_test_dep, - c_args: test_unit[2], - ) - test( - 'platform/' + test_unit[0], - test_script, - timeout: test_unit[3], - args: test_args + [exe.full_path()], - ) -endforeach - -name = 'monitor' - -executable( - name, - name + '.c', - dependencies: libnetwork_manager_test_dep, - c_args: test_c_flags, -) diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c deleted file mode 100644 index a8d0fad2..00000000 --- a/src/platform/tests/monitor.c +++ /dev/null @@ -1,78 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <stdlib.h> -#include <syslog.h> - -#include "platform/nm-linux-platform.h" - -#include "nm-test-utils-core.h" - -NMTST_DEFINE(); - -static struct { - gboolean persist; -} global_opt = { - .persist = TRUE, -}; - -static gboolean -read_argv(int *argc, char ***argv) -{ - GOptionContext *context; - GOptionEntry options[] = { - {"no-persist", - 'P', - G_OPTION_FLAG_REVERSE, - G_OPTION_ARG_NONE, - &global_opt.persist, - "Exit after processing netlink messages", - NULL}, - {0}, - }; - gs_free_error GError *error = NULL; - - context = g_option_context_new(NULL); - g_option_context_set_summary(context, "Monitor netlink events in NMPlatform."); - g_option_context_add_main_entries(context, options, NULL); - - if (!g_option_context_parse(context, argc, argv, &error)) { - g_warning("Error parsing command line arguments: %s", error->message); - g_option_context_free(context); - return FALSE; - } - - g_option_context_free(context); - return TRUE; -} - -int -main(int argc, char **argv) -{ - GMainLoop *loop; - - if (!g_getenv("G_MESSAGES_DEBUG")) - g_setenv("G_MESSAGES_DEBUG", "all", TRUE); - - nmtst_init_with_logging(&argc, &argv, "DEBUG", "ALL"); - - if (!read_argv(&argc, &argv)) - return 2; - - nm_log_info(LOGD_PLATFORM, "platform monitor start"); - - loop = g_main_loop_new(NULL, FALSE); - - nm_linux_platform_setup(); - - if (global_opt.persist) - g_main_loop_run(loop); - - g_main_loop_unref(loop); - - return EXIT_SUCCESS; -} diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c deleted file mode 100644 index 5ea30a57..00000000 --- a/src/platform/tests/test-address.c +++ /dev/null @@ -1,465 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "test-common.h" - -#define IP4_ADDRESS "192.0.2.1" -#define IP4_ADDRESS_PEER "192.0.2.2" -#define IP4_ADDRESS_PEER2 "192.0.3.1" -#define IP4_PLEN 24 -#define IP6_ADDRESS "2001:db8:a:b:1:2:3:4" -#define IP6_PLEN 64 - -#define DEVICE_IFINDEX NMTSTP_ENV1_IFINDEX -#define EX NMTSTP_ENV1_EX - -/*****************************************************************************/ - -static void -ip4_address_callback(NMPlatform * platform, - NMPObjectType obj_type, - int ifindex, - NMPlatformIP4Address * received, - NMPlatformSignalChangeType change_type, - SignalData * data) -{ - g_assert(received); - g_assert_cmpint(received->ifindex, ==, ifindex); - g_assert(data && data->name); - g_assert_cmpstr(data->name, ==, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED); - - if (data->ifindex && data->ifindex != received->ifindex) - return; - if (data->change_type != change_type) - return; - - if (data->loop) - g_main_loop_quit(data->loop); - - data->received_count++; - _LOGD("Received signal '%s' %dth time.", data->name, data->received_count); -} - -static void -ip6_address_callback(NMPlatform * platform, - NMPObjectType obj_type, - int ifindex, - NMPlatformIP6Address * received, - NMPlatformSignalChangeType change_type, - SignalData * data) -{ - g_assert(received); - g_assert_cmpint(received->ifindex, ==, ifindex); - g_assert(data && data->name); - g_assert_cmpstr(data->name, ==, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED); - - if (data->ifindex && data->ifindex != received->ifindex) - return; - if (data->change_type != change_type) - return; - - if (data->loop) - g_main_loop_quit(data->loop); - - data->received_count++; - _LOGD("Received signal '%s' %dth time.", data->name, data->received_count); -} - -/*****************************************************************************/ - -static void -test_ip4_address_general(void) -{ - const int ifindex = DEVICE_IFINDEX; - SignalData *address_added = add_signal_ifindex(NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip4_address_callback, - ifindex); - SignalData *address_changed = add_signal_ifindex(NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_CHANGED, - ip4_address_callback, - ifindex); - SignalData *address_removed = add_signal_ifindex(NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip4_address_callback, - ifindex); - GArray * addresses; - NMPlatformIP4Address *address; - in_addr_t addr; - guint32 lifetime = 2000; - guint32 preferred = 1000; - - inet_pton(AF_INET, IP4_ADDRESS, &addr); - - /* Add address */ - g_assert(!nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); - nmtstp_ip4_address_add(NULL, EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL); - g_assert(nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); - accept_signal(address_added); - - /* Add address again (aka update) */ - nmtstp_ip4_address_add(NULL, - EX, - ifindex, - addr, - IP4_PLEN, - addr, - lifetime + 100, - preferred + 50, - 0, - NULL); - accept_signals(address_changed, 0, 1); - - /* Test address listing */ - addresses = nmtstp_platform_ip4_address_get_all(NM_PLATFORM_GET, ifindex); - g_assert(addresses); - g_assert_cmpint(addresses->len, ==, 1); - address = &g_array_index(addresses, NMPlatformIP4Address, 0); - g_assert_cmpint(address->ifindex, ==, ifindex); - g_assert_cmphex(address->address, ==, addr); - g_assert_cmphex(address->peer_address, ==, addr); - g_assert_cmpint(address->plen, ==, IP4_PLEN); - g_array_unref(addresses); - - /* Remove address */ - nmtstp_ip4_address_del(NULL, EX, ifindex, addr, IP4_PLEN, addr); - g_assert(!nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); - accept_signal(address_removed); - - /* Remove address again */ - nmtstp_ip4_address_del(NULL, EX, ifindex, addr, IP4_PLEN, addr); - - free_signal(address_added); - free_signal(address_changed); - free_signal(address_removed); -} - -static void -test_ip6_address_general(void) -{ - const int ifindex = DEVICE_IFINDEX; - SignalData *address_added = add_signal_ifindex(NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip6_address_callback, - ifindex); - SignalData *address_changed = add_signal_ifindex(NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_CHANGED, - ip6_address_callback, - ifindex); - SignalData *address_removed = add_signal_ifindex(NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip6_address_callback, - ifindex); - GArray * addresses; - NMPlatformIP6Address *address; - struct in6_addr addr; - guint32 lifetime = 2000; - guint32 preferred = 1000; - guint flags = 0; - - inet_pton(AF_INET6, IP6_ADDRESS, &addr); - - /* Add address */ - g_assert(!nm_platform_ip6_address_get(NM_PLATFORM_GET, ifindex, &addr)); - nmtstp_ip6_address_add(NULL, - EX, - ifindex, - addr, - IP6_PLEN, - in6addr_any, - lifetime, - preferred, - flags); - g_assert(nm_platform_ip6_address_get(NM_PLATFORM_GET, ifindex, &addr)); - accept_signal(address_added); - - /* Add address again (aka update) */ - nmtstp_ip6_address_add(NULL, - EX, - ifindex, - addr, - IP6_PLEN, - in6addr_any, - lifetime, - preferred, - flags); - accept_signals(address_changed, 0, 2); - - /* Test address listing */ - addresses = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex); - g_assert(addresses); - g_assert_cmpint(addresses->len, ==, 1); - address = &g_array_index(addresses, NMPlatformIP6Address, 0); - g_assert_cmpint(address->ifindex, ==, ifindex); - g_assert(!memcmp(&address->address, &addr, sizeof(addr))); - g_assert_cmpint(address->plen, ==, IP6_PLEN); - g_array_unref(addresses); - - /* Remove address */ - nmtstp_ip6_address_del(NULL, EX, ifindex, addr, IP6_PLEN); - g_assert(!nm_platform_ip6_address_get(NM_PLATFORM_GET, ifindex, &addr)); - accept_signal(address_removed); - - /* Remove address again */ - nmtstp_ip6_address_del(NULL, EX, ifindex, addr, IP6_PLEN); - - /* ensure not pending signal. */ - accept_signals(address_changed, 0, 1); - - free_signal(address_added); - free_signal(address_changed); - free_signal(address_removed); -} - -static void -test_ip4_address_general_2(void) -{ - const int ifindex = DEVICE_IFINDEX; - SignalData *address_added = add_signal(NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip4_address_callback); - SignalData *address_removed = add_signal(NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip4_address_callback); - in_addr_t addr; - guint32 lifetime = 2000; - guint32 preferred = 1000; - - inet_pton(AF_INET, IP4_ADDRESS, &addr); - g_assert(ifindex > 0); - - /* Looks like addresses are not announced by kernel when the interface - * is down. Link-local IPv6 address is automatically added. - */ - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, DEVICE_IFINDEX, NULL)); - - /* Add/delete notification */ - nmtstp_ip4_address_add(NULL, EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL); - accept_signal(address_added); - g_assert(nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); - nmtstp_ip4_address_del(NULL, EX, ifindex, addr, IP4_PLEN, addr); - accept_signal(address_removed); - g_assert(!nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); - - /* Add/delete conflict */ - nmtstp_ip4_address_add(NULL, EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL); - g_assert(nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); - accept_signal(address_added); - - free_signal(address_added); - free_signal(address_removed); -} - -static void -test_ip6_address_general_2(void) -{ - const int ifindex = DEVICE_IFINDEX; - SignalData * address_added = add_signal(NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip6_address_callback); - SignalData * address_removed = add_signal(NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip6_address_callback); - struct in6_addr addr; - guint32 lifetime = 2000; - guint32 preferred = 1000; - guint flags = 0; - - inet_pton(AF_INET6, IP6_ADDRESS, &addr); - - /* Add/delete notification */ - nmtstp_ip6_address_add(NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); - accept_signal(address_added); - g_assert(nm_platform_ip6_address_get(NM_PLATFORM_GET, ifindex, &addr)); - - nmtstp_ip6_address_del(NULL, EX, ifindex, addr, IP6_PLEN); - accept_signal(address_removed); - g_assert(!nm_platform_ip6_address_get(NM_PLATFORM_GET, ifindex, &addr)); - - /* Add/delete conflict */ - nmtstp_ip6_address_add(NULL, EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); - accept_signal(address_added); - g_assert(nm_platform_ip6_address_get(NM_PLATFORM_GET, ifindex, &addr)); - - nmtstp_ip6_address_add(NULL, - EX, - ifindex, - addr, - IP6_PLEN, - in6addr_any, - lifetime, - preferred, - flags); - ensure_no_signal(address_added); - g_assert(nm_platform_ip6_address_get(NM_PLATFORM_GET, ifindex, &addr)); - - free_signal(address_added); - free_signal(address_removed); -} - -/*****************************************************************************/ - -static void -test_ip4_address_peer(void) -{ - const int ifindex = DEVICE_IFINDEX; - SignalData * address_added = add_signal(NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip4_address_callback); - SignalData * address_removed = add_signal(NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip4_address_callback); - in_addr_t addr, addr_peer, addr_peer2; - guint32 lifetime = 2000; - guint32 preferred = 1000; - const NMPlatformIP4Address *a; - - inet_pton(AF_INET, IP4_ADDRESS, &addr); - inet_pton(AF_INET, IP4_ADDRESS_PEER, &addr_peer); - inet_pton(AF_INET, IP4_ADDRESS_PEER2, &addr_peer2); - g_assert(ifindex > 0); - - g_assert(addr != addr_peer); - - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL)); - accept_signals(address_removed, 0, G_MAXINT); - accept_signals(address_added, 0, G_MAXINT); - - /* Add/delete notification */ - nmtstp_ip4_address_add(NULL, - EX, - ifindex, - addr, - IP4_PLEN, - addr_peer, - lifetime, - preferred, - 0, - NULL); - accept_signal(address_added); - a = nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer); - g_assert(a); - g_assert(!nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2)); - - nmtstp_ip_address_assert_lifetime((NMPlatformIPAddress *) a, -1, lifetime, preferred); - - nmtstp_ip4_address_add(NULL, - EX, - ifindex, - addr, - IP4_PLEN, - addr_peer2, - lifetime, - preferred, - 0, - NULL); - accept_signal(address_added); - g_assert(nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer)); - a = nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2); - g_assert(a); - - nmtstp_ip_address_assert_lifetime((NMPlatformIPAddress *) a, -1, lifetime, preferred); - - g_assert(addr != addr_peer); - nmtstp_ip4_address_del(NULL, EX, ifindex, addr, IP4_PLEN, addr_peer); - accept_signal(address_removed); - g_assert(!nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer)); - g_assert(nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2)); - - free_signal(address_added); - free_signal(address_removed); -} - -/*****************************************************************************/ - -static void -test_ip4_address_peer_zero(void) -{ - const int ifindex = DEVICE_IFINDEX; - in_addr_t addr, addr_peer; - guint32 lifetime = 2000; - guint32 preferred = 1000; - const gint8 plen = 24; - const char *label = NULL; - in_addr_t peers[3], r_peers[3]; - int i; - GArray * addrs; - - g_assert(ifindex > 0); - - inet_pton(AF_INET, "192.168.5.2", &addr); - inet_pton(AF_INET, "192.168.6.2", &addr_peer); - peers[0] = addr; - peers[1] = addr_peer; - peers[2] = 0; - - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL)); - - nmtst_rand_perm(NULL, r_peers, peers, sizeof(peers[0]), G_N_ELEMENTS(peers)); - for (i = 0; i < G_N_ELEMENTS(peers); i++) { - g_assert(!nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); - - nmtstp_ip4_address_add(NULL, - EX, - ifindex, - addr, - plen, - r_peers[i], - lifetime, - preferred, - 0, - label); - - addrs = nmtstp_platform_ip4_address_get_all(NM_PLATFORM_GET, ifindex); - g_assert(addrs); - g_assert_cmpint(addrs->len, ==, i + 1); - g_array_unref(addrs); - } - - if (nmtst_is_debug() && nmtstp_is_root_test()) - nmtstp_run_command_check("ip address show dev %s", DEVICE_NAME); - - nmtst_rand_perm(NULL, r_peers, peers, sizeof(peers[0]), G_N_ELEMENTS(peers)); - for (i = 0; i < G_N_ELEMENTS(peers); i++) { - g_assert(nm_platform_ip4_address_get(NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); - - nmtstp_ip4_address_del(NULL, EX, ifindex, addr, plen, r_peers[i]); - - addrs = nmtstp_platform_ip4_address_get_all(NM_PLATFORM_GET, ifindex); - g_assert(addrs); - g_assert_cmpint(addrs->len, ==, G_N_ELEMENTS(peers) - i - 1); - g_array_unref(addrs); - } -} - -/*****************************************************************************/ - -NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; - -void -_nmtstp_init_tests(int *argc, char ***argv) -{ - nmtst_init_with_logging(argc, argv, NULL, "ALL"); -} - -/***************************************************************************** - * SETUP TESTS - *****************************************************************************/ - -void -_nmtstp_setup_tests(void) -{ -#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, FALSE) - add_test_func("/address/ipv4/general", test_ip4_address_general); - add_test_func("/address/ipv6/general", test_ip6_address_general); - - add_test_func("/address/ipv4/general-2", test_ip4_address_general_2); - add_test_func("/address/ipv6/general-2", test_ip6_address_general_2); - - add_test_func("/address/ipv4/peer", test_ip4_address_peer); - add_test_func("/address/ipv4/peer/zero", test_ip4_address_peer_zero); -} diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c deleted file mode 100644 index c364bfa1..00000000 --- a/src/platform/tests/test-cleanup.c +++ /dev/null @@ -1,188 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "test-common.h" - -static void -test_cleanup_internal(void) -{ - SignalData * link_added = add_signal_ifname(NM_PLATFORM_SIGNAL_LINK_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - link_callback, - DEVICE_NAME); - int ifindex; - GArray * addresses4; - GArray * addresses6; - GPtrArray * routes4; - GPtrArray * routes6; - in_addr_t addr4; - in_addr_t network4; - int plen4 = 24; - in_addr_t gateway4; - struct in6_addr addr6; - struct in6_addr network6; - int plen6 = 64; - struct in6_addr gateway6; - int lifetime = NM_PLATFORM_LIFETIME_PERMANENT; - int preferred = NM_PLATFORM_LIFETIME_PERMANENT; - int metric = 20; - int mss = 1000; - guint32 flags = 0; - - inet_pton(AF_INET, "192.0.2.1", &addr4); - inet_pton(AF_INET, "192.0.3.0", &network4); - inet_pton(AF_INET, "198.51.100.1", &gateway4); - inet_pton(AF_INET6, "2001:db8:a:b:1:2:3:4", &addr6); - inet_pton(AF_INET6, "2001:db8:c:d:0:0:0:0", &network6); - inet_pton(AF_INET6, "2001:db8:e:f:1:2:3:4", &gateway6); - - /* Create and set up device */ - g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL))); - accept_signal(link_added); - free_signal(link_added); - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, - nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME), - NULL)); - ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - g_assert(ifindex > 0); - - /* wait for kernel to add the IPv6 link local address... it takes a bit. */ - NMTST_WAIT_ASSERT(300, { - gs_unref_array GArray * addrs = NULL; - const NMPlatformIP6Address *a; - - if (nmtst_wait_iteration > 0) { - nmtstp_wait_for_signal(NM_PLATFORM_GET, nmtst_wait_remaining_us / 1000); - nm_platform_process_events(NM_PLATFORM_GET); - } - addrs = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex); - if (addrs->len == 1 && (a = &g_array_index(addrs, NMPlatformIP6Address, 0)) - && IN6_IS_ADDR_LINKLOCAL(&a->address)) - break; - }); - - g_assert(nm_platform_ip4_address_add(NM_PLATFORM_GET, - ifindex, - addr4, - plen4, - addr4, - nm_platform_ip4_broadcast_address_create(addr4, plen4), - lifetime, - preferred, - 0, - NULL)); - g_assert(nm_platform_ip6_address_add(NM_PLATFORM_GET, - ifindex, - addr6, - plen6, - in6addr_any, - lifetime, - preferred, - flags)); - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - gateway4, - 32, - INADDR_ANY, - 0, - metric, - mss); - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network4, - plen4, - gateway4, - 0, - metric, - mss); - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - gateway4, - 0, - metric, - mss); - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - gateway6, - 128, - in6addr_any, - in6addr_any, - metric, - mss); - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network6, - plen6, - gateway6, - in6addr_any, - metric, - mss); - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - in6addr_any, - 0, - gateway6, - in6addr_any, - metric, - mss); - - addresses4 = nmtstp_platform_ip4_address_get_all(NM_PLATFORM_GET, ifindex); - addresses6 = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex); - routes4 = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex); - routes6 = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, ifindex); - - g_assert_cmpint(addresses4->len, ==, 1); - g_assert_cmpint(addresses6->len, ==, 2); /* also has a IPv6 LL address. */ - g_assert_cmpint(routes4->len, ==, 3); - g_assert_cmpint(routes6->len, ==, 3); - - g_array_unref(addresses4); - g_array_unref(addresses6); - g_ptr_array_unref(routes4); - g_ptr_array_unref(routes6); - - /* Delete interface with all addresses and routes */ - g_assert(nm_platform_link_delete(NM_PLATFORM_GET, ifindex)); - - addresses4 = nmtstp_platform_ip4_address_get_all(NM_PLATFORM_GET, ifindex); - addresses6 = nmtstp_platform_ip6_address_get_all(NM_PLATFORM_GET, ifindex); - routes4 = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex); - routes6 = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, ifindex); - - g_assert_cmpint(addresses4->len, ==, 0); - g_assert_cmpint(addresses6->len, ==, 0); - g_assert(!routes4); - g_assert(!routes6); - - g_array_unref(addresses4); - g_array_unref(addresses6); -} - -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); - - g_test_add_func("/internal", test_cleanup_internal); - /* FIXME: add external cleanup check */ -} diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c deleted file mode 100644 index fb430046..00000000 --- a/src/platform/tests/test-common.c +++ /dev/null @@ -1,2569 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <sys/mount.h> -#include <sched.h> -#include <sys/wait.h> -#include <fcntl.h> -#include <linux/if_tun.h> - -#include "test-common.h" - -#define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" -#define SIGNAL_DATA_ARG(data) \ - (data)->name, nm_platform_signal_change_type_to_string((data)->change_type), (data)->ifindex, \ - (data)->ifname ? " ifname '" : "", (data)->ifname ?: "", (data)->ifname ? "'" : "", \ - (data)->received_count - -int NMTSTP_ENV1_IFINDEX = -1; -int NMTSTP_ENV1_EX = -1; - -/*****************************************************************************/ - -void -nmtstp_setup_platform(void) -{ - g_assert(_nmtstp_setup_platform_func); - _nmtstp_setup_platform_func(); -} - -gboolean -nmtstp_is_root_test(void) -{ - g_assert(_nmtstp_setup_platform_func); - return _nmtstp_setup_platform_func == nm_linux_platform_setup; -} - -gboolean -nmtstp_is_sysfs_writable(void) -{ - return !nmtstp_is_root_test() || (access("/sys/devices", W_OK) == 0); -} - -static void -_init_platform(NMPlatform **platform, gboolean external_command) -{ - g_assert(platform); - if (!*platform) - *platform = NM_PLATFORM_GET; - g_assert(NM_IS_PLATFORM(*platform)); - - if (external_command) - g_assert(NM_IS_LINUX_PLATFORM(*platform)); -} - -/*****************************************************************************/ - -static GArray * -_ipx_address_get_all(NMPlatform *self, int ifindex, NMPObjectType obj_type) -{ - NMPLookup lookup; - - g_assert(NM_IS_PLATFORM(self)); - g_assert(ifindex > 0); - g_assert(NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - nmp_lookup_init_object(&lookup, obj_type, ifindex); - return nmp_cache_lookup_to_array(nm_platform_lookup(self, &lookup), - obj_type, - FALSE /*addresses are always visible. */); -} - -GArray * -nmtstp_platform_ip4_address_get_all(NMPlatform *self, int ifindex) -{ - return _ipx_address_get_all(self, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); -} - -GArray * -nmtstp_platform_ip6_address_get_all(NMPlatform *self, int ifindex) -{ - return _ipx_address_get_all(self, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); -} - -const NMPlatformIPAddress * -nmtstp_platform_ip_address_find(NMPlatform *self, int ifindex, int addr_family, gconstpointer addr) -{ - const gboolean IS_IPv4 = NM_IS_IPv4(addr_family); - const NMPlatformIPAddress *found = NULL; - NMDedupMultiIter iter; - const NMPObject * obj; - NMPLookup lookup; - - g_assert(NM_IS_PLATFORM(self)); - nm_assert(ifindex >= 0); - nm_assert_addr_family(addr_family); - nm_assert(addr); - - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex); - nm_platform_iter_obj_for_each(&iter, self, &lookup, &obj) - { - const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS(obj); - - g_assert(NMP_OBJECT_GET_ADDR_FAMILY(obj) == addr_family); - g_assert(ifindex <= 0 || a->ifindex == ifindex); - - if (memcmp(addr, a->address_ptr, nm_utils_addr_family_to_size(addr_family)) != 0) - continue; - - g_assert(!found); - found = a; - } - - if (!IS_IPv4 && ifindex > 0) - g_assert(found - == (const NMPlatformIPAddress *) nm_platform_ip6_address_get(self, ifindex, addr)); - - return found; -} - -/*****************************************************************************/ - -typedef struct { - NMIPAddr addr; - int addr_family; - bool found : 1; -} IPAddressesAssertData; - -void -_nmtstp_platform_ip_addresses_assert(const char * filename, - int lineno, - NMPlatform * self, - int ifindex, - gboolean force_exact_4, - gboolean force_exact_6, - gboolean ignore_ll6, - guint addrs_len, - const char *const *addrs) -{ - gs_free IPAddressesAssertData *addrs_bin = NULL; - int IS_IPv4; - guint i; - - g_assert(filename); - g_assert(lineno >= 0); - g_assert(NM_IS_PLATFORM(self)); - g_assert(ifindex >= 0); - - addrs_bin = g_new(IPAddressesAssertData, addrs_len); - - for (i = 0; i < addrs_len; i++) { - const char *addrstr = addrs[i]; - int addr_family; - NMIPAddr a; - - if (!addrstr) { - addr_family = AF_UNSPEC; - a = nm_ip_addr_zero; - } else if (inet_pton(AF_INET, addrstr, &a) == 1) - addr_family = AF_INET; - else if (inet_pton(AF_INET6, addrstr, &a) == 1) - addr_family = AF_INET6; - else - g_error("%s:%d: invalid IP address in argument: %s", filename, lineno, addrstr); - - addrs_bin[i] = (IPAddressesAssertData){ - .addr_family = addr_family, - .addr = a, - .found = FALSE, - }; - } - - for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) { - const int addr_family = IS_IPv4 ? AF_INET : AF_INET6; - gs_unref_ptrarray GPtrArray *plat_addrs = NULL; - NMPLookup lookup; - guint j; - - plat_addrs = nm_platform_lookup_clone( - self, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), - NULL, - NULL); - - for (i = 0; i < addrs_len; i++) { - IPAddressesAssertData *addr_bin = &addrs_bin[i]; - - if (addr_bin->addr_family != addr_family) - continue; - - g_assert(!addr_bin->found); - for (j = 0; j < nm_g_ptr_array_len(plat_addrs);) { - const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS(plat_addrs->pdata[j]); - - if (memcmp(&addr_bin->addr, - a->address_ptr, - nm_utils_addr_family_to_size(addr_family)) - != 0) { - j++; - continue; - } - - g_assert(!addr_bin->found); - addr_bin->found = TRUE; - g_ptr_array_remove_index_fast(plat_addrs, j); - } - - if (!addr_bin->found) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - g_error("%s:%d: IPv%c address %s was not found on ifindex %d", - filename, - lineno, - nm_utils_addr_family_to_char(addr_bin->addr_family), - nm_utils_inet_ntop(addr_bin->addr_family, &addr_bin->addr, sbuf), - ifindex); - } - } - if (!IS_IPv4 && ignore_ll6 && nm_g_ptr_array_len(plat_addrs) > 0) { - /* we prune all remaining, non-matching IPv6 link local addresses. */ - for (j = 0; j < nm_g_ptr_array_len(plat_addrs);) { - const NMPlatformIPAddress *a = NMP_OBJECT_CAST_IP_ADDRESS(plat_addrs->pdata[j]); - - if (!IN6_IS_ADDR_LINKLOCAL(a->address_ptr)) { - j++; - continue; - } - - g_ptr_array_remove_index_fast(plat_addrs, j); - } - } - if ((IS_IPv4 ? force_exact_4 : force_exact_6) && nm_g_ptr_array_len(plat_addrs) > 0) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; - - g_error("%s:%d: %u IPv%c addresses found on ifindex %d that should not be there (one " - "is %s)", - filename, - lineno, - plat_addrs->len, - nm_utils_addr_family_to_char(addr_family), - ifindex, - nmp_object_to_string(plat_addrs->pdata[0], - NMP_OBJECT_TO_STRING_PUBLIC, - sbuf, - sizeof(sbuf))); - } - } -} - -/*****************************************************************************/ - -gboolean -nmtstp_platform_ip4_route_delete(NMPlatform *platform, - int ifindex, - in_addr_t network, - guint8 plen, - guint32 metric) -{ - NMDedupMultiIter iter; - - nm_platform_process_events(platform); - - nm_dedup_multi_iter_for_each ( - &iter, - nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex)) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(iter.current->obj); - - if (r->ifindex != ifindex || r->network != network || r->plen != plen - || r->metric != metric) { - continue; - } - - return nm_platform_object_delete(platform, NMP_OBJECT_UP_CAST(r)); - } - - return TRUE; -} - -gboolean -nmtstp_platform_ip6_route_delete(NMPlatform * platform, - int ifindex, - struct in6_addr network, - guint8 plen, - guint32 metric) -{ - NMDedupMultiIter iter; - - nm_platform_process_events(platform); - - nm_dedup_multi_iter_for_each ( - &iter, - nm_platform_lookup_object(platform, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex)) { - const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE(iter.current->obj); - - if (r->ifindex != ifindex || !IN6_ARE_ADDR_EQUAL(&r->network, &network) || r->plen != plen - || r->metric != metric) { - continue; - } - - return nm_platform_object_delete(platform, NMP_OBJECT_UP_CAST(r)); - } - - return TRUE; -} - -/*****************************************************************************/ - -SignalData * -add_signal_full(const char * name, - NMPlatformSignalChangeType change_type, - GCallback callback, - int ifindex, - const char * ifname) -{ - SignalData *data = g_new0(SignalData, 1); - - data->name = name; - data->change_type = change_type; - data->received_count = 0; - data->handler_id = g_signal_connect(NM_PLATFORM_GET, name, callback, data); - data->ifindex = ifindex; - data->ifname = ifname; - - g_assert(data->handler_id > 0); - - return data; -} - -void -_accept_signal(const char *file, int line, const char *func, SignalData *data) -{ - _LOGD("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal one time: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - if (data->received_count != 1) - g_error("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal one " - "time: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - data->received_count = 0; -} - -void -_accept_signals(const char *file, int line, const char *func, SignalData *data, int min, int max) -{ - _LOGD("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal [%d,%d] times: " SIGNAL_DATA_FMT, - file, - line, - func, - min, - max, - SIGNAL_DATA_ARG(data)); - if (data->received_count < min || data->received_count > max) - g_error("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal [%d,%d] " - "times: " SIGNAL_DATA_FMT, - file, - line, - func, - min, - max, - SIGNAL_DATA_ARG(data)); - data->received_count = 0; -} - -void -_ensure_no_signal(const char *file, int line, const char *func, SignalData *data) -{ - _LOGD("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal 0 times: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - if (data->received_count > 0) - g_error("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal 0 " - "times: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); -} - -void -_accept_or_wait_signal(const char *file, int line, const char *func, SignalData *data) -{ - _LOGD("NMPlatformSignalAssert: %s:%d, %s(): accept-or-wait signal: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - if (data->received_count == 0) { - data->loop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(data->loop); - nm_clear_pointer(&data->loop, g_main_loop_unref); - } - - _accept_signal(file, line, func, data); -} - -void -_wait_signal(const char *file, int line, const char *func, SignalData *data) -{ - _LOGD("NMPlatformSignalAssert: %s:%d, %s(): wait signal: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - if (data->received_count) - g_error("NMPlatformSignalAssert: %s:%d, %s(): failure to wait for signal: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - - data->loop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(data->loop); - nm_clear_pointer(&data->loop, g_main_loop_unref); - - _accept_signal(file, line, func, data); -} - -void -_free_signal(const char *file, int line, const char *func, SignalData *data) -{ - _LOGD("NMPlatformSignalAssert: %s:%d, %s(): free signal: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - if (data->received_count != 0) - g_error("NMPlatformSignalAssert: %s:%d, %s(): failure to free non-accepted " - "signal: " SIGNAL_DATA_FMT, - file, - line, - func, - SIGNAL_DATA_ARG(data)); - - g_signal_handler_disconnect(NM_PLATFORM_GET, data->handler_id); - g_free(data); -} - -void -link_callback(NMPlatform * platform, - int obj_type_i, - int ifindex, - NMPlatformLink *received, - int change_type_i, - SignalData * data) -{ - const NMPObjectType obj_type = obj_type_i; - const NMPlatformSignalChangeType change_type = change_type_i; - NMPLookup lookup; - NMDedupMultiIter iter; - const NMPlatformLink * cached; - - g_assert_cmpint(obj_type, ==, NMP_OBJECT_TYPE_LINK); - g_assert(received); - g_assert_cmpint(received->ifindex, ==, ifindex); - g_assert(data && data->name); - g_assert_cmpstr(data->name, ==, NM_PLATFORM_SIGNAL_LINK_CHANGED); - - if (data->ifindex && data->ifindex != received->ifindex) - return; - if (data->ifname - && g_strcmp0(data->ifname, nm_platform_link_get_name(NM_PLATFORM_GET, ifindex)) != 0) - return; - if (change_type != data->change_type) - return; - - if (data->loop) { - _LOGD("Quitting main loop."); - g_main_loop_quit(data->loop); - } - - data->received_count++; - _LOGD("Received signal '%s-%s' ifindex %d ifname '%s' %dth time.", - data->name, - nm_platform_signal_change_type_to_string(data->change_type), - ifindex, - received->name, - data->received_count); - - if (change_type == NM_PLATFORM_SIGNAL_REMOVED) - g_assert(!nm_platform_link_get_name(NM_PLATFORM_GET, ifindex)); - else - g_assert(nm_platform_link_get_name(NM_PLATFORM_GET, ifindex)); - - /* Check the data */ - g_assert(received->ifindex > 0); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK); - nmp_cache_iter_for_each_link (&iter, nm_platform_lookup(platform, &lookup), &cached) { - if (!nmp_object_is_visible(NMP_OBJECT_UP_CAST(cached))) - continue; - if (cached->ifindex == received->ifindex) { - g_assert_cmpint(nm_platform_link_cmp(cached, received), ==, 0); - g_assert(!memcmp(cached, received, sizeof(*cached))); - if (data->change_type == NM_PLATFORM_SIGNAL_REMOVED) - g_error("Deleted link still found in the local cache."); - return; - } - } - - if (data->change_type != NM_PLATFORM_SIGNAL_REMOVED) - g_error("Added/changed link not found in the local cache."); -} - -/*****************************************************************************/ - -static const NMPlatformIP4Route * -_ip4_route_get(NMPlatform *platform, - int ifindex, - guint32 network, - int plen, - guint32 metric, - guint8 tos, - guint * out_c_exists) -{ - NMDedupMultiIter iter; - NMPLookup lookup; - const NMPObject * o = NULL; - guint c; - const NMPlatformIP4Route *r = NULL; - - _init_platform(&platform, FALSE); - - nmp_lookup_init_ip4_route_by_weak_id(&lookup, network, plen, metric, tos); - - c = 0; - nmp_cache_iter_for_each (&iter, nm_platform_lookup(platform, &lookup), &o) { - if (NMP_OBJECT_CAST_IP4_ROUTE(o)->ifindex != ifindex && ifindex > 0) - continue; - if (!r) - r = NMP_OBJECT_CAST_IP4_ROUTE(o); - c++; - } - - NM_SET_OUT(out_c_exists, c); - return r; -} - -const NMPlatformIP4Route * -_nmtstp_assert_ip4_route_exists(const char *file, - guint line, - const char *func, - NMPlatform *platform, - int c_exists, - const char *ifname, - guint32 network, - int plen, - guint32 metric, - guint8 tos) -{ - int ifindex; - guint c; - const NMPlatformIP4Route *r = NULL; - - _init_platform(&platform, FALSE); - - ifindex = -1; - if (ifname) { - ifindex = nm_platform_link_get_ifindex(platform, ifname); - g_assert(ifindex > 0); - } - - r = _ip4_route_get(platform, ifindex, network, plen, metric, tos, &c); - - if (c != c_exists && c_exists != -1) { - char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - - g_error("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but " - "platform has it %u times", - file, - line, - func, - _nm_utils_inet4_ntop(network, sbuf), - plen, - metric, - tos, - c_exists, - c); - } - - return r; -} - -const NMPlatformIP4Route * -nmtstp_ip4_route_get(NMPlatform *platform, - int ifindex, - guint32 network, - int plen, - guint32 metric, - guint8 tos) -{ - return _ip4_route_get(platform, ifindex, network, plen, metric, tos, NULL); -} - -/*****************************************************************************/ - -static const NMPlatformIP6Route * -_ip6_route_get(NMPlatform * platform, - int ifindex, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen, - guint * out_c_exists) -{ - NMDedupMultiIter iter; - NMPLookup lookup; - const NMPObject * o = NULL; - guint c; - const NMPlatformIP6Route *r = NULL; - - _init_platform(&platform, FALSE); - - nmp_lookup_init_ip6_route_by_weak_id(&lookup, network, plen, metric, src, src_plen); - - c = 0; - nmp_cache_iter_for_each (&iter, nm_platform_lookup(platform, &lookup), &o) { - if (NMP_OBJECT_CAST_IP6_ROUTE(o)->ifindex != ifindex && ifindex > 0) - continue; - if (!r) - r = NMP_OBJECT_CAST_IP6_ROUTE(o); - c++; - } - - NM_SET_OUT(out_c_exists, c); - return r; -} - -const NMPlatformIP6Route * -_nmtstp_assert_ip6_route_exists(const char * file, - guint line, - const char * func, - NMPlatform * platform, - int c_exists, - const char * ifname, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen) -{ - int ifindex; - guint c; - const NMPlatformIP6Route *r = NULL; - - _init_platform(&platform, FALSE); - - ifindex = -1; - if (ifname) { - ifindex = nm_platform_link_get_ifindex(platform, ifname); - g_assert(ifindex > 0); - } - - r = _ip6_route_get(platform, ifindex, network, plen, metric, src, src_plen, &c); - - if (c != c_exists && c_exists != -1) { - char s_src[NM_UTILS_INET_ADDRSTRLEN]; - char s_network[NM_UTILS_INET_ADDRSTRLEN]; - - g_error("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but " - "platform has it %u times", - file, - line, - func, - _nm_utils_inet6_ntop(network, s_network), - plen, - metric, - _nm_utils_inet6_ntop(src, s_src), - src_plen, - c_exists, - c); - } - - return r; -} - -const NMPlatformIP6Route * -nmtstp_ip6_route_get(NMPlatform * platform, - int ifindex, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen) -{ - return _ip6_route_get(platform, ifindex, network, plen, metric, src, src_plen, NULL); -} - -/*****************************************************************************/ - -int -nmtstp_run_command(const char *format, ...) -{ - int result; - gs_free char *command = NULL; - va_list ap; - - va_start(ap, format); - command = g_strdup_vprintf(format, ap); - va_end(ap); - - _LOGD("Running command: %s", command); - result = system(command); - _LOGD("Command finished: result=%d", result); - - return result; -} - -/*****************************************************************************/ - -typedef struct { - GMainLoop *loop; - guint signal_counts; - guint id; -} WaitForSignalData; - -static void -_wait_for_signal_cb(NMPlatform * platform, - int obj_type_i, - int ifindex, - NMPlatformLink *plink, - int change_type_i, - gpointer user_data) -{ - WaitForSignalData *data = user_data; - - data->signal_counts++; - nm_clear_g_source(&data->id); - g_main_loop_quit(data->loop); -} - -static gboolean -_wait_for_signal_timeout(gpointer user_data) -{ - WaitForSignalData *data = user_data; - - g_assert(data->id); - data->id = 0; - g_main_loop_quit(data->loop); - return G_SOURCE_REMOVE; -} - -guint -nmtstp_wait_for_signal(NMPlatform *platform, gint64 timeout_msec) -{ - WaitForSignalData data = {0}; - gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route; - gulong id_qdisc, id_tfilter; - - _init_platform(&platform, FALSE); - - data.loop = g_main_loop_new(NULL, FALSE); - - id_link = g_signal_connect(platform, - NM_PLATFORM_SIGNAL_LINK_CHANGED, - G_CALLBACK(_wait_for_signal_cb), - &data); - id_ip4_address = g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, - G_CALLBACK(_wait_for_signal_cb), - &data); - id_ip6_address = g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - G_CALLBACK(_wait_for_signal_cb), - &data); - id_ip4_route = g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - G_CALLBACK(_wait_for_signal_cb), - &data); - id_ip6_route = g_signal_connect(platform, - NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - G_CALLBACK(_wait_for_signal_cb), - &data); - id_qdisc = g_signal_connect(platform, - NM_PLATFORM_SIGNAL_QDISC_CHANGED, - G_CALLBACK(_wait_for_signal_cb), - &data); - id_tfilter = g_signal_connect(platform, - NM_PLATFORM_SIGNAL_TFILTER_CHANGED, - G_CALLBACK(_wait_for_signal_cb), - &data); - - /* if timeout_msec is negative, it means the wait-time already expired. - * Maybe, we should do nothing and return right away, without even - * processing events from platform. However, that inconsistency (of not - * processing events from mainloop) is inconvenient. - * - * It's better that on the return of nmtstp_wait_for_signal(), we always - * have no events pending. So, a negative timeout is treated the same as - * a zero timeout: we check whether there are any events pending in platform, - * and quite the mainloop immediately afterwards. But we always check. */ - - data.id = g_timeout_add(CLAMP(timeout_msec, 0, G_MAXUINT32), _wait_for_signal_timeout, &data); - - g_main_loop_run(data.loop); - - g_assert(!data.id); - g_assert(nm_clear_g_signal_handler(platform, &id_link)); - g_assert(nm_clear_g_signal_handler(platform, &id_ip4_address)); - g_assert(nm_clear_g_signal_handler(platform, &id_ip6_address)); - g_assert(nm_clear_g_signal_handler(platform, &id_ip4_route)); - g_assert(nm_clear_g_signal_handler(platform, &id_ip6_route)); - g_assert(nm_clear_g_signal_handler(platform, &id_tfilter)); - g_assert(nm_clear_g_signal_handler(platform, &id_qdisc)); - - nm_clear_pointer(&data.loop, g_main_loop_unref); - - /* return the number of signals, or 0 if timeout was reached .*/ - return data.signal_counts; -} - -guint -nmtstp_wait_for_signal_until(NMPlatform *platform, gint64 until_ms) -{ - gint64 now; - guint signal_counts; - - while (TRUE) { - now = nm_utils_get_monotonic_timestamp_msec(); - - if (until_ms < now) - return 0; - - signal_counts = nmtstp_wait_for_signal(platform, until_ms - now); - if (signal_counts) - return signal_counts; - } -} - -const NMPlatformLink * -nmtstp_wait_for_link(NMPlatform *platform, - const char *ifname, - NMLinkType expected_link_type, - gint64 timeout_msec) -{ - return nmtstp_wait_for_link_until( - platform, - ifname, - expected_link_type, - timeout_msec ? nm_utils_get_monotonic_timestamp_msec() + timeout_msec : 0); -} - -const NMPlatformLink * -nmtstp_wait_for_link_until(NMPlatform *platform, - const char *ifname, - NMLinkType expected_link_type, - gint64 until_ms) -{ - const NMPlatformLink *plink; - gint64 now; - gboolean waited_once = FALSE; - - _init_platform(&platform, FALSE); - - while (TRUE) { - now = nm_utils_get_monotonic_timestamp_msec(); - - plink = nm_platform_link_get_by_ifname(platform, ifname); - if (plink && (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type)) - return plink; - - if (until_ms == 0) { - /* don't wait, don't even poll the socket. */ - return NULL; - } - - if (waited_once && until_ms < now) { - /* timeout reached (+ we already waited for a signal at least once). */ - return NULL; - } - - waited_once = TRUE; - /* regardless of whether timeout is already reached, we poll the netlink - * socket a bit. */ - nmtstp_wait_for_signal(platform, until_ms - now); - } -} - -/*****************************************************************************/ - -int -nmtstp_run_command_check_external_global(void) -{ - if (!nmtstp_is_root_test()) - return FALSE; - switch (nmtst_get_rand_uint32() % 3) { - case 0: - return -1; - case 1: - return FALSE; - default: - return TRUE; - } -} - -gboolean -nmtstp_run_command_check_external(int external_command) -{ - if (external_command != -1) { - g_assert(NM_IN_SET(external_command, FALSE, TRUE)); - g_assert(!external_command || nmtstp_is_root_test()); - return !!external_command; - } - if (!nmtstp_is_root_test()) - return FALSE; - return (nmtst_get_rand_uint32() % 2) == 0; -} - -/*****************************************************************************/ - -#define CHECK_LIFETIME_MAX_DIFF 2 - -gboolean -nmtstp_ip_address_check_lifetime(const NMPlatformIPAddress *addr, - gint64 now, - guint32 expected_lifetime, - guint32 expected_preferred) -{ - gint64 offset; - int i; - - g_assert(addr); - - if (now == -1) - now = nm_utils_get_monotonic_timestamp_sec(); - g_assert(now > 0); - - g_assert(expected_preferred <= expected_lifetime); - - if (expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && expected_preferred == NM_PLATFORM_LIFETIME_PERMANENT) { - return addr->timestamp == 0 && addr->lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && addr->preferred == NM_PLATFORM_LIFETIME_PERMANENT; - } - - if (addr->timestamp == 0) - return FALSE; - - offset = (gint64) now - addr->timestamp; - - for (i = 0; i < 2; i++) { - guint32 lft = i ? expected_lifetime : expected_preferred; - guint32 adr = i ? addr->lifetime : addr->preferred; - - if (lft == NM_PLATFORM_LIFETIME_PERMANENT) { - if (adr != NM_PLATFORM_LIFETIME_PERMANENT) - return FALSE; - } else { - if (((gint64) adr) - offset <= ((gint64) lft) - CHECK_LIFETIME_MAX_DIFF - || ((gint64) adr) - offset >= ((gint64) lft) + CHECK_LIFETIME_MAX_DIFF) - return FALSE; - } - } - return TRUE; -} - -void -nmtstp_ip_address_assert_lifetime(const NMPlatformIPAddress *addr, - gint64 now, - guint32 expected_lifetime, - guint32 expected_preferred) -{ - gint64 n = now; - gint64 offset; - int i; - - g_assert(addr); - - if (now == -1) - now = nm_utils_get_monotonic_timestamp_sec(); - g_assert(now > 0); - - g_assert(expected_preferred <= expected_lifetime); - - if (expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && expected_preferred == NM_PLATFORM_LIFETIME_PERMANENT) { - g_assert_cmpint(addr->timestamp, ==, 0); - g_assert_cmpint(addr->lifetime, ==, NM_PLATFORM_LIFETIME_PERMANENT); - g_assert_cmpint(addr->preferred, ==, NM_PLATFORM_LIFETIME_PERMANENT); - return; - } - - g_assert_cmpint(addr->timestamp, >, 0); - g_assert_cmpint(addr->timestamp, <=, now); - - offset = (gint64) now - addr->timestamp; - g_assert_cmpint(offset, >=, 0); - - for (i = 0; i < 2; i++) { - guint32 lft = i ? expected_lifetime : expected_preferred; - guint32 adr = i ? addr->lifetime : addr->preferred; - - if (lft == NM_PLATFORM_LIFETIME_PERMANENT) - g_assert_cmpint(adr, ==, NM_PLATFORM_LIFETIME_PERMANENT); - else { - g_assert_cmpint(adr - offset, <=, lft + CHECK_LIFETIME_MAX_DIFF); - g_assert_cmpint(adr - offset, >=, lft - CHECK_LIFETIME_MAX_DIFF); - } - } - - g_assert(nmtstp_ip_address_check_lifetime(addr, n, expected_lifetime, expected_preferred)); -} - -/*****************************************************************************/ - -static void -_ip_address_add(NMPlatform * platform, - gboolean external_command, - gboolean is_v4, - int ifindex, - const NMIPAddr *address, - int plen, - const NMIPAddr *peer_address, - guint32 lifetime, - guint32 preferred, - guint32 flags, - const char * label) -{ - gint64 end_time; - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - const char * ifname; - gs_free char *s_valid = NULL; - gs_free char *s_preferred = NULL; - gs_free char *s_label = NULL; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; - - ifname = nm_platform_link_get_name(platform, ifindex); - g_assert(ifname); - - if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT) - s_valid = g_strdup_printf(" valid_lft %d", lifetime); - if (preferred != NM_PLATFORM_LIFETIME_PERMANENT) - s_preferred = g_strdup_printf(" preferred_lft %d", preferred); - if (label) - s_label = g_strdup_printf("%s:%s", ifname, label); - - if (is_v4) { - char s_peer[NM_UTILS_INET_ADDRSTRLEN + 50]; - - g_assert(flags == 0); - - if (peer_address->addr4 != address->addr4 || nmtst_get_rand_uint32() % 2) { - /* If the peer is the same as the local address, we can omit it. The result should be identical */ - nm_sprintf_buf(s_peer, " peer %s", _nm_utils_inet4_ntop(peer_address->addr4, b2)); - } else - s_peer[0] = '\0'; - - nmtstp_run_command_check("ip address change %s%s/%d dev %s%s%s%s", - _nm_utils_inet4_ntop(address->addr4, b1), - s_peer, - plen, - ifname, - s_valid ?: "", - s_preferred ?: "", - s_label ?: ""); - } else { - g_assert(label == NULL); - - /* flags not implemented (yet) */ - g_assert(flags == 0); - nmtstp_run_command_check("ip address change %s%s%s/%d dev %s%s%s%s", - _nm_utils_inet6_ntop(&address->addr6, b1), - !IN6_IS_ADDR_UNSPECIFIED(&peer_address->addr6) ? " peer " : "", - !IN6_IS_ADDR_UNSPECIFIED(&peer_address->addr6) - ? _nm_utils_inet6_ntop(&peer_address->addr6, b2) - : "", - plen, - ifname, - s_valid ?: "", - s_preferred ?: "", - s_label ?: ""); - } - } else { - gboolean success; - - if (is_v4) { - success = nm_platform_ip4_address_add(platform, - ifindex, - address->addr4, - plen, - peer_address->addr4, - 0u, - lifetime, - preferred, - flags, - label); - } else { - g_assert(label == NULL); - success = nm_platform_ip6_address_add(platform, - ifindex, - address->addr6, - plen, - peer_address->addr6, - lifetime, - preferred, - flags); - } - g_assert(success); - } - - /* Let's wait until we see the address. */ - end_time = nm_utils_get_monotonic_timestamp_msec() + 500; - do { - if (external_command) - nm_platform_process_events(platform); - - /* let's wait until we see the address as we added it. */ - if (is_v4) { - const NMPlatformIP4Address *a; - - g_assert(flags == 0); - a = nm_platform_ip4_address_get(platform, - ifindex, - address->addr4, - plen, - peer_address->addr4); - if (a && a->peer_address == peer_address->addr4 - && nmtstp_ip_address_check_lifetime((NMPlatformIPAddress *) a, - -1, - lifetime, - preferred) - && strcmp(a->label, label ?: "") == 0) - break; - } else { - const NMPlatformIP6Address *a; - - g_assert(label == NULL); - g_assert(flags == 0); - - a = nm_platform_ip6_address_get(platform, ifindex, &address->addr6); - if (a - && !memcmp(nm_platform_ip6_address_get_peer(a), - (IN6_IS_ADDR_UNSPECIFIED(&peer_address->addr6) - || IN6_ARE_ADDR_EQUAL(&address->addr6, &peer_address->addr6)) - ? &address->addr6 - : &peer_address->addr6, - sizeof(struct in6_addr)) - && nmtstp_ip_address_check_lifetime((NMPlatformIPAddress *) a, - -1, - lifetime, - preferred)) - break; - } - - /* for internal command, we expect not to reach this line.*/ - g_assert(external_command); - - nmtstp_assert_wait_for_signal_until(platform, end_time); - } while (TRUE); -} - -void -nmtstp_ip4_address_add(NMPlatform *platform, - gboolean external_command, - int ifindex, - in_addr_t address, - int plen, - in_addr_t peer_address, - guint32 lifetime, - guint32 preferred, - guint32 flags, - const char *label) -{ - _ip_address_add(platform, - external_command, - TRUE, - ifindex, - (NMIPAddr *) &address, - plen, - (NMIPAddr *) &peer_address, - lifetime, - preferred, - flags, - label); -} - -void -nmtstp_ip6_address_add(NMPlatform * platform, - gboolean external_command, - int ifindex, - struct in6_addr address, - int plen, - struct in6_addr peer_address, - guint32 lifetime, - guint32 preferred, - guint32 flags) -{ - _ip_address_add(platform, - external_command, - FALSE, - ifindex, - (NMIPAddr *) &address, - plen, - (NMIPAddr *) &peer_address, - lifetime, - preferred, - flags, - NULL); -} - -void -nmtstp_ip4_route_add(NMPlatform * platform, - int ifindex, - NMIPConfigSource source, - in_addr_t network, - guint8 plen, - in_addr_t gateway, - in_addr_t pref_src, - guint32 metric, - guint32 mss) -{ - NMPlatformIP4Route route = {}; - - route.ifindex = ifindex; - route.rt_source = source; - route.network = network; - route.plen = plen; - route.gateway = gateway; - route.pref_src = pref_src; - route.metric = metric; - route.mss = mss; - - g_assert( - NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_REPLACE, &route))); -} - -void -nmtstp_ip6_route_add(NMPlatform * platform, - int ifindex, - NMIPConfigSource source, - struct in6_addr network, - guint8 plen, - struct in6_addr gateway, - struct in6_addr pref_src, - guint32 metric, - guint32 mss) -{ - NMPlatformIP6Route route = {}; - - route.ifindex = ifindex; - route.rt_source = source; - route.network = network; - route.plen = plen; - route.gateway = gateway; - route.pref_src = pref_src; - route.metric = metric; - route.mss = mss; - - g_assert( - NMTST_NM_ERR_SUCCESS(nm_platform_ip6_route_add(platform, NMP_NLM_FLAG_REPLACE, &route))); -} - -/*****************************************************************************/ - -static void -_ip_address_del(NMPlatform * platform, - gboolean external_command, - gboolean is_v4, - int ifindex, - const NMIPAddr *address, - int plen, - const NMIPAddr *peer_address) -{ - gint64 end_time; - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - const char *ifname; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; - int success; - gboolean had_address; - - ifname = nm_platform_link_get_name(platform, ifindex); - g_assert(ifname); - - /* let's wait until we see the address as we added it. */ - if (is_v4) - had_address = !!nm_platform_ip4_address_get(platform, - ifindex, - address->addr4, - plen, - peer_address->addr4); - else - had_address = !!nm_platform_ip6_address_get(platform, ifindex, &address->addr6); - - if (is_v4) { - success = nmtstp_run_command("ip address delete %s%s%s/%d dev %s", - _nm_utils_inet4_ntop(address->addr4, b1), - peer_address->addr4 != address->addr4 ? " peer " : "", - peer_address->addr4 != address->addr4 - ? _nm_utils_inet4_ntop(peer_address->addr4, b2) - : "", - plen, - ifname); - } else { - g_assert(!peer_address); - success = nmtstp_run_command("ip address delete %s/%d dev %s", - _nm_utils_inet6_ntop(&address->addr6, b1), - plen, - ifname); - } - g_assert(success == 0 || !had_address); - } else { - gboolean success; - - if (is_v4) { - success = nm_platform_ip4_address_delete(platform, - ifindex, - address->addr4, - plen, - peer_address->addr4); - } else { - g_assert(!peer_address); - success = nm_platform_ip6_address_delete(platform, ifindex, address->addr6, plen); - } - g_assert(success); - } - - /* Let's wait until we get the result */ - end_time = nm_utils_get_monotonic_timestamp_msec() + 250; - do { - if (external_command) - nm_platform_process_events(platform); - - /* let's wait until we see the address as we added it. */ - if (is_v4) { - const NMPlatformIP4Address *a; - - a = nm_platform_ip4_address_get(platform, - ifindex, - address->addr4, - plen, - peer_address->addr4); - if (!a) - break; - } else { - const NMPlatformIP6Address *a; - - a = nm_platform_ip6_address_get(platform, ifindex, &address->addr6); - if (!a) - break; - } - - /* for internal command, we expect not to reach this line.*/ - g_assert(external_command); - - nmtstp_assert_wait_for_signal_until(platform, end_time); - } while (TRUE); -} - -void -nmtstp_ip4_address_del(NMPlatform *platform, - gboolean external_command, - int ifindex, - in_addr_t address, - int plen, - in_addr_t peer_address) -{ - _ip_address_del(platform, - external_command, - TRUE, - ifindex, - (NMIPAddr *) &address, - plen, - (NMIPAddr *) &peer_address); -} - -void -nmtstp_ip6_address_del(NMPlatform * platform, - gboolean external_command, - int ifindex, - struct in6_addr address, - int plen) -{ - _ip_address_del(platform, external_command, FALSE, ifindex, (NMIPAddr *) &address, plen, NULL); -} - -/*****************************************************************************/ - -#define _assert_pllink(platform, success, pllink, name, type) \ - G_STMT_START \ - { \ - const NMPlatformLink *_pllink = (pllink); \ - \ - if ((success)) { \ - g_assert(_pllink); \ - g_assert(_pllink \ - == nmtstp_link_get_typed(platform, _pllink->ifindex, (name), (type))); \ - } else { \ - g_assert(!_pllink); \ - g_assert(!nmtstp_link_get(platform, 0, (name))); \ - } \ - } \ - G_STMT_END - -const NMPlatformLink * -nmtstp_link_bridge_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkBridge *lnk) -{ - const NMPlatformLink * pllink = NULL; - const NMPlatformLnkBridge *ll = NULL; - int r = 0; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - char sbuf_gfw[100]; - char sbuf_mhm[100]; - char sbuf_mlmc[100]; - char sbuf_mlmi[100]; - char sbuf_mmi[100]; - char sbuf_mqi[100]; - char sbuf_mqii[100]; - char sbuf_msqc[100]; - char sbuf_msqi[100]; - char sbuf_mqri[100]; - - r = nmtstp_run_command( - "ip link add %s type bridge " - "forward_delay %u " - "hello_time %u " - "max_age %u " - "ageing_time %u " - "stp_state %d " - "priority %u " - "vlan_protocol %u " - "vlan_stats_enabled %d " - "%s" /* group_fwd_mask */ - "group_address " NM_ETHER_ADDR_FORMAT_STR " " - "mcast_snooping %d " - "mcast_router %u " - "mcast_query_use_ifaddr %d " - "mcast_querier %d " - "%s" /* mcast_hash_max */ - "%s" /* mcast_last_member_count */ - "%s" /* mcast_startup_query_count */ - "%s" /* mcast_last_member_interval */ - "%s" /* mcast_membership_interval */ - "%s" /* mcast_querier_interval */ - "%s" /* mcast_query_interval */ - "%s" /* mcast_query_response_interval */ - "%s" /* mcast_startup_query_interval */ - "", - name, - lnk->forward_delay, - lnk->hello_time, - lnk->max_age, - lnk->ageing_time, - (int) lnk->stp_state, - lnk->priority, - lnk->vlan_protocol, - (int) lnk->vlan_stats_enabled, - lnk->group_fwd_mask != 0 - ? nm_sprintf_buf(sbuf_gfw, "group_fwd_mask %#x ", lnk->group_fwd_mask) - : "", - NM_ETHER_ADDR_FORMAT_VAL(lnk->group_addr), - (int) lnk->mcast_snooping, - lnk->mcast_router, - (int) lnk->mcast_query_use_ifaddr, - (int) lnk->mcast_querier, - lnk->mcast_hash_max != NM_BRIDGE_MULTICAST_HASH_MAX_DEF - ? nm_sprintf_buf(sbuf_mhm, "mcast_hash_max %u ", lnk->mcast_hash_max) - : "", - lnk->mcast_last_member_count != NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_DEF - ? nm_sprintf_buf(sbuf_mlmc, - "mcast_last_member_count %u ", - lnk->mcast_last_member_count) - : "", - lnk->mcast_startup_query_count != NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_DEF - ? nm_sprintf_buf(sbuf_msqc, - "mcast_startup_query_count %u ", - lnk->mcast_startup_query_count) - : "", - lnk->mcast_last_member_interval != NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_DEF - ? nm_sprintf_buf(sbuf_mlmi, - "mcast_last_member_interval %" G_GUINT64_FORMAT " ", - lnk->mcast_last_member_interval) - : "", - lnk->mcast_membership_interval != NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_DEF - ? nm_sprintf_buf(sbuf_mmi, - "mcast_membership_interval %" G_GUINT64_FORMAT " ", - lnk->mcast_membership_interval) - : "", - lnk->mcast_querier_interval != NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_DEF - ? nm_sprintf_buf(sbuf_mqi, - "mcast_querier_interval %" G_GUINT64_FORMAT " ", - lnk->mcast_querier_interval) - : "", - lnk->mcast_query_interval != NM_BRIDGE_MULTICAST_QUERY_INTERVAL_DEF - ? nm_sprintf_buf(sbuf_mqii, - "mcast_query_interval %" G_GUINT64_FORMAT " ", - lnk->mcast_query_interval) - : "", - lnk->mcast_query_response_interval != NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_DEF - ? nm_sprintf_buf(sbuf_mqri, - "mcast_query_response_interval %" G_GUINT64_FORMAT " ", - lnk->mcast_query_response_interval) - : "", - lnk->mcast_startup_query_interval != NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_DEF - ? nm_sprintf_buf(sbuf_msqi, - "mcast_startup_query_interval %" G_GUINT64_FORMAT " ", - lnk->mcast_startup_query_interval) - : ""); - if (r == 0) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_BRIDGE, 100); - else - _LOGI( - "Adding bridge device via iproute2 failed. Assume iproute2 is not up to the task."); - } - - if (!pllink) { - r = nm_platform_link_bridge_add(platform, name, NULL, 0, 0, lnk, &pllink); - } - - _assert_pllink(platform, r == 0, pllink, name, NM_LINK_TYPE_BRIDGE); - - ll = NMP_OBJECT_CAST_LNK_BRIDGE(NMP_OBJECT_UP_CAST(pllink)->_link.netlink.lnk); - - g_assert_cmpint(lnk->forward_delay, ==, ll->forward_delay); - g_assert_cmpint(lnk->hello_time, ==, ll->hello_time); - g_assert_cmpint(lnk->max_age, ==, ll->max_age); - g_assert_cmpint(lnk->ageing_time, ==, ll->ageing_time); - g_assert_cmpint(lnk->stp_state, ==, ll->stp_state); - g_assert_cmpint(lnk->priority, ==, ll->priority); - g_assert_cmpint(lnk->vlan_stats_enabled, ==, ll->vlan_stats_enabled); - g_assert_cmpint(lnk->group_fwd_mask, ==, ll->group_fwd_mask); - g_assert_cmpint(lnk->mcast_snooping, ==, ll->mcast_snooping); - g_assert_cmpint(lnk->mcast_router, ==, ll->mcast_router); - g_assert_cmpint(lnk->mcast_query_use_ifaddr, ==, ll->mcast_query_use_ifaddr); - g_assert_cmpint(lnk->mcast_querier, ==, ll->mcast_querier); - g_assert_cmpint(lnk->mcast_hash_max, ==, ll->mcast_hash_max); - g_assert_cmpint(lnk->mcast_last_member_count, ==, ll->mcast_last_member_count); - g_assert_cmpint(lnk->mcast_startup_query_count, ==, ll->mcast_startup_query_count); - g_assert_cmpint(lnk->mcast_last_member_interval, ==, ll->mcast_last_member_interval); - g_assert_cmpint(lnk->mcast_membership_interval, ==, ll->mcast_membership_interval); - g_assert_cmpint(lnk->mcast_querier_interval, ==, ll->mcast_querier_interval); - g_assert_cmpint(lnk->mcast_query_interval, ==, ll->mcast_query_interval); - g_assert_cmpint(lnk->mcast_query_response_interval, ==, ll->mcast_query_response_interval); - g_assert_cmpint(lnk->mcast_startup_query_interval, ==, ll->mcast_startup_query_interval); - - return pllink; -} -const NMPlatformLink * -nmtstp_link_veth_add(NMPlatform *platform, - gboolean external_command, - const char *name, - const char *peer) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - success = !nmtstp_run_command("ip link add dev %s type veth peer name %s", name, peer); - if (success) { - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VETH, 100); - nmtstp_assert_wait_for_link(platform, peer, NM_LINK_TYPE_VETH, 10); - } - } else - success = NMTST_NM_ERR_SUCCESS(nm_platform_link_veth_add(platform, name, peer, &pllink)); - - g_assert(success); - _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_VETH); - return pllink; -} - -const NMPlatformLink * -nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const char *name) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - success = !nmtstp_run_command("ip link add %s type dummy", name); - if (success) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_DUMMY, 100); - } else - success = NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(platform, name, &pllink)); - - g_assert(success); - _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_DUMMY); - return pllink; -} - -const NMPlatformLink * -nmtstp_link_gre_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkGre *lnk) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - char b1[INET_ADDRSTRLEN]; - char b2[INET_ADDRSTRLEN]; - NMLinkType link_type; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - link_type = lnk->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE; - - _init_platform(&platform, external_command); - - if (external_command) { - gs_free char *dev = NULL; - char * obj, *type; - - if (lnk->parent_ifindex) - dev = - g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); - - obj = lnk->is_tap ? "link" : "tunnel"; - type = lnk->is_tap ? "type gretap" : "mode gre"; - - success = !nmtstp_run_command("ip %s add %s %s %s local %s remote %s ttl %u tos %02x %s", - obj, - name, - type, - dev ?: "", - _nm_utils_inet4_ntop(lnk->local, b1), - _nm_utils_inet4_ntop(lnk->remote, b2), - lnk->ttl, - lnk->tos, - lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); - if (success) - pllink = nmtstp_assert_wait_for_link(platform, name, link_type, 100); - } else - success = - NMTST_NM_ERR_SUCCESS(nm_platform_link_gre_add(platform, name, NULL, 0, lnk, &pllink)); - - _assert_pllink(platform, success, pllink, name, link_type); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_ip6tnl_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkIp6Tnl *lnk) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; - char encap[20]; - char tclass[20]; - gboolean encap_ignore; - gboolean tclass_inherit; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - g_assert(!lnk->is_gre); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - gs_free char *dev = NULL; - const char * mode; - - if (lnk->parent_ifindex) - dev = - g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); - - switch (lnk->proto) { - case IPPROTO_IPIP: - mode = "ipip6"; - break; - case IPPROTO_IPV6: - mode = "ip6ip6"; - break; - default: - g_assert_not_reached(); - } - - encap_ignore = NM_FLAGS_HAS(lnk->flags, IP6_TNL_F_IGN_ENCAP_LIMIT); - tclass_inherit = NM_FLAGS_HAS(lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS); - - success = !nmtstp_run_command( - "ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %s encaplimit %s " - "flowlabel %x", - name, - mode, - dev, - _nm_utils_inet6_ntop(&lnk->local, b1), - _nm_utils_inet6_ntop(&lnk->remote, b2), - lnk->ttl, - tclass_inherit ? "inherit" : nm_sprintf_buf(tclass, "%02x", lnk->tclass), - encap_ignore ? "none" : nm_sprintf_buf(encap, "%u", lnk->encap_limit), - lnk->flow_label); - if (success) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_IP6TNL, 100); - } else - success = NMTST_NM_ERR_SUCCESS(nm_platform_link_ip6tnl_add(platform, name, lnk, &pllink)); - - _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_IP6TNL); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_ip6gre_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkIp6Tnl *lnk) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - char b1[NM_UTILS_INET_ADDRSTRLEN]; - char b2[NM_UTILS_INET_ADDRSTRLEN]; - char tclass[20]; - gboolean tclass_inherit; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - g_assert(lnk->is_gre); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - gs_free char *dev = NULL; - - if (lnk->parent_ifindex) - dev = - g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); - - tclass_inherit = NM_FLAGS_HAS(lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS); - - success = !nmtstp_run_command( - "ip link add %s type %s %s local %s remote %s ttl %u tclass %s flowlabel %x", - name, - lnk->is_tap ? "ip6gretap" : "ip6gre", - dev, - _nm_utils_inet6_ntop(&lnk->local, b1), - _nm_utils_inet6_ntop(&lnk->remote, b2), - lnk->ttl, - tclass_inherit ? "inherit" : nm_sprintf_buf(tclass, "%02x", lnk->tclass), - lnk->flow_label); - if (success) { - pllink = nmtstp_assert_wait_for_link(platform, - name, - lnk->is_tap ? NM_LINK_TYPE_IP6GRETAP - : NM_LINK_TYPE_IP6GRE, - 100); - } - } else - success = NMTST_NM_ERR_SUCCESS( - nm_platform_link_ip6gre_add(platform, name, NULL, 0, lnk, &pllink)); - - _assert_pllink(platform, - success, - pllink, - name, - lnk->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_ipip_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkIpIp *lnk) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - char b1[INET_ADDRSTRLEN]; - char b2[INET_ADDRSTRLEN]; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - gs_free char *dev = NULL; - - if (lnk->parent_ifindex) - dev = - g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); - - success = !nmtstp_run_command( - "ip tunnel add %s mode ipip %s local %s remote %s ttl %u tos %02x %s", - name, - dev, - _nm_utils_inet4_ntop(lnk->local, b1), - _nm_utils_inet4_ntop(lnk->remote, b2), - lnk->ttl, - lnk->tos, - lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); - if (success) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_IPIP, 100); - } else - success = NMTST_NM_ERR_SUCCESS(nm_platform_link_ipip_add(platform, name, lnk, &pllink)); - - _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_IPIP); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_macvlan_add(NMPlatform * platform, - gboolean external_command, - const char * name, - int parent, - const NMPlatformLnkMacvlan *lnk) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - NMLinkType link_type; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - link_type = lnk->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN; - - if (external_command) { - const char *dev; - char * modes[] = { - [MACVLAN_MODE_BRIDGE] = "bridge", - [MACVLAN_MODE_VEPA] = "vepa", - [MACVLAN_MODE_PRIVATE] = "private", - [MACVLAN_MODE_PASSTHRU] = "passthru", - }; - - dev = nm_platform_link_get_name(platform, parent); - g_assert(dev); - g_assert_cmpint(lnk->mode, <, G_N_ELEMENTS(modes)); - - success = !nmtstp_run_command("ip link add name %s link %s type %s mode %s %s", - name, - dev, - lnk->tap ? "macvtap" : "macvlan", - modes[lnk->mode], - lnk->no_promisc ? "nopromisc" : ""); - if (success) - pllink = nmtstp_assert_wait_for_link(platform, name, link_type, 100); - } else - success = NMTST_NM_ERR_SUCCESS( - nm_platform_link_macvlan_add(platform, name, parent, lnk, &pllink)); - - _assert_pllink(platform, success, pllink, name, link_type); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_sit_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkSit *lnk) -{ - const NMPlatformLink *pllink = NULL; - gboolean success; - char b1[INET_ADDRSTRLEN]; - char b2[INET_ADDRSTRLEN]; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - const char *dev = ""; - - if (lnk->parent_ifindex) { - const char *parent_name; - - parent_name = nm_platform_link_get_name(platform, lnk->parent_ifindex); - g_assert(parent_name); - dev = nm_sprintf_bufa(100, " dev %s", parent_name); - } - - success = - !nmtstp_run_command("ip tunnel add %s mode sit%s local %s remote %s ttl %u tos %02x %s", - name, - dev, - _nm_utils_inet4_ntop(lnk->local, b1), - _nm_utils_inet4_ntop(lnk->remote, b2), - lnk->ttl, - lnk->tos, - lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); - if (success) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_SIT, 100); - } else - success = NMTST_NM_ERR_SUCCESS(nm_platform_link_sit_add(platform, name, lnk, &pllink)); - - _assert_pllink(platform, success, pllink, name, NM_LINK_TYPE_SIT); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_tun_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkTun *lnk, - int * out_fd) -{ - const NMPlatformLink *pllink = NULL; - int err; - int r; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - g_assert(lnk); - g_assert(NM_IN_SET(lnk->type, IFF_TUN, IFF_TAP)); - g_assert(!out_fd || *out_fd == -1); - - if (!lnk->persist) { - /* ip tuntap does not support non-persistent devices. - * - * Add this device only via NMPlatform. */ - if (external_command == -1) - external_command = FALSE; - } - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - g_assert(lnk->persist); - - err = nmtstp_run_command( - "ip tuntap add" - " mode %s" - "%s" /* user */ - "%s" /* group */ - "%s" /* pi */ - "%s" /* vnet_hdr */ - "%s" /* multi_queue */ - " name %s", - lnk->type == IFF_TUN ? "tun" : "tap", - lnk->owner_valid ? nm_sprintf_bufa(100, " user %u", (guint) lnk->owner) : "", - lnk->group_valid ? nm_sprintf_bufa(100, " group %u", (guint) lnk->group) : "", - lnk->pi ? " pi" : "", - lnk->vnet_hdr ? " vnet_hdr" : "", - lnk->multi_queue ? " multi_queue" : "", - name); - /* Older versions of iproute2 don't support adding devices. - * On failure, fallback to using platform code. */ - if (err == 0) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_TUN, 100); - else - g_error("failure to add tun/tap device via ip-route"); - } else { - g_assert(lnk->persist || out_fd); - r = nm_platform_link_tun_add(platform, name, lnk, &pllink, out_fd); - g_assert_cmpint(r, ==, 0); - } - - g_assert(pllink); - g_assert_cmpint(pllink->type, ==, NM_LINK_TYPE_TUN); - g_assert_cmpstr(pllink->name, ==, name); - return pllink; -} - -const NMPlatformLink * -nmtstp_link_vrf_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkVrf *lnk, - gboolean * out_not_supported) -{ - const NMPlatformLink *pllink = NULL; - int r = 0; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - NM_SET_OUT(out_not_supported, FALSE); - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - r = nmtstp_run_command("ip link add %s type vrf table %u", name, lnk->table); - - if (r == 0) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VRF, 100); - else - _LOGI("Adding vrf device via iproute2 failed. Assume iproute2 is not up to the task."); - } - - if (!pllink) { - r = nm_platform_link_vrf_add(platform, name, lnk, &pllink); - if (r == -EOPNOTSUPP) - NM_SET_OUT(out_not_supported, TRUE); - } - - _assert_pllink(platform, r == 0, pllink, name, NM_LINK_TYPE_VRF); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_vxlan_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkVxlan *lnk) -{ - const NMPlatformLink *pllink = NULL; - int err; - int r; - - g_assert(nm_utils_ifname_valid_kernel(name, NULL)); - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - gs_free char *dev = NULL; - char local[NM_UTILS_INET_ADDRSTRLEN]; - char group[NM_UTILS_INET_ADDRSTRLEN]; - - if (lnk->parent_ifindex) - dev = - g_strdup_printf("dev %s", nm_platform_link_get_name(platform, lnk->parent_ifindex)); - - if (lnk->local) - _nm_utils_inet4_ntop(lnk->local, local); - else if (memcmp(&lnk->local6, &in6addr_any, sizeof(in6addr_any))) - _nm_utils_inet6_ntop(&lnk->local6, local); - else - local[0] = '\0'; - - if (lnk->group) - _nm_utils_inet4_ntop(lnk->group, group); - else if (memcmp(&lnk->group6, &in6addr_any, sizeof(in6addr_any))) - _nm_utils_inet6_ntop(&lnk->group6, group); - else - group[0] = '\0'; - - err = nmtstp_run_command("ip link add %s type vxlan id %u %s local %s group %s ttl %u tos " - "%02x dstport %u srcport %u %u ageing %u", - name, - lnk->id, - dev ?: "", - local, - group, - lnk->ttl, - lnk->tos, - lnk->dst_port, - lnk->src_port_min, - lnk->src_port_max, - lnk->ageing); - /* Older versions of iproute2 don't support adding vxlan devices. - * On failure, fallback to using platform code. */ - if (err == 0) - pllink = nmtstp_assert_wait_for_link(platform, name, NM_LINK_TYPE_VXLAN, 100); - else - _LOGI( - "Adding vxlan device via iproute2 failed. Assume iproute2 is not up to the task."); - } - if (!pllink) { - r = nm_platform_link_vxlan_add(platform, name, lnk, &pllink); - g_assert(NMTST_NM_ERR_SUCCESS(r)); - g_assert(pllink); - } - - g_assert_cmpint(pllink->type, ==, NM_LINK_TYPE_VXLAN); - g_assert_cmpstr(pllink->name, ==, name); - return pllink; -} - -/*****************************************************************************/ - -const NMPlatformLink * -nmtstp_link_get_typed(NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type) -{ - const NMPlatformLink *pllink = NULL; - - _init_platform(&platform, FALSE); - - if (ifindex > 0) { - pllink = nm_platform_link_get(platform, ifindex); - - if (pllink) { - g_assert_cmpint(pllink->ifindex, ==, ifindex); - if (name) - g_assert_cmpstr(name, ==, pllink->name); - } else { - if (name) - g_assert(!nm_platform_link_get_by_ifname(platform, name)); - } - } else { - g_assert(name); - - pllink = nm_platform_link_get_by_ifname(platform, name); - - if (pllink) - g_assert_cmpstr(name, ==, pllink->name); - } - - g_assert(!name || nm_utils_ifname_valid_kernel(name, NULL)); - - if (pllink && link_type != NM_LINK_TYPE_NONE) - g_assert_cmpint(pllink->type, ==, link_type); - - return pllink; -} - -const NMPlatformLink * -nmtstp_link_get(NMPlatform *platform, int ifindex, const char *name) -{ - return nmtstp_link_get_typed(platform, ifindex, name, NM_LINK_TYPE_NONE); -} - -/*****************************************************************************/ - -void -nmtstp_link_delete(NMPlatform *platform, - gboolean external_command, - int ifindex, - const char *name, - gboolean require_exist) -{ - gint64 end_time; - const NMPlatformLink *pllink; - gboolean success; - gs_free char * name_copy = NULL; - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - pllink = nmtstp_link_get(platform, ifindex, name); - - if (!pllink) { - g_assert(!require_exist); - return; - } - - name = name_copy = g_strdup(pllink->name); - ifindex = pllink->ifindex; - - if (external_command) { - nmtstp_run_command_check("ip link delete %s", name); - } else { - success = nm_platform_link_delete(platform, ifindex); - g_assert(success); - } - - /* Let's wait until we get the result */ - end_time = nm_utils_get_monotonic_timestamp_msec() + 250; - do { - if (external_command) - nm_platform_process_events(platform); - - if (!nm_platform_link_get(platform, ifindex)) { - g_assert(!nm_platform_link_get_by_ifname(platform, name)); - break; - } - - /* for internal command, we expect not to reach this line.*/ - g_assert(external_command); - - nmtstp_assert_wait_for_signal_until(platform, end_time); - } while (TRUE); -} - -/*****************************************************************************/ - -void -nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up) -{ - const NMPlatformLink *plink; - gint64 end_time; - - external_command = nmtstp_run_command_check_external(external_command); - - _init_platform(&platform, external_command); - - if (external_command) { - const char *ifname; - - ifname = nm_platform_link_get_name(platform, ifindex); - g_assert(ifname); - - nmtstp_run_command_check("ip link set %s %s", ifname, up ? "up" : "down"); - } else { - if (up) - g_assert(nm_platform_link_set_up(platform, ifindex, NULL)); - else - g_assert(nm_platform_link_set_down(platform, ifindex)); - } - - /* Let's wait until we get the result */ - end_time = nm_utils_get_monotonic_timestamp_msec() + 250; - do { - if (external_command) - nm_platform_process_events(platform); - - /* let's wait until we see the address as we added it. */ - plink = nm_platform_link_get(platform, ifindex); - g_assert(plink); - - if (NM_FLAGS_HAS(plink->n_ifi_flags, IFF_UP) == !!up) - break; - - /* for internal command, we expect not to reach this line.*/ - g_assert(external_command); - - nmtstp_assert_wait_for_signal_until(platform, end_time); - } while (TRUE); -} - -/*****************************************************************************/ - -gboolean -nmtstp_kernel_support_get(NMPlatformKernelSupportType type) -{ - const NMPlatformLink *pllink; - NMTernary v; - - v = nm_platform_kernel_support_get_full(type, FALSE); - if (v != NM_TERNARY_DEFAULT) - return v != NM_TERNARY_FALSE; - - switch (type) { - case NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED: - pllink = nmtstp_link_bridge_add(NULL, -1, "br-test-11", &nm_platform_lnk_bridge_default); - nmtstp_link_delete(NULL, -1, pllink->ifindex, NULL, TRUE); - v = nm_platform_kernel_support_get_full(type, FALSE); - g_assert(v != NM_TERNARY_DEFAULT); - return v; - default: - g_assert_not_reached(); - } -} - -/*****************************************************************************/ - -struct _NMTstpNamespaceHandle { - pid_t pid; - int pipe_fd; -}; - -NMTstpNamespaceHandle * -nmtstp_namespace_create(int unshare_flags, GError **error) -{ - NMTstpNamespaceHandle *ns_handle; - int e; - int errsv; - pid_t pid, pid2; - int pipefd_c2p[2]; - int pipefd_p2c[2]; - ssize_t r; - - e = pipe2(pipefd_c2p, O_CLOEXEC); - if (e != 0) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "pipe() failed with %d (%s)", - errsv, - nm_strerror_native(errsv)); - return FALSE; - } - - e = pipe2(pipefd_p2c, O_CLOEXEC); - if (e != 0) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "pipe() failed with %d (%s)", - errsv, - nm_strerror_native(errsv)); - nm_close(pipefd_c2p[0]); - nm_close(pipefd_c2p[1]); - return FALSE; - } - - pid = fork(); - if (pid < 0) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "fork() failed with %d (%s)", - errsv, - nm_strerror_native(errsv)); - nm_close(pipefd_c2p[0]); - nm_close(pipefd_c2p[1]); - nm_close(pipefd_p2c[0]); - nm_close(pipefd_p2c[1]); - return FALSE; - } - - if (pid == 0) { - char read_buf[1]; - - nm_close(pipefd_c2p[0]); /* close read-end */ - nm_close(pipefd_p2c[1]); /* close write-end */ - - if (unshare(unshare_flags) != 0) { - errsv = errno; - if (errsv == 0) - errsv = -1; - } else - errsv = 0; - - /* sync with parent process and send result. */ - do { - r = write(pipefd_c2p[1], &errsv, sizeof(errsv)); - } while (r < 0 && errno == EINTR); - if (r != sizeof(errsv)) { - errsv = errno; - if (errsv == 0) - errsv = -2; - } - nm_close(pipefd_c2p[1]); - - /* wait until parent process terminates (or kills us). */ - if (errsv == 0) { - do { - r = read(pipefd_p2c[0], read_buf, sizeof(read_buf)); - } while (r < 0 && errno == EINTR); - } - nm_close(pipefd_p2c[0]); - _exit(0); - } - - nm_close(pipefd_c2p[1]); /* close write-end */ - nm_close(pipefd_p2c[0]); /* close read-end */ - - /* sync with child process. */ - do { - r = read(pipefd_c2p[0], &errsv, sizeof(errsv)); - } while (r < 0 && errno == EINTR); - - nm_close(pipefd_c2p[0]); - - if (r != sizeof(errsv) || errsv != 0) { - int status; - - if (r != sizeof(errsv)) { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "child process failed for unknown reason"); - } else { - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "child process signaled failure %d (%s)", - errsv, - nm_strerror_native(errsv)); - } - nm_close(pipefd_p2c[1]); - kill(pid, SIGKILL); - do { - pid2 = waitpid(pid, &status, 0); - } while (pid2 == -1 && errno == EINTR); - return FALSE; - } - - ns_handle = g_new0(NMTstpNamespaceHandle, 1); - ns_handle->pid = pid; - ns_handle->pipe_fd = pipefd_p2c[1]; - return ns_handle; -} - -pid_t -nmtstp_namespace_handle_get_pid(NMTstpNamespaceHandle *ns_handle) -{ - g_return_val_if_fail(ns_handle, 0); - g_return_val_if_fail(ns_handle->pid > 0, 0); - - return ns_handle->pid; -} - -void -nmtstp_namespace_handle_release(NMTstpNamespaceHandle *ns_handle) -{ - pid_t pid; - int status; - - if (!ns_handle) - return; - - g_return_if_fail(ns_handle->pid > 0); - - nm_close(ns_handle->pipe_fd); - ns_handle->pipe_fd = 0; - - kill(ns_handle->pid, SIGKILL); - - do { - pid = waitpid(ns_handle->pid, &status, 0); - } while (pid == -1 && errno == EINTR); - ns_handle->pid = 0; - - g_free(ns_handle); -} - -int -nmtstp_namespace_get_fd_for_process(pid_t pid, const char *ns_name) -{ - char p[1000]; - - g_return_val_if_fail(pid > 0, 0); - g_return_val_if_fail(ns_name && ns_name[0] && strlen(ns_name) < 50, 0); - - nm_sprintf_buf(p, "/proc/%lu/ns/%s", (unsigned long) pid, ns_name); - - return open(p, O_RDONLY | O_CLOEXEC); -} - -/*****************************************************************************/ - -void -nmtstp_netns_select_random(NMPlatform **platforms, gsize n_platforms, NMPNetns **netns) -{ - int i; - - g_assert(platforms); - g_assert(n_platforms && n_platforms <= G_MAXINT32); - g_assert(netns && !*netns); - for (i = 0; i < n_platforms; i++) - g_assert(NM_IS_PLATFORM(platforms[i])); - - i = nmtst_get_rand_uint32() % (n_platforms + 1); - if (i == 0) - return; - g_assert(nm_platform_netns_push(platforms[i - 1], netns)); -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -static gboolean -unshare_user(void) -{ - FILE *f; - uid_t uid = geteuid(); - gid_t gid = getegid(); - - /* Already a root? */ - if (gid == 0 && uid == 0) - return TRUE; - - /* Become a root in new user NS. */ - if (unshare(CLONE_NEWUSER) != 0) - return FALSE; - - /* Since Linux 3.19 we have to disable setgroups() in order to map users. - * Just proceed if the file is not there. */ - f = fopen("/proc/self/setgroups", "we"); - if (f) { - fprintf(f, "deny"); - fclose(f); - } - - /* Map current UID to root in NS to be created. */ - f = fopen("/proc/self/uid_map", "we"); - if (!f) - return FALSE; - fprintf(f, "0 %d 1", uid); - fclose(f); - - /* Map current GID to root in NS to be created. */ - f = fopen("/proc/self/gid_map", "we"); - if (!f) - return FALSE; - fprintf(f, "0 %d 1", gid); - fclose(f); - - return TRUE; -} - -int -main(int argc, char **argv) -{ - int result; - const char *program = *argv; - - _nmtstp_init_tests(&argc, &argv); - - if (nmtstp_is_root_test() && (geteuid() != 0 || getegid() != 0)) { - if (g_getenv("NMTST_FORCE_REAL_ROOT") || !unshare_user()) { - /* Try to exec as sudo, this function does not return, if a sudo-cmd is set. */ - nmtst_reexec_sudo(); - -#ifdef REQUIRE_ROOT_TESTS - g_print("Fail test: requires root privileges (%s)\n", program); - return EXIT_FAILURE; -#else - g_print("Skipping test: requires root privileges (%s)\n", program); - return g_test_run(); -#endif - } - } - - if (nmtstp_is_root_test() && !g_getenv("NMTST_NO_UNSHARE")) { - int errsv; - - if (unshare(CLONE_NEWNET | CLONE_NEWNS) != 0) { - errsv = errno; - g_error("unshare(CLONE_NEWNET|CLONE_NEWNS) failed with %s (%d)", - nm_strerror_native(errsv), - errsv); - } - - /* We need a read-only /sys so that the platform knows there's no udev. */ - mount(NULL, "/sys", "sysfs", MS_SLAVE, NULL); - if (mount("sys", "/sys", "sysfs", MS_RDONLY, NULL) != 0) { - errsv = errno; - g_error("mount(\"/sys\") failed with %s (%d)", nm_strerror_native(errsv), errsv); - } - } - - nmtstp_setup_platform(); - - _nmtstp_setup_tests(); - - result = g_test_run(); - - nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); - - g_object_unref(NM_PLATFORM_GET); - return result; -} diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h deleted file mode 100644 index 1d996f46..00000000 --- a/src/platform/tests/test-common.h +++ /dev/null @@ -1,610 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 - 2017 Red Hat, Inc. - */ - -#include <stdlib.h> -#include <unistd.h> -#include <syslog.h> -#include <arpa/inet.h> -#include <linux/if.h> -#include <linux/if_link.h> -#include <linux/ip6_tunnel.h> - -#include "platform/nm-platform.h" -#include "platform/nmp-object.h" -#include "platform/nm-fake-platform.h" -#include "platform/nm-linux-platform.h" - -#include "nm-test-utils-core.h" - -#define DEVICE_NAME "nm-test-device" - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "platform-test" -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, __VA_ARGS__) - -#define _LOG(level, domain, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = (domain); \ - \ - if (nm_logging_enabled(__level, __domain)) { \ - gint64 _ts = nm_utils_get_monotonic_timestamp_nsec(); \ - \ - _nm_log(__level, \ - __domain, \ - 0, \ - NULL, \ - NULL, \ - "%s[%ld.%09ld]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (long) (_ts / NM_UTILS_NSEC_PER_SEC), \ - (long) (_ts % NM_UTILS_NSEC_PER_SEC) _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -gboolean nmtstp_is_root_test(void); -gboolean nmtstp_is_sysfs_writable(void); - -/*****************************************************************************/ - -typedef struct _NMTstpNamespaceHandle NMTstpNamespaceHandle; - -NMTstpNamespaceHandle *nmtstp_namespace_create(int flags, GError **error); - -void nmtstp_namespace_handle_release(NMTstpNamespaceHandle *handle); -pid_t nmtstp_namespace_handle_get_pid(NMTstpNamespaceHandle *handle); - -int nmtstp_namespace_get_fd_for_process(pid_t pid, const char *ns_name); - -/*****************************************************************************/ - -void nmtstp_netns_select_random(NMPlatform **platforms, gsize n_platforms, NMPNetns **netns); - -/*****************************************************************************/ - -typedef struct { - gulong handler_id; - const char * name; - NMPlatformSignalChangeType change_type; - int received_count; - GMainLoop * loop; - int ifindex; - const char * ifname; -} SignalData; - -SignalData *add_signal_full(const char * name, - NMPlatformSignalChangeType change_type, - GCallback callback, - int ifindex, - const char * ifname); -#define add_signal(name, change_type, callback) \ - add_signal_full(name, change_type, (GCallback) callback, 0, NULL) -#define add_signal_ifindex(name, change_type, callback, ifindex) \ - add_signal_full(name, change_type, (GCallback) callback, ifindex, NULL) -#define add_signal_ifname(name, change_type, callback, ifname) \ - add_signal_full(name, change_type, (GCallback) callback, 0, ifname) -void _accept_signal(const char *file, int line, const char *func, SignalData *data); -void -_accept_signals(const char *file, int line, const char *func, SignalData *data, int min, int max); -void _wait_signal(const char *file, int line, const char *func, SignalData *data); -void _accept_or_wait_signal(const char *file, int line, const char *func, SignalData *data); -void _ensure_no_signal(const char *file, int line, const char *func, SignalData *data); -void _free_signal(const char *file, int line, const char *func, SignalData *data); -#define accept_signal(data) _accept_signal(__FILE__, __LINE__, G_STRFUNC, data) -#define accept_signals(data, min, max) \ - _accept_signals(__FILE__, __LINE__, G_STRFUNC, data, min, max) -#define wait_signal(data) _wait_signal(__FILE__, __LINE__, G_STRFUNC, data) -#define accept_or_wait_signal(data) _accept_or_wait_signal(__FILE__, __LINE__, G_STRFUNC, data) -#define ensure_no_signal(data) _ensure_no_signal(__FILE__, __LINE__, G_STRFUNC, data) -#define free_signal(data) _free_signal(__FILE__, __LINE__, G_STRFUNC, data) - -void link_callback(NMPlatform * platform, - int obj_type_i, - int ifindex, - NMPlatformLink *received, - int change_type_i, - SignalData * data); - -/*****************************************************************************/ - -int nmtstp_run_command(const char *format, ...) _nm_printf(1, 2); -#define nmtstp_run_command_check(...) \ - do { \ - g_assert_cmpint(nmtstp_run_command(__VA_ARGS__), ==, 0); \ - } while (0) - -/*****************************************************************************/ - -guint nmtstp_wait_for_signal(NMPlatform *platform, gint64 timeout_msec); -guint nmtstp_wait_for_signal_until(NMPlatform *platform, gint64 until_ms); -const NMPlatformLink *nmtstp_wait_for_link(NMPlatform *platform, - const char *ifname, - NMLinkType expected_link_type, - gint64 timeout_msec); -const NMPlatformLink *nmtstp_wait_for_link_until(NMPlatform *platform, - const char *ifname, - NMLinkType expected_link_type, - gint64 until_ms); - -#define nmtstp_assert_wait_for_signal(platform, timeout_msec) \ - G_STMT_START \ - { \ - if (nmtstp_wait_for_signal(platform, timeout_msec) == 0) \ - g_assert_not_reached(); \ - } \ - G_STMT_END - -#define nmtstp_assert_wait_for_signal_until(platform, until_ms) \ - G_STMT_START \ - { \ - if (nmtstp_wait_for_signal_until(platform, until_ms) == 0) \ - g_assert_not_reached(); \ - } \ - G_STMT_END - -#define nmtstp_assert_wait_for_link(platform, ifname, expected_link_type, timeout_msec) \ - nmtst_assert_nonnull(nmtstp_wait_for_link(platform, ifname, expected_link_type, timeout_msec)) - -#define nmtstp_assert_wait_for_link_until(platform, ifname, expected_link_type, until_ms) \ - nmtst_assert_nonnull(nmtstp_wait_for_link_until(platform, ifname, expected_link_type, until_ms)) - -/*****************************************************************************/ - -int nmtstp_run_command_check_external_global(void); -gboolean nmtstp_run_command_check_external(int external_command); - -/*****************************************************************************/ - -const NMPlatformIP4Route *_nmtstp_assert_ip4_route_exists(const char *file, - guint line, - const char *func, - NMPlatform *platform, - int c_exists, - const char *ifname, - guint32 network, - int plen, - guint32 metric, - guint8 tos); -#define nmtstp_assert_ip4_route_exists(platform, c_exists, ifname, network, plen, metric, tos) \ - _nmtstp_assert_ip4_route_exists(__FILE__, \ - __LINE__, \ - G_STRFUNC, \ - platform, \ - c_exists, \ - ifname, \ - network, \ - plen, \ - metric, \ - tos) - -const NMPlatformIP4Route *nmtstp_ip4_route_get(NMPlatform *platform, - int ifindex, - guint32 network, - int plen, - guint32 metric, - guint8 tos); - -const NMPlatformIP6Route *_nmtstp_assert_ip6_route_exists(const char * file, - guint line, - const char * func, - NMPlatform * platform, - int c_exists, - const char * ifname, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen); -#define nmtstp_assert_ip6_route_exists(platform, \ - c_exists, \ - ifname, \ - network, \ - plen, \ - metric, \ - src, \ - src_plen) \ - _nmtstp_assert_ip6_route_exists(__FILE__, \ - __LINE__, \ - G_STRFUNC, \ - platform, \ - c_exists, \ - ifname, \ - network, \ - plen, \ - metric, \ - src, \ - src_plen) - -const NMPlatformIP6Route *nmtstp_ip6_route_get(NMPlatform * platform, - int ifindex, - const struct in6_addr *network, - guint plen, - guint32 metric, - const struct in6_addr *src, - guint8 src_plen); - -/*****************************************************************************/ - -gboolean nmtstp_ip_address_check_lifetime(const NMPlatformIPAddress *addr, - gint64 now, - guint32 expected_lifetime, - guint32 expected_preferred); -void nmtstp_ip_address_assert_lifetime(const NMPlatformIPAddress *addr, - gint64 now, - guint32 expected_lifetime, - guint32 expected_preferred); - -void nmtstp_ip4_address_add(NMPlatform *platform, - gboolean external_command, - int ifindex, - in_addr_t address, - int plen, - in_addr_t peer_address, - guint32 lifetime, - guint32 preferred, - guint32 flags, - const char *label); -void nmtstp_ip6_address_add(NMPlatform * platform, - gboolean external_command, - int ifindex, - struct in6_addr address, - int plen, - struct in6_addr peer_address, - guint32 lifetime, - guint32 preferred, - guint32 flags); -void nmtstp_ip4_address_del(NMPlatform *platform, - gboolean external_command, - int ifindex, - in_addr_t address, - int plen, - in_addr_t peer_address); -void nmtstp_ip6_address_del(NMPlatform * platform, - gboolean external_command, - int ifindex, - struct in6_addr address, - int plen); - -void nmtstp_ip4_route_add(NMPlatform * platform, - int ifindex, - NMIPConfigSource source, - in_addr_t network, - guint8 plen, - in_addr_t gateway, - in_addr_t pref_src, - guint32 metric, - guint32 mss); - -void nmtstp_ip6_route_add(NMPlatform * platform, - int ifindex, - NMIPConfigSource source, - struct in6_addr network, - guint8 plen, - struct in6_addr gateway, - struct in6_addr pref_src, - guint32 metric, - guint32 mss); - -static inline GPtrArray * -nmtstp_ip4_route_get_all(NMPlatform *platform, int ifindex) -{ - return nm_platform_lookup_object_clone( - platform, - NMP_OBJECT_TYPE_IP4_ROUTE, - ifindex, - nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, - NULL); -} - -static inline GPtrArray * -nmtstp_ip6_route_get_all(NMPlatform *platform, int ifindex) -{ - return nm_platform_lookup_object_clone( - platform, - NMP_OBJECT_TYPE_IP6_ROUTE, - ifindex, - nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, - NULL); -} - -/*****************************************************************************/ - -GArray *nmtstp_platform_ip4_address_get_all(NMPlatform *self, int ifindex); -GArray *nmtstp_platform_ip6_address_get_all(NMPlatform *self, int ifindex); - -/*****************************************************************************/ - -const NMPlatformIPAddress * -nmtstp_platform_ip_address_find(NMPlatform *self, int ifindex, int addr_family, gconstpointer addr); - -static inline const NMPlatformIP4Address * -nmtstp_platform_ip4_address_find(NMPlatform *self, int ifindex, in_addr_t addr) -{ - return (const NMPlatformIP4Address *) - nmtstp_platform_ip_address_find(self, ifindex, AF_INET, &addr); -} - -static inline const NMPlatformIP6Address * -nmtstp_platform_ip6_address_find(NMPlatform *self, int ifindex, const struct in6_addr *addr) -{ - return (const NMPlatformIP6Address *) - nmtstp_platform_ip_address_find(self, ifindex, AF_INET6, addr); -} - -void _nmtstp_platform_ip_addresses_assert(const char * filename, - int lineno, - NMPlatform * self, - int ifindex, - gboolean force_exact_4, - gboolean force_exact_6, - gboolean ignore_ll6, - guint addrs_len, - const char *const *addrs); - -#define nmtstp_platform_ip_addresses_assert(self, \ - ifindex, \ - force_exact_4, \ - force_exact_6, \ - ignore_ll6, \ - ...) \ - _nmtstp_platform_ip_addresses_assert(__FILE__, \ - __LINE__, \ - (self), \ - (ifindex), \ - (force_exact_4), \ - (force_exact_6), \ - (ignore_ll6), \ - NM_NARG(__VA_ARGS__), \ - ((const char *const[]){"dummy", ##__VA_ARGS__, NULL}) \ - + 1) - -/*****************************************************************************/ - -static inline gboolean -_nmtstp_platform_routing_rules_get_all_predicate(const NMPObject *obj, gpointer user_data) -{ - int addr_family = GPOINTER_TO_INT(user_data); - - g_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_ROUTING_RULE); - - return addr_family == AF_UNSPEC - || NMP_OBJECT_CAST_ROUTING_RULE(obj)->addr_family == addr_family; -} - -static inline GPtrArray * -nmtstp_platform_routing_rules_get_all(NMPlatform *platform, int addr_family) -{ - NMPLookup lookup; - - g_assert(NM_IS_PLATFORM(platform)); - g_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - return nm_platform_lookup_clone(platform, - &lookup, - _nmtstp_platform_routing_rules_get_all_predicate, - GINT_TO_POINTER(addr_family)); -} - -static inline guint -nmtstp_platform_routing_rules_get_count(NMPlatform *platform, int addr_family) -{ - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject * obj; - NMPLookup lookup; - guint n; - - g_assert(NM_IS_PLATFORM(platform)); - g_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - head_entry = nm_platform_lookup(platform, &lookup); - - n = 0; - nmp_cache_iter_for_each (&iter, head_entry, &obj) { - if (_nmtstp_platform_routing_rules_get_all_predicate(obj, GINT_TO_POINTER(addr_family))) - n++; - } - return n; -} - -gboolean nmtstp_platform_ip4_route_delete(NMPlatform *platform, - int ifindex, - in_addr_t network, - guint8 plen, - guint32 metric); -gboolean nmtstp_platform_ip6_route_delete(NMPlatform * platform, - int ifindex, - struct in6_addr network, - guint8 plen, - guint32 metric); - -const NMPlatformLink * -nmtstp_link_get_typed(NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type); -const NMPlatformLink *nmtstp_link_get(NMPlatform *platform, int ifindex, const char *name); - -gboolean nmtstp_kernel_support_get(NMPlatformKernelSupportType type); - -void -nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up); - -const NMPlatformLink *nmtstp_link_bridge_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkBridge *lnk); -const NMPlatformLink *nmtstp_link_veth_add(NMPlatform *platform, - gboolean external_command, - const char *name, - const char *peer); -const NMPlatformLink * -nmtstp_link_dummy_add(NMPlatform *platform, gboolean external_command, const char *name); -const NMPlatformLink *nmtstp_link_gre_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkGre *lnk); -const NMPlatformLink *nmtstp_link_ip6tnl_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkIp6Tnl *lnk); -const NMPlatformLink *nmtstp_link_ip6gre_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkIp6Tnl *lnk); -const NMPlatformLink *nmtstp_link_ipip_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkIpIp *lnk); -const NMPlatformLink *nmtstp_link_macvlan_add(NMPlatform * platform, - gboolean external_command, - const char * name, - int parent, - const NMPlatformLnkMacvlan *lnk); -const NMPlatformLink *nmtstp_link_sit_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkSit *lnk); -const NMPlatformLink *nmtstp_link_tun_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkTun *lnk, - int * out_fd); -const NMPlatformLink *nmtstp_link_vrf_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkVrf *lnk, - gboolean * out_not_supported); -const NMPlatformLink *nmtstp_link_vxlan_add(NMPlatform * platform, - gboolean external_command, - const char * name, - const NMPlatformLnkVxlan *lnk); - -void nmtstp_link_delete(NMPlatform *platform, - gboolean external_command, - int ifindex, - const char *name, - gboolean require_exist); - -/*****************************************************************************/ - -extern int NMTSTP_ENV1_IFINDEX; -extern int NMTSTP_ENV1_EX; - -static inline void -_nmtstp_env1_wrapper_setup(const NmtstTestData *test_data) -{ - int * p_ifindex; - gpointer p_ifup; - - nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, &p_ifup); - - g_assert(p_ifindex && *p_ifindex == -1); - - _LOGT("TEST[%s]: setup", test_data->testpath); - - nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE); - - g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL))); - - *p_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint(*p_ifindex, >, 0); - g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, -1); - - if (GPOINTER_TO_INT(p_ifup)) - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, *p_ifindex, NULL)); - - nm_platform_process_events(NM_PLATFORM_GET); - - NMTSTP_ENV1_IFINDEX = *p_ifindex; - NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global(); -} - -static inline void -_nmtstp_env1_wrapper_run(gconstpointer user_data) -{ - const NmtstTestData *test_data = user_data; - GTestDataFunc test_func_data; - GTestFunc test_func; - gconstpointer d; - - nmtst_test_data_unpack(test_data, NULL, &test_func, &test_func_data, &d, NULL); - - _LOGT("TEST[%s]: run", test_data->testpath); - if (test_func) - test_func(); - else - test_func_data(d); -} - -static inline void -_nmtstp_env1_wrapper_teardown(const NmtstTestData *test_data) -{ - int *p_ifindex; - - nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, NULL); - - g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, *p_ifindex); - NMTSTP_ENV1_IFINDEX = -1; - - _LOGT("TEST[%s]: teardown", test_data->testpath); - - g_assert_cmpint(*p_ifindex, ==, nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME)); - g_assert(nm_platform_link_delete(NM_PLATFORM_GET, *p_ifindex)); - - nm_platform_process_events(NM_PLATFORM_GET); - - _LOGT("TEST[%s]: finished", test_data->testpath); - - *p_ifindex = -1; -} - -/* add test function, that set's up a particular environment, consisting - * of a dummy device with ifindex NMTSTP_ENV1_IFINDEX. */ -#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, ifup) \ - nmtst_add_test_func_full(testpath, \ - _nmtstp_env1_wrapper_run, \ - _nmtstp_env1_wrapper_setup, \ - _nmtstp_env1_wrapper_teardown, \ - ({ \ - static int _ifindex = -1; \ - &_ifindex; \ - }), \ - ({ \ - GTestFunc _test_func = (test_func); \ - _test_func; \ - }), \ - ({ \ - GTestDataFunc _test_func = (test_data_func); \ - _test_func; \ - }), \ - (arg), \ - ({ \ - gboolean _ifup = (ifup); \ - GINT_TO_POINTER(_ifup); \ - })) - -#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, ifup) \ - _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, ifup) - -#define nmtstp_env1_add_test_func(testpath, test_func, ifup) \ - _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, ifup) - -/*****************************************************************************/ - -typedef void (*NMTstpSetupFunc)(void); -extern NMTstpSetupFunc const _nmtstp_setup_platform_func; - -void nmtstp_setup_platform(void); - -/*****************************************************************************/ - -void _nmtstp_init_tests(int *argc, char ***argv); -void _nmtstp_setup_tests(void); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c deleted file mode 100644 index 5fd67ec4..00000000 --- a/src/platform/tests/test-link.c +++ /dev/null @@ -1,3819 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <sched.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <linux/if_tun.h> - -#include "nm-glib-aux/nm-io-utils.h" -#include "platform/nmp-object.h" -#include "platform/nmp-netns.h" -#include "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))) - -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(!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; - - r = 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 r >= 0; - } - 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_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_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, VLAN_ID, 0, 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_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, 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_set_down(NM_PLATFORM_GET, ifindex)); - 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_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, 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_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 */ - 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; - - 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); - - if (nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { - 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(NMTST_NM_ERR_SUCCESS( - nm_platform_link_set_user_ipv6ll_enabled(NM_PLATFORM_GET, link.ifindex, TRUE))); - 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(NMTST_NM_ERR_SUCCESS( - nm_platform_link_set_user_ipv6ll_enabled(NM_PLATFORM_GET, link.ifindex, FALSE))); - 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->l_address.len, ==, sizeof(addr)); - g_assert(!memcmp(plink->l_address.data, addr, sizeof(addr))); - - 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_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(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; - 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); -} - -/*****************************************************************************/ - -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_UTILS_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, - (const NMPWireGuardPeer *) peers->data, - 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 = {}; - nm_auto_close int tun_fd = -1; - - 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 = 0x8100; - 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 = (NMEtherAddr){{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: - { - 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_GRETAP: - { - gboolean gracefully_skip = FALSE; - - 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 (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "gretap0")) { - /* 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 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 = {}; - 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; - } - - 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 (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_IP6GRE: - { - NMPlatformLnkIp6Tnl lnk_ip6tnl = {}; - gboolean gracefully_skip = FALSE; - - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) { - /* Seems that the ip6_tunnel module is not loaded... try to load it. */ - gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0; - } - - lnk_ip6tnl.local = *nmtst_inet6_from_string("fd01::42"); - 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; - - if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { - if (gracefully_skip) { - 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 = {}; - gboolean gracefully_skip = FALSE; - - if (!nm_platform_link_get_by_ifname(NM_PLATFORM_GET, "ip6gre0")) { - /* Seems that the ip6_tunnel module is not loaded... try to load it. */ - gracefully_skip = nm_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 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 = 22; - lnk_ip6tnl.flow_label = 1339; - lnk_ip6tnl.is_gre = TRUE; - lnk_ip6tnl.is_tap = TRUE; - - if (!nmtstp_link_ip6gre_add(NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { - if (gracefully_skip) { - 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++) { - 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 = {}; - 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_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_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; - - g_assert(plnk == nm_platform_link_get_lnk_bridge(NM_PLATFORM_GET, ifindex, NULL)); - g_assert_cmpint(nm_platform_lnk_bridge_cmp(&lnk_bridge, plnk), ==, 0); - g_assert_cmpint(plnk->forward_delay, ==, 1560); - g_assert_cmpint(plnk->hello_time, ==, 150); - g_assert_cmpint(plnk->max_age, ==, 2100); - g_assert_cmpint(plnk->ageing_time, ==, 2200); - g_assert_cmpint(plnk->stp_state, ==, TRUE); - g_assert_cmpint(plnk->priority, ==, 22); - g_assert_cmpint(plnk->vlan_protocol, ==, 0x8100); - g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge.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, ==, 5000); - g_assert_cmpint(plnk->mcast_membership_interval, ==, 25000); - g_assert_cmpint(plnk->mcast_querier_interval, ==, 26000); - g_assert_cmpint(plnk->mcast_query_interval, ==, 12000); - g_assert_cmpint(plnk->mcast_query_response_interval, ==, 5200); - g_assert_cmpint(plnk->mcast_startup_query_interval, ==, 3000); - g_assert_cmpint(nm_platform_lnk_bridge_cmp(&lnk_bridge, plnk), ==, 0); - 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; - - 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::42"); - 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 NM_LINK_TYPE_IP6GRETAP: - { - const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; - - 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, ==, 22); - g_assert_cmpint(plnk->flow_label, ==, 1339); - g_assert_cmpint(plnk->is_gre, ==, TRUE); - g_assert_cmpint(plnk->is_tap, ==, TRUE); - 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)); - g_assert_cmpint(plnk->id, ==, 1242); - 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_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_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, 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; - - /* 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); - 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_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; - 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_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; - 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_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(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; -} - -/*****************************************************************************/ - -#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_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(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(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(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(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 ?: "<unknown>", "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(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 ?: "<unknown>", "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; - NMTernary * requested; - gboolean do_set = TRUE; - - requested = g_new(NMTernary, _NM_ETHTOOL_ID_FEATURE_NUM); - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) - requested[i] = NM_TERNARY_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_TERNARY_FALSE; - requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_TSO)] = NM_TERNARY_FALSE; - requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION)] = - NM_TERNARY_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_TERNARY_FALSE; - requested[_NM_ETHTOOL_ID_FEATURE_AS_IDX(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION)] = - NM_TERNARY_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", NM_LINK_TYPE_IP6GRE, 0); - test_software_detect_add("/link/software/detect/ip6gretap", NM_LINK_TYPE_IP6GRETAP, 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/tun", NM_LINK_TYPE_TUN, 0); - test_software_detect_add("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 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_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); - } -} diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c deleted file mode 100644 index e61e6b09..00000000 --- a/src/platform/tests/test-nmp-object.c +++ /dev/null @@ -1,617 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <libudev.h> -#include <linux/pkt_sched.h> - -#include "platform/nmp-object.h" -#include "nm-udev-aux/nm-udev-utils.h" - -#include "nm-test-utils-core.h" - -struct { - GList *udev_devices; -} global; - -/*****************************************************************************/ - -static void -test_obj_base(void) -{ - static const union { - GObject g; - NMPObject k; - } x = {}; - static const union { - GTypeClass k; - NMPClass c; - } l = {}; - static const GObject * g = &x.g; - static const GTypeClass *k = &l.k; - static const NMPObject * o = &x.k; - static const NMPClass * c = &l.c; - - NMObjBaseInst * obj; - gs_unref_object GCancellable *obj_cancellable = g_cancellable_new(); - nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link(10); - - g_assert(&g->g_type_instance == (void *) &o->_class); - g_assert(&g->g_type_instance.g_class == (void *) &o->_class); - - g_assert(sizeof(o->parent.parent) == sizeof(GTypeInstance)); - - g_assert(&c->parent == (void *) c); - g_assert(&c->parent.parent.g_type_class == (void *) c); - g_assert(&c->parent.parent.g_type == (void *) c); - g_assert(&c->parent.parent.g_type == &k->g_type); - - g_assert(sizeof(c->parent.parent) == sizeof(GTypeClass)); - - g_assert(&o->parent == (void *) o); - g_assert(&o->parent.klass == (void *) &o->_class); - - obj = (NMObjBaseInst *) obj_cancellable; - g_assert(!NMP_CLASS_IS_VALID((NMPClass *) obj->klass)); - g_assert(G_TYPE_CHECK_INSTANCE_TYPE(obj, G_TYPE_CANCELLABLE)); - - obj = (NMObjBaseInst *) obj_link; - g_assert(NMP_CLASS_IS_VALID((NMPClass *) obj->klass)); - g_assert(!G_TYPE_CHECK_INSTANCE_TYPE(obj, G_TYPE_CANCELLABLE)); -} - -/*****************************************************************************/ - -static gboolean -_nmp_object_id_equal(const NMPObject *a, const NMPObject *b) -{ - gboolean a_b = nmp_object_id_equal(a, b); - - g_assert(NM_IN_SET(a_b, FALSE, TRUE) && a_b == nmp_object_id_equal(b, a)); - return a_b; -} -#define nmp_object_id_equal _nmp_object_id_equal - -static gboolean -_nmp_object_equal(const NMPObject *a, const NMPObject *b) -{ - gboolean a_b = nmp_object_equal(a, b); - - g_assert(NM_IN_SET(a_b, FALSE, TRUE) && a_b == nmp_object_equal(b, a)); - return a_b; -} -#define nmp_object_equal _nmp_object_equal - -/*****************************************************************************/ - -static void -_assert_cache_multi_lookup_contains(const NMPCache * cache, - const NMDedupMultiHeadEntry *head_entry, - const NMPObject * obj, - gboolean visible_only, - gboolean contains) -{ - NMDedupMultiIter iter; - gboolean found; - guint i, len; - const NMPObject *o; - - g_assert(NMP_OBJECT_IS_VALID(obj)); - - g_assert(nmp_cache_lookup_obj(cache, obj) == obj); - g_assert(!head_entry - || (head_entry->len > 0 - && c_list_length(&head_entry->lst_entries_head) == head_entry->len)); - - len = head_entry ? head_entry->len : 0; - - found = FALSE; - i = 0; - nmp_cache_iter_for_each (&iter, head_entry, &o) { - g_assert(NMP_OBJECT_IS_VALID(o)); - if (obj == o) { - if (!visible_only || nmp_object_is_visible(o)) { - g_assert(!found); - found = TRUE; - } - } - i++; - } - - g_assert(len == i); - g_assert(!!contains == found); -} - -static void -_assert_cache_multi_lookup_contains_link(const NMPCache * cache, - gboolean visible_only, - const NMPObject *obj, - gboolean contains) -{ - const NMDedupMultiHeadEntry *head_entry; - NMPLookup lookup; - - g_assert(cache); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_LINK); - head_entry = nmp_cache_lookup(cache, &lookup); - _assert_cache_multi_lookup_contains(cache, head_entry, obj, visible_only, contains); -} - -/*****************************************************************************/ - -static void -ops_post_check(NMPCache * cache, - NMPCacheOpsType ops_type, - const NMPObject *obj_old, - const NMPObject *obj_new, - const NMPObject *obj_new_expected, - NMPCacheOpsType expected_ops_type) -{ - g_assert(cache); - - g_assert_cmpint(expected_ops_type, ==, ops_type); - - switch (ops_type) { - case NMP_CACHE_OPS_ADDED: - g_assert(!obj_old); - g_assert(NMP_OBJECT_IS_VALID(obj_new)); - g_assert(nmp_object_is_alive(obj_new)); - g_assert(nmp_object_id_equal(obj_new_expected, obj_new)); - g_assert(nmp_object_equal(obj_new_expected, obj_new)); - break; - case NMP_CACHE_OPS_UPDATED: - g_assert(obj_old != obj_new); - g_assert(NMP_OBJECT_IS_VALID(obj_old)); - g_assert(NMP_OBJECT_IS_VALID(obj_new)); - g_assert(nmp_object_is_alive(obj_old)); - g_assert(nmp_object_is_alive(obj_new)); - g_assert(nmp_object_id_equal(obj_new_expected, obj_new)); - g_assert(nmp_object_id_equal(obj_new_expected, obj_old)); - g_assert(nmp_object_id_equal(obj_old, obj_new)); - g_assert(nmp_object_equal(obj_new_expected, obj_new)); - g_assert(!nmp_object_equal(obj_new_expected, obj_old)); - g_assert(!nmp_object_equal(obj_old, obj_new)); - break; - case NMP_CACHE_OPS_REMOVED: - g_assert(!obj_new); - g_assert(NMP_OBJECT_IS_VALID(obj_old)); - g_assert(nmp_object_is_alive(obj_old)); - if (obj_new_expected) - g_assert(nmp_object_id_equal(obj_new_expected, obj_old)); - break; - case NMP_CACHE_OPS_UNCHANGED: - g_assert(obj_old == obj_new); - if (obj_old) { - g_assert(NMP_OBJECT_IS_VALID(obj_old)); - g_assert(nmp_object_is_alive(obj_old)); - g_assert(nmp_object_equal(obj_old, obj_new)); - g_assert(nmp_object_id_equal(obj_new_expected, obj_new)); - } else - g_assert(!obj_new_expected); - break; - default: - g_assert_not_reached(); - } -} - -static void -_nmp_cache_update_netlink(NMPCache * cache, - NMPObject * obj, - const NMPObject **out_obj_old, - const NMPObject **out_obj_new, - NMPCacheOpsType expected_ops_type) -{ - NMPCacheOpsType ops_type; - const NMPObject *obj_prev; - const NMPObject *obj_old; - const NMPObject *obj_new; - nm_auto_nmpobj NMPObject *obj_new_expected = NULL; - - g_assert(cache); - g_assert(NMP_OBJECT_IS_VALID(obj)); - - obj_prev = nmp_cache_lookup_link(cache, NMP_OBJECT_CAST_LINK(obj)->ifindex); - obj_new_expected = nmp_object_clone(obj, FALSE); - if (obj_prev && obj_prev->_link.udev.device) - obj_new_expected->_link.udev.device = udev_device_ref(obj_prev->_link.udev.device); - _nmp_object_fixup_link_udev_fields(&obj_new_expected, NULL, nmp_cache_use_udev_get(cache)); - - ops_type = nmp_cache_update_netlink(cache, obj, FALSE, &obj_old, &obj_new); - ops_post_check(cache, - ops_type, - obj_old, - obj_new, - nmp_object_is_alive(obj_new_expected) ? obj_new_expected : NULL, - expected_ops_type); - - if (out_obj_new) - *out_obj_new = obj_new; - else - nmp_object_unref(obj_new); - if (out_obj_old) - *out_obj_old = obj_old; - else - nmp_object_unref(obj_old); -} - -static const NMPlatformLink pl_link_2 = { - .ifindex = 2, - .name = "eth0", - .type = NM_LINK_TYPE_ETHERNET, -}; - -static const NMPlatformLink pl_link_3 = { - .ifindex = 3, - .name = "wlan0", - .type = NM_LINK_TYPE_WIFI, -}; - -static void -test_cache_link(void) -{ - NMPCache * cache; - NMPObject * objm1; - const NMPObject * obj_old, *obj_new; - NMPObject objs1; - struct udev_device * udev_device_2 = g_list_nth_data(global.udev_devices, 0); - struct udev_device * udev_device_3 = g_list_nth_data(global.udev_devices, 0); - NMPCacheOpsType ops_type; - nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL; - gboolean use_udev = nmtst_get_rand_uint32() % 2; - - multi_idx = nm_dedup_multi_index_new(); - - cache = nmp_cache_new(multi_idx, use_udev); - - /* if we have a link, and don't set is_in_netlink, adding it has no effect. */ - objm1 = nmp_object_new(NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert(NMP_OBJECT_UP_CAST(&objm1->object) == objm1); - g_assert(!nmp_object_is_alive(objm1)); - _nmp_cache_update_netlink(cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert(!obj_old); - g_assert(!obj_new); - g_assert(!nmp_cache_lookup_obj(cache, objm1)); - g_assert(!nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex))); - nmp_object_unref(objm1); - - /* Only when setting @is_in_netlink the link is added. */ - objm1 = nmp_object_new(NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - objm1->_link.netlink.is_in_netlink = TRUE; - g_assert(nmp_object_is_alive(objm1)); - _nmp_cache_update_netlink(cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_ADDED); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert(!obj_old); - g_assert(obj_new); - g_assert(objm1 == obj_new); - g_assert(nmp_object_equal(objm1, obj_new)); - g_assert(nmp_cache_lookup_obj(cache, objm1) == obj_new); - g_assert(nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex)) - == obj_new); - g_assert(nmp_object_is_visible(obj_new)); - _assert_cache_multi_lookup_contains_link(cache, FALSE, obj_new, TRUE); - _assert_cache_multi_lookup_contains_link(cache, TRUE, obj_new, TRUE); - nmp_object_unref(objm1); - nmp_object_unref(obj_new); - - /* updating the same link with identical value, has no effect. */ - objm1 = nmp_object_new(NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - objm1->_link.netlink.is_in_netlink = TRUE; - g_assert(nmp_object_is_alive(objm1)); - _nmp_cache_update_netlink(cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UNCHANGED); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert(obj_old); - g_assert(obj_new); - g_assert(obj_new != objm1); - g_assert(nmp_object_equal(objm1, obj_new)); - g_assert(nmp_cache_lookup_obj(cache, objm1) == obj_new); - g_assert(nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex)) - == obj_new); - nmp_object_unref(objm1); - nmp_object_unref(obj_new); - nmp_object_unref(obj_new); - - /* remove the link from netlink */ - objm1 = nmp_object_new(NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - g_assert(!nmp_object_is_alive(objm1)); - _nmp_cache_update_netlink(cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_REMOVED); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert(obj_old); - g_assert(!obj_new); - g_assert(!nmp_cache_lookup_obj(cache, objm1)); - g_assert(!nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex))); - nmp_object_unref(objm1); - nmp_object_unref(obj_old); - nmp_object_unref(obj_new); - - if (udev_device_2) { - /* now add the link only with aspect UDEV. */ - ops_type = - nmp_cache_update_link_udev(cache, pl_link_2.ifindex, udev_device_2, &obj_old, &obj_new); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert_cmpint(ops_type, ==, NMP_CACHE_OPS_ADDED); - g_assert(!obj_old); - g_assert(obj_new); - g_assert( - nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex)) - == obj_new); - g_assert(!nmp_object_is_visible(obj_new)); - _assert_cache_multi_lookup_contains_link(cache, TRUE, obj_new, FALSE); - _assert_cache_multi_lookup_contains_link(cache, FALSE, obj_new, TRUE); - nmp_object_unref(obj_new); - } - - /* add it in netlink too. */ - objm1 = nmp_object_new(NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - objm1->_link.netlink.is_in_netlink = TRUE; - g_assert(nmp_object_is_alive(objm1)); - _nmp_cache_update_netlink(cache, - objm1, - &obj_old, - &obj_new, - udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_ADDED); - nmtst_assert_nmp_cache_is_consistent(cache); - if (udev_device_2) { - g_assert(obj_old); - g_assert(!nmp_object_is_visible(obj_old)); - } else - g_assert(!obj_old); - g_assert(nmp_object_equal(objm1, obj_new)); - g_assert(nmp_cache_lookup_obj(cache, objm1) == obj_new); - g_assert(nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex)) - == obj_new); - g_assert(nmp_object_is_visible(obj_new)); - _assert_cache_multi_lookup_contains_link(cache, TRUE, obj_new, TRUE); - _assert_cache_multi_lookup_contains_link(cache, FALSE, obj_new, TRUE); - nmp_object_unref(objm1); - nmp_object_unref(obj_old); - nmp_object_unref(obj_new); - - /* remove again from netlink. */ - objm1 = nmp_object_new(NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_2); - objm1->_link.netlink.is_in_netlink = FALSE; - g_assert(!nmp_object_is_alive(objm1)); - _nmp_cache_update_netlink(cache, - objm1, - &obj_old, - &obj_new, - udev_device_2 ? NMP_CACHE_OPS_UPDATED : NMP_CACHE_OPS_REMOVED); - nmtst_assert_nmp_cache_is_consistent(cache); - if (udev_device_2) - g_assert(obj_new == objm1); - else - g_assert(!obj_new); - g_assert(obj_old); - g_assert(nmp_object_is_alive(obj_old)); - if (udev_device_2) { - g_assert(nmp_cache_lookup_obj(cache, objm1) == obj_new); - g_assert( - nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex)) - == obj_new); - g_assert(!nmp_object_is_visible(obj_new)); - _assert_cache_multi_lookup_contains_link(cache, TRUE, obj_new, FALSE); - _assert_cache_multi_lookup_contains_link(cache, FALSE, obj_new, TRUE); - } else { - g_assert(nmp_cache_lookup_obj(cache, objm1) == NULL); - g_assert( - nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_2.ifindex)) - == NULL); - g_assert(!nmp_object_is_alive(obj_new)); - g_assert(!nmp_object_is_visible(obj_new)); - } - nmp_object_unref(objm1); - nmp_object_unref(obj_old); - nmp_object_unref(obj_new); - - /* now another link only with aspect UDEV. */ - if (udev_device_3) { - /* now add the link only with aspect UDEV. */ - ops_type = - nmp_cache_update_link_udev(cache, pl_link_3.ifindex, udev_device_3, &obj_old, &obj_new); - g_assert_cmpint(ops_type, ==, NMP_CACHE_OPS_ADDED); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert(NMP_OBJECT_IS_VALID(obj_new)); - g_assert(!obj_old); - g_assert(!nmp_object_is_visible(obj_new)); - g_assert( - nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_3.ifindex)) - == obj_new); - _assert_cache_multi_lookup_contains_link(cache, TRUE, obj_new, FALSE); - _assert_cache_multi_lookup_contains_link(cache, FALSE, obj_new, TRUE); - g_assert_cmpint(obj_new->_link.netlink.is_in_netlink, ==, FALSE); - g_assert_cmpint(obj_new->link.initialized, ==, FALSE); - nmp_object_unref(obj_new); - - /* add it in netlink too. */ - objm1 = nmp_object_new(NMP_OBJECT_TYPE_LINK, (NMPlatformObject *) &pl_link_3); - objm1->_link.netlink.is_in_netlink = TRUE; - g_assert(nmp_object_is_alive(objm1)); - _nmp_cache_update_netlink(cache, objm1, &obj_old, &obj_new, NMP_CACHE_OPS_UPDATED); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert(obj_old); - g_assert(obj_new == objm1); - g_assert(nmp_object_equal(objm1, obj_new)); - g_assert(!obj_old || !nmp_object_is_visible(obj_old)); - g_assert(nmp_cache_lookup_obj(cache, objm1) == obj_new); - g_assert( - nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_3.ifindex)) - == obj_new); - g_assert(nmp_object_is_visible(obj_new)); - _assert_cache_multi_lookup_contains_link(cache, TRUE, obj_new, TRUE); - _assert_cache_multi_lookup_contains_link(cache, FALSE, obj_new, TRUE); - g_assert_cmpint(obj_new->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint(obj_new->link.initialized, ==, TRUE); - nmp_object_unref(objm1); - nmp_object_unref(obj_old); - nmp_object_unref(obj_new); - - /* remove UDEV. */ - ops_type = nmp_cache_update_link_udev(cache, pl_link_3.ifindex, NULL, &obj_old, &obj_new); - g_assert_cmpint(ops_type, ==, NMP_CACHE_OPS_UPDATED); - nmtst_assert_nmp_cache_is_consistent(cache); - g_assert(obj_old && nmp_object_is_visible(obj_old)); - g_assert( - nmp_cache_lookup_obj(cache, nmp_object_stackinit_id_link(&objs1, pl_link_3.ifindex)) - == obj_new); - g_assert(nmp_object_is_visible(obj_new)); - _assert_cache_multi_lookup_contains_link(cache, TRUE, obj_new, TRUE); - _assert_cache_multi_lookup_contains_link(cache, FALSE, obj_new, TRUE); - g_assert_cmpint(obj_new->_link.netlink.is_in_netlink, ==, TRUE); - g_assert_cmpint(obj_new->link.initialized, ==, !nmp_cache_use_udev_get(cache)); - nmp_object_unref(obj_new); - nmp_object_unref(obj_old); - } - - nmp_cache_free(cache); -} - -const char noqueue[] = "noqueue"; -const char fq_codel[] = "fq_codel"; -const char ingress[] = "ingress"; - -static const NMPlatformQdisc pl_qdisc_1a = { - .ifindex = 1, - .kind = noqueue, - .addr_family = AF_UNSPEC, - .handle = 0, - .parent = TC_H_ROOT, - .info = 0, -}; - -static const NMPlatformQdisc pl_qdisc_1b = { - .ifindex = 1, - .kind = fq_codel, - .addr_family = AF_UNSPEC, - .handle = 0, - .parent = TC_H_ROOT, - .info = 0, -}; - -static const NMPlatformQdisc pl_qdisc_1c = { - .ifindex = 1, - .kind = ingress, - .addr_family = AF_UNSPEC, - .handle = TC_H_MAKE(TC_H_INGRESS, 0), - .parent = TC_H_INGRESS, - .info = 0, -}; - -static const NMPlatformQdisc pl_qdisc_2 = { - .ifindex = 2, - .kind = fq_codel, - .addr_family = AF_UNSPEC, - .handle = 0, - .parent = TC_H_ROOT, - .info = 0, -}; - -static void -test_cache_qdisc(void) -{ - NMPCache * cache; - nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL; - NMPLookup lookup; - const NMDedupMultiHeadEntry * head_entry; - nm_auto_nmpobj NMPObject *obj1a = - nmp_object_new(NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_1a); - nm_auto_nmpobj NMPObject *obj1b = - nmp_object_new(NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_1b); - nm_auto_nmpobj NMPObject *obj1c = - nmp_object_new(NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_1c); - nm_auto_nmpobj NMPObject *obj2 = - nmp_object_new(NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_2); - - multi_idx = nm_dedup_multi_index_new(); - cache = nmp_cache_new(multi_idx, nmtst_get_rand_uint32() % 2); - - g_assert(nmp_cache_lookup_obj(cache, obj1a) == NULL); - - g_assert(nmp_cache_update_netlink(cache, obj1a, FALSE, NULL, NULL) == NMP_CACHE_OPS_ADDED); - g_assert(nmp_cache_lookup_obj(cache, obj1a) == obj1a); - g_assert(nmp_cache_lookup_obj(cache, obj1b) == obj1a); - g_assert(nmp_cache_lookup_obj(cache, obj2) == NULL); - - g_assert(nmp_cache_update_netlink(cache, obj1b, FALSE, NULL, NULL) == NMP_CACHE_OPS_UPDATED); - g_assert(nmp_cache_lookup_obj(cache, obj1a) == obj1b); - g_assert(nmp_cache_lookup_obj(cache, obj1b) == obj1b); - g_assert(nmp_cache_lookup_obj(cache, obj2) == NULL); - - g_assert(nmp_cache_update_netlink(cache, obj1c, FALSE, NULL, NULL) == NMP_CACHE_OPS_ADDED); - g_assert(nmp_cache_lookup_obj(cache, obj1a) == obj1b); - g_assert(nmp_cache_lookup_obj(cache, obj1b) == obj1b); - g_assert(nmp_cache_lookup_obj(cache, obj1c) == obj1c); - g_assert(nmp_cache_lookup_obj(cache, obj2) == NULL); - - g_assert(nmp_cache_update_netlink(cache, obj2, FALSE, NULL, NULL) == NMP_CACHE_OPS_ADDED); - g_assert(nmp_cache_lookup_obj(cache, obj1a) == obj1b); - g_assert(nmp_cache_lookup_obj(cache, obj1b) == obj1b); - g_assert(nmp_cache_lookup_obj(cache, obj2) == obj2); - - head_entry = nmp_cache_lookup(cache, nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_QDISC, 1)); - g_assert(head_entry->len == 2); - - nmp_cache_free(cache); -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - int result; - NMUdevClient *udev_client; - - nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT"); - - udev_client = nm_udev_client_new((const char *[]){"net", NULL}, NULL, NULL); - { - struct udev_enumerate * enumerator; - struct udev_list_entry *devices, *l; - - enumerator = nm_udev_client_enumerate_new(udev_client); - - /* Demand that the device is initialized (udev rules ran, - * device has a stable name now) in case udev is running - * (not in a container). */ - if (access("/sys", W_OK) == 0) - udev_enumerate_add_match_is_initialized(enumerator); - - udev_enumerate_scan_devices(enumerator); - - devices = udev_enumerate_get_list_entry(enumerator); - for (l = devices; l != NULL; l = udev_list_entry_get_next(l)) { - struct udev_device *udevice; - - udevice = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerator), - udev_list_entry_get_name(l)); - if (udevice == NULL) - continue; - - global.udev_devices = g_list_prepend(global.udev_devices, udevice); - } - global.udev_devices = g_list_reverse(global.udev_devices); - - udev_enumerate_unref(enumerator); - } - - g_test_add_func("/nmp-object/obj-base", test_obj_base); - g_test_add_func("/nmp-object/cache_link", test_cache_link); - g_test_add_func("/nmp-object/cache_qdisc", test_cache_qdisc); - - result = g_test_run(); - - while (global.udev_devices) { - udev_device_unref(global.udev_devices->data); - global.udev_devices = g_list_delete_link(global.udev_devices, global.udev_devices); - } - - nm_udev_client_unref(udev_client); - - return result; -} diff --git a/src/platform/tests/test-platform-general.c b/src/platform/tests/test-platform-general.c deleted file mode 100644 index 703c886e..00000000 --- a/src/platform/tests/test-platform-general.c +++ /dev/null @@ -1,772 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2015 - 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <linux/rtnetlink.h> - -#include "platform/nm-platform-utils.h" -#include "platform/nm-linux-platform.h" - -#include "nm-test-utils-core.h" - -/*****************************************************************************/ - -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPAddress, address_ptr) - == G_STRUCT_OFFSET(NMPlatformIP4Address, address)); -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPAddress, address_ptr) - == G_STRUCT_OFFSET(NMPlatformIP6Address, address)); - -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPRoute, network_ptr) - == G_STRUCT_OFFSET(NMPlatformIP4Route, network)); -G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPlatformIPRoute, network_ptr) - == G_STRUCT_OFFSET(NMPlatformIP6Route, network)); - -/*****************************************************************************/ - -static void -test_init_linux_platform(void) -{ - gs_unref_object NMPlatform *platform = NULL; - - platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT); -} - -/*****************************************************************************/ - -static void -test_link_get_all(void) -{ - gs_unref_object NMPlatform *platform = NULL; - gs_unref_ptrarray GPtrArray *links = NULL; - - platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT); - - links = nm_platform_link_get_all(platform, TRUE); -} - -/*****************************************************************************/ - -static void -test_nm_platform_link_flags2str(void) -{ - int i; - - for (i = 0; i < 100; i++) { - char buf[NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN + 100]; - const char *s; - const guint flags = ((i == 0) ? ~0u : nmtst_get_rand_uint()); - gsize l; - - s = nm_platform_link_flags2str(flags, buf, sizeof(buf)); - g_assert(s); - - l = strlen(s); - if (l > NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN) - g_error( - "nm_platform_link_flags2str(%x) produced a longer output than %zu chars: \"%s\"", - flags, - NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN, - s); - if (flags == ~0u && l != NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN) - g_error("nm_platform_link_flags2str(%x) is expected to produce %zu chars, but produced " - "%zu: \"%s\"", - flags, - NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN, - l, - s); - } -} - -/*****************************************************************************/ - -static int -_address_pretty_sort_cmp(gconstpointer a, gconstpointer b, gpointer test_data) -{ - const int TEST_DATA_I = GPOINTER_TO_INT(test_data); - const int addr_family = (TEST_DATA_I == 0 ? AF_INET : AF_INET6); - const int IPV6_PREFER_TEMP = (TEST_DATA_I == 2); - const NMPlatformIPXAddress *a_a = a; - const NMPlatformIPXAddress *a_b = b; - - if (addr_family == AF_INET) - return nm_platform_ip4_address_pretty_sort_cmp(&a_a->a4, &a_b->a4); - - return nm_platform_ip6_address_pretty_sort_cmp(&a_a->a6, &a_b->a6, IPV6_PREFER_TEMP); -} - -static void -test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) -{ - static const char *const EXPECTED_BUFFER[3] = { - [0] = ("5b1aea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb504e03d53e1124c5" - "0065aeb2e6fbf952902383028e3b47f280f062ea1a7e0b7be218d067530e1b0487b8c3b99f2b8a1a" - "8982c42f0000003437c5156e072b2f2f0037c9cfe07c34ddb3980deb14ab7b5af84a034703000000" - "883b0f3fd6ed84d6c959e553b887edcd01c2f5d200000043b809d259e499db7d00f1853bdcb0e4bc" - "0e2b00b667b7b16d8d1e69c803000000b973972c17a47631c169f11ff9119c400368b6630000007a" - "034f43340d01683c0045097aea4a849f060ddf57b24a5be9636360d603000000ad7c499dd538d345" - "74c038404923e75d0209e2fc0000004acc807cdce682f80f00315c45ef817264c89a736ed55ed637" - "b96c200203000000faf1809becd2506315a6da29b2e94d3a031fe0e900000083a36035fb6297dfa7" - "00686b5efd0d53bb6215de4bb6f6f3d031a79028030000008bb836c0a25ea71f5daaed4d99eb2ebe" - "033c432f000000bf4ccf30d3aaaf02a4005d7308b67f91bf9d82c856ba942455e8d07c8403000000" - "f2abb982b001ec16901f55f960c55c22022099a80000002b4d4647f53b1921af0088e3759a08e7a5" - "6663861eea1bf42c12ea3b9503000000fb95e8332fdfff658483a2d039a7bf1402d3481e00000060" - "e89f7abdb682380a00eae374835b4a49a2b980b6aba92da6409969aa03000000e00473755d31e5b2" - "de252167c1c91b3a02ba0c700000007740318db913a353ed006efc068829c0e66ad0143a0554efb4" - "40e55b8b03000000c0cfb2b4386bec092fa5757ecde9348b00c12ebd000000ab667224dae775e5cc" - "0041aca2ff0f576767d3648102b61886d149f07403000000153ece68ade15cec25a59273e7519f34" - "c4458d70000000f3819aa46fbe1439340033ae6dec0fb124f264af67eed7c9a8ecc8fb1103000000" - "fcebbaeb0c56535923f14874042a8aff021f8e5ec3cc13cc36bbe3c9bb0ec36f00e007bb64a2827b" - "7cdd38d0314c178e5a06c40b03000000ab90135fa636af4464d210a256be75e0029c44770000004b" - "2e69220d6c0fc09c25d6534c809829af4a9df58dbfef186d416f3a1e030000002c932e655203d82a" - "3c84c4eb274ed18603780281000000f2235376239daeaacb3cae864b437baaae91921681c2162b9b" - "69e66142030000004fda8a3e0b841cf76391dd68269b53ec0244a831000000b78c54dda9ac3bb1b1" - "d43e6505621b9a7f0422ae3fc8979ee0416f95e70300000057d6249b652ba98c7dc7f17f666969e4" - "023baf7a000000ee0b06fa9e988f80f0de6dc8dfcf2a3ad3bbcc0fa3b314f695111d891d03000000" - "cd897619f51f44e644d7cf1d06b2b1150082549e62c12fba5b1cdec48d10bbb13b8313d8cd2a24d3" - "4fc812bd2f8a59d90fcc00ac030000005292cd32dc096cd5d8a4c5cf3351ee1c03c2056c00000051" - "bbce426cfa4b861cc78592be7b14e7ba9c15acb881ae55f0e5fe7d360300000066a3ae3939762df3" - "3a2d55060c78d55100b110fd00000041b9aca07b6e4925dd27943a272c171ed15abbbe1cd911db7b" - "86ed271803000000a5edc511c1507a141e0f515638c7ba31027609450000003357ae79989870ccec" - "3def0ad92749e016663fe6ee0228c1da82d1595603000000348352d715cf9d411ea012e5307294b6" - "0146dac4000000075efee38dd16f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e26bb20413203000000" - "862573c2303dd1d65c7b2cffeca6d1adaccae11f0000000f855ebf3b772eb2b1c896c9a7304f6645" - "0a5f6abd850b06e3b10123e6030000001dff045298cfee0636674cdefb57b9ae54cfe8f400000038" - "1ba2c4396de60f032bc7f34de2959871c0d4c0d4eb720c4ab550c5db03000000f32f4af595d785d5" - "c1b5aad487c192f08bd7a09b00000043288cd9bf66ec305a225a0c71b2ce78bb16104c8eaf18c565" - "b16c7376030000005219061ab4c5c79489b2cc6a883c146972decb8b000000f5e6d66df46ea13910" - "7754dee62c36d2fc70ccc567df7a49b8585287dc03000000cfb18b2b2cb749e2e03e544d0eb4f73e" - "75039fcf251b32fc79685b05ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0beb42b3403000000" - "895d5f24037d233302ad3b82d639272e0246eadfbd2146bf8cfdb205f90e54b58a6ee136a779f37c" - "30d2c5053c40ecaec38b6b8e03000000bea73223e59bf0193432e9fa7a899f2d8ec7e4b89bf5a5d0" - "6776e66a9d80ab132e1ac921eb76adbb229df32e561fa80a0fc4676703000000a23eb66e720da9e0" - "7ac998b5690807d50217369ee1af4ca5a6a95581af5fd7ceacdca10f47d7b351a36b178aabc78a4a" - "1a0dd8c003000000e2815a1a37a52bddd1c2f1018b587eed03bb58f0e9201f17bd99fcf72909ac9e" - "7a55299e9bb4fd53bc7417940fcffe3f81cafd6302000000d6732578acd14320aefd4503189f7630" - "038e501c0000002b9f3c39f24b0572b100745cb25851429b3bbfb50168dfd04eb62f22ca02000000" - "891715df7fc6a902edae579e2e10c7f7022ba0340436242cbeb0248cee3fbc160032d4f28aa28c08" - "f80dd50c6712dfb4abba4a32020000008ffe423d01883918039249f398f9b37ea091465100000064" - "3722d9b707c0d8a400b7c8307f06b4b29088f20d9ac676d5e4bafc4e02000000fde69eec3af2e6d0" - "bd68ab722af14548b29572e504265f6c72923e22594f3f790008ed2e2ebb0771db46a54cadb245ea" - "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd6011fa56dfdcfc1155f236c8b9c79a620" - "00660bbf024b03ff0a8e27c405e64244e36f90d402000000fd41fe47684b370b6ec6584d64496089" - "570968ead4d1ae91c819bb068196d59900de3246e43f5e7945aaf95e2ffa3a119c64ed0402000000" - "a660ac824b7fae389861419c50da49bf02092583000000259f9f0251becc987907879cca68fec7bb" - "bb5f8edf248b4995d184e82002000000a19cdf6dd1c173f6078a806d329c9b0000bfc972000000f5" - "b2cd3dbddc74e26de958e48d2ab8b0313e7f8933e315130f641e447502000000b9b68c08a5e4351e" - "a349e1ccf662e058b879a45100000045fcb6a035339d504c9726d80d9c2d89df765b4d9a130257dc" - "d1e1b34902000000bcd7be07d78e6222e45aaf61814f703b40125e6b000000cbaaa37b861e6d46da" - "fe7d6ec4ac1ea051010911915ddb05f2c43bd794010000005bfe47c6f53a54e01b0c1d89414d94d0" - "032d2ec50000000103781b0f294a2b7300421398f4de67e9cee64b38b56e03e01539ce4101000000" - "18238487a417f3da01d99dae5f19009601582cab0000005b2363d13edc5aa115005eb914d8fbe9a4" - "fdb3d117d76b0de5bd82e9ea010000003d1b91caae8cb60b49ba9be338d856a40267c3d400000064" - "9da90bd2fff2f2560046870bd7d5f14870c6d18d6242b356b9ef1b2101000000f90adac616a31dc2" - "e46a234558817151008fc9c900000073e64b0bd761fdf274005dca8ce1bd1871ae17bb4515856092" - "b4d9e89b010000006855676c277cf1bd017c9148da5892bb0351c3a70000009f3391ce7d3fd48469" - "005f0c233dbdd2a97835df1f1782dee86c1de913010000003ee1d76fc1ea76e98c9dfc78997ab53e" - "00c21963f5b4cb2454830c68e44ea74b002b83f9b3bee14d861a4c9eeecc89f65408c1b701000000" - "d9e1825fa638e1af284a024b7f9e85ff00e050a2000000276e08cb887464b93400c3127c750fabdd" - "69121aec129cdc690d58fbcc01000000d0d44ff1e51c35157666c05348e6f50700a8e89800000012" - "b900b2061d0c334b009f2dd1cdf64b0f9a60e0e289f08db3fde6b0250100000039dd8f88152a5845" - "4d9ca9d20f45dfa702e78604000000fbcd7db68b9ae586da00b4070c50320427c4dd3d031e33f22c" - "210aeb09010000007000b96d06992b6a58acd3995b9663d20248f333ef467092818aa77d6732b678" - "00844ef5a943825fcd743f59bd14c89b955e1a13010000007acbce3e3bcdf3824f1b134847ff26ba" - "009a774900000017ec852d59f3d17232edd86ad6c3103a68843a9aef34983882d3d3878501000000" - "5e0dfe491d1ba96742c7b5e02b2271220188b02a06d0dc5504b0595daf37deb499996bfb667f072d" - "ec1e5d9cdc8a11f4409bcfb30100000099d90a8543961b2ccd47724a3c460ba80383f4c500000063" - "eafb1ac4c0982b283aa9986700b2a2b3ed257b8b0489f48f053ec8ca0100000027335a25a364d101" - "5ffac03089f4553902b01784000000d7b83579b8da27345a72437f9b6245de39ec9e71ee4b951507" - "f121014a01000000efc67bce716c856e3973dc42a1003be94f89d8ee0000009b5d5bbe6c10085f3d" - "6176f78a19bb8df1804c122fce5078c156e8f3fe010000000ea8042602a1f8e6f5657f3f9e3eb807" - "cbad7645000000b8df6f628a70456d79f25d5895fb57fa60d9279fb2b8fcbac65ad47b8a01000000" - "da40d88d40a6d75bc404156225b7eedefc2b44574b15e2ae496ad01bf007eacb0a28aec868282510" - "b60291ea6480e356925b568a0000000087bb24e5264fd3ebe9cf9f6df9615189018ee815000000a7" - "5c9555876b6a3f13002b6cb8360feaac1d5c302df59dd32a7a859db500000000362956cd46646a0e" - "222160e5f769bb290366ed370000005b6a813387e99bb834009da86c64fefab2548759d313a5b92d" - "8e47935e0000000034f0386a253c21d94064f6b021281e230094ae20000000d71fd050bf8d85055b" - "00e3756ccdb3455c60ca7b11c66af76e594f24a100000000e143fd52599364e13468f80fd514573f" - "b572671c0000006932d1d5f5d0ce2cf6007a70ba5193a162bc92ec1b11d9172c857ae81200000000" - "3e29535402e9b690c628d048eacce745ea213cb1000000b632ef3be6070dafa200187470e9da5570" - "9427c226d324d9a08487fd0d00000000b7a350f9fc1519defa7db4532545666937c22a3b000000dc" - "c405cbee5016c25200d8901d7a0165fe20744edb6ba04f14a4c73cf500000000a4bab14874afdf54" - "e6aae816430607ca0675e09818e9bbec5918c59068baf76a008940f6fc3bbdc7f6090f756aae660b" - "6e4c699300000000d9c1e67743efb54e54270e46042e911803894e38000000376feecb80ac245409" - "c0becc271d9c2f67179bff0644399ae7e3c9472e00000000c5cfd9f2343b21362c19a0921dce2f83" - "00defa45000000270b9977e166bee737fe73670c439a644c323b59b4cd20eb7dabea74f700000000" - "f6989d2d6a909e986ff7add5df2c93e05459507b0000000f466554d2ae4d52a8c67b2e48b47003c8" - "1785d3ffdbd9a617df6b3542000000004c5cf8107ba282f4f983821918f93e742d08f0550000006f" - "2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b5159747cf200000000ab2dadc5a39411fd" - "4ff1116d478987316a553fc2000000cfc6ebe434a7ae8ff040483e310819e3b10db116431ec6f769" - "438a72e1000000002495a609675344f7e2e3a5ebaec3c85f0a1742f70abe95c50345132a61eda239" - "d9d083c3bf085387046ef8a36f0e9e696b382ab0000000009a6ce5d906837dbca6a5ee19d6f63fe9" - "03dd01f7246f13b2050424a2b3a45ef7a029c896b4132bd895072cfeffe9d6815997069500000000" - "0d3c723b91adb0da7c4aa7e7eb5a15bc03015fb98b841fd84cc43c510385b9a4c2aac1d67a909b29" - "7c703915312e9c3cae02dfa000000000dd603bd35e7fa0f02f2f3313d8469d09a92409c0b7f0318a" - "575a4f8e061db3dd7fde25654a4059d565dbc8a91e3b4457b077ddad3108be69f9b97d05c917ad6b" - "10e693bb6e26f2ba90c8e909a9fe20e5c7a4c656482a9b0d00625009a40aeb62a42b6a62548e3c38" - "cd3c72f203000000ca82ac5180101be4f85cef468ea086ea01aafdc3a9fe1ec787bc45db7c52a52d" - "00bd39a44e8e8bc17c01ac63eca0c1cf5ff7f03a03000000c9a89192c1c8be55281a59d1fd338f35" - "0075f8cea9fec34573654ea6624f138ef9531cd9367a02e4d241989477a363d53b02239e03000000" - "24438387def0f4c6544e4b275d9b714600f810d2a9fec17647176b7c07d856e3b883efebc09dd9d6" - "1966b7ae7412041d57393c6f03000000182c0287822a272bec4501a1e27acfee018588ffa9fe6cae" - "426de59560fad65d67c624f285d7174177a47579dda0b6eaa9a84c820300000070b1646d8026e9f1" - "704f1b16286ba2da017ef082a9feed33ef60a8b540b26f66761d1f13badfad0fe8fa8f3c1aad2a82" - "fa40546c03000000df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea9fe3e7d75a2f42b50c6a363" - "40132378b95c59313bacba64dbe996206e6904f50200000047150b9b14010469823acb72bb89182f" - "00112196a9feb9153b36bc60be5b534e006527f67485ab35aca0c7ee419733853cf09e8b02000000" - "e79c10acfce165e332a62384ec04e5ba009185ada9fe0070a36dd51323b2c54200154d12f86c260a" - "9edfa7a74c1c83c1050f63f802000000443cacf59c6379a44b7892f487afa98c0221c19ca9fe942f" - "460bcea75481f25e007d0de9a7afe283bd2f22ead05ff72006c83bc0020000004bdade862c224f6f" - "36506ebd455e679c00369bb8a9fecec3f8c8fa6867a982be8a934f852cc3d4d82bc0ec7303f99f8f" - "def85b7502000000a0bef8675b29a197b7b3cceaf5f1bb120335256aa9fe6e5d58099ffc4a503a71" - "2350acbd48411f0dc15d2f0f49dad345d966279502000000e06302aba042aaa218dc091e9aa1477f" - "6fdc9830a9fec95829a8838314dff34d24c332219a1b163a732d803e0e2f4f916d06412601000000" - "98c39e7cc282208fefc57ff447036b9501adcb22a9fe793f797a3c7dadd1c86e009d0c90bc512e13" - "7dcef5e4a27985bd5cfd5ce601000000152f2b70eaef7443e0f79ab6902dde5301b3ec71a9fe9f25" - "4ac95883195580410062ed564153e17478f8c3344d89c0bbfaa100fd01000000be184524a6bdc878" - "9cf851782d895bcc03a98489a9fe8c1287e6f7bb020ffdb00012098610e52bb2a16a4008aefd545b" - "0d80684e01000000ba8110fb9733cc24904f288262e6ea77032fa5f8a9feeefa701d120523bd98f2" - "00098b43cd68be6e3f81268193fd637e9037d7a701000000c47cf0f551e96770a754ac19ef820fe0" - "0031f2d3a9fe049150b8d10ab700cc3a7cf51be0403b654ba2f56808092069af5f5b481b01000000" - "68cb3bc873b04d937a6ed8f7bc51e54066fed098a9fe048a92d3adc69a84eb47622400207799416a" - "f1f0a086fbd7e2f7dea0077a00000000c386e9c6e6a2cbfa10ee58bdc75183600085d627a9feb1cb" - "e491cbbbf9443fd6007eb3c5bf64b671d6f18dbf463f9b83f512dc1c00000000fbab244735d67c61" - "283031667b2d74a102e0b1b1a9fe2aa590a2312e17f1a35900459582f4ef43c780908872746e39ef" - "a9a89f8700000000fcdf6d9be94030b34774d1d7dddedd9802f0f627a9fe965a87041331b2834bcf" - "00b4e3ce848518c4e3f6cbf25e5e1b992231bb0200000000173c333cd03bc905b7b899afeb760e3f" - "024a2efca9fe96b89b1bc8f415bd4e77be46bae5a1b3cae76665a268abfe8a41a84e27c100000000" - "cb29efdf672d2fa57fc85ebbe276c5660364192da9fee7af5eb888e9eb37bb046686943b101e1f55" - "3215abf8fbdf17c3677e5a3a00000000608df061d45d864d09f4ecf17625f82d03c74828a9fe1e37" - "1051852c972ea7954079884af257b044fd13a6826a4c619f3d136cac000000009402a4c216772167" - "3f2b02b3256ead1f03ea9bc1a9fefca162fb81e733cff620ca7feefe1933631e8e69f6d9d6962d2c"), - [1] = ("54270e46020000006a894e387625da376feecb80ac245409c0becc271d9c2f67179bff0644399ae7" - "9c64ed0432d599eaa660ac824b7fae389861419c7a899f2d010000009bf5a5d06776e66a9d80ab13" - "2e1ac921eb76adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" - "c4e1966800000000b3121cd5ef51e696c816290dbaee0e7726d082e1530ff5397c9125f59577d71c" - "4b258a005116d11354edff62ceaa458fc75a91c425a5927300000000c4458d70911714f1819aa46f" - "be143934c933ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa" - "4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5ab90135fa636af44" - "64d210a256be75e0509c4477d19ac64b2e69220d12ea3b9501000000fb95e8332fdfff658483a2d0" - "39a7bf148ed3481e0bccc460e89f7abdb682380a62eae374835b4a49a2b980b6aba92da6409969aa" - "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ecc8fb112087b97cfcebbaeb0c565359" - "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9bb0ec36f0000000064a2827b7cdd38d0314c178e" - "925b568ae56bc4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" - "02e9b69000000000eacce745ea213cb1a84035b632ef3be6070dafa2fed87470e9da55709427c226" - "d324d9a086ed27184b443181a5edc511c1507a142fea9bc10300000062fb81e733cff620ca7feefe" - "1933631e8e69f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" - "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6f18dbffeff9b8306c83bc026977911" - "4bdade862c224f6f36506ebd455e679cef369bb8d514573f03000000f6932f6932d1d5f5d0ce2cf6" - "bf7a70ba5193a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" - "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e45e9c1feb836c0a25ea71f5daaed4d" - "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a41e0f515601000000f57609453df7803357ae7998" - "9870ccec3def0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" - "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2fff2f256000000000000000000000000" - "00000001955e1a13d1111b067acbce3e3bcdf38200154d12010000009edfa7a74c1c83c18e47935e" - "8354846e34f0386a00000000000000000000000000000001ac26c7d41fd050bf8d85055bcfe3756c" - "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789cf85178000000000000000000000000" - "00000000020ffdb0e612098610e52bb2a16a40086f0e9e6903000000ec2e2924348352d715cf9d41" - "1ea012e5307294b600000000000000000000000000000000ccd2f50c30706cae3fdcc2f0ee3d5e26" - "81ae55f0030000000aa20624fbab244735d67c61283031667b2d74a1000000000000000000000000" - "0000000000459582f4ef43c780908872746e39efaefd545b020000007ced4f8b362956cd46646a0e" - "222160e5f769bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" - "f05d7308020000009d82c856ba942455050f63f8282f3464443cacf5000000000000000000000000" - "0000000069d9942f460bcea75481f25e307d0de96e91b91501000000be5b534e0a6527f67485ab35" - "aca0c7ee4197338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" - "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99289462000000000000000000000000" - "00000000b49185ad9b7d0070a36dd51323b2c542e5fe7d360100000066a3ae3939762df33a2d5506" - "0c78d551cbb110fd000000000000000000000000000000002c171ed15abbbe1cd911db7b56e8f3fe" - "9a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7246f13b2050424a2000000000000000000000000" - "00000000ffe9d6812231bb02f922259b173c333cbae87d60000000005d31e5b2de252167c1c91b3a" - "36ba0c700ee78477000000000000000000000000000000006ad0143a0554efb46b382ab0404cdf02" - "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb7213e4000000000000000000000000" - "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8" - "9b1bc8f415bd4e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" - "7666c05303000000ffa8e89871fb9510b900b2061d0c334b819f2dd1000000000000000000000000" - "00000000095a3230fde69eec3af2e6d0bd68ab7210613fc90200000016a31dc2e46a234558817151" - "b38fc9c909cf8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" - "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b06fa9e000000000000000000000000" - "00000000b314f695bd82e9ea8891ed0a3d1b91cab56e03e0010000002f70249eefc67bce716c856e" - "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1" - "eecc89f601000000d635edea8ffe423d01883918039249f398f9b37e000000000000000000000000" - "000000001fb7c8307f06b4b29088f20d9ac676d5f90e54b501000000a779f37c30d2c5053c40ecae" - "210aeb0934cb2a2400000000000000000000000000000000df48f333ef467092818aa77d6732b678" - "bcd7be0700000000e45aaf61814f703b40125e6baf4648cbaaa37b86000000000000000000000000" - "000000005ddb05f2a84e27c18a06e2c9cb29efdf50da49bf00000000d94beb259f9f0251becc9879" - "07879cca68fec7bb000000000000000000000000000000009402a4c2167721673f2b02b3256ead1f" - "a349e1cc00000000b879a45176740744fcb6a035339d504c9726d80d000000000000000000000000" - "000000009518862bc9a89192c1c8be55281a59d1ebfdcec30000000067a982be8a934f852cc3d4d8" - "2bc0ec7303f99f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" - "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f218424e48000000000000000000000000" - "00000002e6fbf952902383028e3b47f2111d891d536c75bb01000000720da9e07ac998b5690807d5" - "2617369ee1af4ca500000000000000000000ffff47d7b351a36b178aabc78a4a0d80684ee5dbf45b" - "17a4763102000000f9119c40b468b66331bca67a034f43340d01683c00000000000000000000ffff" - "b24a5be9b10123e6e4f15ecd1dff045298cfee069c88b02a0300000004b0595daf37deb499996bfb" - "667f072dec1e5d9cdc8a11f4409bcfb3c167090e99d90a8543961b2ccd47724a3c460ba85183f4c5" - "a7afe28302000000d05ff720b16c73761a3fcb675219061ab4c5c79489b2cc6a883c146972decb8b" - "5adac8f5e6d66df46ea139107754dee62c36d2fcc7a4c65601000000cd625009a40aeb62a42b6a62" - "548e3c38b96c20022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" - "e36f90d401000000fd41fe47684b370b6ec6584d64496089570968ead4d1ae91c819bb068196d599" - "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b21cdb3455c00000000c66af76e80f062ea1a7e0b7b" - "e218d067530e1b0487b8c3b99f2b8a1a8982c42f53700a3437c5156e072b2f2fb337c9cfe07c34dd" - "c896c9a7000000000a5f6abd850b06e3d9662795ce2cc1cee06302aba042aaa218dc091e9aa1477f" - "6fdc9830d7fbc95829a8838314dff34d24c332214d4647f5000000000f88e3759a08e7a56663861e" - "ea1bf42ccd3c72f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" - "7625ca4a00000000e682f80f94315c45ef817264c89a736ed55ed637b077ddad3108be69f9b97d05" - "c917ad6b10e693bb6e26f2ba90c8e909e85e20e5b0916e5d030000004a503a712350acbd48411f0d" - "c15d2f0f49dad345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" - "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e" - "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c" - "db544e44df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" - "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c1fa56dfdcfc1155f236c8b9c79a620" - "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2" - "ff0f576767d3648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" - "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c93ef054e93f8120abb42316c533d9c9" - "4d88189c471a1ff2f0ce3ff66e782110125ae2eddaae7d6300000000c0982b283aa9986700b2a2b3" - "ed257b8b0489f48f053ec8cac72105b327335a25a364d1015ffac03089f45539e1b0178462156cd6" - "6855676c00000000017c9148da5892bb4951c3a7ed55689d3391ce7d3fd48469845f0c233dbdd2a9" - "7835df1f1782dee88487fd0db5971b46b7a350f998c39e7c00000000efc57ff447036b9551adcb22" - "fd50793f797a3c7dadd1c86e759d0c90bc512e137dcef5e4a27985bd8d1e69c83dc21056b973972c" - "bc51e540030000006c3d048a92d3adc69a84eb47622400207799416afef0a086fbd7e2f7dea0077a" - "e9c0c0d9c386e9c6e6a2cbfa10ee58bdc75183600422ae3f03000000c43bd794c697895e5bfe47c6" - "f53a54e01b0c1d89fecd94d0e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38" - "69121aec010000000fcc00ac2daa755e5292cd32dc096cd5d8a4c5cffe91ee1cfcc2056cc1ff8e51" - "bbce426cfa4b861cc78592be7b14e7ba9c15acb836674cde0200000054cfe8f42bf0e9381ba2c439" - "6de60f032bc7f34dfe959871c0d4c0d4eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21362c19a092" - "b2cd3dbd01000000e958e48d2ab8b0313e7f8933e315130fa9a84c82feb83f6e70b1646d8026e9f1" - "704f1b16286ba2dabc7ef0820c91ed33ef60a8b5bcfe97e1000000007ad742d3d7320a4f880cf47f" - "5dd0cf69cb22840ffeb3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f6e8e99cd" - "1dce2f83020000003a3efc240b9977e166bee737fe73670c439a644c000000000000000000000000" - "000000015e0dfe491d1ba96742c7b5e02b227122cf844ef501000000cd743f59bd14c89b5cfd5ce6" - "e82e7854152f2b700000000000000000000000000000000127e19f254ac9588319558041fc62ed56" - "b83579b80300000072437f9b6245de39ec9e71ee4b951507beb42b34000000000000000000000000" - "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f" - "c1ea76e98c9dfc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" - "0436242c03000000ee3fbc164632d4f28aa28c08f80dd50c6712dfb4000000000000000000000000" - "00000000078a806d329c9b008bbfc9723107f3f52af145480300000004265f6c72923e22594f3f79" - "e208ed2e2ebb07710000000000000000000000000000000039dd8f88152a58454d9ca9d20f45dfa7" - "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f608df061000000000000000000000000" - "000000008ae21e371051852c972ea7954079884a9a1b163a030000000e2f4f911a0dd8c052ed6b13" - "e2815a1a37a52bdd00000000000000000000000000000000bd99fcf72909ac9e7a55299e9bb4fd53" - "9f3c2c8803000000f51f44e644d7cf1d06b2b115d882549e62c12fba000000000000000000000000" - "000000004fc812bd2f8a59d96d064126666befbbf257b044030000006a4c619f9037d7a754655354" - "c47cf0f551e967700000000000000000000000000000000050b8d10ab700cc3a7cf51be0403b654b" - "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4dd3d03000000000000000000000000" - "00000000d538d34574c038404923e75d0e09e2fcfc1519de010000002545666937c22a3b6e9686dc" - "c405cbee5016c252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" - "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d9b7146000000000000000000000000" - "00000000b883efebc09dd9d61966b7ae7412041dd7ac5f9901000000822a272bec4501a1e27acfee" - "7a8588ffd5a06cae0000000000000000000000000000000077a47579dda0b6ead1e1b34901eaedd5" - "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e2b00b6000000000000000000000000" - "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096" - "cc582cabd5ddd15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" - "fc2b445700000000496ad01bf007eacb0a28aec868282510b60291ea000000000000000000000000" - "000000005b29a197b7b3cceaf5f1bb12c535256a74993ade03000000e62112898dab2dade2ab2fc9" - "c56a7c86be5f962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" - "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a631480000000000000000000000000" - "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050" - "ed7afd741024e83600000000000000000000000000000000f493472201e48d163106cc397446a33f" - "bc74179403000000438a72e12ae0436a2495a609675344f7e2e3a5eb000000000000000000000000" - "0000000061eda239d9d083c3bf085387046ef8a34153e174020000004d89c0bb5408c1b7d17fb084" - "d9e1825fa638e1af000000000000000000000000000000006e08cb887464b9344ec3127c750fabdd" - "6c0fc09c02000000809829af4a9df58dbfef186d416f3a1ef170d10f000000000000000000000000" - "0000000087780281d47a32f2235376239daeaacba9a89f8701000000fcdf6d9be94030b34774d1d7" - "dddedd9899f0f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" - "fd338f35010000006e8cc34573654ea6624f138ef9531cd9367a02e4000000000000000000000000" - "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc" - "79685b05ddd3aa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" - "b7f0318a01000000061db3dd7fde25654a4059d565dbc8a91e3b4457000000000000000000000000" - "00000000c1b5aad487c192f08bd7a09bdd21444359747cf200000000ab2dadc5a39411fd4ff1116d" - "478987316a553fc2000000000000000000000000000000000819e3b10db116431ec6f7691539ce41" - "b95c593100000000dbe9962069e66142e22753344fda8a3e0b841cf7000000000000000000000000" - "000000008c54dda9ac3bb1b1d43e6505621b9a7f672d2fa501000000e276c5661a64192db44ce7af" - "5eb888e9eb37bb0400000000000000000000000000000002641e447514ca565fb9b68c08a5e4351e" - "9f3c39f201000000de745cb25851429b3bbfb50168dfd04edf6b3542000000000000000000000000" - "0000000218f93e742d08f0550b55726f2292362e70ccc56700000000a4c73cf5ffa4ffc8a4bab148" - "74afdf54e6aae8160000000000000000000000000000000268baf76ab08940f6fc3bbdc7f6090f75" - "4f1b134803000000449a774929e86716ec852d59f3d17232edd86ad6000000000000000000000000" - "000000027dba1ee7e143fd52599364e13468f80f40b26f6603000000badfad0fe8fa8f3c1aad2a82" - "530b7447d9c2f401000000000000000000000000000000028147507e1cd59ea1ae6da48b1eba6d16" - "dedea2030100000002a1f8e6f5657f3f9e3eb807cbad76451055edb8000000000000000000000000" - "00000002d9279fb2b8fcbac6abea74f7a6df7979f6989d2d000000006ff7add5df2c93e05459507b" - "5d8c6a0f466554d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), - [2] = ("4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5ab90135fa636af44" - "64d210a256be75e0509c4477d19ac64b2e69220d54270e46020000006a894e387625da376feecb80" - "ac245409c0becc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c" - "12ea3b9501000000fb95e8332fdfff658483a2d039a7bf148ed3481e0bccc460e89f7abdb682380a" - "62eae374835b4a49a2b980b6aba92da6409969aa7a899f2d010000009bf5a5d06776e66a9d80ab13" - "2e1ac921eb76adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b" - "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ecc8fb112087b97cfcebbaeb0c565359" - "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9c4e1966800000000b3121cd5ef51e696c816290d" - "baee0e7726d082e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4" - "25a5927300000000c4458d70911714f1819aa46fbe143934c933ae6dec0fb124f264af67eed7c9a8" - "c38b6b8ee2614344bea73223e59bf0193432e9fa2fea9bc10300000062fb81e733cff620ca7feefe" - "1933631e8e69f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196" - "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6f18dbffeff9b8306c83bc026977911" - "4bdade862c224f6f36506ebd455e679cef369bb8bb0ec36f0000000064a2827b7cdd38d0314c178e" - "925b568ae56bc4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13" - "02e9b69000000000eacce745ea213cb1a84035b632ef3be6070dafa2fed87470e9da55709427c226" - "d324d9a086ed27184b443181a5edc511c1507a14d514573f03000000f6932f6932d1d5f5d0ce2cf6" - "bf7a70ba5193a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd" - "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e45e9c1feb836c0a25ea71f5daaed4d" - "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a4ae8cb60b0300000038d856a40c67c3d4afa9c864" - "9da90bd2fff2f25600000000000000000000000000000001955e1a13d1111b067acbce3e3bcdf382" - "00154d12010000009edfa7a74c1c83c18e47935e8354846e34f0386a000000000000000000000000" - "00000001ac26c7d41fd050bf8d85055bcfe3756c1e0f515601000000f57609453df7803357ae7998" - "9870ccec3def0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7" - "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb7213e4000000000000000000000000" - "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8" - "9b1bc8f415bd4e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec" - "6f0e9e6903000000ec2e2924348352d715cf9d411ea012e5307294b6000000000000000000000000" - "00000000ccd2f50c30706cae3fdcc2f0ee3d5e267666c05303000000ffa8e89871fb9510b900b206" - "1d0c334b819f2dd100000000000000000000000000000000095a3230fde69eec3af2e6d0bd68ab72" - "81ae55f0030000000aa20624fbab244735d67c61283031667b2d74a1000000000000000000000000" - "0000000000459582f4ef43c780908872746e39ef10613fc90200000016a31dc2e46a234558817151" - "b38fc9c909cf8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2" - "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b06fa9e000000000000000000000000" - "00000000b314f695bd82e9ea8891ed0a3d1b91ca6e91b91501000000be5b534e0a6527f67485ab35" - "aca0c7ee4197338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b" - "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99289462000000000000000000000000" - "00000000b49185ad9b7d0070a36dd51323b2c542b56e03e0010000002f70249eefc67bce716c856e" - "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1" - "e5fe7d360100000066a3ae3939762df33a2d55060c78d551cbb110fd000000000000000000000000" - "000000002c171ed15abbbe1cd911db7b56e8f3feeecc89f601000000d635edea8ffe423d01883918" - "039249f398f9b37e000000000000000000000000000000001fb7c8307f06b4b29088f20d9ac676d5" - "f90e54b501000000a779f37c30d2c5053c40ecae210aeb0934cb2a24000000000000000000000000" - "00000000df48f333ef467092818aa77d6732b678bcd7be0700000000e45aaf61814f703b40125e6b" - "af4648cbaaa37b86000000000000000000000000000000005ddb05f2a84e27c18a06e2c9cb29efdf" - "50da49bf00000000d94beb259f9f0251becc987907879cca68fec7bb000000000000000000000000" - "000000009402a4c2167721673f2b02b3256ead1fa349e1cc00000000b879a45176740744fcb6a035" - "339d504c9726d80d000000000000000000000000000000009518862bc9a89192c1c8be55281a59d1" - "bae87d60000000005d31e5b2de252167c1c91b3a36ba0c700ee78477000000000000000000000000" - "000000006ad0143a0554efb46b382ab0404cdf02ebfdcec30000000067a982be8a934f852cc3d4d8" - "2bc0ec7303f99f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a" - "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789cf85178000000000000000000000000" - "00000000020ffdb0e612098610e52bb2a16a4008aefd545b020000007ced4f8b362956cd46646a0e" - "222160e5f769bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d" - "f05d7308020000009d82c856ba942455050f63f8282f3464443cacf5000000000000000000000000" - "0000000069d9942f460bcea75481f25e307d0de99a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7" - "246f13b2050424a200000000000000000000000000000000ffe9d6812231bb02f922259b173c333c" - "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f218424e48000000000000000000000000" - "00000002e6fbf952902383028e3b47f2111d891d17a4763102000000f9119c40b468b66331bca67a" - "034f43340d01683c00000000000000000000ffffb24a5be9b10123e6e4f15ecd1dff045298cfee06" - "536c75bb01000000720da9e07ac998b5690807d52617369ee1af4ca500000000000000000000ffff" - "47d7b351a36b178aabc78a4a0d80684ee5dbf45bb0916e5d030000004a503a712350acbd48411f0d" - "c15d2f0f49dad345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8" - "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e" - "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c" - "db544e44df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378" - "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c1fa56dfdcfc1155f236c8b9c79a620" - "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2" - "ff0f576767d3648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034" - "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c93ef054e93f8120abb42316c533d9c9" - "4d88189c471a1ff2f0ce3ff66e782110125ae2edc7a4c65601000000cd625009a40aeb62a42b6a62" - "548e3c38b96c20022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb" - "daae7d6300000000c0982b283aa9986700b2a2b3ed257b8b0489f48f053ec8cac72105b327335a25" - "a364d1015ffac03089f45539e1b0178462156cd66855676c00000000017c9148da5892bb4951c3a7" - "ed55689d3391ce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46b7a350f9" - "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d067530e1b0487b8c3b99f2b8a1a8982c42f" - "53700a3437c5156e072b2f2fb337c9cfe07c34ddc896c9a7000000000a5f6abd850b06e3d9662795" - "ce2cc1cee06302aba042aaa218dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d24c33221" - "98c39e7c00000000efc57ff447036b9551adcb22fd50793f797a3c7dadd1c86e759d0c90bc512e13" - "7dcef5e4a27985bd8d1e69c83dc21056b973972c7625ca4a00000000e682f80f94315c45ef817264" - "c89a736ed55ed637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5" - "9c88b02a0300000004b0595daf37deb499996bfb667f072dec1e5d9cdc8a11f4409bcfb3c167090e" - "99d90a8543961b2ccd47724a3c460ba85183f4c5a7afe28302000000d05ff720b16c73761a3fcb67" - "5219061ab4c5c79489b2cc6a883c146972decb8b5adac8f5e6d66df46ea139107754dee62c36d2fc" - "e36f90d401000000fd41fe47684b370b6ec6584d64496089570968ead4d1ae91c819bb068196d599" - "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b214d4647f5000000000f88e3759a08e7a56663861e" - "ea1bf42ccd3c72f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db" - "0422ae3f03000000c43bd794c697895e5bfe47c6f53a54e01b0c1d89fecd94d0e02d2ec587fc3300" - "03781b0f294a2b73c4421398f4de67e9cee64b38bc51e540030000006c3d048a92d3adc69a84eb47" - "622400207799416afef0a086fbd7e2f7dea0077ae9c0c0d9c386e9c6e6a2cbfa10ee58bdc7518360" - "36674cde0200000054cfe8f42bf0e9381ba2c4396de60f032bc7f34dfe959871c0d4c0d4eb720c4a" - "e3c9472e5f2da5d9c5cfd9f2343b21362c19a092b2cd3dbd01000000e958e48d2ab8b0313e7f8933" - "e315130fa9a84c82feb83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5" - "bcfe97e1000000007ad742d3d7320a4f880cf47f5dd0cf69cb22840ffeb3fe7749509cb6752b2cee" - "30b7e7736a0afc9879ea40e69710fc9f6e8e99cd69121aec010000000fcc00ac2daa755e5292cd32" - "dc096cd5d8a4c5cffe91ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8" - "b83579b80300000072437f9b6245de39ec9e71ee4b951507beb42b34000000000000000000000000" - "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f" - "c1ea76e98c9dfc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e" - "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e7854152f2b70000000000000000000000000" - "0000000127e19f254ac9588319558041fc62ed561dce2f83020000003a3efc240b9977e166bee737" - "fe73670c439a644c000000000000000000000000000000015e0dfe491d1ba96742c7b5e02b227122" - "2af145480300000004265f6c72923e22594f3f79e208ed2e2ebb0771000000000000000000000000" - "0000000039dd8f88152a58454d9ca9d20f45dfa774993ade03000000e62112898dab2dade2ab2fc9" - "c56a7c86be5f962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8" - "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f608df061000000000000000000000000" - "000000008ae21e371051852c972ea7954079884a9f3c2c8803000000f51f44e644d7cf1d06b2b115" - "d882549e62c12fba000000000000000000000000000000004fc812bd2f8a59d96d064126666befbb" - "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a631480000000000000000000000000" - "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050" - "ed7afd741024e83600000000000000000000000000000000f493472201e48d163106cc397446a33f" - "f257b044030000006a4c619f9037d7a754655354c47cf0f551e96770000000000000000000000000" - "0000000050b8d10ab700cc3a7cf51be0403b654bbc74179403000000438a72e12ae0436a2495a609" - "675344f7e2e3a5eb0000000000000000000000000000000061eda239d9d083c3bf085387046ef8a3" - "4153e174020000004d89c0bb5408c1b7d17fb084d9e1825fa638e1af000000000000000000000000" - "000000006e08cb887464b9344ec3127c750fabdd6c0fc09c02000000809829af4a9df58dbfef186d" - "416f3a1ef170d10f0000000000000000000000000000000087780281d47a32f2235376239daeaacb" - "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4dd3d03000000000000000000000000" - "00000000d538d34574c038404923e75d0e09e2fca9a89f8701000000fcdf6d9be94030b34774d1d7" - "dddedd9899f0f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767" - "fd338f35010000006e8cc34573654ea6624f138ef9531cd9367a02e4000000000000000000000000" - "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc" - "79685b05ddd3aa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515" - "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d9b7146000000000000000000000000" - "00000000b883efebc09dd9d61966b7ae7412041db7f0318a01000000061db3dd7fde25654a4059d5" - "65dbc8a91e3b445700000000000000000000000000000000c1b5aad487c192f08bd7a09bdd214443" - "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e2b00b6000000000000000000000000" - "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096" - "cc582cabd5ddd15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f" - "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a553fc2000000000000000000000000" - "000000000819e3b10db116431ec6f7691539ce41b95c593100000000dbe9962069e66142e2275334" - "4fda8a3e0b841cf7000000000000000000000000000000008c54dda9ac3bb1b1d43e6505621b9a7f" - "fc2b445700000000496ad01bf007eacb0a28aec868282510b60291ea000000000000000000000000" - "000000005b29a197b7b3cceaf5f1bb12c535256a0436242c03000000ee3fbc164632d4f28aa28c08" - "f80dd50c6712dfb400000000000000000000000000000000078a806d329c9b008bbfc9723107f3f5" - "9a1b163a030000000e2f4f911a0dd8c052ed6b13e2815a1a37a52bdd000000000000000000000000" - "00000000bd99fcf72909ac9e7a55299e9bb4fd53fc1519de010000002545666937c22a3b6e9686dc" - "c405cbee5016c252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e" - "d7ac5f9901000000822a272bec4501a1e27acfee7a8588ffd5a06cae000000000000000000000000" - "0000000077a47579dda0b6ead1e1b34901eaedd54f1b134803000000449a774929e86716ec852d59" - "f3d17232edd86ad6000000000000000000000000000000027dba1ee7e143fd52599364e13468f80f" - "40b26f6603000000badfad0fe8fa8f3c1aad2a82530b7447d9c2f401000000000000000000000000" - "000000028147507e1cd59ea1ae6da48b1eba6d16dedea2030100000002a1f8e6f5657f3f9e3eb807" - "cbad76451055edb800000000000000000000000000000002d9279fb2b8fcbac6abea74f7a6df7979" - "672d2fa501000000e276c5661a64192db44ce7af5eb888e9eb37bb04000000000000000000000000" - "00000002641e447514ca565fb9b68c08a5e4351e9f3c39f201000000de745cb25851429b3bbfb501" - "68dfd04edf6b35420000000000000000000000000000000218f93e742d08f0550b55726f2292362e" - "70ccc56700000000a4c73cf5ffa4ffc8a4bab14874afdf54e6aae816000000000000000000000000" - "0000000268baf76ab08940f6fc3bbdc7f6090f75f6989d2d000000006ff7add5df2c93e05459507b" - "5d8c6a0f466554d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"), - }; - const int TEST_DATA_I = GPOINTER_TO_INT(test_data); - const int addr_family = (TEST_DATA_I == 0 ? AF_INET : AF_INET6); - const int IPV6_PREFER_TEMP = (TEST_DATA_I == 2); - const guint N_ADDRESSES = 100; - const gsize ELM_SIZE = - addr_family == AF_INET ? sizeof(NMPlatformIP4Address) : sizeof(NMPlatformIP6Address); - const gboolean DO_REGENERATE = FALSE; - const gboolean PRINT_RESULT = DO_REGENERATE; - const gboolean CHECK_RESULT = !DO_REGENERATE; - gs_free guint8 *addresses = NULL; - gs_free guint64 *rand_map = NULL; - gsize i, j; - -#if !defined(__amd64__) - /* The test generates a random array of NMPlatformIPXAddress (by crudely randomizing the memory, - * not the structures themself) and then compares the sorted result with the expected output. - * The sole purpose is to ensure that the sorting order stays stable. - * - * This only works on an architecture for which the test was made, otherwise - * the expected data does not match (due to different layout of the structures - * in memory). - * - * That's fine. Skip the test. */ - g_test_skip("skip test on non-amd64 architecture"); - return; -#endif - - /* - * First we create a list of addresses filled with (stable) random bytes. - * We tweak some fields explicitly (stable randomly), so that we cover all - * relevant cases. - * - * Then we sort the list of addresses, and compare that the result is - * as our EXPECTED_BUFFER. - */ - - addresses = g_malloc(ELM_SIZE * N_ADDRESSES); - rand_map = g_malloc(sizeof(rand_map[0]) * N_ADDRESSES); - nmtst_stable_rand(258829693, addresses, ELM_SIZE * N_ADDRESSES); - nmtst_stable_rand(710086081, rand_map, sizeof(rand_map[0]) * N_ADDRESSES); - - for (i = 0; i < N_ADDRESSES; i++) { - NMPlatformIPXAddress *a = (gpointer)(&addresses[i * ELM_SIZE]); - guint64 r = rand_map[i]; - struct in6_addr * a6; - -#define CONSUME_BITS(r, nbits) \ - ({ \ - guint64 _r = (r); \ - const int _nbits = (nbits); \ - \ - r >>= (_nbits); \ - (_r & (((((guint64) 1u) << (_nbits)) - 1u))); \ - }) - - if (addr_family == AF_INET) { - if (CONSUME_BITS(r, 1)) { - /* randomly create a link-local address or not. */ - g_assert((NM_IPV4LL_NETWORK & ~NM_IPV4LL_NETMASK) == 0); - a->a4.address = (a->a4.address & ~NM_IPV4LL_NETMASK) | NM_IPV4LL_NETWORK; - } - } else { - a6 = &a->a6.address; - switch (CONSUME_BITS(r, 4)) { - case 0: - a6->s6_addr32[0] = 0; - a6->s6_addr32[1] = 0; - a6->s6_addr32[2] = htonl(0xffff); - g_assert(IN6_IS_ADDR_V4MAPPED(a6)); - break; - case 1: - a6->s6_addr32[0] = 0; - a6->s6_addr32[1] = 0; - a6->s6_addr32[2] = 0; - a6->s6_addr32[3] = htonl(2); - g_assert(IN6_IS_ADDR_V4COMPAT(a6)); - break; - case 2: - a6->s6_addr32[0] = 0; - a6->s6_addr32[1] = 0; - a6->s6_addr32[2] = 0; - a6->s6_addr32[3] = 0; - g_assert(IN6_IS_ADDR_UNSPECIFIED(a6)); - break; - case 3: - a6->s6_addr32[0] = 0; - a6->s6_addr32[1] = 0; - a6->s6_addr32[2] = 0; - a6->s6_addr32[3] = htonl(1); - g_assert(IN6_IS_ADDR_LOOPBACK(a6)); - break; - case 4: - a6->s6_addr32[0] = (a6->s6_addr32[0] & ~htonl(0xffc00000)) | htonl(0xfe800000); - g_assert(IN6_IS_ADDR_LINKLOCAL(a6)); - break; - case 6: - a6->s6_addr32[0] = (a6->s6_addr32[0] & ~htonl(0xffc00000)) | htonl(0xfec00000); - g_assert(IN6_IS_ADDR_SITELOCAL(a6)); - break; - case 7: - case 8: - case 9: - case 10: - break; - default: - memset(a6, 0, sizeof(*a6)); - break; - } - } - - if (CONSUME_BITS(r, 5) != 0) { - /* randomly make addr-source the same (so that several addresses compare - * equal). */ - a->a4.addr_source = CONSUME_BITS(r, 2); - } - - if (addr_family == AF_INET) { - if (CONSUME_BITS(r, 1)) { - /* randomly make the label empty or not. */ - a->a4.label[0] = '\0'; - } - } - if (addr_family == AF_INET) { - if (CONSUME_BITS(r, 2) != 0) { - /* randomly make the label empty or not. */ - a->a4.plen = CONSUME_BITS(r, 2); - } - } - if (addr_family == AF_INET) { - if (CONSUME_BITS(r, 1)) { - a->a4.address = (a->a4.address & ~0xFFFFFFu) | (CONSUME_BITS(r, 2) << 24); - } - } - } - - g_qsort_with_data(addresses, - N_ADDRESSES, - ELM_SIZE, - _address_pretty_sort_cmp, - (gpointer) test_data); - - for (i = 0; i < N_ADDRESSES; i++) { - const NMPlatformIPXAddress *a = (gconstpointer)(&addresses[i * ELM_SIZE]); - - for (j = i + 1; j < N_ADDRESSES; j++) { - const NMPlatformIPXAddress *b = (gconstpointer)(&addresses[j * ELM_SIZE]); - int c1; - int c2; - - if (addr_family == AF_INET) { - c1 = nm_platform_ip4_address_pretty_sort_cmp(&a->a4, &b->a4); - c2 = nm_platform_ip4_address_pretty_sort_cmp(&b->a4, &a->a4); - } else { - c1 = nm_platform_ip6_address_pretty_sort_cmp(&a->a6, &b->a6, IPV6_PREFER_TEMP); - c2 = nm_platform_ip6_address_pretty_sort_cmp(&b->a6, &a->a6, IPV6_PREFER_TEMP); - } - - g_assert_cmpint(c1, >=, -1); - g_assert_cmpint(c1, <=, 1); - g_assert_cmpint(c1, ==, -c2); - } - } - - if (PRINT_RESULT) { - g_print("\n [%d] = (", TEST_DATA_I); - for (i = 0; i < ELM_SIZE * N_ADDRESSES;) { - if (i > 0) - g_print("\n "); - g_print("\""); - for (j = 0; j < 40 && i < ELM_SIZE * N_ADDRESSES; j++, i++) - g_print("%02x", addresses[i]); - g_print("\""); - } - g_print("),\n"); - return; - } - - if (CHECK_RESULT) { - gs_free guint8 *bin_arr = NULL; - gsize bin_len; - - bin_arr = nm_utils_hexstr2bin_alloc(EXPECTED_BUFFER[TEST_DATA_I], - FALSE, - FALSE, - NULL, - 0, - &bin_len); - - if (bin_len != ELM_SIZE * N_ADDRESSES || memcmp(addresses, bin_arr, bin_len) != 0) { - char *addresses_str = nm_utils_bin2hexstr(addresses, ELM_SIZE * N_ADDRESSES, -1); - - g_error(">>> test_platform_ip_address_pretty_sort_cmp() will fail:\n" - ">>> addresses[%zu]: %s\n" - ">>> expected [%zu]: %s\n", - ELM_SIZE * N_ADDRESSES, - addresses_str, - bin_len, - EXPECTED_BUFFER[TEST_DATA_I]); - } - - g_assert_cmpmem(addresses, ELM_SIZE * N_ADDRESSES, bin_arr, bin_len); - } -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - nmtst_init_assert_logging(&argc, &argv, "WARN", "DEFAULT"); - - g_test_add_func("/general/init_linux_platform", test_init_linux_platform); - g_test_add_func("/general/link_get_all", test_link_get_all); - g_test_add_func("/general/nm_platform_link_flags2str", test_nm_platform_link_flags2str); - g_test_add_data_func("/general/platform_ip_address_pretty_sort_cmp/4", - GINT_TO_POINTER(0), - test_platform_ip_address_pretty_sort_cmp); - g_test_add_data_func("/general/platform_ip_address_pretty_sort_cmp/6/1", - GINT_TO_POINTER(1), - test_platform_ip_address_pretty_sort_cmp); - g_test_add_data_func("/general/platform_ip_address_pretty_sort_cmp/6/2", - GINT_TO_POINTER(2), - test_platform_ip_address_pretty_sort_cmp); - - return g_test_run(); -} diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c deleted file mode 100644 index 1305e586..00000000 --- a/src/platform/tests/test-route.c +++ /dev/null @@ -1,1926 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2016 - 2017 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include <linux/rtnetlink.h> -#include <linux/fib_rules.h> - -#include "nm-core-utils.h" -#include "platform/nm-platform-utils.h" -#include "platform/nmp-rules-manager.h" - -#include "test-common.h" - -#define DEVICE_IFINDEX NMTSTP_ENV1_IFINDEX -#define EX NMTSTP_ENV1_EX - -static void -_wait_for_ipv4_addr_device_route(NMPlatform *platform, - gint64 timeout_msec, - int ifindex, - in_addr_t addr, - guint8 plen) -{ - /* Wait that the addresses gets a device-route. After adding a address, - * the device route is not added immediately. It takes a moment... */ - - addr = nm_utils_ip4_address_clear_host_address(addr, plen); - NMTST_WAIT_ASSERT(400, { - NMDedupMultiIter iter; - NMPLookup lookup; - const NMPObject *o; - - nmp_cache_iter_for_each ( - &iter, - nm_platform_lookup(platform, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex)), - &o) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE(o); - - if (r->plen == plen && addr == nm_utils_ip4_address_clear_host_address(r->network, plen) - && r->metric == 0 && r->scope_inv == nm_platform_route_scope_inv(RT_SCOPE_LINK) - && r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) - return; - } - nmtstp_assert_wait_for_signal(platform, - (nmtst_wait_end_us - g_get_monotonic_time()) / 1000); - }); -} - -static void -_wait_for_ipv6_addr_non_tentative(NMPlatform * platform, - gint64 timeout_msec, - int ifindex, - guint addr_n, - const struct in6_addr *addrs) -{ - guint i; - - /* Wait that the addresses become non-tentative. Dummy interfaces are NOARP - * and thus don't do DAD, but the kernel sets the address as tentative for a - * small amount of time, which prevents the immediate addition of the route - * with RTA_PREFSRC */ - - NMTST_WAIT_ASSERT(timeout_msec, { - gboolean should_wait = FALSE; - const NMPlatformIP6Address *plt_addr; - - for (i = 0; i < addr_n; i++) { - plt_addr = nm_platform_ip6_address_get(platform, ifindex, &addrs[i]); - if (!plt_addr || NM_FLAGS_HAS(plt_addr->n_ifa_flags, IFA_F_TENTATIVE)) { - should_wait = TRUE; - break; - } - } - if (!should_wait) - return; - nmtstp_assert_wait_for_signal(platform, - (nmtst_wait_end_us - g_get_monotonic_time()) / 1000); - }); -} - -static void -ip4_route_callback(NMPlatform * platform, - int obj_type_i, - int ifindex, - const NMPlatformIP4Route *received, - int change_type_i, - SignalData * data) -{ - const NMPObjectType obj_type = obj_type_i; - const NMPlatformSignalChangeType change_type = change_type_i; - NMPObject o_id; - nm_auto_nmpobj NMPObject *o_id_p = nmp_object_new(NMP_OBJECT_TYPE_IP4_ROUTE, NULL); - - g_assert_cmpint(obj_type, ==, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert(received); - g_assert_cmpint(received->ifindex, ==, ifindex); - g_assert(data && data->name); - g_assert_cmpstr(data->name, ==, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED); - - /* run code for initializing the ID only */ - nmp_object_stackinit_id(&o_id, NMP_OBJECT_UP_CAST(received)); - nmp_object_copy(o_id_p, NMP_OBJECT_UP_CAST(received), TRUE); - nmp_object_copy(o_id_p, NMP_OBJECT_UP_CAST(received), FALSE); - - if (data->ifindex && data->ifindex != received->ifindex) - return; - if (data->change_type != change_type) - return; - - if (data->loop) - g_main_loop_quit(data->loop); - - data->received_count++; - _LOGD("Received signal '%s' %dth time.", data->name, data->received_count); -} - -static void -ip6_route_callback(NMPlatform * platform, - int obj_type_i, - int ifindex, - const NMPlatformIP6Route *received, - int change_type_i, - SignalData * data) -{ - const NMPObjectType obj_type = obj_type_i; - const NMPlatformSignalChangeType change_type = change_type_i; - NMPObject o_id; - nm_auto_nmpobj NMPObject *o_id_p = nmp_object_new(NMP_OBJECT_TYPE_IP6_ROUTE, NULL); - - g_assert_cmpint(obj_type, ==, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert(received); - g_assert_cmpint(received->ifindex, ==, ifindex); - g_assert(data && data->name); - g_assert_cmpstr(data->name, ==, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED); - - /* run code for initializing the ID only */ - nmp_object_stackinit_id(&o_id, NMP_OBJECT_UP_CAST(received)); - nmp_object_copy(o_id_p, NMP_OBJECT_UP_CAST(received), TRUE); - nmp_object_copy(o_id_p, NMP_OBJECT_UP_CAST(received), FALSE); - - if (data->ifindex && data->ifindex != received->ifindex) - return; - if (data->change_type != change_type) - return; - - if (data->loop) - g_main_loop_quit(data->loop); - - data->received_count++; - _LOGD("Received signal '%s' %dth time.", data->name, data->received_count); -} - -static void -test_ip4_route_metric0(void) -{ - int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - SignalData *route_added = add_signal(NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip4_route_callback); - SignalData *route_changed = add_signal(NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_CHANGED, - ip4_route_callback); - SignalData *route_removed = add_signal(NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip4_route_callback); - in_addr_t network = - nmtst_inet4_from_string("192.0.2.5"); /* from 192.0.2.0/24 (TEST-NET-1) (rfc5737) */ - int plen = 32; - int metric = 22987; - int mss = 1000; - - /* No routes initially */ - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, 0, 0); - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, metric, 0); - - /* add the first route */ - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network, - plen, - INADDR_ANY, - 0, - metric, - mss); - accept_signal(route_added); - - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, 0, 0); - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, network, plen, metric, 0); - - /* Deleting route with metric 0 does nothing */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, network, plen, 0)); - ensure_no_signal(route_removed); - - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, 0, 0); - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, network, plen, metric, 0); - - /* add the second route */ - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network, - plen, - INADDR_ANY, - 0, - 0, - mss); - accept_signal(route_added); - - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, network, plen, 0, 0); - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, network, plen, metric, 0); - - /* Delete route with metric 0 */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, network, plen, 0)); - accept_signal(route_removed); - - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, 0, 0); - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, network, plen, metric, 0); - - /* Delete route with metric 0 again (we expect nothing to happen) */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, network, plen, 0)); - ensure_no_signal(route_removed); - - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, 0, 0); - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, network, plen, metric, 0); - - /* Delete the other route */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, network, plen, metric)); - accept_signal(route_removed); - - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, 0, 0); - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, metric, 0); - - free_signal(route_added); - free_signal(route_changed); - free_signal(route_removed); -} - -static void -test_ip4_route(void) -{ - int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - SignalData * route_added = add_signal(NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip4_route_callback); - SignalData * route_changed = add_signal(NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_CHANGED, - ip4_route_callback); - SignalData * route_removed = add_signal(NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip4_route_callback); - GPtrArray * routes; - NMPlatformIP4Route rts[3]; - in_addr_t network; - guint8 plen = 24; - in_addr_t gateway; - /* Choose a high metric so that we hopefully don't conflict. */ - int metric = 22986; - int mss = 1000; - - inet_pton(AF_INET, "192.0.3.0", &network); - inet_pton(AF_INET, "198.51.100.1", &gateway); - - /* Add route to gateway */ - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - gateway, - 32, - INADDR_ANY, - 0, - metric, - mss); - accept_signal(route_added); - - /* Add route */ - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, metric, 0); - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network, - plen, - gateway, - 0, - metric, - mss); - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, network, plen, metric, 0); - accept_signal(route_added); - - /* Add route again */ - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network, - plen, - gateway, - 0, - metric, - mss); - accept_signals(route_changed, 0, 1); - - /* Add default route */ - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, 0, 0, metric, 0); - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - gateway, - 0, - metric, - mss); - nmtstp_assert_ip4_route_exists(NULL, 1, DEVICE_NAME, 0, 0, metric, 0); - accept_signal(route_added); - - /* Add default route again */ - nmtstp_ip4_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0, - gateway, - 0, - metric, - mss); - accept_signals(route_changed, 0, 1); - - /* Test route listing */ - routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex); - memset(rts, 0, sizeof(rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[0].network = gateway; - rts[0].plen = 32; - rts[0].ifindex = ifindex; - rts[0].gateway = INADDR_ANY; - rts[0].metric = metric; - rts[0].mss = mss; - rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK); - rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[1].network = network; - rts[1].plen = plen; - rts[1].ifindex = ifindex; - rts[1].gateway = gateway; - rts[1].metric = metric; - rts[1].mss = mss; - rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); - rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[2].network = 0; - rts[2].plen = 0; - rts[2].ifindex = ifindex; - rts[2].gateway = gateway; - rts[2].metric = metric; - rts[2].mss = mss; - rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); - g_assert_cmpint(routes->len, ==, 3); - nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata, - rts, - routes->len, - TRUE); - g_ptr_array_unref(routes); - - /* Remove route */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, network, plen, metric)); - nmtstp_assert_ip4_route_exists(NULL, 0, DEVICE_NAME, network, plen, metric, 0); - accept_signal(route_removed); - - /* Remove route again */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, network, plen, metric)); - - /* Remove default route */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, 0, 0, metric)); - accept_signal(route_removed); - - /* Remove route to gateway */ - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, gateway, 32, metric)); - accept_signal(route_removed); - - free_signal(route_added); - free_signal(route_changed); - free_signal(route_removed); -} - -static void -test_ip6_route(void) -{ - int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - SignalData * route_added = add_signal(NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - ip6_route_callback); - SignalData * route_changed = add_signal(NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_CHANGED, - ip6_route_callback); - SignalData * route_removed = add_signal(NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, - NM_PLATFORM_SIGNAL_REMOVED, - ip6_route_callback); - GPtrArray * routes; - NMPlatformIP6Route rts[3]; - struct in6_addr network; - guint8 plen = 64; - struct in6_addr gateway, pref_src; - /* Choose a high metric so that we hopefully don't conflict. */ - const int metric = 22987; - int mss = 1000; - - inet_pton(AF_INET6, "2001:db8:a:b:0:0:0:0", &network); - inet_pton(AF_INET6, "2001:db8:c:d:1:2:3:4", &gateway); - inet_pton(AF_INET6, "::42", &pref_src); - - g_assert(nm_platform_ip6_address_add(NM_PLATFORM_GET, - ifindex, - pref_src, - 128, - in6addr_any, - NM_PLATFORM_LIFETIME_PERMANENT, - NM_PLATFORM_LIFETIME_PERMANENT, - 0)); - accept_signals(route_added, 0, 3); - - _wait_for_ipv6_addr_non_tentative(NM_PLATFORM_GET, 200, ifindex, 1, &pref_src); - - /* Add route to gateway */ - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - gateway, - 128, - in6addr_any, - in6addr_any, - metric, - mss); - accept_signals(route_added, 0, 3); - - /* Add route */ - g_assert(!nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network, - plen, - gateway, - pref_src, - metric, - mss); - g_assert(nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); - accept_signal(route_added); - - /* Add route again */ - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - network, - plen, - gateway, - pref_src, - metric, - mss); - accept_signals(route_changed, 0, 1); - - /* Add default route */ - g_assert(!nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - in6addr_any, - 0, - gateway, - in6addr_any, - metric, - mss); - g_assert(nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); - accept_signal(route_added); - - /* Add default route again */ - nmtstp_ip6_route_add(NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - in6addr_any, - 0, - gateway, - in6addr_any, - metric, - mss); - accept_signals(route_changed, 0, 1); - - /* Test route listing */ - routes = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, ifindex); - memset(rts, 0, sizeof(rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[0].network = gateway; - rts[0].plen = 128; - rts[0].ifindex = ifindex; - rts[0].gateway = in6addr_any; - rts[0].pref_src = in6addr_any; - rts[0].metric = metric; - rts[0].mss = mss; - rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[1].network = network; - rts[1].plen = plen; - rts[1].ifindex = ifindex; - rts[1].gateway = gateway; - rts[1].pref_src = pref_src; - rts[1].metric = metric; - rts[1].mss = mss; - rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[2].network = in6addr_any; - rts[2].plen = 0; - rts[2].ifindex = ifindex; - rts[2].gateway = gateway; - rts[2].pref_src = in6addr_any; - rts[2].metric = metric; - rts[2].mss = mss; - g_assert_cmpint(routes->len, ==, 3); - nmtst_platform_ip6_routes_equal_aptr((const NMPObject *const *) routes->pdata, - rts, - routes->len, - TRUE); - g_ptr_array_unref(routes); - - /* Remove route */ - g_assert(nmtstp_platform_ip6_route_delete(NM_PLATFORM_GET, ifindex, network, plen, metric)); - g_assert(!nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); - accept_signal(route_removed); - - /* Remove route again */ - g_assert(nmtstp_platform_ip6_route_delete(NM_PLATFORM_GET, ifindex, network, plen, metric)); - - /* Remove default route */ - g_assert(nmtstp_platform_ip6_route_delete(NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); - accept_signal(route_removed); - - /* Remove route to gateway */ - g_assert(nmtstp_platform_ip6_route_delete(NM_PLATFORM_GET, ifindex, gateway, 128, metric)); - accept_signal(route_removed); - - free_signal(route_added); - free_signal(route_changed); - free_signal(route_removed); -} - -/*****************************************************************************/ - -static void -test_ip4_route_get(void) -{ - int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - in_addr_t a; - int result; - nm_auto_nmpobj NMPObject *route = NULL; - const NMPlatformIP4Route *r; - - nmtstp_run_command_check("ip route add 1.2.3.0/24 dev %s", DEVICE_NAME); - - NMTST_WAIT_ASSERT(100, { - nmtstp_wait_for_signal(NM_PLATFORM_GET, 10); - if (nmtstp_ip4_route_get(NM_PLATFORM_GET, - ifindex, - nmtst_inet4_from_string("1.2.3.0"), - 24, - 0, - 0)) - break; - }); - - a = nmtst_inet4_from_string("1.2.3.1"); - result = nm_platform_ip_route_get(NM_PLATFORM_GET, - AF_INET, - &a, - nmtst_get_rand_uint32() % 2 ? 0 : ifindex, - &route); - - g_assert(NMTST_NM_ERR_SUCCESS(result)); - g_assert(NMP_OBJECT_GET_TYPE(route) == NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert(!NMP_OBJECT_IS_STACKINIT(route)); - g_assert(route->parent._ref_count == 1); - r = NMP_OBJECT_CAST_IP4_ROUTE(route); - g_assert(NM_FLAGS_HAS(r->r_rtm_flags, RTM_F_CLONED)); - g_assert(r->ifindex == ifindex); - g_assert(r->network == a); - g_assert(r->plen == 32); - - nmtstp_run_command_check("ip route flush dev %s", DEVICE_NAME); - - nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); -} - -static void -test_ip4_zero_gateway(void) -{ - int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - - nmtstp_run_command_check("ip route add 1.2.3.1/32 via 0.0.0.0 dev %s", DEVICE_NAME); - nmtstp_run_command_check("ip route add 1.2.3.2/32 dev %s", DEVICE_NAME); - - NMTST_WAIT_ASSERT(100, { - nmtstp_wait_for_signal(NM_PLATFORM_GET, 10); - if (nmtstp_ip4_route_get(NM_PLATFORM_GET, - ifindex, - nmtst_inet4_from_string("1.2.3.1"), - 32, - 0, - 0) - && nmtstp_ip4_route_get(NM_PLATFORM_GET, - ifindex, - nmtst_inet4_from_string("1.2.3.2"), - 32, - 0, - 0)) - break; - }); - - nmtstp_run_command_check("ip route flush dev %s", DEVICE_NAME); - - nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); -} - -static void -test_ip4_route_options(gconstpointer test_data) -{ - const int TEST_IDX = GPOINTER_TO_INT(test_data); - const int IFINDEX = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - gs_unref_ptrarray GPtrArray *routes = NULL; -#define RTS_MAX 3 - NMPlatformIP4Route rts_add[RTS_MAX] = {}; - NMPlatformIP4Route rts_cmp[RTS_MAX] = {}; - NMPlatformIP4Address addr[1] = {}; - guint i; - guint rts_n = 0; - guint addr_n = 0; - - switch (TEST_IDX) { - case 1: - rts_add[rts_n++] = ((NMPlatformIP4Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string("172.16.1.0"), - .plen = 24, - .metric = 20, - .tos = 0x28, - .window = 10000, - .cwnd = 16, - .initcwnd = 30, - .initrwnd = 50, - .mtu = 1350, - .lock_cwnd = TRUE, - }); - break; - case 2: - addr[addr_n++] = ((NMPlatformIP4Address){ - .ifindex = IFINDEX, - .address = nmtst_inet4_from_string("172.16.1.5"), - .peer_address = nmtst_inet4_from_string("172.16.1.5"), - .plen = 24, - .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, - .preferred = NM_PLATFORM_LIFETIME_PERMANENT, - .n_ifa_flags = 0, - }); - rts_add[rts_n++] = ((NMPlatformIP4Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string("172.17.1.0"), - .gateway = nmtst_inet4_from_string("172.16.1.1"), - .plen = 24, - .metric = 20, - }); - rts_add[rts_n++] = ((NMPlatformIP4Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string("172.19.1.0"), - .gateway = nmtst_inet4_from_string("172.18.1.1"), - .r_rtm_flags = RTNH_F_ONLINK, - .plen = 24, - .metric = 20, - }); - break; - default: - g_assert_not_reached(); - break; - } - g_assert(rts_n <= G_N_ELEMENTS(rts_add)); - g_assert(addr_n <= G_N_ELEMENTS(addr)); - - for (i = 0; i < addr_n; i++) { - const NMPlatformIP4Address *a = &addr[i]; - - g_assert(a->ifindex == IFINDEX); - g_assert(nm_platform_ip4_address_add( - NM_PLATFORM_GET, - a->ifindex, - a->address, - a->plen, - a->peer_address, - nm_platform_ip4_broadcast_address_create(a->address, a->plen), - a->lifetime, - a->preferred, - a->n_ifa_flags, - a->label)); - if (a->peer_address == a->address) - _wait_for_ipv4_addr_device_route(NM_PLATFORM_GET, 200, a->ifindex, a->address, a->plen); - } - - for (i = 0; i < rts_n; i++) - g_assert(NMTST_NM_ERR_SUCCESS( - nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]))); - - for (i = 0; i < rts_n; i++) { - rts_cmp[i] = rts_add[i]; - nm_platform_ip_route_normalize(AF_INET, NM_PLATFORM_IP_ROUTE_CAST(&rts_cmp[i])); - } - - routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, IFINDEX); - g_assert_cmpint(routes->len, ==, rts_n); - nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata, - rts_cmp, - routes->len, - TRUE); - - for (i = 0; i < rts_n; i++) { - g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, - IFINDEX, - rts_add[i].network, - rts_add[i].plen, - rts_add[i].metric)); - } -#undef RTS_MAX -} - -static void -test_ip6_route_get(void) -{ - int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - const struct in6_addr *a; - int result; - nm_auto_nmpobj NMPObject *route = NULL; - const NMPlatformIP6Route *r; - - nmtstp_run_command_check("ip -6 route add fd01:abcd::/64 via fe80::99 dev %s", DEVICE_NAME); - - NMTST_WAIT_ASSERT(100, { - nmtstp_wait_for_signal(NM_PLATFORM_GET, 10); - if (nmtstp_ip6_route_get(NM_PLATFORM_GET, - ifindex, - nmtst_inet6_from_string("fd01:abcd::"), - 64, - NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, - NULL, - 0)) - break; - }); - - a = nmtst_inet6_from_string("fd01:abcd::42"); - result = nm_platform_ip_route_get(NM_PLATFORM_GET, - AF_INET6, - a, - nmtst_get_rand_uint32() % 2 ? 0 : ifindex, - &route); - - g_assert(NMTST_NM_ERR_SUCCESS(result)); - g_assert(NMP_OBJECT_GET_TYPE(route) == NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert(!NMP_OBJECT_IS_STACKINIT(route)); - g_assert(route->parent._ref_count == 1); - r = NMP_OBJECT_CAST_IP6_ROUTE(route); - g_assert(r->ifindex == ifindex); - nmtst_assert_ip6_address(&r->network, "fd01:abcd::42"); - g_assert_cmpint(r->plen, ==, 128); - nmtst_assert_ip6_address(&r->gateway, "fe80::99"); - - nmtstp_run_command_check("ip -6 route flush dev %s", DEVICE_NAME); - - nmtstp_wait_for_signal(NM_PLATFORM_GET, 50); -} - -static void -test_ip6_route_options(gconstpointer test_data) -{ - const int TEST_IDX = GPOINTER_TO_INT(test_data); - const int IFINDEX = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - gs_unref_ptrarray GPtrArray *routes = NULL; -#define RTS_MAX 3 - NMPlatformIP6Route rts_add[RTS_MAX] = {}; - NMPlatformIP6Route rts_cmp[RTS_MAX] = {}; - NMPlatformIP6Address addr[1] = {}; - struct in6_addr addr_in6[G_N_ELEMENTS(addr)] = {}; - guint rts_n = 0; - guint addr_n = 0; - guint i; - - switch (TEST_IDX) { - case 1: - rts_add[rts_n++] = ((NMPlatformIP6Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = *nmtst_inet6_from_string("2001:db8:a:b:0:0:0:0"), - .plen = 64, - .gateway = in6addr_any, - .metric = 1024, - .window = 20000, - .cwnd = 8, - .initcwnd = 22, - .initrwnd = 33, - .mtu = 1300, - .lock_mtu = TRUE, - }); - break; - case 2: - addr[addr_n++] = ((NMPlatformIP6Address){ - .ifindex = IFINDEX, - .address = *nmtst_inet6_from_string("2000::2"), - .plen = 128, - .peer_address = in6addr_any, - .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, - .preferred = NM_PLATFORM_LIFETIME_PERMANENT, - .n_ifa_flags = 0, - }); - rts_add[rts_n++] = ((NMPlatformIP6Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = *nmtst_inet6_from_string("1010::1"), - .plen = 128, - .gateway = in6addr_any, - .metric = 256, - .pref_src = *nmtst_inet6_from_string("2000::2"), - }); - break; - case 3: - addr[addr_n++] = ((NMPlatformIP6Address){ - .ifindex = IFINDEX, - .address = *nmtst_inet6_from_string("2001:db8:8086::5"), - .plen = 128, - .peer_address = in6addr_any, - .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, - .preferred = NM_PLATFORM_LIFETIME_PERMANENT, - .n_ifa_flags = 0, - }); - rts_add[rts_n++] = ((NMPlatformIP6Route){ - .ifindex = IFINDEX, - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string("2001:db8:8086::"), - .plen = 110, - .metric = 10021, - .mss = 0, - }); - rts_add[rts_n++] = ((NMPlatformIP6Route){ - .ifindex = IFINDEX, - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string("2001:db8:abad:c0de::"), - .plen = 64, - .gateway = *nmtst_inet6_from_string("2001:db8:8086::1"), - .metric = 21, - .mss = 0, - }); - break; - default: - g_assert_not_reached(); - } - g_assert(rts_n <= G_N_ELEMENTS(rts_add)); - g_assert(addr_n <= G_N_ELEMENTS(addr)); - - for (i = 0; i < addr_n; i++) { - g_assert(addr[i].ifindex == IFINDEX); - addr_in6[i] = addr[i].address; - g_assert(nm_platform_ip6_address_add(NM_PLATFORM_GET, - IFINDEX, - addr[i].address, - addr[i].plen, - addr[i].peer_address, - addr[i].lifetime, - addr[i].preferred, - addr[i].n_ifa_flags)); - } - _wait_for_ipv6_addr_non_tentative(NM_PLATFORM_GET, 400, IFINDEX, addr_n, addr_in6); - - for (i = 0; i < rts_n; i++) - g_assert(NMTST_NM_ERR_SUCCESS( - nm_platform_ip6_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]))); - - for (i = 0; i < rts_n; i++) { - rts_cmp[i] = rts_add[i]; - nm_platform_ip_route_normalize(AF_INET6, NM_PLATFORM_IP_ROUTE_CAST(&rts_cmp[i])); - } - - routes = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, IFINDEX); - g_assert_cmpint(routes->len, ==, rts_n); - nmtst_platform_ip6_routes_equal_aptr((const NMPObject *const *) routes->pdata, - rts_cmp, - routes->len, - TRUE); - - for (i = 0; i < rts_n; i++) { - g_assert(nmtstp_platform_ip6_route_delete(NM_PLATFORM_GET, - IFINDEX, - rts_add[i].network, - rts_add[i].plen, - rts_add[i].metric)); - } - - for (i = 0; i < addr_n; i++) { - nmtstp_ip6_address_del(NM_PLATFORM_GET, EX, IFINDEX, rts_add[i].network, rts_add[i].plen); - } -#undef RTS_MAX -} - -/*****************************************************************************/ - -static void -test_ip(gconstpointer test_data) -{ - const int TEST_IDX = GPOINTER_TO_INT(test_data); - const int IFINDEX = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - guint i, j, k; - const NMPlatformLink *l; - char ifname[IFNAMSIZ]; - char ifname2[IFNAMSIZ]; - char s1[NM_UTILS_INET_ADDRSTRLEN]; - NMPlatform * platform = NM_PLATFORM_GET; - const int EX_ = -1; - struct { - int ifindex; - } iface_data[10] = { - {0}, - }; - int order_idx[G_N_ELEMENTS(iface_data)] = {0}; - guint order_len; - guint try; - - for (i = 0; i < G_N_ELEMENTS(iface_data); i++) { - nm_sprintf_buf(ifname, "v%02u", i); - nm_sprintf_buf(ifname2, "w%02u", i); - - g_assert(!nm_platform_link_get_by_ifname(platform, ifname)); - g_assert(!nm_platform_link_get_by_ifname(platform, ifname2)); - l = nmtstp_link_veth_add(platform, EX_, ifname, ifname2); - iface_data[i].ifindex = l->ifindex; - - nmtstp_link_set_updown(platform, EX_, iface_data[i].ifindex, TRUE); - nmtstp_link_set_updown(platform, - EX_, - nmtstp_link_get(platform, -1, ifname2)->ifindex, - TRUE); - - nm_sprintf_buf(s1, "192.168.7.%d", 100 + i); - nmtstp_ip4_address_add(platform, - EX_, - iface_data[i].ifindex, - nmtst_inet4_from_string(s1), - 24, - nmtst_inet4_from_string(s1), - 3600, - 3600, - 0, - NULL); - } - - order_len = 0; - for (try = 0; try < 5 * G_N_ELEMENTS(order_idx); try++) { - NMPObject o; - NMPlatformIP4Route * r; - guint idx; - const NMDedupMultiHeadEntry *head_entry; - NMPLookup lookup; - - nmp_object_stackinit(&o, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); - r = NMP_OBJECT_CAST_IP4_ROUTE(&o); - r->network = nmtst_inet4_from_string("192.168.9.0"); - r->plen = 24; - r->metric = 109; - - if (order_len == 0 - || (order_len < G_N_ELEMENTS(order_idx) && nmtst_get_rand_uint32() % 2)) { -again_find_idx: - idx = nmtst_get_rand_uint32() % G_N_ELEMENTS(iface_data); - for (i = 0; i < order_len; i++) { - if (order_idx[i] == idx) - goto again_find_idx; - } - order_idx[order_len++] = idx; - - r->ifindex = iface_data[idx].ifindex; - g_assert( - NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_APPEND, r))); - } else { - i = nmtst_get_rand_uint32() % order_len; - idx = order_idx[i]; - for (i++; i < order_len; i++) - order_idx[i - 1] = order_idx[i]; - order_len--; - - r->ifindex = iface_data[idx].ifindex; - g_assert(nm_platform_object_delete(platform, &o)); - } - - head_entry = - nm_platform_lookup(platform, - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_IP4_ROUTE)); - for (j = 0; j < G_N_ELEMENTS(iface_data); j++) { - gboolean has; - NMDedupMultiIter iter; - const NMPObject *o_cached; - - has = FALSE; - for (k = 0; k < order_len; k++) { - if (order_idx[k] == j) { - g_assert(!has); - has = TRUE; - } - } - - nmp_cache_iter_for_each (&iter, head_entry, &o_cached) { - const NMPlatformIP4Route *r_cached = NMP_OBJECT_CAST_IP4_ROUTE(o_cached); - - if (r_cached->ifindex != iface_data[j].ifindex || r_cached->metric != 109) - continue; - - g_assert(has); - has = FALSE; - } - g_assert(!has); - } - } - - for (i = 0; i < G_N_ELEMENTS(iface_data); i++) - g_assert(nm_platform_link_delete(platform, iface_data[i].ifindex)); - - (void) TEST_IDX; - (void) IFINDEX; -} - -/*****************************************************************************/ - -#define FRA_SUPPRESS_IFGROUP 13 -#define FRA_SUPPRESS_PREFIXLEN 14 -#define FRA_L3MDEV 19 -#define FRA_UID_RANGE 20 -#define FRA_PROTOCOL 21 -#define FRA_IP_PROTO 22 -#define FRA_SPORT_RANGE 23 -#define FRA_DPORT_RANGE 24 - -static const NMPObject * -_rule_find_by_priority(NMPlatform *platform, guint32 priority) -{ - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject * o; - const NMPObject * obj = NULL; - NMPLookup lookup; - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - head_entry = nm_platform_lookup(platform, &lookup); - nmp_cache_iter_for_each (&iter, head_entry, &o) { - if (NMP_OBJECT_CAST_ROUTING_RULE(o)->priority != priority) - continue; - g_assert(!obj); - obj = o; - } - return obj; -} - -static const NMPObject * -_rule_check_kernel_support_one(NMPlatform *platform, const NMPlatformRoutingRule *rr) -{ - nm_auto_nmpobj const NMPObject *obj = NULL; - int r; - - g_assert(!_rule_find_by_priority(platform, rr->priority)); - - r = nm_platform_routing_rule_add(platform, NMP_NLM_FLAG_ADD, rr); - g_assert_cmpint(r, ==, 0); - - obj = nmp_object_ref(_rule_find_by_priority(platform, rr->priority)); - g_assert(obj); - - r = nm_platform_object_delete(platform, obj); - g_assert_cmpint(r, ==, TRUE); - - g_assert(!_rule_find_by_priority(platform, rr->priority)); - - return g_steal_pointer(&obj); -} - -static gboolean -_rule_check_kernel_support(NMPlatform *platform, int attribute) -{ - static int support[] = { - [FRA_SUPPRESS_IFGROUP] = 0, - [FRA_SUPPRESS_PREFIXLEN] = 0, - [FRA_L3MDEV] = 0, - [FRA_UID_RANGE] = 0, - [FRA_PROTOCOL] = 0, - [FRA_IP_PROTO] = 0, - [FRA_SPORT_RANGE] = 0, - [FRA_DPORT_RANGE] = 0, - }; - const guint32 PROBE_PRORITY = 12033; - gboolean sup; - int i; - - g_assert(NM_IS_PLATFORM(platform)); - - if (attribute == -1) { - for (i = 0; i < G_N_ELEMENTS(support); i++) { - if (support[i] < 0) { - /* indicate that some test was skipped. */ - return FALSE; - } - } - return TRUE; - } - - g_assert(attribute >= 0 && attribute < G_N_ELEMENTS(support)); - - if (support[attribute] != 0) - return support[attribute] >= 0; - - switch (attribute) { - case FRA_SUPPRESS_IFGROUP: - { - nm_auto_nmpobj const NMPObject *obj = NULL; - const NMPlatformRoutingRule rr = { - .addr_family = AF_INET, - .priority = PROBE_PRORITY, - .suppress_ifgroup_inverse = ~((guint32) 1245), - }; - - obj = _rule_check_kernel_support_one(platform, &rr); - - sup = NMP_OBJECT_CAST_ROUTING_RULE(obj)->suppress_prefixlen_inverse - == rr.suppress_ifgroup_inverse; - break; - } - case FRA_SUPPRESS_PREFIXLEN: - { - nm_auto_nmpobj const NMPObject *obj = NULL; - const NMPlatformRoutingRule rr = { - .addr_family = AF_INET, - .priority = PROBE_PRORITY, - .suppress_prefixlen_inverse = ~((guint32) 1245), - }; - - obj = _rule_check_kernel_support_one(platform, &rr); - - sup = NMP_OBJECT_CAST_ROUTING_RULE(obj)->suppress_prefixlen_inverse - == rr.suppress_prefixlen_inverse; - break; - } - case FRA_L3MDEV: - { - nm_auto_nmpobj const NMPObject *obj = NULL; - const NMPlatformRoutingRule rr = { - .addr_family = AF_INET, - .priority = PROBE_PRORITY, - .l3mdev = TRUE, - }; - - obj = _rule_check_kernel_support_one(platform, &rr); - - sup = NMP_OBJECT_CAST_ROUTING_RULE(obj)->l3mdev != 0; - break; - } - case FRA_UID_RANGE: - { - nm_auto_nmpobj const NMPObject *obj = NULL; - const NMPlatformRoutingRule rr = { - .addr_family = AF_INET, - .priority = PROBE_PRORITY, - .uid_range = - { - .start = 0, - .end = 0, - }, - .uid_range_has = TRUE, - }; - - obj = _rule_check_kernel_support_one(platform, &rr); - - sup = NMP_OBJECT_CAST_ROUTING_RULE(obj)->uid_range_has; - break; - } - case FRA_PROTOCOL: - { - nm_auto_nmpobj const NMPObject *obj = NULL; - const NMPlatformRoutingRule rr = { - .addr_family = AF_INET, - .priority = PROBE_PRORITY, - .protocol = 30, - }; - - obj = _rule_check_kernel_support_one(platform, &rr); - - sup = NMP_OBJECT_CAST_ROUTING_RULE(obj)->protocol == 30; - break; - } - case FRA_IP_PROTO: - { - nm_auto_nmpobj const NMPObject *obj = NULL; - const NMPlatformRoutingRule rr = { - .addr_family = AF_INET, - .priority = PROBE_PRORITY, - .ip_proto = 30, - }; - - obj = _rule_check_kernel_support_one(platform, &rr); - - sup = NMP_OBJECT_CAST_ROUTING_RULE(obj)->ip_proto == 30; - break; - } - case FRA_SPORT_RANGE: - case FRA_DPORT_RANGE: - /* these were added at the same time as FRA_IP_PROTO. */ - sup = _rule_check_kernel_support(platform, FRA_IP_PROTO); - break; - default: - g_assert_not_reached(); - return FALSE; - } - - support[attribute] = sup ? 1 : -1; - - _LOGD("kernel support for routing rule attribute #%d %s", - attribute, - sup ? "detected" : "not detected"); - return sup; -} - -static const NMPObject * -_platform_has_routing_rule(NMPlatform *platform, const NMPObject *obj) -{ - const NMPObject *o; - - g_assert(NM_IS_PLATFORM(platform)); - g_assert(NMP_OBJECT_IS_VALID(obj)); - g_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_ROUTING_RULE); - - o = nm_platform_lookup_obj(platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj); - if (o) - g_assert(nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(obj), - NMP_OBJECT_CAST_ROUTING_RULE(o), - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) - == 0); - - return o; -} - -static guint32 -_rr_rand_choose_u32(guint32 p) -{ - /* mostly, we just return zero. We want that each rule only has few - * fields set -- having most fields at zero. */ - if ((p % 10000u) < 7500u) - return 0; - - /* give 0xFFFFFFFFu extra probability. */ - if ((p % 10000u) < 8250u) - return 0xFFFFFFFFu; - - /* choose a small number. */ - if ((p % 10000u) < 9125u) - return (~p) % 10; - - /* finally, full random number. */ - return ~p; -} - -#define _rr_rand_choose_u8(p) ((guint8) _rr_rand_choose_u32((p))) - -static const NMPObject * -_rule_create_random(NMPlatform *platform) -{ - NMPObject * obj; - NMPlatformRoutingRule *rr; - guint32 p; - int addr_size; - guint i; - char saddr[NM_UTILS_INET_ADDRSTRLEN]; - static struct { - guint32 uid; - guint32 euid; - bool initialized; - } uids; - - if (G_UNLIKELY(!uids.initialized)) { - uids.uid = getuid(); - uids.euid = geteuid(); - uids.initialized = TRUE; - } - - obj = nmp_object_new(NMP_OBJECT_TYPE_ROUTING_RULE, NULL); - rr = NMP_OBJECT_CAST_ROUTING_RULE(obj); - - rr->addr_family = nmtst_rand_select(AF_INET, AF_INET6); - - addr_size = nm_utils_addr_family_to_size(rr->addr_family); - - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 50) - rr->priority = 10000 + ((~p) % 20u); - - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 40) - nm_sprintf_buf(rr->iifname, "t-iif-%u", (~p) % 20); - else if ((p % 1000u) < 80) - nm_sprintf_buf(rr->iifname, "%s", DEVICE_NAME); - - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 40) - nm_sprintf_buf(rr->oifname, "t-oif-%d", (~p) % 20); - else if ((p % 1000u) < 80) - nm_sprintf_buf(rr->oifname, "%s", DEVICE_NAME); - - for (i = 0; i < 2; i++) { - NMIPAddr *p_addr = i ? &rr->src : &rr->dst; - guint8 * p_len = i ? &rr->src_len : &rr->dst_len; - - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 100) { - /* if we set src_len/dst_len to zero, the src/dst is actually ignored. - * - * For fuzzying, still set the address. It shall have no further effect. - * */ - *p_len = (~p) % (addr_size * 8 + 1); - p = nmtst_get_rand_uint32(); - if ((p % 3u) == 0) { - if (rr->addr_family == AF_INET) - p_addr->addr4 = - nmtst_inet4_from_string(nm_sprintf_buf(saddr, "192.192.5.%u", (~p) % 256u)); - else - p_addr->addr6 = *nmtst_inet6_from_string( - nm_sprintf_buf(saddr, "1:2:3:4::f:%02x", (~p) % 256u)); - } else if ((p % 3u) == 1) - nmtst_rand_buf(NULL, p_addr, addr_size); - } - } - - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 50) - rr->tun_id = 10000 + ((~p) % 20); - -again_action: - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 500) - rr->action = FR_ACT_UNSPEC; - else if ((p % 1000u) < 750) - rr->action = (~p) % 12u; - else - rr->action = (~p) % 0x100u; - - rr->priority = _rr_rand_choose_u32(nmtst_get_rand_uint32()); - - if (rr->action == FR_ACT_GOTO && rr->priority == G_MAXINT32) - goto again_action; - - p = nmtst_get_rand_uint32(); - if ((p % 10000u) < 100) - rr->goto_target = rr->priority + 1; - else - rr->goto_target = _rr_rand_choose_u32(nmtst_get_rand_uint32()); - if (rr->action == FR_ACT_GOTO && rr->goto_target <= rr->priority) - goto again_action; - - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 25) { - if (_rule_check_kernel_support(platform, FRA_L3MDEV)) { - rr->l3mdev = TRUE; - rr->table = RT_TABLE_UNSPEC; - } - } - -again_table: - if (!rr->l3mdev) { - p = nmtst_get_rand_uint32(); - if ((p % 1000u) < 700) - rr->table = RT_TABLE_UNSPEC; - else if ((p % 1000u) < 850) - rr->table = RT_TABLE_MAIN; - else - rr->table = 10000 + ((~p) % 10); - if (rr->action == FR_ACT_TO_TBL && rr->table == RT_TABLE_UNSPEC) - goto again_table; - } - - rr->fwmark = _rr_rand_choose_u32(nmtst_get_rand_uint32()); - rr->fwmask = _rr_rand_choose_u32(nmtst_get_rand_uint32()); - - rr->flow = _rr_rand_choose_u32(nmtst_get_rand_uint32()); - - if (_rule_check_kernel_support(platform, FRA_PROTOCOL)) - rr->protocol = _rr_rand_choose_u8(nmtst_get_rand_uint32()); - -#define IPTOS_TOS_MASK 0x1E - -again_tos: - rr->tos = _rr_rand_choose_u8(nmtst_get_rand_uint32()); - if (rr->addr_family == AF_INET && rr->tos & ~IPTOS_TOS_MASK) - goto again_tos; - - if (_rule_check_kernel_support(platform, FRA_IP_PROTO)) - rr->ip_proto = _rr_rand_choose_u8(nmtst_get_rand_uint32()); - - if (_rule_check_kernel_support(platform, FRA_SUPPRESS_PREFIXLEN)) - rr->suppress_prefixlen_inverse = ~_rr_rand_choose_u32(nmtst_get_rand_uint32()); - - if (_rule_check_kernel_support(platform, FRA_SUPPRESS_IFGROUP)) - rr->suppress_ifgroup_inverse = ~_rr_rand_choose_u32(nmtst_get_rand_uint32()); - - if (_rule_check_kernel_support(platform, FRA_UID_RANGE)) { - p = nmtst_get_rand_uint32(); - rr->uid_range_has = (p % 10000u) < 200; - } - -again_uid_range: - rr->uid_range.start = nmtst_rand_select(0u, uids.uid, uids.euid); - rr->uid_range.end = nmtst_rand_select(0u, uids.uid, uids.euid); - if (rr->uid_range_has) { - if (rr->uid_range.end < rr->uid_range.start) - NM_SWAP(&rr->uid_range.start, &rr->uid_range.end); - if (rr->uid_range.start == ((guint32) -1) || rr->uid_range.end == ((guint32) -1)) - goto again_uid_range; - } - - for (i = 0; i < 2; i++) { - NMFibRulePortRange *range = i ? &rr->sport_range : &rr->dport_range; - int attribute = i ? FRA_SPORT_RANGE : FRA_DPORT_RANGE; - - if (!_rule_check_kernel_support(platform, attribute)) - continue; - - p = nmtst_get_rand_uint32(); - if ((p % 10000u) < 300) { - while (range->start == 0) { - p = p ^ nmtst_get_rand_uint32(); - range->start = nmtst_rand_select(1u, 0xFFFEu, ((p) % 0xFFFEu) + 1); - range->end = - nmtst_rand_select(1u, 0xFFFEu, ((p >> 16) % 0xFFFEu) + 1, range->start); - if (range->end < range->start) - NM_SWAP(&range->start, &range->end); - } - } - } - - p = nmtst_get_rand_uint32() % 1000u; - if (p < 100) - rr->flags |= FIB_RULE_INVERT; - - return obj; -} - -static gboolean -_rule_fuzzy_equal(const NMPObject *obj, const NMPObject *obj_comp, int op_type) -{ - const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE(obj); - NMPlatformRoutingRule rr_co = *NMP_OBJECT_CAST_ROUTING_RULE(obj_comp); - - switch (op_type) { - case RTM_NEWRULE: - /* when adding rules with RTM_NEWRULE, kernel checks whether an existing - * rule already exists and may fail with EEXIST. This check has issues - * and reject legitimate rules (rh#1686075). - * - * Work around that. */ - if (rr->src_len == 0) - rr_co.src_len = 0; - if (rr->dst_len == 0) - rr_co.dst_len = 0; - if (rr->flow == 0) - rr_co.flow = 0; - if (rr->tos == 0) - rr_co.tos = 0; - rr_co.suppress_prefixlen_inverse = rr->suppress_prefixlen_inverse; - rr_co.suppress_ifgroup_inverse = rr->suppress_ifgroup_inverse; - if (!NM_FLAGS_HAS(rr->flags, FIB_RULE_INVERT)) - rr_co.flags &= ~((guint32) FIB_RULE_INVERT); - else - rr_co.flags |= ((guint32) FIB_RULE_INVERT); - break; - case RTM_DELRULE: - /* when deleting a rule with RTM_DELRULE, kernel tries to find the - * candidate to delete. It might delete the wrong rule (rh#1685816). */ - if (rr->action == FR_ACT_UNSPEC) - rr_co.action = FR_ACT_UNSPEC; - if (rr->iifname[0] == '\0') - rr_co.iifname[0] = '\0'; - if (rr->oifname[0] == '\0') - rr_co.oifname[0] = '\0'; - if (rr->src_len == 0) - rr_co.src_len = 0; - if (rr->dst_len == 0) - rr_co.dst_len = 0; - if (rr->tun_id == 0) - rr_co.tun_id = 0; - if (rr->fwmark == 0) - rr_co.fwmark = 0; - if (rr->fwmask == 0) - rr_co.fwmask = 0; - if (rr->flow == 0) - rr_co.flow = 0; - if (rr->protocol == 0) - rr_co.protocol = 0; - if (rr->table == RT_TABLE_UNSPEC) - rr_co.table = RT_TABLE_UNSPEC; - if (rr->l3mdev == 0) - rr_co.l3mdev = 0; - if (rr->tos == 0) - rr_co.tos = 0; - if (rr->ip_proto == 0) - rr_co.ip_proto = 0; - rr_co.suppress_prefixlen_inverse = rr->suppress_prefixlen_inverse; - if (rr->suppress_ifgroup_inverse == 0) - rr_co.suppress_ifgroup_inverse = 0; - if (!rr->uid_range_has) - rr_co.uid_range_has = FALSE; - if (rr->sport_range.start == 0 && rr->sport_range.end == 0) { - rr_co.sport_range.start = 0; - rr_co.sport_range.end = 0; - } - if (rr->dport_range.start == 0 && rr->dport_range.end == 0) { - rr_co.dport_range.start = 0; - rr_co.dport_range.end = 0; - } - if (!NM_FLAGS_HAS(rr->flags, FIB_RULE_INVERT)) - rr_co.flags &= ~((guint32) FIB_RULE_INVERT); - else - rr_co.flags |= ((guint32) FIB_RULE_INVERT); - break; - default: - g_assert_not_reached(); - break; - } - - return nm_platform_routing_rule_cmp(rr, &rr_co, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0; -} - -static void -test_rule(gconstpointer test_data) -{ - const int TEST_IDX = GPOINTER_TO_INT(test_data); - const gboolean TEST_SYNC = (TEST_IDX == 4); - gs_unref_ptrarray GPtrArray *objs = NULL; - gs_unref_ptrarray GPtrArray *objs_initial = NULL; - NMPlatform * platform = NM_PLATFORM_GET; - guint i, j, n; - int r; - gboolean had_an_issue_exist = FALSE; - - nm_platform_process_events(platform); - - objs_initial = nmtstp_platform_routing_rules_get_all(platform, AF_UNSPEC); - g_assert(objs_initial); - g_assert_cmpint(objs_initial->len, ==, 5); - - nmtstp_run_command_check("ip rule add table 766"); - nm_platform_process_events(platform); - - for (i = 6; i > 0; i--) { - gs_unref_ptrarray GPtrArray *objs_extern = NULL; - const NMPObject * obj; - - objs_extern = nmtstp_platform_routing_rules_get_all(platform, AF_UNSPEC); - - g_assert(objs_extern); - g_assert_cmpint(objs_extern->len, ==, i); - - if (TEST_IDX != 1) - nmtst_rand_perm(NULL, objs_extern->pdata, NULL, sizeof(gpointer), objs_extern->len); - - obj = objs_extern->pdata[0]; - - r = nm_platform_object_delete(platform, obj); - g_assert_cmpint(r, ==, TRUE); - - g_assert(!_platform_has_routing_rule(platform, obj)); - } - - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), ==, 0); - -#define RR(...) \ - nmp_object_new(NMP_OBJECT_TYPE_ROUTING_RULE, \ - (const NMPlatformObject *) &((NMPlatformRoutingRule){__VA_ARGS__})) - - objs = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - - g_ptr_array_add(objs, RR(.addr_family = AF_INET, .priority = 10, )); - - g_ptr_array_add( - objs, - RR(.addr_family = AF_INET, .priority = 400, .action = FR_ACT_GOTO, .goto_target = 10000, )); - - g_ptr_array_add(objs, RR(.addr_family = AF_INET6, )); - - g_ptr_array_add(objs, - RR(.addr_family = AF_INET6, .action = FR_ACT_TO_TBL, .table = RT_TABLE_MAIN, )); - - g_ptr_array_add(objs, RR(.addr_family = AF_INET6, .priority = 30, )); - - g_ptr_array_add(objs, RR(.addr_family = AF_INET6, .priority = 50, .iifname = "t-iif-1", )); - - g_ptr_array_add(objs, RR(.addr_family = AF_INET6, .priority = 50, .iifname = "t-oif-1", )); - - g_ptr_array_add(objs, RR(.addr_family = AF_INET, .priority = 50, .iifname = "t-oif-2", )); - - g_ptr_array_add(objs, RR(.addr_family = AF_INET, .priority = 51, .iifname = DEVICE_NAME, )); - - if (TEST_IDX == 1) { - g_ptr_array_add(objs, RR(.addr_family = AF_INET, .table = 10000, )); - } - - if (TEST_IDX != 1) { - nmtst_rand_perm(NULL, objs->pdata, NULL, sizeof(gpointer), objs->len); - g_ptr_array_set_size(objs, nmtst_get_rand_uint32() % (objs->len + 1)); - } - - n = (TEST_IDX != 1) ? nmtst_get_rand_uint32() % 50u : 0u; - for (i = 0; i < n; i++) { - nm_auto_nmpobj const NMPObject *o = NULL; - guint try = 0; - -again: - o = _rule_create_random(platform); - for (j = 0; j < objs->len; j++) { - if (nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(o), - NMP_OBJECT_CAST_ROUTING_RULE(objs->pdata[j]), - NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) - == 0) { - try++; - g_assert(try < 200); - nm_clear_pointer(&o, nmp_object_unref); - goto again; - } - } - g_ptr_array_add(objs, (gpointer) g_steal_pointer(&o)); - } - - if (TEST_IDX != 1) - nmtst_rand_perm(NULL, objs->pdata, NULL, sizeof(gpointer), objs->len); - - if (TEST_SYNC) { - gs_unref_hashtable GHashTable *unique_priorities = g_hash_table_new(NULL, NULL); - nm_auto_unref_rules_manager NMPRulesManager *rules_manager = - nmp_rules_manager_new(platform); - gs_unref_ptrarray GPtrArray *objs_sync = NULL; - gconstpointer USER_TAG_1 = &platform; - gconstpointer USER_TAG_2 = &unique_priorities; - - objs_sync = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - - /* ensure that priorities are unique. Otherwise, it confuses the test, because - * kernel may wrongly be unable to add/delete routes based on a wrong match - * (rh#1685816, rh#1685816). */ - for (i = 0; i < objs->len; i++) { - const NMPObject *obj = objs->pdata[i]; - guint32 prio = NMP_OBJECT_CAST_ROUTING_RULE(obj)->priority; - - if (!NM_IN_SET(prio, 0, 32766, 32767) - && !g_hash_table_contains(unique_priorities, GUINT_TO_POINTER(prio))) { - g_hash_table_add(unique_priorities, GUINT_TO_POINTER(prio)); - g_ptr_array_add(objs_sync, (gpointer) nmp_object_ref(obj)); - } - } - - for (i = 0; i < objs_sync->len; i++) { - nmp_rules_manager_track(rules_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - 1, - USER_TAG_1, - NULL); - if (nmtst_get_rand_bool()) { - /* this has no effect, because a negative priority (of same absolute value) - * has lower priority than the positive priority above. */ - nmp_rules_manager_track(rules_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - -1, - USER_TAG_2, - NULL); - } - if (nmtst_get_rand_uint32() % objs_sync->len == 0) { - nmp_rules_manager_sync(rules_manager, FALSE); - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), - ==, - i + 1); - } - } - - nmp_rules_manager_sync(rules_manager, FALSE); - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), - ==, - objs_sync->len); - - for (i = 0; i < objs_sync->len; i++) { - switch (nmtst_get_rand_uint32() % 3) { - case 0: - nmp_rules_manager_untrack(rules_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - USER_TAG_1); - nmp_rules_manager_untrack(rules_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - USER_TAG_1); - break; - case 1: - nmp_rules_manager_track(rules_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - -1, - USER_TAG_1, - NULL); - break; - case 2: - nmp_rules_manager_track(rules_manager, - NMP_OBJECT_CAST_ROUTING_RULE(objs_sync->pdata[i]), - -2, - USER_TAG_2, - NULL); - break; - } - if (nmtst_get_rand_uint32() % objs_sync->len == 0) { - nmp_rules_manager_sync(rules_manager, FALSE); - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), - ==, - objs_sync->len - i - 1); - } - } - - nmp_rules_manager_sync(rules_manager, FALSE); - - } else { - for (i = 0; i < objs->len;) { - const NMPObject *obj = objs->pdata[i]; - - for (j = 0; j < objs->len; j++) - g_assert((j < i) == (!!_platform_has_routing_rule(platform, objs->pdata[j]))); - - r = nm_platform_routing_rule_add(platform, - NMP_NLM_FLAG_ADD, - NMP_OBJECT_CAST_ROUTING_RULE(obj)); - - if (r == -EEXIST) { - g_assert(!_platform_has_routing_rule(platform, obj)); - /* this should not happen, but there are bugs in kernel (rh#1686075). */ - for (j = 0; j < i; j++) { - const NMPObject *obj2 = objs->pdata[j]; - - g_assert(_platform_has_routing_rule(platform, obj2)); - - if (_rule_fuzzy_equal(obj, obj2, RTM_NEWRULE)) { - r = 0; - break; - } - } - if (r == 0) { - /* OK, the rule is shadowed by another rule, and kernel does not allow - * us to add this one (rh#1686075). Drop this from the test. */ - g_ptr_array_remove_index(objs, i); - had_an_issue_exist = TRUE; - continue; - } - } - - if (r != 0) { - NMPLookup lookup; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject * o; - - g_print(">>> failing... errno=%d, rule=%s\n", - r, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - head_entry = nm_platform_lookup(platform, &lookup); - nmp_cache_iter_for_each (&iter, head_entry, &o) { - char ch = ' '; - - if (NMP_OBJECT_CAST_ROUTING_RULE(o)->addr_family - == NMP_OBJECT_CAST_ROUTING_RULE(obj)->addr_family - && NMP_OBJECT_CAST_ROUTING_RULE(o)->priority - == NMP_OBJECT_CAST_ROUTING_RULE(obj)->priority) - ch = '*'; - g_print(">>> existing rule: %c %s\n", - ch, - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - } - - nmtstp_run_command_check("ip rule"); - nmtstp_run_command_check("ip -6 rule"); - g_assert_cmpint(r, ==, 0); - } - - g_assert(_platform_has_routing_rule(platform, obj)); - - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), - ==, - i + 1); - - i++; - } - - if (TEST_IDX != 1) - nmtst_rand_perm(NULL, objs->pdata, NULL, sizeof(gpointer), objs->len); - - if (_LOGD_ENABLED()) { - nmtstp_run_command_check("ip rule"); - nmtstp_run_command_check("ip -6 rule"); - } - - for (i = 0; i < objs->len; i++) { - const NMPObject *obj = objs->pdata[i]; - const NMPObject *obj2; - - for (j = 0; j < objs->len; j++) - g_assert((j < i) == (!_platform_has_routing_rule(platform, objs->pdata[j]))); - - g_assert(_platform_has_routing_rule(platform, obj)); - - r = nm_platform_object_delete(platform, obj); - g_assert_cmpint(r, ==, TRUE); - - obj2 = _platform_has_routing_rule(platform, obj); - - if (obj2) { - guint k; - - /* When deleting a rule, kernel does a fuzzy match, ignoring for example: - * - action, if it is FR_ACT_UNSPEC - * - iifname,oifname if it is unspecified - * rh#1685816 - * - * That means, we may have deleted the wrong rule. Which one? */ - k = i; - for (j = i + 1; j < objs->len; j++) { - if (!_platform_has_routing_rule(platform, objs->pdata[j])) { - g_assert_cmpint(k, ==, i); - k = j; - } - } - g_assert_cmpint(k, >, i); - - if (!_rule_fuzzy_equal(obj, objs->pdata[k], RTM_DELRULE)) { - g_print(">>> failing...\n"); - g_print(">>> no fuzzy match between: %s\n", - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_print( - ">>> and: %s\n", - nmp_object_to_string(objs->pdata[k], NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_assert_not_reached(); - } - - objs->pdata[i] = objs->pdata[k]; - objs->pdata[k] = (gpointer) obj; - obj2 = NULL; - } - - g_assert(!obj2); - - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), - ==, - objs->len - i - 1); - } - } - - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), ==, 0); - - for (i = 0; i < objs_initial->len; i++) { - const NMPObject *obj = objs_initial->pdata[i]; - - for (j = 0; j < objs_initial->len; j++) - g_assert((j < i) == (!!_platform_has_routing_rule(platform, objs_initial->pdata[j]))); - - r = nm_platform_routing_rule_add(platform, - NMP_NLM_FLAG_ADD, - NMP_OBJECT_CAST_ROUTING_RULE(obj)); - g_assert_cmpint(r, ==, 0); - } - for (j = 0; j < objs_initial->len; j++) - g_assert(_platform_has_routing_rule(platform, objs_initial->pdata[j])); - g_assert_cmpint(nmtstp_platform_routing_rules_get_count(platform, AF_UNSPEC), - ==, - objs_initial->len); - - /* the tests passed as good as we could (as good as we implemented workarounds for them). - * Still, with this kernel, not all features were fully tested. Mark the test as skipped. */ - if (had_an_issue_exist) - g_test_skip("adding a rule failed with EEXIST although it should not (rh#1686075)"); - else if (!_rule_check_kernel_support(platform, -1)) - g_test_skip("some kernel features were not available and skipped for the test"); -} - -/*****************************************************************************/ - -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) -{ -#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE) -#define add_test_func_data(testpath, test_func, arg) \ - nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE) - add_test_func("/route/ip4", test_ip4_route); - add_test_func("/route/ip6", test_ip6_route); - add_test_func("/route/ip4_metric0", test_ip4_route_metric0); - add_test_func_data("/route/ip4_options/1", test_ip4_route_options, GINT_TO_POINTER(1)); - if (nmtstp_is_root_test()) - add_test_func_data("/route/ip4_options/2", test_ip4_route_options, GINT_TO_POINTER(2)); - add_test_func_data("/route/ip6_options/1", test_ip6_route_options, GINT_TO_POINTER(1)); - add_test_func_data("/route/ip6_options/2", test_ip6_route_options, GINT_TO_POINTER(2)); - add_test_func_data("/route/ip6_options/3", test_ip6_route_options, GINT_TO_POINTER(3)); - - if (nmtstp_is_root_test()) { - add_test_func_data("/route/ip/1", test_ip, GINT_TO_POINTER(1)); - add_test_func("/route/ip4_route_get", test_ip4_route_get); - add_test_func("/route/ip6_route_get", test_ip6_route_get); - add_test_func("/route/ip4_zero_gateway", test_ip4_zero_gateway); - } - - if (nmtstp_is_root_test()) { - add_test_func_data("/route/rule/1", test_rule, GINT_TO_POINTER(1)); - add_test_func_data("/route/rule/2", test_rule, GINT_TO_POINTER(2)); - add_test_func_data("/route/rule/3", test_rule, GINT_TO_POINTER(3)); - add_test_func_data("/route/rule/4", test_rule, GINT_TO_POINTER(4)); - } -} diff --git a/src/platform/tests/test-tc.c b/src/platform/tests/test-tc.c deleted file mode 100644 index 4cbe4f5d..00000000 --- a/src/platform/tests/test-tc.c +++ /dev/null @@ -1,222 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ - -#include "nm-default.h" - -#include <linux/pkt_sched.h> - -#include "nm-test-utils-core.h" -#include "platform/nmp-object.h" -#include "platform/nmp-netns.h" -#include "platform/nm-platform-utils.h" -#include "test-common.h" - -static NMPObject * -qdisc_new(int ifindex, const char *kind, guint32 parent) -{ - NMPObject *obj; - - obj = nmp_object_new(NMP_OBJECT_TYPE_QDISC, NULL); - obj->qdisc = (NMPlatformQdisc){ - .ifindex = ifindex, - .kind = kind, - .parent = parent, - }; - - return obj; -} - -static GPtrArray * -qdiscs_lookup(int ifindex) -{ - NMPLookup lookup; - - return nm_platform_lookup_clone(NM_PLATFORM_GET, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_QDISC, ifindex), - NULL, - NULL); -} - -static void -test_qdisc1(void) -{ - int ifindex; - gs_unref_ptrarray GPtrArray *known = NULL; - gs_unref_ptrarray GPtrArray *plat = NULL; - NMPObject * obj; - NMPlatformQdisc * qdisc; - - ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint(ifindex, >, 0); - - nmtstp_run_command("tc qdisc del dev %s root", DEVICE_NAME); - nmtstp_run_command_check("tc qdisc add dev %s root sfq", DEVICE_NAME); - - nmtstp_wait_for_signal(NM_PLATFORM_GET, 0); - - known = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - g_ptr_array_add(known, qdisc_new(ifindex, "fq_codel", TC_H_ROOT)); - g_ptr_array_add(known, qdisc_new(ifindex, "ingress", TC_H_INGRESS)); - - g_assert(nm_platform_qdisc_sync(NM_PLATFORM_GET, ifindex, known)); - plat = qdiscs_lookup(ifindex); - g_assert(plat); - g_assert_cmpint(plat->len, ==, 2); - - obj = plat->pdata[0]; - qdisc = NMP_OBJECT_CAST_QDISC(obj); - g_assert_cmpint(qdisc->parent, ==, TC_H_ROOT); - g_assert_cmpstr(qdisc->kind, ==, "fq_codel"); - - obj = plat->pdata[1]; - qdisc = NMP_OBJECT_CAST_QDISC(obj); - g_assert_cmpint(qdisc->parent, ==, TC_H_INGRESS); - g_assert_cmpstr(qdisc->kind, ==, "ingress"); -} - -static void -test_qdisc_fq_codel(void) -{ - int ifindex; - gs_unref_ptrarray GPtrArray *known = NULL; - gs_unref_ptrarray GPtrArray *plat = NULL; - NMPObject * obj; - NMPlatformQdisc * qdisc; - - ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint(ifindex, >, 0); - - nmtstp_run_command("tc qdisc del dev %s root", DEVICE_NAME); - - nmtstp_wait_for_signal(NM_PLATFORM_GET, 0); - - known = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - obj = qdisc_new(ifindex, "fq_codel", TC_H_ROOT); - obj->qdisc.handle = TC_H_MAKE(0x8142 << 16, 0); - obj->qdisc.fq_codel.limit = 2048; - obj->qdisc.fq_codel.flows = 64; - obj->qdisc.fq_codel.quantum = 1000; - g_ptr_array_add(known, obj); - - g_assert(nm_platform_qdisc_sync(NM_PLATFORM_GET, ifindex, known)); - plat = qdiscs_lookup(ifindex); - g_assert(plat); - g_assert_cmpint(plat->len, ==, 1); - - obj = plat->pdata[0]; - qdisc = NMP_OBJECT_CAST_QDISC(obj); - g_assert_cmpstr(qdisc->kind, ==, "fq_codel"); - g_assert_cmpint(qdisc->handle, ==, TC_H_MAKE(0x8142 << 16, 0)); - g_assert_cmpint(qdisc->parent, ==, TC_H_ROOT); - g_assert_cmpint(qdisc->fq_codel.limit, ==, 2048); - g_assert_cmpint(qdisc->fq_codel.flows, ==, 64); - g_assert_cmpint(qdisc->fq_codel.quantum, ==, 1000); -} - -static void -test_qdisc_sfq(void) -{ - int ifindex; - gs_unref_ptrarray GPtrArray *known = NULL; - gs_unref_ptrarray GPtrArray *plat = NULL; - NMPObject * obj; - NMPlatformQdisc * qdisc; - - ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint(ifindex, >, 0); - - nmtstp_run_command("tc qdisc del dev %s root", DEVICE_NAME); - - nmtstp_wait_for_signal(NM_PLATFORM_GET, 0); - - known = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - obj = qdisc_new(ifindex, "sfq", TC_H_ROOT); - obj->qdisc.handle = TC_H_MAKE(0x8143 << 16, 0); - obj->qdisc.sfq.perturb_period = 10; - obj->qdisc.sfq.quantum = 1540; - obj->qdisc.sfq.flows = 256; - g_ptr_array_add(known, obj); - - g_assert(nm_platform_qdisc_sync(NM_PLATFORM_GET, ifindex, known)); - plat = qdiscs_lookup(ifindex); - g_assert(plat); - g_assert_cmpint(plat->len, ==, 1); - - obj = plat->pdata[0]; - qdisc = NMP_OBJECT_CAST_QDISC(obj); - g_assert_cmpstr(qdisc->kind, ==, "sfq"); - g_assert_cmpint(qdisc->handle, ==, TC_H_MAKE(0x8143 << 16, 0)); - g_assert_cmpint(qdisc->parent, ==, TC_H_ROOT); - g_assert_cmpint(qdisc->sfq.perturb_period, ==, 10); - g_assert_cmpint(qdisc->sfq.quantum, ==, 1540); - g_assert_cmpint(qdisc->sfq.flows, ==, 256); -} - -static void -test_qdisc_tbf(void) -{ - int ifindex; - gs_unref_ptrarray GPtrArray *known = NULL; - gs_unref_ptrarray GPtrArray *plat = NULL; - NMPObject * obj; - NMPlatformQdisc * qdisc; - - ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); - g_assert_cmpint(ifindex, >, 0); - - nmtstp_run_command("tc qdisc del dev %s root", DEVICE_NAME); - - nmtstp_wait_for_signal(NM_PLATFORM_GET, 0); - - known = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - obj = qdisc_new(ifindex, "tbf", TC_H_ROOT); - obj->qdisc.handle = TC_H_MAKE(0x8143 << 16, 0); - obj->qdisc.tbf.rate = 1000000; - obj->qdisc.tbf.burst = 2000; - obj->qdisc.tbf.limit = 3000; - g_ptr_array_add(known, obj); - - obj = qdisc_new(ifindex, "sfq", TC_H_MAKE(0x8143 << 16, 0)); - obj->qdisc.handle = TC_H_MAKE(0x8005 << 16, 0); - g_ptr_array_add(known, obj); - - g_assert(nm_platform_qdisc_sync(NM_PLATFORM_GET, ifindex, known)); - plat = qdiscs_lookup(ifindex); - g_assert(plat); - g_assert_cmpint(plat->len, ==, 2); - - obj = plat->pdata[0]; - qdisc = NMP_OBJECT_CAST_QDISC(obj); - g_assert_cmpstr(qdisc->kind, ==, "tbf"); - g_assert_cmpint(qdisc->handle, ==, TC_H_MAKE(0x8143 << 16, 0)); - g_assert_cmpint(qdisc->parent, ==, TC_H_ROOT); - g_assert_cmpint(qdisc->tbf.rate, ==, 1000000); - g_assert_cmpint(qdisc->tbf.burst, ==, 2000); - g_assert_cmpint(qdisc->tbf.limit, ==, 3000); - - obj = plat->pdata[1]; - qdisc = NMP_OBJECT_CAST_QDISC(obj); - g_assert_cmpstr(qdisc->kind, ==, "sfq"); - g_assert_cmpint(qdisc->parent, ==, TC_H_MAKE(0x8143 << 16, 0)); - g_assert_cmpint(qdisc->handle, ==, TC_H_MAKE(0x8005 << 16, 0)); -} - -/*****************************************************************************/ - -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) -{ - if (nmtstp_is_root_test()) { - nmtstp_env1_add_test_func("/link/qdisc/1", test_qdisc1, TRUE); - nmtstp_env1_add_test_func("/link/qdisc/fq_codel", test_qdisc_fq_codel, TRUE); - nmtstp_env1_add_test_func("/link/qdisc/sfq", test_qdisc_sfq, TRUE); - nmtstp_env1_add_test_func("/link/qdisc/tbf", test_qdisc_tbf, TRUE); - } -} diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c deleted file mode 100644 index dddfa8fa..00000000 --- a/src/platform/wifi/nm-wifi-utils-nl80211.c +++ /dev/null @@ -1,1028 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2005 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - * Copyright (C) 2011 Intel Corporation. All rights reserved. - */ - -#include "nm-default.h" - -#include "nm-wifi-utils-nl80211.h" - -#include <sys/ioctl.h> -#include <net/ethernet.h> -#include <unistd.h> -#include <linux/nl80211.h> -#include <linux/if.h> - -#include "platform/nm-netlink.h" -#include "nm-wifi-utils-private.h" -#include "platform/nm-platform.h" -#include "platform/nm-platform-utils.h" -#include "nm-utils.h" - -#define _NMLOG_PREFIX_NAME "wifi-nl80211" -#define _NMLOG_DOMAIN LOGD_PLATFORM | LOGD_WIFI -#define _NMLOG(level, ...) \ - G_STMT_START \ - { \ - char _ifname_buf[IFNAMSIZ]; \ - const char *_ifname = \ - self ? nmp_utils_if_indextoname(self->parent.ifindex, _ifname_buf) : NULL; \ - \ - nm_log((level), \ - _NMLOG_DOMAIN, \ - _ifname ?: NULL, \ - NULL, \ - "%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - NM_PRINT_FMT_QUOTED(_ifname, " (", _ifname, ")", "") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - G_STMT_END - -typedef struct { - NMWifiUtils parent; - struct nl_sock *nl_sock; - guint32 * freqs; - int id; - int num_freqs; - int phy; - bool can_wowlan : 1; -} NMWifiUtilsNl80211; - -typedef struct { - NMWifiUtilsClass parent; -} NMWifiUtilsNl80211Class; - -G_DEFINE_TYPE(NMWifiUtilsNl80211, nm_wifi_utils_nl80211, NM_TYPE_WIFI_UTILS) - -static int -ack_handler(struct nl_msg *msg, void *arg) -{ - int *done = arg; - *done = 1; - return NL_STOP; -} - -static int -finish_handler(struct nl_msg *msg, void *arg) -{ - int *done = arg; - *done = 1; - return NL_SKIP; -} - -static int -error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) -{ - int *done = arg; - *done = err->error; - return NL_SKIP; -} - -static struct nl_msg * -_nl80211_alloc_msg(int id, int ifindex, int phy, guint32 cmd, guint32 flags) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - - msg = nlmsg_alloc(); - genlmsg_put(msg, 0, 0, id, 0, flags, cmd, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - if (phy != -1) - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy); - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static struct nl_msg * -nl80211_alloc_msg(NMWifiUtilsNl80211 *self, guint32 cmd, guint32 flags) -{ - return _nl80211_alloc_msg(self->id, self->parent.ifindex, self->phy, cmd, flags); -} - -static int -nl80211_send_and_recv(NMWifiUtilsNl80211 *self, - struct nl_msg * msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data) -{ - int err; - int done = 0; - const struct nl_cb cb = { - .err_cb = error_handler, - .err_arg = &done, - .finish_cb = finish_handler, - .finish_arg = &done, - .ack_cb = ack_handler, - .ack_arg = &done, - .valid_cb = valid_handler, - .valid_arg = valid_data, - }; - - g_return_val_if_fail(msg != NULL, -ENOMEM); - - err = nl_send_auto(self->nl_sock, msg); - if (err < 0) - return err; - - /* Loop until one of our NL callbacks says we're done; on success - * done will be 1, on error it will be < 0. - */ - while (!done) { - err = nl_recvmsgs(self->nl_sock, &cb); - if (err < 0 && err != -EAGAIN) { - /* Kernel scan list can change while we are dumping it, as new scan - * results from H/W can arrive. BSS info is assured to be consistent - * and we don't need consistent view of whole scan list. Hence do - * not warn on DUMP_INTR error for get scan command. - */ - if (err == -NME_NL_DUMP_INTR - && genlmsg_hdr(nlmsg_hdr(msg))->cmd == NL80211_CMD_GET_SCAN) - break; - - _LOGW("nl_recvmsgs() error: (%d) %s", err, nm_strerror(err)); - break; - } - } - - if (err >= 0 && done < 0) - err = done; - return err; -} - -static void -dispose(GObject *object) -{ - NMWifiUtilsNl80211 *self = NM_WIFI_UTILS_NL80211(object); - - nm_clear_g_free(&self->freqs); -} - -struct nl80211_iface_info { - NM80211Mode mode; -}; - -static int -nl80211_iface_info_handler(struct nl_msg *msg, void *arg) -{ - struct nl80211_iface_info *info = arg; - struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr * tb[NL80211_ATTR_MAX + 1]; - - if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) - return NL_SKIP; - - if (!tb[NL80211_ATTR_IFTYPE]) - return NL_SKIP; - - switch (nla_get_u32(tb[NL80211_ATTR_IFTYPE])) { - case NL80211_IFTYPE_ADHOC: - info->mode = NM_802_11_MODE_ADHOC; - break; - case NL80211_IFTYPE_AP: - info->mode = NM_802_11_MODE_AP; - break; - case NL80211_IFTYPE_STATION: - info->mode = NM_802_11_MODE_INFRA; - break; - case NL80211_IFTYPE_MESH_POINT: - info->mode = NM_802_11_MODE_MESH; - break; - } - - return NL_SKIP; -} - -static NM80211Mode -wifi_nl80211_get_mode(NMWifiUtils *data) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_iface_info iface_info = { - .mode = NM_802_11_MODE_UNKNOWN, - }; - nm_auto_nlmsg struct nl_msg *msg = NULL; - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_INTERFACE, 0); - - if (nl80211_send_and_recv(self, msg, nl80211_iface_info_handler, &iface_info) < 0) - return NM_802_11_MODE_UNKNOWN; - - return iface_info.mode; -} - -static gboolean -wifi_nl80211_set_mode(NMWifiUtils *data, const NM80211Mode mode) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - msg = nl80211_alloc_msg(self, NL80211_CMD_SET_INTERFACE, 0); - - switch (mode) { - case NM_802_11_MODE_INFRA: - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_STATION); - break; - case NM_802_11_MODE_ADHOC: - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_ADHOC); - break; - case NM_802_11_MODE_AP: - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); - break; - case NM_802_11_MODE_MESH: - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MESH_POINT); - break; - default: - g_assert_not_reached(); - } - - err = nl80211_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static gboolean -wifi_nl80211_set_powersave(NMWifiUtils *data, guint32 powersave) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - msg = nl80211_alloc_msg(self, NL80211_CMD_SET_POWER_SAVE, 0); - NLA_PUT_U32(msg, - NL80211_ATTR_PS_STATE, - powersave == 1 ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); - err = nl80211_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static int -nl80211_get_wake_on_wlan_handler(struct nl_msg *msg, void *arg) -{ - NMSettingWirelessWakeOnWLan *wowl = arg; - struct nlattr * attrs[NL80211_ATTR_MAX + 1]; - struct nlattr * trig[NUM_NL80211_WOWLAN_TRIG]; - struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg)); - - nla_parse_arr(attrs, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); - - if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) - return NL_SKIP; - - nla_parse_arr(trig, - nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - NULL); - - *wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE; - if (trig[NL80211_WOWLAN_TRIG_ANY]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY; - if (trig[NL80211_WOWLAN_TRIG_DISCONNECT]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT; - if (trig[NL80211_WOWLAN_TRIG_MAGIC_PKT]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC; - if (trig[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE; - if (trig[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST; - if (trig[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE; - if (trig[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE; - if (trig[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) - *wowl |= NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP; - - return NL_SKIP; -} - -static NMSettingWirelessWakeOnWLan -wifi_nl80211_get_wake_on_wlan(NMWifiUtils *data) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - NMSettingWirelessWakeOnWLan wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE; - nm_auto_nlmsg struct nl_msg *msg = NULL; - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WOWLAN, 0); - - nl80211_send_and_recv(self, msg, nl80211_get_wake_on_wlan_handler, &wowl); - - return wowl; -} - -static gboolean -wifi_nl80211_set_wake_on_wlan(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nlattr * triggers; - int err; - - if (wowl == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE) - return TRUE; - - msg = nl80211_alloc_msg(self, NL80211_CMD_SET_WOWLAN, 0); - - triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); - if (!triggers) - goto nla_put_failure; - - if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY)) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); - if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT)) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); - if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC)) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); - if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE)) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); - if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST)) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); - if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE)) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); - if (NM_FLAGS_HAS(wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE)) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); - - nla_nest_end(msg, triggers); - - err = nl80211_send_and_recv(self, msg, NULL, NULL); - - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -/* @divisor: pass what value @xbm should be divided by to get dBm */ -static guint32 -nl80211_xbm_to_percent(gint32 xbm, guint32 divisor) -{ -#define NOISE_FLOOR_DBM -90 -#define SIGNAL_MAX_DBM -20 - - xbm /= divisor; - xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); - - return 100 - - 70 - * (((float) SIGNAL_MAX_DBM - (float) xbm) - / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM)); -} - -struct nl80211_bss_info { - guint32 freq; - guint8 bssid[ETH_ALEN]; - guint8 ssid[32]; - guint32 ssid_len; - guint32 beacon_signal; - gboolean valid; -}; - -#define WLAN_EID_SSID 0 - -static void -find_ssid(guint8 *ies, guint32 ies_len, guint8 **ssid, guint32 *ssid_len) -{ - *ssid = NULL; - *ssid_len = 0; - - while (ies_len > 2 && ies[0] != WLAN_EID_SSID) { - ies_len -= ies[1] + 2; - ies += ies[1] + 2; - } - if (ies_len < 2) - return; - if (ies_len < 2 + ies[1]) - return; - - *ssid_len = ies[1]; - *ssid = ies + 2; -} - -static int -nl80211_bss_dump_handler(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy bss_policy[] = { - [NL80211_BSS_TSF] = {.type = NLA_U64}, - [NL80211_BSS_FREQUENCY] = {.type = NLA_U32}, - [NL80211_BSS_BSSID] = {.minlen = ETH_ALEN}, - [NL80211_BSS_BEACON_INTERVAL] = {.type = NLA_U16}, - [NL80211_BSS_CAPABILITY] = {.type = NLA_U16}, - [NL80211_BSS_INFORMATION_ELEMENTS] = {}, - [NL80211_BSS_SIGNAL_MBM] = {.type = NLA_U32}, - [NL80211_BSS_SIGNAL_UNSPEC] = {.type = NLA_U8}, - [NL80211_BSS_STATUS] = {.type = NLA_U32}, - }; - struct nl80211_bss_info *info = arg; - struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr * tb[NL80211_ATTR_MAX + 1]; - struct nlattr * bss[G_N_ELEMENTS(bss_policy)]; - guint32 status; - - if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) - return NL_SKIP; - - if (tb[NL80211_ATTR_BSS] == NULL) - return NL_SKIP; - - if (nla_parse_nested_arr(bss, tb[NL80211_ATTR_BSS], bss_policy)) - return NL_SKIP; - - if (bss[NL80211_BSS_STATUS] == NULL) - return NL_SKIP; - - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - - if (status != NL80211_BSS_STATUS_ASSOCIATED && status != NL80211_BSS_STATUS_IBSS_JOINED) - return NL_SKIP; - - if (bss[NL80211_BSS_BSSID] == NULL) - return NL_SKIP; - memcpy(info->bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); - - if (bss[NL80211_BSS_FREQUENCY]) - info->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - - if (bss[NL80211_BSS_SIGNAL_UNSPEC]) - info->beacon_signal = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - - if (bss[NL80211_BSS_SIGNAL_MBM]) - info->beacon_signal = nl80211_xbm_to_percent(nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]), 100); - - if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { - guint8 *ssid; - guint32 ssid_len; - - find_ssid(nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]), - nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]), - &ssid, - &ssid_len); - if (ssid && ssid_len && ssid_len <= sizeof(info->ssid)) { - memcpy(info->ssid, ssid, ssid_len); - info->ssid_len = ssid_len; - } - } - - info->valid = TRUE; - - return NL_SKIP; -} - -static void -nl80211_get_bss_info(NMWifiUtilsNl80211 *self, struct nl80211_bss_info *bss_info) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - - memset(bss_info, 0, sizeof(*bss_info)); - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_SCAN, NLM_F_DUMP); - - nl80211_send_and_recv(self, msg, nl80211_bss_dump_handler, bss_info); -} - -static guint32 -wifi_nl80211_get_freq(NMWifiUtils *data) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_bss_info bss_info; - - nl80211_get_bss_info(self, &bss_info); - - return bss_info.freq; -} - -static guint32 -wifi_nl80211_find_freq(NMWifiUtils *data, const guint32 *freqs) -{ - NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; - int i; - - for (i = 0; i < self->num_freqs; i++) { - while (*freqs) { - if (self->freqs[i] == *freqs) - return *freqs; - freqs++; - } - } - return 0; -} - -static gboolean -wifi_nl80211_get_bssid(NMWifiUtils *data, guint8 *out_bssid) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_bss_info bss_info; - - nl80211_get_bss_info(self, &bss_info); - - if (bss_info.valid) - memcpy(out_bssid, bss_info.bssid, ETH_ALEN); - - return bss_info.valid; -} - -struct nl80211_station_info { - guint32 txrate; - gboolean txrate_valid; - guint8 signal; - gboolean signal_valid; -}; - -static int -nl80211_station_handler(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy stats_policy[] = { - [NL80211_STA_INFO_INACTIVE_TIME] = {.type = NLA_U32}, - [NL80211_STA_INFO_RX_BYTES] = {.type = NLA_U32}, - [NL80211_STA_INFO_TX_BYTES] = {.type = NLA_U32}, - [NL80211_STA_INFO_RX_PACKETS] = {.type = NLA_U32}, - [NL80211_STA_INFO_TX_PACKETS] = {.type = NLA_U32}, - [NL80211_STA_INFO_SIGNAL] = {.type = NLA_U8}, - [NL80211_STA_INFO_TX_BITRATE] = {.type = NLA_NESTED}, - [NL80211_STA_INFO_LLID] = {.type = NLA_U16}, - [NL80211_STA_INFO_PLID] = {.type = NLA_U16}, - [NL80211_STA_INFO_PLINK_STATE] = {.type = NLA_U8}, - }; - static const struct nla_policy rate_policy[] = { - [NL80211_RATE_INFO_BITRATE] = {.type = NLA_U16}, - [NL80211_RATE_INFO_MCS] = {.type = NLA_U8}, - [NL80211_RATE_INFO_40_MHZ_WIDTH] = {.type = NLA_FLAG}, - [NL80211_RATE_INFO_SHORT_GI] = {.type = NLA_FLAG}, - }; - struct nlattr * rinfo[G_N_ELEMENTS(rate_policy)]; - struct nlattr * sinfo[G_N_ELEMENTS(stats_policy)]; - struct nl80211_station_info *info = arg; - struct nlattr * tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg)); - - if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) - return NL_SKIP; - - if (tb[NL80211_ATTR_STA_INFO] == NULL) - return NL_SKIP; - - if (nla_parse_nested_arr(sinfo, tb[NL80211_ATTR_STA_INFO], stats_policy)) - return NL_SKIP; - - if (sinfo[NL80211_STA_INFO_TX_BITRATE] == NULL) - return NL_SKIP; - - if (nla_parse_nested_arr(rinfo, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy)) - return NL_SKIP; - - if (rinfo[NL80211_RATE_INFO_BITRATE] == NULL) - return NL_SKIP; - - /* convert from nl80211's units of 100kbps to NM's kbps */ - info->txrate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100; - info->txrate_valid = TRUE; - - if (sinfo[NL80211_STA_INFO_SIGNAL] != NULL) { - info->signal = - nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]), 1); - info->signal_valid = TRUE; - } - - return NL_SKIP; -} - -static void -nl80211_get_ap_info(NMWifiUtilsNl80211 *self, struct nl80211_station_info *sta_info) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nl80211_bss_info bss_info; - - memset(sta_info, 0, sizeof(*sta_info)); - - nl80211_get_bss_info(self, &bss_info); - if (!bss_info.valid) - return; - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_STATION, 0); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bss_info.bssid); - - nl80211_send_and_recv(self, msg, nl80211_station_handler, sta_info); - if (!sta_info->signal_valid) { - /* Fall back to bss_info signal quality (both are in percent) */ - sta_info->signal = bss_info.beacon_signal; - } - - return; - -nla_put_failure: - g_return_if_reached(); -} - -static guint32 -wifi_nl80211_get_rate(NMWifiUtils *data) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_station_info sta_info; - - nl80211_get_ap_info(self, &sta_info); - - return sta_info.txrate; -} - -static int -wifi_nl80211_get_qual(NMWifiUtils *data) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_station_info sta_info; - - nl80211_get_ap_info(self, &sta_info); - return sta_info.signal; -} - -static gboolean -wifi_nl80211_indicate_addressing_running(NMWifiUtils *data, gboolean running) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - msg = nl80211_alloc_msg(self, - running ? 98 /* NL80211_CMD_CRIT_PROTOCOL_START */ - : 99 /* NL80211_CMD_CRIT_PROTOCOL_STOP */, - 0); - /* Despite the DHCP name, we're using this for any type of IP addressing, - * DHCPv4, DHCPv6, and IPv6 SLAAC. - */ - NLA_PUT_U16(msg, 179 /* NL80211_ATTR_CRIT_PROT_ID */, 1 /* NL80211_CRIT_PROTO_DHCP */); - if (running) { - /* Give DHCP 5 seconds to complete */ - NLA_PUT_U16(msg, 180 /* NL80211_ATTR_MAX_CRIT_PROT_DURATION */, 5000); - } - - err = nl80211_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -struct nl80211_device_info { - NMWifiUtilsNl80211 *self; - int phy; - guint32 * freqs; - int num_freqs; - guint32 freq; - guint32 caps; - gboolean can_scan; - gboolean can_scan_ssid; - gboolean supported; - gboolean success; - gboolean can_wowlan; -}; - -#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 -#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 -#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 -#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 -#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 -#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 -#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 -#define WLAN_CIPHER_SUITE_SMS4 0x00147201 - -static int -nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy freq_policy[] = { - [NL80211_FREQUENCY_ATTR_FREQ] = {.type = NLA_U32}, - [NL80211_FREQUENCY_ATTR_DISABLED] = {.type = NLA_FLAG}, -#ifdef NL80211_FREQUENCY_ATTR_NO_IR - [NL80211_FREQUENCY_ATTR_NO_IR] = {.type = NLA_FLAG}, -#else - [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = {.type = NLA_FLAG}, - [NL80211_FREQUENCY_ATTR_NO_IBSS] = {.type = NLA_FLAG}, -#endif - [NL80211_FREQUENCY_ATTR_RADAR] = {.type = NLA_FLAG}, - [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = {.type = NLA_U32}, - }; - struct nlattr * tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nl80211_device_info *info = arg; - NMWifiUtilsNl80211 * self = info->self; - struct nlattr * tb_band[NL80211_BAND_ATTR_MAX + 1]; - struct nlattr * tb_freq[G_N_ELEMENTS(freq_policy)]; - struct nlattr * nl_band; - struct nlattr * nl_freq; - int rem_freq; - int rem_band; - int freq_idx; - -#ifdef NL80211_FREQUENCY_ATTR_NO_IR - G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR - && NL80211_FREQUENCY_ATTR_NO_IBSS == NL80211_FREQUENCY_ATTR_NO_IR); -#else - G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN != NL80211_FREQUENCY_ATTR_NO_IBSS); -#endif - - if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) - return NL_SKIP; - - if (tb[NL80211_ATTR_WIPHY] == NULL || tb[NL80211_ATTR_WIPHY_BANDS] == NULL) - return NL_SKIP; - - info->phy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); - - if (tb[NL80211_ATTR_WIPHY_FREQ]) - info->freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); - else - info->freq = 0; - - if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) { - info->can_scan_ssid = nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) > 0; - } else { - /* old kernel that only had mac80211, so assume it can */ - info->can_scan_ssid = TRUE; - } - - if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) { - struct nlattr *nl_cmd; - int i; - - nla_for_each_nested (nl_cmd, tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) { - switch (nla_get_u32(nl_cmd)) { - case NL80211_CMD_TRIGGER_SCAN: - info->can_scan = TRUE; - break; - case NL80211_CMD_CONNECT: - case NL80211_CMD_AUTHENTICATE: - /* Only devices that support CONNECT or AUTH actually support - * 802.11, unlike say ipw2x00 (up to at least kernel 3.4) which - * has minimal info support, but no actual command support. - * This check mirrors what wpa_supplicant does to determine - * whether or not to use the nl80211 driver. - */ - info->supported = TRUE; - break; - default: - break; - } - } - } - - /* Find number of supported frequencies */ - info->num_freqs = 0; - - nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { - if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0) - return NL_SKIP; - - nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0) - continue; - - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; - - info->num_freqs++; - } - } - - /* Read supported frequencies */ - info->freqs = g_malloc0(sizeof(guint32) * info->num_freqs); - - freq_idx = 0; - nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { - if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0) - return NL_SKIP; - - nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0) - continue; - - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; - - info->freqs[freq_idx] = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); - - info->caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID; - - if (info->freqs[freq_idx] > 2400 && info->freqs[freq_idx] < 2500) - info->caps |= NM_WIFI_DEVICE_CAP_FREQ_2GHZ; - if (info->freqs[freq_idx] > 4900 && info->freqs[freq_idx] < 6000) - info->caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ; - - freq_idx++; - } - } - - /* Read security/encryption support */ - if (tb[NL80211_ATTR_CIPHER_SUITES]) { - guint32 *ciphers = nla_data(tb[NL80211_ATTR_CIPHER_SUITES]); - guint i, num; - - num = nla_len(tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof(guint32); - for (i = 0; i < num; i++) { - switch (ciphers[i]) { - case WLAN_CIPHER_SUITE_WEP40: - info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40; - break; - case WLAN_CIPHER_SUITE_WEP104: - info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP104; - break; - case WLAN_CIPHER_SUITE_TKIP: - info->caps |= (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_WPA); - break; - case WLAN_CIPHER_SUITE_CCMP: - info->caps |= (NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN); - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - case WLAN_CIPHER_SUITE_GCMP: - case WLAN_CIPHER_SUITE_SMS4: - break; - default: - _LOGD("don't know the meaning of NL80211_ATTR_CIPHER_SUITE %#8.8x.", ciphers[i]); - break; - } - } - } - - if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) { - struct nlattr *nl_mode; - int i; - - nla_for_each_nested (nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { - switch (nla_type(nl_mode)) { - case NL80211_IFTYPE_AP: - info->caps |= NM_WIFI_DEVICE_CAP_AP; - break; - case NL80211_IFTYPE_ADHOC: - info->caps |= NM_WIFI_DEVICE_CAP_ADHOC; - break; - case NL80211_IFTYPE_MESH_POINT: - info->caps |= NM_WIFI_DEVICE_CAP_MESH; - break; - } - } - } - - if (tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]) - info->can_wowlan = TRUE; - - if (tb[NL80211_ATTR_SUPPORT_IBSS_RSN]) - info->caps |= NM_WIFI_DEVICE_CAP_IBSS_RSN; - - info->success = TRUE; - - return NL_SKIP; -} - -static guint32 -wifi_nl80211_get_mesh_channel(NMWifiUtils *data) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nl80211_device_info device_info = {.self = self}; - int i; - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, 0); - - if (nl80211_send_and_recv(self, msg, nl80211_wiphy_info_handler, &device_info) < 0) { - _LOGW("NL80211_CMD_GET_WIPHY request failed"); - return 0; - } - - for (i = 0; i < self->num_freqs; i++) { - if (device_info.freq == self->freqs[i]) - return i + 1; - } - return 0; -} - -static gboolean -wifi_nl80211_set_mesh_channel(NMWifiUtils *data, guint32 channel) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - if (channel > self->num_freqs) - return FALSE; - - msg = nl80211_alloc_msg(self, NL80211_CMD_SET_WIPHY, 0); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, self->freqs[channel - 1]); - err = nl80211_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static gboolean -wifi_nl80211_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - msg = nl80211_alloc_msg(self, NL80211_CMD_SET_INTERFACE, 0); - NLA_PUT(msg, NL80211_ATTR_MESH_ID, len, ssid); - err = nl80211_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -static void -nm_wifi_utils_nl80211_init(NMWifiUtilsNl80211 *self) -{} - -static void -nm_wifi_utils_nl80211_class_init(NMWifiUtilsNl80211Class *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMWifiUtilsClass *wifi_utils_class = NM_WIFI_UTILS_CLASS(klass); - - object_class->dispose = dispose; - - wifi_utils_class->get_mode = wifi_nl80211_get_mode; - wifi_utils_class->set_mode = wifi_nl80211_set_mode; - wifi_utils_class->set_powersave = wifi_nl80211_set_powersave; - wifi_utils_class->get_wake_on_wlan = wifi_nl80211_get_wake_on_wlan, - wifi_utils_class->set_wake_on_wlan = wifi_nl80211_set_wake_on_wlan, - wifi_utils_class->get_freq = wifi_nl80211_get_freq; - wifi_utils_class->find_freq = wifi_nl80211_find_freq; - wifi_utils_class->get_bssid = wifi_nl80211_get_bssid; - wifi_utils_class->get_rate = wifi_nl80211_get_rate; - wifi_utils_class->get_qual = wifi_nl80211_get_qual; - wifi_utils_class->indicate_addressing_running = wifi_nl80211_indicate_addressing_running; - wifi_utils_class->get_mesh_channel = wifi_nl80211_get_mesh_channel; - wifi_utils_class->set_mesh_channel = wifi_nl80211_set_mesh_channel; - wifi_utils_class->set_mesh_ssid = wifi_nl80211_set_mesh_ssid; -} - -NMWifiUtils * -nm_wifi_utils_nl80211_new(int ifindex, struct nl_sock *genl) -{ - gs_unref_object NMWifiUtilsNl80211 *self = NULL; - nm_auto_nlmsg struct nl_msg * msg = NULL; - struct nl80211_device_info device_info = {}; - - if (!genl) - return NULL; - - self = g_object_new(NM_TYPE_WIFI_UTILS_NL80211, NULL); - - self->parent.ifindex = ifindex; - self->nl_sock = genl; - - self->id = genl_ctrl_resolve(self->nl_sock, "nl80211"); - if (self->id < 0) { - _LOGD("genl_ctrl_resolve: failed to resolve \"nl80211\""); - return NULL; - } - - self->phy = -1; - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_WIPHY, 0); - - device_info.self = self; - if (nl80211_send_and_recv(self, msg, nl80211_wiphy_info_handler, &device_info) < 0) { - _LOGD("NL80211_CMD_GET_WIPHY request failed"); - return NULL; - } - - if (!device_info.success) { - _LOGD("NL80211_CMD_GET_WIPHY request indicated failure"); - return NULL; - } - - if (!device_info.supported) { - _LOGD("driver does not fully support nl80211, falling back to WEXT"); - return NULL; - } - - if (!device_info.can_scan_ssid) { - _LOGE("driver does not support SSID scans"); - return NULL; - } - - if (device_info.num_freqs == 0 || device_info.freqs == NULL) { - _LOGE("driver reports no supported frequencies"); - return NULL; - } - - if (device_info.caps == 0) { - _LOGE("driver doesn't report support of any encryption"); - return NULL; - } - - self->phy = device_info.phy; - self->freqs = device_info.freqs; - self->num_freqs = device_info.num_freqs; - self->parent.caps = device_info.caps; - self->can_wowlan = device_info.can_wowlan; - - _LOGD("using nl80211 for Wi-Fi device control"); - return (NMWifiUtils *) g_steal_pointer(&self); -} diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.h b/src/platform/wifi/nm-wifi-utils-nl80211.h deleted file mode 100644 index 59fc1acc..00000000 --- a/src/platform/wifi/nm-wifi-utils-nl80211.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2011 Intel Corporation. All rights reserved. - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __WIFI_UTILS_NL80211_H__ -#define __WIFI_UTILS_NL80211_H__ - -#include "nm-wifi-utils.h" -#include "platform/nm-netlink.h" - -#define NM_TYPE_WIFI_UTILS_NL80211 (nm_wifi_utils_nl80211_get_type()) -#define NM_WIFI_UTILS_NL80211(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211)) -#define NM_WIFI_UTILS_NL80211_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class)) -#define NM_IS_WIFI_UTILS_NL80211(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS_NL80211)) -#define NM_IS_WIFI_UTILS_NL80211_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_UTILS_NL80211)) -#define NM_WIFI_UTILS_NL80211_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class)) - -GType nm_wifi_utils_nl80211_get_type(void); - -NMWifiUtils *nm_wifi_utils_nl80211_new(int ifindex, struct nl_sock *genl); - -#endif /* __WIFI_UTILS_NL80211_H__ */ diff --git a/src/platform/wifi/nm-wifi-utils-private.h b/src/platform/wifi/nm-wifi-utils-private.h deleted file mode 100644 index 1a5303a6..00000000 --- a/src/platform/wifi/nm-wifi-utils-private.h +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2011 - 2018 Red Hat, Inc. - */ - -#ifndef __WIFI_UTILS_PRIVATE_H__ -#define __WIFI_UTILS_PRIVATE_H__ - -#include "nm-dbus-interface.h" -#include "nm-wifi-utils.h" - -typedef struct { - GObjectClass parent; - - NM80211Mode (*get_mode)(NMWifiUtils *data); - - gboolean (*set_mode)(NMWifiUtils *data, const NM80211Mode mode); - - /* Set power saving mode on an interface */ - gboolean (*set_powersave)(NMWifiUtils *data, guint32 powersave); - - /* Get WakeOnWLAN configuration on an interface */ - NMSettingWirelessWakeOnWLan (*get_wake_on_wlan)(NMWifiUtils *data); - - /* Set WakeOnWLAN mode on an interface */ - gboolean (*set_wake_on_wlan)(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl); - - /* Return current frequency in MHz (really associated BSS frequency) */ - guint32 (*get_freq)(NMWifiUtils *data); - - /* Return first supported frequency in the zero-terminated list */ - guint32 (*find_freq)(NMWifiUtils *data, const guint32 *freqs); - - /* Return current bitrate in Kbps */ - guint32 (*get_rate)(NMWifiUtils *data); - - gboolean (*get_bssid)(NMWifiUtils *data, guint8 *out_bssid); - - /* Return a signal strength percentage 0 - 100% for the current BSSID; - * return -1 on errors or if not associated. - */ - int (*get_qual)(NMWifiUtils *data); - - /* OLPC Mesh-only functions */ - - guint32 (*get_mesh_channel)(NMWifiUtils *data); - - /* channel == 0 means "auto channel" */ - gboolean (*set_mesh_channel)(NMWifiUtils *data, guint32 channel); - - /* ssid == NULL means "auto SSID" */ - gboolean (*set_mesh_ssid)(NMWifiUtils *data, const guint8 *ssid, gsize len); - - gboolean (*indicate_addressing_running)(NMWifiUtils *data, gboolean running); -} NMWifiUtilsClass; - -struct NMWifiUtils { - GObject parent; - - int ifindex; - NMDeviceWifiCapabilities caps; -}; - -#endif /* __WIFI_UTILS_PRIVATE_H__ */ diff --git a/src/platform/wifi/nm-wifi-utils-wext.c b/src/platform/wifi/nm-wifi-utils-wext.c deleted file mode 100644 index 8299415d..00000000 --- a/src/platform/wifi/nm-wifi-utils-wext.c +++ /dev/null @@ -1,805 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2005 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "nm-default.h" - -#include "nm-wifi-utils-wext.h" - -#include <sys/ioctl.h> -#include <net/ethernet.h> -#include <unistd.h> - -/* Hacks necessary to #include wireless.h; yay for WEXT */ -#ifndef __user - #define __user -#endif -#include <sys/types.h> -#include <linux/types.h> -#include <sys/socket.h> -#include <linux/wireless.h> - -#include "nm-wifi-utils-private.h" -#include "nm-utils.h" -#include "platform/nm-platform-utils.h" -#include "nm-core-internal.h" - -typedef struct { - NMWifiUtils parent; - int fd; - struct iw_quality max_qual; - gint8 num_freqs; - guint32 freqs[IW_MAX_FREQUENCIES]; -} NMWifiUtilsWext; - -typedef struct { - NMWifiUtilsClass parent; -} NMWifiUtilsWextClass; - -G_DEFINE_TYPE(NMWifiUtilsWext, nm_wifi_utils_wext, NM_TYPE_WIFI_UTILS) - -/* Until a new wireless-tools comes out that has the defs and the structure, - * need to copy them here. - */ -/* Scan capability flags - in (struct iw_range *)->scan_capa */ -#define NM_IW_SCAN_CAPA_NONE 0x00 -#define NM_IW_SCAN_CAPA_ESSID 0x01 - -struct iw_range_with_scan_capa { - guint32 throughput; - guint32 min_nwid; - guint32 max_nwid; - guint16 old_num_channels; - guint8 old_num_frequency; - - guint8 scan_capa; - /* don't need the rest... */ -}; - -#define _NMLOG_PREFIX_NAME "wifi-wext" -#define _NMLOG(level, domain, ...) \ - G_STMT_START \ - { \ - nm_log((level), \ - (domain), \ - NULL, \ - NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - G_STMT_END - -static guint32 -iw_freq_to_uint32(const struct iw_freq *freq) -{ - if (freq->e == 0) { - /* Some drivers report channel not frequency. Convert to a - * frequency; but this assumes that the device is in b/g mode. - */ - if ((freq->m >= 1) && (freq->m <= 13)) - return 2407 + (5 * freq->m); - else if (freq->m == 14) - return 2484; - } - return (guint32)((((double) freq->m) * nm_utils_exp10(freq->e)) / 1000000.0); -} - -static void -dispose(GObject *object) -{ - NMWifiUtilsWext *wext = NM_WIFI_UTILS_WEXT(object); - - wext->fd = nm_close(wext->fd); -} - -static gboolean -get_ifname(int ifindex, char *buffer, const char *op) -{ - int errsv; - - if (!nmp_utils_if_indextoname(ifindex, buffer)) { - errsv = errno; - _LOGW(LOGD_PLATFORM | LOGD_WIFI, - "error getting interface name for ifindex %d, operation '%s': %s (%d)", - ifindex, - op, - nm_strerror_native(errsv), - errsv); - return FALSE; - } - - return TRUE; -} - -static NM80211Mode -wifi_wext_get_mode_ifname(NMWifiUtils *data, const char *ifname) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - int errsv; - - memset(&wrq, 0, sizeof(struct iwreq)); - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - - if (ioctl(wext->fd, SIOCGIWMODE, &wrq) < 0) { - errsv = errno; - if (errsv != ENODEV) { - _LOGW(LOGD_PLATFORM | LOGD_WIFI, "(%s): error %d getting card mode", ifname, errsv); - } - return NM_802_11_MODE_UNKNOWN; - } - - switch (wrq.u.mode) { - case IW_MODE_ADHOC: - return NM_802_11_MODE_ADHOC; - case IW_MODE_MASTER: - return NM_802_11_MODE_AP; - case IW_MODE_INFRA: - case IW_MODE_AUTO: /* hack for WEXT devices reporting IW_MODE_AUTO */ - return NM_802_11_MODE_INFRA; - default: - break; - } - return NM_802_11_MODE_UNKNOWN; -} - -static NM80211Mode -wifi_wext_get_mode(NMWifiUtils *data) -{ - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "get-mode")) - return FALSE; - - return wifi_wext_get_mode_ifname(data, ifname); -} - -static gboolean -wifi_wext_set_mode(NMWifiUtils *data, const NM80211Mode mode) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "set-mode")) - return FALSE; - - if (wifi_wext_get_mode_ifname(data, ifname) == mode) - return TRUE; - - memset(&wrq, 0, sizeof(struct iwreq)); - switch (mode) { - case NM_802_11_MODE_ADHOC: - wrq.u.mode = IW_MODE_ADHOC; - break; - case NM_802_11_MODE_AP: - wrq.u.mode = IW_MODE_MASTER; - break; - case NM_802_11_MODE_INFRA: - wrq.u.mode = IW_MODE_INFRA; - break; - default: - g_warn_if_reached(); - return FALSE; - } - - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - if (ioctl(wext->fd, SIOCSIWMODE, &wrq) < 0) { - if (errno != ENODEV) { - _LOGE(LOGD_PLATFORM | LOGD_WIFI, "(%s): error setting mode %d", ifname, mode); - } - return FALSE; - } - - return TRUE; -} - -static gboolean -wifi_wext_set_powersave(NMWifiUtils *data, guint32 powersave) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "set-powersave")) - return FALSE; - - memset(&wrq, 0, sizeof(struct iwreq)); - if (powersave == 1) { - wrq.u.power.flags = IW_POWER_ALL_R; - } else - wrq.u.power.disabled = 1; - - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - if (ioctl(wext->fd, SIOCSIWPOWER, &wrq) < 0) { - if (errno != ENODEV) { - _LOGE(LOGD_PLATFORM | LOGD_WIFI, - "(%s): error setting powersave %" G_GUINT32_FORMAT, - ifname, - powersave); - } - return FALSE; - } - - return TRUE; -} - -static guint32 -wifi_wext_get_freq(NMWifiUtils *data) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "get-freq")) - return FALSE; - - memset(&wrq, 0, sizeof(struct iwreq)); - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - if (ioctl(wext->fd, SIOCGIWFREQ, &wrq) < 0) { - _LOGW(LOGD_PLATFORM | LOGD_WIFI, - "(%s): error getting frequency: %s", - ifname, - nm_strerror_native(errno)); - return 0; - } - - return iw_freq_to_uint32(&wrq.u.freq); -} - -static guint32 -wifi_wext_find_freq(NMWifiUtils *data, const guint32 *freqs) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - int i; - - for (i = 0; i < wext->num_freqs; i++) { - while (*freqs) { - if (wext->freqs[i] == *freqs) - return *freqs; - freqs++; - } - } - return 0; -} - -static gboolean -wifi_wext_get_bssid(NMWifiUtils *data, guint8 *out_bssid) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "get-bssid")) - return FALSE; - - memset(&wrq, 0, sizeof(wrq)); - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - if (ioctl(wext->fd, SIOCGIWAP, &wrq) < 0) { - _LOGW(LOGD_PLATFORM | LOGD_WIFI, - "(%s): error getting associated BSSID: %s", - ifname, - nm_strerror_native(errno)); - return FALSE; - } - memcpy(out_bssid, &(wrq.u.ap_addr.sa_data), ETH_ALEN); - return TRUE; -} - -static guint32 -wifi_wext_get_rate(NMWifiUtils *data) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - int err; - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "get-rate")) - return FALSE; - - memset(&wrq, 0, sizeof(wrq)); - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - err = ioctl(wext->fd, SIOCGIWRATE, &wrq); - return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0); -} - -static int -wext_qual_to_percent(const struct iw_quality *qual, const struct iw_quality *max_qual) -{ - int percent = -1; - int level_percent = -1; - - g_return_val_if_fail(qual != NULL, -1); - g_return_val_if_fail(max_qual != NULL, -1); - - /* Magically convert the many different WEXT quality representations to a percentage */ - - _LOGD(LOGD_WIFI, - "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual " - "%d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X", - (__s8) qual->qual, - qual->qual, - qual->qual, - (__s8) qual->level, - qual->level, - qual->level, - (__s8) qual->noise, - qual->noise, - qual->noise, - qual->updated, - (__s8) max_qual->qual, - max_qual->qual, - max_qual->qual, - (__s8) max_qual->level, - max_qual->level, - max_qual->level, - (__s8) max_qual->noise, - max_qual->noise, - max_qual->noise, - max_qual->updated); - - /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is. - * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be - * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers - * are free to use whatever they want to calculate "Link Quality". - */ - if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) - && !(qual->updated & IW_QUAL_QUAL_INVALID)) - percent = (int) (100 * ((double) qual->qual / (double) max_qual->qual)); - - /* If the driver doesn't specify a complete and valid quality, we have two options: - * - * 1) dBm: driver must specify max_qual->level = 0, and have valid values for - * qual->level and (qual->noise OR max_qual->noise) - * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for - * qual->level and max_qual->level - * - * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise, - * If drivers don't conform to it, they are wrong and need to be fixed. - */ - - if ((max_qual->level == 0) - && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */ - && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */ - && (((max_qual->noise > 0) - && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */ - || ((qual->noise > 0) - && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */ - ) { -/* Absolute power values (dBm) */ - -/* Reasonable fallbacks for dumb drivers that don't specify either level. */ -#define FALLBACK_NOISE_FLOOR_DBM -90 -#define FALLBACK_SIGNAL_MAX_DBM -20 - int max_level = FALLBACK_SIGNAL_MAX_DBM; - int noise = FALLBACK_NOISE_FLOOR_DBM; - int level = qual->level - 0x100; - - level = CLAMP(level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); - - if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID)) - noise = qual->noise - 0x100; - else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) - noise = max_qual->noise - 0x100; - noise = CLAMP(noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM - 1); - - /* A sort of signal-to-noise ratio calculation */ - level_percent = (int) (100 - - 70 - * (((double) max_level - (double) level) - / ((double) max_level - (double) noise))); - _LOGD(LOGD_WIFI, - "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.", - level_percent, - max_level, - level, - noise); - } else if ((max_qual->level != 0) - && !(max_qual->updated - & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */ - && !(qual->updated & IW_QUAL_LEVEL_INVALID)) { - /* Relative power values (RSSI) */ - - int level = qual->level; - - /* Signal level is relavtive (0 -> max_qual->level) */ - level = CLAMP(level, 0, max_qual->level); - level_percent = (int) (100 * ((double) level / (double) max_qual->level)); - _LOGD(LOGD_WIFI, - "QL2: level_percent is %d. max_level %d, level %d.", - level_percent, - max_qual->level, - level); - } else if (percent == -1) { - _LOGD(LOGD_WIFI, - "QL: Could not get quality %% value from driver. Driver is probably buggy."); - } - - /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */ - if ((percent < 1) && (level_percent >= 0)) - percent = level_percent; - - _LOGD(LOGD_WIFI, "QL: Final quality percent is %d (%d).", percent, CLAMP(percent, 0, 100)); - return (CLAMP(percent, 0, 100)); -} - -static int -wifi_wext_get_qual(NMWifiUtils *data) -{ - NMWifiUtilsWext * wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - struct iw_statistics stats; - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "get-qual")) - return FALSE; - - memset(&stats, 0, sizeof(stats)); - wrq.u.data.pointer = &stats; - wrq.u.data.length = sizeof(stats); - wrq.u.data.flags = 1; /* Clear updated flag */ - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - - if (ioctl(wext->fd, SIOCGIWSTATS, &wrq) < 0) { - _LOGW(LOGD_PLATFORM | LOGD_WIFI, - "(%s): error getting signal strength: %s", - ifname, - nm_strerror_native(errno)); - return -1; - } - - return wext_qual_to_percent(&stats.qual, &wext->max_qual); -} - -/*****************************************************************************/ -/* OLPC Mesh-only functions */ - -static guint32 -wifi_wext_get_mesh_channel(NMWifiUtils *data) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - guint32 freq; - int i; - - freq = nm_wifi_utils_get_freq(data); - for (i = 0; i < wext->num_freqs; i++) { - if (freq == wext->freqs[i]) - return i + 1; - } - return 0; -} - -static gboolean -wifi_wext_set_mesh_channel(NMWifiUtils *data, guint32 channel) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - char ifname[IFNAMSIZ]; - - if (!get_ifname(data->ifindex, ifname, "set-mesh-channel")) - return FALSE; - - memset(&wrq, 0, sizeof(struct iwreq)); - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - - if (channel > 0) { - wrq.u.freq.flags = IW_FREQ_FIXED; - wrq.u.freq.e = 0; - wrq.u.freq.m = channel; - } - - if (ioctl(wext->fd, SIOCSIWFREQ, &wrq) < 0) { - _LOGE(LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC, - "(%s): error setting channel to %d: %s", - ifname, - channel, - nm_strerror_native(errno)); - return FALSE; - } - - return TRUE; -} - -static gboolean -wifi_wext_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len) -{ - NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; - struct iwreq wrq; - char buf[IW_ESSID_MAX_SIZE + 1]; - char ifname[IFNAMSIZ]; - int errsv; - - if (!get_ifname(data->ifindex, ifname, "set-mesh-ssid")) - return FALSE; - - memset(buf, 0, sizeof(buf)); - memcpy(buf, ssid, MIN(sizeof(buf) - 1, len)); - - wrq.u.essid.pointer = (caddr_t) buf; - wrq.u.essid.length = len; - wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */ - - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - if (ioctl(wext->fd, SIOCSIWESSID, &wrq) == 0) - return TRUE; - - errsv = errno; - if (errsv != ENODEV) { - gs_free char *ssid_str = NULL; - - _LOGE(LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC, - "(%s): error setting SSID to %s: %s", - ifname, - (ssid_str = _nm_utils_ssid_to_string_arr(ssid, len)), - nm_strerror_native(errsv)); - } - - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -wext_can_scan_ifname(NMWifiUtilsWext *wext, const char *ifname) -{ - struct iwreq wrq; - - memset(&wrq, 0, sizeof(struct iwreq)); - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - if (ioctl(wext->fd, SIOCSIWSCAN, &wrq) < 0) { - if (errno == EOPNOTSUPP) - return FALSE; - } - return TRUE; -} - -static gboolean -wext_get_range_ifname(NMWifiUtilsWext *wext, - const char * ifname, - struct iw_range *range, - guint32 * response_len) -{ - int i = 26; - gboolean success = FALSE; - struct iwreq wrq; - int errsv; - - memset(&wrq, 0, sizeof(struct iwreq)); - nm_utils_ifname_cpy(wrq.ifr_name, ifname); - wrq.u.data.pointer = (caddr_t) range; - wrq.u.data.length = sizeof(struct iw_range); - - /* Need to give some drivers time to recover after suspend/resume - * (ex ipw3945 takes a few seconds to talk to its regulatory daemon; - * see rh bz#362421) - */ - while (i-- > 0) { - if (ioctl(wext->fd, SIOCGIWRANGE, &wrq) == 0) { - if (response_len) - *response_len = wrq.u.data.length; - success = TRUE; - break; - } else { - errsv = errno; - if (errsv != EAGAIN) { - _LOGE(LOGD_PLATFORM | LOGD_WIFI, - "(%s): couldn't get driver range information (%d).", - ifname, - errsv); - break; - } - } - - g_usleep(G_USEC_PER_SEC / 4); - } - - if (i <= 0) { - _LOGW(LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver took too long to respond to IWRANGE query.", - ifname); - } - - return success; -} - -#define WPA_CAPS \ - (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_WPA \ - | NM_WIFI_DEVICE_CAP_RSN) - -static guint32 -wext_get_caps(NMWifiUtilsWext *wext, const char *ifname, struct iw_range *range) -{ - guint32 caps = NM_WIFI_DEVICE_CAP_NONE; - - g_return_val_if_fail(wext != NULL, NM_WIFI_DEVICE_CAP_NONE); - g_return_val_if_fail(range != NULL, NM_WIFI_DEVICE_CAP_NONE); - - /* All drivers should support WEP by default */ - caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104; - - if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) - caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP; - - if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) - caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP; - - if (range->enc_capa & IW_ENC_CAPA_WPA) - caps |= NM_WIFI_DEVICE_CAP_WPA; - - if (range->enc_capa & IW_ENC_CAPA_WPA2) - caps |= NM_WIFI_DEVICE_CAP_RSN; - - /* Check for cipher support but not WPA support */ - if ((caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) - && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) { - _LOGW(LOGD_WIFI, - "%s: device supports WPA ciphers but not WPA protocol; WPA unavailable.", - ifname); - caps &= ~WPA_CAPS; - } - - /* Check for WPA support but not cipher support */ - if ((caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)) - && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) { - _LOGW(LOGD_WIFI, - "%s: device supports WPA protocol but not WPA ciphers; WPA unavailable.", - ifname); - caps &= ~WPA_CAPS; - } - - /* There's no way to detect Ad-Hoc/AP mode support with WEXT - * (other than actually trying to do it), so just assume that - * Ad-Hoc is supported and AP isn't. - */ - caps |= NM_WIFI_DEVICE_CAP_ADHOC; - - return caps; -} - -/*****************************************************************************/ - -static void -nm_wifi_utils_wext_init(NMWifiUtilsWext *self) -{} - -static void -nm_wifi_utils_wext_class_init(NMWifiUtilsWextClass *klass) -{ - GObjectClass * object_class = G_OBJECT_CLASS(klass); - NMWifiUtilsClass *wifi_utils_class = NM_WIFI_UTILS_CLASS(klass); - - object_class->dispose = dispose; - - wifi_utils_class->get_mode = wifi_wext_get_mode; - wifi_utils_class->set_mode = wifi_wext_set_mode; - wifi_utils_class->set_powersave = wifi_wext_set_powersave; - wifi_utils_class->get_freq = wifi_wext_get_freq; - wifi_utils_class->find_freq = wifi_wext_find_freq; - wifi_utils_class->get_bssid = wifi_wext_get_bssid; - wifi_utils_class->get_rate = wifi_wext_get_rate; - wifi_utils_class->get_qual = wifi_wext_get_qual; - wifi_utils_class->get_mesh_channel = wifi_wext_get_mesh_channel; - wifi_utils_class->set_mesh_channel = wifi_wext_set_mesh_channel; - wifi_utils_class->set_mesh_ssid = wifi_wext_set_mesh_ssid; -} - -NMWifiUtils * -nm_wifi_utils_wext_new(int ifindex, gboolean check_scan) -{ - NMWifiUtilsWext * wext; - struct iw_range range; - guint32 response_len = 0; - struct iw_range_with_scan_capa *scan_capa_range; - int i; - gboolean freq_valid = FALSE, has_5ghz = FALSE, has_2ghz = FALSE; - char ifname[IFNAMSIZ]; - - if (!nmp_utils_if_indextoname(ifindex, ifname)) { - _LOGW(LOGD_PLATFORM | LOGD_WIFI, "can't determine interface name for ifindex %d", ifindex); - return NULL; - } - - wext = g_object_new(NM_TYPE_WIFI_UTILS_WEXT, NULL); - - wext->parent.ifindex = ifindex; - wext->fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (wext->fd < 0) - goto error; - - memset(&range, 0, sizeof(struct iw_range)); - if (wext_get_range_ifname(wext, ifname, &range, &response_len) == FALSE) { - _LOGI(LOGD_PLATFORM | LOGD_WIFI, "(%s): driver WEXT range request failed", ifname); - goto error; - } - - if ((response_len < 300) || (range.we_version_compiled < 21)) { - _LOGI(LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver WEXT version too old (got %d, expected >= 21)", - ifname, - range.we_version_compiled); - goto error; - } - - wext->max_qual.qual = range.max_qual.qual; - wext->max_qual.level = range.max_qual.level; - wext->max_qual.noise = range.max_qual.noise; - wext->max_qual.updated = range.max_qual.updated; - - wext->num_freqs = MIN(range.num_frequency, IW_MAX_FREQUENCIES); - for (i = 0; i < wext->num_freqs; i++) { - wext->freqs[i] = iw_freq_to_uint32(&range.freq[i]); - freq_valid = TRUE; - if (wext->freqs[i] > 2400 && wext->freqs[i] < 2500) - has_2ghz = TRUE; - else if (wext->freqs[i] > 4900 && wext->freqs[i] < 6000) - has_5ghz = TRUE; - } - - /* Check for scanning capability; cards that can't scan are not supported */ - if (check_scan && (wext_can_scan_ifname(wext, ifname) == FALSE)) { - _LOGI(LOGD_PLATFORM | LOGD_WIFI, "(%s): drivers that cannot scan are unsupported", ifname); - goto error; - } - - /* Check for the ability to scan specific SSIDs. Until the scan_capa - * field gets added to wireless-tools, need to work around that by casting - * to the custom structure. - */ - scan_capa_range = (struct iw_range_with_scan_capa *) ⦥ - if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) { - _LOGI(LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver supports SSID scans (scan_capa 0x%02X).", - ifname, - scan_capa_range->scan_capa); - } else { - _LOGI(LOGD_PLATFORM | LOGD_WIFI, - "(%s): driver does not support SSID scans (scan_capa 0x%02X).", - ifname, - scan_capa_range->scan_capa); - } - - wext->parent.caps = wext_get_caps(wext, ifname, &range); - if (freq_valid) - wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID; - if (has_2ghz) - wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_2GHZ; - if (has_5ghz) - wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ; - - _LOGI(LOGD_PLATFORM | LOGD_WIFI, "(%s): using WEXT for Wi-Fi device control", ifname); - - return (NMWifiUtils *) wext; - -error: - g_object_unref(wext); - return NULL; -} - -gboolean -nm_wifi_utils_wext_is_wifi(const char *iface) -{ - int fd; - struct iwreq iwr; - gboolean is_wifi = FALSE; - - /* performing an ioctl on a non-existing name may cause the automatic - * loading of kernel modules, which should be avoided. - * - * Usually, we should thus make sure that an interface with this name - * exists. - * - * Note that wifi_wext_is_wifi() has only one caller which just verified - * that an interface with this name exists. - */ - - fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (fd >= 0) { - nm_utils_ifname_cpy(iwr.ifr_ifrn.ifrn_name, iface); - if (ioctl(fd, SIOCGIWNAME, &iwr) == 0) - is_wifi = TRUE; - nm_close(fd); - } - return is_wifi; -} diff --git a/src/platform/wifi/nm-wifi-utils-wext.h b/src/platform/wifi/nm-wifi-utils-wext.h deleted file mode 100644 index 210b87e5..00000000 --- a/src/platform/wifi/nm-wifi-utils-wext.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2011 - 2018 Red Hat, Inc. - */ - -#ifndef __WIFI_UTILS_WEXT_H__ -#define __WIFI_UTILS_WEXT_H__ - -#include "nm-wifi-utils.h" - -#define NM_TYPE_WIFI_UTILS_WEXT (nm_wifi_utils_wext_get_type()) -#define NM_WIFI_UTILS_WEXT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWext)) -#define NM_WIFI_UTILS_WEXT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWextClass)) -#define NM_IS_WIFI_UTILS_WEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS_WEXT)) -#define NM_IS_WIFI_UTILS_WEXT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_UTILS_WEXT)) -#define NM_WIFI_UTILS_WEXT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WIFI_UTILS_WEXT, NMWifiUtilsWextClass)) - -GType nm_wifi_utils_wext_get_type(void); - -NMWifiUtils *nm_wifi_utils_wext_new(int ifindex, gboolean check_scan); - -gboolean nm_wifi_utils_wext_is_wifi(const char *iface); - -#endif /* __WIFI_UTILS_WEXT_H__ */ diff --git a/src/platform/wifi/nm-wifi-utils.c b/src/platform/wifi/nm-wifi-utils.c deleted file mode 100644 index 3f9d3a48..00000000 --- a/src/platform/wifi/nm-wifi-utils.c +++ /dev/null @@ -1,224 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2005 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "nm-default.h" - -#include "nm-wifi-utils.h" - -#include <sys/stat.h> -#include <stdio.h> -#include <fcntl.h> - -#include "nm-wifi-utils-private.h" -#include "nm-wifi-utils-nl80211.h" -#if HAVE_WEXT - #include "nm-wifi-utils-wext.h" -#endif -#include "nm-core-utils.h" - -#include "platform/nm-platform-utils.h" - -G_DEFINE_ABSTRACT_TYPE(NMWifiUtils, nm_wifi_utils, G_TYPE_OBJECT) - -/*****************************************************************************/ - -static void -nm_wifi_utils_init(NMWifiUtils *self) -{} - -static void -nm_wifi_utils_class_init(NMWifiUtilsClass *klass) -{} - -NMWifiUtils * -nm_wifi_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan) -{ - NMWifiUtils *ret; - - g_return_val_if_fail(ifindex > 0, NULL); - - ret = nm_wifi_utils_nl80211_new(ifindex, genl); - -#if HAVE_WEXT - if (ret == NULL) - ret = nm_wifi_utils_wext_new(ifindex, check_scan); -#endif - - return ret; -} - -NMDeviceWifiCapabilities -nm_wifi_utils_get_caps(NMWifiUtils *data) -{ - g_return_val_if_fail(data != NULL, NM_WIFI_DEVICE_CAP_NONE); - - return data->caps; -} - -NM80211Mode -nm_wifi_utils_get_mode(NMWifiUtils *data) -{ - g_return_val_if_fail(data != NULL, NM_802_11_MODE_UNKNOWN); - return NM_WIFI_UTILS_GET_CLASS(data)->get_mode(data); -} - -gboolean -nm_wifi_utils_set_mode(NMWifiUtils *data, const NM80211Mode mode) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, FALSE); - g_return_val_if_fail((mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_AP) - || (mode == NM_802_11_MODE_ADHOC) || (mode == NM_802_11_MODE_MESH), - FALSE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - - /* nl80211 probably doesn't need this */ - return klass->set_mode ? klass->set_mode(data, mode) : TRUE; -} - -gboolean -nm_wifi_utils_set_powersave(NMWifiUtils *data, guint32 powersave) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, FALSE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - return klass->set_powersave ? klass->set_powersave(data, powersave) : TRUE; -} - -NMSettingWirelessWakeOnWLan -nm_wifi_utils_get_wake_on_wlan(NMWifiUtils *data) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - - return klass->get_wake_on_wlan ? klass->get_wake_on_wlan(data) - : NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE; -} - -gboolean -nm_wifi_utils_set_wake_on_wlan(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, FALSE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - return klass->set_wake_on_wlan ? klass->set_wake_on_wlan(data, wowl) : FALSE; -} - -guint32 -nm_wifi_utils_get_freq(NMWifiUtils *data) -{ - g_return_val_if_fail(data != NULL, 0); - return NM_WIFI_UTILS_GET_CLASS(data)->get_freq(data); -} - -guint32 -nm_wifi_utils_find_freq(NMWifiUtils *data, const guint32 *freqs) -{ - g_return_val_if_fail(data != NULL, 0); - g_return_val_if_fail(freqs != NULL, 0); - return NM_WIFI_UTILS_GET_CLASS(data)->find_freq(data, freqs); -} - -gboolean -nm_wifi_utils_get_bssid(NMWifiUtils *data, guint8 *out_bssid) -{ - g_return_val_if_fail(data != NULL, FALSE); - g_return_val_if_fail(out_bssid != NULL, FALSE); - - memset(out_bssid, 0, ETH_ALEN); - return NM_WIFI_UTILS_GET_CLASS(data)->get_bssid(data, out_bssid); -} - -guint32 -nm_wifi_utils_get_rate(NMWifiUtils *data) -{ - g_return_val_if_fail(data != NULL, 0); - return NM_WIFI_UTILS_GET_CLASS(data)->get_rate(data); -} - -int -nm_wifi_utils_get_qual(NMWifiUtils *data) -{ - g_return_val_if_fail(data != NULL, 0); - return NM_WIFI_UTILS_GET_CLASS(data)->get_qual(data); -} - -gboolean -nm_wifi_utils_is_wifi(int dirfd, const char *ifname) -{ - g_return_val_if_fail(dirfd >= 0, FALSE); - - if (faccessat(dirfd, "phy80211", F_OK, 0) == 0) - return TRUE; -#if HAVE_WEXT - if (nm_wifi_utils_wext_is_wifi(ifname)) - return TRUE; -#endif - return FALSE; -} - -/* OLPC Mesh-only functions */ - -guint32 -nm_wifi_utils_get_mesh_channel(NMWifiUtils *data) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, FALSE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - g_return_val_if_fail(klass->get_mesh_channel != NULL, FALSE); - - return klass->get_mesh_channel(data); -} - -gboolean -nm_wifi_utils_set_mesh_channel(NMWifiUtils *data, guint32 channel) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, FALSE); - g_return_val_if_fail(channel <= 13, FALSE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - g_return_val_if_fail(klass->set_mesh_channel != NULL, FALSE); - - return klass->set_mesh_channel(data, channel); -} - -gboolean -nm_wifi_utils_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, FALSE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - g_return_val_if_fail(klass->set_mesh_ssid != NULL, FALSE); - - return klass->set_mesh_ssid(data, ssid, len); -} - -gboolean -nm_wifi_utils_indicate_addressing_running(NMWifiUtils *data, gboolean running) -{ - NMWifiUtilsClass *klass; - - g_return_val_if_fail(data != NULL, FALSE); - - klass = NM_WIFI_UTILS_GET_CLASS(data); - return klass->indicate_addressing_running ? klass->indicate_addressing_running(data, running) - : FALSE; -} diff --git a/src/platform/wifi/nm-wifi-utils.h b/src/platform/wifi/nm-wifi-utils.h deleted file mode 100644 index d1df2fb8..00000000 --- a/src/platform/wifi/nm-wifi-utils.h +++ /dev/null @@ -1,71 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2005 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#ifndef __WIFI_UTILS_H__ -#define __WIFI_UTILS_H__ - -#include <net/ethernet.h> - -#include "nm-dbus-interface.h" -#include "nm-setting-wireless.h" -#include "platform/nm-netlink.h" - -typedef struct NMWifiUtils NMWifiUtils; - -#define NM_TYPE_WIFI_UTILS (nm_wifi_utils_get_type()) -#define NM_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WIFI_UTILS, NMWifiUtils)) -#define NM_WIFI_UTILS_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass)) -#define NM_IS_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WIFI_UTILS)) -#define NM_IS_WIFI_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_UTILS)) -#define NM_WIFI_UTILS_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass)) - -GType nm_wifi_utils_get_type(void); - -gboolean nm_wifi_utils_is_wifi(int dirfd, const char *ifname); - -NMWifiUtils *nm_wifi_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan); - -NMDeviceWifiCapabilities nm_wifi_utils_get_caps(NMWifiUtils *data); - -NM80211Mode nm_wifi_utils_get_mode(NMWifiUtils *data); - -gboolean nm_wifi_utils_set_mode(NMWifiUtils *data, const NM80211Mode mode); - -/* Returns frequency in MHz */ -guint32 nm_wifi_utils_get_freq(NMWifiUtils *data); - -/* Return the first supported frequency in the zero-terminated list. - * Frequencies are specified in MHz. */ -guint32 nm_wifi_utils_find_freq(NMWifiUtils *data, const guint32 *freqs); - -/* out_bssid must be ETH_ALEN bytes */ -gboolean nm_wifi_utils_get_bssid(NMWifiUtils *data, guint8 *out_bssid); - -/* Returns current bitrate in Kbps */ -guint32 nm_wifi_utils_get_rate(NMWifiUtils *data); - -/* Returns quality 0 - 100% on success, or -1 on error */ -int nm_wifi_utils_get_qual(NMWifiUtils *data); - -/* Tells the driver DHCP or SLAAC is running */ -gboolean nm_wifi_utils_indicate_addressing_running(NMWifiUtils *data, gboolean running); - -gboolean nm_wifi_utils_set_powersave(NMWifiUtils *data, guint32 powersave); - -NMSettingWirelessWakeOnWLan nm_wifi_utils_get_wake_on_wlan(NMWifiUtils *data); - -gboolean nm_wifi_utils_set_wake_on_wlan(NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl); - -/* OLPC Mesh-only functions */ -guint32 nm_wifi_utils_get_mesh_channel(NMWifiUtils *data); - -gboolean nm_wifi_utils_set_mesh_channel(NMWifiUtils *data, guint32 channel); - -gboolean nm_wifi_utils_set_mesh_ssid(NMWifiUtils *data, const guint8 *ssid, gsize len); - -#endif /* __WIFI_UTILS_H__ */ diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c deleted file mode 100644 index 6f651414..00000000 --- a/src/platform/wpan/nm-wpan-utils.c +++ /dev/null @@ -1,286 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-wpan-utils.h" - -#include <linux/if.h> - -#include "platform/linux/nl802154.h" -#include "platform/nm-netlink.h" -#include "platform/nm-platform-utils.h" - -#define _NMLOG_PREFIX_NAME "wpan-nl802154" -#define _NMLOG(level, domain, ...) \ - G_STMT_START \ - { \ - char _ifname_buf[IFNAMSIZ]; \ - const char *_ifname = self ? nmp_utils_if_indextoname(self->ifindex, _ifname_buf) : NULL; \ - \ - nm_log((level), \ - (domain), \ - _ifname ?: NULL, \ - NULL, \ - "%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - NM_PRINT_FMT_QUOTED(_ifname, " (", _ifname, ")", "") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -struct NMWpanUtils { - GObject parent; - int ifindex; - struct nl_sock *nl_sock; - int id; -}; - -typedef struct { - GObjectClass parent; -} NMWpanUtilsClass; - -G_DEFINE_TYPE(NMWpanUtils, nm_wpan_utils, G_TYPE_OBJECT) - -/*****************************************************************************/ - -static int -ack_handler(struct nl_msg *msg, void *arg) -{ - int *done = arg; - *done = 1; - return NL_STOP; -} - -static int -finish_handler(struct nl_msg *msg, void *arg) -{ - int *done = arg; - *done = 1; - return NL_SKIP; -} - -static int -error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) -{ - int *done = arg; - *done = err->error; - return NL_SKIP; -} - -static struct nl_msg * -_nl802154_alloc_msg(int id, int ifindex, guint32 cmd, guint32 flags) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - - msg = nlmsg_alloc(); - genlmsg_put(msg, 0, 0, id, 0, flags, cmd, 0); - NLA_PUT_U32(msg, NL802154_ATTR_IFINDEX, ifindex); - return g_steal_pointer(&msg); - -nla_put_failure: - g_return_val_if_reached(NULL); -} - -static struct nl_msg * -nl802154_alloc_msg(NMWpanUtils *self, guint32 cmd, guint32 flags) -{ - return _nl802154_alloc_msg(self->id, self->ifindex, cmd, flags); -} - -static int -nl802154_send_and_recv(NMWpanUtils * self, - struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data) -{ - int err; - int done = 0; - const struct nl_cb cb = { - .err_cb = error_handler, - .err_arg = &done, - .finish_cb = finish_handler, - .finish_arg = &done, - .ack_cb = ack_handler, - .ack_arg = &done, - .valid_cb = valid_handler, - .valid_arg = valid_data, - }; - - g_return_val_if_fail(msg != NULL, -ENOMEM); - - err = nl_send_auto(self->nl_sock, msg); - if (err < 0) - return err; - - /* Loop until one of our NL callbacks says we're done; on success - * done will be 1, on error it will be < 0. - */ - while (!done) { - err = nl_recvmsgs(self->nl_sock, &cb); - if (err < 0 && err != -EAGAIN) { - _LOGW(LOGD_PLATFORM, "nl_recvmsgs() error: (%d) %s", err, nm_strerror(err)); - break; - } - } - - if (err >= 0 && done < 0) - err = done; - return err; -} - -struct nl802154_interface { - guint16 pan_id; - guint16 short_addr; - - gboolean valid; -}; - -static int -nl802154_get_interface_handler(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy nl802154_policy[] = { - [NL802154_ATTR_PAN_ID] = {.type = NLA_U16}, - [NL802154_ATTR_SHORT_ADDR] = {.type = NLA_U16}, - }; - struct nlattr * tb[G_N_ELEMENTS(nl802154_policy)]; - struct nl802154_interface *info = arg; - struct genlmsghdr * gnlh = nlmsg_data(nlmsg_hdr(msg)); - - if (nla_parse_arr(tb, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), nl802154_policy) < 0) - return NL_SKIP; - - if (tb[NL802154_ATTR_PAN_ID]) - info->pan_id = le16toh(nla_get_u16(tb[NL802154_ATTR_PAN_ID])); - - if (tb[NL802154_ATTR_SHORT_ADDR]) - info->short_addr = le16toh(nla_get_u16(tb[NL802154_ATTR_SHORT_ADDR])); - - info->valid = TRUE; - - return NL_SKIP; -} - -static void -nl802154_get_interface(NMWpanUtils *self, struct nl802154_interface *interface) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - - memset(interface, 0, sizeof(*interface)); - - msg = nl802154_alloc_msg(self, NL802154_CMD_GET_INTERFACE, 0); - - nl802154_send_and_recv(self, msg, nl802154_get_interface_handler, interface); -} - -/*****************************************************************************/ - -guint16 -nm_wpan_utils_get_pan_id(NMWpanUtils *self) -{ - struct nl802154_interface interface; - - nl802154_get_interface(self, &interface); - - return interface.pan_id; -} - -gboolean -nm_wpan_utils_set_pan_id(NMWpanUtils *self, guint16 pan_id) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - g_return_val_if_fail(self != NULL, FALSE); - - msg = nl802154_alloc_msg(self, NL802154_CMD_SET_PAN_ID, 0); - NLA_PUT_U16(msg, NL802154_ATTR_PAN_ID, htole16(pan_id)); - err = nl802154_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -guint16 -nm_wpan_utils_get_short_addr(NMWpanUtils *self) -{ - struct nl802154_interface interface; - - nl802154_get_interface(self, &interface); - - return interface.short_addr; -} - -gboolean -nm_wpan_utils_set_short_addr(NMWpanUtils *self, guint16 short_addr) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - g_return_val_if_fail(self != NULL, FALSE); - - msg = nl802154_alloc_msg(self, NL802154_CMD_SET_SHORT_ADDR, 0); - NLA_PUT_U16(msg, NL802154_ATTR_SHORT_ADDR, htole16(short_addr)); - err = nl802154_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -gboolean -nm_wpan_utils_set_channel(NMWpanUtils *self, guint8 page, guint8 channel) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - int err; - - g_return_val_if_fail(self != NULL, FALSE); - - msg = nl802154_alloc_msg(self, NL802154_CMD_SET_CHANNEL, 0); - NLA_PUT_U8(msg, NL802154_ATTR_PAGE, page); - NLA_PUT_U8(msg, NL802154_ATTR_CHANNEL, channel); - err = nl802154_send_and_recv(self, msg, NULL, NULL); - return err >= 0; - -nla_put_failure: - g_return_val_if_reached(FALSE); -} - -/*****************************************************************************/ - -static void -nm_wpan_utils_init(NMWpanUtils *self) -{} - -static void -nm_wpan_utils_class_init(NMWpanUtilsClass *klass) -{} - -NMWpanUtils * -nm_wpan_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan) -{ - NMWpanUtils *self; - - g_return_val_if_fail(ifindex > 0, NULL); - - if (!genl) - return NULL; - - self = g_object_new(NM_TYPE_WPAN_UTILS, NULL); - self->ifindex = ifindex; - self->nl_sock = genl; - self->id = genl_ctrl_resolve(genl, "nl802154"); - - if (self->id < 0) { - _LOGD(LOGD_PLATFORM, "genl_ctrl_resolve: failed to resolve \"nl802154\""); - g_object_unref(self); - return NULL; - } - - return self; -} diff --git a/src/platform/wpan/nm-wpan-utils.h b/src/platform/wpan/nm-wpan-utils.h deleted file mode 100644 index b30c89a6..00000000 --- a/src/platform/wpan/nm-wpan-utils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __WPAN_UTILS_H__ -#define __WPAN_UTILS_H__ - -#include <net/ethernet.h> - -#include "nm-dbus-interface.h" -#include "platform/nm-netlink.h" - -typedef struct NMWpanUtils NMWpanUtils; - -#define NM_TYPE_WPAN_UTILS (nm_wpan_utils_get_type()) -#define NM_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_WPAN_UTILS, NMWpanUtils)) -#define NM_WPAN_UTILS_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass)) -#define NM_IS_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_WPAN_UTILS)) -#define NM_IS_WPAN_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WPAN_UTILS)) -#define NM_WPAN_UTILS_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass)) - -GType nm_wpan_utils_get_type(void); - -NMWpanUtils *nm_wpan_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan); - -guint16 nm_wpan_utils_get_pan_id(NMWpanUtils *self); -gboolean nm_wpan_utils_set_pan_id(NMWpanUtils *self, guint16 pan_id); - -guint16 nm_wpan_utils_get_short_addr(NMWpanUtils *self); -gboolean nm_wpan_utils_set_short_addr(NMWpanUtils *self, guint16 short_addr); - -gboolean nm_wpan_utils_set_channel(NMWpanUtils *self, guint8 page, guint8 channel); - -#endif /* __WPAN_UTILS_H__ */ |