summary refs log tree commit diff
path: root/src/nm-device-ethernet.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2010-11-29 19:00:22 +0100
committerMichael Biebl <biebl@debian.org>2010-11-29 19:00:22 +0100
commita6b89d35fabd9f5934b84d7cde22e9268f92c3d3 (patch)
tree9e44755880d5933fcc90c6008d4585a76298a1bd /src/nm-device-ethernet.c
parent9fae01fa351805b2903e535736e06971bc0b925e (diff)
Imported Upstream version 0.8.2 upstream/0.8.2
Diffstat (limited to 'src/nm-device-ethernet.c')
-rw-r--r--src/nm-device-ethernet.c400
1 files changed, 365 insertions, 35 deletions
diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c
index be6d4e2f..08bbc501 100644
--- a/src/nm-device-ethernet.c
+++ b/src/nm-device-ethernet.c
@@ -34,6 +34,9 @@
 #include <linux/if.h>
 #include <errno.h>
 
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
+
 #include <netlink/route/addr.h>
 
 #include "nm-glib-compat.h"
@@ -102,10 +105,12 @@ typedef struct Supplicant {
 } Supplicant;
 
 typedef struct {
-	gboolean	disposed;
+	gboolean            disposed;
 
-	struct ether_addr	hw_addr;
-	gboolean			carrier;
+	guint8              hw_addr[ETH_ALEN];         /* Currently set MAC address */
+	guint8              perm_hw_addr[ETH_ALEN];    /* Permanent MAC address */
+	guint8              initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
+	gboolean            carrier;
 
 	NMNetlinkMonitor *  monitor;
 	gulong              link_connected_id;
@@ -115,6 +120,12 @@ typedef struct {
 	Supplicant          supplicant;
 	guint               supplicant_timeout_id;
 
+	/* s390 */
+	char *              subchan1;
+	char *              subchan2;
+	char *              subchan3;
+	char *              subchannels; /* Composite used for checking unmanaged specs */
+
 	/* PPPoE */
 	NMPPPManager *ppp_manager;
 	NMIP4Config  *pending_ip4_config;
@@ -131,6 +142,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 enum {
 	PROP_0,
 	PROP_HW_ADDRESS,
+	PROP_PERM_HW_ADDRESS,
 	PROP_SPEED,
 	PROP_CARRIER,
 
@@ -289,6 +301,109 @@ carrier_off (NMNetlinkMonitor *monitor,
 	}
 }
 
+static void
+_update_s390_subchannels (NMDeviceEthernet *self)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	const char *iface;
+	GUdevClient *client;
+	GUdevDevice *dev;
+	GUdevDevice *parent;
+	const char *parent_path, *item, *driver;
+	const char *subsystems[] = { "net", NULL };
+	GDir *dir;
+	GError *error = NULL;
+
+	iface = nm_device_get_iface (NM_DEVICE (self));
+
+	client = g_udev_client_new (subsystems);
+	if (!client) {
+		nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to initialize GUdev client", iface);
+		return;
+	}
+
+	dev = g_udev_client_query_by_subsystem_and_name (client, "net", iface);
+	if (!dev) {
+		nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to find device with udev", iface);
+		goto out;
+	}
+
+	/* Try for the "ccwgroup" parent */
+	parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL);
+	if (!parent) {
+		/* FIXME: whatever 'lcs' devices' subsystem is here... */
+		if (!parent) {
+			/* Not an s390 device */
+			goto out;
+		}
+	}
+
+	parent_path = g_udev_device_get_sysfs_path (parent);
+	dir = g_dir_open (parent_path, 0, &error);
+	if (!dir) {
+		nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to open directory '%s': %s",
+		             iface, parent_path,
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+		goto out;
+	}
+
+	/* FIXME: we probably care about ordering here to ensure that we map
+	 * cdev0 -> subchan1, cdev1 -> subchan2, etc.
+	 */
+	while ((item = g_dir_read_name (dir))) {
+		char buf[50];
+		char *cdev_path;
+
+		if (strncmp (item, "cdev", 4))
+			continue;  /* Not a subchannel link */
+
+		cdev_path = g_strdup_printf ("%s/%s", parent_path, item);
+
+		memset (buf, 0, sizeof (buf));
+		errno = 0;
+		if (readlink (cdev_path, &buf[0], sizeof (buf) - 1) >= 0) {
+			if (!priv->subchan1)
+				priv->subchan1 = g_path_get_basename (buf);
+			else if (!priv->subchan2)
+				priv->subchan2 = g_path_get_basename (buf);
+			else if (!priv->subchan3)
+				priv->subchan3 = g_path_get_basename (buf);
+		} else {
+			nm_log_warn (LOGD_DEVICE | LOGD_HW,
+			             "(%s): failed to read cdev link '%s': %s",
+			             iface, cdev_path, errno);
+		}
+		g_free (cdev_path);
+	};
+
+	g_dir_close (dir);
+
+	if (priv->subchan3) {
+		priv->subchannels = g_strdup_printf ("%s,%s,%s",
+		                                     priv->subchan1,
+		                                     priv->subchan2,
+		                                     priv->subchan3);
+	} else if (priv->subchan2) {
+		priv->subchannels = g_strdup_printf ("%s,%s",
+		                                     priv->subchan1,
+		                                     priv->subchan2);
+	} else
+		priv->subchannels = g_strdup (priv->subchan1);
+
+	driver = nm_device_get_driver (NM_DEVICE (self));
+	nm_log_info (LOGD_DEVICE | LOGD_HW,
+	             "(%s): found s390 '%s' subchannels [%s]",
+	             iface, driver ? driver : "(unknown driver)", priv->subchannels);
+
+out:
+	if (parent)
+		g_object_unref (parent);
+	if (dev)
+		g_object_unref (dev);
+	g_object_unref (client);
+}
+
 static GObject*
 constructor (GType type,
 			 guint n_construct_params,
@@ -312,6 +427,9 @@ constructor (GType type,
 	            nm_device_get_iface (NM_DEVICE (self)),
 	            nm_device_get_ifindex (NM_DEVICE (self)));
 
+	/* s390 stuff */
+	_update_s390_subchannels (NM_DEVICE_ETHERNET (self));
+
 	caps = nm_device_get_capabilities (self);
 	if (caps & NM_DEVICE_CAP_CARRIER_DETECT) {
 		GError *error = NULL;
@@ -449,10 +567,13 @@ nm_device_ethernet_new (const char *udi,
 void
 nm_device_ethernet_get_address (NMDeviceEthernet *self, struct ether_addr *addr)
 {
+	NMDeviceEthernetPrivate *priv;
+
 	g_return_if_fail (self != NULL);
 	g_return_if_fail (addr != NULL);
 
-	memcpy (addr, &(NM_DEVICE_ETHERNET_GET_PRIVATE (self)->hw_addr), sizeof (struct ether_addr));
+	priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	memcpy (addr, &priv->hw_addr, sizeof (priv->hw_addr));
 }
 
 /* Returns speed in Mb/s */
@@ -496,10 +617,65 @@ out:
 }
 
 static void
+_update_hw_addr (NMDeviceEthernet *self, const guint8 *addr)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	g_return_if_fail (addr != NULL);
+
+	if (memcmp (&priv->hw_addr, addr, ETH_ALEN)) {
+		memcpy (&priv->hw_addr, addr, ETH_ALEN);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_ETHERNET_HW_ADDRESS);
+	}
+}
+
+static gboolean
+_set_hw_addr (NMDeviceEthernet *self, const guint8 *addr, const char *detail)
+{
+	NMDevice *dev = NM_DEVICE (self);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	const char *iface;
+	char *mac_str = NULL;
+	gboolean success = FALSE;
+
+	g_return_val_if_fail (addr != NULL, FALSE);
+
+	iface = nm_device_get_iface (dev);
+
+	mac_str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+	                           addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+	/* Do nothing if current MAC is same */
+	if (!memcmp (&priv->hw_addr, addr, ETH_ALEN)) {
+		nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): no MAC address change needed",
+		            iface, detail, mac_str);
+		g_free (mac_str);
+		return TRUE;
+	}
+
+	/* Can't change MAC address while device is up */
+	real_hw_take_down (dev);
+
+	success = nm_system_device_set_mac (iface, (struct ether_addr *) addr);
+	if (success) {
+		/* MAC address succesfully changed; update the current MAC to match */
+		_update_hw_addr (self, addr);
+		nm_log_info (LOGD_DEVICE | LOGD_ETHER, "(%s): %s MAC address to %s",
+		             iface, detail, mac_str);
+	} else {
+		nm_log_warn (LOGD_DEVICE | LOGD_ETHER, "(%s): failed to %s MAC address to %s",
+		             iface, detail, mac_str);
+	}
+	real_hw_bring_up (dev, NULL);
+	g_free (mac_str);
+
+	return success;
+}
+
+static void
 real_update_hw_address (NMDevice *dev)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	struct ifreq req;
 	int fd;
 
