Commit 7c1de05f authored by Lubomir Rintel's avatar Lubomir Rintel

libnm-core: add functionality for dealing with tc-style qdisc specifiers

Tailored to fit both nmcli and keyfile needs.
parent da13c7a1
......@@ -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
......@@ -33,6 +33,7 @@
#include <gmodule.h>
#include <sys/stat.h>
#include <net/if.h>
#include <linux/pkt_sched.h>
#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
......
......@@ -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__ */
......@@ -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 {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment