diff --git a/man/systemd.network.xml b/man/systemd.network.xml index d8485f736f..6a2dd0d04c 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2677,6 +2677,31 @@ + + [TrivialLinkEqualizer] Section Options + The [TrivialLinkEqualizer] section manages the queueing discipline (qdisc) of + trivial link equalizer (teql). + + + + Parent= + + Specifies the parent Queueing Discipline (qdisc). Takes one of root, + clsact or ingress. Defaults to root. + + + + Id= + + Specifies the interface ID N of teql. Defaults to 0. + Note that when teql is used, currently, the module sch_teql with + max_equalizers=N+1 option must be loaded before + systemd-networkd is started. + + + + + [BridgeVLAN] Section Options The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts diff --git a/src/network/meson.build b/src/network/meson.build index 7cb1bb5a38..c1c02cfda1 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -123,6 +123,8 @@ sources = files(''' tc/tbf.h tc/tc-util.c tc/tc-util.h + tc/teql.c + tc/teql.h '''.split()) systemd_networkd_sources = files('networkd.c') diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 81ce9ee1e4..3e2d8d8ee9 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -293,6 +293,8 @@ TokenBucketFilter.MTUBytes, config_parse_token_bucket_filter_si TokenBucketFilter.MPUBytes, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 TokenBucketFilter.PeakRate, config_parse_token_bucket_filter_size, QDISC_KIND_TBF, 0 TokenBucketFilter.LatencySec, config_parse_token_bucket_filter_latency, QDISC_KIND_TBF, 0 +TrivialLinkEqualizer.Parent, config_parse_qdisc_parent, QDISC_KIND_TEQL, 0 +TrivialLinkEqualizer.Id, config_parse_trivial_link_equalizer_id, QDISC_KIND_TEQL, 0 /* backwards compatibility: do not add new entries to this section */ Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 96f9c785ba..b06ae75c05 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -488,7 +488,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi "FairQueueingControlledDelay\0" "NetworkEmulator\0" "StochasticFairnessQueueing\0" - "TokenBucketFilter\0", + "TokenBucketFilter\0" + "TrivialLinkEqualizer\0", config_item_perf_lookup, network_network_gperf_lookup, CONFIG_PARSE_WARN, network); if (r < 0) diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index f1527cc1f5..0d622c8aaf 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -20,6 +20,7 @@ const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = { [QDISC_KIND_NETEM] = &netem_vtable, [QDISC_KIND_SFQ] = &sfq_vtable, [QDISC_KIND_TBF] = &tbf_vtable, + [QDISC_KIND_TEQL] = &teql_vtable, }; static int qdisc_new(QDiscKind kind, QDisc **ret) { @@ -176,13 +177,21 @@ int qdisc_configure(Link *link, QDisc *qdisc) { } if (QDISC_VTABLE(qdisc)) { - r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind); - if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); + if (QDISC_VTABLE(qdisc)->fill_tca_kind) { + r = QDISC_VTABLE(qdisc)->fill_tca_kind(link, qdisc, req); + if (r < 0) + return r; + } else { + r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); + } - r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req); - if (r < 0) - return r; + if (QDISC_VTABLE(qdisc)->fill_message) { + r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req); + if (r < 0) + return r; + } } else { r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind); if (r < 0) diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index bae4d1ab3f..b1753e4d95 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -14,6 +14,7 @@ typedef enum QDiscKind { QDISC_KIND_NETEM, QDISC_KIND_SFQ, QDISC_KIND_TBF, + QDISC_KIND_TEQL, _QDISC_KIND_MAX, _QDISC_KIND_INVALID = -1, } QDiscKind; @@ -35,6 +36,7 @@ typedef struct QDiscVTable { const char *tca_kind; /* called in qdisc_new() */ int (*init)(QDisc *qdisc); + int (*fill_tca_kind)(Link *link, QDisc *qdisc, sd_netlink_message *m); int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m); int (*verify)(QDisc *qdisc); } QDiscVTable; @@ -71,3 +73,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_parent); #include "netem.h" #include "sfq.h" #include "tbf.h" +#include "teql.h" diff --git a/src/network/tc/teql.c b/src/network/tc/teql.c new file mode 100644 index 0000000000..aac9499616 --- /dev/null +++ b/src/network/tc/teql.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "macro.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "teql.h" + +static int trivial_link_equalizer_fill_tca_kind(Link *link, QDisc *qdisc, sd_netlink_message *req) { + char kind[STRLEN("teql") + DECIMAL_STR_MAX(unsigned)]; + TrivialLinkEqualizer *teql; + int r; + + assert(link); + assert(qdisc); + assert(req); + + teql = TEQL(qdisc); + + xsprintf(kind, "teql%u", teql->id); + r = sd_netlink_message_append_string(req, TCA_KIND, kind); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); + + return 0; +} + +const QDiscVTable teql_vtable = { + .object_size = sizeof(TrivialLinkEqualizer), + .fill_tca_kind = trivial_link_equalizer_fill_tca_kind, +}; + +int config_parse_trivial_link_equalizer_id( + 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; + TrivialLinkEqualizer *teql; + Network *network = data; + unsigned id; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(QDISC_KIND_TEQL, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + + teql = TEQL(qdisc); + + if (isempty(rvalue)) { + teql->id = 0; + + qdisc = NULL; + return 0; + } + + r = safe_atou(rvalue, &id); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (id > INT_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "'%s=' is too large, ignoring assignment: %s", + lvalue, rvalue); + } + + teql->id = id; + + qdisc = NULL; + return 0; +} diff --git a/src/network/tc/teql.h b/src/network/tc/teql.h new file mode 100644 index 0000000000..5b091aaf0f --- /dev/null +++ b/src/network/tc/teql.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "conf-parser.h" +#include "qdisc.h" + +typedef struct TrivialLinkEqualizer { + QDisc meta; + + unsigned id; +} TrivialLinkEqualizer; + +DEFINE_QDISC_CAST(TEQL, TrivialLinkEqualizer); +extern const QDiscVTable teql_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_trivial_link_equalizer_id); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 5b04fefeb5..cfee5db02f 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -326,3 +326,6 @@ NetworkEmulatorDelayJitterSec= NetworkEmulatorLossRate= NetworkEmulatorDuplicateRate= NetworkEmulatorPacketLimit= +[TrivialLinkEqualizer] +Parent= +Id=