summary refs log tree commit diff
path: root/src/nm-device.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2012-03-24 01:37:02 +0100
committerMichael Biebl <biebl@debian.org>2012-03-24 01:37:02 +0100
commitde06e5715e780baade318f3490ac7a4c9ce84e32 (patch)
tree23fbc3fafc12072476eff98bee60100eb54c29db /src/nm-device.c
parentb436a68a20ff3114ded32a7a3d70cdd4954039f9 (diff)
Imported Upstream version 0.9.4.0 upstream/0.9.4.0
Diffstat (limited to 'src/nm-device.c')
-rw-r--r--src/nm-device.c3159
1 files changed, 1867 insertions, 1292 deletions
diff --git a/src/nm-device.c b/src/nm-device.c
index 95f07e44..1dc94ee9 100644
--- a/src/nm-device.c
+++ b/src/nm-device.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2005 - 2011 Red Hat, Inc.
+ * Copyright (C) 2005 - 2012 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
@@ -36,9 +36,9 @@
 #include <linux/if.h>
 
 #include "nm-glib-compat.h"
-#include "nm-device-interface.h"
 #include "nm-device.h"
 #include "nm-device-private.h"
+#include "backends/nm-backend.h"
 #include "NetworkManagerUtils.h"
 #include "nm-system.h"
 #include "nm-dhcp-manager.h"
@@ -57,31 +57,91 @@
 #include "nm-ip6-manager.h"
 #include "nm-marshal.h"
 #include "nm-rfkill.h"
+#include "nm-firewall-manager.h"
+#include "nm-properties-changed-signal.h"
+#include "nm-enum-types.h"
+#include "nm-settings-connection.h"
 
-#define NM_ACT_REQUEST_IP4_CONFIG "nm-act-request-ip4-config"
-#define NM_ACT_REQUEST_IP6_CONFIG "nm-act-request-ip6-config"
+static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context);
 
-static void device_interface_init (NMDeviceInterface *device_interface_class);
+#include "nm-device-interface-glue.h"
 
-G_DEFINE_TYPE_EXTENDED (NMDevice, nm_device, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT,
-						G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init))
+#define PENDING_IP4_CONFIG "pending-ip4-config"
+#define PENDING_IP6_CONFIG "pending-ip6-config"
+
+#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
+
+/***********************************************************/
+#define NM_DEVICE_ERROR (nm_device_error_quark ())
+
+static GQuark
+nm_device_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-device-error");
+	return quark;
+}
+
+/***********************************************************/
 
 enum {
+	STATE_CHANGED,
+	DISCONNECT_REQUEST,
 	AUTOCONNECT_ALLOWED,
 	LAST_SIGNAL,
 };
-
 static guint signals[LAST_SIGNAL] = { 0 };
 
+enum {
+	PROP_0,
+	PROP_UDI,
+	PROP_IFACE,
+	PROP_IP_IFACE,
+	PROP_DRIVER,
+	PROP_CAPABILITIES,
+	PROP_IP4_ADDRESS,
+	PROP_IP4_CONFIG,
+	PROP_DHCP4_CONFIG,
+	PROP_IP6_CONFIG,
+	PROP_DHCP6_CONFIG,
+	PROP_STATE,
+	PROP_STATE_REASON,
+	PROP_ACTIVE_CONNECTION,
+	PROP_DEVICE_TYPE,
+	PROP_MANAGED,
+	PROP_FIRMWARE_MISSING,
+	PROP_TYPE_DESC,
+	PROP_RFKILL_TYPE,
+	PROP_IFINDEX,
+	LAST_PROP
+};
+
+/***********************************************************/
+
+G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, G_TYPE_OBJECT)
+
 #define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
 
+typedef enum {
+	IP_NONE = 0,
+	IP_CONF,
+	IP_DONE
+} IpState;
+
+typedef struct {
+	NMDeviceState state;
+	NMDeviceStateReason reason;
+	guint id;
+} QueuedState;
+
 typedef struct {
 	gboolean disposed;
 	gboolean initialized;
 
 	NMDeviceState state;
-	guint         failed_to_disconnected_id;
-	guint         unavailable_to_disconnected_id;
+	NMDeviceStateReason state_reason;
+	QueuedState   queued_state;
 
 	char *        udi;
 	char *        path;
@@ -104,12 +164,11 @@ typedef struct {
 	gpointer        act_source_func;
 	guint           act_source6_id;
 	gpointer        act_source6_func;
+	guint           act_dep_result_id;
+	guint           act_dep_timeout_id;
 	gulong          secrets_updated_id;
 	gulong          secrets_failed_id;
 
-	gboolean        ip4_ready;
-	gboolean        ip6_ready;
-
 	/* Generic DHCP stuff */
 	NMDHCPManager * dhcp_manager;
 	guint32         dhcp_timeout;
@@ -117,6 +176,7 @@ typedef struct {
 
 	/* IP4 configuration info */
 	NMIP4Config *   ip4_config;			/* Config from DHCP, PPP, or system config files */
+	IpState         ip4_state;
 	NMDHCPClient *  dhcp4_client;
 	gulong          dhcp4_state_sigid;
 	gulong          dhcp4_timeout_sigid;
@@ -126,45 +186,47 @@ typedef struct {
 	NMDnsMasqManager *dnsmasq_manager;
 	gulong            dnsmasq_state_id;
 
+	/* Firewall Manager */
+	NMFirewallManager *fw_manager;
+	DBusGProxyCall    *fw_call;
+
 	/* avahi-autoipd stuff */
 	GPid    aipd_pid;
 	guint   aipd_watch;
 	guint   aipd_timeout;
-	guint32 aipd_addr;
 
 	/* IP6 configuration info */
 	NMIP6Config *  ip6_config;
+	IpState        ip6_state;
+
 	NMIP6Manager * ip6_manager;
 	gulong         ip6_addrconf_sigid;
 	gulong         ip6_config_changed_sigid;
 	gboolean       ip6_waiting_for_config;
+	/* IP6 config from autoconf */
+	NMIP6Config *  ac_ip6_config;
 
 	char *         ip6_accept_ra_path;
-	guint32        ip6_accept_ra_save;
+	gint32         ip6_accept_ra_save;
+
+	/* IPv6 privacy extensions (RFC4941) */
+	char *         ip6_privacy_tempaddr_path;
+	gint32         ip6_privacy_tempaddr_save;
 
 	NMDHCPClient *  dhcp6_client;
 	guint32         dhcp6_mode;
 	gulong          dhcp6_state_sigid;
 	gulong          dhcp6_timeout_sigid;
 	NMDHCP6Config * dhcp6_config;
+	/* IP6 config from DHCP */
+	NMIP6Config *   dhcp6_ip6_config;
 
 	/* inhibit autoconnect feature */
 	gboolean	autoconnect_inhibit;
-} NMDevicePrivate;
-
-static gboolean check_connection_compatible (NMDeviceInterface *device,
-                                             NMConnection *connection,
-                                             GError **error);
-static gboolean nm_device_activate (NMDeviceInterface *device,
-                                    NMActRequest *req,
-                                    GError **error);
-static void nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason);
-static gboolean device_disconnect (NMDeviceInterface *device, GError **error);
-static gboolean spec_match_list (NMDeviceInterface *device, const GSList *specs);
-static NMConnection *connection_match_config (NMDeviceInterface *device, const GSList *connections);
-static gboolean can_assume_connections (NMDeviceInterface *device);
 
-static void nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family);
+	/* master interface for bridge, bond, vlan, etc */
+	NMDevice *	master;
+} NMDevicePrivate;
 
 static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReason reason);
 
@@ -180,32 +242,12 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev,
                                           gboolean assumed,
                                           NMDeviceStateReason *reason);
 
-static NMActStageReturn dhcp6_start (NMDevice *self,
-                                     NMConnection *connection,
-                                     guint32 dhcp_opt,
-                                     NMDeviceStateReason *reason);
+static gboolean nm_device_activate_ip6_config_commit (gpointer user_data);
 
-static void addrconf6_cleanup (NMDevice *self);
-static void dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release);
 static void dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release);
 
 static const char *reason_to_string (NMDeviceStateReason reason);
 
-
-static void
-device_interface_init (NMDeviceInterface *device_interface_class)
-{
-	/* interface implementation */
-	device_interface_class->check_connection_compatible = check_connection_compatible;
-	device_interface_class->activate = nm_device_activate;
-	device_interface_class->deactivate = nm_device_deactivate;
-	device_interface_class->disconnect = device_disconnect;
-	device_interface_class->spec_match_list = spec_match_list;
-	device_interface_class->connection_match_config = connection_match_config;
-	device_interface_class->can_assume_connections = can_assume_connections;
-}
-
-
 static void
 nm_device_init (NMDevice *self)
 {
@@ -214,6 +256,7 @@ nm_device_init (NMDevice *self)
 	priv->type = NM_DEVICE_TYPE_UNKNOWN;
 	priv->capabilities = NM_DEVICE_CAP_NONE;
 	priv->state = NM_DEVICE_STATE_UNMANAGED;
+	priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
 	priv->dhcp_timeout = 0;
 	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
 }
@@ -245,14 +288,50 @@ update_accept_ra_save (NMDevice *self)
 
 	/* Grab the original value of "accept_ra" so we can restore it when NM exits */
 	priv->ip6_accept_ra_path = new_path;
-	if (!nm_utils_get_proc_sys_net_value (priv->ip6_accept_ra_path,
-	                                      ip_iface,
-	                                      &priv->ip6_accept_ra_save)) {
+	if (!nm_utils_get_proc_sys_net_value_with_bounds (priv->ip6_accept_ra_path,
+	                                                  ip_iface,
+	                                                  &priv->ip6_accept_ra_save,
+	                                                  0, 1)) {
 		g_free (priv->ip6_accept_ra_path);
 		priv->ip6_accept_ra_path = NULL;
 	}
 }
 
+static void
+update_ip6_privacy_save (NMDevice *self)
+{
+	NMDevicePrivate *priv;
+	const char *ip_iface;
+	char *new_path;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	ip_iface = nm_device_get_ip_iface (self);
+
+	new_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/use_tempaddr", ip_iface);
+	g_assert (new_path);
+
+	if (priv->ip6_privacy_tempaddr_path) {
+		/* If the IP iface is different from before, use the new value */
+		if (!strcmp (new_path, priv->ip6_privacy_tempaddr_path)) {
+			g_free (new_path);
+			return;
+		}
+		g_free (priv->ip6_privacy_tempaddr_path);
+	}
+
+	/* Grab the original value of "use_tempaddr" so we can restore it when NM exits */
+	priv->ip6_privacy_tempaddr_path = new_path;
+	if (!nm_utils_get_proc_sys_net_value (priv->ip6_privacy_tempaddr_path,
+	                                      ip_iface,
+	                                      &priv->ip6_privacy_tempaddr_save)) {
+		g_free (priv->ip6_privacy_tempaddr_path);
+		priv->ip6_privacy_tempaddr_path = NULL;
+	}
+}
+
 static GObject*
 constructor (GType type,
 			 guint n_construct_params,
@@ -287,18 +366,12 @@ constructor (GType type,
 		goto error;
 	}
 
-	if (NM_DEVICE_GET_CLASS (dev)->update_hw_address)
-		NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev);
-
-	if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address)
-		NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev);
-
-	if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address)
-		NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev);
-
 	priv->dhcp_manager = nm_dhcp_manager_get ();
 
+	priv->fw_manager = nm_firewall_manager_get ();
+
 	update_accept_ra_save (dev);
+	update_ip6_privacy_save (dev);
 
 	priv->initialized = TRUE;
 	return object;
@@ -308,6 +381,24 @@ error:
 	return NULL;
 }
 
+static void
+constructed (GObject *object)
+{
+	NMDevice *dev = NM_DEVICE (object);
+
+	if (NM_DEVICE_GET_CLASS (dev)->update_hw_address)
+		NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev);
+
+	if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address)
+		NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev);
+
+	if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address)
+		NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev);
+
+	if (G_OBJECT_CLASS (nm_device_parent_class)->constructed)
+		G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
+}
+
 static gboolean
 nm_device_hw_is_up (NMDevice *self)
 {
@@ -412,14 +503,14 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 	priv->ip_iface = g_strdup (iface);
 	if (priv->ip_iface) {
 		priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface);
-		if (!priv->ip_ifindex) {
+		if (priv->ip_ifindex < 0) {
 			nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface);
 		}
 	}
 
 	/* Emit change notification */
 	if (g_strcmp0 (old_ip_iface, priv->ip_iface))
-		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP_IFACE);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_IP_IFACE);
 	g_free (old_ip_iface);
 }
 
@@ -494,6 +585,51 @@ nm_device_get_type_desc (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->type_desc;
 }
 
+/**
+ * nm_device_enslave_slave:
+ * @dev: the master device
+ * @slave: the slave device to enslave
+ *
+ * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc)
+ * then this function enslaves @slave.
+ *
+ * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave
+ *  other devices.
+ */
+gboolean
+nm_device_enslave_slave (NMDevice *dev, NMDevice *slave)
+{
+	g_return_val_if_fail (dev != NULL, FALSE);
+	g_return_val_if_fail (slave != NULL, FALSE);
+	g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
+
+	if (NM_DEVICE_GET_CLASS (dev)->enslave_slave)
+		return NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave);
+	return FALSE;
+}
+
+/**
+ * nm_device_release_slave:
+ * @dev: the master device
+ * @slave: the slave device to release
+ *
+ * If @dev is capable of enslaving other devices (ie it's a bridge, bond, etc)
+ * then this function releases the previously enslaved @slave.
+ *
+ * Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave
+ *  other devices, or if @slave was never enslaved.
+ */
+gboolean
+nm_device_release_slave (NMDevice *dev, NMDevice *slave)
+{
+	g_return_val_if_fail (dev != NULL, FALSE);
+	g_return_val_if_fail (slave != NULL, FALSE);
+
+	if (NM_DEVICE_GET_CLASS (dev)->release_slave)
+		return NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave);
+	return FALSE;
+}
+
 /*
  * nm_device_get_act_request
  *
@@ -508,6 +644,13 @@ nm_device_get_act_request (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->act_request;
 }
 
+NMConnection *
+nm_device_get_connection (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	return priv->act_request ? nm_act_request_get_connection (priv->act_request) : NULL;
+}
 
 gboolean
 nm_device_is_available (NMDevice *self)
@@ -522,6 +665,36 @@ nm_device_is_available (NMDevice *self)
 	return TRUE;
 }
 
+gboolean
+nm_device_get_enabled (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	if (NM_DEVICE_GET_CLASS (self)->get_enabled)
+		return NM_DEVICE_GET_CLASS (self)->get_enabled (self);
+	return TRUE;
+}
+
+void
+nm_device_set_enabled (NMDevice *self, gboolean enabled)
+{
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	if (NM_DEVICE_GET_CLASS (self)->set_enabled)
+		NM_DEVICE_GET_CLASS (self)->set_enabled (self, enabled);
+}
+
+RfKillType
+nm_device_get_rfkill_type (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+
+	return NM_DEVICE_GET_PRIVATE (self)->rfkill_type;
+}
+
 static gboolean
 autoconnect_allowed_accumulator (GSignalInvocationHint *ihint,
                                  GValue *return_accu,
@@ -589,9 +762,7 @@ nm_device_complete_connection (NMDevice *self,
 	g_return_val_if_fail (connection != NULL, FALSE);
 
 	if (!NM_DEVICE_GET_CLASS (self)->complete_connection) {
-		g_set_error (error,
-		             NM_DEVICE_INTERFACE_ERROR,
-		             NM_DEVICE_INTERFACE_ERROR_CONNECTION_INVALID,
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CONNECTION_INVALID,
 		             "Device class %s had no complete_connection method",
 		             G_OBJECT_TYPE_NAME (self));
 		return FALSE;
@@ -605,9 +776,41 @@ nm_device_complete_connection (NMDevice *self,
 	if (success)
 		success = nm_connection_verify (connection, error);
 
+	/* If ip6-privacy is unknown, enable it with temporary address preferred */
+	if (success) {
+		NMSettingIP6Config *s_ip6 = nm_connection_get_setting_ip6_config (connection);
+		if (s_ip6 && nm_setting_ip6_config_get_ip6_privacy (s_ip6) == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
+			g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_IP6_PRIVACY,
+			              NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NULL);
+	}
+
 	return success;
 }
 
