summary refs log tree commit diff
path: root/libnm-core/nm-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core/nm-utils.c')
-rw-r--r--libnm-core/nm-utils.c466
1 files changed, 436 insertions, 30 deletions
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 1080d9ea..a31a9190 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -33,6 +33,11 @@
 #include <gmodule.h>
 #include <sys/stat.h>
 
+#if WITH_JANSSON
+#include <jansson.h>
+#endif
+
+#include "nm-common-macros.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
 #include "crypto.h"
@@ -438,12 +443,6 @@ nm_utils_same_ssid (const guint8 *ssid1, gsize len1,
 	return memcmp (ssid1, ssid2, len1) == 0 ? TRUE : FALSE;
 }
 
-gboolean
-_nm_utils_string_in_list (const char *str, const char **valid_strings)
-{
-	return _nm_utils_strv_find_first ((char **) valid_strings, -1, str) >= 0;
-}
-
 /**
  * _nm_utils_strv_find_first:
  * @list: the strv list to search
@@ -526,7 +525,7 @@ _nm_utils_string_slist_validate (GSList *list, const char **valid_values)
 	GSList *iter;
 
 	for (iter = list; iter; iter = iter->next) {
-		if (!_nm_utils_string_in_list ((char *) iter->data, valid_values))
+		if (!g_strv_contains (valid_values, (char *) iter->data))
 			return FALSE;
 	}
 
@@ -3068,24 +3067,15 @@ nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize length)
 		return NULL;
 }
 
-/**
- * nm_utils_hwaddr_ntoa:
- * @addr: (type guint8) (array length=length): a binary hardware address
- * @length: the length of @addr
- *
- * Converts @addr to textual form.
- *
- * Return value: (transfer full): the textual form of @addr
- */
-char *
-nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length)
+static char *
+_bin2str (gconstpointer addr, gsize length, gboolean upper_case)
 {
 	const guint8 *in = addr;
 	char *out, *result;
-	const char *LOOKUP = "0123456789ABCDEF";
+	const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef";
 
 	g_return_val_if_fail (addr != NULL, g_strdup (""));
-	g_return_val_if_fail (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX, g_strdup (""));
+	g_return_val_if_fail (length > 0, g_strdup (""));
 
 	result = out = g_malloc (length * 3);
 	while (length--) {
@@ -3101,6 +3091,37 @@ nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length)
 	return result;
 }
 
+/**
+ * nm_utils_hwaddr_ntoa:
+ * @addr: (type guint8) (array length=length): a binary hardware address
+ * @length: the length of @addr
+ *
+ * Converts @addr to textual form.
+ *
+ * Return value: (transfer full): the textual form of @addr
+ */
+char *
+nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length)
+{
+	return _bin2str (addr, length, TRUE);
+}
+
+/**
+ * _nm_utils_bin2str:
+ * @addr: (type guint8) (array length=length): a binary hardware address
+ * @length: the length of @addr
+ * @upper_case: the case for the hexadecimal digits.
+ *
+ * Converts @addr to textual form.
+ *
+ * Return value: (transfer full): the textual form of @addr
+ */
+char *
+_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case)
+{
+	return _bin2str (addr, length, upper_case);
+}
+
 static int
 hwaddr_binary_len (const char *asc)
 {
@@ -3117,6 +3138,33 @@ hwaddr_binary_len (const char *asc)
 }
 
 /**
+ * _nm_utils_hwaddr_length:
+ * @asc: the ASCII representation of the hardware address
+ *
+ * Validates that @asc is a valid representation of a hardware
+ * address up to (including) %NM_UTILS_HWADDR_LEN_MAX bytes.
+ *
+ * Returns: binary length of the hardware address @asc or
+ *   0 on error.
+ */
+guint
+_nm_utils_hwaddr_length (const char *asc)
+{
+	int l;
+
+	if (!asc)
+		return 0;
+
+	l = hwaddr_binary_len (asc);
+	if (l <= 0 || l > NM_UTILS_HWADDR_LEN_MAX)
+		return 0;
+
+	if (!nm_utils_hwaddr_valid (asc, l))
+		return 0;
+	return l;
+}
+
+/**
  * nm_utils_hwaddr_valid:
  * @asc: the ASCII representation of a hardware address
  * @length: the length of address that @asc is expected to convert to
@@ -3288,24 +3336,124 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1,
 	return !memcmp (hwaddr1, hwaddr2, hwaddr1_len);
 }
 
-GVariant *
-_nm_utils_hwaddr_to_dbus (const GValue *prop_value)
+/*****************************************************************************/
+
+static GVariant *
+_nm_utils_hwaddr_to_dbus_impl (const char *str)
 {
-	const char *str = g_value_get_string (prop_value);
 	guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
 	int len;
 
-	if (str) {
-		len = hwaddr_binary_len (str);
-		g_return_val_if_fail (len > 0 && len <= NM_UTILS_HWADDR_LEN_MAX, NULL);
-		if (!nm_utils_hwaddr_aton (str, buf, len))
-			len = 0;
-	} else
-		len = 0;
+	if (!str)
+		return NULL;
+
+	len = _nm_utils_hwaddr_length (str);
+	if (len == 0)
+		return NULL;
+
+	if (!nm_utils_hwaddr_aton (str, buf, len))
+		return NULL;
 
 	return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, buf, len, 1);
 }
 
