summary refs log tree commit diff
path: root/src/nm-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-config.c')
-rw-r--r--src/nm-config.c266
1 files changed, 208 insertions, 58 deletions
diff --git a/src/nm-config.c b/src/nm-config.c
index de727c98..29f0d517 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -121,6 +121,14 @@ typedef struct {
 	 * because the state changes only on explicit actions from the daemon
 	 * itself. */
 	State *state;
+
+	/* the hash table of device states. It is only loaded from disk
+	 * once and kept immutable afterwards.
+	 *
+	 * We also read all state file at once. We don't want to support
+	 * that they are changed outside of NM (at least not while NM is running).
+	 * Hence, we read them once, that's it. */
+	GHashTable *device_states;
 } NMConfigPrivate;
 
 struct _NMConfig {
@@ -182,6 +190,33 @@ nm_config_keyfile_get_boolean (const GKeyFile *keyfile,
 	return nm_config_parse_boolean (str, default_value);
 }
 
+gint64
+nm_config_keyfile_get_int64 (const GKeyFile *keyfile,
+                             const char *section,
+                             const char *key,
+                             guint base,
+                             gint64 min,
+                             gint64 max,
+                             gint64 fallback)
+{
+	gint64 v;
+	int errsv;
+	char *str;
+
+	g_return_val_if_fail (keyfile, fallback);
+	g_return_val_if_fail (section, fallback);
+	g_return_val_if_fail (key, fallback);
+
+	str = g_key_file_get_value ((GKeyFile *) keyfile, section, key, NULL);
+	v = _nm_utils_ascii_str_to_int64 (str, base, min, max, fallback);
+	if (str) {
+		errsv = errno;
+		g_free (str);
+		errno = errsv;
+	}
+	return v;
+}
+
 char *
 nm_config_keyfile_get_value (const GKeyFile *keyfile,
                              const char *section,
@@ -1898,6 +1933,8 @@ _nm_config_state_set (NMConfig *self,
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE   "perm-hw-addr-fake"
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID     "connection-uuid"
 #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED            "nm-owned"
+#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED   "route-metric-default-aspired"
+#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE "route-metric-default-effective"
 
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType,
 	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
@@ -1917,47 +1954,63 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
 	gsize perm_hw_addr_fake_len;
 	gint nm_owned = -1;
 	char *p;
+	guint32 route_metric_default_effective;
+	guint32 route_metric_default_aspired;
 
+	nm_assert (kf);
 	nm_assert (ifindex > 0);
 
-	if (kf) {
-		switch (nm_config_keyfile_get_boolean (kf,
-		                                       DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
-		                                       DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
-		                                       -1)) {
-		case TRUE:
-			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
-			connection_uuid = nm_config_keyfile_get_value (kf,
-			                                               DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
-			                                               DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID,
-			                                               NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
-			break;
-		case FALSE:
-			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
-			break;
-		case -1:
-			/* missing property in keyfile. */
-			break;
-		}
-
-		perm_hw_addr_fake = nm_config_keyfile_get_value (kf,
-		                                                 DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
-		                                                 DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE,
-		                                                 NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
-		if (perm_hw_addr_fake) {
-			char *normalized;
-
-			normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1);
-			g_free (perm_hw_addr_fake);
-			perm_hw_addr_fake = normalized;
-		}
-
-		nm_owned = nm_config_keyfile_get_boolean (kf,
-		                                          DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
-		                                          DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED,
-		                                          -1);
+	switch (nm_config_keyfile_get_boolean (kf,
+	                                       DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+	                                       DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
+	                                       -1)) {
+	case TRUE:
+		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
+		connection_uuid = nm_config_keyfile_get_value (kf,
+		                                               DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+		                                               DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID,
+		                                               NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
+		break;
+	case FALSE:
+		managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
+		break;
+	case -1:
+		/* missing property in keyfile. */
+		break;
 	}
 
+	perm_hw_addr_fake = nm_config_keyfile_get_value (kf,
+	                                                 DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+	                                                 DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE,
+	                                                 NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
+	if (perm_hw_addr_fake) {
+		char *normalized;
+
+		normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1);
+		g_free (perm_hw_addr_fake);
+		perm_hw_addr_fake = normalized;
+	}
+
+	nm_owned = nm_config_keyfile_get_boolean (kf,
+	                                          DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+	                                          DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED,
+	                                          -1);
+
+	/* metric zero is not a valid metric. While zero valid for IPv4, for IPv6 it is an alias
+	 * for 1024. Since we handle here IPv4 and IPv6 the same, we cannot allow zero. */
+	route_metric_default_effective = nm_config_keyfile_get_int64 (kf,
+	                                                              DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+	                                                              DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE,
+	                                                              10, 1, G_MAXUINT32, 0);
+	if (route_metric_default_effective) {
+		route_metric_default_aspired = nm_config_keyfile_get_int64 (kf,
+		                                                            DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+		                                                            DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE,
+		                                                            10, 1, route_metric_default_effective,
+		                                                            route_metric_default_effective);
+	} else
+		route_metric_default_aspired = 0;
+
 	connection_uuid_len = connection_uuid ? strlen (connection_uuid) + 1 : 0;
 	perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen (perm_hw_addr_fake) + 1 : 0;
 
@@ -1970,6 +2023,8 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
 	device_state->connection_uuid = NULL;
 	device_state->perm_hw_addr_fake = NULL;
 	device_state->nm_owned = nm_owned;
+	device_state->route_metric_default_aspired = route_metric_default_aspired;
+	device_state->route_metric_default_effective = route_metric_default_effective;
 
 	p = (char *) (&device_state[1]);
 	if (connection_uuid) {
@@ -2007,31 +2062,77 @@ nm_config_device_state_load (int ifindex)
 
 	kf = nm_config_create_keyfile ();
 	if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
-		g_clear_pointer (&kf, g_key_file_unref);
+		return NULL;
 
 	device_state = _config_device_state_data_new (ifindex, kf);
 	nm_owned_str = device_state->nm_owned == TRUE ?
 	               ", nm-owned=1" :
 	               (device_state->nm_owned == FALSE ? ", nm-owned=0" : "");
 
-
-	_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s",
+	_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"",
 	       kf ? "read" : "miss",
 	       ifindex, path,
 	       _device_state_managed_type_to_str (device_state->managed),
 	       NM_PRINT_FMT_QUOTED (device_state->connection_uuid, ", connection-uuid=", device_state->connection_uuid, "", ""),
 	       NM_PRINT_FMT_QUOTED (device_state->perm_hw_addr_fake, ", perm-hw-addr-fake=", device_state->perm_hw_addr_fake, "", ""),
-	       nm_owned_str);
+	       nm_owned_str,
+	       device_state->route_metric_default_aspired,
+	       device_state->route_metric_default_effective);
 
 	return device_state;
 }
 
+static int
+_device_state_parse_filename (const char *filename)
+{
+	if (!filename || !filename[0])
+		return 0;
+	if (!NM_STRCHAR_ALL (filename, ch, g_ascii_isdigit (ch)))
+		return 0;
+	return _nm_utils_ascii_str_to_int64 (filename, 10, 1, G_MAXINT, 0);
+}
+
+GHashTable *
+nm_config_device_state_load_all (void)
+{
+	GHashTable *states;
+	GDir *dir;
+	const char *fn;
+	int ifindex;
+
+	states = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_free);
+
+	dir = g_dir_open (NM_CONFIG_DEVICE_STATE_DIR, 0, NULL);
+	if (!dir)
+		return states;
+
+	while ((fn = g_dir_read_name (dir))) {
+		NMConfigDeviceStateData *state;
+
+		ifindex = _device_state_parse_filename (fn);
+		if (ifindex <= 0)
+			continue;
+
+		state = nm_config_device_state_load (ifindex);
+		if (!state)
+			continue;
+
+		if (!nm_g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state))
+			nm_assert_not_reached ();
+	}
+	g_dir_close (dir);
+
+	return states;
+}
+
 gboolean
 nm_config_device_state_write (int ifindex,
                               NMConfigDeviceStateManagedType managed,
                               const char *perm_hw_addr_fake,
                               const char *connection_uuid,
-                              gint nm_owned)
+                              gint nm_owned,
+                              guint32 route_metric_default_aspired,
+                              guint32 route_metric_default_effective)
 {
 	char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60];
 	GError *local = NULL;
@@ -2073,17 +2174,31 @@ nm_config_device_state_write (int ifindex,
 		                        nm_owned);
 	}
 
