]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: introduce struct TClass to prepare for supporting tc class
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 11 Feb 2020 14:52:24 +0000 (23:52 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 2 Mar 2020 06:46:28 +0000 (15:46 +0900)
src/network/meson.build
src/network/networkd-network-gperf.gperf
src/network/tc/tc.c
src/network/tc/tclass.c [new file with mode: 0644]
src/network/tc/tclass.h [new file with mode: 0644]

index ce0e7fb0ddd3152cd52166c3428e215d828379df..407ec246c180edf0ae93743e2712f44d96b7bbd4 100644 (file)
@@ -127,6 +127,8 @@ sources = files('''
         tc/tc-util.h
         tc/tc.c
         tc/tc.h
+        tc/tclass.c
+        tc/tclass.h
         tc/teql.c
         tc/teql.h
 '''.split())
index 4ebf285a6e67fcb4511d705e854e00926e4a598c..4f8a654cc3a930230ff6b516b2f54d01aba584e3 100644 (file)
@@ -14,6 +14,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "networkd-ndisc.h"
 #include "networkd-network.h"
 #include "qdisc.h"
+#include "tclass.h"
 #include "vlan-util.h"
 %}
 struct ConfigPerfItem;
index e90ebf30a83c3f5cdb6984db4679ef63643d3acc..30a00133d6579a9dbfe54956ef04ccf8bdfbd463 100644 (file)
@@ -3,6 +3,7 @@
 #include "macro.h"
 #include "qdisc.h"
 #include "tc.h"
