diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 08a5d087e1..98d0c8b19d 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" @@ -44,26 +45,28 @@ NmcOutputField nmc_fields_setting_connection[] = { SETTING_FIELD ("name"), /* 0 */ SETTING_FIELD (NM_SETTING_CONNECTION_ID), /* 1 */ SETTING_FIELD (NM_SETTING_CONNECTION_UUID), /* 2 */ - SETTING_FIELD (NM_SETTING_CONNECTION_INTERFACE_NAME), /* 3 */ - SETTING_FIELD (NM_SETTING_CONNECTION_TYPE), /* 4 */ - SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT), /* 5 */ - SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY), /* 6 */ - SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP), /* 7 */ - SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY), /* 8 */ - SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS), /* 9 */ - SETTING_FIELD (NM_SETTING_CONNECTION_ZONE), /* 10 */ - SETTING_FIELD (NM_SETTING_CONNECTION_MASTER), /* 11 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE), /* 12 */ - SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES), /* 13 */ - SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES), /* 14 */ - SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT), /* 15 */ - SETTING_FIELD (NM_SETTING_CONNECTION_METERED), /* 16 */ - SETTING_FIELD (NM_SETTING_CONNECTION_LLDP), /* 17 */ + SETTING_FIELD (NM_SETTING_CONNECTION_STABLE_ID), /* 3 */ + SETTING_FIELD (NM_SETTING_CONNECTION_INTERFACE_NAME), /* 4 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TYPE), /* 5 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT), /* 6 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY), /* 7 */ + SETTING_FIELD (NM_SETTING_CONNECTION_TIMESTAMP), /* 8 */ + SETTING_FIELD (NM_SETTING_CONNECTION_READ_ONLY), /* 9 */ + SETTING_FIELD (NM_SETTING_CONNECTION_PERMISSIONS), /* 10 */ + SETTING_FIELD (NM_SETTING_CONNECTION_ZONE), /* 11 */ + SETTING_FIELD (NM_SETTING_CONNECTION_MASTER), /* 12 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE), /* 13 */ + SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES), /* 14 */ + SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES), /* 15 */ + SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT), /* 16 */ + SETTING_FIELD (NM_SETTING_CONNECTION_METERED), /* 17 */ + SETTING_FIELD (NM_SETTING_CONNECTION_LLDP), /* 18 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ NM_SETTING_CONNECTION_ID","\ NM_SETTING_CONNECTION_UUID","\ + NM_SETTING_CONNECTION_STABLE_ID","\ NM_SETTING_CONNECTION_INTERFACE_NAME","\ NM_SETTING_CONNECTION_TYPE","\ NM_SETTING_CONNECTION_AUTOCONNECT","\ @@ -89,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"","\ @@ -105,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","\ @@ -200,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"","\ @@ -218,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","\ @@ -1252,6 +1259,7 @@ DEFINE_SECRET_FLAGS_GETTER (nmc_property_cdma_get_password_flags, NM_SETTING_CDM /* --- NM_SETTING_CONNECTION_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_connection_get_id, NM_SETTING_CONNECTION_ID) DEFINE_GETTER (nmc_property_connection_get_uuid, NM_SETTING_CONNECTION_UUID) +DEFINE_GETTER (nmc_property_connection_get_stable_id, NM_SETTING_CONNECTION_STABLE_ID) DEFINE_GETTER (nmc_property_connection_get_interface_name, NM_SETTING_CONNECTION_INTERFACE_NAME) DEFINE_GETTER (nmc_property_connection_get_type, NM_SETTING_CONNECTION_TYPE) DEFINE_GETTER (nmc_property_connection_get_autoconnect, NM_SETTING_CONNECTION_AUTOCONNECT) @@ -1761,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) @@ -1887,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) @@ -2886,11 +2896,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; } @@ -2899,6 +2910,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) { @@ -6217,6 +6240,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, STABLE_ID), + nmc_property_connection_get_stable_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (CONNECTION, INTERFACE_NAME), nmc_property_connection_get_interface_name, nmc_property_set_ifname, @@ -7188,7 +7218,14 @@ 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, + 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, @@ -7306,7 +7343,14 @@ 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, + 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, @@ -8063,21 +8107,22 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_pro set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); set_val_str (arr, 1, nmc_property_connection_get_id (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 2, nmc_property_connection_get_uuid (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 3, nmc_property_connection_get_interface_name (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 4, nmc_property_connection_get_type (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 5, nmc_property_connection_get_autoconnect (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 6, nmc_property_connection_get_autoconnect_priority (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 7, nmc_property_connection_get_timestamp (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 8, nmc_property_connection_get_read_only (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 9, nmc_property_connection_get_permissions (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_connection_get_zone (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_connection_get_master (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_connection_get_slave_type (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_connection_get_autoconnect_slaves (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 14, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 15, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 16, nmc_property_connection_get_metered (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 17, nmc_property_connection_get_lldp (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 3, nmc_property_connection_get_stable_id (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 4, nmc_property_connection_get_interface_name (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 5, nmc_property_connection_get_type (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 6, nmc_property_connection_get_autoconnect (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 7, nmc_property_connection_get_autoconnect_priority (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 8, nmc_property_connection_get_timestamp (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 9, nmc_property_connection_get_read_only (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_connection_get_permissions (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_connection_get_zone (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_connection_get_master (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_connection_get_slave_type (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_connection_get_autoconnect_slaves (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 15, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 16, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 17, nmc_property_connection_get_metered (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 18, nmc_property_connection_get_lldp (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ @@ -8109,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 */ @@ -8209,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/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-connection.c b/libnm-core/nm-connection.c index a823af9bea..7695518115 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -847,6 +847,52 @@ _normalize_bond_mode (NMConnection *self, GHashTable *parameters) return FALSE; } +static gboolean +_normalize_wireless_mac_address_randomization (NMConnection *self, GHashTable *parameters) +{ + NMSettingWireless *s_wifi = nm_connection_get_setting_wireless (self); + const char *cloned_mac_address; + NMSettingMacRandomization mac_address_randomization; + + if (!s_wifi) + return FALSE; + + mac_address_randomization = nm_setting_wireless_get_mac_address_randomization (s_wifi); + if (!NM_IN_SET (mac_address_randomization, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT, + NM_SETTING_MAC_RANDOMIZATION_NEVER, + NM_SETTING_MAC_RANDOMIZATION_ALWAYS)) + return FALSE; + + cloned_mac_address = nm_setting_wireless_get_cloned_mac_address (s_wifi); + if (cloned_mac_address) { + if (nm_streq (cloned_mac_address, "random")) { + if (mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_ALWAYS) + return FALSE; + mac_address_randomization = NM_SETTING_MAC_RANDOMIZATION_ALWAYS; + } else if (nm_streq (cloned_mac_address, "permanent")) { + if (mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_NEVER) + return FALSE; + mac_address_randomization = NM_SETTING_MAC_RANDOMIZATION_NEVER; + } else { + if (mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) + return FALSE; + mac_address_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT; + } + g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, mac_address_randomization, NULL); + return TRUE; + } + if (mac_address_randomization != NM_SETTING_MAC_RANDOMIZATION_DEFAULT) { + g_object_set (s_wifi, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_ALWAYS + ? "random" : "permanent", + NULL); + return TRUE; + } + return FALSE; +} + /** * nm_connection_verify: * @connection: the #NMConnection to verify @@ -1089,6 +1135,7 @@ nm_connection_normalize (NMConnection *connection, was_modified |= _normalize_ip_config (connection, parameters); was_modified |= _normalize_infiniband_mtu (connection, parameters); was_modified |= _normalize_bond_mode (connection, parameters); + was_modified |= _normalize_wireless_mac_address_randomization (connection, parameters); /* Verify anew. */ success = _nm_connection_verify (connection, error); diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 7ae126c1bc..956c910a87 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -124,6 +124,8 @@ guint32 _nm_setting_get_setting_priority (NMSetting *setting); gboolean _nm_setting_get_property (NMSetting *setting, const char *name, GValue *value); +guint _nm_utils_hwaddr_length (const char *asc); + GSList * _nm_utils_hash_values_to_slist (GHashTable *hash); GHashTable *_nm_utils_copy_strdict (GHashTable *strdict); @@ -281,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-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 e1cd913aad..f6b801c6bd 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -63,6 +63,7 @@ typedef struct { typedef struct { char *id; char *uuid; + char *stable_id; char *interface_name; char *type; char *master; @@ -99,6 +100,7 @@ enum { PROP_GATEWAY_PING_TIMEOUT, PROP_METERED, PROP_LLDP, + PROP_STABLE_ID, LAST_PROP }; @@ -230,6 +232,24 @@ nm_setting_connection_get_uuid (NMSettingConnection *setting) return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->uuid; } +/** + * nm_setting_connection_get_stable_id: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:stable_id property of the connection. + * + * Returns: the stable-id for the connection + * + * Since: 1.4 + **/ +const char * +nm_setting_connection_get_stable_id (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->stable_id; +} + /** * nm_setting_connection_get_interface_name: * @setting: the #NMSettingConnection @@ -1128,6 +1148,7 @@ finalize (GObject *object) g_free (priv->id); g_free (priv->uuid); + g_free (priv->stable_id); g_free (priv->interface_name); g_free (priv->type); g_free (priv->zone); @@ -1174,6 +1195,10 @@ set_property (GObject *object, guint prop_id, g_free (priv->uuid); priv->uuid = g_value_dup_string (value); break; + case PROP_STABLE_ID: + g_free (priv->stable_id); + priv->stable_id = g_value_dup_string (value); + break; case PROP_INTERFACE_NAME: g_free (priv->interface_name); priv->interface_name = g_value_dup_string (value); @@ -1260,6 +1285,9 @@ get_property (GObject *object, guint prop_id, case PROP_UUID: g_value_set_string (value, nm_setting_connection_get_uuid (setting)); break; + case PROP_STABLE_ID: + g_value_set_string (value, nm_setting_connection_get_stable_id (setting)); + break; case PROP_INTERFACE_NAME: g_value_set_string (value, nm_setting_connection_get_interface_name (setting)); break; @@ -1368,7 +1396,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) * property: uuid * variable: UUID(+) * description: UUID for the connection profile. When missing, NetworkManager - * creates the UUID itself (by hashing the file). + * creates the UUID itself (by hashing the filename). * ---end--- */ g_object_class_install_property @@ -1379,6 +1407,33 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS)); + /** + * NMSettingConnection:stable-id: + * + * This token to generate stable IDs for the connection. If unset, + * the UUID will be used instead. + * + * 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 + **/ + /* ---ifcfg-rh--- + * property: stable-id + * variable: STABLE_ID(+) + * description: Token to generate stable IDs. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_STABLE_ID, + g_param_spec_string (NM_SETTING_CONNECTION_STABLE_ID, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + /** * NMSettingConnection:interface-name: * diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h index 0d4966f05a..67263d1f01 100644 --- a/libnm-core/nm-setting-connection.h +++ b/libnm-core/nm-setting-connection.h @@ -46,6 +46,7 @@ G_BEGIN_DECLS #define NM_SETTING_CONNECTION_ID "id" #define NM_SETTING_CONNECTION_UUID "uuid" +#define NM_SETTING_CONNECTION_STABLE_ID "stable-id" #define NM_SETTING_CONNECTION_INTERFACE_NAME "interface-name" #define NM_SETTING_CONNECTION_TYPE "type" #define NM_SETTING_CONNECTION_AUTOCONNECT "autoconnect" @@ -116,6 +117,8 @@ GType nm_setting_connection_get_type (void); NMSetting * nm_setting_connection_new (void); const char *nm_setting_connection_get_id (NMSettingConnection *setting); const char *nm_setting_connection_get_uuid (NMSettingConnection *setting); +NM_AVAILABLE_IN_1_4 +const char *nm_setting_connection_get_stable_id (NMSettingConnection *setting); const char *nm_setting_connection_get_interface_name (NMSettingConnection *setting); const char *nm_setting_connection_get_connection_type (NMSettingConnection *setting); gboolean nm_setting_connection_get_autoconnect (NMSettingConnection *setting); diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index 0b97b07482..55ad7bb0cc 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -299,6 +299,52 @@ nm_ip_address_unref (NMIPAddress *address) } } +/** + * _nm_ip_address_equal: + * @address: the #NMIPAddress + * @other: the #NMIPAddress to compare @address to. + * @consider_attributes: whether to check for equality of attributes too. + * + * Determines if two #NMIPAddress objects are equal. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +static gboolean +_nm_ip_address_equal (NMIPAddress *address, NMIPAddress *other, gboolean consider_attributes) +{ + g_return_val_if_fail (address != NULL, FALSE); + g_return_val_if_fail (address->refcount > 0, FALSE); + + g_return_val_if_fail (other != NULL, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if ( address->family != other->family + || address->prefix != other->prefix + || strcmp (address->address, other->address) != 0) + return FALSE; + if (consider_attributes) { + GHashTableIter iter; + const char *key; + GVariant *value, *value2; + guint n; + + n = address->attributes ? g_hash_table_size (address->attributes) : 0; + if (n != (other->attributes ? g_hash_table_size (other->attributes) : 0)) + return FALSE; + if (n) { + g_hash_table_iter_init (&iter, address->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + value2 = g_hash_table_lookup (other->attributes, key); + if (!value2) + return FALSE; + if (!g_variant_equal (value, value2)) + return FALSE; + } + } + } + return TRUE; +} + /** * nm_ip_address_equal: * @address: the #NMIPAddress @@ -312,17 +358,7 @@ nm_ip_address_unref (NMIPAddress *address) gboolean nm_ip_address_equal (NMIPAddress *address, NMIPAddress *other) { - g_return_val_if_fail (address != NULL, FALSE); - g_return_val_if_fail (address->refcount > 0, FALSE); - - g_return_val_if_fail (other != NULL, FALSE); - g_return_val_if_fail (other->refcount > 0, FALSE); - - if ( address->family != other->family - || address->prefix != other->prefix - || strcmp (address->address, other->address) != 0) - return FALSE; - return TRUE; + return _nm_ip_address_equal (address, other, FALSE); } /** @@ -717,17 +753,18 @@ nm_ip_route_unref (NMIPRoute *route) } /** - * nm_ip_route_equal: + * _nm_ip_route_equal: * @route: the #NMIPRoute * @other: the #NMIPRoute to compare @route to. + * @consider_attributes: whether to compare attributes too * * Determines if two #NMIPRoute objects contain the same destination, prefix, - * next hop, and metric. (Attributes are not compared.) + * next hop, and metric. * * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. **/ -gboolean -nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other) +static gboolean +_nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other, gboolean consider_attributes) { g_return_val_if_fail (route != NULL, FALSE); g_return_val_if_fail (route->refcount > 0, FALSE); @@ -740,9 +777,45 @@ nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other) || strcmp (route->dest, other->dest) != 0 || g_strcmp0 (route->next_hop, other->next_hop) != 0) return FALSE; + if (consider_attributes) { + GHashTableIter iter; + const char *key; + GVariant *value, *value2; + guint n; + + n = route->attributes ? g_hash_table_size (route->attributes) : 0; + if (n != (other->attributes ? g_hash_table_size (other->attributes) : 0)) + return FALSE; + if (n) { + g_hash_table_iter_init (&iter, route->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + value2 = g_hash_table_lookup (other->attributes, key); + if (!value2) + return FALSE; + if (!g_variant_equal (value, value2)) + return FALSE; + } + } + } return TRUE; } +/** + * nm_ip_route_equal: + * @route: the #NMIPRoute + * @other: the #NMIPRoute to compare @route to. + * + * Determines if two #NMIPRoute objects contain the same destination, prefix, + * next hop, and metric. (Attributes are not compared.) + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +gboolean +nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other) +{ + return _nm_ip_route_equal (route, other, FALSE); +} + /** * nm_ip_route_dup: * @route: the #NMIPRoute @@ -2306,6 +2379,48 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingIPConfigPrivate *a_priv, *b_priv; + NMSettingClass *parent_class; + guint i; + + if (nm_streq (prop_spec->name, NM_SETTING_IP_CONFIG_ADDRESSES)) { + a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting); + b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other); + + if (a_priv->addresses->len != b_priv->addresses->len) + return FALSE; + for (i = 0; i < a_priv->addresses->len; i++) { + if (!_nm_ip_address_equal (a_priv->addresses->pdata[i], b_priv->addresses->pdata[i], TRUE)) + return FALSE; + } + return TRUE; + } + + if (nm_streq (prop_spec->name, NM_SETTING_IP_CONFIG_ROUTES)) { + a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting); + b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other); + + if (a_priv->routes->len != b_priv->routes->len) + return FALSE; + for (i = 0; i < a_priv->routes->len; i++) { + if (!_nm_ip_route_equal (a_priv->routes->pdata[i], b_priv->routes->pdata[i], TRUE)) + return FALSE; + } + return TRUE; + } + + /* Otherwise chain up to parent to handle generic compare */ + parent_class = NM_SETTING_CLASS (nm_setting_ip_config_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ static void nm_setting_ip_config_init (NMSettingIPConfig *setting) @@ -2536,6 +2651,7 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class) object_class->get_property = get_property; object_class->finalize = finalize; parent_class->verify = verify; + parent_class->compare_property = compare_property; /* Properties */ diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c index a76218fd24..66ed9c48f0 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" @@ -52,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; @@ -69,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, @@ -187,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 @@ -611,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, @@ -692,7 +712,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, @@ -701,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, @@ -732,6 +768,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) { @@ -763,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) @@ -807,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); @@ -872,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; @@ -900,18 +963,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 +1087,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 +1097,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 +1125,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 +1139,75 @@ 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: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: @@ -1183,7 +1332,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-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 6074959349..2aa29c9ee3 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" @@ -54,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; @@ -73,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, @@ -419,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 @@ -721,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, @@ -801,7 +822,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, @@ -810,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); @@ -836,9 +873,61 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if (!NM_IN_SET (priv->mac_address_randomization, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT, + NM_SETTING_MAC_RANDOMIZATION_NEVER, + NM_SETTING_MAC_RANDOMIZATION_ALWAYS)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid value")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION); + return FALSE; + } + + /* from here on, check for NM_SETTING_VERIFY_NORMALIZABLE conditions. */ + + if (priv->cloned_mac_address) { + if ( priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_ALWAYS + && nm_streq (priv->cloned_mac_address, "random")) + goto mac_addr_rand_ok; + if ( priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_NEVER + && nm_streq (priv->cloned_mac_address, "permanent")) + goto mac_addr_rand_ok; + if (priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) + goto mac_addr_rand_ok; + } else if (priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) + goto mac_addr_rand_ok; + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("conflicting value of mac-address-randomization and cloned-mac-address")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS); + return NM_SETTING_VERIFY_NORMALIZABLE; +mac_addr_rand_ok: + 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, @@ -879,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); @@ -931,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); @@ -998,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; @@ -1023,18 +1120,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 +1252,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 +1329,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 +1337,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 +1367,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 +1380,75 @@ 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: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: @@ -1398,6 +1581,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * (always randomize the MAC address). * * Since: 1.2 + * Deprecated: 1.4: Deprecated by NMSettingWireless:cloned-mac-address property **/ /* ---ifcfg-rh--- * property: mac-address-randomization @@ -1428,7 +1612,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-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-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 4bcde68474..50575b30c9 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" @@ -3114,6 +3115,33 @@ hwaddr_binary_len (const char *asc) return octets; } +/** + * _nm_utils_hwaddr_length: + * @asc: the ASCII representation of the hardware address + * + * Validates that @asc is a valid representation of a hardware + * address up to (including) %NM_UTILS_HWADDR_LEN_MAX bytes. + * + * Returns: binary length of the hardware address @asc or + * 0 on error. + */ +guint +_nm_utils_hwaddr_length (const char *asc) +{ + int l; + + if (!asc) + return 0; + + l = hwaddr_binary_len (asc); + if (l <= 0 || l > NM_UTILS_HWADDR_LEN_MAX) + return 0; + + if (!nm_utils_hwaddr_valid (asc, l)) + return 0; + return l; +} + /** * nm_utils_hwaddr_valid: * @asc: the ASCII representation of a hardware address @@ -3286,24 +3314,124 @@ 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 = g_value_get_string (prop_value); guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; int len; - if (str) { - len = hwaddr_binary_len (str); - g_return_val_if_fail (len > 0 && len <= NM_UTILS_HWADDR_LEN_MAX, NULL); - if (!nm_utils_hwaddr_aton (str, buf, len)) - len = 0; - } else - len = 0; + if (!str) + return NULL; + + len = _nm_utils_hwaddr_length (str); + if (len == 0) + return NULL; + + if (!nm_utils_hwaddr_aton (str, buf, len)) + return NULL; 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) @@ -3316,6 +3444,93 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, g_value_take_string (prop_value, str); } +/*****************************************************************************/ + +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 9aaf82977d..aef9f2b13d 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1916,6 +1916,7 @@ test_connection_diff_a_only (void) { NM_SETTING_CONNECTION_SETTING_NAME, { { NM_SETTING_CONNECTION_ID, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_UUID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_STABLE_ID, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_TYPE, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_TIMESTAMP, NM_SETTING_DIFF_RESULT_IN_A }, @@ -1940,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 }, @@ -2384,6 +2386,154 @@ test_setting_compare_id (void) g_assert (success); } +static void +test_setting_compare_addresses (void) +{ + gs_unref_object NMSetting *s1 = NULL, *s2 = NULL; + gboolean success; + NMIPAddress *a; + GHashTable *result = NULL; + + s1 = nm_setting_ip4_config_new (); + s2 = nm_setting_ip4_config_new (); + + a = nm_ip_address_new (AF_INET, "192.168.7.5", 24, NULL); + + nm_ip_address_set_attribute (a, "label", g_variant_new_string ("xoxoxo")); + nm_setting_ip_config_add_address ((NMSettingIPConfig *) s1, a); + + nm_ip_address_set_attribute (a, "label", g_variant_new_string ("hello")); + nm_setting_ip_config_add_address ((NMSettingIPConfig *) s2, a); + + nm_ip_address_unref (a); + + if (nmtst_get_rand_int () % 2) + NMTST_SWAP (s1, s2); + + success = nm_setting_compare (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + + success = nm_setting_diff (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, &result); + g_assert (!success); + g_clear_pointer (&result, g_hash_table_unref); +} + +static void +test_setting_compare_routes (void) +{ + gs_unref_object NMSetting *s1 = NULL, *s2 = NULL; + gboolean success; + NMIPRoute *r; + GHashTable *result = NULL; + + s1 = nm_setting_ip4_config_new (); + s2 = nm_setting_ip4_config_new (); + + r = nm_ip_route_new (AF_INET, "192.168.12.0", 24, "192.168.11.1", 473, NULL); + + nm_ip_route_set_attribute (r, "label", g_variant_new_string ("xoxoxo")); + nm_setting_ip_config_add_route ((NMSettingIPConfig *) s1, r); + + nm_ip_route_set_attribute (r, "label", g_variant_new_string ("hello")); + nm_setting_ip_config_add_route ((NMSettingIPConfig *) s2, r); + + nm_ip_route_unref (r); + + if (nmtst_get_rand_int () % 2) + NMTST_SWAP (s1, s2); + + success = nm_setting_compare (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + + success = nm_setting_diff (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, &result); + g_assert (!success); + 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) { @@ -5135,6 +5285,10 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_setting_to_dbus_transform", test_setting_to_dbus_transform); g_test_add_func ("/core/general/test_setting_to_dbus_enum", test_setting_to_dbus_enum); 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/libnm/libnm.ver b/libnm/libnm.ver index 41f650f008..cc2fc7df62 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1066,8 +1066,11 @@ libnm_1_2_4 { libnm_1_4_0 { global: nm_device_team_get_config; + 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 9fa89aa443..220a12c6a7 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -224,30 +224,12 @@ no-auto-default=* ignore-carrier - Specify devices for which NetworkManager will (partially) - ignore the carrier state. Normally, for - device types that support carrier-detect, such as Ethernet - and InfiniBand, NetworkManager will only allow a - connection to be activated on the device if carrier is - present (ie, a cable is plugged in), and it will - deactivate the device if carrier drops for more than a few - seconds. - - - Listing a device here will allow activating connections on - that device even when it does not have carrier, provided - that the connection uses only statically-configured IP - addresses. Additionally, it will allow any active - connection (whether static or dynamic) to remain active on - the device when carrier is lost. - - - Note that the "carrier" property of NMDevices and device D-Bus - interfaces will still reflect the actual device state; it's just - that NetworkManager will not make use of that information. - - See for the syntax how to - specify a device. + This setting is deprecated for the per-device setting + ignore-carrier which overwrites this setting + if specified (See ). + Otherwise, it is a list of matches to specify for which device + carrier should be ignored. See for the + syntax how to specify a device. @@ -583,6 +565,13 @@ ipv6.ip6-privacy=0 connection.lldp + + ethernet.cloned-mac-address + If left unspecified, it defaults to "permanent". + + + ethernet.generate-mac-address-mask + ethernet.wake-on-lan @@ -610,9 +599,18 @@ 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.generate-mac-address-mask + wifi.mac-address-randomization - If left unspecified, MAC address randomization is disabled. + If left unspecified, MAC address randomization is disabled. + This setting is deprecated for wifi.cloned-mac-address. + wifi.powersave @@ -623,7 +621,7 @@ ipv6.ip6-privacy=0 - + Sections You can configure multiple connection @@ -696,6 +694,97 @@ ipv6.ip6-privacy=1 + + <literal>device</literal> section + Contains per-device persistent configuration. + + + Example: + +[device] +match-device=interface-name:eth3 +unmanaged=1 + + + + Supported Properties + + The following properties can be configured per-device. + + + ignore-carrier + + + Specify devices for which NetworkManager will (partially) + ignore the carrier state. Normally, for + device types that support carrier-detect, such as Ethernet + and InfiniBand, NetworkManager will only allow a + connection to be activated on the device if carrier is + present (ie, a cable is plugged in), and it will + deactivate the device if carrier drops for more than a few + seconds. + + + A device with carrier ignored will allow activating connections on + that device even when it does not have carrier, provided + that the connection uses only statically-configured IP + addresses. Additionally, it will allow any active + connection (whether static or dynamic) to remain active on + the device when carrier is lost. + + + Note that the "carrier" property of NMDevices and device D-Bus + interfaces will still reflect the actual device state; it's just + that NetworkManager will not make use of that information. + + + This setting overwrites the deprecated main.ignore-carrier + setting above. + + + + + wifi.scan-rand-mac-address + + + Configures the MAC address of a Wi-Fi device during scanning. + This defaults to yes in which case a random, + locally-administered MAC address will be confiugred. + Otherwise, the MAC address is left unchanged to whatever was + configured. + For the MAC address used while the device is connected, see instead + the per-connection setting wifi.cloned-mac-address. + + + + + 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. + + + + + + + + + Sections + + The [device] section works the same as the [connection] section. + That is, multiple sections that all start with the prefix "device" can be specified. + The settings "match-device" and "stop-match" are available to match a device section + on a device. The order of multiple sections is also top-down within the file and + later files overwrite previous settings. See + for details. + + + + <literal>connectivity</literal> section This section controls NetworkManager's optional connectivity @@ -978,7 +1067,8 @@ enable=nm-version-min:1.3,nm-version-min:1.2.6,nm-version-min:1.0.16 Device List Format The configuration options main.no-auto-default, main.ignore-carrier, - and keyfile.unmanaged-devices select devices based on a list of matchings. + keyfile.unmanaged-devices, connection*.match-device and + device*.match-device select devices based on a list of matchings. Devices can be specified using the following format: @@ -993,7 +1083,7 @@ enable=nm-version-min:1.3,nm-version-min:1.2.6,nm-version-min:1.0.16 HWADDR - Match the MAC address of the device. Globbing is not supported + Match the permanent MAC address of the device. Globbing is not supported interface-name:IFNAME @@ -1008,7 +1098,7 @@ enable=nm-version-min:1.3,nm-version-min:1.2.6,nm-version-min:1.0.16 mac:HWADDR - Match the MAC address of the device. Globbing is not supported + Match the permanent MAC address of the device. Globbing is not supported s390-subchannels:HWADDR diff --git a/man/nm-settings-ifcfg-rh.xsl b/man/nm-settings-ifcfg-rh.xsl index aac457c80b..80d33f2bdd 100644 --- a/man/nm-settings-ifcfg-rh.xsl +++ b/man/nm-settings-ifcfg-rh.xsl @@ -257,6 +257,11 @@ DEVICETYPE=TeamPort assigns the interface 10.42.0.1, or it uses the first static address, if configured. + + HWADDR - + initscripts compare the currently set hardware address of a device, while + NetworkManager considers the permanent one. + 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/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index 8f3772c69b..67ae15bc82 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -1002,7 +1002,7 @@ nm_device_bt_new (NMBluezDevice *bt_device, NM_DEVICE_UDI, udi, NM_DEVICE_IFACE, bdaddr, NM_DEVICE_DRIVER, "bluez", - NM_DEVICE_HW_ADDRESS, bdaddr, + NM_DEVICE_PERM_HW_ADDRESS, bdaddr, NM_DEVICE_BT_DEVICE, bt_device, NM_DEVICE_BT_NAME, name, NM_DEVICE_BT_CAPABILITIES, capabilities, diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 1367555b3e..5f7c9f0da6 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -120,7 +120,6 @@ typedef struct { enum { PROP_0, - PROP_PERM_HW_ADDRESS, PROP_SPEED, PROP_S390_SUBCHANNELS, @@ -307,14 +306,6 @@ nm_device_ethernet_init (NMDeviceEthernet *self) priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } -static void -realize_start_notify (NMDevice *device, const NMPlatformLink *plink) -{ - NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->realize_start_notify (device, plink); - - g_object_notify (G_OBJECT (device), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS); -} - static NMDeviceCapabilities get_generic_capabilities (NMDevice *device) { @@ -408,7 +399,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!match_subchans (self, s_wired, &try_mac)) return FALSE; - perm_hw_addr = nm_device_get_permanent_hw_address (device); + perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); mac = nm_setting_wired_get_mac_address (s_wired); if (perm_hw_addr) { if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) @@ -813,21 +804,17 @@ 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; 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) { - /* Set device MAC address if the connection wants to change it */ - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_ETHER); - } + if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE)) + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { /* 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 * remote side to handle the disconnection. Otherwise the peer may @@ -1365,10 +1352,6 @@ deactivate (NMDevice *device) /* Set last PPPoE connection time */ if (nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPPOE)) NM_DEVICE_ETHERNET_GET_PRIVATE (device)->last_pppoe_time = nm_utils_get_monotonic_timestamp_s (); - - /* Reset MAC address back to initial address */ - if (nm_device_get_initial_hw_address (device)) - nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_ETHER); } static gboolean @@ -1409,7 +1392,7 @@ complete_connection (NMDevice *device, nm_connection_add_setting (connection, NM_SETTING (s_wired)); } - perm_hw_addr = nm_device_get_permanent_hw_address (device); + perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); if (perm_hw_addr) { setting_mac = nm_setting_wired_get_mac_address (s_wired); if (setting_mac) { @@ -1438,7 +1421,7 @@ new_default_connection (NMDevice *self) NMConnection *connection; NMSettingsConnection *const*connections; NMSetting *setting; - const char *hw_address; + const char *perm_hw_addr; gs_free char *defname = NULL; gs_free char *uuid = NULL; gs_free char *machine_id = NULL; @@ -1446,8 +1429,8 @@ new_default_connection (NMDevice *self) if (nm_config_get_no_auto_default_for_device (nm_config_get (), self)) return NULL; - hw_address = nm_device_get_hw_address (self); - if (!hw_address) + perm_hw_addr = nm_device_get_permanent_hw_address (self, FALSE); + if (!perm_hw_addr) return NULL; connection = nm_simple_connection_new (); @@ -1466,7 +1449,7 @@ new_default_connection (NMDevice *self) uuid = _nm_utils_uuid_generate_from_strings ("default-wired", machine_id ?: "", defname, - hw_address, + perm_hw_addr, NULL); g_object_set (setting, @@ -1480,7 +1463,7 @@ new_default_connection (NMDevice *self) /* Lock the connection to the device */ setting = nm_setting_wired_new (); - g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, hw_address, NULL); + g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, perm_hw_addr, NULL); nm_connection_add_setting (connection, setting); return connection; @@ -1506,7 +1489,7 @@ update_connection (NMDevice *device, NMConnection *connection) { NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device); NMSettingWired *s_wired = nm_connection_get_setting_wired (connection); - const char *perm_hw_addr = nm_device_get_permanent_hw_address (device); + const char *perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); const char *mac = nm_device_get_hw_address (device); const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS; GHashTableIter iter; @@ -1582,34 +1565,10 @@ link_changed (NMDevice *device, NMPlatformLink *info) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - const guint8 *hwaddr; - gsize hwaddrlen = 0; NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->link_changed (device, info); if (!priv->subchan1 && info->initialized) _update_s390_subchannels (self); - - if (!nm_device_get_initial_hw_address (device)) { - hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, - nm_device_get_ifindex (self), - &hwaddrlen); - if (!nm_utils_hwaddr_matches (hwaddr, hwaddrlen, nm_ip_addr_zero.addr_eth, sizeof (nm_ip_addr_zero.addr_eth))) { - _LOGD (LOGD_DEVICE, "device got a valid hw address"); - nm_device_update_hw_address (self); - nm_device_update_initial_hw_address (self); - if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) { - /* - * If the device is UNAVAILABLE, any previous try to - * bring it up probably has failed because of the - * invalid hardware address; try again. - */ - nm_device_bring_up (self, TRUE, NULL); - nm_device_queue_recheck_available (device, - NM_DEVICE_STATE_REASON_NONE, - NM_DEVICE_STATE_REASON_NONE); - } - } - } } static gboolean @@ -1663,9 +1622,6 @@ get_property (GObject *object, guint prop_id, NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); switch (prop_id) { - case PROP_PERM_HW_ADDRESS: - g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (object))); - break; case PROP_SPEED: g_value_set_uint (value, priv->speed); break; @@ -1707,7 +1663,6 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) object_class->set_property = set_property; parent_class->get_generic_capabilities = get_generic_capabilities; - parent_class->realize_start_notify = realize_start_notify; parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; parent_class->new_default_connection = new_default_connection; @@ -1726,13 +1681,6 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) parent_class->state_changed = device_state_changed; /* properties */ - g_object_class_install_property - (object_class, PROP_PERM_HW_ADDRESS, - g_param_spec_string (NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_SPEED, g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED, "", "", diff --git a/src/devices/nm-device-ethernet.h b/src/devices/nm-device-ethernet.h index 2d284822d2..e39d689666 100644 --- a/src/devices/nm-device-ethernet.h +++ b/src/devices/nm-device-ethernet.h @@ -33,7 +33,6 @@ G_BEGIN_DECLS #define NM_IS_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ETHERNET)) #define NM_DEVICE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass)) -#define NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS "perm-hw-address" #define NM_DEVICE_ETHERNET_SPEED "speed" #define NM_DEVICE_ETHERNET_S390_SUBCHANNELS "s390-subchannels" diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h index ae78968a42..4a62468c88 100644 --- a/src/devices/nm-device-factory.h +++ b/src/devices/nm-device-factory.h @@ -92,10 +92,10 @@ typedef struct { * @connection: the #NMConnection to return the parent name for, if supported * * Given a connection, returns the a parent interface name, parent connection - * UUID, or parent device hardware address for @connection. + * UUID, or parent device permanent hardware address for @connection. * * Returns: the parent interface name, parent connection UUID, parent - * device hardware address, or %NULL + * device permenent hardware address, or %NULL */ const char * (*get_connection_parent) (NMDeviceFactory *factory, NMConnection *connection); diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index ae9543e4e9..17f50ba2c7 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -147,10 +147,15 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (nm_device_is_real (device)) { const char *mac; + const char *hw_addr; mac = nm_setting_infiniband_get_mac_address (s_infiniband); - if (mac && !nm_utils_hwaddr_matches (mac, -1, nm_device_get_hw_address (device), -1)) - return FALSE; + if (mac) { + hw_addr = nm_device_get_permanent_hw_address (device, TRUE); + if ( !hw_addr + || !nm_utils_hwaddr_matches (mac, -1, hw_addr, -1)) + return FALSE; + } } return TRUE; @@ -183,7 +188,7 @@ complete_connection (NMDevice *device, } setting_mac = nm_setting_infiniband_get_mac_address (s_infiniband); - hw_address = nm_device_get_hw_address (device); + hw_address = nm_device_get_permanent_hw_address (device, TRUE); if (setting_mac) { /* Make sure the setting MAC (if any) matches the device's MAC */ if (!nm_utils_hwaddr_matches (setting_mac, -1, hw_address, -1)) { @@ -209,7 +214,7 @@ static void update_connection (NMDevice *device, NMConnection *connection) { NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection); - const char *mac = nm_device_get_hw_address (device); + const char *mac = nm_device_get_permanent_hw_address (device, TRUE); const char *transport_mode = "datagram"; int ifindex; diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index c5443ecd23..2bfc65cb2d 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -373,9 +373,8 @@ match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hw if (!priv->parent) return !fail_if_no_hwaddr; - parent_mac = nm_device_get_hw_address (priv->parent); - - return nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); + parent_mac = nm_device_get_permanent_hw_address (priv->parent, FALSE); + return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); } static gboolean @@ -503,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; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -513,14 +510,9 @@ 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) { - /* Set device MAC address if the connection wants to change it */ - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_HW); - } - - return TRUE; + if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE)) + return NM_ACT_STAGE_RETURN_FAILURE; + return NM_ACT_STAGE_RETURN_SUCCESS; } static void @@ -549,16 +541,6 @@ realize_start_notify (NMDevice *device, const NMPlatformLink *plink) update_properties (device); } -static void -deactivate (NMDevice *device) -{ - /* Reset MAC address back to initial address */ - if (nm_device_get_initial_hw_address (device)) { - nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), - "reset", LOGD_DEVICE); - } -} - /******************************************************************/ static void @@ -639,7 +621,6 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass) device_class->complete_connection = complete_connection; device_class->connection_type = NM_SETTING_MACVLAN_SETTING_NAME; device_class->create_and_realize = create_and_realize; - device_class->deactivate = deactivate; device_class->get_generic_capabilities = get_generic_capabilities; device_class->ip4_config_pre_commit = ip4_config_pre_commit; device_class->is_available = is_available; diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 418ae2d9ca..7381fd930f 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -56,8 +56,9 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar void nm_device_take_down (NMDevice *self, gboolean block); -gboolean nm_device_set_hw_addr (NMDevice *device, const char *addr, - const char *detail, guint64 hw_log_domain); +gboolean nm_device_hw_addr_set (NMDevice *device, const char *addr, const char *detail); +gboolean nm_device_hw_addr_set_cloned (NMDevice *device, NMConnection *connection, gboolean is_wifi); +gboolean nm_device_hw_addr_reset (NMDevice *device, const char *detail); 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 01cd8ea6a3..1f42d06015 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; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -305,12 +303,8 @@ 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) { - /* Set device MAC address if the connection wants to change it */ - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); - } + if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE)) + return NM_ACT_STAGE_RETURN_FAILURE; return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 95f36298f5..16edc0d2ba 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); @@ -114,7 +110,7 @@ parent_hwaddr_maybe_changed (NMDevice *parent, _LOGD (LOGD_VLAN, "parent hardware address changed to %s%s%s", NM_PRINT_FMT_QUOTE_STRING (new_mac)); if (new_mac) { - nm_device_set_hw_addr (self, new_mac, "set", LOGD_VLAN); + nm_device_hw_addr_set (self, new_mac, "vlan-parent"); /* When changing the hw address the interface is taken down, * removing the IPv6 configuration; reapply it. */ @@ -378,21 +374,25 @@ match_parent (NMDeviceVlan *self, const char *parent) static gboolean match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr) { - NMSettingWired *s_wired; - const char *setting_mac; - const char *device_mac; + NMDeviceVlanPrivate *priv; + NMSettingWired *s_wired; + const char *setting_mac; + const char *parent_mac; - s_wired = nm_connection_get_setting_wired (connection); - if (!s_wired) - return !fail_if_no_hwaddr; + s_wired = nm_connection_get_setting_wired (connection); + if (!s_wired) + return !fail_if_no_hwaddr; - setting_mac = nm_setting_wired_get_mac_address (s_wired); - if (!setting_mac) - return !fail_if_no_hwaddr; + setting_mac = nm_setting_wired_get_mac_address (s_wired); + if (!setting_mac) + return !fail_if_no_hwaddr; - device_mac = nm_device_get_hw_address (device); + priv = NM_DEVICE_VLAN_GET_PRIVATE (device); + if (!priv->parent) + return !fail_if_no_hwaddr; - return nm_utils_hwaddr_matches (setting_mac, -1, device_mac, -1); + parent_mac = nm_device_get_permanent_hw_address (priv->parent, FALSE); + return parent_mac && nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1); } static gboolean @@ -550,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; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -560,12 +558,8 @@ 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) { - /* Set device MAC address if the connection wants to change it */ - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_VLAN); - } + if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE)) + return NM_ACT_STAGE_RETURN_FAILURE; /* Change MAC address to parent's one if needed */ if (priv->parent) @@ -619,14 +613,6 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) } } -static void -deactivate (NMDevice *device) -{ - /* Reset MAC address back to initial address */ - if (nm_device_get_initial_hw_address (device)) - nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_VLAN); -} - /******************************************************************/ static void @@ -690,7 +676,6 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) parent_class->bring_up = bring_up; parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->ip4_config_pre_commit = ip4_config_pre_commit; - parent_class->deactivate = deactivate; parent_class->is_available = is_available; parent_class->notify_new_device_added = notify_new_device_added; diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index ba2b2d68d7..674c2a787d 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; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -521,12 +519,8 @@ 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) { - /* Set device MAC address if the connection wants to change it */ - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); - } + if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE)) + return NM_ACT_STAGE_RETURN_FAILURE; return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 5fd0bf343e..76133bfb9e 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -132,6 +132,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, PROP_IS_MASTER, PROP_MASTER, PROP_HW_ADDRESS, + PROP_PERM_HW_ADDRESS, PROP_HAS_PENDING_ACTION, PROP_METERED, PROP_LLDP_NEIGHBORS, @@ -208,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; @@ -225,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; @@ -237,13 +250,13 @@ typedef struct _NMDevicePrivate { char * driver_version; char * firmware_version; RfKillType rfkill_type; - bool firmware_missing; - bool nm_plugin_missing; + bool firmware_missing:1; + bool nm_plugin_missing:1; + 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 * perm_hw_addr; - char * initial_hw_addr; + char * hw_addr_perm; + char * hw_addr_initial; char * physical_port_id; guint dev_id; @@ -616,6 +629,30 @@ _add_capabilities (NMDevice *self, NMDeviceCapabilities capabilities) /***********************************************************/ +static const char * +_get_stable_id (NMConnection *connection, NMUtilsStableType *out_stable_type) +{ + NMSettingConnection *s_con; + const char *stable_id; + + nm_assert (NM_IS_CONNECTION (connection)); + nm_assert (out_stable_type); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con, NULL); + + stable_id = nm_setting_connection_get_stable_id (s_con); + if (!stable_id) { + *out_stable_type = NM_UTILS_STABLE_TYPE_UUID; + return nm_connection_get_uuid (connection); + } + + *out_stable_type = NM_UTILS_STABLE_TYPE_STABLE_ID; + return stable_id; +} + +/***********************************************************/ + const char * nm_device_get_udi (NMDevice *self) { @@ -1106,7 +1143,11 @@ nm_device_get_settings_connection (NMDevice *self) NMConnection * nm_device_get_applied_connection (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); return priv->act_request ? nm_act_request_get_applied_connection (priv->act_request) : NULL; } @@ -1123,23 +1164,12 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla } NMSetting * -nm_device_get_applied_setting (NMDevice *device, GType setting_type) +nm_device_get_applied_setting (NMDevice *self, GType setting_type) { - NMActRequest *req; - NMSetting *setting = NULL; + NMConnection *connection; - g_return_val_if_fail (NM_IS_DEVICE (device), NULL); - - req = nm_device_get_act_request (device); - if (req) { - NMConnection *connection; - - connection = nm_act_request_get_applied_connection (req); - if (connection) - setting = nm_connection_get_setting (connection, setting_type); - } - - return setting; + connection = nm_device_get_applied_connection (self); + return connection ? nm_connection_get_setting (connection, setting_type) : NULL; } RfKillType @@ -1647,6 +1677,7 @@ device_link_changed (NMDevice *self) int ifindex; gboolean was_up; gboolean update_unmanaged_specs = FALSE; + gboolean got_hw_addr = FALSE, had_hw_addr; priv->device_link_changed_id = 0; @@ -1684,6 +1715,11 @@ device_link_changed (NMDevice *self) _notify (self, PROP_DRIVER); } + had_hw_addr = (priv->hw_addr != NULL); + nm_device_update_hw_address (self); + got_hw_addr = (!had_hw_addr && priv->hw_addr); + nm_device_update_permanent_hw_address (self); + if (info.name[0] && strcmp (priv->iface, info.name) != 0) { _LOGI (LOGD_DEVICE, "interface index %d renamed iface from '%s' to '%s'", priv->ifindex, priv->iface, info.name); @@ -1775,6 +1811,20 @@ device_link_changed (NMDevice *self) if (update_unmanaged_specs) nm_device_set_unmanaged_by_user_settings (self, nm_settings_get_unmanaged_specs (priv->settings)); + if ( got_hw_addr + && !priv->up + && nm_device_get_state (self) == NM_DEVICE_STATE_UNAVAILABLE) { + /* + * If the device is UNAVAILABLE, any previous try to + * bring it up probably has failed because of the + * invalid hardware address; try again. + */ + nm_device_bring_up (self, TRUE, NULL); + nm_device_queue_recheck_available (self, + NM_DEVICE_STATE_REASON_NONE, + NM_DEVICE_STATE_REASON_NONE); + } + return G_SOURCE_REMOVE; } @@ -2123,6 +2173,7 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink) nm_device_update_hw_address (self); nm_device_update_initial_hw_address (self); + nm_device_update_permanent_hw_address (self); /* Note: initial hardware address must be read before calling get_ignore_carrier() */ config = nm_config_get (); @@ -2308,6 +2359,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) _notify (self, PROP_UDI); } if (priv->hw_addr) { + priv->hw_addr_len = 0; g_clear_pointer (&priv->hw_addr, g_free); _notify (self, PROP_HW_ADDRESS); } @@ -2316,8 +2368,10 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) _notify (self, PROP_PHYSICAL_PORT_ID); } - g_clear_pointer (&priv->perm_hw_addr, g_free); - g_clear_pointer (&priv->initial_hw_addr, g_free); + 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); priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED; if (NM_DEVICE_GET_CLASS (self)->get_generic_capabilities) @@ -3308,7 +3362,7 @@ nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave static gboolean nm_device_can_assume_connections (NMDevice *self) { - return !!NM_DEVICE_GET_CLASS (self)->update_connection; + return !!NM_DEVICE_GET_CLASS (self)->update_connection; } /** @@ -3324,7 +3378,7 @@ nm_device_can_assume_connections (NMDevice *self) * if there is no active connection or the active connection cannot be * assumed. */ -gboolean +static gboolean nm_device_can_assume_active_connection (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -3371,6 +3425,45 @@ nm_device_can_assume_active_connection (NMDevice *self) return TRUE; } +static gboolean +unmanaged_on_quit (NMDevice *self) +{ + /* Leave certain devices alone when quitting so their configuration + * can be taken over when NM restarts. This ensures connectivity while + * NM is stopped. + */ + if (nm_device_uses_assumed_connection (self)) { + /* An assume connection must be left alone */ + return FALSE; + } + + if (!nm_device_get_act_request (self)) { + /* a device without any active connection is either UNAVAILABLE or DISCONNECTED + * state. Since we don't know whether the device was upped by NetworkManager, + * we must leave it up on exit. + */ + return FALSE; + } + + if (!nm_platform_link_can_assume (NM_PLATFORM_GET, nm_device_get_ifindex (self))) { + /* The device has no layer 3 configuration. Leave it up. */ + return FALSE; + } + + if (nm_device_can_assume_active_connection (self)) + return FALSE; + + return TRUE; +} + +gboolean +nm_device_unmanage_on_quit (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + return NM_DEVICE_GET_CLASS (self)->unmanaged_on_quit (self); +} + static gboolean nm_device_emit_recheck_assume (gpointer user_data) { @@ -5859,11 +5952,17 @@ check_and_add_ipv6ll_addr (NMDevice *self) s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); if (s_ip6 && nm_setting_ip6_config_get_addr_gen_mode (s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { - if (!nm_utils_ipv6_addr_set_stable_privacy (&lladdr, - nm_device_get_iface (self), - nm_connection_get_uuid (connection), - priv->linklocal6_dad_counter++, - &error)) { + NMUtilsStableType stable_type; + const char *stable_id; + + stable_id = _get_stable_id (connection, &stable_type); + if ( !stable_id + || !nm_utils_ipv6_addr_set_stable_privacy (stable_type, + &lladdr, + nm_device_get_iface (self), + stable_id, + priv->linklocal6_dad_counter++, + &error)) { _LOGW (LOGD_IP6, "linklocal6: failed to generate an address: %s", error->message); g_clear_error (&error); linklocal6_failed (self); @@ -6205,6 +6304,8 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) NMActStageReturn ret; NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; + NMUtilsStableType stable_type; + const char *stable_id; connection = nm_device_get_applied_connection (self); g_assert (connection); @@ -6218,12 +6319,16 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)); g_assert (s_ip6); - priv->rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, - nm_device_get_ip_ifindex (self), - nm_device_get_ip_iface (self), - nm_connection_get_uuid (connection), - nm_setting_ip6_config_get_addr_gen_mode (s_ip6), - &error); + stable_id = _get_stable_id (connection, &stable_type); + if (stable_id) { + priv->rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, + nm_device_get_ip_ifindex (self), + nm_device_get_ip_iface (self), + stable_type, + stable_id, + nm_setting_ip6_config_get_addr_gen_mode (s_ip6), + &error); + } if (!priv->rdisc) { _LOGE (LOGD_IP6, "addrconf6: failed to start router discovery: %s", error->message); g_error_free (error); @@ -10548,6 +10653,8 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); } + nm_device_hw_addr_reset (self, "deactivate"); + /* Call device type-specific deactivation */ if (NM_DEVICE_GET_CLASS (self)->deactivate) NM_DEVICE_GET_CLASS (self)->deactivate (self); @@ -10622,6 +10729,8 @@ nm_device_spawn_iface_helper (NMDevice *self) GPtrArray *argv; gs_free char *dhcp4_address = NULL; char *logging_backend; + NMUtilsStableType stable_type; + const char *stable_id; if (priv->state != NM_DEVICE_STATE_ACTIVATED) return; @@ -10640,6 +10749,12 @@ nm_device_spawn_iface_helper (NMDevice *self) g_ptr_array_add (argv, g_strdup ("--uuid")); g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection))); + stable_id = _get_stable_id (connection, &stable_type); + if (stable_id && stable_type != NM_UTILS_STABLE_TYPE_UUID) { + g_ptr_array_add (argv, g_strdup ("--stable-id")); + g_ptr_array_add (argv, g_strdup_printf ("%d %s", (int) stable_type, stable_id)); + } + logging_backend = nm_config_get_is_debug (nm_config_get ()) ? g_strdup ("debug") : nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, @@ -10935,6 +11050,7 @@ _set_state_full (NMDevice *self, if (nm_device_get_act_request (self)) nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE); nm_device_take_down (self, TRUE); + nm_device_hw_addr_reset (self, "unmanage"); set_nm_ipv6ll (self, FALSE); restore_ip6_properties (self); } @@ -10982,6 +11098,9 @@ _set_state_full (NMDevice *self, } } break; + case NM_DEVICE_STATE_PREPARE: + nm_device_update_initial_hw_address (self); + break; case NM_DEVICE_STATE_NEED_AUTH: if (old_state > NM_DEVICE_STATE_NEED_AUTH) { /* Clean up any half-done IP operations if the device's layer2 @@ -11300,23 +11419,26 @@ nm_device_get_hw_address (NMDevice *self) g_return_val_if_fail (NM_IS_DEVICE (self), NULL); priv = NM_DEVICE_GET_PRIVATE (self); - return priv->hw_addr_len ? priv->hw_addr : NULL; + nm_assert ((!priv->hw_addr) ^ (priv->hw_addr_len > 0)); + + return priv->hw_addr; } void nm_device_update_hw_address (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex = nm_device_get_ifindex (self); + NMDevicePrivate *priv; const guint8 *hwaddr; gsize hwaddrlen = 0; - if (ifindex <= 0) + priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->ifindex <= 0) return; - hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen); + hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &hwaddrlen); if ( priv->type == NM_DEVICE_TYPE_ETHERNET + && hwaddr && nm_utils_hwaddr_matches (hwaddr, hwaddrlen, nm_ip_addr_zero.addr_eth, sizeof (nm_ip_addr_zero.addr_eth))) hwaddrlen = 0; @@ -11326,17 +11448,28 @@ nm_device_update_hw_address (NMDevice *self) g_free (priv->hw_addr); priv->hw_addr = nm_utils_hwaddr_ntoa (hwaddr, hwaddrlen); - _LOGD (LOGD_HW | LOGD_DEVICE, "hardware address now %s", priv->hw_addr); + _LOGD (LOGD_HW | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr); _notify (self, PROP_HW_ADDRESS); + + if ( !priv->hw_addr_initial + || ( priv->hw_addr_type == HW_ADDR_TYPE_UNSET + && priv->state < NM_DEVICE_STATE_PREPARE + && !nm_device_is_activating (self))) { + /* when we get a hw_addr the first time or while the device + * is not activated (with no explict hw address set), always + * update our inital hw-address as well. */ + nm_device_update_initial_hw_address (self); + } } } else { /* Invalid or no hardware address */ if (priv->hw_addr_len != 0) { - g_clear_pointer (&priv->hw_addr, g_free); - priv->hw_addr_len = 0; _LOGD (LOGD_HW | LOGD_DEVICE, - "previous hardware address is no longer valid"); - _notify (self, PROP_HW_ADDRESS); + "hw-addr: failed reading current MAC address (stay with %s)", + priv->hw_addr); + } else { + _LOGD (LOGD_HW | LOGD_DEVICE, + "hw-addr: failed reading current MAC address"); } } } @@ -11346,84 +11479,361 @@ nm_device_update_initial_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->hw_addr_len) { - priv->initial_hw_addr = g_strdup (priv->hw_addr); - _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); - - if (priv->ifindex > 0) { - guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - size_t len = 0; - - if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) { - g_warn_if_fail (len == priv->hw_addr_len); - priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); - _LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s", - priv->perm_hw_addr); - } else { - /* Fall back to current address */ - _LOGD (LOGD_HW | LOGD_ETHER, "unable to read permanent MAC address"); - priv->perm_hw_addr = g_strdup (priv->hw_addr); - } + if ( priv->hw_addr + && !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) { + if ( priv->hw_addr_initial + && priv->hw_addr_type != HW_ADDR_TYPE_UNSET) { + /* once we have the initial hw address set, we only allow + * update if the currenty type is "unset". */ + return; } + g_free (priv->hw_addr_initial); + priv->hw_addr_initial = g_strdup (priv->hw_addr); + _LOGD (LOGD_DEVICE, "hw-addr: update initial MAC address %s", + priv->hw_addr_initial); } } -gboolean -nm_device_set_hw_addr (NMDevice *self, const char *addr, - const char *detail, guint64 hw_log_domain) +void +nm_device_update_permanent_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - gboolean success = FALSE; - const char *cur_addr = nm_device_get_hw_address (self); - guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + size_t len = 0; + gboolean success_read; - /* Fall back to the permanent address */ - if (!addr) - addr = priv->perm_hw_addr; - if (!addr) - return FALSE; + if (priv->hw_addr_perm) { + /* the permanent hardware address is only read once and not + * re-read later. + * + * Except during unrealize/realize cycles, where we clear the permanent + * hardware address during unrealization. */ + return; + } + + if (priv->ifindex <= 0) + return; + + if (!priv->hw_addr_len) { + nm_device_update_hw_address (self); + if (!priv->hw_addr_len) + return; + } + + success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len); + if (!success_read || len != priv->hw_addr_len) { + /* Fall back to current address. We use the fake address and keep it + * until the device unrealizes. + * + * In some cases it might be necessary to know whether this is a "real" or + * a temporary address (fake). */ + _LOGD (LOGD_HW | LOGD_ETHER, "hw-addr: %s (use current: %s)", + success_read + ? "unable to read permanent MAC address" + : "read HW addr length of permanent MAC address differs", + priv->hw_addr); + priv->hw_addr_perm_fake = TRUE; + priv->hw_addr_perm = g_strdup (priv->hw_addr); + } else { + priv->hw_addr_perm_fake = FALSE; + priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, len); + _LOGD (LOGD_DEVICE, "hw-addr: read permanent MAC address '%s'", + priv->hw_addr_perm); + } + _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); + /* default is permanent. */ + addr = NM_CLONED_MAC_PERMANENT; + + if (!a) { + if (is_wifi) { + NMSettingMacRandomization v; + + /* for backward compatibility, read the deprecated wifi.mac-address-randomization setting. */ + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, + self); + v = _nm_utils_ascii_str_to_int64 (a, 10, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT, + NM_SETTING_MAC_RANDOMIZATION_ALWAYS, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT); + if (v == NM_SETTING_MAC_RANDOMIZATION_ALWAYS) + addr = NM_CLONED_MAC_RANDOM; + } + } else if ( NM_CLONED_MAC_IS_SPECIAL (a) + || nm_utils_hwaddr_valid (a, ETH_ALEN)) + addr = *out_addr = g_steal_pointer (&a); + } + + 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) +{ + 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, + const char *operation, + const char *detail) +{ + NMDevicePrivate *priv; + gboolean success = FALSE; + const char *cur_addr; + guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; + guint hw_addr_len; + gboolean was_up; + + nm_assert (NM_IS_DEVICE (self)); + nm_assert (addr); + nm_assert (operation); + + priv = NM_DEVICE_GET_PRIVATE (self); + + cur_addr = nm_device_get_hw_address (self); /* Do nothing if current MAC is same */ if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) { - _LOGD (LOGD_DEVICE | hw_log_domain, "no MAC address change needed"); + _LOGD (LOGD_DEVICE, "set-hw-addr: no MAC address change needed"); return TRUE; } - if (!nm_utils_hwaddr_aton (addr, addr_bytes, priv->hw_addr_len)) { - _LOGW (LOGD_DEVICE | hw_log_domain, "invalid MAC address %s", addr); - return FALSE; + + hw_addr_len = priv->hw_addr_len; + if (!hw_addr_len) + hw_addr_len = _nm_utils_hwaddr_length (addr); + if ( !hw_addr_len + || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) + g_return_val_if_reached (FALSE); + + _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s' (%s, %s)...", addr, operation, detail); + + was_up = nm_device_is_up (self); + if (was_up) { + /* Can't change MAC address while device is up */ + nm_device_take_down (self, FALSE); } - /* Can't change MAC address while device is up */ - nm_device_take_down (self, FALSE); - - success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, priv->hw_addr_len); + success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, hw_addr_len); if (success) { /* MAC address succesfully changed; update the current MAC to match */ nm_device_update_hw_address (self); cur_addr = nm_device_get_hw_address (self); if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) { - _LOGI (LOGD_DEVICE | hw_log_domain, "%s MAC address to %s", - detail, addr); + _LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s (%s)", + operation, addr, detail); } else { - _LOGW (LOGD_DEVICE | hw_log_domain, - "new MAC address %s not successfully set", addr); + _LOGW (LOGD_DEVICE, + "set-hw-addr: new MAC address %s not successfully set to %s (%s)", + addr, operation, detail); success = FALSE; } } else { - _LOGW (LOGD_DEVICE | hw_log_domain, "failed to %s MAC address to %s", - detail, addr); + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s)", + operation, addr, detail); + } + + if (was_up) { + if (!nm_device_bring_up (self, TRUE, NULL)) + return FALSE; } - nm_device_bring_up (self, TRUE, NULL); return success; } -const char * -nm_device_get_permanent_hw_address (NMDevice *self) +gboolean +nm_device_hw_addr_set (NMDevice *self, const char *addr, const char *detail) { + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!addr) + g_return_val_if_reached (FALSE); + + /* this is called by NMDeviceVlan to take the MAC address from the parent + * and by NMDeviceWifi to set a random MAC address during scanning. + * 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", detail); +} + +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; + gs_free char *generate_mac_address_mask_tmp = NULL; + const char *addr, *addr_setting; + + 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 = addr_setting = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp); + + if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) { + /* "preserve" means to reset the initial MAC address. */ + return nm_device_hw_addr_reset (self, addr_setting); + } + + 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)) { + if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) { + /* hm, we already use a generate MAC address. Most certainly, that is from the same + * activation request, so we should not create a new random address, instead keep + * the current. */ + return TRUE; + } + 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; + } + 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; + + if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) { + /* hm, we already use a generate MAC address. Most certainly, that is from the same + * activation request, so let's skip creating the stable address anew. */ + return TRUE; + } + + 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_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"); + 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-cloned", addr_setting); +} + +gboolean +nm_device_hw_addr_reset (NMDevice *self, const char *detail) +{ + NMDevicePrivate *priv; + const char *addr; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + 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) { + /* 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", detail); +} + +const char * +nm_device_get_permanent_hw_address (NMDevice *self, gboolean fallback_fake) +{ + NMDevicePrivate *priv; + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->perm_hw_addr; + priv = NM_DEVICE_GET_PRIVATE (self); + if (!priv->hw_addr_perm) + return NULL; + if ( priv->hw_addr_perm_fake + && !fallback_fake) + return NULL; + return priv->hw_addr_perm; } const char * @@ -11431,7 +11841,7 @@ nm_device_get_initial_hw_address (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->initial_hw_addr; + return NM_DEVICE_GET_PRIVATE (self)->hw_addr_initial; } /** @@ -11469,9 +11879,9 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs) static NMMatchSpecMatchType spec_match_list (NMDevice *self, const GSList *specs) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMMatchSpecMatchType matched = NM_MATCH_SPEC_NO_MATCH, m; const GSList *iter; + const char *hw_addr_perm; for (iter = specs; iter; iter = g_slist_next (iter)) { if (!strcmp ((const char *) iter->data, "*")) { @@ -11479,8 +11889,10 @@ spec_match_list (NMDevice *self, const GSList *specs) break; } } - if (priv->hw_addr_len && priv->hw_addr) { - m = nm_match_spec_hwaddr (specs, priv->hw_addr); + + hw_addr_perm = nm_device_get_permanent_hw_address (self, FALSE); + if (hw_addr_perm) { + m = nm_match_spec_hwaddr (specs, hw_addr_perm); matched = MAX (matched, m); } if (matched != NM_MATCH_SPEC_NEG_MATCH) { @@ -11563,7 +11975,8 @@ constructor (GType type, self = NM_DEVICE (object); priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->iface) { + if ( priv->iface + && G_LIKELY (!nm_utils_get_testing ())) { pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface); if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) { @@ -11572,6 +11985,17 @@ constructor (GType type, } } + if (priv->hw_addr_perm) { + priv->hw_addr_len = _nm_utils_hwaddr_length (priv->hw_addr_perm); + if (!priv->hw_addr_len) { + g_clear_pointer (&priv->hw_addr_perm, g_free); + g_return_val_if_reached (object); + } + + priv->hw_addr = g_strdup (priv->hw_addr_perm); + _LOGT (LOGD_DEVICE, "hw-addr: has permanent hw-address '%s'", priv->hw_addr_perm); + } + return object; } @@ -11697,8 +12121,8 @@ finalize (GObject *object) _LOGD (LOGD_DEVICE, "finalize(): %s", G_OBJECT_TYPE_NAME (self)); g_free (priv->hw_addr); - g_free (priv->perm_hw_addr); - g_free (priv->initial_hw_addr); + g_free (priv->hw_addr_perm); + g_free (priv->hw_addr_initial); g_slist_free_full (priv->pending_actions, g_free); g_slist_free_full (priv->dad6_failed_addrs, g_free); g_clear_pointer (&priv->physical_port_id, g_free); @@ -11727,10 +12151,8 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMDevice *self = NM_DEVICE (object); + NMDevice *self = (NMDevice *) object; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *hw_addr, *p; - guint count; switch (prop_id) { case PROP_UDI: @@ -11808,32 +12230,9 @@ set_property (GObject *object, guint prop_id, case PROP_IS_MASTER: priv->is_master = g_value_get_boolean (value); break; - case PROP_HW_ADDRESS: + case PROP_PERM_HW_ADDRESS: /* construct only */ - p = hw_addr = g_value_get_string (value); - - /* Hardware address length is the number of ':' plus 1 */ - count = 1; - while (p && *p) { - if (*p++ == ':') - count++; - } - if (count < ETH_ALEN || count > NM_UTILS_HWADDR_LEN_MAX) { - if (hw_addr && *hw_addr) { - _LOGW (LOGD_DEVICE, "ignoring hardware address '%s' with unexpected length %d", - hw_addr, count); - } - break; - } - - priv->hw_addr_len = count; - g_free (priv->hw_addr); - if (nm_utils_hwaddr_valid (hw_addr, priv->hw_addr_len)) - priv->hw_addr = g_strdup (hw_addr); - else { - _LOGW (LOGD_DEVICE, "could not parse hw-address '%s'", hw_addr); - priv->hw_addr = NULL; - } + priv->hw_addr_perm = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -11956,6 +12355,10 @@ get_property (GObject *object, guint prop_id, case PROP_HW_ADDRESS: g_value_set_string (value, priv->hw_addr); break; + case PROP_PERM_HW_ADDRESS: + /* this property is exposed on D-Bus for NMDeviceEthernet and NMDeviceWifi. */ + g_value_set_string (value, nm_device_get_permanent_hw_address (self, FALSE)); + break; case PROP_HAS_PENDING_ACTION: g_value_set_boolean (value, nm_device_has_pending_action (self)); break; @@ -12041,6 +12444,7 @@ nm_device_class_init (NMDeviceClass *klass) klass->take_down = take_down; klass->carrier_changed = carrier_changed; klass->get_ip_iface_identifier = get_ip_iface_identifier; + klass->unmanaged_on_quit = unmanaged_on_quit; /* Properties */ obj_properties[PROP_UDI] = @@ -12198,6 +12602,11 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_STATIC_STRINGS); obj_properties[PROP_HW_ADDRESS] = g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_PERM_HW_ADDRESS] = + g_param_spec_string (NM_DEVICE_PERM_HW_ADDRESS, "", "", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 4b365d6a60..8383ea250d 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -57,6 +57,11 @@ #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id" #define NM_DEVICE_MTU "mtu" #define NM_DEVICE_HW_ADDRESS "hw-address" + +/* "perm-hw-address" is exposed on D-Bus both for NMDeviceEthernet + * and NMDeviceWifi. */ +#define NM_DEVICE_PERM_HW_ADDRESS "perm-hw-address" + #define NM_DEVICE_METERED "metered" #define NM_DEVICE_LLDP_NEIGHBORS "lldp-neighbors" #define NM_DEVICE_REAL "real" @@ -323,6 +328,8 @@ typedef struct { gboolean (* owns_iface) (NMDevice *self, const char *iface); NMConnection * (* new_default_connection) (NMDevice *self); + + gboolean (* unmanaged_on_quit) (NMDevice *self); } NMDeviceClass; typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device, @@ -353,7 +360,8 @@ guint32 nm_device_get_ip4_route_metric (NMDevice *dev); guint32 nm_device_get_ip6_route_metric (NMDevice *dev); const char * nm_device_get_hw_address (NMDevice *dev); -const char * nm_device_get_permanent_hw_address (NMDevice *dev); +const char * nm_device_get_permanent_hw_address (NMDevice *dev, + gboolean fallback_fake); const char * nm_device_get_initial_hw_address (NMDevice *dev); NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *dev); @@ -411,7 +419,7 @@ gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnec gboolean nm_device_uses_assumed_connection (NMDevice *device); -gboolean nm_device_can_assume_active_connection (NMDevice *device); +gboolean nm_device_unmanage_on_quit (NMDevice *self); gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs); @@ -578,6 +586,7 @@ void nm_device_reactivate_ip6_config (NMDevice *device, void nm_device_update_hw_address (NMDevice *self); void nm_device_update_initial_hw_address (NMDevice *self); +void nm_device_update_permanent_hw_address (NMDevice *self); void nm_device_update_dynamic_ip_setup (NMDevice *self); G_END_DECLS diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 781ecb7e36..5ddf5d84b7 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -62,6 +62,8 @@ _LOG_DECLARE_SELF(NMDeviceWifi); #define SCAN_INTERVAL_STEP 20 #define SCAN_INTERVAL_MAX 120 +#define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5 + #define WIRELESS_SECRETS_TRIES "wireless-secrets-tries" G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) @@ -71,7 +73,6 @@ G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) enum { PROP_0, - PROP_PERM_HW_ADDRESS, PROP_MODE, PROP_BITRATE, PROP_ACCESS_POINTS, @@ -120,6 +121,9 @@ struct _NMDeviceWifiPrivate { guint reacquire_iface_id; NMDeviceWifiCapabilities capabilities; + + gint32 hw_addr_scan_expire; + char *hw_addr_scan; }; static gboolean check_scanning_allowed (NMDeviceWifi *self); @@ -170,6 +174,8 @@ static void ap_add_remove (NMDeviceWifi *self, static void remove_supplicant_interface_error_handler (NMDeviceWifi *self); +static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset); + /*****************************************************************/ static void @@ -187,6 +193,18 @@ constructed (GObject *object) priv->sup_mgr = g_object_ref (nm_supplicant_manager_get ()); } +static gboolean +unmanaged_on_quit (NMDevice *self) +{ + /* Wi-Fi devices cannot be assumed and are always taken down. + * However, also when being disconnected, we scan and thus + * set the MAC address to a random value. + * + * We must restore the original MAC address when quitting, thus + * signal to unmanage the device. */ + return TRUE; +} + static gboolean supplicant_interface_acquire (NMDeviceWifi *self) { @@ -403,14 +421,6 @@ periodic_update_cb (gpointer user_data) return TRUE; } -static void -realize_start_notify (NMDevice *device, const NMPlatformLink *plink) -{ - NM_DEVICE_CLASS (nm_device_wifi_parent_class)->realize_start_notify (device, plink); - - g_object_notify (G_OBJECT (device), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS); -} - static gboolean bring_up (NMDevice *device, gboolean *no_firmware) { @@ -491,9 +501,8 @@ deactivate (NMDevice *device) /* Clear any critical protocol notification in the Wi-Fi stack */ nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE); - /* Reset MAC address back to initial address */ - if (nm_device_get_initial_hw_address (device)) - nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device), "reset", LOGD_WIFI); + g_clear_pointer (&priv->hw_addr_scan, g_free); + _hw_addr_set_scanning (self, TRUE); /* Ensure we're in infrastructure mode after deactivation; some devices * (usually older ones) don't scan well in adhoc mode. @@ -572,7 +581,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection) if (!s_wireless) return FALSE; - perm_hw_addr = nm_device_get_permanent_hw_address (device); + perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); mac = nm_setting_wireless_get_mac_address (s_wireless); if (perm_hw_addr) { if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) @@ -685,35 +694,31 @@ check_connection_available (NMDevice *device, return !!find_first_compatible_ap (NM_DEVICE_WIFI (device), connection, TRUE); } -/* - * List of manufacturer default SSIDs that are often unchanged by users. - * - * NOTE: this list should *not* contain networks that you would like to - * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot". - */ -static const char * -manf_defaults[] = { - "linksys", - "linksys-a", - "linksys-g", - "default", - "belkin54g", - "NETGEAR", - "o2DSL", - "WLAN", - "ALICE-WLAN", - "Speedport W 501V", - "TURBONETT", -}; - -#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) - static gboolean is_manf_default_ssid (const GByteArray *ssid) { int i; + /* + * List of manufacturer default SSIDs that are often unchanged by users. + * + * NOTE: this list should *not* contain networks that you would like to + * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot". + */ + static const char *manf_defaults[] = { + "linksys", + "linksys-a", + "linksys-g", + "default", + "belkin54g", + "NETGEAR", + "o2DSL", + "WLAN", + "ALICE-WLAN", + "Speedport W 501V", + "TURBONETT", + }; - for (i = 0; i < ARRAY_SIZE (manf_defaults); i++) { + for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) { if (ssid->len == strlen (manf_defaults[i])) { if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0) return TRUE; @@ -868,7 +873,7 @@ complete_connection (NMDevice *device, if (hidden) g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); - perm_hw_addr = nm_device_get_permanent_hw_address (device); + perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); if (perm_hw_addr) { setting_mac = nm_setting_wireless_get_mac_address (s_wifi); if (setting_mac) { @@ -1027,6 +1032,60 @@ impl_device_wifi_get_all_access_points (NMDeviceWifi *self, g_ptr_array_unref (paths); } +static void +_hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) +{ + NMDevice *device = (NMDevice *) self; + NMDeviceWifiPrivate *priv; + guint32 now; + gboolean randomize; + + g_return_if_fail (NM_IS_DEVICE_WIFI (self)); + + if ( nm_device_is_activating (device) + || nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) + return; + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + randomize = nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA, + "wifi.scan-rand-mac-address", + device, + TRUE, TRUE); + + if (!randomize) { + g_clear_pointer (&priv->hw_addr_scan, g_free); + if (do_reset) + nm_device_hw_addr_reset (device, "scanning"); + return; + } + + now = nm_utils_get_monotonic_timestamp_s (); + + 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 + * it expires, instead on the next scan request, we will generate + * 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 (nm_device_get_initial_hw_address (device), + generate_mac_address_mask); + } + + nm_device_hw_addr_set (device, priv->hw_addr_scan, "scanning"); +} + static void request_scan_cb (NMDevice *device, GDBusMethodInvocation *context, @@ -1336,6 +1395,8 @@ request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options) _LOGD (LOGD_WIFI_SCAN, "no SSIDs to probe scan"); } + _hw_addr_set_scanning (self, FALSE); + if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) { /* success */ backoff = TRUE; @@ -2210,9 +2271,6 @@ build_supplicant_config (NMDeviceWifi *self, NMSupplicantConfig *config = NULL; NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wireless_sec; - NMSupplicantFeature mac_randomization_support; - NMSettingMacRandomization mac_randomization_fallback; - gs_free char *svalue = NULL; g_return_val_if_fail (priv->sup_iface, NULL); @@ -2227,20 +2285,9 @@ build_supplicant_config (NMDeviceWifi *self, _LOGW (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out."); } - mac_randomization_support = nm_supplicant_interface_get_mac_randomization_support (priv->sup_iface); - svalue = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, - NM_DEVICE (self)); - mac_randomization_fallback = _nm_utils_ascii_str_to_int64 (svalue, 10, - NM_SETTING_MAC_RANDOMIZATION_DEFAULT, - NM_SETTING_MAC_RANDOMIZATION_ALWAYS, - NM_SETTING_MAC_RANDOMIZATION_DEFAULT); - if (!nm_supplicant_config_add_setting_wireless (config, s_wireless, fixed_freq, - mac_randomization_support, - mac_randomization_fallback, error)) { g_prefix_error (error, "802-11-wireless: "); goto error; @@ -2290,7 +2337,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; @@ -2330,9 +2376,12 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) return NM_ACT_STAGE_RETURN_FAILURE; } + /* forget the temporary MAC address used during scanning */ + g_clear_pointer (&priv->hw_addr_scan, g_free); + /* Set spoof MAC to the interface */ - cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_WIFI); + if (!nm_device_hw_addr_set_cloned (device, connection, TRUE)) + return NM_ACT_STAGE_RETURN_FAILURE; /* AP mode never uses a specific object or existing scanned AP */ if (priv->mode != NM_802_11_MODE_AP) { @@ -2972,6 +3021,8 @@ finalize (GObject *object) g_hash_table_unref (priv->aps); + g_free (priv->hw_addr_scan); + G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object); } @@ -2986,9 +3037,6 @@ get_property (GObject *object, guint prop_id, GPtrArray *array; switch (prop_id) { - case PROP_PERM_HW_ADDRESS: - g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (device))); - break; case PROP_MODE: g_value_set_uint (value, priv->mode); break; @@ -3053,7 +3101,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; - parent_class->realize_start_notify = realize_start_notify; parent_class->bring_up = bring_up; parent_class->can_auto_connect = can_auto_connect; parent_class->is_available = is_available; @@ -3070,19 +3117,13 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; parent_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; parent_class->deactivate = deactivate; + parent_class->unmanaged_on_quit = unmanaged_on_quit; parent_class->state_changed = device_state_changed; klass->scanning_allowed = scanning_allowed; /* Properties */ - g_object_class_install_property - (object_class, PROP_PERM_HW_ADDRESS, - g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, "", "", - NULL, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_MODE, g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "", diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index 7e0d06f5a1..2b7e8217a6 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -36,7 +36,6 @@ G_BEGIN_DECLS #define NM_IS_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIFI)) #define NM_DEVICE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass)) -#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address" #define NM_DEVICE_WIFI_MODE "mode" #define NM_DEVICE_WIFI_BITRATE "bitrate" #define NM_DEVICE_WIFI_ACCESS_POINTS "access-points" diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 28057b47a6..23c4cecdc9 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -40,7 +40,7 @@ typedef struct { gboolean has; GSList *spec; } match_device; -} ConnectionInfo; +} MatchSectionInfo; typedef struct { char *config_main_file; @@ -52,7 +52,11 @@ typedef struct { /* A zero-terminated list of pre-processed information from the * [connection] sections. This is to speed up lookup. */ - ConnectionInfo *connection_infos; + MatchSectionInfo *connection_infos; + + /* A zero-terminated list of pre-processed information from the + * [device] sections. This is to speed up lookup. */ + MatchSectionInfo *device_infos; struct { char *uri; @@ -268,9 +272,16 @@ nm_config_data_get_rc_manager (const NMConfigData *self) gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device) { + gs_free char *value = NULL; + gboolean has_match; + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + value = nm_config_data_get_device_config (self, NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER, device, &has_match); + if (has_match) + return nm_config_parse_boolean (value, FALSE); + return nm_device_spec_match_list (device, NM_CONFIG_DATA_GET_PRIVATE (self)->ignore_carrier); } @@ -451,6 +462,7 @@ static int _nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy) { gboolean a_is_connection, b_is_connection; + gboolean a_is_device, b_is_device; gboolean a_is_intern, b_is_intern; const char *a = *pa; const char *b = *pb; @@ -488,6 +500,28 @@ _nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy) if (b_is_connection && !a_is_connection) return -1; + /* we sort device groups before connection groups (to the end). */ + a_is_device = a && g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + b_is_device = b && g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + + if (a_is_device && b_is_device) { + /* if both are device groups, we want the explicit [device] group first. */ + a_is_device = a[NM_STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE)] == '\0'; + b_is_device = b[NM_STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE)] == '\0'; + + if (a_is_device != b_is_device) { + if (a_is_device) + return -1; + return 1; + } + /* the sections are ordered lowest-priority first. Reverse their order. */ + return pa < pb ? 1 : -1; + } + if (a_is_device && !b_is_device) + return 1; + if (b_is_device && !a_is_device) + return -1; + /* no reordering. */ return 0; } @@ -1049,25 +1083,18 @@ global_dns_equal (NMGlobalDnsConfig *old, NMGlobalDnsConfig *new) /************************************************************************/ -char * -nm_config_data_get_connection_default (const NMConfigData *self, - const char *property, - NMDevice *device) +static const MatchSectionInfo * +_match_section_infos_lookup (const MatchSectionInfo *match_section_infos, + GKeyFile *keyfile, + const char *property, + NMDevice *device, + char **out_value) { - NMConfigDataPrivate *priv; - const ConnectionInfo *connection_info; - - g_return_val_if_fail (self, NULL); - g_return_val_if_fail (property && *property, NULL); - g_return_val_if_fail (strchr (property, '.'), NULL); - - priv = NM_CONFIG_DATA_GET_PRIVATE (self); - - if (!priv->connection_infos) + if (!match_section_infos) return NULL; - for (connection_info = &priv->connection_infos[0]; connection_info->group_name; connection_info++) { - char *value; + for (; match_section_infos->group_name; match_section_infos++) { + char *value = NULL; gboolean match; /* FIXME: Here we use g_key_file_get_string(). This should be in sync with what keyfile-reader @@ -1077,23 +1104,87 @@ nm_config_data_get_connection_default (const NMConfigData *self, * string_to_value(keyfile_to_string(keyfile)) in one. Optimally, keyfile library would * expose both functions, and we would return here keyfile_to_string(keyfile). * The caller then could convert the string to the proper value via string_to_value(value). */ - value = g_key_file_get_string (priv->keyfile, connection_info->group_name, property, NULL); - if (!value && !connection_info->stop_match) + value = g_key_file_get_string (keyfile, match_section_infos->group_name, property, NULL); + if (!value && !match_section_infos->stop_match) continue; match = TRUE; - if (connection_info->match_device.has) - match = device && nm_device_spec_match_list (device, connection_info->match_device.spec); + if (match_section_infos->match_device.has) + match = device && nm_device_spec_match_list (device, match_section_infos->match_device.spec); - if (match) - return value; + if (match) { + *out_value = value; + return match_section_infos; + } g_free (value); } return NULL; } +char * +nm_config_data_get_device_config (const NMConfigData *self, + const char *property, + NMDevice *device, + gboolean *has_match) +{ + NMConfigDataPrivate *priv; + const MatchSectionInfo *connection_info; + char *value = NULL; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (property && *property, NULL); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + connection_info = _match_section_infos_lookup (&priv->device_infos[0], + priv->keyfile, + property, + device, + &value); + NM_SET_OUT (has_match, !!connection_info); + return value; +} + +gboolean +nm_config_data_get_device_config_boolean (const NMConfigData *self, + const char *property, + NMDevice *device, + gint val_no_match, + gint val_invalid) +{ + gs_free char *value = NULL; + gboolean has_match; + + value = nm_config_data_get_device_config (self, property, device, &has_match); + if (!has_match) + return val_no_match; + return nm_config_parse_boolean (value, val_invalid); +} + +char * +nm_config_data_get_connection_default (const NMConfigData *self, + const char *property, + NMDevice *device) +{ + NMConfigDataPrivate *priv; + char *value = NULL; + + g_return_val_if_fail (self, NULL); + g_return_val_if_fail (property && *property, NULL); + g_return_val_if_fail (strchr (property, '.'), NULL); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + _match_section_infos_lookup (&priv->connection_infos[0], + priv->keyfile, + property, + device, + &value); + return value; +} + static void -_get_connection_info_init (ConnectionInfo *connection_info, GKeyFile *keyfile, char *group) +_get_connection_info_init (MatchSectionInfo *connection_info, GKeyFile *keyfile, char *group) { /* pass ownership of @group on... */ connection_info->group_name = group; @@ -1105,27 +1196,43 @@ _get_connection_info_init (ConnectionInfo *connection_info, GKeyFile *keyfile, c connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, group, "stop-match", FALSE); } -static ConnectionInfo * -_get_connection_infos (GKeyFile *keyfile) +static void +_match_section_infos_free (MatchSectionInfo *match_section_infos) +{ + guint i; + + if (!match_section_infos) + return; + for (i = 0; match_section_infos[i].group_name; i++) { + g_free (match_section_infos[i].group_name); + g_slist_free_full (match_section_infos[i].match_device.spec, g_free); + } + g_free (match_section_infos); +} + +static MatchSectionInfo * +_match_section_infos_construct (GKeyFile *keyfile, const char *prefix) { char **groups; gsize i, j, ngroups; char *connection_tag = NULL; - ConnectionInfo *connection_infos = NULL; + MatchSectionInfo *match_section_infos = NULL; - /* get the list of existing [connection.\+] sections that we consider - * for nm_config_data_get_connection_default(). + /* get the list of existing [connection.\+]/[device.\+] sections. * * We expect the sections in their right order, with lowest priority * first. Only exception is the (literal) [connection] section, which * we will always reorder to the end. */ groups = g_key_file_get_groups (keyfile, &ngroups); if (!groups) - ngroups = 0; - else if (ngroups > 0) { + return NULL; + + if (ngroups > 0) { + gsize l = strlen (prefix); + for (i = 0, j = 0; i < ngroups; i++) { - if (g_str_has_prefix (groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)) { - if (groups[i][NM_STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0') + if (g_str_has_prefix (groups[i], prefix)) { + if (groups[i][l] == '\0') connection_tag = groups[i]; else groups[j++] = groups[i]; @@ -1135,18 +1242,23 @@ _get_connection_infos (GKeyFile *keyfile) ngroups = j; } - connection_infos = g_new0 (ConnectionInfo, ngroups + 1 + (connection_tag ? 1 : 0)); + if (ngroups == 0 && !connection_tag) { + g_free (groups); + return NULL; + } + + match_section_infos = g_new0 (MatchSectionInfo, ngroups + 1 + (connection_tag ? 1 : 0)); for (i = 0; i < ngroups; i++) { /* pass ownership of @group on... */ - _get_connection_info_init (&connection_infos[i], keyfile, groups[ngroups - i - 1]); + _get_connection_info_init (&match_section_infos[i], keyfile, groups[ngroups - i - 1]); } if (connection_tag) { /* pass ownership of @connection_tag on... */ - _get_connection_info_init (&connection_infos[i], keyfile, connection_tag); + _get_connection_info_init (&match_section_infos[i], keyfile, connection_tag); } g_free (groups); - return connection_infos; + return match_section_infos; } /************************************************************************/ @@ -1306,7 +1418,6 @@ static void finalize (GObject *gobject) { NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (gobject); - guint i; g_free (priv->config_main_file); g_free (priv->config_description); @@ -1326,13 +1437,8 @@ finalize (GObject *gobject) nm_global_dns_config_free (priv->global_dns); - if (priv->connection_infos) { - for (i = 0; priv->connection_infos[i].group_name; i++) { - g_free (priv->connection_infos[i].group_name); - g_slist_free_full (priv->connection_infos[i].match_device.spec, g_free); - } - g_free (priv->connection_infos); - } + _match_section_infos_free (priv->connection_infos); + _match_section_infos_free (priv->device_infos); g_key_file_unref (priv->keyfile); if (priv->keyfile_user) @@ -1359,7 +1465,8 @@ constructed (GObject *object) priv->keyfile = _merge_keyfiles (priv->keyfile_user, priv->keyfile_intern); - priv->connection_infos = _get_connection_infos (priv->keyfile); + priv->connection_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION); + priv->device_infos = _match_section_infos_construct (priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL)); priv->connectivity.response = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", NULL); diff --git a/src/nm-config-data.h b/src/nm-config-data.h index e01ad6eaf5..e63b45904e 100644 --- a/src/nm-config-data.h +++ b/src/nm-config-data.h @@ -183,6 +183,17 @@ char *nm_config_data_get_connection_default (const NMConfigData *self, const char *property, NMDevice *device); +char *nm_config_data_get_device_config (const NMConfigData *self, + const char *property, + NMDevice *device, + gboolean *has_match); + +gboolean nm_config_data_get_device_config_boolean (const NMConfigData *self, + const char *property, + NMDevice *device, + gint val_no_match, + gint val_invalid); + char **nm_config_data_get_groups (const NMConfigData *self); char **nm_config_data_get_keys (const NMConfigData *self, const char *group); gboolean nm_config_data_is_intern_atomic_group (const NMConfigData *self, const char *group); diff --git a/src/nm-config.c b/src/nm-config.c index 4d0ebc1f8f..4bc4f48340 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -399,7 +399,9 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device) priv = NM_CONFIG_GET_PRIVATE (self); - hw_address = nm_device_get_hw_address (device); + hw_address = nm_device_get_permanent_hw_address (device, FALSE); + if (!hw_address) + return; no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data); @@ -576,14 +578,7 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy) { const char *a, *b; gboolean a_is_connection, b_is_connection; - - /* basic NULL checking... */ - if (pa == pb) - return 0; - if (!pa) - return -1; - if (!pb) - return 1; + gboolean a_is_device, b_is_device; a = *pa; b = *pb; @@ -598,16 +593,34 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy) return 1; return -1; } - if (!a_is_connection) { - /* both are non-connection entries. Don't reorder. */ - return 0; + if (a_is_connection) { + /* both are [connection.\+] entires. Reverse their order. + * One of the sections might be literally [connection]. That section + * is special and it's order will be fixed later. It doesn't actually + * matter here how it compares with [connection.\+] sections. */ + return pa > pb ? -1 : 1; } - /* both are [connection.\+] entires. Reverse their order. - * One of the sections might be literally [connection]. That section - * is special and it's order will be fixed later. It doesn't actually - * matter here how it compares with [connection.\+] sections. */ - return pa > pb ? -1 : 1; + a_is_device = g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + b_is_device = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE); + + if (a_is_device != b_is_device) { + /* one is a [device*] entry, the other not. We sort [device*] entires + * after. */ + if (a_is_device) + return 1; + return -1; + } + if (a_is_device) { + /* both are [device.\+] entires. Reverse their order. + * One of the sections might be literally [device]. That section + * is special and it's order will be fixed later. It doesn't actually + * matter here how it compares with [device.\+] sections. */ + return pa > pb ? -1 : 1; + } + + /* don't reorder the rest. */ + return 0; } void @@ -630,7 +643,8 @@ _setting_is_device_spec (const char *group, const char *key) || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier") || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only") || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices") - || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device")); + || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device")) + || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE ) && !strcmp (key, "match-device")); } static gboolean diff --git a/src/nm-config.h b/src/nm-config.h index d7c38583b6..a75e393523 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -45,6 +45,7 @@ #define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN ".intern." #define NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION "connection" +#define NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE "device" #define NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN "global-dns-domain-" #define NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".test-append-stringlist" @@ -69,6 +70,8 @@ #define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed" #define NM_CONFIG_KEYFILE_KEY_AUDIT "audit" +#define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier" + #define NM_CONFIG_KEYFILE_KEYPREFIX_WAS ".was." #define NM_CONFIG_KEYFILE_KEYPREFIX_SET ".set." diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index b20653838d..6dfbb4ce10 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,7 @@ #endif G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int)); -int _nm_utils_testing = 0; +static int _nm_utils_testing = 0; gboolean nm_utils_get_testing_initialized () @@ -1271,8 +1272,10 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr) { const GSList *iter; NMMatchSpecMatchType match = NM_MATCH_SPEC_NO_MATCH; + guint hwaddr_len = 0; + guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX]; - g_return_val_if_fail (hwaddr != NULL, NM_MATCH_SPEC_NO_MATCH); + nm_assert (nm_utils_hwaddr_valid (hwaddr, -1)); for (iter = specs; iter; iter = g_slist_next (iter)) { const char *spec_str = iter->data; @@ -1293,7 +1296,15 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr) else if (except) continue; - if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr, -1)) { + if (G_UNLIKELY (hwaddr_len == 0)) { + hwaddr_len = _nm_utils_hwaddr_length (hwaddr); + if (!hwaddr_len) + g_return_val_if_reached (NM_MATCH_SPEC_NO_MATCH); + if (!nm_utils_hwaddr_aton (hwaddr, hwaddr_bin, hwaddr_len)) + nm_assert_not_reached (); + } + + if (nm_utils_hwaddr_matches (spec_str, -1, hwaddr_bin, hwaddr_len)) { if (except) return NM_MATCH_SPEC_NEG_MATCH; match = NM_MATCH_SPEC_MATCH; @@ -2701,6 +2712,123 @@ nm_utils_machine_id_read (void) /*****************************************************************************/ +/* taken from systemd's fd_wait_for_event(). Note that the timeout + * is here in nano-seconds, not micro-seconds. */ +int +nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns) +{ + struct pollfd pollfd = { + .fd = fd, + .events = event, + }; + struct timespec ts, *pts; + int r; + + if (timeout_ns < 0) + pts = NULL; + else { + ts.tv_sec = (time_t) (timeout_ns / NM_UTILS_NS_PER_SECOND); + ts.tv_nsec = (long int) (timeout_ns % NM_UTILS_NS_PER_SECOND); + pts = &ts; + } + + r = ppoll (&pollfd, 1, pts, NULL); + if (r < 0) + return -errno; + if (r == 0) + return 0; + return pollfd.revents; +} + +/* taken from systemd's loop_read() */ +ssize_t +nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) +{ + uint8_t *p = buf; + ssize_t n = 0; + + g_return_val_if_fail (fd >= 0, -EINVAL); + g_return_val_if_fail (buf, -EINVAL); + + /* If called with nbytes == 0, let's call read() at least + * once, to validate the operation */ + + if (nbytes > (size_t) SSIZE_MAX) + return -EINVAL; + + do { + ssize_t k; + + k = read (fd, p, nbytes); + if (k < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN && do_poll) { + + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via read() */ + + (void) nm_utils_fd_wait_for_event (fd, POLLIN, -1); + continue; + } + + return n > 0 ? n : -errno; + } + + if (k == 0) + return n; + + g_assert ((size_t) k <= nbytes); + + p += k; + nbytes -= k; + n += k; + } while (nbytes > 0); + + return n; +} + +/* taken from systemd's loop_read_exact() */ +int +nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll) +{ + ssize_t n; + + n = nm_utils_fd_read_loop (fd, buf, nbytes, do_poll); + if (n < 0) + return (int) n; + if ((size_t) n != nbytes) + return -EIO; + + return 0; +} + +/* taken from systemd's dev_urandom(). */ +int +nm_utils_read_urandom (void *p, size_t nbytes) +{ + int fd = -1; + int r; + +again: + fd = open ("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + r = errno; + if (r == EINTR) + goto again; + return r == ENOENT ? -ENOSYS : -r; + } + + r = nm_utils_fd_read_loop_exact (fd, p, nbytes, TRUE); + close (fd); + + return r; +} + +/*****************************************************************************/ + guint8 * nm_utils_secret_key_read (gsize *out_key_len, GError **error) { @@ -2719,34 +2847,28 @@ nm_utils_secret_key_read (gsize *out_key_len, GError **error) key_len = 0; } } else { - int urandom = open ("/dev/urandom", O_RDONLY); + int r; mode_t key_mask; - if (urandom == -1) { - g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Can't open /dev/urandom: %s", strerror (errno)); - key_len = 0; - goto out; - } - /* RFC7217 mandates the key SHOULD be at least 128 bits. * Let's use twice as much. */ key_len = 32; secret_key = g_malloc (key_len); + r = nm_utils_read_urandom (secret_key, key_len); + if (r < 0) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Can't read /dev/urandom: %s", strerror (-r)); + key_len = 0; + goto out; + } + key_mask = umask (0077); - if (read (urandom, secret_key, key_len) == key_len) { - if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) { - g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: "); - key_len = 0; - } - } else { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Could not obtain a secret"); + if (!g_file_set_contents (NMSTATEDIR "/secret_key", (char *) secret_key, key_len, error)) { + g_prefix_error (error, "Can't write " NMSTATEDIR "/secret_key: "); key_len = 0; } umask (key_mask); - close (urandom); } out: @@ -2953,9 +3075,10 @@ nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid, char *buf) /*****************************************************************************/ static gboolean -_set_stable_privacy (struct in6_addr *addr, +_set_stable_privacy (guint8 stable_type, + struct in6_addr *addr, const char *ifname, - const char *uuid, + const char *network_id, guint dad_counter, guint8 *secret_key, gsize key_len, @@ -2979,11 +3102,24 @@ _set_stable_privacy (struct in6_addr *addr, key_len = MIN (key_len, G_MAXUINT32); + if (stable_type != NM_UTILS_STABLE_TYPE_UUID) { + /* Preferably, we would always like to include the stable-type, + * but for backward compatibility reasons, we cannot for UUID. + * + * That is no real problem and it is still impossible to + * force a collision here, because of how the remaining + * fields are hashed. That is, as we also hash @key_len + * and the terminating '\0' of @network_id, it is unambigiously + * possible to revert the process and deduce the @stable_type. + */ + g_checksum_update (sum, &stable_type, sizeof (stable_type)); + } + g_checksum_update (sum, addr->s6_addr, 8); g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1); - if (!uuid) - uuid = ""; - g_checksum_update (sum, (const guchar *) uuid, strlen (uuid) + 1); + if (!network_id) + network_id = ""; + g_checksum_update (sum, (const guchar *) network_id, strlen (network_id) + 1); tmp[0] = htonl (dad_counter); tmp[1] = htonl (key_len); g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp)); @@ -3009,15 +3145,20 @@ _set_stable_privacy (struct in6_addr *addr, * Returns: %TRUE on success, %FALSE if the address could not be generated. */ gboolean -nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, +nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, + struct in6_addr *addr, const char *ifname, - const char *uuid, + const char *network_id, guint dad_counter, GError **error) { gs_free guint8 *secret_key = NULL; gsize key_len = 0; + nm_assert (NM_IN_SET (stable_type, + NM_UTILS_STABLE_TYPE_UUID, + NM_UTILS_STABLE_TYPE_STABLE_ID)); + if (dad_counter >= RFC7217_IDGEN_RETRIES) { g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "Too many DAD collisions"); @@ -3028,10 +3169,151 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, if (!secret_key) return FALSE; - return _set_stable_privacy (addr, ifname, uuid, dad_counter, + return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, secret_key, key_len, error); } +/*****************************************************************************/ + +static void +_hw_addr_eth_complete (struct ether_addr *addr, + const char *current_mac_address, + const char *generate_mac_address_mask) +{ + 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". 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 (const char *current_mac_address, + const char *generate_mac_address_mask) +{ + struct ether_addr bin_addr; + + if (nm_utils_read_urandom (&bin_addr, ETH_ALEN) < 0) + return NULL; + _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); + 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, + const char *current_mac_address, + const char *generate_mac_address_mask) +{ + GChecksum *sum; + guint32 tmp; + guint8 digest[32]; + gsize len = sizeof (digest); + struct ether_addr bin_addr; + 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, 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 *current_mac_address, + const char *generate_mac_address_mask) +{ + 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, + current_mac_address, + generate_mac_address_mask); +} + +/*****************************************************************************/ + /** * nm_utils_setpgid: * @unused: unused diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index ce22439b1b..bfba35f575 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -310,6 +310,12 @@ const char *nm_utils_ip4_property_path (const char *ifname, const char *property gboolean nm_utils_is_specific_hostname (const char *name); +int nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns); +ssize_t nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll); +int nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll); + +int nm_utils_read_urandom (void *p, size_t n); + char *nm_utils_machine_id_read (void); gboolean nm_utils_machine_id_parse (const char *id_str, /*uuid_t*/ guchar *out_uuid); @@ -353,12 +359,26 @@ gboolean nm_utils_get_ipv6_interface_identifier (NMLinkType link_type, guint dev_id, NMUtilsIPv6IfaceId *out_iid); -gboolean nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, +typedef enum { + NM_UTILS_STABLE_TYPE_UUID = 0, + NM_UTILS_STABLE_TYPE_STABLE_ID = 1, +} NMUtilsStableType; + +gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type, + struct in6_addr *addr, const char *ifname, - const char *uuid, + const char *network_id, guint dad_counter, GError **error); +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 *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); void nm_utils_setpgid (gpointer unused); diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 1058e76f4c..e7f4b85434 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -66,6 +66,7 @@ static struct { int tempaddr; char *ifname; char *uuid; + char *stable_id; char *dhcp4_address; char *dhcp4_clientid; char *dhcp4_hostname; @@ -278,8 +279,9 @@ do_early_setup (int *argc, char **argv[]) gint64 priority64_v6 = -1; GOptionEntry options[] = { /* Interface/IP config */ - { "ifname", 'i', 0, G_OPTION_ARG_STRING, &global_opt.ifname, N_("The interface to manage"), N_("eth0") }, - { "uuid", 'u', 0, G_OPTION_ARG_STRING, &global_opt.uuid, N_("Connection UUID"), N_("661e8cd0-b618-46b8-9dc9-31a52baaa16b") }, + { "ifname", 'i', 0, G_OPTION_ARG_STRING, &global_opt.ifname, N_("The interface to manage"), "eth0" }, + { "uuid", 'u', 0, G_OPTION_ARG_STRING, &global_opt.uuid, N_("Connection UUID"), "661e8cd0-b618-46b8-9dc9-31a52baaa16b" }, + { "stable-id", '\0', 0, G_OPTION_ARG_STRING, &global_opt.stable_id, N_("Connection Token for Stable IDs"), "eth" }, { "slaac", 's', 0, G_OPTION_ARG_NONE, &global_opt.slaac, N_("Whether to manage IPv6 SLAAC"), NULL }, { "slaac-required", '6', 0, G_OPTION_ARG_NONE, &global_opt.slaac_required, N_("Whether SLAAC must be successful"), NULL }, { "slaac-tempaddr", 't', 0, G_OPTION_ARG_INT, &global_opt.tempaddr, N_("Use an IPv6 temporary privacy address"), NULL }, @@ -469,9 +471,23 @@ main (int argc, char *argv[]) } if (global_opt.slaac) { + NMUtilsStableType stable_type = NM_UTILS_STABLE_TYPE_UUID; + const char *stable_id = global_opt.uuid; + nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, TRUE); - rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, ifindex, global_opt.ifname, global_opt.uuid, global_opt.addr_gen_mode, NULL); + if ( global_opt.stable_id + && (global_opt.stable_id[0] >= '0' && global_opt.stable_id[0] <= '9') + && global_opt.stable_id[1] == ' ') { + /* strict parsing of --stable-id, which is the numeric stable-type + * and the ID, joined with one space. For now, only support stable-types + * from 0 to 9. */ + stable_type = (global_opt.stable_id[0] - '0'); + stable_id = &global_opt.stable_id[2]; + } + rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, ifindex, global_opt.ifname, + stable_type, stable_id, + global_opt.addr_gen_mode, NULL); g_assert (rdisc); if (iid) diff --git a/src/nm-manager.c b/src/nm-manager.c index fe53ae9b2a..e7ca81219f 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -595,7 +595,7 @@ nm_manager_get_device_by_ifindex (NMManager *manager, int ifindex) } static NMDevice * -find_device_by_hw_addr (NMManager *manager, const char *hwaddr) +find_device_by_permanent_hw_addr (NMManager *manager, const char *hwaddr) { GSList *iter; const char *device_addr; @@ -604,7 +604,7 @@ find_device_by_hw_addr (NMManager *manager, const char *hwaddr) if (nm_utils_hwaddr_valid (hwaddr, -1)) { for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) { - device_addr = nm_device_get_hw_address (NM_DEVICE (iter->data)); + device_addr = nm_device_get_permanent_hw_address (NM_DEVICE (iter->data), FALSE); if (device_addr && nm_utils_hwaddr_matches (hwaddr, -1, device_addr, -1)) return NM_DEVICE (iter->data); } @@ -945,28 +945,12 @@ remove_device (NMManager *self, nm_device_get_iface (device), allow_unmanage, nm_device_get_managed (device, FALSE)); if (allow_unmanage && nm_device_get_managed (device, FALSE)) { - unmanage = TRUE; - if (!quitting) { + if (quitting) + unmanage = nm_device_unmanage_on_quit (device); + else { /* the device is already gone. Unmanage it. */ - } else { - /* Leave certain devices alone when quitting so their configuration - * can be taken over when NM restarts. This ensures connectivity while - * NM is stopped. - */ - if (nm_device_uses_assumed_connection (device)) { - /* An assume connection must be left alone */ - unmanage = FALSE; - } else if (!nm_device_get_act_request (device)) { - /* a device without any active connection is either UNAVAILABLE or DISCONNECTED - * state. Since we don't know whether the device was upped by NetworkManager, - * we must leave it up on exit. */ - unmanage = FALSE; - } else if (!nm_platform_link_can_assume (NM_PLATFORM_GET, nm_device_get_ifindex (device))) { - /* The device has no layer 3 configuration. Leave it up. */ - unmanage = FALSE; - } else if (nm_device_can_assume_active_connection (device)) - unmanage = FALSE; + unmanage = TRUE; } if (unmanage) { @@ -1054,7 +1038,7 @@ find_parent_device_for_connection (NMManager *self, NMConnection *connection, NM return parent; /* Maybe a hardware address */ - parent = find_device_by_hw_addr (self, parent_name); + parent = find_device_by_permanent_hw_addr (self, parent_name); if (parent) return parent; diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index 0c5971f42b..fca61f79ad 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -321,7 +321,8 @@ NMRDisc * nm_lndp_rdisc_new (NMPlatform *platform, int ifindex, const char *ifname, - const char *uuid, + NMUtilsStableType stable_type, + const char *network_id, NMSettingIP6ConfigAddrGenMode addr_gen_mode, GError **error) { @@ -342,7 +343,8 @@ nm_lndp_rdisc_new (NMPlatform *platform, rdisc->ifindex = ifindex; rdisc->ifname = g_strdup (ifname); - rdisc->uuid = g_strdup (uuid); + rdisc->stable_type = stable_type; + rdisc->network_id = g_strdup (network_id); rdisc->addr_gen_mode = addr_gen_mode; rdisc->max_addresses = ipv6_sysctl_get (platform, ifname, "max_addresses", diff --git a/src/rdisc/nm-lndp-rdisc.h b/src/rdisc/nm-lndp-rdisc.h index 4c7c4743e7..51290089ec 100644 --- a/src/rdisc/nm-lndp-rdisc.h +++ b/src/rdisc/nm-lndp-rdisc.h @@ -22,6 +22,7 @@ #define __NETWORKMANAGER_LNDP_RDISC_H__ #include "nm-rdisc.h" +#include "nm-core-utils.h" #define NM_TYPE_LNDP_RDISC (nm_lndp_rdisc_get_type ()) #define NM_LNDP_RDISC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_LNDP_RDISC, NMLNDPRDisc)) @@ -47,7 +48,8 @@ GType nm_lndp_rdisc_get_type (void); NMRDisc *nm_lndp_rdisc_new (NMPlatform *platform, int ifindex, const char *ifname, - const char *uuid, + NMUtilsStableType stable_type, + const char *network_id, NMSettingIP6ConfigAddrGenMode addr_gen_mode, GError **error); diff --git a/src/rdisc/nm-rdisc.c b/src/rdisc/nm-rdisc.c index 85c803dc79..c11557d943 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -140,9 +140,10 @@ complete_address (NMRDisc *rdisc, NMRDiscAddress *addr) GError *error = NULL; if (rdisc->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { - if (!nm_utils_ipv6_addr_set_stable_privacy (&addr->address, + if (!nm_utils_ipv6_addr_set_stable_privacy (rdisc->stable_type, + &addr->address, rdisc->ifname, - rdisc->uuid, + rdisc->network_id, addr->dad_counter++, &error)) { _LOGW ("complete-address: failed to generate an stable-privacy address: %s", @@ -777,7 +778,7 @@ finalize (GObject *object) NMRDisc *rdisc = NM_RDISC (object); g_free (rdisc->ifname); - g_free (rdisc->uuid); + g_free (rdisc->network_id); g_array_unref (rdisc->gateways); g_array_unref (rdisc->addresses); g_array_unref (rdisc->routes); diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h index 5b97bec563..8c14576bcb 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -116,9 +116,11 @@ typedef struct { NMPlatform *_platform; NMPNetns *_netns; + NMUtilsStableType stable_type; + int ifindex; char *ifname; - char *uuid; + char *network_id; NMSettingIP6ConfigAddrGenMode addr_gen_mode; NMUtilsIPv6IfaceId iid; gint32 max_addresses; diff --git a/src/rdisc/tests/test-rdisc-linux.c b/src/rdisc/tests/test-rdisc-linux.c index c3eadf4eab..299abf3eeb 100644 --- a/src/rdisc/tests/test-rdisc-linux.c +++ b/src/rdisc/tests/test-rdisc-linux.c @@ -64,6 +64,7 @@ main (int argc, char **argv) rdisc = nm_lndp_rdisc_new (NM_PLATFORM_GET, ifindex, ifname, + NM_UTILS_STABLE_TYPE_UUID, "8ce666e8-d34d-4fb1-b858-f15a7al28086", NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64, &error); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index a11696fd65..284e173d24 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1879,11 +1879,11 @@ have_connection_for_device (NMSettings *self, NMDevice *device) NMSettingConnection *s_con; NMSettingWired *s_wired; const char *setting_hwaddr; - const char *device_hwaddr; + const char *perm_hw_addr; g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); - device_hwaddr = nm_device_get_hw_address (device); + perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE); /* Find a wired connection locked to the given MAC address, if any */ g_hash_table_iter_init (&iter, priv->connections); @@ -1917,8 +1917,8 @@ have_connection_for_device (NMSettings *self, NMDevice *device) setting_hwaddr = nm_setting_wired_get_mac_address (s_wired); if (setting_hwaddr) { /* A connection mac-locked to this device */ - if ( device_hwaddr - && nm_utils_hwaddr_matches (setting_hwaddr, -1, device_hwaddr, -1)) + if ( perm_hw_addr + && nm_utils_hwaddr_matches (setting_hwaddr, -1, perm_hw_addr, -1)) return TRUE; } else { /* A connection that applies to any wired device */ diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index b933e1a5fa..af5d291026 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -128,6 +128,7 @@ make_connection_setting (const char *file, NMSettingConnectionLldp lldp; const char *ifcfg_name = NULL; char *new_id, *uuid = NULL, *zone = NULL, *value; + gs_free char *stable_id = NULL; ifcfg_name = utils_get_ifcfg_name (file, TRUE); if (!ifcfg_name) @@ -146,9 +147,12 @@ make_connection_setting (const char *file, uuid = nm_utils_uuid_generate_from_string (ifcfg->fileName, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL); } + stable_id = svGetValue (ifcfg, "STABLE_ID", FALSE); + g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, type, NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_STABLE_ID, stable_id, NULL); g_free (uuid); @@ -3364,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; @@ -3878,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 2864078a48..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]) { @@ -1255,19 +1263,22 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) has_wired = TRUE; device_mac = nm_setting_wired_get_mac_address (s_wired); - if (device_mac) - svSetValue (ifcfg, "HWADDR", device_mac, FALSE); + svSetValue (ifcfg, "HWADDR", device_mac, FALSE); cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - if (cloned_mac) - svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE); + 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); svSetValue (ifcfg, "MTU", tmp, FALSE); g_free (tmp); - } + } else + svSetValue (ifcfg, "MTU", NULL, FALSE); } return has_wired; } @@ -1800,6 +1811,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) svSetValue (ifcfg, "NAME", nm_setting_connection_get_id (s_con), FALSE); svSetValue (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con), FALSE); + svSetValue (ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id (s_con), FALSE); svSetValue (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con), FALSE); svSetValue (ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect (s_con) ? "yes" : "no", diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c index 5ce8bb3196..6283edd6ca 100644 --- a/src/supplicant-manager/nm-supplicant-config.c +++ b/src/supplicant-manager/nm-supplicant-config.c @@ -47,7 +47,6 @@ typedef struct GHashTable *config; GHashTable *blobs; guint32 ap_scan; - NMSettingMacRandomization mac_randomization; gboolean fast_required; gboolean dispose_has_run; } NMSupplicantConfigPrivate; @@ -85,7 +84,6 @@ nm_supplicant_config_init (NMSupplicantConfig * self) (GDestroyNotify) blob_free); priv->ap_scan = 1; - priv->mac_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT; priv->dispose_has_run = FALSE; } @@ -272,32 +270,6 @@ nm_supplicant_config_get_ap_scan (NMSupplicantConfig * self) return NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->ap_scan; } -const char * -nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self) -{ - g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), 0); - - /** - * mac_addr - MAC address policy default - * - * 0 = use permanent MAC address - * 1 = use random MAC address for each ESS connection - * 2 = like 1, but maintain OUI (with local admin bit set) - * - * By default, permanent MAC address is used unless policy is changed by - * the per-network mac_addr parameter. - */ - - switch (NM_SUPPLICANT_CONFIG_GET_PRIVATE (self)->mac_randomization) { - case NM_SETTING_MAC_RANDOMIZATION_ALWAYS: - return "1"; - case NM_SETTING_MAC_RANDOMIZATION_NEVER: - case NM_SETTING_MAC_RANDOMIZATION_DEFAULT: - default: - return "0"; - } -} - gboolean nm_supplicant_config_fast_required (NMSupplicantConfig *self) { @@ -385,8 +357,6 @@ gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, NMSettingWireless * setting, guint32 fixed_freq, - NMSupplicantFeature mac_randomization_support, - NMSettingMacRandomization mac_randomization_fallback, GError **error) { NMSupplicantConfigPrivate *priv; @@ -477,23 +447,6 @@ nm_supplicant_config_add_setting_wireless (NMSupplicantConfig * self, } } - priv->mac_randomization = nm_setting_wireless_get_mac_address_randomization (setting); - if (priv->mac_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) { - priv->mac_randomization = mac_randomization_fallback; - if (priv->mac_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) { - /* Don't use randomization, unless explicitly enabled. - * Randomization can work badly with captive portals. */ - priv->mac_randomization = NM_SETTING_MAC_RANDOMIZATION_NEVER; - } - } - - if ( priv->mac_randomization != NM_SETTING_MAC_RANDOMIZATION_NEVER - && mac_randomization_support != NM_SUPPLICANT_FEATURE_YES) { - g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, - "cannot enable mac-randomization due to missing supplicant support"); - return FALSE; - } - return TRUE; } diff --git a/src/supplicant-manager/nm-supplicant-config.h b/src/supplicant-manager/nm-supplicant-config.h index 921bc16cbc..32589930c7 100644 --- a/src/supplicant-manager/nm-supplicant-config.h +++ b/src/supplicant-manager/nm-supplicant-config.h @@ -54,8 +54,6 @@ NMSupplicantConfig *nm_supplicant_config_new (void); guint32 nm_supplicant_config_get_ap_scan (NMSupplicantConfig *self); -const char *nm_supplicant_config_get_mac_randomization (NMSupplicantConfig *self); - gboolean nm_supplicant_config_fast_required (NMSupplicantConfig *self); GVariant *nm_supplicant_config_to_variant (NMSupplicantConfig *self); @@ -65,8 +63,6 @@ GHashTable *nm_supplicant_config_get_blobs (NMSupplicantConfig *self); gboolean nm_supplicant_config_add_setting_wireless (NMSupplicantConfig *self, NMSettingWireless *setting, guint32 fixed_freq, - NMSupplicantFeature mac_randomization_support, - NMSettingMacRandomization mac_randomization_fallback, GError **error); gboolean nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self, diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c index 59e0ef3905..626472d072 100644 --- a/src/supplicant-manager/nm-supplicant-interface.c +++ b/src/supplicant-manager/nm-supplicant-interface.c @@ -503,12 +503,6 @@ nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, priv->ap_support = ap_support; } -NMSupplicantFeature -nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *self) -{ - return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->mac_randomization_support; -} - static void set_preassoc_scan_mac_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { @@ -563,7 +557,7 @@ iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data g_variant_new ("(ssv)", WPAS_DBUS_IFACE_INTERFACE, "PreassocMacAddr", - g_variant_new_string ("1")), + g_variant_new_string ("0")), G_DBUS_CALL_FLAGS_NONE, -1, priv->init_cancellable, @@ -1223,9 +1217,7 @@ set_mac_randomization_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user return; } - _LOGI ("config: set MAC randomization to %s", - nm_supplicant_config_get_mac_randomization (priv->cfg)); - + _LOGT ("config: set MAC randomization to 0"); add_network (self); } @@ -1256,23 +1248,20 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) nm_supplicant_config_get_ap_scan (priv->cfg)); if (priv->mac_randomization_support == NM_SUPPLICANT_FEATURE_YES) { - const char *mac_randomization = nm_supplicant_config_get_mac_randomization (priv->cfg); - /* Enable/disable association MAC address randomization */ g_dbus_proxy_call (priv->iface_proxy, DBUS_INTERFACE_PROPERTIES ".Set", g_variant_new ("(ssv)", WPAS_DBUS_IFACE_INTERFACE, "MacAddr", - g_variant_new_string (mac_randomization)), + g_variant_new_string ("0")), G_DBUS_CALL_FLAGS_NONE, -1, priv->assoc_cancellable, (GAsyncReadyCallback) set_mac_randomization_cb, self); - } else { + } else add_network (self); - } } gboolean diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h index dbe77b1c1b..d514c53bc9 100644 --- a/src/supplicant-manager/nm-supplicant-interface.h +++ b/src/supplicant-manager/nm-supplicant-interface.h @@ -165,6 +165,4 @@ NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterfac void nm_supplicant_interface_set_ap_support (NMSupplicantInterface *self, NMSupplicantFeature apmode); -NMSupplicantFeature nm_supplicant_interface_get_mac_randomization_support (NMSupplicantInterface *self); - #endif /* NM_SUPPLICANT_INTERFACE_H */ diff --git a/src/supplicant-manager/tests/test-supplicant-config.c b/src/supplicant-manager/tests/test-supplicant-config.c index 0e53080273..e8f8444863 100644 --- a/src/supplicant-manager/tests/test-supplicant-config.c +++ b/src/supplicant-manager/tests/test-supplicant-config.c @@ -160,8 +160,6 @@ test_wifi_open (void) g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0, - NM_SUPPLICANT_FEATURE_UNKNOWN, - NM_SETTING_MAC_RANDOMIZATION_DEFAULT, &error)); g_assert_no_error (error); g_test_assert_expected_messages (); @@ -265,8 +263,6 @@ test_wifi_wep_key (const char *detail, g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0, - NM_SUPPLICANT_FEATURE_UNKNOWN, - NM_SETTING_MAC_RANDOMIZATION_DEFAULT, &error)); g_assert_no_error (error); g_test_assert_expected_messages (); @@ -410,8 +406,6 @@ test_wifi_wpa_psk (const char *detail, g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0, - NM_SUPPLICANT_FEATURE_UNKNOWN, - NM_SETTING_MAC_RANDOMIZATION_DEFAULT, &error)); g_assert_no_error (error); g_test_assert_expected_messages (); @@ -557,8 +551,6 @@ test_wifi_eap (void) g_assert (nm_supplicant_config_add_setting_wireless (config, s_wifi, 0, - NM_SUPPLICANT_FEATURE_UNKNOWN, - NM_SETTING_MAC_RANDOMIZATION_DEFAULT, &error)); g_assert_no_error (error); g_test_assert_expected_messages (); diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c index 04ccaec214..1ae964eef2 100644 --- a/src/tests/config/nm-test-device.c +++ b/src/tests/config/nm-test-device.c @@ -38,17 +38,6 @@ nm_test_device_init (NMTestDevice *self) /* We jump over NMDevice's construct/destruct methods, which require NMPlatform * and NMConnectionProvider to be initialized. */ - -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - return PARENT_CLASS->constructor (type, - n_construct_params, - construct_params); -} - static void constructed (GObject *object) { @@ -73,7 +62,6 @@ nm_test_device_class_init (NMTestDeviceClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); - object_class->constructor = constructor; object_class->constructed = constructed; object_class->dispose = dispose; @@ -85,6 +73,6 @@ nm_test_device_new (const char *hwaddr) { return g_object_new (NM_TYPE_TEST_DEVICE, NM_DEVICE_IFACE, "dummy", - NM_DEVICE_HW_ADDRESS, hwaddr, + NM_DEVICE_PERM_HW_ADDRESS, hwaddr, NULL); } diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index 2cd8b8588d..000a9d1ef4 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -34,21 +34,93 @@ test_stable_privacy (void) struct in6_addr addr1; inet_pton (AF_INET6, "1234::", &addr1); - _set_stable_privacy (&addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL); + _set_stable_privacy (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "1234::4ceb:14cd:3d54:793f"); /* We get an address without the UUID. */ inet_pton (AF_INET6, "1::", &addr1); - _set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL); + _set_stable_privacy (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "1::11aa:2530:9144:dafa"); /* We get a different address in a different network. */ inet_pton (AF_INET6, "2::", &addr1); - _set_stable_privacy (&addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL); + _set_stable_privacy (NM_UTILS_STABLE_TYPE_UUID, &addr1, "eth666", NULL, 384, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "2::338e:8d:c11:8726"); + + inet_pton (AF_INET6, "1234::", &addr1); + _set_stable_privacy (NM_UTILS_STABLE_TYPE_STABLE_ID, &addr1, "eth666", "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", 0, (guint8 *) "key", 3, NULL); + nmtst_assert_ip6_address (&addr1, "1234::ad4c:ae44:3d30:af1e"); + + 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 *current_mac_address, + const char *generate_mac_address_mask, + const char **expected) +{ + gs_free char *generated = NULL; + const char **e; + gboolean found = FALSE; + + 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, + current_mac_address, + generate_mac_address_mask); + + g_assert (generated); + g_assert (nm_utils_hwaddr_valid (generated, ETH_ALEN)); + 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, 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", "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"); +} + +/*****************************************************************************/ NMTST_DEFINE (); @@ -58,6 +130,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 (); }