From 19f86a63515715387e367e99f76c8ecce4405adb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 12 Feb 2020 09:52:56 +0900 Subject: network: tc: support HTB class --- src/network/tc/htb.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ src/network/tc/htb.h | 15 ++++ src/network/tc/tclass.c | 1 + 3 files changed, 197 insertions(+) (limited to 'src/network/tc') diff --git a/src/network/tc/htb.c b/src/network/tc/htb.c index 06dd5cbc78..f2b9c4507e 100644 --- a/src/network/tc/htb.c +++ b/src/network/tc/htb.c @@ -9,6 +9,7 @@ #include "qdisc.h" #include "htb.h" #include "string-util.h" +#include "tc-util.h" static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { HierarchyTokenBucket *htb; @@ -96,3 +97,183 @@ const QDiscVTable htb_vtable = { .tca_kind = "htb", .fill_message = hierarchy_token_bucket_fill_message, }; + +static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) { + HierarchyTokenBucketClass *htb; + struct tc_htb_opt opt = {}; + uint32_t rtab[256], ctab[256], mtu = 1600; /* Ethernet packet length */ + int r; + + assert(link); + assert(tclass); + assert(req); + + htb = TCLASS_TO_HTB(tclass); + + if (htb->ceil_rate == 0) + htb->ceil_rate = htb->rate; + + opt.prio = htb->priority; + opt.rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate; + opt.ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate; + r = tc_transmit_time(htb->rate, mtu, &opt.buffer); + if (r < 0) + return log_link_error_errno(link, r, "Failed to calculate buffer size: %m"); + + r = tc_transmit_time(htb->ceil_rate, mtu, &opt.cbuffer); + if (r < 0) + return log_link_error_errno(link, r, "Failed to calculate ceil buffer size: %m"); + + r = tc_fill_ratespec_and_table(&opt.rate, rtab, mtu); + if (r < 0) + return log_link_error_errno(link, r, "Failed to calculate rate table: %m"); + + r = tc_fill_ratespec_and_table(&opt.ceil, ctab, mtu); + if (r < 0) + return log_link_error_errno(link, r, "Failed to calculate ceil rate table: %m"); + + r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb"); + 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_HTB_PARMS, &opt, sizeof(opt)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_HTB_PARMS attribute: %m"); + + if (htb->rate >= (1ULL << 32)) { + r = sd_netlink_message_append_u64(req, TCA_HTB_RATE64, htb->rate); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_HTB_RATE64 attribute: %m"); + } + + if (htb->ceil_rate >= (1ULL << 32)) { + r = sd_netlink_message_append_u64(req, TCA_HTB_CEIL64, htb->ceil_rate); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_HTB_CEIL64 attribute: %m"); + } + + r = sd_netlink_message_append_data(req, TCA_HTB_RTAB, rtab, sizeof(rtab)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_HTB_RTAB attribute: %m"); + + r = sd_netlink_message_append_data(req, TCA_HTB_CTAB, ctab, sizeof(ctab)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append TCA_HTB_CTAB 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_hierarchy_token_bucket_u32( + 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; + HierarchyTokenBucketClass *htb; + Network *network = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + + htb = TCLASS_TO_HTB(tclass); + + if (isempty(rvalue)) { + htb->priority = 0; + + tclass = NULL; + return 0; + } + + r = safe_atou32(rvalue, &htb->priority); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + tclass = NULL; + + return 0; +} + +int config_parse_hierarchy_token_bucket_rate( + 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; + HierarchyTokenBucketClass *htb; + Network *network = data; + uint64_t *v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to create traffic control class, ignoring assignment: %m"); + + htb = TCLASS_TO_HTB(tclass); + if (streq(lvalue, "Rate")) + v = &htb->rate; + else if (streq(lvalue, "CeilRate")) + v = &htb->ceil_rate; + else + assert_not_reached("Invalid lvalue"); + + if (isempty(rvalue)) { + *v = 0; + + tclass = NULL; + return 0; + } + + r = parse_size(rvalue, 1000, v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + *v /= 8; + tclass = NULL; + + return 0; +} + +const TClassVTable htb_tclass_vtable = { + .object_size = sizeof(HierarchyTokenBucketClass), + .tca_kind = "htb", + .fill_message = hierarchy_token_bucket_class_fill_message, +}; diff --git a/src/network/tc/htb.h b/src/network/tc/htb.h index 6b5ef8cfb4..c8dce2c1e3 100644 --- a/src/network/tc/htb.h +++ b/src/network/tc/htb.h @@ -3,6 +3,7 @@ #include "conf-parser.h" #include "qdisc.h" +#include "tclass.h" typedef struct HierarchyTokenBucket { QDisc meta; @@ -14,3 +15,17 @@ DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket); extern const QDiscVTable htb_vtable; CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class); + +typedef struct HierarchyTokenBucketClass { + TClass meta; + + uint32_t priority; + uint64_t rate; + uint64_t ceil_rate; +} HierarchyTokenBucketClass; + +DEFINE_TCLASS_CAST(HTB, HierarchyTokenBucketClass); +extern const TClassVTable htb_tclass_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32); +CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_rate); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index 236437929e..87f5bcf704 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -16,6 +16,7 @@ #include "tclass.h" const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = { + [TCLASS_KIND_HTB] = &htb_tclass_vtable, }; static int tclass_new(TClassKind kind, TClass **ret) { -- cgit v1.2.1