summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-04-20 21:11:10 +0200
committerMichael Biebl <biebl@debian.org>2016-04-20 21:11:10 +0200
commit78c3b8801ecf4975e5da1af3b10ae20dd2d876de (patch)
tree19b80fbe9429f554a48ed126911026d4fa8a4b3f /src
parent288a08a3672a89a9bfdb8354b6863cc039759fc2 (diff)
Imported Upstream version 1.2.0 upstream/1.2.0
Diffstat (limited to 'src')
-rw-r--r--src/devices/nm-device-factory.c5
-rw-r--r--src/devices/nm-device-infiniband.c67
-rw-r--r--src/devices/nm-device-macvlan.c9
-rw-r--r--src/devices/nm-device-vlan.c57
-rw-r--r--src/devices/nm-device.c76
-rw-r--r--src/devices/nm-device.h10
-rw-r--r--src/devices/wwan/nm-modem.c3
-rw-r--r--src/nm-config.c2
-rw-r--r--src/nm-core-utils.c34
-rw-r--r--src/nm-core-utils.h5
-rw-r--r--src/nm-manager.c16
-rw-r--r--src/platform/nm-fake-platform.c14
-rw-r--r--src/platform/nm-linux-platform.c38
-rw-r--r--src/platform/nm-platform.c50
-rw-r--r--src/platform/nm-platform.h4
-rw-r--r--src/vpn-manager/nm-vpn-connection.c19
16 files changed, 274 insertions, 135 deletions
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index 9dfa9bcb..482eb185 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -187,10 +187,9 @@ nm_device_factory_get_connection_iface (NMDeviceFactory *factory,
 
 	klass = NM_DEVICE_FACTORY_GET_INTERFACE (factory);
 
-	if (klass->get_connection_iface)
+	ifname = g_strdup (nm_connection_get_interface_name (connection));
+	if (!ifname && klass->get_connection_iface)
 		ifname = klass->get_connection_iface (factory, connection, parent_iface);
-	else
-		ifname = g_strdup (nm_connection_get_interface_name (connection));
 
 	if (!ifname) {
 		g_set_error (error,
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index a27af314..ae9543e4 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -42,6 +42,7 @@ G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE)
 
 typedef struct {
 	gboolean is_partition;
+	int parent_ifindex, p_key;
 } NMDeviceInfinibandPrivate;
 
 enum {
@@ -235,36 +236,43 @@ create_and_realize (NMDevice *device,
                     const NMPlatformLink **out_plink,
                     GError **error)
 {
+	NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (device);
 	NMSettingInfiniband *s_infiniband;
-	int parent_ifindex, p_key;
 	NMPlatformError plerr;
 
-	if (!NM_IS_DEVICE_INFINIBAND (parent)) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-		             "Parent interface %s must be an InfiniBand interface",
-		             nm_device_get_iface (parent));
-		return FALSE;
-	}
-
 	s_infiniband = nm_connection_get_setting_infiniband (connection);
+	g_assert (s_infiniband);
 
 	/* Can only create partitions at this time */
-	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
-	if (p_key < 0) {
-		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+	priv->p_key = nm_setting_infiniband_get_p_key (s_infiniband);
+	if (priv->p_key < 0) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 		                     "only InfiniBand partitions can be created");
 		return FALSE;
 	}
 
-	parent_ifindex = nm_device_get_ifindex (parent);
-	if (parent_ifindex <= 0) {
-		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+	if (!parent) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "InfiniBand partitions can not be created without a parent interface");
+		return FALSE;
+	}
+
+	if (!NM_IS_DEVICE_INFINIBAND (parent)) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "Parent interface %s must be an InfiniBand interface",
+		             nm_device_get_iface (parent));
+		return FALSE;
+	}
+
+	priv->parent_ifindex = nm_device_get_ifindex (parent);
+	if (priv->parent_ifindex <= 0) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 		             "failed to get InfiniBand parent %s ifindex",
 		             nm_device_get_iface (parent));
 		return FALSE;
 	}
 
