diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 46fd75b60e..0a2cf101d1 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -17089,11 +17089,11 @@ nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_freeze) { NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self); guint8 buf[_NM_UTILS_HWADDR_LEN_MAX]; - size_t len = 0; gboolean success_read; int ifindex; const NMPlatformLink * pllink; const NMConfigDeviceStateData *dev_state; + NMPLinkAddress cached_hw_addr_perm; if (priv->hw_addr_perm) { /* the permanent hardware address is only read once and not @@ -17132,11 +17132,13 @@ nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_freeze) return; } - success_read = - nm_platform_link_get_permanent_address(nm_device_get_platform(self), ifindex, buf, &len); - if (success_read && priv->hw_addr_len == len) { + success_read = nm_platform_link_get_permanent_address(nm_device_get_platform(self), + pllink, + &cached_hw_addr_perm); + if (success_read && priv->hw_addr_len == cached_hw_addr_perm.len) { priv->hw_addr_perm_fake = FALSE; - priv->hw_addr_perm = nm_utils_hwaddr_ntoa(buf, len); + priv->hw_addr_perm = + nm_utils_hwaddr_ntoa(cached_hw_addr_perm.data, cached_hw_addr_perm.len); _LOGD(LOGD_DEVICE, "hw-addr: read permanent MAC address '%s'", priv->hw_addr_perm); goto notify_and_out; } diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c index f6c3841ed8..69e4846d6f 100644 --- a/src/core/platform/tests/test-link.c +++ b/src/core/platform/tests/test-link.c @@ -580,6 +580,7 @@ test_bridge_addr(void) char addr[ETH_ALEN]; NMPlatformLink link; const NMPlatformLink *plink = NULL; + NMPLinkAddress hw_perm_addr; nm_utils_hwaddr_aton("de:ad:be:ef:00:11", addr, sizeof(addr)); @@ -599,6 +600,7 @@ test_bridge_addr(void) plink = nm_platform_link_get(NM_PLATFORM_GET, link.ifindex); g_assert(plink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, plink, &hw_perm_addr)); if (nm_platform_kernel_support_get(NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL)) { g_assert(!nm_platform_link_get_user_ipv6ll_enabled(NM_PLATFORM_GET, link.ifindex)); @@ -611,6 +613,7 @@ test_bridge_addr(void) g_assert(nm_platform_link_get_user_ipv6ll_enabled(NM_PLATFORM_GET, link.ifindex)); plink = nm_platform_link_get(NM_PLATFORM_GET, link.ifindex); g_assert(plink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, plink, &hw_perm_addr)); g_assert_cmpint(_nm_platform_uint8_inv(plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE); @@ -620,6 +623,7 @@ test_bridge_addr(void) g_assert(!nm_platform_link_get_user_ipv6ll_enabled(NM_PLATFORM_GET, link.ifindex)); plink = nm_platform_link_get(NM_PLATFORM_GET, link.ifindex); g_assert(plink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, plink, &hw_perm_addr)); g_assert_cmpint(_nm_platform_uint8_inv(plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); @@ -730,6 +734,7 @@ static void test_external(void) { const NMPlatformLink *pllink; + NMPLinkAddress hw_perm_addr; SignalData * link_added, *link_changed, *link_removed; int ifindex; @@ -758,6 +763,7 @@ test_external(void) pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex); g_assert(pllink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (!pllink->initialized) { /* we still lack the notification via UDEV. Expect another link changed signal. */ wait_signal(link_changed); @@ -2522,6 +2528,7 @@ test_nl_bugs_veth(void) const NMPlatformLink *pllink_veth0, *pllink_veth1; gs_free_error GError * error = NULL; NMTstpNamespaceHandle *ns_handle = NULL; + NMPLinkAddress hw_perm_addr; /* create veth pair. */ ifindex_veth0 = nmtstp_link_veth_add(NM_PLATFORM_GET, -1, IFACE_VETH0, IFACE_VETH1)->ifindex; @@ -2538,6 +2545,7 @@ test_nl_bugs_veth(void) /* assert that NMPlatformLink.parent is the peer-ifindex. */ pllink_veth0 = nm_platform_link_get(NM_PLATFORM_GET, ifindex_veth0); g_assert(pllink_veth0); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink_veth0, &hw_perm_addr)); if (pllink_veth0->parent == 0) { /* Kernels prior to 4.1 dated 21 June, 2015 don't support exposing the veth peer * as IFA_LINK. skip the remainder of the test. */ @@ -2549,6 +2557,7 @@ test_nl_bugs_veth(void) * https://bugzilla.redhat.com/show_bug.cgi?id=1285827 in place. */ pllink_veth1 = nm_platform_link_get(NM_PLATFORM_GET, ifindex_veth1); g_assert(pllink_veth1); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink_veth1, &hw_perm_addr)); g_assert_cmpint(pllink_veth1->parent, ==, ifindex_veth0); /* move one veth peer to another namespace and check that the @@ -2588,6 +2597,7 @@ test_nl_bugs_spuroius_newlink(void) const char * IFACE_DUMMY0 = "nm-test-dummy0"; int ifindex_bond0, ifindex_dummy0; const NMPlatformLink *pllink; + NMPLinkAddress hw_perm_addr; gboolean wait_for_settle; /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ @@ -2609,6 +2619,7 @@ test_nl_bugs_spuroius_newlink(void) pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_dummy0); g_assert(pllink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (pllink->master == ifindex_bond0) break; }); @@ -2621,6 +2632,7 @@ again: nm_platform_process_events(NM_PLATFORM_GET); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_bond0); g_assert(!pllink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (wait_for_settle) { wait_for_settle = FALSE; @@ -2641,6 +2653,7 @@ test_nl_bugs_spuroius_dellink(void) const char * IFACE_DUMMY0 = "nm-test-dummy0"; int ifindex_bridge0, ifindex_dummy0; const NMPlatformLink *pllink; + NMPLinkAddress hw_perm_addr; gboolean wait_for_settle; /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ @@ -2663,6 +2676,7 @@ test_nl_bugs_spuroius_dellink(void) pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_dummy0); g_assert(pllink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); if (pllink->master == ifindex_bridge0) break; }); @@ -2677,8 +2691,10 @@ again: nm_platform_process_events(NM_PLATFORM_GET); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_bridge0); g_assert(pllink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); pllink = nm_platform_link_get(NM_PLATFORM_GET, ifindex_dummy0); g_assert(pllink); + g_assert(!nm_platform_link_get_permanent_address(NM_PLATFORM_GET, pllink, &hw_perm_addr)); g_assert_cmpint(pllink->parent, ==, 0); if (wait_for_settle) { diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index 3d57b7ff3f..c0c57618f9 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -145,6 +145,8 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1)); #define IFLA_BR_VLAN_STATS_ENABLED 41 +#define IFLA_PERM_ADDRESS 54 + /*****************************************************************************/ /* Appeared in the kernel prior to 3.13 dated 19 January, 2014 */ @@ -2935,6 +2937,7 @@ _new_from_nl_link(NMPlatform * platform, [IFLA_NET_NS_PID] = {.type = NLA_U32}, [IFLA_NET_NS_FD] = {.type = NLA_U32}, [IFLA_LINK_NETNSID] = {}, + [IFLA_PERM_ADDRESS] = {.type = NLA_UNSPEC}, }; const struct ifinfomsg *ifi; struct nlattr * tb[G_N_ELEMENTS(policy)]; @@ -2945,12 +2948,13 @@ _new_from_nl_link(NMPlatform * platform, gboolean * completed_from_cache = cache ? &completed_from_cache_val : NULL; const NMPObject * link_cached = NULL; const NMPObject * lnk_data = NULL; - gboolean address_complete_from_cache = TRUE; - gboolean broadcast_complete_from_cache = TRUE; - gboolean lnk_data_complete_from_cache = TRUE; - gboolean need_ext_data = FALSE; - gboolean af_inet6_token_valid = FALSE; - gboolean af_inet6_addr_gen_mode_valid = FALSE; + gboolean address_complete_from_cache = TRUE; + gboolean perm_address_complete_from_cache = TRUE; + gboolean broadcast_complete_from_cache = TRUE; + gboolean lnk_data_complete_from_cache = TRUE; + gboolean need_ext_data = FALSE; + gboolean af_inet6_token_valid = FALSE; + gboolean af_inet6_addr_gen_mode_valid = FALSE; if (!nlmsg_valid_hdr(nlh, sizeof(*ifi))) return NULL; @@ -3053,6 +3057,20 @@ _new_from_nl_link(NMPlatform * platform, address_complete_from_cache = FALSE; } + if (tb[IFLA_PERM_ADDRESS]) { + if (!_nm_platform_kernel_support_detected( + NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_PERM_ADDRESS)) { + /* support for IFLA_PERM_ADDRESS was added in f74877a5457d34d604dba6dbbb13c4c05bac8b93, + * kernel 5.6, 30 March 2020. + * + * We can only detect support if the attribute is present. A missing attribute + * is not conclusive. */ + _nm_platform_kernel_support_init(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_PERM_ADDRESS, 1); + } + _nmp_link_address_set(&obj->link.l_perm_address, tb[IFLA_PERM_ADDRESS]); + perm_address_complete_from_cache = FALSE; + } + if (tb[IFLA_BROADCAST]) { _nmp_link_address_set(&obj->link.l_broadcast, tb[IFLA_BROADCAST]); broadcast_complete_from_cache = FALSE; @@ -3135,8 +3153,8 @@ _new_from_nl_link(NMPlatform * platform, if (completed_from_cache && (lnk_data_complete_from_cache || need_ext_data || address_complete_from_cache - || broadcast_complete_from_cache || !af_inet6_token_valid - || !af_inet6_addr_gen_mode_valid || !tb[IFLA_STATS64])) { + || perm_address_complete_from_cache || broadcast_complete_from_cache + || !af_inet6_token_valid || !af_inet6_addr_gen_mode_valid || !tb[IFLA_STATS64])) { _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached); if (link_cached && link_cached->_link.netlink.is_in_netlink) { if (lnk_data_complete_from_cache && link_cached->link.type == obj->link.type @@ -3160,6 +3178,8 @@ _new_from_nl_link(NMPlatform * platform, if (address_complete_from_cache) obj->link.l_address = link_cached->link.l_address; + if (perm_address_complete_from_cache) + obj->link.l_perm_address = link_cached->link.l_perm_address; if (broadcast_complete_from_cache) obj->link.l_broadcast = link_cached->link.l_broadcast; if (!af_inet6_token_valid) @@ -7625,14 +7645,21 @@ nla_put_failure: } static gboolean -link_get_permanent_address(NMPlatform *platform, int ifindex, guint8 *buf, size_t *length) +link_get_permanent_address_ethtool(NMPlatform *platform, int ifindex, NMPLinkAddress *out_address) { nm_auto_pop_netns NMPNetns *netns = NULL; + guint8 buffer[_NM_UTILS_HWADDR_LEN_MAX]; + gsize len; if (!nm_platform_netns_push(platform, &netns)) return FALSE; - return nmp_utils_ethtool_get_permanent_address(ifindex, buf, length); + if (!nmp_utils_ethtool_get_permanent_address(ifindex, buffer, &len)) + return FALSE; + nm_assert(len <= _NM_UTILS_HWADDR_LEN_MAX); + memcpy(out_address->data, buffer, len); + out_address->len = len; + return TRUE; } static int @@ -9642,13 +9669,13 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass) platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; platform_class->link_set_token = link_set_token; - platform_class->link_set_address = link_set_address; - platform_class->link_get_permanent_address = link_get_permanent_address; - platform_class->link_set_mtu = link_set_mtu; - platform_class->link_set_name = link_set_name; - platform_class->link_set_sriov_params_async = link_set_sriov_params_async; - platform_class->link_set_sriov_vfs = link_set_sriov_vfs; - platform_class->link_set_bridge_vlans = link_set_bridge_vlans; + platform_class->link_set_address = link_set_address; + platform_class->link_get_permanent_address_ethtool = link_get_permanent_address_ethtool; + platform_class->link_set_mtu = link_set_mtu; + platform_class->link_set_name = link_set_name; + platform_class->link_set_sriov_params_async = link_set_sriov_params_async; + platform_class->link_set_sriov_vfs = link_set_sriov_vfs; + platform_class->link_set_bridge_vlans = link_set_bridge_vlans; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index db5e0db061..ae9ee04e97 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -56,6 +56,7 @@ G_STATIC_ASSERT(_nm_alignof(NMPlatformIPAddress) == _nm_alignof(NMPlatformIPXAdd G_STATIC_ASSERT(sizeof(((NMPLinkAddress *) NULL)->data) == _NM_UTILS_HWADDR_LEN_MAX); G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_address.data) == _NM_UTILS_HWADDR_LEN_MAX); +G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_perm_address.data) == _NM_UTILS_HWADDR_LEN_MAX); G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == _NM_UTILS_HWADDR_LEN_MAX); static const char * @@ -348,6 +349,12 @@ static const struct { .name = "IFLA_BR_VLAN_STATS_ENABLE", .desc = "IFLA_BR_VLAN_STATS_ENABLE bridge link attribute", }, + [NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_PERM_ADDRESS] = + { + .compile_time_default = (IFLA_MAX >= 54 /* IFLA_PERM_ADDRESS */), + .name = "IFLA_PERM_ADDRESS", + .desc = "IFLA_PERM_ADDRESS netlink attribute", + }, }; int @@ -1736,7 +1743,7 @@ nm_platform_link_get_address(NMPlatform *self, int ifindex, size_t *length) } /** - * nm_platform_link_get_permanent_address: + * nm_platform_link_get_permanent_address_ethtool: * @self: platform instance * @ifindex: Interface index * @buf: buffer of at least %_NM_UTILS_HWADDR_LEN_MAX bytes, on success @@ -1747,22 +1754,47 @@ nm_platform_link_get_address(NMPlatform *self, int ifindex, size_t *length) * address. */ gboolean -nm_platform_link_get_permanent_address(NMPlatform *self, int ifindex, guint8 *buf, size_t *length) +nm_platform_link_get_permanent_address_ethtool(NMPlatform * self, + int ifindex, + NMPLinkAddress *out_address) { _CHECK_SELF(self, klass, FALSE); - if (length) - *length = 0; + if (out_address) + out_address->len = 0; g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(buf, FALSE); - g_return_val_if_fail(length, FALSE); + g_return_val_if_fail(out_address, FALSE); - if (klass->link_get_permanent_address) - return klass->link_get_permanent_address(self, ifindex, buf, length); + if (klass->link_get_permanent_address_ethtool) + return klass->link_get_permanent_address_ethtool(self, ifindex, out_address); return FALSE; } +gboolean +nm_platform_link_get_permanent_address(NMPlatform * self, + const NMPlatformLink *plink, + NMPLinkAddress * out_address) +{ + _CHECK_SELF(self, klass, FALSE); + nm_assert(out_address); + + if (!plink) + return FALSE; + if (plink->l_perm_address.len > 0) { + *out_address = plink->l_perm_address; + return TRUE; + } + if (nm_platform_kernel_support_get_full(NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_PERM_ADDRESS, + FALSE) + == NM_OPTION_BOOL_TRUE) { + /* kernel supports the netlink API IFLA_PERM_ADDRESS, but we don't have the + * address cached. There is no need to fallback to ethtool ioctl. */ + return FALSE; + } + return nm_platform_link_get_permanent_address_ethtool(self, plink->ifindex, out_address); +} + gboolean nm_platform_link_supports_carrier_detect(NMPlatform *self, int ifindex) { @@ -5527,6 +5559,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len) gsize l; char str_addrmode[30]; char str_address[_NM_UTILS_HWADDR_LEN_MAX * 3]; + char str_perm_address[_NM_UTILS_HWADDR_LEN_MAX * 3]; char str_broadcast[_NM_UTILS_HWADDR_LEN_MAX * 3]; char str_inet6_token[NM_UTILS_INET_ADDRSTRLEN]; const char *str_link_type; @@ -5565,6 +5598,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len) parent[0] = 0; _nmp_link_address_to_string(&link->l_address, str_address); + _nmp_link_address_to_string(&link->l_perm_address, str_perm_address); _nmp_link_address_to_string(&link->l_broadcast, str_broadcast); str_link_type = nm_link_type_to_string(link->type); @@ -5584,6 +5618,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len) "%s" /* is-in-udev */ "%s%s" /* addr-gen-mode */ "%s%s" /* l_address */ + "%s%s" /* l_perm_address */ "%s%s" /* l_broadcast */ "%s%s" /* inet6_token */ "%s%s" /* driver */ @@ -5609,6 +5644,8 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len) : "", str_address[0] ? " addr " : "", str_address[0] ? str_address : "", + str_perm_address[0] ? " permaddr " : "", + str_perm_address[0] ? str_perm_address : "", str_broadcast[0] ? " brd " : "", str_broadcast[0] ? str_broadcast : "", link->inet6_token.id ? " inet6token " : "", @@ -7305,6 +7342,9 @@ nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h) nm_hash_update_mem(h, obj->l_address.data, NM_MIN(obj->l_address.len, sizeof(obj->l_address.data))); + nm_hash_update_mem(h, + obj->l_perm_address.data, + NM_MIN(obj->l_perm_address.len, sizeof(obj->l_perm_address.data))); nm_hash_update_mem(h, obj->l_broadcast.data, NM_MIN(obj->l_broadcast.len, sizeof(obj->l_broadcast.data))); @@ -7325,12 +7365,15 @@ nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b) NM_CMP_FIELD_BOOL(a, b, initialized); NM_CMP_FIELD(a, b, arptype); NM_CMP_FIELD(a, b, l_address.len); + NM_CMP_FIELD(a, b, l_perm_address.len); NM_CMP_FIELD(a, b, l_broadcast.len); NM_CMP_FIELD(a, b, inet6_addr_gen_mode_inv); NM_CMP_FIELD_STR_INTERNED(a, b, kind); NM_CMP_FIELD_STR_INTERNED(a, b, driver); if (a->l_address.len) NM_CMP_FIELD_MEMCMP_LEN(a, b, l_address.data, a->l_address.len); + if (a->l_perm_address.len) + NM_CMP_FIELD_MEMCMP_LEN(a, b, l_perm_address.data, a->l_perm_address.len); if (a->l_broadcast.len) NM_CMP_FIELD_MEMCMP_LEN(a, b, l_broadcast.data, a->l_broadcast.len); NM_CMP_FIELD_MEMCMP(a, b, inet6_token); diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 5b7a73e887..b20b18c4dd 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -231,6 +231,9 @@ struct _NMPlatformLink { /* IFLA_ADDRESS */ NMPLinkAddress l_address; + /* IFLA_PERM_ADDRESS */ + NMPLinkAddress l_perm_address; + /* IFLA_BROADCAST */ NMPLinkAddress l_broadcast; @@ -1015,6 +1018,7 @@ typedef enum { NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_PROTOCOL, NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BR_VLAN_STATS_ENABLED, + NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_PERM_ADDRESS, /* this also includes FRA_SPORT_RANGE and FRA_DPORT_RANGE which * were added at the same time. */ @@ -1108,10 +1112,9 @@ typedef struct { int (*link_set_user_ipv6ll_enabled)(NMPlatform *self, int ifindex, gboolean enabled); gboolean (*link_set_token)(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); - gboolean (*link_get_permanent_address)(NMPlatform *self, - int ifindex, - guint8 * buf, - size_t * length); + gboolean (*link_get_permanent_address_ethtool)(NMPlatform * self, + int ifindex, + NMPLinkAddress *out_address); int (*link_set_address)(NMPlatform *self, int ifindex, gconstpointer address, size_t length); int (*link_set_mtu)(NMPlatform *self, int ifindex, guint32 mtu); gboolean (*link_set_name)(NMPlatform *self, int ifindex, const char *name); @@ -1861,8 +1864,12 @@ struct udev_device *nm_platform_link_get_udev_device(NMPlatform *self, int ifind int nm_platform_link_set_user_ipv6ll_enabled(NMPlatform *self, int ifindex, gboolean enabled); gboolean nm_platform_link_set_ipv6_token(NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); -gboolean -nm_platform_link_get_permanent_address(NMPlatform *self, int ifindex, guint8 *buf, size_t *length); +gboolean nm_platform_link_get_permanent_address_ethtool(NMPlatform * self, + int ifindex, + NMPLinkAddress *out_address); +gboolean nm_platform_link_get_permanent_address(NMPlatform * self, + const NMPlatformLink *plink, + NMPLinkAddress * out_address); int nm_platform_link_set_address(NMPlatform *self, int ifindex, const void *address, size_t length); int nm_platform_link_set_mtu(NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name);