// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Enhanced Transmission Selection - 802.1Qaz-based Qdisc */ #include #include #include #include #include #include #include #include #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 (nbands < 1) { fprintf(stderr, "The number of \"bands\" must be >= 1\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, };