diff options
Diffstat (limited to '')
-rw-r--r-- | src/network/tc/cake.c | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/src/network/tc/cake.c b/src/network/tc/cake.c new file mode 100644 index 0000000..8d770b0 --- /dev/null +++ b/src/network/tc/cake.c @@ -0,0 +1,613 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright © 2020 VMware, Inc. */ + +#include <linux/pkt_sched.h> + +#include "alloc-util.h" +#include "cake.h" +#include "conf-parser.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "qdisc.h" +#include "string-table.h" +#include "string-util.h" + +static int cake_init(QDisc *qdisc) { + CommonApplicationsKeptEnhanced *c; + + assert(qdisc); + + c = CAKE(qdisc); + + c->autorate = -1; + c->compensation_mode = _CAKE_COMPENSATION_MODE_INVALID; + c->raw = -1; + c->flow_isolation_mode = _CAKE_FLOW_ISOLATION_MODE_INVALID; + c->nat = -1; + c->preset = _CAKE_PRESET_INVALID; + c->wash = -1; + c->split_gso = -1; + + return 0; +} + +static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { + CommonApplicationsKeptEnhanced *c; + int r; + + assert(link); + assert(qdisc); + assert(req); + + assert_se(c = CAKE(qdisc)); + + r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "cake"); + if (r < 0) + return r; + + if (c->bandwidth > 0) { + r = sd_netlink_message_append_u64(req, TCA_CAKE_BASE_RATE64, c->bandwidth); + if (r < 0) + return r; + } + + if (c->autorate >= 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_AUTORATE, c->autorate); + if (r < 0) + return r; + } + + if (c->overhead_set) { + r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead); + if (r < 0) + return r; + } + + if (c->mpu > 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_MPU, c->mpu); + if (r < 0) + return r; + } + + if (c->compensation_mode >= 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_ATM, c->compensation_mode); + if (r < 0) + return r; + } + + if (c->raw > 0) { + /* TCA_CAKE_RAW attribute is mostly a flag, not boolean. */ + r = sd_netlink_message_append_u32(req, TCA_CAKE_RAW, 0); + if (r < 0) + return r; + } + + if (c->flow_isolation_mode >= 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_FLOW_MODE, c->flow_isolation_mode); + if (r < 0) + return r; + } + + if (c->nat >= 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_NAT, c->nat); + if (r < 0) + return r; + } + + if (c->preset >= 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_DIFFSERV_MODE, c->preset); + if (r < 0) + return r; + } + + if (c->fwmark > 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_FWMARK, c->fwmark); + if (r < 0) + return r; + } + + if (c->wash >= 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_WASH, c->wash); + if (r < 0) + return r; + } + + if (c->split_gso >= 0) { + r = sd_netlink_message_append_u32(req, TCA_CAKE_SPLIT_GSO, c->split_gso); + if (r < 0) + return r; + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return r; + + return 0; +} + +int config_parse_cake_bandwidth( + 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; + CommonApplicationsKeptEnhanced *c; + Network *network = ASSERT_PTR(data); + uint64_t k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->bandwidth = 0; + + TAKE_PTR(qdisc); + return 0; + } + + r = parse_size(rvalue, 1000, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->bandwidth = k/8; + TAKE_PTR(qdisc); + + return 0; +} + +int config_parse_cake_overhead( + 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; + CommonApplicationsKeptEnhanced *c; + Network *network = ASSERT_PTR(data); + int32_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->overhead_set = false; + TAKE_PTR(qdisc); + return 0; + } + + r = safe_atoi32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (v < -64 || v > 256) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->overhead = v; + c->overhead_set = true; + TAKE_PTR(qdisc); + return 0; +} + +int config_parse_cake_mpu( + 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; + CommonApplicationsKeptEnhanced *c; + Network *network = ASSERT_PTR(data); + uint32_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->mpu = 0; + TAKE_PTR(qdisc); + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (v <= 0 || v > 256) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->mpu = v; + TAKE_PTR(qdisc); + return 0; +} + +int config_parse_cake_tristate( + 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; + CommonApplicationsKeptEnhanced *c; + Network *network = ASSERT_PTR(data); + int *dest, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (streq(lvalue, "AutoRateIngress")) + dest = &c->autorate; + else if (streq(lvalue, "UseRawPacketSize")) + dest = &c->raw; + else if (streq(lvalue, "NAT")) + dest = &c->nat; + else if (streq(lvalue, "Wash")) + dest = &c->wash; + else if (streq(lvalue, "SplitGSO")) + dest = &c->split_gso; + else + assert_not_reached(); + + if (isempty(rvalue)) { + *dest = -1; + TAKE_PTR(qdisc); + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + *dest = r; + TAKE_PTR(qdisc); + return 0; +} + +static const char * const cake_compensation_mode_table[_CAKE_COMPENSATION_MODE_MAX] = { + [CAKE_COMPENSATION_MODE_NONE] = "none", + [CAKE_COMPENSATION_MODE_ATM] = "atm", + [CAKE_COMPENSATION_MODE_PTM] = "ptm", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_compensation_mode, CakeCompensationMode); + +int config_parse_cake_compensation_mode( + 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; + CommonApplicationsKeptEnhanced *c; + Network *network = ASSERT_PTR(data); + CakeCompensationMode mode; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->compensation_mode = _CAKE_COMPENSATION_MODE_INVALID; + TAKE_PTR(qdisc); + return 0; + } + + mode = cake_compensation_mode_from_string(rvalue); + if (mode < 0) { + log_syntax(unit, LOG_WARNING, filename, line, mode, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->compensation_mode = mode; + TAKE_PTR(qdisc); + return 0; +} + +static const char * const cake_flow_isolation_mode_table[_CAKE_FLOW_ISOLATION_MODE_MAX] = { + [CAKE_FLOW_ISOLATION_MODE_NONE] = "none", + [CAKE_FLOW_ISOLATION_MODE_SRC_IP] = "src-host", + [CAKE_FLOW_ISOLATION_MODE_DST_IP] = "dst-host", + [CAKE_FLOW_ISOLATION_MODE_HOSTS] = "hosts", + [CAKE_FLOW_ISOLATION_MODE_FLOWS] = "flows", + [CAKE_FLOW_ISOLATION_MODE_DUAL_SRC] = "dual-src-host", + [CAKE_FLOW_ISOLATION_MODE_DUAL_DST] = "dual-dst-host", + [CAKE_FLOW_ISOLATION_MODE_TRIPLE] = "triple", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_flow_isolation_mode, CakeFlowIsolationMode); + +int config_parse_cake_flow_isolation_mode( + 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; + CommonApplicationsKeptEnhanced *c; + Network *network = ASSERT_PTR(data); + CakeFlowIsolationMode mode; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->flow_isolation_mode = _CAKE_FLOW_ISOLATION_MODE_INVALID; + TAKE_PTR(qdisc); + return 0; + } + + mode = cake_flow_isolation_mode_from_string(rvalue); + if (mode < 0) { + log_syntax(unit, LOG_WARNING, filename, line, mode, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->flow_isolation_mode = mode; + TAKE_PTR(qdisc); + return 0; +} + +static const char * const cake_priority_queueing_preset_table[_CAKE_PRESET_MAX] = { + [CAKE_PRESET_DIFFSERV3] = "diffserv3", + [CAKE_PRESET_DIFFSERV4] = "diffserv4", + [CAKE_PRESET_DIFFSERV8] = "diffserv8", + [CAKE_PRESET_BESTEFFORT] = "besteffort", + [CAKE_PRESET_PRECEDENCE] = "precedence", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_priority_queueing_preset, CakePriorityQueueingPreset); + +int config_parse_cake_priority_queueing_preset( + 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; + CommonApplicationsKeptEnhanced *c; + CakePriorityQueueingPreset preset; + Network *network = ASSERT_PTR(data); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->preset = _CAKE_PRESET_INVALID; + TAKE_PTR(qdisc); + return 0; + } + + preset = cake_priority_queueing_preset_from_string(rvalue); + if (preset < 0) { + log_syntax(unit, LOG_WARNING, filename, line, preset, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->preset = preset; + TAKE_PTR(qdisc); + return 0; +} + +int config_parse_cake_fwmark( + 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; + CommonApplicationsKeptEnhanced *c; + Network *network = ASSERT_PTR(data); + uint32_t fwmark; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "More than one kind of queueing discipline, ignoring assignment: %m"); + return 0; + } + + c = CAKE(qdisc); + + if (isempty(rvalue)) { + c->fwmark = 0; + TAKE_PTR(qdisc); + return 0; + } + + r = safe_atou32(rvalue, &fwmark); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + if (fwmark <= 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid '%s=', ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + c->fwmark = fwmark; + TAKE_PTR(qdisc); + return 0; +} + +const QDiscVTable cake_vtable = { + .object_size = sizeof(CommonApplicationsKeptEnhanced), + .tca_kind = "cake", + .init = cake_init, + .fill_message = cake_fill_message, +}; |