diff options
| author | Michael Biebl <biebl@debian.org> | 2018-10-20 01:30:31 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2018-10-20 01:30:31 +0200 |
| commit | 6518e361171f64bcaaa4bf868139362ed95cc2e0 (patch) | |
| tree | d2d5b53faf80646a40ec2c0c7f2a42b3959612f5 /src/platform | |
| parent | e126f3e804c35480c4f075777430419d6ece23da (diff) | |
New upstream version 1.14.2 upstream/1.14.2
Diffstat (limited to 'src/platform')
27 files changed, 4728 insertions, 1376 deletions
diff --git a/src/platform/linux/nl802154.h b/src/platform/linux/nl802154.h new file mode 100644 index 00000000..ddcee128 --- /dev/null +++ b/src/platform/linux/nl802154.h @@ -0,0 +1,452 @@ +#ifndef __NL802154_H +#define __NL802154_H +/* + * 802.15.4 netlink interface public header + * + * Copyright 2014 Alexander Aring <aar@pengutronix.de> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#define NL802154_GENL_NAME "nl802154" + +enum nl802154_commands { +/* don't change the order or add anything between, this is ABI! */ +/* currently we don't shipping this file via uapi, ignore the above one */ + NL802154_CMD_UNSPEC, + + NL802154_CMD_GET_WPAN_PHY, /* can dump */ + NL802154_CMD_SET_WPAN_PHY, + NL802154_CMD_NEW_WPAN_PHY, + NL802154_CMD_DEL_WPAN_PHY, + + NL802154_CMD_GET_INTERFACE, /* can dump */ + NL802154_CMD_SET_INTERFACE, + NL802154_CMD_NEW_INTERFACE, + NL802154_CMD_DEL_INTERFACE, + + NL802154_CMD_SET_CHANNEL, + + NL802154_CMD_SET_PAN_ID, + NL802154_CMD_SET_SHORT_ADDR, + + NL802154_CMD_SET_TX_POWER, + NL802154_CMD_SET_CCA_MODE, + NL802154_CMD_SET_CCA_ED_LEVEL, + + NL802154_CMD_SET_MAX_FRAME_RETRIES, + + NL802154_CMD_SET_BACKOFF_EXPONENT, + NL802154_CMD_SET_MAX_CSMA_BACKOFFS, + + NL802154_CMD_SET_LBT_MODE, + + NL802154_CMD_SET_ACKREQ_DEFAULT, + + NL802154_CMD_SET_WPAN_PHY_NETNS, + + /* add new commands above here */ + +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + NL802154_CMD_SET_SEC_PARAMS, + NL802154_CMD_GET_SEC_KEY, /* can dump */ + NL802154_CMD_NEW_SEC_KEY, + NL802154_CMD_DEL_SEC_KEY, + NL802154_CMD_GET_SEC_DEV, /* can dump */ + NL802154_CMD_NEW_SEC_DEV, + NL802154_CMD_DEL_SEC_DEV, + NL802154_CMD_GET_SEC_DEVKEY, /* can dump */ + NL802154_CMD_NEW_SEC_DEVKEY, + NL802154_CMD_DEL_SEC_DEVKEY, + NL802154_CMD_GET_SEC_LEVEL, /* can dump */ + NL802154_CMD_NEW_SEC_LEVEL, + NL802154_CMD_DEL_SEC_LEVEL, +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + + /* used to define NL802154_CMD_MAX below */ + __NL802154_CMD_AFTER_LAST, + NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1 +}; + +enum nl802154_attrs { +/* don't change the order or add anything between, this is ABI! */ +/* currently we don't shipping this file via uapi, ignore the above one */ + NL802154_ATTR_UNSPEC, + + NL802154_ATTR_WPAN_PHY, + NL802154_ATTR_WPAN_PHY_NAME, + + NL802154_ATTR_IFINDEX, + NL802154_ATTR_IFNAME, + NL802154_ATTR_IFTYPE, + + NL802154_ATTR_WPAN_DEV, + + NL802154_ATTR_PAGE, + NL802154_ATTR_CHANNEL, + + NL802154_ATTR_PAN_ID, + NL802154_ATTR_SHORT_ADDR, + + NL802154_ATTR_TX_POWER, + + NL802154_ATTR_CCA_MODE, + NL802154_ATTR_CCA_OPT, + NL802154_ATTR_CCA_ED_LEVEL, + + NL802154_ATTR_MAX_FRAME_RETRIES, + + NL802154_ATTR_MAX_BE, + NL802154_ATTR_MIN_BE, + NL802154_ATTR_MAX_CSMA_BACKOFFS, + + NL802154_ATTR_LBT_MODE, + + NL802154_ATTR_GENERATION, + + NL802154_ATTR_CHANNELS_SUPPORTED, + NL802154_ATTR_SUPPORTED_CHANNEL, + + NL802154_ATTR_EXTENDED_ADDR, + + NL802154_ATTR_WPAN_PHY_CAPS, + + NL802154_ATTR_SUPPORTED_COMMANDS, + + NL802154_ATTR_ACKREQ_DEFAULT, + + NL802154_ATTR_PAD, + + NL802154_ATTR_PID, + NL802154_ATTR_NETNS_FD, + + /* add attributes here, update the policy in nl802154.c */ + +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + NL802154_ATTR_SEC_ENABLED, + NL802154_ATTR_SEC_OUT_LEVEL, + NL802154_ATTR_SEC_OUT_KEY_ID, + NL802154_ATTR_SEC_FRAME_COUNTER, + + NL802154_ATTR_SEC_LEVEL, + NL802154_ATTR_SEC_DEVICE, + NL802154_ATTR_SEC_DEVKEY, + NL802154_ATTR_SEC_KEY, +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + + __NL802154_ATTR_AFTER_LAST, + NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_iftype { + /* for backwards compatibility TODO */ + NL802154_IFTYPE_UNSPEC = -1, + + NL802154_IFTYPE_NODE, + NL802154_IFTYPE_MONITOR, + NL802154_IFTYPE_COORD, + + /* keep last */ + NUM_NL802154_IFTYPES, + NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1 +}; + +/** + * enum nl802154_wpan_phy_capability_attr - wpan phy capability attributes + * + * @__NL802154_CAP_ATTR_INVALID: attribute number 0 is reserved + * @NL802154_CAP_ATTR_CHANNELS: a nested attribute for nl802154_channel_attr + * @NL802154_CAP_ATTR_TX_POWERS: a nested attribute for + * nl802154_wpan_phy_tx_power + * @NL802154_CAP_ATTR_MIN_CCA_ED_LEVEL: minimum value for cca_ed_level + * @NL802154_CAP_ATTR_MAX_CCA_ED_LEVEL: maxmimum value for cca_ed_level + * @NL802154_CAP_ATTR_CCA_MODES: nl802154_cca_modes flags + * @NL802154_CAP_ATTR_CCA_OPTS: nl802154_cca_opts flags + * @NL802154_CAP_ATTR_MIN_MINBE: minimum of minbe value + * @NL802154_CAP_ATTR_MAX_MINBE: maximum of minbe value + * @NL802154_CAP_ATTR_MIN_MAXBE: minimum of maxbe value + * @NL802154_CAP_ATTR_MAX_MINBE: maximum of maxbe value + * @NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS: minimum of csma backoff value + * @NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS: maximum of csma backoffs value + * @NL802154_CAP_ATTR_MIN_FRAME_RETRIES: minimum of frame retries value + * @NL802154_CAP_ATTR_MAX_FRAME_RETRIES: maximum of frame retries value + * @NL802154_CAP_ATTR_IFTYPES: nl802154_iftype flags + * @NL802154_CAP_ATTR_LBT: nl802154_supported_bool_states flags + * @NL802154_CAP_ATTR_MAX: highest cap attribute currently defined + * @__NL802154_CAP_ATTR_AFTER_LAST: internal use + */ +enum nl802154_wpan_phy_capability_attr { + __NL802154_CAP_ATTR_INVALID, + + NL802154_CAP_ATTR_IFTYPES, + + NL802154_CAP_ATTR_CHANNELS, + NL802154_CAP_ATTR_TX_POWERS, + + NL802154_CAP_ATTR_CCA_ED_LEVELS, + NL802154_CAP_ATTR_CCA_MODES, + NL802154_CAP_ATTR_CCA_OPTS, + + NL802154_CAP_ATTR_MIN_MINBE, + NL802154_CAP_ATTR_MAX_MINBE, + + NL802154_CAP_ATTR_MIN_MAXBE, + NL802154_CAP_ATTR_MAX_MAXBE, + + NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS, + NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS, + + NL802154_CAP_ATTR_MIN_FRAME_RETRIES, + NL802154_CAP_ATTR_MAX_FRAME_RETRIES, + + NL802154_CAP_ATTR_LBT, + + /* keep last */ + __NL802154_CAP_ATTR_AFTER_LAST, + NL802154_CAP_ATTR_MAX = __NL802154_CAP_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl802154_cca_modes - cca modes + * + * @__NL802154_CCA_INVALID: cca mode number 0 is reserved + * @NL802154_CCA_ENERGY: Energy above threshold + * @NL802154_CCA_CARRIER: Carrier sense only + * @NL802154_CCA_ENERGY_CARRIER: Carrier sense with energy above threshold + * @NL802154_CCA_ALOHA: CCA shall always report an idle medium + * @NL802154_CCA_UWB_SHR: UWB preamble sense based on the SHR of a frame + * @NL802154_CCA_UWB_MULTIPLEXED: UWB preamble sense based on the packet with + * the multiplexed preamble + * @__NL802154_CCA_ATTR_AFTER_LAST: Internal + * @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number + */ +enum nl802154_cca_modes { + __NL802154_CCA_INVALID, + NL802154_CCA_ENERGY, + NL802154_CCA_CARRIER, + NL802154_CCA_ENERGY_CARRIER, + NL802154_CCA_ALOHA, + NL802154_CCA_UWB_SHR, + NL802154_CCA_UWB_MULTIPLEXED, + + /* keep last */ + __NL802154_CCA_ATTR_AFTER_LAST, + NL802154_CCA_ATTR_MAX = __NL802154_CCA_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl802154_cca_opts - additional options for cca modes + * + * @NL802154_CCA_OPT_ENERGY_CARRIER_OR: NL802154_CCA_ENERGY_CARRIER with OR + * @NL802154_CCA_OPT_ENERGY_CARRIER_AND: NL802154_CCA_ENERGY_CARRIER with AND + */ +enum nl802154_cca_opts { + NL802154_CCA_OPT_ENERGY_CARRIER_AND, + NL802154_CCA_OPT_ENERGY_CARRIER_OR, + + /* keep last */ + __NL802154_CCA_OPT_ATTR_AFTER_LAST, + NL802154_CCA_OPT_ATTR_MAX = __NL802154_CCA_OPT_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl802154_supported_bool_states - bool states for bool capability entry + * + * @NL802154_SUPPORTED_BOOL_FALSE: indicates to set false + * @NL802154_SUPPORTED_BOOL_TRUE: indicates to set true + * @__NL802154_SUPPORTED_BOOL_INVALD: reserved + * @NL802154_SUPPORTED_BOOL_BOTH: indicates to set true and false + * @__NL802154_SUPPORTED_BOOL_AFTER_LAST: Internal + * @NL802154_SUPPORTED_BOOL_MAX: highest value for bool states + */ +enum nl802154_supported_bool_states { + NL802154_SUPPORTED_BOOL_FALSE, + NL802154_SUPPORTED_BOOL_TRUE, + /* to handle them in a mask */ + __NL802154_SUPPORTED_BOOL_INVALD, + NL802154_SUPPORTED_BOOL_BOTH, + + /* keep last */ + __NL802154_SUPPORTED_BOOL_AFTER_LAST, + NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1 +}; + +#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL + +enum nl802154_dev_addr_modes { + NL802154_DEV_ADDR_NONE, + __NL802154_DEV_ADDR_INVALID, + NL802154_DEV_ADDR_SHORT, + NL802154_DEV_ADDR_EXTENDED, + + /* keep last */ + __NL802154_DEV_ADDR_AFTER_LAST, + NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1 +}; + +enum nl802154_dev_addr_attrs { + NL802154_DEV_ADDR_ATTR_UNSPEC, + + NL802154_DEV_ADDR_ATTR_PAN_ID, + NL802154_DEV_ADDR_ATTR_MODE, + NL802154_DEV_ADDR_ATTR_SHORT, + NL802154_DEV_ADDR_ATTR_EXTENDED, + NL802154_DEV_ADDR_ATTR_PAD, + + /* keep last */ + __NL802154_DEV_ADDR_ATTR_AFTER_LAST, + NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_key_id_modes { + NL802154_KEY_ID_MODE_IMPLICIT, + NL802154_KEY_ID_MODE_INDEX, + NL802154_KEY_ID_MODE_INDEX_SHORT, + NL802154_KEY_ID_MODE_INDEX_EXTENDED, + + /* keep last */ + __NL802154_KEY_ID_MODE_AFTER_LAST, + NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1 +}; + +enum nl802154_key_id_attrs { + NL802154_KEY_ID_ATTR_UNSPEC, + + NL802154_KEY_ID_ATTR_MODE, + NL802154_KEY_ID_ATTR_INDEX, + NL802154_KEY_ID_ATTR_IMPLICIT, + NL802154_KEY_ID_ATTR_SOURCE_SHORT, + NL802154_KEY_ID_ATTR_SOURCE_EXTENDED, + NL802154_KEY_ID_ATTR_PAD, + + /* keep last */ + __NL802154_KEY_ID_ATTR_AFTER_LAST, + NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_seclevels { + NL802154_SECLEVEL_NONE, + NL802154_SECLEVEL_MIC32, + NL802154_SECLEVEL_MIC64, + NL802154_SECLEVEL_MIC128, + NL802154_SECLEVEL_ENC, + NL802154_SECLEVEL_ENC_MIC32, + NL802154_SECLEVEL_ENC_MIC64, + NL802154_SECLEVEL_ENC_MIC128, + + /* keep last */ + __NL802154_SECLEVEL_AFTER_LAST, + NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1 +}; + +enum nl802154_frames { + NL802154_FRAME_BEACON, + NL802154_FRAME_DATA, + NL802154_FRAME_ACK, + NL802154_FRAME_CMD, + + /* keep last */ + __NL802154_FRAME_AFTER_LAST, + NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1 +}; + +enum nl802154_cmd_frames { + __NL802154_CMD_FRAME_INVALID, + NL802154_CMD_FRAME_ASSOC_REQUEST, + NL802154_CMD_FRAME_ASSOC_RESPONSE, + NL802154_CMD_FRAME_DISASSOC_NOTIFY, + NL802154_CMD_FRAME_DATA_REQUEST, + NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY, + NL802154_CMD_FRAME_ORPHAN_NOTIFY, + NL802154_CMD_FRAME_BEACON_REQUEST, + NL802154_CMD_FRAME_COORD_REALIGNMENT, + NL802154_CMD_FRAME_GTS_REQUEST, + + /* keep last */ + __NL802154_CMD_FRAME_AFTER_LAST, + NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1 +}; + +enum nl802154_seclevel_attrs { + NL802154_SECLEVEL_ATTR_UNSPEC, + + NL802154_SECLEVEL_ATTR_LEVELS, + NL802154_SECLEVEL_ATTR_FRAME, + NL802154_SECLEVEL_ATTR_CMD_FRAME, + NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, + + /* keep last */ + __NL802154_SECLEVEL_ATTR_AFTER_LAST, + NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1 +}; + +/* TODO what is this? couldn't find in mib */ +enum { + NL802154_DEVKEY_IGNORE, + NL802154_DEVKEY_RESTRICT, + NL802154_DEVKEY_RECORD, + + /* keep last */ + __NL802154_DEVKEY_AFTER_LAST, + NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1 +}; + +enum nl802154_dev { + NL802154_DEV_ATTR_UNSPEC, + + NL802154_DEV_ATTR_FRAME_COUNTER, + NL802154_DEV_ATTR_PAN_ID, + NL802154_DEV_ATTR_SHORT_ADDR, + NL802154_DEV_ATTR_EXTENDED_ADDR, + NL802154_DEV_ATTR_SECLEVEL_EXEMPT, + NL802154_DEV_ATTR_KEY_MODE, + NL802154_DEV_ATTR_PAD, + + /* keep last */ + __NL802154_DEV_ATTR_AFTER_LAST, + NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_devkey { + NL802154_DEVKEY_ATTR_UNSPEC, + + NL802154_DEVKEY_ATTR_FRAME_COUNTER, + NL802154_DEVKEY_ATTR_EXTENDED_ADDR, + NL802154_DEVKEY_ATTR_ID, + NL802154_DEVKEY_ATTR_PAD, + + /* keep last */ + __NL802154_DEVKEY_ATTR_AFTER_LAST, + NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1 +}; + +enum nl802154_key { + NL802154_KEY_ATTR_UNSPEC, + + NL802154_KEY_ATTR_ID, + NL802154_KEY_ATTR_USAGE_FRAMES, + NL802154_KEY_ATTR_USAGE_CMDS, + NL802154_KEY_ATTR_BYTES, + + /* keep last */ + __NL802154_KEY_ATTR_AFTER_LAST, + NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1 +}; + +#define NL802154_KEY_SIZE 16 +#define NL802154_CMD_FRAME_NR_IDS 256 + +#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ + +#endif /* __NL802154_H */ diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index f8f4627e..82f9e2fb 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -602,12 +602,6 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) return NM_PLATFORM_ERROR_SUCCESS; } -static gboolean -link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs) -{ - return TRUE; -} - static const char * link_get_udi (NMPlatform *platform, int ifindex) { @@ -893,12 +887,6 @@ wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid) return FALSE; } -static GByteArray * -wifi_get_ssid (NMPlatform *platform, int ifindex) -{ - return NULL; -} - static guint32 wifi_get_frequency (NMPlatform *platform, int ifindex) { @@ -1422,7 +1410,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_set_address = link_set_address; platform_class->link_set_mtu = link_set_mtu; - platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs; platform_class->link_get_driver_info = link_get_driver_info; @@ -1442,7 +1429,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->wifi_get_capabilities = wifi_get_capabilities; platform_class->wifi_get_bssid = wifi_get_bssid; - platform_class->wifi_get_ssid = wifi_get_ssid; platform_class->wifi_get_frequency = wifi_get_frequency; platform_class->wifi_get_quality = wifi_get_quality; platform_class->wifi_get_rate = wifi_get_rate; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 9bd662a1..e73d5d8c 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2012 - 2017 Red Hat, Inc. + * Copyright (C) 2012 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -44,15 +44,18 @@ #include "nm-core-internal.h" #include "nm-setting-vlan.h" +#include "nm-utils/nm-secret-utils.h" #include "nm-netlink.h" #include "nm-core-utils.h" #include "nmp-object.h" #include "nmp-netns.h" #include "nm-platform-utils.h" #include "nm-platform-private.h" -#include "wifi/wifi-utils.h" -#include "wifi/wifi-utils-wext.h" +#include "wifi/nm-wifi-utils.h" +#include "wifi/nm-wifi-utils-wext.h" +#include "wpan/nm-wpan-utils.h" #include "nm-utils/unaligned.h" +#include "nm-utils/nm-io-utils.h" #include "nm-utils/nm-udev-utils.h" /*****************************************************************************/ @@ -154,6 +157,13 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +/* Appeared in in kernel prior to 3.13 dated 19 January, 2014 */ +#ifndef ARPHRD_6LOWPAN +#define ARPHRD_6LOWPAN 825 +#endif + +/*****************************************************************************/ + #define IFLA_MACSEC_UNSPEC 0 #define IFLA_MACSEC_SCI 1 #define IFLA_MACSEC_PORT 2 @@ -173,63 +183,79 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ -#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 WG_CMD_GET_DEVICE 0 +#define WG_CMD_SET_DEVICE 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 -#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 +/* 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; +}; -#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 alread 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__), \ - g_strerror (__errsv), __errsv); \ - } \ - } G_STMT_END +struct _ifla_vf_rate { + guint32 vf; + guint32 min_tx_rate; + guint32 max_tx_rate; +}; -#define LOG_FMT_IP_TUNNEL "adding %s '%s' parent %u local %s remote %s" +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 */ +}; -/****************************************************************** - * Forward declarations and enums - ******************************************************************/ +/*****************************************************************************/ typedef enum { INFINIBAND_ACTION_CREATE_CHILD, @@ -307,6 +333,133 @@ typedef enum { 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; + guint32 nlh_seq_next; +#if NM_MORE_LOGGING + guint32 nlh_seq_last_handled; +#endif + guint32 nlh_seq_last_seen; + GIOChannel *event_channel; + guint event_id; + + bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + + bool sysctl_get_warned; + GHashTable *sysctl_get_prev_values; + + 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[_DELAYED_ACTION_IDX_REFRESH_ALL_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 alread 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__), \ + g_strerror (__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); @@ -317,6 +470,7 @@ static void cache_on_change (NMPlatform *platform, 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); /*****************************************************************************/ @@ -533,6 +687,8 @@ static const LinkDesc linktypes[] = { { 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 }, @@ -540,6 +696,8 @@ static const LinkDesc linktypes[] = { { 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 }, @@ -552,6 +710,7 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, { NM_LINK_TYPE_VLAN, "vlan", "vlan", "vlan" }, { 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" }, @@ -725,19 +884,21 @@ _addrtime_get_lifetimes (guint32 timestamp, /*****************************************************************************/ static const NMPObject * -_lookup_cached_link (const NMPCache *cache, int ifindex, gboolean *completed_from_cache, const NMPObject **link_cached) +_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; + obj = ifindex > 0 && cache + ? nmp_cache_lookup_link (cache, ifindex) + : NULL; - if (obj && obj->_link.netlink.is_in_netlink) - *link_cached = obj; - else - *link_cached = NULL; + *link_cached = obj; *completed_from_cache = TRUE; } return *link_cached; @@ -755,7 +916,9 @@ _linktype_read_devtype (int dirfd) nm_assert (dirfd >= 0); - if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024, &contents, NULL, NULL) < 0) + if (nm_utils_file_get_contents (dirfd, "uevent", 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, NULL, NULL) < 0) return NULL; for (cont = contents; cont; cont = end) { end = strpbrk (cont, "\r\n"); @@ -806,6 +969,7 @@ _linktype_get_type (NMPlatform *platform, * when moving interfce 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 @@ -841,6 +1005,10 @@ _linktype_get_type (NMPlatform *platform, 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; @@ -885,7 +1053,7 @@ _linktype_get_type (NMPlatform *platform, } /* Fallback for drivers that don't call SET_NETDEV_DEVTYPE() */ - if (wifi_utils_is_wifi (dirfd, ifname_verified)) + if (nm_wifi_utils_is_wifi (dirfd, ifname_verified)) return NM_LINK_TYPE_WIFI; } @@ -953,18 +1121,21 @@ _nl_addattr_l (struct nlmsghdr *n, * NMPObject/netlink functions ******************************************************************/ -#define _check_addr_or_errout(tb, attr, addr_len) \ +#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)) { \ - goto errout; \ + 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(). */ @@ -991,20 +1162,19 @@ _parse_af_inet6 (NMPlatform *platform, gboolean token_valid = FALSE; gboolean addr_gen_mode_valid = FALSE; guint8 i6_addr_gen_mode_inv = 0; - gboolean success = FALSE; err = nla_parse_nested (tb, IFLA_INET6_MAX, attr, policy); if (err < 0) - goto errout; + return FALSE; if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) - goto errout; + return FALSE; if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) - goto errout; + return FALSE; if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) - goto errout; + return FALSE; - if (_check_addr_or_errout (tb, IFLA_INET6_TOKEN, sizeof (struct in6_addr))) { + 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; } @@ -1020,12 +1190,11 @@ _parse_af_inet6 (NMPlatform *platform, if (i6_addr_gen_mode_inv == 0) { /* an inverse addrgenmode of zero is unexpected. We need to reserve zero * to signal "unset". */ - goto errout; + return FALSE; } addr_gen_mode_valid = TRUE; } - success = TRUE; if (token_valid) { *out_token_valid = token_valid; nm_utils_ipv6_interface_identifier_get_from_addr (out_token, &i6_token); @@ -1034,8 +1203,7 @@ _parse_af_inet6 (NMPlatform *platform, *out_addr_gen_mode_valid = addr_gen_mode_valid; *out_addr_gen_mode_inv = i6_addr_gen_mode_inv; } -errout: - return success; + return TRUE; } /*****************************************************************************/ @@ -1059,15 +1227,23 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data) int err; NMPObject *obj; NMPlatformLnkGre *props; + gboolean is_tap; + + if (!info_data || !kind) + return NULL; - if (!info_data || g_strcmp0 (kind, "gre")) + if (nm_streq (kind, "gretap")) + is_tap = TRUE; + else if (nm_streq (kind, "gre")) + is_tap = FALSE; + else return NULL; err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, policy); if (err < 0) return NULL; - obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_GRE, 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; @@ -1080,6 +1256,7 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data) 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; } @@ -1200,6 +1377,79 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) return obj; } +static NMPObject * +_parse_lnk_ip6gre (const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[IFLA_GRE_MAX + 1] = { + [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[IFLA_GRE_MAX + 1]; + int err; + 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; + + err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, policy); + if (err < 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]) + memcpy (&props->local, nla_data (tb[IFLA_GRE_LOCAL]), sizeof (props->local)); + if (tb[IFLA_GRE_REMOTE]) + memcpy (&props->remote, nla_data (tb[IFLA_GRE_REMOTE]), sizeof (props->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 * @@ -1663,6 +1913,378 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data) /*****************************************************************************/ +static gboolean +_wireguard_update_from_allowed_ips_nla (NMPWireGuardAllowedIP *allowed_ip, + struct nlattr *nlattr) +{ + static const struct nla_policy policy[WGALLOWEDIP_A_MAX + 1] = { + [WGALLOWEDIP_A_FAMILY] = { .type = NLA_U16 }, + [WGALLOWEDIP_A_IPADDR] = { .minlen = sizeof (struct in_addr) }, + [WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8 }, + }; + struct nlattr *tb[WGALLOWEDIP_A_MAX + 1]; + int family; + int addr_len; + + if (nla_parse_nested (tb, WGALLOWEDIP_A_MAX, 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); + + memset (allowed_ip, 0, sizeof (NMPWireGuardAllowedIP)); + + allowed_ip->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_MAX + 1] = { + [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 }, + }; + WireGuardPeerConstruct *peer_c; + struct nlattr *tb[WGPEER_A_MAX + 1]; + + if (nla_parse_nested (tb, WGPEER_A_MAX, 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])); + } + if (tb[WGPEER_A_ENDPOINT]) { + const struct sockaddr *addr = nla_data (tb[WGPEER_A_ENDPOINT]); + unsigned short family; + + G_STATIC_ASSERT (sizeof (addr->sa_family) == sizeof (family)); + memcpy (&family, &addr->sa_family, sizeof (addr->sa_family)); + + if ( family == AF_INET + && nla_len (tb[WGPEER_A_ENDPOINT]) == sizeof (struct sockaddr_in)) { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr; + + peer_c->data.endpoint_family = AF_INET; + peer_c->data.endpoint_port = unaligned_read_be16 (&addr4->sin_port); + peer_c->data.endpoint_addr.addr4 = unaligned_read_ne32 (&addr4->sin_addr.s_addr); + memcpy (&peer_c->data.endpoint_addr.addr4, &addr4->sin_addr.s_addr, 4); + } else if ( family == AF_INET6 + && nla_len (tb[WGPEER_A_ENDPOINT]) == sizeof (struct sockaddr_in6)) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr; + + peer_c->data.endpoint_family = AF_INET6; + peer_c->data.endpoint_port = unaligned_read_be16 (&addr6->sin6_port); + memcpy (&peer_c->data.endpoint_addr.addr6, &addr6->sin6_addr, 16); + } + } + if (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]) + peer_c->data.persistent_keepalive_interval = nla_get_u64 (tb[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); + if (tb[WGPEER_A_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_MAX + 1] = { + [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 }, + }; + WireGuardParseData *parse_data = arg; + struct nlattr *tb[WGDEVICE_A_MAX + 1]; + int nlerr; + + nlerr = genlmsg_parse (nlmsg_hdr (msg), 0, tb, WGDEVICE_A_MAX, policy); + if (nlerr < 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); + + 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 convered 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); +} + +/*****************************************************************************/ + /* 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) @@ -1707,37 +2329,39 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr const char *nl_info_kind = NULL; int err; nm_auto_nmpobj NMPObject *obj = NULL; - NMPObject *obj_result = NULL; gboolean completed_from_cache_val = FALSE; gboolean *completed_from_cache = cache ? &completed_from_cache_val : NULL; const NMPObject *link_cached = NULL; - NMPObject *lnk_data = NULL; + const NMPObject *lnk_data = NULL; gboolean address_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); + 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) - goto id_only_handled; + return g_steal_pointer (&obj); err = nlmsg_parse (nlh, sizeof (*ifi), tb, IFLA_MAX, policy); if (err < 0) - goto errout; + return NULL; if (!tb[IFLA_IFNAME]) - goto errout; + return NULL; nla_strlcpy(obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ); if (!obj->link.name[0]) - goto errout; + return NULL; if (!tb[IFLA_MTU]) { /* Kernel has two places that send RTM_GETLINK messages: @@ -1752,14 +2376,14 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr * To some extent this is a hack and correct approach is to * merge objects per-field. */ - goto errout; + return NULL; } obj->link.mtu = nla_get_u32 (tb[IFLA_MTU]); if (tb[IFLA_LINKINFO]) { err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], policy_link_info); if (err < 0) - goto errout; + return NULL; if (li[IFLA_INFO_KIND]) nl_info_kind = nla_get_string (li[IFLA_INFO_KIND]); @@ -1838,6 +2462,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr switch (obj->link.type) { 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: @@ -1846,6 +2471,10 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr 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; @@ -1868,6 +2497,15 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr 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; @@ -1875,12 +2513,14 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr if ( completed_from_cache && ( lnk_data_complete_from_cache + || need_ext_data || address_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) { + 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 @@ -1893,8 +2533,16 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr * 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 = (NMPObject *) nmp_object_ref (link_cached->_link.netlink.lnk); + 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.addr = link_cached->link.addr; if (!af_inet6_token_valid) @@ -1912,12 +2560,71 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr obj->_link.netlink.lnk = lnk_data; + if ( need_ext_data + && obj->_link.ext_data == NULL) { + switch (obj->link.type) { + case NM_LINK_TYPE_WIFI: + obj->_link.ext_data = (GObject *) nm_wifi_utils_new (ifi->ifi_index, + _genl_sock (NM_LINUX_PLATFORM (platform)), + TRUE); + break; + case NM_LINK_TYPE_OLPC_MESH: +#if HAVE_WEXT + /* The kernel driver now uses nl80211, but we force use of WEXT because + * the cfg80211 interactions are not quite ready to support access to + * mesh control through nl80211 just yet. + */ + obj->_link.ext_data = (GObject *) nm_wifi_utils_wext_new (ifi->ifi_index, FALSE); +#endif + 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; -id_only_handled: - obj_result = obj; - obj = NULL; -errout: - return obj_result; + return g_steal_pointer (&obj); } /* Copied and heavily modified from libnl3's addr_msg_parser(). */ @@ -1934,7 +2641,6 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) int err; gboolean is_v4; nm_auto_nmpobj NMPObject *obj = NULL; - NMPObject *obj_result = NULL; int addr_len; guint32 lifetime, preferred, timestamp; @@ -1943,19 +2649,19 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) ifa = nlmsg_data(nlh); if (!NM_IN_SET (ifa->ifa_family, AF_INET, AF_INET6)) - goto errout; + return NULL; is_v4 = ifa->ifa_family == AF_INET; err = nlmsg_parse (nlh, sizeof(*ifa), tb, IFA_MAX, policy); if (err < 0) - goto errout; + return NULL; addr_len = is_v4 ? sizeof (in_addr_t) : sizeof (struct in6_addr); if (ifa->ifa_prefixlen > (is_v4 ? 32 : 128)) - goto errout; + return NULL; /*****************************************************************/ @@ -1964,8 +2670,8 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) obj->ip_address.ifindex = ifa->ifa_index; obj->ip_address.plen = ifa->ifa_prefixlen; - _check_addr_or_errout (tb, IFA_ADDRESS, addr_len); - _check_addr_or_errout (tb, IFA_LOCAL, addr_len); + _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). */ @@ -2029,10 +2735,7 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) &obj->ip_address.lifetime, &obj->ip_address.preferred); - obj_result = obj; - obj = NULL; -errout: - return obj_result; + return g_steal_pointer (&obj); } /* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */ @@ -2055,7 +2758,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) int err; gboolean is_v4; nm_auto_nmpobj NMPObject *obj = NULL; - NMPObject *obj_result = NULL; int addr_len; struct { gboolean is_present; @@ -2074,14 +2776,14 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) *****************************************************************/ if (!NM_IN_SET (rtm->rtm_family, AF_INET, AF_INET6)) - goto errout; + return NULL; if (rtm->rtm_type != RTN_UNICAST) - goto errout; + return NULL; err = nlmsg_parse (nlh, sizeof (struct rtmsg), tb, RTA_MAX, policy); if (err < 0) - goto errout; + return NULL; /*****************************************************************/ @@ -2091,7 +2793,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) : sizeof (struct in6_addr); if (rtm->rtm_dst_len > (is_v4 ? 32 : 128)) - goto errout; + return NULL; /***************************************************************** * parse nexthops. Only handle routes with one nh. @@ -2107,7 +2809,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (nh.is_present) { /* we don't support multipath routes. */ - goto errout; + return NULL; } nh.is_present = TRUE; @@ -2121,9 +2823,9 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) rtnh->rtnh_len - sizeof (*rtnh), policy); if (err < 0) - goto errout; + return NULL; - if (_check_addr_or_errout (ntb, RTA_GATEWAY, addr_len)) + if (_check_addr_or_return_null (ntb, RTA_GATEWAY, addr_len)) memcpy (&nh.gateway, nla_data (ntb[RTA_GATEWAY]), addr_len); } @@ -2140,7 +2842,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (tb[RTA_OIF]) ifindex = nla_get_u32 (tb[RTA_OIF]); - if (_check_addr_or_errout (tb, RTA_GATEWAY, addr_len)) + if (_check_addr_or_return_null (tb, RTA_GATEWAY, addr_len)) memcpy (&gateway, nla_data (tb[RTA_GATEWAY]), addr_len); if (!nh.is_present) { @@ -2154,10 +2856,10 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) * verify that it is a duplicate and ignore old-style nexthop. */ if ( nh.ifindex != ifindex || memcmp (&nh.gateway, &gateway, addr_len) != 0) - goto errout; + return NULL; } } else if (!nh.is_present) - goto errout; + return NULL; /*****************************************************************/ @@ -2176,7 +2878,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) err = nla_parse_nested (mtb, RTAX_MAX, tb[RTA_METRICS], rtax_policy); if (err < 0) - goto errout; + return NULL; if (mtb[RTAX_LOCK]) lock = nla_get_u32 (mtb[RTAX_LOCK]); @@ -2204,7 +2906,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj->ip_route.ifindex = nh.ifindex; - if (_check_addr_or_errout (tb, RTA_DST, addr_len)) + 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; @@ -2220,7 +2922,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (is_v4) obj->ip4_route.scope_inv = nm_platform_route_scope_inv (rtm->rtm_scope); - if (_check_addr_or_errout (tb, RTA_PREFSRC, addr_len)) { + 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 @@ -2231,7 +2933,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj->ip4_route.tos = rtm->rtm_tos; else { if (tb[RTA_SRC]) { - _check_addr_or_errout (tb, RTA_SRC, addr_len); + _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; @@ -2261,10 +2963,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj->ip_route.r_rtm_flags = rtm->rtm_flags; obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol); - obj_result = obj; - obj = NULL; -errout: - return obj_result; + return g_steal_pointer (&obj); } static NMPObject * @@ -2941,103 +3640,16 @@ nla_put_failure: g_return_val_if_reached (NULL); } -/****************************************************************** - * NMPlatform types and functions - ******************************************************************/ - -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 { - gint *out_refresh_all_in_progess; - NMPObject **out_route_get; - gpointer out_data; - } response; -} DelayedActionWaitForNlResponseData; - -typedef struct { - struct nl_sock *nlh; - guint32 nlh_seq_next; -#ifdef NM_MORE_LOGGING - guint32 nlh_seq_last_handled; -#endif - guint32 nlh_seq_last_seen; - GIOChannel *event_channel; - guint event_id; - - bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; - - bool sysctl_get_warned; - GHashTable *sysctl_get_prev_values; - - 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. */ - gint refresh_all_in_progess[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; - - GPtrArray *list_master_connected; - GPtrArray *list_refresh_link; - GArray *list_wait_for_nl_response; - - gint is_handling; - } delayed_action; - - GHashTable *wifi_data; -} 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) +/*****************************************************************************/ -NMPlatform * -nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support) +static struct nl_sock * +_genl_sock (NMLinuxPlatform *platform) { - gboolean use_udev = FALSE; - - if ( nmp_netns_is_initial () - && access ("/sys", W_OK) == 0) - 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); -} + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); -void -nm_linux_platform_setup (void) -{ - nm_platform_setup (nm_linux_platform_new (FALSE, FALSE)); + return priv->genl; } -/*****************************************************************************/ - #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ G_STMT_START { \ const char *const _pathid = (pathid); \ @@ -3064,7 +3676,9 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c char *contents; gs_free char *value_escaped = g_strescape (value, NULL); - if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) { + if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, NULL, &error) < 0) { _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, value_escaped, error->message); g_clear_error (&error); return; @@ -3281,7 +3895,9 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat pathid = path; } - if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) { + if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, NULL, &error) < 0) { /* We assume FAILED means EOPNOTSUP */ if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT) || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NODEV) @@ -3452,7 +4068,7 @@ delayed_action_refresh_all_in_progress (NMPlatform *platform, DelayedActionType if (NM_FLAGS_ANY (priv->delayed_action.flags, action_type)) return TRUE; - if (priv->delayed_action.refresh_all_in_progess[delayed_action_refresh_all_to_idx (action_type)] > 0) + if (priv->delayed_action.refresh_all_in_progress[delayed_action_refresh_all_to_idx (action_type)] > 0) return TRUE; return FALSE; @@ -3482,10 +4098,10 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform, case DELAYED_ACTION_RESPONSE_TYPE_VOID: break; case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: - if (data->response.out_refresh_all_in_progess) { - nm_assert (*data->response.out_refresh_all_in_progess > 0); - *data->response.out_refresh_all_in_progess -= 1; - data->response.out_refresh_all_in_progess = NULL; + 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: @@ -3510,7 +4126,7 @@ delayed_action_wait_for_nl_response_complete_check (NMPlatform *platform, guint i; guint32 next_seq_number = 0; gint64 next_timeout_abs_ns = 0; - gint now_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); @@ -3932,7 +4548,10 @@ cache_on_change (NMPlatform *platform, 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, @@ -4205,11 +4824,11 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio const NMPClass *klass = nmp_class_from_type (obj_type); nm_auto_nlmsg struct nl_msg *nlmsg = NULL; int nle; - gint *out_refresh_all_in_progess; + int *out_refresh_all_in_progress; - out_refresh_all_in_progess = &priv->delayed_action.refresh_all_in_progess[delayed_action_refresh_all_to_idx (iflags)]; - nm_assert (*out_refresh_all_in_progess >= 0); - *out_refresh_all_in_progess += 1; + out_refresh_all_in_progress = &priv->delayed_action.refresh_all_in_progress[delayed_action_refresh_all_to_idx (iflags)]; + 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; @@ -4243,9 +4862,9 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio if (nle < 0) continue; - if (_nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) { - nm_assert (*out_refresh_all_in_progess > 0); - *out_refresh_all_in_progess -= 1; + if (_nl_send_nlmsg (platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progress) < 0) { + nm_assert (*out_refresh_all_in_progress > 0); + *out_refresh_all_in_progress -= 1; } } } @@ -4274,10 +4893,10 @@ event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number) 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_progess + && data->response.out_refresh_all_in_progress && data->seq_number == priv->nlh_seq_last_seen) { - *data->response.out_refresh_all_in_progess -= 1; - data->response.out_refresh_all_in_progess = NULL; + *data->response.out_refresh_all_in_progress -= 1; + data->response.out_refresh_all_in_progress = NULL; break; } } @@ -4318,7 +4937,7 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu } } -#ifdef NM_MORE_LOGGING +#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; @@ -4741,6 +5360,9 @@ retry: } else if (NM_IN_SET (-((int) seq_result), ENODEV)) { log_level = LOGL_DEBUG; result = NM_PLATFORM_ERROR_NOT_FOUND; + } else if (-((int) seq_result) == EAFNOSUPPORT) { + log_level = LOGL_DEBUG; + result = NM_PLATFORM_ERROR_OPNOTSUPP; } else { log_level = LOGL_WARN; result = NM_PLATFORM_ERROR_UNSPECIFIED; @@ -4841,8 +5463,6 @@ link_set_netns (NMPlatform *platform, { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - _LOGD ("link: move link %d to network namespace with fd %d", ifindex, netns_fd); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -5031,7 +5651,6 @@ static NMPlatformError link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - gs_free char *mac = NULL; const ChangeLinkData d = { .set_address = { .address = address, @@ -5042,10 +5661,6 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size if (!address || !length) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - _LOGD ("link: change %d: address: %s (%lu bytes)", ifindex, - (mac = nm_utils_hwaddr_ntoa (address, length)), - (unsigned long) length); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -5067,8 +5682,6 @@ link_set_name (NMPlatform *platform, int ifindex, const char *name) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - _LOGD ("link: change %d: name: %s", ifindex, name); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -5104,8 +5717,6 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - _LOGD ("link: change %d: mtu: %u", ifindex, (unsigned) mtu); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -5123,16 +5734,18 @@ nla_put_failure: } static gboolean -link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs) +link_set_sriov_params (NMPlatform *platform, + int ifindex, + guint num_vfs, + int autoprobe) { nm_auto_pop_netns NMPNetns *netns = NULL; nm_auto_close int dirfd = -1; - int total, current; + gboolean current_autoprobe; + guint total, current_num; char ifname[IFNAMSIZ]; char buf[64]; - _LOGD ("link: change %d: num VFs: %u", ifindex, num_vfs); - if (!nm_platform_netns_push (platform, &netns)) return FALSE; @@ -5140,46 +5753,69 @@ link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs) if (!dirfd) return FALSE; - total = nm_platform_sysctl_get_int32 (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_totalvfs"), - -1); - if (total < 1) + total = nm_platform_sysctl_get_int_checked (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, + ifname, + "device/sriov_totalvfs"), + 10, 0, G_MAXUINT, 0); + if (errno) return FALSE; if (num_vfs > total) { _LOGW ("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs); num_vfs = total; } - current = nm_platform_sysctl_get_int32 (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - -1); - if (current == num_vfs) + /* + * 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, 0); + current_autoprobe = nm_platform_sysctl_get_int_checked (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, + ifname, + "device/sriov_drivers_autoprobe"), + 10, 0, G_MAXUINT, 0); + if ( current_num == num_vfs + && (autoprobe == -1 || current_autoprobe == autoprobe)) return TRUE; - if (current != 0) { - /* We need to destroy all other VFs before changing the value */ + if (current_num != 0) { + /* We need to destroy all other VFs before changing any value */ if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), "0")) { - _LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", 0, strerror (errno)); + _LOGW ("link: couldn't reset SR-IOV num_vfs: %s", strerror (errno)); return FALSE; } - if (num_vfs == 0) - return TRUE; } - /* Finally, set the desired value */ + if (num_vfs == 0) + return TRUE; + + if ( autoprobe >= 0 + && 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", autoprobe))) { + _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", autoprobe, strerror (errno)); + return FALSE; + } + if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), - nm_sprintf_buf (buf, "%d", num_vfs))) { + nm_sprintf_buf (buf, "%u", num_vfs))) { _LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, strerror (errno)); return FALSE; } @@ -5187,6 +5823,101 @@ link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs) return TRUE; } +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, + 0, + 0); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + + 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) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); +} + static char * link_get_physical_port_id (NMPlatform *platform, int ifindex) { @@ -5213,7 +5944,7 @@ link_get_dev_id (NMPlatform *platform, int ifindex) 16, 0, G_MAXUINT16, 0); } -static int +static gboolean vlan_add (NMPlatform *platform, const char *name, int parent, @@ -5229,10 +5960,6 @@ vlan_add (NMPlatform *platform, G_STATIC_ASSERT (NM_VLAN_FLAG_MVRP == (guint32) VLAN_FLAG_MVRP); vlan_flags &= (guint32) NM_VLAN_FLAGS_ALL; - - _LOGD ("link: add vlan '%s', parent %d, vlan id %d, flags %X", - name, parent, vlan_id, (unsigned) vlan_flags); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, @@ -5259,7 +5986,7 @@ nla_put_failure: g_return_val_if_reached (FALSE); } -static int +static gboolean link_gre_add (NMPlatform *platform, const char *name, const NMPlatformLnkGre *props, @@ -5268,14 +5995,6 @@ link_gre_add (NMPlatform *platform, nm_auto_nlmsg struct nl_msg *nlmsg = NULL; struct nlattr *info; struct nlattr *data; - char buffer[INET_ADDRSTRLEN]; - - _LOGD (LOG_FMT_IP_TUNNEL, - "gre", - name, - props->parent_ifindex, - nm_utils_inet4_ntop (props->local, NULL), - nm_utils_inet4_ntop (props->remote, buffer)); nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, @@ -5289,7 +6008,7 @@ link_gre_add (NMPlatform *platform, if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) goto nla_put_failure; - NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, "gre"); + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, props->is_tap ? "gretap" : "gre"); if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) goto nla_put_failure; @@ -5303,18 +6022,20 @@ link_gre_add (NMPlatform *platform, NLA_PUT_U8 (nlmsg, IFLA_GRE_PMTUDISC, !!props->path_mtu_discovery); NLA_PUT_U32 (nlmsg, IFLA_GRE_IKEY, htonl (props->input_key)); NLA_PUT_U32 (nlmsg, IFLA_GRE_OKEY, htonl (props->output_key)); - NLA_PUT_U32 (nlmsg, IFLA_GRE_IFLAGS, htons (props->input_flags)); - NLA_PUT_U32 (nlmsg, IFLA_GRE_OFLAGS, htons (props->output_flags)); + NLA_PUT_U16 (nlmsg, IFLA_GRE_IFLAGS, htons (props->input_flags)); + NLA_PUT_U16 (nlmsg, IFLA_GRE_OFLAGS, htons (props->output_flags)); nla_nest_end (nlmsg, data); nla_nest_end (nlmsg, info); - return do_add_link_with_lookup (platform, NM_LINK_TYPE_GRE, name, nlmsg, out_link); + return do_add_link_with_lookup (platform, + props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE, + name, nlmsg, out_link); nla_put_failure: g_return_val_if_reached (FALSE); } -static int +static gboolean link_ip6tnl_add (NMPlatform *platform, const char *name, const NMPlatformLnkIp6Tnl *props, @@ -5323,15 +6044,9 @@ link_ip6tnl_add (NMPlatform *platform, nm_auto_nlmsg struct nl_msg *nlmsg = NULL; struct nlattr *info; struct nlattr *data; - char buffer[INET_ADDRSTRLEN]; guint32 flowinfo; - _LOGD (LOG_FMT_IP_TUNNEL, - "ip6tnl", - name, - props->parent_ifindex, - nm_utils_inet6_ntop (&props->local, NULL), - nm_utils_inet6_ntop (&props->remote, buffer)); + g_return_val_if_fail (!props->is_gre, FALSE); nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, @@ -5376,7 +6091,69 @@ nla_put_failure: g_return_val_if_reached (FALSE); } -static int +static gboolean +link_ip6gre_add (NMPlatform *platform, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + struct nlattr *data; + guint32 flowinfo; + + g_return_val_if_fail (props->is_gre, FALSE); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE | NLM_F_EXCL, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; + + if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, props->is_tap ? "ip6gretap" : "ip6gre"); + + if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->parent_ifindex) + NLA_PUT_U32 (nlmsg, IFLA_GRE_LINK, props->parent_ifindex); + + NLA_PUT_U32 (nlmsg, IFLA_GRE_IKEY, htonl (props->input_key)); + NLA_PUT_U32 (nlmsg, IFLA_GRE_OKEY, htonl (props->output_key)); + NLA_PUT_U16 (nlmsg, IFLA_GRE_IFLAGS, htons (props->input_flags)); + NLA_PUT_U16 (nlmsg, IFLA_GRE_OFLAGS, htons (props->output_flags)); + + if (memcmp (&props->local, &in6addr_any, sizeof (in6addr_any))) + NLA_PUT (nlmsg, IFLA_GRE_LOCAL, sizeof (props->local), &props->local); + if (memcmp (&props->remote, &in6addr_any, sizeof (in6addr_any))) + NLA_PUT (nlmsg, IFLA_GRE_REMOTE, sizeof (props->remote), &props->remote); + + NLA_PUT_U8 (nlmsg, IFLA_GRE_TTL, props->ttl); + NLA_PUT_U8 (nlmsg, 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 (nlmsg, IFLA_GRE_FLOWINFO, htonl (flowinfo)); + NLA_PUT_U32 (nlmsg, IFLA_GRE_FLAGS, props->flags); + + nla_nest_end (nlmsg, data); + nla_nest_end (nlmsg, info); + + return do_add_link_with_lookup (platform, + props->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE, + name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); +} + +static gboolean link_ipip_add (NMPlatform *platform, const char *name, const NMPlatformLnkIpIp *props, @@ -5385,14 +6162,6 @@ link_ipip_add (NMPlatform *platform, nm_auto_nlmsg struct nl_msg *nlmsg = NULL; struct nlattr *info; struct nlattr *data; - char buffer[INET_ADDRSTRLEN]; - - _LOGD (LOG_FMT_IP_TUNNEL, - "ipip", - name, - props->parent_ifindex, - nm_utils_inet4_ntop (props->local, NULL), - nm_utils_inet4_ntop (props->remote, buffer)); nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, @@ -5427,7 +6196,7 @@ nla_put_failure: g_return_val_if_reached (FALSE); } -static int +static gboolean link_macsec_add (NMPlatform *platform, const char *name, int parent, @@ -5438,11 +6207,6 @@ link_macsec_add (NMPlatform *platform, struct nlattr *info; struct nlattr *data; - _LOGD ("adding macsec '%s' parent %u sci %llx", - name, - parent, - (unsigned long long) props->sci); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, @@ -5489,7 +6253,7 @@ nla_put_failure: g_return_val_if_reached (FALSE); } -static int +static gboolean link_macvlan_add (NMPlatform *platform, const char *name, int parent, @@ -5500,12 +6264,6 @@ link_macvlan_add (NMPlatform *platform, struct nlattr *info; struct nlattr *data; - _LOGD ("adding %s '%s' parent %u mode %u", - props->tap ? "macvtap" : "macvlan", - name, - parent, - props->mode); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, @@ -5538,7 +6296,7 @@ nla_put_failure: g_return_val_if_reached (FALSE); } -static int +static gboolean link_sit_add (NMPlatform *platform, const char *name, const NMPlatformLnkSit *props, @@ -5547,14 +6305,6 @@ link_sit_add (NMPlatform *platform, nm_auto_nlmsg struct nl_msg *nlmsg = NULL; struct nlattr *info; struct nlattr *data; - char buffer[INET_ADDRSTRLEN]; - - _LOGD (LOG_FMT_IP_TUNNEL, - "sit", - name, - props->parent_ifindex, - nm_utils_inet4_ntop (props->local, NULL), - nm_utils_inet4_ntop (props->remote, buffer)); nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, @@ -5658,9 +6408,6 @@ link_vxlan_add (NMPlatform *platform, g_return_val_if_fail (props, FALSE); - _LOGD ("link: add vxlan '%s', parent %d, vxlan id %d", - name, props->parent_ifindex, props->id); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, @@ -5718,6 +6465,41 @@ nla_put_failure: g_return_val_if_reached (FALSE); } +static gboolean +link_6lowpan_add (NMPlatform *platform, + const char *name, + int parent, + const NMPlatformLink **out_link) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *info; + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE | NLM_F_EXCL, + 0, + name, + 0, + 0); + if (!nlmsg) + return FALSE; + + NLA_PUT_U32 (nlmsg, IFLA_LINK, parent); + + if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, "lowpan"); + + nla_nest_end (nlmsg, info); + + return do_add_link_with_lookup (platform, + NM_LINK_TYPE_6LOWPAN, + name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); +} + + static void _vlan_change_vlan_qos_mapping_create (gboolean is_ingress_map, gboolean reset_all, @@ -5812,9 +6594,6 @@ link_vlan_change (NMPlatform *platform, guint new_n_egress_map = 0; gs_free NMVlanQosMapping *new_ingress_map = NULL; gs_free NMVlanQosMapping *new_egress_map = NULL; - char s_flags[64]; - char s_ingress[256]; - char s_egress[256]; obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if ( !obj_cache @@ -5845,26 +6624,6 @@ link_vlan_change (NMPlatform *platform, &new_egress_map, &new_n_egress_map); - _LOGD ("link: change %d: vlan:%s%s%s", - ifindex, - flags_mask - ? nm_sprintf_buf (s_flags, " flags 0x%x/0x%x", (unsigned) flags_set, (unsigned) flags_mask) - : "", - new_n_ingress_map - ? nm_platform_vlan_qos_mapping_to_string (" ingress-qos-map", - new_ingress_map, - new_n_ingress_map, - s_ingress, - sizeof (s_ingress)) - : "", - new_n_egress_map - ? nm_platform_vlan_qos_mapping_to_string (" egress-qos-map", - new_egress_map, - new_n_egress_map, - s_egress, - sizeof (s_egress)) - : ""); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -5891,8 +6650,6 @@ link_enslave (NMPlatform *platform, int master, int slave) nm_auto_nlmsg struct nl_msg *nlmsg = NULL; int ifindex = slave; - _LOGD ("link: change %d: enslave: master %d", slave, master); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -5980,43 +6737,26 @@ infiniband_partition_delete (NMPlatform *platform, int parent, int p_key) /*****************************************************************************/ -static WifiData * -wifi_get_wifi_data (NMPlatform *platform, int ifindex) +static GObject * +get_ext_data (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const NMPlatformLink *pllink; - WifiData *wifi_data; - - wifi_data = g_hash_table_lookup (priv->wifi_data, GINT_TO_POINTER (ifindex)); - pllink = nm_platform_link_get (platform, ifindex); - - if (!wifi_data) { - if (pllink) { - if (pllink->type == NM_LINK_TYPE_WIFI) - wifi_data = wifi_utils_init (ifindex, TRUE); - else if (pllink->type == NM_LINK_TYPE_OLPC_MESH) { - /* The kernel driver now uses nl80211, but we force use of WEXT because - * the cfg80211 interactions are not quite ready to support access to - * mesh control through nl80211 just yet. - */ -#if HAVE_WEXT - wifi_data = wifi_wext_init (ifindex, FALSE); -#endif - } + const NMPObject *obj; - if (wifi_data) - g_hash_table_insert (priv->wifi_data, GINT_TO_POINTER (ifindex), wifi_data); - } - } + obj = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); + if (!obj) + return NULL; - return wifi_data; + return obj->_link.ext_data; } + +/*****************************************************************************/ + #define WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, retval) \ nm_auto_pop_netns NMPNetns *netns = NULL; \ - WifiData *wifi_data; \ + NMWifiUtils *wifi_data; \ if (!nm_platform_netns_push (platform, &netns)) \ return retval; \ - wifi_data = wifi_get_wifi_data (platform, ifindex); \ + wifi_data = NM_WIFI_UTILS (get_ext_data (platform, ifindex)); \ if (!wifi_data) \ return retval; @@ -6025,7 +6765,7 @@ wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabiliti { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); if (caps) - *caps = wifi_utils_get_caps (wifi_data); + *caps = nm_wifi_utils_get_caps (wifi_data); return TRUE; } @@ -6033,63 +6773,70 @@ static gboolean wifi_get_bssid (NMPlatform *platform, int ifindex, guint8 *bssid) { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return wifi_utils_get_bssid (wifi_data, bssid); + return nm_wifi_utils_get_bssid (wifi_data, bssid); } static guint32 wifi_get_frequency (NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0); - return wifi_utils_get_freq (wifi_data); + return nm_wifi_utils_get_freq (wifi_data); } static gboolean wifi_get_quality (NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return wifi_utils_get_qual (wifi_data); + return nm_wifi_utils_get_qual (wifi_data); } static guint32 wifi_get_rate (NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return wifi_utils_get_rate (wifi_data); + return nm_wifi_utils_get_rate (wifi_data); } static NM80211Mode wifi_get_mode (NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, NM_802_11_MODE_UNKNOWN); - return wifi_utils_get_mode (wifi_data); + 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, ); - wifi_utils_set_mode (wifi_data, mode); + 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, ); - wifi_utils_set_powersave (wifi_data, powersave); + 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 wifi_utils_find_freq (wifi_data, freqs); + 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, ); - wifi_utils_indicate_addressing_running (wifi_data, running); + 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 @@ -6097,7 +6844,7 @@ wifi_set_wake_on_wlan (NMPlatform *platform, int ifindex, NMSettingWirelessWakeOnWLan wowl) { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, FALSE); - return wifi_utils_set_wake_on_wlan (wifi_data, wowl); + return nm_wifi_utils_set_wake_on_wlan (wifi_data, wowl); } /*****************************************************************************/ @@ -6148,21 +6895,56 @@ static guint32 mesh_get_channel (NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS (wifi_data, platform, ifindex, 0); - return wifi_utils_get_mesh_channel (wifi_data); + 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 wifi_utils_set_mesh_channel (wifi_data, channel); + 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 wifi_utils_set_mesh_ssid (wifi_data, ssid, len); + 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); } /*****************************************************************************/ @@ -6179,12 +6961,12 @@ link_get_wake_on_lan (NMPlatform *platform, int ifindex) if (type == NM_LINK_TYPE_ETHERNET) return nmp_utils_ethtool_get_wake_on_lan (ifindex); else if (type == NM_LINK_TYPE_WIFI) { - WifiData *wifi_data = wifi_get_wifi_data (platform, ifindex); + NMWifiUtils *wifi_data = NM_WIFI_UTILS (get_ext_data (platform, ifindex)); if (!wifi_data) return FALSE; - return wifi_utils_get_wowlan (wifi_data); + return nm_wifi_utils_get_wake_on_wlan (wifi_data) != NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE; } else return FALSE; } @@ -6998,6 +7780,14 @@ handle_udev_event (NMUdevClient *udev_client, /*****************************************************************************/ +void +nm_linux_platform_setup (void) +{ + nm_platform_setup (nm_linux_platform_new (FALSE, FALSE)); +} + +/*****************************************************************************/ + static void nm_linux_platform_init (NMLinuxPlatform *self) { @@ -7006,7 +7796,6 @@ nm_linux_platform_init (NMLinuxPlatform *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)); - priv->wifi_data = g_hash_table_new_full (nm_direct_hash, NULL, NULL, (GDestroyNotify) wifi_utils_unref); } static void @@ -7036,6 +7825,18 @@ constructed (GObject *_object) 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)", + nl_geterror (nle), -nle); + nl_socket_free (priv->genl); + priv->genl = NULL; + } + priv->nlh = nl_socket_alloc (); g_assert (priv->nlh); @@ -7127,6 +7928,22 @@ constructed (GObject *_object) } } +NMPlatform * +nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support) +{ + gboolean use_udev = FALSE; + + if ( nmp_netns_is_initial () + && access ("/sys", W_OK) == 0) + 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) { @@ -7154,12 +7971,12 @@ finalize (GObject *object) 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); + g_source_remove (priv->event_id); g_io_channel_unref (priv->event_channel); nl_socket_free (priv->nlh); - g_hash_table_unref (priv->wifi_data); - 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); @@ -7205,7 +8022,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) 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_num_vfs = link_set_sriov_num_vfs; + platform_class->link_set_sriov_params = link_set_sriov_params; + platform_class->link_set_sriov_vfs = link_set_sriov_vfs; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; @@ -7238,19 +8056,27 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) 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->link_gre_add = link_gre_add; platform_class->link_ip6tnl_add = link_ip6tnl_add; + platform_class->link_ip6gre_add = link_ip6gre_add; platform_class->link_macsec_add = link_macsec_add; platform_class->link_macvlan_add = link_macvlan_add; platform_class->link_ipip_add = link_ipip_add; platform_class->link_sit_add = link_sit_add; platform_class->link_tun_add = link_tun_add; + platform_class->link_6lowpan_add = link_6lowpan_add; platform_class->object_delete = object_delete; platform_class->ip4_address_add = ip4_address_add; diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c index 4cb19780..3e2ad911 100644 --- a/src/platform/nm-netlink.c +++ b/src/platform/nm-netlink.c @@ -52,7 +52,6 @@ struct nl_msg { struct ucred nm_creds; struct nlmsghdr * nm_nlh; size_t nm_size; - int nm_refcnt; }; struct nl_sock { @@ -84,18 +83,18 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int, ) const char * -nl_geterror (int err) +nl_geterror (int nlerr) { const char *s; - err = nl_errno (err); + nlerr = nl_errno (nlerr); - if (err >= _NLE_BASE) { - s = _geterror (err); + if (nlerr >= _NLE_BASE) { + s = _geterror (nlerr); if (s) return s; } - return g_strerror (err); + return g_strerror (nlerr); } /*****************************************************************************/ @@ -252,27 +251,13 @@ nlmsg_reserve (struct nl_msg *n, size_t len, int pad) n->nm_nlh->nlmsg_len += tlen; if (tlen > len) - memset(buf + len, 0, tlen - len); + memset (buf + len, 0, tlen - len); return buf; } /*****************************************************************************/ -static int - get_default_page_size (void) -{ - static int val = 0; - int v; - - if (G_UNLIKELY (val == 0)) { - v = getpagesize (); - g_assert (v > 0); - val = v; - } - return val; -} - struct nlattr * nla_reserve (struct nl_msg *msg, int attrtype, int attrlen) { @@ -282,22 +267,38 @@ nla_reserve (struct nl_msg *msg, int attrtype, int attrlen) if (attrlen < 0) return NULL; - tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + tlen = NLMSG_ALIGN (msg->nm_nlh->nlmsg_len) + nla_total_size (attrlen); if (tlen > msg->nm_size) return NULL; - nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + nla = (struct nlattr *) nlmsg_tail (msg->nm_nlh); nla->nla_type = attrtype; - nla->nla_len = nla_attr_size(attrlen); + nla->nla_len = nla_attr_size (attrlen); if (attrlen) - memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + memset ((unsigned char *) nla + nla->nla_len, 0, nla_padlen (attrlen)); msg->nm_nlh->nlmsg_len = tlen; return nla; } +/*****************************************************************************/ + +static int +get_default_page_size (void) +{ + static int val = 0; + int v; + + if (G_UNLIKELY (val == 0)) { + v = getpagesize (); + g_assert (v > 0); + val = v; + } + return val; +} + struct nl_msg * nlmsg_alloc_size (size_t len) { @@ -308,7 +309,6 @@ nlmsg_alloc_size (size_t len) nm = g_slice_new0 (struct nl_msg); - nm->nm_refcnt = 1; nm->nm_protocol = -1; nm->nm_size = len; nm->nm_nlh = g_malloc0 (len); @@ -331,48 +331,40 @@ nlmsg_alloc (void) return nlmsg_alloc_size (get_default_page_size ()); } -/** - * Allocate a new netlink message with maximum payload size specified. - */ struct nl_msg * -nlmsg_alloc_inherit (struct nlmsghdr *hdr) +nlmsg_alloc_convert (struct nlmsghdr *hdr) { struct nl_msg *nm; - nm = nlmsg_alloc (); - if (hdr) { - struct nlmsghdr *new = nm->nm_nlh; - - new->nlmsg_type = hdr->nlmsg_type; - new->nlmsg_flags = hdr->nlmsg_flags; - new->nlmsg_seq = hdr->nlmsg_seq; - new->nlmsg_pid = hdr->nlmsg_pid; - } - + nm = nlmsg_alloc_size (NLMSG_ALIGN (hdr->nlmsg_len)); + memcpy (nm->nm_nlh, hdr, hdr->nlmsg_len); return nm; } struct nl_msg * -nlmsg_alloc_convert (struct nlmsghdr *hdr) +nlmsg_alloc_simple (int nlmsgtype, int flags) { struct nl_msg *nm; + struct nlmsghdr *new; - nm = nlmsg_alloc_size (NLMSG_ALIGN (hdr->nlmsg_len)); - memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); + nm = nlmsg_alloc (); + new = nm->nm_nlh; + new->nlmsg_type = nlmsgtype; + new->nlmsg_flags = flags; return nm; } -struct nl_msg * -nlmsg_alloc_simple (int nlmsgtype, int flags) +void nlmsg_free (struct nl_msg *msg) { - struct nlmsghdr nlh = { - .nlmsg_type = nlmsgtype, - .nlmsg_flags = flags, - }; + if (!msg) + return; - return nlmsg_alloc_inherit (&nlh); + g_free (msg->nm_nlh); + g_slice_free (struct nl_msg, msg); } +/*****************************************************************************/ + int nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad) { @@ -382,19 +374,21 @@ nlmsg_append (struct nl_msg *n, void *data, size_t len, int pad) if (tmp == NULL) return -ENOMEM; - memcpy(tmp, data, len); + memcpy (tmp, data, len); return 0; } +/*****************************************************************************/ + int nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy) { - if (!nlmsg_valid_hdr(nlh, hdrlen)) + if (!nlmsg_valid_hdr (nlh, hdrlen)) return -NLE_MSG_TOOSHORT; - return nla_parse (tb, maxtype, nlmsg_attrdata(nlh, hdrlen), - nlmsg_attrlen(nlh, hdrlen), policy); + return nla_parse (tb, maxtype, nlmsg_attrdata (nlh, hdrlen), + nlmsg_attrlen (nlh, hdrlen), policy); } struct nlmsghdr * @@ -412,8 +406,8 @@ nlmsg_put (struct nl_msg *n, uint32_t pid, uint32_t seq, nlh->nlmsg_pid = pid; nlh->nlmsg_seq = seq; - if (payload > 0 && - nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) + if ( payload > 0 + && nlmsg_reserve (n, payload, NLMSG_ALIGNTO) == NULL) return NULL; return nlh; @@ -424,8 +418,8 @@ nla_get_u64 (const struct nlattr *nla) { uint64_t tmp = 0; - if (nla && nla_len(nla) >= sizeof (tmp)) - memcpy(&tmp, nla_data(nla), sizeof (tmp)); + if (nla && nla_len (nla) >= sizeof (tmp)) + memcpy (&tmp, nla_data (nla), sizeof (tmp)); return tmp; } @@ -433,8 +427,8 @@ nla_get_u64 (const struct nlattr *nla) size_t nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize) { - size_t srclen = nla_len(nla); - const char *src = nla_data(nla); + size_t srclen = nla_len (nla); + const char *src = nla_data (nla); if (srclen > 0 && src[srclen - 1] == '\0') srclen--; @@ -442,8 +436,8 @@ nla_strlcpy (char *dst, const struct nlattr *nla, size_t dstsize) if (dstsize > 0) { size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; - memset(dst, 0, dstsize); - memcpy(dst, src, len); + memset (dst, 0, dstsize); + memcpy (dst, src, len); } return srclen; @@ -458,7 +452,7 @@ nla_memcpy (void *dest, const struct nlattr *src, int count) return 0; minlen = NM_MIN (count, (int) nla_len (src)); - memcpy(dest, nla_data(src), minlen); + memcpy (dest, nla_data (src), minlen); return minlen; } @@ -468,7 +462,7 @@ nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data) { struct nlattr *nla; - nla = nla_reserve(msg, attrtype, datalen); + nla = nla_reserve (msg, attrtype, datalen); if (!nla) { if (datalen < 0) g_return_val_if_reached (-NLE_BUG); @@ -477,7 +471,7 @@ nla_put (struct nl_msg *msg, int attrtype, int datalen, const void *data) } if (datalen > 0) - memcpy (nla_data(nla), data, datalen); + memcpy (nla_data (nla), data, datalen); return 0; } @@ -501,21 +495,21 @@ nla_nest_cancel (struct nl_msg *msg, const struct nlattr *attr) { ssize_t len; - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; + len = (char *) nlmsg_tail (msg->nm_nlh) - (char *) attr; if (len < 0) g_return_if_reached (); else if (len > 0) { msg->nm_nlh->nlmsg_len -= len; - memset(nlmsg_tail(msg->nm_nlh), 0, len); + memset (nlmsg_tail (msg->nm_nlh), 0, len); } } struct nlattr * nla_nest_start (struct nl_msg *msg, int attrtype) { - struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); + struct nlattr *start = (struct nlattr *) nlmsg_tail (msg->nm_nlh); - if (nla_put(msg, attrtype, 0, NULL) < 0) + if (nla_put (msg, attrtype, 0, NULL) < 0) return NULL; return start; @@ -526,7 +520,7 @@ _nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) { size_t pad, len; - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; + len = (char *) nlmsg_tail (msg->nm_nlh) - (char *) start; if ( len > USHRT_MAX || (!keep_empty && len == NLA_HDRLEN)) { @@ -534,7 +528,7 @@ _nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) * Max nlattr size exceeded or empty nested attribute, trim the * attribute header again */ - nla_nest_cancel(msg, start); + nla_nest_cancel (msg, start); /* Return error only if nlattr size was exceeded */ return (len == NLA_HDRLEN) ? 0 : -NLE_ATTRSIZE; @@ -542,7 +536,7 @@ _nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) start->nla_len = len; - pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; + pad = NLMSG_ALIGN (msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; if (pad > 0) { /* * Data inside attribute does not end at a alignment boundry. @@ -550,7 +544,7 @@ _nest_end (struct nl_msg *msg, struct nlattr *start, int keep_empty) * the message. nlmsg_reserve() may never fail in this situation, * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. */ - if (!nlmsg_reserve(msg, pad, 0)) + if (!nlmsg_reserve (msg, pad, 0)) g_return_val_if_reached (-NLE_BUG); } @@ -578,7 +572,7 @@ validate_nla (const struct nlattr *nla, int maxtype, { const struct nla_policy *pt; unsigned int minlen = 0; - int type = nla_type(nla); + int type = nla_type (nla); if (type < 0 || type > maxtype) return 0; @@ -593,15 +587,15 @@ validate_nla (const struct nlattr *nla, int maxtype, else if (pt->type != NLA_UNSPEC) minlen = nla_attr_minlen[pt->type]; - if (nla_len(nla) < minlen) + if (nla_len (nla) < minlen) return -NLE_UNSPEC; - if (pt->maxlen && nla_len(nla) > pt->maxlen) + if (pt->maxlen && nla_len (nla) > pt->maxlen) return -NLE_UNSPEC; if (pt->type == NLA_STRING) { - const char *data = nla_data(nla); - if (data[nla_len(nla) - 1] != '\0') + const char *data = nla_data (nla); + if (data[nla_len (nla) - 1] != '\0') return -NLE_UNSPEC; } @@ -613,48 +607,32 @@ nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy) { struct nlattr *nla; - int rem, err; + int rem, nlerr; - memset(tb, 0, sizeof (struct nlattr *) * (maxtype + 1)); + memset (tb, 0, sizeof (struct nlattr *) * (maxtype + 1)); - nla_for_each_attr(nla, head, len, rem) { - int type = nla_type(nla); + nla_for_each_attr (nla, head, len, rem) { + int type = nla_type (nla); if (type > maxtype) continue; if (policy) { - err = validate_nla(nla, maxtype, policy); - if (err < 0) + nlerr = validate_nla (nla, maxtype, policy); + if (nlerr < 0) goto errout; } tb[type] = nla; } - err = 0; + nlerr = 0; errout: - return err; + return nlerr; } /*****************************************************************************/ -void nlmsg_free (struct nl_msg *msg) -{ - if (!msg) - return; - - if (msg->nm_refcnt < 1) - g_return_if_reached (); - - msg->nm_refcnt--; - - if (msg->nm_refcnt <= 0) { - g_free (msg->nm_nlh); - g_slice_free (struct nl_msg, msg); - } -} - int nlmsg_get_proto (struct nl_msg *msg) { @@ -813,7 +791,7 @@ int genl_ctrl_resolve (struct nl_sock *sk, const char *name) { nm_auto_nlmsg struct nl_msg *msg = NULL; - int result = -ENOMEM; + int nlerr; gint32 response_data = -1; const struct nl_cb cb = { .valid_cb = _genl_parse_getfamily, @@ -824,31 +802,29 @@ genl_ctrl_resolve (struct nl_sock *sk, const char *name) if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1)) - goto out; + return -ENOMEM; - if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0) - goto out; + nlerr = nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name); + if (nlerr < 0) + return nlerr; - result = nl_send_auto (sk, msg); - if (result < 0) - goto out; + nlerr = nl_send_auto (sk, msg); + if (nlerr < 0) + return nlerr; - result = nl_recvmsgs (sk, &cb); - if (result < 0) - goto out; + nlerr = nl_recvmsgs (sk, &cb); + if (nlerr < 0) + return nlerr; /* If search was successful, request may be ACKed after data */ - result = nl_wait_for_ack (sk, NULL); - if (result < 0) - goto out; + nlerr = nl_wait_for_ack (sk, NULL); + if (nlerr < 0) + return nlerr; - if (response_data > 0) - result = response_data; - else - result = -ENOENT; + if (response_data < 0) + return -NLE_UNSPEC; -out: - return result; + return response_data; } /*****************************************************************************/ @@ -863,7 +839,7 @@ nl_socket_alloc (void) sk->s_fd = -1; sk->s_local.nl_family = AF_NETLINK; sk->s_peer.nl_family = AF_NETLINK; - sk->s_seq_expect = sk->s_seq_next = time(NULL); + sk->s_seq_expect = sk->s_seq_next = time (NULL); return sk; } @@ -938,7 +914,7 @@ nl_socket_set_nonblocking (const struct nl_sock *sk) if (sk->s_fd == -1) return -NLE_BAD_SOCK; - if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) + if (fcntl (sk->s_fd, F_SETFL, O_NONBLOCK) < 0) return -nl_syserr2nlerr (errno); return 0; @@ -982,25 +958,27 @@ nl_socket_add_memberships (struct nl_sock *sk, int group, ...) if (sk->s_fd == -1) return -NLE_BAD_SOCK; - va_start(ap, group); + va_start (ap, group); while (group != 0) { if (group < 0) { - va_end(ap); + va_end (ap); g_return_val_if_reached (-NLE_BUG); } err = setsockopt (sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof (group)); if (err < 0) { - va_end(ap); - return -nl_syserr2nlerr (errno); + int errsv = errno; + + va_end (ap); + return -nl_syserr2nlerr (errsv); } - group = va_arg(ap, int); + group = va_arg (ap, int); } - va_end(ap); + va_end (ap); return 0; } @@ -1030,7 +1008,7 @@ void nl_socket_disable_msg_peek (struct nl_sock *sk) int nl_connect (struct nl_sock *sk, int protocol) { - int err; + int err, nlerr; socklen_t addrlen; struct sockaddr_nl local = { 0 }; @@ -1039,12 +1017,12 @@ nl_connect (struct nl_sock *sk, int protocol) sk->s_fd = socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); if (sk->s_fd < 0) { - err = -nl_syserr2nlerr (errno); + nlerr = -nl_syserr2nlerr (errno); goto errout; } - err = nl_socket_set_buffer_size(sk, 0, 0); - if (err < 0) + nlerr = nl_socket_set_buffer_size (sk, 0, 0); + if (nlerr < 0) goto errout; nm_assert (sk->s_local.nl_pid == 0); @@ -1052,7 +1030,7 @@ nl_connect (struct nl_sock *sk, int protocol) err = bind (sk->s_fd, (struct sockaddr*) &sk->s_local, sizeof (sk->s_local)); if (err != 0) { - err = -nl_syserr2nlerr (errno); + nlerr = -nl_syserr2nlerr (errno); goto errout; } @@ -1060,17 +1038,17 @@ nl_connect (struct nl_sock *sk, int protocol) err = getsockname (sk->s_fd, (struct sockaddr *) &local, &addrlen); if (err < 0) { - err = -nl_syserr2nlerr (errno); + nlerr = -nl_syserr2nlerr (errno); goto errout; } if (addrlen != sizeof (local)) { - err = -NLE_UNSPEC; + nlerr = -NLE_UNSPEC; goto errout; } if (local.nl_family != AF_NETLINK) { - err = -NLE_UNSPEC; + nlerr = -NLE_UNSPEC; goto errout; } @@ -1081,10 +1059,10 @@ nl_connect (struct nl_sock *sk, int protocol) errout: if (sk->s_fd != -1) { - close(sk->s_fd); + close (sk->s_fd); sk->s_fd = -1; } - return err; + return nlerr; } /*****************************************************************************/ @@ -1100,7 +1078,7 @@ _cb_init (struct nl_cb *dst, const struct nl_cb *src) memset (dst, 0, sizeof (*dst)); } -static int ack_wait_handler(struct nl_msg *msg, void *arg) +static int ack_wait_handler (struct nl_msg *msg, void *arg) { return NL_STOP; } @@ -1121,16 +1099,22 @@ do { \ const struct nl_cb *_cb = (cb); \ \ if (_cb->type##_cb) { \ - err = _cb->type##_cb ((msg), _cb->type##_arg); \ - switch (err) { \ + /* the returned value here must be either a negative + * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ \ + nlerr = _cb->type##_cb ((msg), _cb->type##_arg); \ + switch (nlerr) { \ case NL_OK: \ - err = 0; \ + nlerr = 0; \ break; \ case NL_SKIP: \ goto skip; \ case NL_STOP: \ goto stop; \ default: \ + if (nlerr >= 0) { \ + nm_assert_not_reached (); \ + nlerr = -NLE_BUG; \ + } \ goto out; \ } \ } \ @@ -1139,7 +1123,7 @@ do { \ int nl_recvmsgs (struct nl_sock *sk, const struct nl_cb *cb) { - int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0; + int n, nlerr = 0, multipart = 0, interrupted = 0, nrecv = 0; gs_free unsigned char *buf = NULL; struct nlmsghdr *hdr; struct sockaddr_nl nla = { 0 }; @@ -1164,9 +1148,9 @@ continue_reading: nrecv++; /* Only do sequence checking if auto-ack mode is enabled */ - if (!(sk->s_flags & NL_NO_AUTO_ACK)) { + if (! (sk->s_flags & NL_NO_AUTO_ACK)) { if (hdr->nlmsg_seq != sk->s_seq_expect) { - err = -NLE_SEQ_MISMATCH; + nlerr = -NLE_SEQ_MISMATCH; goto out; } } @@ -1198,7 +1182,7 @@ continue_reading: * this action by skipping this packet. */ if (hdr->nlmsg_type == NLMSG_DONE) { multipart = 0; - NL_CB_CALL(cb, finish, msg); + NL_CB_CALL (cb, finish, msg); } /* Message to be ignored, the default action is to @@ -1212,50 +1196,53 @@ continue_reading: * quit parsing. The user may overrule this action by retuning * NL_SKIP or NL_PROCEED (dangerous) */ else if (hdr->nlmsg_type == NLMSG_OVERRUN) { - err = -NLE_MSG_OVERFLOW; + nlerr = -NLE_MSG_OVERFLOW; goto out; } /* Message carries a nlmsgerr */ else if (hdr->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *e = nlmsg_data(hdr); + struct nlmsgerr *e = nlmsg_data (hdr); - if (hdr->nlmsg_len < nlmsg_size(sizeof (*e))) { + 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 = -NLE_MSG_TRUNC; + nlerr = -NLE_MSG_TRUNC; goto out; } if (e->error) { /* Error message reported back from kernel. */ if (cb->err_cb) { - err = cb->err_cb (&nla, e, - cb->err_arg); - if (err < 0) + /* the returned value here must be either a negative + * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ + nlerr = cb->err_cb (&nla, e, + cb->err_arg); + if (nlerr < 0) goto out; - else if (err == NL_SKIP) + else if (nlerr == NL_SKIP) goto skip; - else if (err == NL_STOP) { - err = -e->error; + else if (nlerr == NL_STOP) { + nlerr = -nl_syserr2nlerr (e->error); goto out; } + nm_assert (nlerr == NL_OK); } else { - err = -e->error; + nlerr = -nl_syserr2nlerr (e->error); goto out; } } else - NL_CB_CALL(cb, ack, msg); + NL_CB_CALL (cb, ack, msg); } else { /* Valid message (not checking for MULTIPART bit to * get along with broken kernels. NL_SKIP has no * effect on this. */ - NL_CB_CALL(cb, valid, msg); + NL_CB_CALL (cb, valid, msg); } skip: - err = 0; - hdr = nlmsg_next(hdr, &n); + nlerr = 0; + hdr = nlmsg_next (hdr, &n); } if (multipart) { @@ -1267,13 +1254,14 @@ skip: } stop: - err = 0; + nlerr = 0; out: if (interrupted) - err = -NLE_DUMP_INTR; + nlerr = -NLE_DUMP_INTR; - return err ?: nrecv; + nm_assert (nlerr <= 0); + return nlerr ?: nrecv; } int @@ -1286,7 +1274,7 @@ nl_sendmsg (struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) nlmsg_set_src (msg, &sk->s_local); - ret = sendmsg(sk->s_fd, hdr, 0); + ret = sendmsg (sk->s_fd, hdr, 0); if (ret < 0) return -nl_syserr2nlerr (errno); @@ -1304,31 +1292,31 @@ nl_send_iovec (struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsign .msg_iov = iov, .msg_iovlen = iovlen, }; - char buf[CMSG_SPACE(sizeof (struct ucred))]; + char buf[CMSG_SPACE (sizeof (struct ucred))]; /* Overwrite destination if specified in the message itself, defaults * to the peer address of the socket. */ - dst = nlmsg_get_dst(msg); + dst = nlmsg_get_dst (msg); if (dst->nl_family == AF_NETLINK) hdr.msg_name = dst; /* Add credentials if present. */ - creds = nlmsg_get_creds(msg); + creds = nlmsg_get_creds (msg); if (creds != NULL) { struct cmsghdr *cmsg; hdr.msg_control = buf; hdr.msg_controllen = sizeof (buf); - cmsg = CMSG_FIRSTHDR(&hdr); + cmsg = CMSG_FIRSTHDR (&hdr); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof (struct ucred)); - memcpy(CMSG_DATA(cmsg), creds, sizeof (struct ucred)); + cmsg->cmsg_len = CMSG_LEN (sizeof (struct ucred)); + memcpy (CMSG_DATA (cmsg), creds, sizeof (struct ucred)); } - return nl_sendmsg(sk, msg, &hdr); + return nl_sendmsg (sk, msg, &hdr); } void @@ -1336,9 +1324,9 @@ nl_complete_msg (struct nl_sock *sk, struct nl_msg *msg) { struct nlmsghdr *nlh; - nlh = nlmsg_hdr(msg); + nlh = nlmsg_hdr (msg); if (nlh->nlmsg_pid == NL_AUTO_PORT) - nlh->nlmsg_pid = nl_socket_get_local_port(sk); + nlh->nlmsg_pid = nl_socket_get_local_port (sk); if (nlh->nlmsg_seq == NL_AUTO_SEQ) nlh->nlmsg_seq = sk->s_seq_next++; @@ -1356,18 +1344,18 @@ int nl_send (struct nl_sock *sk, struct nl_msg *msg) { struct iovec iov = { - .iov_base = (void *) nlmsg_hdr(msg), - .iov_len = nlmsg_hdr(msg)->nlmsg_len, + .iov_base = (void *) nlmsg_hdr (msg), + .iov_len = nlmsg_hdr (msg)->nlmsg_len, }; - return nl_send_iovec(sk, msg, &iov, 1); + return nl_send_iovec (sk, msg, &iov, 1); } -int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) +int nl_send_auto (struct nl_sock *sk, struct nl_msg *msg) { - nl_complete_msg(sk, msg); + nl_complete_msg (sk, msg); - return nl_send(sk, msg); + return nl_send (sk, msg); } int @@ -1397,9 +1385,9 @@ nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, flags |= MSG_PEEK | MSG_TRUNC; if (page_size == 0) - page_size = getpagesize() * 4; + page_size = getpagesize () * 4; - iov.iov_len = sk->s_bufsize ? : page_size; + iov.iov_len = sk->s_bufsize ?: page_size; iov.iov_base = g_malloc (iov.iov_len); if ( creds @@ -1409,7 +1397,7 @@ nl_recv (struct nl_sock *sk, struct sockaddr_nl *nla, } retry: - n = recvmsg(sk->s_fd, &msg, flags); + n = recvmsg (sk->s_fd, &msg, flags); if (!n) { retval = 0; goto abort; @@ -1465,12 +1453,12 @@ retry: if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) { struct cmsghdr *cmsg; - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET) continue; if (cmsg->cmsg_type != SCM_CREDENTIALS) continue; - tmpcreds = g_memdup (CMSG_DATA(cmsg), sizeof (*tmpcreds)); + tmpcreds = nm_memdup (CMSG_DATA (cmsg), sizeof (*tmpcreds)); break; } } diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index 185269ba..d5df7ab9 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -52,32 +52,48 @@ #endif static inline int -nl_errno (int err) +nl_errno (int nlerr) { - /* the error codes from our netlink implementation are plain errno - * extended with our own error in a particular range starting from - * _NLE_BASE. + /* Normalizes an netlink error to be positive. Various API returns negative + * error codes, and this function converts the negative value to its + * positive. * - * However, often we encode errors as negative values. This function - * normalizes the error and returns its positive value. */ - return err >= 0 - ? err - : ((err == G_MININT) ? NLE_BUG : -errno); + * It's very similar to nm_errno(), but not exactly. The difference is that + * nm_errno() is for plain errno, while nl_errno() is for netlink error numbers. + * Yes, netlink error number are ~almost~ the same as errno, except that a particular + * range (_NLE_BASE, _NLE_BASE_END) is reserved. The difference between the two + * functions is only how G_MININT is mapped. + * + * See also nl_syserr2nlerr() below. */ + return nlerr >= 0 + ? nlerr + : ((nlerr == G_MININT) ? NLE_BUG : -nlerr); } static inline int -nl_syserr2nlerr (int err) +nl_syserr2nlerr (int errsv) { - if (err == G_MININT) + /* this maps a native errno to a (always non-negative) netlink error number. + * + * Note that netlink error numbers are embedded into the range of regular + * errno. The only difference is, that netlink error numbers reserve a + * range (_NLE_BASE, _NLE_BASE_END) for their own purpose. + * + * That means, converting an errno to netlink error number means in + * most cases just returning itself (negative values are normalized + * to be positive). Only values G_MININT and [_NLE_BASE, _NLE_BASE_END] + * are coerced to the special value NLE_NATIVE_ERRNO, as they cannot + * otherwise be represented in netlink error number domain. */ + if (errsv == G_MININT) return NLE_NATIVE_ERRNO; - if (err < 0) - err = -err; - return (err >= _NLE_BASE && err < _NLE_BASE_END) + if (errsv < 0) + errsv = -errsv; + return (errsv >= _NLE_BASE && errsv < _NLE_BASE_END) ? NLE_NATIVE_ERRNO - : err; + : errsv; } -const char *nl_geterror (int err); +const char *nl_geterror (int nlerr); /*****************************************************************************/ @@ -177,6 +193,12 @@ nla_get_u8 (const struct nlattr *nla) } static inline uint8_t +nla_get_s8 (const struct nlattr *nla) +{ + return *(const int8_t *) nla_data (nla); +} + +static inline uint8_t nla_get_u8_cond (/*const*/ struct nlattr *const*tb, int attr, uint8_t default_val) { nm_assert (tb); @@ -197,6 +219,12 @@ nla_get_u32(const struct nlattr *nla) return *(const uint32_t *) nla_data (nla); } +static inline int32_t +nla_get_s32(const struct nlattr *nla) +{ + return *(const int32_t *) nla_data (nla); +} + uint64_t nla_get_u64 (const struct nlattr *nla); static inline char * @@ -232,12 +260,18 @@ nla_put_string (struct nl_msg *msg, int attrtype, const char *str) #define NLA_PUT_U8(msg, attrtype, value) \ NLA_PUT_TYPE(msg, uint8_t, attrtype, value) +#define NLA_PUT_S8(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int8_t, attrtype, value) + #define NLA_PUT_U16(msg, attrtype, value) \ NLA_PUT_TYPE(msg, uint16_t, attrtype, value) #define NLA_PUT_U32(msg, attrtype, value) \ NLA_PUT_TYPE(msg, uint32_t, attrtype, value) +#define NLA_PUT_S32(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int32_t, attrtype, value) + #define NLA_PUT_U64(msg, attrtype, value) \ NLA_PUT_TYPE(msg, uint64_t, attrtype, value) @@ -252,7 +286,7 @@ struct nlattr *nla_find (const struct nlattr *head, int len, int attrtype); static inline int nla_ok (const struct nlattr *nla, int remaining) { - return remaining >= sizeof(*nla) && + return remaining >= (int) sizeof(*nla) && nla->nla_len >= sizeof(*nla) && nla->nla_len <= remaining; } @@ -296,8 +330,6 @@ struct nl_msg *nlmsg_alloc (void); struct nl_msg *nlmsg_alloc_size (size_t max); -struct nl_msg *nlmsg_alloc_inherit (struct nlmsghdr *hdr); - struct nl_msg *nlmsg_alloc_convert (struct nlmsghdr *hdr); struct nl_msg *nlmsg_alloc_simple (int nlmsgtype, int flags); diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 0d224443..216b1547 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -36,12 +36,15 @@ #include "nm-utils.h" #include "nm-setting-wired.h" +#include "nm-ethtool-utils.h" #include "nm-core-utils.h" -/****************************************************************** +#define ONOFF(bool_val) ((bool_val) ? "on" : "off") + +/****************************************************************************** * utils - ******************************************************************/ + *****************************************************************************/ extern char *if_indextoname (unsigned __ifindex, char *__ifname); unsigned if_nametoindex (const char *__ifname); @@ -63,9 +66,45 @@ nmp_utils_if_nametoindex (const char *ifname) return if_nametoindex (ifname); } -/****************************************************************** +/*****************************************************************************/ + +typedef struct { + int fd; + int ifindex; + char ifname[IFNAMSIZ]; +} SocketHandle; + +static int +socket_handle_init (SocketHandle *shandle, int ifindex) +{ + if (!nmp_utils_if_indextoname (ifindex, shandle->ifname)) { + shandle->ifindex = 0; + return -ENODEV; + } + + shandle->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (shandle->fd < 0) { + shandle->ifindex = 0; + return -errno; + } + + shandle->ifindex = ifindex; + return 0; +} + +static void +socket_handle_destroy (SocketHandle *shandle) +{ + if (shandle->ifindex) { + shandle->ifindex = 0; + nm_close (shandle->fd); + } +} +#define nm_auto_socket_handle nm_auto(socket_handle_destroy) + +/****************************************************************************** * ethtool - ******************************************************************/ + *****************************************************************************/ NM_UTILS_ENUM2STR_DEFINE_STATIC (_ethtool_cmd_to_string, guint32, NM_UTILS_ENUM2STR (ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), @@ -77,6 +116,7 @@ NM_UTILS_ENUM2STR_DEFINE_STATIC (_ethtool_cmd_to_string, guint32, NM_UTILS_ENUM2STR (ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), NM_UTILS_ENUM2STR (ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), NM_UTILS_ENUM2STR (ETHTOOL_GWOL, "ETHTOOL_GWOL"), + NM_UTILS_ENUM2STR (ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"), NM_UTILS_ENUM2STR (ETHTOOL_SSET, "ETHTOOL_SSET"), NM_UTILS_ENUM2STR (ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); @@ -87,6 +127,8 @@ _ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) return _ethtool_cmd_to_string (*((guint32 *) edata), buf, len); } +/*****************************************************************************/ + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) #define ethtool_cmd_speed(pedata) ((pedata)->speed) @@ -94,117 +136,553 @@ _ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) G_STMT_START { (pedata)->speed = (guint16) (speed); } G_STMT_END #endif -static gboolean -ethtool_get (int ifindex, gpointer edata) +static int +ethtool_call_handle (SocketHandle *shandle, gpointer edata) { - char ifname[IFNAMSIZ]; + struct ifreq ifr = { + .ifr_data = edata, + }; char sbuf[50]; + int errsv; + + nm_assert (shandle); + nm_assert (shandle->ifindex); + nm_assert (shandle->ifname[0]); + nm_assert (strlen (shandle->ifname) < IFNAMSIZ); + nm_assert (edata); + + memcpy (ifr.ifr_name, shandle->ifname, IFNAMSIZ); + if (ioctl (shandle->fd, SIOCETHTOOL, &ifr) < 0) { + errsv = errno; + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed: %s", + shandle->ifindex, + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + shandle->ifname, + strerror (errsv)); + return -errsv; + } - nm_assert (ifindex > 0); + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success", + shandle->ifindex, + _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), + shandle->ifname); + return 0; +} - /* ethtool ioctl API uses the ifname to refer to an interface. That is racy - * as interfaces can be renamed *sigh*. - * - * Note that we anyway have to verify whether the interface exists, before - * calling ioctl for a non-existing ifname. This is to prevent autoloading - * of kernel modules *sigh*. - * Thus, as we anyway verify the existence of ifname before doing the call, - * go one step further and lookup the ifname everytime anew. - * - * This does not solve the renaming race, but it minimizes the time for - * the race to happen as much as possible. */ +static int +ethtool_call_ifindex (int ifindex, gpointer edata) +{ + nm_auto_socket_handle SocketHandle shandle = { }; + int r; + char sbuf[50]; - if (!nmp_utils_if_indextoname (ifindex, ifname)) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: request fails resolving ifindex: %s", + nm_assert (edata); + + if ((r = socket_handle_init (&shandle, ifindex)) < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", ifindex, _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - g_strerror (errno)); + g_strerror (-r)); + return r; + } + + return ethtool_call_handle (&shandle, edata); +} + +/*****************************************************************************/ + +static struct ethtool_gstrings * +ethtool_get_stringset (SocketHandle *shandle, int stringset_id) +{ + struct { + struct ethtool_sset_info info; + guint32 sentinel; + } sset_info = { }; + gs_free struct ethtool_gstrings *gstrings = NULL; + guint32 i, len; + + sset_info.info.cmd = ETHTOOL_GSSET_INFO; + sset_info.info.reserved = 0; + sset_info.info.sset_mask = (1ULL << stringset_id); + + if (ethtool_call_handle (shandle, &sset_info) < 0) + return NULL; + if (!sset_info.info.sset_mask) + return NULL; + + len = sset_info.info.data[0]; + + gstrings = g_malloc0 (sizeof (*gstrings) + (len * ETH_GSTRING_LEN)); + gstrings->cmd = ETHTOOL_GSTRINGS; + gstrings->string_set = stringset_id; + gstrings->len = len; + if (gstrings->len > 0) { + if (ethtool_call_handle (shandle, gstrings) < 0) + return NULL; + for (i = 0; i < gstrings->len; i++) { + /* ensure NUL terminated */ + gstrings->data[i * ETH_GSTRING_LEN + (ETH_GSTRING_LEN - 1)] = '\0'; + } + } + + return g_steal_pointer (&gstrings); +} + +static int +ethtool_gstrings_find (const struct ethtool_gstrings *gstrings, const char *needle) +{ + guint32 i; + + /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. + * that means, we cannot possibly request longer names. */ + nm_assert (needle && strlen (needle) < ETH_GSTRING_LEN); + + for (i = 0; i < gstrings->len; i++) { + if (nm_streq ((char *) &gstrings->data[i * ETH_GSTRING_LEN], needle)) + return i; + } + return -1; +} + +static int +ethtool_get_stringset_index (SocketHandle *shandle, int stringset_id, const char *needle) +{ + gs_free struct ethtool_gstrings *gstrings = NULL; + + /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. + * that means, we cannot possibly request longer names. */ + nm_assert (needle && strlen (needle) < ETH_GSTRING_LEN); + + gstrings = ethtool_get_stringset (shandle, stringset_id); + if (gstrings) + return ethtool_gstrings_find (gstrings, needle); + return -1; +} + +/*****************************************************************************/ + +static const NMEthtoolFeatureInfo _ethtool_feature_infos[_NM_ETHTOOL_ID_FEATURE_NUM] = { +#define ETHT_FEAT(eid, ...) \ + { \ + .ethtool_id = eid, \ + .n_kernel_names = NM_NARG (__VA_ARGS__), \ + .kernel_names = ((const char *const[]) { __VA_ARGS__ }), \ + } + + /* the order does only matter for one thing: if it happens that more than one NMEthtoolID + * reference the same kernel-name, then the one that is mentioned *later* will win in + * case these NMEthtoolIDs are set. That mostly only makes sense for ethtool-ids which + * refer to multiple features ("feature-tso"), while also having more specific ids + * ("feature-tx-tcp-segmentation"). */ + + /* names from ethtool utility, which are aliases for multiple features. */ + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_SG, "tx-scatter-gather", + "tx-scatter-gather-fraglist"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TSO, "tx-tcp-segmentation", + "tx-tcp-ecn-segmentation", + "tx-tcp-mangleid-segmentation", + "tx-tcp6-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX, "tx-checksum-ipv4", + "tx-checksum-ip-generic", + "tx-checksum-ipv6", + "tx-checksum-fcoe-crc", + "tx-checksum-sctp"), + + /* names from ethtool utility, which are aliases for one feature. */ + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert"), + + /* names of features, as known by kernel. */ + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, "esp-hw-offload"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, "esp-tx-csum-hw-offload"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_FCOE_MTU, "fcoe-mtu"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_HIGHDMA, "highdma"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, "hw-tc-offload"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, "l2-fwd-offload"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_LOOPBACK, "loopback"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX_ALL, "rx-all"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX_FCS, "rx-fcs"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, "rx-gro-hw"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, "rx-udp_tunnel-port-offload"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, "rx-vlan-filter"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, "rx-vlan-stag-filter"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, "rx-vlan-stag-hw-parse"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, "tls-hw-record"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, "tls-hw-tx-offload"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, "tx-checksum-fcoe-crc"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, "tx-checksum-ipv4"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, "tx-checksum-ipv6"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, "tx-checksum-ip-generic"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, "tx-checksum-sctp"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, "tx-esp-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, "tx-fcoe-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, "tx-gre-csum-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, "tx-gre-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, "tx-gso-partial"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, "tx-gso-robust"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, "tx-ipxip4-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, "tx-ipxip6-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, "tx-nocache-copy"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, "tx-scatter-gather"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, "tx-scatter-gather-fraglist"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, "tx-sctp-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, "tx-tcp-ecn-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, "tx-udp-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"), + ETHT_FEAT (NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"), +}; + +/* the number of kernel features that we handle. It essentially is the sum of all + * kernel_names. So, all ethtool-ids that reference exactly one kernel-name + * (_NM_ETHTOOL_ID_FEATURE_NUM) + some extra, for ethtool-ids that are aliases + * for multiple kernel-names. */ +#define N_ETHTOOL_KERNEL_FEATURES (((guint) _NM_ETHTOOL_ID_FEATURE_NUM) + 8u) + +static void +_ASSERT_ethtool_feature_infos (void) +{ +#if NM_MORE_ASSERTS > 10 + guint i, k, n; + bool found[_NM_ETHTOOL_ID_FEATURE_NUM] = { }; + + G_STATIC_ASSERT_EXPR (G_N_ELEMENTS (_ethtool_feature_infos) == _NM_ETHTOOL_ID_FEATURE_NUM); + + n = 0; + for (i = 0; i < G_N_ELEMENTS (_ethtool_feature_infos); i++) { + NMEthtoolFeatureState kstate; + const NMEthtoolFeatureInfo *inf = &_ethtool_feature_infos[i]; + + g_assert (inf->ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST); + g_assert (inf->ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST); + g_assert (inf->n_kernel_names > 0); + + for (k = 0; k < i; k++) + g_assert (inf->ethtool_id != _ethtool_feature_infos[k].ethtool_id); + + g_assert (!found[inf->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]); + found[inf->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = TRUE; + + kstate.idx_kernel_name = inf->n_kernel_names - 1; + g_assert ((guint) kstate.idx_kernel_name == (guint) (inf->n_kernel_names - 1)); + + n += inf->n_kernel_names; + for (k = 0; k < inf->n_kernel_names; k++) { + g_assert (nm_utils_strv_find_first ((char **) inf->kernel_names, + k, + inf->kernel_names[k]) < 0); + } + } + + for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) + g_assert (found[i]); + + g_assert (n == N_ETHTOOL_KERNEL_FEATURES); +#endif +} + +static NMEthtoolFeatureStates * +ethtool_get_features (SocketHandle *shandle) +{ + gs_free NMEthtoolFeatureStates *states = NULL; + gs_free struct ethtool_gstrings *ss_features = NULL; + + _ASSERT_ethtool_feature_infos (); + + ss_features = ethtool_get_stringset (shandle, ETH_SS_FEATURES); + if (!ss_features) + return NULL; + + if (ss_features->len > 0) { + gs_free struct ethtool_gfeatures *gfeatures = NULL; + guint idx; + const NMEthtoolFeatureState *states_list0 = NULL; + const NMEthtoolFeatureState *const*states_plist0 = NULL; + guint states_plist_n = 0; + + gfeatures = g_malloc0 ( sizeof (struct ethtool_gfeatures) + + (NM_DIV_ROUND_UP (ss_features->len, 32u) * sizeof(gfeatures->features[0]))); + + gfeatures->cmd = ETHTOOL_GFEATURES; + gfeatures->size = NM_DIV_ROUND_UP (ss_features->len, 32u); + if (ethtool_call_handle (shandle, gfeatures) < 0) + return NULL; + + for (idx = 0; idx < G_N_ELEMENTS (_ethtool_feature_infos); idx++) { + const NMEthtoolFeatureInfo *info = &_ethtool_feature_infos[idx]; + guint idx_kernel_name; + + for (idx_kernel_name = 0; idx_kernel_name < info->n_kernel_names; idx_kernel_name++) { + NMEthtoolFeatureState *kstate; + const char *kernel_name = info->kernel_names[idx_kernel_name]; + int i_feature; + guint i_block; + guint32 i_flag; + + i_feature = ethtool_gstrings_find (ss_features, kernel_name); + if (i_feature < 0) + continue; + + i_block = ((guint) i_feature) / 32u; + i_flag = (guint32) (1u << (((guint) i_feature) % 32u)); + + if (!states) { + states = g_malloc0 (sizeof (NMEthtoolFeatureStates) + + (N_ETHTOOL_KERNEL_FEATURES * sizeof (NMEthtoolFeatureState)) + + ((N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS (_ethtool_feature_infos)) * sizeof (NMEthtoolFeatureState *))); + states_list0 = &states->states_list[0]; + states_plist0 = (gpointer) &states_list0[N_ETHTOOL_KERNEL_FEATURES]; + states->n_ss_features = ss_features->len; + } + + nm_assert (states->n_states < N_ETHTOOL_KERNEL_FEATURES); + kstate = (NMEthtoolFeatureState *) &states_list0[states->n_states]; + states->n_states++; + + kstate->info = info; + kstate->idx_ss_features = i_feature; + kstate->idx_kernel_name = idx_kernel_name; + kstate->available = !!(gfeatures->features[i_block].available & i_flag); + kstate->requested = !!(gfeatures->features[i_block].requested & i_flag); + kstate->active = !!(gfeatures->features[i_block].active & i_flag); + kstate->never_changed = !!(gfeatures->features[i_block].never_changed & i_flag); + + nm_assert (states_plist_n < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS (_ethtool_feature_infos)); + + if (!states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]) + states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST] = &states_plist0[states_plist_n]; + ((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate; + states_plist_n++; + } + + if (states && states->states_indexed[info->ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]) { + nm_assert (states_plist_n < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS (_ethtool_feature_infos)); + nm_assert (!states_plist0[states_plist_n]); + states_plist_n++; + } + } + } + + return g_steal_pointer (&states); +} + +NMEthtoolFeatureStates * +nmp_utils_ethtool_get_features (int ifindex) +{ + nm_auto_socket_handle SocketHandle shandle = { }; + NMEthtoolFeatureStates *features; + int r; + + g_return_val_if_fail (ifindex > 0, 0); + + if ((r = socket_handle_init (&shandle, ifindex)) < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", + ifindex, + "get-features", + g_strerror (-r)); return FALSE; } - { - nm_auto_close int fd = -1; - struct ifreq ifr = { - .ifr_data = edata, - }; + features = ethtool_get_features (&shandle); - memcpy (ifr.ifr_name, ifname, sizeof (ifname)); + if (!features) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure getting features", + ifindex, + "get-features"); + return NULL; + } - fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed creating socket for ioctl: %s", - ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - ifname, - g_strerror (errno)); - return FALSE; + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: retrieved kernel features", + ifindex, + "get-features"); + return features; +} + +static const char * +_ethtool_feature_state_to_string (char *buf, gsize buf_size, const NMEthtoolFeatureState *s, const char *prefix) +{ + int l; + + l = g_snprintf (buf, buf_size, + "%s %s%s", + prefix ?: "", + ONOFF (s->active), + (!s->available || s->never_changed) + ? ", [fixed]" + : ((s->requested != s->active) + ? (s->requested ? ", [requested on]" : ", [requested off]") + : "")); + nm_assert (l < buf_size); + return buf; +} + +gboolean +nmp_utils_ethtool_set_features (int ifindex, + const NMEthtoolFeatureStates *features, + const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, + gboolean do_set /* or reset */) +{ + nm_auto_socket_handle SocketHandle shandle = { }; + gs_free struct ethtool_sfeatures *sfeatures = NULL; + int r; + guint i, j; + struct { + const NMEthtoolFeatureState *f_state; + NMTernary requested; + } set_states[N_ETHTOOL_KERNEL_FEATURES]; + guint set_states_n = 0; + gboolean success = TRUE; + + g_return_val_if_fail (ifindex > 0, 0); + g_return_val_if_fail (features, 0); + g_return_val_if_fail (requested, 0); + + nm_assert (features->n_states <= N_ETHTOOL_KERNEL_FEATURES); + + for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { + const NMEthtoolFeatureState *const*states_indexed; + + if (requested[i] == NM_TERNARY_DEFAULT) + continue; + + if (!(states_indexed = features->states_indexed[i])) { + if (do_set) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: set feature %s: skip (not found)", + ifindex, + "set-features", + nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname); + success = FALSE; + } + continue; } - if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed: %s", + for (j = 0; states_indexed[j]; j++) { + const NMEthtoolFeatureState *s = states_indexed[j]; + char sbuf[255]; + + if (set_states_n >= G_N_ELEMENTS (set_states)) + g_return_val_if_reached (FALSE); + + if (s->never_changed) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: %s feature %s (%s): %s, %s (skip feature marked as never changed)", + ifindex, + "set-features", + do_set ? "set" : "reset", + nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, + s->info->kernel_names[s->idx_kernel_name], + ONOFF (do_set ? requested[i] == NM_TERNARY_TRUE : s->active), + _ethtool_feature_state_to_string (sbuf, sizeof (sbuf), s, do_set ? " currently:" : " before:")); + continue; + } + + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: %s feature %s (%s): %s, %s", ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - ifname, - strerror (errno)); - return FALSE; + "set-features", + do_set ? "set" : "reset", + nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, + s->info->kernel_names[s->idx_kernel_name], + ONOFF (do_set ? requested[i] == NM_TERNARY_TRUE : s->active), + _ethtool_feature_state_to_string (sbuf, sizeof (sbuf), s, do_set ? " currently:" : " before:")); + + if ( do_set + && (!s->available || s->never_changed) + && (s->active != (requested[i] == NM_TERNARY_TRUE))) { + /* we request to change a flag which kernel reported as fixed. + * While the ethtool operation will silently succeed, mark the request + * as failure. */ + success = FALSE; + } + + set_states[set_states_n].f_state = s; + set_states[set_states_n].requested = requested[i]; + set_states_n++; } + } - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success", + if (set_states_n == 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: no feature requested", ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - ifname); + "set-features"); return TRUE; } -} -static int -ethtool_get_stringset_index (int ifindex, int stringset_id, const char *string) -{ - gs_free struct ethtool_sset_info *info = NULL; - gs_free struct ethtool_gstrings *strings = NULL; - guint32 len, i; + if ((r = socket_handle_init (&shandle, ifindex)) < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", + ifindex, + "set-features", + g_strerror (-r)); + return FALSE; + } - g_return_val_if_fail (ifindex > 0, -1); + sfeatures = g_malloc0 (sizeof (struct ethtool_sfeatures) + + (NM_DIV_ROUND_UP (features->n_ss_features, 32U) * sizeof(sfeatures->features[0]))); + sfeatures->cmd = ETHTOOL_SFEATURES; + sfeatures->size = NM_DIV_ROUND_UP (features->n_ss_features, 32U); - info = g_malloc0 (sizeof (*info) + sizeof (guint32)); - info->cmd = ETHTOOL_GSSET_INFO; - info->reserved = 0; - info->sset_mask = 1ULL << stringset_id; + for (i = 0; i < set_states_n; i++) { + const NMEthtoolFeatureState *s = set_states[i].f_state; + guint i_block; + guint32 i_flag; + gboolean is_requested; - if (!ethtool_get (ifindex, info)) - return -1; - if (!info->sset_mask) - return -1; + i_block = s->idx_ss_features / 32u; + i_flag = (guint32) (1u << (s->idx_ss_features % 32u)); - len = info->data[0]; + sfeatures->features[i_block].valid |= i_flag; - strings = g_malloc0 (sizeof (*strings) + len * ETH_GSTRING_LEN); - strings->cmd = ETHTOOL_GSTRINGS; - strings->string_set = stringset_id; - strings->len = len; - if (!ethtool_get (ifindex, strings)) - return -1; + if (do_set) + is_requested = (set_states[i].requested == NM_TERNARY_TRUE); + else + is_requested = s->active; - for (i = 0; i < len; i++) { - if (!strcmp ((char *) &strings->data[i * ETH_GSTRING_LEN], string)) - return i; + if (is_requested) + sfeatures->features[i_block].requested |= i_flag; + else + sfeatures->features[i_block].requested &= ~i_flag; } - return -1; + if ((r = ethtool_call_handle (&shandle, sfeatures)) < 0) { + success = FALSE; + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure setting features (%s)", + ifindex, + "set-features", + g_strerror (-r)); + return FALSE; + } + + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: %s", + ifindex, + "set-features", + success + ? "successfully setting features" + : "at least some of the features were not successfuly set"); + return success; } +/*****************************************************************************/ + gboolean nmp_utils_ethtool_get_driver_info (int ifindex, NMPUtilsEthtoolDriverInfo *data) { struct ethtool_drvinfo *drvinfo; - G_STATIC_ASSERT (sizeof (*data) == sizeof (*drvinfo)); - G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, driver) == offsetof (struct ethtool_drvinfo, driver)); - G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, version) == offsetof (struct ethtool_drvinfo, version)); - G_STATIC_ASSERT (offsetof (NMPUtilsEthtoolDriverInfo, fw_version) == offsetof (struct ethtool_drvinfo, fw_version)); - G_STATIC_ASSERT (sizeof (data->driver) == sizeof (drvinfo->driver)); - G_STATIC_ASSERT (sizeof (data->version) == sizeof (drvinfo->version)); - G_STATIC_ASSERT (sizeof (data->fw_version) == sizeof (drvinfo->fw_version)); + + G_STATIC_ASSERT_EXPR (sizeof (*data) == sizeof (*drvinfo)); + G_STATIC_ASSERT_EXPR (offsetof (NMPUtilsEthtoolDriverInfo, driver) == offsetof (struct ethtool_drvinfo, driver)); + G_STATIC_ASSERT_EXPR (offsetof (NMPUtilsEthtoolDriverInfo, version) == offsetof (struct ethtool_drvinfo, version)); + G_STATIC_ASSERT_EXPR (offsetof (NMPUtilsEthtoolDriverInfo, fw_version) == offsetof (struct ethtool_drvinfo, fw_version)); + G_STATIC_ASSERT_EXPR (sizeof (data->driver) == sizeof (drvinfo->driver)); + G_STATIC_ASSERT_EXPR (sizeof (data->version) == sizeof (drvinfo->version)); + G_STATIC_ASSERT_EXPR (sizeof (data->fw_version) == sizeof (drvinfo->fw_version)); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (data, FALSE); @@ -213,7 +691,7 @@ nmp_utils_ethtool_get_driver_info (int ifindex, memset (drvinfo, 0, sizeof (*drvinfo)); drvinfo->cmd = ETHTOOL_GDRVINFO; - return ethtool_get (ifindex, drvinfo); + return ethtool_call_ifindex (ifindex, drvinfo) >= 0; } gboolean @@ -233,7 +711,7 @@ nmp_utils_ethtool_get_permanent_address (int ifindex, edata.e.cmd = ETHTOOL_GPERMADDR; edata.e.size = NM_UTILS_HWADDR_LEN_MAX; - if (!ethtool_get (ifindex, &edata.e)) + if (ethtool_call_ifindex (ifindex, &edata.e) < 0) return FALSE; if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX) @@ -252,8 +730,8 @@ nmp_utils_ethtool_get_permanent_address (int ifindex, } return FALSE; } -not_all_0or1: +not_all_0or1: memcpy (buf, edata.e.data, edata.e.size); *length = edata.e.size; return TRUE; @@ -270,20 +748,30 @@ nmp_utils_ethtool_supports_carrier_detect (int ifindex) * assume the device supports carrier-detect, otherwise we assume it * doesn't. */ - return ethtool_get (ifindex, &edata); + return ethtool_call_ifindex (ifindex, &edata) >= 0; } gboolean nmp_utils_ethtool_supports_vlans (int ifindex) { + nm_auto_socket_handle SocketHandle shandle = { }; + int r; gs_free struct ethtool_gfeatures *features = NULL; int idx, block, bit, size; g_return_val_if_fail (ifindex > 0, FALSE); - idx = ethtool_get_stringset_index (ifindex, ETH_SS_FEATURES, "vlan-challenged"); - if (idx == -1) { - nm_log_dbg (LOGD_PLATFORM, "ethtool: vlan-challenged ethtool feature does not exist for %d?", ifindex); + if ((r = socket_handle_init (&shandle, ifindex)) < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", + ifindex, + "support-vlans", + g_strerror (-r)); + return FALSE; + } + + idx = ethtool_get_stringset_index (&shandle, ETH_SS_FEATURES, "vlan-challenged"); + if (idx < 0) { + nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: vlan-challenged ethtool feature does not exist?", ifindex); return FALSE; } @@ -295,7 +783,7 @@ nmp_utils_ethtool_supports_vlans (int ifindex) features->cmd = ETHTOOL_GFEATURES; features->size = size; - if (!ethtool_get (ifindex, features)) + if (ethtool_call_handle (&shandle, features) < 0) return FALSE; return !(features->features[block].active & (1 << bit)); @@ -304,21 +792,32 @@ nmp_utils_ethtool_supports_vlans (int ifindex) int nmp_utils_ethtool_get_peer_ifindex (int ifindex) { + nm_auto_socket_handle SocketHandle shandle = { }; + int r; + gs_free struct ethtool_stats *stats = NULL; int peer_ifindex_stat; g_return_val_if_fail (ifindex > 0, 0); - peer_ifindex_stat = ethtool_get_stringset_index (ifindex, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat == -1) { - nm_log_dbg (LOGD_PLATFORM, "ethtool: peer_ifindex stat for %d does not exist?", ifindex); + if ((r = socket_handle_init (&shandle, ifindex)) < 0) { + nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", + ifindex, + "get-peer-ifindex", + g_strerror (-r)); + return FALSE; + } + + peer_ifindex_stat = ethtool_get_stringset_index (&shandle, ETH_SS_STATS, "peer_ifindex"); + if (peer_ifindex_stat < 0) { + nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); return FALSE; } stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); stats->cmd = ETHTOOL_GSTATS; stats->n_stats = peer_ifindex_stat + 1; - if (!ethtool_get (ifindex, stats)) + if (ethtool_call_ifindex (ifindex, stats) < 0) return 0; return stats->data[peer_ifindex_stat]; @@ -333,7 +832,7 @@ nmp_utils_ethtool_get_wake_on_lan (int ifindex) memset (&wol, 0, sizeof (wol)); wol.cmd = ETHTOOL_GWOL; - if (!ethtool_get (ifindex, &wol)) + if (ethtool_call_ifindex (ifindex, &wol) < 0) return FALSE; return wol.wolopts != 0; @@ -351,7 +850,7 @@ nmp_utils_ethtool_get_link_settings (int ifindex, g_return_val_if_fail (ifindex > 0, FALSE); - if (!ethtool_get (ifindex, &edata)) + if (ethtool_call_ifindex (ifindex, &edata) < 0) return FALSE; if (out_autoneg) @@ -433,9 +932,13 @@ nmp_utils_ethtool_set_link_settings (int ifindex, || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), FALSE); /* retrieve first current settings */ - if (!ethtool_get (ifindex, &edata)) + if (ethtool_call_ifindex (ifindex, &edata) < 0) return FALSE; + /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf + */ + /* then change the needed ones */ edata.cmd = ETHTOOL_SSET; if (autoneg) { @@ -485,7 +988,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, } } - return ethtool_get (ifindex, &edata); + return ethtool_call_ifindex (ifindex, &edata) >= 0; } gboolean @@ -500,8 +1003,8 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) return TRUE; - nm_log_dbg (LOGD_PLATFORM, "setting Wake-on-LAN options 0x%x, password '%s'", - (unsigned) wol, wol_password); + nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", + ifindex, (unsigned) wol, wol_password); wol_info.cmd = ETHTOOL_SWOL; wol_info.wolopts = 0; @@ -521,45 +1024,41 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, if (wol_password) { if (!nm_utils_hwaddr_aton (wol_password, wol_info.sopass, ETH_ALEN)) { - nm_log_dbg (LOGD_PLATFORM, "couldn't parse Wake-on-LAN password '%s'", wol_password); + nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: couldn't parse Wake-on-LAN password '%s'", ifindex, wol_password); return FALSE; } wol_info.wolopts |= WAKE_MAGICSECURE; } - return ethtool_get (ifindex, &wol_info); + return ethtool_call_ifindex (ifindex, &wol_info) >= 0; } -/****************************************************************** +/****************************************************************************** * mii - ******************************************************************/ + *****************************************************************************/ gboolean nmp_utils_mii_supports_carrier_detect (int ifindex) { - char ifname[IFNAMSIZ]; - nm_auto_close int fd = -1; + nm_auto_socket_handle SocketHandle shandle = { }; + int r; struct ifreq ifr; struct mii_ioctl_data *mii; g_return_val_if_fail (ifindex > 0, FALSE); - if (!nmp_utils_if_indextoname (ifindex, ifname)) { - nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: request fails resolving ifindex: %s", ifindex, g_strerror (errno)); - return FALSE; - } - - fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: couldn't open control socket: %s", ifindex, ifname, g_strerror (errno)); + if ((r = socket_handle_init (&shandle, ifindex)) < 0) { + nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: failed creating ethtool socket: %s", + ifindex, + g_strerror (-r)); return FALSE; } memset (&ifr, 0, sizeof (struct ifreq)); - memcpy (ifr.ifr_name, ifname, IFNAMSIZ); + memcpy (ifr.ifr_name, shandle.ifname, IFNAMSIZ); - if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, ifname, strerror (errno)); + if (ioctl (shandle.fd, SIOCGMIIPHY, &ifr) < 0) { + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, shandle.ifname, strerror (errno)); return FALSE; } @@ -567,18 +1066,18 @@ nmp_utils_mii_supports_carrier_detect (int ifindex) mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; mii->reg_num = MII_BMSR; - if (ioctl (fd, SIOCGMIIREG, &ifr) != 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, ifname, strerror (errno)); + if (ioctl (shandle.fd, SIOCGMIIREG, &ifr) != 0) { + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, shandle.ifname, strerror (errno)); return FALSE; } - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, ifname, mii->val_out); + nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, shandle.ifname, mii->val_out); return TRUE; } -/****************************************************************** +/****************************************************************************** * udev - ******************************************************************/ + *****************************************************************************/ const char * nmp_utils_udev_get_driver (struct udev_device *udevice) diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h index 97f5630a..bad77aee 100644 --- a/src/platform/nm-platform-utils.h +++ b/src/platform/nm-platform-utils.h @@ -23,6 +23,9 @@ #include "nm-platform.h" #include "nm-setting-wired.h" +#include "nm-ethtool-utils.h" + +/*****************************************************************************/ const char *nmp_utils_ethtool_get_driver (int ifindex); gboolean nmp_utils_ethtool_supports_carrier_detect (int ifindex); @@ -35,6 +38,10 @@ gboolean nmp_utils_ethtool_set_wake_on_lan (int ifindex, NMSettingWiredWakeOnLan gboolean nmp_utils_ethtool_get_link_settings (int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); gboolean nmp_utils_ethtool_set_link_settings (int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); +gboolean nmp_utils_ethtool_get_permanent_address (int ifindex, + guint8 *buf, + size_t *length); + typedef struct { /* We don't want to include <linux/ethtool.h> in header files, * thus create a ABI compatible version of struct ethtool_drvinfo.*/ @@ -55,9 +62,52 @@ typedef struct { gboolean nmp_utils_ethtool_get_driver_info (int ifindex, NMPUtilsEthtoolDriverInfo *data); -gboolean nmp_utils_ethtool_get_permanent_address (int ifindex, - guint8 *buf, - size_t *length); +typedef struct { + NMEthtoolID ethtool_id; + + guint8 n_kernel_names; + + /* one NMEthtoolID refers to one or more kernel_names. The reason for supporting this complexity + * (where one NMSettingEthtool option refers to multiple kernel features) is to follow what + * ethtool does, where "tx" is an alias for multiple features. */ + const char *const*kernel_names; +} NMEthtoolFeatureInfo; + +typedef struct { + const NMEthtoolFeatureInfo *info; + + guint idx_ss_features; + + /* one NMEthtoolFeatureInfo references one or more kernel_names. This is the index + * of the matching info->kernel_names */ + guint8 idx_kernel_name; + + bool available:1; + bool requested:1; + bool active:1; + bool never_changed:1; +} NMEthtoolFeatureState; + +struct _NMEthtoolFeatureStates { + guint n_states; + + guint n_ss_features; + + /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */ + const NMEthtoolFeatureState *const*states_indexed[_NM_ETHTOOL_ID_FEATURE_NUM]; + + /* the same content, here as a list of n_states entries. */ + const NMEthtoolFeatureState states_list[]; +}; + +NMEthtoolFeatureStates *nmp_utils_ethtool_get_features (int ifindex); + +gboolean nmp_utils_ethtool_set_features (int ifindex, + const NMEthtoolFeatureStates *features, + const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, + gboolean do_set /* or reset */); + +/*****************************************************************************/ gboolean nmp_utils_mii_supports_carrier_detect (int ifindex); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f75019e8..7ddcf41a 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2012 - 2017 Red Hat, Inc. + * Copyright (C) 2012 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -27,6 +27,8 @@ #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <sys/socket.h> +#include <netdb.h> #include <string.h> #include <linux/ip.h> #include <linux/if_tun.h> @@ -74,8 +76,6 @@ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OF } \ } G_STMT_END -#define LOG_FMT_IP_TUNNEL "adding %s '%s' parent %u local %s remote %s" - /*****************************************************************************/ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 }; @@ -807,6 +807,7 @@ _nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_pl **/ const NMPlatformLink * nm_platform_link_get_by_address (NMPlatform *self, + NMLinkType link_type, gconstpointer address, size_t length) { @@ -827,7 +828,7 @@ nm_platform_link_get_by_address (NMPlatform *self, g_return_val_if_reached (NULL); obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self), - 0, NULL, TRUE, NM_LINK_TYPE_NONE, + 0, NULL, TRUE, link_type, (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); return NMP_OBJECT_CAST_LINK (obj); } @@ -901,7 +902,7 @@ nm_platform_link_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("link: adding link '%s' of type '%s' (%d)" + _LOGD ("link: adding link '%s': %s (%d)" "%s%s" /* address */ "%s%s" /* veth peer */ "", @@ -985,7 +986,7 @@ nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd) if (!pllink) return FALSE; - _LOGD ("link: ifindex %d changing network namespace to %d", ifindex, netns_fd); + _LOGD ("link: move link %d to network namespace with fd %d", ifindex, netns_fd); return klass->link_set_netns (self, ifindex, netns_fd); } @@ -1354,15 +1355,18 @@ nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolea NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer address, size_t length) { + gs_free char *mac = NULL; + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (ifindex > 0, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (length > 0, NM_PLATFORM_ERROR_BUG); - _LOGD ("link: setting %s (%d) hardware address", + _LOGD ("link: setting %s (%d) hardware address to %s", nm_strquote_a (20, nm_platform_link_get_name (self, ifindex)), - ifindex); + ifindex, + (mac = nm_utils_hwaddr_ntoa (address, length))); return klass->link_set_address (self, ifindex, address, length); } @@ -1459,18 +1463,52 @@ nm_platform_link_supports_sriov (NMPlatform *self, int ifindex) 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: -1 to keep the current autoprobe-drivers value, + * or {0,1} to set a new value + */ gboolean -nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs) +nm_platform_link_set_sriov_params (NMPlatform *self, + int ifindex, + guint num_vfs, + int autoprobe) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (NM_IN_SET (autoprobe, -1, 0, 1), FALSE); - _LOGD ("link: setting %u VFs for %s (%d)", + _LOGD ("link: setting %u total VFs and autoprobe %d for %s (%d)", num_vfs, + autoprobe, nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex); - return klass->link_set_sriov_num_vfs (self, ifindex, num_vfs); + return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe); +} + +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); + + _LOGD ("link: setting VFs for \"%s\" (%d):", + nm_platform_link_get_name (self, ifindex), + ifindex); + + for (i = 0; vfs[i]; i++) { + const NMPlatformVF *vf = vfs[i]; + + _LOGD ("link: VF %s", nm_platform_vf_to_string (vf, NULL, 0)); + } + + return klass->link_set_sriov_vfs (self, ifindex, vfs); } /** @@ -1852,6 +1890,12 @@ nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLin 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) { @@ -1864,6 +1908,18 @@ nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatform 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) { @@ -1912,6 +1968,12 @@ nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformL 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); +} + /*****************************************************************************/ /** @@ -1996,8 +2058,9 @@ nm_platform_link_vlan_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x", + _LOGD ("link: adding link '%s': vlan parent %d vlanid %d vlanflags %x", name, parent, vlanid, vlanflags); + if (!klass->vlan_add (self, name, parent, vlanid, vlanflags, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; return NM_PLATFORM_ERROR_SUCCESS; @@ -2028,8 +2091,9 @@ nm_platform_link_vxlan_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("link: adding vxlan '%s' parent %d id %d", - name, props->parent_ifindex, props->id); + _LOGD ("link: adding link '%s': %s", + name, nm_platform_lnk_vxlan_to_string (props, NULL, 0)); + if (!klass->link_vxlan_add (self, name, props, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; return NM_PLATFORM_ERROR_SUCCESS; @@ -2080,13 +2144,78 @@ nm_platform_link_tun_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("link: adding tun '%s' %s", + _LOGD ("link: adding link '%s': %s", name, nm_platform_lnk_tun_to_string (props, b, sizeof (b))); + if (!klass->link_tun_add (self, name, props, out_link, out_fd)) return NM_PLATFORM_ERROR_UNSPECIFIED; return NM_PLATFORM_ERROR_SUCCESS; } +/** + * nm_platform_6lowpan_add: + * @self: platform instance + * @parent: parent link + * @name: name of the new interface + * @out_link: on success, the link object + * + * Create a 6LoWPAN interface. + */ +NMPlatformError +nm_platform_link_6lowpan_add (NMPlatform *self, + const char *name, + int parent, + const NMPlatformLink **out_link) +{ + NMPlatformError plerr; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_6LOWPAN, out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD ("adding link '%s': 6lowpan parent %u", name, parent); + + if (!klass->link_6lowpan_add (self, name, parent, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; +} + +gboolean +nm_platform_link_6lowpan_get_properties (NMPlatform *self, int ifindex, int *out_parent) +{ + const NMPlatformLink *plink; + _CHECK_SELF (self, klass, FALSE); + + 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->addr.data, + plink->addr.len); + NM_SET_OUT (out_parent, parent_plink ? parent_plink->ifindex : -1); + } + + return TRUE; +} + /*****************************************************************************/ static gboolean @@ -2310,23 +2439,18 @@ nm_platform_link_gre_add (NMPlatform *self, const NMPlatformLink **out_link) { NMPlatformError plerr; - char buffer[INET_ADDRSTRLEN]; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); - plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_GRE, out_link); + plerr = _link_add_check_existing (self, name, props->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE, out_link); if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD (LOG_FMT_IP_TUNNEL, - "gre", - name, - props->parent_ifindex, - nm_utils_inet4_ntop (props->local, NULL), - nm_utils_inet4_ntop (props->remote, buffer)); + _LOGD ("adding link '%s': %s", + name, nm_platform_lnk_gre_to_string (props, NULL, 0)); if (!klass->link_gre_add (self, name, props, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; @@ -2372,6 +2496,9 @@ _infiniband_add_add_or_delete (NMPlatform *self, if (!klass->infiniband_partition_add (self, parent, p_key, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; } else { + _LOGD ("link: deleting infiniband partition %s for parent '%s' (%d), key %d", + name, parent_link->name, parent, p_key); + if (!klass->infiniband_partition_delete (self, parent, p_key)) return NM_PLATFORM_ERROR_UNSPECIFIED; } @@ -2472,23 +2599,19 @@ nm_platform_link_ip6tnl_add (NMPlatform *self, const NMPlatformLink **out_link) { NMPlatformError plerr; - char buffer[INET6_ADDRSTRLEN]; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (!props->is_gre, NM_PLATFORM_ERROR_BUG); plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IP6TNL, out_link); if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD (LOG_FMT_IP_TUNNEL, - "ip6tnl", - name, - props->parent_ifindex, - nm_utils_inet6_ntop (&props->local, NULL), - nm_utils_inet6_ntop (&props->remote, buffer)); + _LOGD ("adding link '%s': %s", + name, nm_platform_lnk_ip6tnl_to_string (props, NULL, 0)); if (!klass->link_ip6tnl_add (self, name, props, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; @@ -2496,6 +2619,46 @@ nm_platform_link_ip6tnl_add (NMPlatform *self, } /** + * nm_platform_ip6gre_add: + * @self: platform instance + * @name: name of the new interface + * @props: interface properties + * @out_link: on success, the link object + * + * Create an IPv6 GRE/GRETAP tunnel. + */ +NMPlatformError +nm_platform_link_ip6gre_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link) +{ + NMPlatformError plerr; + + _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); + + g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (props->is_gre, NM_PLATFORM_ERROR_BUG); + + plerr = _link_add_check_existing (self, + name, + props->is_tap + ? NM_LINK_TYPE_IP6GRETAP + : NM_LINK_TYPE_IP6GRE, + out_link); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) + return plerr; + + _LOGD ("adding link '%s': %s", + name, nm_platform_lnk_ip6tnl_to_string (props, NULL, 0)); + + if (!klass->link_ip6gre_add (self, name, props, out_link)) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return NM_PLATFORM_ERROR_SUCCESS; +} + +/** * nm_platform_ipip_add: * @self: platform instance * @name: name of the new interface @@ -2511,7 +2674,6 @@ nm_platform_link_ipip_add (NMPlatform *self, const NMPlatformLink **out_link) { NMPlatformError plerr; - char buffer[INET_ADDRSTRLEN]; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); @@ -2522,12 +2684,8 @@ nm_platform_link_ipip_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD (LOG_FMT_IP_TUNNEL, - "ipip", - name, - props->parent_ifindex, - nm_utils_inet4_ntop (props->local, NULL), - nm_utils_inet4_ntop (props->remote, buffer)); + _LOGD ("adding link '%s': %s", + name, nm_platform_lnk_ipip_to_string (props, NULL, 0)); if (!klass->link_ipip_add (self, name, props, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; @@ -2538,6 +2696,7 @@ nm_platform_link_ipip_add (NMPlatform *self, * nm_platform_macsec_add: * @self: platform instance * @name: name of the new interface + * @parent: parent link * @props: interface properties * @out_link: on success, the link object * @@ -2561,10 +2720,8 @@ nm_platform_link_macsec_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("adding macsec '%s' parent %u sci %llx", - name, - parent, - (unsigned long long) props->sci); + _LOGD ("adding link '%s': %s", + name, nm_platform_lnk_macsec_to_string (props, NULL, 0)); if (!klass->link_macsec_add (self, name, parent, props, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; @@ -2601,11 +2758,8 @@ nm_platform_link_macvlan_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD ("adding %s '%s' parent %u mode %u", - props->tap ? "macvtap" : "macvlan", - name, - parent, - props->mode); + _LOGD ("adding link '%s': %s", + name, nm_platform_lnk_macvlan_to_string (props, NULL, 0)); if (!klass->link_macvlan_add (self, name, parent, props, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; @@ -2628,7 +2782,6 @@ nm_platform_link_sit_add (NMPlatform *self, const NMPlatformLink **out_link) { NMPlatformError plerr; - char buffer[INET_ADDRSTRLEN]; _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG); @@ -2639,12 +2792,8 @@ nm_platform_link_sit_add (NMPlatform *self, if (plerr != NM_PLATFORM_ERROR_SUCCESS) return plerr; - _LOGD (LOG_FMT_IP_TUNNEL, - "sit", - name, - props->parent_ifindex, - nm_utils_inet4_ntop (props->local, NULL), - nm_utils_inet4_ntop (props->remote, buffer)); + _LOGD ("adding link '%s': %s", + name, nm_platform_lnk_sit_to_string (props, NULL, 0)); if (!klass->link_sit_add (self, name, props, out_link)) return NM_PLATFORM_ERROR_UNSPECIFIED; @@ -2888,6 +3037,16 @@ nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gbo 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) { @@ -2929,6 +3088,46 @@ nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gs 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); +} + #define TO_STRING_DEV_BUF_SIZE (5+15+1) static const char * _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size) @@ -3014,6 +3213,32 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_platform_link_duplex_type_to_string, NMPlatformLi /*****************************************************************************/ +NMEthtoolFeatureStates * +nm_platform_ethtool_get_link_features (NMPlatform *self, int ifindex) +{ + _CHECK_SELF_NETNS (self, klass, netns, NULL); + + g_return_val_if_fail (ifindex > 0, NULL); + + return nmp_utils_ethtool_get_features (ifindex); +} + +gboolean +nm_platform_ethtool_set_features (NMPlatform *self, + int ifindex, + const NMEthtoolFeatureStates *features, + const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, + gboolean do_set /* or reset */) +{ + _CHECK_SELF_NETNS (self, klass, netns, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + return nmp_utils_ethtool_set_features (ifindex, features, requested, do_set); +} + +/*****************************************************************************/ + const NMDedupMultiHeadEntry * nm_platform_lookup_all (NMPlatform *platform, NMPCacheIdType cache_id_type, @@ -3622,7 +3847,7 @@ ip6_address_scope_priority (const struct in6_addr *addr) return 3; } -static gint +static int ip6_address_scope_cmp (gconstpointer a, gconstpointer b) { const NMPlatformIP6Address *x = NMP_OBJECT_CAST_IP6_ADDRESS (*(const void **) a); @@ -4957,7 +5182,7 @@ nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len return buf; g_snprintf (buf, len, - "gre" + lnk->is_tap ? "gretap" : "gre" "%s" /* remote */ "%s" /* local */ "%s" /* parent_ifindex */ @@ -5014,12 +5239,18 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi 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, - "ip6tnl" + "%s" /* type */ "%s" /* remote */ "%s" /* local */ "%s" /* parent_ifindex */ @@ -5030,6 +5261,7 @@ nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsi "%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) : "", @@ -5114,7 +5346,8 @@ nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, g return buf; g_snprintf (buf, len, - "macvlan mode %u %s", + "%s mode %u %s", + lnk->tap ? "macvtap" : "macvlan", lnk->mode, lnk->no_promisc ? "not-promisc" : "promisc"); return buf; @@ -5298,6 +5531,91 @@ nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize return buf; } +const char * +nm_platform_wireguard_peer_to_string (const NMPWireGuardPeer *peer, char *buf, gsize len) +{ + gs_free char *public_key_b64 = NULL; + char s_endpoint[NM_UTILS_INET_ADDRSTRLEN + 100]; + char s_addr[NM_UTILS_INET_ADDRSTRLEN]; + guint i; + + nm_utils_to_string_buffer_init (&buf, &len); + + if (peer->endpoint_family == AF_INET) { + nm_sprintf_buf (s_endpoint, + " endpoint %s:%u", + nm_utils_inet4_ntop (peer->endpoint_addr.addr4, s_addr), + (guint) peer->endpoint_port); + } else if (peer->endpoint_family == AF_INET6) { + nm_sprintf_buf (s_endpoint, + " endpoint [%s]:%u", + nm_utils_inet6_ntop (&peer->endpoint_addr.addr6, s_addr), + (guint) peer->endpoint_port); + } else + s_endpoint[0] = '\0'; + + public_key_b64 = g_base64_encode (peer->public_key, sizeof (peer->public_key)); + + nm_utils_strbuf_append (&buf, &len, + "public-key %s" + "%s" /* preshared-key */ + "%s" /* endpoint */ + " rx %"G_GUINT64_FORMAT + " tx %"G_GUINT64_FORMAT + "%s", /* allowed-ips */ + public_key_b64, + nm_utils_mem_all_zero (peer->preshared_key, sizeof (peer->preshared_key)) + ? "" + : " preshared-key (hidden)", + s_endpoint, + peer->rx_bytes, + peer->tx_bytes, + 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 buf; +} + +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_mem_all_zero (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_mem_all_zero (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 @@ -5775,6 +6093,56 @@ nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b) 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_buf (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; +} + void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) { @@ -5843,7 +6211,8 @@ nm_platform_lnk_gre_hash_update (const NMPlatformLnkGre *obj, NMHashState *h) obj->output_key, obj->ttl, obj->tos, - (bool) obj->path_mtu_discovery); + (bool) obj->path_mtu_discovery, + (bool) obj->is_tap); } int @@ -5860,6 +6229,7 @@ nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) 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; } @@ -5891,7 +6261,13 @@ nm_platform_lnk_ip6tnl_hash_update (const NMPlatformLnkIp6Tnl *obj, NMHashState obj->encap_limit, obj->proto, obj->flow_label, - obj->flags); + obj->flags, + obj->input_flags, + obj->output_flags, + obj->input_key, + obj->output_key, + (bool) obj->is_gre, + (bool) obj->is_tap); } int @@ -5907,6 +6283,12 @@ nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6 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; } @@ -6124,6 +6506,27 @@ nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan } 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; +} + +void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashState *h) { nm_hash_update_vals (h, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 866df736..11495aff 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2009 - 2017 Red Hat, Inc. + * Copyright (C) 2009 - 2018 Red Hat, Inc. */ #ifndef __NETWORKMANAGER_PLATFORM_H__ @@ -608,6 +608,26 @@ extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; 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 { in_addr_t local; in_addr_t remote; int parent_ifindex; @@ -618,6 +638,7 @@ typedef struct { guint8 ttl; guint8 tos; bool path_mtu_discovery:1; + bool is_tap:1; } NMPlatformLnkGre; typedef struct { @@ -635,6 +656,14 @@ typedef struct { 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 { @@ -723,6 +752,16 @@ typedef struct { bool l3miss:1; } NMPlatformLnkVxlan; +#define NMP_WIREGUARD_PUBLIC_KEY_LEN 32 +#define NMP_WIREGUARD_SYMMETRIC_KEY_LEN 32 + +typedef struct { + guint32 fwmark; + guint16 listen_port; + guint8 private_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; + guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; +} NMPlatformLnkWireGuard; + typedef enum { NM_PLATFORM_LINK_DUPLEX_UNKNOWN, NM_PLATFORM_LINK_DUPLEX_HALF, @@ -786,7 +825,8 @@ typedef struct { NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); NMPlatformError (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); - gboolean (*link_set_sriov_num_vfs) (NMPlatform *, int ifindex, guint num_vfs); + gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe); + gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); guint (*link_get_dev_id) (NMPlatform *, int ifindex); @@ -829,6 +869,10 @@ typedef struct { const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link); + gboolean (*link_ip6gre_add) (NMPlatform *, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link); gboolean (*link_ipip_add) (NMPlatform *, const char *name, const NMPlatformLnkIpIp *props, @@ -854,12 +898,16 @@ typedef struct { const NMPlatformLink **out_link, int *out_fd); + gboolean (*link_6lowpan_add) (NMPlatform *platform, + const char *name, + int parent, + const NMPlatformLink **out_link); + gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link); gboolean (*infiniband_partition_delete) (NMPlatform *, int parent, int p_key); gboolean (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps); gboolean (*wifi_get_bssid) (NMPlatform *, int ifindex, guint8 *bssid); - GByteArray *(*wifi_get_ssid) (NMPlatform *, int ifindex); guint32 (*wifi_get_frequency) (NMPlatform *, int ifindex); int (*wifi_get_quality) (NMPlatform *, int ifindex); guint32 (*wifi_get_rate) (NMPlatform *, int ifindex); @@ -868,12 +916,18 @@ typedef struct { void (*wifi_set_powersave) (NMPlatform *, int ifindex, guint32 powersave); guint32 (*wifi_find_frequency) (NMPlatform *, int ifindex, const guint32 *freqs); void (*wifi_indicate_addressing_running) (NMPlatform *, int ifindex, gboolean running); + NMSettingWirelessWakeOnWLan (*wifi_get_wake_on_wlan) (NMPlatform *, int ifindex); gboolean (*wifi_set_wake_on_wlan) (NMPlatform *, int ifindex, NMSettingWirelessWakeOnWLan wowl); guint32 (*mesh_get_channel) (NMPlatform *, int ifindex); gboolean (*mesh_set_channel) (NMPlatform *, int ifindex, guint32 channel); gboolean (*mesh_set_ssid) (NMPlatform *, int ifindex, const guint8 *ssid, gsize len); + guint16 (*wpan_get_pan_id) (NMPlatform *, int ifindex); + gboolean (*wpan_set_pan_id) (NMPlatform *, int ifindex, guint16 pan_id); + guint16 (*wpan_get_short_addr) (NMPlatform *, int ifindex); + gboolean (*wpan_set_short_addr) (NMPlatform *, int ifindex, guint16 short_addr); + gboolean (*object_delete) (NMPlatform *, const NMPObject *obj); gboolean (*ip4_address_add) (NMPlatform *, @@ -1078,7 +1132,7 @@ const NMPObject *nm_platform_link_get_obj (NMPlatform *self, 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, gconstpointer address, size_t length); +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); NMPlatformError nm_platform_link_dummy_add (NMPlatform *self, const char *name, const NMPlatformLink **out_link); @@ -1162,7 +1216,8 @@ gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); -gboolean nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs); +gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe); +gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex); @@ -1187,7 +1242,10 @@ char *nm_platform_sysctl_slave_get_option (NMPlatform *self, int ifindex, const const NMPObject *nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, 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); @@ -1198,6 +1256,7 @@ const NMPlatformLnkSit *nm_platform_link_get_lnk_sit (NMPlatform *self, int ifin 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 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); NMPlatformError nm_platform_link_vlan_add (NMPlatform *self, const char *name, @@ -1247,12 +1306,18 @@ void nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM 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 *platform, int ifindex); +gboolean nm_platform_wpan_set_pan_id (NMPlatform *platform, int ifindex, guint16 pan_id); +guint16 nm_platform_wpan_get_short_addr (NMPlatform *platform, int ifindex); +gboolean nm_platform_wpan_set_short_addr (NMPlatform *platform, int ifindex, guint16 short_addr); + 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); @@ -1266,6 +1331,10 @@ NMPlatformError nm_platform_link_ip6tnl_add (NMPlatform *self, const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link); +NMPlatformError nm_platform_link_ip6gre_add (NMPlatform *self, + const char *name, + const NMPlatformLnkIp6Tnl *props, + const NMPlatformLink **out_link); NMPlatformError nm_platform_link_ipip_add (NMPlatform *self, const char *name, const NMPlatformLnkIpIp *props, @@ -1289,6 +1358,13 @@ NMPlatformError nm_platform_link_tun_add (NMPlatform *self, const NMPlatformLnkTun *props, const NMPlatformLink **out_link, int *out_fd); +NMPlatformError nm_platform_link_6lowpan_add (NMPlatform *self, + const char *name, + int parent, + const NMPlatformLink **out_link); +gboolean nm_platform_link_6lowpan_get_properties (NMPlatform *self, + int ifindex, + int *out_parent); const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address); @@ -1375,12 +1451,14 @@ const char *nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *bu 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_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_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_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, @@ -1388,6 +1466,11 @@ const char *nm_platform_vlan_qos_mapping_to_string (const char *name, char *buf, gsize len); +struct _NMPWireGuardPeer; +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_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b); int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b); @@ -1399,6 +1482,7 @@ int nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit * 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_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); @@ -1435,6 +1519,7 @@ void nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState * 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_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); @@ -1452,6 +1537,17 @@ int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPla gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password); gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); + +typedef struct _NMEthtoolFeatureStates NMEthtoolFeatureStates; + +NMEthtoolFeatureStates *nm_platform_ethtool_get_link_features (NMPlatform *self, + int ifindex); +gboolean nm_platform_ethtool_set_features (NMPlatform *self, + int ifindex, + const NMEthtoolFeatureStates *features, + const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, + gboolean do_set /* or reset */); + const char * nm_platform_link_duplex_type_to_string (NMPlatformLinkDuplexType duplex); void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 50e6825b..fdc27440 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2015 - 2017 Red Hat, Inc. + * Copyright (C) 2015 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -27,10 +27,14 @@ #include <libudev.h> #include "nm-utils.h" +#include "nm-utils/nm-secret-utils.h" #include "nm-core-utils.h" #include "nm-platform-utils.h" +#include "wifi/nm-wifi-utils.h" +#include "wpan/nm-wpan-utils.h" + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_PLATFORM @@ -337,12 +341,103 @@ _vlan_xgress_qos_mappings_cpy (guint *dst_n_map, g_clear_pointer (dst_map, g_free); *dst_n_map = src_n_map; if (src_n_map > 0) - *dst_map = g_memdup (src_map, sizeof (*src_map) * src_n_map); + *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, + peer->endpoint_port, + peer->endpoint_family); + + if (peer->endpoint_family == AF_INET) + nm_hash_update_val (h, peer->endpoint_addr.addr4); + else if (peer->endpoint_family == AF_INET6) + nm_hash_update_val (h, peer->endpoint_addr.addr6); + + 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_port); + NM_CMP_FIELD (a, b, endpoint_family); + NM_CMP_FIELD_MEMCMP (a, b, public_key); + NM_CMP_FIELD_MEMCMP (a, b, preshared_key); + + if (a->endpoint_family == AF_INET) + NM_CMP_FIELD (a, b, endpoint_addr.addr4); + else if (a->endpoint_family == AF_INET6) + NM_CMP_FIELD_IN6ADDR (a, b, endpoint_addr.addr6); + + 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) { @@ -456,6 +551,7 @@ _vt_cmd_obj_dispose_link (NMPObject *obj) 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); } @@ -466,6 +562,28 @@ _vt_cmd_obj_dispose_lnk_vlan (NMPObject *obj) 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) { @@ -631,31 +749,35 @@ 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 buf2[sizeof (_nm_utils_to_string_buffer)]; - char buf3[sizeof (_nm_utils_to_string_buffer)]; + 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: - g_snprintf (buf, buf_size, - "[%s,%p,%u,%calive,%cvisible,%cin-nl,%p; %s]", - 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_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2))); + 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) { - NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2)); - nmp_object_to_string (obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_PUBLIC, buf3, sizeof (buf3)); - g_snprintf (buf, buf_size, - "%s; %s", - buf2, buf3); - } else - NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size); + 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"); @@ -720,6 +842,55 @@ _vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_s } } +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) \ @@ -768,6 +939,7 @@ _vt_cmd_obj_hash_update_link (const NMPObject *obj, NMHashState *h) 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); @@ -787,17 +959,26 @@ _vt_cmd_obj_hash_update_lnk_vlan (const NMPObject *obj, NMHashState *h) 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; - if (obj1 == obj2) - return 0; - if (!obj1) - return -1; - if (!obj2) - return 1; + 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); @@ -818,16 +999,11 @@ nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) static int _vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2) { - int i; + 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); - i = nm_platform_link_cmp (&obj1->link, &obj2->link); - if (i) - return i; - if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink) - return obj1->_link.netlink.is_in_netlink ? -1 : 1; - i = nmp_object_cmp (obj1->_link.netlink.lnk, obj2->_link.netlink.lnk); - if (i) - return i; if (obj1->_link.udev.device != obj2->_link.udev.device) { if (!obj1->_link.udev.device) return -1; @@ -840,6 +1016,7 @@ _vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2) * Have this check as very last. */ return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1; } + return 0; } @@ -865,6 +1042,21 @@ _vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1, const NMPObject *obj2) 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. @@ -908,6 +1100,12 @@ _vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src) 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; } @@ -925,6 +1123,41 @@ _vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const NMPObject *src) 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) \ @@ -2702,6 +2935,17 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_gre_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) 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 = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_gre_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_gre_hash_update, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_gre_cmp, + }, [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND, @@ -2724,6 +2968,28 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) 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 = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) 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 = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_ip6tnl_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_ip6tnl_hash_update, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_ip6tnl_cmp, + }, [NMP_OBJECT_TYPE_LNK_IPIP - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), .obj_type = NMP_OBJECT_TYPE_LNK_IPIP, @@ -2817,5 +3083,20 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_vxlan_hash_update, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) 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 = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_wireguard_to_string, + .cmd_plobj_hash_update = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_lnk_vlan_hash_update, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp, + }, }; - diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index f473f462..97be8321 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2015 - 2017 Red Hat, Inc. + * Copyright (C) 2015 - 2018 Red Hat, Inc. */ #ifndef __NMP_OBJECT_H__ @@ -27,6 +27,36 @@ struct udev_device; +/*****************************************************************************/ + +typedef struct { + NMIPAddr addr; + guint8 family; + guint8 mask; +} NMPWireGuardAllowedIP; + +typedef struct _NMPWireGuardPeer { + NMIPAddr endpoint_addr; + struct timespec 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; + guint16 endpoint_port; + guint8 public_key[NMP_WIREGUARD_PUBLIC_KEY_LEN]; + guint8 preshared_key[NMP_WIREGUARD_SYMMETRIC_KEY_LEN]; + guint8 endpoint_family; +} NMPWireGuardPeer; + +/*****************************************************************************/ + typedef enum { /*< skip >*/ NMP_OBJECT_TO_STRING_ID, NMP_OBJECT_TO_STRING_PUBLIC, @@ -164,6 +194,14 @@ typedef struct { */ 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 { @@ -214,6 +252,14 @@ typedef struct { } 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; @@ -278,6 +324,9 @@ struct _NMPObject { NMPlatformLnkVxlan lnk_vxlan; NMPObjectLnkVxlan _lnk_vxlan; + NMPlatformLnkWireGuard lnk_wireguard; + NMPObjectLnkWireGuard _lnk_wireguard; + NMPlatformIPAddress ip_address; NMPlatformIPXAddress ipx_address; NMPlatformIP4Address ip4_address; diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 8b8c87d8..12d91812 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -60,7 +60,7 @@ test_cleanup_internal (void) g_assert (ifindex > 0); /* wait for kernel to add the IPv6 link local address... it takes a bit. */ - NMTST_WAIT_ASSERT (100, { + NMTST_WAIT_ASSERT (300, { gs_unref_array GArray *addrs = NULL; const NMPlatformIP6Address *a; diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 42569d5b..3dedbd10 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1243,21 +1243,29 @@ nmtstp_link_gre_add (NMPlatform *platform, const NMPlatformLink *pllink = NULL; gboolean success; char buffer[INET_ADDRSTRLEN]; + NMLinkType link_type; g_assert (nm_utils_is_valid_iface_name (name, NULL)); external_command = nmtstp_run_command_check_external (external_command); + link_type = lnk->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE; _init_platform (&platform, external_command); if (external_command) { gs_free char *dev = NULL; + char *obj, *type; if (lnk->parent_ifindex) dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (platform, lnk->parent_ifindex)); - success = !nmtstp_run_command ("ip tunnel add %s mode gre %s local %s remote %s ttl %u tos %02x %s", + obj = lnk->is_tap ? "link" : "tunnel"; + type = lnk->is_tap ? "type gretap" : "mode gre"; + + success = !nmtstp_run_command ("ip %s add %s %s %s local %s remote %s ttl %u tos %02x %s", + obj, name, + type, dev ?: "", nm_utils_inet4_ntop (lnk->local, NULL), nm_utils_inet4_ntop (lnk->remote, buffer), @@ -1265,11 +1273,11 @@ nmtstp_link_gre_add (NMPlatform *platform, lnk->tos, lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); if (success) - pllink = nmtstp_assert_wait_for_link (platform, name, NM_LINK_TYPE_GRE, 100); + pllink = nmtstp_assert_wait_for_link (platform, name, link_type, 100); } else success = nm_platform_link_gre_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; - _assert_pllink (platform, success, pllink, name, NM_LINK_TYPE_GRE); + _assert_pllink (platform, success, pllink, name, link_type); return pllink; } @@ -1289,6 +1297,7 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform, gboolean tclass_inherit; g_assert (nm_utils_is_valid_iface_name (name, NULL)); + g_assert (!lnk->is_gre); external_command = nmtstp_run_command_check_external (external_command); @@ -1336,6 +1345,56 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform, } const NMPlatformLink * +nmtstp_link_ip6gre_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const NMPlatformLnkIp6Tnl *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET6_ADDRSTRLEN]; + char tclass[20]; + gboolean tclass_inherit; + + g_assert (nm_utils_is_valid_iface_name (name, NULL)); + g_assert (lnk->is_gre); + + external_command = nmtstp_run_command_check_external (external_command); + + _init_platform (&platform, external_command); + + if (external_command) { + gs_free char *dev = NULL; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (platform, lnk->parent_ifindex)); + + tclass_inherit = NM_FLAGS_HAS (lnk->flags, IP6_TNL_F_USE_ORIG_TCLASS); + + success = !nmtstp_run_command ("ip link add %s type %s %s local %s remote %s ttl %u tclass %s flowlabel %x", + name, + lnk->is_tap ? "ip6gretap" : "ip6gre", + dev, + nm_utils_inet6_ntop (&lnk->local, NULL), + nm_utils_inet6_ntop (&lnk->remote, buffer), + lnk->ttl, + tclass_inherit ? "inherit" : nm_sprintf_buf (tclass, "%02x", lnk->tclass), + lnk->flow_label); + if (success) { + pllink = nmtstp_assert_wait_for_link (platform, + name, + lnk->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE, + 100); + } + } else + success = nm_platform_link_ip6gre_add (platform, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (platform, success, pllink, name, lnk->is_tap ? NM_LINK_TYPE_IP6GRETAP : NM_LINK_TYPE_IP6GRE); + + return pllink; +} + +const NMPlatformLink * nmtstp_link_ipip_add (NMPlatform *platform, gboolean external_command, const char *name, diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index bd02b0d7..1baadfa1 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -80,7 +80,7 @@ typedef struct { gulong handler_id; const char *name; NMPlatformSignalChangeType change_type; - gint received_count; + int received_count; GMainLoop *loop; int ifindex; const char *ifname; @@ -297,6 +297,10 @@ const NMPlatformLink *nmtstp_link_ip6tnl_add (NMPlatform *platform, gboolean external_command, const char *name, const NMPlatformLnkIp6Tnl *lnk); +const NMPlatformLink *nmtstp_link_ip6gre_add (NMPlatform *platform, + gboolean external_command, + const char *name, + const NMPlatformLnkIp6Tnl *lnk); const NMPlatformLink *nmtstp_link_ipip_add (NMPlatform *platform, gboolean external_command, const char *name, diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index 8708c80f..eebc15d8 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2015 Red Hat, Inc. + * Copyright (C) 2015 - 2018 Red Hat, Inc. */ #include "nm-default.h" @@ -57,7 +57,7 @@ NMTST_DEFINE (); int main (int argc, char **argv) { - nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + nmtst_init_assert_logging (&argc, &argv, "WARN", "DEFAULT"); g_test_add_func ("/general/init_linux_platform", test_init_linux_platform); g_test_add_func ("/general/link_get_all", test_link_get_all); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index cd5880e3..057490c4 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -25,6 +25,7 @@ #include <sys/types.h> #include <linux/if_tun.h> +#include "nm-utils/nm-io-utils.h" #include "platform/nmp-object.h" #include "platform/nmp-netns.h" #include "platform/nm-platform-utils.h" @@ -699,6 +700,7 @@ test_software_detect (gconstpointer user_data) guint i_step; const gboolean ext = test_data->external_command; NMPlatformLnkTun lnk_tun; + NMPlatformLnkGre lnk_gre = { }; nm_auto_close int tun_fd = -1; nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); @@ -706,7 +708,6 @@ test_software_detect (gconstpointer user_data) switch (test_data->link_type) { case NM_LINK_TYPE_GRE: { - NMPlatformLnkGre lnk_gre = { }; gboolean gracefully_skip = FALSE; lnk_gre.local = nmtst_inet4_from_string ("192.168.233.204"); @@ -730,6 +731,31 @@ test_software_detect (gconstpointer user_data) } break; } + case NM_LINK_TYPE_GRETAP: { + gboolean gracefully_skip = FALSE; + + lnk_gre.local = nmtst_inet4_from_string ("192.168.1.133"); + lnk_gre.remote = nmtst_inet4_from_string ("172.168.101.2"); + lnk_gre.parent_ifindex = ifindex_parent; + lnk_gre.ttl = 39; + lnk_gre.tos = 12; + lnk_gre.path_mtu_discovery = FALSE; + lnk_gre.is_tap = TRUE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "gretap0")) { + /* Seems that the ip_gre module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip_gre", NULL) != 0; + } + + if (!nmtstp_link_gre_add (NULL, ext, DEVICE_NAME, &lnk_gre)) { + if (gracefully_skip) { + g_test_skip ("Cannot create gretap tunnel because of missing ip_gre module (modprobe ip_gre)"); + goto out_delete_parent; + } + g_error ("Failed adding GRETAP tunnel"); + } + break; + } case NM_LINK_TYPE_IPIP: { NMPlatformLnkIpIp lnk_ipip = { }; gboolean gracefully_skip = FALSE; @@ -794,6 +820,58 @@ test_software_detect (gconstpointer user_data) } break; } + case NM_LINK_TYPE_IP6GRE: { + NMPlatformLnkIp6Tnl lnk_ip6tnl = { }; + gboolean gracefully_skip = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6gre0")) { + /* Seems that the ip6_tunnel module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_gre", NULL) != 0; + } + + lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::42"); + lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::aaaa"); + lnk_ip6tnl.parent_ifindex = ifindex_parent; + lnk_ip6tnl.tclass = 21; + lnk_ip6tnl.flow_label = 1338; + lnk_ip6tnl.is_gre = TRUE; + + if (!nmtstp_link_ip6gre_add (NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { + if (gracefully_skip) { + g_test_skip ("Cannot create ip6gre tunnel because of missing ip6_gre module (modprobe ip6_gre)"); + goto out_delete_parent; + } + g_error ("Failed adding IP6GRE tunnel"); + } + break; + } + case NM_LINK_TYPE_IP6GRETAP: { + NMPlatformLnkIp6Tnl lnk_ip6tnl = { }; + gboolean gracefully_skip = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6gre0")) { + /* Seems that the ip6_tunnel module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_gre", NULL) != 0; + } + + lnk_ip6tnl.local = *nmtst_inet6_from_string ("fe80::abcd"); + lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fc01::bbbb"); + lnk_ip6tnl.parent_ifindex = ifindex_parent; + lnk_ip6tnl.ttl = 10; + lnk_ip6tnl.tclass = 22; + lnk_ip6tnl.flow_label = 1339; + lnk_ip6tnl.is_gre = TRUE; + lnk_ip6tnl.is_tap = TRUE; + + if (!nmtstp_link_ip6gre_add (NULL, ext, DEVICE_NAME, &lnk_ip6tnl)) { + if (gracefully_skip) { + g_test_skip ("Cannot create ip6gretap tunnel because of missing ip6_gre module (modprobe ip6_gre)"); + goto out_delete_parent; + } + g_error ("Failed adding IP6GRETAP tunnel"); + } + break; + } case NM_LINK_TYPE_MACVLAN: { NMPlatformLnkMacvlan lnk_macvlan = { }; const NMPlatformLink *dummy; @@ -970,16 +1048,15 @@ test_software_detect (gconstpointer user_data) const NMPlatformLnkGre *plnk = &lnk->lnk_gre; g_assert (plnk == nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL)); - g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); - g_assert_cmpint (plnk->input_flags, ==, 0); - g_assert_cmpint (plnk->output_flags, ==, 0); - g_assert_cmpint (plnk->input_key, ==, 0); - g_assert_cmpint (plnk->output_key, ==, 0); - nmtst_assert_ip4_address (plnk->local, "192.168.233.204"); - nmtst_assert_ip4_address (plnk->remote, "172.168.10.25"); - g_assert_cmpint (plnk->ttl, ==, 174); - g_assert_cmpint (plnk->tos, ==, 37); - g_assert_cmpint (plnk->path_mtu_discovery, ==, TRUE); + g_assert (nm_platform_lnk_gre_cmp (plnk, &lnk_gre) == 0); + + break; + } + case NM_LINK_TYPE_GRETAP: { + const NMPlatformLnkGre *plnk = &lnk->lnk_gre; + + g_assert (plnk == nm_platform_link_get_lnk_gretap (NM_PLATFORM_GET, ifindex, NULL)); + g_assert (nm_platform_lnk_gre_cmp (plnk, &lnk_gre) == 0); break; } case NM_LINK_TYPE_IP6TNL: { @@ -1012,6 +1089,33 @@ test_software_detect (gconstpointer user_data) } break; } + case NM_LINK_TYPE_IP6GRE: { + const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; + + g_assert (plnk == nm_platform_link_get_lnk_ip6gre (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip6_address (&plnk->local, "fd01::42"); + nmtst_assert_ip6_address (&plnk->remote, "fd01::aaaa"); + g_assert_cmpint (plnk->tclass, ==, 21); + g_assert_cmpint (plnk->flow_label, ==, 1338); + g_assert_cmpint (plnk->is_gre, ==, TRUE); + g_assert_cmpint (plnk->is_tap, ==, FALSE); + break; + } + case NM_LINK_TYPE_IP6GRETAP: { + const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; + + g_assert (plnk == nm_platform_link_get_lnk_ip6gretap (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip6_address (&plnk->local, "fe80::abcd"); + nmtst_assert_ip6_address (&plnk->remote, "fc01::bbbb"); + g_assert_cmpint (plnk->ttl, ==, 10); + g_assert_cmpint (plnk->tclass, ==, 22); + g_assert_cmpint (plnk->flow_label, ==, 1339); + g_assert_cmpint (plnk->is_gre, ==, TRUE); + g_assert_cmpint (plnk->is_tap, ==, TRUE); + break; + } case NM_LINK_TYPE_IPIP: { const NMPlatformLnkIpIp *plnk = &lnk->lnk_ipip; @@ -1732,7 +1836,7 @@ test_create_many_links_do (guint n_devices) char name[64]; const NMPlatformLink *pllink; gs_unref_array GArray *ifindexes = g_array_sized_new (FALSE, FALSE, sizeof (int), n_devices); - const gint EX = ((int) (nmtst_get_rand_int () % 4)) - 1; + const int EX = ((int) (nmtst_get_rand_int () % 4)) - 1; g_assert (EX >= -1 && EX <= 2); @@ -2519,7 +2623,9 @@ test_sysctl_rename (void) case 0: { gs_free char *c = NULL; - if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024, &c, NULL, NULL) < 0) + if (nm_utils_file_get_contents (dirfd, "ifindex", 1*1024*1024, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &c, NULL, NULL) < 0) g_assert_not_reached(); g_assert_cmpint (ifindex[0], ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1)); break; @@ -2583,7 +2689,9 @@ test_sysctl_netns_switch (void) { gs_free char *c = NULL; - if (nm_utils_file_get_contents (dirfd, "ifindex", 0, &c, NULL, NULL) < 0) + if (nm_utils_file_get_contents (dirfd, "ifindex", 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &c, NULL, NULL) < 0) g_assert_not_reached(); g_assert_cmpint (ifindex, ==, (int) _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -1)); } @@ -2595,7 +2703,11 @@ test_sysctl_netns_switch (void) { gs_free char *c = NULL; - if (nm_utils_file_get_contents (-1, nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME), 0, &c, NULL, NULL) < 0) + if (nm_utils_file_get_contents (-1, + nm_sprintf_bufa (100, "/sys/class/net/%s/ifindex", IFNAME), + 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &c, NULL, NULL) < 0) ifindex_tmp = -1; else ifindex_tmp = _nm_utils_ascii_str_to_int64 (c, 10, 0, G_MAXINT, -2); @@ -2610,6 +2722,102 @@ test_sysctl_netns_switch (void) /*****************************************************************************/ +static void +ethtool_features_dump (const NMEthtoolFeatureStates *features) +{ + guint i, j; + + g_assert (features); + + _LOGT (">>> %u features (%u ss-features)", features->n_states, features->n_ss_features); + + for (i = 0; i < features->n_states; i++) { + const NMEthtoolFeatureState *s = &features->states_list[i]; + + _LOGT (">>> feature-list[%3u]: %3d = %-32s (%3u) | %s %s %s %s", + i, + (int) s->info->ethtool_id, + s->info->kernel_names[s->idx_kernel_name], + s->idx_ss_features, + s->active ? "ACT" : "act", + s->available ? "AVA" : "ava", + s->never_changed ? "NCH" : "nch", + s->requested ? "REQ" : "req"); + } + for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { + _LOGT (">>> feature-idx [%3u]: %-32s = %u features", + i + (guint) _NM_ETHTOOL_ID_FEATURE_FIRST, + nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, + (guint) NM_PTRARRAY_LEN (features->states_indexed[i])); + for (j = 0; features->states_indexed[i] && features->states_indexed[i][j]; j++) { + const NMEthtoolFeatureState *s = features->states_indexed[i][j]; + + _LOGT (">>> %3u: %-32s | %s %s %s %s", + j, + s->info->kernel_names[s->idx_kernel_name], + s->active ? "ACT" : "act", + s->available ? "AVA" : "ava", + s->never_changed ? "NCH" : "nch", + s->requested ? "REQ" : "req"); + } + } +} + +static void +test_ethtool_features_get (void) +{ + gs_unref_ptrarray GPtrArray *gfree_keeper = g_ptr_array_new_with_free_func (g_free); + const int IFINDEX = 1; + guint i; + guint i_run; + + for (i_run = 0; i_run < 5; i_run++) { + NMEthtoolFeatureStates *features; + NMTernary *requested; + gboolean do_set = TRUE; + + requested = g_new (NMTernary, _NM_ETHTOOL_ID_FEATURE_NUM); + for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) + requested[i] = NM_TERNARY_DEFAULT; + g_ptr_array_add (gfree_keeper, requested); + + if (i_run == 0) { + requested[NM_ETHTOOL_ID_FEATURE_RX] = NM_TERNARY_FALSE; + requested[NM_ETHTOOL_ID_FEATURE_TSO] = NM_TERNARY_FALSE; + requested[NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION] = NM_TERNARY_FALSE; + } else if (i_run == 1) + do_set = FALSE; + else if (i_run == 2) { + requested[NM_ETHTOOL_ID_FEATURE_TSO] = NM_TERNARY_FALSE; + requested[NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION] = NM_TERNARY_TRUE; + } else if (i_run == 3) + do_set = FALSE; + + _LOGT (">>> ethtool-features-get RUN %u (do-set=%s", i_run, do_set ? "set" : "reset"); + + features = nmp_utils_ethtool_get_features (IFINDEX); + g_ptr_array_add (gfree_keeper, features); + + ethtool_features_dump (features); + + if (_LOGT_ENABLED ()) { + int ignore; + + ignore = system ("ethtool -k lo"); + (void) ignore; + } + + if (!do_set) { + requested = gfree_keeper->pdata[i_run * 2 - 2]; + features = gfree_keeper->pdata[i_run * 2 - 1]; + } + + nmp_utils_ethtool_set_features (IFINDEX, features, requested, do_set); + } +} + +/*****************************************************************************/ + NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void @@ -2641,8 +2849,11 @@ _nmtstp_setup_tests (void) g_test_add_func ("/link/external", test_external); test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0); + test_software_detect_add ("/link/software/detect/gretap", NM_LINK_TYPE_GRETAP, 0); test_software_detect_add ("/link/software/detect/ip6tnl/0", NM_LINK_TYPE_IP6TNL, 0); test_software_detect_add ("/link/software/detect/ip6tnl/1", NM_LINK_TYPE_IP6TNL, 1); + test_software_detect_add ("/link/software/detect/ip6gre", NM_LINK_TYPE_IP6GRE, 0); + test_software_detect_add ("/link/software/detect/ip6gretap", NM_LINK_TYPE_IP6GRETAP, 0); test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0); test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0); test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0); @@ -2668,5 +2879,7 @@ _nmtstp_setup_tests (void) g_test_add_func ("/general/sysctl/rename", test_sysctl_rename); g_test_add_func ("/general/sysctl/netns-switch", test_sysctl_netns_switch); + + g_test_add_func ("/link/ethtool/features/get", test_ethtool_features_get); } } diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c index 6c2ff3f5..b3bb2bb6 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/nm-wifi-utils-nl80211.c @@ -15,14 +15,14 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2011 Red Hat, Inc. + * Copyright (C) 2005 - 2018 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. * Copyright (C) 2011 Intel Corporation. All rights reserved. */ #include "nm-default.h" -#include "wifi-utils-nl80211.h" +#include "nm-wifi-utils-nl80211.h" #include <errno.h> #include <string.h> @@ -32,7 +32,7 @@ #include <linux/nl80211.h> #include "platform/nm-netlink.h" -#include "wifi-utils-private.h" +#include "nm-wifi-utils-private.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "nm-utils.h" @@ -47,14 +47,20 @@ } G_STMT_END typedef struct { - WifiData parent; + NMWifiUtils parent; struct nl_sock *nl_sock; guint32 *freqs; int id; int num_freqs; int phy; bool can_wowlan:1; -} WifiDataNl80211; +} 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) @@ -97,7 +103,7 @@ nla_put_failure: } static struct nl_msg * -nl80211_alloc_msg (WifiDataNl80211 *nl80211, guint32 cmd, guint32 flags) +nl80211_alloc_msg (NMWifiUtilsNl80211 *nl80211, guint32 cmd, guint32 flags) { return _nl80211_alloc_msg (nl80211->id, nl80211->parent.ifindex, nl80211->phy, cmd, flags); } @@ -154,7 +160,7 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, } static int -nl80211_send_and_recv (WifiDataNl80211 *nl80211, +nl80211_send_and_recv (NMWifiUtilsNl80211 *nl80211, struct nl_msg *msg, int (*valid_handler) (struct nl_msg *, void *), void *valid_data) @@ -164,13 +170,11 @@ nl80211_send_and_recv (WifiDataNl80211 *nl80211, } static void -wifi_nl80211_deinit (WifiData *parent) +dispose (GObject *object) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) parent; + NMWifiUtilsNl80211 *nl80211 = NM_WIFI_UTILS_NL80211 (object); - if (nl80211->nl_sock) - nl_socket_free (nl80211->nl_sock); - g_free (nl80211->freqs); + g_clear_pointer (&nl80211->freqs, g_free); } struct nl80211_iface_info { @@ -207,9 +211,9 @@ nl80211_iface_info_handler (struct nl_msg *msg, void *arg) } static NM80211Mode -wifi_nl80211_get_mode (WifiData *data) +wifi_nl80211_get_mode (NMWifiUtils *data) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; struct nl80211_iface_info iface_info = { .mode = NM_802_11_MODE_UNKNOWN, }; @@ -225,9 +229,9 @@ wifi_nl80211_get_mode (WifiData *data) } static gboolean -wifi_nl80211_set_mode (WifiData *data, const NM80211Mode mode) +wifi_nl80211_set_mode (NMWifiUtils *data, const NM80211Mode mode) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; int err; @@ -255,9 +259,9 @@ nla_put_failure: } static gboolean -wifi_nl80211_set_powersave (WifiData *data, guint32 powersave) +wifi_nl80211_set_powersave (NMWifiUtils *data, guint32 powersave) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; int err; @@ -271,10 +275,64 @@ nla_put_failure: return 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 (attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) + return NL_SKIP; + + nla_parse (trig, MAX_NL80211_WOWLAN_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 *nl80211 = (NMWifiUtilsNl80211 *) data; + NMSettingWirelessWakeOnWLan wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE; + nm_auto_nlmsg struct nl_msg *msg = NULL; + + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0); + + nl80211_send_and_recv (nl80211, msg, nl80211_get_wake_on_wlan_handler, &wowl); + + return wowl; +} + static gboolean -wifi_nl80211_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl) +wifi_nl80211_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; struct nlattr *triggers; int err; @@ -282,11 +340,11 @@ wifi_nl80211_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl) if (wowl == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE) return TRUE; - msg = nl80211_alloc_msg(nl80211, NL80211_CMD_SET_WOWLAN, 0); + msg = nl80211_alloc_msg (nl80211, NL80211_CMD_SET_WOWLAN, 0); if (!msg) return FALSE; - triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); + triggers = nla_nest_start (msg, NL80211_ATTR_WOWLAN_TRIGGERS); if (NM_FLAGS_HAS (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY)) NLA_PUT_FLAG (msg, NL80211_WOWLAN_TRIG_ANY); @@ -306,7 +364,8 @@ wifi_nl80211_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl) nla_nest_end(msg, triggers); err = nl80211_send_and_recv (nl80211, msg, NULL, NULL); - return err ? FALSE : TRUE; + + return err >= 0; nla_put_failure: return FALSE; @@ -432,7 +491,7 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) } static void -nl80211_get_bss_info (WifiDataNl80211 *nl80211, +nl80211_get_bss_info (NMWifiUtilsNl80211 *nl80211, struct nl80211_bss_info *bss_info) { nm_auto_nlmsg struct nl_msg *msg = NULL; @@ -445,9 +504,9 @@ nl80211_get_bss_info (WifiDataNl80211 *nl80211, } static guint32 -wifi_nl80211_get_freq (WifiData *data) +wifi_nl80211_get_freq (NMWifiUtils *data) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; struct nl80211_bss_info bss_info; nl80211_get_bss_info (nl80211, &bss_info); @@ -456,9 +515,9 @@ wifi_nl80211_get_freq (WifiData *data) } static guint32 -wifi_nl80211_find_freq (WifiData *data, const guint32 *freqs) +wifi_nl80211_find_freq (NMWifiUtils *data, const guint32 *freqs) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; int i; for (i = 0; i < nl80211->num_freqs; i++) { @@ -472,9 +531,9 @@ wifi_nl80211_find_freq (WifiData *data, const guint32 *freqs) } static gboolean -wifi_nl80211_get_bssid (WifiData *data, guint8 *out_bssid) +wifi_nl80211_get_bssid (NMWifiUtils *data, guint8 *out_bssid) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; struct nl80211_bss_info bss_info; nl80211_get_bss_info (nl80211, &bss_info); @@ -556,7 +615,7 @@ nl80211_station_handler (struct nl_msg *msg, void *arg) } static void -nl80211_get_ap_info (WifiDataNl80211 *nl80211, +nl80211_get_ap_info (NMWifiUtilsNl80211 *nl80211, struct nl80211_station_info *sta_info) { nm_auto_nlmsg struct nl_msg *msg = NULL; @@ -586,9 +645,9 @@ nla_put_failure: } static guint32 -wifi_nl80211_get_rate (WifiData *data) +wifi_nl80211_get_rate (NMWifiUtils *data) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; struct nl80211_station_info sta_info; nl80211_get_ap_info (nl80211, &sta_info); @@ -597,9 +656,9 @@ wifi_nl80211_get_rate (WifiData *data) } static int -wifi_nl80211_get_qual (WifiData *data) +wifi_nl80211_get_qual (NMWifiUtils *data) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; struct nl80211_station_info sta_info; nl80211_get_ap_info (nl80211, &sta_info); @@ -607,9 +666,9 @@ wifi_nl80211_get_qual (WifiData *data) } static gboolean -wifi_nl80211_indicate_addressing_running (WifiData *data, gboolean running) +wifi_nl80211_indicate_addressing_running (NMWifiUtils *data, gboolean running) { - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; + NMWifiUtilsNl80211 *nl80211 = (NMWifiUtilsNl80211 *) data; nm_auto_nlmsg struct nl_msg *msg = NULL; int err; @@ -638,44 +697,6 @@ nla_put_failure: return FALSE; } -struct nl80211_wowlan_info { - gboolean enabled; -}; - -static int -nl80211_wowlan_handler (struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); - struct nl80211_wowlan_info *info = arg; - - info->enabled = FALSE; - - if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), - genlmsg_attrlen (gnlh, 0), NULL) < 0) - return NL_SKIP; - - if (tb[NL80211_ATTR_WOWLAN_TRIGGERS]) - info->enabled = TRUE; - - return NL_SKIP; -} - -static gboolean -wifi_nl80211_get_wowlan (WifiData *data) -{ - WifiDataNl80211 *nl80211 = (WifiDataNl80211 *) data; - nm_auto_nlmsg struct nl_msg *msg = NULL; - struct nl80211_wowlan_info info; - - if (!nl80211->can_wowlan) - return FALSE; - - msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_WOWLAN, 0); - nl80211_send_and_recv (nl80211, msg, nl80211_wowlan_handler, &info); - return info.enabled; -} - struct nl80211_device_info { int phy; guint32 *freqs; @@ -879,48 +900,58 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) return NL_SKIP; } -WifiData * -wifi_nl80211_init (int ifindex) +static void +nm_wifi_utils_nl80211_init (NMWifiUtilsNl80211 *self) { - static const WifiDataClass klass = { - .struct_size = sizeof (WifiDataNl80211), - .get_mode = wifi_nl80211_get_mode, - .set_mode = wifi_nl80211_set_mode, - .set_powersave = wifi_nl80211_set_powersave, - .set_wake_on_wlan = wifi_nl80211_set_wake_on_wlan, - .get_freq = wifi_nl80211_get_freq, - .find_freq = wifi_nl80211_find_freq, - .get_bssid = wifi_nl80211_get_bssid, - .get_rate = wifi_nl80211_get_rate, - .get_qual = wifi_nl80211_get_qual, - .get_wowlan = wifi_nl80211_get_wowlan, - .indicate_addressing_running = wifi_nl80211_indicate_addressing_running, - .deinit = wifi_nl80211_deinit, - }; - WifiDataNl80211 *nl80211; +} + +static void +nm_wifi_utils_nl80211_class_init (NMWifiUtilsNl80211Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMWifiUtilsClass *wifi_utils_class = NM_WIFI_UTILS_CLASS (klass); + + object_class->dispose = dispose; + + wifi_utils_class->get_mode = wifi_nl80211_get_mode; + wifi_utils_class->set_mode = wifi_nl80211_set_mode; + wifi_utils_class->set_powersave = wifi_nl80211_set_powersave; + wifi_utils_class->get_wake_on_wlan = wifi_nl80211_get_wake_on_wlan, + wifi_utils_class->set_wake_on_wlan = wifi_nl80211_set_wake_on_wlan, + wifi_utils_class->get_freq = wifi_nl80211_get_freq; + wifi_utils_class->find_freq = wifi_nl80211_find_freq; + wifi_utils_class->get_bssid = wifi_nl80211_get_bssid; + wifi_utils_class->get_rate = wifi_nl80211_get_rate; + wifi_utils_class->get_qual = wifi_nl80211_get_qual; + wifi_utils_class->indicate_addressing_running = wifi_nl80211_indicate_addressing_running; +} + +NMWifiUtils * +nm_wifi_utils_nl80211_new (int ifindex, struct nl_sock *genl) +{ + gs_unref_object NMWifiUtilsNl80211 *nl80211 = NULL; nm_auto_nlmsg struct nl_msg *msg = NULL; struct nl80211_device_info device_info = {}; char ifname[IFNAMSIZ]; + if (!genl) + return NULL; + if (!nmp_utils_if_indextoname (ifindex, ifname)) { _LOGW (LOGD_PLATFORM | LOGD_WIFI, "can't determine interface name for ifindex %d", ifindex); nm_sprintf_buf (ifname, "if %d", ifindex); } - nl80211 = wifi_data_new (&klass, ifindex); - - nl80211->nl_sock = nl_socket_alloc (); - if (nl80211->nl_sock == NULL) - goto error; + nl80211 = g_object_new (NM_TYPE_WIFI_UTILS_NL80211, NULL); - if (nl_connect (nl80211->nl_sock, NETLINK_GENERIC)) - goto error; + nl80211->parent.ifindex = ifindex; + nl80211->nl_sock = genl; nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211"); if (nl80211->id < 0) { _LOGD (LOGD_WIFI, "genl_ctrl_resolve: failed to resolve \"nl80211\""); - goto error; + return NULL; } nl80211->phy = -1; @@ -932,42 +963,42 @@ wifi_nl80211_init (int ifindex) _LOGD (LOGD_PLATFORM | LOGD_WIFI, "(%s): NL80211_CMD_GET_WIPHY request failed", ifname); - goto error; + return NULL; } if (!device_info.success) { _LOGD (LOGD_PLATFORM | LOGD_WIFI, "(%s): NL80211_CMD_GET_WIPHY request indicated failure", ifname); - goto error; + return NULL; } if (!device_info.supported) { _LOGD (LOGD_PLATFORM | LOGD_WIFI, "(%s): driver does not fully support nl80211, falling back to WEXT", ifname); - goto error; + return NULL; } if (!device_info.can_scan_ssid) { _LOGE (LOGD_PLATFORM | LOGD_WIFI, "(%s): driver does not support SSID scans", ifname); - goto error; + return NULL; } if (device_info.num_freqs == 0 || device_info.freqs == NULL) { nm_log_err (LOGD_PLATFORM | LOGD_WIFI, "(%s): driver reports no supported frequencies", ifname); - goto error; + return NULL; } if (device_info.caps == 0) { _LOGE (LOGD_PLATFORM | LOGD_WIFI, "(%s): driver doesn't report support of any encryption", ifname); - goto error; + return NULL; } nl80211->phy = device_info.phy; @@ -979,10 +1010,5 @@ wifi_nl80211_init (int ifindex) _LOGI (LOGD_PLATFORM | LOGD_WIFI, "(%s): using nl80211 for WiFi device control", ifname); - return (WifiData *) nl80211; - -error: - wifi_utils_unref ((WifiData *) nl80211); - return NULL; + return (NMWifiUtils *) g_steal_pointer (&nl80211); } - diff --git a/src/platform/wifi/wifi-utils-nl80211.h b/src/platform/wifi/nm-wifi-utils-nl80211.h index aff24555..27f67697 100644 --- a/src/platform/wifi/wifi-utils-nl80211.h +++ b/src/platform/wifi/nm-wifi-utils-nl80211.h @@ -16,13 +16,24 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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 "wifi-utils.h" +#include "nm-wifi-utils.h" +#include "platform/nm-netlink.h" -WifiData *wifi_nl80211_init (int ifindex); +#define NM_TYPE_WIFI_UTILS_NL80211 (nm_wifi_utils_nl80211_get_type ()) +#define NM_WIFI_UTILS_NL80211(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211)) +#define NM_WIFI_UTILS_NL80211_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class)) +#define NM_IS_WIFI_UTILS_NL80211(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIFI_UTILS_NL80211)) +#define NM_IS_WIFI_UTILS_NL80211_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WIFI_UTILS_NL80211)) +#define NM_WIFI_UTILS_NL80211_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WIFI_UTILS_NL80211, NMWifiUtilsNl80211Class)) + +GType nm_wifi_utils_nl80211_get_type (void); + +NMWifiUtils *nm_wifi_utils_nl80211_new (int ifindex, struct nl_sock *genl); #endif /* __WIFI_UTILS_NL80211_H__ */ diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/nm-wifi-utils-private.h index 627108cb..bcbc1c51 100644 --- a/src/platform/wifi/wifi-utils-private.h +++ b/src/platform/wifi/nm-wifi-utils-private.h @@ -15,67 +15,65 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 - 2018 Red Hat, Inc. */ #ifndef __WIFI_UTILS_PRIVATE_H__ #define __WIFI_UTILS_PRIVATE_H__ #include "nm-dbus-interface.h" -#include "wifi-utils.h" +#include "nm-wifi-utils.h" typedef struct { - gsize struct_size; + GObjectClass parent; - NM80211Mode (*get_mode) (WifiData *data); + NM80211Mode (*get_mode) (NMWifiUtils *data); - gboolean (*set_mode) (WifiData *data, const NM80211Mode mode); + gboolean (*set_mode) (NMWifiUtils *data, const NM80211Mode mode); /* Set power saving mode on an interface */ - gboolean (*set_powersave) (WifiData *data, guint32 powersave); + 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) (WifiData *data, NMSettingWirelessWakeOnWLan wowl); + gboolean (*set_wake_on_wlan) (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl); /* Return current frequency in MHz (really associated BSS frequency) */ - guint32 (*get_freq) (WifiData *data); + guint32 (*get_freq) (NMWifiUtils *data); /* Return first supported frequency in the zero-terminated list */ - guint32 (*find_freq) (WifiData *data, const guint32 *freqs); + guint32 (*find_freq) (NMWifiUtils *data, const guint32 *freqs); /* Return current bitrate in Kbps */ - guint32 (*get_rate) (WifiData *data); + guint32 (*get_rate) (NMWifiUtils *data); - gboolean (*get_bssid) (WifiData *data, guint8 *out_bssid); + gboolean (*get_bssid) (NMWifiUtils *data, guint8 *out_bssid); /* Return a signal strength percentage 0 - 100% for the current BSSID; * return -1 on errors or if not associated. */ - int (*get_qual) (WifiData *data); - - void (*deinit) (WifiData *data); - - gboolean (*get_wowlan) (WifiData *data); + int (*get_qual) (NMWifiUtils *data); /* OLPC Mesh-only functions */ - guint32 (*get_mesh_channel) (WifiData *data); + guint32 (*get_mesh_channel) (NMWifiUtils *data); /* channel == 0 means "auto channel" */ - gboolean (*set_mesh_channel) (WifiData *data, guint32 channel); + gboolean (*set_mesh_channel) (NMWifiUtils *data, guint32 channel); /* ssid == NULL means "auto SSID" */ - gboolean (*set_mesh_ssid) (WifiData *data, const guint8 *ssid, gsize len); + gboolean (*set_mesh_ssid) (NMWifiUtils *data, const guint8 *ssid, gsize len); - gboolean (*indicate_addressing_running) (WifiData *data, gboolean running); -} WifiDataClass; + gboolean (*indicate_addressing_running) (NMWifiUtils *data, gboolean running); +} NMWifiUtilsClass; + +struct NMWifiUtils { + GObject parent; -struct WifiData { - const WifiDataClass *klass; int ifindex; NMDeviceWifiCapabilities caps; }; -gpointer wifi_data_new (const WifiDataClass *klass, int ifindex); - #endif /* __WIFI_UTILS_PRIVATE_H__ */ diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/nm-wifi-utils-wext.c index c8744f79..e52ae5a3 100644 --- a/src/platform/wifi/wifi-utils-wext.c +++ b/src/platform/wifi/nm-wifi-utils-wext.c @@ -15,13 +15,13 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2011 Red Hat, Inc. + * Copyright (C) 2005 - 2018 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ #include "nm-default.h" -#include "wifi-utils-wext.h" +#include "nm-wifi-utils-wext.h" #include <errno.h> #include <string.h> @@ -38,17 +38,24 @@ #include <sys/socket.h> #include <linux/wireless.h> -#include "wifi-utils-private.h" +#include "nm-wifi-utils-private.h" #include "nm-utils.h" #include "platform/nm-platform-utils.h" +#include "nm-core-internal.h" typedef struct { - WifiData parent; + NMWifiUtils parent; int fd; struct iw_quality max_qual; gint8 num_freqs; guint32 freqs[IW_MAX_FREQUENCIES]; -} WifiDataWext; +} 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. @@ -94,11 +101,11 @@ iw_freq_to_uint32 (const struct iw_freq *freq) } static void -wifi_wext_deinit (WifiData *parent) +dispose (GObject *object) { - WifiDataWext *wext = (WifiDataWext *) parent; + NMWifiUtilsWext *wext = NM_WIFI_UTILS_WEXT (object); - nm_close (wext->fd); + wext->fd = nm_close (wext->fd); } static gboolean @@ -118,9 +125,9 @@ get_ifname (int ifindex, char *buffer, const char *op) } static NM80211Mode -wifi_wext_get_mode_ifname (WifiData *data, const char *ifname) +wifi_wext_get_mode_ifname (NMWifiUtils *data, const char *ifname) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; memset (&wrq, 0, sizeof (struct iwreq)); @@ -150,7 +157,7 @@ wifi_wext_get_mode_ifname (WifiData *data, const char *ifname) } static NM80211Mode -wifi_wext_get_mode (WifiData *data) +wifi_wext_get_mode (NMWifiUtils *data) { char ifname[IFNAMSIZ]; @@ -161,9 +168,9 @@ wifi_wext_get_mode (WifiData *data) } static gboolean -wifi_wext_set_mode (WifiData *data, const NM80211Mode mode) +wifi_wext_set_mode (NMWifiUtils *data, const NM80211Mode mode) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; char ifname[IFNAMSIZ]; @@ -203,9 +210,9 @@ wifi_wext_set_mode (WifiData *data, const NM80211Mode mode) } static gboolean -wifi_wext_set_powersave (WifiData *data, guint32 powersave) +wifi_wext_set_powersave (NMWifiUtils *data, guint32 powersave) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; char ifname[IFNAMSIZ]; @@ -232,9 +239,9 @@ wifi_wext_set_powersave (WifiData *data, guint32 powersave) } static guint32 -wifi_wext_get_freq (WifiData *data) +wifi_wext_get_freq (NMWifiUtils *data) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; char ifname[IFNAMSIZ]; @@ -254,9 +261,9 @@ wifi_wext_get_freq (WifiData *data) } static guint32 -wifi_wext_find_freq (WifiData *data, const guint32 *freqs) +wifi_wext_find_freq (NMWifiUtils *data, const guint32 *freqs) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; int i; for (i = 0; i < wext->num_freqs; i++) { @@ -270,9 +277,9 @@ wifi_wext_find_freq (WifiData *data, const guint32 *freqs) } static gboolean -wifi_wext_get_bssid (WifiData *data, guint8 *out_bssid) +wifi_wext_get_bssid (NMWifiUtils *data, guint8 *out_bssid) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; char ifname[IFNAMSIZ]; @@ -292,9 +299,9 @@ wifi_wext_get_bssid (WifiData *data, guint8 *out_bssid) } static guint32 -wifi_wext_get_rate (WifiData *data) +wifi_wext_get_rate (NMWifiUtils *data) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; int err; char ifname[IFNAMSIZ]; @@ -403,9 +410,9 @@ wext_qual_to_percent (const struct iw_quality *qual, } static int -wifi_wext_get_qual (WifiData *data) +wifi_wext_get_qual (NMWifiUtils *data) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; struct iw_statistics stats; char ifname[IFNAMSIZ]; @@ -433,13 +440,13 @@ wifi_wext_get_qual (WifiData *data) /* OLPC Mesh-only functions */ static guint32 -wifi_wext_get_mesh_channel (WifiData *data) +wifi_wext_get_mesh_channel (NMWifiUtils *data) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; guint32 freq; int i; - freq = wifi_utils_get_freq (data); + freq = nm_wifi_utils_get_freq (data); for (i = 0; i < wext->num_freqs; i++) { if (freq == wext->freqs[i]) return i + 1; @@ -448,9 +455,9 @@ wifi_wext_get_mesh_channel (WifiData *data) } static gboolean -wifi_wext_set_mesh_channel (WifiData *data, guint32 channel) +wifi_wext_set_mesh_channel (NMWifiUtils *data, guint32 channel) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; char ifname[IFNAMSIZ]; @@ -477,9 +484,9 @@ wifi_wext_set_mesh_channel (WifiData *data, guint32 channel) } static gboolean -wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len) +wifi_wext_set_mesh_ssid (NMWifiUtils *data, const guint8 *ssid, gsize len) { - WifiDataWext *wext = (WifiDataWext *) data; + NMWifiUtilsWext *wext = (NMWifiUtilsWext *) data; struct iwreq wrq; char buf[IW_ESSID_MAX_SIZE + 1]; char ifname[IFNAMSIZ]; @@ -500,11 +507,13 @@ wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len) return TRUE; if (errno != ENODEV) { + gs_free char *ssid_str = NULL; + errsv = errno; _LOGE (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC, "(%s): error setting SSID to '%s': %s", ifname, - ssid ? nm_utils_escape_ssid (ssid, len) : "(null)", + (ssid_str = _nm_utils_ssid_to_string_arr (ssid, len)), strerror (errsv)); } @@ -514,7 +523,7 @@ wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len) /*****************************************************************************/ static gboolean -wext_can_scan_ifname (WifiDataWext *wext, const char *ifname) +wext_can_scan_ifname (NMWifiUtilsWext *wext, const char *ifname) { struct iwreq wrq; @@ -528,7 +537,7 @@ wext_can_scan_ifname (WifiDataWext *wext, const char *ifname) } static gboolean -wext_get_range_ifname (WifiDataWext *wext, +wext_get_range_ifname (NMWifiUtilsWext *wext, const char *ifname, struct iw_range *range, guint32 *response_len) @@ -577,7 +586,7 @@ wext_get_range_ifname (WifiDataWext *wext, NM_WIFI_DEVICE_CAP_RSN) static guint32 -wext_get_caps (WifiDataWext *wext, const char *ifname, struct iw_range *range) +wext_get_caps (NMWifiUtilsWext *wext, const char *ifname, struct iw_range *range) { guint32 caps = NM_WIFI_DEVICE_CAP_NONE; @@ -626,25 +635,38 @@ wext_get_caps (WifiDataWext *wext, const char *ifname, struct iw_range *range) return caps; } -WifiData * -wifi_wext_init (int ifindex, gboolean check_scan) +/*****************************************************************************/ + +static void +nm_wifi_utils_wext_init (NMWifiUtilsWext *self) +{ +} + +static void +nm_wifi_utils_wext_class_init (NMWifiUtilsWextClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMWifiUtilsClass *wifi_utils_class = NM_WIFI_UTILS_CLASS (klass); + + object_class->dispose = dispose; + + wifi_utils_class->get_mode = wifi_wext_get_mode; + wifi_utils_class->set_mode = wifi_wext_set_mode; + wifi_utils_class->set_powersave = wifi_wext_set_powersave; + wifi_utils_class->get_freq = wifi_wext_get_freq; + wifi_utils_class->find_freq = wifi_wext_find_freq; + wifi_utils_class->get_bssid = wifi_wext_get_bssid; + wifi_utils_class->get_rate = wifi_wext_get_rate; + wifi_utils_class->get_qual = wifi_wext_get_qual; + wifi_utils_class->get_mesh_channel = wifi_wext_get_mesh_channel; + wifi_utils_class->set_mesh_channel = wifi_wext_set_mesh_channel; + wifi_utils_class->set_mesh_ssid = wifi_wext_set_mesh_ssid; +} + +NMWifiUtils * +nm_wifi_utils_wext_new (int ifindex, gboolean check_scan) { - static const WifiDataClass klass = { - .struct_size = sizeof (WifiDataWext), - .get_mode = wifi_wext_get_mode, - .set_mode = wifi_wext_set_mode, - .set_powersave = wifi_wext_set_powersave, - .get_freq = wifi_wext_get_freq, - .find_freq = wifi_wext_find_freq, - .get_bssid = wifi_wext_get_bssid, - .get_rate = wifi_wext_get_rate, - .get_qual = wifi_wext_get_qual, - .deinit = wifi_wext_deinit, - .get_mesh_channel = wifi_wext_get_mesh_channel, - .set_mesh_channel = wifi_wext_set_mesh_channel, - .set_mesh_ssid = wifi_wext_set_mesh_ssid, - }; - WifiDataWext *wext; + NMWifiUtilsWext *wext; struct iw_range range; guint32 response_len = 0; struct iw_range_with_scan_capa *scan_capa_range; @@ -658,8 +680,9 @@ wifi_wext_init (int ifindex, gboolean check_scan) return NULL; } - wext = wifi_data_new (&klass, ifindex); + 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; @@ -731,15 +754,15 @@ wifi_wext_init (int ifindex, gboolean check_scan) "(%s): using WEXT for WiFi device control", ifname); - return (WifiData *) wext; + return (NMWifiUtils *) wext; error: - wifi_utils_unref ((WifiData *) wext); + g_object_unref (wext); return NULL; } gboolean -wifi_wext_is_wifi (const char *iface) +nm_wifi_utils_wext_is_wifi (const char *iface) { int fd; struct iwreq iwr; diff --git a/src/platform/wifi/wifi-utils-wext.h b/src/platform/wifi/nm-wifi-utils-wext.h index 3ef5a173..44b11afb 100644 --- a/src/platform/wifi/wifi-utils-wext.h +++ b/src/platform/wifi/nm-wifi-utils-wext.h @@ -15,16 +15,25 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 - 2018 Red Hat, Inc. */ #ifndef __WIFI_UTILS_WEXT_H__ #define __WIFI_UTILS_WEXT_H__ -#include "wifi-utils.h" +#include "nm-wifi-utils.h" -WifiData *wifi_wext_init (int ifindex, gboolean check_scan); +#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)) -gboolean wifi_wext_is_wifi (const char *iface); +GType nm_wifi_utils_wext_get_type (void); + +NMWifiUtils *nm_wifi_utils_wext_new (int ifindex, gboolean check_scan); + +gboolean nm_wifi_utils_wext_is_wifi (const char *iface); #endif /* __WIFI_UTILS_WEXT_H__ */ diff --git a/src/platform/wifi/nm-wifi-utils.c b/src/platform/wifi/nm-wifi-utils.c new file mode 100644 index 00000000..25d71c6a --- /dev/null +++ b/src/platform/wifi/nm-wifi-utils.c @@ -0,0 +1,240 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2018 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include "nm-default.h" + +#include "nm-wifi-utils.h" + +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> + +#include "nm-wifi-utils-private.h" +#include "nm-wifi-utils-nl80211.h" +#if HAVE_WEXT +#include "nm-wifi-utils-wext.h" +#endif +#include "nm-core-utils.h" + +#include "platform/nm-platform-utils.h" + +G_DEFINE_ABSTRACT_TYPE (NMWifiUtils, nm_wifi_utils, G_TYPE_OBJECT) + +/*****************************************************************************/ + +static void +nm_wifi_utils_init (NMWifiUtils *self) +{ +} + +static void +nm_wifi_utils_class_init (NMWifiUtilsClass *klass) +{ +} + +NMWifiUtils * +nm_wifi_utils_new (int ifindex, struct nl_sock *genl, gboolean check_scan) +{ + NMWifiUtils *ret; + + g_return_val_if_fail (ifindex > 0, NULL); + + ret = nm_wifi_utils_nl80211_new (ifindex, genl); + +#if HAVE_WEXT + if (ret == NULL) + ret = nm_wifi_utils_wext_new (ifindex, check_scan); +#endif + + return ret; +} + +NMDeviceWifiCapabilities +nm_wifi_utils_get_caps (NMWifiUtils *data) +{ + g_return_val_if_fail (data != NULL, NM_WIFI_DEVICE_CAP_NONE); + + return data->caps; +} + +NM80211Mode +nm_wifi_utils_get_mode (NMWifiUtils *data) +{ + g_return_val_if_fail (data != NULL, NM_802_11_MODE_UNKNOWN); + return NM_WIFI_UTILS_GET_CLASS (data)->get_mode (data); +} + +gboolean +nm_wifi_utils_set_mode (NMWifiUtils *data, const NM80211Mode mode) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail ( (mode == NM_802_11_MODE_INFRA) + || (mode == NM_802_11_MODE_AP) + || (mode == NM_802_11_MODE_ADHOC), FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + + /* nl80211 probably doesn't need this */ + return klass->set_mode ? klass->set_mode (data, mode) : TRUE; +} + +gboolean +nm_wifi_utils_set_powersave (NMWifiUtils *data, guint32 powersave) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + return klass->set_powersave ? klass->set_powersave (data, powersave) : TRUE; +} + +NMSettingWirelessWakeOnWLan +nm_wifi_utils_get_wake_on_wlan (NMWifiUtils *data) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + + return klass->get_wake_on_wlan ? klass->get_wake_on_wlan (data) : NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE; +} + +gboolean +nm_wifi_utils_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + return klass->set_wake_on_wlan ? klass->set_wake_on_wlan (data, wowl) : FALSE; +} + +guint32 +nm_wifi_utils_get_freq (NMWifiUtils *data) +{ + g_return_val_if_fail (data != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS (data)->get_freq (data); +} + +guint32 +nm_wifi_utils_find_freq (NMWifiUtils *data, const guint32 *freqs) +{ + g_return_val_if_fail (data != NULL, 0); + g_return_val_if_fail (freqs != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS (data)->find_freq (data, freqs); +} + +gboolean +nm_wifi_utils_get_bssid (NMWifiUtils *data, guint8 *out_bssid) +{ + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (out_bssid != NULL, FALSE); + + memset (out_bssid, 0, ETH_ALEN); + return NM_WIFI_UTILS_GET_CLASS (data)->get_bssid (data, out_bssid); +} + +guint32 +nm_wifi_utils_get_rate (NMWifiUtils *data) +{ + g_return_val_if_fail (data != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS (data)->get_rate (data); +} + +int +nm_wifi_utils_get_qual (NMWifiUtils *data) +{ + g_return_val_if_fail (data != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS (data)->get_qual (data); +} + +gboolean +nm_wifi_utils_is_wifi (int dirfd, const char *ifname) +{ + g_return_val_if_fail (dirfd >= 0, FALSE); + + if (faccessat (dirfd, "phy80211", F_OK, 0) == 0) + return TRUE; +#if HAVE_WEXT + if (nm_wifi_utils_wext_is_wifi (ifname)) + return TRUE; +#endif + return FALSE; +} + +/* OLPC Mesh-only functions */ + +guint32 +nm_wifi_utils_get_mesh_channel (NMWifiUtils *data) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + g_return_val_if_fail (klass->get_mesh_channel != NULL, FALSE); + + return klass->get_mesh_channel (data); +} + +gboolean +nm_wifi_utils_set_mesh_channel (NMWifiUtils *data, guint32 channel) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (channel <= 13, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + g_return_val_if_fail (klass->set_mesh_channel != NULL, FALSE); + + return klass->set_mesh_channel (data, channel); +} + +gboolean +nm_wifi_utils_set_mesh_ssid (NMWifiUtils *data, const guint8 *ssid, gsize len) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + g_return_val_if_fail (klass->set_mesh_ssid != NULL, FALSE); + + return klass->set_mesh_ssid (data, ssid, len); +} + +gboolean +nm_wifi_utils_indicate_addressing_running (NMWifiUtils *data, gboolean running) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail (data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS (data); + return klass->indicate_addressing_running ? klass->indicate_addressing_running (data, running) : FALSE; +} diff --git a/src/platform/wifi/nm-wifi-utils.h b/src/platform/wifi/nm-wifi-utils.h new file mode 100644 index 00000000..6cd178bd --- /dev/null +++ b/src/platform/wifi/nm-wifi-utils.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2005 - 2018 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef __WIFI_UTILS_H__ +#define __WIFI_UTILS_H__ + +#include <net/ethernet.h> + +#include "nm-dbus-interface.h" +#include "nm-setting-wireless.h" +#include "platform/nm-netlink.h" + +typedef struct NMWifiUtils NMWifiUtils; + +#define NM_TYPE_WIFI_UTILS (nm_wifi_utils_get_type ()) +#define NM_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIFI_UTILS, NMWifiUtils)) +#define NM_WIFI_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass)) +#define NM_IS_WIFI_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIFI_UTILS)) +#define NM_IS_WIFI_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WIFI_UTILS)) +#define NM_WIFI_UTILS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WIFI_UTILS, NMWifiUtilsClass)) + +GType nm_wifi_utils_get_type (void); + +gboolean nm_wifi_utils_is_wifi (int dirfd, const char *ifname); + +NMWifiUtils *nm_wifi_utils_new (int ifindex, struct nl_sock *genl, gboolean check_scan); + +NMDeviceWifiCapabilities nm_wifi_utils_get_caps (NMWifiUtils *data); + +NM80211Mode nm_wifi_utils_get_mode (NMWifiUtils *data); + +gboolean nm_wifi_utils_set_mode (NMWifiUtils *data, const NM80211Mode mode); + +/* Returns frequency in MHz */ +guint32 nm_wifi_utils_get_freq (NMWifiUtils *data); + +/* Return the first supported frequency in the zero-terminated list. + * Frequencies are specified in MHz. */ +guint32 nm_wifi_utils_find_freq (NMWifiUtils *data, const guint32 *freqs); + +/* out_bssid must be ETH_ALEN bytes */ +gboolean nm_wifi_utils_get_bssid (NMWifiUtils *data, guint8 *out_bssid); + +/* Returns current bitrate in Kbps */ +guint32 nm_wifi_utils_get_rate (NMWifiUtils *data); + +/* Returns quality 0 - 100% on succes, or -1 on error */ +int nm_wifi_utils_get_qual (NMWifiUtils *data); + +/* Tells the driver DHCP or SLAAC is running */ +gboolean nm_wifi_utils_indicate_addressing_running (NMWifiUtils *data, gboolean running); + +gboolean nm_wifi_utils_set_powersave (NMWifiUtils *data, guint32 powersave); + +NMSettingWirelessWakeOnWLan nm_wifi_utils_get_wake_on_wlan (NMWifiUtils *data); + +gboolean nm_wifi_utils_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wowl); + +/* OLPC Mesh-only functions */ +guint32 nm_wifi_utils_get_mesh_channel (NMWifiUtils *data); + +gboolean nm_wifi_utils_set_mesh_channel (NMWifiUtils *data, guint32 channel); + +gboolean nm_wifi_utils_set_mesh_ssid (NMWifiUtils *data, const guint8 *ssid, gsize len); + +#endif /* __WIFI_UTILS_H__ */ diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c deleted file mode 100644 index b3dd92ba..00000000 --- a/src/platform/wifi/wifi-utils.c +++ /dev/null @@ -1,231 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2005 - 2011 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include "nm-default.h" - -#include "wifi-utils.h" - -#include <sys/stat.h> -#include <stdio.h> -#include <string.h> -#include <fcntl.h> - -#include "wifi-utils-private.h" -#include "wifi-utils-nl80211.h" -#if HAVE_WEXT -#include "wifi-utils-wext.h" -#endif -#include "nm-core-utils.h" - -#include "platform/nm-platform-utils.h" - -gpointer -wifi_data_new (const WifiDataClass *klass, int ifindex) -{ - WifiData *data; - - nm_assert (klass); - nm_assert (klass->struct_size > sizeof (WifiData)); - - data = g_malloc0 (klass->struct_size); - data->klass = klass; - data->ifindex = ifindex; - return data; -} - -/*****************************************************************************/ - -WifiData * -wifi_utils_init (int ifindex, gboolean check_scan) -{ - WifiData *ret; - - g_return_val_if_fail (ifindex > 0, NULL); - - ret = wifi_nl80211_init (ifindex); - if (ret == NULL) { -#if HAVE_WEXT - ret = wifi_wext_init (ifindex, check_scan); -#endif - } - return ret; -} - -int -wifi_utils_get_ifindex (WifiData *data) -{ - g_return_val_if_fail (data != NULL, -1); - - return data->ifindex; -} - -NMDeviceWifiCapabilities -wifi_utils_get_caps (WifiData *data) -{ - g_return_val_if_fail (data != NULL, NM_WIFI_DEVICE_CAP_NONE); - - return data->caps; -} - -NM80211Mode -wifi_utils_get_mode (WifiData *data) -{ - g_return_val_if_fail (data != NULL, NM_802_11_MODE_UNKNOWN); - return data->klass->get_mode (data); -} - -gboolean -wifi_utils_set_mode (WifiData *data, const NM80211Mode mode) -{ - 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), FALSE); - - /* nl80211 probably doesn't need this */ - return data->klass->set_mode ? data->klass->set_mode (data, mode) : TRUE; -} - -gboolean -wifi_utils_set_powersave (WifiData *data, guint32 powersave) -{ - g_return_val_if_fail (data != NULL, FALSE); - - return data->klass->set_powersave ? data->klass->set_powersave (data, powersave) : TRUE; -} - -gboolean -wifi_utils_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl) -{ - g_return_val_if_fail (data != NULL, FALSE); - - return data->klass->set_wake_on_wlan ? - data->klass->set_wake_on_wlan (data, wowl) : FALSE; -} - -guint32 -wifi_utils_get_freq (WifiData *data) -{ - g_return_val_if_fail (data != NULL, 0); - return data->klass->get_freq (data); -} - -guint32 -wifi_utils_find_freq (WifiData *data, const guint32 *freqs) -{ - g_return_val_if_fail (data != NULL, 0); - g_return_val_if_fail (freqs != NULL, 0); - return data->klass->find_freq (data, freqs); -} - -gboolean -wifi_utils_get_bssid (WifiData *data, guint8 *out_bssid) -{ - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (out_bssid != NULL, FALSE); - - memset (out_bssid, 0, ETH_ALEN); - return data->klass->get_bssid (data, out_bssid); -} - -guint32 -wifi_utils_get_rate (WifiData *data) -{ - g_return_val_if_fail (data != NULL, 0); - return data->klass->get_rate (data); -} - -int -wifi_utils_get_qual (WifiData *data) -{ - g_return_val_if_fail (data != NULL, 0); - return data->klass->get_qual (data); -} - -gboolean -wifi_utils_get_wowlan (WifiData *data) -{ - g_return_val_if_fail (data != NULL, 0); - - if (!data->klass->get_wowlan) - return FALSE; - return data->klass->get_wowlan (data); -} - -void -wifi_utils_unref (WifiData *data) -{ - g_return_if_fail (data != NULL); - - data->klass->deinit (data); - g_free (data); -} - -gboolean -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 (wifi_wext_is_wifi (ifname)) - return TRUE; -#endif - return FALSE; -} - -/* OLPC Mesh-only functions */ - -guint32 -wifi_utils_get_mesh_channel (WifiData *data) -{ - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (data->klass->get_mesh_channel != NULL, FALSE); - return data->klass->get_mesh_channel (data); -} - -gboolean -wifi_utils_set_mesh_channel (WifiData *data, guint32 channel) -{ - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (channel <= 13, FALSE); - g_return_val_if_fail (data->klass->set_mesh_channel != NULL, FALSE); - return data->klass->set_mesh_channel (data, channel); -} - -gboolean -wifi_utils_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len) -{ - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (data->klass->set_mesh_ssid != NULL, FALSE); - return data->klass->set_mesh_ssid (data, ssid, len); -} - -gboolean -wifi_utils_indicate_addressing_running (WifiData *data, gboolean running) -{ - g_return_val_if_fail (data != NULL, FALSE); - if (data->klass->indicate_addressing_running) - return data->klass->indicate_addressing_running (data, running); - return FALSE; -} - diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h deleted file mode 100644 index c69561de..00000000 --- a/src/platform/wifi/wifi-utils.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2005 - 2011 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" - -typedef struct WifiData WifiData; - -gboolean wifi_utils_is_wifi (int dirfd, const char *ifname); - -WifiData *wifi_utils_init (int ifindex, gboolean check_scan); - -int wifi_utils_get_ifindex (WifiData *data); - -void wifi_utils_unref (WifiData *data); - -NMDeviceWifiCapabilities wifi_utils_get_caps (WifiData *data); - -NM80211Mode wifi_utils_get_mode (WifiData *data); - -gboolean wifi_utils_set_mode (WifiData *data, const NM80211Mode mode); - -/* Returns frequency in MHz */ -guint32 wifi_utils_get_freq (WifiData *data); - -/* Return the first supported frequency in the zero-terminated list. - * Frequencies are specified in MHz. */ -guint32 wifi_utils_find_freq (WifiData *data, const guint32 *freqs); - -/* out_bssid must be ETH_ALEN bytes */ -gboolean wifi_utils_get_bssid (WifiData *data, guint8 *out_bssid); - -/* Returns current bitrate in Kbps */ -guint32 wifi_utils_get_rate (WifiData *data); - -/* Returns quality 0 - 100% on succes, or -1 on error */ -int wifi_utils_get_qual (WifiData *data); - -/* Tells the driver DHCP or SLAAC is running */ -gboolean wifi_utils_indicate_addressing_running (WifiData *data, gboolean running); - -/* Returns true if WoWLAN is enabled on device */ -gboolean wifi_utils_get_wowlan (WifiData *data); - -gboolean wifi_utils_set_powersave (WifiData *data, guint32 powersave); - -gboolean wifi_utils_set_wake_on_wlan (WifiData *data, NMSettingWirelessWakeOnWLan wowl); - -/* OLPC Mesh-only functions */ -guint32 wifi_utils_get_mesh_channel (WifiData *data); - -gboolean wifi_utils_set_mesh_channel (WifiData *data, guint32 channel); - -gboolean wifi_utils_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len); - -#endif /* __WIFI_UTILS_H__ */ diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c new file mode 100644 index 00000000..0544539a --- /dev/null +++ b/src/platform/wpan/nm-wpan-utils.c @@ -0,0 +1,286 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-wpan-utils.h" + +#include "platform/linux/nl802154.h" +#include "platform/nm-netlink.h" + +#define _NMLOG_PREFIX_NAME "wpan-nl802154" +#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 + +/*****************************************************************************/ + +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: + return 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 (struct nl_sock *nl_sock, + 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 (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 (nl_sock, &cb); + if (err < 0 && err != -EAGAIN) { + _LOGW (LOGD_PLATFORM, "nl_recvmsgs() error: (%d) %s", + err, nl_geterror (err)); + break; + } + } + + if (err >= 0 && done < 0) + err = done; + return err; +} + +static int +nl802154_send_and_recv (NMWpanUtils *self, + struct nl_msg *msg, + int (*valid_handler) (struct nl_msg *, void *), + void *valid_data) +{ + return _nl802154_send_and_recv (self->nl_sock, msg, + valid_handler, valid_data); +} + +struct nl802154_interface { + guint16 pan_id; + guint16 short_addr; + + gboolean valid; +}; + +static int +nl802154_get_interface_handler (struct nl_msg *msg, void *arg) +{ + struct nl802154_interface *info = arg; + struct genlmsghdr *gnlh = nlmsg_data (nlmsg_hdr (msg)); + struct nlattr *tb[NL802154_ATTR_MAX + 1] = { 0, }; + static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX + 1] = { + [NL802154_ATTR_PAN_ID] = { .type = NLA_U16 }, + [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16 }, + }; + + if (nla_parse (tb, NL802154_ATTR_MAX, 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: + return 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: + return 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; + int id; + + g_return_val_if_fail (ifindex > 0, NULL); + + if (!genl) + return NULL; + + id = genl_ctrl_resolve (genl, "nl802154"); + if (id < 0) { + _LOGD (LOGD_PLATFORM, "genl_ctrl_resolve: failed to resolve \"nl802154\""); + return NULL; + } + + self = g_object_new (NM_TYPE_WPAN_UTILS, NULL); + self->ifindex = ifindex; + self->nl_sock = genl; + self->id = id; + + return self; +} diff --git a/src/platform/wpan/nm-wpan-utils.h b/src/platform/wpan/nm-wpan-utils.h new file mode 100644 index 00000000..f7d0c03e --- /dev/null +++ b/src/platform/wpan/nm-wpan-utils.h @@ -0,0 +1,47 @@ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __WPAN_UTILS_H__ +#define __WPAN_UTILS_H__ + +#include <net/ethernet.h> + +#include "nm-dbus-interface.h" +#include "platform/nm-netlink.h" + +typedef struct NMWpanUtils NMWpanUtils; + +#define NM_TYPE_WPAN_UTILS (nm_wpan_utils_get_type ()) +#define NM_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WPAN_UTILS, NMWpanUtils)) +#define NM_WPAN_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass)) +#define NM_IS_WPAN_UTILS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WPAN_UTILS)) +#define NM_IS_WPAN_UTILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WPAN_UTILS)) +#define NM_WPAN_UTILS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WPAN_UTILS, NMWpanUtilsClass)) + +GType nm_wpan_utils_get_type (void); + +NMWpanUtils *nm_wpan_utils_new (int ifindex, struct nl_sock *genl, gboolean check_scan); + +guint16 nm_wpan_utils_get_pan_id (NMWpanUtils *self); +gboolean nm_wpan_utils_set_pan_id (NMWpanUtils *self, guint16 pan_id); + +guint16 nm_wpan_utils_get_short_addr (NMWpanUtils *self); +gboolean nm_wpan_utils_set_short_addr (NMWpanUtils *self, guint16 short_addr); + +#endif /* __WPAN_UTILS_H__ */ |