diff options
| author | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-10-01 23:05:04 +0200 |
| commit | e74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch) | |
| tree | 3469f17ea9af91f7ff169b890633bda68b0cf76e /src/core/platform | |
| parent | bfe522304da217296e2a61040f58e35ec5d6f3f2 (diff) | |
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'src/core/platform')
31 files changed, 257 insertions, 29789 deletions
diff --git a/src/core/platform/linux/nl802154.h b/src/core/platform/linux/nl802154.h deleted file mode 100644 index c21bd079..00000000 --- a/src/core/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/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c index 81c9f06c..b773df9a 100644 --- a/src/core/platform/nm-fake-platform.c +++ b/src/core/platform/nm-fake-platform.c @@ -13,12 +13,9 @@ #include <linux/if.h> #include <linux/rtnetlink.h> -#include "nm-utils.h" - -#include "nm-core-utils.h" -#include "nm-platform/nm-platform-utils.h" -#include "nm-platform-private.h" -#include "nmp-object.h" +#include "libnm-platform/nm-platform-utils.h" +#include "libnm-platform/nm-platform-private.h" +#include "libnm-platform/nmp-object.h" #include "nm-test-utils-core.h" @@ -506,6 +503,21 @@ link_set_flags(NMPlatform *platform, NMFakePlatformLink *device, guint n_ifi_fla link_set_obj(platform, device, obj_tmp); } +static int +link_change_flags(NMPlatform *platform, int ifindex, unsigned flags_mask, unsigned flags_set) +{ + NMFakePlatformLink *device = link_get(platform, ifindex); + + if (!device) + return -ENOENT; + + link_set_flags(platform, + device, + NM_FLAGS_ASSIGN_MASK(device->obj->link.n_ifi_flags, flags_mask, flags_set)); + + return 0; +} + static void link_changed(NMPlatform * platform, NMFakePlatformLink *device, @@ -543,72 +555,13 @@ link_changed(NMPlatform * platform, } } -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) + if (len == 0 || len > _NM_UTILS_HWADDR_LEN_MAX || !addr) g_return_val_if_reached(-NME_BUG); if (!device) @@ -749,8 +702,8 @@ link_release(NMPlatform *platform, int master_idx, int slave_idx) static gboolean link_vlan_change(NMPlatform * platform, int ifindex, - NMVlanFlags flags_mask, - NMVlanFlags flags_set, + _NMVlanFlags flags_mask, + _NMVlanFlags flags_set, gboolean ingress_reset_all, const NMVlanQosMapping *ingress_map, gsize n_ingress_map, @@ -799,7 +752,7 @@ infiniband_partition_add(NMPlatform * platform, 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); + nmp_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; @@ -814,12 +767,12 @@ infiniband_partition_delete(NMPlatform *platform, int parent, int 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); + nmp_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) +wifi_get_capabilities(NMPlatform *platform, int ifindex, _NMDeviceWifiCapabilities *caps) { NMFakePlatformLink *device = link_get(platform, ifindex); @@ -829,10 +782,10 @@ wifi_get_capabilities(NMPlatform *platform, int ifindex, NMDeviceWifiCapabilitie 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); + *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; } @@ -861,14 +814,14 @@ wifi_get_rate(NMPlatform *platform, int ifindex) return 0; } -static NM80211Mode +static _NM80211Mode wifi_get_mode(NMPlatform *platform, int ifindex) { - return NM_802_11_MODE_UNKNOWN; + return _NM_802_11_MODE_UNKNOWN; } static void -wifi_set_mode(NMPlatform *platform, int ifindex, NM80211Mode mode) +wifi_set_mode(NMPlatform *platform, int ifindex, _NM80211Mode mode) { ; } @@ -1164,6 +1117,9 @@ ip_route_add(NMPlatform * platform, : NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); r = NMP_OBJECT_CAST_IP_ROUTE(obj); + + r->is_external = TRUE; + nm_platform_ip_route_normalize(addr_family, r); switch (addr_family) { @@ -1357,14 +1313,11 @@ nm_fake_platform_class_init(NMFakePlatformClass *klass) 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_change_flags = link_change_flags; + platform_class->link_get_driver_info = link_get_driver_info; platform_class->link_supports_carrier_detect = link_supports_carrier_detect; diff --git a/src/core/platform/nm-fake-platform.h b/src/core/platform/nm-fake-platform.h index a1d44f8f..8f8571fa 100644 --- a/src/core/platform/nm-fake-platform.h +++ b/src/core/platform/nm-fake-platform.h @@ -6,7 +6,7 @@ #ifndef __NETWORKMANAGER_FAKE_PLATFORM_H__ #define __NETWORKMANAGER_FAKE_PLATFORM_H__ -#include "nm-platform.h" +#include "libnm-platform/nm-platform.h" #define NM_TYPE_FAKE_PLATFORM (nm_fake_platform_get_type()) #define NM_FAKE_PLATFORM(obj) \ diff --git a/src/core/platform/nm-linux-platform.c b/src/core/platform/nm-linux-platform.c deleted file mode 100644 index 7bd42bba..00000000 --- a/src/core/platform/nm-linux-platform.c +++ /dev/null @@ -1,9696 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2012 - 2018 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include "nm-linux-platform.h" - -#include <arpa/inet.h> -#include <dlfcn.h> -#include <endian.h> -#include <fcntl.h> -#include <libudev.h> -#include <net/ethernet.h> -#include <linux/fib_rules.h> -#include <linux/ip.h> -#include <linux/if.h> -#include <linux/if_bridge.h> -#include <linux/if_link.h> -#include <linux/if_tun.h> -#include <linux/if_tunnel.h> -#include <linux/if_vlan.h> -#include <linux/ip6_tunnel.h> -#include <linux/tc_act/tc_mirred.h> -#include <netinet/icmp6.h> -#include <netinet/in.h> -#include <net/if_arp.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-platform/nm-netlink.h" -#include "nm-core-utils.h" -#include "nmp-object.h" -#include "nm-platform/nmp-netns.h" -#include "nm-platform/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_end(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, - NMOptionBool 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_OPTION_BOOL_DEFAULT || current_autoprobe == autoprobe)) - goto out_idle; - - if (NM_IN_SET(autoprobe, NM_OPTION_BOOL_TRUE, NM_OPTION_BOOL_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 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_station(NMPlatform * platform, - int ifindex, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate) -{ - WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); - return nm_wifi_utils_get_station(wifi_data, out_bssid, out_quality, out_rate); -} - -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; - - nmp_object_stackinit(&obj, - NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), - (const NMPlatformObject *) route); - - 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_destroy(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_frequency = wifi_get_frequency; - platform_class->wifi_get_station = wifi_get_station; - 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/core/platform/nm-linux-platform.h b/src/core/platform/nm-linux-platform.h deleted file mode 100644 index 6837e64a..00000000 --- a/src/core/platform/nm-linux-platform.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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/core/platform/nm-platform-private.h b/src/core/platform/nm-platform-private.h deleted file mode 100644 index cf805689..00000000 --- a/src/core/platform/nm-platform-private.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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/core/platform/nm-platform.c b/src/core/platform/nm-platform.c deleted file mode 100644 index 459a3307..00000000 --- a/src/core/platform/nm-platform.c +++ /dev/null @@ -1,9066 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2012 - 2018 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.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/nm-platform-utils.h" -#include "nm-platform-private.h" -#include "nmp-object.h" -#include "nm-platform/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_OPTION_BOOL_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, - NMOptionBool 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); -} - -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); -} - -gboolean -nm_platform_wifi_get_station(NMPlatform * self, - int ifindex, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate) -{ - _CHECK_SELF(self, klass, FALSE); - - g_return_val_if_fail(ifindex > 0, FALSE); - - return klass->wifi_get_station(self, ifindex, out_bssid, out_quality, out_rate); -} - -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); -} - -/*****************************************************************************/ - -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 NMOptionBool *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 (!NM_IS_IPv4(addr_family) && 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 int 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 int 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; - NMPlatformIP4Route rt_local4; - NMPlatformIP6Route rt_local6; - const NMPlatformLink * pllink; - const NMPlatformLnkVrf * lnk_vrf; - guint32 local_table; - - 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, - NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE)); - - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), ifindex); - head_entry = nm_platform_lookup(self, &lookup); - if (!head_entry) - return NULL; - - lnk_vrf = nm_platform_link_get_lnk_vrf(self, ifindex, &pllink); - if (!lnk_vrf && pllink && pllink->master > 0) - lnk_vrf = nm_platform_link_get_lnk_vrf(self, pllink->master, NULL); - local_table = lnk_vrf ? lnk_vrf->table : RT_TABLE_LOCAL; - - rt_local4.plen = 0; - rt_local6.plen = 0; - - 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; - const NMPlatformIPXRoute *rt = NMP_OBJECT_CAST_IPX_ROUTE(obj); - - switch (route_table_sync) { - case NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN: - if (!nm_platform_route_table_is_main(nm_platform_ip_route_get_effective_table(&rt->rx))) - continue; - break; - case NM_IP_ROUTE_TABLE_SYNC_MODE_FULL: - if (nm_platform_ip_route_get_effective_table(&rt->rx) == RT_TABLE_LOCAL) - continue; - break; - case NM_IP_ROUTE_TABLE_SYNC_MODE_ALL: - - /* FIXME: we should better handle routes that are automatically added by kernel. - * - * For now, make a good guess which are those routes and exclude them from - * pruning them. */ - - if (NM_IS_IPv4(addr_family)) { - /* for each IPv4 address kernel adds a route like - * - * local $ADDR dev $IFACE table local proto kernel scope host src $PRIMARY_ADDR - * - * Check whether route could be of that kind. */ - if (nm_platform_ip_route_get_effective_table(&rt->rx) == local_table - && rt->rx.plen == 32 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL - && rt->rx.metric == 0 - && rt->r4.scope_inv == nm_platform_route_scope_inv(RT_SCOPE_HOST) - && rt->r4.gateway == INADDR_ANY) { - if (rt_local4.plen == 0) { - rt_local4 = (NMPlatformIP4Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), - .plen = 32, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, - .metric = 0, - .table_coerced = nm_platform_route_table_coerce(local_table), - .scope_inv = nm_platform_route_scope_inv(RT_SCOPE_HOST), - .gateway = INADDR_ANY, - }; - } - - /* the possible "network" depends on the addresses we have. We don't check that - * carefully. If the other parameters match, we assume that this route is the one - * generated by kernel. */ - rt_local4.network = rt->r4.network; - rt_local4.pref_src = rt->r4.pref_src; - - /* to be more confident about comparing the value, use our nm_platform_ip4_route_cmp() - * implementation. That will also consider parameters that we leave unspecified here. */ - if (nm_platform_ip4_route_cmp(&rt->r4, - &rt_local4, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - == 0) - continue; - } - } else { - /* for each IPv6 address (that is no longer tentative) kernel adds a route like - * - * local $ADDR dev $IFACE table local proto kernel metric 0 pref medium - * - * Same as for the IPv4 case. */ - if (nm_platform_ip_route_get_effective_table(&rt->rx) == local_table - && rt->rx.plen == 128 && rt->rx.rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL - && rt->rx.metric == 0 && rt->r6.rt_pref == NM_ICMPV6_ROUTER_PREF_MEDIUM - && IN6_IS_ADDR_UNSPECIFIED(&rt->r6.gateway)) { - if (rt_local6.plen == 0) { - rt_local6 = (NMPlatformIP6Route){ - .ifindex = ifindex, - .type_coerced = nm_platform_route_type_coerce(RTN_LOCAL), - .plen = 128, - .rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL, - .metric = 0, - .table_coerced = nm_platform_route_table_coerce(local_table), - .rt_pref = NM_ICMPV6_ROUTER_PREF_MEDIUM, - .gateway = IN6ADDR_ANY_INIT, - }; - } - - rt_local6.network = rt->r6.network; - - if (nm_platform_ip6_route_cmp(&rt->r6, - &rt_local6, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) - == 0) - continue; - } - } - break; - - case NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE: - break; - - default: - nm_assert_not_reached(); - break; - } - - 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 int IS_IPv4 = NM_IS_IPv4(addr_family); - 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)]; - - nm_assert(NM_IS_PLATFORM(self)); - 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((NM_IS_IPv4(addr_family) - && NMP_OBJECT_GET_TYPE(prune_o) == NMP_OBJECT_TYPE_IP4_ROUTE) - || (!NM_IS_IPv4(addr_family) - && 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_PRUNE); - 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_PRUNE); - 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), - NM_IS_IPv4(addr_family) ? 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" /* ip4acd_not_ready */ - "", - s_address, - address->plen, - broadcast_address != 0u || address->use_ip4_broadcast_address - ? (address->use_ip4_broadcast_address ? " brd " : " brd* ") - : "", - broadcast_address != 0u || address->use_ip4_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" : "", - address->ip4acd_not_ready ? " ip4acd-not-ready" : ""); - 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 - ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") - : 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 - ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") - : 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, - obj->use_ip4_broadcast_address ? obj->broadcast_address : ((in_addr_t) 0u), - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, - obj->external, - obj->use_ip4_broadcast_address, - obj->ip4acd_not_ready)); - 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_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); - if (a->use_ip4_broadcast_address) - NM_CMP_FIELD(a, b, broadcast_address); - 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); - NM_CMP_FIELD_UNSAFE(a, b, ip4acd_not_ready); - 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, - obj->metric, - 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, - obj->metric, - 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, - obj->metric, - 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, - obj->metric, - 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); - 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); - 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, - obj->metric, - *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, - obj->metric, - *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, - obj->metric, - 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, - obj->metric, - 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); - 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); - 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/core/platform/nm-platform.h b/src/core/platform/nm-platform.h deleted file mode 100644 index d400c568..00000000 --- a/src/core/platform/nm-platform.h +++ /dev/null @@ -1,2372 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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-platform/nmp-base.h" -#include "nm-base/nm-base.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; \ - \ - /* Whether the address is ready to be configured. By default, an address is, but this - * flag may indicate that the address is just for tracking purpose only, but the ACD - * state is not yet ready for the address to be configured. */ \ - bool ip4acd_not_ready : 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 is interpreted as an offset that is added to a default - * metric. For example, form a DHCP lease we don't know the actually used metric, because - * that is determined by upper layers (the configuration). However, we have a default - * metric that should be used. So we set "metric_any" to %TRUE, which means to use - * the default metric. However, we still treat the "metric" field as an offset that - * will be added to the default metric. In most case, you want that "metric" is zero - * when setting "metric_any". */ \ - bool metric_any : 1; \ - \ - /* like "metric_any", the table is determined by other layers of the code. - * This field overrides "table_coerced" field. If "table_any" is true, then - * the "table_coerced" field is ignored (unlike for the metric). */ \ - 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) - * If "metric_any" is %TRUE, then this is interpreted as an offset that will be - * added to a default base metric. In such cases, the offset is usually zero. */ \ - 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_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 NMOptionBool -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_OPTION_BOOL_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_OPTION_BOOL_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, - NMOptionBool 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_station)(NMPlatform * self, - int ifindex, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate); - 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, - NMOptionBool 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); -guint32 nm_platform_wifi_get_frequency(NMPlatform *self, int ifindex); -gboolean nm_platform_wifi_get_station(NMPlatform * self, - int ifindex, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate); -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); - - return r->metric_any ? nm_add_clamped_u32(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, r->metric) - : r->metric; -} - -static inline guint32 -nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r) -{ - nm_assert(r); - - return r->metric_any ? nm_add_clamped_u32(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, r->metric) - : 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); - -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 NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */); - -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); - -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); - -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/core/platform/nmp-object.c b/src/core/platform/nmp-object.c deleted file mode 100644 index 6a26595e..00000000 --- a/src/core/platform/nmp-object.c +++ /dev/null @@ -1,3457 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 - 2018 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.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/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; \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_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 \ - } \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_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; \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_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; \ - } \ - } \ - _NM_DUMMY_STRUCT_FOR_TRAILING_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/core/platform/nmp-object.h b/src/core/platform/nmp-object.h deleted file mode 100644 index 19f6bcd7..00000000 --- a/src/core/platform/nmp-object.h +++ /dev/null @@ -1,1144 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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; - -G_STATIC_ASSERT(sizeof(NMSockAddrUnion) == sizeof(((NMSockAddrUnion *) NULL)->in6)); - -/* we initialize the largest union member, to ensure that all fields are initialized. */ - -#define NM_SOCK_ADDR_UNION_INIT_UNSPEC \ - { \ - .in6 = { \ - .sin6_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; -} - -#define _nm_platform_dedup_multi_iter_next(ipconf_iter, out_obj, field, ...) \ - ({ \ - NMDedupMultiIter *const _ipconf_iter = (ipconf_iter); \ - const typeof(((NMPObject *) NULL)->field) **const _out_obj = (out_obj); \ - gboolean _has_next; \ - \ - if (G_LIKELY(nm_dedup_multi_iter_next(_ipconf_iter))) { \ - if (_out_obj) { \ - *_out_obj = _NMP_OBJECT_CAST(_ipconf_iter->current->obj, field, __VA_ARGS__); \ - } else { \ - nm_assert( \ - NM_IN_SET(NMP_OBJECT_GET_TYPE(_ipconf_iter->current->obj), __VA_ARGS__)); \ - } \ - _has_next = TRUE; \ - } else { \ - if (_out_obj) \ - *_out_obj = NULL; \ - _has_next = FALSE; \ - } \ - _has_next; \ - }) - -#define nm_platform_dedup_multi_iter_next_ip_address(ipconf_iter, out_obj) \ - _nm_platform_dedup_multi_iter_next((ipconf_iter), \ - (out_obj), \ - ip_address, \ - NMP_OBJECT_TYPE_IP4_ADDRESS, \ - NMP_OBJECT_TYPE_IP6_ADDRESS) - -#define nm_platform_dedup_multi_iter_next_ip4_address(ipconf_iter, out_obj) \ - _nm_platform_dedup_multi_iter_next((ipconf_iter), \ - (out_obj), \ - ip4_address, \ - NMP_OBJECT_TYPE_IP4_ADDRESS) - -#define nm_platform_dedup_multi_iter_next_ip6_address(ipconf_iter, out_obj) \ - _nm_platform_dedup_multi_iter_next((ipconf_iter), \ - (out_obj), \ - ip6_address, \ - NMP_OBJECT_TYPE_IP6_ADDRESS) - -#define nm_platform_dedup_multi_iter_next_ip_route(ipconf_iter, out_obj) \ - _nm_platform_dedup_multi_iter_next((ipconf_iter), \ - (out_obj), \ - ip_route, \ - NMP_OBJECT_TYPE_IP4_ROUTE, \ - NMP_OBJECT_TYPE_IP6_ROUTE) - -#define nm_platform_dedup_multi_iter_next_ip4_route(ipconf_iter, out_obj) \ - _nm_platform_dedup_multi_iter_next((ipconf_iter), \ - (out_obj), \ - ip4_route, \ - NMP_OBJECT_TYPE_IP4_ROUTE) - -#define nm_platform_dedup_multi_iter_next_ip6_route(ipconf_iter, out_obj) \ - _nm_platform_dedup_multi_iter_next((ipconf_iter), \ - (out_obj), \ - ip6_route, \ - NMP_OBJECT_TYPE_IP6_ROUTE) - -#endif /* __NMP_OBJECT_H__ */ diff --git a/src/core/platform/nmp-rules-manager.c b/src/core/platform/nmp-rules-manager.c deleted file mode 100644 index 0c617b80..00000000 --- a/src/core/platform/nmp-rules-manager.c +++ /dev/null @@ -1,808 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "src/core/nm-default-daemon.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/core/platform/nmp-rules-manager.h b/src/core/platform/nmp-rules-manager.h deleted file mode 100644 index 69cf9075..00000000 --- a/src/core/platform/nmp-rules-manager.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#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/core/platform/tests/monitor.c b/src/core/platform/tests/monitor.c index abdf2cd5..ff6fae65 100644 --- a/src/core/platform/tests/monitor.c +++ b/src/core/platform/tests/monitor.c @@ -8,7 +8,7 @@ #include <stdlib.h> #include <syslog.h> -#include "platform/nm-linux-platform.h" +#include "libnm-platform/nm-linux-platform.h" #include "nm-test-utils-core.h" diff --git a/src/core/platform/tests/test-address.c b/src/core/platform/tests/test-address.c index d4d67105..4e1d1d6e 100644 --- a/src/core/platform/tests/test-address.c +++ b/src/core/platform/tests/test-address.c @@ -235,7 +235,7 @@ test_ip4_address_general_2(void) /* 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)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, DEVICE_IFINDEX, IFF_UP, TRUE) >= 0); /* Add/delete notification */ nmtstp_ip4_address_add(NULL, EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, 0, NULL); @@ -325,7 +325,7 @@ test_ip4_address_peer(void) g_assert(addr != addr_peer); - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0); accept_signals(address_removed, 0, G_MAXINT); accept_signals(address_added, 0, G_MAXINT); @@ -397,7 +397,7 @@ test_ip4_address_peer_zero(void) peers[1] = addr_peer; peers[2] = 0; - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, ifindex, NULL)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0); nmtst_rand_perm(NULL, r_peers, peers, sizeof(peers[0]), G_N_ELEMENTS(peers)); for (i = 0; i < G_N_ELEMENTS(peers); i++) { diff --git a/src/core/platform/tests/test-cleanup.c b/src/core/platform/tests/test-cleanup.c index b54eca34..e13bb8c8 100644 --- a/src/core/platform/tests/test-cleanup.c +++ b/src/core/platform/tests/test-cleanup.c @@ -44,9 +44,12 @@ test_cleanup_internal(void) 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)); + g_assert( + nm_platform_link_change_flags(NM_PLATFORM_GET, + nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME), + IFF_UP, + TRUE) + >= 0); ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME); g_assert(ifindex > 0); diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c index 1b977145..2fc9e836 100644 --- a/src/core/platform/tests/test-common.c +++ b/src/core/platform/tests/test-common.c @@ -1413,6 +1413,48 @@ nmtstp_ip6_address_del(NMPlatform * platform, } \ G_STMT_END +/* Due to rounding errors with clock_t_to_jiffies()/jiffies_to_clock_t(), kernel cannot + * store all requested values. That means, when we try to configure a bridge with + * the @requested values, the actually configured settings are slightly off, as + * @kernel. + * + * This function takes @requested and returns it as @dst output. All fields + * that might be mangled by kernel (according to @kernel) are adjusted. The + * result is almost identical to @requested, but some fields might be adjusted + * to their @kernel value. */ +const NMPlatformLnkBridge * +nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested, + const NMPlatformLnkBridge *kernel, + NMPlatformLnkBridge * dst) +{ + g_assert(requested); + g_assert(dst); + g_assert(kernel); + + if (dst != requested) + *dst = *requested; + +#define _normalize_field(dst, kernel, field) \ + G_STMT_START \ + { \ + (dst)->field = nmtstp_normalize_jiffies_time((dst)->field, (kernel)->field); \ + } \ + G_STMT_END + + _normalize_field(dst, kernel, forward_delay); + _normalize_field(dst, kernel, hello_time); + _normalize_field(dst, kernel, max_age); + _normalize_field(dst, kernel, ageing_time); + _normalize_field(dst, kernel, mcast_last_member_interval); + _normalize_field(dst, kernel, mcast_membership_interval); + _normalize_field(dst, kernel, mcast_querier_interval); + _normalize_field(dst, kernel, mcast_query_interval); + _normalize_field(dst, kernel, mcast_query_response_interval); + _normalize_field(dst, kernel, mcast_startup_query_interval); + + return dst; +} + const NMPlatformLink * nmtstp_link_bridge_add(NMPlatform * platform, gboolean external_command, @@ -1421,7 +1463,8 @@ nmtstp_link_bridge_add(NMPlatform * platform, { const NMPlatformLink * pllink = NULL; const NMPlatformLnkBridge *ll = NULL; - int r = 0; + NMPlatformLnkBridge lnk_normalized; + int r = 0; g_assert(nm_utils_ifname_valid_kernel(name, NULL)); @@ -1542,10 +1585,9 @@ nmtstp_link_bridge_add(NMPlatform * platform, ll = NMP_OBJECT_CAST_LNK_BRIDGE(NMP_OBJECT_UP_CAST(pllink)->_link.netlink.lnk); - /* account for roundtrip rounding error with clock_t_to_jiffies()/jiffies_to_clock_t(). */ - g_assert_cmpint(lnk->forward_delay, >=, ll->forward_delay - 1); - g_assert_cmpint(lnk->forward_delay, <=, ll->forward_delay); + lnk = nmtstp_link_bridge_normalize_jiffies_time(lnk, ll, &lnk_normalized); + 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); @@ -2246,9 +2288,9 @@ nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifin 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)); + g_assert(nm_platform_link_change_flags(platform, ifindex, IFF_UP, TRUE) >= 0); else - g_assert(nm_platform_link_set_down(platform, ifindex)); + g_assert(nm_platform_link_change_flags(platform, ifindex, IFF_UP, FALSE) >= 0); } /* Let's wait until we get the result */ diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h index 618645fe..1fb97d24 100644 --- a/src/core/platform/tests/test-common.h +++ b/src/core/platform/tests/test-common.h @@ -11,10 +11,10 @@ #include <linux/if_link.h> #include <linux/ip6_tunnel.h> -#include "platform/nm-platform.h" -#include "platform/nmp-object.h" +#include "libnm-platform/nm-platform.h" +#include "libnm-platform/nmp-object.h" #include "platform/nm-fake-platform.h" -#include "platform/nm-linux-platform.h" +#include "libnm-platform/nm-linux-platform.h" #include "nm-test-utils-core.h" @@ -22,6 +22,24 @@ /*****************************************************************************/ +#define nmtstp_normalize_jiffies_time(requested_value, kernel_value) \ + ({ \ + typeof(kernel_value) _kernel_value = (kernel_value); \ + typeof(_kernel_value) _requested_value = (requested_value); \ + \ + /* kernel stores some values (like bridge's forward_delay) in jiffies. When converting + * back and forth (clock_t_to_jiffies()/jiffies_to_clock_t()), the value reported back + * to user space may have rounding errors (of +/- 1), depending on CONFIG_HZ setting. + * + * Normalize the requested_value to the kernel_value, if it look as if a rounding + * error happens. If the difference is larger than +/- 1, no normalization happens! */ \ + \ + ((_requested_value >= (NM_MAX(_kernel_value, 1) - 1)) \ + && (_requested_value <= (NM_MIN(_kernel_value, ~((typeof(_kernel_value)) 0) - 1) + 1))) \ + ? _kernel_value \ + : _requested_value; \ + }) + #define _NMLOG_PREFIX_NAME "platform-test" #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, __VA_ARGS__) @@ -437,6 +455,11 @@ gboolean nmtstp_kernel_support_get(NMPlatformKernelSupportType type); void nmtstp_link_set_updown(NMPlatform *platform, gboolean external_command, int ifindex, gboolean up); +const NMPlatformLnkBridge * +nmtstp_link_bridge_normalize_jiffies_time(const NMPlatformLnkBridge *requested, + const NMPlatformLnkBridge *kernel, + NMPlatformLnkBridge * dst); + const NMPlatformLink *nmtstp_link_bridge_add(NMPlatform * platform, gboolean external_command, const char * name, @@ -519,7 +542,7 @@ _nmtstp_env1_wrapper_setup(const NmtstTestData *test_data) 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)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, *p_ifindex, IFF_UP, TRUE) >= 0); nm_platform_process_events(NM_PLATFORM_GET); diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c index 3aaa3759..f6c3841e 100644 --- a/src/core/platform/tests/test-link.c +++ b/src/core/platform/tests/test-link.c @@ -11,11 +11,11 @@ #include <sys/types.h> #include <linux/if_tun.h> -#include "nm-glib-aux/nm-io-utils.h" -#include "nm-base/nm-ethtool-base.h" -#include "platform/nmp-object.h" -#include "nm-platform/nmp-netns.h" -#include "nm-platform/nm-platform-utils.h" +#include "libnm-glib-aux/nm-io-utils.h" +#include "libnm-base/nm-ethtool-base.h" +#include "libnm-platform/nmp-object.h" +#include "libnm-platform/nmp-netns.h" +#include "libnm-platform/nm-platform-utils.h" #include "test-common.h" #include "nm-test-utils-core.h" @@ -48,13 +48,15 @@ test_bogus(void) 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_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_UP, TRUE) >= 0)); - g_assert(!nm_platform_link_set_down(NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert(!(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_UP, FALSE) >= 0)); - g_assert(!nm_platform_link_set_arp(NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert( + !(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_NOARP, TRUE) >= 0)); - g_assert(!nm_platform_link_set_noarp(NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert( + !(nm_platform_link_change_flags(NM_PLATFORM_GET, BOGUS_IFINDEX, IFF_NOARP, FALSE) >= 0)); g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert(!nm_platform_link_is_connected(NM_PLATFORM_GET, BOGUS_IFINDEX)); @@ -147,7 +149,8 @@ software_add(NMLinkType link_type, const char *name) NM_PLATFORM_SIGNAL_CHANGED, link_callback, parent_ifindex); - g_assert(nm_platform_link_set_up(NM_PLATFORM_GET, parent_ifindex, NULL)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, parent_ifindex, IFF_UP, TRUE) + >= 0); if (was_up) { /* when NM is running in the background, it will mess with addrgenmode which might cause additional signals. */ accept_signals(parent_changed, 0, 1); @@ -233,7 +236,7 @@ test_slave(int master, int type, SignalData *master_changed) * 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_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0); g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); ensure_no_signal(link_changed); @@ -263,7 +266,7 @@ test_slave(int master, int type, SignalData *master_changed) &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_change_flags(NM_PLATFORM_GET, master, IFF_UP, TRUE) >= 0); g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, master)); accept_signals(master_changed, 1, 3); @@ -284,7 +287,7 @@ test_slave(int master, int type, SignalData *master_changed) 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)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0); accept_signal(link_changed); accept_signals(master_changed, 0, 3); break; @@ -317,7 +320,7 @@ test_slave(int master, int type, SignalData *master_changed) } /* 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_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0); g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, master)); accept_signals(link_changed, 1, 3); @@ -439,10 +442,10 @@ test_software(NMLinkType link_type, const char *link_typename) /* 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_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, TRUE) >= 0); g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signals(link_changed, 1, 2); - g_assert(nm_platform_link_set_arp(NM_PLATFORM_GET, ifindex)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, FALSE) >= 0); g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signal(link_changed); @@ -672,21 +675,21 @@ test_internal(void) /* 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_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, TRUE) >= 0); g_assert(nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); g_assert(nm_platform_link_is_connected(NM_PLATFORM_GET, ifindex)); accept_signals(link_changed, 1, 2); - g_assert(nm_platform_link_set_down(NM_PLATFORM_GET, ifindex)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_UP, FALSE) >= 0); g_assert(!nm_platform_link_is_up(NM_PLATFORM_GET, ifindex)); 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_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, FALSE) >= 0); g_assert(nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signal(link_changed); - g_assert(nm_platform_link_set_noarp(NM_PLATFORM_GET, ifindex)); + g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, ifindex, IFF_NOARP, TRUE) >= 0); g_assert(!nm_platform_link_uses_arp(NM_PLATFORM_GET, ifindex)); accept_signal(link_changed); @@ -1303,7 +1306,7 @@ test_software_detect(gconstpointer user_data) 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; + gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0; } if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) { @@ -1330,7 +1333,7 @@ test_software_detect(gconstpointer user_data) 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; + gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip_gre", NULL) != 0; } if (!nmtstp_link_gre_add(NULL, ext, DEVICE_NAME, &lnk_gre)) { @@ -1350,7 +1353,7 @@ test_software_detect(gconstpointer user_data) 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; + gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ipip", NULL) != 0; } lnk_ipip.local = nmtst_inet4_from_string("1.2.3.4"); @@ -1376,7 +1379,7 @@ test_software_detect(gconstpointer user_data) 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; + gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_tunnel", NULL) != 0; } switch (test_data->test_mode) { @@ -1418,7 +1421,7 @@ test_software_detect(gconstpointer user_data) 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; + gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0; } lnk_ip6tnl.local = *nmtst_inet6_from_string("fd01::42"); @@ -1445,7 +1448,7 @@ test_software_detect(gconstpointer user_data) 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; + gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "ip6_gre", NULL) != 0; } lnk_ip6tnl.local = *nmtst_inet6_from_string("fe80::abcd"); @@ -1526,7 +1529,7 @@ test_software_detect(gconstpointer user_data) 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; + gracefully_skip = nmp_utils_modprobe(NULL, TRUE, "sit", NULL) != 0; } if (!nmtstp_link_sit_add(NULL, ext, DEVICE_NAME, &lnk_sit)) { @@ -1679,17 +1682,25 @@ test_software_detect(gconstpointer user_data) case NM_LINK_TYPE_BRIDGE: { const NMPlatformLnkBridge *plnk = &lnk->lnk_bridge; + NMPlatformLnkBridge lnk_bridge_norm_stack; + const NMPlatformLnkBridge *lnk_bridge_norm; g_assert(plnk == nm_platform_link_get_lnk_bridge(NM_PLATFORM_GET, ifindex, NULL)); - 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); + + lnk_bridge_norm = nmtstp_link_bridge_normalize_jiffies_time(&lnk_bridge, + plnk, + &lnk_bridge_norm_stack); + + g_assert_cmpint(nm_platform_lnk_bridge_cmp(lnk_bridge_norm, plnk), ==, 0); + + g_assert_cmpint(plnk->forward_delay, ==, lnk_bridge_norm->forward_delay); + g_assert_cmpint(plnk->hello_time, ==, lnk_bridge_norm->hello_time); + g_assert_cmpint(plnk->max_age, ==, lnk_bridge_norm->max_age); + g_assert_cmpint(plnk->ageing_time, ==, lnk_bridge_norm->ageing_time); g_assert_cmpint(plnk->stp_state, ==, TRUE); g_assert_cmpint(plnk->priority, ==, 22); g_assert_cmpint(plnk->vlan_protocol, ==, 0x8100); - g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge.vlan_stats_enabled); + g_assert_cmpint(plnk->vlan_stats_enabled, ==, lnk_bridge_norm->vlan_stats_enabled); g_assert_cmpint(plnk->group_fwd_mask, ==, 8); g_assert_cmpint(plnk->mcast_snooping, ==, TRUE); g_assert_cmpint(plnk->mcast_router, ==, 1); @@ -1698,13 +1709,22 @@ test_software_detect(gconstpointer user_data) 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); + g_assert_cmpint(plnk->mcast_last_member_interval, + ==, + lnk_bridge_norm->mcast_last_member_interval); + g_assert_cmpint(plnk->mcast_membership_interval, + ==, + lnk_bridge_norm->mcast_membership_interval); + g_assert_cmpint(plnk->mcast_querier_interval, + ==, + lnk_bridge_norm->mcast_querier_interval); + g_assert_cmpint(plnk->mcast_query_interval, ==, lnk_bridge_norm->mcast_query_interval); + g_assert_cmpint(plnk->mcast_query_response_interval, + ==, + lnk_bridge_norm->mcast_query_response_interval); + g_assert_cmpint(plnk->mcast_startup_query_interval, + ==, + lnk_bridge_norm->mcast_startup_query_interval); break; } case NM_LINK_TYPE_GRE: @@ -2049,7 +2069,7 @@ _assert_xgress_qos_mappings_impl(int ifindex, gboolean is_ingress_map, int n_ent _assert_xgress_qos_mappings(ifindex, FALSE, n_entries, __VA_ARGS__) static void -_assert_vlan_flags(int ifindex, NMVlanFlags flags) +_assert_vlan_flags(int ifindex, _NMVlanFlags flags) { const NMPlatformLnkVlan *plnk; @@ -2362,8 +2382,8 @@ test_vlan_set_xgress(void) 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, + _NM_VLAN_FLAG_REORDER_HEADERS | _NM_VLAN_FLAG_GVRP, + _NM_VLAN_FLAG_REORDER_HEADERS, TRUE, ingress_map, G_N_ELEMENTS(ingress_map), @@ -2372,7 +2392,7 @@ test_vlan_set_xgress(void) 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); + _assert_vlan_flags(ifindex, _NM_VLAN_FLAG_REORDER_HEADERS); } { @@ -2393,8 +2413,8 @@ test_vlan_set_xgress(void) g_assert(nm_platform_link_vlan_change(NM_PLATFORM_GET, ifindex, - NM_VLAN_FLAG_GVRP, - NM_VLAN_FLAG_GVRP, + _NM_VLAN_FLAG_GVRP, + _NM_VLAN_FLAG_GVRP, FALSE, ingress_map, G_N_ELEMENTS(ingress_map), @@ -2403,7 +2423,7 @@ test_vlan_set_xgress(void) 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); + _assert_vlan_flags(ifindex, _NM_VLAN_FLAG_REORDER_HEADERS | _NM_VLAN_FLAG_GVRP); } nmtstp_link_delete(NULL, -1, ifindex, DEVICE_NAME, TRUE); diff --git a/src/core/platform/tests/test-nmp-object.c b/src/core/platform/tests/test-nmp-object.c index 310611ee..6d2236ec 100644 --- a/src/core/platform/tests/test-nmp-object.c +++ b/src/core/platform/tests/test-nmp-object.c @@ -8,8 +8,8 @@ #include <libudev.h> #include <linux/pkt_sched.h> -#include "platform/nmp-object.h" -#include "nm-udev-aux/nm-udev-utils.h" +#include "libnm-platform/nmp-object.h" +#include "libnm-udev-aux/nm-udev-utils.h" #include "nm-test-utils-core.h" diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c index b426cc65..05158228 100644 --- a/src/core/platform/tests/test-platform-general.c +++ b/src/core/platform/tests/test-platform-general.c @@ -7,8 +7,8 @@ #include <linux/rtnetlink.h> -#include "nm-platform/nm-platform-utils.h" -#include "platform/nm-linux-platform.h" +#include "libnm-platform/nm-platform-utils.h" +#include "libnm-platform/nm-linux-platform.h" #include "nm-test-utils-core.h" @@ -584,7 +584,7 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) 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]); + NMPlatformIPXAddress *a = (gpointer) (&addresses[i * ELM_SIZE]); guint64 r = rand_map[i]; struct in6_addr * a6; @@ -684,10 +684,10 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data) (gpointer) test_data); for (i = 0; i < N_ADDRESSES; i++) { - const NMPlatformIPXAddress *a = (gconstpointer)(&addresses[i * ELM_SIZE]); + const NMPlatformIPXAddress *a = (gconstpointer) (&addresses[i * ELM_SIZE]); for (j = i + 1; j < N_ADDRESSES; j++) { - const NMPlatformIPXAddress *b = (gconstpointer)(&addresses[j * ELM_SIZE]); + const NMPlatformIPXAddress *b = (gconstpointer) (&addresses[j * ELM_SIZE]); int c1; int c2; diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index 645bb5b1..4b1db7fa 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -9,8 +9,8 @@ #include <linux/fib_rules.h> #include "nm-core-utils.h" -#include "nm-platform/nm-platform-utils.h" -#include "platform/nmp-rules-manager.h" +#include "libnm-platform/nm-platform-utils.h" +#include "libnm-platform/nmp-rules-manager.h" #include "test-common.h" @@ -332,30 +332,33 @@ test_ip4_route(void) /* 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); + 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].is_external = TRUE; + 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[1].is_external = TRUE; + 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); + rts[2].is_external = TRUE; g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -489,30 +492,33 @@ test_ip6_route(void) /* 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; + 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[0].is_external = TRUE; + 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[1].is_external = TRUE; + 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; + rts[2].is_external = TRUE; g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip6_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -709,6 +715,7 @@ test_ip4_route_options(gconstpointer test_data) 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])); + rts_cmp[i].is_external = TRUE; } routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, IFINDEX); @@ -880,6 +887,7 @@ test_ip6_route_options(gconstpointer test_data) 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])); + rts_cmp[i].is_external = TRUE; } routes = nmtstp_ip6_route_get_all(NM_PLATFORM_GET, IFINDEX); diff --git a/src/core/platform/tests/test-tc.c b/src/core/platform/tests/test-tc.c index d15c3d76..cd9536cc 100644 --- a/src/core/platform/tests/test-tc.c +++ b/src/core/platform/tests/test-tc.c @@ -5,9 +5,9 @@ #include <linux/pkt_sched.h> #include "nm-test-utils-core.h" -#include "platform/nmp-object.h" -#include "nm-platform/nmp-netns.h" -#include "nm-platform/nm-platform-utils.h" +#include "libnm-platform/nmp-object.h" +#include "libnm-platform/nmp-netns.h" +#include "libnm-platform/nm-platform-utils.h" #include "test-common.h" static NMPObject * diff --git a/src/core/platform/wifi/nm-wifi-utils-nl80211.c b/src/core/platform/wifi/nm-wifi-utils-nl80211.c deleted file mode 100644 index ad067248..00000000 --- a/src/core/platform/wifi/nm-wifi-utils-nl80211.c +++ /dev/null @@ -1,909 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2005 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - * Copyright (C) 2011 Intel Corporation. All rights reserved. - */ - -#include "src/core/nm-default-daemon.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 "nm-platform/nm-netlink.h" -#include "nm-wifi-utils-private.h" -#include "platform/nm-platform.h" -#include "nm-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; - uint32_t freq; -}; - -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; - } - - if (tb[NL80211_ATTR_WIPHY_FREQ] != NULL) - info->freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); - - 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); -} - -static guint32 -wifi_nl80211_get_freq(NMWifiUtils *data) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - struct nl80211_iface_info iface_info = {}; - 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 0; - - return iface_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; -} - -/* @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_station_info { - gboolean valid; - guint8 bssid[ETH_ALEN]; - guint32 txrate; - gboolean txrate_valid; - guint8 signal; - gboolean signal_valid; -}; - -static int -nl80211_station_dump_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}, - [NL80211_STA_INFO_STA_FLAGS] = {.minlen = sizeof(struct nl80211_sta_flag_update)}, - [NL80211_STA_INFO_BEACON_SIGNAL_AVG] = {.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_MAC] == NULL) - 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_STA_FLAGS] != NULL) { - const struct nl80211_sta_flag_update *flags = nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]); - - if (flags->mask & ~flags->set & (1 << NL80211_STA_FLAG_ASSOCIATED)) - return NL_SKIP; - } - - memcpy(info->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - info->valid = TRUE; - - if (sinfo[NL80211_STA_INFO_TX_BITRATE] != NULL - && !nla_parse_nested_arr(rinfo, sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy) - && rinfo[NL80211_RATE_INFO_BITRATE] != NULL) { - /* 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; - } else if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG] != NULL) { - /* Fall back to beacon signal strength */ - info->signal = - nl80211_xbm_to_percent((gint8) nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]), - 1); - info->signal_valid = TRUE; - } - - return NL_SKIP; -} - -static gboolean -wifi_nl80211_get_station(NMWifiUtils *data, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate) -{ - NMWifiUtilsNl80211 * self = (NMWifiUtilsNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nl80211_station_info sta_info = {}; - - msg = nl80211_alloc_msg(self, NL80211_CMD_GET_STATION, NLM_F_DUMP); - - nl80211_send_and_recv(self, msg, nl80211_station_dump_handler, &sta_info); - - if (!sta_info.valid || (out_quality && !sta_info.signal_valid) - || (out_rate && !sta_info.txrate_valid)) - return FALSE; - - if (out_bssid) - memcpy(out_bssid, sta_info.bssid, ETH_ALEN); - - if (out_quality) - *out_quality = sta_info.signal; - - if (out_rate) - *out_rate = sta_info.txrate; - - return TRUE; -} - -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_station = wifi_nl80211_get_station; - 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/core/platform/wifi/nm-wifi-utils-nl80211.h b/src/core/platform/wifi/nm-wifi-utils-nl80211.h deleted file mode 100644 index 37c32ed7..00000000 --- a/src/core/platform/wifi/nm-wifi-utils-nl80211.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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 "nm-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/core/platform/wifi/nm-wifi-utils-private.h b/src/core/platform/wifi/nm-wifi-utils-private.h deleted file mode 100644 index 9ed57453..00000000 --- a/src/core/platform/wifi/nm-wifi-utils-private.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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); - - /* - * @out_bssid: must be NULL or an ETH_ALEN-byte buffer - * @out_quality: receives signal strength percentage 0 - 100% for the current BSSID, if not NULL - * @out_rate: receives current bitrate in Kbps if not NULL - * - * Returns %TRUE on succcess, %FALSE on errors or if not associated. - */ - gboolean (*get_station)(NMWifiUtils *data, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate); - - /* 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/core/platform/wifi/nm-wifi-utils-wext.c b/src/core/platform/wifi/nm-wifi-utils-wext.c deleted file mode 100644 index a44fcf62..00000000 --- a/src/core/platform/wifi/nm-wifi-utils-wext.c +++ /dev/null @@ -1,836 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2005 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "src/core/nm-default-daemon.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 "nm-platform/nm-platform-utils.h" -#include "nm-core-internal.h" -#include "nm-core-utils.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, NMEtherAddr *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); -} - -static gboolean -wifi_wext_get_station(NMWifiUtils *data, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate) -{ - NMEtherAddr local_addr; - - if (!out_bssid && !out_quality && !out_rate) { - /* hm, the caller requested no parameter at all? - * Don't simply return TRUE, but at least check that - * we can successfully fetch the bssid. */ - out_bssid = &local_addr; - } - - if (out_bssid) { - if (!wifi_wext_get_bssid(data, out_bssid)) - return FALSE; - } - if (out_quality) { - *out_quality = wifi_wext_get_qual(data); - if (*out_quality < 0) - return FALSE; - } - if (out_rate) { - *out_rate = wifi_wext_get_rate(data); - if (*out_rate == 0) - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ -/* 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_station = wifi_wext_get_station; - 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/core/platform/wifi/nm-wifi-utils-wext.h b/src/core/platform/wifi/nm-wifi-utils-wext.h deleted file mode 100644 index d6f3453c..00000000 --- a/src/core/platform/wifi/nm-wifi-utils-wext.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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/core/platform/wifi/nm-wifi-utils.c b/src/core/platform/wifi/nm-wifi-utils.c deleted file mode 100644 index 49ae1811..00000000 --- a/src/core/platform/wifi/nm-wifi-utils.c +++ /dev/null @@ -1,211 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2005 - 2018 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "src/core/nm-default-daemon.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 "nm-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_station(NMWifiUtils *data, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate) -{ - g_return_val_if_fail(data != NULL, FALSE); - - return NM_WIFI_UTILS_GET_CLASS(data)->get_station(data, out_bssid, out_quality, out_rate); -} - -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/core/platform/wifi/nm-wifi-utils.h b/src/core/platform/wifi/nm-wifi-utils.h deleted file mode 100644 index a252e43e..00000000 --- a/src/core/platform/wifi/nm-wifi-utils.h +++ /dev/null @@ -1,74 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * 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 "nm-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 NULL or an ETH_ALEN-byte buffer - * @out_quality: receives signal quality in 0 - 100% range if not NULL - * @out_rate: receives current bitrate in Kbps if not NULL - * - * Returns %TRUE on succcess. - */ -gboolean nm_wifi_utils_get_station(NMWifiUtils *data, - NMEtherAddr *out_bssid, - int * out_quality, - guint32 * out_rate); - -/* 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/core/platform/wpan/nm-wpan-utils.c b/src/core/platform/wpan/nm-wpan-utils.c deleted file mode 100644 index efb19979..00000000 --- a/src/core/platform/wpan/nm-wpan-utils.c +++ /dev/null @@ -1,286 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "src/core/nm-default-daemon.h" - -#include "nm-wpan-utils.h" - -#include <linux/if.h> - -#include "nm-platform/nm-netlink.h" -#include "platform/linux/nl802154.h" -#include "nm-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/core/platform/wpan/nm-wpan-utils.h b/src/core/platform/wpan/nm-wpan-utils.h deleted file mode 100644 index 940c2c9d..00000000 --- a/src/core/platform/wpan/nm-wpan-utils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __WPAN_UTILS_H__ -#define __WPAN_UTILS_H__ - -#include <net/ethernet.h> - -#include "nm-dbus-interface.h" -#include "nm-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__ */ |