diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 0ea59e4f08..8fba357167 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -124,7 +124,10 @@ guint32 _nm_setting_get_setting_priority (NMSetting *setting); gboolean _nm_setting_get_property (NMSetting *setting, const char *name, GValue *value); -guint _nm_utils_hwaddr_length (const char *asc); +#define NM_UTILS_HWADDR_LEN_MAX_STR (NM_UTILS_HWADDR_LEN_MAX * 3) + +guint8 *_nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length); +const char *nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_case, char *buf, gsize buf_len); char *_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index d963323e22..4be430af6b 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2995,17 +2995,64 @@ nm_utils_wifi_strength_bars (guint8 strength) gsize nm_utils_hwaddr_len (int type) { - g_return_val_if_fail (type == ARPHRD_ETHER || type == ARPHRD_INFINIBAND, 0); - if (type == ARPHRD_ETHER) return ETH_ALEN; else if (type == ARPHRD_INFINIBAND) return INFINIBAND_ALEN; - g_assert_not_reached (); + g_return_val_if_reached (0); } -#define HEXVAL(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10) +static guint8 * +hwaddr_aton (const char *asc, guint8 *buffer, gsize buffer_length, gsize *out_len) +{ + const char *in = asc; + guint8 *out = buffer; + guint8 delimiter = '\0'; + + nm_assert (asc); + nm_assert (buffer); + nm_assert (buffer_length); + nm_assert (out_len); + + while (TRUE) { + const guint8 d1 = in[0]; + guint8 d2; + + if (!g_ascii_isxdigit (d1)) + return NULL; + +#define HEXVAL(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - ('A' - 10)) + + /* If there's no leading zero (ie "aa:b:cc") then fake it */ + d2 = in[1]; + if (d2 && g_ascii_isxdigit (d2)) { + *out++ = (HEXVAL (d1) << 4) + HEXVAL (d2); + d2 = in[2]; + in += 3; + } else { + /* Fake leading zero */ + *out++ = HEXVAL (d1); + in += 2; + } + + if (!d2) + break; + if (--buffer_length == 0) + return NULL; + + if (d2 != delimiter) { + if ( delimiter == '\0' + && (d2 == ':' || d2 == '-')) + delimiter = d2; + else + return NULL; + } + } + + *out_len = out - buffer; + return buffer; +} /** * nm_utils_hwaddr_atoba: @@ -3022,18 +3069,52 @@ GByteArray * nm_utils_hwaddr_atoba (const char *asc, gsize length) { GByteArray *ba; + gsize l; - g_return_val_if_fail (asc != NULL, NULL); + g_return_val_if_fail (asc, NULL); g_return_val_if_fail (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX, NULL); ba = g_byte_array_sized_new (length); g_byte_array_set_size (ba, length); - if (!nm_utils_hwaddr_aton (asc, ba->data, length)) { - g_byte_array_unref (ba); - return NULL; - } + if (!hwaddr_aton (asc, ba->data, length, &l)) + goto fail; + if (length != l) + goto fail; return ba; +fail: + g_byte_array_unref (ba); + return NULL; +} + +/** + * _nm_utils_hwaddr_aton: + * @asc: the ASCII representation of a hardware address + * @buffer: buffer to store the result into. Must have + * at least a size of @buffer_length. + * @buffer_length: the length of the input buffer @buffer. + * The result must fit into that buffer, otherwise + * the function fails and returns %NULL. + * @out_length: the output length in case of success. + * + * Parses @asc and converts it to binary form in @buffer. + * Bytes in @asc can be sepatared by colons (:), or hyphens (-), but not mixed. + * + * It is like nm_utils_hwaddr_aton(), but contrary to that it + * can parse addresses of any length. That is, you don't need + * to know the length before-hand. + * + * Return value: @buffer, or %NULL if @asc couldn't be parsed. + */ +guint8 * +_nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length) +{ + g_return_val_if_fail (asc, NULL); + g_return_val_if_fail (buffer, NULL); + g_return_val_if_fail (buffer_length > 0, NULL); + g_return_val_if_fail (out_length, NULL); + + return hwaddr_aton (asc, buffer, buffer_length, out_length); } /** @@ -3052,72 +3133,55 @@ nm_utils_hwaddr_atoba (const char *asc, gsize length) guint8 * nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize length) { - const char *in = asc; - guint8 *out = (guint8 *)buffer; - char delimiter = '\0'; + gsize l; - g_return_val_if_fail (asc != NULL, NULL); - g_return_val_if_fail (buffer != NULL, NULL); + g_return_val_if_fail (asc, NULL); + g_return_val_if_fail (buffer, NULL); g_return_val_if_fail (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX, NULL); - while (length && *in) { - guint8 d1 = in[0], d2 = in[1]; + if (!hwaddr_aton (asc, buffer, length, &l)) + return NULL; + if (length != l) + return NULL; + return buffer; +} - if (!g_ascii_isxdigit (d1)) - return NULL; +static void +_bin2str_buf (gconstpointer addr, gsize length, gboolean upper_case, char *out) +{ + const guint8 *in = addr; + const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; - /* If there's no leading zero (ie "aa:b:cc") then fake it */ - if (d2 && g_ascii_isxdigit (d2)) { - *out++ = (HEXVAL (d1) << 4) + HEXVAL (d2); - in += 2; - } else { - /* Fake leading zero */ - *out++ = (HEXVAL ('0') << 4) + HEXVAL (d1); - in += 1; - } + nm_assert (addr); + nm_assert (out); + nm_assert (length > 0); + /* @out must contain at least @length*3 bytes */ + + for (;;) { + const guint8 v = *in++; + + *out++ = LOOKUP[v >> 4]; + *out++ = LOOKUP[v & 0x0F]; length--; - if (*in) { - if (delimiter == '\0') { - if (*in == ':' || *in == '-') - delimiter = *in; - else - return NULL; - } else { - if (*in != delimiter) - return NULL; - } - in++; - } + if (!length) + break; + *out++ = ':'; } - if (length == 0 && !*in) - return buffer; - else - return NULL; + *out = 0; } static char * _bin2str (gconstpointer addr, gsize length, gboolean upper_case) { - const guint8 *in = addr; - char *out, *result; - const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; + char *result; - g_return_val_if_fail (addr != NULL, g_strdup ("")); - g_return_val_if_fail (length > 0, g_strdup ("")); + nm_assert (addr); + nm_assert (length > 0); - result = out = g_malloc (length * 3); - while (length--) { - guint8 v = *in++; - - *out++ = LOOKUP[v >> 4]; - *out++ = LOOKUP[v & 0x0F]; - if (length) - *out++ = ':'; - } - - *out = 0; + result = g_malloc (length * 3); + _bin2str_buf (addr, length, upper_case, result); return result; } @@ -3133,9 +3197,25 @@ _bin2str (gconstpointer addr, gsize length, gboolean upper_case) char * nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length) { + g_return_val_if_fail (addr, g_strdup ("")); + g_return_val_if_fail (length > 0, g_strdup ("")); + return _bin2str (addr, length, TRUE); } +const char * +nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_case, char *buf, gsize buf_len) +{ + g_return_val_if_fail (addr, NULL); + g_return_val_if_fail (addr_len > 0, NULL); + g_return_val_if_fail (buf, NULL); + if (buf_len < addr_len * 3) + g_return_val_if_reached (NULL); + + _bin2str_buf (addr, addr_len, TRUE, buf); + return buf; +} + /** * _nm_utils_bin2str: * @addr: (type guint8) (array length=length): a binary hardware address @@ -3149,51 +3229,12 @@ nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length) char * _nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case) { + g_return_val_if_fail (addr, g_strdup ("")); + g_return_val_if_fail (length > 0, g_strdup ("")); + return _bin2str (addr, length, upper_case); } -static int -hwaddr_binary_len (const char *asc) -{ - int octets = 1; - - if (!*asc) - return 0; - - for (; *asc; asc++) { - if (*asc == ':' || *asc == '-') - octets++; - } - return octets; -} - -/** - * _nm_utils_hwaddr_length: - * @asc: the ASCII representation of the hardware address - * - * Validates that @asc is a valid representation of a hardware - * address up to (including) %NM_UTILS_HWADDR_LEN_MAX bytes. - * - * Returns: binary length of the hardware address @asc or - * 0 on error. - */ -guint -_nm_utils_hwaddr_length (const char *asc) -{ - int l; - - if (!asc) - return 0; - - l = hwaddr_binary_len (asc); - if (l <= 0 || l > NM_UTILS_HWADDR_LEN_MAX) - return 0; - - if (!nm_utils_hwaddr_valid (asc, l)) - return 0; - return l; -} - /** * nm_utils_hwaddr_valid: * @asc: the ASCII representation of a hardware address @@ -3210,17 +3251,18 @@ gboolean nm_utils_hwaddr_valid (const char *asc, gssize length) { guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + gsize l; g_return_val_if_fail (asc != NULL, FALSE); - g_return_val_if_fail (length == -1 || (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX), FALSE); - if (length == -1) { - length = hwaddr_binary_len (asc); - if (length == 0 || length > NM_UTILS_HWADDR_LEN_MAX) + if (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX) { + if (!hwaddr_aton (asc, buf, length, &l)) return FALSE; - } - - return nm_utils_hwaddr_aton (asc, buf, length) != NULL; + return length == l; + } else if (length == -1) { + return !!hwaddr_aton (asc, buf, sizeof (buf), &l); + } else + g_return_val_if_reached (FALSE); } /** @@ -3240,20 +3282,23 @@ char * nm_utils_hwaddr_canonical (const char *asc, gssize length) { guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + gsize l; - g_return_val_if_fail (asc != NULL, NULL); + g_return_val_if_fail (asc, NULL); g_return_val_if_fail (length == -1 || (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX), NULL); - if (length == -1) { - length = hwaddr_binary_len (asc); - if (length == 0 || length > NM_UTILS_HWADDR_LEN_MAX) + if (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX) { + if (!hwaddr_aton (asc, buf, length, &l)) return NULL; - } + if (l != length) + return NULL; + } else if (length == -1) { + if (!hwaddr_aton (asc, buf, NM_UTILS_HWADDR_LEN_MAX, &l)) + return NULL; + } else + g_return_val_if_reached (NULL); - if (nm_utils_hwaddr_aton (asc, buf, length) == NULL) - return NULL; - - return nm_utils_hwaddr_ntoa (buf, length); + return nm_utils_hwaddr_ntoa (buf, l); } /* This is used to possibly canonicalize values passed to MAC address property @@ -3317,17 +3362,17 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, gssize hwaddr2_len) { guint8 buf1[NM_UTILS_HWADDR_LEN_MAX], buf2[NM_UTILS_HWADDR_LEN_MAX]; + gsize l; if (hwaddr1_len == -1) { g_return_val_if_fail (hwaddr1 != NULL, FALSE); - hwaddr1_len = hwaddr_binary_len (hwaddr1); - if (hwaddr1_len == 0 || hwaddr1_len > NM_UTILS_HWADDR_LEN_MAX) + if (!hwaddr_aton (hwaddr1, buf1, sizeof (buf1), &l)) { + g_return_val_if_fail ((hwaddr2_len == -1 && hwaddr2) || (hwaddr2_len > 0 && hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX), FALSE); return FALSE; - if (!nm_utils_hwaddr_aton (hwaddr1, buf1, hwaddr1_len)) - return FALSE; - + } hwaddr1 = buf1; + hwaddr1_len = l; } else { g_return_val_if_fail (hwaddr1_len > 0 && hwaddr1_len <= NM_UTILS_HWADDR_LEN_MAX, FALSE); @@ -3340,23 +3385,24 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, if (hwaddr2_len == -1) { g_return_val_if_fail (hwaddr2 != NULL, FALSE); - if (!nm_utils_hwaddr_aton (hwaddr2, buf2, hwaddr1_len)) + if (!hwaddr_aton (hwaddr2, buf2, sizeof (buf2), &l)) + return FALSE; + if (l != hwaddr1_len) return FALSE; - hwaddr2 = buf2; hwaddr2_len = hwaddr1_len; } else { g_return_val_if_fail (hwaddr2_len > 0 && hwaddr2_len <= NM_UTILS_HWADDR_LEN_MAX, FALSE); + if (hwaddr2_len != hwaddr1_len) + return FALSE; + if (!hwaddr2) { memset (buf2, 0, hwaddr2_len); hwaddr2 = buf2; } } - if (hwaddr1_len != hwaddr2_len) - return FALSE; - if (hwaddr1_len == INFINIBAND_ALEN) { hwaddr1 = (guint8 *)hwaddr1 + INFINIBAND_ALEN - 8; hwaddr2 = (guint8 *)hwaddr2 + INFINIBAND_ALEN - 8; @@ -3372,16 +3418,11 @@ static GVariant * _nm_utils_hwaddr_to_dbus_impl (const char *str) { guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - int len; + gsize len; if (!str) return NULL; - - len = _nm_utils_hwaddr_length (str); - if (len == 0) - return NULL; - - if (!nm_utils_hwaddr_aton (str, buf, len)) + if (!hwaddr_aton (str, buf, sizeof (buf), &len)) return NULL; return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, buf, len, 1); diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 26b69bc138..658b3b92a4 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -305,9 +305,11 @@ nm_strdup_not_empty (const char *str) #if NM_MORE_ASSERTS #define nm_assert(cond) G_STMT_START { g_assert (cond); } G_STMT_END +#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } else { g_assert (FALSE && (cond)); } } G_STMT_END #define nm_assert_not_reached() G_STMT_START { g_assert_not_reached (); } G_STMT_END #else #define nm_assert(cond) G_STMT_START { if (FALSE) { if (cond) { } } } G_STMT_END +#define nm_assert_se(cond) G_STMT_START { if (G_LIKELY (cond)) { ; } } G_STMT_END #define nm_assert_not_reached() G_STMT_START { ; } G_STMT_END #endif diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index b99271afdd..6a1f8034f7 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -394,7 +394,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!match_subchans (self, s_wired, &try_mac)) return FALSE; - perm_hw_addr = nm_device_get_permanent_hw_address (device, TRUE); + perm_hw_addr = nm_device_get_permanent_hw_address (device); mac = nm_setting_wired_get_mac_address (s_wired); if (perm_hw_addr) { if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) @@ -1344,6 +1344,7 @@ complete_connection (NMDevice *device, NMSettingPppoe *s_pppoe; const char *setting_mac; const char *perm_hw_addr; + gboolean perm_hw_addr_is_fake; s_pppoe = nm_connection_get_setting_pppoe (connection); @@ -1371,8 +1372,8 @@ complete_connection (NMDevice *device, nm_connection_add_setting (connection, NM_SETTING (s_wired)); } - perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); - if (perm_hw_addr) { + perm_hw_addr = nm_device_get_permanent_hw_address_full (device, TRUE, &perm_hw_addr_is_fake); + if (perm_hw_addr && !perm_hw_addr_is_fake) { setting_mac = nm_setting_wired_get_mac_address (s_wired); if (setting_mac) { /* Make sure the setting MAC (if any) matches the device's permanent MAC */ @@ -1408,7 +1409,7 @@ new_default_connection (NMDevice *self) if (nm_config_get_no_auto_default_for_device (nm_config_get (), self)) return NULL; - perm_hw_addr = nm_device_get_permanent_hw_address (self, TRUE); + perm_hw_addr = nm_device_get_permanent_hw_address (self); if (!perm_hw_addr) return NULL; @@ -1468,7 +1469,8 @@ update_connection (NMDevice *device, NMConnection *connection) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE ((NMDeviceEthernet *) device); NMSettingWired *s_wired = nm_connection_get_setting_wired (connection); - const char *perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); + gboolean perm_hw_addr_is_fake; + const char *perm_hw_addr; const char *mac = nm_device_get_hw_address (device); const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS; GHashTableIter iter; @@ -1487,7 +1489,8 @@ update_connection (NMDevice *device, NMConnection *connection) /* If the device reports a permanent address, use that for the MAC address * and the current MAC, if different, is the cloned MAC. */ - if (perm_hw_addr) { + perm_hw_addr = nm_device_get_permanent_hw_address_full (device, TRUE, &perm_hw_addr_is_fake); + if (perm_hw_addr && !perm_hw_addr_is_fake) { g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL); mac_prop = NULL; diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index fa0d591754..88bd7c0aef 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -160,7 +160,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) mac = nm_setting_infiniband_get_mac_address (s_infiniband); if (mac) { - hw_addr = nm_device_get_permanent_hw_address (device, TRUE); + hw_addr = nm_device_get_permanent_hw_address (device); if ( !hw_addr || !nm_utils_hwaddr_matches (mac, -1, hw_addr, -1)) return FALSE; @@ -197,7 +197,7 @@ complete_connection (NMDevice *device, } setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband); - hw_address = nm_device_get_permanent_hw_address (device, TRUE); + hw_address = nm_device_get_permanent_hw_address (device); if (setting_mac) { /* Make sure the setting MAC (if any) matches the device's MAC */ if (!nm_utils_hwaddr_matches (setting_mac, -1, hw_address, -1)) { @@ -223,7 +223,7 @@ static void update_connection (NMDevice *device, NMConnection *connection) { NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection); - const char *mac = nm_device_get_permanent_hw_address (device, TRUE); + const char *mac = nm_device_get_permanent_hw_address (device); const char *transport_mode = "datagram"; int ifindex; diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index 9e32694ffd..7b240ec6ae 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -369,7 +369,7 @@ match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hw if (!priv->parent) return !fail_if_no_hwaddr; - parent_mac = nm_device_get_permanent_hw_address (priv->parent, FALSE); + parent_mac = nm_device_get_permanent_hw_address (priv->parent); return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 71364658e6..87a36130b8 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -384,7 +384,7 @@ match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hw if (!priv->parent) return !fail_if_no_hwaddr; - parent_mac = nm_device_get_permanent_hw_address (priv->parent, FALSE); + parent_mac = nm_device_get_permanent_hw_address (priv->parent); return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 60c60f62e7..27c111964f 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -227,10 +227,18 @@ typedef struct _NMDevicePrivate { char * iface; /* may change, could be renamed by user */ int ifindex; - guint hw_addr_len; + union { + const guint8 hw_addr_len; /* read-only */ + guint8 hw_addr_len_; + }; guint8 /*HwAddrType*/ hw_addr_type; - bool real; + bool real:1; + + /* there was a IP config change, but no idle action was scheduled because device + * is still not platform-init */ + bool queued_ip4_config_pending:1; + bool queued_ip6_config_pending:1; char * ip_iface; int ip_ifindex; @@ -1859,7 +1867,7 @@ device_link_changed (NMDevice *self) had_hw_addr = (priv->hw_addr != NULL); nm_device_update_hw_address (self); got_hw_addr = (!had_hw_addr && priv->hw_addr); - nm_device_update_permanent_hw_address (self); + nm_device_update_permanent_hw_address (self, FALSE); if (info.name[0] && strcmp (priv->iface, info.name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", @@ -1871,7 +1879,7 @@ device_link_changed (NMDevice *self) ip_ifname_changed = !priv->ip_iface; if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) - nm_device_set_unmanaged_by_user_settings (self, nm_settings_get_unmanaged_specs (priv->settings)); + nm_device_set_unmanaged_by_user_settings (self); else update_unmanaged_specs = TRUE; @@ -1950,7 +1958,7 @@ device_link_changed (NMDevice *self) } if (update_unmanaged_specs) - nm_device_set_unmanaged_by_user_settings (self, nm_settings_get_unmanaged_specs (priv->settings)); + nm_device_set_unmanaged_by_user_settings (self); if ( got_hw_addr && !priv->up @@ -2320,13 +2328,12 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) _notify (self, PROP_UDI); } - /* trigger initial ip config change to initialize ip-config */ - priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); - priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + priv->queued_ip4_config_pending = TRUE; + priv->queued_ip6_config_pending = TRUE; nm_device_update_hw_address (self); nm_device_update_initial_hw_address (self); - nm_device_update_permanent_hw_address (self); + nm_device_update_permanent_hw_address (self, FALSE); /* Note: initial hardware address must be read before calling get_ignore_carrier() */ config = nm_config_get (); @@ -2516,11 +2523,6 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) g_clear_pointer (&priv->udi, g_free); _notify (self, PROP_UDI); } - if (priv->hw_addr) { - priv->hw_addr_len = 0; - g_clear_pointer (&priv->hw_addr, g_free); - _notify (self, PROP_HW_ADDRESS); - } if (priv->physical_port_id) { g_clear_pointer (&priv->physical_port_id, g_free); _notify (self, PROP_PHYSICAL_PORT_ID); @@ -2529,9 +2531,12 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) nm_clear_g_source (&priv->stats.timeout_id); _stats_update_counters (self, 0, 0); + priv->hw_addr_len_ = 0; + if (nm_clear_g_free (&priv->hw_addr)) + _notify (self, PROP_HW_ADDRESS); priv->hw_addr_type = HW_ADDR_TYPE_UNSET; - g_clear_pointer (&priv->hw_addr_perm, g_free); - _notify (self, PROP_PERM_HW_ADDRESS); + if (nm_clear_g_free (&priv->hw_addr_perm)) + _notify (self, PROP_PERM_HW_ADDRESS); g_clear_pointer (&priv->hw_addr_initial, g_free); priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; @@ -7697,6 +7702,7 @@ _cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type) if (nm_clear_g_source (&priv->queued_ip4_config_id)) _LOGD (LOGD_DEVICE, "clearing queued IP4 config change"); + priv->queued_ip4_config_pending = FALSE; dhcp4_cleanup (self, cleanup_type, FALSE); arp_cleanup (self); @@ -7713,6 +7719,7 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) if (nm_clear_g_source (&priv->queued_ip6_config_id)) _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); + priv->queued_ip6_config_pending = FALSE; g_clear_object (&priv->dad6_ip6_config); dhcp6_cleanup (self, cleanup_type, FALSE); @@ -9408,6 +9415,7 @@ update_ip4_config (NMDevice *self, gboolean initial) && activation_source_is_scheduled (self, activate_stage5_ip4_config_commit, AF_INET)) { + priv->queued_ip4_config_pending = FALSE; priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); _LOGT (LOGD_DEVICE, "IP4 update was postponed"); return; @@ -9498,6 +9506,7 @@ update_ip6_config (NMDevice *self, gboolean initial) && activation_source_is_scheduled (self, activate_stage5_ip6_config_commit, AF_INET6)) { + priv->queued_ip6_config_pending = FALSE; priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); _LOGT (LOGD_DEVICE, "IP6 update was postponed"); return; @@ -9575,6 +9584,8 @@ queued_ip4_config_change (gpointer user_data) priv = NM_DEVICE_GET_PRIVATE (self); + nm_assert (!priv->queued_ip4_config_pending); + /* Wait for any queued state changes */ if (priv->queued_state.id) return TRUE; @@ -9599,6 +9610,8 @@ queued_ip6_config_change (gpointer user_data) priv = NM_DEVICE_GET_PRIVATE (self); + nm_assert (!priv->queued_ip4_config_pending); + /* Wait for any queued state changes */ if (priv->queued_state.id) return TRUE; @@ -9678,7 +9691,11 @@ device_ipx_changed (NMPlatform *platform, switch (obj_type) { case NMP_OBJECT_TYPE_IP4_ADDRESS: case NMP_OBJECT_TYPE_IP4_ROUTE: - if (!priv->queued_ip4_config_id) { + if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { + priv->queued_ip4_config_pending = TRUE; + nm_assert_se (!nm_clear_g_source (&priv->queued_ip4_config_id)); + } else if (!priv->queued_ip4_config_id) { + priv->queued_ip4_config_pending = FALSE; priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); _LOGD (LOGD_DEVICE, "queued IP4 config change"); } @@ -9695,7 +9712,11 @@ device_ipx_changed (NMPlatform *platform, } /* fallthrough */ case NMP_OBJECT_TYPE_IP6_ROUTE: - if (!priv->queued_ip6_config_id) { + if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { + priv->queued_ip6_config_pending = TRUE; + nm_assert_se (!nm_clear_g_source (&priv->queued_ip6_config_id)); + } else if (!priv->queued_ip6_config_id) { + priv->queued_ip6_config_pending = FALSE; priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); _LOGD (LOGD_DEVICE, "queued IP6 config change"); } @@ -9937,6 +9958,33 @@ _set_unmanaged_flags (NMDevice *self, allow_state_transition = FALSE; was_managed = allow_state_transition && nm_device_get_managed (self, FALSE); + if ( NM_FLAGS_HAS (priv->unmanaged_flags, NM_UNMANAGED_PLATFORM_INIT) + && NM_FLAGS_HAS (flags, NM_UNMANAGED_PLATFORM_INIT) + && NM_IN_SET (set_op, NM_UNMAN_FLAG_OP_SET_MANAGED)) { + /* we are clearing the platform-init flags. This triggers additional actions. */ + if (!NM_FLAGS_HAS (flags, NM_UNMANAGED_USER_SETTINGS)) { + gboolean unmanaged; + + unmanaged = nm_device_spec_match_list (self, + nm_settings_get_unmanaged_specs (NM_DEVICE_GET_PRIVATE (self)->settings)); + nm_device_set_unmanaged_flags (self, + NM_UNMANAGED_USER_SETTINGS, + !!unmanaged); + } + + if (priv->queued_ip4_config_pending) { + priv->queued_ip4_config_pending = FALSE; + nm_assert_se (!nm_clear_g_source (&priv->queued_ip4_config_id)); + priv->queued_ip4_config_id = g_idle_add (queued_ip4_config_change, self); + } + + if (priv->queued_ip6_config_pending) { + priv->queued_ip6_config_pending = FALSE; + nm_assert_se (!nm_clear_g_source (&priv->queued_ip6_config_id)); + priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self); + } + } + old_flags = priv->unmanaged_flags; old_mask = priv->unmanaged_mask; @@ -10051,20 +10099,30 @@ nm_device_set_unmanaged_by_flags_queue (NMDevice *self, } void -nm_device_set_unmanaged_by_user_settings (NMDevice *self, const GSList *unmanaged_specs) +nm_device_set_unmanaged_by_user_settings (NMDevice *self) { - NMDevicePrivate *priv; gboolean unmanaged; g_return_if_fail (NM_IS_DEVICE (self)); - priv = NM_DEVICE_GET_PRIVATE (self); + if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { + /* the device is already unmanaged due to platform-init. + * + * We want to delay evaluating the device spec, because it will freeze + * the permanent MAC address. That should not be done, before the platform + * link is fully initialized (via UDEV). + * + * Note that when clearing NM_UNMANAGED_PLATFORM_INIT, we will re-evaluate + * whether the device is unmanaged by user-settings. */ + return; + } - unmanaged = nm_device_spec_match_list (self, unmanaged_specs); + unmanaged = nm_device_spec_match_list (self, + nm_settings_get_unmanaged_specs (NM_DEVICE_GET_PRIVATE (self)->settings)); nm_device_set_unmanaged_by_flags (self, NM_UNMANAGED_USER_SETTINGS, - unmanaged, + !!unmanaged, unmanaged ? NM_DEVICE_STATE_REASON_NOW_UNMANAGED : NM_DEVICE_STATE_REASON_NOW_MANAGED); @@ -11587,11 +11645,17 @@ const char * nm_device_get_hw_address (NMDevice *self) { NMDevicePrivate *priv; + char buf[NM_UTILS_HWADDR_LEN_MAX]; + gsize l; g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + priv = NM_DEVICE_GET_PRIVATE (self); - nm_assert ((!priv->hw_addr) ^ (priv->hw_addr_len > 0)); + nm_assert ( (!priv->hw_addr && priv->hw_addr_len == 0) + || ( priv->hw_addr + && _nm_utils_hwaddr_aton (priv->hw_addr, buf, sizeof (buf), &l) + && l == priv->hw_addr_len)); return priv->hw_addr; } @@ -11602,7 +11666,6 @@ nm_device_update_hw_address (NMDevice *self) NMDevicePrivate *priv; const guint8 *hwaddr; gsize hwaddrlen = 0; - gboolean changed = FALSE; priv = NM_DEVICE_GET_PRIVATE (self); if (priv->ifindex <= 0) @@ -11615,38 +11678,46 @@ nm_device_update_hw_address (NMDevice *self) && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, nm_ip_addr_zero.addr_eth, sizeof (nm_ip_addr_zero.addr_eth))) hwaddrlen = 0; - if (hwaddrlen) { - priv->hw_addr_len = hwaddrlen; - if (!priv->hw_addr || !nm_utils_hwaddr_matches (priv->hw_addr, -1, hwaddr, hwaddrlen)) { - g_free (priv->hw_addr); - priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen); + if (!hwaddrlen) + return FALSE; - _LOGD (LOGD_PLATFORM | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr); - _notify (self, PROP_HW_ADDRESS); + if ( priv->hw_addr_len + && priv->hw_addr_len != hwaddrlen) { + char s_buf[NM_UTILS_HWADDR_LEN_MAX_STR]; - if ( !priv->hw_addr_initial - || ( priv->hw_addr_type == HW_ADDR_TYPE_UNSET - && priv->state < NM_DEVICE_STATE_PREPARE - && !nm_device_is_activating (self))) { - /* when we get a hw_addr the first time or while the device - * is not activated (with no explict hw address set), always - * update our inital hw-address as well. */ - nm_device_update_initial_hw_address (self); - } - changed = TRUE; - } - } else { - /* Invalid or no hardware address */ - if (priv->hw_addr_len != 0) { - _LOGD (LOGD_PLATFORM | LOGD_DEVICE, - "hw-addr: failed reading current MAC address (stay with %s)", - priv->hw_addr); - } else { - _LOGD (LOGD_PLATFORM | LOGD_DEVICE, - "hw-addr: failed reading current MAC address"); - } + /* we cannot change the address length of a device once it is set (except + * unrealizing the device). + * + * The reason is that the permanent and initial MAC addresses also must have the + * same address length, so it's unclear what it would mean that the length changes. */ + _LOGD (LOGD_PLATFORM | LOGD_DEVICE, + "hw-addr: read a MAC address with differing length (%s vs. %s)", + priv->hw_addr, + nm_utils_hwaddr_ntoa_buf (hwaddr, hwaddrlen, TRUE, s_buf, sizeof (s_buf))); + return FALSE; } - return changed; + + if ( priv->hw_addr + && nm_utils_hwaddr_matches (priv->hw_addr, -1, hwaddr, hwaddrlen)) + return FALSE; + + g_free (priv->hw_addr); + priv->hw_addr_len_ = hwaddrlen; + priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen); + + _LOGD (LOGD_PLATFORM | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr); + _notify (self, PROP_HW_ADDRESS); + + if ( !priv->hw_addr_initial + || ( priv->hw_addr_type == HW_ADDR_TYPE_UNSET + && priv->state < NM_DEVICE_STATE_PREPARE + && !nm_device_is_activating (self))) { + /* when we get a hw_addr the first time or while the device + * is not activated (with no explict hw address set), always + * update our inital hw-address as well. */ + nm_device_update_initial_hw_address (self); + } + return TRUE; } void @@ -11670,12 +11741,14 @@ nm_device_update_initial_hw_address (NMDevice *self) } void -nm_device_update_permanent_hw_address (NMDevice *self) +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; if (priv->hw_addr_perm) { /* the permanent hardware address is only read once and not @@ -11686,35 +11759,82 @@ nm_device_update_permanent_hw_address (NMDevice *self) return; } - if (priv->ifindex <= 0) + ifindex = priv->ifindex; + if (ifindex <= 0) return; - if (!priv->hw_addr_len) { - nm_device_update_hw_address (self); - if (!priv->hw_addr_len) + /* the user is advised to configure stable MAC addresses for software devices via + * UDEV. Thus, check whether the link is fully initialized. */ + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + if ( !pllink + || !pllink->initialized) { + if (!force_freeze) { + /* we can afford to wait. Back off and leave the permanent MAC address + * undecided for now. */ return; + } + /* try to refresh the link just to give UDEV a bit more time... */ + nm_platform_link_refresh (NM_PLATFORM_GET, ifindex); + /* maybe the MAC address changed... */ + nm_device_update_hw_address (self); + } else if (!priv->hw_addr_len) + nm_device_update_hw_address (self); + + if (!priv->hw_addr_len) { + /* we need the current MAC address because we require the permanent MAC address + * to have the same length as the current address. + * + * Abort if there is no current MAC address. */ + return; } - success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len); - if (!success_read || len != priv->hw_addr_len) { - /* Fall back to current address. We use the fake address and keep it - * until the device unrealizes. - * - * In some cases it might be necessary to know whether this is a "real" or - * a temporary address (fake). */ - _LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use current: %s)", - success_read - ? "read HW addr length of permanent MAC address differs" - : "unable to read permanent MAC address", - priv->hw_addr); - priv->hw_addr_perm_fake = TRUE; - priv->hw_addr_perm = g_strdup (priv->hw_addr); - } else { + success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, ifindex, buf, &len); + if (success_read && priv->hw_addr_len == len) { priv->hw_addr_perm_fake = FALSE; priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, len); _LOGD (LOGD_DEVICE, "hw-addr: read permanent MAC address '%s'", priv->hw_addr_perm); + goto notify_and_out; } + + /* we failed to read a permanent MAC address, thus we use a fake address, + * that is the current MAC address of the device. + * + * Note that the permanet MAC address of a NMDevice instance does not change + * after being set once. Thus, we use now a fake address and stick to that + * (until we unrealize the device). */ + priv->hw_addr_perm_fake = TRUE; + + /* We also persist our choice of the fake address to the device state + * file to use the same address on restart of NetworkManager. + * First, try to reload the address from the state file. */ + { + gs_free NMConfigDeviceStateData *dev_state = NULL; + + dev_state = nm_config_device_state_load (nm_config_get (), ifindex); + if ( dev_state + && dev_state->perm_hw_addr_fake + && nm_utils_hwaddr_aton (dev_state->perm_hw_addr_fake, buf, priv->hw_addr_len) + && !nm_utils_hwaddr_matches (buf, priv->hw_addr_len, priv->hw_addr, -1)) { + _LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use from statefile: %s, current: %s)", + success_read + ? "read HW addr length of permanent MAC address differs" + : "unable to read permanent MAC address", + dev_state->perm_hw_addr_fake, + priv->hw_addr); + priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); + goto notify_and_out; + } + } + + _LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use current: %s)", + success_read + ? "read HW addr length of permanent MAC address differs" + : "unable to read permanent MAC address", + priv->hw_addr); + priv->hw_addr_perm = g_strdup (priv->hw_addr); + +notify_and_out: _notify (self, PROP_PERM_HW_ADDRESS); } @@ -11806,25 +11926,25 @@ nm_device_hw_addr_is_explict (NMDevice *self) } static gboolean -_hw_addr_matches (NMDevice *self, const char *addr) +_hw_addr_matches (NMDevice *self, const guint8 *addr, gsize addr_len) { const char *cur_addr; cur_addr = nm_device_get_hw_address (self); - return cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1); + return cur_addr && nm_utils_hwaddr_matches (addr, addr_len, cur_addr, -1); } static gboolean _hw_addr_set (NMDevice *self, - const char *addr, - const char *operation, - const char *detail) + const char *const addr, + const char *const operation, + const char *const detail) { NMDevicePrivate *priv; gboolean success = FALSE; NMPlatformError plerr; guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; - guint hw_addr_len; + gsize addr_len; gboolean was_up; nm_assert (NM_IS_DEVICE (self)); @@ -11833,17 +11953,17 @@ _hw_addr_set (NMDevice *self, priv = NM_DEVICE_GET_PRIVATE (self); + if (!_nm_utils_hwaddr_aton (addr, addr_bytes, sizeof (addr_bytes), &addr_len)) + g_return_val_if_reached (FALSE); + /* Do nothing if current MAC is same */ - if (_hw_addr_matches (self, addr)) { + if (_hw_addr_matches (self, addr_bytes, addr_len)) { _LOGT (LOGD_DEVICE, "set-hw-addr: no MAC address change needed (%s)", addr); return TRUE; } - hw_addr_len = priv->hw_addr_len; - if (!hw_addr_len) - hw_addr_len = _nm_utils_hwaddr_length (addr); - if ( !hw_addr_len - || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) + if ( priv->hw_addr_len + && priv->hw_addr_len != addr_len) g_return_val_if_reached (FALSE); _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s' (%s, %s)...", addr, operation, detail); @@ -11854,12 +11974,12 @@ _hw_addr_set (NMDevice *self, nm_device_take_down (self, FALSE); } - plerr = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, hw_addr_len); + plerr = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, addr_len); success = (plerr == NM_PLATFORM_ERROR_SUCCESS); if (success) { /* MAC address succesfully changed; update the current MAC to match */ nm_device_update_hw_address (self); - if (_hw_addr_matches (self, addr)) { + if (_hw_addr_matches (self, addr_bytes, addr_len)) { _LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s (%s)", operation, addr, detail); } else { @@ -11889,7 +12009,7 @@ _hw_addr_set (NMDevice *self, goto handle_fail; if (!nm_device_update_hw_address (self)) goto handle_wait; - if (!_hw_addr_matches (self, addr)) + if (!_hw_addr_matches (self, addr_bytes, addr_len)) goto handle_fail; break; @@ -11972,7 +12092,7 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean } if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) { - addr = nm_device_get_permanent_hw_address (self, TRUE); + addr = nm_device_get_permanent_hw_address (self); if (!addr) return FALSE; priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; @@ -12016,9 +12136,8 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean addr = hw_addr_generated; } else { /* this must be a valid address. Otherwise, we shouldn't come here. */ - if (_nm_utils_hwaddr_length (addr) <= 0) { + if (!nm_utils_hwaddr_valid (addr, -1)) g_return_val_if_reached (FALSE); - } priv->hw_addr_type = HW_ADDR_TYPE_EXPLICIT; } @@ -12050,21 +12169,32 @@ nm_device_hw_addr_reset (NMDevice *self, const char *detail) } const char * -nm_device_get_permanent_hw_address (NMDevice *self, gboolean fallback_fake) +nm_device_get_permanent_hw_address_full (NMDevice *self, gboolean force_freeze, gboolean *out_is_fake) { NMDevicePrivate *priv; g_return_val_if_fail (NM_IS_DEVICE (self), NULL); priv = NM_DEVICE_GET_PRIVATE (self); - if (!priv->hw_addr_perm) - return NULL; - if ( priv->hw_addr_perm_fake - && !fallback_fake) - return NULL; + + if ( !priv->hw_addr_perm + && force_freeze) { + /* somebody requests a permanent MAC address, but we don't have it set + * yet. We cannot delay it any longer and try to get it without waiting + * for UDEV. */ + nm_device_update_permanent_hw_address (self, TRUE); + } + + NM_SET_OUT (out_is_fake, priv->hw_addr_perm && priv->hw_addr_perm_fake); return priv->hw_addr_perm; } +const char * +nm_device_get_permanent_hw_address (NMDevice *self) +{ + return nm_device_get_permanent_hw_address_full (self, TRUE, NULL); +} + const char * nm_device_get_initial_hw_address (NMDevice *self) { @@ -12119,7 +12249,7 @@ spec_match_list (NMDevice *self, const GSList *specs) } } - hw_addr_perm = nm_device_get_permanent_hw_address (self, TRUE); + hw_addr_perm = nm_device_get_permanent_hw_address (self); if (hw_addr_perm) { m = nm_match_spec_hwaddr (specs, hw_addr_perm); matched = MAX (matched, m); @@ -12217,13 +12347,16 @@ constructor (GType type, } if (priv->hw_addr_perm) { - priv->hw_addr_len = _nm_utils_hwaddr_length (priv->hw_addr_perm); - if (!priv->hw_addr_len) { + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + gsize l; + + if (!_nm_utils_hwaddr_aton (priv->hw_addr_perm, buf, sizeof (buf), &l)) { g_clear_pointer (&priv->hw_addr_perm, g_free); g_return_val_if_reached (object); } - priv->hw_addr = g_strdup (priv->hw_addr_perm); + priv->hw_addr_len_ = l; + priv->hw_addr = nm_utils_hwaddr_ntoa (buf, l); _LOGT (LOGD_DEVICE, "hw-addr: has permanent hw-address '%s'", priv->hw_addr_perm); } @@ -12595,10 +12728,15 @@ get_property (GObject *object, guint prop_id, case PROP_HW_ADDRESS: g_value_set_string (value, priv->hw_addr); break; - case PROP_PERM_HW_ADDRESS: + case PROP_PERM_HW_ADDRESS: { + const char *perm_hw_addr; + gboolean perm_hw_addr_is_fake; + + perm_hw_addr = nm_device_get_permanent_hw_address_full (self, FALSE, &perm_hw_addr_is_fake); /* this property is exposed on D-Bus for NMDeviceEthernet and NMDeviceWifi. */ - g_value_set_string (value, nm_device_get_permanent_hw_address (self, FALSE)); + g_value_set_string (value, perm_hw_addr && !perm_hw_addr_is_fake ? perm_hw_addr : NULL); break; + } case PROP_HAS_PENDING_ACTION: g_value_set_boolean (value, nm_device_has_pending_action (self)); break; diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index ceee6c0b83..356331189c 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -362,8 +362,10 @@ guint32 nm_device_get_ip4_route_metric (NMDevice *dev); guint32 nm_device_get_ip6_route_metric (NMDevice *dev); const char * nm_device_get_hw_address (NMDevice *dev); -const char * nm_device_get_permanent_hw_address (NMDevice *dev, - gboolean fallback_fake); +const char * nm_device_get_permanent_hw_address (NMDevice *self); +const char * nm_device_get_permanent_hw_address_full (NMDevice *self, + gboolean force_freeze, + gboolean *out_is_fake); const char * nm_device_get_initial_hw_address (NMDevice *dev); NMProxyConfig * nm_device_get_proxy_config (NMDevice *dev); @@ -506,7 +508,7 @@ void nm_device_set_unmanaged_by_flags_queue (NMDevice *self, NMUnmanagedFlags flags, NMUnmanFlagOp set_op, NMDeviceStateReason reason); -void nm_device_set_unmanaged_by_user_settings (NMDevice *self, const GSList *unmanaged_specs); +void nm_device_set_unmanaged_by_user_settings (NMDevice *self); void nm_device_set_unmanaged_by_user_udev (NMDevice *self); void nm_device_set_unmanaged_by_quitting (NMDevice *device); @@ -586,7 +588,7 @@ void nm_device_reactivate_ip6_config (NMDevice *device, gboolean nm_device_update_hw_address (NMDevice *self); void nm_device_update_initial_hw_address (NMDevice *self); -void nm_device_update_permanent_hw_address (NMDevice *self); +void nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze); void nm_device_update_dynamic_ip_setup (NMDevice *self); #endif /* __NETWORKMANAGER_DEVICE_H__ */ diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 320d3a1c55..8381aaccb3 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -603,7 +603,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_wireless) return FALSE; - perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); + perm_hw_addr = nm_device_get_permanent_hw_address (device); mac = nm_setting_wireless_get_mac_address (s_wireless); if (perm_hw_addr) { if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) @@ -895,7 +895,7 @@ complete_connection (NMDevice *device, if (hidden) g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); - perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); + perm_hw_addr = nm_device_get_permanent_hw_address (device); if (perm_hw_addr) { setting_mac = nm_setting_wireless_get_mac_address (s_wifi); if (setting_mac) { diff --git a/src/nm-config.c b/src/nm-config.c index 458265b507..3f492026a1 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -400,7 +400,7 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device) priv = NM_CONFIG_GET_PRIVATE (self); - hw_address = nm_device_get_permanent_hw_address (device, TRUE); + hw_address = nm_device_get_permanent_hw_address (device); if (!hw_address) return; @@ -1879,6 +1879,7 @@ _nm_config_state_set (NMConfig *self, #define DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE "device" #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED "managed" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE "perm-hw-addr-fake" #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID "connection-uuid" static NMConfigDeviceStateData * @@ -1887,7 +1888,10 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf) NMConfigDeviceStateData *device_state; NMConfigDeviceStateManagedType managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN; gs_free char *connection_uuid = NULL; - gsize len_plus_1; + gs_free char *perm_hw_addr_fake = NULL; + gsize connection_uuid_len; + gsize perm_hw_addr_fake_len; + char *p; nm_assert (ifindex > 0); @@ -1908,21 +1912,42 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf) DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID, NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); } + + perm_hw_addr_fake = nm_config_keyfile_get_value (kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + if (perm_hw_addr_fake) { + char *normalized; + + normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1); + g_free (perm_hw_addr_fake); + perm_hw_addr_fake = normalized; + } } - len_plus_1 = connection_uuid ? strlen (connection_uuid) + 1 : 0; + connection_uuid_len = connection_uuid ? strlen (connection_uuid) + 1 : 0; + perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen (perm_hw_addr_fake) + 1 : 0; - device_state = g_malloc (sizeof (NMConfigDeviceStateData) + len_plus_1); + device_state = g_malloc (sizeof (NMConfigDeviceStateData) + + connection_uuid_len + + perm_hw_addr_fake_len); device_state->ifindex = ifindex; device_state->managed = managed_type; device_state->connection_uuid = NULL; - if (connection_uuid) { - char *device_state_data; + device_state->perm_hw_addr_fake = NULL; - device_state_data = (char *) (&device_state[1]); - memcpy (device_state_data, connection_uuid, len_plus_1); - device_state->connection_uuid = device_state_data; + p = (char *) (&device_state[1]); + if (connection_uuid) { + memcpy (p, connection_uuid, connection_uuid_len); + device_state->connection_uuid = p; + p += connection_uuid_len; + } + if (perm_hw_addr_fake) { + memcpy (p, perm_hw_addr_fake, perm_hw_addr_fake_len); + device_state->perm_hw_addr_fake = p; + p += perm_hw_addr_fake_len; } return device_state; @@ -1943,23 +1968,23 @@ nm_config_device_state_load (NMConfig *self, NMConfigDeviceStateData *device_state; char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60]; gs_unref_keyfile GKeyFile *kf = NULL; - gs_free_error GError *error = NULL; g_return_val_if_fail (ifindex > 0, NULL); nm_sprintf_buf (path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); kf = nm_config_create_keyfile (); - if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, &error)) + if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL)) g_clear_pointer (&kf, g_key_file_unref); device_state = _config_device_state_data_new (ifindex, kf); if (kf) { - _LOGT ("device-state: read #%d (%s); managed=%d, connection-uuid=%s%s%s", + _LOGT ("device-state: read #%d (%s); managed=%d%s%s%s%s%s%s", ifindex, path, device_state->managed, - NM_PRINT_FMT_QUOTE_STRING (device_state->connection_uuid)); + NM_PRINT_FMT_QUOTED (device_state->connection_uuid, ", connection-uuid=", device_state->connection_uuid, "", ""), + NM_PRINT_FMT_QUOTED (device_state->perm_hw_addr_fake, ", perm-hw-addr-fake=", device_state->perm_hw_addr_fake, "", "")); } else { _LOGT ("device-state: read #%d (%s); no persistent state", ifindex, path); @@ -1972,6 +1997,7 @@ gboolean nm_config_device_state_write (NMConfig *self, int ifindex, gboolean managed, + const char *perm_hw_addr_fake, const char *connection_uuid) { char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60]; @@ -1983,6 +2009,8 @@ nm_config_device_state_write (NMConfig *self, g_return_val_if_fail (!connection_uuid || *connection_uuid, FALSE); g_return_val_if_fail (managed || !connection_uuid, FALSE); + nm_assert (!perm_hw_addr_fake || nm_utils_hwaddr_valid (perm_hw_addr_fake, -1)); + nm_sprintf_buf (path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); kf = nm_config_create_keyfile (); @@ -1990,6 +2018,12 @@ nm_config_device_state_write (NMConfig *self, DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED, !!managed); + if (perm_hw_addr_fake) { + g_key_file_set_string (kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE, + perm_hw_addr_fake); + } if (connection_uuid) { g_key_file_set_string (kf, DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, @@ -2002,10 +2036,11 @@ nm_config_device_state_write (NMConfig *self, g_error_free (local); return FALSE; } - _LOGT ("device-state: write #%d (%s); managed=%d, connection-uuid=%s%s%s", + _LOGT ("device-state: write #%d (%s); managed=%d%s%s%s%s%s%s", ifindex, path, (bool) managed, - NM_PRINT_FMT_QUOTE_STRING (connection_uuid)); + NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""), + NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", "")); return TRUE; } diff --git a/src/nm-config.h b/src/nm-config.h index 0d3faa8832..326661e9f2 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -199,6 +199,8 @@ struct _NMConfigDeviceStateData { /* the UUID of the last settings-connection active * on the device. */ const char *connection_uuid; + + const char *perm_hw_addr_fake; }; NMConfigDeviceStateData *nm_config_device_state_load (NMConfig *self, @@ -206,6 +208,7 @@ NMConfigDeviceStateData *nm_config_device_state_load (NMConfig *self, gboolean nm_config_device_state_write (NMConfig *self, int ifindex, gboolean managed, + const char *perm_hw_addr_fake, const char *connection_uuid); void nm_config_device_state_prune_unseen (NMConfig *self, GHashTable *seen_ifindexes); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 984e3ec2ed..c48bf6cd45 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -1272,7 +1272,7 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr) { const GSList *iter; NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; - guint hwaddr_len = 0; + gsize hwaddr_len = 0; guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX]; nm_assert (nm_utils_hwaddr_valid (hwaddr, -1)); @@ -1297,11 +1297,8 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr) continue; if (G_UNLIKELY (hwaddr_len == 0)) { - hwaddr_len = _nm_utils_hwaddr_length (hwaddr); - if (!hwaddr_len) + if (!_nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, sizeof (hwaddr_bin), &hwaddr_len)) g_return_val_if_reached (NM_MATCH_SPEC_NO_MATCH); - if (!nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, hwaddr_len)) - nm_assert_not_reached (); } if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr_bin, hwaddr_len)) { diff --git a/src/nm-manager.c b/src/nm-manager.c index ed63b58605..556ae32e9d 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -621,7 +621,7 @@ find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr) if (nm_utils_hwaddr_valid (hwaddr, -1)) { for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data), FALSE); + device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data)); if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1)) return NM_DEVICE (iter->data); } @@ -1331,11 +1331,10 @@ system_unmanaged_devices_changed_cb (NMSettings *settings, { NMManager *self = NM_MANAGER (user_data); NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - const GSList *unmanaged_specs, *iter; + const GSList *iter; - unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings); for (iter = priv->devices; iter; iter = g_slist_next (iter)) - nm_device_set_unmanaged_by_user_settings (NM_DEVICE (iter->data), unmanaged_specs); + nm_device_set_unmanaged_by_user_settings (NM_DEVICE (iter->data)); } static void @@ -2004,7 +2003,7 @@ add_device (NMManager *self, NMDevice *device, GError **error) type_desc = nm_device_get_type_desc (device); g_assert (type_desc); - nm_device_set_unmanaged_by_user_settings (device, nm_settings_get_unmanaged_specs (priv->settings)); + nm_device_set_unmanaged_by_user_settings (device); nm_device_set_unmanaged_flags (device, NM_UNMANAGED_SLEEPING, @@ -2880,15 +2879,15 @@ unmanaged_to_disconnected (NMDevice *device) if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) { nm_device_state_changed (device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_USER_REQUESTED); + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_USER_REQUESTED); } if ( nm_device_is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST) && (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE)) { nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_USER_REQUESTED); + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_USER_REQUESTED); } } @@ -4674,6 +4673,8 @@ nm_manager_write_device_state (NMManager *self) gboolean managed; NMConnection *settings_connection; const char *uuid = NULL; + const char *perm_hw_addr_fake = NULL; + gboolean perm_hw_addr_is_fake; ifindex = nm_device_get_ip_ifindex (device); if (ifindex <= 0) @@ -4693,9 +4694,14 @@ nm_manager_write_device_state (NMManager *self) uuid = nm_connection_get_uuid (settings_connection); } + perm_hw_addr_fake = nm_device_get_permanent_hw_address_full (device, FALSE, &perm_hw_addr_is_fake); + if (perm_hw_addr_fake && !perm_hw_addr_is_fake) + perm_hw_addr_fake = NULL; + if (nm_config_device_state_write (priv->config, ifindex, managed, + perm_hw_addr_fake, uuid)) g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex)); } diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 385a917f62..ff7fea0152 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1883,7 +1883,7 @@ have_connection_for_device (NMSettings *self, NMDevice *device) g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); - perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); + perm_hw_addr = nm_device_get_permanent_hw_address (device); /* Find a wired connection locked to the given MAC address, if any */ g_hash_table_iter_init (&iter, priv->connections);