From 7c1de05f410af1930f12d0e65e38c258f021fb58 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Sat, 18 Nov 2017 17:17:24 +0100 Subject: [PATCH] libnm-core: add functionality for dealing with tc-style qdisc specifiers Tailored to fit both nmcli and keyfile needs. --- libnm-core/nm-core-internal.h | 10 ++ libnm-core/nm-utils.c | 258 ++++++++++++++++++++++++++++++++++ libnm-core/nm-utils.h | 10 +- libnm/libnm.ver | 2 + 4 files changed, 279 insertions(+), 1 deletion(-) diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 9760ba431..4d714ba8f 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -472,4 +472,14 @@ nm_setting_ip_config_get_addr_family (NMSettingIPConfig *s_ip) /*****************************************************************************/ +guint32 _nm_utils_parse_tc_handle (const char *str, + GError **error); +void _nm_utils_string_append_tc_parent (GString *string, + const char *prefix, + guint32 parent); +void _nm_utils_string_append_tc_qdisc_rest (GString *string, + NMTCQdisc *qdisc); + +/*****************************************************************************/ + #endif diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 30d2602c9..3429b6334 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "nm-utils/nm-jansson.h" #include "nm-utils/nm-enum-utils.h" @@ -2059,6 +2060,263 @@ next: /*****************************************************************************/ +static void +_string_append_tc_handle (GString *string, guint32 handle) +{ + g_string_append_printf (string, "%x:", TC_H_MAJ (handle) >> 16); + if (TC_H_MIN (handle) != TC_H_UNSPEC) + g_string_append_printf (string, "%x", TC_H_MIN (handle)); +} + +/** + * _nm_utils_string_append_tc_parent: + * @string: the string to write the parent handle to + * @prefix: optional prefix for the numeric handle + * @parent: the parent handle + * + * This is used to either write out the parent handle to the tc qdisc string + * or to pretty-format (use symbolic name for root) the key in keyfile. + * The presence of prefix determnines which one is the case. + * + * Private API due to general uglyness and overall uselessness for anything + * sensible. + */ +void +_nm_utils_string_append_tc_parent (GString *string, const char *prefix, guint32 parent) +{ + if (parent == TC_H_ROOT) { + g_string_append (string, "root"); + } else { + if (prefix) { + if (parent == TC_H_INGRESS) + return; + g_string_append_printf (string, "%s ", prefix); + } + _string_append_tc_handle (string, parent); + } + + if (prefix) + g_string_append_c (string, ' '); +} + +/** + * _nm_utils_parse_tc_handle: + * @str: the string representation of a qdisc handle + * @error: location of the error + * + * Parses tc style handle number into a numeric representation. + * Don't use this, use nm_utils_tc_qdisc_from_str() instead. + */ +guint32 +_nm_utils_parse_tc_handle (const char *str, GError **error) +{ + gint64 maj, min; + char *sep; + + maj = g_ascii_strtoll (str, &sep, 0x10); + if (*sep == ':') + min = g_ascii_strtoll (&sep[1], &sep, 0x10); + else + min = 0; + + if (*sep != '\0' || maj <= 0 || maj > 0xffff || min < 0 || min > 0xffff) { + g_set_error (error, 1, 0, _("'%s' is not a valid handle."), str); + return TC_H_UNSPEC; + } + + return TC_H_MAKE (maj << 16, min); +} + +#define TC_ATTR_SPEC_PTR(name, type, no_value, consumes_rest, str_type) \ + &(NMVariantAttributeSpec) { name, type, FALSE, FALSE, no_value, consumes_rest, str_type } + +static const NMVariantAttributeSpec * const tc_object_attribute_spec[] = { + TC_ATTR_SPEC_PTR ("root", G_VARIANT_TYPE_BOOLEAN, TRUE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("parent", G_VARIANT_TYPE_STRING, FALSE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("handle", G_VARIANT_TYPE_STRING, FALSE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("kind", G_VARIANT_TYPE_STRING, TRUE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("", G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a' ), + NULL, +}; + +/*****************************************************************************/ + +/** + * _nm_utils_string_append_tc_qdisc_rest: + * @string: the string to write the formatted qdisc to + * @qdisc: the %NMTCQdisc + * + * This formats the rest of the qdisc string but the parent. Useful to format + * the keyfile value and nowhere else. + * Use nm_utils_tc_qdisc_to_str() that also includes the parent instead. + */ +void +_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); + + if (handle != TC_H_UNSPEC && strcmp (kind, "ingress") != 0) { + g_string_append (string, "handle "); + _string_append_tc_handle (string, handle); + g_string_append_c (string, ' '); + } + + g_string_append (string, kind); +} + +/** + * nm_utils_tc_qdisc_to_str: + * @qdisc: the %NMTCQdisc + * @error: location of the error + * + * Turns the %NMTCQdisc into a tc style string representation of the queueing + * discipline. + * + * Returns: formatted string or %NULL + * + * Since: 1.12 + */ +char * +nm_utils_tc_qdisc_to_str (NMTCQdisc *qdisc, GError **error) +{ + GString *string; + + string = g_string_sized_new (60); + + _nm_utils_string_append_tc_parent (string, "parent", + nm_tc_qdisc_get_parent (qdisc)); + _nm_utils_string_append_tc_qdisc_rest (string, qdisc); + + return g_string_free (string, FALSE); +} + + +static gboolean +_tc_read_common_opts (const char *str, + guint32 *handle, + guint32 *parent, + char **kind, + char **rest, + GError **error) +{ + gs_unref_hashtable GHashTable *ht = NULL; + GVariant *variant; + + ht = nm_utils_parse_variant_attributes (str, + ' ', ' ', FALSE, + tc_object_attribute_spec, + error); + if (!ht) + return FALSE; + + if (g_hash_table_contains (ht, "root")) + *parent = TC_H_ROOT; + + variant = g_hash_table_lookup (ht, "parent"); + if (variant) { + if (*parent != TC_H_UNSPEC) { + g_set_error (error, 1, 0, + _("'%s' unexpected: parent already specified."), + g_variant_get_string (variant, NULL)); + return FALSE; + } + *parent = _nm_utils_parse_tc_handle (g_variant_get_string (variant, NULL), error); + if (*parent == TC_H_UNSPEC) + return FALSE; + } + + variant = g_hash_table_lookup (ht, "handle"); + if (variant) { + *handle = _nm_utils_parse_tc_handle (g_variant_get_string (variant, NULL), error); + if (*handle == TC_H_UNSPEC) + return FALSE; + if (TC_H_MIN (*handle)) { + g_set_error (error, 1, 0, + _("invalid handle: '%s'"), + g_variant_get_string (variant, NULL)); + return FALSE; + } + } + + variant = g_hash_table_lookup (ht, "kind"); + if (variant) { + *kind = g_variant_dup_string (variant, NULL); + if (strcmp (*kind, "ingress") == 0) { + if (*parent == TC_H_UNSPEC) + *parent = TC_H_INGRESS; + if (*handle == TC_H_UNSPEC) + *handle = TC_H_MAKE (TC_H_INGRESS, 0); + } + } + + if (*parent == TC_H_UNSPEC) { + if (*kind) { + g_free (*kind); + *kind = NULL; + } + g_set_error_literal (error, 1, 0, _("parent not specified.")); + return FALSE; + } + + variant = g_hash_table_lookup (ht, ""); + if (variant) + *rest = g_variant_dup_string (variant, NULL); + + return TRUE; +} + +/** + * nm_utils_tc_qdisc_from_str: + * @str: the string representation of a qdisc + * @error: location of the error + * + * Parces 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 + * + * Since: 1.12 + */ +NMTCQdisc * +nm_utils_tc_qdisc_from_str (const char *str, GError **error) +{ + guint32 handle = TC_H_UNSPEC; + guint32 parent = TC_H_UNSPEC; + gs_free char *kind = NULL; + gs_free char *rest = NULL; + NMTCQdisc *qdisc = NULL; + gs_unref_hashtable GHashTable *ht = NULL; + + 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; + + if (rest) { + g_set_error (error, 1, 0, _("unsupported qdisc option: '%s'."), rest); + return NULL; + } + + qdisc = nm_tc_qdisc_new (kind, parent, error); + if (!qdisc) + return NULL; + + nm_tc_qdisc_set_handle (qdisc, handle); + + return qdisc; +} + +/*****************************************************************************/ + /** * nm_utils_uuid_generate_buf_: * @buf: input buffer, must contain at least 37 bytes diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h index 77fe18a90..0d496d7f0 100644 --- a/libnm-core/nm-utils.h +++ b/libnm-core/nm-utils.h @@ -15,7 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2005 - 2014 Red Hat, Inc. + * Copyright 2005 - 2017 Red Hat, Inc. */ #ifndef __NM_UTILS_H__ @@ -35,6 +35,7 @@ #include "nm-core-enum-types.h" #include "nm-setting-wireless-security.h" +#include "nm-setting-tc-config.h" G_BEGIN_DECLS @@ -230,6 +231,13 @@ char * nm_utils_format_variant_attributes (GHashTable *attributes, char attr_separator, char key_value_separator); +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_12 +NMTCQdisc *nm_utils_tc_qdisc_from_str (const char *str, GError **error); +NM_AVAILABLE_IN_1_12 +char *nm_utils_tc_qdisc_to_str (NMTCQdisc *qdisc, GError **error); + G_END_DECLS #endif /* __NM_UTILS_H__ */ diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 0bdc2b796..afb89594c 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1280,6 +1280,8 @@ global: nm_team_link_watcher_new_nsna_ping; nm_team_link_watcher_ref; nm_team_link_watcher_unref; + nm_utils_tc_qdisc_from_str; + nm_utils_tc_qdisc_to_str; } libnm_1_10_0; libnm_1_12_0 {