@@ -511,27 +687,91 @@ real_update_hw_address (NMDevice *dev)
 
 	memset (&req, 0, sizeof (struct ifreq));
 	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+
+	errno = 0;
 	if (ioctl (fd, SIOCGIFHWADDR, &req) < 0) {
 		nm_log_err (LOGD_HW | LOGD_ETHER,
-		            "(%s) error getting hardware address: %d",
+		            "(%s) failed to read hardware address (error %d)",
 		            nm_device_get_iface (dev), errno);
-		goto out;
+	} else
+		_update_hw_addr (self, (const guint8 *) &req.ifr_hwaddr.sa_data);
+
+	close (fd);
+}
+
+static void
+real_update_permanent_hw_address (NMDevice *dev)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	struct ifreq req;
+	struct ethtool_perm_addr *epaddr = NULL;
+	int fd, ret;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_warn (LOGD_HW, "couldn't open control socket.");
+		return;
 	}
 
-	if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) {
-		memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr));
-		g_object_notify (G_OBJECT (dev), NM_DEVICE_ETHERNET_HW_ADDRESS);
+	/* Get permanent MAC address */
+	memset (&req, 0, sizeof (struct ifreq));
+	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+
+	epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN);
+	epaddr->cmd = ETHTOOL_GPERMADDR;
+	epaddr->size = ETH_ALEN;
+	req.ifr_data = (void *) epaddr;
+
+	errno = 0;
+	ret = ioctl (fd, SIOCETHTOOL, &req);
+	if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) {
+		nm_log_err (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)",
+		            nm_device_get_iface (dev), errno);
+		/* Fall back to current address */
+		memcpy (epaddr->data, &priv->hw_addr, ETH_ALEN);
+	}
+
+	if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) {
+		memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN);
+		g_object_notify (G_OBJECT (dev), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS);
 	}
 
