summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-09-20 21:04:15 +0200
committerMichael Biebl <biebl@debian.org>2017-09-20 21:04:15 +0200
commitbdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (patch)
tree7f6a4560c8cd3e1730df2af4ed6c3e4c5c0265f6 /src
parentb9f0451fa35393ceedf6d9d20b78c43578ebea5d (diff)
New upstream version 1.8.4 upstream/1.8.4
Diffstat (limited to 'src')
-rw-r--r--src/devices/nm-device.c26
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.c27
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.h1
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c29
-rw-r--r--src/dhcp/tests/test-dhcp-dhclient.c110
-rw-r--r--src/dns/nm-dns-manager.c18
-rw-r--r--src/nm-checkpoint.c24
-rw-r--r--src/nm-connectivity.c1
-rw-r--r--src/nm-default-route-manager.c46
-rw-r--r--src/nm-route-manager.c26
-rw-r--r--src/platform/nm-linux-platform.c2
-rw-r--r--src/platform/tests/test-route.c3
12 files changed, 227 insertions, 86 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index fbf315ed..bacbfb33 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -3422,6 +3422,7 @@ slave_state_changed (NMDevice *slave,
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean release = FALSE;
+	gboolean configure;
 
 	_LOGD (LOGD_DEVICE, "slave %s state change %d (%s) -> %d (%s)",
 	       nm_device_get_iface (slave),
@@ -3445,8 +3446,11 @@ slave_state_changed (NMDevice *slave,
 	}
 
 	if (release) {
+		configure =    priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED
+		            && nm_device_sys_iface_state_get (slave) != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL;
+
 		nm_device_master_release_one_slave (self, slave,
-		                                    priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED,
+		                                    configure,
 		                                    reason);
 		/* Bridge/bond/team interfaces are left up until manually deactivated */
 		if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED)
@@ -3692,13 +3696,6 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success)
 
 			priv->is_enslaved = TRUE;
 
-			if (   NM_IN_SET_TYPED (NMDeviceSysIfaceState,
-			                        priv->sys_iface_state,
-			                        NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
-			                        NM_DEVICE_SYS_IFACE_STATE_ASSUME)
-			    && nm_device_sys_iface_state_get (priv->master) == NM_DEVICE_SYS_IFACE_STATE_MANAGED)
-				nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
-
 			_notify (self, PROP_MASTER);
 			_notify (priv->master, PROP_SLAVES);
 		} else if (activating) {
@@ -13475,8 +13472,17 @@ _hw_addr_get_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi,
 	}
 
 	if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) {
-		addr = nm_device_get_permanent_hw_address (self);
-		if (!addr) {
+		gboolean is_fake;
+
+		addr = nm_device_get_permanent_hw_address_full (self, TRUE, &is_fake);
+		if (is_fake) {
+			/* Preserve the current address if the permanent address if fake */
+			NM_SET_OUT (preserve, TRUE);
+			NM_SET_OUT (hwaddr, NULL);
+			NM_SET_OUT (hwaddr_type, HW_ADDR_TYPE_UNSET);
+			NM_SET_OUT (hwaddr_detail, g_steal_pointer (&addr_setting_free) ?: g_strdup (addr_setting));
+			return TRUE;
+		} else if (!addr) {
 			g_set_error_literal (error,
 			                     NM_DEVICE_ERROR,
 			                     NM_DEVICE_ERROR_FAILED,
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
index 216319b3..6a1b6865 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp/nm-dhcp-dhclient-utils.c
@@ -31,7 +31,9 @@
 #include "platform/nm-platform.h"
 #include "NetworkManagerUtils.h"
 
-#define CLIENTID_TAG            "send dhcp-client-identifier"
+#define TIMEOUT_TAG      "timeout "
+#define RETRY_TAG        "retry "
+#define CLIENTID_TAG     "send dhcp-client-identifier"
 
 #define HOSTNAME4_TAG    "send host-name"
 #define HOSTNAME4_FORMAT HOSTNAME4_TAG " \"%s\"; # added by NetworkManager"
@@ -263,6 +265,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
                                 GBytes *client_id,
                                 const char *anycast_addr,
                                 const char *hostname,
+                                guint32 timeout,
                                 gboolean use_fqdn,
                                 const char *orig_path,
                                 const char *orig_contents,
@@ -296,7 +299,8 @@ nm_dhcp_dhclient_create_config (const char *interface,
 				continue;
 
 			if (   !intf[0]
-			    && g_str_has_prefix (p, "interface")) {
+			    && g_str_has_prefix (p, "interface")
+			    && !in_req) {
 				if (read_interface (p, intf, sizeof (intf)))
 					continue;
 			}
@@ -309,6 +313,17 @@ nm_dhcp_dhclient_create_config (const char *interface,
 			if (intf[0] && !nm_streq (intf, interface))
 				continue;
 
+			/* Some timing parameters in dhclient should not be imported (timeout, retry).
+			 * The retry parameter will be simply not used as we will exit on first failure.
+			 * The timeout one instead may affect NetworkManager behavior: if the timeout
+			 * elapses before dhcp-timeout dhclient will report failure and cause NM to
+			 * fail the dhcp process before dhcp-timeout. So, always skip importing timeout
+			 * as we will need to add one greater than dhcp-timeout.
+			 */
+			if (   !strncmp (p, TIMEOUT_TAG, strlen (TIMEOUT_TAG))
+			    || !strncmp (p, RETRY_TAG, strlen (RETRY_TAG)))
+				continue;
+
 			if (!strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG))) {
 				/* Override config file "dhcp-client-id" and use one from the connection */
 				if (client_id)
@@ -374,6 +389,14 @@ nm_dhcp_dhclient_create_config (const char *interface,
 	} else
 		g_string_append_c (new_contents, '\n');
 
+	/* ensure dhclient timeout is greater than dhcp-timeout: as dhclient timeout default value is
+	 * 60 seconds, we need this only if dhcp-timeout is greater than 60.
+	 */
+	if (timeout >= 60) {
+		timeout = timeout < G_MAXINT32 ? timeout + 1 : G_MAXINT32;
+		g_string_append_printf (new_contents, "timeout %u;\n", timeout);
+	}
+
 	if (is_ip6) {
 		add_hostname6 (new_contents, hostname);
 		add_request (reqs, "dhcp6.name-servers");
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h
index 994b1b9f..2268890b 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp/nm-dhcp-dhclient-utils.h
@@ -27,6 +27,7 @@ char *nm_dhcp_dhclient_create_config (const char *interface,
                                       GBytes *client_id,
                                       const char *anycast_addr,
                                       const char *hostname,
+                                      guint32 timeout,
                                       gboolean use_fqdn,
                                       const char *orig_path,
                                       const char *orig_contents,
diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c
index a56e5a3c..f20158c6 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -182,6 +182,7 @@ merge_dhclient_config (NMDhcpDhclient *self,
                        GBytes *client_id,
                        const char *anycast_addr,
                        const char *hostname,
+                       guint32 timeout,
                        gboolean use_fqdn,
                        const char *orig_path,
                        GBytes **out_new_client_id,
@@ -206,7 +207,8 @@ merge_dhclient_config (NMDhcpDhclient *self,
 	if (is_ip6 && hostname && !strchr (hostname, '.'))
 		_LOGW ("hostname is not a FQDN, it will be ignored");
 
-	new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, use_fqdn, orig_path, orig, out_new_client_id);
+	new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, timeout,
+	                                      use_fqdn, orig_path, orig, out_new_client_id);
 	g_assert (new);
 	success = g_file_set_contents (conf_file, new, -1, error);
 	g_free (new);
@@ -294,6 +296,7 @@ create_dhclient_config (NMDhcpDhclient *self,
                         GBytes *client_id,
                         const char *dhcp_anycast_addr,
                         const char *hostname,
+                        guint32 timeout,
                         gboolean use_fqdn,
                         GBytes **out_new_client_id)
 {
@@ -314,7 +317,7 @@ create_dhclient_config (NMDhcpDhclient *self,
 
 	error = NULL;
 	success = merge_dhclient_config (self, iface, new, is_ip6, client_id, dhcp_anycast_addr,
-			                         hostname, use_fqdn, orig, out_new_client_id, &error);
+	                                 hostname, timeout, use_fqdn, orig, out_new_client_id, &error);
 	if (!success) {
 		_LOGW ("error creating dhclient configuration: %s", error->message);
 		g_error_free (error);
@@ -342,8 +345,6 @@ dhclient_start (NMDhcpClient *client,
 	char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL;
 	gboolean ipv6, success;
 	char *escaped, *preferred_leasefile_path = NULL;
-	guint32 timeout;
-	char timeout_str[64];
 
 	g_return_val_if_fail (priv->pid_file == NULL, FALSE);
 
@@ -446,17 +447,6 @@ dhclient_start (NMDhcpClient *client,
 		g_ptr_array_add (argv, (gpointer) priv->conf_file);
 	}
 
-	/* Specify a timeout longer than configuration's one,
-	 * so that dhclient doesn't send back a FAIL event before
-	 * that time.
-	 */
-	timeout = nm_dhcp_client_get_timeout (client);
-	if (timeout >= 60) {
-		timeout = timeout < G_MAXINT32 ? timeout + 1 : G_MAXINT32;
-		g_ptr_array_add (argv, (gpointer) "-timeout");
-		g_ptr_array_add (argv, (gpointer) nm_sprintf_buf (timeout_str, "%u", (unsigned) timeout));
-	}
-
 	/* Usually the system bus address is well-known; but if it's supposed
 	 * to be something else, we need to push it to dhclient, since dhclient
 	 * sanitizes the environment it gives the action scripts.
@@ -506,6 +496,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	GBytes *client_id;
 	gs_unref_bytes GBytes *new_client_id = NULL;
 	const char *iface, *uuid, *hostname;
+	guint32 timeout;
 	gboolean success = FALSE;
 	gboolean use_fqdn;
 
@@ -513,10 +504,11 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	uuid = nm_dhcp_client_get_uuid (client);
 	client_id = nm_dhcp_client_get_client_id (client);
 	hostname = nm_dhcp_client_get_hostname (client);
+	timeout = nm_dhcp_client_get_timeout (client);
 	use_fqdn = nm_dhcp_client_get_use_fqdn (client);
 
 	priv->conf_file = create_dhclient_config (self, iface, FALSE, uuid, client_id, dhcp_anycast_addr,
-	                                          hostname, use_fqdn, &new_client_id);
+	                                          hostname, timeout, use_fqdn, &new_client_id);
 	if (priv->conf_file) {
 		if (new_client_id)
 			nm_dhcp_client_set_client_id (client, new_client_id);
@@ -539,12 +531,15 @@ ip6_start (NMDhcpClient *client,
 	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
 	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	const char *iface, *uuid, *hostname;
+	guint32 timeout;
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
 	hostname = nm_dhcp_client_get_hostname (client);
+	timeout = nm_dhcp_client_get_timeout (client);
 
-	priv->conf_file = create_dhclient_config (self, iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, TRUE, NULL);
+	priv->conf_file = create_dhclient_config (self, iface, TRUE, uuid, NULL, dhcp_anycast_addr,
+	                                          hostname, timeout, TRUE, NULL);
 	if (!priv->conf_file) {
 		_LOGW ("error creating dhclient configuration file");
 		return FALSE;
diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
index 40a3e072..5816932b 100644
--- a/src/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/dhcp/tests/test-dhcp-dhclient.c
@@ -40,6 +40,7 @@ test_config (const char *orig,
              const char *expected,
              gboolean ipv6,
              const char *hostname,
+             guint32 timeout,
              gboolean use_fqdn,
              const char *dhcp_client_id,
              GBytes *expected_new_client_id,
@@ -60,23 +61,23 @@ test_config (const char *orig,
 	                                      client_id,
 	                                      anycast_addr,
 	                                      hostname,
+	                                      timeout,
 	                                      use_fqdn,
 	                                      "/path/to/dhclient.conf",
 	                                      orig,
 	                                      &new_client_id);
 	g_assert (new != NULL);
 
-#if DEBUG
-	if (   strlen (new) != strlen (expected)
-	    || strcmp (new, expected)) {
-		g_message ("\n- NEW ---------------------------------\n"
+	if (!nm_streq (new, expected)) {
+		g_message ("\n* OLD ---------------------------------\n"
 		           "%s"
-		           "+ EXPECTED ++++++++++++++++++++++++++++++\n"
+		           "\n- NEW -----------------------------------\n"
 		           "%s"
-		           "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
-		           new, expected);
+		           "\n+ EXPECTED ++++++++++++++++++++++++++++++\n"
+		           "%s"
+		           "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
+		           orig, new, expected);
 	}
-#endif
 	g_assert_cmpstr (new, ==, expected);
 
 	if (expected_new_client_id) {
@@ -105,7 +106,7 @@ static const char *orig_missing_expected = \
 static void
 test_orig_missing (void)
 {
-	test_config (NULL, orig_missing_expected, FALSE, NULL, FALSE, NULL, NULL, "eth0", NULL);
+	test_config (NULL, orig_missing_expected, FALSE, NULL, 0, FALSE, NULL, NULL, "eth0", NULL);
 }
 
 /*****************************************************************************/
@@ -134,7 +135,7 @@ static void
 test_override_client_id (void)
 {
 	test_config (override_client_id_orig, override_client_id_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             "11:22:33:44:55:66",
 	             NULL,
 	             "eth0",
@@ -163,7 +164,7 @@ static void
 test_quote_client_id (void)
 {
 	test_config (NULL, quote_client_id_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             "1234",
 	             NULL,
 	             "eth0",
@@ -192,7 +193,7 @@ static void
 test_ascii_client_id (void)
 {
 	test_config (NULL, ascii_client_id_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             "qb:cd:ef:12:34:56",
 	             NULL,
 	             "eth0",
@@ -221,7 +222,7 @@ static void
 test_hex_single_client_id (void)
 {
 	test_config (NULL, hex_single_client_id_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             "ab:cd:e:12:34:56",
 	             NULL,
 	             "eth0",
@@ -258,7 +259,7 @@ test_existing_hex_client_id (void)
 
 	new_client_id = g_bytes_new (bytes, sizeof (bytes));
 	test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -298,7 +299,7 @@ test_existing_ascii_client_id (void)
 	memcpy (buf + 1, EACID, NM_STRLEN (EACID));
 	new_client_id = g_bytes_new (buf, sizeof (buf));
 	test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -327,7 +328,7 @@ static void
 test_fqdn (void)
 {
 	test_config (NULL, fqdn_expected,
-	             FALSE, "foo.bar.com",
+	             FALSE, "foo.bar.com", 0,
 	             TRUE, NULL,
 	             NULL,
 	             "eth0",
@@ -367,7 +368,7 @@ test_fqdn_options_override (void)
 {
 	test_config (fqdn_options_override_orig,
 	             fqdn_options_override_expected,
-	             FALSE, "example2.com",
+	             FALSE, "example2.com", 0,
 	             TRUE, NULL,
 	             NULL,
 	             "eth0",
@@ -400,7 +401,7 @@ static void
 test_override_hostname (void)
 {
 	test_config (override_hostname_orig, override_hostname_expected,
-	             FALSE, "blahblah", FALSE,
+	             FALSE, "blahblah", 0, FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -429,7 +430,7 @@ static void
 test_override_hostname6 (void)
 {
 	test_config (override_hostname6_orig, override_hostname6_expected,
-	             TRUE, "blahblah.local", TRUE,
+	             TRUE, "blahblah.local", 0, TRUE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -451,8 +452,8 @@ test_nonfqdn_hostname6 (void)
 {
 	/* Non-FQDN hostname can't be used with dhclient */
 	test_config (NULL, nonfqdn_hostname6_expected,
-	             TRUE, "blahblah",
-	             TRUE, NULL,
+	             TRUE, "blahblah", 0, TRUE,
+	             NULL,
 	             NULL,
 	             "eth0",
 	             NULL);
@@ -486,8 +487,7 @@ static void
 test_existing_alsoreq (void)
 {
 	test_config (existing_alsoreq_orig, existing_alsoreq_expected,
-	             FALSE, NULL,
-	             FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -525,8 +525,7 @@ static void
 test_existing_req (void)
 {
 	test_config (existing_req_orig, existing_req_expected,
-	             FALSE, NULL,
-	             FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -565,7 +564,7 @@ static void
 test_existing_multiline_alsoreq (void)
 {
 	test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -779,7 +778,7 @@ static void
 test_interface1 (void)
 {
 	test_config (interface1_orig, interface1_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -824,13 +823,67 @@ static void
 test_interface2 (void)
 {
 	test_config (interface2_orig, interface2_expected,
-	             FALSE, NULL, FALSE,
+	             FALSE, NULL, 0, FALSE,
 	             NULL,
 	             NULL,
 	             "eth1",
 	             NULL);
 }
 
+static void
+test_config_req_intf (void)
+{
+	static const char *const orig = \
+		"request subnet-mask, broadcast-address, routers,\n"
+		"	rfc3442-classless-static-routes,\n"
+		"	interface-mtu, host-name, domain-name, domain-search,\n"
+		"	domain-name-servers, nis-domain, nis-servers,\n"
+		"	nds-context, nds-servers, nds-tree-name,\n"
+		"	netbios-name-servers, netbios-dd-server,\n"
+		"	netbios-node-type, netbios-scope, ntp-servers;\n"
+		"";
+	static const char *const expected = \
+		"# Created by NetworkManager\n"
+		"# Merged from /path/to/dhclient.conf\n"
+		"\n"
+		"\n"
+		"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+		"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+		"option wpad code 252 = string;\n"
+		"\n"
+		"request; # override dhclient defaults\n"
+		"also request subnet-mask;\n"
+		"also request broadcast-address;\n"
+		"also request routers;\n"
+		"also request rfc3442-classless-static-routes;\n"
+		"also request interface-mtu;\n"
+		"also request host-name;\n"
+		"also request domain-name;\n"
+		"also request domain-search;\n"
+		"also request domain-name-servers;\n"
+		"also request nis-domain;\n"
+		"also request nis-servers;\n"
+		"also request nds-context;\n"
+		"also request nds-servers;\n"
+		"also request nds-tree-name;\n"
+		"also request netbios-name-servers;\n"
+		"also request netbios-dd-server;\n"
+		"also request netbios-node-type;\n"
+		"also request netbios-scope;\n"
+		"also request ntp-servers;\n"
+		"also request ms-classless-static-routes;\n"
+		"also request static-routes;\n"
+		"also request wpad;\n"
+		"\n";
+
+	test_config (orig, expected,
+	             FALSE, NULL, 0, FALSE,
+	             NULL,
+	             NULL,
+	             "eth0",
+	             NULL);
+}
+
 /*****************************************************************************/
 
 static void
@@ -982,6 +1035,7 @@ main (int argc, char **argv)
 	g_test_add_func ("/dhcp/dhclient/duids", test_duids);
 	g_test_add_func ("/dhcp/dhclient/interface/1", test_interface1);
 	g_test_add_func ("/dhcp/dhclient/interface/2", test_interface2);
+	g_test_add_func ("/dhcp/dhclient/config/req_intf", test_config_req_intf);
 
 	g_test_add_func ("/dhcp/dhclient/read_duid_from_leasefile", test_read_duid_from_leasefile);
 	g_test_add_func ("/dhcp/dhclient/read_commented_duid_from_leasefile", test_read_commented_duid_from_leasefile);
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index f443f340..952468e3 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -158,12 +158,12 @@ G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_EXPORTED_OBJECT)
 #define NM_DNS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsManager, NM_IS_DNS_MANAGER)
 
 static gboolean
-domain_is_valid (const gchar *domain)
+domain_is_valid (const gchar *domain, gboolean check_public_suffix)
 {
 	if (*domain == '\0')
 		return FALSE;
 #if WITH_LIBPSL
-	if (psl_is_public_suffix (psl_builtin (), domain))
+	if (check_public_suffix && psl_is_public_suffix (psl_builtin (), domain))
 		return FALSE;
 #endif
 	return TRUE;
@@ -312,7 +312,7 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
 		const char *search;
 
 		search = nm_ip4_config_get_search (src, i);
-		if (!domain_is_valid (search))
+		if (!domain_is_valid (search, FALSE))
 			continue;
 		add_string_item (rc->searches, search);
 	}
@@ -322,7 +322,7 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
 			const char *domain;
 
 			domain = nm_ip4_config_get_domain (src, i);
-			if (!domain_is_valid (domain))
+			if (!domain_is_valid (domain, FALSE))
 				continue;
 			add_string_item (rc->searches, domain);
 		}
@@ -382,7 +382,7 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src, const char *iface)
 		const char *search;
 
 		search = nm_ip6_config_get_search (src, i);
-		if (!domain_is_valid (search))
+		if (!domain_is_valid (search, FALSE))
 			continue;
 		add_string_item (rc->searches, search);
 	}
@@ -392,7 +392,7 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src, const char *iface)
 			const char *domain;
 
 			domain = nm_ip6_config_get_domain (src, i);
-			if (!domain_is_valid (domain))
+			if (!domain_is_valid (domain, FALSE))
 				continue;
 			add_string_item (rc->searches, domain);
 		}
@@ -923,7 +923,7 @@ merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
 	options = nm_global_dns_config_get_options (global_conf);
 
 	for (i = 0; searches && searches[i]; i++) {
-		if (domain_is_valid (searches[i]))
+		if (domain_is_valid (searches[i], FALSE))
 			add_string_item (rc->searches, searches[i]);
 	}
 
@@ -1055,9 +1055,9 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o
 		if (   hostdomain
 		    && !nm_utils_ipaddr_valid (AF_UNSPEC, hostname)) {
 			hostdomain++;
-			if (domain_is_valid (hostdomain))
+			if (domain_is_valid (hostdomain, TRUE))
 				add_string_item (rc.searches, hostdomain);
-			else if (domain_is_valid (hostname))
+			else if (domain_is_valid (hostname, TRUE))
 				add_string_item (rc.searches, hostname);
 		}
 	}
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index d59bc574..04f00e89 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -46,7 +46,7 @@ typedef struct {
 	guint64 ac_version_id;
 	NMDeviceState state;
 	bool realized:1;
-	bool unmanaged_explicit:1;
+	NMUnmanFlagOp unmanaged_explicit;
 } DeviceCheckpoint;
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
@@ -224,9 +224,19 @@ nm_checkpoint_rollback (NMCheckpoint *self)
 		}
 
 activate:
+		/* Manage the device again if needed */
+		if (   nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT)
+		    && dev_checkpoint->unmanaged_explicit != NM_UNMAN_FLAG_OP_SET_UNMANAGED) {
+			_LOGD ("rollback: restore unmanaged user-explicit");
+			nm_device_set_unmanaged_by_flags_queue (device,
+			                                        NM_UNMANAGED_USER_EXPLICIT,
+			                                        dev_checkpoint->unmanaged_explicit,
+			                                        NM_DEVICE_STATE_REASON_NOW_MANAGED);
+		}
+
 		if (dev_checkpoint->state == NM_DEVICE_STATE_UNMANAGED) {
 			if (   nm_device_get_state (device) != NM_DEVICE_STATE_UNMANAGED
-			    || dev_checkpoint->unmanaged_explicit) {
+			    || dev_checkpoint->unmanaged_explicit == NM_UNMAN_FLAG_OP_SET_UNMANAGED) {
 				_LOGD ("rollback: explicitly unmanage device");
 				nm_device_set_unmanaged_by_flags_queue (device,
 				                                        NM_UNMANAGED_USER_EXPLICIT,
@@ -370,19 +380,21 @@ device_checkpoint_create (NMDevice *device,
 	NMConnection *applied_connection;
 	NMSettingsConnection *settings_connection;
 	const char *path;
-	gboolean unmanaged_explicit;
 	NMActRequest *act_request;
 
 	path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
-	unmanaged_explicit = !!nm_device_get_unmanaged_flags (device,
-	                                                      NM_UNMANAGED_USER_EXPLICIT);
 
 	dev_checkpoint = g_slice_new0 (DeviceCheckpoint);
 	dev_checkpoint->device = g_object_ref (device);
 	dev_checkpoint->original_dev_path = g_strdup (path);
 	dev_checkpoint->state = nm_device_get_state (device);
 	dev_checkpoint->realized = nm_device_is_real (device);
-	dev_checkpoint->unmanaged_explicit = unmanaged_explicit;
+
+	if (nm_device_get_unmanaged_mask (device, NM_UNMANAGED_USER_EXPLICIT)) {
+		dev_checkpoint->unmanaged_explicit =
+			!!nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT);
+	} else
+		dev_checkpoint->unmanaged_explicit = NM_UNMAN_FLAG_OP_FORGET;
 
 	applied_connection = nm_device_get_applied_connection (device);
 	if (applied_connection) {
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index 6f16b28e..b895a82b 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -124,6 +124,7 @@ finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state)
 	g_object_unref (cb_data->simple);
 	curl_slist_free_all (cb_data->request_headers);
 	g_free (cb_data->response);
+	g_free (cb_data->ifspec);
 	g_source_remove (cb_data->timeout_id);
 	g_slice_free (ConCheckCbData, cb_data);
 }
diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c
index 9ac6d552..1ab8f02d 100644
--- a/src/nm-default-route-manager.c
+++ b/src/nm-default-route-manager.c
@@ -28,6 +28,7 @@
 #include "devices/nm-device.h"
 #include "vpn/nm-vpn-connection.h"
 #include "platform/nm-platform.h"
+#include "platform/nm-platform-utils.h"
 #include "nm-manager.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -266,6 +267,8 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
 {
 	NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
 	GPtrArray *entries = vtable->get_entries (priv);
+	char buf1[sizeof (_nm_utils_to_string_buffer)];
+	char buf2[sizeof (_nm_utils_to_string_buffer)];
 	guint i;
 	Entry *entry_unsynced = NULL;
 	Entry *entry = NULL;
@@ -304,21 +307,64 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
 
 	if (vtable->vt->is_ip4) {
 		NMPlatformIP4Route rt = entry->route.r4;
+		const NMPlatformIP4Route *plat_rt;
 
 		rt.network = 0;
 		rt.plen = 0;
 		rt.metric = entry->effective_metric;
+		rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source);
+
+		plat_rt = nm_platform_ip4_route_get (priv->platform,
+		                                     entry->route.r4.ifindex,
+		                                     0,
+		                                     0,
+		                                     entry->effective_metric);
+		if (plat_rt && nm_platform_ip4_route_cmp (plat_rt, &rt) == 0) {
+			_LOGt (AF_INET, "already exists: %s",
+			       nm_platform_ip4_route_to_string (&rt, NULL, 0));
+			return FALSE;
+		}
+
+		rt.rt_source = entry->route.r4.rt_source;
+
+		if (plat_rt) {
+			_LOGt (AF_INET, "update platform route: %s; with route: %s",
+			       nm_platform_ip4_route_to_string (plat_rt, buf1, sizeof (buf1)),
+			       nm_platform_ip4_route_to_string (&rt, buf2, sizeof (buf2)));
+		}
 
 		success = nm_platform_ip4_route_add (priv->platform, &rt);
 	} else {
 		NMPlatformIP6Route rt = entry->route.r6;
+		const NMPlatformIP6Route *plat_rt;
 
 		rt.network = in6addr_any;
 		rt.plen = 0;
 		rt.metric = entry->effective_metric;
+		rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source);
+
+		plat_rt = nm_platform_ip6_route_get (priv->platform,
+		                                     entry->route.r6.ifindex,
+		                                     in6addr_any,
+		                                     0,
+		                                     entry->effective_metric);
+		if (plat_rt && nm_platform_ip6_route_cmp (plat_rt, &rt) == 0) {
+			_LOGt (AF_INET6, "already exists: %s",
+			       nm_platform_ip6_route_to_string (&rt, NULL, 0));
+			return FALSE;
+		}
+
+		rt.rt_source = entry->route.r6.rt_source;
+
+		if (plat_rt) {
+			_LOGt (AF_INET, "update platform route: %s; with route: %s",
+			       nm_platform_ip6_route_to_string (plat_rt, buf1, sizeof (buf1)),
+			       nm_platform_ip6_route_to_string (&rt, buf2, sizeof (buf2)));
+		}
 
 		success = nm_platform_ip6_route_add (priv->platform, &rt);
 	}
+
 	if (!success) {
 		_LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u",
 		       vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric);
diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c
index b58cdeb0..f293f130 100644
--- a/src/nm-route-manager.c
+++ b/src/nm-route-manager.c
@@ -25,6 +25,7 @@
 #include <string.h>
 
 #include "platform/nm-platform.h"
+#include "platform/nm-platform-utils.h"
 #include "platform/nmp-object.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
@@ -364,19 +365,20 @@ _route_index_reverse_idx (const VTableIP *vtable, const RouteIndex *index, guint
 /*****************************************************************************/
 
 static gboolean
-_route_equals_ignoring_ifindex (const VTableIP *vtable, const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, gint64 r2_metric)
+_route_equals_ignoring_ifindex (const VTableIP *vtable,
+                                const NMPlatformIPXRoute *plat_rt,
+                                const NMPlatformIPXRoute *rt,
+                                gint64 rt_metric)
 {
-	NMPlatformIPXRoute r2_backup;
-
-	if (   r1->rx.ifindex != r2->rx.ifindex
-	    || (r2_metric >= 0 && ((guint32) r2_metric) != r2->rx.metric)) {
-		memcpy (&r2_backup, r2, vtable->vt->sizeof_route);
-		r2_backup.rx.ifindex = r1->rx.ifindex;
-		if (r2_metric >= 0)
-			r2_backup.rx.metric = (guint32) r2_metric;
-		r2 = &r2_backup;
-	}
-	return vtable->vt->route_cmp (r1, r2, FALSE) == 0;
+	NMPlatformIPXRoute rt_backup;
+
+	memcpy (&rt_backup, rt, vtable->vt->sizeof_route);
+	rt_backup.rx.ifindex = plat_rt->rx.ifindex;
+	if (rt_metric >= 0)
+		rt_backup.rx.metric = (guint32) rt_metric;
+	rt_backup.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt_backup.rx.rt_source);
+
+	return vtable->vt->route_cmp (plat_rt, &rt_backup, FALSE) == 0;
 }
 
 static NMPlatformIPXRoute *
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 487725e4..6b84c185 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -4230,7 +4230,7 @@ out:
 	/* such an object still exists in the cache. To be sure, refetch it (and
 	 * hope it's gone) */
 	do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id));
-	return !!nmp_cache_lookup_obj (priv->cache, obj_id);
+	return !nmp_cache_lookup_obj (priv->cache, obj_id);
 }
 
 static WaitForNlResponseResult
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 6862f13e..9960d867 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -421,7 +421,8 @@ test_ip4_route_options (void)
 	nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE);
 
 	/* Remove route */
-	g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, 24, 20));
+	/* FIXME. Due to a bug, we cannot delete routes with non-zero TOS. See bgo#785004. */
+	//g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, 24, 20));
 
 	g_array_unref (routes);
 }