Blame SOURCES/0009-distinct-route-metric-rh1505893.patch

c48088
From e7d51475a855f29f124b12e1de8709c2addb8be2 Mon Sep 17 00:00:00 2001
c48088
From: Thomas Haller <thaller@redhat.com>
c48088
Date: Wed, 6 Dec 2017 13:16:30 +0100
c48088
Subject: [PATCH 1/7] device: expose nm_device_get_route_metric_default()
c48088
c48088
(cherry picked from commit 989b5fabaac95f9367fb5f1c730db5dca7eab0de)
c48088
(cherry picked from commit ea78f156f2ef3402a5c4dde9c395cbf720b7aa4c)
c48088
---
c48088
 src/devices/nm-device.c | 8 ++++----
c48088
 src/devices/nm-device.h | 2 ++
c48088
 2 files changed, 6 insertions(+), 4 deletions(-)
c48088
c48088
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
c48088
index f75cc86e3..8179dbd3b 100644
c48088
--- a/src/devices/nm-device.c
c48088
+++ b/src/devices/nm-device.c
c48088
@@ -1616,8 +1616,8 @@ nm_device_get_metered (NMDevice *self)
c48088
 	return NM_DEVICE_GET_PRIVATE (self)->metered;
c48088
 }
c48088
 
c48088
-static guint32
c48088
-_get_route_metric_default (NMDevice *self)
c48088
+guint32
c48088
+nm_device_get_route_metric_default (NMDeviceType device_type)
c48088
 {
c48088
 	/* Device 'priority' is used for the default route-metric and is based on
c48088
 	 * the device type. The settings ipv4.route-metric and ipv6.route-metric
c48088
@@ -1636,7 +1636,7 @@ _get_route_metric_default (NMDevice *self)
c48088
 	 * metrics (except for IPv6, where 0 means 1024).
c48088
 	 */
c48088
 
c48088
-	switch (nm_device_get_device_type (self)) {
c48088
+	switch (device_type) {
c48088
 	/* 50 is reserved for VPN (NM_VPN_ROUTE_METRIC_DEFAULT) */
c48088
 	case NM_DEVICE_TYPE_ETHERNET:
c48088
 	case NM_DEVICE_TYPE_VETH:
c48088
@@ -1765,7 +1765,7 @@ nm_device_get_route_metric (NMDevice *self,
c48088
 		if (route_metric >= 0)
c48088
 			goto out;
c48088
 	}
c48088
-	route_metric = _get_route_metric_default (self);
c48088
+	route_metric = nm_device_get_route_metric_default (nm_device_get_device_type (self));
c48088
 out:
c48088
 	return nm_utils_ip_route_metric_normalize (addr_family, route_metric);
c48088
 }
c48088
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
c48088
index 810a613dd..ac73ee0c4 100644
c48088
--- a/src/devices/nm-device.h
c48088
+++ b/src/devices/nm-device.h
c48088
@@ -450,6 +450,8 @@ NMMetered       nm_device_get_metered           (NMDevice *dev);
c48088
 guint32         nm_device_get_route_table       (NMDevice *self, int addr_family, gboolean fallback_main);
c48088
 guint32         nm_device_get_route_metric      (NMDevice *dev, int addr_family);
c48088
 
c48088
+guint32         nm_device_get_route_metric_default (NMDeviceType device_type);
c48088
+
c48088
 const char *    nm_device_get_hw_address        (NMDevice *dev);
c48088
 const char *    nm_device_get_permanent_hw_address (NMDevice *self);
c48088
 const char *    nm_device_get_permanent_hw_address_full (NMDevice *self,
c48088
-- 
c48088
2.14.3
c48088
c48088
c48088
From 009ce5a739ec760acc38e8265776570e5d1a6a34 Mon Sep 17 00:00:00 2001
c48088
From: Thomas Haller <thaller@redhat.com>
c48088
Date: Wed, 6 Dec 2017 12:49:21 +0100
c48088
Subject: [PATCH 2/7] core: add nm_config_keyfile_get_int64() util
c48088
c48088
(cherry picked from commit 3f38b765158c2ffcec7d1b314c3ba6aea3bc3e7b)
c48088
(cherry picked from commit 42fbc9410ba5c131e84c21d073d3d818feb2666d)
c48088
---
c48088
 src/nm-config.c | 27 +++++++++++++++++++++++++++
c48088
 src/nm-config.h |  7 +++++++
c48088
 2 files changed, 34 insertions(+)
c48088
c48088
diff --git a/src/nm-config.c b/src/nm-config.c
c48088
index de727c988..c344d5cd1 100644
c48088
--- a/src/nm-config.c
c48088
+++ b/src/nm-config.c
c48088
@@ -182,6 +182,33 @@ nm_config_keyfile_get_boolean (const GKeyFile *keyfile,
c48088
 	return nm_config_parse_boolean (str, default_value);
c48088
 }
c48088
 
c48088
+gint64
c48088
+nm_config_keyfile_get_int64 (const GKeyFile *keyfile,
c48088
+                             const char *section,
c48088
+                             const char *key,
c48088
+                             guint base,
c48088
+                             gint64 min,
c48088
+                             gint64 max,
c48088
+                             gint64 fallback)
c48088
+{
c48088
+	gint64 v;
c48088
+	int errsv;
c48088
+	char *str;
c48088
+
c48088
+	g_return_val_if_fail (keyfile, fallback);
c48088
+	g_return_val_if_fail (section, fallback);
c48088
+	g_return_val_if_fail (key, fallback);
c48088
+
c48088
+	str = g_key_file_get_value ((GKeyFile *) keyfile, section, key, NULL);
c48088
+	v = _nm_utils_ascii_str_to_int64 (str, base, min, max, fallback);
c48088
+	if (str) {
c48088
+		errsv = errno;
c48088
+		g_free (str);
c48088
+		errno = errsv;
c48088
+	}
c48088
+	return v;
c48088
+}
c48088
+
c48088
 char *
c48088
 nm_config_keyfile_get_value (const GKeyFile *keyfile,
c48088
                              const char *section,
c48088
diff --git a/src/nm-config.h b/src/nm-config.h
c48088
index 8bdd5002d..5b2dc65c4 100644
c48088
--- a/src/nm-config.h
c48088
+++ b/src/nm-config.h
c48088
@@ -165,6 +165,13 @@ gint nm_config_keyfile_get_boolean (const GKeyFile *keyfile,
c48088
                                     const char *section,
c48088
                                     const char *key,
c48088
                                     gint default_value);
c48088
+gint64 nm_config_keyfile_get_int64 (const GKeyFile *keyfile,
c48088
+                                    const char *section,
c48088
+                                    const char *key,
c48088
+                                    guint base,
c48088
+                                    gint64 min,
c48088
+                                    gint64 max,
c48088
+                                    gint64 fallback);
c48088
 char *nm_config_keyfile_get_value (const GKeyFile *keyfile,
c48088
                                    const char *section,
c48088
                                    const char *key,
c48088
-- 
c48088
2.14.3
c48088
c48088
c48088
From 412200a581b00c76757afb2250c3a9e3e719ca6b Mon Sep 17 00:00:00 2001
c48088
From: Thomas Haller <thaller@redhat.com>
c48088
Date: Wed, 6 Dec 2017 13:09:31 +0100
c48088
Subject: [PATCH 3/7] core: cache device state in NMConfig and load all at once
c48088
c48088
NMManager will need to know the state of all device at once.
c48088
Hence, load it once and cache it in NMConfig.
c48088
c48088
Note that this wastes a bit of memory in the order of
c48088
O(number-of-interfaces). But each device state entry is
c48088
rather small, and we always consume memory in the order
c48088
of O(number-of-interfaces).
c48088
c48088
(cherry picked from commit ea08df925f6a01e30ddcea4c15cea98d532593c6)
c48088
(cherry picked from commit 7b899334068c6945b0604b37510d91b02cbe62d9)
c48088
---
c48088
 src/devices/nm-device.c |  31 ++++----
c48088
 src/nm-config.c         | 186 +++++++++++++++++++++++++++++++++++-------------
c48088
 src/nm-config.h         |   5 ++
c48088
 src/nm-manager.c        |   5 +-
c48088
 4 files changed, 156 insertions(+), 71 deletions(-)
c48088
c48088
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
c48088
index 8179dbd3b..755589674 100644
c48088
--- a/src/devices/nm-device.c
c48088
+++ b/src/devices/nm-device.c
c48088
@@ -13512,6 +13512,7 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
c48088
 	gboolean success_read;
c48088
 	int ifindex;
c48088
 	const NMPlatformLink *pllink;
c48088
+	const NMConfigDeviceStateData *dev_state;
c48088
 
c48088
 	if (priv->hw_addr_perm) {
c48088
 		/* the permanent hardware address is only read once and not
c48088
@@ -13571,23 +13572,19 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
c48088
 	/* We also persist our choice of the fake address to the device state
c48088
 	 * file to use the same address on restart of NetworkManager.
c48088
 	 * First, try to reload the address from the state file. */
c48088
-	{
c48088
-		gs_free NMConfigDeviceStateData *dev_state = NULL;
c48088
-
c48088
-		dev_state = nm_config_device_state_load (ifindex);
c48088
-		if (   dev_state
c48088
-		    && dev_state->perm_hw_addr_fake
c48088
-		    && nm_utils_hwaddr_aton (dev_state->perm_hw_addr_fake, buf, priv->hw_addr_len)
c48088
-		    && !nm_utils_hwaddr_matches (buf, priv->hw_addr_len, priv->hw_addr, -1)) {
c48088
-			_LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use from statefile: %s, current: %s)",
c48088
-			       success_read
c48088
-			           ? "read HW addr length of permanent MAC address differs"
c48088
-			           : "unable to read permanent MAC address",
c48088
-			       dev_state->perm_hw_addr_fake,
c48088
-			       priv->hw_addr);
c48088
-			priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
c48088
-			goto notify_and_out;
c48088
-		}
c48088
+	dev_state = nm_config_device_state_get (nm_config_get (), ifindex);
c48088
+	if (   dev_state
c48088
+	    && dev_state->perm_hw_addr_fake
c48088
+	    && nm_utils_hwaddr_aton (dev_state->perm_hw_addr_fake, buf, priv->hw_addr_len)
c48088
+	    && !nm_utils_hwaddr_matches (buf, priv->hw_addr_len, priv->hw_addr, -1)) {
c48088
+		_LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use from statefile: %s, current: %s)",
c48088
+		       success_read
c48088
+		           ? "read HW addr length of permanent MAC address differs"
c48088
+		           : "unable to read permanent MAC address",
c48088
+		       dev_state->perm_hw_addr_fake,
c48088
+		       priv->hw_addr);
c48088
+		priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
c48088
+		goto notify_and_out;
c48088
 	}
c48088
 
c48088
 	_LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use current: %s)",
c48088
diff --git a/src/nm-config.c b/src/nm-config.c
c48088
index c344d5cd1..771d74f2a 100644
c48088
--- a/src/nm-config.c
c48088
+++ b/src/nm-config.c
c48088
@@ -121,6 +121,14 @@ typedef struct {
c48088
 	 * because the state changes only on explicit actions from the daemon
c48088
 	 * itself. */
c48088
 	State *state;
c48088
+
c48088
+	/* the hash table of device states. It is only loaded from disk
c48088
+	 * once and kept immutable afterwards.
c48088
+	 *
c48088
+	 * We also read all state file at once. We don't want to support
c48088
+	 * that they are changed outside of NM (at least not while NM is running).
c48088
+	 * Hence, we read them once, that's it. */
c48088
+	GHashTable *device_states;
c48088
 } NMConfigPrivate;
c48088
 
c48088
 struct _NMConfig {
c48088
@@ -1945,46 +1953,45 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
c48088
 	gint nm_owned = -1;
c48088
 	char *p;
c48088
 
c48088
+	nm_assert (kf);
c48088
 	nm_assert (ifindex > 0);
c48088
 
c48088
-	if (kf) {
c48088
-		switch (nm_config_keyfile_get_boolean (kf,
c48088
-		                                       DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
-		                                       DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
c48088
-		                                       -1)) {
c48088
-		case TRUE:
c48088
-			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
c48088
-			connection_uuid = nm_config_keyfile_get_value (kf,
c48088
-			                                               DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
-			                                               DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID,
c48088
-			                                               NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
c48088
-			break;
c48088
-		case FALSE:
c48088
-			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
c48088
-			break;
c48088
-		case -1:
c48088
-			/* missing property in keyfile. */
c48088
-			break;
c48088
-		}
c48088
-
c48088
-		perm_hw_addr_fake = nm_config_keyfile_get_value (kf,
c48088
-		                                                 DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
-		                                                 DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE,
c48088
-		                                                 NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
c48088
-		if (perm_hw_addr_fake) {
c48088
-			char *normalized;
c48088
+	switch (nm_config_keyfile_get_boolean (kf,
c48088
+	                                       DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+	                                       DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
c48088
+	                                       -1)) {
c48088
+	case TRUE:
c48088
+		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
c48088
+		connection_uuid = nm_config_keyfile_get_value (kf,
c48088
+		                                               DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+		                                               DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID,
c48088
+		                                               NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
c48088
+		break;
c48088
+	case FALSE:
c48088
+		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
c48088
+		break;
c48088
+	case -1:
c48088
+		/* missing property in keyfile. */
c48088
+		break;
c48088
+	}
c48088
 
c48088
-			normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1);
c48088
-			g_free (perm_hw_addr_fake);
c48088
-			perm_hw_addr_fake = normalized;
c48088
-		}
c48088
+	perm_hw_addr_fake = nm_config_keyfile_get_value (kf,
c48088
+	                                                 DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+	                                                 DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE,
c48088
+	                                                 NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
c48088
+	if (perm_hw_addr_fake) {
c48088
+		char *normalized;
c48088
 
c48088
-		nm_owned = nm_config_keyfile_get_boolean (kf,
c48088
-		                                          DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
-		                                          DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED,
c48088
-		                                          -1);
c48088
+		normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1);
c48088
+		g_free (perm_hw_addr_fake);
c48088
+		perm_hw_addr_fake = normalized;
c48088
 	}
c48088
 
c48088
+	nm_owned = nm_config_keyfile_get_boolean (kf,
c48088
+	                                          DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+	                                          DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED,
c48088
+	                                          -1);
c48088
+
c48088
 	connection_uuid_len = connection_uuid ? strlen (connection_uuid) + 1 : 0;
c48088
 	perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen (perm_hw_addr_fake) + 1 : 0;
c48088
 
c48088
@@ -2034,14 +2041,13 @@ nm_config_device_state_load (int ifindex)
c48088
 
c48088
 	kf = nm_config_create_keyfile ();
c48088
 	if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
c48088
-		g_clear_pointer (&kf, g_key_file_unref);
c48088
+		return NULL;
c48088
 
c48088
 	device_state = _config_device_state_data_new (ifindex, kf);
c48088
 	nm_owned_str = device_state->nm_owned == TRUE ?
c48088
 	               ", nm-owned=1" :
c48088
 	               (device_state->nm_owned == FALSE ? ", nm-owned=0" : "");
c48088
 
c48088
-
c48088
 	_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s",
c48088
 	       kf ? "read" : "miss",
c48088
 	       ifindex, path,
c48088
@@ -2053,6 +2059,49 @@ nm_config_device_state_load (int ifindex)
c48088
 	return device_state;
c48088
 }
c48088
 
c48088
+static int
c48088
+_device_state_parse_filename (const char *filename)
c48088
+{
c48088
+	if (!filename || !filename[0])
c48088
+		return 0;
c48088
+	if (!NM_STRCHAR_ALL (filename, ch, g_ascii_isdigit (ch)))
c48088
+		return 0;
c48088
+	return _nm_utils_ascii_str_to_int64 (filename, 10, 1, G_MAXINT, 0);
c48088
+}
c48088
+
c48088
+GHashTable *
c48088
+nm_config_device_state_load_all (void)
c48088
+{
c48088
+	GHashTable *states;
c48088
+	GDir *dir;
c48088
+	const char *fn;
c48088
+	int ifindex;
c48088
+
c48088
+	states = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_free);
c48088
+
c48088
+	dir = g_dir_open (NM_CONFIG_DEVICE_STATE_DIR, 0, NULL);
c48088
+	if (!dir)
c48088
+		return states;
c48088
+
c48088
+	while ((fn = g_dir_read_name (dir))) {
c48088
+		NMConfigDeviceStateData *state;
c48088
+
c48088
+		ifindex = _device_state_parse_filename (fn);
c48088
+		if (ifindex <= 0)
c48088
+			continue;
c48088
+
c48088
+		state = nm_config_device_state_load (ifindex);
c48088
+		if (!state)
c48088
+			continue;
c48088
+
c48088
+		if (!nm_g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state))
c48088
+			nm_assert_not_reached ();
c48088
+	}
c48088
+	g_dir_close (dir);
c48088
+
c48088
+	return states;
c48088
+}
c48088
+
c48088
 gboolean
c48088
 nm_config_device_state_write (int ifindex,
c48088
                               NMConfigDeviceStateManagedType managed,
c48088
@@ -2121,7 +2170,6 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
c48088
 	const char *fn;
c48088
 	int ifindex;
c48088
 	gsize fn_len;
c48088
-	gsize i;
c48088
 	char buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/") + 30 + 3] = NM_CONFIG_DEVICE_STATE_DIR"/";
c48088
 	char *buf_p = &buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/")];
c48088
 
c48088
@@ -2132,24 +2180,20 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
c48088
 		return;
c48088
 
c48088
 	while ((fn = g_dir_read_name (dir))) {
c48088
-		fn_len = strlen (fn);
c48088
-
c48088
-		/* skip over file names that are not plain integers. */
c48088
-		for (i = 0; i < fn_len; i++) {
c48088
-			if (!g_ascii_isdigit (fn[i]))
c48088
-				break;
c48088
-		}
c48088
-		if (fn_len == 0 || i != fn_len)
c48088
+		ifindex = _device_state_parse_filename (fn);
c48088
+		if (ifindex <= 0)
c48088
 			continue;
c48088
-
c48088
-		ifindex = _nm_utils_ascii_str_to_int64 (fn, 10, 1, G_MAXINT, 0);
c48088
-		if (!ifindex)
c48088
-			continue;
c48088
-
c48088
 		if (g_hash_table_contains (seen_ifindexes, GINT_TO_POINTER (ifindex)))
c48088
 			continue;
c48088
 
c48088
-		memcpy (buf_p, fn, fn_len + 1);
c48088
+		fn_len = strlen (fn) + 1;
c48088
+		nm_assert (&buf_p[fn_len] < &buf[G_N_ELEMENTS (buf)]);
c48088
+		memcpy (buf_p, fn, fn_len);
c48088
+		nm_assert (({
c48088
+		                char bb[30];
c48088
+		                nm_sprintf_buf (bb, "%d", ifindex);
c48088
+		                nm_streq0 (bb, buf_p);
c48088
+		           }));
c48088
 		_LOGT ("device-state: prune #%d (%s)", ifindex, buf);
c48088
 		(void) unlink (buf);
c48088
 	}
c48088
@@ -2159,6 +2203,46 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
c48088
 
c48088
 /*****************************************************************************/
c48088
 
c48088
+static GHashTable *
c48088
+_device_state_get_all (NMConfig *self)
c48088
+{
c48088
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
c48088
+
c48088
+	if (G_UNLIKELY (!priv->device_states))
c48088
+		priv->device_states = nm_config_device_state_load_all ();
c48088
+	return priv->device_states;
c48088
+}
c48088
+
c48088
+/**
c48088
+ * nm_config_device_state_get_all:
c48088
+ * @self: the #NMConfig
c48088
+ *
c48088
+ * This function exists to give convenient access to all
c48088
+ * device states. Do not ever try to modify the returned
c48088
+ * hash, it's supposed to be immutable.
c48088
+ *
c48088
+ * Returns: the internal #GHashTable object with all device states.
c48088
+ */
c48088
+const GHashTable *
c48088
+nm_config_device_state_get_all (NMConfig *self)
c48088
+{
c48088
+	g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
c48088
+
c48088
+	return _device_state_get_all (self);
c48088
+}
c48088
+
c48088
+const NMConfigDeviceStateData *
c48088
+nm_config_device_state_get (NMConfig *self,
c48088
+                            int ifindex)
c48088
+{
c48088
+	g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
c48088
+	g_return_val_if_fail (ifindex > 0 , NULL);
c48088
+
c48088
+	return g_hash_table_lookup (_device_state_get_all (self), GINT_TO_POINTER (ifindex));
c48088
+}
c48088
+
c48088
+/*****************************************************************************/
c48088
+
c48088
 void
c48088
 nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags)
c48088
 {
c48088
diff --git a/src/nm-config.h b/src/nm-config.h
c48088
index 5b2dc65c4..52a4099b2 100644
c48088
--- a/src/nm-config.h
c48088
+++ b/src/nm-config.h
c48088
@@ -224,6 +224,7 @@ struct _NMConfigDeviceStateData {
c48088
 };
c48088
 
c48088
 NMConfigDeviceStateData *nm_config_device_state_load (int ifindex);
c48088
+GHashTable *nm_config_device_state_load_all (void);
c48088
 gboolean nm_config_device_state_write (int ifindex,
c48088
                                        NMConfigDeviceStateManagedType managed,
c48088
                                        const char *perm_hw_addr_fake,
c48088
@@ -231,6 +232,10 @@ gboolean nm_config_device_state_write (int ifindex,
c48088
                                        gint nm_owned);
c48088
 void nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes);
c48088
 
c48088
+const GHashTable *nm_config_device_state_get_all (NMConfig *self);
c48088
+const NMConfigDeviceStateData *nm_config_device_state_get (NMConfig *self,
c48088
+                                                           int ifindex);
c48088
+
c48088
 /*****************************************************************************/
c48088
 
c48088
 #endif /* __NETWORKMANAGER_CONFIG_H__ */
c48088
diff --git a/src/nm-manager.c b/src/nm-manager.c
c48088
index 7f1b9a9d9..9e5f7ad4c 100644
c48088
--- a/src/nm-manager.c
c48088
+++ b/src/nm-manager.c
c48088
@@ -2640,10 +2640,9 @@ platform_query_devices (NMManager *self)
c48088
 		return;
c48088
 	for (i = 0; i < links->len; i++) {
c48088
 		const NMPlatformLink *link = NMP_OBJECT_CAST_LINK (links->pdata[i]);
c48088
-		gs_free NMConfigDeviceStateData *dev_state = NULL;
c48088
-
c48088
-		dev_state = nm_config_device_state_load (link->ifindex);
c48088
+		const NMConfigDeviceStateData *dev_state;
c48088
 
c48088
+		dev_state = nm_config_device_state_get (priv->config, link->ifindex);
c48088
 		platform_link_added (self,
c48088
 		                     link->ifindex,
c48088
 		                     link,
c48088
-- 
c48088
2.14.3
c48088
c48088
c48088
From e6d73154f23bdd6e0c4e9b52a9ea64bd3c1d8a13 Mon Sep 17 00:00:00 2001
c48088
From: Thomas Haller <thaller@redhat.com>
c48088
Date: Wed, 6 Dec 2017 15:51:18 +0100
c48088
Subject: [PATCH 4/7] core: add read/write support for route-metric to
c48088
 NMConfig's device state
c48088
c48088
(cherry picked from commit a90b523a3e09f68d5700e73981ba84d40e4682a5)
c48088
(cherry picked from commit 282ed0d17501fda8b8c30020c029eb86f364e8bc)
c48088
---
c48088
 src/nm-config.c  | 29 ++++++++++++++++++++++++-----
c48088
 src/nm-config.h  |  9 +++++++--
c48088
 src/nm-manager.c |  6 +++++-
c48088
 3 files changed, 36 insertions(+), 8 deletions(-)
c48088
c48088
diff --git a/src/nm-config.c b/src/nm-config.c
c48088
index 771d74f2a..97ae3f6f4 100644
c48088
--- a/src/nm-config.c
c48088
+++ b/src/nm-config.c
c48088
@@ -1933,6 +1933,7 @@ _nm_config_state_set (NMConfig *self,
c48088
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE   "perm-hw-addr-fake"
c48088
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID     "connection-uuid"
c48088
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED            "nm-owned"
c48088
+#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT "route-metric-default"
c48088
 
c48088
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType,
c48088
 	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
c48088
@@ -1952,6 +1953,7 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
c48088
 	gsize perm_hw_addr_fake_len;
c48088
 	gint nm_owned = -1;
c48088
 	char *p;
c48088
+	guint32 route_metric_default;
c48088
 
c48088
 	nm_assert (kf);
c48088
 	nm_assert (ifindex > 0);
c48088
@@ -1992,6 +1994,13 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
c48088
 	                                          DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED,
c48088
 	                                          -1);
c48088
 
c48088
+	/* metric zero is not a valid metric. While zero valid for IPv4, for IPv6 it is an alias
c48088
+	 * for 1024. Since we handle here IPv4 and IPv6 the same, we cannot allow zero. */
c48088
+	route_metric_default = nm_config_keyfile_get_int64 (kf,
c48088
+	                                                    DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+	                                                    DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT,
c48088
+	                                                    10, 1, G_MAXUINT32, 0);
c48088
+
c48088
 	connection_uuid_len = connection_uuid ? strlen (connection_uuid) + 1 : 0;
c48088
 	perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen (perm_hw_addr_fake) + 1 : 0;
c48088
 
c48088
@@ -2004,6 +2013,7 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
c48088
 	device_state->connection_uuid = NULL;
c48088
 	device_state->perm_hw_addr_fake = NULL;
c48088
 	device_state->nm_owned = nm_owned;
c48088
+	device_state->route_metric_default = route_metric_default;
c48088
 
c48088
 	p = (char *) (&device_state[1]);
c48088
 	if (connection_uuid) {
c48088
@@ -2048,13 +2058,14 @@ nm_config_device_state_load (int ifindex)
c48088
 	               ", nm-owned=1" :
c48088
 	               (device_state->nm_owned == FALSE ? ", nm-owned=0" : "");
c48088
 
c48088
-	_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s",
c48088
+	_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT,
c48088
 	       kf ? "read" : "miss",
c48088
 	       ifindex, path,
c48088
 	       _device_state_managed_type_to_str (device_state->managed),
c48088
 	       NM_PRINT_FMT_QUOTED (device_state->connection_uuid, ", connection-uuid=", device_state->connection_uuid, "", ""),
c48088
 	       NM_PRINT_FMT_QUOTED (device_state->perm_hw_addr_fake, ", perm-hw-addr-fake=", device_state->perm_hw_addr_fake, "", ""),
c48088
-	       nm_owned_str);
c48088
+	       nm_owned_str,
c48088
+	       device_state->route_metric_default);
c48088
 
c48088
 	return device_state;
c48088
 }
c48088
@@ -2107,7 +2118,8 @@ nm_config_device_state_write (int ifindex,
c48088
                               NMConfigDeviceStateManagedType managed,
c48088
                               const char *perm_hw_addr_fake,
c48088
                               const char *connection_uuid,
c48088
-                              gint nm_owned)
c48088
+                              gint nm_owned,
c48088
+                              guint32 route_metric_default)
c48088
 {
c48088
 	char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60];
c48088
 	GError *local = NULL;
c48088
@@ -2149,17 +2161,24 @@ nm_config_device_state_write (int ifindex,
c48088
 		                        nm_owned);
c48088
 	}
c48088
 
c48088
+	if (route_metric_default != 0) {
c48088
+		g_key_file_set_int64 (kf,
c48088
+		                      DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+		                      DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT,
c48088
+		                      route_metric_default);
c48088
+	}
c48088
 
c48088
 	if (!g_key_file_save_to_file (kf, path, &local)) {
c48088
 		_LOGW ("device-state: write #%d (%s) failed: %s", ifindex, path, local->message);
c48088
 		g_error_free (local);
c48088
 		return FALSE;
c48088
 	}
c48088
-	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s",
c48088
+	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT,
c48088
 	       ifindex, path,
c48088
 	       _device_state_managed_type_to_str (managed),
c48088
 	       NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""),
c48088
-	       NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""));
c48088
+	       NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""),
c48088
+	       route_metric_default);
c48088
 	return TRUE;