-	plerr = nm_platform_link_infiniband_add (NM_PLATFORM_GET, parent_ifindex, p_key, out_plink);
+	plerr = nm_platform_link_infiniband_add (NM_PLATFORM_GET, priv->parent_ifindex, priv->p_key, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create InfiniBand P_Key interface '%s' for '%s': %s",
@@ -274,7 +282,33 @@ create_and_realize (NMDevice *device,
 		return FALSE;
 	}
 
-	NM_DEVICE_INFINIBAND_GET_PRIVATE (device)->is_partition = TRUE;
+	priv->is_partition = TRUE;
+	return TRUE;
+}
+
+static gboolean
+unrealize (NMDevice *device, GError **error)
+{
+	NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (device);
+	NMPlatformError plerr;
+
+	g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (device), FALSE);
+
+	if (priv->p_key < 0) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "Only InfiniBand partitions can be removed");
+		return FALSE;
+	}
+
+	plerr = nm_platform_link_infiniband_delete (NM_PLATFORM_GET, priv->parent_ifindex, priv->p_key);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to remove InfiniBand P_Key interface '%s': %s",
+		             nm_device_get_iface (device),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
@@ -328,6 +362,7 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 	object_class->set_property = set_property;
 
 	parent_class->create_and_realize = create_and_realize;
+	parent_class->unrealize = unrealize;
 	parent_class->get_generic_capabilities = get_generic_capabilities;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->complete_connection = complete_connection;
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index cd02cb8e..c431fe89 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -223,12 +223,19 @@ create_and_realize (NMDevice *device,
 	s_macvlan = nm_connection_get_setting_macvlan (connection);
 	g_assert (s_macvlan);
 
+	if (!parent) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "MACVLAN devices can not be created without a parent interface");
+		return FALSE;
+	}
+
 	parent_ifindex = nm_device_get_ifindex (parent);
 	g_warn_if_fail (parent_ifindex > 0);
 
 	lnk.mode = setting_mode_to_platform (nm_setting_macvlan_get_mode (s_macvlan));
 	if (!lnk.mode) {
-		nm_log_info (LOGD_DEVICE, "unsupported MACVLAN mode %u in connection %s",
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "unsupported MACVLAN mode %u in connection %s",
 		             nm_setting_macvlan_get_mode (s_macvlan),
 		             nm_connection_get_uuid (connection));
 		return FALSE;
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index 4d606e9d..eb6527de 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -80,14 +80,14 @@ parent_state_changed (NMDevice *parent,
 }
 
 static void
-parent_hwaddr_changed (NMDevice *parent,
-                       GParamSpec *pspec,
-                       gpointer user_data)
+parent_hwaddr_maybe_changed (NMDevice *parent,
+                             GParamSpec *pspec,
+                             gpointer user_data)
 {
 	NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
 	NMConnection *connection;
 	NMSettingWired *s_wired;
-	const char *cloned_mac = NULL, *new_mac;
+	const char *new_mac, *old_mac;
 	NMSettingIPConfig *s_ip6;
 
 	/* Never touch assumed devices */
@@ -100,23 +100,26 @@ parent_hwaddr_changed (NMDevice *parent,
 
 	/* Update the VLAN MAC only if configuration does not specify one */
 	s_wired = nm_connection_get_setting_wired (connection);
-	if (s_wired)
-		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
+	if (s_wired) {
+		if (nm_setting_wired_get_cloned_mac_address (s_wired))
+			return;
+	}
 
-	if (!cloned_mac) {
-		new_mac = nm_device_get_hw_address (parent);
-		_LOGD (LOGD_VLAN, "parent hardware address changed to %s%s%s",
-		       NM_PRINT_FMT_QUOTE_STRING (new_mac));
-		if (new_mac) {
-			nm_device_set_hw_addr (self, nm_device_get_hw_address (parent),
-			                       "set", LOGD_VLAN);
-			/* When changing the hw address the interface is taken down,
-			 * removing the IPv6 configuration; reapply it.
-			 */
-			s_ip6 = nm_connection_get_setting_ip6_config (connection);
-			if (s_ip6)
-				nm_device_reactivate_ip6_config (NM_DEVICE (self), s_ip6, s_ip6);
-		}
+	old_mac = nm_device_get_hw_address (self);
+	new_mac = nm_device_get_hw_address (parent);
+	if (nm_streq0 (old_mac, new_mac))
+		return;
+
+	_LOGD (LOGD_VLAN, "parent hardware address changed to %s%s%s",
+	       NM_PRINT_FMT_QUOTE_STRING (new_mac));
+	if (new_mac) {
+		nm_device_set_hw_addr (self, new_mac, "set", LOGD_VLAN);
+		/* When changing the hw address the interface is taken down,
+		 * removing the IPv6 configuration; reapply it.
+		 */
+		s_ip6 = nm_connection_get_setting_ip6_config (connection);
+		if (s_ip6)
+			nm_device_reactivate_ip6_config (NM_DEVICE (self), s_ip6, s_ip6);
 	}
 }
 
@@ -141,7 +144,8 @@ nm_device_vlan_set_parent (NMDeviceVlan *self, NMDevice *parent)
 		                                          device);
 
 		priv->parent_hwaddr_id = g_signal_connect (priv->parent, "notify::" NM_DEVICE_HW_ADDRESS,
-		                                           G_CALLBACK (parent_hwaddr_changed), device);
+		                                           G_CALLBACK (parent_hwaddr_maybe_changed), device);
+		parent_hwaddr_maybe_changed (parent, NULL, self);
 
 		/* Set parent-dependent unmanaged flag */
 		nm_device_set_unmanaged_by_flags (device,
@@ -218,6 +222,12 @@ create_and_realize (NMDevice *device,
 	s_vlan = nm_connection_get_setting_vlan (connection);
 	g_assert (s_vlan);
 
+	if (!parent) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
+		             "VLAN devices can not be created without a parent interface");
+		return FALSE;
+	}
+
 	if (!nm_device_supports_vlans (parent)) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED,
 		             "no support for VLANs on interface %s of type %s",
@@ -537,6 +547,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 static NMActStageReturn
 act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 {
+	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (dev);
 	NMSettingVlan *s_vlan;
 	NMSettingWired *s_wired;
 	const char *cloned_mac;
@@ -555,6 +566,10 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 		nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_VLAN);
 	}
 
