diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 6573e0b2bc..d3645ee031 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -24,6 +24,7 @@ #include #include +#include "nm-common-macros.h" #include "utils.h" #include "common.h" #include "nm-vpn-helpers.h" @@ -2889,11 +2890,12 @@ nmc_property_set_ssid (NMSetting *setting, const char *prop, const char *val, GE } static gboolean -nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +_property_set_mac (NMSetting *setting, const char *prop, const char *val, gboolean cloned_mac_addr, GError **error) { g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (!nm_utils_hwaddr_valid (val, ETH_ALEN)) { + if ( (!cloned_mac_addr || !NM_CLONED_MAC_IS_SPECIAL (val)) + && !nm_utils_hwaddr_valid (val, ETH_ALEN)) { g_set_error (error, 1, 0, _("'%s' is not a valid Ethernet MAC"), val); return FALSE; } @@ -2902,6 +2904,18 @@ nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GEr return TRUE; } +static gboolean +nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return _property_set_mac (setting, prop, val, FALSE, error); +} + +static gboolean +nmc_property_set_mac_cloned (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return _property_set_mac (setting, prop, val, TRUE, error); +} + static gboolean nmc_property_set_mtu (NMSetting *setting, const char *prop, const char *val, GError **error) { @@ -7198,7 +7212,7 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (WIRED, CLONED_MAC_ADDRESS), nmc_property_wired_get_cloned_mac_address, - nmc_property_set_mac, + nmc_property_set_mac_cloned, NULL, NULL, NULL, @@ -7316,7 +7330,7 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (WIRELESS, CLONED_MAC_ADDRESS), nmc_property_wireless_get_cloned_mac_address, - nmc_property_set_mac, + nmc_property_set_mac_cloned, NULL, NULL, NULL, diff --git a/clients/tui/nmt-mac-entry.c b/clients/tui/nmt-mac-entry.c index d0f07e120a..da7f55fac1 100644 --- a/clients/tui/nmt-mac-entry.c +++ b/clients/tui/nmt-mac-entry.c @@ -28,11 +28,13 @@ #include "nm-default.h" +#include "nmt-mac-entry.h" + #include #include "NetworkManager.h" +#include "nm-common-macros.h" -#include "nmt-mac-entry.h" G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) @@ -41,6 +43,7 @@ G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) typedef struct { int mac_length; int mac_str_length; + NmtMacEntryType entry_type; } NmtMacEntryPrivate; @@ -48,6 +51,7 @@ enum { PROP_0, PROP_MAC_LENGTH, PROP_MAC_ADDRESS, + PROP_ENTRY_TYPE, LAST_PROP }; @@ -57,6 +61,7 @@ enum { * @width: the width in characters of the entry * @mac_length: the length in bytes of the hardware address * (either %ETH_ALEN or %INFINIBAND_ALEN) + * @entry_type: the type of the entry. * * Creates a new #NmtMacEntry. * @@ -64,11 +69,13 @@ enum { */ NmtNewtWidget * nmt_mac_entry_new (int width, - int mac_length) + int mac_length, + NmtMacEntryType entry_type) { return g_object_new (NMT_TYPE_MAC_ENTRY, "width", width, "mac-length", mac_length, + "entry-type", (int) entry_type, NULL); } @@ -81,6 +88,9 @@ mac_filter (NmtNewtEntry *entry, { NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry); + if (priv->entry_type != NMT_MAC_ENTRY_TYPE_MAC) + return TRUE; + if (position >= priv->mac_str_length) return FALSE; @@ -98,6 +108,11 @@ mac_validator (NmtNewtEntry *entry, if (!*text) return TRUE; + if (priv->entry_type == NMT_MAC_ENTRY_TYPE_CLONED) { + if (NM_CLONED_MAC_IS_SPECIAL (text)) + return TRUE; + } + p = text; while ( g_ascii_isxdigit (p[0]) && g_ascii_isxdigit (p[1]) @@ -112,7 +127,9 @@ mac_validator (NmtNewtEntry *entry, if (!*p) return (p - text == priv->mac_str_length); - if (g_ascii_isxdigit (p[0]) && !p[1]) { + if ( g_ascii_isxdigit (p[0]) + && !p[1] + && p - text < priv->mac_str_length) { char *fixed = g_strdup_printf ("%.*s:%c", (int)(p - text), text, *p); nmt_newt_entry_set_text (entry, fixed); @@ -161,6 +178,10 @@ nmt_mac_entry_set_property (GObject *object, case PROP_MAC_ADDRESS: nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), g_value_get_string (value)); break; + case PROP_ENTRY_TYPE: + /* construct-only */ + priv->entry_type = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -182,6 +203,9 @@ nmt_mac_entry_get_property (GObject *object, case PROP_MAC_ADDRESS: g_value_set_string (value, nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))); break; + case PROP_ENTRY_TYPE: + g_value_set_int (value, priv->entry_type); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -224,4 +248,17 @@ nmt_mac_entry_class_init (NmtMacEntryClass *entry_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtMacEntry:entry-type: + * + * The type of the #NmtMacEntry. Can be either used for plain + * MAC addresses or for the extended format for cloned MAC addresses. + */ + g_object_class_install_property + (object_class, PROP_ENTRY_TYPE, + g_param_spec_int ("entry-type", "", "", + NMT_MAC_ENTRY_TYPE_MAC, NMT_MAC_ENTRY_TYPE_CLONED, NMT_MAC_ENTRY_TYPE_MAC, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); } diff --git a/clients/tui/nmt-mac-entry.h b/clients/tui/nmt-mac-entry.h index 099bacb71b..b318911767 100644 --- a/clients/tui/nmt-mac-entry.h +++ b/clients/tui/nmt-mac-entry.h @@ -25,6 +25,11 @@ G_BEGIN_DECLS +typedef enum { /*< skip >*/ + NMT_MAC_ENTRY_TYPE_MAC, + NMT_MAC_ENTRY_TYPE_CLONED, +} NmtMacEntryType; + #define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type ()) #define NMT_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry)) #define NMT_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) @@ -45,7 +50,8 @@ typedef struct { GType nmt_mac_entry_get_type (void); NmtNewtWidget *nmt_mac_entry_new (int width, - int mac_length); + int mac_length, + NmtMacEntryType type); G_END_DECLS diff --git a/clients/tui/nmt-page-ethernet.c b/clients/tui/nmt-page-ethernet.c index dfe2e44077..6b9243271f 100644 --- a/clients/tui/nmt-page-ethernet.c +++ b/clients/tui/nmt-page-ethernet.c @@ -70,7 +70,7 @@ nmt_page_ethernet_constructed (GObject *object) section = nmt_editor_section_new (_("ETHERNET"), NULL, FALSE); grid = nmt_editor_section_get_body (section); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/clients/tui/nmt-page-vlan.c b/clients/tui/nmt-page-vlan.c index c63b190430..9cd6542163 100644 --- a/clients/tui/nmt-page-vlan.c +++ b/clients/tui/nmt-page-vlan.c @@ -115,7 +115,7 @@ nmt_page_vlan_constructed (GObject *object) nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/clients/tui/nmt-page-wifi.c b/clients/tui/nmt-page-wifi.c index cd9b60e109..35625fe4c7 100644 --- a/clients/tui/nmt-page-wifi.c +++ b/clients/tui/nmt-page-wifi.c @@ -351,13 +351,13 @@ nmt_page_wifi_constructed (GObject *object) nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_MAC); g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BSSID, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); nmt_editor_grid_append (grid, _("BSSID"), widget, NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index 4a17d2c488..cafbf8fd73 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -31,6 +31,7 @@ #include #include +#include "nm-common-macros.h" #include "nm-core-internal.h" #include "nm-keyfile-utils.h" @@ -581,19 +582,28 @@ ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const cha } static void -mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length) +mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length, gboolean cloned_mac_addr) { const char *setting_name = nm_setting_get_name (setting); - char *tmp_string = NULL, *p, *mac_str; - gint *tmp_list; - GByteArray *array = NULL; + gs_free char *tmp_string = NULL; + const char *p, *mac_str; + gs_free guint8 *buf_arr = NULL; + guint buf_len; gsize length; - p = tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); + tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); + + if ( cloned_mac_addr + && NM_CLONED_MAC_IS_SPECIAL (tmp_string)) { + mac_str = tmp_string; + goto out; + } + if (tmp_string && tmp_string[0]) { /* Look for enough ':' characters to signify a MAC address */ guint i = 0; + p = tmp_string; while (*p) { if (*p == ':') i++; @@ -602,23 +612,24 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key if (enforce_length == 0 || enforce_length == i+1) { /* If we found enough it's probably a string-format MAC address */ - array = g_byte_array_sized_new (i+1); - g_byte_array_set_size (array, i+1); - if (!nm_utils_hwaddr_aton (tmp_string, array->data, array->len)) { - g_byte_array_unref (array); - array = NULL; - } + buf_len = i + 1; + buf_arr = g_new (guint8, buf_len); + if (!nm_utils_hwaddr_aton (tmp_string, buf_arr, buf_len)) + g_clear_pointer (&buf_arr, g_free); } } - g_free (tmp_string); + g_clear_pointer (&tmp_string, g_free); + + if (!buf_arr) { + gs_free int *tmp_list = NULL; - if (array == NULL) { /* Old format; list of ints */ tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL); if (length > 0 && (enforce_length == 0 || enforce_length == length)) { gsize i; - array = g_byte_array_sized_new (length); + buf_len = length; + buf_arr = g_new (guint8, buf_len); for (i = 0; i < length; i++) { int val = tmp_list[i]; const guint8 v = (guint8) (val & 0xFF); @@ -627,38 +638,42 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"), val); - g_byte_array_free (array, TRUE); - g_free (tmp_list); return; } - g_byte_array_append (array, &v, 1); + buf_arr[i] = v; } } - g_free (tmp_list); } - if (!array) { + if (!buf_arr) { handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid MAC address")); return; } - mac_str = nm_utils_hwaddr_ntoa (array->data, array->len); + tmp_string = nm_utils_hwaddr_ntoa (buf_arr, buf_len); + mac_str = tmp_string; + +out: g_object_set (setting, key, mac_str, NULL); - g_free (mac_str); - g_byte_array_free (array, TRUE); } static void mac_address_parser_ETHER (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (info, setting, key, ETH_ALEN); + mac_address_parser (info, setting, key, ETH_ALEN, FALSE); +} + +static void +mac_address_parser_ETHER_cloned (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + mac_address_parser (info, setting, key, ETH_ALEN, TRUE); } static void mac_address_parser_INFINIBAND (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (info, setting, key, INFINIBAND_ALEN); + mac_address_parser (info, setting, key, INFINIBAND_ALEN, FALSE); } static void @@ -1209,7 +1224,7 @@ static KeyParser key_parsers[] = { { NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, TRUE, - mac_address_parser_ETHER }, + mac_address_parser_ETHER_cloned }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS, TRUE, @@ -1217,7 +1232,7 @@ static KeyParser key_parsers[] = { { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, TRUE, - mac_address_parser_ETHER }, + mac_address_parser_ETHER_cloned }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BSSID, TRUE, diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index a8f6e87653..f6b801c6bd 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -1415,6 +1415,8 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) * * The stable-id is used instead of the connection UUID for generating * IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. + * It is also used to seed the generated cloned MAC address for + * ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. * * Since: 1.4 **/ diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c index a76218fd24..3e72056afd 100644 --- a/libnm-core/nm-setting-wired.c +++ b/libnm-core/nm-setting-wired.c @@ -28,6 +28,7 @@ #include #include "nm-utils.h" +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -692,7 +693,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->cloned_mac_address && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { + if ( priv->cloned_mac_address + && !NM_CLONED_MAC_IS_SPECIAL (priv->cloned_mac_address) + && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -732,6 +735,25 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingClass *parent_class; + + if (nm_streq (prop_spec->name, NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) { + return nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address, + NM_SETTING_WIRED_GET_PRIVATE (other)->cloned_mac_address); + } + + parent_class = NM_SETTING_CLASS (nm_setting_wired_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ + static void clear_blacklist_item (char **item_p) { @@ -900,18 +922,19 @@ get_property (GObject *object, guint prop_id, } static void -nm_setting_wired_class_init (NMSettingWiredClass *setting_class) +nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class) { - GObjectClass *object_class = G_OBJECT_CLASS (setting_class); - NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + GObjectClass *object_class = G_OBJECT_CLASS (setting_wired_class); + NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wired_class); - g_type_class_add_private (setting_class, sizeof (NMSettingWiredPrivate)); + g_type_class_add_private (setting_wired_class, sizeof (NMSettingWiredPrivate)); /* virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; - parent_class->verify = verify; + setting_class->verify = verify; + setting_class->compare_property = compare_property; /* Properties */ /** @@ -1023,7 +1046,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_MAC_ADDRESS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_MAC_ADDRESS, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1033,6 +1056,20 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) * * If specified, request that the device use this MAC address instead of its * permanent MAC address. This is known as MAC cloning or spoofing. + * + * Beside explicitly specifing a MAC address, the special values "preserve", "permanent", + * "random" and "stable" are supported. + * "preserve" means not to touch the MAC address on activation. + * "permanent" means to use the permanent hardware address of the device. + * "random" creates a random MAC address on each connect. + * "stable" creates a hashed MAC address based on connection.stable-id (or + * the connection's UUID) and a machine dependent key. + * + * If unspecified, the value can be overwritten via global defaults, see manual + * of NetworkManager.conf. If still unspecified, it defaults to "permanent". + * + * On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated + * "cloned-mac-address". **/ /* ---keyfile--- * property: cloned-mac-address @@ -1047,6 +1084,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- + * ---dbus--- + * property: cloned-mac-address + * format: byte array + * description: This D-Bus field is deprecated in favor of "assigned-mac-address" + * which is more flexible and allows specifying special variants like "random". + * ---end--- */ g_object_class_install_property (object_class, PROP_CLONED_MAC_ADDRESS, @@ -1055,10 +1098,28 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - G_VARIANT_TYPE_BYTESTRING, - _nm_utils_hwaddr_to_dbus, - _nm_utils_hwaddr_from_dbus); + _nm_setting_class_override_property (setting_class, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + G_VARIANT_TYPE_BYTESTRING, + _nm_utils_hwaddr_cloned_get, + _nm_utils_hwaddr_cloned_set, + _nm_utils_hwaddr_cloned_not_set); + + /* ---dbus--- + * property: assigned-mac-address + * format: string + * description: The new field for the cloned MAC address. It can be either + * a hardware address in ASCII representation, or one of the special values + * "preserve", "permanent", "random", "random" or "stable". + * This field replaces the deprecated "cloned-mac-address" on D-Bus, which + * can only contain explict hardware addresses. + * ---end--- + */ + _nm_setting_class_add_dbus_only_property (setting_class, + "assigned-mac-address", + G_VARIANT_TYPE_STRING, + _nm_utils_hwaddr_cloned_data_synth, + _nm_utils_hwaddr_cloned_data_set); /** * NMSettingWired:mac-address-blacklist: @@ -1183,7 +1244,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_S390_OPTIONS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_S390_OPTIONS, G_VARIANT_TYPE ("a{ss}"), _nm_utils_strdict_to_dbus, _nm_utils_strdict_from_dbus); diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c index 6074959349..78824887cd 100644 --- a/libnm-core/nm-setting-wireless.c +++ b/libnm-core/nm-setting-wireless.c @@ -22,11 +22,13 @@ #include "nm-default.h" +#include "nm-setting-wireless.h" + #include #include -#include "nm-setting-wireless.h" #include "nm-utils.h" +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -801,7 +803,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->cloned_mac_address && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { + if ( priv->cloned_mac_address + && !NM_CLONED_MAC_IS_SPECIAL (priv->cloned_mac_address) + && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -839,6 +843,25 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingClass *parent_class; + + if (nm_streq (prop_spec->name, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS)) { + return nm_streq0 (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address, + NM_SETTING_WIRELESS_GET_PRIVATE (other)->cloned_mac_address); + } + + parent_class = NM_SETTING_CLASS (nm_setting_wireless_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ + static GVariant * nm_setting_wireless_get_security (NMSetting *setting, NMConnection *connection, @@ -1023,18 +1046,19 @@ get_property (GObject *object, guint prop_id, } static void -nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) +nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class) { - GObjectClass *object_class = G_OBJECT_CLASS (setting_class); - NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + GObjectClass *object_class = G_OBJECT_CLASS (setting_wireless_class); + NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wireless_class); - g_type_class_add_private (setting_class, sizeof (NMSettingWirelessPrivate)); + g_type_class_add_private (setting_wireless_class, sizeof (NMSettingWirelessPrivate)); /* virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; - parent_class->verify = verify; + setting_class->verify = verify; + setting_class->compare_property = compare_property; /* Properties */ /** @@ -1154,7 +1178,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_BSSID, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_BSSID, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1231,7 +1255,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_MAC_ADDRESS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_MAC_ADDRESS, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1239,8 +1263,22 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) /** * NMSettingWireless:cloned-mac-address: * - * If specified, request that the Wi-Fi device use this MAC address instead - * of its permanent MAC address. This is known as MAC cloning or spoofing. + * If specified, request that the device use this MAC address instead of its + * permanent MAC address. This is known as MAC cloning or spoofing. + * + * Beside explicitly specifing a MAC address, the special values "preserve", "permanent", + * "random" and "stable" are supported. + * "preserve" means not to touch the MAC address on activation. + * "permanent" means to use the permanent hardware address of the device. + * "random" creates a random MAC address on each connect. + * "stable" creates a hashed MAC address based on connection.stable-id (or + * the connection's UUID) and a machine dependent key. + * + * If unspecified, the value can be overwritten via global defaults, see manual + * of NetworkManager.conf. If still unspecified, it defaults to "permanent". + * + * On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated + * "cloned-mac-address". **/ /* ---keyfile--- * property: cloned-mac-address @@ -1255,6 +1293,12 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- + * ---dbus--- + * property: cloned-mac-address + * format: byte array + * description: This D-Bus field is deprecated in favor of "assigned-mac-address" + * which is more flexible and allows specifying special variants like "random". + * ---end--- */ g_object_class_install_property (object_class, PROP_CLONED_MAC_ADDRESS, @@ -1262,10 +1306,28 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, - G_VARIANT_TYPE_BYTESTRING, - _nm_utils_hwaddr_to_dbus, - _nm_utils_hwaddr_from_dbus); + _nm_setting_class_override_property (setting_class, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + G_VARIANT_TYPE_BYTESTRING, + _nm_utils_hwaddr_cloned_get, + _nm_utils_hwaddr_cloned_set, + _nm_utils_hwaddr_cloned_not_set); + + /* ---dbus--- + * property: assigned-mac-address + * format: string + * description: The new field for the cloned MAC address. It can be either + * a hardware address in ASCII representation, or one of the special values + * "preserve", "permanent", "random", "random" or "stable". + * This field replaces the deprecated "cloned-mac-address" on D-Bus, which + * can only contain explict hardware addresses. + * ---end--- + */ + _nm_setting_class_add_dbus_only_property (setting_class, + "assigned-mac-address", + G_VARIANT_TYPE_STRING, + _nm_utils_hwaddr_cloned_data_synth, + _nm_utils_hwaddr_cloned_data_set); /** * NMSettingWireless:mac-address-blacklist: @@ -1428,7 +1490,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * NetworkManager daemons. * ---end--- */ - _nm_setting_class_add_dbus_only_property (parent_class, "security", + _nm_setting_class_add_dbus_only_property (setting_class, "security", G_VARIANT_TYPE_STRING, nm_setting_wireless_get_security, NULL); } diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 611c467d06..bd54756ac3 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -36,6 +36,29 @@ gboolean _nm_utils_team_config_equal (const char *conf1, const char *conf2, g /* D-Bus transform funcs */ +GVariant *_nm_utils_hwaddr_cloned_get (NMSetting *setting, + const char *property); +gboolean _nm_utils_hwaddr_cloned_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error); +gboolean _nm_utils_hwaddr_cloned_not_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + NMSettingParseFlags parse_flags, + GError **error); +GVariant * _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, + NMConnection *connection, + const char *property); +gboolean _nm_utils_hwaddr_cloned_data_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error); + GVariant * _nm_utils_hwaddr_to_dbus (const GValue *prop_value); void _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, GValue *prop_value); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 5c4bfb50b9..7567f6d88d 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -37,6 +37,7 @@ #include #endif +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" #include "crypto.h" @@ -3313,14 +3314,14 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, return !memcmp (hwaddr1, hwaddr2, hwaddr1_len); } -GVariant * -_nm_utils_hwaddr_to_dbus (const GValue *prop_value) +/*****************************************************************************/ + +static GVariant * +_nm_utils_hwaddr_to_dbus_impl (const char *str) { - const char *str; guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; int len; - str = g_value_get_string (prop_value); if (!str) return NULL; @@ -3334,6 +3335,103 @@ _nm_utils_hwaddr_to_dbus (const GValue *prop_value) return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, buf, len, 1); } +GVariant * +_nm_utils_hwaddr_cloned_get (NMSetting *setting, + const char *property) +{ + gs_free char *addr = NULL; + + nm_assert (nm_streq0 (property, "cloned-mac-address")); + + g_object_get (setting, "cloned-mac-address", &addr, NULL); + return _nm_utils_hwaddr_to_dbus_impl (addr); +} + +gboolean +_nm_utils_hwaddr_cloned_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + gsize length; + const guint8 *array; + char *str; + + nm_assert (nm_streq0 (property, "cloned-mac-address")); + + if (!_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address")) + return TRUE; + + length = 0; + array = g_variant_get_fixed_array (value, &length, 1); + + if (!length) + return TRUE; + + str = nm_utils_hwaddr_ntoa (array, length); + g_object_set (setting, + "cloned-mac-address", + str, + NULL); + g_free (str); + return TRUE; +} + +gboolean +_nm_utils_hwaddr_cloned_not_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + NMSettingParseFlags parse_flags, + GError **error) +{ + nm_assert (nm_streq0 (property, "cloned-mac-address")); + return TRUE; +} + +GVariant * +_nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, + NMConnection *connection, + const char *property) +{ + gs_free char *addr = NULL; + + nm_assert (nm_streq0 (property, "assigned-mac-address")); + + g_object_get (setting, + "cloned-mac-address", + &addr, + NULL); + return addr ? g_variant_new_string (addr) : NULL; +} + +gboolean +_nm_utils_hwaddr_cloned_data_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + nm_assert (nm_streq0 (property, "assigned-mac-address")); + + if (_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address")) + return TRUE; + + g_object_set (setting, + "cloned-mac-address", + g_variant_get_string (value, NULL), + NULL); + return TRUE; +} + +GVariant * +_nm_utils_hwaddr_to_dbus (const GValue *prop_value) +{ + return _nm_utils_hwaddr_to_dbus_impl (g_value_get_string (prop_value)); +} + void _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, GValue *prop_value) @@ -3346,6 +3444,8 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, g_value_take_string (prop_value, str); } +/*****************************************************************************/ + /** * nm_utils_bin2hexstr: * @src: (type guint8) (array length=len): an array of bytes diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 9d263dcb38..6e4be2cffc 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -2449,6 +2449,90 @@ test_setting_compare_routes (void) g_clear_pointer (&result, g_hash_table_unref); } +static void +test_setting_compare_wired_cloned_mac_address (void) +{ + gs_unref_object NMSetting *old = NULL, *new = NULL; + gboolean success; + gs_free char *str1 = NULL; + + old = nm_setting_wired_new (); + g_object_set (old, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "stable", + NULL); + + g_assert_cmpstr ("stable", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) old)); + g_object_get (old, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable", ==, str1); + g_clear_pointer (&str1, g_free); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "11:22:33:44:55:66", NULL); + + g_assert_cmpstr ("11:22:33:44:55:66", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) new)); + g_object_get (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("11:22:33:44:55:66", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "stable-bia", NULL); + + g_assert_cmpstr ("stable-bia", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) new)); + g_object_get (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable-bia", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); +} + +static void +test_setting_compare_wireless_cloned_mac_address (void) +{ + gs_unref_object NMSetting *old = NULL, *new = NULL; + gboolean success; + gs_free char *str1 = NULL; + + old = nm_setting_wireless_new (); + g_object_set (old, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "stable", + NULL); + + g_assert_cmpstr ("stable", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) old)); + g_object_get (old, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable", ==, str1); + g_clear_pointer (&str1, g_free); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "11:22:33:44:55:66", NULL); + + g_assert_cmpstr ("11:22:33:44:55:66", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) new)); + g_object_get (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("11:22:33:44:55:66", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "stable-bia", NULL); + + g_assert_cmpstr ("stable-bia", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) new)); + g_object_get (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable-bia", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); +} + static void test_setting_compare_timestamp (void) { @@ -5202,6 +5286,8 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_setting_compare_id", test_setting_compare_id); g_test_add_func ("/core/general/test_setting_compare_addresses", test_setting_compare_addresses); g_test_add_func ("/core/general/test_setting_compare_routes", test_setting_compare_routes); + g_test_add_func ("/core/general/test_setting_compare_wired_cloned_mac_address", test_setting_compare_wired_cloned_mac_address); + g_test_add_func ("/core/general/test_setting_compare_wirless_cloned_mac_address", test_setting_compare_wireless_cloned_mac_address); g_test_add_func ("/core/general/test_setting_compare_timestamp", test_setting_compare_timestamp); #define ADD_FUNC(name, func, secret_flags, comp_flags, remove_secret) \ g_test_add_data_func_full ("/core/general/" G_STRINGIFY (func) "_" name, \ diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index b70438ded3..e6050b8326 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -565,6 +565,10 @@ ipv6.ip6-privacy=0 connection.lldp + + ethernet.cloned-mac-address + If left unspecified, it defaults to "permanent". + ethernet.wake-on-lan @@ -592,6 +596,10 @@ ipv6.ip6-privacy=0 vpn.timeout If left unspecified, default value of 60 seconds is used. + + wifi.cloned-mac-address + If left unspecified, it defaults to "permanent". + wifi.mac-address-randomization If left unspecified, MAC address randomization is disabled. diff --git a/shared/nm-common-macros.h b/shared/nm-common-macros.h index 922117c3fc..3e5f349f46 100644 --- a/shared/nm-common-macros.h +++ b/shared/nm-common-macros.h @@ -38,6 +38,21 @@ #define NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS "org.freedesktop.NetworkManager.settings.modify.global-dns" #define NM_AUTH_PERMISSION_RELOAD "org.freedesktop.NetworkManager.reload" +#define NM_CLONED_MAC_PRESERVE "preserve" +#define NM_CLONED_MAC_PERMANENT "permanent" +#define NM_CLONED_MAC_RANDOM "random" +#define NM_CLONED_MAC_STABLE "stable" + +static inline gboolean +NM_CLONED_MAC_IS_SPECIAL (const char *str) +{ + return NM_IN_STRSET (str, + NM_CLONED_MAC_PRESERVE, + NM_CLONED_MAC_PERMANENT, + NM_CLONED_MAC_RANDOM, + NM_CLONED_MAC_STABLE); +} + /******************************************************************************/ #endif /* __NM_COMMON_MACROS_H__ */ diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 9894872b8d..7eb6cf1607 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -529,8 +529,8 @@ check_connection_mac_address (NMConnection *orig, static gboolean check_connection_cloned_mac_address (NMConnection *orig, - NMConnection *candidate, - GHashTable *settings) + NMConnection *candidate, + GHashTable *settings) { GHashTable *props; const char *orig_mac = NULL, *cand_mac = NULL; @@ -551,6 +551,12 @@ check_connection_cloned_mac_address (NMConnection *orig, if (s_wired_cand) cand_mac = nm_setting_wired_get_cloned_mac_address (s_wired_cand); + /* special cloned mac address entires are accepted. */ + if (NM_CLONED_MAC_IS_SPECIAL (orig_mac)) + orig_mac = NULL; + if (NM_CLONED_MAC_IS_SPECIAL (cand_mac)) + cand_mac = NULL; + if (!orig_mac || !cand_mac) { remove_from_hash (settings, props, NM_SETTING_WIRED_SETTING_NAME, diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 21a1d5cd2d..d26ef71d65 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -804,18 +804,13 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (dev, reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { - s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (dev, cloned_mac); + nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); /* If we're re-activating a PPPoE connection a short while after * a previous PPPoE connection was torn down, wait a bit to allow the diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index a238bdcf79..0fa83af949 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -502,8 +502,6 @@ update_connection (NMDevice *device, NMConnection *connection) static NMActStageReturn act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -512,11 +510,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (dev, cloned_mac); - + nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); return TRUE; } diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 4d4bd720ac..b641751205 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -57,6 +57,7 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar void nm_device_take_down (NMDevice *self, gboolean block); gboolean nm_device_hw_addr_set (NMDevice *device, const char *addr); +gboolean nm_device_hw_addr_set_cloned (NMDevice *device, NMConnection *connection, gboolean is_wifi); gboolean nm_device_hw_addr_reset (NMDevice *device); void nm_device_set_firmware_missing (NMDevice *self, gboolean missing); @@ -107,6 +108,8 @@ void nm_device_queue_recheck_available (NMDevice *device, void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config); void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config); +gboolean nm_device_hw_addr_is_explict (NMDevice *device); + void nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason); gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value); diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 238d3a97b5..2e8f832e16 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -291,8 +291,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { NMDeviceTun *self = NM_DEVICE_TUN (device); NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -305,10 +303,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) if (g_strcmp0 (priv->mode, "tap")) return NM_ACT_STAGE_RETURN_SUCCESS; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (device, cloned_mac); + nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE); return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 4cedcd078b..5e82f69b77 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -87,7 +87,6 @@ parent_hwaddr_maybe_changed (NMDevice *parent, { NMDeviceVlan *self = NM_DEVICE_VLAN (user_data); NMConnection *connection; - NMSettingWired *s_wired; const char *new_mac, *old_mac; NMSettingIPConfig *s_ip6; @@ -100,11 +99,8 @@ parent_hwaddr_maybe_changed (NMDevice *parent, return; /* Update the VLAN MAC only if configuration does not specify one */ - s_wired = nm_connection_get_setting_wired (connection); - if (s_wired) { - if (nm_setting_wired_get_cloned_mac_address (s_wired)) - return; - } + if (nm_device_hw_addr_is_explict (self)) + return; old_mac = nm_device_get_hw_address (self); new_mac = nm_device_get_hw_address (parent); @@ -554,8 +550,6 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (dev); NMSettingVlan *s_vlan; - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -564,10 +558,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (dev, cloned_mac); + nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); /* Change MAC address to parent's one if needed */ if (priv->parent) diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index 4bdada349d..40e5b5512a 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -511,8 +511,6 @@ update_connection (NMDevice *device, NMConnection *connection) static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -521,10 +519,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (device, cloned_mac); + nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE); return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index e9e1e88193..ddf7933185 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -209,6 +209,13 @@ typedef struct { NMIP4Config **configs; } ArpingData; +typedef enum { + HW_ADDR_TYPE_UNSET = 0, + HW_ADDR_TYPE_PERMANENT, + HW_ADDR_TYPE_EXPLICIT, + HW_ADDR_TYPE_GENERATED, +} HwAddrType; + typedef struct _NMDevicePrivate { bool in_state_changed; @@ -226,7 +233,12 @@ typedef struct _NMDevicePrivate { char * udi; char * iface; /* may change, could be renamed by user */ int ifindex; + + guint hw_addr_len; + guint8 /*HwAddrType*/ hw_addr_type; + bool real; + char * ip_iface; int ip_ifindex; NMDeviceType type; @@ -243,7 +255,6 @@ typedef struct _NMDevicePrivate { bool hw_addr_perm_fake:1; /* whether the permanent HW address could not be read and is a fake */ GHashTable * available_connections; char * hw_addr; - guint hw_addr_len; char * hw_addr_perm; char * hw_addr_initial; char * physical_port_id; @@ -2357,6 +2368,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) _notify (self, PROP_PHYSICAL_PORT_ID); } + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; g_clear_pointer (&priv->hw_addr_perm, g_free); _notify (self, PROP_PERM_HW_ADDRESS); g_clear_pointer (&priv->hw_addr_initial, g_free); @@ -11421,6 +11433,8 @@ nm_device_update_initial_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; + if ( priv->hw_addr && !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) { g_free (priv->hw_addr_initial); @@ -11479,6 +11493,50 @@ nm_device_update_permanent_hw_address (NMDevice *self) _notify (self, PROP_PERM_HW_ADDRESS); } +static const char * +_get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_addr) +{ + NMSetting *setting; + const char *addr = NULL; + + nm_assert (out_addr && !*out_addr); + + setting = nm_connection_get_setting (connection, + is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED); + if (setting) { + addr = is_wifi + ? nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) setting) + : nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) setting); + } + + if (!addr) { + gs_free char *a = NULL; + + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address", + self); + if ( !a + || ( !NM_CLONED_MAC_IS_SPECIAL (a) + && !nm_utils_hwaddr_valid (a, ETH_ALEN))) + addr = NM_CLONED_MAC_PERMANENT; + else + addr = *out_addr = g_steal_pointer (&a); + } + + return addr; +} + +gboolean +nm_device_hw_addr_is_explict (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + return !NM_IN_SET (priv->hw_addr_type, HW_ADDR_TYPE_PERMANENT, HW_ADDR_TYPE_UNSET); +} + static gboolean _hw_addr_set (NMDevice *self, const char *addr, @@ -11508,10 +11566,8 @@ _hw_addr_set (NMDevice *self, 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)) { - _LOGW (LOGD_DEVICE, "set-hw-addr: invalid MAC address %s", addr); - return FALSE; - } + || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) + g_return_val_if_reached (FALSE); _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s'...", addr); @@ -11535,7 +11591,9 @@ _hw_addr_set (NMDevice *self, _LOGW (LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s", detail, addr); } - nm_device_bring_up (self, TRUE, NULL); + + if (!nm_device_bring_up (self, TRUE, NULL)) + return FALSE; return success; } @@ -11549,12 +11607,73 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr) priv = NM_DEVICE_GET_PRIVATE (self); - if (!addr) { + if (!addr) + g_return_val_if_reached (FALSE); + + /* this is called by NMDeviceVlan to take the MAC address from the parent. + * In this case, it's like setting it to PERMANENT. */ + priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; + + return _hw_addr_set (self, addr, "set"); +} + +gboolean +nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi) +{ + NMDevicePrivate *priv; + gs_free char *hw_addr_tmp = NULL; + gs_free char *hw_addr_generated = NULL; + const char *addr; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!connection) + g_return_val_if_reached (FALSE); + + addr = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp); + + if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) + return TRUE; + + if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) { addr = nm_device_get_permanent_hw_address (self, TRUE); if (!addr) return FALSE; + priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; + } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_RANDOM)) { + hw_addr_generated = nm_utils_hw_addr_gen_random_eth (); + if (!hw_addr_generated) { + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "random"); + return FALSE; + } + priv->hw_addr_type = HW_ADDR_TYPE_GENERATED; + addr = hw_addr_generated; + } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_STABLE)) { + NMUtilsStableType stable_type; + const char *stable_id; + + stable_id = _get_stable_id (connection, &stable_type); + if (stable_id) { + hw_addr_generated = nm_utils_hw_addr_gen_stable_eth (stable_type, stable_id, + nm_device_get_ip_iface (self)); + } + if (!hw_addr_generated) { + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "stable"); + return FALSE; + } + priv->hw_addr_type = HW_ADDR_TYPE_GENERATED; + addr = hw_addr_generated; + } else { + /* this must be a valid address. Otherwise, we shouldn't come here. */ + if (_nm_utils_hwaddr_length (addr) <= 0) { + g_return_val_if_reached (FALSE); + } + priv->hw_addr_type = HW_ADDR_TYPE_EXPLICIT; } - return _hw_addr_set (self, addr, "set"); + + return _hw_addr_set (self, addr, "set-cloned"); } gboolean @@ -11567,9 +11686,17 @@ nm_device_hw_addr_reset (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->hw_addr_type == HW_ADDR_TYPE_UNSET) + return TRUE; + + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; addr = nm_device_get_initial_hw_address (self); - if (!addr) - return FALSE; + if (!addr) { + /* as hw_addr_type is not UNSET, we expect that we can get an + * initial address to which to reset. */ + g_return_val_if_reached (FALSE); + } + return _hw_addr_set (self, addr, "reset"); } diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index bb5fd30aba..8471a20730 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -2275,7 +2275,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) NMActRequest *req; NMConnection *connection; NMSettingWireless *s_wireless; - const char *cloned_mac; const char *mode; const char *ap_path; @@ -2316,8 +2315,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) } /* Set spoof MAC to the interface */ - cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); - nm_device_hw_addr_set (device, cloned_mac); + nm_device_hw_addr_set_cloned (device, connection, TRUE); /* AP mode never uses a specific object or existing scanned AP */ if (priv->mode != NM_802_11_MODE_AP) { diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index bb15ca387a..06989fa710 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -3173,6 +3173,100 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, secret_key, key_len, error); } +/*****************************************************************************/ + +static void +_hw_addr_eth_complete (guint8 *bin_addr) +{ + /* this LSB of the first octet cannot be set, + * it means Unicast vs. Multicast */ + bin_addr[0] &= ~1; + + /* the second LSB of the first octet means + * "globally unique, OUI enforced, BIA (burned-in-address)" + * vs. "locally-administered" */ + bin_addr[0] |= 2; +} + +char * +nm_utils_hw_addr_gen_random_eth (void) +{ + guint8 bin_addr[ETH_ALEN]; + + if (nm_utils_read_urandom (bin_addr, ETH_ALEN) < 0) + return NULL; + _hw_addr_eth_complete (bin_addr); + return nm_utils_hwaddr_ntoa (bin_addr, ETH_ALEN); +} + +static char * +_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, + const char *stable_id, + const guint8 *secret_key, + gsize key_len, + const char *ifname) +{ + GChecksum *sum; + guint32 tmp; + guint8 digest[32]; + gsize len = sizeof (digest); + guint8 bin_addr[ETH_ALEN]; + guint8 stable_type_uint8; + + nm_assert (stable_id); + nm_assert (NM_IN_SET (stable_type, + NM_UTILS_STABLE_TYPE_UUID, + NM_UTILS_STABLE_TYPE_STABLE_ID)); + nm_assert (secret_key); + + sum = g_checksum_new (G_CHECKSUM_SHA256); + if (!sum) + return NULL; + + key_len = MIN (key_len, G_MAXUINT32); + + stable_type_uint8 = stable_type; + g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8)); + + tmp = htonl ((guint32) key_len); + g_checksum_update (sum, (const guchar *) &tmp, sizeof (tmp)); + g_checksum_update (sum, (const guchar *) secret_key, key_len); + g_checksum_update (sum, (const guchar *) (ifname ?: ""), ifname ? (strlen (ifname) + 1) : 1); + g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); + + g_checksum_get_digest (sum, digest, &len); + g_checksum_free (sum); + + g_return_val_if_fail (len == 32, NULL); + + memcpy (bin_addr, digest, ETH_ALEN); + _hw_addr_eth_complete (bin_addr); + return nm_utils_hwaddr_ntoa (bin_addr, ETH_ALEN); +} + +char * +nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, + const char *stable_id, + const char *ifname) +{ + gs_free guint8 *secret_key = NULL; + gsize key_len = 0; + + g_return_val_if_fail (stable_id, NULL); + + secret_key = nm_utils_secret_key_read (&key_len, NULL); + if (!secret_key) + return NULL; + + return _hw_addr_gen_stable_eth (stable_type, + stable_id, + secret_key, + key_len, + ifname); +} + +/*****************************************************************************/ + /** * nm_utils_setpgid: * @unused: unused diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 86b4fe6ad2..082eabf4f6 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -371,6 +371,11 @@ gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type, guint dad_counter, GError **error); +char *nm_utils_hw_addr_gen_random_eth (void); +char *nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, + const char *stable_id, + const char *iname); + void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); void nm_utils_setpgid (gpointer unused); diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index 3d60668259..2a3fd9d63d 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -54,10 +54,45 @@ test_stable_privacy (void) inet_pton (AF_INET6, "1234::", &addr1); _set_stable_privacy (NM_UTILS_STABLE_TYPE_STABLE_ID, &addr1, "eth666", "stable-id-1", 0, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "1234::4944:67b0:7a6c:1cf"); - } -/*******************************************/ +/*****************************************************************************/ + +static void +_do_test_hw_addr (NMUtilsStableType stable_type, + const char *stable_id, + const guint8 *secret_key, + gsize key_len, + const char *ifname, + const char *expected) +{ + gs_free char *generated = NULL; + + g_assert (expected); + g_assert (nm_utils_hwaddr_valid (expected, ETH_ALEN)); + + generated = _hw_addr_gen_stable_eth (stable_type, + stable_id, + secret_key, + key_len, + ifname); + + g_assert (generated); + g_assert (nm_utils_hwaddr_valid (generated, ETH_ALEN)); + g_assert_cmpstr (generated, ==, expected); + g_assert (nm_utils_hwaddr_matches (generated, -1, expected, -1)); +} +#define do_test_hw_addr(stable_type, stable_id, secret_key, ifname, expected) \ + _do_test_hw_addr ((stable_type), (stable_id), (const guint8 *) ""secret_key"", NM_STRLEN (secret_key), (ifname), ""expected"") + +static void +test_hw_addr_gen_stable_eth (void) +{ + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "06:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_STABLE_ID, "stable-1", "key1", "eth0", "C6:AE:A9:9A:76:09"); +} + +/*****************************************************************************/ NMTST_DEFINE (); @@ -67,6 +102,7 @@ main (int argc, char **argv) nmtst_init_with_logging (&argc, &argv, NULL, "ALL"); g_test_add_func ("/utils/stable_privacy", test_stable_privacy); + g_test_add_func ("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth); return g_test_run (); }