+GVariant *
+_nm_utils_hwaddr_cloned_get (NMSetting     *setting,
+                             const char    *property)
+{
+	gs_free char *addr = NULL;
+
+	nm_assert (nm_streq0 (property, "cloned-mac-address"));
+
+	g_object_get (setting, "cloned-mac-address", &addr, NULL);
+	return _nm_utils_hwaddr_to_dbus_impl (addr);
+}
+
+gboolean
+_nm_utils_hwaddr_cloned_set (NMSetting     *setting,
+                             GVariant      *connection_dict,
+                             const char    *property,
+                             GVariant      *value,
+                             NMSettingParseFlags parse_flags,
+                             GError       **error)
+{
+	gsize length;
+	const guint8 *array;
+	char *str;
+
+	nm_assert (nm_streq0 (property, "cloned-mac-address"));
+
+	if (!_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address"))
+		return TRUE;
+
+	length = 0;
+	array = g_variant_get_fixed_array (value, &length, 1);
+
+	if (!length)
+		return TRUE;
+
+	str = nm_utils_hwaddr_ntoa (array, length);
+	g_object_set (setting,
+	              "cloned-mac-address",
+	              str,
+	              NULL);
+	g_free (str);
+	return TRUE;
+}
+
+gboolean
+_nm_utils_hwaddr_cloned_not_set (NMSetting *setting,
+                                 GVariant      *connection_dict,
+                                 const char    *property,
+                                 NMSettingParseFlags parse_flags,
+                                 GError       **error)
+{
+	nm_assert (nm_streq0 (property, "cloned-mac-address"));
+	return TRUE;
+}
+
+GVariant *
+_nm_utils_hwaddr_cloned_data_synth (NMSetting *setting,
+                                    NMConnection *connection,
+                                    const char *property)
+{
+	gs_free char *addr = NULL;
+
+	nm_assert (nm_streq0 (property, "assigned-mac-address"));
+
+	g_object_get (setting,
+	              "cloned-mac-address",
+	              &addr,
+	              NULL);
+	return addr ? g_variant_new_string (addr) : NULL;
+}
+
+gboolean
+_nm_utils_hwaddr_cloned_data_set (NMSetting *setting,
+                                  GVariant *connection_dict,
+                                  const char *property,
+                                  GVariant *value,
+                                  NMSettingParseFlags parse_flags,
+                                  GError **error)
+{
+	nm_assert (nm_streq0 (property, "assigned-mac-address"));
+
+	if (_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address"))
+		return TRUE;
+
+	g_object_set (setting,
+	              "cloned-mac-address",
+	              g_variant_get_string (value, NULL),
+	              NULL);
+	return TRUE;
+}
+
+GVariant *
+_nm_utils_hwaddr_to_dbus (const GValue *prop_value)
+{
+	return _nm_utils_hwaddr_to_dbus_impl (g_value_get_string (prop_value));
+}
+
 void
 _nm_utils_hwaddr_from_dbus (GVariant *dbus_value,
                             GValue *prop_value)
@@ -3318,6 +3466,93 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value,
 	g_value_take_string (prop_value, str);
 }
 
+/*****************************************************************************/
+
+static char *
+_split_word (char *s)
+{
+	/* takes @s and truncates the string on the first white-space.
+	 * then it returns the first word afterwards (again seeking
+	 * over leading white-space). */
+	for (; s[0]; s++) {
+		if (g_ascii_isspace (s[0])) {
+			s[0] = '\0';
+			s++;
+			while (g_ascii_isspace (s[0]))
+				s++;
+			return s;
+		}
+	}
+	return s;
+}
+
+gboolean
+_nm_utils_generate_mac_address_mask_parse (const char *value,
+                                           struct ether_addr *out_mask,
+                                           struct ether_addr **out_ouis,
+                                           gsize *out_ouis_len,
+                                           GError **error)
+{
+	gs_free char *s_free = NULL;
+	char *s, *s_next;
+	struct ether_addr mask;
+	gs_unref_array GArray *ouis = NULL;
+
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	if (!value || !*value)  {
+		/* NULL and "" are valid values and both mean the default
+		 * "q */
+		if (out_mask) {
+			memset (out_mask, 0, sizeof (*out_mask));
+			out_mask->ether_addr_octet[0] |= 0x02;
+		}
+		NM_SET_OUT (out_ouis, NULL);
+		NM_SET_OUT (out_ouis_len, 0);
+		return TRUE;
+	}
+
+	s_free = g_strdup (value);
+	s = s_free;
+
+	/* skip over leading whitespace */
+	while (g_ascii_isspace (s[0]))
+		s++;
+
+	/* parse the first mask */
+	s_next = _split_word (s);
+	if (!nm_utils_hwaddr_aton (s, &mask, ETH_ALEN)) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             _("not a valid ethernet MAC address for mask at position %lld"),
+		             (long long) (s - s_free));
+		return FALSE;
+	}
+
+	if (s_next[0]) {
+		ouis = g_array_sized_new (FALSE, FALSE, sizeof (struct ether_addr), 4);
+
+		do {
+			s = s_next;
+			s_next = _split_word (s);
+
+			g_array_set_size (ouis, ouis->len + 1);
+			if (!nm_utils_hwaddr_aton (s, &g_array_index (ouis, struct ether_addr, ouis->len - 1), ETH_ALEN)) {
+				g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+				             _("not a valid ethernet MAC address #%u at position %lld"),
+				             ouis->len, (long long) (s - s_free));
+				return FALSE;
+			}
+		} while (s_next[0]);
+	}
+
+	NM_SET_OUT (out_mask, mask);
+	NM_SET_OUT (out_ouis_len, ouis ? ouis->len : 0);
+	NM_SET_OUT (out_ouis, ouis ? ((struct ether_addr *) g_array_free (g_steal_pointer (&ouis), FALSE)) : NULL);
+	return TRUE;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_bin2hexstr:
  * @src: (type guint8) (array length=len): an array of bytes
