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 1b49f941a6.

"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
This commit is contained in:
Thomas Haller 2016-05-24 15:57:16 +02:00
parent 1a6d6d56e6
commit 8eed67122c
27 changed files with 792 additions and 124 deletions

View file

@ -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;
}
@ -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,

View file

@ -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));
}

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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
**/

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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

View file

@ -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, \

View file

@ -565,6 +565,10 @@ ipv6.ip6-privacy=0
<varlistentry>
<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>
@ -592,6 +596,10 @@ ipv6.ip6-privacy=0
<term><varname>vpn.timeout</varname></term>
<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>

View file

@ -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__ */

View file

@ -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,

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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");
}

View file

@ -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) {

View file

@ -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

View file

@ -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);

View file

@ -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 ();
}