diff options
Diffstat (limited to 'libnm-core/nm-utils.c')
| -rw-r--r-- | libnm-core/nm-utils.c | 466 |
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 |