+gboolean
+nm_device_check_connection_compatible (NMDevice *device,
+                                       NMConnection *connection,
+                                       GError **error)
+{
+	g_return_val_if_fail (device != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	g_return_val_if_fail (connection != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+	if (NM_DEVICE_GET_CLASS (device)->check_connection_compatible)
+		return NM_DEVICE_GET_CLASS (device)->check_connection_compatible (device, connection, error);
+	return TRUE;
+}
+
+gboolean
+nm_device_can_assume_connections (NMDevice *device)
+{
+	g_return_val_if_fail (device != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+	return !!NM_DEVICE_GET_CLASS (device)->connection_match_config;
+}
+
 static void
 dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer user_data)
 {
@@ -679,25 +882,22 @@ activation_source_schedule (NMDevice *self, GSourceFunc func, int family)
 gboolean
 nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6)
 {
-	NMActRequest *req;
 	NMConnection *connection;
 	NMSettingIP4Config *s_ip4;
 	NMSettingIP6Config *s_ip6;
 
 	g_return_val_if_fail (self != NULL, TRUE);
 
-	req = nm_device_get_act_request (self);
-	g_assert (req);
-	connection = nm_act_request_get_connection (req);
+	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
 	/* Fail the connection if the failed IP method is required to complete */
 	if (ip6) {
-		s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+		s_ip6 = nm_connection_get_setting_ip6_config (connection);
 		if (s_ip6 && !nm_setting_ip6_config_get_may_fail (s_ip6))
 			return TRUE;
 	} else {
-		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+		s_ip4 = nm_connection_get_setting_ip4_config (connection);
 		if (s_ip4 && !nm_setting_ip4_config_get_may_fail (s_ip4))
 			return TRUE;
 	}
@@ -705,185 +905,48 @@ nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6)
 	return FALSE;
 }
 
-static void
-ip6_addrconf_complete (NMIP6Manager *ip6_manager,
-                       int ifindex,
-                       guint dhcp_opts,
-                       gboolean success,
-                       gpointer user_data)
-{
-	NMDevice *self = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActRequest *req;
-	NMConnection *connection;
-	NMActStageReturn ret;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
-	NMDeviceState state;
-
-	if (ifindex != nm_device_get_ip_ifindex (self))
-		return;
-	req = nm_device_get_act_request (self);
-	if (!req)
-		return;
-	connection = nm_act_request_get_connection (req);
-	g_assert (connection);
-
-	if (!priv->ip6_waiting_for_config)
-		return;
-
-	priv->ip6_waiting_for_config = FALSE;
-
-	if (!success) {
-		nm_device_activate_schedule_stage4_ip6_config_timeout (self);
-		return;
-	}
-
-	priv->dhcp6_mode = dhcp_opts;
-
-	/* If addrconf is all that's required, we're done */
-	if (priv->dhcp6_mode == IP6_DHCP_OPT_NONE) {
-		nm_device_activate_schedule_stage4_ip6_config_get (self);
-		return;
-	}
-
-	/* If the router said to use DHCP for managed or otherconf, do it */
-
-	/* Don't re-start DHCPv6 if it's already in progress */
-	state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self));
-	if ((state != NM_DEVICE_STATE_IP_CONFIG) || priv->dhcp6_client)
-		return;
-
-	nm_log_info (LOGD_DEVICE | LOGD_DHCP6,
-	             "Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6"
-	             " as requested by IPv6 router...",
-	             priv->iface);
-
-	ret = dhcp6_start (self, connection, priv->dhcp6_mode, &reason);
-	switch (ret) {
-	case NM_ACT_STAGE_RETURN_SUCCESS:
-		/* Shouldn't get this, but handle it anyway */
-		g_warn_if_reached ();
-		nm_device_activate_schedule_stage4_ip6_config_get (self);
-		break;
-	case NM_ACT_STAGE_RETURN_POSTPONE:
-		/* Success; wait for DHCPv6 to complete */
-		break;
-	default:
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-		break;
-	}
-}
-
-static void
-ip6_config_changed (NMIP6Manager *ip6_manager,
-                    int ifindex,
-                    guint dhcp_opts,
-                    gboolean success,
-                    gpointer user_data)
-{
-	NMDevice *self = NM_DEVICE (user_data);
-
-	if (ifindex != nm_device_get_ip_ifindex (self))
-		return;
-	if (!nm_device_get_act_request (self))
-		return;
-
-	/* FIXME: re-run DHCPv6 here to get any new nameservers or whatever */
-
-	if (!success && (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) {
-		nm_device_state_changed (self,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-		return;
-	}
-
-	nm_device_activate_schedule_stage4_ip6_config_get (self);
-}
-
 static gboolean
 ip6_method_matches (NMConnection *connection, const char *match)
 {
 	NMSettingIP6Config *s_ip6;
 	const char *method = NULL;
 
-	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	if (s_ip6) {
 		method = nm_setting_ip6_config_get_method (s_ip6);
 		g_assert (method);
 	}
 
-	/* Treat missing IP6 setting as IGNORE */
-	if (!s_ip6 && !strcmp (match, NM_SETTING_IP6_CONFIG_METHOD_IGNORE))
+	/* Treat missing IP6 setting as AUTO */
+	if (!s_ip6 && !strcmp (match, NM_SETTING_IP6_CONFIG_METHOD_AUTO))
 		return TRUE;
 
 	return method && !strcmp (method, match);
 }
 
-static gboolean
-addrconf6_setup (NMDevice *self)
+static NMActStageReturn
+real_act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActRequest *req;
-	NMConnection *connection;
-	NMSettingIP6Config *s_ip6;
-	gboolean success;
+	NMActiveConnection *master_ac;
+	NMDevice *master;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 
 	req = nm_device_get_act_request (self);
 	g_assert (req);
-	connection = nm_act_request_get_connection (req);
-	g_assert (connection);
-
-	if (!priv->ip6_manager) {
-		priv->ip6_manager = nm_ip6_manager_get ();
-		priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager,
-		                                             "addrconf-complete",
-		                                             G_CALLBACK (ip6_addrconf_complete),
-		                                             self);
-		priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager,
-		                                                   "config-changed",
-		                                                   G_CALLBACK (ip6_config_changed),
-		                                                   self);
-	}
-
-	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
-	success = nm_ip6_manager_prepare_interface (priv->ip6_manager,
-	                                            nm_device_get_ip_ifindex (self),
-	                                            s_ip6,
-	                                            priv->ip6_accept_ra_path);
-	if (success)
-		priv->ip6_waiting_for_config = TRUE;
-
-	return success;
-}
-
-static void
-addrconf6_cleanup (NMDevice *self)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (!priv->ip6_manager)
-		return;
+	/* If the interface is going to be a slave, let the master enslave it here */
+	master_ac = nm_act_request_get_dependency (req);
+	if (master_ac && NM_IS_ACT_REQUEST (master_ac)) {
+		/* FIXME: handle VPNs here too */
 
-	if (priv->ip6_addrconf_sigid) {
-		g_signal_handler_disconnect (priv->ip6_manager,
-		                             priv->ip6_addrconf_sigid);
-		priv->ip6_addrconf_sigid = 0;
-	}
-	if (priv->ip6_config_changed_sigid) {
-		g_signal_handler_disconnect (priv->ip6_manager,
-		                             priv->ip6_config_changed_sigid);
-		priv->ip6_config_changed_sigid = 0;
+		master = NM_DEVICE (nm_act_request_get_device (NM_ACT_REQUEST (master_ac)));
+		g_assert (master);
+		if (!nm_device_enslave_slave (master, self))
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	nm_ip6_manager_cancel_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self));
-	g_object_unref (priv->ip6_manager);
-	priv->ip6_manager = NULL;
-}
-
-static NMActStageReturn
-real_act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
-{
-	return NM_ACT_STAGE_RETURN_SUCCESS;
+	return ret;
 }
 
 /*
@@ -904,7 +967,12 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, 0);
 
-	priv->ip4_ready = priv->ip6_ready = FALSE;
+	if (priv->act_dep_timeout_id) {
+		g_source_remove (priv->act_dep_timeout_id);
+		priv->act_dep_timeout_id = 0;
+	}
+
+	priv->ip4_state = priv->ip6_state = IP_NONE;
 
 	iface = nm_device_get_iface (self);
 	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
@@ -1029,6 +1097,9 @@ nm_device_activate_schedule_stage2_device_config (NMDevice *self)
 	         nm_device_get_iface (self));
 }
 
+/*********************************************/
+/* avahi-autoipd stuff */
+
 static void
 aipd_timeout_remove (NMDevice *self)
 {
@@ -1062,28 +1133,20 @@ aipd_cleanup (NMDevice *self)
 	}
 
 	aipd_timeout_remove (self);
-
-	priv->aipd_addr = 0;
 }
 
 static NMIP4Config *
-aipd_get_ip4_config (NMDevice *self, NMDeviceStateReason *reason)
+aipd_get_ip4_config (NMDevice *self, struct in_addr lla)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMIP4Config *config = NULL;
 	NMIP4Address *addr;
 	NMIP4Route *route;
 
-	g_return_val_if_fail (priv->aipd_addr > 0, NULL);
-
 	config = nm_ip4_config_new ();
-	if (!config) {
-		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
-		return NULL;
-	}
+	g_assert (config);
 
 	addr = nm_ip4_address_new ();
-	nm_ip4_address_set_address (addr, (guint32) priv->aipd_addr);
+	nm_ip4_address_set_address (addr, (guint32) lla.s_addr);
 	nm_ip4_address_set_prefix (addr, 16);
 	nm_ip4_config_take_address (config, addr);
 
@@ -1098,35 +1161,19 @@ aipd_get_ip4_config (NMDevice *self, NMDeviceStateReason *reason)
 	return config;	
 }
 
-static gboolean
-handle_autoip_change (NMDevice *self, NMDeviceStateReason *reason)
+static void
+autoip_changed (NMDevice *self,
+                NMIP4Config *config,
+                NMSettingIP4Config *s_ip4)
 {
-	NMActRequest *req;
-	NMConnection *connection;
-	NMIP4Config *config;
-
-	g_return_val_if_fail (reason != NULL, FALSE);
-
-	config = aipd_get_ip4_config (self, reason);
-	if (!config) {
-		nm_log_err (LOGD_AUTOIP4, "failed to get autoip config for rebind");
-		return FALSE;
-	}
-
-	req = nm_device_get_act_request (self);
-	g_assert (req);
-	connection = nm_act_request_get_connection (req);
-	g_assert (connection);
-
-	g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config);
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
-	if (!nm_device_set_ip4_config (self, config, FALSE, reason)) {
+	nm_utils_merge_ip4_config (config, s_ip4);
+	if (!nm_device_set_ip4_config (self, config, FALSE, &reason)) {
 		nm_log_err (LOGD_AUTOIP4, "(%s): failed to update IP4 config in response to autoip event.",
-		            nm_device_get_iface (self));
-		return FALSE;
+			        nm_device_get_iface (self));
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 	}
-
-	return TRUE;
 }
 
 #define IPV4LL_NETWORK (htonl (0xA9FE0000L))
@@ -1138,74 +1185,62 @@ nm_device_handle_autoip4_event (NMDevice *self,
                                 const char *address)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActRequest *req;
 	NMConnection *connection = NULL;
 	NMSettingIP4Config *s_ip4 = NULL;
-	NMDeviceState state;
 	const char *iface, *method = NULL;
 
 	g_return_if_fail (event != NULL);
 
-	req = nm_device_get_act_request (self);
-	if (!req)
+	if (priv->act_request == NULL)
 		return;
 
-	connection = nm_act_request_get_connection (req);
-	if (!connection)
-		return;
+	connection = nm_act_request_get_connection (priv->act_request);
+	g_assert (connection);
 
 	/* Ignore if the connection isn't an AutoIP connection */
-	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	if (s_ip4)
 		method = nm_setting_ip4_config_get_method (s_ip4);
 
-	if (!s_ip4 || !method || strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+	if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0)
 		return;
 
 	iface = nm_device_get_iface (self);
-	state = nm_device_get_state (self);
 
 	if (strcmp (event, "BIND") == 0) {
-		struct in_addr ip;
-		NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+		struct in_addr lla;
+		NMIP4Config *config;
 
-		if (inet_pton (AF_INET, address, &ip) <= 0) {
+		if (inet_pton (AF_INET, address, &lla) <= 0) {
 			nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd.",
 			            iface, address);
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
 			return;
 		}
 
-		if ((ip.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
+		if ((lla.s_addr & IPV4LL_NETMASK) != IPV4LL_NETWORK) {
 			nm_log_err (LOGD_AUTOIP4, "(%s): invalid address %s received from avahi-autoipd (not link-local).",
 			            iface, address);
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_AUTOIP_ERROR);
 			return;
 		}
 
-		switch (state) {
-		case NM_DEVICE_STATE_IP_CONFIG:
-			if (priv->aipd_addr) {
-				nm_log_warn (LOGD_AUTOIP4, "(%s): already have autoip address!", iface);
-				return;
-			}
+		config = aipd_get_ip4_config (self, lla);
+		if (config == NULL) {
+			nm_log_err (LOGD_AUTOIP4, "failed to get autoip config");
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+			return;
+		}
 
-			priv->aipd_addr = ip.s_addr;
+		if (priv->ip4_state == IP_CONF) {
 			aipd_timeout_remove (self);
-			nm_device_activate_schedule_stage4_ip4_config_get (self);
-			break;
-		case NM_DEVICE_STATE_IP_CHECK:
-		case NM_DEVICE_STATE_SECONDARIES:
-		case NM_DEVICE_STATE_ACTIVATED:
-			priv->aipd_addr = ip.s_addr;
-			if (!handle_autoip_change (self, &reason))
-				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-			break;
-		default:
-			nm_log_warn (LOGD_AUTOIP4, "(%s): unexpected avahi-autoip event %s for %s.",
-			            iface, event, address);
-			break;
-		}
+			nm_device_activate_schedule_ip4_config_result (self, config);
+		} else if (priv->ip4_state == IP_DONE) {
+			autoip_changed (self, config, s_ip4);
+		} else
+			g_assert_not_reached ();
+
+		g_object_unref (config);
 	} else {
 		nm_log_warn (LOGD_AUTOIP4, "(%s): autoip address %s no longer valid because '%s'.",
 		            iface, address, event);
@@ -1255,15 +1290,14 @@ aipd_timeout_cb (gpointer user_data)
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (!priv->aipd_timeout)
-		return FALSE;
-	priv->aipd_timeout = 0;
-
-	nm_log_info (LOGD_AUTOIP4, "(%s): avahi-autoipd timed out.", nm_device_get_iface (self));
-	aipd_cleanup (self);
+	if (priv->aipd_timeout) {
+		nm_log_info (LOGD_AUTOIP4, "(%s): avahi-autoipd timed out.", nm_device_get_iface (self));
+		priv->aipd_timeout = 0;
+		aipd_cleanup (self);
 
-	if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
-		nm_device_activate_schedule_stage4_ip4_config_timeout (self);
+		if (priv->ip4_state == IP_CONF)
+			nm_device_activate_schedule_ip4_config_timeout (self);
+	}
 
 	return FALSE;
 }
@@ -1279,12 +1313,12 @@ aipd_child_setup (gpointer user_data G_GNUC_UNUSED)
 	setpgid (pid, pid);
 }
 
-static gboolean
-aipd_exec (NMDevice *self, GError **error)
+static NMActStageReturn
+aipd_start (NMDevice *self, NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const char *iface = nm_device_get_iface (self);
 	char *argv[6], *cmdline;
-	gboolean success = FALSE;
 	const char **aipd_binary = NULL;
 	static const char *aipd_paths[] = {
 		"/usr/sbin/avahi-autoipd",
@@ -1292,6 +1326,7 @@ aipd_exec (NMDevice *self, GError **error)
 		NULL
 	};
 	int i = 0;
+	GError *error = NULL;
 
 	aipd_cleanup (self);
 
@@ -1304,8 +1339,11 @@ aipd_exec (NMDevice *self, GError **error)
 	}
 
 	if (!*aipd_binary) {
-		g_set_error (error, 0, 0, "%s", "couldn't find avahi-autoipd");
-		return FALSE;
+		nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4,
+		             "Activation (%s) Stage 3 of 5 (IP Configure Start) failed"
+		             " to start avahi-autoipd: not found", iface);
+		*reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	argv[i++] = (char *) (*aipd_binary);
@@ -1317,13 +1355,24 @@ aipd_exec (NMDevice *self, GError **error)
 	argv[i++] = NULL;
 
 	cmdline = g_strjoinv (" ", argv);
-	nm_log_dbg(LOGD_AUTOIP4, "running: %s", cmdline);
+	nm_log_dbg (LOGD_AUTOIP4, "running: %s", cmdline);
 	g_free (cmdline);
 
-	success = g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                         &aipd_child_setup, NULL, &(priv->aipd_pid), error);
-	if (!success)
-		return FALSE;
+	if (!g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	                    &aipd_child_setup, NULL, &(priv->aipd_pid), &error)) {
+		nm_log_warn (LOGD_DEVICE | LOGD_AUTOIP4,
+		             "Activation (%s) Stage 3 of 5 (IP Configure Start) failed"
+		             " to start avahi-autoipd: %s",
+		             iface,
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+		aipd_cleanup (self);
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4,
+	             "Activation (%s) Stage 3 of 5 (IP Configure Start) started"
+	             " avahi-autoipd...", iface);
 
 	/* Monitor the child process so we know when it dies */
 	priv->aipd_watch = g_child_watch_add (priv->aipd_pid, aipd_watch_cb, self);
@@ -1331,9 +1380,12 @@ aipd_exec (NMDevice *self, GError **error)
 	/* Start a timeout to bound the address attempt */
 	priv->aipd_timeout = g_timeout_add_seconds (20, aipd_timeout_cb, self);
 
-	return TRUE;
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
+/*********************************************/
+/* DHCPv4 stuff */
+
 static void
 dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
 {
@@ -1343,156 +1395,95 @@ dhcp4_add_option_cb (gpointer key, gpointer value, gpointer user_data)
 }
 
 static void
-dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data)
+dhcp4_lease_change (NMDevice *device, NMIP4Config *config)
 {
-	nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data),
-	                            (const char *) key,
-	                            (const char *) value);
-}
-
-static void
-handle_dhcp_lease_change (NMDevice *device, gboolean ipv6)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	NMIP4Config *ip4_config;
-	NMSettingIP4Config *s_ip4;
-	NMIP6Config *ip6_config;
-	NMSettingIP6Config *s_ip6;
 	NMConnection *connection;
-	NMActRequest *req;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
-	gboolean assumed;
 
-	req = nm_device_get_act_request (device);
-	g_assert (req);
-	connection = nm_act_request_get_connection (req);
+	if (config == NULL) {
+		nm_log_warn (LOGD_DHCP4, "(%s): failed to get DHCPv4 config for rebind",
+		             nm_device_get_ip_iface (device));
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		return;
+	}
+
+	connection = nm_device_get_connection (device);
 	g_assert (connection);
-	assumed = nm_act_request_get_assumed (req);
-
-	if (ipv6) {
-		ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
-		if (!ip6_config) {
-			nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv6 config for rebind",
-			             nm_device_get_ip_iface (device));
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
-			return;
-		}
 
