diff options
| author | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
| commit | 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (patch) | |
| tree | cce7b0b02d28fae2df9fdf2c1804cacd1500f2d7 /libnm-core/nm-utils.c | |
| parent | 9a6dcbf895f9da01768e64b73cec88c16157d91e (diff) | |
New upstream version 1.18.0 upstream/1.18.0
Diffstat (limited to 'libnm-core/nm-utils.c')
| -rw-r--r-- | libnm-core/nm-utils.c | 422 |
1 files changed, 305 insertions, 117 deletions
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index d276cfe6..04d5b1b5 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -37,15 +37,16 @@ #include "nm-json.h" #endif -#include "nm-utils/nm-enum-utils.h" -#include "nm-utils/nm-secret-utils.h" +#include "nm-glib-aux/nm-enum-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" #include "systemd/nm-sd-utils-shared.h" -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" #include "nm-crypto.h" #include "nm-setting-bond.h" #include "nm-setting-bridge.h" +#include "nm-setting-bridge-port.h" #include "nm-setting-infiniband.h" #include "nm-setting-ip6-config.h" #include "nm-setting-team.h" @@ -1034,30 +1035,6 @@ _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray) return strv; } -/** - * _nm_utils_strv_equal: - * @strv1: a string array - * @strv2: a string array - * - * Compare NULL-terminated string arrays for equality. - * - * Returns: %TRUE if the arrays are equal, %FALSE otherwise. - **/ -gboolean -_nm_utils_strv_equal (char **strv1, char **strv2) -{ - if (strv1 == strv2) - return TRUE; - - if (!strv1 || !strv2) - return FALSE; - - for ( ; *strv1 && *strv2 && !strcmp (*strv1, *strv2); strv1++, strv2++) - ; - - return !*strv1 && !*strv2; -} - static gboolean device_supports_ap_ciphers (guint32 dev_caps, guint32 ap_flags, @@ -2332,6 +2309,15 @@ static const NMVariantAttributeSpec * const tc_object_attribute_spec[] = { NULL, }; +typedef struct { + const char *kind; + const NMVariantAttributeSpec * const *attrs; +} NMQdiscAttributeSpec; + +static const NMQdiscAttributeSpec *const tc_qdisc_attribute_spec[] = { + NULL, +}; + /*****************************************************************************/ /** @@ -2348,6 +2334,7 @@ _nm_utils_string_append_tc_qdisc_rest (GString *string, NMTCQdisc *qdisc) { guint32 handle = nm_tc_qdisc_get_handle (qdisc); const char *kind = nm_tc_qdisc_get_kind (qdisc); + gs_free char *str = NULL; if (handle != TC_H_UNSPEC && strcmp (kind, "ingress") != 0) { g_string_append (string, "handle "); @@ -2356,6 +2343,13 @@ _nm_utils_string_append_tc_qdisc_rest (GString *string, NMTCQdisc *qdisc) } g_string_append (string, kind); + + str = nm_utils_format_variant_attributes (_nm_tc_qdisc_get_attributes (qdisc), + ' ', ' '); + if (str) { + g_string_append_c (string, ' '); + g_string_append (string, str); + } } /** @@ -2463,7 +2457,7 @@ _tc_read_common_opts (const char *str, * @str: the string representation of a qdisc * @error: location of the error * - * Parces the tc style string qdisc representation of the queueing + * Parses the tc style string qdisc representation of the queueing * discipline to a %NMTCQdisc instance. Supports a subset of the tc language. * * Returns: the %NMTCQdisc or %NULL @@ -2478,21 +2472,36 @@ nm_utils_tc_qdisc_from_str (const char *str, GError **error) gs_free char *kind = NULL; gs_free char *rest = NULL; NMTCQdisc *qdisc = NULL; - gs_unref_hashtable GHashTable *ht = NULL; + gs_unref_hashtable GHashTable *options = NULL; + GHashTableIter iter; + gpointer key, value; + guint i; nm_assert (str); nm_assert (!error || !*error); - ht = nm_utils_parse_variant_attributes (str, - ' ', ' ', FALSE, - tc_object_attribute_spec, - error); - if (!ht) - return NULL; - if (!_tc_read_common_opts (str, &handle, &parent, &kind, &rest, error)) return NULL; + for (i = 0; rest && tc_qdisc_attribute_spec[i]; i++) { + if (strcmp (tc_qdisc_attribute_spec[i]->kind, kind) == 0) { + options = nm_utils_parse_variant_attributes (rest, + ' ', ' ', FALSE, + tc_qdisc_attribute_spec[i]->attrs, + error); + if (!options) + return NULL; + break; + } + } + nm_clear_pointer (&rest, g_free); + + if (options) { + value = g_hash_table_lookup (options, ""); + if (value) + rest = g_variant_dup_string (value, NULL); + } + if (rest) { g_set_error (error, 1, 0, _("unsupported qdisc option: '%s'."), rest); return NULL; @@ -2504,8 +2513,15 @@ nm_utils_tc_qdisc_from_str (const char *str, GError **error) nm_tc_qdisc_set_handle (qdisc, handle); + if (options) { + g_hash_table_iter_init (&iter, options); + while (g_hash_table_iter_next (&iter, &key, &value)) + nm_tc_qdisc_set_attribute (qdisc, key, g_variant_ref_sink (value)); + } + return qdisc; } + /*****************************************************************************/ static const NMVariantAttributeSpec * const tc_action_simple_attribute_spec[] = { @@ -2522,24 +2538,14 @@ static const NMVariantAttributeSpec * const tc_action_attribute_spec[] = { static gboolean _string_append_tc_action (GString *string, NMTCAction *action, GError **error) { - gs_unref_hashtable GHashTable *ht = NULL; const char *kind = nm_tc_action_get_kind (action); - gs_strfreev char **attr_names = NULL; gs_free char *str = NULL; - int i; - - ht = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); g_string_append (string, kind); - attr_names = nm_tc_action_get_attribute_names (action); - for (i = 0; attr_names[i]; i++) { - g_hash_table_insert (ht, attr_names[i], - nm_tc_action_get_attribute (action, attr_names[i])); - } - - if (i) { - str = nm_utils_format_variant_attributes (ht, ' ', ' '); + str = nm_utils_format_variant_attributes (_nm_tc_action_get_attributes (action), + ' ', ' '); + if (str) { g_string_append_c (string, ' '); g_string_append (string, str); } @@ -2578,7 +2584,7 @@ nm_utils_tc_action_to_str (NMTCAction *action, GError **error) * @str: the string representation of a action * @error: location of the error * - * Parces the tc style string action representation of the queueing + * Parses the tc style string action representation of the queueing * discipline to a %NMTCAction instance. Supports a subset of the tc language. * * Returns: the %NMTCAction or %NULL @@ -2731,7 +2737,7 @@ static const NMVariantAttributeSpec * const tc_tfilter_attribute_spec[] = { * @str: the string representation of a tfilter * @error: location of the error * - * Parces the tc style string tfilter representation of the queueing + * Parses the tc style string tfilter representation of the queueing * discipline to a %NMTCTfilter instance. Supports a subset of the tc language. * * Returns: the %NMTCTfilter or %NULL @@ -2872,7 +2878,7 @@ _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error) gs_free const char **vlans = NULL; guint i; - vlans = nm_utils_strsplit_set (str, ";", FALSE); + vlans = nm_utils_strsplit_set (str, ";"); if (!vlans) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -2986,8 +2992,7 @@ nm_utils_sriov_vf_from_str (const char *str, GError **error) detail = strchr (str, ' '); if (detail) { - index_free = g_strndup (str, detail - str); - str = index_free; + str = nm_strndup_a (200, str, detail - str, &index_free); detail++; } @@ -4565,6 +4570,29 @@ _nm_utils_generate_mac_address_mask_parse (const char *value, /*****************************************************************************/ +gboolean +nm_utils_is_valid_iface_name_utf8safe (const char *utf8safe_name) +{ + gs_free gpointer bin_to_free = NULL; + gconstpointer bin; + gsize len; + + g_return_val_if_fail (utf8safe_name, FALSE); + + bin = nm_utils_buf_utf8safe_unescape (utf8safe_name, &len, &bin_to_free); + + if (bin_to_free) { + /* some unescaping happened... */ + + if (len != strlen (bin)) { + /* there are embedded NUL chars. Invalid. */ + return FALSE; + } + } + + return nm_utils_is_valid_iface_name (bin, NULL); +} + /** * nm_utils_is_valid_iface_name: * @name: Name of interface @@ -5430,11 +5458,11 @@ _json_team_add_defaults (json_t *json, if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) { _json_add_object (json, "notify_peers", "count", NULL, - json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT)); + json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT)); _json_add_object (json, "mcast_rejoin", "count", NULL, - json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT)); + json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT)); } else if ( nm_streq (runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) - || nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { + || nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { json_element = json_array (); json_array_append_new (json_element, json_string ("eth")); json_array_append_new (json_element, json_string ("ipv4")); @@ -5538,6 +5566,7 @@ _json_team_normalize_defaults (json_t *json, gboolean reset) { json_t *json_element; const char *runner = NM_SETTING_TEAM_RUNNER_DEFAULT; + gs_free char *runner_free = NULL; int notify_peers_count = 0, notify_peers_interval = 0; int mcast_rejoin_count = 0, mcast_rejoin_interval = 0; int runner_tx_balancer_interval = -1; @@ -5546,7 +5575,8 @@ _json_team_normalize_defaults (json_t *json, gboolean reset) json_element = _json_find_object (json, "runner", "name", NULL); if (json_element) { - runner = json_string_value (json_element); + runner_free = g_strdup (json_string_value (json_element)); + runner = runner_free; _json_delete_object_on_string_match (json, "runner", "name", NULL, NM_SETTING_TEAM_RUNNER_DEFAULT); } @@ -5922,11 +5952,9 @@ _nm_utils_team_config_get (const char *conf, if (json_is_string (str_element)) g_ptr_array_add (data, g_strdup (json_string_value (str_element))); } - if (data->len) { - g_value_init (value, G_TYPE_STRV); - g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (data)); - } - g_ptr_array_free (data, TRUE); + g_ptr_array_add (data, NULL); + g_value_init (value, G_TYPE_STRV); + g_value_take_boxed (value, g_ptr_array_free (data, FALSE)); } else { g_assert_not_reached (); g_free (value); @@ -5948,14 +5976,13 @@ _nm_utils_team_config_set (char **conf, const char *key3, const GValue *value) { - json_t *json, *json_element, *json_link, *json_value = NULL; + nm_auto_decref_json json_t *json = NULL; + nm_auto_decref_json json_t *json_value = NULL; + json_t *json_element; + json_t *json_link; json_error_t jerror; - gboolean updated = FALSE; - char **strv; - GPtrArray *array; const char *iter_key = key; - int i; - NMTeamLinkWatcher *watcher; + gs_free char *conf_new = NULL; g_return_val_if_fail (key, FALSE); @@ -5966,14 +5993,12 @@ _nm_utils_team_config_set (char **conf, if (!json) return FALSE; - /* no new value? delete element */ if (!value) { - updated = _json_del_object (json, key, key2, key3); + if (!_json_del_object (json, key, key2, key3)) + return FALSE; goto done; } - /* insert new value */ - updated = TRUE; if (G_VALUE_HOLDS_STRING (value)) json_value = json_string (g_value_get_string (value)); else if (G_VALUE_HOLDS_INT (value)) @@ -5982,53 +6007,56 @@ _nm_utils_team_config_set (char **conf, json_value = json_boolean (g_value_get_boolean (value)); else if (G_VALUE_HOLDS_BOXED (value)) { if (nm_streq (key, "link_watch")) { + gboolean has_array = FALSE; + GPtrArray *array; + guint i; + array = g_value_get_boxed (value); - if (!array || !array->len) { - updated = FALSE; - goto done; - } + if (!array || !array->len) + return FALSE; - /* - * json_value: will hold the final link_watcher json (array) object - * json_element: is the next link_watcher to append to json_value - * json_link: used to transit the json_value from a single link_watcher - * object to an array of link watcher objects - */ - json_value = NULL; for (i = 0; i < array->len; i++) { - watcher = array->pdata[i]; - json_element = _nm_utils_team_link_watcher_to_json (watcher); - if (!json_element) + json_t *el; + + el = _nm_utils_team_link_watcher_to_json (array->pdata[i]); + if (!el) continue; + /* if there is only one watcher, it is added as-is. If there + * are multiple watchers, they are added in an array. */ if (!json_value) { - json_value = json_element; + json_value = el; continue; } - if (!json_is_array (json_value)) { - json_link = json_value; - json_value = json_array (); - json_array_append_new (json_value, json_link); + if (!has_array) { + json_t *el_arr; + + has_array = TRUE; + el_arr = json_array(); + json_array_append_new (el_arr, json_value); + json_value = el_arr; } - json_array_append_new (json_value, json_element); + json_array_append_new (json_value, el); } } else if ( nm_streq (key, "runner") && nm_streq0 (key2, "tx_hash")) { + const char *const*strv; + gsize i; + strv = g_value_get_boxed (value); - if (!strv) { - updated = FALSE; - goto done; - } + if (!strv) + return FALSE; + json_value = json_array (); for (i = 0; strv[i]; i++) json_array_append_new (json_value, json_string (strv[i])); } else { - updated = FALSE; - goto done; + nm_assert_not_reached (); + return FALSE; } + } else { /* G_VALUE_HOLDS_? */ - g_assert_not_reached (); - updated = FALSE; - goto done; + nm_assert_not_reached (); + return FALSE; } /* Simplest case: first level key only */ @@ -6054,22 +6082,19 @@ _nm_utils_team_config_set (char **conf, iter_key = key3; } - json_object_set_new (json_element, iter_key, json_value); + json_object_set_new (json_element, iter_key, g_steal_pointer (&json_value)); done: - if (updated) { - _json_team_normalize_defaults (json, ( nm_streq0 (key, "runner") - && nm_streq0 (key2, "name"))); - g_free (*conf); - *conf = json_dumps (json, JSON_PRESERVE_ORDER); - /* Don't save an empty config */ - if (nm_streq0 (*conf, "{}")) { - g_free (*conf); - *conf = NULL; - } - } - json_decref (json); - return updated; + _json_team_normalize_defaults (json, ( nm_streq0 (key, "runner") + && nm_streq0 (key2, "name"))); + conf_new = json_dumps (json, JSON_PRESERVE_ORDER); + if (nm_streq0 (conf_new, "{}")) + nm_clear_g_free (&conf_new); + if (nm_streq0 (conf_new, *conf)) + return FALSE; + g_free (*conf); + *conf = g_steal_pointer (&conf_new); + return TRUE; } #else /* !WITH_JSON_VALIDATION */ @@ -6701,7 +6726,7 @@ nm_utils_base64secret_decode (const char *base64_key, base64_key_len = strlen (base64_key); - r = nm_sd_utils_unbase64mem (base64_key, base64_key_len, &bin_arr, &bin_len); + r = nm_sd_utils_unbase64mem (base64_key, base64_key_len, TRUE, &bin_arr, &bin_len); if (r < 0) return FALSE; if (bin_len != required_key_len) { @@ -6740,3 +6765,166 @@ nm_utils_base64secret_normalize (const char *base64_key, nm_explicit_bzero (buf, required_key_len); return TRUE; } + +GVariant * +_nm_utils_bridge_vlans_to_dbus (NMSetting *setting, const char *property) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + GVariantBuilder builder; + guint i; + + g_object_get (setting, property, &vlans, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + if (vlans) { + for (i = 0; i < vlans->len; i++) { + NMBridgeVlan *vlan = vlans->pdata[i]; + GVariantBuilder vlan_builder; + guint16 vid_start, vid_end; + + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); + + g_variant_builder_init (&vlan_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&vlan_builder, "{sv}", "vid-start", + g_variant_new_uint16 (vid_start)); + g_variant_builder_add (&vlan_builder, "{sv}", "vid-end", + g_variant_new_uint16 (vid_end)); + g_variant_builder_add (&vlan_builder, "{sv}", "pvid", + g_variant_new_boolean (nm_bridge_vlan_is_pvid (vlan))); + g_variant_builder_add (&vlan_builder, "{sv}", "untagged", + g_variant_new_boolean (nm_bridge_vlan_is_untagged (vlan))); + g_variant_builder_add (&builder, "a{sv}", &vlan_builder); + } + } + + return g_variant_builder_end (&builder); +} + +gboolean +_nm_utils_bridge_vlans_from_dbus (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + gs_unref_ptrarray GPtrArray *vlans = NULL; + GVariantIter vlan_iter; + GVariant *vlan_var; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), FALSE); + + vlans = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_bridge_vlan_unref); + g_variant_iter_init (&vlan_iter, value); + while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) { + _nm_unused gs_unref_variant GVariant *var_unref = vlan_var; + NMBridgeVlan *vlan; + guint16 vid_start, vid_end; + gboolean pvid = FALSE, untagged = FALSE; + + if (!g_variant_lookup (vlan_var, "vid-start", "q", &vid_start)) + continue; + if ( vid_start < NM_BRIDGE_VLAN_VID_MIN + || vid_start > NM_BRIDGE_VLAN_VID_MAX) + continue; + + if (!g_variant_lookup (vlan_var, "vid-end", "q", &vid_end)) + continue; + if ( vid_end < NM_BRIDGE_VLAN_VID_MIN + || vid_end > NM_BRIDGE_VLAN_VID_MAX) + continue; + if (vid_start > vid_end) + continue; + + g_variant_lookup (vlan_var, "pvid", "b", &pvid); + if (pvid && vid_start != vid_end) + continue; + g_variant_lookup (vlan_var, "untagged", "b", &untagged); + + vlan = nm_bridge_vlan_new (vid_start, vid_end); + nm_bridge_vlan_set_untagged (vlan, untagged); + nm_bridge_vlan_set_pvid (vlan, pvid); + g_ptr_array_add (vlans, vlan); + } + + g_object_set (setting, property, vlans, NULL); + + return TRUE; +} + +gboolean +_nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, + gboolean check_normalizable, + GError **error, + const char *setting, + const char *property) +{ + guint i; + gs_unref_hashtable GHashTable *h = NULL; + gboolean pvid_found = FALSE; + + if ( !vlans + || vlans->len <= 1) + return TRUE; + + if (check_normalizable) { + guint16 vid_prev_end, vid_start, vid_end; + + nm_assert (_nm_utils_bridge_vlan_verify_list (vlans, FALSE, NULL, setting, property)); + + nm_bridge_vlan_get_vid_range (vlans->pdata[0], NULL, &vid_prev_end); + for (i = 1; i < vlans->len; i++) { + const NMBridgeVlan *vlan = vlans->pdata[i]; + + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); + + if (vid_prev_end > vid_start) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("Bridge VLANs %d and %d are not sorted by ascending vid"), + vid_prev_end, + vid_start); + g_prefix_error (error, "%s.%s: ", setting, property); + return FALSE; + } + + vid_prev_end = vid_end; + } + return TRUE; + } + + h = g_hash_table_new (nm_direct_hash, NULL); + for (i = 0; i < vlans->len; i++) { + NMBridgeVlan *vlan = vlans->pdata[i]; + guint16 v, vid_start, vid_end; + + nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end); + + for (v = vid_start; v <= vid_end; v++) { + if (!nm_g_hash_table_add (h, GUINT_TO_POINTER (v))) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("duplicate bridge VLAN vid %u"), v); + g_prefix_error (error, "%s.%s: ", setting, property); + return FALSE; + } + } + + if (nm_bridge_vlan_is_pvid (vlan)) { + if ( vid_start != vid_end + || pvid_found) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("only one VLAN can be the PVID")); + g_prefix_error (error, "%s.%s: ", setting, property); + return FALSE; + } + pvid_found = TRUE; + } + } + + return TRUE; +} |