c48088
 }
c48088
 
c48088
diff --git a/src/nm-config.h b/src/nm-config.h
c48088
index 52a4099b2..4c6fcca2b 100644
c48088
--- a/src/nm-config.h
c48088
+++ b/src/nm-config.h
c48088
@@ -212,6 +212,9 @@ struct _NMConfigDeviceStateData {
c48088
 	int ifindex;
c48088
 	NMConfigDeviceStateManagedType managed;
c48088
 
c48088
+	/* a value of zero means that no metric is set. */
c48088
+	guint32 route_metric_default;
c48088
+
c48088
 	/* the UUID of the last settings-connection active
c48088
 	 * on the device. */
c48088
 	const char *connection_uuid;
c48088
@@ -220,7 +223,7 @@ struct _NMConfigDeviceStateData {
c48088
 
c48088
 	/* whether the device was nm-owned (0/1) or -1 for
c48088
 	 * non-software devices. */
c48088
-	gint nm_owned;
c48088
+	int nm_owned:3;
c48088
 };
c48088
 
c48088
 NMConfigDeviceStateData *nm_config_device_state_load (int ifindex);
c48088
@@ -229,7 +232,9 @@ gboolean nm_config_device_state_write (int ifindex,
c48088
                                        NMConfigDeviceStateManagedType managed,
c48088
                                        const char *perm_hw_addr_fake,
c48088
                                        const char *connection_uuid,
c48088
-                                       gint nm_owned);
c48088
+                                       gint nm_owned,
c48088
+                                       guint32 route_metric_default);
c48088
+
c48088
 void nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes);