-		s_ip6 = NM_SETTING_IP6_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG));
-		nm_utils_merge_ip6_config (ip6_config, s_ip6);
+	/* Merge with user overrides */
+	nm_utils_merge_ip4_config (config, nm_connection_get_setting_ip4_config (connection));
 
-		g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP6_CONFIG, ip6_config);
+	if (!nm_device_set_ip4_config (device, config, FALSE, &reason)) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv4 config in response to DHCP event.",
+		             nm_device_get_ip_iface (device));
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		return;
+	}
 
-		if (nm_device_set_ip6_config (device, ip6_config, assumed, &reason)) {
-			nm_dhcp6_config_reset (priv->dhcp6_config);
-			nm_dhcp_client_foreach_option (priv->dhcp6_client,
-			                               dhcp6_add_option_cb,
-			                               priv->dhcp6_config);
-			nm_utils_call_dispatcher ("dhcp6-change", connection, device, NULL, NULL, NULL);
-		} else {
-			nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.",
-			             nm_device_get_ip_iface (device));
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
-		}
-	} else {
-		ip4_config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
-		if (!ip4_config) {
-			nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv4 config for rebind",
-			             nm_device_get_ip_iface (device));
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
-			return;
-		}
+	/* Notify dispatcher scripts of new DHCP4 config */
+	nm_utils_call_dispatcher ("dhcp4-change", connection, device, NULL, NULL, NULL);
+}
 
-		s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG));
-		nm_utils_merge_ip4_config (ip4_config, s_ip4);
+static void
+dhcp4_fail (NMDevice *device, gboolean timeout)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
 
-		g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
+	nm_dhcp4_config_reset (priv->dhcp4_config);
 
-		if (nm_device_set_ip4_config (device, ip4_config, assumed, &reason)) {
-			nm_dhcp4_config_reset (priv->dhcp4_config);
-			nm_dhcp_client_foreach_option (priv->dhcp4_client,
-			                               dhcp4_add_option_cb,
-			                               priv->dhcp4_config);
-			nm_utils_call_dispatcher ("dhcp4-change", connection, device, NULL, NULL, NULL);
-		} else {
-			nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv4 config in response to DHCP event.",
-			             nm_device_get_ip_iface (device));
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
-		}
-	}
+	if (timeout || (priv->ip4_state == IP_CONF))
+		nm_device_activate_schedule_ip4_config_timeout (device);
+	else if (priv->ip4_state == IP_DONE)
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
 }
 
 static void
-dhcp_state_changed (NMDHCPClient *client,
-					NMDHCPState state,
-					gpointer user_data)
+dhcp4_state_changed (NMDHCPClient *client,
+                     NMDHCPState state,
+                     gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
 	NMDeviceState dev_state;
-	gboolean ipv6;
+	NMIP4Config *config;
 
-	ipv6 = nm_dhcp_client_get_ipv6 (client);
-	dev_state = nm_device_get_state (device);
+	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE);
 
-	if (ipv6) {
-		nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d",
-		            nm_device_get_iface (device), dev_state);
-	} else {
-		nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d",
-		            nm_device_get_iface (device), dev_state);
-	}
+	nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d",
+	            nm_device_get_iface (device), state);
+
+	dev_state = nm_device_get_state (device);
 
 	switch (state) {
 	case DHC_BOUND4:     /* lease obtained */
-	case DHC_BOUND6:
 	case DHC_RENEW4:     /* lease renewed */
-	case DHC_RENEW6:     /* lease renewed */
 	case DHC_REBOOT:     /* have valid lease, but now obtained a different one */
 	case DHC_REBIND4:    /* new, different lease */
-	case DHC_REBIND6:    /* new, different lease */
-		if (dev_state == NM_DEVICE_STATE_IP_CONFIG) {
-			if (ipv6)
-				nm_device_activate_schedule_stage4_ip6_config_get (device);
-			else
-				nm_device_activate_schedule_stage4_ip4_config_get (device);
-		} else if (dev_state == NM_DEVICE_STATE_ACTIVATED)
-			handle_dhcp_lease_change (device, ipv6);
-		break;
-	case DHC_TIMEOUT: /* timed out contacting DHCP server */
-		if (ipv6) {
-			nm_dhcp6_config_reset (priv->dhcp6_config);
-			if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
-				nm_device_activate_schedule_stage4_ip6_config_timeout (device);
-		} else {
+		config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
+		if (priv->ip4_state == IP_CONF)
+			nm_device_activate_schedule_ip4_config_result (device, config);
+		else if (priv->ip4_state == IP_DONE)
+			dhcp4_lease_change (device, config);
+
+		if (config) {
+			/* Update the DHCP4 config object with new DHCP options */
 			nm_dhcp4_config_reset (priv->dhcp4_config);
-			if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
-				nm_device_activate_schedule_stage4_ip4_config_timeout (device);
+			nm_dhcp_client_foreach_option (priv->dhcp4_client,
+			                               dhcp4_add_option_cb,
+			                               priv->dhcp4_config);
+			g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP4_CONFIG);
+
+			g_object_unref (config);
 		}
 		break;
+	case DHC_TIMEOUT: /* timed out contacting DHCP server */
+		dhcp4_fail (device, TRUE);
+		break;
 	case DHC_END: /* dhclient exited normally */
-		/* In IPv6 info-only mode, the client doesn't handle leases so it
-		 * may exit right after getting a response from the server.  That's
-		 * normal.  In that case we just ignore the exit.
-		 */
-		if (ipv6 && (priv->dhcp6_mode == IP6_DHCP_OPT_OTHERCONF))
-			break;
-		/* Otherwise, fall through */
 	case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
 	case DHC_ABEND: /* dhclient exited abnormally */
-		if (ipv6)
-			nm_dhcp6_config_reset (priv->dhcp6_config);
-		else
-			nm_dhcp4_config_reset (priv->dhcp4_config);
-
 		/* dhclient quit and can't get/renew a lease; so kill the connection */
-		if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
-			if (ipv6)
-				nm_device_activate_schedule_stage4_ip6_config_timeout (device);
-			else
-				nm_device_activate_schedule_stage4_ip4_config_timeout (device);
-		} else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		dhcp4_fail (device, FALSE);
 		break;
 	default:
 		break;
@@ -1500,21 +1491,15 @@ dhcp_state_changed (NMDHCPClient *client,
 }
 
 static void
-dhcp_timeout (NMDHCPClient *client, gpointer user_data)
+dhcp4_timeout (NMDHCPClient *client, gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
 
-	if (!nm_device_get_act_request (device))
-		return;
+	g_return_if_fail (nm_device_get_act_request (device) != NULL);
+	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == FALSE);
 
 	nm_dhcp_client_stop (client, FALSE);
-
-	if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
-		if (nm_dhcp_client_get_ipv6 (client))
-			nm_device_activate_schedule_stage4_ip6_config_timeout (device);
-		else
-			nm_device_activate_schedule_stage4_ip4_config_timeout (device);
-	}
+	dhcp4_fail (device, TRUE);
 }
 
 static NMActStageReturn
@@ -1526,7 +1511,7 @@ dhcp4_start (NMDevice *self,
 	NMSettingIP4Config *s_ip4;
 	guint8 *anycast = NULL;
 
-	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 
 	if (priv->dhcp_anycast_address)
 		anycast = priv->dhcp_anycast_address->data;
@@ -1551,11 +1536,11 @@ dhcp4_start (NMDevice *self,
 
 	priv->dhcp4_state_sigid = g_signal_connect (priv->dhcp4_client,
 	                                            "state-changed",
-	                                            G_CALLBACK (dhcp_state_changed),
+	                                            G_CALLBACK (dhcp4_state_changed),
 	                                            self);
 	priv->dhcp4_timeout_sigid = g_signal_connect (priv->dhcp4_client,
 	                                              "timeout",
-	                                              G_CALLBACK (dhcp_timeout),
+	                                              G_CALLBACK (dhcp4_timeout),
 	                                              self);
 
 	/* DHCP devices will be notified by the DHCP manager when stuff happens */
@@ -1568,7 +1553,6 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret;
 	NMDeviceStateReason reason;
-	NMActRequest *req;
 	NMConnection *connection;
 
 	g_return_val_if_fail (priv->dhcp4_client != NULL, FALSE);
@@ -1579,9 +1563,7 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 	/* Terminate old DHCP instance and release the old lease */
 	dhcp4_cleanup (self, TRUE, release);
 
-	req = nm_device_get_act_request (self);
-	g_assert (req);
-	connection = nm_act_request_get_connection (req);
+	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
 	/* Start DHCP again on the interface */
@@ -1590,63 +1572,315 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 	return (ret != NM_ACT_STAGE_RETURN_FAILURE);
 }
 
+/*********************************************/
+
+static GHashTable *shared_ips = NULL;
+
+static void
+release_shared_ip (gpointer data)
+{
+	g_hash_table_remove (shared_ips, data);
+}
+
+static guint32
+reserve_shared_ip (void)
+{
+	guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */
+	guint32 count = 0;
+
+	while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) {
+		count += ntohl (0x100);
+		if (count > ntohl (0xFE00)) {
+			nm_log_err (LOGD_SHARING, "ran out of shared IP addresses!");
+			return 0;
+		}
+	}
+
+	g_hash_table_insert (shared_ips, GUINT_TO_POINTER (start + count), GUINT_TO_POINTER (TRUE));
+	return start + count;
+}
+
+static NMIP4Config *
+shared4_new_config (NMDevice *self, NMDeviceStateReason *reason)
+{
+	NMIP4Config *config = NULL;
+	NMIP4Address *addr;
+	guint32 tmp_addr;
+
+	g_return_val_if_fail (self != NULL, NULL);
+
+	if (G_UNLIKELY (shared_ips == NULL))
+		shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+	tmp_addr = reserve_shared_ip ();
+	if (!tmp_addr) {
+		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		return NULL;
+	}
+
+	config = nm_ip4_config_new ();
+	addr = nm_ip4_address_new ();
+	nm_ip4_address_set_address (addr, tmp_addr);
+	nm_ip4_address_set_prefix (addr, 24);
+	nm_ip4_config_take_address (config, addr);
+
+	/* Remove the address lock when the object gets disposed */
+	g_object_set_data_full (G_OBJECT (config), "shared-ip",
+	                        GUINT_TO_POINTER (tmp_addr), release_shared_ip);
+
+	return config;
+}
+
+/*********************************************/
+
 static NMActStageReturn
-real_act_stage3_ip4_config_start (NMDevice *self, NMDeviceStateReason *reason)
+real_act_stage3_ip4_config_start (NMDevice *self,
+                                  NMIP4Config **out_config,
+                                  NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
 	NMSettingIP4Config *s_ip4;
-	NMActRequest *req;
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
-	const char *method = NULL;
-	int ifindex;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	/* Make sure the interface is up before trying to do anything with it */
-	ifindex = nm_device_get_ip_ifindex (self);
-	if (!nm_system_iface_is_up (ifindex))
-		nm_system_iface_set_up (ifindex, TRUE, NULL);
-
-	req = nm_device_get_act_request (self);
-	connection = nm_act_request_get_connection (req);
-
-	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
 
 	/* If we did not receive IP4 configuration information, default to DHCP */
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	if (s_ip4)
 		method = nm_setting_ip4_config_get_method (s_ip4);
+	else if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME))
+		method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
 
-	if (!s_ip4 || !method || !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+	/* Start IPv4 addressing based on the method requested */
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
 		ret = dhcp4_start (self, connection, reason);
-	} else if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
-		GError *error = NULL;
-		const char *iface = nm_device_get_iface (self);
-
-		/* Start avahi-autoipd */
-		if (aipd_exec (self, &error)) {
-			nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4,
-			             "Activation (%s) Stage 3 of 5 (IP Configure Start) started"
-			             " avahi-autoipd...", iface);
-			ret = NM_ACT_STAGE_RETURN_POSTPONE;
-		} else {
-			nm_log_info (LOGD_DEVICE | LOGD_AUTOIP4,
-			             "Activation (%s) Stage 3 of 5 (IP Configure Start) failed"
-			             " to start avahi-autoipd: %s", iface, error->message);
-			g_error_free (error);
-			aipd_cleanup (self);
-			*reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED;
+	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0)
+		ret = aipd_start (self, reason);
+	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
+		/* Use only IPv4 config from the connection data */
+		*out_config = nm_ip4_config_new ();
+		g_assert (*out_config);
+		ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
+		*out_config = shared4_new_config (self, reason);
+		if (*out_config) {
+			priv->dnsmasq_manager = nm_dnsmasq_manager_new (nm_device_get_ip_iface (self));
+			ret = NM_ACT_STAGE_RETURN_SUCCESS;
+		} else
 			ret = NM_ACT_STAGE_RETURN_FAILURE;
-		}
 	} else if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
 		/* Nothing to do... */
-		priv->ip4_ready = TRUE;
 		ret = NM_ACT_STAGE_RETURN_STOP;
+	} else {
+		nm_log_warn (LOGD_IP4, "(%s): unhandled IPv4 config method; will fail",
+		             nm_device_get_ip_iface (self));
 	}
 
 	return ret;
 }
 