+	/* Change MAC address to parent's one if needed */
+	if (priv->parent)
+		parent_hwaddr_maybe_changed (priv->parent, NULL, dev);
+
 	s_vlan = (NMSettingVlan *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_VLAN);
 	if (s_vlan) {
 		gs_free NMVlanQosMapping *ingress_map = NULL;
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 3d949ec2..08cf6dce 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -280,11 +280,11 @@ typedef struct _NMDevicePrivate {
 	NMIP4Config *   wwan_ip4_config; /* WWAN configuration */
 	GSList *        vpn4_configs;   /* VPNs which use this device */
 	struct {
-		gboolean v4_has;
-		gboolean v4_is_assumed;
+		bool v4_has;
+		bool v4_is_assumed;
+		bool v6_has;
+		bool v6_is_assumed;
 		NMPlatformIP4Route v4;
-		gboolean v6_has;
-		gboolean v6_is_assumed;
 		NMPlatformIP6Route v6;
 	} default_route;
 
@@ -878,6 +878,34 @@ nm_device_get_ip6_route_metric (NMDevice *self)
 	return _get_ipx_route_metric (self, FALSE);
 }
 
+static void
+_update_default_route (NMDevice *self, int addr_family, gboolean has, gboolean is_assumed)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	bool *p_has, *p_is_assumed;
+
+	nm_assert (NM_IN_SET (addr_family, 0, AF_INET, AF_INET6));
+
+	if (addr_family == AF_INET) {
+		p_has = &priv->default_route.v4_has;
+		p_is_assumed = &priv->default_route.v4_is_assumed;
+	} else {
+		p_has = &priv->default_route.v6_has;
+		p_is_assumed = &priv->default_route.v6_is_assumed;
+	}
+
+	if (*p_has == has && *p_is_assumed == is_assumed)
+		return;
+
+	*p_has = has;
+	*p_is_assumed = is_assumed;
+
+	if (addr_family == AF_INET)
+		nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
+	else
+		nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
+}
+
 const NMPlatformIP4Route *
 nm_device_get_ip4_default_route (NMDevice *self, gboolean *out_is_assumed)
 {
@@ -2067,8 +2095,12 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 	_LOGD (LOGD_DEVICE, "unrealize (ifindex %d)", ifindex > 0 ? ifindex : 0);
 
 	if (remove_resources) {
-		if (ifindex > 0)
+		if (NM_DEVICE_GET_CLASS (self)->unrealize) {
+			if (!NM_DEVICE_GET_CLASS (self)->unrealize (self, error))
+				return FALSE;
+		} else if (ifindex > 0) {
 			nm_platform_link_delete (NM_PLATFORM_GET, ifindex);
+		}
 	}
 
 	NM_DEVICE_GET_CLASS (self)->unrealize_notify (self);
@@ -2568,12 +2600,14 @@ nm_device_get_enslaved (NMDevice *self)
 /**
  * nm_device_removed:
  * @self: the #NMDevice
+ * @unconfigure_ip_config: whether to clear the IP config objects
+ *   of the device (provided, it is still not cleared at this point).
  *
  * Called by the manager when the device was removed. Releases the device from
  * the master in case it's enslaved.
  */
 void
-nm_device_removed (NMDevice *self)
+nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config)
 {
 	NMDevicePrivate *priv;
 
@@ -2586,16 +2620,19 @@ nm_device_removed (NMDevice *self)
 		nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 	}
 