c48088
 
c48088
 const GHashTable *nm_config_device_state_get_all (NMConfig *self);
c48088
diff --git a/src/nm-manager.c b/src/nm-manager.c
c48088
index 9e5f7ad4c..8b0b8013b 100644
c48088
--- a/src/nm-manager.c
c48088
+++ b/src/nm-manager.c
c48088
@@ -5198,6 +5198,7 @@ nm_manager_write_device_state (NMManager *self)
c48088
 		const char *uuid = NULL;
c48088
 		const char *perm_hw_addr_fake = NULL;
c48088
 		gboolean perm_hw_addr_is_fake;
c48088
+		guint32 route_metric_default;
c48088
 
c48088
 		ifindex = nm_device_get_ip_ifindex (device);
c48088
 		if (ifindex <= 0)
c48088
@@ -5227,11 +5228,14 @@ nm_manager_write_device_state (NMManager *self)
c48088
 
c48088
 		nm_owned = nm_device_is_software (device) ? nm_device_is_nm_owned (device) : -1;
c48088
 
c48088
+		route_metric_default = 0;
c48088
+
c48088
 		if (nm_config_device_state_write (ifindex,
c48088
 		                                  managed_type,
c48088
 		                                  perm_hw_addr_fake,
c48088
 		                                  uuid,
c48088
-		                                  nm_owned))
c48088
+		                                  nm_owned,
c48088
+		                                  route_metric_default))
c48088
 			g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex));