-out:
 	close (fd);
 }
 
+static void
+real_update_initial_hw_address (NMDevice *dev)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	char *mac_str = NULL;
+	guint8 *addr = priv->initial_hw_addr;
+	guint8 zero[ETH_ALEN] = {0,0,0,0,0,0};
+
+	/* This sets initial MAC address from current MAC address. It should only
+	 * be called from NMDevice constructor() to really get the initial address.
+	 */
+	if (!memcmp (&priv->hw_addr, &zero, ETH_ALEN))
+		real_update_hw_address (dev);
+
+	if (memcmp (&priv->initial_hw_addr, &priv->hw_addr, ETH_ALEN))
+		memcpy (&priv->initial_hw_addr, &priv->hw_addr, ETH_ALEN);
+
+	mac_str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+	                           addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+	nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s",
+	            nm_device_get_iface (dev), mac_str);
+
+	g_free (mac_str);
+}
+
 static guint32
 real_get_generic_capabilities (NMDevice *dev)
 {
-	NMDeviceEthernet *	self = NM_DEVICE_ETHERNET (dev);
-	guint32		caps = NM_DEVICE_CAP_NONE;
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+	guint32	caps = NM_DEVICE_CAP_NONE;
 
 	/* cipsec devices are also explicitly unsupported at this time */
 	if (strstr (nm_device_get_iface (dev), "cipsec"))
@@ -573,6 +813,39 @@ real_is_available (NMDevice *dev)
 	return TRUE;
 }
 