+/*********************************************/
+/* DHCPv6 stuff */
+
+static void
+dhcp6_add_option_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	nm_dhcp6_config_add_option (NM_DHCP6_CONFIG (user_data),
+	                            (const char *) key,
+	                            (const char *) value);
+}
+
+static void
+merge_ip6_configs (NMIP6Config *dst, NMIP6Config *src)
+{
+	guint32 i;
+
+	g_return_if_fail (src != NULL);
+	g_return_if_fail (dst != NULL);
+
+	/* addresses */
+	for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++)
+		nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i));
+
+	/* ptp address; only replace if src doesn't have one */
+	if (!nm_ip6_config_get_ptp_address (dst))
+		nm_ip6_config_set_ptp_address (dst, nm_ip6_config_get_ptp_address (src));
+
+	/* nameservers */
+	for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++)
+		nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
+
+	/* routes */
+	for (i = 0; i < nm_ip6_config_get_num_routes (src); i++)
+		nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
+
+	/* domains */
+	for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
+		nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
+
+	/* dns searches */
+	for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
+		nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
+
+	if (!nm_ip6_config_get_mss (dst))
+		nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src));
+}
+
+static gboolean
+ip6_config_merge_and_apply (NMDevice *self,
+                            NMIP6Config *src_config,
+                            NMDeviceStateReason *out_reason)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	gboolean assumed, success;
+	NMIP6Config *composite;
+
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+
+	/* If no config was passed in, create a new one */
+	composite = nm_ip6_config_new ();
+	g_assert (composite);
+
+	/* Merge in the given config first, if any */
+	if (src_config)
+		merge_ip6_configs (composite, src_config);
+
+	/* Merge RA and DHCPv6 configs into the composite config */
+	if (priv->ac_ip6_config && (src_config != priv->ac_ip6_config))
+		merge_ip6_configs (composite, priv->ac_ip6_config);
+	if (priv->dhcp6_ip6_config && (src_config != priv->dhcp6_ip6_config))
+		merge_ip6_configs (composite, priv->dhcp6_ip6_config);
+
+	/* Merge user overrides into the composite config */
+	nm_utils_merge_ip6_config (composite, nm_connection_get_setting_ip6_config (connection));
+
+	assumed = nm_act_request_get_assumed (priv->act_request);
+	success = nm_device_set_ip6_config (self, composite, assumed, out_reason);
+	g_object_unref (composite);
+	return success;
+}
+
+static void
+dhcp6_lease_change (NMDevice *device)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMConnection *connection;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	if (priv->dhcp6_ip6_config == NULL) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to get DHCPv6 config for rebind",
+		             nm_device_get_ip_iface (device));
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+		return;
+	}
+
+	g_assert (priv->dhcp6_client);  /* sanity check */
+
+	connection = nm_device_get_connection (device);
+	g_assert (connection);
+
+	/* Apply the updated config */
+	if (ip6_config_merge_and_apply (device, NULL, &reason) == FALSE) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.",
+		             nm_device_get_ip_iface (device));
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+	} else {
+		/* Notify dispatcher scripts of new DHCPv6 config */
+		nm_utils_call_dispatcher ("dhcp6-change", connection, device, NULL, NULL, NULL);
+	}
+}
+
+static void
+dhcp6_fail (NMDevice *device, gboolean timeout)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+
+	nm_dhcp6_config_reset (priv->dhcp6_config);
+
+	if (timeout || (priv->ip6_state == IP_CONF))
+		nm_device_activate_schedule_ip6_config_timeout (device);
+	else if (priv->ip6_state == IP_DONE)
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
+}
+
+static void
+dhcp6_state_changed (NMDHCPClient *client,
+                     NMDHCPState state,
+                     gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+	NMDeviceState dev_state;
+
+	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
+
+	nm_log_dbg (LOGD_DHCP6, "(%s): new DHCPv6 client state %d",
+	            nm_device_get_iface (device), state);
+
+	dev_state = nm_device_get_state (device);
+
+	switch (state) {
+	case DHC_BOUND6:
+	case DHC_RENEW6:     /* lease renewed */
+	case DHC_REBOOT:     /* have valid lease, but now obtained a different one */
+	case DHC_REBIND6:    /* new, different lease */
+		if (priv->dhcp6_ip6_config)
+			g_object_unref (priv->dhcp6_ip6_config);
+		priv->dhcp6_ip6_config = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
+		if (priv->ip6_state == IP_CONF)
+			nm_device_activate_schedule_ip6_config_result (device, priv->dhcp6_ip6_config);
+		else if (priv->ip6_state == IP_DONE)
+			dhcp6_lease_change (device);
+
+		if (priv->dhcp6_ip6_config) {
+			/* Update the DHCP6 config object with new DHCP options */
+			nm_dhcp6_config_reset (priv->dhcp6_config);
+			nm_dhcp_client_foreach_option (priv->dhcp6_client,
+			                               dhcp6_add_option_cb,
+			                               priv->dhcp6_config);
+			g_object_notify (G_OBJECT (device), NM_DEVICE_DHCP6_CONFIG);
+		}
+		break;
+	case DHC_TIMEOUT: /* timed out contacting DHCP server */
+		dhcp6_fail (device, TRUE);
+		break;
+	case DHC_END: /* dhclient exited normally */
+		/* In IPv6 info-only mode, the client doesn't handle leases so it
+		 * may exit right after getting a response from the server.  That's
+		 * normal.  In that case we just ignore the exit.
+		 */
+		if (priv->dhcp6_mode == IP6_DHCP_OPT_OTHERCONF)
+			break;
+		/* Otherwise, fall through */
+	case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
+	case DHC_ABEND: /* dhclient exited abnormally */
+		/* dhclient quit and can't get/renew a lease; so kill the connection */
+		dhcp6_fail (device, FALSE);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+dhcp6_timeout (NMDHCPClient *client, gpointer user_data)
+{
+	NMDevice *device = NM_DEVICE (user_data);
+
+	g_return_if_fail (nm_device_get_act_request (device) != NULL);
+	g_return_if_fail (nm_dhcp_client_get_ipv6 (client) == TRUE);
+
+	nm_dhcp_client_stop (client, FALSE);
+	dhcp6_fail (device, TRUE);
+}
+
 static NMActStageReturn
 dhcp6_start (NMDevice *self,
              NMConnection *connection,
@@ -1656,17 +1890,12 @@ dhcp6_start (NMDevice *self,
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	guint8 *anycast = NULL;
-	NMSettingIP6Config *s_ip6;
 	const char *ip_iface;
 	const struct in6_addr dest = { { { 0xFF,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
 	int err;
 
 	if (!connection) {
-		NMActRequest *req;
-
-		req = nm_device_get_act_request (self);
-		g_assert (req);
-		connection = nm_act_request_get_connection (req);
+		connection = nm_device_get_connection (self);
 		g_assert (connection);
 	}
 
@@ -1680,6 +1909,12 @@ dhcp6_start (NMDevice *self,
 		g_object_unref (priv->dhcp6_config);
 	priv->dhcp6_config = nm_dhcp6_config_new ();
 
+	g_warn_if_fail (priv->dhcp6_ip6_config == NULL);
+	if (priv->dhcp6_ip6_config) {
+		g_object_unref (priv->dhcp6_ip6_config);
+		priv->dhcp6_ip6_config = NULL;
+	}
+
 	/* DHCPv6 communicates with the DHCPv6 server via two multicast addresses,
 	 * ff02::1:2 (link-scope) and ff05::1:3 (site-scope).  Make sure we have
 	 * a multicast route (ff00::/8) for client <-> server communication.
@@ -1692,24 +1927,22 @@ dhcp6_start (NMDevice *self,
 		            priv->ip_iface ? priv->ip_iface : priv->iface, nl_geterror (err));
 	}
 
-	s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
-
 	ip_iface = nm_device_get_ip_iface (self);
 	priv->dhcp6_client = nm_dhcp_manager_start_ip6 (priv->dhcp_manager,
 	                                                ip_iface,
 	                                                nm_connection_get_uuid (connection),
-	                                                s_ip6,
+	                                                nm_connection_get_setting_ip6_config (connection),
 	                                                priv->dhcp_timeout,
 	                                                anycast,
 	                                                (dhcp_opt == IP6_DHCP_OPT_OTHERCONF) ? TRUE : FALSE);
 	if (priv->dhcp6_client) {
 		priv->dhcp6_state_sigid = g_signal_connect (priv->dhcp6_client,
 		                                            "state-changed",
-		                                            G_CALLBACK (dhcp_state_changed),
+		                                            G_CALLBACK (dhcp6_state_changed),
 		                                            self);
 		priv->dhcp6_timeout_sigid = g_signal_connect (priv->dhcp6_client,
 		                                              "timeout",
-		                                              G_CALLBACK (dhcp_timeout),
+		                                              G_CALLBACK (dhcp6_timeout),
 		                                              self);
 
 		/* DHCP devices will be notified by the DHCP manager when stuff happens */
@@ -1722,36 +1955,215 @@ dhcp6_start (NMDevice *self,
 	return ret;
 }
 
+/******************************************/
+
+static void
+ip6_addrconf_complete (NMIP6Manager *ip6_manager,
+                       int ifindex,
+                       guint dhcp_opts,
+                       gboolean success,
+                       gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	NMActStageReturn ret;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	if (ifindex != nm_device_get_ip_ifindex (self))
+		return;
+	g_return_if_fail (priv->act_request != NULL);
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+
+	if (!priv->ip6_waiting_for_config)
+		return;
+
+	priv->ip6_waiting_for_config = FALSE;
+
+	if (!success) {
+		nm_device_activate_schedule_ip6_config_timeout (self);
+		return;
+	}
+
+	priv->dhcp6_mode = dhcp_opts;
+
+	/* If addrconf is all that's required, we're done */
+	if (priv->dhcp6_mode == IP6_DHCP_OPT_NONE) {
+		priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex);
+		nm_device_activate_schedule_ip6_config_result (self, priv->ac_ip6_config);
+		return;
+	}
+
+	/* If the router said to use DHCP for managed or otherconf, do it */
+
+	/* Don't re-start DHCPv6 if it's already in progress */
+	if (priv->ip6_state != IP_CONF)
+		return;
+
+	nm_log_info (LOGD_DEVICE | LOGD_DHCP6,
+	             "Activation (%s) Stage 3 of 5 (IP Configure Start) starting DHCPv6"
+	             " as requested by IPv6 router...",
+	             priv->iface);
+
+	ret = dhcp6_start (self, connection, priv->dhcp6_mode, &reason);
+	switch (ret) {
+	case NM_ACT_STAGE_RETURN_SUCCESS:
+		/* Shouldn't get this, but handle it anyway */
+		g_warn_if_reached ();
+		priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex);
+		nm_device_activate_schedule_ip6_config_result (self, priv->ac_ip6_config);
+		break;
+	case NM_ACT_STAGE_RETURN_POSTPONE:
+		/* Cache acquired autoconf config and wait for DHCPv6 to complete */
+		priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex);
+		break;
+	default:
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		break;
+	}
+}
+
+static void
+ip6_config_changed (NMIP6Manager *ip6_manager,
+                    int ifindex,
+                    guint dhcp_opts,
+                    gboolean success,
+                    gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+
+	if (ifindex != nm_device_get_ip_ifindex (self))
+		return;
+	g_return_if_fail (priv->act_request != NULL);
+
+	/* If autoconf failed and IPv6 previously succeeded, fail */
+	if (!success && (priv->ip6_state == IP_DONE)) {
+		nm_device_state_changed (self,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		return;
+	}
+
+	/* FIXME: re-run DHCPv6 here to get any new nameservers or whatever */
+
+	if (priv->ac_ip6_config)
+		g_object_unref (priv->ac_ip6_config);
+	priv->ac_ip6_config = nm_ip6_manager_get_ip6_config (ip6_manager, ifindex);
+
+	if (ip6_config_merge_and_apply (self, NULL, &reason) == FALSE) {
+		nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to Router Advertisement.",
+		             nm_device_get_ip_iface (self));
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+	}
+}
+
+static gboolean
+addrconf6_start (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	gboolean success;
+
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+
+	g_warn_if_fail (priv->ac_ip6_config == NULL);
+	if (priv->ac_ip6_config) {
+		g_object_unref (priv->ac_ip6_config);
+		priv->ac_ip6_config = NULL;
+	}
+
+	if (!priv->ip6_manager) {
+		priv->ip6_manager = nm_ip6_manager_get ();
+		priv->ip6_addrconf_sigid = g_signal_connect (priv->ip6_manager,
+		                                             "addrconf-complete",
+		                                             G_CALLBACK (ip6_addrconf_complete),
+		                                             self);
+		priv->ip6_config_changed_sigid = g_signal_connect (priv->ip6_manager,
+		                                                   "config-changed",
+		                                                   G_CALLBACK (ip6_config_changed),
+		                                                   self);
+	}
+
+	success = nm_ip6_manager_prepare_interface (priv->ip6_manager,
+	                                            nm_device_get_ip_ifindex (self),
+	                                            nm_connection_get_setting_ip6_config (connection),
+	                                            priv->ip6_accept_ra_path);
+	if (success) {
+		priv->ip6_waiting_for_config = TRUE;
+		nm_ip6_manager_begin_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self));
+	}
+
+	return success;
+}
+
+static void
+addrconf6_cleanup (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->ac_ip6_config) {
+		g_object_unref (priv->ac_ip6_config);
+		priv->ac_ip6_config = NULL;
+	}
+
+	if (!priv->ip6_manager)
+		return;
+
+	if (priv->ip6_addrconf_sigid) {
+		g_signal_handler_disconnect (priv->ip6_manager,
+		                             priv->ip6_addrconf_sigid);
+		priv->ip6_addrconf_sigid = 0;
+	}
+	if (priv->ip6_config_changed_sigid) {
+		g_signal_handler_disconnect (priv->ip6_manager,
+		                             priv->ip6_config_changed_sigid);
+		priv->ip6_config_changed_sigid = 0;
+	}
+
+	nm_ip6_manager_cancel_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self));
+	g_object_unref (priv->ip6_manager);
+	priv->ip6_manager = NULL;
+}
+
+/******************************************/
+
 static NMActStageReturn
-real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason)
+real_act_stage3_ip6_config_start (NMDevice *self,
+                                  NMIP6Config **out_config,
+                                  NMDeviceStateReason *reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *ip_iface;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	NMActRequest *req;
 	NMConnection *connection;
+	NMSettingIP6Config *s_ip6;
+	int conf_use_tempaddr;
+	NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+	const char *ip6_privacy_str = "0\n";
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
-	req = nm_device_get_act_request (self);
-	g_assert (req);
-	connection = nm_act_request_get_connection (req);
+	connection = nm_device_get_connection (self);
 	g_assert (connection);
 
 	ip_iface = nm_device_get_ip_iface (self);
 
 	update_accept_ra_save (self);
+	update_ip6_privacy_save (self);
 
 	priv->dhcp6_mode = IP6_DHCP_OPT_NONE;
 
 	if (   ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
 	    || ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
-		if (!addrconf6_setup (self)) {
+		if (!addrconf6_start (self)) {
 			*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
-			goto out;
-		}
-		nm_ip6_manager_begin_addrconf (priv->ip6_manager, nm_device_get_ip_ifindex (self));
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
+		} else
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
 		/* Router advertisements shouldn't be used in pure DHCP mode */
 		if (priv->ip6_accept_ra_path)
@@ -1765,19 +2177,52 @@ real_act_stage3_ip6_config_start (NMDevice *self, NMDeviceStateReason *reason)
 			nm_utils_do_sysctl (priv->ip6_accept_ra_path,
 			                    priv->ip6_accept_ra_save ? "1\n" : "0\n");
 		}
-
-		priv->ip6_ready = TRUE;
 		ret = NM_ACT_STAGE_RETURN_STOP;
 	} else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+		/* New blank config */
+		*out_config = nm_ip6_config_new ();
+		g_assert (*out_config);
+
 		/* Router advertisements shouldn't be used in manual mode */
 		if (priv->ip6_accept_ra_path)
 			nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0\n");
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	} else {
+		nm_log_warn (LOGD_IP6, "(%s): unhandled IPv6 config method; will fail",
+		             nm_device_get_ip_iface (self));
 	}
 
 	/* Other methods (shared) aren't implemented yet */
 
-out:
+	/* Enable/disable IPv6 Privacy Extensions.
+	 * If a global value is configured by sysadmin (e.g. /etc/sysctl.conf),
+	 * use that value instead of per-connection value.
+	 */
+	conf_use_tempaddr = nm_backend_ipv6_use_tempaddr ();
+	if (conf_use_tempaddr >= 0)
+		ip6_privacy = conf_use_tempaddr;
+	else {
+		s_ip6 = nm_connection_get_setting_ip6_config (connection);
+		if (s_ip6)
+			ip6_privacy = nm_setting_ip6_config_get_ip6_privacy (s_ip6);
+	}
+	ip6_privacy = CLAMP (ip6_privacy, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR);
+
+	switch (ip6_privacy) {
+	case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN:
+	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
+		ip6_privacy_str = "0\n";
+	break;
+	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR:
+		ip6_privacy_str = "1\n";
+	break;
+	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR:
+		ip6_privacy_str = "2\n";
+	break;
+	}
+	if (priv->ip6_privacy_tempaddr_path)
+		nm_utils_do_sysctl (priv->ip6_privacy_tempaddr_path, ip6_privacy_str);
+
 	return ret;
 }
 
@@ -1792,9 +2237,13 @@ static gboolean
 nm_device_activate_stage3_ip_config_start (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *iface;
 	NMActStageReturn ret;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMIP4Config *ip4_config = NULL;
+	NMIP6Config *ip6_config = NULL;
+	int ifindex;
 
 	/* Clear the activation source ID now that this stage has run */
 	activation_source_clear (self, FALSE, 0);
@@ -1803,271 +2252,119 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
 	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface);
 	nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &reason);
-	if (ret == NM_ACT_STAGE_RETURN_SUCCESS)
-		nm_device_activate_schedule_stage4_ip4_config_get (self);
-	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+	/* Make sure the interface is up before trying to do anything with it */
+	ifindex = nm_device_get_ip_ifindex (self);
+	if ((ifindex > 0) && (nm_system_iface_is_up (ifindex) == FALSE))
+		nm_system_iface_set_up (ifindex, TRUE, NULL);
+
+	priv->ip4_state = priv->ip6_state = IP_CONF;
+
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
+		g_assert (ip4_config);
+		nm_device_activate_schedule_ip4_config_result (self, ip4_config);
+		g_object_unref (ip4_config);
+	} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 		goto out;
 	} else if (ret == NM_ACT_STAGE_RETURN_STOP) {
 		/* Nothing to do */
+		priv->ip4_state = IP_DONE;
 	} else
 		g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE);
 
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &reason);
-	if (ret == NM_ACT_STAGE_RETURN_SUCCESS)
-		nm_device_activate_schedule_stage4_ip6_config_get (self);
-	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &reason);
+	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
+		g_assert (ip6_config);
+		nm_device_activate_schedule_ip6_config_result (self, ip6_config);
+		g_object_unref (ip6_config);
+	} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 		goto out;
 	} else if (ret == NM_ACT_STAGE_RETURN_STOP) {
 		/* Nothing to do */
+		priv->ip6_state = IP_DONE;
 	} else
 		g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE);
 
 out:
 	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface);
-	return FALSE;
-}
-
 
-/*
- * nm_device_activate_schedule_stage3_ip_config_start
- *
- * Schedule IP configuration start
- */
-void
-nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
-{
-	NMDevicePrivate *priv;
-
-	g_return_if_fail (NM_IS_DEVICE (self));
-
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request);
-
-	activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
-
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.",
-	             nm_device_get_iface (self));
-}
-
-static GHashTable *shared_ips = NULL;
-
-static void
-release_shared_ip (gpointer data)
-{
-	g_hash_table_remove (shared_ips, data);
-}
-
-static guint32
-reserve_shared_ip (void)
-{
-	guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */
-	guint32 count = 0;
-
-	while (g_hash_table_lookup (shared_ips, GUINT_TO_POINTER (start + count))) {
-		count += ntohl (0x100);
-		if (count > ntohl (0xFE00)) {
-			nm_log_err (LOGD_SHARING, "ran out of shared IP addresses!");
-			return 0;
-		}
-	}
-
-	g_hash_table_insert (shared_ips, GUINT_TO_POINTER (start + count), GUINT_TO_POINTER (TRUE));
-	return start + count;
-}
-
-static NMIP4Config *
-nm_device_new_ip4_shared_config (NMDevice *self, NMDeviceStateReason *reason)
-{
-	NMIP4Config *config = NULL;
-	NMIP4Address *addr;
-	guint32 tmp_addr;
-
-	g_return_val_if_fail (self != NULL, NULL);
-
-	if (G_UNLIKELY (shared_ips == NULL))
-		shared_ips = g_hash_table_new (g_direct_hash, g_direct_equal);
-
-	tmp_addr = reserve_shared_ip ();
-	if (!tmp_addr) {
-		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
-		return NULL;
+	/* Handle interfaces (bond slaves, etc) that won't have any IP config; they
+	 * need to move to ACTIVATED.
+	 */
+	if (priv->ip4_state == IP_DONE && priv->ip6_state == IP_DONE) {
+		/* FIXME: call layer2 stuff to set MTU? */
+		nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
 	}
 
-	config = nm_ip4_config_new ();
-	addr = nm_ip4_address_new ();
-	nm_ip4_address_set_address (addr, tmp_addr);
-	nm_ip4_address_set_prefix (addr, 24);
-	nm_ip4_config_take_address (config, addr);
-
-	/* Remove the address lock when the object gets disposed */
-	g_object_set_data_full (G_OBJECT (config), "shared-ip",
-	                        GUINT_TO_POINTER (tmp_addr), release_shared_ip);
-
-	return config;
+	return FALSE;
 }
 
-static NMActStageReturn
-real_act_stage4_get_ip4_config (NMDevice *self,
-                                NMIP4Config **config,
-                                NMDeviceStateReason *reason)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	NMConnection *connection;
-	NMSettingIP4Config *s_ip4;
-	const char *ip_iface;
-
-	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	/* Use the IP interface (not the control interface) for IP stuff */
-	ip_iface = nm_device_get_ip_iface (self);
-
-	connection = nm_act_request_get_connection (nm_device_get_act_request (self));
-	g_assert (connection);
-
-	s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
 
-	if (priv->dhcp4_client) {
-		/* DHCP */
-		*config = nm_dhcp_client_get_ip4_config (priv->dhcp4_client, FALSE);
-		if (*config) {
-			/* Merge user-defined overrides into the IP4Config to be applied */
-			nm_utils_merge_ip4_config (*config, s_ip4);
-
-			nm_dhcp4_config_reset (priv->dhcp4_config);
-			nm_dhcp_client_foreach_option (priv->dhcp4_client,
-			                               dhcp4_add_option_cb,
-			                               priv->dhcp4_config);
-
-			/* Notify of new DHCP4 config */
-			g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
-		} else
-			*reason = NM_DEVICE_STATE_REASON_DHCP_ERROR;
-	} else {
-		/* Not DHCP */
-		const char *method;
-
-		g_assert (s_ip4);
-
-		method = nm_setting_ip4_config_get_method (s_ip4);
-		g_assert (method);
-
-		if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
-			*config = aipd_get_ip4_config (self, reason);
-		} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
-			*config = nm_ip4_config_new ();
-			if (*config) {
-				/* Merge user-defined overrides into the IP4Config to be applied */
-				nm_utils_merge_ip4_config (*config, s_ip4);
-			} else
-				*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
-		} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
-			*config = nm_device_new_ip4_shared_config (self, reason);
-			if (*config)
-				priv->dnsmasq_manager = nm_dnsmasq_manager_new (ip_iface);
-		} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
-			ret = NM_ACT_STAGE_RETURN_SUCCESS;
-	}
-
-	if (!*config) {
-		/* Make sure device is up even if config fails */
-		nm_device_bring_up (self, FALSE, NULL);
-	} else
-		ret = NM_ACT_STAGE_RETURN_SUCCESS;
-
-	return ret;
-}
-
-/*
- * nm_device_activate_stage4_ip4_config_get
- *
- * Retrieve the correct IPv4 config.
- *
- */
-static gboolean
-nm_device_activate_stage4_ip4_config_get (gpointer user_data)
+static void
+fw_add_to_zone_cb (GError *error, gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
-	NMIP4Config *ip4_config = NULL;
-	NMActStageReturn ret;
-	const char *iface = NULL;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	/* Clear the activation source ID now that this stage has run */
-	activation_source_clear (self, FALSE, AF_INET);
+	priv->fw_call = NULL;
 
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IP4 Configure Get) started...",
-	             iface);
-
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_get_ip4_config (self, &ip4_config, &reason);
-	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
-		goto out;
-	else if (!ip4_config || (ret == NM_ACT_STAGE_RETURN_FAILURE)) {
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-		goto out;
+	if (error) {
+		/* FIXME: fail the device activation? */
 	}
-	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);	
-
-	g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
-					   NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
 