+	if (!unconfigure_ip_config)
+		return;
+
 	/* Clean up IP configs; this does not actually deconfigure the
 	 * interface, it just clears the configuration to which policy
 	 * is reacting via NM_DEVICE_IP4_CONFIG_CHANGED/NM_DEVICE_IP6_CONFIG_CHANGED
 	 * signal. As NMPolicy registered the NMIPxConfig instances in NMDnsManager,
 	 * these would be leaked otherwise. */
-	priv->default_route.v4_has = FALSE;
-	priv->default_route.v4_is_assumed = TRUE;
+	_update_default_route (self, AF_INET,  priv->default_route.v4_has, TRUE);
+	_update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE);
+	_update_default_route (self, AF_INET,  FALSE, TRUE);
+	_update_default_route (self, AF_INET6, FALSE, TRUE);
 	nm_device_set_ip4_config (self, NULL, 0, FALSE, FALSE, NULL);
-	priv->default_route.v6_has = FALSE;
-	priv->default_route.v6_is_assumed = TRUE;
 	nm_device_set_ip6_config (self, NULL, FALSE, FALSE, NULL);
 }
 
@@ -9994,20 +10031,15 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	priv->default_route.v4_has = FALSE;
-	priv->default_route.v6_has = FALSE;
-
 	if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
-		priv->default_route.v4_is_assumed = FALSE;
-		priv->default_route.v6_is_assumed = FALSE;
-		nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
-		nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
+		_update_default_route (self, AF_INET,  FALSE, FALSE);
+		_update_default_route (self, AF_INET6, FALSE, FALSE);
+	} else {
+		_update_default_route (self, AF_INET,  priv->default_route.v4_has, TRUE);
+		_update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE);
 	}