+static gboolean
+match_subchans (NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_mac)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	const GPtrArray *subchans;
+	int i;
+
+	*try_mac = TRUE;
+
+	subchans = nm_setting_wired_get_s390_subchannels (s_wired);
+	if (!subchans)
+		return TRUE;
+
+	/* connection requires subchannels but the device has none */
+	if (!priv->subchannels)
+		return FALSE;
+
+	/* Make sure each subchannel in the connection is a subchannel of this device */
+	for (i = 0; i < subchans->len; i++) {
+		const char *candidate = g_ptr_array_index (subchans, i);
+
+		if (   (priv->subchan1 && !strcmp (priv->subchan1, candidate))
+		    || (priv->subchan2 && !strcmp (priv->subchan2, candidate))
+		    || (priv->subchan3 && !strcmp (priv->subchan3, candidate)))
+			continue;
+
+		return FALSE;  /* a subchannel was not found */
+	}
+
+	*try_mac = FALSE;
+	return TRUE;
+}
+
 static NMConnection *
 real_get_best_auto_connection (NMDevice *dev,
                                GSList *connections,
@@ -608,9 +881,13 @@ real_get_best_auto_connection (NMDevice *dev,
 
 		if (s_wired) {
 			const GByteArray *mac;
+			gboolean try_mac = TRUE;
+
+			if (!match_subchans (self, s_wired, &try_mac))
+				continue;
 
 			mac = nm_setting_wired_get_mac_address (s_wired);
-			if (mac && memcmp (mac->data, priv->hw_addr.ether_addr_octet, ETH_ALEN))
+			if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
 				continue;
 		}
 
@@ -1219,6 +1496,31 @@ supplicant_interface_init (NMDeviceEthernet *self)
 }
 
 static NMActStageReturn
+real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+	NMActRequest *req;
+	NMSettingWired *s_wired;
+	const GByteArray *cloned_mac;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_wired = NM_SETTING_WIRED (device_get_setting (dev, NM_TYPE_SETTING_WIRED));
+	g_assert (s_wired);
+
+	/* Set device MAC address if the connection wants to change it */
+	cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
+	if (cloned_mac && (cloned_mac->len == ETH_ALEN))
+		_set_hw_addr (self, (const guint8 *) cloned_mac->data, "set");
+
+	return ret;
+}
+
+static NMActStageReturn
 nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 {
 	NMConnection *connection;
@@ -1446,9 +1748,8 @@ real_act_stage4_get_ip4_config (NMDevice *device,
 static void
 real_deactivate_quickly (NMDevice *device)
 {
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
-
-	nm_device_set_ip_iface (device, NULL);
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
 	if (priv->pending_ip4_config) {
 		g_object_unref (priv->pending_ip4_config);
@@ -1460,7 +1761,10 @@ real_deactivate_quickly (NMDevice *device)
 		priv->ppp_manager = NULL;
 	}
 
-	supplicant_interface_release (NM_DEVICE_ETHERNET (device));
+	supplicant_interface_release (self);
+
+	/* Reset MAC address back to initial address */
+	_set_hw_addr (self, priv->initial_hw_addr, "reset");
 }
 
 static gboolean
@@ -1474,6 +1778,8 @@ real_check_connection_compatible (NMDevice *device,
 	NMSettingWired *s_wired;
 	const char *connection_type;
 	gboolean is_pppoe = FALSE;
+	const GByteArray *mac;
+	gboolean try_mac = TRUE;
 
 	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
 	g_assert (s_con);
@@ -1500,10 +1806,15 @@ real_check_connection_compatible (NMDevice *device,
 	}
 
 	if (s_wired) {
-		const GByteArray *mac;
+		if (!match_subchans (self, s_wired, &try_mac)) {
+			g_set_error (error,
+			             NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,
+			             "The connection's s390 subchannels did not match this device.");
+			return FALSE;
+		}
 
 		mac = nm_setting_wired_get_mac_address (s_wired);
-		if (mac && memcmp (mac->data, &(priv->hw_addr.ether_addr_octet), ETH_ALEN)) {
+		if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) {
 			g_set_error (error,
 			             NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,
 			             "The connection's MAC address did not match this device.");
@@ -1519,37 +1830,39 @@ real_check_connection_compatible (NMDevice *device,
 static gboolean
 spec_match_list (NMDevice *device, const GSList *specs)
 {
-	struct ether_addr ether;
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
 	char *hwaddr;
 	gboolean matched;
 
-	nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (device), &ether);
-	hwaddr = nm_ether_ntop (&ether);
+	hwaddr = nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr);
 	matched = nm_match_spec_hwaddr (specs, hwaddr);
 	g_free (hwaddr);
 
+	if (!matched && priv->subchannels)
+		matched = nm_match_spec_s390_subchannels (specs, priv->subchannels);
+
 	return matched;
 }
 
 static gboolean
 wired_match_config (NMDevice *self, NMConnection *connection)
 {
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMSettingWired *s_wired;
-	struct ether_addr ether;
 	const GByteArray *s_ether;
+	gboolean try_mac = TRUE;
 
 	s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
 	if (!s_wired)
 		return FALSE;
 
+	if (!match_subchans (NM_DEVICE_ETHERNET (self), s_wired, &try_mac))
+		return FALSE;
+
 	/* MAC address check */
 	s_ether = nm_setting_wired_get_mac_address (s_wired);
-	if (s_ether) {
-		nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (self), &ether);
-
-		if (memcmp (s_ether->data, ether.ether_addr_octet, ETH_ALEN))
-			return FALSE;
-	}
+	if (try_mac && s_ether && memcmp (s_ether->data, priv->perm_hw_addr, ETH_ALEN))
+		return FALSE;
 
 	return TRUE;
 }
@@ -1756,6 +2069,11 @@ dispose (GObject *object)
 		priv->monitor = NULL;
 	}
 
+	g_free (priv->subchan1);
+	g_free (priv->subchan2);
+	g_free (priv->subchan3);
+	g_free (priv->subchannels);
+
 	G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
 }
 
@@ -1765,12 +2083,13 @@ get_property (GObject *object, guint prop_id,
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	struct ether_addr hw_addr;
 
 	switch (prop_id) {
 	case PROP_HW_ADDRESS:
-		nm_device_ethernet_get_address (self, &hw_addr);
-		g_value_take_string (value, nm_ether_ntop (&hw_addr));
+		g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->hw_addr));
+		break;
+	case PROP_PERM_HW_ADDRESS:
+		g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr));
 		break;
 	case PROP_SPEED:
 		g_value_set_uint (value, nm_device_ethernet_get_speed (self));