-	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET);
+	activation_source_schedule (self, nm_device_activate_stage3_ip_config_start, 0);
 
-out:
-	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IP4 Configure Get) complete.",
-	             iface);
-	return FALSE;
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.",
+	             nm_device_get_iface (self));
 }
 
-
 /*
- * nm_device_activate_schedule_stage4_ip4_config_get
- *
- * Schedule creation of the IPv4 config
+ * nm_device_activate_schedule_stage3_ip_config_start
  *
+ * Schedule IP configuration start
  */
 void
-nm_device_activate_schedule_stage4_ip4_config_get (NMDevice *self)
+nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
 {
 	NMDevicePrivate *priv;
+	NMConnection *connection;
+	NMSettingConnection *s_con = NULL;
+	NMDeviceState state;
+	const char *zone;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
-	activation_source_schedule (self, nm_device_activate_stage4_ip4_config_get, AF_INET);
+	state = nm_device_get_state (self);
+	g_warn_if_fail (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_NEED_AUTH);
 
-	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IP4 Configure Get) scheduled...",
-	             nm_device_get_iface (self));
-}
+	/* Add the interface to the specified firewall zone */
+	connection = nm_device_get_connection (self);
+	g_assert (connection);
+	s_con = nm_connection_get_setting_connection (connection);
 
+	zone = nm_setting_connection_get_zone (s_con);
+	nm_log_dbg (LOGD_DEVICE, "Activation (%s) setting firewall zone '%s'",
+	            nm_device_get_iface (self), zone ? zone : "default");
+	priv->fw_call = nm_firewall_manager_add_or_change_zone (priv->fw_manager,
+	                                                        nm_device_get_ip_iface (self),
+	                                                        zone,
+	                                                        TRUE,
+	                                                        fw_add_to_zone_cb,
+	                                                        self);
+}
 
 static NMActStageReturn
-real_act_stage4_ip4_config_timeout (NMDevice *self,
-									NMIP4Config **config,
-									NMDeviceStateReason *reason)
+real_act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 {
-	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	/* Notify of invalid DHCP4 config object */
-	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
-
 	if (nm_device_ip_config_should_fail (self, FALSE)) {
 		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
-
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
@@ -2079,10 +2376,10 @@ real_act_stage4_ip4_config_timeout (NMDevice *self,
  *
  */
 static gboolean
-nm_device_activate_stage4_ip4_config_timeout (gpointer user_data)
+nm_device_activate_ip4_config_timeout (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
-	NMIP4Config *ip4_config = NULL;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *iface;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
@@ -2092,10 +2389,10 @@ nm_device_activate_stage4_ip4_config_timeout (gpointer user_data)
 
 	iface = nm_device_get_iface (self);
 	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) started...",
+	             "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) started...",
 	             iface);
 
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &ip4_config, &reason);
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
 		goto out;
 	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
@@ -2104,29 +2401,31 @@ nm_device_activate_stage4_ip4_config_timeout (gpointer user_data)
 	}
 	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);	
 
-	if (ip4_config) {
-		g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
-						   NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
-	}
+	priv->ip4_state = IP_DONE;
 
-	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET);
+	/* If IPv4 failed and IPv6 failed, the activation fails */
+	if ((priv->ip6_state == IP_DONE) && (priv->ip6_config == NULL)) {
+		nm_device_state_changed (self,
+				                 NM_DEVICE_STATE_FAILED,
+				                 NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+	}
 
 out:
 	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) complete.",
+	             "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) complete.",
 	             iface);
 	return FALSE;
 }
 
 
 /*
- * nm_device_activate_schedule_stage4_ip4_config_timeout
+ * nm_device_activate_schedule_ip4_config_timeout
  *
  * Deal with a timeout of the IPv4 configuration
  *
  */
 void
-nm_device_activate_schedule_stage4_ip4_config_timeout (NMDevice *self)
+nm_device_activate_schedule_ip4_config_timeout (NMDevice *self)
 {
 	NMDevicePrivate *priv;
 
@@ -2135,214 +2434,17 @@ nm_device_activate_schedule_stage4_ip4_config_timeout (NMDevice *self)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
-	activation_source_schedule (self, nm_device_activate_stage4_ip4_config_timeout, AF_INET);
+	activation_source_schedule (self, nm_device_activate_ip4_config_timeout, AF_INET);
 
 	nm_log_info (LOGD_DEVICE | LOGD_IP4,
-	             "Activation (%s) Stage 4 of 5 (IP4 Configure Timeout) scheduled...",
+	             "Activation (%s) Stage 4 of 5 (IPv4 Configure Timeout) scheduled...",
 	             nm_device_get_iface (self));
 }
 
-static void
-merge_dhcp_config_to_master (NMIP6Config *dst, NMIP6Config *src)
-{
-	guint32 i;
-
-	g_return_if_fail (src != NULL);
-	g_return_if_fail (dst != NULL);
-
-	/* addresses */
-	for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++)
-		nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i));
-
-	/* ptp address; only replace if src doesn't have one */
-	if (!nm_ip6_config_get_ptp_address (dst))
-		nm_ip6_config_set_ptp_address (dst, nm_ip6_config_get_ptp_address (src));
-
-	/* nameservers */
-	for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++)
-		nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
-
-	/* routes */
-	for (i = 0; i < nm_ip6_config_get_num_routes (src); i++)
-		nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
-
-	/* domains */
-	for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
-		nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
-
-	/* dns searches */
-	for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
-		nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
-
-	if (!nm_ip6_config_get_mss (dst))
-		nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src));
-}
 
 static NMActStageReturn
-real_act_stage4_get_ip6_config (NMDevice *self,
-                                NMIP6Config **config,
-                                NMDeviceStateReason *reason)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMConnection *connection;
-	NMSettingIP6Config *s_ip6;
-	const char *ip_iface;
-
-	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	/* Use the IP interface (not the control interface) for IP stuff */
-	ip_iface = nm_device_get_ip_iface (self);
-
-	connection = nm_act_request_get_connection (nm_device_get_act_request (self));
-	g_assert (connection);
-
-	if (   ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
-	    || ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
-		*config = nm_ip6_manager_get_ip6_config (priv->ip6_manager,
-		                                         nm_device_get_ip_ifindex (self));
-		if (!*config) {
-			*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
-			goto out;
-		}
-	} else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
-		*config = nm_ip6_config_new ();
-		if (!*config) {
-			*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
-			goto out;
-		}
-	} else if (ip6_method_matches (connection, NM_SETTING_IP6_CONFIG_METHOD_DHCP))
-		g_assert (priv->dhcp6_client);  /* sanity check */
-
-	/* Autoconf might have triggered DHCPv6 too */
-	if (priv->dhcp6_client) {
-		NMIP6Config *dhcp;
-
-		dhcp = nm_dhcp_client_get_ip6_config (priv->dhcp6_client, FALSE);
-		if (!dhcp) {
-			*reason = NM_DEVICE_STATE_REASON_DHCP_ERROR;
-			goto out;
-		}
-
-		/* For "managed" and DHCP-only setups, we use only the DHCP-supplied
-		 * IPv6 config.  But when autoconf is enabled, we have to merge the
-		 * autoconf config and the DHCP-supplied config, then merge the
-		 * user's overrides from the connection to get the final configuration
-		 * that gets applied to the device.
-		 */
-		if (*config) {
-			/* Merge autoconf and DHCP configs */
-			merge_dhcp_config_to_master (*config, dhcp);
-			g_object_unref (dhcp);
-			dhcp = NULL;
-		} else {
-			*config = dhcp;
-		}
-
-		/* Copy the new DHCPv6 configuration into the DHCP config object that's
-		 * exported over D-Bus to clients.
-		 */
-		nm_dhcp6_config_reset (priv->dhcp6_config);
-		nm_dhcp_client_foreach_option (priv->dhcp6_client,
-		                               dhcp6_add_option_cb,
-		                               priv->dhcp6_config);
-
-		/* Notify of new DHCP6 config */
-		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
-	}
-
-	/* Merge user-defined overrides into the IP6Config to be applied */
-	if (*config) {
-		s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
-		nm_utils_merge_ip6_config (*config, s_ip6);
-	}
-
-out:
-	return *config ? NM_ACT_STAGE_RETURN_SUCCESS : NM_ACT_STAGE_RETURN_FAILURE;
-}
-
-/*
- * nm_device_activate_stage4_ip6_config_get
- *
- * Retrieve the correct IPv6 config.
- *
- */
-static gboolean
-nm_device_activate_stage4_ip6_config_get (gpointer user_data)
+real_act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
 {
-	NMDevice *self = NM_DEVICE (user_data);
-	NMIP6Config *ip6_config = NULL;
-	NMActStageReturn ret;
-	const char *iface = NULL;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
-
-	/* Clear the activation source ID now that this stage has run */
-	activation_source_clear (self, FALSE, AF_INET6);
-
-	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IP6 Configure Get) started...",
-	             iface);
-
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_get_ip6_config (self, &ip6_config, &reason);
-	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
-		goto out;
-	else if (ret == NM_ACT_STAGE_RETURN_FAILURE)
-	{
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-		goto out;
-	}
-	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
-
-	g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
-					   NM_ACT_REQUEST_IP6_CONFIG, ip6_config);
-
-	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET6);
-
-out:
-	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IP6 Configure Get) complete.",
-	             iface);
-	return FALSE;
-}
-
-
-/*
- * nm_device_activate_schedule_stage4_ip6_config_get
- *
- * Schedule creation of the IPv6 config
- *
- */
-void
-nm_device_activate_schedule_stage4_ip6_config_get (NMDevice *self)
-{
-	NMDevicePrivate *priv;
-
-	g_return_if_fail (NM_IS_DEVICE (self));
-
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	g_return_if_fail (priv->act_request);
-
-	activation_source_schedule (self, nm_device_activate_stage4_ip6_config_get, AF_INET6);
-
-	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IP6 Configure Get) scheduled...",
-	             nm_device_get_iface (self));
-}
-
-
-static NMActStageReturn
-real_act_stage4_ip6_config_timeout (NMDevice *self,
-									NMIP6Config **config,
-									NMDeviceStateReason *reason)
-{
-	g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	/* Notify of invalid DHCP4 config object */
-	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
-
 	if (nm_device_ip_config_should_fail (self, TRUE)) {
 		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
 		return NM_ACT_STAGE_RETURN_FAILURE;
@@ -2353,16 +2455,16 @@ real_act_stage4_ip6_config_timeout (NMDevice *self,
 
 
 /*
- * nm_device_activate_stage4_ip6_config_timeout
+ * nm_device_activate_ip6_config_timeout
  *
  * Time out on retrieving the IPv6 config.
  *
  */
 static gboolean
-nm_device_activate_stage4_ip6_config_timeout (gpointer user_data)
+nm_device_activate_ip6_config_timeout (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
-	NMIP6Config *ip6_config = NULL;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const char *iface;
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
@@ -2372,10 +2474,10 @@ nm_device_activate_stage4_ip6_config_timeout (gpointer user_data)
 
 	iface = nm_device_get_iface (self);
 	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) started...",
+	             "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) started...",
 	             iface);
 
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &ip6_config, &reason);
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
 		goto out;
 	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
@@ -2384,29 +2486,31 @@ nm_device_activate_stage4_ip6_config_timeout (gpointer user_data)
 	}
 	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 
-	if (ip6_config) {
-		g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
-						   NM_ACT_REQUEST_IP6_CONFIG, ip6_config);
-	}
+	priv->ip6_state = IP_DONE;
 
-	nm_device_activate_schedule_stage5_ip_config_commit (self, AF_INET6);
+	/* If IPv6 failed and IPv4 failed, the activation fails */
+	if ((priv->ip4_state == IP_DONE) && (priv->ip4_config == NULL)) {
+		nm_device_state_changed (self,
+				                 NM_DEVICE_STATE_FAILED,
+				                 NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+	}
 
 out:
 	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) complete.",
+	             "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) complete.",
 	             iface);
 	return FALSE;
 }
 
 
 /*
- * nm_device_activate_schedule_stage4_ip6_config_timeout
+ * nm_device_activate_schedule_ip6_config_timeout
  *
  * Deal with a timeout of the IPv6 configuration
  *
  */
 void
-nm_device_activate_schedule_stage4_ip6_config_timeout (NMDevice *self)
+nm_device_activate_schedule_ip6_config_timeout (NMDevice *self)
 {
 	NMDevicePrivate *priv;
 
@@ -2415,10 +2519,10 @@ nm_device_activate_schedule_stage4_ip6_config_timeout (NMDevice *self)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
-	activation_source_schedule (self, nm_device_activate_stage4_ip6_config_timeout, AF_INET6);
+	activation_source_schedule (self, nm_device_activate_ip6_config_timeout, AF_INET6);
 
 	nm_log_info (LOGD_DEVICE | LOGD_IP6,
-	             "Activation (%s) Stage 4 of 5 (IP6 Configure Timeout) scheduled...",
+	             "Activation (%s) Stage 4 of 5 (IPv6 Configure Timeout) scheduled...",
 	             nm_device_get_iface (self));
 }
 
@@ -2483,7 +2587,7 @@ add_share_rule (NMActRequest *req, const char *table, const char *fmt, ...)
 }
 
 static gboolean
-start_sharing (NMDevice *self)
+start_sharing (NMDevice *self, NMIP4Config *config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActRequest *req;
@@ -2491,17 +2595,14 @@ start_sharing (NMDevice *self)
 	char str_addr[INET_ADDRSTRLEN + 1];
 	char str_mask[INET_ADDRSTRLEN + 1];
 	guint32 netmask, network;
-	NMIP4Config *ip4_config;
 	NMIP4Address *ip4_addr;
 	const char *ip_iface;
 
-	ip_iface = nm_device_get_ip_iface (self);
+	g_return_val_if_fail (config != NULL, FALSE);
 
-	ip4_config = nm_device_get_ip4_config (self);
-	if (!ip4_config)
-		return FALSE;
+	ip_iface = nm_device_get_ip_iface (self);
 
-	ip4_addr = nm_ip4_config_get_address (ip4_config, 0);
+	ip4_addr = nm_ip4_config_get_address (config, 0);
 	if (!ip4_addr || !nm_ip4_address_get_address (ip4_addr))
 		return FALSE;
 
@@ -2532,7 +2633,7 @@ start_sharing (NMDevice *self)
 
 	nm_act_request_set_shared (req, TRUE);
 
-	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, ip4_config, &error)) {
+	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &error)) {
 		nm_log_err (LOGD_SHARING, "(%s/%s): failed to start dnsmasq: %s",
 		            nm_device_get_iface (self), ip_iface, error->message);
 		g_error_free (error);
@@ -2546,114 +2647,86 @@ start_sharing (NMDevice *self)
 	return TRUE;
 }
 
-/*
- * nm_device_activate_stage5_ip_config_commit
- *
- * Commit the IP config on the device
- *
- */
 static gboolean