-
-	priv->default_route.v4_is_assumed = TRUE;
-	priv->default_route.v6_is_assumed = TRUE;
-	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
-	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
+	_update_default_route (self, AF_INET,  FALSE, TRUE);
+	_update_default_route (self, AF_INET6, FALSE, TRUE);
 
 	priv->v4_commit_first_time = TRUE;
 	priv->v6_commit_first_time = TRUE;
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 368e77f7..9f689850 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -180,6 +180,14 @@ typedef struct {
 	void        (*realize_start_notify) (NMDevice *self, const NMPlatformLink *plink);
 
 	/**
+	 * unrealize():
+	 * @self: the #NMDevice
+	 *
+	 * Remove the device backing resources.
+	 */
+	gboolean               (*unrealize) (NMDevice *self, GError **error);
+
+	/**
 	 * unrealize_notify():
 	 * @self: the #NMDevice
 	 *
@@ -377,7 +385,7 @@ gboolean        nm_device_has_unmodified_applied_connection (NMDevice *self,
                                                              NMSettingCompareFlags compare_flags);
 NMSetting *     nm_device_get_applied_setting   (NMDevice *dev, GType setting_type);
 
-void            nm_device_removed               (NMDevice *dev);
+void            nm_device_removed               (NMDevice *self, gboolean unconfigure_ip_config);
 
 gboolean        nm_device_is_available          (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags);
 gboolean        nm_device_has_carrier           (NMDevice *dev);
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 2a8a9d1e..804a6d9c 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -742,7 +742,8 @@ cancel_get_secrets (NMModem *self)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 
-	nm_act_request_cancel_secrets (priv->act_request, priv->secrets_id);
+	if (priv->secrets_id)
+		nm_act_request_cancel_secrets (priv->act_request, priv->secrets_id);
 }
 
 static void
diff --git a/src/nm-config.c b/src/nm-config.c
index 58fd96cb..8bca6870 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -125,7 +125,7 @@ gint
 nm_config_parse_boolean (const char *str,
                          gint default_value)
 {
-	return nm_utils_ascii_str_to_bool (str, default_value);
+	return _nm_utils_ascii_str_to_bool (str, default_value);
 }
 
 gint
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index 714aaa92..bbe46654 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -154,40 +154,6 @@ _nm_singleton_instance_register_destruction (GObject *instance)
 
 /*****************************************************************************/
 
-gint
-nm_utils_ascii_str_to_bool (const char *str,
-                            gint default_value)
-{
-	gsize len;
-	char *s = NULL;
-
-	if (!str)
-		return default_value;
-
-	while (str[0] && g_ascii_isspace (str[0]))
-		str++;
-
-	if (!str[0])
-		return default_value;
-
-	len = strlen (str);
-	if (g_ascii_isspace (str[len - 1])) {
-		s = g_strdup (str);
-		g_strchomp (s);
-		str = s;
-	}
-
-	if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
-		default_value = TRUE;
-	else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
-		default_value = FALSE;
-	if (s)
-		g_free (s);
-	return default_value;
-}
-
-/*****************************************************************************/
-
 /*
  * nm_ethernet_address_is_valid:
  * @addr: pointer to a binary or ASCII Ethernet address
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index 280be047..528288c3 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -91,11 +91,6 @@ GETTER (void) \
 
 /*****************************************************************************/
 
-gint nm_utils_ascii_str_to_bool (const char *str,
-                                 gint default_value);
-
-/*****************************************************************************/
-
 gboolean nm_ethernet_address_is_valid (gconstpointer addr, gssize len);
 
 in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen);
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 289a91d6..e64c68af 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -833,13 +833,13 @@ remove_device (NMManager *self,
                gboolean allow_unmanage)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	gboolean unmanage = FALSE;
 
 	_LOGD (LOGD_DEVICE, "(%s): removing device (allow_unmanage %d, managed %d)",
 	       nm_device_get_iface (device), allow_unmanage, nm_device_get_managed (device, FALSE));
 
 	if (allow_unmanage && nm_device_get_managed (device, FALSE)) {
 		NMActRequest *req = nm_device_get_act_request (device);
-		gboolean unmanage = FALSE;
 
 		/* Leave activated interfaces up when quitting so their configuration
 		 * can be taken over when NM restarts.  This ensures connectivity while
@@ -869,7 +869,18 @@ remove_device (NMManager *self,
 	priv->devices = g_slist_remove (priv->devices, device);
 
 	if (nm_device_is_real (device)) {
-		nm_device_removed (device);
+		gboolean unconfigure_ip_config = !quitting || unmanage;
+
+		/* When we don't unmanage the device on shutdown, we want to preserve the DNS
+		 * configuration in resolv.conf. For that, we must leak the configuration
+		 * in NMPolicy/NMDnsManager. We do that, by emitting the device-removed signal
+		 * with device's ip-config object still uncleared. In that case, NMPolicy
+		 * never learns to unconfigure the ip-config objects and does not remove them
+		 * from DNS on shutdown (which is ugly, because we don't cleanup the memory
+		 * properly).
+		 *
+		 * Control that by passing @unconfigure_ip_config.  */
+		nm_device_removed (device, unconfigure_ip_config);
 
 		g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
 		_notify (self, PROP_DEVICES);
@@ -3512,7 +3523,6 @@ activation_add_done (NMSettings *settings,
 	                            FALSE,
 	                            nm_active_connection_get_subject (active),
 	                            error->message);
-	g_object_unref (active);
 	g_clear_error (&local);
 }
 
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 15d9c543..7b545076 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -731,6 +731,19 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMP
 }
 
 static gboolean
+infiniband_partition_delete (NMPlatform *platform, int parent, int p_key)
+{
+	NMFakePlatformLink *parent_device;
+	gs_free char *name = NULL;
+
+	parent_device = link_get (platform, parent);
+	g_return_val_if_fail (parent_device != NULL, FALSE);
+
+	name = g_strdup_printf ("%s.%04x", parent_device->link.name, p_key);
+	return link_delete (platform, nm_platform_link_get_ifindex (platform, name));
+}
+
+static gboolean
 wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps)
 {
 	NMFakePlatformLink *device = link_get (platform, ifindex);
@@ -1460,6 +1473,7 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass)
 	platform_class->link_vxlan_add = link_vxlan_add;
 
 	platform_class->infiniband_partition_add = infiniband_partition_add;