@@ -1818,11 +2137,14 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	parent_class->take_down = real_take_down;
 	parent_class->can_interrupt_activation = real_can_interrupt_activation;
 	parent_class->update_hw_address = real_update_hw_address;
+	parent_class->update_permanent_hw_address = real_update_permanent_hw_address;
+	parent_class->update_initial_hw_address = real_update_initial_hw_address;
 	parent_class->get_best_auto_connection = real_get_best_auto_connection;
 	parent_class->is_available = real_is_available;
 	parent_class->connection_secrets_updated = real_connection_secrets_updated;
 	parent_class->check_connection_compatible = real_check_connection_compatible;
 
+	parent_class->act_stage1_prepare = real_act_stage1_prepare;
 	parent_class->act_stage2_config = real_act_stage2_config;
 	parent_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
 	parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
@@ -1834,8 +2156,16 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	g_object_class_install_property
 		(object_class, PROP_HW_ADDRESS,
 		 g_param_spec_string (NM_DEVICE_ETHERNET_HW_ADDRESS,
-							  "MAC Address",
-							  "Hardware MAC address",
+							  "Active MAC Address",
+							  "Currently set hardware MAC address",
+							  NULL,
+							  G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_PERM_HW_ADDRESS,
+		 g_param_spec_string (NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS,
+							  "Permanent MAC Address",
+							  "Permanent hardware MAC address",
 							  NULL,
 							  G_PARAM_READABLE));