c48088
 	}
c48088
 
c48088
-- 
c48088
2.14.3
c48088
c48088
c48088
From 747eb4bbb0f69f106e857e37256f26770bd9d518 Mon Sep 17 00:00:00 2001
c48088
From: Thomas Haller <thaller@redhat.com>
c48088
Date: Tue, 5 Dec 2017 16:32:04 +0100
c48088
Subject: [PATCH 5/7] device: generate unique default route-metrics per
c48088
 interface
c48088
c48088
In the past we had NMDefaultRouteManager which would coordinate adding
c48088
the default-route with identical metrics. That especially happened, when
c48088
activating two devices of the same type, without explicitly specifying
c48088
ipv4.route-metric. For example, with ethernet devices, the routes on
c48088
both interfaces would get a metric of 100.
c48088
c48088
Coordinating routes was especially necessary, because we added
c48088
routes with NLM_F_EXCL flag, akin to `ip route replace`. We not
c48088
only had to avoid that activating two devices in NetworkManager would
c48088
result in a fight over the default-route, but more importently
c48088
to preserve externally added default-routes on unmanaged interfaces.
c48088
c48088
NMDefaultRouteManager would ensure that in case of duplicate
c48088
metrics, that the device that activated first would keep the
c48088
best default-route. It would do so by bumping the metric
c48088
of the second device to find a unused metric. The bumping itself
c48088
was not very important -- MDefaultRouteManager could also just not
c48088
configure any default-routes that show up as second, the result
c48088
would be quite similar. More important was to keep the best
c48088
default-route on the first activating device until the device
c48088
deactivates or a device activates that really has a better
c48088
default-route..
c48088
c48088
Likewise, NMRouteManager would globally manage non-default-routes.
c48088
It would not do any bumping of metrics, but it would also ensure that the routes
c48088
of the device that activates first are not overwritten by a device activating
c48088
later.
c48088
c48088
However, the `ip route replace` approach has downsides, especially
c48088
that it messes with routes on other interfaces, interfaces that are
c48088
possibly not managed by NetworkManager. Another downside is, that
c48088
binding a socket to an interface might not result in correct
c48088
routes, because the route might just not be there (in case of
c48088
NMRouteManager, which wouldn't configure duplicate routes by bumping
c48088
their metric).
c48088
c48088
Since commit 77ec302714795f905301d500b9aab6c88001f32e we would no longer
c48088
use NLM_F_EXCL, but add routes akin to `ip route append`. When
c48088
activating for example two ethernet devices with no explict route
c48088
metric configuration, there are two routes like
c48088
c48088
   default via 10.16.122.254 dev eth0 proto dhcp metric 100
c48088
   default via 192.168.100.1 dev eth1 proto dhcp metric 100
c48088
c48088
This does not only affect default routes. In case of a multi-homing
c48088
setup you'd get
c48088
c48088
  192.168.100.0/24 dev eth0 proto kernel scope link src 192.168.100.1 metric 100
c48088
  192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1 metric 100
c48088
c48088
but it's visible the most for default-routes.
c48088
c48088
Note that we would append the routes that are activated later, as the order
c48088
of `ip route show` confirms. One might hence expect, that kernel selects
c48088
a route based on the order in the routing tables. However, that isn't
c48088
the case, and activating the second interface will non-deterministically
c48088
re-route traffic via the new interface. That will interfere badly with
c48088
with NAT, stateful firewalls, and existing connections (like TCP).
c48088
c48088
The solution is to have NMManager keep a global index of the default route-metrics
c48088
currently in use. So, instead of determining the default-route metric based solely
c48088
on the device-type, we now in addition generate default metrics that do not
c48088
overlap. For example, if you activate eth0 first, it gets route-metric 100,
c48088
and if you then activate eth1, it gets 101. Note that if you deactivate
c48088
and re-activate eth0, then it will get route-metric 102, because the
c48088
best route should stick on eth1 (which reserves the range 100 to 101).
c48088
c48088
Note that when a connection explititly selects a particular metric, then that
c48088
choice is honored (contrary to NMDefaultRouteManager which was more concerned
c48088
with avoiding conflicts, then keeping the exact metric).
c48088
c48088
https://bugzilla.redhat.com/show_bug.cgi?id=1505893
c48088
(cherry picked from commit 6a32c64d8fb2a9c1cfb78ab7e2f0bb3a269c81d7)
c48088
(cherry picked from commit bd2d71754b770b43a71d85ca5f79832bbdb6b77a)
c48088
---
c48088
 src/devices/nm-device.c |  10 +-
c48088
 src/nm-manager.c        | 237 +++++++++++++++++++++++++++++++++++++++++++++++-
c48088
 src/nm-manager.h        |  10 ++
c48088
 3 files changed, 255 insertions(+), 2 deletions(-)