+	platform_class->infiniband_partition_delete = infiniband_partition_delete;
 
 	platform_class->wifi_get_capabilities = wifi_get_capabilities;
 	platform_class->wifi_get_bssid = wifi_get_bssid;
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index ded019e6..254f9c85 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -1562,6 +1562,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
 		_lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached);
 		if (   link_cached
 		    && link_cached->link.type == obj->link.type
+		    && link_cached->_link.netlink.lnk
 		    && (   !lnk_data
 		        || nmp_object_equal (lnk_data, link_cached->_link.netlink.lnk))) {
 			nmp_object_unref (lnk_data);
@@ -5077,24 +5078,35 @@ link_release (NMPlatform *platform, int master, int slave)
 /******************************************************************/
 
 static gboolean
-infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link)
+_infiniband_partition_action (NMPlatform *platform, int parent, int p_key, const char *action, char **ifname)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	const NMPObject *obj_parent;
-	const NMPObject *obj;
 	gs_free char *path = NULL;
 	gs_free char *id = NULL;
-	gs_free char *ifname = NULL;
 
 	obj_parent = nmp_cache_lookup_link (priv->cache, parent);
 	if (!obj_parent || !obj_parent->link.name[0])
 		g_return_val_if_reached (FALSE);
 
-	ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key);
+	*ifname = g_strdup_printf ("%s.%04x", obj_parent->link.name, p_key);
 
-	path = g_strdup_printf ("/sys/class/net/%s/create_child", NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name));
+	path = g_strdup_printf ("/sys/class/net/%s/%s",
+	                        NM_ASSERT_VALID_PATH_COMPONENT (obj_parent->link.name),
+	                        action);
 	id = g_strdup_printf ("0x%04x", p_key);
-	if (!nm_platform_sysctl_set (platform, path, id))
+
+	return nm_platform_sysctl_set (platform, path, id);
+}
+
+
+static gboolean
+infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMPlatformLink **out_link)
+{
+	const NMPObject *obj;
+	gs_free char *ifname = NULL;
+
+	if (!_infiniband_partition_action (platform, parent, p_key, "create_child", &ifname))
 		return FALSE;
 
 	do_request_link (platform, 0, ifname);
@@ -5106,6 +5118,19 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMP
 	return !!obj;
 }
 