+	if (route_metric_default_effective != 0) {
+		g_key_file_set_int64 (kf,
+		                      DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+		                      DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE,
+		                      route_metric_default_effective);
+		if (route_metric_default_aspired != route_metric_default_effective) {
+			g_key_file_set_int64 (kf,
+			                      DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+			                      DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_ASPIRED,
+			                      route_metric_default_aspired);
+		}
+	}
 
 	if (!g_key_file_save_to_file (kf, path, &local)) {
 		_LOGW ("device-state: write #%d (%s) failed: %s", ifindex, path, local->message);
 		g_error_free (local);
 		return FALSE;
 	}
-	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s",
+	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT"-%"G_GUINT32_FORMAT"",
 	       ifindex, path,
 	       _device_state_managed_type_to_str (managed),
 	       NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""),
-	       NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""));
+	       NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""),
+	       route_metric_default_aspired,
+	       route_metric_default_effective);
 	return TRUE;
 }
 
@@ -2094,7 +2209,6 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
 	const char *fn;
 	int ifindex;
 	gsize fn_len;
-	gsize i;
 	char buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/") + 30 + 3] = NM_CONFIG_DEVICE_STATE_DIR"/";
 	char *buf_p = &buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/")];
 
@@ -2105,24 +2219,20 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
 		return;
 
 	while ((fn = g_dir_read_name (dir))) {
-		fn_len = strlen (fn);
-
-		/* skip over file names that are not plain integers. */
-		for (i = 0; i < fn_len; i++) {
-			if (!g_ascii_isdigit (fn[i]))
-				break;
-		}
-		if (fn_len == 0 || i != fn_len)
+		ifindex = _device_state_parse_filename (fn);
+		if (ifindex <= 0)
 			continue;
-
-		ifindex = _nm_utils_ascii_str_to_int64 (fn, 10, 1, G_MAXINT, 0);
-		if (!ifindex)
-			continue;
-
 		if (g_hash_table_contains (seen_ifindexes, GINT_TO_POINTER (ifindex)))
 			continue;
 
-		memcpy (buf_p, fn, fn_len + 1);
+		fn_len = strlen (fn) + 1;
+		nm_assert (&buf_p[fn_len] < &buf[G_N_ELEMENTS (buf)]);
+		memcpy (buf_p, fn, fn_len);
+		nm_assert (({
+		                char bb[30];
+		                nm_sprintf_buf (bb, "%d", ifindex);
+		                nm_streq0 (bb, buf_p);
+		           }));
 		_LOGT ("device-state: prune #%d (%s)", ifindex, buf);
 		(void) unlink (buf);
 	}
@@ -2132,6 +2242,46 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
 
 /*****************************************************************************/
 
+static GHashTable *
+_device_state_get_all (NMConfig *self)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
+
+	if (G_UNLIKELY (!priv->device_states))
+		priv->device_states = nm_config_device_state_load_all ();
+	return priv->device_states;
+}
+
+/**
+ * nm_config_device_state_get_all:
+ * @self: the #NMConfig
+ *
+ * This function exists to give convenient access to all
+ * device states. Do not ever try to modify the returned
+ * hash, it's supposed to be immutable.
+ *
+ * Returns: the internal #GHashTable object with all device states.
+ */
+const GHashTable *
+nm_config_device_state_get_all (NMConfig *self)
+{
+	g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
+
+	return _device_state_get_all (self);
+}
+
+const NMConfigDeviceStateData *
+nm_config_device_state_get (NMConfig *self,
+                            int ifindex)
+{
+	g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
+	g_return_val_if_fail (ifindex > 0 , NULL);
+
+	return g_hash_table_lookup (_device_state_get_all (self), GINT_TO_POINTER (ifindex));
+}
+
+/*****************************************************************************/
+
 void
 nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags)
 {