c48088
c48088
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
c48088
index 755589674..40c425a5b 100644
c48088
--- a/src/devices/nm-device.c
c48088
+++ b/src/devices/nm-device.c
c48088
@@ -1765,7 +1765,10 @@ nm_device_get_route_metric (NMDevice *self,
c48088
 		if (route_metric >= 0)
c48088
 			goto out;
c48088
 	}
c48088
-	route_metric = nm_device_get_route_metric_default (nm_device_get_device_type (self));
c48088
+
c48088
+	route_metric = nm_manager_device_route_metric_reserve (nm_manager_get (),
c48088
+	                                                       nm_device_get_ip_ifindex (self),
c48088
+	                                                       nm_device_get_device_type (self));
c48088
 out:
c48088
 	return nm_utils_ip_route_metric_normalize (addr_family, route_metric);
c48088
 }
c48088
@@ -12482,6 +12485,11 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
c48088
 
c48088
 	_cancel_activation (self);
c48088
 
c48088
+	if (cleanup_type != CLEANUP_TYPE_KEEP) {
c48088
+		nm_manager_device_route_metric_clear (nm_manager_get (),
c48088
+		                                      nm_device_get_ip_ifindex (self));
c48088
+	}
c48088
+
c48088
 	if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
c48088
 	    && priv->fw_state >= FIREWALL_STATE_INITIALIZED
c48088
 	    && priv->fw_mgr
c48088
diff --git a/src/nm-manager.c b/src/nm-manager.c
c48088
index 8b0b8013b..4d3416b37 100644
c48088
--- a/src/nm-manager.c
c48088
+++ b/src/nm-manager.c
c48088
@@ -160,6 +160,8 @@ typedef struct {
c48088
 
c48088
 	NMAuthManager *auth_mgr;
c48088
 
c48088
+	GHashTable *device_route_metrics;
c48088
+
c48088
 	GSList *auth_chains;
c48088
 	GHashTable *sleep_devices;
c48088
 
c48088
@@ -324,6 +326,237 @@ static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
c48088
 
c48088
 /*****************************************************************************/
c48088
 
c48088
+typedef struct {
c48088
+	int ifindex;
c48088
+	guint32 aspired_metric;
c48088
+	guint32 effective_metric;
c48088
+} DeviceRouteMetricData;
c48088
+
c48088
+static DeviceRouteMetricData *
c48088
+_device_route_metric_data_new (int ifindex, guint32 metric)
c48088
+{
c48088
+	DeviceRouteMetricData *data;
c48088
+
c48088
+	nm_assert (ifindex > 0);
c48088
+
c48088
+	/* For IPv4, metrics can use the entire uint32 bit range. For IPv6,
c48088
+	 * zero is treated like 1024. Since we handle IPv4 and IPv6 identically,
c48088
+	 * we cannot allow a zero metric here.
c48088
+	 */
c48088
+	nm_assert (metric > 0);
c48088
+
c48088
+	data = g_slice_new0 (DeviceRouteMetricData);
c48088
+	data->ifindex = ifindex;
c48088
+	data->aspired_metric = metric;
c48088
+	data->effective_metric = metric;
c48088
+	return data;
c48088
+}
c48088
+
c48088
+static guint
c48088
+_device_route_metric_data_by_ifindex_hash (gconstpointer p)
c48088
+{
c48088
+	const DeviceRouteMetricData *data = p;
c48088
+	NMHashState h;
c48088
+
c48088
+	nm_hash_init (&h, 1030338191);
c48088
+	nm_hash_update_vals (&h, data->ifindex);
c48088
+	return nm_hash_complete (&h);
c48088
+}
c48088
+
c48088
+static gboolean
c48088
+_device_route_metric_data_by_ifindex_equal (gconstpointer pa, gconstpointer pb)
c48088
+{
c48088
+	const DeviceRouteMetricData *a = pa;
c48088
+	const DeviceRouteMetricData *b = pb;
c48088
+
c48088
+	return a->ifindex == b->ifindex;
c48088
+}
c48088
+
c48088
+static guint32
c48088
+_device_route_metric_get (NMManager *self,
c48088
+                          int ifindex,
c48088
+                          NMDeviceType device_type,
c48088
+                          gboolean lookup_only)
c48088
+{
c48088
+	NMManagerPrivate *priv;
c48088
+	const DeviceRouteMetricData *d2;
c48088
+	DeviceRouteMetricData *data;
c48088
+	DeviceRouteMetricData data_lookup;
c48088
+	const NMDedupMultiHeadEntry *all_links_head;
c48088
+	NMPObject links_needle;
c48088
+	guint n_links;
c48088
+	gboolean cleaned = FALSE;
c48088
+	GHashTableIter h_iter;
c48088
+
c48088
+	g_return_val_if_fail (NM_IS_MANAGER (self), 0);
c48088
+
c48088
+	if (ifindex <= 0) {
c48088
+		if (lookup_only)
c48088
+			return 0;
c48088
+		return nm_device_get_route_metric_default (device_type);
c48088
+	}
c48088
+
c48088
+	priv = NM_MANAGER_GET_PRIVATE (self);
c48088
+
c48088
+	if (   lookup_only
c48088
+	    && !priv->device_route_metrics)
c48088
+		return 0;
c48088
+
c48088
+	if (G_UNLIKELY (!priv->device_route_metrics)) {
c48088
+		const GHashTable *h;
c48088
+		const NMConfigDeviceStateData *device_state;
c48088
+
c48088
+		priv->device_route_metrics = g_hash_table_new_full (_device_route_metric_data_by_ifindex_hash,
c48088
+		                                                    _device_route_metric_data_by_ifindex_equal,
c48088
+		                                                    NULL,
c48088
+		                                                    nm_g_slice_free_fcn (DeviceRouteMetricData));
c48088
+		cleaned = TRUE;
c48088
+
c48088
+		/* we need to pre-populate the cache for all (still existing) devices from the state-file */
c48088
+		h = nm_config_device_state_get_all (priv->config);
c48088
+		if (!h)
c48088
+			goto initited;
c48088
+
c48088
+		g_hash_table_iter_init (&h_iter, (GHashTable *) h);
c48088
+		while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &device_state)) {
c48088
+			if (!device_state->route_metric_default)
c48088
+				continue;
c48088
+			if (!nm_platform_link_get (priv->platform, device_state->ifindex)) {
c48088
+				/* we have the entry in the state file, but (currently) no such
c48088
+				 * ifindex exists in platform. Most likely the entry is obsolete,
c48088
+				 * hence we skip it. */
c48088
+				continue;
c48088
+			}
c48088
+			if (!nm_g_hash_table_add (priv->device_route_metrics,
c48088
+			                          _device_route_metric_data_new (device_state->ifindex,
c48088
+			                                                         device_state->route_metric_default)))
c48088
+				nm_assert_not_reached ();
c48088
+		}
c48088
+	}
c48088
+
c48088
+initited:
c48088
+	data_lookup.ifindex = ifindex;
c48088
+
c48088
+	data = g_hash_table_lookup (priv->device_route_metrics, &data_lookup);
c48088
+	if (data)
c48088
+		return data->effective_metric;
c48088
+	if (lookup_only)
c48088
+		return 0;
c48088
+
c48088
+	if (!cleaned) {
c48088
+		/* get the number of all links in the platform cache. */
c48088
+		all_links_head = nm_platform_lookup_all (priv->platform,
c48088
+		                                         NMP_CACHE_ID_TYPE_OBJECT_TYPE,
c48088
+		                                         nmp_object_stackinit_id_link (&links_needle, 1));
c48088
+		n_links = all_links_head ? all_links_head->len : 0;
c48088
+
c48088
+		/* on systems where a lot of devices are created and go away, the index contains
c48088
+		 * a lot of stale entries. We must from time to time clean them up.
c48088
+		 *
c48088
+		 * Do do this cleanup, whenever we have more enties then 2 times the number of links. */
c48088
+		if (G_UNLIKELY (g_hash_table_size (priv->device_route_metrics) > NM_MAX (20, n_links * 2))) {
c48088
+			/* from time to time, we need to do some house-keeping and prune stale entries.
c48088
+			 * Otherwise, on a system where interfaces frequently come and go (docker), we
c48088
+			 * keep growing this cache for ifindexes that no longer exist. */
c48088
+			g_hash_table_iter_init (&h_iter, priv->device_route_metrics);
c48088
+			while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &d2)) {
c48088
+				if (!nm_platform_link_get (priv->platform, d2->ifindex))
c48088
+					g_hash_table_iter_remove (&h_iter);
c48088
+			}
c48088
+			cleaned = TRUE;
c48088
+		}
c48088
+	}
c48088
+
c48088
+	data = _device_route_metric_data_new (ifindex, nm_device_get_route_metric_default (device_type));
c48088
+
c48088
+	/* unfortunately, there is no stright forward way to lookup all reserved metrics.
c48088
+	 * Note, that we don't only have to know which metrics are currently reserved,
c48088
+	 * but also, which metrics are now seemingly un-used but caused another reserved
c48088
+	 * metric to be bumped. Hence, the naive O(n^2) search :( */
c48088
+again:
c48088
+	g_hash_table_iter_init (&h_iter, priv->device_route_metrics);
c48088
+	while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &d2)) {
c48088
+		if (   data->effective_metric < d2->aspired_metric
c48088
+		    || data->effective_metric > d2->effective_metric) {
c48088
+			/* no overlap. Skip. */
c48088
+			continue;
c48088
+		}
c48088
+		if (   !cleaned
c48088
+		    && !nm_platform_link_get (priv->platform, d2->ifindex)) {
c48088
+			/* the metric seems taken, but there is no such interface. This entry
c48088
+			 * is stale, forget about it. */
c48088
+			g_hash_table_iter_remove (&h_iter);
c48088
+			continue;
c48088
+		}
c48088
+		data->effective_metric = d2->effective_metric;
c48088
+		if (data->effective_metric == G_MAXUINT32) {
c48088
+			/* we cannot bump any further. Done. */
c48088
+			break;
c48088
+		}
c48088
+
c48088
+		if (data->effective_metric - data->aspired_metric > 50) {
c48088
+			/* as one active interface reserves an entire range of metrics
c48088
+			 * (from aspired_metric to effective_metric), that means if you
c48088
+			 * alternatingly activate two interfaces, their metric will
c48088
+			 * juggle up.
c48088
+			 *
c48088
+			 * Limit this, don't bump the metric more then 50 times. */
c48088
+			break;
c48088
+		}
c48088
+
c48088
+		/* bump the metric, and search again. */
c48088
+		data->effective_metric++;
c48088
+		goto again;
c48088
+	}
c48088
+
c48088
+	_LOGT (LOGD_DEVICE, "default-route-metric: ifindex %d reserves metric %u (aspired %u)",
c48088
+	       data->ifindex, data->effective_metric, data->aspired_metric);
c48088
+
c48088
+	if (!nm_g_hash_table_add (priv->device_route_metrics, data))
c48088
+		nm_assert_not_reached ();
c48088
+
c48088
+	return data->effective_metric;
c48088
+}
c48088
+
c48088
+guint32
c48088
+nm_manager_device_route_metric_reserve (NMManager *self,
c48088
+                                        int ifindex,
c48088
+                                        NMDeviceType device_type)
c48088
+{
c48088
+	guint32 metric;
c48088
+
c48088
+	metric = _device_route_metric_get (self, ifindex, device_type, FALSE);
c48088
+	nm_assert (metric != 0);
c48088
+	return metric;
c48088
+}
c48088
+
c48088
+guint32
c48088
+nm_manager_device_route_metric_get (NMManager *self,
c48088
+                                    int ifindex)
c48088
+{
c48088
+	return _device_route_metric_get (self, ifindex, NM_DEVICE_TYPE_UNKNOWN, TRUE);
c48088
+}
c48088
+
c48088
+void
c48088
+nm_manager_device_route_metric_clear (NMManager *self,
c48088
+                                      int ifindex)
c48088
+{
c48088
+	NMManagerPrivate *priv;
c48088
+	DeviceRouteMetricData data_lookup;
c48088
+
c48088
+	priv = NM_MANAGER_GET_PRIVATE (self);
c48088
+
c48088
+	if (!priv->device_route_metrics)
c48088
+		return;
c48088
+	data_lookup.ifindex = ifindex;
c48088
+	if (g_hash_table_remove (priv->device_route_metrics, &data_lookup)) {
c48088
+		_LOGT (LOGD_DEVICE, "default-route-metric: ifindex %d released",
c48088
+		       ifindex);
c48088
+	}
c48088
+}
c48088
+
c48088
+/*****************************************************************************/
c48088
+
c48088
 static void