+static gboolean
+infiniband_partition_delete (NMPlatform *platform, int parent, int p_key)
+{
+	gs_free char *ifname = NULL;
+
+	if (!_infiniband_partition_action (platform, parent, p_key, "delete_child", &ifname)) {
+		if (errno != ENODEV)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
 /******************************************************************/
 
 static WifiData *
@@ -6380,6 +6405,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 	platform_class->tun_add = tun_add;
 
 	platform_class->infiniband_partition_add = infiniband_partition_add;
+	platform_class->infiniband_partition_delete = infiniband_partition_delete;
 
 	platform_class->wifi_get_capabilities = wifi_get_capabilities;
 	platform_class->wifi_get_bssid = wifi_get_bssid;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 69b0d420..26ac766d 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -1888,11 +1888,12 @@ nm_platform_link_gre_add (NMPlatform *self,
 	return NM_PLATFORM_ERROR_SUCCESS;
 }
 
-NMPlatformError
-nm_platform_link_infiniband_add (NMPlatform *self,
-                                 int parent,
-                                 int p_key,
-                                 const NMPlatformLink **out_link)
+static NMPlatformError
+_infiniband_add_add_or_delete (NMPlatform *self,
+                               int parent,
+                               int p_key,
+                               gboolean add,
+                               const NMPlatformLink **out_link)
 {
 	gs_free char *parent_name = NULL;
 	gs_free char *name = NULL;
@@ -1909,17 +1910,42 @@ nm_platform_link_infiniband_add (NMPlatform *self,
 		return NM_PLATFORM_ERROR_WRONG_TYPE;
 
 	name = g_strdup_printf ("%s.%04x", parent_name, p_key);
-	plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link);
-	if (plerr != NM_PLATFORM_ERROR_SUCCESS)
-		return plerr;
 
-	_LOGD ("link: adding infiniband partition %s for parent '%s' (%d), key %d",
-	       name, parent_name, parent, p_key);
-	if (!klass->infiniband_partition_add (self, parent, p_key, out_link))
-		return NM_PLATFORM_ERROR_UNSPECIFIED;
+	if (add) {
+		plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link);
+		if (plerr != NM_PLATFORM_ERROR_SUCCESS)
+			return plerr;
+
+		_LOGD ("link: adding infiniband partition %s for parent '%s' (%d), key %d",
+		       name, parent_name, parent, p_key);
+		if (!klass->infiniband_partition_add (self, parent, p_key, out_link))
+			return NM_PLATFORM_ERROR_UNSPECIFIED;
+	} else {
+		if (!klass->infiniband_partition_delete (self, parent, p_key))
+			return NM_PLATFORM_ERROR_UNSPECIFIED;
+	}
+
 	return NM_PLATFORM_ERROR_SUCCESS;
 }
 
+NMPlatformError
+nm_platform_link_infiniband_add (NMPlatform *self,
+                                 int parent,
+                                 int p_key,
+                                 const NMPlatformLink **out_link)
+{
+	return _infiniband_add_add_or_delete (self, parent, p_key, TRUE, out_link);
+}
+
+NMPlatformError
+nm_platform_link_infiniband_delete (NMPlatform *self,
+                                   int parent,
+                                   int p_key)
+{
+	return _infiniband_add_add_or_delete (self, parent, p_key, FALSE, NULL);
+}
+
+
 gboolean
 nm_platform_link_infiniband_get_properties (NMPlatform *self,
                                             int ifindex,
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 9f055b04..658b709a 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -570,6 +570,7 @@ typedef struct {
 	                          const NMPlatformLink **out_link);
 
 	gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link);
+	gboolean (*infiniband_partition_delete) (NMPlatform *, int parent, int p_key);
 
 	gboolean (*tun_add) (NMPlatform *platform, const char *name, gboolean tap, gint64 owner, gint64 group, gboolean pi,
 	                     gboolean vnet_hdr, gboolean multi_queue, const NMPlatformLink **out_link);
@@ -815,6 +816,9 @@ NMPlatformError nm_platform_link_infiniband_add (NMPlatform *self,
                                                  int parent,
                                                  int p_key,
                                                  const NMPlatformLink **out_link);
+NMPlatformError nm_platform_link_infiniband_delete (NMPlatform *self,
+                                                    int parent,
+                                                    int p_key);
 gboolean nm_platform_link_infiniband_get_properties (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode);
 
 gboolean nm_platform_link_veth_get_properties   (NMPlatform *self, int ifindex, int *out_peer_ifindex);
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 2945ad34..a1cd0c32 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -1210,7 +1210,7 @@ process_generic_config (NMVpnConnection *self, GVariant *dict)
 	const char *str;
 	GVariant *v;
 	guint32 u32;
-	gboolean b, success = FALSE;
+	gboolean b;
 
 	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CAN_PERSIST, "b", &b) && b) {
 		/* Defaults to FALSE, so only let service indicate TRUE */
@@ -1246,17 +1246,15 @@ process_generic_config (NMVpnConnection *self, GVariant *dict)
 
 	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, "u", &u32)) {
 		priv->ip4_external_gw = u32;
-		success = TRUE;
 	} else if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, "@ay", &v)) {
 		priv->ip6_external_gw = ip6_addr_dup_from_variant (v);
-		success = !!priv->ip6_external_gw;
 		g_variant_unref (v);
-	}
 
-	if (!success) {
-		_LOGE ("VPN gateway is neither IPv4 nor IPv6");
-		nm_vpn_connection_config_maybe_complete (self, FALSE);
-		return FALSE;
+		if (!priv->ip6_external_gw) {
+			_LOGE ("Invalid IPv6 VPN gateway address received");
+			nm_vpn_connection_config_maybe_complete (self, FALSE);
+			return FALSE;
+		}
 	}
 
 	priv->mtu = 0;
@@ -2021,7 +2019,10 @@ nm_vpn_connection_activate (NMVpnConnection *self,
 	s_vpn = nm_connection_get_setting_vpn (_get_applied_connection (self));
 	g_return_if_fail (s_vpn);
 
-	service = nm_setting_vpn_get_service_type (s_vpn);
+	service = nm_vpn_plugin_info_lookup_property (plugin_info,
+	                                              NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION,
+	                                              "service");
+	g_return_if_fail (service);
 
 	if (nm_vpn_plugin_info_supports_multiple (plugin_info)) {
 		const char *path;