diff options
Diffstat (limited to '')
-rw-r--r-- | tc/q_ets.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/tc/q_ets.c b/tc/q_ets.c new file mode 100644 index 0000000..7380bb2 --- /dev/null +++ b/tc/q_ets.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* + * Enhanced Transmission Selection - 802.1Qaz-based Qdisc + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> + +#include "utils.h" +#include "tc_util.h" + +static void explain(void) +{ + fprintf(stderr, "Usage: ... ets [bands NUMBER] [strict NUMBER] [quanta Q1 Q2...] [priomap P1 P2...]\n"); +} + +static void cexplain(void) +{ + fprintf(stderr, "Usage: ... ets [quantum Q1]\n"); +} + +static unsigned int parse_quantum(const char *arg) +{ + unsigned int quantum; + + if (get_unsigned(&quantum, arg, 10)) { + fprintf(stderr, "Illegal \"quanta\" element\n"); + return 0; + } + if (!quantum) + fprintf(stderr, "\"quanta\" must be > 0\n"); + return quantum; +} + +static int parse_nbands(const char *arg, __u8 *pnbands, const char *what) +{ + unsigned int tmp; + + if (get_unsigned(&tmp, arg, 10)) { + fprintf(stderr, "Illegal \"%s\"\n", what); + return -1; + } + if (tmp > TCQ_ETS_MAX_BANDS) { + fprintf(stderr, "The number of \"%s\" must be <= %d\n", + what, TCQ_ETS_MAX_BANDS); + return -1; + } + + *pnbands = tmp; + return 0; +} + +static int ets_parse_opt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n, const char *dev) +{ + __u8 nbands = 0; + __u8 nstrict = 0; + bool quanta_mode = false; + unsigned int nquanta = 0; + __u32 quanta[TCQ_ETS_MAX_BANDS]; + bool priomap_mode = false; + unsigned int nprio = 0; + __u8 priomap[TC_PRIO_MAX + 1]; + unsigned int tmp; + struct rtattr *tail, *nest; + + while (argc > 0) { + if (strcmp(*argv, "bands") == 0) { + if (nbands) { + fprintf(stderr, "Duplicate \"bands\"\n"); + return -1; + } + NEXT_ARG(); + if (parse_nbands(*argv, &nbands, "bands")) + return -1; + priomap_mode = quanta_mode = false; + } else if (strcmp(*argv, "strict") == 0) { + if (nstrict) { + fprintf(stderr, "Duplicate \"strict\"\n"); + return -1; + } + NEXT_ARG(); + if (parse_nbands(*argv, &nstrict, "strict")) + return -1; + priomap_mode = quanta_mode = false; + } else if (strcmp(*argv, "quanta") == 0) { + if (nquanta) { + fprintf(stderr, "Duplicate \"quanta\"\n"); + return -1; + } + NEXT_ARG(); + priomap_mode = false; + quanta_mode = true; + goto parse_quantum; + } else if (strcmp(*argv, "priomap") == 0) { + if (nprio) { + fprintf(stderr, "Duplicate \"priomap\"\n"); + return -1; + } + NEXT_ARG(); + priomap_mode = true; + quanta_mode = false; + goto parse_priomap; + } else if (strcmp(*argv, "help") == 0) { + explain(); + return -1; + } else if (quanta_mode) { + unsigned int quantum; + +parse_quantum: + quantum = parse_quantum(*argv); + if (!quantum) + return -1; + quanta[nquanta++] = quantum; + } else if (priomap_mode) { + unsigned int band; + +parse_priomap: + if (get_unsigned(&band, *argv, 10)) { + fprintf(stderr, "Illegal \"priomap\" element\n"); + return -1; + } + if (nprio > TC_PRIO_MAX) { + fprintf(stderr, "\"priomap\" index cannot be higher than %u\n", TC_PRIO_MAX); + return -1; + } + priomap[nprio++] = band; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + explain(); + return -1; + } + argc--; argv++; + } + + if (!nbands) + nbands = nquanta + nstrict; + if (!nbands) { + fprintf(stderr, "One of \"bands\", \"quanta\" or \"strict\" needs to be specified\n"); + explain(); + return -1; + } + if (nstrict + nquanta > nbands) { + fprintf(stderr, "Not enough total bands to cover all the strict bands and quanta\n"); + explain(); + return -1; + } + for (tmp = 0; tmp < nprio; tmp++) { + if (priomap[tmp] >= nbands) { + fprintf(stderr, "\"priomap\" element is out of bounds\n"); + return -1; + } + } + + tail = addattr_nest(n, 1024, TCA_OPTIONS | NLA_F_NESTED); + addattr_l(n, 1024, TCA_ETS_NBANDS, &nbands, sizeof(nbands)); + if (nstrict) + addattr_l(n, 1024, TCA_ETS_NSTRICT, &nstrict, sizeof(nstrict)); + if (nquanta) { + nest = addattr_nest(n, 1024, TCA_ETS_QUANTA | NLA_F_NESTED); + for (tmp = 0; tmp < nquanta; tmp++) + addattr_l(n, 1024, TCA_ETS_QUANTA_BAND, + &quanta[tmp], sizeof(quanta[0])); + addattr_nest_end(n, nest); + } + if (nprio) { + nest = addattr_nest(n, 1024, TCA_ETS_PRIOMAP | NLA_F_NESTED); + for (tmp = 0; tmp < nprio; tmp++) + addattr_l(n, 1024, TCA_ETS_PRIOMAP_BAND, + &priomap[tmp], sizeof(priomap[0])); + addattr_nest_end(n, nest); + } + addattr_nest_end(n, tail); + + return 0; +} + +static int ets_parse_copt(struct qdisc_util *qu, int argc, char **argv, + struct nlmsghdr *n, const char *dev) +{ + unsigned int quantum = 0; + struct rtattr *tail; + + while (argc > 0) { + if (strcmp(*argv, "quantum") == 0) { + if (quantum) { + fprintf(stderr, "Duplicate \"quantum\"\n"); + return -1; + } + NEXT_ARG(); + quantum = parse_quantum(*argv); + if (!quantum) + return -1; + } else if (strcmp(*argv, "help") == 0) { + cexplain(); + return -1; + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + cexplain(); + return -1; + } + argc--; argv++; + } + + tail = addattr_nest(n, 1024, TCA_OPTIONS | NLA_F_NESTED); + if (quantum) + addattr_l(n, 1024, TCA_ETS_QUANTA_BAND, &quantum, + sizeof(quantum)); + addattr_nest_end(n, tail); + + return 0; +} + +static int ets_print_opt_quanta(struct rtattr *opt) +{ + int len = RTA_PAYLOAD(opt); + unsigned int offset; + + open_json_array(PRINT_ANY, "quanta"); + for (offset = 0; offset < len; ) { + struct rtattr *tb[TCA_ETS_MAX + 1] = {NULL}; + struct rtattr *attr; + __u32 quantum; + + attr = RTA_DATA(opt) + offset; + parse_rtattr(tb, TCA_ETS_MAX, attr, len - offset); + offset += RTA_LENGTH(RTA_PAYLOAD(attr)); + + if (!tb[TCA_ETS_QUANTA_BAND]) { + fprintf(stderr, "No ETS band quantum\n"); + return -1; + } + + quantum = rta_getattr_u32(tb[TCA_ETS_QUANTA_BAND]); + print_uint(PRINT_ANY, NULL, " %u", quantum); + + } + close_json_array(PRINT_ANY, " "); + + return 0; +} + +static int ets_print_opt_priomap(struct rtattr *opt) +{ + int len = RTA_PAYLOAD(opt); + unsigned int offset; + + open_json_array(PRINT_ANY, "priomap"); + for (offset = 0; offset < len; ) { + struct rtattr *tb[TCA_ETS_MAX + 1] = {NULL}; + struct rtattr *attr; + __u8 band; + + attr = RTA_DATA(opt) + offset; + parse_rtattr(tb, TCA_ETS_MAX, attr, len - offset); + offset += RTA_LENGTH(RTA_PAYLOAD(attr)) + 3 /* padding */; + + if (!tb[TCA_ETS_PRIOMAP_BAND]) { + fprintf(stderr, "No ETS priomap band\n"); + return -1; + } + + band = rta_getattr_u8(tb[TCA_ETS_PRIOMAP_BAND]); + print_uint(PRINT_ANY, NULL, " %u", band); + + } + close_json_array(PRINT_ANY, " "); + + return 0; +} + +static int ets_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_ETS_MAX + 1]; + __u8 nbands; + __u8 nstrict; + int err; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ETS_MAX, opt); + + if (!tb[TCA_ETS_NBANDS] || !tb[TCA_ETS_PRIOMAP]) { + fprintf(stderr, "Incomplete ETS options\n"); + return -1; + } + + nbands = rta_getattr_u8(tb[TCA_ETS_NBANDS]); + print_uint(PRINT_ANY, "bands", "bands %u ", nbands); + + if (tb[TCA_ETS_NSTRICT]) { + nstrict = rta_getattr_u8(tb[TCA_ETS_NSTRICT]); + print_uint(PRINT_ANY, "strict", "strict %u ", nstrict); + } + + if (tb[TCA_ETS_QUANTA]) { + err = ets_print_opt_quanta(tb[TCA_ETS_QUANTA]); + if (err) + return err; + } + + return ets_print_opt_priomap(tb[TCA_ETS_PRIOMAP]); +} + +static int ets_print_copt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) +{ + struct rtattr *tb[TCA_ETS_MAX + 1]; + __u32 quantum; + + if (opt == NULL) + return 0; + + parse_rtattr_nested(tb, TCA_ETS_MAX, opt); + + if (tb[TCA_ETS_QUANTA_BAND]) { + quantum = rta_getattr_u32(tb[TCA_ETS_QUANTA_BAND]); + print_uint(PRINT_ANY, "quantum", "quantum %u ", quantum); + } + + return 0; +} + +struct qdisc_util ets_qdisc_util = { + .id = "ets", + .parse_qopt = ets_parse_opt, + .parse_copt = ets_parse_copt, + .print_qopt = ets_print_opt, + .print_copt = ets_print_copt, +}; |