-nm_device_activate_stage5_ip_config_commit (gpointer user_data)
+nm_device_activate_ip4_config_commit (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActRequest *act_request;
-	NMIP4Config *ip4_config = NULL;
-	NMIP6Config *ip6_config = NULL;
+	NMActRequest *req;
+	NMIP4Config *config = NULL;
 	const char *iface, *method = NULL;
 	NMConnection *connection;
 	NMSettingIP4Config *s_ip4;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	gboolean assumed;
-
-	/* Get the new IP4 and IP6 configs; since this stage gets rerun
-	 * when automatic configuration changes (DHCP lease renewal, new
-	 * IPv6 router advertisement, etc), it's possible that only one of
-	 * them will be set.
-	 */
-	act_request = nm_device_get_act_request (self);
-
-	ip4_config = g_object_get_data (G_OBJECT (act_request),
-									NM_ACT_REQUEST_IP4_CONFIG);
-	g_object_set_data (G_OBJECT (act_request),
-					   NM_ACT_REQUEST_IP4_CONFIG, NULL);
-
-	ip6_config = g_object_get_data (G_OBJECT (act_request),
-									NM_ACT_REQUEST_IP6_CONFIG);
-	g_object_set_data (G_OBJECT (act_request),
-					   NM_ACT_REQUEST_IP6_CONFIG, NULL);
+	int ifindex;
 
 	/* Clear the activation source ID now that this stage has run */
-	activation_source_clear (self, FALSE, 0);
+	activation_source_clear (self, FALSE, AF_INET);
 
 	iface = nm_device_get_iface (self);
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IP Configure Commit) started...",
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) started...",
 	             iface);
 
-	assumed = nm_act_request_get_assumed (priv->act_request);
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
 
-	if (!ip6_config && !ip4_config) {
-		nm_log_info (LOGD_DEVICE,
-		             "Activation (%s) Stage 5 of 5 (IP Configure Commit) failed (no IP configuration found)",
-				     iface);
-		nm_device_state_changed (self,
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
-		goto out;
-	}
+	config = g_object_get_data (G_OBJECT (req), PENDING_IP4_CONFIG);
+	g_assert (config);
 
-	if (ip4_config && !nm_device_set_ip4_config (self, ip4_config, assumed, &reason)) {
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
-		goto out;
-	}
+	/* Make sure the interface is up again just because */
+	ifindex = nm_device_get_ip_ifindex (self);
+	if ((ifindex > 0) && (nm_system_iface_is_up (ifindex) == FALSE))
+		nm_system_iface_set_up (ifindex, TRUE, NULL);
 
-	if (ip6_config && !nm_device_set_ip6_config (self, ip6_config, assumed, &reason)) {
-		nm_log_info (LOGD_DEVICE | LOGD_IP6,
-		             "Activation (%s) Stage 5 of 5 (IP Configure Commit) IPv6 failed",
-				     iface);
+	/* Allow setting MTU etc */
+	if (NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit)
+		NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, config);
+
+	/* Merge with user overrides */
+	nm_utils_merge_ip4_config (config, nm_connection_get_setting_ip4_config (connection));
+
+	assumed = nm_act_request_get_assumed (priv->act_request);
+	if (!nm_device_set_ip4_config (self, config, assumed, &reason)) {
+		nm_log_info (LOGD_DEVICE | LOGD_IP4,
+			         "Activation (%s) Stage 5 of 5 (IPv4 Commit) failed",
+					 iface);
 		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
 		goto out;
 	}
 
-	connection = nm_act_request_get_connection (nm_device_get_act_request (self));
-
-	if (ip4_config) {
-		s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
-		if (s_ip4)
-			method = nm_setting_ip4_config_get_method (s_ip4);
+	/* Start IPv4 sharing if we need it */
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (s_ip4)
+		method = nm_setting_ip4_config_get_method (s_ip4);
 
-		if (s_ip4 && !strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
-			if (!start_sharing (self)) {
-				nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IP Configure Commit) start sharing failed.", iface);
-				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
-				goto out;
-			}
+	if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
+		if (!start_sharing (self, config)) {
+			nm_log_warn (LOGD_SHARING, "Activation (%s) Stage 5 of 5 (IPv4 Commit) start sharing failed.", iface);
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
+			goto out;
 		}
 	}
 
-	nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
+	/* Enter the ACTIVATED state if this is the first method to complete */
+	priv->ip4_state = IP_DONE;
+	if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
+		nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
 
 out:
-	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IP Configure Commit) complete.",
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) complete.",
 	             iface);
 
-	/* Balance IP config creation; device takes ownership in set_ip*_config() */
-	if (ip4_config)
-		g_object_unref (ip4_config);
-	if (ip6_config)
-		g_object_unref (ip6_config);
+	/* Balance IP config creation; nm_device_set_ip4_config() takes a reference */
+	g_object_set_data (G_OBJECT (req), PENDING_IP4_CONFIG, NULL);
 
 	return FALSE;
 }
 
-
-/*
- * nm_device_activate_schedule_stage5_ip_config_commit
- *
- * Schedule commit of the IP config
- */
-static void
-nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family)
+void
+nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *config)
 {
 	NMDevicePrivate *priv;
 
@@ -2662,26 +2735,121 @@ nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	g_return_if_fail (priv->act_request);
 
-	if (family == AF_INET)
-		priv->ip4_ready = TRUE;
-	else if (family == AF_INET6)
-		priv->ip6_ready = TRUE;
+	if (config == NULL) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
+		return;
+	}
 
-	/* Note that these are only set FALSE at stage3, so once you've
-	 * made it all the way through activation once, you can jump back
-	 * into stage4 (eg, for a DHCP lease change) and not worry about
-	 * needing both IPv4 and IPv6 to complete.
-	 */
-	if (!priv->ip4_ready || !priv->ip6_ready)
+	g_object_set_data_full (G_OBJECT (priv->act_request),
+	                        PENDING_IP4_CONFIG,
+	                        g_object_ref (config),
+	                        g_object_unref);
+
+	activation_source_schedule (self, nm_device_activate_ip4_config_commit, AF_INET);
+
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+		         "Activation (%s) Stage 5 of 5 (IPv4 Configure Commit) scheduled...",
+		         nm_device_get_iface (self));
+}
+
+gboolean
+nm_device_activate_ip4_state_in_conf (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_CONF;
+}
+
+static gboolean
+nm_device_activate_ip6_config_commit (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMActRequest *req;
+	NMIP6Config *config = NULL;
+	const char *iface;
+	NMConnection *connection;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	int ifindex;
+
+	/* Clear the activation source ID now that this stage has run */
+	activation_source_clear (self, FALSE, AF_INET6);
+
+	iface = nm_device_get_iface (self);
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv6 Commit) started...",
+	             iface);
+
+	req = nm_device_get_act_request (self);
+	g_assert (req);
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	config = g_object_get_data (G_OBJECT (req), PENDING_IP6_CONFIG);
+	g_assert (config);
+
+	/* Make sure the interface is up again just because */
+	ifindex = nm_device_get_ip_ifindex (self);
+	if ((ifindex > 0) && (nm_system_iface_is_up (ifindex) == FALSE))
+		nm_system_iface_set_up (ifindex, TRUE, NULL);
+
+	/* Allow setting MTU etc */
+	if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit)
+		NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, config);
+
+	if (ip6_config_merge_and_apply (self, config, &reason)) {
+		/* Enter the ACTIVATED state if this is the first method to complete */
+		priv->ip6_state = IP_DONE;
+		if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
+			nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
+	} else {
+		nm_log_info (LOGD_DEVICE | LOGD_IP6,
+			         "Activation (%s) Stage 5 of 5 (IPv6 Commit) failed",
+					 iface);
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+	}
+
+	nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv6 Commit) complete.",
+	             iface);
+
+	/* Balance IP config creation; nm_device_set_ip6_config() takes a reference */
+	g_object_set_data (G_OBJECT (req), PENDING_IP6_CONFIG, NULL);
+
+	return FALSE;
+}
+
+void
+nm_device_activate_schedule_ip6_config_result (NMDevice *self, NMIP6Config *config)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	g_return_if_fail (priv->act_request);
+
+	if (config == NULL) {
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return;
+	}
 
-	activation_source_schedule (self, nm_device_activate_stage5_ip_config_commit, 0);
+	/* Save the pending config */
+	g_object_set_data_full (G_OBJECT (priv->act_request),
+	                        PENDING_IP6_CONFIG,
+	                        g_object_ref (config),
+	                        g_object_unref);
 
-	nm_log_info (LOGD_DEVICE,
-	             "Activation (%s) Stage 5 of 5 (IP Configure Commit) scheduled...",
-	             nm_device_get_iface (self));
+	activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6);
+
+	nm_log_info (LOGD_DEVICE | LOGD_IP4,
+		         "Activation (%s) Stage 5 of 5 (IPv6 Commit) scheduled...",
+		         nm_device_get_iface (self));
 }
 
+gboolean
+nm_device_activate_ip6_state_in_conf (NMDevice *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_CONF;
+}
 
 static void
 clear_act_request (NMDevice *self)
@@ -2707,29 +2875,23 @@ clear_act_request (NMDevice *self)
 		priv->secrets_failed_id = 0;
 	}
 
-	nm_act_request_set_default (priv->act_request, FALSE);
+	nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), FALSE);
 
 	g_object_unref (priv->act_request);
 	priv->act_request = NULL;
 }
 
 static void
-delayed_transitions_clear (NMDevice *self)
+queued_state_clear (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->failed_to_disconnected_id) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): clearing failed->disconnected transition",
-		            nm_device_get_iface (self));
-		g_source_remove (priv->failed_to_disconnected_id);
-		priv->failed_to_disconnected_id = 0;
-	}
-	if (priv->unavailable_to_disconnected_id) {
-		nm_log_dbg (LOGD_DEVICE, "(%s): clearing unavailable->disconnected transition",
-		            nm_device_get_iface (self));
-		g_source_remove (priv->unavailable_to_disconnected_id);
-		priv->unavailable_to_disconnected_id = 0;
+	if (priv->queued_state.id) {
+		nm_log_dbg (LOGD_DEVICE, "(%s): clearing queued state transition (id %d)",
+		            nm_device_get_iface (self), priv->queued_state.id);
+		g_source_remove (priv->queued_state.id);
 	}
+	memset (&priv->queued_state, 0, sizeof (priv->queued_state));
 }
 
 static void
@@ -2738,7 +2900,7 @@ dhcp4_cleanup (NMDevice *self, gboolean stop, gboolean release)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (priv->dhcp4_config) {
-		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP4_CONFIG);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP4_CONFIG);
 		g_object_unref (priv->dhcp4_config);
 		priv->dhcp4_config = NULL;
 	}
@@ -2770,8 +2932,13 @@ dhcp6_cleanup (NMDevice *self, gboolean stop, gboolean release)
 
 	priv->dhcp6_mode = IP6_DHCP_OPT_NONE;
 
+	if (priv->dhcp6_ip6_config) {
+		g_object_unref (priv->dhcp6_ip6_config);
+		priv->dhcp6_ip6_config = NULL;
+	}
+
 	if (priv->dhcp6_config) {
-		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_DHCP6_CONFIG);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_DHCP6_CONFIG);
 		g_object_unref (priv->dhcp6_config);
 		priv->dhcp6_config = NULL;
 	}
@@ -2813,6 +2980,32 @@ dnsmasq_cleanup (NMDevice *self)
 	priv->dnsmasq_manager = NULL;
 }
 
+static void
+_update_ip4_address (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	struct ifreq req;
+	guint32 new_address;
+	int fd;
+
+	g_return_if_fail (self  != NULL);
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_IP4, "couldn't open control socket.");
+		return;
+	}
+
+	memset (&req, 0, sizeof (struct ifreq));
+	strncpy (req.ifr_name, nm_device_get_ip_iface (self), IFNAMSIZ);
+	if (ioctl (fd, SIOCGIFADDR, &req) == 0) {
+		new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr;
+		if (new_address != priv->ip4_address)
+			priv->ip4_address = new_address;
+	}
+	close (fd);
+}
+
 /*
  * nm_device_deactivate
  *
@@ -2820,29 +3013,58 @@ dnsmasq_cleanup (NMDevice *self)
  *
  */
 static void
-nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason)
+nm_device_deactivate (NMDevice *self, NMDeviceStateReason reason)
 {
-	NMDevice *self = NM_DEVICE (device);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDevicePrivate *priv;
 	NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
+	NMConnection *connection = NULL;
+	NMSettingConnection *s_con = NULL;
 	gboolean tried_ipv6 = FALSE;
 	int ifindex, family;
 
 	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DEVICE (self));
 
 	nm_log_info (LOGD_DEVICE, "(%s): deactivating device (reason '%s') [%d]",
 	             nm_device_get_iface (self), reason_to_string (reason), reason);
 
 	/* Save whether or not we tried IPv6 for later */
+	priv = NM_DEVICE_GET_PRIVATE (self);
 	if (priv->ip6_manager || priv->ip6_config)
 		tried_ipv6 = TRUE;
 
+	/* Clean up when device was deactivated during call to firewall */
+	if (priv->fw_call) {
+		nm_firewall_manager_cancel_call (priv->fw_manager, priv->fw_call);
+		priv->fw_call = NULL;
+	}
+
+	if (priv->act_request)
+		connection = nm_act_request_get_connection (priv->act_request);
+	if (connection) {
+		s_con = nm_connection_get_setting_connection (connection);
+		nm_firewall_manager_remove_from_zone (priv->fw_manager,
+		                                      nm_device_get_ip_iface (self),
+		                                      nm_setting_connection_get_zone (s_con));
+	}
+
 	/* Break the activation chain */
 	activation_source_clear (self, TRUE, AF_INET);
 	activation_source_clear (self, TRUE, AF_INET6);
 
-	/* Clear any delayed transitions */
-	delayed_transitions_clear (self);
+	if (priv->act_dep_result_id) {
+		g_source_remove (priv->act_dep_result_id);
+		priv->act_dep_result_id = 0;
+	}
+	if (priv->act_dep_timeout_id) {
+		g_source_remove (priv->act_dep_timeout_id);
+		priv->act_dep_timeout_id = 0;
+	}
+
+	/* Clear any queued transitions */
+	queued_state_clear (self);
+
+	priv->ip4_state = priv->ip6_state = IP_NONE;
 
 	dhcp4_cleanup (self, TRUE, FALSE);
 	dhcp6_cleanup (self, TRUE, FALSE);
@@ -2856,6 +3078,10 @@ nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason)
 	if (priv->ip6_accept_ra_path)
 		nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0\n");
 
+	/* Turn off IPv6 privacy extensions */
+	if (priv->ip6_privacy_tempaddr_path)
+		nm_utils_do_sysctl (priv->ip6_privacy_tempaddr_path, "0\n");
+
 	/* Call device type-specific deactivation */
 	if (NM_DEVICE_GET_CLASS (self)->deactivate)
 		NM_DEVICE_GET_CLASS (self)->deactivate (self);
@@ -2866,46 +3092,119 @@ nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason)
 	/* Take out any entries in the routing table and any IP address the device had. */
 	ifindex = nm_device_get_ip_ifindex (self);
 	family = tried_ipv6 ? AF_UNSPEC : AF_INET;
-	nm_system_iface_flush_routes (ifindex, family);
-	nm_system_iface_flush_addresses (ifindex, family);
-	nm_device_update_ip4_address (self);	
+	if (ifindex >= 0) {
+		nm_system_iface_flush_routes (ifindex, family);
+		nm_system_iface_flush_addresses (ifindex, family);
+	}
+	_update_ip4_address (self);
 
 	/* Clean up nameservers and addresses */
 	nm_device_set_ip4_config (self, NULL, FALSE, &ignored);
 	nm_device_set_ip6_config (self, NULL, FALSE, &ignored);
 }
 
-static gboolean
-device_disconnect (NMDeviceInterface *device,
-                   GError **error)
+gboolean
+nm_device_disconnect (NMDevice *device, GError **error)
 {
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (NM_DEVICE (device));
+	NMDevicePrivate *priv;
+
+	g_return_val_if_fail (device != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (device);
+	if (priv->state <= NM_DEVICE_STATE_DISCONNECTED) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ACTIVE,
+		                     "Cannot disconnect an inactive device.");
+		return FALSE;
+	}
 
 	priv->autoconnect_inhibit = TRUE;	
-	nm_device_state_changed (NM_DEVICE (device), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	nm_device_state_changed (device,
+	                         NM_DEVICE_STATE_DISCONNECTED,
+	                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
 	return TRUE;
 }
 
+static void
+impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context)
+{
+	g_signal_emit (device, signals[DISCONNECT_REQUEST], 0, context);
+}
+
 static gboolean
-check_connection_compatible (NMDeviceInterface *dev_iface,
-                             NMConnection *connection,
-                             GError **error)
+act_dep_timeout_cb (gpointer user_data)
 {
-	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (NM_DEVICE (dev_iface));
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
 
-	if (klass->check_connection_compatible)
-		return klass->check_connection_compatible (NM_DEVICE (dev_iface), connection, error);
+	connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request));
+	nm_log_warn (LOGD_DEVICE,
+	             "Activation (%s) connection '%s' dependency timed out",
+	             nm_device_get_iface (self),
+	             nm_connection_get_id (connection));
 
-	return TRUE;
+	nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+	return FALSE;
 }
 