c48088
 _delete_volatile_connection_do (NMManager *self,
c48088
                                 NMSettingsConnection *connection)
c48088
@@ -5228,7 +5461,7 @@ nm_manager_write_device_state (NMManager *self)
c48088
 
c48088
 		nm_owned = nm_device_is_software (device) ? nm_device_is_nm_owned (device) : -1;
c48088
 
c48088
-		route_metric_default = 0;
c48088
+		route_metric_default = nm_manager_device_route_metric_get (self, ifindex);
c48088
 
c48088
 		if (nm_config_device_state_write (ifindex,
c48088
 		                                  managed_type,
c48088
@@ -6607,6 +6840,8 @@ dispose (GObject *object)
c48088
 
c48088
 	nm_clear_g_source (&priv->timestamp_update_id);
c48088
 
c48088
+	g_clear_pointer (&priv->device_route_metrics, g_hash_table_destroy);
c48088
+
c48088
 	G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
c48088
 }
c48088
 
c48088
diff --git a/src/nm-manager.h b/src/nm-manager.h
c48088
index 2d463c718..d82570889 100644
c48088
--- a/src/nm-manager.h
c48088
+++ b/src/nm-manager.h
c48088
@@ -114,6 +114,16 @@ NMDevice *          nm_manager_get_device_by_ifindex   (NMManager *manager,
c48088
 NMDevice *          nm_manager_get_device_by_path      (NMManager *manager,
c48088
                                                         const char *path);
c48088
 
c48088
+guint32             nm_manager_device_route_metric_reserve (NMManager *self,
c48088
+                                                            int ifindex,
c48088
+                                                            NMDeviceType device_type);
c48088
+
c48088
+guint32             nm_manager_device_route_metric_get (NMManager *self,
c48088
+                                                        int ifindex);
c48088
+
c48088
+void                nm_manager_device_route_metric_clear (NMManager *self,
c48088
+                                                          int ifindex);
c48088
+
c48088
 char *              nm_manager_get_connection_iface (NMManager *self,
c48088
                                                      NMConnection *connection,
c48088
                                                      NMDevice **out_parent,
c48088
-- 
c48088
2.14.3
c48088
c48088
c48088
From f1728bdeac80fdc45ed969b8821a207383962296 Mon Sep 17 00:00:00 2001
c48088
From: Thomas Haller <thaller@redhat.com>
c48088
Date: Tue, 19 Dec 2017 10:10:15 +0100
c48088
Subject: [PATCH 6/7] core: ensure that the default route-metric bumps at most
c48088
 50 points
c48088
c48088
First check that the limit of 50 metric points is not surpassed.
c48088
Otherwise, if you have an ethernet device (aspired 100, effective
c48088
130) and a MACSec devic (aspired 125, effective 155), activating a
c48088
new ethernet device would bump it's metric to 155 -- more then
c48088
the 50 points limit.
c48088
c48088
It doesn't matter too much, because the cases where the limit of
c48088
50 could have been surpassed were very specific. Still, change
c48088
it to ensure that the limit is always honored as one would expect.
c48088
c48088
Fixes: 6a32c64d8fb2a9c1cfb78ab7e2f0bb3a269c81d7
c48088
(cherry picked from commit 2499d3bdc6007308bf282cb44462990a4cd03b0e)
c48088
(cherry picked from commit 5fd91fb67d3e3865545f5cbc1f39292635440cd3)
c48088
---
c48088
 src/nm-manager.c | 26 ++++++++++++++++++--------
c48088
 1 file changed, 18 insertions(+), 8 deletions(-)
c48088
c48088
diff --git a/src/nm-manager.c b/src/nm-manager.c
c48088
index 4d3416b37..fad4f64b8 100644
c48088
--- a/src/nm-manager.c
c48088
+++ b/src/nm-manager.c
c48088
@@ -472,7 +472,11 @@ initited:
c48088
 	/* unfortunately, there is no stright forward way to lookup all reserved metrics.
c48088
 	 * Note, that we don't only have to know which metrics are currently reserved,
c48088
 	 * but also, which metrics are now seemingly un-used but caused another reserved
c48088
-	 * metric to be bumped. Hence, the naive O(n^2) search :( */
c48088
+	 * metric to be bumped. Hence, the naive O(n^2) search :(
c48088
+	 *
c48088
+	 * Well, technically, since we limit bumping the metric to 50, this entire
c48088
+	 * loop runs at most 50 times, so it's still O(n). Let's just say, it's not
c48088
+	 * very efficient. */
c48088
 again:
c48088
 	g_hash_table_iter_init (&h_iter, priv->device_route_metrics);
c48088
 	while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &d2)) {
c48088
@@ -488,24 +492,30 @@ again:
c48088
 			g_hash_table_iter_remove (&h_iter);
c48088
 			continue;
c48088
 		}
c48088
-		data->effective_metric = d2->effective_metric;
c48088
-		if (data->effective_metric == G_MAXUINT32) {
c48088
-			/* we cannot bump any further. Done. */
c48088
+
c48088
+		if (d2->effective_metric == G_MAXUINT32) {
c48088
+			/* we cannot bump the metric any further. Done.
c48088
+			 *
c48088
+			 * Actually, this can currently not happen because the aspired_metric
c48088
+			 * are small numbers and we limit the bumping to 50. Still, for
c48088
+			 * completeness... */
c48088
+			data->effective_metric = G_MAXUINT32;
c48088
 			break;
c48088
 		}
c48088
 
c48088
-		if (data->effective_metric - data->aspired_metric > 50) {
c48088
+		if (d2->effective_metric - data->aspired_metric >= 50) {
c48088
 			/* as one active interface reserves an entire range of metrics
c48088
 			 * (from aspired_metric to effective_metric), that means if you
c48088
 			 * alternatingly activate two interfaces, their metric will
c48088
-			 * juggle up.
c48088
+			 * bump each other.
c48088
 			 *
c48088
-			 * Limit this, don't bump the metric more then 50 times. */
c48088
+			 * Limit this, bump the metric at most 50 points. */
c48088
+			data->effective_metric = data->aspired_metric + 50;
c48088
 			break;
c48088
 		}
c48088
 
c48088
 		/* bump the metric, and search again. */
c48088
-		data->effective_metric++;
c48088
+		data->effective_metric = d2->effective_metric + 1;
c48088
 		goto again;
c48088
 	}
c48088
 
c48088
-- 
c48088
2.14.3
c48088
c48088
c48088
From a245f5eb91e8c30ae19403586f5ef9c14ef5f6d2 Mon Sep 17 00:00:00 2001
c48088
From: Thomas Haller <thaller@redhat.com>
c48088
Date: Wed, 20 Dec 2017 12:45:02 +0100
c48088
Subject: [PATCH 7/7] core: persist aspired default route-metric in device's
c48088
 state file
c48088
c48088
NMManager tries to assign unique route-metrics in an increasing manner
c48088
so that the device which activates first keeps to have the best routes.
c48088
c48088
This information is also persisted in the device's state file, however
c48088
we not only need to persist the effective route-metric which was
c48088
eventually chosen by NMManager, but also the aspired metric.
c48088
c48088
The reason is that when a metric is chosen for a device, the entire
c48088
range between aspired and effective route-metric is reserved for that
c48088
device. We must remember the entire range so that after restart the
c48088
entire range is still considered to be in use.
c48088
c48088
Fixes: 6a32c64d8fb2a9c1cfb78ab7e2f0bb3a269c81d7
c48088
(cherry picked from commit 4277bc0ee0479ad62369c3a261ea8d098e5e25ad)
c48088
(cherry picked from commit fa53c715d1b929516e5e32e13ebc630a2c339394)
c48088
---
c48088
 src/nm-config.c  | 50 +++++++++++++++++++++++++++++++++++---------------
c48088
 src/nm-config.h  |  6 ++++--
c48088
 src/nm-manager.c | 48 +++++++++++++++++++++++++++---------------------
c48088
 src/nm-manager.h |  3 ---
c48088
 4 files changed, 66 insertions(+), 41 deletions(-)
c48088
c48088
diff --git a/src/nm-config.c b/src/nm-config.c
c48088
index 97ae3f6f4..29f0d5177 100644
c48088
--- a/src/nm-config.c
c48088
+++ b/src/nm-config.c
c48088
@@ -1933,7 +1933,8 @@ _nm_config_state_set (NMConfig *self,
c48088
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE   "perm-hw-addr-fake"
c48088
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID     "connection-uuid"
c48088
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED            "nm-owned"
c48088
-#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT "route-metric-default"
c48088
+#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED   "route-metric-default-aspired"
c48088
+#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE "route-metric-default-effective"
c48088
 
c48088
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType,
c48088
 	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
c48088
@@ -1953,7 +1954,8 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
c48088
 	gsize perm_hw_addr_fake_len;
c48088
 	gint nm_owned = -1;
c48088
 	char *p;
c48088
-	guint32 route_metric_default;
c48088
+	guint32 route_metric_default_effective;
c48088
+	guint32 route_metric_default_aspired;
c48088
 
c48088
 	nm_assert (kf);
c48088
 	nm_assert (ifindex > 0);
c48088
@@ -1996,10 +1998,18 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
c48088
 
c48088
 	/* metric zero is not a valid metric. While zero valid for IPv4, for IPv6 it is an alias
c48088
 	 * for 1024. Since we handle here IPv4 and IPv6 the same, we cannot allow zero. */
c48088
-	route_metric_default = nm_config_keyfile_get_int64 (kf,
c48088
-	                                                    DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
-	                                                    DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT,
c48088
-	                                                    10, 1, G_MAXUINT32, 0);
c48088
+	route_metric_default_effective = nm_config_keyfile_get_int64 (kf,
c48088
+	                                                              DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+	                                                              DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE,
c48088
+	                                                              10, 1, G_MAXUINT32, 0);
c48088
+	if (route_metric_default_effective) {
c48088
+		route_metric_default_aspired = nm_config_keyfile_get_int64 (kf,
c48088
+		                                                            DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+		                                                            DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE,
c48088
+		                                                            10, 1, route_metric_default_effective,
c48088
+		                                                            route_metric_default_effective);
c48088
+	} else
c48088
+		route_metric_default_aspired = 0;
c48088
 
c48088
 	connection_uuid_len = connection_uuid ? strlen (connection_uuid) + 1 : 0;
c48088
 	perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen (perm_hw_addr_fake) + 1 : 0;
c48088
@@ -2013,7 +2023,8 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
c48088
 	device_state->connection_uuid = NULL;
c48088
 	device_state->perm_hw_addr_fake = NULL;
c48088
 	device_state->nm_owned = nm_owned;
c48088
-	device_state->route_metric_default = route_metric_default;
c48088
+	device_state->route_metric_default_aspired = route_metric_default_aspired;
c48088
+	device_state->route_metric_default_effective = route_metric_default_effective;
c48088
 
c48088
 	p = (char *) (&device_state[1]);
c48088
 	if (connection_uuid) {
c48088
@@ -2058,14 +2069,15 @@ nm_config_device_state_load (int ifindex)
c48088
 	               ", nm-owned=1" :
c48088
 	               (device_state->nm_owned == FALSE ? ", nm-owned=0" : "");
c48088
 
c48088
-	_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT,
c48088
+	_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"",
c48088
 	       kf ? "read" : "miss",
c48088
 	       ifindex, path,
c48088
 	       _device_state_managed_type_to_str (device_state->managed),
c48088
 	       NM_PRINT_FMT_QUOTED (device_state->connection_uuid, ", connection-uuid=", device_state->connection_uuid, "", ""),
c48088
 	       NM_PRINT_FMT_QUOTED (device_state->perm_hw_addr_fake, ", perm-hw-addr-fake=", device_state->perm_hw_addr_fake, "", ""),
c48088
 	       nm_owned_str,
c48088
-	       device_state->route_metric_default);
c48088
+	       device_state->route_metric_default_aspired,
c48088
+	       device_state->route_metric_default_effective);
c48088
 
c48088
 	return device_state;
c48088
 }
c48088
@@ -2119,7 +2131,8 @@ nm_config_device_state_write (int ifindex,
c48088
                               const char *perm_hw_addr_fake,
c48088
                               const char *connection_uuid,
c48088
                               gint nm_owned,
c48088
-                              guint32 route_metric_default)
c48088
+                              guint32 route_metric_default_aspired,
c48088
+                              guint32 route_metric_default_effective)
c48088
 {
c48088
 	char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60];
c48088
 	GError *local = NULL;
c48088
@@ -2161,11 +2174,17 @@ nm_config_device_state_write (int ifindex,
c48088
 		                        nm_owned);
c48088
 	}
