summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2016-05-24 15:57:16 +0200
committerThomas Haller <thaller@redhat.com>2016-06-30 08:29:56 +0200
commit8eed67122c58540360b617eb42d5df8328e21b5d (patch)
treedc235662d6ced2cb0267eee6c14ff53df168c2c0
parent1a6d6d56e646c8c148f442f1d089ecf6a6776298 (diff)
downloadNetworkManager-8eed67122c58540360b617eb42d5df8328e21b5d.tar.gz
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:<TOKEN>" and "stable-bia:<TOKEN>". 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
-rw-r--r--clients/cli/settings.c22
-rw-r--r--clients/tui/nmt-mac-entry.c43
-rw-r--r--clients/tui/nmt-mac-entry.h8
-rw-r--r--clients/tui/nmt-page-ethernet.c2
-rw-r--r--clients/tui/nmt-page-vlan.c2
-rw-r--r--clients/tui/nmt-page-wifi.c4
-rw-r--r--libnm-core/nm-keyfile-reader.c67
-rw-r--r--libnm-core/nm-setting-connection.c2
-rw-r--r--libnm-core/nm-setting-wired.c85
-rw-r--r--libnm-core/nm-setting-wireless.c94
-rw-r--r--libnm-core/nm-utils-private.h23
-rw-r--r--libnm-core/nm-utils.c108
-rw-r--r--libnm-core/tests/test-general.c86
-rw-r--r--man/NetworkManager.conf.xml8
-rw-r--r--shared/nm-common-macros.h15
-rw-r--r--src/NetworkManagerUtils.c10
-rw-r--r--src/devices/nm-device-ethernet.c7
-rw-r--r--src/devices/nm-device-macvlan.c8
-rw-r--r--src/devices/nm-device-private.h3
-rw-r--r--src/devices/nm-device-tun.c7
-rw-r--r--src/devices/nm-device-vlan.c15
-rw-r--r--src/devices/nm-device-vxlan.c7
-rw-r--r--src/devices/nm-device.c147
-rw-r--r--src/devices/wifi/nm-device-wifi.c4
-rw-r--r--src/nm-core-utils.c94
-rw-r--r--src/nm-core-utils.h5
-rw-r--r--src/tests/test-utils.c38
27 files changed, 791 insertions, 123 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 <stdlib.h>
#include <arpa/inet.h>
+#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;
}
@@ -2903,6 +2905,18 @@ nmc_property_set_mac (NMSetting *setting, const char *prop, const char *val, GEr
}
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)
{
const char *mtu = val;
@@ -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 <string.h>
#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 <arpa/inet.h>
#include <string.h>
+#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 <net/ethernet.h>
#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 <string.h>
#include <net/ethernet.h>
-#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 <jansson.h>
#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
@@ -2450,6 +2450,90 @@ test_setting_compare_routes (void)
}
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)
{
gs_unref_object NMSetting *old = NULL, *new = NULL;
@@ -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
@@ -566,6 +566,10 @@ ipv6.ip6-privacy=0
<term><varname>connection.lldp</varname></term>
</varlistentry>
<varlistentry>
+ <term><varname>ethernet.cloned-mac-address</varname></term>
+ <listitem><para>If left unspecified, it defaults to "permanent".</para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>ethernet.wake-on-lan</varname></term>
</varlistentry>
<varlistentry>
@@ -593,6 +597,10 @@ ipv6.ip6-privacy=0
<listitem><para>If left unspecified, default value of 60 seconds is used.</para></listitem>
</varlistentry>
<varlistentry>
+ <term><varname>wifi.cloned-mac-address</varname></term>
+ <listitem><para>If left unspecified, it defaults to "permanent".</para></listitem>
+ </varlistentry>
+ <varlistentry>
<term><varname>wifi.mac-address-randomization</varname></term>
<listitem><para>If left unspecified, MAC address randomization is disabled.</para></listitem>
</varlistentry>
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 ();
}