-static gboolean
-nm_device_activate (NMDeviceInterface *device,
-                    NMActRequest *req,
-                    GError **error)
+static void
+act_dep_result_cb (NMActRequest *req,
+                   NMActRequestDependencyResult result,
+                   NMDevice *self)
 {
-	NMDevice *self = NM_DEVICE (device);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+
+	connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request));
+
+	switch (result) {
+	case NM_ACT_REQUEST_DEP_RESULT_FAILED:
+		g_source_remove (priv->act_dep_result_id);
+		priv->act_dep_result_id = 0;
+
+		nm_log_warn (LOGD_DEVICE,
+			         "Activation (%s) connection '%s' dependency failed",
+			         nm_device_get_iface (self),
+			         nm_connection_get_id (connection));
+		nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+		break;
+	case NM_ACT_REQUEST_DEP_RESULT_READY:
+		g_warn_if_fail (priv->state == NM_DEVICE_STATE_PREPARE);
+		if (priv->state == NM_DEVICE_STATE_PREPARE) {
+			nm_log_info (LOGD_DEVICE,
+					     "Activation (%s) connection '%s' dependency ready, continuing activation",
+					     nm_device_get_iface (self),
+					     nm_connection_get_id (connection));
+			nm_device_activate_schedule_stage1_device_prepare (self);
+		}
+		break;
+	case NM_ACT_REQUEST_DEP_RESULT_WAIT:
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+}
+
+gboolean
+nm_device_activate (NMDevice *self, NMActRequest *req, GError **error)
+{
+	NMDevicePrivate *priv;
+	NMConnection *connection;
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (req != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	nm_log_info (LOGD_DEVICE, "Activation (%s) starting connection '%s'",
+	             nm_device_get_iface (self),
+	             nm_connection_get_id (connection));
 
 	/* Make sure this connection isn't activated already, or in the process of
 	 * being activated.
@@ -2916,9 +3215,7 @@ nm_device_activate (NMDeviceInterface *device,
 		NMConnection *current = nm_act_request_get_connection (priv->act_request);
 
 		if (new == current) {
-			g_set_error_literal (error,
-			                     NM_DEVICE_INTERFACE_ERROR,
-			                     NM_DEVICE_INTERFACE_ERROR_CONNECTION_ACTIVATING,
+			g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CONNECTION_ACTIVATING,
 			                     "Connection is already activating");
 			return FALSE;
 		}
@@ -2927,13 +3224,44 @@ nm_device_activate (NMDeviceInterface *device,
 	priv->act_request = g_object_ref (req);
 
 	if (!nm_act_request_get_assumed (req)) {
+		NMActiveConnection *dep_ac;
+		NMConnection *dep_con;
+
 		/* HACK: update the state a bit early to avoid a race between the 
 		 * scheduled stage1 handler and nm_policy_device_change_check() thinking
 		 * that the activation request isn't deferred because the deferred bit
 		 * gets cleared a bit too early, when the connection becomes valid.
 		 */
 		nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
-		nm_device_activate_schedule_stage1_device_prepare (self);
+
+		/* Handle any dependencies this connection might have */
+		switch (nm_act_request_get_dependency_result (priv->act_request)) {
+		case NM_ACT_REQUEST_DEP_RESULT_FAILED:
+			nm_device_queue_state (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+			break;
+		case NM_ACT_REQUEST_DEP_RESULT_WAIT:
+			dep_ac = nm_act_request_get_dependency (priv->act_request);
+			g_assert (dep_ac);
+			dep_con = nm_active_connection_get_connection (dep_ac);
+			g_assert (dep_con);
+			nm_log_info (LOGD_DEVICE, "Activation (%s) connection '%s' waiting on dependency '%s'",
+						 nm_device_get_iface (self),
+						 nm_connection_get_id (connection),
+						 nm_connection_get_id (dep_con));
+
+			priv->act_dep_result_id = g_signal_connect (priv->act_request,
+					                                    NM_ACT_REQUEST_DEPENDENCY_RESULT,
+					                                    G_CALLBACK (act_dep_result_cb),
+					                                    self);
+			priv->act_dep_timeout_id = g_timeout_add_seconds (60, act_dep_timeout_cb, self);
+			break;
+		default:
+			g_warn_if_reached ();
+			/* fall through */
+		case NM_ACT_REQUEST_DEP_RESULT_READY:
+			nm_device_activate_schedule_stage1_device_prepare (self);
+			break;
+		}
 	} else {
 		/* If it's an assumed connection, let the device subclass short-circuit
 		 * the normal connection process and just copy its IP configs from the
@@ -3062,56 +3390,16 @@ nm_device_set_ip4_config (NMDevice *self,
 			/* Add the DNS information to the DNS manager */
 			nm_dns_manager_add_ip4_config (dns_mgr, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 
-			nm_device_update_ip4_address (self);
+			_update_ip4_address (self);
 		}
 	}
 	g_object_unref (dns_mgr);
 
-	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP4_CONFIG);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_IP4_CONFIG);
 
 	return success;
 }
 
-/*
- * nm_device_get_ip4_address
- *
- * Get a device's IPv4 address
- *
- */
-guint32
-nm_device_get_ip4_address (NMDevice *self)
-{
-	g_return_val_if_fail (self != NULL, 0);
-
-	return NM_DEVICE_GET_PRIVATE (self)->ip4_address;
-}
-
-
-void
-nm_device_update_ip4_address (NMDevice *self)
-{
-	struct ifreq req;
-	guint32 new_address;
-	int fd;
-	
-	g_return_if_fail (self  != NULL);
-
-	fd = socket (PF_INET, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		nm_log_err (LOGD_IP4, "couldn't open control socket.");
-		return;
-	}
-
-	memset (&req, 0, sizeof (struct ifreq));
-	strncpy (req.ifr_name, nm_device_get_ip_iface (self), IFNAMSIZ);
-	if (ioctl (fd, SIOCGIFADDR, &req) == 0) {
-		new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr;
-		if (new_address != nm_device_get_ip4_address (self))
-			NM_DEVICE_GET_PRIVATE (self)->ip4_address = new_address;
-	}
-	close (fd);
-}
-
 static gboolean
 nm_device_set_ip6_config (NMDevice *self,
                           NMIP6Config *new_config,
@@ -3170,7 +3458,7 @@ nm_device_set_ip6_config (NMDevice *self,
 	}
 	g_object_unref (dns_mgr);
 
-	g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP6_CONFIG);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_IP6_CONFIG);
 
 	return success;
 }
@@ -3237,7 +3525,7 @@ out:
 	if (NM_DEVICE_GET_CLASS (self)->update_hw_address)
 		NM_DEVICE_GET_CLASS (self)->update_hw_address (self);
 
-	nm_device_update_ip4_address (self);
+	_update_ip4_address (self);
 	return TRUE;
 }
 
@@ -3288,7 +3576,7 @@ nm_device_take_down (NMDevice *self, gboolean block, NMDeviceStateReason reason)
 	g_return_if_fail (NM_IS_DEVICE (self));
 
 	if (nm_device_get_act_request (self))