+#include "tclass.h"
 
 void traffic_control_free(TrafficControl *tc) {
         if (!tc)
@@ -12,6 +13,9 @@ void traffic_control_free(TrafficControl *tc) {
         case TC_KIND_QDISC:
                 qdisc_free(TC_TO_QDISC(tc));
                 break;
+        case TC_KIND_TCLASS:
+                tclass_free(TC_TO_TCLASS(tc));
+                break;
         default:
                 assert_not_reached("Invalid traffic control type");
         }
@@ -24,6 +28,8 @@ int traffic_control_configure(Link *link, TrafficControl *tc) {
         switch(tc->kind) {
         case TC_KIND_QDISC:
                 return qdisc_configure(link, TC_TO_QDISC(tc));
+        case TC_KIND_TCLASS:
+                return tclass_configure(link, TC_TO_TCLASS(tc));
         default:
                 assert_not_reached("Invalid traffic control type");
         }
@@ -35,6 +41,8 @@ int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, boo
         switch(tc->kind) {
         case TC_KIND_QDISC:
                 return qdisc_section_verify(TC_TO_QDISC(tc), qdisc_has_root, qdisc_has_clsact);
+        case TC_KIND_TCLASS:
+                return tclass_section_verify(TC_TO_TCLASS(tc));
         default:
                 assert_not_reached("Invalid traffic control type");
         }
diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c
new file mode 100644 (file)
index 0000000..2364379
--- /dev/null
@@ -0,0 +1,276 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tc-util.h"
+#include "tclass.h"
+
+const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
+};
+
+static int tclass_new(TClassKind kind, TClass **ret) {
+        TClass *tclass;
+        int r;
+
+        tclass = malloc0(tclass_vtable[kind]->object_size);
+        if (!tclass)
+                return -ENOMEM;
+
+        tclass->meta.kind = TC_KIND_TCLASS,
+        tclass->parent = TC_H_ROOT;
+        tclass->kind = kind;
+
+        if (TCLASS_VTABLE(tclass)->init) {
+                r = TCLASS_VTABLE(tclass)->init(tclass);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(tclass);
+
+        return 0;
+}
+
+int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
+        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(tclass_freep) TClass *tclass = NULL;
+        TrafficControl *existing;
+        int r;
+
+        assert(network);
+        assert(ret);
+        assert(filename);
+        assert(section_line > 0);
+
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        existing = ordered_hashmap_get(network->tc_by_section, n);
+        if (existing) {
+                TClass *t;
+
+                if (existing->kind != TC_KIND_TCLASS)
+                        return -EINVAL;
+
+                t = TC_TO_TCLASS(existing);
+
+                if (t->kind != kind)
+                        return -EINVAL;
+
+                *ret = t;
+                return 0;
+        }
+
+        r = tclass_new(kind, &tclass);
+        if (r < 0)
+                return r;
+
+        tclass->network = network;
+        tclass->section = TAKE_PTR(n);
+
+        r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(tclass);
+        return 0;
+}
+
+void tclass_free(TClass *tclass) {
+        if (!tclass)
+                return;
+
+        if (tclass->network && tclass->section)
+                ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
+
+        network_config_section_free(tclass->section);
+
+        free(tclass);
+}
+
+static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->tc_messages > 0);
+        link->tc_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_error_errno(link, m, r, "Could not set TClass");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->tc_messages == 0) {
+                log_link_debug(link, "Traffic control configured");
+                link->tc_configured = true;
+                link_check_ready(link);
+        }
+
+        return 1;
+}
+
+int tclass_configure(Link *link, TClass *tclass) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+        assert(link->ifindex > 0);
+
+        r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m");
+
+        r = sd_rtnl_message_set_tclass_parent(req, tclass->parent);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
+
+        if (tclass->classid != TC_H_UNSPEC) {
+                r = sd_rtnl_message_set_tclass_handle(req, tclass->classid);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
+        }
+
+        r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+
+        if (TCLASS_VTABLE(tclass)->fill_message) {
+                r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req);
+                if (r < 0)
+                        return r;
+        }
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_handler, link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+        link->tc_messages++;
+
+        return 0;
+}
+
+int tclass_section_verify(TClass *tclass) {
+        int r;
+
+        assert(tclass);
+
+        if (section_is_invalid(tclass->section))
+                return -EINVAL;
+
+        if (TCLASS_VTABLE(tclass)->verify) {
+                r = TCLASS_VTABLE(tclass)->verify(tclass);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int config_parse_tclass_parent(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(ltype, network, filename, section_line, &tclass);
+        if (r < 0)
+                return r;
+
+        if (streq(rvalue, "root"))
+                tclass->parent = TC_H_ROOT;
+        else {
+                r = parse_handle(rvalue, &tclass->parent);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Failed to parse 'Parent=', ignoring assignment: %s",
+                                   rvalue);
+                        return 0;
+                }
+        }
+
+        tclass = NULL;
+
+        return 0;
+}
+
+int config_parse_tclass_classid(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(ltype, network, filename, section_line, &tclass);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                tclass->classid = TC_H_UNSPEC;
+                tclass = NULL;
+                return 0;
+        }
+
+        r = parse_handle(rvalue, &tclass->classid);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse 'ClassId=', ignoring assignment: %s",
+                           rvalue);
+                return 0;
+        }
+
+        tclass = NULL;
+
+        return 0;
+}
diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h
new file mode 100644 (file)
index 0000000..9698e58
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd-util.h"
+#include "tc.h"
+
+typedef enum TClassKind {
+        TCLASS_KIND_HTB,
+        _TCLASS_KIND_MAX,
+        _TCLASS_KIND_INVALID = -1,
+} TClassKind;
+
+typedef struct TClass {
+        TrafficControl meta;
+
+        NetworkConfigSection *section;
+        Network *network;
+
+        uint32_t classid;
+        uint32_t parent;
+
+        TClassKind kind;
+} TClass;
+
+typedef struct TClassVTable {
+        size_t object_size;
+        const char *tca_kind;
+        /* called in tclass_new() */
+        int (*init)(TClass *tclass);
+        int (*fill_message)(Link *link, TClass *tclass, sd_netlink_message *m);
+        int (*verify)(TClass *tclass);
+} TClassVTable;
+
+extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX];
+
+#define TCLASS_VTABLE(t) ((t)->kind != _TCLASS_KIND_INVALID ? tclass_vtable[(t)->kind] : NULL)
+
+/* For casting a tclass into the various tclass kinds */
+#define DEFINE_TCLASS_CAST(UPPERCASE, MixedCase)                          \
+        static inline MixedCase* TCLASS_TO_##UPPERCASE(TClass *t) {       \
+                if (_unlikely_(!t || t->kind != TCLASS_KIND_##UPPERCASE)) \
+                        return NULL;                                      \
+                                                                          \
+                return (MixedCase*) t;                                    \
+        }
+
+/* For casting the various tclass kinds into a tclass */
+#define TCLASS(t) (&(t)->meta)
+
+void tclass_free(TClass *tclass);
+int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
+
+int tclass_configure(Link *link, TClass *tclass);
+int tclass_section_verify(TClass *tclass);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free);
+
+DEFINE_TC_CAST(TCLASS, TClass);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent);
+CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid);
+
+#include "htb.h"