From 96cabbcbb8f484f74fe69305b425b5109608e9d7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 22 Jun 2016 20:31:39 +0200 Subject: [PATCH] all: make MAC address randomization algorithm configurable For the per-connection settings "ethernet.cloned-mac-address" and "wifi.cloned-mac-address", and for the per-device setting "wifi.scan-rand-mac-address", we may generate MAC addresses using either the "random" or "stable" algorithm. Add new properties "generate-mac-address-mask" that allow to configure which bits of the MAC address will be scrambled. By default, the "random" and "stable" algorithms scamble all bits of the MAC address, including the OUI part and generate a locally- administered, unicast address. By specifying a MAC address mask, we can now configure to perserve parts of the current MAC address of the device. For example, setting "FF:FF:FF:00:00:00" will preserve the first 3 octects of the current MAC address. One can also explicitly specify a MAC address to use instead of the current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00" sets the OUI part of the MAC address to "68:F7:28" while scrambling the last 3 octects. Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble all bits of the MAC address, except clearing the second-least significant bit. Thus, creating a burned-in address, globally administered. One can also supply a list of MAC addresses like "FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which case a MAC address is choosen randomly. To fully scamble the MAC address one can configure "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00". which also randomly creates either a locally or globally administered address. With this, the following macchanger options can be implemented: `macchanger --random` This is the default if no mask is configured. -> "" while is the same as: -> "00:00:00:00:00:00" -> "02:00:00:00:00:00 02:00:00:00:00:00" `macchanger --random --bia` -> "02:00:00:00:00:00 00:00:00:00:00:00" `macchanger --ending` This option cannot be fully implemented, because macchanger uses the current MAC address but also implies --bia. -> "FF:FF:FF:00:00:00" This would yields the same result only if the current MAC address is already a burned-in address too. Otherwise, it has not the same effect as --ending. -> "FF:FF:FF:00:00:00 " Alternatively, instead of using the current MAC address, spell the OUI part out. But again, that is not really the same as macchanger does because you explictly have to name the OUI part to use. `machanger --another` `machanger --another_any` -> "FF:FF:FF:00:00:00 ..." "$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")" --- clients/cli/settings.c | 74 ++++++++++++++-------- libnm-core/nm-core-internal.h | 10 +++ libnm-core/nm-setting-wired.c | 88 ++++++++++++++++++++++++++ libnm-core/nm-setting-wired.h | 4 ++ libnm-core/nm-setting-wireless.c | 88 ++++++++++++++++++++++++++ libnm-core/nm-setting-wireless.h | 4 ++ libnm-core/nm-utils.c | 85 +++++++++++++++++++++++++ libnm-core/tests/test-general.c | 1 + libnm/libnm.ver | 2 + man/NetworkManager.conf.xml | 17 +++++ src/devices/nm-device.c | 36 ++++++++++- src/devices/wifi/nm-device-wifi.c | 10 ++- src/nm-core-utils.c | 83 ++++++++++++++++++------ src/nm-core-utils.h | 7 +- src/settings/plugins/ifcfg-rh/reader.c | 8 +++ src/settings/plugins/ifcfg-rh/writer.c | 12 ++++ src/tests/test-utils.c | 48 +++++++++++--- 17 files changed, 518 insertions(+), 59 deletions(-) diff --git a/clients/cli/settings.c b/clients/cli/settings.c index d3645ee031..98d0c8b19d 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -92,13 +92,14 @@ NmcOutputField nmc_fields_setting_wired[] = { SETTING_FIELD (NM_SETTING_WIRED_AUTO_NEGOTIATE), /* 4 */ SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS), /* 5 */ SETTING_FIELD (NM_SETTING_WIRED_CLONED_MAC_ADDRESS), /* 6 */ - SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST), /* 7 */ - SETTING_FIELD (NM_SETTING_WIRED_MTU), /* 8 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS), /* 9 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE), /* 10 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS), /* 11 */ - SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN), /* 12 */ - SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK), /* 7 */ + SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST), /* 8 */ + SETTING_FIELD (NM_SETTING_WIRED_MTU), /* 9 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD), /* 14 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRED_ALL "name"","\ @@ -108,6 +109,7 @@ NmcOutputField nmc_fields_setting_wired[] = { NM_SETTING_WIRED_AUTO_NEGOTIATE","\ NM_SETTING_WIRED_MAC_ADDRESS","\ NM_SETTING_WIRED_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK","\ NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST","\ NM_SETTING_WIRED_MTU","\ NM_SETTING_WIRED_S390_SUBCHANNELS","\ @@ -203,12 +205,13 @@ NmcOutputField nmc_fields_setting_wireless[] = { SETTING_FIELD (NM_SETTING_WIRELESS_TX_POWER), /* 7 */ SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS), /* 8 */ SETTING_FIELD (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS), /* 9 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST), /* 10 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION), /* 11 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MTU), /* 12 */ - SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS), /* 13 */ - SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN), /* 14 */ - SETTING_FIELD (NM_SETTING_WIRELESS_POWERSAVE), /* 15 */ + SETTING_FIELD (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MTU), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS), /* 14 */ + SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN), /* 15 */ + SETTING_FIELD (NM_SETTING_WIRELESS_POWERSAVE), /* 16 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRELESS_ALL "name"","\ @@ -221,6 +224,7 @@ NmcOutputField nmc_fields_setting_wireless[] = { NM_SETTING_WIRELESS_TX_POWER","\ NM_SETTING_WIRELESS_MAC_ADDRESS","\ NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK","\ NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST","\ NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION","\ NM_SETTING_WIRELESS_MTU","\ @@ -1765,6 +1769,7 @@ DEFINE_GETTER (nmc_property_wired_get_duplex, NM_SETTING_WIRED_DUPLEX) DEFINE_GETTER (nmc_property_wired_get_auto_negotiate, NM_SETTING_WIRED_AUTO_NEGOTIATE) DEFINE_GETTER (nmc_property_wired_get_mac_address, NM_SETTING_WIRED_MAC_ADDRESS) DEFINE_GETTER (nmc_property_wired_get_cloned_mac_address, NM_SETTING_WIRED_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wired_get_generate_mac_address_mask, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK) DEFINE_GETTER (nmc_property_wired_get_mac_address_blacklist, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST) DEFINE_GETTER (nmc_property_wired_get_s390_subchannels, NM_SETTING_WIRED_S390_SUBCHANNELS) DEFINE_GETTER (nmc_property_wired_get_s390_nettype, NM_SETTING_WIRED_S390_NETTYPE) @@ -1891,6 +1896,7 @@ DEFINE_GETTER (nmc_property_wireless_get_rate, NM_SETTING_WIRELESS_RATE) DEFINE_GETTER (nmc_property_wireless_get_tx_power, NM_SETTING_WIRELESS_TX_POWER) DEFINE_GETTER (nmc_property_wireless_get_mac_address, NM_SETTING_WIRELESS_MAC_ADDRESS) DEFINE_GETTER (nmc_property_wireless_get_cloned_mac_address, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wireless_get_generate_mac_address_mask, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK) DEFINE_GETTER (nmc_property_wireless_get_mac_address_blacklist, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST) DEFINE_GETTER (nmc_property_wireless_get_seen_bssids, NM_SETTING_WIRELESS_SEEN_BSSIDS) DEFINE_GETTER (nmc_property_wireless_get_hidden, NM_SETTING_WIRELESS_HIDDEN) @@ -7217,6 +7223,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (WIRED, GENERATE_MAC_ADDRESS_MASK), + nmc_property_wired_get_generate_mac_address_mask, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (WIRED, MAC_ADDRESS_BLACKLIST), nmc_property_wired_get_mac_address_blacklist, nmc_property_wired_set_mac_address_blacklist, @@ -7335,6 +7348,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, GENERATE_MAC_ADDRESS_MASK), + nmc_property_wireless_get_generate_mac_address_mask, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (WIRELESS, MAC_ADDRESS_BLACKLIST), nmc_property_wireless_get_mac_address_blacklist, nmc_property_wireless_set_mac_address_blacklist, @@ -8134,13 +8154,14 @@ setting_wired_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gb set_val_str (arr, 4, nmc_property_wired_get_auto_negotiate (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 5, nmc_property_wired_get_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 6, nmc_property_wired_get_cloned_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 7, nmc_property_wired_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 8, nmc_property_wired_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 9, nmc_property_wired_get_s390_subchannels (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_wired_get_s390_nettype (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_wired_get_s390_options (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_wired_get_wake_on_lan (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_wired_get_wake_on_lan_password (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 7, nmc_property_wired_get_generate_mac_address_mask (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 8, nmc_property_wired_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 9, nmc_property_wired_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_wired_get_s390_subchannels (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_wired_get_s390_nettype (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_wired_get_s390_options (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_wired_get_wake_on_lan (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_wired_get_wake_on_lan_password (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ @@ -8234,12 +8255,13 @@ setting_wireless_details (NMSetting *setting, NmCli *nmc, const char *one_prop, set_val_str (arr, 7, nmc_property_wireless_get_tx_power (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 8, nmc_property_wireless_get_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 9, nmc_property_wireless_get_cloned_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_wireless_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_wireless_get_mac_address_randomization (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_wireless_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_wireless_get_seen_bssids (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 14, nmc_property_wireless_get_hidden (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 15, nmc_property_wireless_get_powersave (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_wireless_get_generate_mac_address_mask (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_wireless_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_wireless_get_mac_address_randomization (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_wireless_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_wireless_get_seen_bssids (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 15, nmc_property_wireless_get_hidden (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 16, nmc_property_wireless_get_powersave (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 96624d05b8..956c910a87 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -283,6 +283,16 @@ void _nm_setting_vlan_get_priorities (NMSettingVlan *setting, /***********************************************************/ +struct ether_addr; + +gboolean _nm_utils_generate_mac_address_mask_parse (const char *value, + struct ether_addr *out_mask, + struct ether_addr **out_ouis, + gsize *out_ouis_len, + GError **error); + +/***********************************************************/ + typedef enum { NM_BOND_OPTION_TYPE_INT, NM_BOND_OPTION_TYPE_STRING, diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c index 3e72056afd..66ed9c48f0 100644 --- a/libnm-core/nm-setting-wired.c +++ b/libnm-core/nm-setting-wired.c @@ -53,6 +53,7 @@ typedef struct { gboolean auto_negotiate; char *device_mac_address; char *cloned_mac_address; + char *generate_mac_address_mask; GArray *mac_address_blacklist; guint32 mtu; char **s390_subchannels; @@ -70,6 +71,7 @@ enum { PROP_AUTO_NEGOTIATE, PROP_MAC_ADDRESS, PROP_CLONED_MAC_ADDRESS, + PROP_GENERATE_MAC_ADDRESS_MASK, PROP_MAC_ADDRESS_BLACKLIST, PROP_MTU, PROP_S390_SUBCHANNELS, @@ -188,6 +190,22 @@ nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting) return NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address; } +/** + * nm_setting_wired_get_generate_mac_address_mask: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:generate-mac-address-mask property of the setting + * + * Since: 1.4 + **/ +const char * +nm_setting_wired_get_generate_mac_address_mask (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->generate_mac_address_mask; +} + /** * nm_setting_wired_get_mac_address_blacklist: * @setting: the #NMSettingWired @@ -612,6 +630,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) GHashTableIter iter; const char *key, *value; int i; + GError *local = NULL; if (priv->port && !g_strv_contains (valid_ports, priv->port)) { g_set_error (error, @@ -704,6 +723,20 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + /* generate-mac-address-mask only makes sense with cloned-mac-address "random" or + * "stable". Still, let's not be so strict about that and accept the value + * even if it is unused. */ + if (!_nm_utils_generate_mac_address_mask_parse (priv->generate_mac_address_mask, + NULL, NULL, NULL, &local)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK); + g_error_free (local); + return FALSE; + } + if ( NM_FLAGS_ANY (priv->wol, NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS) && !nm_utils_is_power_of_two (priv->wol)) { g_set_error_literal (error, @@ -785,6 +818,7 @@ finalize (GObject *object) g_free (priv->device_mac_address); g_free (priv->cloned_mac_address); + g_free (priv->generate_mac_address_mask); g_array_unref (priv->mac_address_blacklist); if (priv->s390_subchannels) @@ -829,6 +863,10 @@ set_property (GObject *object, guint prop_id, priv->cloned_mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value), ETH_ALEN); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_free (priv->generate_mac_address_mask); + priv->generate_mac_address_mask = g_value_dup_string (value); + break; case PROP_MAC_ADDRESS_BLACKLIST: blacklist = g_value_get_boxed (value); g_array_set_size (priv->mac_address_blacklist, 0); @@ -894,6 +932,9 @@ get_property (GObject *object, guint prop_id, case PROP_CLONED_MAC_ADDRESS: g_value_set_string (value, nm_setting_wired_get_cloned_mac_address (setting)); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_value_set_string (value, nm_setting_wired_get_generate_mac_address_mask (setting)); + break; case PROP_MAC_ADDRESS_BLACKLIST: g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data); break; @@ -1121,6 +1162,53 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class) _nm_utils_hwaddr_cloned_data_synth, _nm_utils_hwaddr_cloned_data_set); + /** + * NMSettingWired:generate-mac-address-mask: + * + * With #NMSettingWired:cloned-mac-address setting "random" or "stable", + * by default all bits of the MAC address are scrambled and a locally-administered, + * unicast MAC address is created. This property allows to specify that certain bits + * are fixed. Note that the least significant bit of the first MAC address will + * always be unset to create a unicast MAC address. + * + * If the property is %NULL, it is eligible to be overwritten by a default + * connection setting. If the value is still %NULL or an empty string, the + * default is to create a locally-administered, unicast MAC address. + * + * If the value contains one MAC address, this address is used as mask. The set + * bits of the mask are to be filled with the current MAC address of the device, + * while the unset bits are subject to randomization. + * Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address + * and only randomize the lower 3 bytes using the "random" or "stable" algorithm. + * + * If the value contains one additional MAC address after the mask, + * this address is used instead of the current MAC address to fill the bits + * that shall not be randomized. For example, a value of + * "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address + * to 68:F7:28, while the lower bits are randomized. A value of + * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + * globally-administered, burned-in MAC address. + * + * If the value contains more then one additional MAC addresses, one of + * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" + * will create a fully scrambled MAC address, randomly locally or globally + * administered. + **/ + /* ---ifcfg-rh--- + * property: generate-mac-address-mask + * variable: GENERATE_MAC_ADDRESS_MASK + * description: the MAC address mask for generating randomized and stable + * cloned-mac-address. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_GENERATE_MAC_ADDRESS_MASK, + g_param_spec_string (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + /** * NMSettingWired:mac-address-blacklist: * diff --git a/libnm-core/nm-setting-wired.h b/libnm-core/nm-setting-wired.h index 6bc6678238..30b0b6c254 100644 --- a/libnm-core/nm-setting-wired.h +++ b/libnm-core/nm-setting-wired.h @@ -85,6 +85,7 @@ typedef enum { /*< flags >*/ #define NM_SETTING_WIRED_AUTO_NEGOTIATE "auto-negotiate" #define NM_SETTING_WIRED_MAC_ADDRESS "mac-address" #define NM_SETTING_WIRED_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask" #define NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" #define NM_SETTING_WIRED_MTU "mtu" #define NM_SETTING_WIRED_S390_SUBCHANNELS "s390-subchannels" @@ -117,6 +118,9 @@ gboolean nm_setting_wired_get_auto_negotiate (NMSettingWired *setting const char * nm_setting_wired_get_mac_address (NMSettingWired *setting); const char * nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting); +NM_AVAILABLE_IN_1_4 +const char * nm_setting_wired_get_generate_mac_address_mask (NMSettingWired *setting); + const char * const *nm_setting_wired_get_mac_address_blacklist (NMSettingWired *setting); guint32 nm_setting_wired_get_num_mac_blacklist_items (NMSettingWired *setting); const char * nm_setting_wired_get_mac_blacklist_item (NMSettingWired *setting, diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c index 15ba71b7f8..2aa29c9ee3 100644 --- a/libnm-core/nm-setting-wireless.c +++ b/libnm-core/nm-setting-wireless.c @@ -56,6 +56,7 @@ typedef struct { guint32 tx_power; char *device_mac_address; char *cloned_mac_address; + char *generate_mac_address_mask; GArray *mac_address_blacklist; guint32 mtu; GSList *seen_bssids; @@ -75,6 +76,7 @@ enum { PROP_TX_POWER, PROP_MAC_ADDRESS, PROP_CLONED_MAC_ADDRESS, + PROP_GENERATE_MAC_ADDRESS_MASK, PROP_MAC_ADDRESS_BLACKLIST, PROP_MTU, PROP_SEEN_BSSIDS, @@ -421,6 +423,22 @@ nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting) return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address; } +/** + * nm_setting_wireless_get_generate_mac_address_mask: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:generate-mac-address-mask property of the setting + * + * Since: 1.4 + **/ +const char * +nm_setting_wireless_get_generate_mac_address_mask (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->generate_mac_address_mask; +} + /** * nm_setting_wireless_get_mac_address_blacklist: * @setting: the #NMSettingWireless @@ -723,6 +741,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) GSList *iter; int i; gsize length; + GError *local = NULL; if (!priv->ssid) { g_set_error_literal (error, @@ -814,6 +833,20 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + /* generate-mac-address-mask only makes sense with cloned-mac-address "random" or + * "stable". Still, let's not be so strict about that and accept the value + * even if it is unused. */ + if (!_nm_utils_generate_mac_address_mask_parse (priv->generate_mac_address_mask, + NULL, NULL, NULL, &local)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK); + g_error_free (local); + return FALSE; + } + for (i = 0; i < priv->mac_address_blacklist->len; i++) { const char *mac = g_array_index (priv->mac_address_blacklist, const char *, i); @@ -935,6 +968,7 @@ finalize (GObject *object) g_free (priv->bssid); g_free (priv->device_mac_address); g_free (priv->cloned_mac_address); + g_free (priv->generate_mac_address_mask); g_array_unref (priv->mac_address_blacklist); g_slist_free_full (priv->seen_bssids, g_free); @@ -987,6 +1021,10 @@ set_property (GObject *object, guint prop_id, priv->cloned_mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value), ETH_ALEN); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_free (priv->generate_mac_address_mask); + priv->generate_mac_address_mask = g_value_dup_string (value); + break; case PROP_MAC_ADDRESS_BLACKLIST: blacklist = g_value_get_boxed (value); g_array_set_size (priv->mac_address_blacklist, 0); @@ -1054,6 +1092,9 @@ get_property (GObject *object, guint prop_id, case PROP_CLONED_MAC_ADDRESS: g_value_set_string (value, nm_setting_wireless_get_cloned_mac_address (setting)); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_value_set_string (value, nm_setting_wireless_get_generate_mac_address_mask (setting)); + break; case PROP_MAC_ADDRESS_BLACKLIST: g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data); break; @@ -1362,6 +1403,53 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class) _nm_utils_hwaddr_cloned_data_synth, _nm_utils_hwaddr_cloned_data_set); + /** + * NMSettingWireless:generate-mac-address-mask: + * + * With #NMSettingWireless:cloned-mac-address setting "random" or "stable", + * by default all bits of the MAC address are scrambled and a locally-administered, + * unicast MAC address is created. This property allows to specify that certain bits + * are fixed. Note that the least significant bit of the first MAC address will + * always be unset to create a unicast MAC address. + * + * If the property is %NULL, it is eligible to be overwritten by a default + * connection setting. If the value is still %NULL or an empty string, the + * default is to create a locally-administered, unicast MAC address. + * + * If the value contains one MAC address, this address is used as mask. The set + * bits of the mask are to be filled with the current MAC address of the device, + * while the unset bits are subject to randomization. + * Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address + * and only randomize the lower 3 bytes using the "random" or "stable" algorithm. + * + * If the value contains one additional MAC address after the mask, + * this address is used instead of the current MAC address to fill the bits + * that shall not be randomized. For example, a value of + * "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address + * to 68:F7:28, while the lower bits are randomized. A value of + * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + * globally-administered, burned-in MAC address. + * + * If the value contains more then one additional MAC addresses, one of + * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" + * will create a fully scrambled MAC address, randomly locally or globally + * administered. + **/ + /* ---ifcfg-rh--- + * property: generate-mac-address-mask + * variable: GENERATE_MAC_ADDRESS_MASK + * description: the MAC address mask for generating randomized and stable + * cloned-mac-address. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_GENERATE_MAC_ADDRESS_MASK, + g_param_spec_string (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + /** * NMSettingWireless:mac-address-blacklist: * diff --git a/libnm-core/nm-setting-wireless.h b/libnm-core/nm-setting-wireless.h index 574cee54f2..35fa79c179 100644 --- a/libnm-core/nm-setting-wireless.h +++ b/libnm-core/nm-setting-wireless.h @@ -50,6 +50,7 @@ G_BEGIN_DECLS #define NM_SETTING_WIRELESS_TX_POWER "tx-power" #define NM_SETTING_WIRELESS_MAC_ADDRESS "mac-address" #define NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask" #define NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" #define NM_SETTING_WIRELESS_MTU "mtu" #define NM_SETTING_WIRELESS_SEEN_BSSIDS "seen-bssids" @@ -126,6 +127,9 @@ guint32 nm_setting_wireless_get_tx_power (NMSettingWireless const char *nm_setting_wireless_get_mac_address (NMSettingWireless *setting); const char *nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting); +NM_AVAILABLE_IN_1_4 +const char *nm_setting_wireless_get_generate_mac_address_mask (NMSettingWireless *setting); + const char * const *nm_setting_wireless_get_mac_address_blacklist (NMSettingWireless *setting); guint32 nm_setting_wireless_get_num_mac_blacklist_items (NMSettingWireless *setting); const char * nm_setting_wireless_get_mac_blacklist_item (NMSettingWireless *setting, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 7567f6d88d..50575b30c9 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -3446,6 +3446,91 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, /*****************************************************************************/ +static char * +_split_word (char *s) +{ + /* takes @s and truncates the string on the first white-space. + * then it returns the first word afterwards (again seeking + * over leading white-space). */ + for (; s[0]; s++) { + if (g_ascii_isspace (s[0])) { + s[0] = '\0'; + s++; + while (g_ascii_isspace (s[0])) + s++; + return s; + } + } + return s; +} + +gboolean +_nm_utils_generate_mac_address_mask_parse (const char *value, + struct ether_addr *out_mask, + struct ether_addr **out_ouis, + gsize *out_ouis_len, + GError **error) +{ + gs_free char *s_free = NULL; + char *s, *s_next; + struct ether_addr mask; + gs_unref_array GArray *ouis = NULL; + + g_return_val_if_fail (!error || !*error, FALSE); + + if (!value || !*value) { + /* NULL and "" are valid values and both mean the default + * "q */ + if (out_mask) { + memset (out_mask, 0, sizeof (*out_mask)); + out_mask->ether_addr_octet[0] |= 0x02; + } + NM_SET_OUT (out_ouis, NULL); + NM_SET_OUT (out_ouis_len, 0); + return TRUE; + } + + s_free = g_strdup (value); + s = s_free; + + /* skip over leading whitespace */ + while (g_ascii_isspace (s[0])) + s++; + + /* parse the first mask */ + s_next = _split_word (s); + if (!nm_utils_hwaddr_aton (s, &mask, ETH_ALEN)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("not a valid ethernet MAC address for mask at position %lld"), + (long long) (s - s_free)); + return FALSE; + } + + if (s_next[0]) { + ouis = g_array_sized_new (FALSE, FALSE, sizeof (struct ether_addr), 4); + + do { + s = s_next; + s_next = _split_word (s); + + g_array_set_size (ouis, ouis->len + 1); + if (!nm_utils_hwaddr_aton (s, &g_array_index (ouis, struct ether_addr, ouis->len - 1), ETH_ALEN)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("not a valid ethernet MAC address #%u at position %lld"), + ouis->len, (long long) (s - s_free)); + return FALSE; + } + } while (s_next[0]); + } + + NM_SET_OUT (out_mask, mask); + NM_SET_OUT (out_ouis_len, ouis ? ouis->len : 0); + NM_SET_OUT (out_ouis, ouis ? ((struct ether_addr *) g_array_free (g_steal_pointer (&ouis), FALSE)) : NULL); + return TRUE; +} + +/*****************************************************************************/ + /** * 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 6e4be2cffc..aef9f2b13d 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1941,6 +1941,7 @@ test_connection_diff_a_only (void) { NM_SETTING_WIRED_AUTO_NEGOTIATE, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_CLONED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MTU, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_S390_SUBCHANNELS, NM_SETTING_DIFF_RESULT_IN_A }, diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 47f6598ff6..cc2fc7df62 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1069,6 +1069,8 @@ global: nm_setting_connection_get_stable_id; nm_setting_ip6_config_get_token; nm_setting_ip_config_get_dns_priority; + nm_setting_wired_get_generate_mac_address_mask; + nm_setting_wireless_get_generate_mac_address_mask; nm_vpn_editor_plugin_get_plugin_info; nm_vpn_editor_plugin_get_vt; nm_vpn_editor_plugin_load; diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 344aa9585c..220a12c6a7 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -569,6 +569,9 @@ ipv6.ip6-privacy=0 ethernet.cloned-mac-address If left unspecified, it defaults to "permanent". + + ethernet.generate-mac-address-mask + ethernet.wake-on-lan @@ -600,6 +603,9 @@ ipv6.ip6-privacy=0 wifi.cloned-mac-address If left unspecified, it defaults to "permanent". + + wifi.generate-mac-address-mask + wifi.mac-address-randomization If left unspecified, MAC address randomization is disabled. @@ -751,6 +757,17 @@ unmanaged=1 + + wifi.scan-generate-mac-address-mask + + + Like the per-connection settings ethernet.generate-mac-address-mask + and wifi.generate-mac-address-mask, this allows to configure the + generated MAC addresses during scanning. See manual of nm-settings + for details. + + + diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d1b0e56357..4bd8c25ad6 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11589,6 +11589,34 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool return addr; } +static const char * +_get_generate_mac_address_mask_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_value) +{ + NMSetting *setting; + const char *value = NULL; + char *a; + + nm_assert (out_value && !*out_value); + + setting = nm_connection_get_setting (connection, + is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED); + if (setting) { + value = is_wifi + ? nm_setting_wireless_get_generate_mac_address_mask ((NMSettingWireless *) setting) + : nm_setting_wired_get_generate_mac_address_mask ((NMSettingWired *) setting); + if (value) + return value; + } + + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-mac-address-mask", + self); + if (!a) + return NULL; + *out_value = a; + return a; +} + gboolean nm_device_hw_addr_is_explict (NMDevice *self) { @@ -11695,6 +11723,7 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean NMDevicePrivate *priv; gs_free char *hw_addr_tmp = NULL; gs_free char *hw_addr_generated = NULL; + gs_free char *generate_mac_address_mask_tmp = NULL; const char *addr, *addr_setting; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -11717,7 +11746,8 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean 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 (); + hw_addr_generated = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (self), + _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp)); if (!hw_addr_generated) { _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "random"); return FALSE; @@ -11731,7 +11761,9 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean 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)); + nm_device_get_ip_iface (self), + nm_device_get_initial_hw_address (self), + _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp)); } if (!hw_addr_generated) { _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "stable"); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 3db69cd890..5ddf5d84b7 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -1064,6 +1064,8 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) if ( !priv->hw_addr_scan || now >= priv->hw_addr_scan_expire) { + gs_free char *generate_mac_address_mask = NULL; + /* the random MAC address for scanning expires after a while. * * We don't bother with to update the MAC address exactly when @@ -1071,8 +1073,14 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) * a new one.*/ priv->hw_addr_scan_expire = now + (SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN * 60); + generate_mac_address_mask = nm_config_data_get_device_config (NM_CONFIG_GET_DATA, + "wifi.scan-generate-mac-address-mask", + device, + NULL); + g_free (priv->hw_addr_scan); - priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (); + priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (device), + generate_mac_address_mask); } nm_device_hw_addr_set (device, priv->hw_addr_scan, "scanning"); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 06989fa710..6dfbb4ce10 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -3176,27 +3176,68 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, /*****************************************************************************/ static void -_hw_addr_eth_complete (guint8 *bin_addr) +_hw_addr_eth_complete (struct ether_addr *addr, + const char *current_mac_address, + const char *generate_mac_address_mask) { - /* this LSB of the first octet cannot be set, - * it means Unicast vs. Multicast */ - bin_addr[0] &= ~1; + struct ether_addr mask; + struct ether_addr oui; + struct ether_addr *ouis; + gsize ouis_len; + guint i; /* the second LSB of the first octet means * "globally unique, OUI enforced, BIA (burned-in-address)" - * vs. "locally-administered" */ - bin_addr[0] |= 2; + * vs. "locally-administered". By default, set it to + * generate locally-administered addresses. + * + * Maybe be overwritten by a mask below. */ + addr->ether_addr_octet[0] |= 2; + + if (!generate_mac_address_mask || !*generate_mac_address_mask) + goto out; + if (!_nm_utils_generate_mac_address_mask_parse (generate_mac_address_mask, + &mask, + &ouis, + &ouis_len, + NULL)) + goto out; + + nm_assert ((ouis == NULL) ^ (ouis_len != 0)); + if (ouis) { + /* g_random_int() is good enough here. It uses a static GRand instance + * that is seeded from /dev/urandom. */ + oui = ouis[g_random_int () % ouis_len]; + g_free (ouis); + } else { + if (!nm_utils_hwaddr_aton (current_mac_address, &oui, ETH_ALEN)) + goto out; + } + + for (i = 0; i < ETH_ALEN; i++) { + const guint8 a = addr->ether_addr_octet[i]; + const guint8 o = oui.ether_addr_octet[i]; + const guint8 m = mask.ether_addr_octet[i]; + + addr->ether_addr_octet[i] = (a & ~m) | (o & m); + } + +out: + /* The LSB of the first octet must always be cleared, + * it means Unicast vs. Multicast */ + addr->ether_addr_octet[0] &= ~1; } char * -nm_utils_hw_addr_gen_random_eth (void) +nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, + const char *generate_mac_address_mask) { - guint8 bin_addr[ETH_ALEN]; + struct ether_addr bin_addr; - if (nm_utils_read_urandom (bin_addr, ETH_ALEN) < 0) + 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); + _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); + return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN); } static char * @@ -3204,13 +3245,15 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *stable_id, const guint8 *secret_key, gsize key_len, - const char *ifname) + const char *ifname, + const char *current_mac_address, + const char *generate_mac_address_mask) { GChecksum *sum; guint32 tmp; guint8 digest[32]; gsize len = sizeof (digest); - guint8 bin_addr[ETH_ALEN]; + struct ether_addr bin_addr; guint8 stable_type_uint8; nm_assert (stable_id); @@ -3239,15 +3282,17 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, 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); + memcpy (&bin_addr, digest, ETH_ALEN); + _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); + 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) + const char *ifname, + const char *current_mac_address, + const char *generate_mac_address_mask) { gs_free guint8 *secret_key = NULL; gsize key_len = 0; @@ -3262,7 +3307,9 @@ nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, stable_id, secret_key, key_len, - ifname); + ifname, + current_mac_address, + generate_mac_address_mask); } /*****************************************************************************/ diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 082eabf4f6..bfba35f575 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -371,10 +371,13 @@ 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_random_eth (const char *current_mac_address, + const char *generate_mac_address_mask); char *nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *stable_id, - const char *iname); + const char *ifname, + const char *current_mac_address, + const char *generate_mac_address_mask); void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 192327043b..af5d291026 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -3368,6 +3368,10 @@ make_wireless_setting (shvarFile *ifcfg, g_free (value); } + value = svGetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", FALSE); + g_object_set (s_wireless, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, value, NULL); + g_free (value); + value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE); if (value) { char **strv; @@ -3882,6 +3886,10 @@ make_wired_setting (shvarFile *ifcfg, g_free (value); } + value = svGetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", FALSE); + g_object_set (s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, value, NULL); + g_free (value); + value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE); if (value) { char **strv; diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 1683a0e33c..3682640077 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -853,6 +853,10 @@ write_wireless_setting (NMConnection *connection, cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE); + svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wireless_get_generate_mac_address_mask (s_wireless), + FALSE); + svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE); macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); if (macaddr_blacklist[0]) { @@ -1111,6 +1115,10 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE); + svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wired_get_generate_mac_address_mask (s_wired), + FALSE); + svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE); macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired); if (macaddr_blacklist[0]) { @@ -1260,6 +1268,10 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE); + svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wired_get_generate_mac_address_mask (s_wired), + FALSE); + mtu = nm_setting_wired_get_mtu (s_wired); if (mtu) { tmp = g_strdup_printf ("%u", mtu); diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index 2a3fd9d63d..000a9d1ef4 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -64,32 +64,60 @@ _do_test_hw_addr (NMUtilsStableType stable_type, const guint8 *secret_key, gsize key_len, const char *ifname, - const char *expected) + const char *current_mac_address, + const char *generate_mac_address_mask, + const char **expected) { gs_free char *generated = NULL; + const char **e; + gboolean found = FALSE; - g_assert (expected); - g_assert (nm_utils_hwaddr_valid (expected, ETH_ALEN)); + for (e = expected; *e; e++) { + g_assert (*e); + g_assert (nm_utils_hwaddr_valid (*e, ETH_ALEN)); + } generated = _hw_addr_gen_stable_eth (stable_type, stable_id, secret_key, key_len, - ifname); + ifname, + current_mac_address, + generate_mac_address_mask); 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)); + for (e = expected; *e; e++) { + if (!nm_utils_hwaddr_matches (generated, -1, *e, -1)) + continue; + g_assert (!found); + found = TRUE; + g_assert_cmpstr (generated, ==, *e); + } + g_assert (found); } -#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"") +#define do_test_hw_addr(stable_type, stable_id, secret_key, ifname, current_mac_address, generate_mac_address_mask, ...) \ + _do_test_hw_addr ((stable_type), (stable_id), (const guint8 *) ""secret_key"", NM_STRLEN (secret_key), (ifname), ""current_mac_address"", generate_mac_address_mask, (const char *[]) { __VA_ARGS__, NULL }) 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"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", NULL, "06:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_STABLE_ID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", NULL, "C6:AE:A9:9A:76:09"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "FF:FF:FF:00:00:00", "00:23:45:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "03:23:45:67:89:ab", "FF:FF:FF:00:00:00", "02:23:45:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "00:00:00:00:00:00", "06:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00", "04:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00", "04:0D:CD:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 00:00:00:00:00:00", "04:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 02:00:00:00:00:00", "06:0D:CD:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "00:00:00:00:00:00 E9:60:CE:F5:ED:2F", "06:0D:CD:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00", "06:0D:CD:0C:9E:2C", "04:0D:CD:0C:9E:2C"); } /*****************************************************************************/