From: Susant Sahani Date: Sun, 17 Nov 2019 06:30:03 +0000 (+0100) Subject: networkd tc: introduce tbf X-Git-Tag: v245-rc1~309^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ba5841b5206dcd35ea68c75ec4172bf7e8b8b337;p=thirdparty%2Fsystemd.git networkd tc: introduce tbf See https://linux.die.net/man/8/tc-tbf --- diff --git a/man/systemd.network.xml b/man/systemd.network.xml index a26e08c99cc..20723bfbfa8 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2368,6 +2368,33 @@ + + TokenBufferFilterLatencySec= + + Specifies the latency parameter, which specifies the maximum amount of time a + packet can sit in the Token Buffer Filter (TBF). Defaults to unset. + + + + + TokenBufferFilterBurst= + + Specifies the size of the bucket. This is the maximum amount of bytes that tokens + can be available for instantaneous transfer. When the size is suffixed with K, M, or G, it is + parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to + unset. + + + + + TokenBufferFilterRate= + + Specifies the device specific bandwidth. When suffixed with K, M, or G, the specified + bandwidth is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. + Defaults to unset. + + + diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 34b66e6fa60..1569f34cc41 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -532,7 +532,6 @@ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) { assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - assert_return(m->n_containers > 0, -EINVAL); r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); if (r < 0) diff --git a/src/network/meson.build b/src/network/meson.build index d502279151c..36336945770 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -109,6 +109,8 @@ sources = files(''' tc/netem.h tc/qdisc.c tc/qdisc.h + tc/tbf.c + tc/tbf.h tc/tc-util.c tc/tc-util.h '''.split()) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index f314b1ec16e..32c6afc49e9 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -250,6 +250,9 @@ TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_pars TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_tc_network_emulator_rate, 0, 0 TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate, config_parse_tc_network_emulator_rate, 0, 0 TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_tc_network_emulator_packet_limit, 0, 0 +TrafficControlQueueingDiscipline.TokenBufferFilterRate, config_parse_tc_token_buffer_filter_size, 0, 0 +TrafficControlQueueingDiscipline.TokenBufferFilterBurst, config_parse_tc_token_buffer_filter_size, 0, 0 +TrafficControlQueueingDiscipline.TokenBufferFilterLatencySec, config_parse_tc_token_buffer_filter_latency, 0, 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/tc/qdisc.c b/src/network/tc/qdisc.c index ed9bd9167ae..9baf7234619 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -152,6 +152,16 @@ int qdisc_configure(Link *link, QDiscs *qdisc) { return r; } + if (qdisc->has_token_buffer_filter) { + r = free_and_strdup(&tca_kind, "tbf"); + if (r < 0) + return log_oom(); + + r = token_buffer_filter_fill_message(link, &qdisc->tbf, req); + if (r < 0) + return r; + } + if (tca_kind) { r = sd_netlink_message_append_string(req, TCA_KIND, tca_kind); if (r < 0) diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index 95ff829b9e3..0453c85c9d1 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -7,6 +7,7 @@ #include "networkd-link.h" #include "networkd-network.h" #include "networkd-util.h" +#include "tbf.h" typedef struct QDiscs { NetworkConfigSection *section; @@ -20,8 +21,10 @@ typedef struct QDiscs { uint32_t parent; bool has_network_emulator:1; + bool has_token_buffer_filter:1; NetworkEmulator ne; + TokenBufferFilter tbf; } QDiscs; void qdisc_free(QDiscs *qdisc); diff --git a/src/network/tc/tbf.c b/src/network/tc/tbf.c new file mode 100644 index 00000000000..47c999e7c7c --- /dev/null +++ b/src/network/tc/tbf.c @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2019 VMware, Inc. */ + +#include +#include + +#include "alloc-util.h" +#include "conf-parser.h" +#include "netem.h" +#include "netlink-util.h" +#include "networkd-manager.h" +#include "parse-util.h" +#include "qdisc.h" +#include "string-util.h" +#include "util.h" + +int token_buffer_filter_new(TokenBufferFilter **ret) { + TokenBufferFilter *ne = NULL; + + ne = new0(TokenBufferFilter, 1); + if (!ne) + return -ENOMEM; + + *ret = TAKE_PTR(ne); + + return 0; +} + +int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req) { + struct tc_tbf_qopt opt = {}; + int r; + + assert(link); + assert(tbf); + assert(req); + + opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate; + opt.limit = tbf->rate * (double) tbf->latency / USEC_PER_SEC + tbf->burst; + + r = sd_netlink_message_open_array(req, TCA_OPTIONS); + if (r < 0) + return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + + r = sd_netlink_message_append_data(req, TCA_TBF_PARMS, &opt, sizeof(struct tc_tbf_qopt)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_TBF_PARMS attribute: %m"); + + r = sd_netlink_message_append_data(req, TCA_TBF_BURST, &tbf->burst, sizeof(tbf->burst)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_TBF_BURST attribute: %m"); + + if (tbf->rate >= (1ULL << 32)) { + r = sd_netlink_message_append_data(req, TCA_TBF_RATE64, &tbf->rate, sizeof(tbf->rate)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_TBF_RATE64 attribute: %m"); + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + + return 0; +} + +int config_parse_tc_token_buffer_filter_size( + 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) QDiscs *qdisc = NULL; + Network *network = data; + uint64_t k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(network, filename, section_line, &qdisc); + if (r < 0) + return r; + + if (isempty(rvalue)) { + if (streq(lvalue, "TokenBufferFilterRate")) + qdisc->tbf.rate = 0; + else if (streq(lvalue, "TokenBufferFilterBurst")) + qdisc->tbf.burst = 0; + + qdisc = NULL; + return 0; + } + + r = parse_size(rvalue, 1000, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "TokenBufferFilterRate")) + qdisc->tbf.rate = k / 8; + else if (streq(lvalue, "TokenBufferFilterBurst")) + qdisc->tbf.burst = k; + + qdisc->has_token_buffer_filter = true; + qdisc = NULL; + + return 0; +} + +int config_parse_tc_token_buffer_filter_latency( + 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) QDiscs *qdisc = NULL; + Network *network = data; + usec_t u; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = qdisc_new_static(network, filename, section_line, &qdisc); + if (r < 0) + return r; + + if (isempty(rvalue)) { + qdisc->tbf.latency = 0; + + qdisc = NULL; + return 0; + } + + r = parse_sec(rvalue, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + qdisc->tbf.latency = u; + + qdisc->has_token_buffer_filter = true; + qdisc = NULL; + + return 0; +} diff --git a/src/network/tc/tbf.h b/src/network/tc/tbf.h new file mode 100644 index 00000000000..c8ae6d057d2 --- /dev/null +++ b/src/network/tc/tbf.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1+ + * Copyright © 2019 VMware, Inc. */ +#pragma once + +#include "sd-netlink.h" + +#include "conf-parser.h" +#include "networkd-link.h" + +typedef struct TokenBufferFilter { + uint64_t rate; + + uint32_t burst; + uint32_t latency; +} TokenBufferFilter; + +int token_buffer_filter_new(TokenBufferFilter **ret); +int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req); + +CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency); +CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index cb10ca306a4..1cdbc07a242 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -270,3 +270,6 @@ NetworkEmulatorDelayJitterSec= NetworkEmulatorLossRate= NetworkEmulatorDuplicateRate= NetworkEmulatorPacketLimit= +TokenBufferFilterRate= +TokenBufferFilterBurst= +TokenBufferFilterLatencySec=