@@ -3562,6 +3797,40 @@ nm_utils_ipaddr_valid (int family, const char *ip)
 }
 
 /**
+ * nm_utils_iinet6_is_token:
+ * @in6addr: the AF_INET6 address structure
+ *
+ * Checks if only the bottom 64bits of the address are set.
+ *
+ * Return value: %TRUE or %FALSE
+ */
+gboolean
+_nm_utils_inet6_is_token (const struct in6_addr *in6addr)
+{
+	if (   in6addr->s6_addr[0]
+	    || in6addr->s6_addr[1]
+	    || in6addr->s6_addr[2]
+	    || in6addr->s6_addr[3]
+	    || in6addr->s6_addr[4]
+	    || in6addr->s6_addr[5]
+	    || in6addr->s6_addr[6]
+	    || in6addr->s6_addr[7])
+		return FALSE;
+
+	if (   in6addr->s6_addr[8]
+	    || in6addr->s6_addr[9]
+	    || in6addr->s6_addr[10]
+	    || in6addr->s6_addr[11]
+	    || in6addr->s6_addr[12]
+	    || in6addr->s6_addr[13]
+	    || in6addr->s6_addr[14]
+	    || in6addr->s6_addr[15])
+		return TRUE;
+
+	return FALSE;
+}
+
+/**
  * nm_utils_check_virtual_device_compatibility:
  * @virtual_type: a virtual connection type
  * @other_type: a connection type to test against @virtual_type
@@ -4090,3 +4359,140 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to)
 	return (const char **) g_ptr_array_free (array, FALSE);
 }
 
+#if WITH_JANSSON
+gboolean
+_nm_utils_check_valid_json (const char *str, GError **error)
+{
+	json_t *json;
+	json_error_t jerror;
+
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	if (!str || !str[0]) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     "value is NULL or empty");
+		return FALSE;
+	}
+
+	json = json_loads (str, 0, &jerror);
+	if (!json) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             "%s at position %d",
+		             jerror.text,
+		             jerror.position);
+		return FALSE;
+	}
+
+	json_decref (json);
+	return TRUE;
+}
+
+/* json_object_foreach_safe() is only available since Jansson 2.8,
+ * reimplement it */
+#define _json_object_foreach_safe(object, n, key, value) \
+    for (key = json_object_iter_key (json_object_iter (object)), \
+         n = json_object_iter_next (object, json_object_iter_at (object, key)); \
+         key && (value = json_object_iter_value (json_object_iter_at (object, key))); \
+         key = json_object_iter_key (n), \
+         n = json_object_iter_next (object, json_object_iter_at (object, key)))
+
+gboolean
+_nm_utils_team_config_equal (const char *conf1,
+                             const char *conf2,
+                             gboolean port_config)
+{
+	json_t *json1 = NULL, *json2 = NULL, *json;
+	gs_free char *dump1 = NULL, *dump2 = NULL;
+	json_t *value, *property;
+	json_error_t jerror;
+	const char *key;
+	gboolean ret;
+	void *tmp;
+	int i;
+
+	if (nm_streq0 (conf1, conf2))
+		return TRUE;
+
+	/* A NULL configuration is equivalent to default value '{}' */
+	json1 = json_loads (conf1 ?: "{}", 0, &jerror);
+	if (json1)
+		json2 = json_loads (conf2 ?: "{}", 0, &jerror);
+
+	if (!json1 || !json2) {
+		ret = FALSE;
+		goto out;
+	}
+
+	/* Some properties are added by teamd when missing from the initial
+	 * configuration.  Add them with the default value if necessary, depending
+	 * on the configuration type.
+	 */
+	for (i = 0, json = json1; i < 2; i++, json = json2) {
+		if  (port_config) {
+			property = json_object_get (json, "link_watch");
+			if (!property) {
+				property = json_object ();
+				json_object_set_new (property, "name", json_string ("ethtool"));
+				json_object_set_new (json, "link_watch", property);
+			}
+		} else {
+			property = json_object_get (json, "runner");
+			if (!property) {
+				property = json_object ();
+				json_object_set_new (property, "name", json_string ("roundrobin"));
+				json_object_set_new (json, "runner", property);
+			}
+		}
+	}
+
+	/* Only consider a given subset of nodes, others can change depending on
+	 * current state */
+	for (i = 0, json = json1; i < 2; i++, json = json2) {
+		_json_object_foreach_safe (json, tmp, key, value) {
+			if (!NM_IN_STRSET (key, "runner", "link_watch"))
+				json_object_del (json, key);
+		}
+	}
+
+	dump1 = json_dumps (json1, JSON_INDENT(0) | JSON_ENSURE_ASCII | JSON_SORT_KEYS);
+	dump2 = json_dumps (json2, JSON_INDENT(0) | JSON_ENSURE_ASCII | JSON_SORT_KEYS);
+
+	ret = nm_streq0 (dump1, dump2);
+out:
+
+	if (json1)
+		json_decref (json1);
+	if (json2)
+		json_decref (json2);
+
+	return ret;
+}
+
+#else /* WITH_JANSSON */
+
+gboolean
+_nm_utils_check_valid_json (const char *str, GError **error)
+{
+	if (!str || !str[0]) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     "value is NULL or empty");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+_nm_utils_team_config_equal (const char *conf1,
+                             const char *conf2,
+                             gboolean port_config)
+{
+	return nm_streq0 (conf1, conf2);
+}
+#endif