device: only set permanent hardware address once

While a device is realized, we only want to read the permanent
MAC address once. If that fails, we fallback to the current MAC
address. Thus, we want the permanent address be stable until
the device unrealizes.

While we want to fallback to the current MAC address, in some cases
the caller wants to know whether this was a "real" permanent MAC
address as read via ethtool.
For example, when matching an ethernet device against ethernet.mac-address
property, the fake (current) address should not be used in such case.
This commit is contained in:
Thomas Haller 2016-05-20 13:47:23 +02:00
parent 9fb5558f96
commit 2a94587232
4 changed files with 68 additions and 41 deletions

View file

@ -408,7 +408,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (!match_subchans (self, s_wired, &try_mac))
return FALSE;
perm_hw_addr = nm_device_get_permanent_hw_address (device);
perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
mac = nm_setting_wired_get_mac_address (s_wired);
if (perm_hw_addr) {
if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
@ -1405,7 +1405,7 @@ complete_connection (NMDevice *device,
nm_connection_add_setting (connection, NM_SETTING (s_wired));
}
perm_hw_addr = nm_device_get_permanent_hw_address (device);
perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
if (perm_hw_addr) {
setting_mac = nm_setting_wired_get_mac_address (s_wired);
if (setting_mac) {
@ -1502,7 +1502,7 @@ update_connection (NMDevice *device, NMConnection *connection)
{
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
NMSettingWired *s_wired = nm_connection_get_setting_wired (connection);
const char *perm_hw_addr = nm_device_get_permanent_hw_address (device);
const char *perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
const char *mac = nm_device_get_hw_address (device);
const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
GHashTableIter iter;
@ -1636,7 +1636,7 @@ get_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_PERM_HW_ADDRESS:
g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (object)));
g_value_set_string (value, nm_device_get_permanent_hw_address ((NMDevice *) object, FALSE));
break;
case PROP_SPEED:
g_value_set_uint (value, priv->speed);

View file

@ -237,8 +237,9 @@ typedef struct _NMDevicePrivate {
char * driver_version;
char * firmware_version;
RfKillType rfkill_type;
bool firmware_missing;
bool nm_plugin_missing;
bool firmware_missing:1;
bool nm_plugin_missing:1;
bool hw_addr_perm_fake:1; /* whether the permanent HW address could not be read and is a fake */
GHashTable * available_connections;
char * hw_addr;
guint hw_addr_len;
@ -11371,17 +11372,14 @@ void
nm_device_update_hw_address (NMDevice *self)
{
NMDevicePrivate *priv;
int ifindex;
const guint8 *hwaddr;
gsize hwaddrlen = 0;
ifindex = nm_device_get_ifindex (self);
if (ifindex <= 0)
priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->ifindex <= 0)
return;
priv = NM_DEVICE_GET_PRIVATE (self);
hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &hwaddrlen);
hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &hwaddrlen);
if ( priv->type == NM_DEVICE_TYPE_ETHERNET
&& hwaddr
@ -11433,29 +11431,49 @@ void
nm_device_update_permanent_hw_address (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
size_t len = 0;
gboolean success_read;
if (priv->hw_addr_len) {
if (priv->ifindex > 0) {
guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
size_t len = 0;
gboolean success_read;
success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len);
if (success_read && len == priv->hw_addr_len) {
priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
_LOGD (LOGD_DEVICE | LOGD_HW, "read permanent MAC address %s",
priv->hw_addr_perm);
} else {
/* Fall back to current address */
_LOGD (LOGD_HW | LOGD_ETHER, "%s",
success_read
? "unable to read permanent MAC address"
: "read HW addr length of permanent MAC address differs");
priv->hw_addr_perm = g_strdup (priv->hw_addr);
}
}
if (priv->hw_addr_perm) {
/* the permanent hardware address is only read once and not
* re-read later.
*
* Except during unrealize/realize cycles, where we clear the permanent
* hardware address during unrealization. */
return;
}
if (priv->ifindex <= 0)
return;
if (!priv->hw_addr_len) {
nm_device_update_hw_address (self);
if (!priv->hw_addr_len)
return;
}
success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, priv->ifindex, buf, &len);
if (!success_read || len != priv->hw_addr_len) {
/* Fall back to current address. We use the fake address and keep it
* until the device unrealizes.
*
* In some cases it might be necessary to know whether this is a "real" or
* a temporary address (fake). */
_LOGD (LOGD_HW | LOGD_ETHER, "hw-addr: %s (use current: %s)",
success_read
? "unable to read permanent MAC address"
: "read HW addr length of permanent MAC address differs",
priv->hw_addr);
priv->hw_addr_perm_fake = TRUE;
priv->hw_addr_perm = g_strdup (priv->hw_addr);
return;
}
priv->hw_addr_perm_fake = FALSE;
priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, len);
_LOGD (LOGD_DEVICE, "hw-addr: read permanent MAC address '%s'",
priv->hw_addr_perm);
}
static gboolean
@ -11529,7 +11547,7 @@ nm_device_hw_addr_set (NMDevice *self, const char *addr)
priv = NM_DEVICE_GET_PRIVATE (self);
if (!addr) {
addr = priv->hw_addr_perm;
addr = nm_device_get_permanent_hw_address (self, TRUE);
if (!addr)
return FALSE;
}
@ -11546,18 +11564,26 @@ nm_device_hw_addr_reset (NMDevice *self)
priv = NM_DEVICE_GET_PRIVATE (self);
addr = priv->hw_addr_initial;
addr = nm_device_get_initial_hw_address (self);
if (!addr)
return FALSE;
return _hw_addr_set (self, addr, "reset");
}
const char *
nm_device_get_permanent_hw_address (NMDevice *self)
nm_device_get_permanent_hw_address (NMDevice *self, gboolean fallback_fake)
{
NMDevicePrivate *priv;
g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
return NM_DEVICE_GET_PRIVATE (self)->hw_addr_perm;
priv = NM_DEVICE_GET_PRIVATE (self);
if (!priv->hw_addr_perm)
return NULL;
if ( priv->hw_addr_perm_fake
&& !fallback_fake)
return NULL;
return priv->hw_addr_perm;
}
const char *

View file

@ -353,7 +353,8 @@ guint32 nm_device_get_ip4_route_metric (NMDevice *dev);
guint32 nm_device_get_ip6_route_metric (NMDevice *dev);
const char * nm_device_get_hw_address (NMDevice *dev);
const char * nm_device_get_permanent_hw_address (NMDevice *dev);
const char * nm_device_get_permanent_hw_address (NMDevice *dev,
gboolean fallback_fake);
const char * nm_device_get_initial_hw_address (NMDevice *dev);
NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *dev);

View file

@ -570,7 +570,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
if (!s_wireless)
return FALSE;
perm_hw_addr = nm_device_get_permanent_hw_address (device);
perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
mac = nm_setting_wireless_get_mac_address (s_wireless);
if (perm_hw_addr) {
if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
@ -862,7 +862,7 @@ complete_connection (NMDevice *device,
if (hidden)
g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
perm_hw_addr = nm_device_get_permanent_hw_address (device);
perm_hw_addr = nm_device_get_permanent_hw_address (device, FALSE);
if (perm_hw_addr) {
setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
if (setting_mac) {
@ -2981,7 +2981,7 @@ get_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_PERM_HW_ADDRESS:
g_value_set_string (value, nm_device_get_permanent_hw_address (NM_DEVICE (device)));
g_value_set_string (value, nm_device_get_permanent_hw_address ((NMDevice *) device, FALSE));
break;
case PROP_MODE:
g_value_set_uint (value, priv->mode);