c48088
 
c48088
-	if (route_metric_default != 0) {
c48088
+	if (route_metric_default_effective != 0) {
c48088
 		g_key_file_set_int64 (kf,
c48088
 		                      DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
-		                      DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT,
c48088
-		                      route_metric_default);
c48088
+		                      DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE,
c48088
+		                      route_metric_default_effective);
c48088
+		if (route_metric_default_aspired != route_metric_default_effective) {
c48088
+			g_key_file_set_int64 (kf,
c48088
+			                      DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
c48088
+			                      DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED,
c48088
+			                      route_metric_default_aspired);
c48088
+		}
c48088
 	}
c48088
 
c48088
 	if (!g_key_file_save_to_file (kf, path, &local)) {
c48088
@@ -2173,12 +2192,13 @@ nm_config_device_state_write (int ifindex,
c48088
 		g_error_free (local);
c48088
 		return FALSE;
c48088
 	}
c48088
-	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT,
c48088
+	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"",
c48088
 	       ifindex, path,
c48088
 	       _device_state_managed_type_to_str (managed),
c48088
 	       NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""),
c48088
 	       NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""),
c48088
-	       route_metric_default);
c48088
+	       route_metric_default_aspired,
c48088
+	       route_metric_default_effective);
c48088
 	return TRUE;
c48088
 }
c48088
 
