/* SPDX-License-Identifier: LGPL-2.1-or-later * Copyright © 2020 VMware, Inc. */ #include #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, };