-		nm_device_interface_deactivate (NM_DEVICE_INTERFACE (self), reason);
+		nm_device_deactivate (self, reason);
 
 	if (nm_device_is_up (self)) {
 		nm_log_info (LOGD_HW, "(%s): cleaning up...", nm_device_get_iface (self));
@@ -3315,20 +3603,19 @@ dispose (GObject *object)
 	/* Don't down can-assume-connection capable devices that are activated with
 	 * a connection that can be assumed.
 	 */
-	if (   nm_device_interface_can_assume_connections (NM_DEVICE_INTERFACE (self))
-	    && (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) {
+	if (nm_device_can_assume_connections (self) && (priv->state == NM_DEVICE_STATE_ACTIVATED)) {
 		NMConnection *connection;
 	    NMSettingIP4Config *s_ip4 = NULL;
 		const char *method = NULL;
 
-		connection = nm_act_request_get_connection (priv->act_request);
+		connection = nm_device_get_connection (self);
 		if (connection) {
 
 			/* Only static or DHCP IPv4 connections can be left up.
 			 * All IPv6 connections can be left up, so we don't have
 			 * to check that.
 			 */
-			s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+			s_ip4 = nm_connection_get_setting_ip4_config (connection);
 			if (s_ip4)
 				method = nm_setting_ip4_config_get_method (s_ip4);
 			if (   !method
@@ -3338,8 +3625,8 @@ dispose (GObject *object)
 		}
 	}
 
-	/* Clear any delayed transitions */
-	delayed_transitions_clear (self);
+	/* Clear any queued transitions */
+	queued_state_clear (self);
 
 	/* Clean up and stop DHCP */
 	dhcp4_cleanup (self, take_down, FALSE);
@@ -3362,6 +3649,15 @@ dispose (GObject *object)
 	}
 	g_free (priv->ip6_accept_ra_path);
 
+	/* reset the saved use_tempaddr value */
+	if (priv->ip6_privacy_tempaddr_path) {
+		char tmp[16];
+
+		snprintf (tmp, sizeof (tmp), "%d\n", priv->ip6_privacy_tempaddr_save);
+		nm_utils_do_sysctl (priv->ip6_privacy_tempaddr_path, tmp);
+	}
+	g_free (priv->ip6_privacy_tempaddr_path);
+
 	activation_source_clear (self, TRUE, AF_INET);
 	activation_source_clear (self, TRUE, AF_INET6);
 
@@ -3380,6 +3676,9 @@ finalize (GObject *object)
 	if (priv->dhcp_manager)
 		g_object_unref (priv->dhcp_manager);
 
+	if (priv->fw_manager)
+		g_object_unref (priv->fw_manager);
+
 	g_free (priv->udi);
 	g_free (priv->iface);
 	g_free (priv->ip_iface);
@@ -3399,11 +3698,15 @@ set_property (GObject *object, guint prop_id,
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
  
 	switch (prop_id) {
-	case NM_DEVICE_INTERFACE_PROP_UDI:
-		/* construct-only */
+	case PROP_UDI:
+		/* Only virtual interfaces can set UDI post-construction */
+		if (priv->initialized)
+			g_return_if_fail (nm_system_get_iface_type (priv->ifindex, NULL) != NM_IFACE_TYPE_UNSPEC);
+
+		g_free (priv->udi);
 		priv->udi = g_strdup (g_value_get_string (value));
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IFACE:
+	case PROP_IFACE:
 		g_free (priv->iface);
 		priv->ifindex = 0;
 		priv->iface = g_value_dup_string (value);
@@ -3414,32 +3717,32 @@ set_property (GObject *object, guint prop_id,
 			}
 		}
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IP_IFACE:
+	case PROP_IP_IFACE:
 		break;
-	case NM_DEVICE_INTERFACE_PROP_DRIVER:
+	case PROP_DRIVER:
 		priv->driver = g_strdup (g_value_get_string (value));
 		break;
-	case NM_DEVICE_INTERFACE_PROP_CAPABILITIES:
+	case PROP_CAPABILITIES:
 		priv->capabilities = g_value_get_uint (value);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS:
+	case PROP_IP4_ADDRESS:
 		priv->ip4_address = g_value_get_uint (value);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_MANAGED:
+	case PROP_MANAGED:
 		priv->managed = g_value_get_boolean (value);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_FIRMWARE_MISSING:
+	case PROP_FIRMWARE_MISSING:
 		priv->firmware_missing = g_value_get_boolean (value);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE:
+	case PROP_DEVICE_TYPE:
 		g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN);
 		priv->type = g_value_get_uint (value);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_TYPE_DESC:
+	case PROP_TYPE_DESC:
 		g_free (priv->type_desc);
 		priv->type_desc = g_value_dup_string (value);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE:
+	case PROP_RFKILL_TYPE:
 		priv->rfkill_type = g_value_get_uint (value);
 		break;
 	default:
@@ -3466,75 +3769,83 @@ get_property (GObject *object, guint prop_id,
 	state = nm_device_get_state (self);
 
 	switch (prop_id) {
-	case NM_DEVICE_INTERFACE_PROP_UDI:
+	case PROP_UDI:
 		g_value_set_string (value, priv->udi);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IFACE:
+	case PROP_IFACE:
 		g_value_set_string (value, priv->iface);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IP_IFACE:
+	case PROP_IP_IFACE:
 		if (_is_connected (state))
 			g_value_set_string (value, nm_device_get_ip_iface (self));
 		else
 			g_value_set_string (value, NULL);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IFINDEX:
+	case PROP_IFINDEX:
 		g_value_set_int (value, priv->ifindex);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_DRIVER:
+	case PROP_DRIVER:
 		g_value_set_string (value, priv->driver);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_CAPABILITIES:
+	case PROP_CAPABILITIES:
 		g_value_set_uint (value, priv->capabilities);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS:
+	case PROP_IP4_ADDRESS:
 		g_value_set_uint (value, priv->ip4_address);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IP4_CONFIG:
+	case PROP_IP4_CONFIG:
 		if (_is_connected (state) && priv->ip4_config)
 			g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
 		else
 			g_value_set_boxed (value, "/");
 		break;
-	case NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG:
+	case PROP_DHCP4_CONFIG:
 		if (_is_connected (state) && priv->dhcp4_client)
 			g_value_set_boxed (value, nm_dhcp4_config_get_dbus_path (priv->dhcp4_config));
 		else
 			g_value_set_boxed (value, "/");
 		break;
-	case NM_DEVICE_INTERFACE_PROP_IP6_CONFIG:
+	case PROP_IP6_CONFIG:
 		if (_is_connected (state) && priv->ip6_config)
 			g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
 		else
 			g_value_set_boxed (value, "/");
 		break;
-	case NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG:
+	case PROP_DHCP6_CONFIG:
 		if (_is_connected (state) && priv->dhcp6_client)
 			g_value_set_boxed (value, nm_dhcp6_config_get_dbus_path (priv->dhcp6_config));
 		else
 			g_value_set_boxed (value, "/");
 		break;
-	case NM_DEVICE_INTERFACE_PROP_STATE:
+	case PROP_STATE:
 		g_value_set_uint (value, priv->state);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_ACTIVE_CONNECTION:
+	case PROP_STATE_REASON:
+		g_value_set_boxed (value,
+			dbus_g_type_specialized_construct (DBUS_G_TYPE_UINT_STRUCT));
+		dbus_g_type_struct_set (value,
+		                        0, priv->state,
+		                        1, priv->state_reason,
+		                        G_MAXUINT);
+		break;
+	case PROP_ACTIVE_CONNECTION:
 		if (priv->act_request)
-			ac_path = nm_act_request_get_active_connection_path (priv->act_request);
+			ac_path = nm_active_connection_get_path (NM_ACTIVE_CONNECTION (priv->act_request));
 		g_value_set_boxed (value, ac_path ? ac_path : "/");
 		break;
-	case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE:
+	case PROP_DEVICE_TYPE:
 		g_value_set_uint (value, priv->type);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_MANAGED:
+	case PROP_MANAGED:
 		g_value_set_boolean (value, priv->managed);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_FIRMWARE_MISSING:
+	case PROP_FIRMWARE_MISSING:
 		g_value_set_boolean (value, priv->firmware_missing);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_TYPE_DESC:
+	case PROP_TYPE_DESC:
 		g_value_set_string (value, priv->type_desc);
 		break;
-	case NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE:
+	case PROP_RFKILL_TYPE:
 		g_value_set_uint (value, priv->rfkill_type);
 		break;
 	default:
@@ -3557,6 +3868,7 @@ nm_device_class_init (NMDeviceClass *klass)
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->constructor = constructor;
+	object_class->constructed = constructed;
 
 	klass->get_type_capabilities = real_get_type_capabilities;
 	klass->get_generic_capabilities = real_get_generic_capabilities;
@@ -3564,84 +3876,180 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->act_stage2_config = real_act_stage2_config;
 	klass->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
 	klass->act_stage3_ip6_config_start = real_act_stage3_ip6_config_start;
-	klass->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
-	klass->act_stage4_get_ip6_config = real_act_stage4_get_ip6_config;
 	klass->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout;
 	klass->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout;
 
 	/* Properties */
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_UDI,
-									  NM_DEVICE_INTERFACE_UDI);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_IFACE,
-									  NM_DEVICE_INTERFACE_IFACE);
-
-	g_object_class_override_property (object_class,
-	                                  NM_DEVICE_INTERFACE_PROP_IP_IFACE,
-	                                  NM_DEVICE_INTERFACE_IP_IFACE);
-
-	g_object_class_override_property (object_class,
-	                                  NM_DEVICE_INTERFACE_PROP_IFINDEX,
-	                                  NM_DEVICE_INTERFACE_IFINDEX);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_DRIVER,
-									  NM_DEVICE_INTERFACE_DRIVER);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_CAPABILITIES,
-									  NM_DEVICE_INTERFACE_CAPABILITIES);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS,
-									  NM_DEVICE_INTERFACE_IP4_ADDRESS);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_IP4_CONFIG,
-									  NM_DEVICE_INTERFACE_IP4_CONFIG);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_DHCP4_CONFIG,
-									  NM_DEVICE_INTERFACE_DHCP4_CONFIG);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_IP6_CONFIG,
-									  NM_DEVICE_INTERFACE_IP6_CONFIG);
-
-	g_object_class_override_property (object_class,
-	                                  NM_DEVICE_INTERFACE_PROP_DHCP6_CONFIG,
-	                                  NM_DEVICE_INTERFACE_DHCP6_CONFIG);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_STATE,
-									  NM_DEVICE_INTERFACE_STATE);
-
-	g_object_class_override_property (object_class,
-	                                  NM_DEVICE_INTERFACE_PROP_ACTIVE_CONNECTION,
-	                                  NM_DEVICE_INTERFACE_ACTIVE_CONNECTION);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE,
-									  NM_DEVICE_INTERFACE_DEVICE_TYPE);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_MANAGED,
-									  NM_DEVICE_INTERFACE_MANAGED);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_FIRMWARE_MISSING,
-									  NM_DEVICE_INTERFACE_FIRMWARE_MISSING);
-
-	g_object_class_override_property (object_class,
-									  NM_DEVICE_INTERFACE_PROP_TYPE_DESC,
-									  NM_DEVICE_INTERFACE_TYPE_DESC);
-
-	g_object_class_override_property (object_class,
-	                                  NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE,
-	                                  NM_DEVICE_INTERFACE_RFKILL_TYPE);
+	g_object_class_install_property
+		(object_class, PROP_UDI,
+		 g_param_spec_string (NM_DEVICE_UDI,
+		                      "UDI",
+		                      "Unique Device Identifier",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property
+		(object_class, PROP_IFACE,
+		 g_param_spec_string (NM_DEVICE_IFACE,
+		                      "Interface",
+		                      "Interface",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_IP_IFACE,
+		 g_param_spec_string (NM_DEVICE_IP_IFACE,
+		                      "IP Interface",
+		                      "IP Interface",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_DRIVER,
+		 g_param_spec_string (NM_DEVICE_DRIVER,
+		                      "Driver",
+		                      "Driver",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_CAPABILITIES,
+		 g_param_spec_uint (NM_DEVICE_CAPABILITIES,
+		                    "Capabilities",
+		                    "Capabilities",
+		                    0, G_MAXUINT32, NM_DEVICE_CAP_NONE,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_IP4_ADDRESS,
+		 g_param_spec_uint (NM_DEVICE_IP4_ADDRESS,
+		                    "IP4 address",
+		                    "IP4 address",
+		                    0, G_MAXUINT32, 0, /* FIXME */
+		                    G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_IP4_CONFIG,
+		 g_param_spec_boxed (NM_DEVICE_IP4_CONFIG,
+		                     "IP4 Config",
+		                     "IP4 Config",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_DHCP4_CONFIG,
+		 g_param_spec_boxed (NM_DEVICE_DHCP4_CONFIG,
+		                     "DHCP4 Config",
+		                     "DHCP4 Config",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_IP6_CONFIG,
+		 g_param_spec_boxed (NM_DEVICE_IP6_CONFIG,
+		                     "IP6 Config",
+		                     "IP6 Config",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_DHCP6_CONFIG,
+		 g_param_spec_boxed (NM_DEVICE_DHCP6_CONFIG,
+		                     "DHCP6 Config",
+		                     "DHCP6 Config",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(object_class, PROP_STATE,
+		 g_param_spec_uint (NM_DEVICE_STATE,
+		                    "State",
+		                    "State",
+		                    0, G_MAXUINT32, NM_DEVICE_STATE_UNKNOWN,
+		                    G_PARAM_READABLE));
+	g_object_class_install_property
+		(object_class, PROP_STATE_REASON,
+		 g_param_spec_boxed (NM_DEVICE_STATE_REASON,
+		                     "StateReason",
+		                     "StateReason",
+		                     DBUS_G_TYPE_UINT_STRUCT,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_CONNECTION,
+		 g_param_spec_boxed (NM_DEVICE_ACTIVE_CONNECTION,
+		                     "ActiveConnection",
+		                     "ActiveConnection",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_DEVICE_TYPE,
+		 g_param_spec_uint (NM_DEVICE_DEVICE_TYPE,
+		                    "DeviceType",
+		                    "DeviceType",
+		                    0, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	g_object_class_install_property
+		(object_class, PROP_MANAGED,
+		 g_param_spec_boolean (NM_DEVICE_MANAGED,
+		                       "Managed",
+		                       "Managed",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_FIRMWARE_MISSING,
+		 g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING,
+		                       "FirmwareMissing",
+		                       "Firmware missing",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_TYPE_DESC,
+		 g_param_spec_string (NM_DEVICE_TYPE_DESC,
+		                      "Type Description",
+		                      "Device type description",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	g_object_class_install_property
+		(object_class, PROP_RFKILL_TYPE,
+		 g_param_spec_uint (NM_DEVICE_RFKILL_TYPE,
+		                    "Rfkill Type",
+		                    "Type of rfkill switch (if any) supported by this device",
+		                    RFKILL_TYPE_WLAN,
+		                    RFKILL_TYPE_MAX,
+		                    RFKILL_TYPE_UNKNOWN,
+		                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	g_object_class_install_property
+		(object_class, PROP_IFINDEX,
+		 g_param_spec_int (NM_DEVICE_IFINDEX,
+		                   "Ifindex",
+		                   "Ifindex",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+
+	/* Signals */
+	signals[STATE_CHANGED] =
+		g_signal_new ("state-changed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              0, NULL, NULL,
+		              _nm_marshal_VOID__UINT_UINT_UINT,
+		              G_TYPE_NONE, 3,
+		              G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+
+	signals[DISCONNECT_REQUEST] =
+		g_signal_new (NM_DEVICE_DISCONNECT_REQUEST,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              0, NULL, NULL,
+		              g_cclosure_marshal_VOID__POINTER,
+		              G_TYPE_NONE, 1, G_TYPE_POINTER);
 
 	signals[AUTOCONNECT_ALLOWED] =
 		g_signal_new ("autoconnect-allowed",
@@ -3651,32 +4059,11 @@ nm_device_class_init (NMDeviceClass *klass)
 		              autoconnect_allowed_accumulator, NULL,
 		              _nm_marshal_BOOLEAN__VOID,
 		              G_TYPE_BOOLEAN, 0);
-}
-
-static gboolean
-failed_to_disconnected (gpointer user_data)
-{
-	NMDevice *self = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-
-	nm_log_dbg (LOGD_DEVICE, "(%s): running failed->disconnected transition",
-	            nm_device_get_iface (self));
-	priv->failed_to_disconnected_id = 0;
-	nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
-	return FALSE;
-}
 
-static gboolean
-unavailable_to_disconnected (gpointer user_data)
-{
-	NMDevice *self = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
+	                                 &dbus_glib_nm_device_interface_object_info);
 
-	nm_log_dbg (LOGD_DEVICE, "(%s): running unavailable->disconnected transition",
-	            nm_device_get_iface (self));
-	priv->unavailable_to_disconnected_id = 0;
-	nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
-	return FALSE;
+	dbus_g_error_domain_register (NM_DEVICE_ERROR, NULL, NM_TYPE_DEVICE_ERROR);
 }
 
 void
@@ -3690,7 +4077,7 @@ nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	if (priv->firmware_missing != new_missing) {
 		priv->firmware_missing = new_missing;
-		g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_FIRMWARE_MISSING);
+		g_object_notify (G_OBJECT (self), NM_DEVICE_FIRMWARE_MISSING);
 	}
 }
 
@@ -3826,6 +4213,18 @@ reason_to_string (NMDeviceStateReason reason)
 		return "modem-not-found";
 	case NM_DEVICE_STATE_REASON_BT_FAILED:
 		return "bluetooth-failed";
+	case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED:
+		return "gsm-sim-not-inserted";
+	case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED:
+		return "gsm-sim-pin-required";
+	case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED:
+		return "gsm-sim-puk-required";
+	case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG:
+		return "gsm-sim-wrong";
+	case NM_DEVICE_STATE_REASON_INFINIBAND_MODE:
+		return "infiniband-mode";
+	case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
+		return "dependency-failed";
 	default:
 		break;
 	}
@@ -3854,6 +4253,7 @@ nm_device_state_changed (NMDevice *device,
 
 	old_state = priv->state;
 	priv->state = state;
+	priv->state_reason = reason;
 
 	nm_log_info (LOGD_DEVICE, "(%s): device state change: %s -> %s (reason '%s') [%d %d %d]",
 	             nm_device_get_iface (device),
@@ -3864,12 +4264,21 @@ nm_device_state_changed (NMDevice *device,
 	             state,
 	             reason);
 
-	/* Clear any delayed transitions */
-	delayed_transitions_clear (device);
+	/* Clear any queued transitions */
+	queued_state_clear (device);
 
 	/* Cache the activation request for the dispatcher */
 	req = priv->act_request ? g_object_ref (priv->act_request) : NULL;
 
+	/* Update connection timestamps; do this before possibly deactivating the
+	 * device since that will clear the activation request and thus the
+	 * connection, which we need.
+	 */
+	if (state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_ACTIVATED) {
+		nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (nm_act_request_get_connection (req)),
+		                                         (guint64) time (NULL), TRUE);
+	}
+
 	/* Handle the new state here; but anything that could trigger
 	 * another state change should be done below.
 	 */
@@ -3891,18 +4300,19 @@ nm_device_state_changed (NMDevice *device,
 		 * assuming the device's existing connection.
 		 */
 		if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
-			nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason);
+			nm_device_deactivate (device, reason);
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
 		if (old_state != NM_DEVICE_STATE_UNAVAILABLE)
-			nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason);
+			nm_device_deactivate (device, reason);
 		break;
 	default:
 		priv->autoconnect_inhibit = FALSE;
 		break;
 	}
 
-	g_object_notify (G_OBJECT (device), NM_DEVICE_INTERFACE_STATE);
+	g_object_notify (G_OBJECT (device), NM_DEVICE_STATE);
+	g_object_notify (G_OBJECT (device), NM_DEVICE_STATE_REASON);
 	g_signal_emit_by_name (device, "state-changed", state, old_state, reason);
 
 	/* Post-process the event after internal notification */
@@ -3918,7 +4328,7 @@ nm_device_state_changed (NMDevice *device,
 		if (nm_device_is_available (device)) {
 			nm_log_dbg (LOGD_DEVICE, "(%s): device is available, will transition to DISCONNECTED",
 			            nm_device_get_iface (device));
-			priv->unavailable_to_disconnected_id = g_idle_add (unavailable_to_disconnected, device);
+			nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
 		} else {
 			nm_log_dbg (LOGD_DEVICE, "(%s): device not yet available for transition to DISCONNECTED",
 			            nm_device_get_iface (device));
@@ -3935,7 +4345,7 @@ nm_device_state_changed (NMDevice *device,
 		 * immediately because we can't change states again from the state
 		 * handler for a variety of reasons.
 		 */
-		priv->failed_to_disconnected_id = g_idle_add (failed_to_disconnected, device);
+		nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
 		break;
 	default:
 		break;
@@ -3949,6 +4359,51 @@ nm_device_state_changed (NMDevice *device,
 		g_object_unref (req);
 }
 
+static gboolean
+queued_set_state (gpointer user_data)
+{
+	NMDevice *self = NM_DEVICE (user_data);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->queued_state.id) {
+		nm_log_dbg (LOGD_DEVICE, "(%s): running queued state change to %s (id %d)",
+			        nm_device_get_iface (self),
+			        state_to_string (priv->queued_state.state),
+			        priv->queued_state.id);
+		priv->queued_state.id = 0;
+		nm_device_state_changed (self, priv->queued_state.state, priv->queued_state.reason);
+	}
+	queued_state_clear (self);
+	return FALSE;
+}
+
+void
+nm_device_queue_state (NMDevice *self,
+                       NMDeviceState state,
+                       NMDeviceStateReason reason)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (NM_IS_DEVICE (self));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* We should only ever have one delayed state transition at a time */
+	if (priv->queued_state.id) {
+		g_warn_if_fail (priv->queued_state.id == 0);
+		queued_state_clear (self);
+	}
+
+	priv->queued_state.state = state;
+	priv->queued_state.reason = reason;
+	priv->queued_state.id = g_idle_add (queued_set_state, self);
+
+	nm_log_dbg (LOGD_DEVICE, "(%s): queued state change to %s (id %d)",
+	            nm_device_get_iface (self), state_to_string (state),
+	            priv->queued_state.id);
+}
+
 NMDeviceState
 nm_device_get_state (NMDevice *device)
 {
@@ -3983,7 +4438,7 @@ nm_device_set_managed (NMDevice *device,
 	             nm_device_get_iface (device),
 	             managed ? "managed" : "unmanaged");
 
-	g_object_notify (G_OBJECT (device), NM_DEVICE_INTERFACE_MANAGED);
+	g_object_notify (G_OBJECT (device), NM_DEVICE_MANAGED);
 
 	/* If now managed, jump to unavailable */
 	if (managed)
@@ -3992,36 +4447,156 @@ nm_device_set_managed (NMDevice *device,
 		nm_device_state_changed (device, NM_DEVICE_STATE_UNMANAGED, reason);
 }
 
+gboolean
+nm_device_spec_match_list (NMDevice *device, const GSList *specs)
+{
+	g_return_val_if_fail (device != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+	if (NM_DEVICE_GET_CLASS (device)->spec_match_list)
+		return NM_DEVICE_GET_CLASS (device)->spec_match_list (device, specs);
+	return FALSE;
+}
+
 static gboolean
-spec_match_list (NMDeviceInterface *device, const GSList *specs)
+ip4_match_config (NMDevice *self, NMConnection *connection)
 {
-	NMDevice *self;
+	NMSettingIP4Config *s_ip4;
+	int i, num;
+	GSList *leases, *iter;
+	NMDHCPManager *dhcp_mgr;
+	const char *method;
 
-	g_return_val_if_fail (device != NULL, FALSE);
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (!s_ip4)
+		return FALSE;
 
-	self = NM_DEVICE (device);
-	if (NM_DEVICE_GET_CLASS (self)->spec_match_list)
-		return NM_DEVICE_GET_CLASS (self)->spec_match_list (self, specs);
+	/* Get any saved leases that apply to this connection */
+	dhcp_mgr = nm_dhcp_manager_get ();
+	leases = nm_dhcp_manager_get_lease_config (dhcp_mgr,
+	                                           nm_device_get_iface (self),
+	                                           nm_connection_get_uuid (connection));
+	g_object_unref (dhcp_mgr);
+
+	method = nm_setting_ip4_config_get_method (s_ip4);
+	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+		gboolean found = FALSE;
+
+		/* Find at least one lease's address on the device */
+		for (iter = leases; iter; iter = g_slist_next (iter)) {
+			NMIP4Config *ip4_config = iter->data;
+			NMIP4Address *addr = nm_ip4_config_get_address (ip4_config, 0);
+			struct in_addr tmp = { .s_addr = nm_ip4_address_get_address (addr) };
+
+			if (addr && nm_netlink_find_address (nm_device_get_ip_ifindex (self),
+			                                     AF_INET,
+			                                     &tmp,
+			                                     nm_ip4_address_get_prefix (addr))) {
+				found = TRUE; /* Yay, device has same address as a lease */
+				break;
+			}
+		}
+		g_slist_foreach (leases, (GFunc) g_object_unref, NULL);
+		g_slist_free (leases);
+		return found;
+	} else {
+		/* Maybe the connection used to be DHCP and there are stale leases; ignore them */
+		g_slist_foreach (leases, (GFunc) g_object_unref, NULL);
+		g_slist_free (leases);
+	}
 
-	return FALSE;
+	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
+		// FIXME: Enforce no ipv4 addresses?
+		return TRUE;
+	}
+
+	/* 'shared' and 'link-local' aren't supported methods because 'shared'
+	 * requires too much iptables and dnsmasq state to be reclaimed, and
+	 * avahi-autoipd isn't smart enough to allow the link-local address to be
+	 * determined at any point other than when it was first assigned.
+	 */
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
+		return FALSE;
+
+	/* Everything below for static addressing */
+
+	/* Find all IP4 addresses of this connection on the device */
+	num = nm_setting_ip4_config_get_num_addresses (s_ip4);
+	for (i = 0; i < num; i++) {
+		NMIP4Address *addr = nm_setting_ip4_config_get_address (s_ip4, i);
+		struct in_addr tmp = { .s_addr = nm_ip4_address_get_address (addr) };
+
+		if (!nm_netlink_find_address (nm_device_get_ip_ifindex (self),
+		                              AF_INET,
+		                              &tmp,
+		                              nm_ip4_address_get_prefix (addr)))
+			return FALSE;
+	}
+
+	/* Success; all the connection's static IP addresses are assigned to the device */
+	return TRUE;
 }
 
-static NMConnection *
-connection_match_config (NMDeviceInterface *device, const GSList *connections)
+gboolean
+nm_device_match_ip_config (NMDevice *device, NMConnection *connection)
+{
+	if (!ip4_match_config (device, connection))
+		return FALSE;
+
+	/* FIXME: match IPv6 config */
+
+	return TRUE;
+}
+
+NMConnection *
+nm_device_connection_match_config (NMDevice *device, const GSList *connections)
 {
 	g_return_val_if_fail (device != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
 	if (NM_DEVICE_GET_CLASS (device)->connection_match_config)
 		return NM_DEVICE_GET_CLASS (device)->connection_match_config (NM_DEVICE (device), connections);
 	return NULL;
 }
 
-static gboolean
-can_assume_connections (NMDeviceInterface *device)
+/**
+ * nm_device_hwaddr_matches:
+ * @device: the device to use when matching the hardware address
+ * @connection: the connection which supplies the hardware address
+ * @other_hwaddr: if given, use this address instead of the device's actual
+ *   hardware address
+ * @other_hwaddr_len: length in bytes of @other_hwaddr
+ * @fail_if_no_hwaddr: whether to fail the match if @connection does not contain
+ *   a hardware address
+ *
+ * Matches a the devices hardware address (or @other_hwaddr if given) against
+ * the hardware-specific setting in @connection.  Allows for device-agnostic
+ * hardware address matching without having to know the internal details of
+ * the connection and which settings are used by each device subclass.
+ *
+ * Returns: %TRUE if the @device 's hardware address or @other_hwaddr matches
+ *  a hardware address in a hardware-specific setting in @connection
+ */
+gboolean
+nm_device_hwaddr_matches (NMDevice *device,
+                          NMConnection *connection,
+                          const guint8 *other_hwaddr,
+                          guint other_hwaddr_len,
+                          gboolean fail_if_no_hwaddr)
 {
 	g_return_val_if_fail (device != NULL, FALSE);
-
-	return !!NM_DEVICE_GET_CLASS (device)->connection_match_config;
+	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+	if (other_hwaddr)
+		g_return_val_if_fail (other_hwaddr_len > 0, FALSE);
+
+	if (NM_DEVICE_GET_CLASS (device)->hwaddr_matches) {
+		return NM_DEVICE_GET_CLASS (device)->hwaddr_matches (device,
+		                                                     connection,
+		                                                     other_hwaddr,
+		                                                     other_hwaddr_len,
+		                                                     fail_if_no_hwaddr);
+	}
+	return FALSE;
 }
 
 void