c48088
diff --git a/src/nm-config.h b/src/nm-config.h
c48088
index 4c6fcca2b..42ab4682d 100644
c48088
--- a/src/nm-config.h
c48088
+++ b/src/nm-config.h
c48088
@@ -213,7 +213,8 @@ struct _NMConfigDeviceStateData {
c48088
 	NMConfigDeviceStateManagedType managed;
c48088
 
c48088
 	/* a value of zero means that no metric is set. */
c48088
-	guint32 route_metric_default;
c48088
+	guint32 route_metric_default_aspired;
c48088
+	guint32 route_metric_default_effective;
c48088
 
c48088
 	/* the UUID of the last settings-connection active
c48088
 	 * on the device. */
c48088
@@ -233,7 +234,8 @@ gboolean nm_config_device_state_write (int ifindex,
c48088
                                        const char *perm_hw_addr_fake,
c48088
                                        const char *connection_uuid,
c48088
                                        gint nm_owned,
c48088
-                                       guint32 route_metric_default);
c48088
+                                       guint32 route_metric_default_aspired,
c48088
+                                       guint32 route_metric_default_effective);
c48088
 
c48088
 void nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes);
c48088
 
c48088
diff --git a/src/nm-manager.c b/src/nm-manager.c
c48088
index fad4f64b8..c011bfd0e 100644
c48088
--- a/src/nm-manager.c
c48088
+++ b/src/nm-manager.c
c48088
@@ -333,7 +333,7 @@ typedef struct {
c48088
 } DeviceRouteMetricData;
c48088
 
c48088
 static DeviceRouteMetricData *
c48088
-_device_route_metric_data_new (int ifindex, guint32 metric)
c48088
+_device_route_metric_data_new (int ifindex, guint32 aspired_metric, guint32 effective_metric)
c48088
 {
c48088
 	DeviceRouteMetricData *data;
c48088
 
c48088
@@ -343,12 +343,13 @@ _device_route_metric_data_new (int ifindex, guint32 metric)
c48088
 	 * zero is treated like 1024. Since we handle IPv4 and IPv6 identically,
c48088
 	 * we cannot allow a zero metric here.
c48088
 	 */
c48088
-	nm_assert (metric > 0);
c48088
+	nm_assert (aspired_metric > 0);
c48088
+	nm_assert (effective_metric == 0 || aspired_metric <= effective_metric);
c48088
 
c48088
 	data = g_slice_new0 (DeviceRouteMetricData);
c48088
 	data->ifindex = ifindex;
c48088
-	data->aspired_metric = metric;
c48088
-	data->effective_metric = metric;
c48088
+	data->aspired_metric = aspired_metric;
c48088
+	data->effective_metric = effective_metric ?: aspired_metric;
c48088
 	return data;
c48088
 }
c48088
 
c48088
@@ -376,7 +377,8 @@ static guint32
c48088
 _device_route_metric_get (NMManager *self,
c48088
                           int ifindex,
c48088
                           NMDeviceType device_type,
c48088
-                          gboolean lookup_only)
c48088
+                          gboolean lookup_only,
c48088
+                          guint32 *out_aspired_metric)
c48088
 {
c48088
 	NMManagerPrivate *priv;
c48088
 	const DeviceRouteMetricData *d2;
c48088
@@ -387,13 +389,18 @@ _device_route_metric_get (NMManager *self,
c48088
 	guint n_links;
c48088
 	gboolean cleaned = FALSE;
c48088
 	GHashTableIter h_iter;
c48088
+	guint32 metric;
c48088
 
c48088
 	g_return_val_if_fail (NM_IS_MANAGER (self), 0);
c48088
 
c48088
+	NM_SET_OUT (out_aspired_metric, 0);
c48088
+
c48088
 	if (ifindex <= 0) {
c48088
 		if (lookup_only)
c48088
 			return 0;
c48088
-		return nm_device_get_route_metric_default (device_type);
c48088
+		metric = nm_device_get_route_metric_default (device_type);
c48088
+		NM_SET_OUT (out_aspired_metric, metric);
c48088
+		return metric;
c48088
 	}
c48088
 
c48088
 	priv = NM_MANAGER_GET_PRIVATE (self);
c48088
@@ -419,7 +426,7 @@ _device_route_metric_get (NMManager *self,
c48088
 
c48088
 		g_hash_table_iter_init (&h_iter, (GHashTable *) h);
c48088
 		while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &device_state)) {
c48088
-			if (!device_state->route_metric_default)
c48088
+			if (!device_state->route_metric_default_effective)
c48088
 				continue;
c48088
 			if (!nm_platform_link_get (priv->platform, device_state->ifindex)) {
c48088
 				/* we have the entry in the state file, but (currently) no such
c48088
@@ -429,7 +436,8 @@ _device_route_metric_get (NMManager *self,
c48088
 			}
c48088
 			if (!nm_g_hash_table_add (priv->device_route_metrics,
c48088
 			                          _device_route_metric_data_new (device_state->ifindex,
c48088
-			                                                         device_state->route_metric_default)))
c48088
+			                                                         device_state->route_metric_default_aspired,
c48088
+			                                                         device_state->route_metric_default_effective)))
c48088
 				nm_assert_not_reached ();
c48088
 		}
c48088
 	}
c48088
@@ -439,7 +447,7 @@ initited:
c48088
 
c48088
 	data = g_hash_table_lookup (priv->device_route_metrics, &data_lookup);
c48088
 	if (data)
c48088
-		return data->effective_metric;
c48088
+		goto out;
c48088
 	if (lookup_only)
c48088
 		return 0;
c48088
 
c48088
@@ -467,7 +475,7 @@ initited:
c48088
 		}
c48088
 	}
c48088
 
c48088
-	data = _device_route_metric_data_new (ifindex, nm_device_get_route_metric_default (device_type));
c48088
+	data = _device_route_metric_data_new (ifindex, nm_device_get_route_metric_default (device_type), 0);
c48088
 
c48088
 	/* unfortunately, there is no stright forward way to lookup all reserved metrics.
c48088
 	 * Note, that we don't only have to know which metrics are currently reserved,
c48088
@@ -525,6 +533,8 @@ again:
c48088
 	if (!nm_g_hash_table_add (priv->device_route_metrics, data))
c48088
 		nm_assert_not_reached ();
c48088
 
c48088
+out:
c48088
+	NM_SET_OUT (out_aspired_metric, data->aspired_metric);
c48088
 	return data->effective_metric;
c48088
 }
c48088
 
c48088
@@ -535,18 +545,11 @@ nm_manager_device_route_metric_reserve (NMManager *self,
c48088
 {
c48088
 	guint32 metric;
c48088
 
c48088
-	metric = _device_route_metric_get (self, ifindex, device_type, FALSE);
c48088
+	metric = _device_route_metric_get (self, ifindex, device_type, FALSE, NULL);
c48088
 	nm_assert (metric != 0);
c48088
 	return metric;
c48088
 }
c48088
 
c48088
-guint32
c48088
-nm_manager_device_route_metric_get (NMManager *self,
c48088
-                                    int ifindex)
c48088
-{
c48088
-	return _device_route_metric_get (self, ifindex, NM_DEVICE_TYPE_UNKNOWN, TRUE);
c48088
-}
c48088
-
c48088
 void
c48088
 nm_manager_device_route_metric_clear (NMManager *self,
c48088
                                       int ifindex)
c48088
@@ -5441,7 +5444,8 @@ nm_manager_write_device_state (NMManager *self)
c48088
 		const char *uuid = NULL;
c48088
 		const char *perm_hw_addr_fake = NULL;
c48088
 		gboolean perm_hw_addr_is_fake;
c48088
-		guint32 route_metric_default;
c48088
+		guint32 route_metric_default_aspired;
c48088
+		guint32 route_metric_default_effective;
c48088
 
c48088
 		ifindex = nm_device_get_ip_ifindex (device);
c48088
 		if (ifindex <= 0)
c48088
@@ -5471,14 +5475,16 @@ nm_manager_write_device_state (NMManager *self)
c48088
 
c48088
 		nm_owned = nm_device_is_software (device) ? nm_device_is_nm_owned (device) : -1;
c48088
 
c48088
-		route_metric_default = nm_manager_device_route_metric_get (self, ifindex);
c48088
+		route_metric_default_effective = _device_route_metric_get (self, ifindex, NM_DEVICE_TYPE_UNKNOWN,
c48088
+		                                                           TRUE, &route_metric_default_aspired);
c48088
 
c48088
 		if (nm_config_device_state_write (ifindex,
c48088
 		                                  managed_type,
c48088
 		                                  perm_hw_addr_fake,
c48088
 		                                  uuid,
c48088
 		                                  nm_owned,
c48088
-		                                  route_metric_default))
c48088
+		                                  route_metric_default_aspired,
c48088
+		                                  route_metric_default_effective))
c48088
 			g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex));
c48088
 	}
c48088
 
c48088
diff --git a/src/nm-manager.h b/src/nm-manager.h
c48088
index d82570889..b4587e088 100644
c48088
--- a/src/nm-manager.h
c48088
+++ b/src/nm-manager.h
c48088
@@ -118,9 +118,6 @@ guint32             nm_manager_device_route_metric_reserve (NMManager *self,
c48088
                                                             int ifindex,
c48088
                                                             NMDeviceType device_type);
c48088
 
c48088
-guint32             nm_manager_device_route_metric_get (NMManager *self,
c48088
-                                                        int ifindex);
c48088
-
c48088
 void                nm_manager_device_route_metric_clear (NMManager *self,
c48088
                                                           int ifindex);
c48088
 
c48088
-- 
c48088
2.14.3
c48088