From 3cda2df12b6a5e3c4c62415c50eb45cd1b1b63f8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 22 Jun 2016 13:14:10 +0200 Subject: [PATCH 01/43] config: add support for per-device configuration to NetworkManager.conf Add a new [device*] section to NetworkManager.conf. This works similar like the default connection settings in [connection*]. This will allow us to express per-device configuration in NetworkManager.conf in our familar style. Later, via NMConfig's write API it will be possible to make settings accessible via D-Bus and persist them in NetworkManager-intern.conf. This way, the user can both edit configuration snippets and modify them via D-Bus, and also support installing default configuration from the package. In a way, a [device*] setting is similar to networkd's link files. The match options is all encoded in the match-device specs. One difference is, that the resulting setting can be merged together by multiple section by partially overwriting them. This makes it more flexible and allows for example to drop a configuration snippet that only sets one property, while the rest can be merged from different snippets. --- man/NetworkManager.conf.xml | 36 ++++++- src/nm-config-data.c | 194 +++++++++++++++++++++++++++--------- src/nm-config-data.h | 11 ++ src/nm-config.c | 46 +++++---- src/nm-config.h | 1 + 5 files changed, 223 insertions(+), 65 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 9fa89aa443..580bdd3c9e 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -623,7 +623,7 @@ ipv6.ip6-privacy=0 - + Sections You can configure multiple connection @@ -696,6 +696,40 @@ 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. + + + + + + + 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 diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 28057b47a6..37686479c5 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; @@ -451,6 +455,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 +493,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 +1076,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 +1097,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 +1189,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 +1235,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 +1411,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 +1430,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 +1458,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..eb827c33ae 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -576,14 +576,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 +591,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 +641,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..acd0e5ff41 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" From c7cee12189d8fe64bc9037d5b9daaf1c887e0466 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 22 Jun 2016 13:43:08 +0200 Subject: [PATCH 02/43] config: make "ignore-carrier" a per-device configuration option NetworkManager.conf already contains several per-device settings, that is, settings that have a device-spec as argument. main.ignore-carrier main.no-auto-default main.assume-ipv6ll-only keyfile.unmanged-devices Optimally, these settings should be moved to the new [device*] section. For now, only move main.ignore-carrier there. For the others it may not make sense to do so: - main.no-auto-default: is already merged with internal state from /var/lib/NetworkManager/no-auto-default.state. While NMConfig's write API would be fine to also persist and merge the no-auto-default setting, we'd still have to read the old file too. Thus, deprecating this setting gets quite cumbersome to still handle the old state file. Also, it seems a less useful setting to configure in the global configuration aside setting main.no-auto-default=*. - main.assume-ipv6ll-only: one day, I hope that we no longer assume connections at all, and this setting becomes entirely obsolete. - keyfile.unmanged-devices: this sets NM_UNMANAGED_USER_SETTINGS, which cannot be overruled via D-Bus. For a future device.managed setting we want it it to be overwritable via D-Bus by an explicit user action. Thus, a device.managed property should have a different semantic, this should be more like a device.unmanaged-force setting, which could be done. --- man/NetworkManager.conf.xml | 65 +++++++++++++++++++++++-------------- src/nm-config-data.c | 7 ++++ src/nm-config.h | 2 ++ 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 580bdd3c9e..9f5906efed 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. @@ -713,6 +695,38 @@ unmanaged=1 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. + + + @@ -1012,7 +1026,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: diff --git a/src/nm-config-data.c b/src/nm-config-data.c index 37686479c5..23c4cecdc9 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -272,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); } diff --git a/src/nm-config.h b/src/nm-config.h index acd0e5ff41..a75e393523 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -70,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." From 3f3ea1df21d3fe686e435f9c0afc3ed5dcd97253 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 21 Jun 2016 16:41:05 +0200 Subject: [PATCH 03/43] libnm: add NMSettingConnection:stable-id property This new property be used as token to generate stable-ids instead of the connection's UUID. Later, this will be used by ipv6.addr-gen-mode=stable-privacy, ethernet.cloned-mac-address=stable, and wifi.cloned-mac-address=stable setting. Those generate stable addresses based on the connection's UUID, but allow to use the stable-id instead. This allows multiple connections to generate the same addresses -- on the same machine, because in the above cases a machine dependant key is also hashed. --- clients/cli/settings.c | 71 +++++++++++++++----------- libnm-core/nm-setting-connection.c | 52 ++++++++++++++++++- libnm-core/nm-setting-connection.h | 3 ++ libnm-core/tests/test-general.c | 1 + libnm/libnm.ver | 1 + src/settings/plugins/ifcfg-rh/reader.c | 4 ++ src/settings/plugins/ifcfg-rh/writer.c | 1 + 7 files changed, 102 insertions(+), 31 deletions(-) diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 08a5d087e1..6573e0b2bc 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -44,26 +44,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","\ @@ -1252,6 +1254,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) @@ -6217,6 +6220,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, @@ -8063,21 +8073,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 */ diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index e1cd913aad..681a9e2f0e 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,28 @@ 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. + * + * 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/tests/test-general.c b/libnm-core/tests/test-general.c index 9aaf82977d..7ff6b8d235 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 }, diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 41f650f008..47f6598ff6 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1066,6 +1066,7 @@ 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_vpn_editor_plugin_get_plugin_info; diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index b933e1a5fa..192327043b 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); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 2864078a48..2de8a9a1f7 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1800,6 +1800,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", From 0df5e9b7367564eea99555ddb4af90a8baf25f71 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 21 Jun 2016 18:07:00 +0200 Subject: [PATCH 04/43] rdisc/trivial: rename @uuid field to @network_id Next we will optionally use a stable-id instead of the UUID. Rename it. Also, RFC 7217 calls this argument Network_ID. --- src/nm-core-utils.c | 12 ++++++------ src/nm-core-utils.h | 2 +- src/rdisc/nm-lndp-rdisc.c | 4 ++-- src/rdisc/nm-lndp-rdisc.h | 2 +- src/rdisc/nm-rdisc.c | 4 ++-- src/rdisc/nm-rdisc.h | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index b20653838d..2d340b8caa 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2955,7 +2955,7 @@ nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid, char *buf) static gboolean _set_stable_privacy (struct in6_addr *addr, const char *ifname, - const char *uuid, + const char *network_id, guint dad_counter, guint8 *secret_key, gsize key_len, @@ -2981,9 +2981,9 @@ _set_stable_privacy (struct in6_addr *addr, 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)); @@ -3011,7 +3011,7 @@ _set_stable_privacy (struct in6_addr *addr, gboolean nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, const char *ifname, - const char *uuid, + const char *network_id, guint dad_counter, GError **error) { @@ -3028,7 +3028,7 @@ 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 (addr, ifname, network_id, dad_counter, secret_key, key_len, error); } diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index ce22439b1b..18ef0766ef 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -355,7 +355,7 @@ gboolean nm_utils_get_ipv6_interface_identifier (NMLinkType link_type, gboolean nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, const char *ifname, - const char *uuid, + const char *network_id, guint dad_counter, GError **error); diff --git a/src/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index 0c5971f42b..f1607d115b 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -321,7 +321,7 @@ NMRDisc * nm_lndp_rdisc_new (NMPlatform *platform, int ifindex, const char *ifname, - const char *uuid, + const char *network_id, NMSettingIP6ConfigAddrGenMode addr_gen_mode, GError **error) { @@ -342,7 +342,7 @@ nm_lndp_rdisc_new (NMPlatform *platform, rdisc->ifindex = ifindex; rdisc->ifname = g_strdup (ifname); - rdisc->uuid = g_strdup (uuid); + 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..3fe0f0d823 100644 --- a/src/rdisc/nm-lndp-rdisc.h +++ b/src/rdisc/nm-lndp-rdisc.h @@ -47,7 +47,7 @@ GType nm_lndp_rdisc_get_type (void); NMRDisc *nm_lndp_rdisc_new (NMPlatform *platform, int ifindex, const char *ifname, - const char *uuid, + 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..3a54f5a1c0 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -142,7 +142,7 @@ complete_address (NMRDisc *rdisc, NMRDiscAddress *addr) if (rdisc->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) { if (!nm_utils_ipv6_addr_set_stable_privacy (&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 +777,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..604ea65b1e 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -118,7 +118,7 @@ typedef struct { int ifindex; char *ifname; - char *uuid; + char *network_id; NMSettingIP6ConfigAddrGenMode addr_gen_mode; NMUtilsIPv6IfaceId iid; gint32 max_addresses; From 0a5af391e0c089faec0e7479b11a0b6542a86923 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 21 Jun 2016 18:07:56 +0200 Subject: [PATCH 05/43] core: prefer connection.stable-id to generate IPv6 stable privacy addresses The Network_ID for generating RFC 7217 stable privacy IPv6 addresses is by default the UUID of the connection. Alternatively, prefer "connection.stable-id" as Network_ID to generate the stable addresses. This allows to configure a set of connections that all use the same Network_ID for generating stable addresses. Note that the stable-id and the UUID do no overlap, that is two connections [connection] uuid=uuid1 stable-id= and [connection] uuid=uuid2 stable-id=uuid1 generate distinct addresses. --- libnm-core/nm-setting-connection.c | 3 ++ src/devices/nm-device.c | 66 +++++++++++++++++++++++++----- src/nm-core-utils.c | 25 +++++++++-- src/nm-core-utils.h | 8 +++- src/nm-iface-helper.c | 22 ++++++++-- src/rdisc/nm-lndp-rdisc.c | 2 + src/rdisc/nm-lndp-rdisc.h | 2 + src/rdisc/nm-rdisc.c | 3 +- src/rdisc/nm-rdisc.h | 2 + src/rdisc/tests/test-rdisc-linux.c | 1 + src/tests/test-utils.c | 15 +++++-- 11 files changed, 127 insertions(+), 22 deletions(-) diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index 681a9e2f0e..a8f6e87653 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -1413,6 +1413,9 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) * 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. + * * Since: 1.4 **/ /* ---ifcfg-rh--- diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 5fd0bf343e..7624c63558 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -616,6 +616,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) { @@ -5859,11 +5883,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 +6235,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 +6250,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); @@ -10622,6 +10658,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 +10678,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, diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 2d340b8caa..27c162886c 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2953,7 +2953,8 @@ 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 *network_id, guint dad_counter, @@ -2979,6 +2980,19 @@ _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 (!network_id) @@ -3009,7 +3023,8 @@ _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 *network_id, guint dad_counter, @@ -3018,6 +3033,10 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, 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,7 +3047,7 @@ nm_utils_ipv6_addr_set_stable_privacy (struct in6_addr *addr, if (!secret_key) return FALSE; - return _set_stable_privacy (addr, ifname, network_id, dad_counter, + return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, secret_key, key_len, error); } diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 18ef0766ef..154e88331e 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -353,7 +353,13 @@ 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 *network_id, guint dad_counter, 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/rdisc/nm-lndp-rdisc.c b/src/rdisc/nm-lndp-rdisc.c index f1607d115b..fca61f79ad 100644 --- a/src/rdisc/nm-lndp-rdisc.c +++ b/src/rdisc/nm-lndp-rdisc.c @@ -321,6 +321,7 @@ NMRDisc * nm_lndp_rdisc_new (NMPlatform *platform, int ifindex, const char *ifname, + NMUtilsStableType stable_type, const char *network_id, NMSettingIP6ConfigAddrGenMode addr_gen_mode, GError **error) @@ -342,6 +343,7 @@ nm_lndp_rdisc_new (NMPlatform *platform, rdisc->ifindex = ifindex; rdisc->ifname = g_strdup (ifname); + rdisc->stable_type = stable_type; rdisc->network_id = g_strdup (network_id); rdisc->addr_gen_mode = addr_gen_mode; diff --git a/src/rdisc/nm-lndp-rdisc.h b/src/rdisc/nm-lndp-rdisc.h index 3fe0f0d823..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,6 +48,7 @@ GType nm_lndp_rdisc_get_type (void); NMRDisc *nm_lndp_rdisc_new (NMPlatform *platform, int ifindex, const char *ifname, + 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 3a54f5a1c0..c11557d943 100644 --- a/src/rdisc/nm-rdisc.c +++ b/src/rdisc/nm-rdisc.c @@ -140,7 +140,8 @@ 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->network_id, addr->dad_counter++, diff --git a/src/rdisc/nm-rdisc.h b/src/rdisc/nm-rdisc.h index 604ea65b1e..8c14576bcb 100644 --- a/src/rdisc/nm-rdisc.h +++ b/src/rdisc/nm-rdisc.h @@ -116,6 +116,8 @@ typedef struct { NMPlatform *_platform; NMPNetns *_netns; + NMUtilsStableType stable_type; + int ifindex; char *ifname; char *network_id; 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/tests/test-utils.c b/src/tests/test-utils.c index 2cd8b8588d..3d60668259 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -34,18 +34,27 @@ 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"); + } /*******************************************/ From c9ab22f41d6ae61ad9110a7e0fc4d198a55ad755 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 20 Jun 2016 12:00:28 +0200 Subject: [PATCH 06/43] wifi: move static lookup-array for is_manf_default_ssid() --- src/devices/wifi/nm-device-wifi.c | 44 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 781ecb7e36..8db14d8f20 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -685,35 +685,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; From 807f84661090588fea299297048ee8c8e76355ac Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 11 Jun 2016 00:31:26 +0200 Subject: [PATCH 07/43] libnm: fix comparing NMSettingIPConfig for address and route properties When comparing settings, nm_setting_compare() performs a complicated logic, which basically serializes each GObject property to a GVariant for the D-Bus representation. That is wrong for example for ipv4.addresses, which don't contain address labels. That is, the GObject property is called "addresses", but the D-Bus field "addresses" cannot encode every information and thus comparison fails. Instead, it would have to look into "address-data". Traditionally, we have virtual functions like compare_property() per NMSetting to do the comparison. That comparison is based on the GObject properties. I think that is wrong, because we should have a generic concept of what a property is, independent from GObject properties. With libnm, we added NMSettingProperty, which indeed is such an GObject independent representation to define properties. However, it is not used thoroughly, instead compare_property() is a hack of special cases, overloads from NMSettingProperty, overloads of compare_property(), and default behavior based on GParamSpec. This should be cleaned up. For now, just hack it by handle the properties with the problems explicitly. --- libnm-core/nm-setting-ip-config.c | 146 +++++++++++++++++++++++++++--- libnm-core/tests/test-general.c | 66 ++++++++++++++ 2 files changed, 197 insertions(+), 15 deletions(-) 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/tests/test-general.c b/libnm-core/tests/test-general.c index 7ff6b8d235..9d263dcb38 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -2385,6 +2385,70 @@ 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_timestamp (void) { @@ -5136,6 +5200,8 @@ 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_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, \ From 05ecb28436d06ad1b39dc5a0e7e2e5899c575948 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 2 Jun 2016 10:57:23 +0200 Subject: [PATCH 08/43] core: make global variable _nm_utils_testing static --- src/nm-core-utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 27c162886c..2b1eb36401 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -54,7 +54,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 () From fc527a237cc9619a12e8abe14757087487b2e588 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 25 May 2016 10:46:48 +0200 Subject: [PATCH 09/43] libnm: add internal util function _nm_utils_hwaddr_length() --- libnm-core/nm-core-internal.h | 2 ++ libnm-core/nm-utils.c | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 7ae126c1bc..96624d05b8 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); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 4bcde68474..21cbe337a1 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -3114,6 +3114,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 From c7b7305b5938b8a4b8b77f65c459f5c89708c2d0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 15 Jun 2016 11:23:39 +0200 Subject: [PATCH 10/43] core/utils: convert MAC address in nm_match_spec_hwaddr() from string once --- src/nm-core-utils.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 2b1eb36401..eb85fa0eb7 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -1271,8 +1271,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 +1295,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; From dab657043caf65afcf3e8c2e639d4e977d25ba6f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 2 Jun 2016 09:48:14 +0200 Subject: [PATCH 11/43] ifcfg-rh: explicitly clear HWADDR setting and others in write_wired_for_virtual() When modifying an existing ifcfg-rh file, we always want to enforce the absense of a certain setting. That is done, by calling svSetValue() with a value of NULL. Same for writing MTU value. --- src/settings/plugins/ifcfg-rh/writer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 2de8a9a1f7..1683a0e33c 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1255,19 +1255,18 @@ 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); 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; } From dcc8de16b2acc43b2a9155fcfb91fa2602f3a401 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jun 2016 13:56:18 +0200 Subject: [PATCH 12/43] core: add utils for file handling Copied and adjusted from systemd code. --- src/nm-core-utils.c | 118 ++++++++++++++++++++++++++++++++++++++++++++ src/nm-core-utils.h | 6 +++ 2 files changed, 124 insertions(+) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index eb85fa0eb7..0a913e9c8e 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2711,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) { diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 154e88331e..86b4fe6ad2 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); From 83d231776b71b12103c2fbebfb29b052f796c87e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jun 2016 15:07:43 +0200 Subject: [PATCH 13/43] core: use nm_utils_read_urandom() in nm_utils_secret_key_read() nm_utils_read_urandom() repeats on EINTR and repeats for partial reads. --- src/nm-core-utils.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 0a913e9c8e..bb15ca387a 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2847,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: From 76aa6f8e0defe8811f9bb28d4ec433b833b80d77 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 8 Jun 2016 00:56:13 +0200 Subject: [PATCH 14/43] libnm: don't serialize empty hardware address on D-Bus _nm_utils_hwaddr_to_dbus() would serialize invalid hardware addresses as "'cloned-mac-address': <@ay []>". An empty array is treated the same as no hardware address set, so we should not serialize it in the first place. This is a change in behavior on how the connection is exported on D-Bus, but it should not have any bad consequences. We need this as we later want to deprecate the 'cloned-mac-address' D-Bus field and overwrite it via a 'assigned-mac-address' field. In this case, the "<@ay []>" is interfering. While it could be worked around by treating an empty MAC address as "unset", fix it instead and just not serialize it. --- libnm-core/nm-utils.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 21cbe337a1..5c4bfb50b9 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -3316,17 +3316,20 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, GVariant * _nm_utils_hwaddr_to_dbus (const GValue *prop_value) { - const char *str = g_value_get_string (prop_value); + const char *str; 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; + str = g_value_get_string (prop_value); + 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); } From 89970b5ca6a60f3343d42c427d0ab53f9a8b08ba Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 2 Jun 2016 22:16:54 +0200 Subject: [PATCH 15/43] device: refactor nm_device_get_applied_setting() --- src/devices/nm-device.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 7624c63558..16e3c3fb80 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1130,7 +1130,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; } @@ -1147,23 +1151,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 From e5637dc0897c9d1b0a2470795580465cf0703b00 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 13:17:41 +0200 Subject: [PATCH 16/43] device: clear initial_hw_addr in nm_device_update_initial_hw_address() There was no leak here, because we would only call nm_device_update_initial_hw_address() when @initial_hw_addr is unset. However, still clear it to make it more robust against later changes. --- src/devices/nm-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 16e3c3fb80..0d7b32e428 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11384,6 +11384,7 @@ nm_device_update_initial_hw_address (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if (priv->hw_addr_len) { + g_free (priv->initial_hw_addr); priv->initial_hw_addr = g_strdup (priv->hw_addr); _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); From 224937f5dddb1c3a2e6af195f2c58ef05a81d5c9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 09:12:40 +0200 Subject: [PATCH 17/43] device: always set "cloned-mac-address" even with missing NMSettingWired When the entire NMSettingWired setting is missing, it should be treated exactly the same as each property having the default/unset value. Otherwise, adding a NMSettingWired setting only to set (say) MTU, would result in different behavior. Although effectively the "cloned-mac-address" shall be in both cases the same. --- src/devices/nm-device-ethernet.c | 8 +++----- src/devices/nm-device-macvlan.c | 8 +++----- src/devices/nm-device-tun.c | 8 +++----- src/devices/nm-device-vlan.c | 8 +++----- src/devices/nm-device-vxlan.c | 8 +++----- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 1367555b3e..a3e88f390b 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -814,7 +814,7 @@ 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; + const char *cloned_mac = NULL; NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -822,11 +822,9 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) 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 */ + if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_ETHER); - } + nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_ETHER); /* If we're re-activating a PPPoE connection a short while after * a previous PPPoE connection was torn down, wait a bit to allow the diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index c5443ecd23..d2ebc38c34 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -504,7 +504,7 @@ static NMActStageReturn act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMSettingWired *s_wired; - const char *cloned_mac; + const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -514,11 +514,9 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) 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 */ + if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_HW); - } + nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_HW); return TRUE; } diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 01cd8ea6a3..63b3fd6173 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -292,7 +292,7 @@ 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; + const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -306,11 +306,9 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) 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 */ + if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); - } + nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 95f36298f5..ca3ed2cadc 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -551,7 +551,7 @@ 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; + const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -561,11 +561,9 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) 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 */ + if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_VLAN); - } + nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_VLAN); /* Change MAC address to parent's one if needed */ if (priv->parent) diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index ba2b2d68d7..ffd05ac678 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -512,7 +512,7 @@ static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { NMSettingWired *s_wired; - const char *cloned_mac; + const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -522,11 +522,9 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) 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 */ + if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); - } + nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); return NM_ACT_STAGE_RETURN_SUCCESS; } From fa5230e25537481854e023062fbc7869c855c746 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 09:41:40 +0200 Subject: [PATCH 18/43] device: refactor setting HW address via nm_device_set_hw_addr() This brings no real change in behavior, except getting rid of the logging domain argument. --- src/devices/nm-device-ethernet.c | 6 +-- src/devices/nm-device-macvlan.c | 8 +--- src/devices/nm-device-private.h | 4 +- src/devices/nm-device-tun.c | 2 +- src/devices/nm-device-vlan.c | 8 ++-- src/devices/nm-device-vxlan.c | 2 +- src/devices/nm-device.c | 76 ++++++++++++++++++++++++------- src/devices/wifi/nm-device-wifi.c | 6 +-- 8 files changed, 72 insertions(+), 40 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index a3e88f390b..e8d826e4be 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -824,7 +824,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_ETHER); + nm_device_hw_addr_set (dev, cloned_mac); /* 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 @@ -1364,9 +1364,7 @@ deactivate (NMDevice *device) 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); + nm_device_hw_addr_reset (device); } static gboolean diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index d2ebc38c34..f829f99c31 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -516,7 +516,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_HW); + nm_device_hw_addr_set (dev, cloned_mac); return TRUE; } @@ -550,11 +550,7 @@ realize_start_notify (NMDevice *device, const NMPlatformLink *plink) 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); - } + nm_device_hw_addr_reset (device); } /******************************************************************/ diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 418ae2d9ca..4d4bd720ac 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -56,8 +56,8 @@ 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); +gboolean nm_device_hw_addr_reset (NMDevice *device); void nm_device_set_firmware_missing (NMDevice *self, gboolean missing); diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 63b3fd6173..238d3a97b5 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -308,7 +308,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); + nm_device_hw_addr_set (device, cloned_mac); return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index ca3ed2cadc..2d6b64064f 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -114,7 +114,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); /* When changing the hw address the interface is taken down, * removing the IPv6 configuration; reapply it. */ @@ -563,7 +563,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_VLAN); + nm_device_hw_addr_set (dev, cloned_mac); /* Change MAC address to parent's one if needed */ if (priv->parent) @@ -620,9 +620,7 @@ 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); + nm_device_hw_addr_reset (device); } /******************************************************************/ diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index ffd05ac678..4bdada349d 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -524,7 +524,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); if (s_wired) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); + nm_device_hw_addr_set (device, cloned_mac); return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 0d7b32e428..22af8b17ea 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11406,49 +11406,58 @@ nm_device_update_initial_hw_address (NMDevice *self) } } -gboolean -nm_device_set_hw_addr (NMDevice *self, const char *addr, - const char *detail, guint64 hw_log_domain) +static gboolean +_hw_addr_set (NMDevice *self, + const char *addr, + const char *detail) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; gboolean success = FALSE; - const char *cur_addr = nm_device_get_hw_address (self); + const char *cur_addr; guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; + guint hw_addr_len; - /* Fall back to the permanent address */ - if (!addr) - addr = priv->perm_hw_addr; - if (!addr) - return FALSE; + nm_assert (NM_IS_DEVICE (self)); + nm_assert (addr); + nm_assert (detail); + + 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, "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); + + 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)) { + _LOGW (LOGD_DEVICE, "invalid MAC address %s", addr); return 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", + _LOGI (LOGD_DEVICE, "%s MAC address to %s", detail, addr); } else { - _LOGW (LOGD_DEVICE | hw_log_domain, + _LOGW (LOGD_DEVICE, "new MAC address %s not successfully set", addr); success = FALSE; } } else { - _LOGW (LOGD_DEVICE | hw_log_domain, "failed to %s MAC address to %s", + _LOGW (LOGD_DEVICE, "failed to %s MAC address to %s", detail, addr); } nm_device_bring_up (self, TRUE, NULL); @@ -11456,6 +11465,39 @@ nm_device_set_hw_addr (NMDevice *self, const char *addr, return success; } +gboolean +nm_device_hw_addr_set (NMDevice *self, const char *addr) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!addr) { + addr = priv->perm_hw_addr; + if (!addr) + return FALSE; + } + return _hw_addr_set (self, addr, "set"); +} + +gboolean +nm_device_hw_addr_reset (NMDevice *self) +{ + NMDevicePrivate *priv; + const char *addr; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + addr = priv->initial_hw_addr; + if (!addr) + return FALSE; + return _hw_addr_set (self, addr, "reset"); +} + const char * nm_device_get_permanent_hw_address (NMDevice *self) { diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 8db14d8f20..4bdc6e8e2a 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -491,9 +491,7 @@ 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); + nm_device_hw_addr_reset (device); /* Ensure we're in infrastructure mode after deactivation; some devices * (usually older ones) don't scan well in adhoc mode. @@ -2328,7 +2326,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) /* Set spoof MAC to the interface */ cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); - nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_WIFI); + nm_device_hw_addr_set (device, cloned_mac); /* AP mode never uses a specific object or existing scanned AP */ if (priv->mode != NM_802_11_MODE_AP) { From e92b743ce926a49d84847d7c951cbc0ee343d19c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 10:09:39 +0200 Subject: [PATCH 19/43] device: don't use g_warning for differing hw-addr-len after reading permanent address Accessing the platform cache might anytime yield unexpected results. E.g. the link could be gone, or the ifindex could even be replaced by a different interface (yes, that can happen when moving links between network namespaces). It's not clear how to handle such a case at runtime. It seems wrong to me to just error out. Still, such case might happen under normal conditions, so it's wrong to just warn and proceed. --- src/devices/nm-device.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 22af8b17ea..25bf01d4a6 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11391,15 +11391,20 @@ nm_device_update_initial_hw_address (NMDevice *self) if (priv->ifindex > 0) { guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; size_t len = 0; + gboolean success_read; - if (nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len)) { - g_warn_if_fail (len == priv->hw_addr_len); + success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len); + + if (success_read && 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"); + _LOGD (LOGD_HW | LOGD_ETHER, "%s", + success_read + ? "unable to read permanent MAC address" + : "read HW addr length of permanent MAC address differs"); priv->perm_hw_addr = g_strdup (priv->hw_addr); } } From 6947aedb6e1970e1bd16552fd31fd8be264d33f4 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 12:34:13 +0200 Subject: [PATCH 20/43] device: initialize NMDevice's hw_addr at end of object construction hw-addr is a constuct-only property. We should not do complex stuff in the property setter before the object is sufficiently initialized. For example, the logging macros access nm_device_get_iface(), which might be unset at that early point. Instead, initialize hw_addr and hw_addr_len later, at the end of the constructor() function. Also, ensure that @hw_addr_len is zero iff @hw_addr is unset. Also, ensure that we always log a message when changing/setting the hardware address -- except when clearing it during unrealize. It's implicit that unrealize clears the hardware address. Also, give all related logging messages a "hw-addr:" prefix. --- src/devices/nm-device.c | 61 ++++++++++++++----------------- src/tests/config/nm-test-device.c | 12 ------ 2 files changed, 28 insertions(+), 45 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 25bf01d4a6..2a9f875fef 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2325,6 +2325,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); } @@ -11337,23 +11338,29 @@ 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; + int ifindex; const guint8 *hwaddr; gsize hwaddrlen = 0; + ifindex = nm_device_get_ifindex (self); if (ifindex <= 0) return; + priv = NM_DEVICE_GET_PRIVATE (self); + hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, 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; @@ -11363,7 +11370,7 @@ 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); } } else { @@ -11372,7 +11379,7 @@ nm_device_update_hw_address (NMDevice *self) 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"); + "hw-addr: previous hardware address is no longer valid"); _notify (self, PROP_HW_ADDRESS); } } @@ -11639,6 +11646,7 @@ constructor (GType type, NMDevice *self; NMDevicePrivate *priv; const NMPlatformLink *pllink; + guint count; klass = G_OBJECT_CLASS (nm_device_parent_class); object = klass->constructor (type, n_construct_params, construct_params); @@ -11648,7 +11656,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)) { @@ -11657,6 +11666,17 @@ constructor (GType type, } } + if (priv->hw_addr) { + count = _nm_utils_hwaddr_length (priv->hw_addr); + if (count <= 0) { + _LOGW (LOGD_DEVICE, "hw-addr: could not parse hw-address '%s'", priv->hw_addr); + g_clear_pointer (&priv->hw_addr, g_free); + } else { + priv->hw_addr_len = count; + _LOGT (LOGD_DEVICE, "hw-addr: set current hw-address '%s'", priv->hw_addr); + } + } + return object; } @@ -11812,10 +11832,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: @@ -11895,30 +11913,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_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 = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c index 04ccaec214..be67646fb4 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; From 89d6dfdb96ac396f0965d5e57b8b6ae74bbc686a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 13:33:28 +0200 Subject: [PATCH 21/43] device: split nm_device_update_permanent_hw_address() out of nm_device_update_initial_hw_address() Either, the function is called different to reflect that it does not only update the initial_hw_addres, or it is split. Split it. --- src/devices/nm-device-ethernet.c | 1 + src/devices/nm-device.c | 9 +++++++++ src/devices/nm-device.h | 1 + 3 files changed, 11 insertions(+) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index e8d826e4be..06a7a4e0bc 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1593,6 +1593,7 @@ link_changed (NMDevice *device, NMPlatformLink *info) _LOGD (LOGD_DEVICE, "device got a valid hw address"); nm_device_update_hw_address (self); nm_device_update_initial_hw_address (self); + nm_device_update_permanent_hw_address (self); if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) { /* * If the device is UNAVAILABLE, any previous try to diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 2a9f875fef..3c0fac4895 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2140,6 +2140,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 (); @@ -11394,7 +11395,15 @@ nm_device_update_initial_hw_address (NMDevice *self) g_free (priv->initial_hw_addr); priv->initial_hw_addr = g_strdup (priv->hw_addr); _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); + } +} +void +nm_device_update_permanent_hw_address (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (priv->hw_addr_len) { if (priv->ifindex > 0) { guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; size_t len = 0; diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 4b365d6a60..b6e563c87f 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -578,6 +578,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 From 4bb1e2a536b1e96205a2afc98244644e095b079c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 13:36:09 +0200 Subject: [PATCH 22/43] device: cleanup logging for setting MAC address Give all related messages a "set-hw-addr"/"hw-addr" prefix. --- src/devices/nm-device.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 3c0fac4895..4711069a16 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11391,10 +11391,12 @@ nm_device_update_initial_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->hw_addr_len) { + if ( priv->hw_addr + && !nm_streq0 (priv->initial_hw_addr, priv->hw_addr)) { g_free (priv->initial_hw_addr); priv->initial_hw_addr = g_strdup (priv->hw_addr); - _LOGD (LOGD_DEVICE | LOGD_HW, "read initial MAC address %s", priv->initial_hw_addr); + _LOGD (LOGD_DEVICE, "hw-addr: update initial MAC address %s", + priv->initial_hw_addr); } } @@ -11448,7 +11450,7 @@ _hw_addr_set (NMDevice *self, /* Do nothing if current MAC is same */ if (cur_addr && nm_utils_hwaddr_matches (cur_addr, -1, addr, -1)) { - _LOGD (LOGD_DEVICE, "no MAC address change needed"); + _LOGD (LOGD_DEVICE, "set-hw-addr: no MAC address change needed"); return TRUE; } @@ -11457,10 +11459,12 @@ _hw_addr_set (NMDevice *self, hw_addr_len = _nm_utils_hwaddr_length (addr); if ( !hw_addr_len || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) { - _LOGW (LOGD_DEVICE, "invalid MAC address %s", addr); + _LOGW (LOGD_DEVICE, "set-hw-addr: invalid MAC address %s", addr); return FALSE; } + _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s'...", addr); + /* Can't change MAC address while device is up */ nm_device_take_down (self, FALSE); @@ -11470,15 +11474,15 @@ _hw_addr_set (NMDevice *self, 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, "%s MAC address to %s", + _LOGI (LOGD_DEVICE, "set-hw-addr: %s MAC address to %s", detail, addr); } else { _LOGW (LOGD_DEVICE, - "new MAC address %s not successfully set", addr); + "set-hw-addr: new MAC address %s not successfully set", addr); success = FALSE; } } else { - _LOGW (LOGD_DEVICE, "failed to %s MAC address to %s", + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s", detail, addr); } nm_device_bring_up (self, TRUE, NULL); From 3704197d873a4f7d94947299dd3d750cd6d46e6c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 24 May 2016 13:45:58 +0200 Subject: [PATCH 23/43] device: re-read the current MAC address when the link changes The current MAC address is part of NMPlatformLink in the platform cache. When it changes, we must update the device's current value. Also, the MAC address of NMDeviceEthernet is exposed on D-Bus. That property should show the currently configured MAC address, not a state that was read some time in the past. Also, nm_device_hw_addr_set() compares the current MAC address before resetting it. If that field is out-of-date, nm_device_hw_addr_set() will behave wrongly. NMDeviceEthernet had some special handling in link_changed() that would re-read the MAC addresses and possibly bring up the interface. Move that code to the parent device. --- src/devices/nm-device-ethernet.c | 25 ------------------------- src/devices/nm-device.c | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 06a7a4e0bc..7c03ae6ac5 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1578,35 +1578,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); - nm_device_update_permanent_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 diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 4711069a16..1f6c72406b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1664,6 +1664,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; @@ -1701,6 +1702,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); @@ -1792,6 +1798,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; } @@ -11373,6 +11393,12 @@ nm_device_update_hw_address (NMDevice *self) _LOGD (LOGD_HW | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr); _notify (self, PROP_HW_ADDRESS); + + if (!priv->initial_hw_addr) { + /* when we get a hw_addr the first time, always update our inital + * hw-address as well. */ + nm_device_update_initial_hw_address (self); + } } } else { /* Invalid or no hardware address */ From 2f05353d9e4282c2ef2b9945c0f20a288a21a4b9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 18:10:51 +0200 Subject: [PATCH 24/43] device: re-read initial hw-address before activating connection Previously, we would only once read the initial hardware address during device realization. When a device activates, NetworkManager always sets the MAC address as configured in the cloned-mac-address setting -- or, if unspecified -- it falls back to use the permanent hardware-address instead. Later, when deactivating the device, the MAC address is reset to the "inital MAC address". This patch changes, that the "initial MAC address" is re-read every time before activating the device, contrary to reading it once in the beginning. This allows for a user to first start NetworkManager and later change the MAC address of the device. When activating the device, NM will reset the MAC address for the time the device is active. But when disconnecting, it resets to the user-changed value, not the value when NM was started. https://bugzilla.gnome.org/show_bug.cgi?id=708820 --- src/devices/nm-device.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 1f6c72406b..c2d9224700 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11041,6 +11041,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 From 9fb5558f9683379f04632b5f8b97601339d557c9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 3 Jun 2016 13:37:08 +0200 Subject: [PATCH 25/43] device/trivial: rename hw-addr related fields in NMDevicePrivate Next I will add two more fields. Being able to efficiently grep the code is important. I want to be able to grep for "->hw_addr" or "\perm_hw_addr, g_free); - g_clear_pointer (&priv->initial_hw_addr, g_free); + g_clear_pointer (&priv->hw_addr_perm, g_free); + 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) @@ -11397,7 +11397,7 @@ nm_device_update_hw_address (NMDevice *self) _LOGD (LOGD_HW | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr); _notify (self, PROP_HW_ADDRESS); - if (!priv->initial_hw_addr) { + if (!priv->hw_addr_initial) { /* when we get a hw_addr the first time, always update our inital * hw-address as well. */ nm_device_update_initial_hw_address (self); @@ -11421,11 +11421,11 @@ nm_device_update_initial_hw_address (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); if ( priv->hw_addr - && !nm_streq0 (priv->initial_hw_addr, priv->hw_addr)) { - g_free (priv->initial_hw_addr); - priv->initial_hw_addr = g_strdup (priv->hw_addr); + && !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) { + 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->initial_hw_addr); + priv->hw_addr_initial); } } @@ -11443,16 +11443,16 @@ nm_device_update_permanent_hw_address (NMDevice *self) success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len); if (success_read && len == priv->hw_addr_len) { - priv->perm_hw_addr = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); + priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); _LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s", - priv->perm_hw_addr); + priv->hw_addr_perm); } else { /* Fall back to current address */ _LOGD (LOGD_HW | LOGD_ETHER, "%s", success_read ? "unable to read permanent MAC address" : "read HW addr length of permanent MAC address differs"); - priv->perm_hw_addr = g_strdup (priv->hw_addr); + priv->hw_addr_perm = g_strdup (priv->hw_addr); } } } @@ -11529,7 +11529,7 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr) priv = NM_DEVICE_GET_PRIVATE (self); if (!addr) { - addr = priv->perm_hw_addr; + addr = priv->hw_addr_perm; if (!addr) return FALSE; } @@ -11546,7 +11546,7 @@ nm_device_hw_addr_reset (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE (self); - addr = priv->initial_hw_addr; + addr = priv->hw_addr_initial; if (!addr) return FALSE; return _hw_addr_set (self, addr, "reset"); @@ -11557,7 +11557,7 @@ nm_device_get_permanent_hw_address (NMDevice *self) { g_return_val_if_fail (NM_IS_DEVICE (self), NULL); - return NM_DEVICE_GET_PRIVATE (self)->perm_hw_addr; + return NM_DEVICE_GET_PRIVATE (self)->hw_addr_perm; } const char * @@ -11565,7 +11565,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; } /** @@ -11844,8 +11844,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); From 2a94587232f5ac401cd8d2f13729836f731566f2 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 20 May 2016 13:47:23 +0200 Subject: [PATCH 26/43] device: only set permanent hardware address once While a device is realized, we only want to read the permanent MAC address once. If that fails, we fallback to the current MAC address. Thus, we want the permanent address be stable until the device unrealizes. While we want to fallback to the current MAC address, in some cases the caller wants to know whether this was a "real" permanent MAC address as read via ethtool. For example, when matching an ethernet device against ethernet.mac-address property, the fake (current) address should not be used in such case. --- src/devices/nm-device-ethernet.c | 8 +-- src/devices/nm-device.c | 92 ++++++++++++++++++++----------- src/devices/nm-device.h | 3 +- src/devices/wifi/nm-device-wifi.c | 6 +- 4 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 7c03ae6ac5..f38798234a 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -408,7 +408,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)) @@ -1405,7 +1405,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) { @@ -1502,7 +1502,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; @@ -1636,7 +1636,7 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PERM_HW_ADDRESS: - g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (object))); + g_value_set_string (value, nm_device_get_permanent_hw_address ((NMDevice *) object, FALSE)); break; case PROP_SPEED: g_value_set_uint (value, priv->speed); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index a0a7e41581..6e5660be5c 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -237,8 +237,9 @@ 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; @@ -11371,17 +11372,14 @@ void nm_device_update_hw_address (NMDevice *self) { NMDevicePrivate *priv; - int ifindex; const guint8 *hwaddr; gsize hwaddrlen = 0; - ifindex = nm_device_get_ifindex (self); - if (ifindex <= 0) + priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->ifindex <= 0) return; - priv = NM_DEVICE_GET_PRIVATE (self); - - 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 @@ -11433,29 +11431,49 @@ void nm_device_update_permanent_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + size_t len = 0; + gboolean success_read; - if (priv->hw_addr_len) { - if (priv->ifindex > 0) { - guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; - size_t len = 0; - gboolean success_read; - - success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len); - - if (success_read && len == priv->hw_addr_len) { - priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len); - _LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s", - priv->hw_addr_perm); - } else { - /* Fall back to current address */ - _LOGD (LOGD_HW | LOGD_ETHER, "%s", - success_read - ? "unable to read permanent MAC address" - : "read HW addr length of permanent MAC address differs"); - priv->hw_addr_perm = g_strdup (priv->hw_addr); - } - } + 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); + return; + } + + 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); } static gboolean @@ -11529,7 +11547,7 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr) priv = NM_DEVICE_GET_PRIVATE (self); if (!addr) { - addr = priv->hw_addr_perm; + addr = nm_device_get_permanent_hw_address (self, TRUE); if (!addr) return FALSE; } @@ -11546,18 +11564,26 @@ nm_device_hw_addr_reset (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE (self); - addr = priv->hw_addr_initial; + addr = nm_device_get_initial_hw_address (self); if (!addr) return FALSE; return _hw_addr_set (self, addr, "reset"); } const char * -nm_device_get_permanent_hw_address (NMDevice *self) +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)->hw_addr_perm; + 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 * diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index b6e563c87f..bd8e64a9ab 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -353,7 +353,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); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 4bdc6e8e2a..e126a98e82 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -570,7 +570,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)) @@ -862,7 +862,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) { @@ -2981,7 +2981,7 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PERM_HW_ADDRESS: - g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (device))); + g_value_set_string (value, nm_device_get_permanent_hw_address ((NMDevice *) device, FALSE)); break; case PROP_MODE: g_value_set_uint (value, priv->mode); From 6db3c80aba13e401d0f4ed87f65849b985feb2ed Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 17 Jun 2016 18:53:45 +0200 Subject: [PATCH 27/43] device: implememnt "perm-hw-address" property in NMDevice Both NMDeviceEthernet and NMDeviceWifi have a property "perm-hw-address". As the hw_addr_perm property is tracked in the parent NMDevice class, let it also implement the GObject property. Then it knows better when to emit a notification about property changes. --- src/devices/nm-device-ethernet.c | 20 -------------------- src/devices/nm-device-ethernet.h | 1 - src/devices/nm-device.c | 23 +++++++++++++++++------ src/devices/nm-device.h | 5 +++++ src/devices/wifi/nm-device-wifi.c | 20 -------------------- src/devices/wifi/nm-device-wifi.h | 1 - 6 files changed, 22 insertions(+), 48 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index f38798234a..ad498004d4 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) { @@ -1635,9 +1626,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 ((NMDevice *) object, FALSE)); - break; case PROP_SPEED: g_value_set_uint (value, priv->speed); break; @@ -1679,7 +1667,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; @@ -1698,13 +1685,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.c b/src/devices/nm-device.c index 6e5660be5c..d857288290 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, @@ -2357,6 +2358,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) } 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; @@ -11467,13 +11469,13 @@ nm_device_update_permanent_hw_address (NMDevice *self) priv->hw_addr); priv->hw_addr_perm_fake = TRUE; priv->hw_addr_perm = g_strdup (priv->hw_addr); - return; + } 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); } - - 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 gboolean @@ -12104,6 +12106,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; @@ -12349,6 +12355,11 @@ nm_device_class_init (NMDeviceClass *klass) NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_PERM_HW_ADDRESS] = + g_param_spec_string (NM_DEVICE_PERM_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_HAS_PENDING_ACTION] = g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, "", "", FALSE, diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index bd8e64a9ab..181ad8e1c5 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" diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index e126a98e82..bb5fd30aba 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -71,7 +71,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, @@ -403,14 +402,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) { @@ -2980,9 +2971,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 ((NMDevice *) device, FALSE)); - break; case PROP_MODE: g_value_set_uint (value, priv->mode); break; @@ -3047,7 +3035,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,13 +3057,6 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) 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" From da3f608802e9850c885e3ad96cec4fd97a75bbab Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 17 Jun 2016 19:38:04 +0200 Subject: [PATCH 28/43] device: don't clear the current MAC address When we were able to read a MAC address previously, we would not expect a failure the next time. Say a failure happens. Still, we should not clear the MAC address, because we also determine hw_addr_len based on that address. And hw_addr_perm and hw_addr_initial have the same length. When we allow hw_addr to be reset (and possibly reset to a different address length), we somehow have to re-fresh also the permanent and initial MAC address. Just don't allow for that complexity, when it's not even clear what such a scenario would mean and what do to in that case. --- src/devices/nm-device.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d857288290..0b4432ecfd 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11406,11 +11406,12 @@ nm_device_update_hw_address (NMDevice *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, - "hw-addr: 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"); } } } From 481cdc2706ae9467ca4a2527463e26c539e49e24 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 15 Jun 2016 11:25:29 +0200 Subject: [PATCH 29/43] device: let device specs match on permanent MAC address Using the current, possibly non-permanent MAC address doesn't really make sense. Also, NM_DEVICE_HW_ADDRESS used to be writable and was set by NMDeviceBt to the bdaddr. That is wrong, because bdaddr should not be the current address, but the permanent one. --- man/NetworkManager.conf.xml | 4 ++-- src/devices/bluetooth/nm-device-bt.c | 2 +- src/devices/nm-device.c | 33 ++++++++++++++-------------- src/nm-config.c | 4 +++- src/tests/config/nm-test-device.c | 2 +- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 9f5906efed..b70438ded3 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -1042,7 +1042,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 @@ -1057,7 +1057,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/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.c b/src/devices/nm-device.c index 0b4432ecfd..e9e1e88193 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11632,9 +11632,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, "*")) { @@ -11642,8 +11642,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) { @@ -11717,7 +11719,6 @@ constructor (GType type, NMDevice *self; NMDevicePrivate *priv; const NMPlatformLink *pllink; - guint count; klass = G_OBJECT_CLASS (nm_device_parent_class); object = klass->constructor (type, n_construct_params, construct_params); @@ -11737,15 +11738,15 @@ constructor (GType type, } } - if (priv->hw_addr) { - count = _nm_utils_hwaddr_length (priv->hw_addr); - if (count <= 0) { - _LOGW (LOGD_DEVICE, "hw-addr: could not parse hw-address '%s'", priv->hw_addr); - g_clear_pointer (&priv->hw_addr, g_free); - } else { - priv->hw_addr_len = count; - _LOGT (LOGD_DEVICE, "hw-addr: set current hw-address '%s'", priv->hw_addr); + 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; @@ -11982,9 +11983,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 */ - priv->hw_addr = g_value_dup_string (value); + priv->hw_addr_perm = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -12354,12 +12355,12 @@ nm_device_class_init (NMDeviceClass *klass) obj_properties[PROP_HW_ADDRESS] = g_param_spec_string (NM_DEVICE_HW_ADDRESS, "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + 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_READABLE | + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_properties[PROP_HAS_PENDING_ACTION] = g_param_spec_boolean (NM_DEVICE_HAS_PENDING_ACTION, "", "", diff --git a/src/nm-config.c b/src/nm-config.c index eb827c33ae..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); diff --git a/src/tests/config/nm-test-device.c b/src/tests/config/nm-test-device.c index be67646fb4..1ae964eef2 100644 --- a/src/tests/config/nm-test-device.c +++ b/src/tests/config/nm-test-device.c @@ -73,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); } From eb3247c09707be41a34987b8d5e0037139f7bfb0 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 15 Jun 2016 13:15:07 +0200 Subject: [PATCH 30/43] core: fix comparing nm_setting_wired_get_mac_address() with permanent MAC address `man nm-settings` says about ethernet.mac-address: If specified, this connection will only apply to the Ethernet device whose permanent MAC address matches. --- man/nm-settings-ifcfg-rh.xsl | 5 +++++ src/settings/nm-settings.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) 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/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 */ From cc4371ef56db94e2ba47f895dcc0c697eea739ba Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 15 Jun 2016 13:43:34 +0200 Subject: [PATCH 31/43] device: fix matching MAC address for VLAN and MACVLAN devices VLAN and MACVLAN devices consider an ethernet.mac-address setting to find the parent device. This setting shall be the permanent MAC address of the device, not the current. --- src/devices/nm-device-factory.h | 4 ++-- src/devices/nm-device-macvlan.c | 5 ++--- src/devices/nm-device-vlan.c | 26 +++++++++++++++----------- src/nm-manager.c | 6 +++--- 4 files changed, 22 insertions(+), 19 deletions(-) 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-macvlan.c b/src/devices/nm-device-macvlan.c index f829f99c31..a238bdcf79 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 diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 2d6b64064f..4cedcd078b 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -378,21 +378,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 diff --git a/src/nm-manager.c b/src/nm-manager.c index fe53ae9b2a..1ab140fd3d 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); } @@ -1054,7 +1054,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; From f578b91a5cd3aa0852ea4ce01f2a060adcc1b32d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 15 Jun 2016 14:11:16 +0200 Subject: [PATCH 32/43] device: let infiniband prefer permanent MAC address for infiniband.mac-address setting Note that usually for infiniband we cannot get a permanent MAC address via ethtool. Thus, nm_device_get_permanent_hw_address() will return the current address due to fallback_fake=TRUE. --- src/devices/nm-device-infiniband.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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; From 1a6d6d56e646c8c148f442f1d089ecf6a6776298 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 15 Jun 2016 17:19:41 +0200 Subject: [PATCH 33/43] device: use permanent MAC address for creating default wired connection --- src/devices/nm-device-ethernet.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index ad498004d4..21a1d5cd2d 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1425,7 +1425,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; @@ -1433,8 +1433,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 (); @@ -1453,7 +1453,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, @@ -1467,7 +1467,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; From 8eed67122c58540360b617eb42d5df8328e21b5d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 24 May 2016 15:57:16 +0200 Subject: [PATCH 34/43] device: extend MAC address handling including randomization for ethernet and wifi Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address" settings. Instead of specifying an explicit MAC address, the additional special values "permanent", "preserve", "random", "random-bia", "stable" and "stable-bia" are supported. "permanent" means to use the permanent hardware address. Previously that was the default if no explict cloned-mac-address was set. The default is thus still "permanent", but it can be overwritten by global configuration. "preserve" means not to configure the MAC address when activating the device. That was actually the default behavior before introducing MAC address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5. "random" and "random-bia" use a randomized MAC address for each connection. "stable" and "stable-bia" use a generated, stable address based on some token. The "bia" suffix says to generate a burned-in address. The stable method by default uses as token the connection UUID, but the token can be explicitly choosen via "stable:" and "stable-bia:". On a D-Bus level, the "cloned-mac-address" is a bytestring and thus cannot express the new forms. It is replaced by the new "assigned-mac-address" field. For the GObject property, libnm's API, nmcli, keyfile, etc. the old name "cloned-mac-address" is still used. Deprecating the old field seems more complicated then just extending the use of the existing "cloned-mac-address" field, although the name doesn't match well with the extended meaning. There is some overlap with the "wifi.mac-address-randomization" setting. https://bugzilla.gnome.org/show_bug.cgi?id=705545 https://bugzilla.gnome.org/show_bug.cgi?id=708820 https://bugzilla.gnome.org/show_bug.cgi?id=758301 --- clients/cli/settings.c | 22 ++++- clients/tui/nmt-mac-entry.c | 43 ++++++++- clients/tui/nmt-mac-entry.h | 8 +- clients/tui/nmt-page-ethernet.c | 2 +- clients/tui/nmt-page-vlan.c | 2 +- clients/tui/nmt-page-wifi.c | 4 +- libnm-core/nm-keyfile-reader.c | 67 ++++++++----- libnm-core/nm-setting-connection.c | 2 + libnm-core/nm-setting-wired.c | 85 ++++++++++++++--- libnm-core/nm-setting-wireless.c | 94 ++++++++++++++---- libnm-core/nm-utils-private.h | 23 +++++ libnm-core/nm-utils.c | 108 ++++++++++++++++++++- libnm-core/tests/test-general.c | 86 +++++++++++++++++ man/NetworkManager.conf.xml | 8 ++ shared/nm-common-macros.h | 15 +++ src/NetworkManagerUtils.c | 10 +- src/devices/nm-device-ethernet.c | 7 +- src/devices/nm-device-macvlan.c | 8 +- src/devices/nm-device-private.h | 3 + src/devices/nm-device-tun.c | 7 +- src/devices/nm-device-vlan.c | 15 +-- src/devices/nm-device-vxlan.c | 7 +- src/devices/nm-device.c | 147 +++++++++++++++++++++++++++-- src/devices/wifi/nm-device-wifi.c | 4 +- src/nm-core-utils.c | 94 ++++++++++++++++++ src/nm-core-utils.h | 5 + src/tests/test-utils.c | 40 +++++++- 27 files changed, 792 insertions(+), 124 deletions(-) diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 6573e0b2bc..d3645ee031 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -24,6 +24,7 @@ #include #include +#include "nm-common-macros.h" #include "utils.h" #include "common.h" #include "nm-vpn-helpers.h" @@ -2889,11 +2890,12 @@ nmc_property_set_ssid (NMSetting *setting, const char *prop, const char *val, GE } static gboolean -nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +_property_set_mac (NMSetting *setting, const char *prop, const char *val, gboolean cloned_mac_addr, GError **error) { g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (!nm_utils_hwaddr_valid (val, ETH_ALEN)) { + if ( (!cloned_mac_addr || !NM_CLONED_MAC_IS_SPECIAL (val)) + && !nm_utils_hwaddr_valid (val, ETH_ALEN)) { g_set_error (error, 1, 0, _("'%s' is not a valid Ethernet MAC"), val); return FALSE; } @@ -2902,6 +2904,18 @@ nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GEr return TRUE; } +static gboolean +nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return _property_set_mac (setting, prop, val, FALSE, error); +} + +static gboolean +nmc_property_set_mac_cloned (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + return _property_set_mac (setting, prop, val, TRUE, error); +} + static gboolean nmc_property_set_mtu (NMSetting *setting, const char *prop, const char *val, GError **error) { @@ -7198,7 +7212,7 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (WIRED, CLONED_MAC_ADDRESS), nmc_property_wired_get_cloned_mac_address, - nmc_property_set_mac, + nmc_property_set_mac_cloned, NULL, NULL, NULL, @@ -7316,7 +7330,7 @@ nmc_properties_init (void) NULL); nmc_add_prop_funcs (GLUE (WIRELESS, CLONED_MAC_ADDRESS), nmc_property_wireless_get_cloned_mac_address, - nmc_property_set_mac, + nmc_property_set_mac_cloned, NULL, NULL, NULL, diff --git a/clients/tui/nmt-mac-entry.c b/clients/tui/nmt-mac-entry.c index d0f07e120a..da7f55fac1 100644 --- a/clients/tui/nmt-mac-entry.c +++ b/clients/tui/nmt-mac-entry.c @@ -28,11 +28,13 @@ #include "nm-default.h" +#include "nmt-mac-entry.h" + #include #include "NetworkManager.h" +#include "nm-common-macros.h" -#include "nmt-mac-entry.h" G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) @@ -41,6 +43,7 @@ G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) typedef struct { int mac_length; int mac_str_length; + NmtMacEntryType entry_type; } NmtMacEntryPrivate; @@ -48,6 +51,7 @@ enum { PROP_0, PROP_MAC_LENGTH, PROP_MAC_ADDRESS, + PROP_ENTRY_TYPE, LAST_PROP }; @@ -57,6 +61,7 @@ enum { * @width: the width in characters of the entry * @mac_length: the length in bytes of the hardware address * (either %ETH_ALEN or %INFINIBAND_ALEN) + * @entry_type: the type of the entry. * * Creates a new #NmtMacEntry. * @@ -64,11 +69,13 @@ enum { */ NmtNewtWidget * nmt_mac_entry_new (int width, - int mac_length) + int mac_length, + NmtMacEntryType entry_type) { return g_object_new (NMT_TYPE_MAC_ENTRY, "width", width, "mac-length", mac_length, + "entry-type", (int) entry_type, NULL); } @@ -81,6 +88,9 @@ mac_filter (NmtNewtEntry *entry, { NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry); + if (priv->entry_type != NMT_MAC_ENTRY_TYPE_MAC) + return TRUE; + if (position >= priv->mac_str_length) return FALSE; @@ -98,6 +108,11 @@ mac_validator (NmtNewtEntry *entry, if (!*text) return TRUE; + if (priv->entry_type == NMT_MAC_ENTRY_TYPE_CLONED) { + if (NM_CLONED_MAC_IS_SPECIAL (text)) + return TRUE; + } + p = text; while ( g_ascii_isxdigit (p[0]) && g_ascii_isxdigit (p[1]) @@ -112,7 +127,9 @@ mac_validator (NmtNewtEntry *entry, if (!*p) return (p - text == priv->mac_str_length); - if (g_ascii_isxdigit (p[0]) && !p[1]) { + if ( g_ascii_isxdigit (p[0]) + && !p[1] + && p - text < priv->mac_str_length) { char *fixed = g_strdup_printf ("%.*s:%c", (int)(p - text), text, *p); nmt_newt_entry_set_text (entry, fixed); @@ -161,6 +178,10 @@ nmt_mac_entry_set_property (GObject *object, case PROP_MAC_ADDRESS: nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), g_value_get_string (value)); break; + case PROP_ENTRY_TYPE: + /* construct-only */ + priv->entry_type = g_value_get_int (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -182,6 +203,9 @@ nmt_mac_entry_get_property (GObject *object, case PROP_MAC_ADDRESS: g_value_set_string (value, nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))); break; + case PROP_ENTRY_TYPE: + g_value_set_int (value, priv->entry_type); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -224,4 +248,17 @@ nmt_mac_entry_class_init (NmtMacEntryClass *entry_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtMacEntry:entry-type: + * + * The type of the #NmtMacEntry. Can be either used for plain + * MAC addresses or for the extended format for cloned MAC addresses. + */ + g_object_class_install_property + (object_class, PROP_ENTRY_TYPE, + g_param_spec_int ("entry-type", "", "", + NMT_MAC_ENTRY_TYPE_MAC, NMT_MAC_ENTRY_TYPE_CLONED, NMT_MAC_ENTRY_TYPE_MAC, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); } diff --git a/clients/tui/nmt-mac-entry.h b/clients/tui/nmt-mac-entry.h index 099bacb71b..b318911767 100644 --- a/clients/tui/nmt-mac-entry.h +++ b/clients/tui/nmt-mac-entry.h @@ -25,6 +25,11 @@ G_BEGIN_DECLS +typedef enum { /*< skip >*/ + NMT_MAC_ENTRY_TYPE_MAC, + NMT_MAC_ENTRY_TYPE_CLONED, +} NmtMacEntryType; + #define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type ()) #define NMT_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry)) #define NMT_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) @@ -45,7 +50,8 @@ typedef struct { GType nmt_mac_entry_get_type (void); NmtNewtWidget *nmt_mac_entry_new (int width, - int mac_length); + int mac_length, + NmtMacEntryType type); G_END_DECLS diff --git a/clients/tui/nmt-page-ethernet.c b/clients/tui/nmt-page-ethernet.c index dfe2e44077..6b9243271f 100644 --- a/clients/tui/nmt-page-ethernet.c +++ b/clients/tui/nmt-page-ethernet.c @@ -70,7 +70,7 @@ nmt_page_ethernet_constructed (GObject *object) section = nmt_editor_section_new (_("ETHERNET"), NULL, FALSE); grid = nmt_editor_section_get_body (section); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/clients/tui/nmt-page-vlan.c b/clients/tui/nmt-page-vlan.c index c63b190430..9cd6542163 100644 --- a/clients/tui/nmt-page-vlan.c +++ b/clients/tui/nmt-page-vlan.c @@ -115,7 +115,7 @@ nmt_page_vlan_constructed (GObject *object) nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/clients/tui/nmt-page-wifi.c b/clients/tui/nmt-page-wifi.c index cd9b60e109..35625fe4c7 100644 --- a/clients/tui/nmt-page-wifi.c +++ b/clients/tui/nmt-page-wifi.c @@ -351,13 +351,13 @@ nmt_page_wifi_constructed (GObject *object) nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_MAC); g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BSSID, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); nmt_editor_grid_append (grid, _("BSSID"), widget, NULL); - widget = nmt_mac_entry_new (40, ETH_ALEN); + widget = nmt_mac_entry_new (40, ETH_ALEN, NMT_MAC_ENTRY_TYPE_CLONED); g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, widget, "mac-address", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index 4a17d2c488..cafbf8fd73 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -31,6 +31,7 @@ #include #include +#include "nm-common-macros.h" #include "nm-core-internal.h" #include "nm-keyfile-utils.h" @@ -581,19 +582,28 @@ ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const cha } static void -mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length) +mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length, gboolean cloned_mac_addr) { const char *setting_name = nm_setting_get_name (setting); - char *tmp_string = NULL, *p, *mac_str; - gint *tmp_list; - GByteArray *array = NULL; + gs_free char *tmp_string = NULL; + const char *p, *mac_str; + gs_free guint8 *buf_arr = NULL; + guint buf_len; gsize length; - p = tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); + tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); + + if ( cloned_mac_addr + && NM_CLONED_MAC_IS_SPECIAL (tmp_string)) { + mac_str = tmp_string; + goto out; + } + if (tmp_string && tmp_string[0]) { /* Look for enough ':' characters to signify a MAC address */ guint i = 0; + p = tmp_string; while (*p) { if (*p == ':') i++; @@ -602,23 +612,24 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key if (enforce_length == 0 || enforce_length == i+1) { /* If we found enough it's probably a string-format MAC address */ - array = g_byte_array_sized_new (i+1); - g_byte_array_set_size (array, i+1); - if (!nm_utils_hwaddr_aton (tmp_string, array->data, array->len)) { - g_byte_array_unref (array); - array = NULL; - } + buf_len = i + 1; + buf_arr = g_new (guint8, buf_len); + if (!nm_utils_hwaddr_aton (tmp_string, buf_arr, buf_len)) + g_clear_pointer (&buf_arr, g_free); } } - g_free (tmp_string); + g_clear_pointer (&tmp_string, g_free); + + if (!buf_arr) { + gs_free int *tmp_list = NULL; - if (array == NULL) { /* Old format; list of ints */ tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL); if (length > 0 && (enforce_length == 0 || enforce_length == length)) { gsize i; - array = g_byte_array_sized_new (length); + buf_len = length; + buf_arr = g_new (guint8, buf_len); for (i = 0; i < length; i++) { int val = tmp_list[i]; const guint8 v = (guint8) (val & 0xFF); @@ -627,38 +638,42 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"), val); - g_byte_array_free (array, TRUE); - g_free (tmp_list); return; } - g_byte_array_append (array, &v, 1); + buf_arr[i] = v; } } - g_free (tmp_list); } - if (!array) { + if (!buf_arr) { handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid MAC address")); return; } - mac_str = nm_utils_hwaddr_ntoa (array->data, array->len); + tmp_string = nm_utils_hwaddr_ntoa (buf_arr, buf_len); + mac_str = tmp_string; + +out: g_object_set (setting, key, mac_str, NULL); - g_free (mac_str); - g_byte_array_free (array, TRUE); } static void mac_address_parser_ETHER (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (info, setting, key, ETH_ALEN); + mac_address_parser (info, setting, key, ETH_ALEN, FALSE); +} + +static void +mac_address_parser_ETHER_cloned (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + mac_address_parser (info, setting, key, ETH_ALEN, TRUE); } static void mac_address_parser_INFINIBAND (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (info, setting, key, INFINIBAND_ALEN); + mac_address_parser (info, setting, key, INFINIBAND_ALEN, FALSE); } static void @@ -1209,7 +1224,7 @@ static KeyParser key_parsers[] = { { NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, TRUE, - mac_address_parser_ETHER }, + mac_address_parser_ETHER_cloned }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS, TRUE, @@ -1217,7 +1232,7 @@ static KeyParser key_parsers[] = { { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, TRUE, - mac_address_parser_ETHER }, + mac_address_parser_ETHER_cloned }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BSSID, TRUE, diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index a8f6e87653..f6b801c6bd 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -1415,6 +1415,8 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) * * The stable-id is used instead of the connection UUID for generating * IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. + * It is also used to seed the generated cloned MAC address for + * ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. * * Since: 1.4 **/ diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c index a76218fd24..3e72056afd 100644 --- a/libnm-core/nm-setting-wired.c +++ b/libnm-core/nm-setting-wired.c @@ -28,6 +28,7 @@ #include #include "nm-utils.h" +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -692,7 +693,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->cloned_mac_address && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { + if ( priv->cloned_mac_address + && !NM_CLONED_MAC_IS_SPECIAL (priv->cloned_mac_address) + && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -732,6 +735,25 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingClass *parent_class; + + if (nm_streq (prop_spec->name, NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) { + return nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address, + NM_SETTING_WIRED_GET_PRIVATE (other)->cloned_mac_address); + } + + parent_class = NM_SETTING_CLASS (nm_setting_wired_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ + static void clear_blacklist_item (char **item_p) { @@ -900,18 +922,19 @@ get_property (GObject *object, guint prop_id, } static void -nm_setting_wired_class_init (NMSettingWiredClass *setting_class) +nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class) { - GObjectClass *object_class = G_OBJECT_CLASS (setting_class); - NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + GObjectClass *object_class = G_OBJECT_CLASS (setting_wired_class); + NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wired_class); - g_type_class_add_private (setting_class, sizeof (NMSettingWiredPrivate)); + g_type_class_add_private (setting_wired_class, sizeof (NMSettingWiredPrivate)); /* virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; - parent_class->verify = verify; + setting_class->verify = verify; + setting_class->compare_property = compare_property; /* Properties */ /** @@ -1023,7 +1046,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_MAC_ADDRESS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_MAC_ADDRESS, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1033,6 +1056,20 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) * * If specified, request that the device use this MAC address instead of its * permanent MAC address. This is known as MAC cloning or spoofing. + * + * Beside explicitly specifing a MAC address, the special values "preserve", "permanent", + * "random" and "stable" are supported. + * "preserve" means not to touch the MAC address on activation. + * "permanent" means to use the permanent hardware address of the device. + * "random" creates a random MAC address on each connect. + * "stable" creates a hashed MAC address based on connection.stable-id (or + * the connection's UUID) and a machine dependent key. + * + * If unspecified, the value can be overwritten via global defaults, see manual + * of NetworkManager.conf. If still unspecified, it defaults to "permanent". + * + * On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated + * "cloned-mac-address". **/ /* ---keyfile--- * property: cloned-mac-address @@ -1047,6 +1084,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- + * ---dbus--- + * property: cloned-mac-address + * format: byte array + * description: This D-Bus field is deprecated in favor of "assigned-mac-address" + * which is more flexible and allows specifying special variants like "random". + * ---end--- */ g_object_class_install_property (object_class, PROP_CLONED_MAC_ADDRESS, @@ -1055,10 +1098,28 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - G_VARIANT_TYPE_BYTESTRING, - _nm_utils_hwaddr_to_dbus, - _nm_utils_hwaddr_from_dbus); + _nm_setting_class_override_property (setting_class, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + G_VARIANT_TYPE_BYTESTRING, + _nm_utils_hwaddr_cloned_get, + _nm_utils_hwaddr_cloned_set, + _nm_utils_hwaddr_cloned_not_set); + + /* ---dbus--- + * property: assigned-mac-address + * format: string + * description: The new field for the cloned MAC address. It can be either + * a hardware address in ASCII representation, or one of the special values + * "preserve", "permanent", "random", "random" or "stable". + * This field replaces the deprecated "cloned-mac-address" on D-Bus, which + * can only contain explict hardware addresses. + * ---end--- + */ + _nm_setting_class_add_dbus_only_property (setting_class, + "assigned-mac-address", + G_VARIANT_TYPE_STRING, + _nm_utils_hwaddr_cloned_data_synth, + _nm_utils_hwaddr_cloned_data_set); /** * NMSettingWired:mac-address-blacklist: @@ -1183,7 +1244,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_S390_OPTIONS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_S390_OPTIONS, G_VARIANT_TYPE ("a{ss}"), _nm_utils_strdict_to_dbus, _nm_utils_strdict_from_dbus); diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c index 6074959349..78824887cd 100644 --- a/libnm-core/nm-setting-wireless.c +++ b/libnm-core/nm-setting-wireless.c @@ -22,11 +22,13 @@ #include "nm-default.h" +#include "nm-setting-wireless.h" + #include #include -#include "nm-setting-wireless.h" #include "nm-utils.h" +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -801,7 +803,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->cloned_mac_address && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { + if ( priv->cloned_mac_address + && !NM_CLONED_MAC_IS_SPECIAL (priv->cloned_mac_address) + && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -839,6 +843,25 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingClass *parent_class; + + if (nm_streq (prop_spec->name, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS)) { + return nm_streq0 (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address, + NM_SETTING_WIRELESS_GET_PRIVATE (other)->cloned_mac_address); + } + + parent_class = NM_SETTING_CLASS (nm_setting_wireless_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ + static GVariant * nm_setting_wireless_get_security (NMSetting *setting, NMConnection *connection, @@ -1023,18 +1046,19 @@ get_property (GObject *object, guint prop_id, } static void -nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) +nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class) { - GObjectClass *object_class = G_OBJECT_CLASS (setting_class); - NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + GObjectClass *object_class = G_OBJECT_CLASS (setting_wireless_class); + NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wireless_class); - g_type_class_add_private (setting_class, sizeof (NMSettingWirelessPrivate)); + g_type_class_add_private (setting_wireless_class, sizeof (NMSettingWirelessPrivate)); /* virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; - parent_class->verify = verify; + setting_class->verify = verify; + setting_class->compare_property = compare_property; /* Properties */ /** @@ -1154,7 +1178,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_BSSID, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_BSSID, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1231,7 +1255,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_MAC_ADDRESS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_MAC_ADDRESS, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1239,8 +1263,22 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) /** * NMSettingWireless:cloned-mac-address: * - * If specified, request that the Wi-Fi device use this MAC address instead - * of its permanent MAC address. This is known as MAC cloning or spoofing. + * If specified, request that the device use this MAC address instead of its + * permanent MAC address. This is known as MAC cloning or spoofing. + * + * Beside explicitly specifing a MAC address, the special values "preserve", "permanent", + * "random" and "stable" are supported. + * "preserve" means not to touch the MAC address on activation. + * "permanent" means to use the permanent hardware address of the device. + * "random" creates a random MAC address on each connect. + * "stable" creates a hashed MAC address based on connection.stable-id (or + * the connection's UUID) and a machine dependent key. + * + * If unspecified, the value can be overwritten via global defaults, see manual + * of NetworkManager.conf. If still unspecified, it defaults to "permanent". + * + * On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated + * "cloned-mac-address". **/ /* ---keyfile--- * property: cloned-mac-address @@ -1255,6 +1293,12 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- + * ---dbus--- + * property: cloned-mac-address + * format: byte array + * description: This D-Bus field is deprecated in favor of "assigned-mac-address" + * which is more flexible and allows specifying special variants like "random". + * ---end--- */ g_object_class_install_property (object_class, PROP_CLONED_MAC_ADDRESS, @@ -1262,10 +1306,28 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, - G_VARIANT_TYPE_BYTESTRING, - _nm_utils_hwaddr_to_dbus, - _nm_utils_hwaddr_from_dbus); + _nm_setting_class_override_property (setting_class, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + G_VARIANT_TYPE_BYTESTRING, + _nm_utils_hwaddr_cloned_get, + _nm_utils_hwaddr_cloned_set, + _nm_utils_hwaddr_cloned_not_set); + + /* ---dbus--- + * property: assigned-mac-address + * format: string + * description: The new field for the cloned MAC address. It can be either + * a hardware address in ASCII representation, or one of the special values + * "preserve", "permanent", "random", "random" or "stable". + * This field replaces the deprecated "cloned-mac-address" on D-Bus, which + * can only contain explict hardware addresses. + * ---end--- + */ + _nm_setting_class_add_dbus_only_property (setting_class, + "assigned-mac-address", + G_VARIANT_TYPE_STRING, + _nm_utils_hwaddr_cloned_data_synth, + _nm_utils_hwaddr_cloned_data_set); /** * NMSettingWireless:mac-address-blacklist: @@ -1428,7 +1490,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * NetworkManager daemons. * ---end--- */ - _nm_setting_class_add_dbus_only_property (parent_class, "security", + _nm_setting_class_add_dbus_only_property (setting_class, "security", G_VARIANT_TYPE_STRING, nm_setting_wireless_get_security, NULL); } diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 611c467d06..bd54756ac3 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -36,6 +36,29 @@ gboolean _nm_utils_team_config_equal (const char *conf1, const char *conf2, g /* D-Bus transform funcs */ +GVariant *_nm_utils_hwaddr_cloned_get (NMSetting *setting, + const char *property); +gboolean _nm_utils_hwaddr_cloned_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error); +gboolean _nm_utils_hwaddr_cloned_not_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + NMSettingParseFlags parse_flags, + GError **error); +GVariant * _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, + NMConnection *connection, + const char *property); +gboolean _nm_utils_hwaddr_cloned_data_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error); + GVariant * _nm_utils_hwaddr_to_dbus (const GValue *prop_value); void _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, GValue *prop_value); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 5c4bfb50b9..7567f6d88d 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -37,6 +37,7 @@ #include #endif +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" #include "crypto.h" @@ -3313,14 +3314,14 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, return !memcmp (hwaddr1, hwaddr2, hwaddr1_len); } -GVariant * -_nm_utils_hwaddr_to_dbus (const GValue *prop_value) +/*****************************************************************************/ + +static GVariant * +_nm_utils_hwaddr_to_dbus_impl (const char *str) { - const char *str; guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; int len; - str = g_value_get_string (prop_value); if (!str) return NULL; @@ -3334,6 +3335,103 @@ _nm_utils_hwaddr_to_dbus (const GValue *prop_value) return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, buf, len, 1); } +GVariant * +_nm_utils_hwaddr_cloned_get (NMSetting *setting, + const char *property) +{ + gs_free char *addr = NULL; + + nm_assert (nm_streq0 (property, "cloned-mac-address")); + + g_object_get (setting, "cloned-mac-address", &addr, NULL); + return _nm_utils_hwaddr_to_dbus_impl (addr); +} + +gboolean +_nm_utils_hwaddr_cloned_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + gsize length; + const guint8 *array; + char *str; + + nm_assert (nm_streq0 (property, "cloned-mac-address")); + + if (!_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address")) + return TRUE; + + length = 0; + array = g_variant_get_fixed_array (value, &length, 1); + + if (!length) + return TRUE; + + str = nm_utils_hwaddr_ntoa (array, length); + g_object_set (setting, + "cloned-mac-address", + str, + NULL); + g_free (str); + return TRUE; +} + +gboolean +_nm_utils_hwaddr_cloned_not_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + NMSettingParseFlags parse_flags, + GError **error) +{ + nm_assert (nm_streq0 (property, "cloned-mac-address")); + return TRUE; +} + +GVariant * +_nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, + NMConnection *connection, + const char *property) +{ + gs_free char *addr = NULL; + + nm_assert (nm_streq0 (property, "assigned-mac-address")); + + g_object_get (setting, + "cloned-mac-address", + &addr, + NULL); + return addr ? g_variant_new_string (addr) : NULL; +} + +gboolean +_nm_utils_hwaddr_cloned_data_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + nm_assert (nm_streq0 (property, "assigned-mac-address")); + + if (_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address")) + return TRUE; + + g_object_set (setting, + "cloned-mac-address", + g_variant_get_string (value, NULL), + NULL); + return TRUE; +} + +GVariant * +_nm_utils_hwaddr_to_dbus (const GValue *prop_value) +{ + return _nm_utils_hwaddr_to_dbus_impl (g_value_get_string (prop_value)); +} + void _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, GValue *prop_value) @@ -3346,6 +3444,8 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, g_value_take_string (prop_value, str); } +/*****************************************************************************/ + /** * nm_utils_bin2hexstr: * @src: (type guint8) (array length=len): an array of bytes diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 9d263dcb38..6e4be2cffc 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -2449,6 +2449,90 @@ test_setting_compare_routes (void) g_clear_pointer (&result, g_hash_table_unref); } +static void +test_setting_compare_wired_cloned_mac_address (void) +{ + gs_unref_object NMSetting *old = NULL, *new = NULL; + gboolean success; + gs_free char *str1 = NULL; + + old = nm_setting_wired_new (); + g_object_set (old, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "stable", + NULL); + + g_assert_cmpstr ("stable", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) old)); + g_object_get (old, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable", ==, str1); + g_clear_pointer (&str1, g_free); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "11:22:33:44:55:66", NULL); + + g_assert_cmpstr ("11:22:33:44:55:66", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) new)); + g_object_get (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("11:22:33:44:55:66", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "stable-bia", NULL); + + g_assert_cmpstr ("stable-bia", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) new)); + g_object_get (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable-bia", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); +} + +static void +test_setting_compare_wireless_cloned_mac_address (void) +{ + gs_unref_object NMSetting *old = NULL, *new = NULL; + gboolean success; + gs_free char *str1 = NULL; + + old = nm_setting_wireless_new (); + g_object_set (old, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "stable", + NULL); + + g_assert_cmpstr ("stable", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) old)); + g_object_get (old, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable", ==, str1); + g_clear_pointer (&str1, g_free); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "11:22:33:44:55:66", NULL); + + g_assert_cmpstr ("11:22:33:44:55:66", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) new)); + g_object_get (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("11:22:33:44:55:66", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "stable-bia", NULL); + + g_assert_cmpstr ("stable-bia", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) new)); + g_object_get (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable-bia", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); +} + static void test_setting_compare_timestamp (void) { @@ -5202,6 +5286,8 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_setting_compare_id", test_setting_compare_id); g_test_add_func ("/core/general/test_setting_compare_addresses", test_setting_compare_addresses); g_test_add_func ("/core/general/test_setting_compare_routes", test_setting_compare_routes); + g_test_add_func ("/core/general/test_setting_compare_wired_cloned_mac_address", test_setting_compare_wired_cloned_mac_address); + g_test_add_func ("/core/general/test_setting_compare_wirless_cloned_mac_address", test_setting_compare_wireless_cloned_mac_address); g_test_add_func ("/core/general/test_setting_compare_timestamp", test_setting_compare_timestamp); #define ADD_FUNC(name, func, secret_flags, comp_flags, remove_secret) \ g_test_add_data_func_full ("/core/general/" G_STRINGIFY (func) "_" name, \ diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index b70438ded3..e6050b8326 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -565,6 +565,10 @@ ipv6.ip6-privacy=0 connection.lldp + + ethernet.cloned-mac-address + If left unspecified, it defaults to "permanent". + ethernet.wake-on-lan @@ -592,6 +596,10 @@ ipv6.ip6-privacy=0 vpn.timeout If left unspecified, default value of 60 seconds is used. + + wifi.cloned-mac-address + If left unspecified, it defaults to "permanent". + wifi.mac-address-randomization If left unspecified, MAC address randomization is disabled. diff --git a/shared/nm-common-macros.h b/shared/nm-common-macros.h index 922117c3fc..3e5f349f46 100644 --- a/shared/nm-common-macros.h +++ b/shared/nm-common-macros.h @@ -38,6 +38,21 @@ #define NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS "org.freedesktop.NetworkManager.settings.modify.global-dns" #define NM_AUTH_PERMISSION_RELOAD "org.freedesktop.NetworkManager.reload" +#define NM_CLONED_MAC_PRESERVE "preserve" +#define NM_CLONED_MAC_PERMANENT "permanent" +#define NM_CLONED_MAC_RANDOM "random" +#define NM_CLONED_MAC_STABLE "stable" + +static inline gboolean +NM_CLONED_MAC_IS_SPECIAL (const char *str) +{ + return NM_IN_STRSET (str, + NM_CLONED_MAC_PRESERVE, + NM_CLONED_MAC_PERMANENT, + NM_CLONED_MAC_RANDOM, + NM_CLONED_MAC_STABLE); +} + /******************************************************************************/ #endif /* __NM_COMMON_MACROS_H__ */ diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 9894872b8d..7eb6cf1607 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -529,8 +529,8 @@ check_connection_mac_address (NMConnection *orig, static gboolean check_connection_cloned_mac_address (NMConnection *orig, - NMConnection *candidate, - GHashTable *settings) + NMConnection *candidate, + GHashTable *settings) { GHashTable *props; const char *orig_mac = NULL, *cand_mac = NULL; @@ -551,6 +551,12 @@ check_connection_cloned_mac_address (NMConnection *orig, if (s_wired_cand) cand_mac = nm_setting_wired_get_cloned_mac_address (s_wired_cand); + /* special cloned mac address entires are accepted. */ + if (NM_CLONED_MAC_IS_SPECIAL (orig_mac)) + orig_mac = NULL; + if (NM_CLONED_MAC_IS_SPECIAL (cand_mac)) + cand_mac = NULL; + if (!orig_mac || !cand_mac) { remove_from_hash (settings, props, NM_SETTING_WIRED_SETTING_NAME, diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 21a1d5cd2d..d26ef71d65 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -804,18 +804,13 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev); NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (dev, reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { - s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (dev, cloned_mac); + nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); /* If we're re-activating a PPPoE connection a short while after * a previous PPPoE connection was torn down, wait a bit to allow the diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index a238bdcf79..0fa83af949 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -502,8 +502,6 @@ update_connection (NMDevice *device, NMConnection *connection) static NMActStageReturn act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -512,11 +510,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (dev, cloned_mac); - + nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); return TRUE; } diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 4d4bd720ac..b641751205 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -57,6 +57,7 @@ gboolean nm_device_bring_up (NMDevice *self, gboolean wait, gboolean *no_firmwar void nm_device_take_down (NMDevice *self, gboolean block); gboolean nm_device_hw_addr_set (NMDevice *device, const char *addr); +gboolean nm_device_hw_addr_set_cloned (NMDevice *device, NMConnection *connection, gboolean is_wifi); gboolean nm_device_hw_addr_reset (NMDevice *device); void nm_device_set_firmware_missing (NMDevice *self, gboolean missing); @@ -107,6 +108,8 @@ void nm_device_queue_recheck_available (NMDevice *device, void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config); void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config); +gboolean nm_device_hw_addr_is_explict (NMDevice *device); + void nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason); gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value); diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 238d3a97b5..2e8f832e16 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -291,8 +291,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { NMDeviceTun *self = NM_DEVICE_TUN (device); NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -305,10 +303,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) if (g_strcmp0 (priv->mode, "tap")) return NM_ACT_STAGE_RETURN_SUCCESS; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (device, cloned_mac); + nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE); return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index 4cedcd078b..5e82f69b77 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -87,7 +87,6 @@ parent_hwaddr_maybe_changed (NMDevice *parent, { NMDeviceVlan *self = NM_DEVICE_VLAN (user_data); NMConnection *connection; - NMSettingWired *s_wired; const char *new_mac, *old_mac; NMSettingIPConfig *s_ip6; @@ -100,11 +99,8 @@ parent_hwaddr_maybe_changed (NMDevice *parent, return; /* Update the VLAN MAC only if configuration does not specify one */ - s_wired = nm_connection_get_setting_wired (connection); - if (s_wired) { - if (nm_setting_wired_get_cloned_mac_address (s_wired)) - return; - } + if (nm_device_hw_addr_is_explict (self)) + return; old_mac = nm_device_get_hw_address (self); new_mac = nm_device_get_hw_address (parent); @@ -554,8 +550,6 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (dev); NMSettingVlan *s_vlan; - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -564,10 +558,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (dev, cloned_mac); + nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); /* Change MAC address to parent's one if needed */ if (priv->parent) diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index 4bdada349d..40e5b5512a 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -511,8 +511,6 @@ update_connection (NMDevice *device, NMConnection *connection) static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { - NMSettingWired *s_wired; - const char *cloned_mac = NULL; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -521,10 +519,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); - if (s_wired) - cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); - nm_device_hw_addr_set (device, cloned_mac); + nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE); return NM_ACT_STAGE_RETURN_SUCCESS; } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index e9e1e88193..ddf7933185 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -209,6 +209,13 @@ typedef struct { NMIP4Config **configs; } ArpingData; +typedef enum { + HW_ADDR_TYPE_UNSET = 0, + HW_ADDR_TYPE_PERMANENT, + HW_ADDR_TYPE_EXPLICIT, + HW_ADDR_TYPE_GENERATED, +} HwAddrType; + typedef struct _NMDevicePrivate { bool in_state_changed; @@ -226,7 +233,12 @@ typedef struct _NMDevicePrivate { char * udi; char * iface; /* may change, could be renamed by user */ int ifindex; + + guint hw_addr_len; + guint8 /*HwAddrType*/ hw_addr_type; + bool real; + char * ip_iface; int ip_ifindex; NMDeviceType type; @@ -243,7 +255,6 @@ typedef struct _NMDevicePrivate { bool hw_addr_perm_fake:1; /* whether the permanent HW address could not be read and is a fake */ GHashTable * available_connections; char * hw_addr; - guint hw_addr_len; char * hw_addr_perm; char * hw_addr_initial; char * physical_port_id; @@ -2357,6 +2368,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) _notify (self, PROP_PHYSICAL_PORT_ID); } + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; g_clear_pointer (&priv->hw_addr_perm, g_free); _notify (self, PROP_PERM_HW_ADDRESS); g_clear_pointer (&priv->hw_addr_initial, g_free); @@ -11421,6 +11433,8 @@ nm_device_update_initial_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; + if ( priv->hw_addr && !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) { g_free (priv->hw_addr_initial); @@ -11479,6 +11493,50 @@ nm_device_update_permanent_hw_address (NMDevice *self) _notify (self, PROP_PERM_HW_ADDRESS); } +static const char * +_get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_addr) +{ + NMSetting *setting; + const char *addr = NULL; + + nm_assert (out_addr && !*out_addr); + + setting = nm_connection_get_setting (connection, + is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED); + if (setting) { + addr = is_wifi + ? nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) setting) + : nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) setting); + } + + if (!addr) { + gs_free char *a = NULL; + + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address", + self); + if ( !a + || ( !NM_CLONED_MAC_IS_SPECIAL (a) + && !nm_utils_hwaddr_valid (a, ETH_ALEN))) + addr = NM_CLONED_MAC_PERMANENT; + else + addr = *out_addr = g_steal_pointer (&a); + } + + return addr; +} + +gboolean +nm_device_hw_addr_is_explict (NMDevice *self) +{ + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + return !NM_IN_SET (priv->hw_addr_type, HW_ADDR_TYPE_PERMANENT, HW_ADDR_TYPE_UNSET); +} + static gboolean _hw_addr_set (NMDevice *self, const char *addr, @@ -11508,10 +11566,8 @@ _hw_addr_set (NMDevice *self, if (!hw_addr_len) hw_addr_len = _nm_utils_hwaddr_length (addr); if ( !hw_addr_len - || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) { - _LOGW (LOGD_DEVICE, "set-hw-addr: invalid MAC address %s", addr); - return FALSE; - } + || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) + g_return_val_if_reached (FALSE); _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s'...", addr); @@ -11535,7 +11591,9 @@ _hw_addr_set (NMDevice *self, _LOGW (LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s", detail, addr); } - nm_device_bring_up (self, TRUE, NULL); + + if (!nm_device_bring_up (self, TRUE, NULL)) + return FALSE; return success; } @@ -11549,12 +11607,73 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr) priv = NM_DEVICE_GET_PRIVATE (self); - if (!addr) { + if (!addr) + g_return_val_if_reached (FALSE); + + /* this is called by NMDeviceVlan to take the MAC address from the parent. + * In this case, it's like setting it to PERMANENT. */ + priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; + + return _hw_addr_set (self, addr, "set"); +} + +gboolean +nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi) +{ + NMDevicePrivate *priv; + gs_free char *hw_addr_tmp = NULL; + gs_free char *hw_addr_generated = NULL; + const char *addr; + + g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE (self); + + if (!connection) + g_return_val_if_reached (FALSE); + + addr = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp); + + if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) + return TRUE; + + if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) { addr = nm_device_get_permanent_hw_address (self, TRUE); if (!addr) return FALSE; + priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; + } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_RANDOM)) { + hw_addr_generated = nm_utils_hw_addr_gen_random_eth (); + if (!hw_addr_generated) { + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "random"); + return FALSE; + } + priv->hw_addr_type = HW_ADDR_TYPE_GENERATED; + addr = hw_addr_generated; + } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_STABLE)) { + NMUtilsStableType stable_type; + const char *stable_id; + + stable_id = _get_stable_id (connection, &stable_type); + if (stable_id) { + hw_addr_generated = nm_utils_hw_addr_gen_stable_eth (stable_type, stable_id, + nm_device_get_ip_iface (self)); + } + if (!hw_addr_generated) { + _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "stable"); + return FALSE; + } + priv->hw_addr_type = HW_ADDR_TYPE_GENERATED; + addr = hw_addr_generated; + } else { + /* this must be a valid address. Otherwise, we shouldn't come here. */ + if (_nm_utils_hwaddr_length (addr) <= 0) { + g_return_val_if_reached (FALSE); + } + priv->hw_addr_type = HW_ADDR_TYPE_EXPLICIT; } - return _hw_addr_set (self, addr, "set"); + + return _hw_addr_set (self, addr, "set-cloned"); } gboolean @@ -11567,9 +11686,17 @@ nm_device_hw_addr_reset (NMDevice *self) priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->hw_addr_type == HW_ADDR_TYPE_UNSET) + return TRUE; + + priv->hw_addr_type = HW_ADDR_TYPE_UNSET; addr = nm_device_get_initial_hw_address (self); - if (!addr) - return FALSE; + if (!addr) { + /* as hw_addr_type is not UNSET, we expect that we can get an + * initial address to which to reset. */ + g_return_val_if_reached (FALSE); + } + return _hw_addr_set (self, addr, "reset"); } diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index bb5fd30aba..8471a20730 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -2275,7 +2275,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) NMActRequest *req; NMConnection *connection; NMSettingWireless *s_wireless; - const char *cloned_mac; const char *mode; const char *ap_path; @@ -2316,8 +2315,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) } /* Set spoof MAC to the interface */ - cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); - nm_device_hw_addr_set (device, cloned_mac); + nm_device_hw_addr_set_cloned (device, connection, TRUE); /* AP mode never uses a specific object or existing scanned AP */ if (priv->mode != NM_802_11_MODE_AP) { diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index bb15ca387a..06989fa710 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -3173,6 +3173,100 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, secret_key, key_len, error); } +/*****************************************************************************/ + +static void +_hw_addr_eth_complete (guint8 *bin_addr) +{ + /* this LSB of the first octet cannot be set, + * it means Unicast vs. Multicast */ + bin_addr[0] &= ~1; + + /* the second LSB of the first octet means + * "globally unique, OUI enforced, BIA (burned-in-address)" + * vs. "locally-administered" */ + bin_addr[0] |= 2; +} + +char * +nm_utils_hw_addr_gen_random_eth (void) +{ + guint8 bin_addr[ETH_ALEN]; + + if (nm_utils_read_urandom (bin_addr, ETH_ALEN) < 0) + return NULL; + _hw_addr_eth_complete (bin_addr); + return nm_utils_hwaddr_ntoa (bin_addr, ETH_ALEN); +} + +static char * +_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, + const char *stable_id, + const guint8 *secret_key, + gsize key_len, + const char *ifname) +{ + GChecksum *sum; + guint32 tmp; + guint8 digest[32]; + gsize len = sizeof (digest); + guint8 bin_addr[ETH_ALEN]; + guint8 stable_type_uint8; + + nm_assert (stable_id); + nm_assert (NM_IN_SET (stable_type, + NM_UTILS_STABLE_TYPE_UUID, + NM_UTILS_STABLE_TYPE_STABLE_ID)); + nm_assert (secret_key); + + sum = g_checksum_new (G_CHECKSUM_SHA256); + if (!sum) + return NULL; + + key_len = MIN (key_len, G_MAXUINT32); + + stable_type_uint8 = stable_type; + g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8)); + + tmp = htonl ((guint32) key_len); + g_checksum_update (sum, (const guchar *) &tmp, sizeof (tmp)); + g_checksum_update (sum, (const guchar *) secret_key, key_len); + g_checksum_update (sum, (const guchar *) (ifname ?: ""), ifname ? (strlen (ifname) + 1) : 1); + g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); + + g_checksum_get_digest (sum, digest, &len); + g_checksum_free (sum); + + g_return_val_if_fail (len == 32, NULL); + + memcpy (bin_addr, digest, ETH_ALEN); + _hw_addr_eth_complete (bin_addr); + return nm_utils_hwaddr_ntoa (bin_addr, ETH_ALEN); +} + +char * +nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, + const char *stable_id, + const char *ifname) +{ + gs_free guint8 *secret_key = NULL; + gsize key_len = 0; + + g_return_val_if_fail (stable_id, NULL); + + secret_key = nm_utils_secret_key_read (&key_len, NULL); + if (!secret_key) + return NULL; + + return _hw_addr_gen_stable_eth (stable_type, + stable_id, + secret_key, + key_len, + ifname); +} + +/*****************************************************************************/ + /** * nm_utils_setpgid: * @unused: unused diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 86b4fe6ad2..082eabf4f6 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -371,6 +371,11 @@ gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type, guint dad_counter, GError **error); +char *nm_utils_hw_addr_gen_random_eth (void); +char *nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, + const char *stable_id, + const char *iname); + void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); void nm_utils_setpgid (gpointer unused); diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index 3d60668259..2a3fd9d63d 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -54,10 +54,45 @@ test_stable_privacy (void) inet_pton (AF_INET6, "1234::", &addr1); _set_stable_privacy (NM_UTILS_STABLE_TYPE_STABLE_ID, &addr1, "eth666", "stable-id-1", 0, (guint8 *) "key", 3, NULL); nmtst_assert_ip6_address (&addr1, "1234::4944:67b0:7a6c:1cf"); - } -/*******************************************/ +/*****************************************************************************/ + +static void +_do_test_hw_addr (NMUtilsStableType stable_type, + const char *stable_id, + const guint8 *secret_key, + gsize key_len, + const char *ifname, + const char *expected) +{ + gs_free char *generated = NULL; + + g_assert (expected); + g_assert (nm_utils_hwaddr_valid (expected, ETH_ALEN)); + + generated = _hw_addr_gen_stable_eth (stable_type, + stable_id, + secret_key, + key_len, + ifname); + + g_assert (generated); + g_assert (nm_utils_hwaddr_valid (generated, ETH_ALEN)); + g_assert_cmpstr (generated, ==, expected); + g_assert (nm_utils_hwaddr_matches (generated, -1, expected, -1)); +} +#define do_test_hw_addr(stable_type, stable_id, secret_key, ifname, expected) \ + _do_test_hw_addr ((stable_type), (stable_id), (const guint8 *) ""secret_key"", NM_STRLEN (secret_key), (ifname), ""expected"") + +static void +test_hw_addr_gen_stable_eth (void) +{ + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "06:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_STABLE_ID, "stable-1", "key1", "eth0", "C6:AE:A9:9A:76:09"); +} + +/*****************************************************************************/ NMTST_DEFINE (); @@ -67,6 +102,7 @@ main (int argc, char **argv) nmtst_init_with_logging (&argc, &argv, NULL, "ALL"); g_test_add_func ("/utils/stable_privacy", test_stable_privacy); + g_test_add_func ("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth); return g_test_run (); } From 143471815da42574030f9d0d447ddf282ef4760f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sat, 18 Jun 2016 19:14:33 +0200 Subject: [PATCH 35/43] device: fail activation on failure to set cloned MAC address When a user want to explicitly spoof the MAC address, a failure to do so should fail activation. For one, failing to do so may be a security problem. In any case, if user asks to configure the interface in a certain way and we fail to do so that shall result in a failure to activate. --- src/devices/nm-device-ethernet.c | 5 ++++- src/devices/nm-device-macvlan.c | 5 +++-- src/devices/nm-device-tun.c | 3 ++- src/devices/nm-device-vlan.c | 3 ++- src/devices/nm-device-vxlan.c | 3 ++- src/devices/wifi/nm-device-wifi.c | 3 ++- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index d26ef71d65..db2eb4f5f3 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -810,8 +810,11 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (dev, reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { - nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); + 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 diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index 0fa83af949..583f260e8d 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -510,8 +510,9 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); - 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 diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 2e8f832e16..1f42d06015 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -303,7 +303,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) if (g_strcmp0 (priv->mode, "tap")) return NM_ACT_STAGE_RETURN_SUCCESS; - nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE); + 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 5e82f69b77..c710917419 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -558,7 +558,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE); + 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) diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index 40e5b5512a..674c2a787d 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -519,7 +519,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE); + 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/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 8471a20730..1437767127 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -2315,7 +2315,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) } /* Set spoof MAC to the interface */ - nm_device_hw_addr_set_cloned (device, connection, TRUE); + 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) { From b7d76b2277113316879d9d2a9348dac5d2358f0c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Sun, 19 Jun 2016 13:01:57 +0200 Subject: [PATCH 36/43] libnm: deprecated wireless.mac-address-randomization property for wireless.cloned-mac-address --- libnm-core/nm-connection.c | 47 ++++++++++++++++++++++++++++++++ libnm-core/nm-setting-wireless.c | 34 +++++++++++++++++++++++ 2 files changed, 81 insertions(+) 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-setting-wireless.c b/libnm-core/nm-setting-wireless.c index 78824887cd..15ba71b7f8 100644 --- a/libnm-core/nm-setting-wireless.c +++ b/libnm-core/nm-setting-wireless.c @@ -840,6 +840,39 @@ 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; } @@ -1460,6 +1493,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_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 From 767abfa69040c62ae1dc0989a77df8c5e0e489c1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 20 Jun 2016 12:28:04 +0200 Subject: [PATCH 37/43] wifi: implement MAC address randomization in NetworkManager instead of supplicant 'wireless.mac-address-randomization' broke 'wireless.cloned-mac-address', because we would always set 'PreassocMacAddr=1'. The reason is that supplicant would set 'wpa_s->mac_addr_changed' during scanning, and later during association it would either set a random MAC address or reset the permanent MAC address [1]. Anyway, 'wireless.mac-address-randomization' conflicts with 'wireless.cloned-mac-address'. Instead of letting supplicant set the MAC address, manage the MAC addresses entirely from NetworkManager. Supplicant should not touch it. [1] https://w1.fi/cgit/hostap/tree/wpa_supplicant/wpa_supplicant.c?id=f885b8e97cf39b56fe7ca6577890f2d20df7ae08#n1663 --- man/NetworkManager.conf.xml | 4 +- src/devices/nm-device.c | 67 ++++++++++++++----- src/devices/wifi/nm-device-wifi.c | 63 ++++++++++++----- src/supplicant-manager/nm-supplicant-config.c | 47 ------------- src/supplicant-manager/nm-supplicant-config.h | 4 -- .../nm-supplicant-interface.c | 19 ++---- .../nm-supplicant-interface.h | 2 - .../tests/test-supplicant-config.c | 8 --- 8 files changed, 105 insertions(+), 109 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index e6050b8326..a766bf90f9 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -602,7 +602,9 @@ ipv6.ip6-privacy=0 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 diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ddf7933185..c448f04818 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11009,6 +11009,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); set_nm_ipv6ll (self, FALSE); restore_ip6_properties (self); } @@ -11409,9 +11410,13 @@ nm_device_update_hw_address (NMDevice *self) _LOGD (LOGD_HW | LOGD_DEVICE, "hw-addr: hardware address now %s", priv->hw_addr); _notify (self, PROP_HW_ADDRESS); - if (!priv->hw_addr_initial) { - /* when we get a hw_addr the first time, always update our inital - * hw-address as well. */ + 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); } } @@ -11433,10 +11438,14 @@ nm_device_update_initial_hw_address (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - priv->hw_addr_type = HW_ADDR_TYPE_UNSET; - if ( priv->hw_addr && !nm_streq0 (priv->hw_addr_initial, priv->hw_addr)) { + 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", @@ -11515,11 +11524,26 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address", self); - if ( !a - || ( !NM_CLONED_MAC_IS_SPECIAL (a) - && !nm_utils_hwaddr_valid (a, ETH_ALEN))) - addr = NM_CLONED_MAC_PERMANENT; - else + /* 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); } @@ -11547,6 +11571,7 @@ _hw_addr_set (NMDevice *self, 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); @@ -11571,8 +11596,11 @@ _hw_addr_set (NMDevice *self, _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s'...", addr); - /* Can't change MAC address while device is up */ - nm_device_take_down (self, FALSE); + 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); + } success = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, hw_addr_len); if (success) { @@ -11592,8 +11620,10 @@ _hw_addr_set (NMDevice *self, detail, addr); } - if (!nm_device_bring_up (self, TRUE, NULL)) - return FALSE; + if (was_up) { + if (!nm_device_bring_up (self, TRUE, NULL)) + return FALSE; + } return success; } @@ -11610,7 +11640,8 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr) if (!addr) g_return_val_if_reached (FALSE); - /* this is called by NMDeviceVlan to take the MAC address from the parent. + /* 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; @@ -11634,8 +11665,10 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean addr = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp); - if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) - return TRUE; + if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) { + /* "preserve" means to reset the initial MAC address. */ + return nm_device_hw_addr_reset (self); + } if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) { addr = nm_device_get_permanent_hw_address (self, TRUE); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 1437767127..39ca8c46be 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) @@ -119,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); @@ -169,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); + /*****************************************************************/ static void @@ -482,7 +489,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); - nm_device_hw_addr_reset (device); + g_clear_pointer (&priv->hw_addr_scan, g_free); + _hw_addr_set_scanning (self); /* Ensure we're in infrastructure mode after deactivation; some devices * (usually older ones) don't scan well in adhoc mode. @@ -1012,6 +1020,38 @@ impl_device_wifi_get_all_access_points (NMDeviceWifi *self, g_ptr_array_unref (paths); } +static void +_hw_addr_set_scanning (NMDeviceWifi *self) +{ + NMDevice *device = (NMDevice *) self; + NMDeviceWifiPrivate *priv; + guint32 now; + + 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); + + now = nm_utils_get_monotonic_timestamp_s (); + if ( !priv->hw_addr_scan + || now >= priv->hw_addr_scan_expire) { + /* 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); + + g_free (priv->hw_addr_scan); + priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (); + } + + nm_device_hw_addr_set (device, priv->hw_addr_scan); +} + static void request_scan_cb (NMDevice *device, GDBusMethodInvocation *context, @@ -1321,6 +1361,8 @@ request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options) _LOGD (LOGD_WIFI_SCAN, "no SSIDs to probe scan"); } + _hw_addr_set_scanning (self); + if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) { /* success */ backoff = TRUE; @@ -2195,9 +2237,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); @@ -2212,20 +2251,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; @@ -2314,6 +2342,9 @@ 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 */ if (!nm_device_hw_addr_set_cloned (device, connection, TRUE)) return NM_ACT_STAGE_RETURN_FAILURE; @@ -2956,6 +2987,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); } 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 (); From 7b585bcc932bc3b41d564ee8018ddc6cfaec7d5e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 22 Jun 2016 14:42:20 +0200 Subject: [PATCH 38/43] wifi: make MAC address randomization during scanning configurable This allows the user to disable MAC address randomization during scanning for Wi-Fi networks, which is done by default. For one, this allows the user to disable the randomization for whatever reason. Also, together with configuring the per-connection setting wifi.cloned-mac-address=preserve, this allows to disable NetworkManager to modify the MAC address of the interface. This may allow the user to set the MAC address outside of NetworkManager without NetworkManager interfering. --- man/NetworkManager.conf.xml | 14 ++++++++++++++ src/devices/wifi/nm-device-wifi.c | 22 ++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index a766bf90f9..344aa9585c 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -737,6 +737,20 @@ unmanaged=1 + + 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. + + + diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 39ca8c46be..60f6f99f27 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -174,7 +174,7 @@ static void ap_add_remove (NMDeviceWifi *self, static void remove_supplicant_interface_error_handler (NMDeviceWifi *self); -static void _hw_addr_set_scanning (NMDeviceWifi *self); +static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset); /*****************************************************************/ @@ -490,7 +490,7 @@ deactivate (NMDevice *device) nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE); g_clear_pointer (&priv->hw_addr_scan, g_free); - _hw_addr_set_scanning (self); + _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. @@ -1021,11 +1021,12 @@ impl_device_wifi_get_all_access_points (NMDeviceWifi *self, } static void -_hw_addr_set_scanning (NMDeviceWifi *self) +_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)); @@ -1035,7 +1036,20 @@ _hw_addr_set_scanning (NMDeviceWifi *self) 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); + return; + } + now = nm_utils_get_monotonic_timestamp_s (); + if ( !priv->hw_addr_scan || now >= priv->hw_addr_scan_expire) { /* the random MAC address for scanning expires after a while. @@ -1361,7 +1375,7 @@ request_wireless_scan (NMDeviceWifi *self, GVariant *scan_options) _LOGD (LOGD_WIFI_SCAN, "no SSIDs to probe scan"); } - _hw_addr_set_scanning (self); + _hw_addr_set_scanning (self, FALSE); if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) { /* success */ From df8cf1462a20a49b29a1574b578a7bf53f287056 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 21 Jun 2016 11:04:38 +0200 Subject: [PATCH 39/43] core: refactor unmanaging devices on shutdown and unmanage Wi-Fi devices Add new virtual function nm_device_unmanage_on_quit() to determine whether to unmanage the device on shutdown. This allows Wi-Fi devices to always be unmanaged. We want that to reset the initial MAC address. --- src/devices/nm-device.c | 44 +++++++++++++++++++++++++++++-- src/devices/nm-device.h | 4 ++- src/devices/wifi/nm-device-wifi.c | 13 +++++++++ src/nm-manager.c | 24 +++-------------- 4 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index c448f04818..279d7e327b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -3362,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; } /** @@ -3378,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); @@ -3425,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) { @@ -12357,6 +12396,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] = diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 181ad8e1c5..8383ea250d 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -328,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, @@ -417,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); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 60f6f99f27..ebbd34fb49 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -193,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) { @@ -3097,6 +3109,7 @@ 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; diff --git a/src/nm-manager.c b/src/nm-manager.c index 1ab140fd3d..e7ca81219f 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -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) { From 6829871c1128e5ce10706e4400ff9baefd344f1c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 21 Jun 2016 11:22:07 +0200 Subject: [PATCH 40/43] device: log more details when setting MAC address --- src/devices/nm-device-ethernet.c | 2 +- src/devices/nm-device-macvlan.c | 2 +- src/devices/nm-device-private.h | 4 ++-- src/devices/nm-device-vlan.c | 4 ++-- src/devices/nm-device.c | 34 ++++++++++++++++--------------- src/devices/wifi/nm-device-wifi.c | 4 ++-- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index db2eb4f5f3..6cf544a3e5 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1353,7 +1353,7 @@ deactivate (NMDevice *device) 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 (); - nm_device_hw_addr_reset (device); + nm_device_hw_addr_reset (device, "deactivate"); } static gboolean diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index 583f260e8d..390c2a3975 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -544,7 +544,7 @@ realize_start_notify (NMDevice *device, const NMPlatformLink *plink) static void deactivate (NMDevice *device) { - nm_device_hw_addr_reset (device); + nm_device_hw_addr_reset (device, "deactivate"); } /******************************************************************/ diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index b641751205..7381fd930f 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -56,9 +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_hw_addr_set (NMDevice *device, const char *addr); +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); +gboolean nm_device_hw_addr_reset (NMDevice *device, const char *detail); void nm_device_set_firmware_missing (NMDevice *self, gboolean missing); diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index c710917419..f4513d530c 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -110,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_hw_addr_set (self, new_mac); + 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. */ @@ -616,7 +616,7 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) static void deactivate (NMDevice *device) { - nm_device_hw_addr_reset (device); + nm_device_hw_addr_reset (device, "deactivate"); } /******************************************************************/ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 279d7e327b..d1b0e56357 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11048,7 +11048,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); + nm_device_hw_addr_reset (self, "unmanage"); set_nm_ipv6ll (self, FALSE); restore_ip6_properties (self); } @@ -11603,6 +11603,7 @@ nm_device_hw_addr_is_explict (NMDevice *self) static gboolean _hw_addr_set (NMDevice *self, const char *addr, + const char *operation, const char *detail) { NMDevicePrivate *priv; @@ -11614,7 +11615,7 @@ _hw_addr_set (NMDevice *self, nm_assert (NM_IS_DEVICE (self)); nm_assert (addr); - nm_assert (detail); + nm_assert (operation); priv = NM_DEVICE_GET_PRIVATE (self); @@ -11633,7 +11634,7 @@ _hw_addr_set (NMDevice *self, || !nm_utils_hwaddr_aton (addr, addr_bytes, hw_addr_len)) g_return_val_if_reached (FALSE); - _LOGT (LOGD_DEVICE, "set-hw-addr: setting MAC address to '%s'...", addr); + _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) { @@ -11647,16 +11648,17 @@ _hw_addr_set (NMDevice *self, 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, "set-hw-addr: %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, - "set-hw-addr: new MAC address %s not successfully set", addr); + "set-hw-addr: new MAC address %s not successfully set to %s (%s)", + addr, operation, detail); success = FALSE; } } else { - _LOGW (LOGD_DEVICE, "set-hw-addr: 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) { @@ -11668,7 +11670,7 @@ _hw_addr_set (NMDevice *self, } gboolean -nm_device_hw_addr_set (NMDevice *self, const char *addr) +nm_device_hw_addr_set (NMDevice *self, const char *addr, const char *detail) { NMDevicePrivate *priv; @@ -11684,7 +11686,7 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr) * 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"); + return _hw_addr_set (self, addr, "set", detail); } gboolean @@ -11693,7 +11695,7 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean NMDevicePrivate *priv; gs_free char *hw_addr_tmp = NULL; gs_free char *hw_addr_generated = NULL; - const char *addr; + const char *addr, *addr_setting; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -11702,11 +11704,11 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean if (!connection) g_return_val_if_reached (FALSE); - addr = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp); + 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); + return nm_device_hw_addr_reset (self, addr_setting); } if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) { @@ -11745,11 +11747,11 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean priv->hw_addr_type = HW_ADDR_TYPE_EXPLICIT; } - return _hw_addr_set (self, addr, "set-cloned"); + return _hw_addr_set (self, addr, "set-cloned", addr_setting); } gboolean -nm_device_hw_addr_reset (NMDevice *self) +nm_device_hw_addr_reset (NMDevice *self, const char *detail) { NMDevicePrivate *priv; const char *addr; @@ -11769,7 +11771,7 @@ nm_device_hw_addr_reset (NMDevice *self) g_return_val_if_reached (FALSE); } - return _hw_addr_set (self, addr, "reset"); + return _hw_addr_set (self, addr, "reset", detail); } const char * diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index ebbd34fb49..3db69cd890 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -1056,7 +1056,7 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) if (!randomize) { g_clear_pointer (&priv->hw_addr_scan, g_free); if (do_reset) - nm_device_hw_addr_reset (device); + nm_device_hw_addr_reset (device, "scanning"); return; } @@ -1075,7 +1075,7 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (); } - nm_device_hw_addr_set (device, priv->hw_addr_scan); + nm_device_hw_addr_set (device, priv->hw_addr_scan, "scanning"); } static void From 96cabbcbb8f484f74fe69305b425b5109608e9d7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 22 Jun 2016 20:31:39 +0200 Subject: [PATCH 41/43] all: make MAC address randomization algorithm configurable For the per-connection settings "ethernet.cloned-mac-address" and "wifi.cloned-mac-address", and for the per-device setting "wifi.scan-rand-mac-address", we may generate MAC addresses using either the "random" or "stable" algorithm. Add new properties "generate-mac-address-mask" that allow to configure which bits of the MAC address will be scrambled. By default, the "random" and "stable" algorithms scamble all bits of the MAC address, including the OUI part and generate a locally- administered, unicast address. By specifying a MAC address mask, we can now configure to perserve parts of the current MAC address of the device. For example, setting "FF:FF:FF:00:00:00" will preserve the first 3 octects of the current MAC address. One can also explicitly specify a MAC address to use instead of the current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00" sets the OUI part of the MAC address to "68:F7:28" while scrambling the last 3 octects. Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble all bits of the MAC address, except clearing the second-least significant bit. Thus, creating a burned-in address, globally administered. One can also supply a list of MAC addresses like "FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which case a MAC address is choosen randomly. To fully scamble the MAC address one can configure "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00". which also randomly creates either a locally or globally administered address. With this, the following macchanger options can be implemented: `macchanger --random` This is the default if no mask is configured. -> "" while is the same as: -> "00:00:00:00:00:00" -> "02:00:00:00:00:00 02:00:00:00:00:00" `macchanger --random --bia` -> "02:00:00:00:00:00 00:00:00:00:00:00" `macchanger --ending` This option cannot be fully implemented, because macchanger uses the current MAC address but also implies --bia. -> "FF:FF:FF:00:00:00" This would yields the same result only if the current MAC address is already a burned-in address too. Otherwise, it has not the same effect as --ending. -> "FF:FF:FF:00:00:00 " Alternatively, instead of using the current MAC address, spell the OUI part out. But again, that is not really the same as macchanger does because you explictly have to name the OUI part to use. `machanger --another` `machanger --another_any` -> "FF:FF:FF:00:00:00 ..." "$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")" --- clients/cli/settings.c | 74 ++++++++++++++-------- libnm-core/nm-core-internal.h | 10 +++ libnm-core/nm-setting-wired.c | 88 ++++++++++++++++++++++++++ libnm-core/nm-setting-wired.h | 4 ++ libnm-core/nm-setting-wireless.c | 88 ++++++++++++++++++++++++++ libnm-core/nm-setting-wireless.h | 4 ++ libnm-core/nm-utils.c | 85 +++++++++++++++++++++++++ libnm-core/tests/test-general.c | 1 + libnm/libnm.ver | 2 + man/NetworkManager.conf.xml | 17 +++++ src/devices/nm-device.c | 36 ++++++++++- src/devices/wifi/nm-device-wifi.c | 10 ++- src/nm-core-utils.c | 83 ++++++++++++++++++------ src/nm-core-utils.h | 7 +- src/settings/plugins/ifcfg-rh/reader.c | 8 +++ src/settings/plugins/ifcfg-rh/writer.c | 12 ++++ src/tests/test-utils.c | 48 +++++++++++--- 17 files changed, 518 insertions(+), 59 deletions(-) diff --git a/clients/cli/settings.c b/clients/cli/settings.c index d3645ee031..98d0c8b19d 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -92,13 +92,14 @@ NmcOutputField nmc_fields_setting_wired[] = { SETTING_FIELD (NM_SETTING_WIRED_AUTO_NEGOTIATE), /* 4 */ SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS), /* 5 */ SETTING_FIELD (NM_SETTING_WIRED_CLONED_MAC_ADDRESS), /* 6 */ - SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST), /* 7 */ - SETTING_FIELD (NM_SETTING_WIRED_MTU), /* 8 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS), /* 9 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE), /* 10 */ - SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS), /* 11 */ - SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN), /* 12 */ - SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK), /* 7 */ + SETTING_FIELD (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST), /* 8 */ + SETTING_FIELD (NM_SETTING_WIRED_MTU), /* 9 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_SUBCHANNELS), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_NETTYPE), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRED_S390_OPTIONS), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD), /* 14 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRED_ALL "name"","\ @@ -108,6 +109,7 @@ NmcOutputField nmc_fields_setting_wired[] = { NM_SETTING_WIRED_AUTO_NEGOTIATE","\ NM_SETTING_WIRED_MAC_ADDRESS","\ NM_SETTING_WIRED_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK","\ NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST","\ NM_SETTING_WIRED_MTU","\ NM_SETTING_WIRED_S390_SUBCHANNELS","\ @@ -203,12 +205,13 @@ NmcOutputField nmc_fields_setting_wireless[] = { SETTING_FIELD (NM_SETTING_WIRELESS_TX_POWER), /* 7 */ SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS), /* 8 */ SETTING_FIELD (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS), /* 9 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST), /* 10 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION), /* 11 */ - SETTING_FIELD (NM_SETTING_WIRELESS_MTU), /* 12 */ - SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS), /* 13 */ - SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN), /* 14 */ - SETTING_FIELD (NM_SETTING_WIRELESS_POWERSAVE), /* 15 */ + SETTING_FIELD (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK), /* 10 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST), /* 11 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION), /* 12 */ + SETTING_FIELD (NM_SETTING_WIRELESS_MTU), /* 13 */ + SETTING_FIELD (NM_SETTING_WIRELESS_SEEN_BSSIDS), /* 14 */ + SETTING_FIELD (NM_SETTING_WIRELESS_HIDDEN), /* 15 */ + SETTING_FIELD (NM_SETTING_WIRELESS_POWERSAVE), /* 16 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_WIRELESS_ALL "name"","\ @@ -221,6 +224,7 @@ NmcOutputField nmc_fields_setting_wireless[] = { NM_SETTING_WIRELESS_TX_POWER","\ NM_SETTING_WIRELESS_MAC_ADDRESS","\ NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS","\ + NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK","\ NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST","\ NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION","\ NM_SETTING_WIRELESS_MTU","\ @@ -1765,6 +1769,7 @@ DEFINE_GETTER (nmc_property_wired_get_duplex, NM_SETTING_WIRED_DUPLEX) DEFINE_GETTER (nmc_property_wired_get_auto_negotiate, NM_SETTING_WIRED_AUTO_NEGOTIATE) DEFINE_GETTER (nmc_property_wired_get_mac_address, NM_SETTING_WIRED_MAC_ADDRESS) DEFINE_GETTER (nmc_property_wired_get_cloned_mac_address, NM_SETTING_WIRED_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wired_get_generate_mac_address_mask, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK) DEFINE_GETTER (nmc_property_wired_get_mac_address_blacklist, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST) DEFINE_GETTER (nmc_property_wired_get_s390_subchannels, NM_SETTING_WIRED_S390_SUBCHANNELS) DEFINE_GETTER (nmc_property_wired_get_s390_nettype, NM_SETTING_WIRED_S390_NETTYPE) @@ -1891,6 +1896,7 @@ DEFINE_GETTER (nmc_property_wireless_get_rate, NM_SETTING_WIRELESS_RATE) DEFINE_GETTER (nmc_property_wireless_get_tx_power, NM_SETTING_WIRELESS_TX_POWER) DEFINE_GETTER (nmc_property_wireless_get_mac_address, NM_SETTING_WIRELESS_MAC_ADDRESS) DEFINE_GETTER (nmc_property_wireless_get_cloned_mac_address, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS) +DEFINE_GETTER (nmc_property_wireless_get_generate_mac_address_mask, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK) DEFINE_GETTER (nmc_property_wireless_get_mac_address_blacklist, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST) DEFINE_GETTER (nmc_property_wireless_get_seen_bssids, NM_SETTING_WIRELESS_SEEN_BSSIDS) DEFINE_GETTER (nmc_property_wireless_get_hidden, NM_SETTING_WIRELESS_HIDDEN) @@ -7217,6 +7223,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (WIRED, GENERATE_MAC_ADDRESS_MASK), + nmc_property_wired_get_generate_mac_address_mask, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (WIRED, MAC_ADDRESS_BLACKLIST), nmc_property_wired_get_mac_address_blacklist, nmc_property_wired_set_mac_address_blacklist, @@ -7335,6 +7348,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (WIRELESS, GENERATE_MAC_ADDRESS_MASK), + nmc_property_wireless_get_generate_mac_address_mask, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (WIRELESS, MAC_ADDRESS_BLACKLIST), nmc_property_wireless_get_mac_address_blacklist, nmc_property_wireless_set_mac_address_blacklist, @@ -8134,13 +8154,14 @@ setting_wired_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gb set_val_str (arr, 4, nmc_property_wired_get_auto_negotiate (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 5, nmc_property_wired_get_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 6, nmc_property_wired_get_cloned_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 7, nmc_property_wired_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 8, nmc_property_wired_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 9, nmc_property_wired_get_s390_subchannels (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_wired_get_s390_nettype (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_wired_get_s390_options (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_wired_get_wake_on_lan (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_wired_get_wake_on_lan_password (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 7, nmc_property_wired_get_generate_mac_address_mask (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 8, nmc_property_wired_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 9, nmc_property_wired_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_wired_get_s390_subchannels (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_wired_get_s390_nettype (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_wired_get_s390_options (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_wired_get_wake_on_lan (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_wired_get_wake_on_lan_password (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ @@ -8234,12 +8255,13 @@ setting_wireless_details (NMSetting *setting, NmCli *nmc, const char *one_prop, set_val_str (arr, 7, nmc_property_wireless_get_tx_power (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 8, nmc_property_wireless_get_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 9, nmc_property_wireless_get_cloned_mac_address (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 10, nmc_property_wireless_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 11, nmc_property_wireless_get_mac_address_randomization (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 12, nmc_property_wireless_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 13, nmc_property_wireless_get_seen_bssids (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 14, nmc_property_wireless_get_hidden (setting, NMC_PROPERTY_GET_PRETTY)); - set_val_str (arr, 15, nmc_property_wireless_get_powersave (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_wireless_get_generate_mac_address_mask (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_wireless_get_mac_address_blacklist (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_wireless_get_mac_address_randomization (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 13, nmc_property_wireless_get_mtu (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 14, nmc_property_wireless_get_seen_bssids (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 15, nmc_property_wireless_get_hidden (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 16, nmc_property_wireless_get_powersave (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 96624d05b8..956c910a87 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -283,6 +283,16 @@ void _nm_setting_vlan_get_priorities (NMSettingVlan *setting, /***********************************************************/ +struct ether_addr; + +gboolean _nm_utils_generate_mac_address_mask_parse (const char *value, + struct ether_addr *out_mask, + struct ether_addr **out_ouis, + gsize *out_ouis_len, + GError **error); + +/***********************************************************/ + typedef enum { NM_BOND_OPTION_TYPE_INT, NM_BOND_OPTION_TYPE_STRING, diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c index 3e72056afd..66ed9c48f0 100644 --- a/libnm-core/nm-setting-wired.c +++ b/libnm-core/nm-setting-wired.c @@ -53,6 +53,7 @@ typedef struct { gboolean auto_negotiate; char *device_mac_address; char *cloned_mac_address; + char *generate_mac_address_mask; GArray *mac_address_blacklist; guint32 mtu; char **s390_subchannels; @@ -70,6 +71,7 @@ enum { PROP_AUTO_NEGOTIATE, PROP_MAC_ADDRESS, PROP_CLONED_MAC_ADDRESS, + PROP_GENERATE_MAC_ADDRESS_MASK, PROP_MAC_ADDRESS_BLACKLIST, PROP_MTU, PROP_S390_SUBCHANNELS, @@ -188,6 +190,22 @@ nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting) return NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address; } +/** + * nm_setting_wired_get_generate_mac_address_mask: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:generate-mac-address-mask property of the setting + * + * Since: 1.4 + **/ +const char * +nm_setting_wired_get_generate_mac_address_mask (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->generate_mac_address_mask; +} + /** * nm_setting_wired_get_mac_address_blacklist: * @setting: the #NMSettingWired @@ -612,6 +630,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) GHashTableIter iter; const char *key, *value; int i; + GError *local = NULL; if (priv->port && !g_strv_contains (valid_ports, priv->port)) { g_set_error (error, @@ -704,6 +723,20 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + /* generate-mac-address-mask only makes sense with cloned-mac-address "random" or + * "stable". Still, let's not be so strict about that and accept the value + * even if it is unused. */ + if (!_nm_utils_generate_mac_address_mask_parse (priv->generate_mac_address_mask, + NULL, NULL, NULL, &local)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK); + g_error_free (local); + return FALSE; + } + if ( NM_FLAGS_ANY (priv->wol, NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS) && !nm_utils_is_power_of_two (priv->wol)) { g_set_error_literal (error, @@ -785,6 +818,7 @@ finalize (GObject *object) g_free (priv->device_mac_address); g_free (priv->cloned_mac_address); + g_free (priv->generate_mac_address_mask); g_array_unref (priv->mac_address_blacklist); if (priv->s390_subchannels) @@ -829,6 +863,10 @@ set_property (GObject *object, guint prop_id, priv->cloned_mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value), ETH_ALEN); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_free (priv->generate_mac_address_mask); + priv->generate_mac_address_mask = g_value_dup_string (value); + break; case PROP_MAC_ADDRESS_BLACKLIST: blacklist = g_value_get_boxed (value); g_array_set_size (priv->mac_address_blacklist, 0); @@ -894,6 +932,9 @@ get_property (GObject *object, guint prop_id, case PROP_CLONED_MAC_ADDRESS: g_value_set_string (value, nm_setting_wired_get_cloned_mac_address (setting)); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_value_set_string (value, nm_setting_wired_get_generate_mac_address_mask (setting)); + break; case PROP_MAC_ADDRESS_BLACKLIST: g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data); break; @@ -1121,6 +1162,53 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class) _nm_utils_hwaddr_cloned_data_synth, _nm_utils_hwaddr_cloned_data_set); + /** + * NMSettingWired:generate-mac-address-mask: + * + * With #NMSettingWired:cloned-mac-address setting "random" or "stable", + * by default all bits of the MAC address are scrambled and a locally-administered, + * unicast MAC address is created. This property allows to specify that certain bits + * are fixed. Note that the least significant bit of the first MAC address will + * always be unset to create a unicast MAC address. + * + * If the property is %NULL, it is eligible to be overwritten by a default + * connection setting. If the value is still %NULL or an empty string, the + * default is to create a locally-administered, unicast MAC address. + * + * If the value contains one MAC address, this address is used as mask. The set + * bits of the mask are to be filled with the current MAC address of the device, + * while the unset bits are subject to randomization. + * Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address + * and only randomize the lower 3 bytes using the "random" or "stable" algorithm. + * + * If the value contains one additional MAC address after the mask, + * this address is used instead of the current MAC address to fill the bits + * that shall not be randomized. For example, a value of + * "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address + * to 68:F7:28, while the lower bits are randomized. A value of + * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + * globally-administered, burned-in MAC address. + * + * If the value contains more then one additional MAC addresses, one of + * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" + * will create a fully scrambled MAC address, randomly locally or globally + * administered. + **/ + /* ---ifcfg-rh--- + * property: generate-mac-address-mask + * variable: GENERATE_MAC_ADDRESS_MASK + * description: the MAC address mask for generating randomized and stable + * cloned-mac-address. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_GENERATE_MAC_ADDRESS_MASK, + g_param_spec_string (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + /** * NMSettingWired:mac-address-blacklist: * diff --git a/libnm-core/nm-setting-wired.h b/libnm-core/nm-setting-wired.h index 6bc6678238..30b0b6c254 100644 --- a/libnm-core/nm-setting-wired.h +++ b/libnm-core/nm-setting-wired.h @@ -85,6 +85,7 @@ typedef enum { /*< flags >*/ #define NM_SETTING_WIRED_AUTO_NEGOTIATE "auto-negotiate" #define NM_SETTING_WIRED_MAC_ADDRESS "mac-address" #define NM_SETTING_WIRED_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask" #define NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" #define NM_SETTING_WIRED_MTU "mtu" #define NM_SETTING_WIRED_S390_SUBCHANNELS "s390-subchannels" @@ -117,6 +118,9 @@ gboolean nm_setting_wired_get_auto_negotiate (NMSettingWired *setting const char * nm_setting_wired_get_mac_address (NMSettingWired *setting); const char * nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting); +NM_AVAILABLE_IN_1_4 +const char * nm_setting_wired_get_generate_mac_address_mask (NMSettingWired *setting); + const char * const *nm_setting_wired_get_mac_address_blacklist (NMSettingWired *setting); guint32 nm_setting_wired_get_num_mac_blacklist_items (NMSettingWired *setting); const char * nm_setting_wired_get_mac_blacklist_item (NMSettingWired *setting, diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c index 15ba71b7f8..2aa29c9ee3 100644 --- a/libnm-core/nm-setting-wireless.c +++ b/libnm-core/nm-setting-wireless.c @@ -56,6 +56,7 @@ typedef struct { guint32 tx_power; char *device_mac_address; char *cloned_mac_address; + char *generate_mac_address_mask; GArray *mac_address_blacklist; guint32 mtu; GSList *seen_bssids; @@ -75,6 +76,7 @@ enum { PROP_TX_POWER, PROP_MAC_ADDRESS, PROP_CLONED_MAC_ADDRESS, + PROP_GENERATE_MAC_ADDRESS_MASK, PROP_MAC_ADDRESS_BLACKLIST, PROP_MTU, PROP_SEEN_BSSIDS, @@ -421,6 +423,22 @@ nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting) return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address; } +/** + * nm_setting_wireless_get_generate_mac_address_mask: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:generate-mac-address-mask property of the setting + * + * Since: 1.4 + **/ +const char * +nm_setting_wireless_get_generate_mac_address_mask (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->generate_mac_address_mask; +} + /** * nm_setting_wireless_get_mac_address_blacklist: * @setting: the #NMSettingWireless @@ -723,6 +741,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) GSList *iter; int i; gsize length; + GError *local = NULL; if (!priv->ssid) { g_set_error_literal (error, @@ -814,6 +833,20 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + /* generate-mac-address-mask only makes sense with cloned-mac-address "random" or + * "stable". Still, let's not be so strict about that and accept the value + * even if it is unused. */ + if (!_nm_utils_generate_mac_address_mask_parse (priv->generate_mac_address_mask, + NULL, NULL, NULL, &local)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK); + g_error_free (local); + return FALSE; + } + for (i = 0; i < priv->mac_address_blacklist->len; i++) { const char *mac = g_array_index (priv->mac_address_blacklist, const char *, i); @@ -935,6 +968,7 @@ finalize (GObject *object) g_free (priv->bssid); g_free (priv->device_mac_address); g_free (priv->cloned_mac_address); + g_free (priv->generate_mac_address_mask); g_array_unref (priv->mac_address_blacklist); g_slist_free_full (priv->seen_bssids, g_free); @@ -987,6 +1021,10 @@ set_property (GObject *object, guint prop_id, priv->cloned_mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value), ETH_ALEN); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_free (priv->generate_mac_address_mask); + priv->generate_mac_address_mask = g_value_dup_string (value); + break; case PROP_MAC_ADDRESS_BLACKLIST: blacklist = g_value_get_boxed (value); g_array_set_size (priv->mac_address_blacklist, 0); @@ -1054,6 +1092,9 @@ get_property (GObject *object, guint prop_id, case PROP_CLONED_MAC_ADDRESS: g_value_set_string (value, nm_setting_wireless_get_cloned_mac_address (setting)); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_value_set_string (value, nm_setting_wireless_get_generate_mac_address_mask (setting)); + break; case PROP_MAC_ADDRESS_BLACKLIST: g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data); break; @@ -1362,6 +1403,53 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class) _nm_utils_hwaddr_cloned_data_synth, _nm_utils_hwaddr_cloned_data_set); + /** + * NMSettingWireless:generate-mac-address-mask: + * + * With #NMSettingWireless:cloned-mac-address setting "random" or "stable", + * by default all bits of the MAC address are scrambled and a locally-administered, + * unicast MAC address is created. This property allows to specify that certain bits + * are fixed. Note that the least significant bit of the first MAC address will + * always be unset to create a unicast MAC address. + * + * If the property is %NULL, it is eligible to be overwritten by a default + * connection setting. If the value is still %NULL or an empty string, the + * default is to create a locally-administered, unicast MAC address. + * + * If the value contains one MAC address, this address is used as mask. The set + * bits of the mask are to be filled with the current MAC address of the device, + * while the unset bits are subject to randomization. + * Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address + * and only randomize the lower 3 bytes using the "random" or "stable" algorithm. + * + * If the value contains one additional MAC address after the mask, + * this address is used instead of the current MAC address to fill the bits + * that shall not be randomized. For example, a value of + * "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address + * to 68:F7:28, while the lower bits are randomized. A value of + * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + * globally-administered, burned-in MAC address. + * + * If the value contains more then one additional MAC addresses, one of + * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" + * will create a fully scrambled MAC address, randomly locally or globally + * administered. + **/ + /* ---ifcfg-rh--- + * property: generate-mac-address-mask + * variable: GENERATE_MAC_ADDRESS_MASK + * description: the MAC address mask for generating randomized and stable + * cloned-mac-address. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_GENERATE_MAC_ADDRESS_MASK, + g_param_spec_string (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + /** * NMSettingWireless:mac-address-blacklist: * diff --git a/libnm-core/nm-setting-wireless.h b/libnm-core/nm-setting-wireless.h index 574cee54f2..35fa79c179 100644 --- a/libnm-core/nm-setting-wireless.h +++ b/libnm-core/nm-setting-wireless.h @@ -50,6 +50,7 @@ G_BEGIN_DECLS #define NM_SETTING_WIRELESS_TX_POWER "tx-power" #define NM_SETTING_WIRELESS_MAC_ADDRESS "mac-address" #define NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask" #define NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" #define NM_SETTING_WIRELESS_MTU "mtu" #define NM_SETTING_WIRELESS_SEEN_BSSIDS "seen-bssids" @@ -126,6 +127,9 @@ guint32 nm_setting_wireless_get_tx_power (NMSettingWireless const char *nm_setting_wireless_get_mac_address (NMSettingWireless *setting); const char *nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting); +NM_AVAILABLE_IN_1_4 +const char *nm_setting_wireless_get_generate_mac_address_mask (NMSettingWireless *setting); + const char * const *nm_setting_wireless_get_mac_address_blacklist (NMSettingWireless *setting); guint32 nm_setting_wireless_get_num_mac_blacklist_items (NMSettingWireless *setting); const char * nm_setting_wireless_get_mac_blacklist_item (NMSettingWireless *setting, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 7567f6d88d..50575b30c9 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -3446,6 +3446,91 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, /*****************************************************************************/ +static char * +_split_word (char *s) +{ + /* takes @s and truncates the string on the first white-space. + * then it returns the first word afterwards (again seeking + * over leading white-space). */ + for (; s[0]; s++) { + if (g_ascii_isspace (s[0])) { + s[0] = '\0'; + s++; + while (g_ascii_isspace (s[0])) + s++; + return s; + } + } + return s; +} + +gboolean +_nm_utils_generate_mac_address_mask_parse (const char *value, + struct ether_addr *out_mask, + struct ether_addr **out_ouis, + gsize *out_ouis_len, + GError **error) +{ + gs_free char *s_free = NULL; + char *s, *s_next; + struct ether_addr mask; + gs_unref_array GArray *ouis = NULL; + + g_return_val_if_fail (!error || !*error, FALSE); + + if (!value || !*value) { + /* NULL and "" are valid values and both mean the default + * "q */ + if (out_mask) { + memset (out_mask, 0, sizeof (*out_mask)); + out_mask->ether_addr_octet[0] |= 0x02; + } + NM_SET_OUT (out_ouis, NULL); + NM_SET_OUT (out_ouis_len, 0); + return TRUE; + } + + s_free = g_strdup (value); + s = s_free; + + /* skip over leading whitespace */ + while (g_ascii_isspace (s[0])) + s++; + + /* parse the first mask */ + s_next = _split_word (s); + if (!nm_utils_hwaddr_aton (s, &mask, ETH_ALEN)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("not a valid ethernet MAC address for mask at position %lld"), + (long long) (s - s_free)); + return FALSE; + } + + if (s_next[0]) { + ouis = g_array_sized_new (FALSE, FALSE, sizeof (struct ether_addr), 4); + + do { + s = s_next; + s_next = _split_word (s); + + g_array_set_size (ouis, ouis->len + 1); + if (!nm_utils_hwaddr_aton (s, &g_array_index (ouis, struct ether_addr, ouis->len - 1), ETH_ALEN)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("not a valid ethernet MAC address #%u at position %lld"), + ouis->len, (long long) (s - s_free)); + return FALSE; + } + } while (s_next[0]); + } + + NM_SET_OUT (out_mask, mask); + NM_SET_OUT (out_ouis_len, ouis ? ouis->len : 0); + NM_SET_OUT (out_ouis, ouis ? ((struct ether_addr *) g_array_free (g_steal_pointer (&ouis), FALSE)) : NULL); + return TRUE; +} + +/*****************************************************************************/ + /** * nm_utils_bin2hexstr: * @src: (type guint8) (array length=len): an array of bytes diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 6e4be2cffc..aef9f2b13d 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1941,6 +1941,7 @@ test_connection_diff_a_only (void) { NM_SETTING_WIRED_AUTO_NEGOTIATE, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_CLONED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MTU, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_S390_SUBCHANNELS, NM_SETTING_DIFF_RESULT_IN_A }, diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 47f6598ff6..cc2fc7df62 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1069,6 +1069,8 @@ global: nm_setting_connection_get_stable_id; nm_setting_ip6_config_get_token; nm_setting_ip_config_get_dns_priority; + nm_setting_wired_get_generate_mac_address_mask; + nm_setting_wireless_get_generate_mac_address_mask; nm_vpn_editor_plugin_get_plugin_info; nm_vpn_editor_plugin_get_vt; nm_vpn_editor_plugin_load; diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 344aa9585c..220a12c6a7 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -569,6 +569,9 @@ ipv6.ip6-privacy=0 ethernet.cloned-mac-address If left unspecified, it defaults to "permanent". + + ethernet.generate-mac-address-mask + ethernet.wake-on-lan @@ -600,6 +603,9 @@ ipv6.ip6-privacy=0 wifi.cloned-mac-address If left unspecified, it defaults to "permanent". + + wifi.generate-mac-address-mask + wifi.mac-address-randomization If left unspecified, MAC address randomization is disabled. @@ -751,6 +757,17 @@ unmanaged=1 + + wifi.scan-generate-mac-address-mask + + + Like the per-connection settings ethernet.generate-mac-address-mask + and wifi.generate-mac-address-mask, this allows to configure the + generated MAC addresses during scanning. See manual of nm-settings + for details. + + + diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d1b0e56357..4bd8c25ad6 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11589,6 +11589,34 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool return addr; } +static const char * +_get_generate_mac_address_mask_setting (NMDevice *self, NMConnection *connection, gboolean is_wifi, char **out_value) +{ + NMSetting *setting; + const char *value = NULL; + char *a; + + nm_assert (out_value && !*out_value); + + setting = nm_connection_get_setting (connection, + is_wifi ? NM_TYPE_SETTING_WIRELESS : NM_TYPE_SETTING_WIRED); + if (setting) { + value = is_wifi + ? nm_setting_wireless_get_generate_mac_address_mask ((NMSettingWireless *) setting) + : nm_setting_wired_get_generate_mac_address_mask ((NMSettingWired *) setting); + if (value) + return value; + } + + a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-mac-address-mask", + self); + if (!a) + return NULL; + *out_value = a; + return a; +} + gboolean nm_device_hw_addr_is_explict (NMDevice *self) { @@ -11695,6 +11723,7 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean NMDevicePrivate *priv; gs_free char *hw_addr_tmp = NULL; gs_free char *hw_addr_generated = NULL; + gs_free char *generate_mac_address_mask_tmp = NULL; const char *addr, *addr_setting; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); @@ -11717,7 +11746,8 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean return FALSE; priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_RANDOM)) { - hw_addr_generated = nm_utils_hw_addr_gen_random_eth (); + hw_addr_generated = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (self), + _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp)); if (!hw_addr_generated) { _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "random"); return FALSE; @@ -11731,7 +11761,9 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean stable_id = _get_stable_id (connection, &stable_type); if (stable_id) { hw_addr_generated = nm_utils_hw_addr_gen_stable_eth (stable_type, stable_id, - nm_device_get_ip_iface (self)); + nm_device_get_ip_iface (self), + nm_device_get_initial_hw_address (self), + _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp)); } if (!hw_addr_generated) { _LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "stable"); diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 3db69cd890..5ddf5d84b7 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -1064,6 +1064,8 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) if ( !priv->hw_addr_scan || now >= priv->hw_addr_scan_expire) { + gs_free char *generate_mac_address_mask = NULL; + /* the random MAC address for scanning expires after a while. * * We don't bother with to update the MAC address exactly when @@ -1071,8 +1073,14 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) * a new one.*/ priv->hw_addr_scan_expire = now + (SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN * 60); + generate_mac_address_mask = nm_config_data_get_device_config (NM_CONFIG_GET_DATA, + "wifi.scan-generate-mac-address-mask", + device, + NULL); + g_free (priv->hw_addr_scan); - priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (); + priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (device), + generate_mac_address_mask); } nm_device_hw_addr_set (device, priv->hw_addr_scan, "scanning"); diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 06989fa710..6dfbb4ce10 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -3176,27 +3176,68 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, /*****************************************************************************/ static void -_hw_addr_eth_complete (guint8 *bin_addr) +_hw_addr_eth_complete (struct ether_addr *addr, + const char *current_mac_address, + const char *generate_mac_address_mask) { - /* this LSB of the first octet cannot be set, - * it means Unicast vs. Multicast */ - bin_addr[0] &= ~1; + struct ether_addr mask; + struct ether_addr oui; + struct ether_addr *ouis; + gsize ouis_len; + guint i; /* the second LSB of the first octet means * "globally unique, OUI enforced, BIA (burned-in-address)" - * vs. "locally-administered" */ - bin_addr[0] |= 2; + * vs. "locally-administered". By default, set it to + * generate locally-administered addresses. + * + * Maybe be overwritten by a mask below. */ + addr->ether_addr_octet[0] |= 2; + + if (!generate_mac_address_mask || !*generate_mac_address_mask) + goto out; + if (!_nm_utils_generate_mac_address_mask_parse (generate_mac_address_mask, + &mask, + &ouis, + &ouis_len, + NULL)) + goto out; + + nm_assert ((ouis == NULL) ^ (ouis_len != 0)); + if (ouis) { + /* g_random_int() is good enough here. It uses a static GRand instance + * that is seeded from /dev/urandom. */ + oui = ouis[g_random_int () % ouis_len]; + g_free (ouis); + } else { + if (!nm_utils_hwaddr_aton (current_mac_address, &oui, ETH_ALEN)) + goto out; + } + + for (i = 0; i < ETH_ALEN; i++) { + const guint8 a = addr->ether_addr_octet[i]; + const guint8 o = oui.ether_addr_octet[i]; + const guint8 m = mask.ether_addr_octet[i]; + + addr->ether_addr_octet[i] = (a & ~m) | (o & m); + } + +out: + /* The LSB of the first octet must always be cleared, + * it means Unicast vs. Multicast */ + addr->ether_addr_octet[0] &= ~1; } char * -nm_utils_hw_addr_gen_random_eth (void) +nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, + const char *generate_mac_address_mask) { - guint8 bin_addr[ETH_ALEN]; + struct ether_addr bin_addr; - if (nm_utils_read_urandom (bin_addr, ETH_ALEN) < 0) + if (nm_utils_read_urandom (&bin_addr, ETH_ALEN) < 0) return NULL; - _hw_addr_eth_complete (bin_addr); - return nm_utils_hwaddr_ntoa (bin_addr, ETH_ALEN); + _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); + return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN); } static char * @@ -3204,13 +3245,15 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *stable_id, const guint8 *secret_key, gsize key_len, - const char *ifname) + const char *ifname, + const char *current_mac_address, + const char *generate_mac_address_mask) { GChecksum *sum; guint32 tmp; guint8 digest[32]; gsize len = sizeof (digest); - guint8 bin_addr[ETH_ALEN]; + struct ether_addr bin_addr; guint8 stable_type_uint8; nm_assert (stable_id); @@ -3239,15 +3282,17 @@ _hw_addr_gen_stable_eth (NMUtilsStableType stable_type, g_return_val_if_fail (len == 32, NULL); - memcpy (bin_addr, digest, ETH_ALEN); - _hw_addr_eth_complete (bin_addr); - return nm_utils_hwaddr_ntoa (bin_addr, ETH_ALEN); + memcpy (&bin_addr, digest, ETH_ALEN); + _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); + return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN); } char * nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *stable_id, - const char *ifname) + const char *ifname, + const char *current_mac_address, + const char *generate_mac_address_mask) { gs_free guint8 *secret_key = NULL; gsize key_len = 0; @@ -3262,7 +3307,9 @@ nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, stable_id, secret_key, key_len, - ifname); + ifname, + current_mac_address, + generate_mac_address_mask); } /*****************************************************************************/ diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index 082eabf4f6..bfba35f575 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -371,10 +371,13 @@ gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type, guint dad_counter, GError **error); -char *nm_utils_hw_addr_gen_random_eth (void); +char *nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, + const char *generate_mac_address_mask); char *nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, const char *stable_id, - const char *iname); + const char *ifname, + const char *current_mac_address, + const char *generate_mac_address_mask); void nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len); diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 192327043b..af5d291026 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -3368,6 +3368,10 @@ make_wireless_setting (shvarFile *ifcfg, g_free (value); } + value = svGetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", FALSE); + g_object_set (s_wireless, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, value, NULL); + g_free (value); + value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE); if (value) { char **strv; @@ -3882,6 +3886,10 @@ make_wired_setting (shvarFile *ifcfg, g_free (value); } + value = svGetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", FALSE); + g_object_set (s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, value, NULL); + g_free (value); + value = svGetValue (ifcfg, "HWADDR_BLACKLIST", FALSE); if (value) { char **strv; diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 1683a0e33c..3682640077 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -853,6 +853,10 @@ write_wireless_setting (NMConnection *connection, cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE); + svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wireless_get_generate_mac_address_mask (s_wireless), + FALSE); + svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE); macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); if (macaddr_blacklist[0]) { @@ -1111,6 +1115,10 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE); + svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wired_get_generate_mac_address_mask (s_wired), + FALSE); + svSetValue (ifcfg, "HWADDR_BLACKLIST", NULL, FALSE); macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired); if (macaddr_blacklist[0]) { @@ -1260,6 +1268,10 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg) cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); svSetValue (ifcfg, "MACADDR", cloned_mac, FALSE); + svSetValue (ifcfg, "GENERATE_MAC_ADDRESS_MASK", + nm_setting_wired_get_generate_mac_address_mask (s_wired), + FALSE); + mtu = nm_setting_wired_get_mtu (s_wired); if (mtu) { tmp = g_strdup_printf ("%u", mtu); diff --git a/src/tests/test-utils.c b/src/tests/test-utils.c index 2a3fd9d63d..000a9d1ef4 100644 --- a/src/tests/test-utils.c +++ b/src/tests/test-utils.c @@ -64,32 +64,60 @@ _do_test_hw_addr (NMUtilsStableType stable_type, const guint8 *secret_key, gsize key_len, const char *ifname, - const char *expected) + const char *current_mac_address, + const char *generate_mac_address_mask, + const char **expected) { gs_free char *generated = NULL; + const char **e; + gboolean found = FALSE; - g_assert (expected); - g_assert (nm_utils_hwaddr_valid (expected, ETH_ALEN)); + for (e = expected; *e; e++) { + g_assert (*e); + g_assert (nm_utils_hwaddr_valid (*e, ETH_ALEN)); + } generated = _hw_addr_gen_stable_eth (stable_type, stable_id, secret_key, key_len, - ifname); + ifname, + current_mac_address, + generate_mac_address_mask); g_assert (generated); g_assert (nm_utils_hwaddr_valid (generated, ETH_ALEN)); - g_assert_cmpstr (generated, ==, expected); - g_assert (nm_utils_hwaddr_matches (generated, -1, expected, -1)); + for (e = expected; *e; e++) { + if (!nm_utils_hwaddr_matches (generated, -1, *e, -1)) + continue; + g_assert (!found); + found = TRUE; + g_assert_cmpstr (generated, ==, *e); + } + g_assert (found); } -#define do_test_hw_addr(stable_type, stable_id, secret_key, ifname, expected) \ - _do_test_hw_addr ((stable_type), (stable_id), (const guint8 *) ""secret_key"", NM_STRLEN (secret_key), (ifname), ""expected"") +#define do_test_hw_addr(stable_type, stable_id, secret_key, ifname, current_mac_address, generate_mac_address_mask, ...) \ + _do_test_hw_addr ((stable_type), (stable_id), (const guint8 *) ""secret_key"", NM_STRLEN (secret_key), (ifname), ""current_mac_address"", generate_mac_address_mask, (const char *[]) { __VA_ARGS__, NULL }) static void test_hw_addr_gen_stable_eth (void) { - do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "06:0D:CD:0C:9E:2C"); - do_test_hw_addr (NM_UTILS_STABLE_TYPE_STABLE_ID, "stable-1", "key1", "eth0", "C6:AE:A9:9A:76:09"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", NULL, "06:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_STABLE_ID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", NULL, "C6:AE:A9:9A:76:09"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "FF:FF:FF:00:00:00", "00:23:45:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "03:23:45:67:89:ab", "FF:FF:FF:00:00:00", "02:23:45:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "00:00:00:00:00:00", "06:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00", "04:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00", "04:0D:CD:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 00:00:00:00:00:00", "04:0D:CD:0C:9E:2C"); + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 02:00:00:00:00:00", "06:0D:CD:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "00:00:00:00:00:00 E9:60:CE:F5:ED:2F", "06:0D:CD:0C:9E:2C"); + + do_test_hw_addr (NM_UTILS_STABLE_TYPE_UUID, "stable-1", "key1", "eth0", "01:23:45:67:89:ab", "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00", "06:0D:CD:0C:9E:2C", "04:0D:CD:0C:9E:2C"); } /*****************************************************************************/ From 4b2e375b3393ca4ebb1069279e919cd5fc9fd2ee Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 23 Jun 2016 15:57:37 +0200 Subject: [PATCH 42/43] device: reset MAC address in NMDevice's deactivate() Instead of letting different subclasses call reset in their virtual deactivate() function, do it in the parent class. This works nicely, because the parent know whether the MAC address is currently modified. --- src/devices/nm-device-ethernet.c | 2 -- src/devices/nm-device-macvlan.c | 7 ------- src/devices/nm-device-vlan.c | 7 ------- src/devices/nm-device.c | 2 ++ 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index 6cf544a3e5..5f7c9f0da6 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -1352,8 +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 (); - - nm_device_hw_addr_reset (device, "deactivate"); } static gboolean diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index 390c2a3975..2bfc65cb2d 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -541,12 +541,6 @@ realize_start_notify (NMDevice *device, const NMPlatformLink *plink) update_properties (device); } -static void -deactivate (NMDevice *device) -{ - nm_device_hw_addr_reset (device, "deactivate"); -} - /******************************************************************/ static void @@ -627,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-vlan.c b/src/devices/nm-device-vlan.c index f4513d530c..16edc0d2ba 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -613,12 +613,6 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) } } -static void -deactivate (NMDevice *device) -{ - nm_device_hw_addr_reset (device, "deactivate"); -} - /******************************************************************/ static void @@ -682,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.c b/src/devices/nm-device.c index 4bd8c25ad6..fb15f0fdc4 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -10653,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); From 60a88fb575425809cdc094b23c4e8ddc8096d2c3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 23 Jun 2016 16:01:10 +0200 Subject: [PATCH 43/43] device: don't regenerate MAC address on multiple _set_cloned() calls Wi-Fi device first have a state-transition "disconnected -> prepare" on which they run activate_stage1_device_prepare() and set the MAC address the first time. Later, after getting secrets, they have a state transition "need-auth -> prepare" and end up calling nm_device_hw_addr_set_cloned() again. In this case, we must not regenerate a new MAC address but bail out. There is a small uncertainty there, because we are not sure that the previously generated connection really entailed the same settings. But since we always call nm_device_hw_addr_reset() during device deactivation, this cannot be a left-over from a previous activation and is thus the same activation request. --- src/devices/nm-device.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index fb15f0fdc4..76133bfb9e 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -11748,6 +11748,12 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean return FALSE; priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT; } else if (NM_IN_STRSET (addr, NM_CLONED_MAC_RANDOM)) { + 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) { @@ -11760,6 +11766,12 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean 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,