This patch adapts the tc command line interface to allow bandwidth limits to be specified as a percentage of the interface's capacity.
For this purpose, we've modified and moved int read_prop() from ip/iptuntap.c to lib.utils.c to make it accessible to tc. Additionally, adding this functionality requires passing the specified device string to each class/qdisc which changes the prototype for a couple of functions: the .parse_qopt and .parse_copt interfaces. The device string is a required parameter for tc-qdisc and tc-class, and when not specified, the kernel returns ENODEV. In this patch, if the user tries to specify a bandwidth percentage without naming the device, we return an error from userspace. Signed-off by: Nishanth Devarajan <ndev2...@gmail.com> --- include/utils.h | 1 + ip/iptuntap.c | 32 ---------------------------- lib/utils.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ man/man8/tc.8 | 4 +++- tc/q_atm.c | 2 +- tc/q_cbq.c | 25 +++++++++++++++++----- tc/q_choke.c | 9 ++++++-- tc/q_clsact.c | 2 +- tc/q_codel.c | 2 +- tc/q_drr.c | 4 ++-- tc/q_dsmark.c | 4 ++-- tc/q_fifo.c | 2 +- tc/q_fq.c | 16 +++++++++++--- tc/q_fq_codel.c | 2 +- tc/q_gred.c | 9 ++++++-- tc/q_hfsc.c | 45 ++++++++++++++++++++++++++------------- tc/q_hhf.c | 2 +- tc/q_htb.c | 18 ++++++++++++---- tc/q_ingress.c | 2 +- tc/q_mqprio.c | 2 +- tc/q_multiq.c | 2 +- tc/q_netem.c | 9 ++++++-- tc/q_pie.c | 2 +- tc/q_prio.c | 2 +- tc/q_qfq.c | 4 ++-- tc/q_red.c | 9 ++++++-- tc/q_rr.c | 2 +- tc/q_sfb.c | 2 +- tc/q_sfq.c | 2 +- tc/q_tbf.c | 16 +++++++++++--- tc/tc.c | 2 +- tc/tc_class.c | 2 +- tc/tc_qdisc.c | 2 +- tc/tc_util.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tc/tc_util.h | 8 +++++-- 35 files changed, 268 insertions(+), 96 deletions(-) diff --git a/include/utils.h b/include/utils.h index 3d91c50..63fea7c 100644 --- a/include/utils.h +++ b/include/utils.h @@ -87,6 +87,7 @@ int get_prefix(inet_prefix *dst, char *arg, int family); int mask2bits(__u32 netmask); int get_addr_ila(__u64 *val, const char *arg); +int read_prop(char *dev, char *prop, long *value); int get_hex(char c); int get_integer(int *val, const char *arg, int base); int get_unsigned(unsigned *val, const char *arg, int base); diff --git a/ip/iptuntap.c b/ip/iptuntap.c index b46e452..09f2be2 100644 --- a/ip/iptuntap.c +++ b/ip/iptuntap.c @@ -223,38 +223,6 @@ static int do_del(int argc, char **argv) return tap_del_ioctl(&ifr); } -static int read_prop(char *dev, char *prop, long *value) -{ - char fname[IFNAMSIZ+25], buf[80], *endp; - ssize_t len; - int fd; - long result; - - sprintf(fname, "/sys/class/net/%s/%s", dev, prop); - fd = open(fname, O_RDONLY); - if (fd < 0) { - if (strcmp(prop, "tun_flags")) - fprintf(stderr, "open %s: %s\n", fname, - strerror(errno)); - return -1; - } - len = read(fd, buf, sizeof(buf)-1); - close(fd); - if (len < 0) { - fprintf(stderr, "read %s: %s", fname, strerror(errno)); - return -1; - } - - buf[len] = 0; - result = strtol(buf, &endp, 0); - if (*endp != '\n') { - fprintf(stderr, "Failed to parse %s\n", fname); - return -1; - } - *value = result; - return 0; -} - static void print_flags(long flags) { if (flags & IFF_TUN) diff --git a/lib/utils.c b/lib/utils.c index 4f2fa28..1332410 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -39,6 +39,57 @@ int resolve_hosts; int timestamp_short; +int read_prop(char *dev, char *prop, long *value) +{ + char fname[128], buf[80], *endp, *nl; + FILE *fp; + long result; + int ret; + + ret = snprintf(fname, sizeof(fname), "/sys/class/net/%s/%s", + dev, prop); + + if (ret <= 0 || ret >= sizeof(fname)) { + fprintf(stderr, "could not build pathname for property\n"); + return -1; + } + + fp = fopen(fname, "r"); + if (fp == NULL) { + fprintf(stderr, "fopen %s: %s\n", fname, strerror(errno)); + return -1; + } + + if (!fgets(buf, sizeof(buf), fp)) { + fclose(fp); + goto out; + } + + nl = strchr(buf, '\n'); + if (nl) + *nl = '\0'; + + fclose(fp); + result = strtoul(buf, &endp, 0); + + if (buf == endp || *endp) { + fprintf(stderr, "value \"%s\" in file %s is not a number\n", + buf, fname); + goto out; + } + + if (result == ULONG_MAX && errno == ERANGE) { + fprintf(stderr, "strtoul %s: %s", fname, strerror(errno)); + goto out; + } + + *value = result; + return 0; +out: + fprintf(stderr, "Failed to parse %s\n", fname); + return -1; +} + int get_hex(char c) { if (c >= 'A' && c <= 'F') diff --git a/man/man8/tc.8 b/man/man8/tc.8 index f96911a..22f699b 100644 --- a/man/man8/tc.8 +++ b/man/man8/tc.8 @@ -443,7 +443,9 @@ see the man pages for individual qdiscs. RATES Bandwidths or rates. These parameters accept a floating point number, possibly followed by -a unit (both SI and IEC units supported). +either a unit (both SI and IEC units supported), or a float followed '%' +character to specify the rate as a percentage of the device's speed +(e.g. 5%, 99.5%). .RS .TP bit or a bare number diff --git a/tc/q_atm.c b/tc/q_atm.c index 570e7be..7dfd811 100644 --- a/tc/q_atm.c +++ b/tc/q_atm.c @@ -44,7 +44,7 @@ static void explain(void) static int atm_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct sockaddr_atmsvc addr = {}; struct atm_qos qos; diff --git a/tc/q_cbq.c b/tc/q_cbq.c index e00d4e3..284a874 100644 --- a/tc/q_cbq.c +++ b/tc/q_cbq.c @@ -46,7 +46,7 @@ static void explain1(char *arg) } -static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { struct tc_ratespec r = {}; struct tc_cbq_lssopt lss = {}; @@ -62,7 +62,12 @@ static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl if (matches(*argv, "bandwidth") == 0 || matches(*argv, "rate") == 0) { NEXT_ARG(); - if (get_rate(&r.rate, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&r.rate, *argv, dev)) { + explain1("bandwidth"); + return -1; + } + } else if (get_rate(&r.rate, *argv)) { explain1("bandwidth"); return -1; } @@ -176,7 +181,7 @@ static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl return 0; } -static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int wrr_ok = 0, fopt_ok = 0; struct tc_ratespec r = {}; @@ -196,13 +201,23 @@ static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str while (argc > 0) { if (matches(*argv, "rate") == 0) { NEXT_ARG(); - if (get_rate(&r.rate, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&r.rate, *argv, dev)) { + explain1("rate"); + return -1; + } + } else if (get_rate(&r.rate, *argv)) { explain1("rate"); return -1; } } else if (matches(*argv, "bandwidth") == 0) { NEXT_ARG(); - if (get_rate(&bndw, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&bndw, *argv, dev)) { + explain1("bandwidth"); + return -1; + } + } else if (get_rate(&bndw, *argv)) { explain1("bandwidth"); return -1; } diff --git a/tc/q_choke.c b/tc/q_choke.c index 726914b..17d70a4 100644 --- a/tc/q_choke.c +++ b/tc/q_choke.c @@ -31,7 +31,7 @@ static void explain(void) } static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct tc_red_qopt opt = {}; unsigned int burst = 0; @@ -53,7 +53,12 @@ static int choke_parse_opt(struct qdisc_util *qu, int argc, char **argv, } } else if (strcmp(*argv, "bandwidth") == 0) { NEXT_ARG(); - if (get_rate(&rate, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&rate, *argv, dev)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + } else if (get_rate(&rate, *argv)) { fprintf(stderr, "Illegal \"bandwidth\"\n"); return -1; } diff --git a/tc/q_clsact.c b/tc/q_clsact.c index e2a1a71..89028e6 100644 --- a/tc/q_clsact.c +++ b/tc/q_clsact.c @@ -10,7 +10,7 @@ static void explain(void) } static int clsact_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { if (argc > 0) { fprintf(stderr, "What is \"%s\"?\n", *argv); diff --git a/tc/q_codel.c b/tc/q_codel.c index 253629e..170cd0a 100644 --- a/tc/q_codel.c +++ b/tc/q_codel.c @@ -58,7 +58,7 @@ static void explain(void) } static int codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { unsigned int limit = 0; unsigned int target = 0; diff --git a/tc/q_drr.c b/tc/q_drr.c index 50623c2..3085268 100644 --- a/tc/q_drr.c +++ b/tc/q_drr.c @@ -33,7 +33,7 @@ static void explain2(void) } -static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { while (argc) { if (strcmp(*argv, "help") == 0) { @@ -49,7 +49,7 @@ static int drr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl } static int drr_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct rtattr *tail; __u32 tmp; diff --git a/tc/q_dsmark.c b/tc/q_dsmark.c index 0aab387..13d2d31 100644 --- a/tc/q_dsmark.c +++ b/tc/q_dsmark.c @@ -25,7 +25,7 @@ static void explain(void) static int dsmark_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct rtattr *tail; __u16 ind; @@ -84,7 +84,7 @@ static void explain_class(void) static int dsmark_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct rtattr *tail; __u8 tmp; diff --git a/tc/q_fifo.c b/tc/q_fifo.c index c3e9088..f0ff1ab 100644 --- a/tc/q_fifo.c +++ b/tc/q_fifo.c @@ -27,7 +27,7 @@ static void explain(void) fprintf(stderr, "Usage: ... <[p|b]fifo | pfifo_head_drop> [ limit NUMBER ]\n"); } -static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int fifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int ok = 0; struct tc_fifo_qopt opt = {}; diff --git a/tc/q_fq.c b/tc/q_fq.c index 49ebeef..9b0bb93 100644 --- a/tc/q_fq.c +++ b/tc/q_fq.c @@ -71,7 +71,7 @@ static unsigned int ilog2(unsigned int val) } static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { unsigned int plimit; unsigned int flow_plimit; @@ -118,7 +118,12 @@ static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv, } } else if (strcmp(*argv, "maxrate") == 0) { NEXT_ARG(); - if (get_rate(&maxrate, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&maxrate, *argv, dev)) { + fprintf(stderr, "Illegal \"maxrate\"\n"); + return -1; + } + } else if (get_rate(&maxrate, *argv)) { fprintf(stderr, "Illegal \"maxrate\"\n"); return -1; } @@ -132,7 +137,12 @@ static int fq_parse_opt(struct qdisc_util *qu, int argc, char **argv, set_low_rate_threshold = true; } else if (strcmp(*argv, "defrate") == 0) { NEXT_ARG(); - if (get_rate(&defrate, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&defrate, *argv, dev)) { + fprintf(stderr, "Illegal \"defrate\"\n"); + return -1; + } + } else if (get_rate(&defrate, *argv)) { fprintf(stderr, "Illegal \"defrate\"\n"); return -1; } diff --git a/tc/q_fq_codel.c b/tc/q_fq_codel.c index 1eac140..ef700cd 100644 --- a/tc/q_fq_codel.c +++ b/tc/q_fq_codel.c @@ -56,7 +56,7 @@ static void explain(void) } static int fq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { unsigned int limit = 0; unsigned int flows = 0; diff --git a/tc/q_gred.c b/tc/q_gred.c index 2eb906d..18d96c9 100644 --- a/tc/q_gred.c +++ b/tc/q_gred.c @@ -116,7 +116,7 @@ static int init_gred(struct qdisc_util *qu, int argc, char **argv, /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ -static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int ok = 0; struct tc_gred_qopt opt = { 0 }; @@ -199,7 +199,12 @@ static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct n ok++; } else if (strcmp(*argv, "bandwidth") == 0) { NEXT_ARG(); - if (get_rate(&rate, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&rate, *argv, dev)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + } else if (get_rate(&rate, *argv)) { fprintf(stderr, "Illegal \"bandwidth\"\n"); return -1; } diff --git a/tc/q_hfsc.c b/tc/q_hfsc.c index dc9fed9..b0923ab 100644 --- a/tc/q_hfsc.c +++ b/tc/q_hfsc.c @@ -23,7 +23,7 @@ #include "utils.h" #include "tc_util.h" -static int hfsc_get_sc(int *, char ***, struct tc_service_curve *); +static int hfsc_get_sc(int *, char ***, struct tc_service_curve *, char *); static void @@ -70,7 +70,7 @@ explain1(char *arg) } static int -hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +hfsc_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { struct tc_hfsc_qopt qopt = {}; @@ -141,7 +141,7 @@ hfsc_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) static int hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct tc_service_curve rsc = {}, fsc = {}, usc = {}; int rsc_ok = 0, fsc_ok = 0, usc_ok = 0; @@ -150,21 +150,21 @@ hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, while (argc > 0) { if (matches(*argv, "rt") == 0) { NEXT_ARG(); - if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { + if (hfsc_get_sc(&argc, &argv, &rsc, dev) < 0) { explain1("rt"); return -1; } rsc_ok = 1; } else if (matches(*argv, "ls") == 0) { NEXT_ARG(); - if (hfsc_get_sc(&argc, &argv, &fsc) < 0) { + if (hfsc_get_sc(&argc, &argv, &fsc, dev) < 0) { explain1("ls"); return -1; } fsc_ok = 1; } else if (matches(*argv, "sc") == 0) { NEXT_ARG(); - if (hfsc_get_sc(&argc, &argv, &rsc) < 0) { + if (hfsc_get_sc(&argc, &argv, &rsc, dev) < 0) { explain1("sc"); return -1; } @@ -173,7 +173,7 @@ hfsc_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, fsc_ok = 1; } else if (matches(*argv, "ul") == 0) { NEXT_ARG(); - if (hfsc_get_sc(&argc, &argv, &usc) < 0) { + if (hfsc_get_sc(&argc, &argv, &usc, dev) < 0) { explain1("ul"); return -1; } @@ -281,7 +281,7 @@ struct qdisc_util hfsc_qdisc_util = { }; static int -hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc) +hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc, char *dev) { char **argv = *argvp; int argc = *argcp; @@ -289,7 +289,12 @@ hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc) if (matches(*argv, "m1") == 0) { NEXT_ARG(); - if (get_rate(&m1, *argv) < 0) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&m1, *argv, dev)) { + explain1("m1"); + return -1; + } + } else if (get_rate(&m1, *argv) < 0) { explain1("m1"); return -1; } @@ -307,7 +312,12 @@ hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc) if (matches(*argv, "m2") == 0) { NEXT_ARG(); - if (get_rate(&m2, *argv) < 0) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&m2, *argv, dev)) { + explain1("m2"); + return -1; + } + } else if (get_rate(&m2, *argv) < 0) { explain1("m2"); return -1; } @@ -324,7 +334,7 @@ hfsc_get_sc1(int *argcp, char ***argvp, struct tc_service_curve *sc) } static int -hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc) +hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc, char *dev) { char **argv = *argvp; int argc = *argcp; @@ -350,7 +360,12 @@ hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc) if (matches(*argv, "rate") == 0) { NEXT_ARG(); - if (get_rate(&rate, *argv) < 0) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&rate, *argv, dev)) { + explain1("rate"); + return -1; + } + } else if (get_rate(&rate, *argv) < 0) { explain1("rate"); return -1; } @@ -386,10 +401,10 @@ hfsc_get_sc2(int *argcp, char ***argvp, struct tc_service_curve *sc) } static int -hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc) +hfsc_get_sc(int *argcp, char ***argvp, struct tc_service_curve *sc, char *dev) { - if (hfsc_get_sc1(argcp, argvp, sc) < 0 && - hfsc_get_sc2(argcp, argvp, sc) < 0) + if (hfsc_get_sc1(argcp, argvp, sc, dev) < 0 && + hfsc_get_sc2(argcp, argvp, sc, dev) < 0) return -1; if (sc->m1 == 0 && sc->m2 == 0) { diff --git a/tc/q_hhf.c b/tc/q_hhf.c index d1f15f9..c60d425 100644 --- a/tc/q_hhf.c +++ b/tc/q_hhf.c @@ -25,7 +25,7 @@ static void explain(void) } static int hhf_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { unsigned int limit = 0; unsigned int quantum = 0; diff --git a/tc/q_htb.c b/tc/q_htb.c index db82852..fb52e72 100644 --- a/tc/q_htb.c +++ b/tc/q_htb.c @@ -59,7 +59,7 @@ static void explain1(char *arg) } -static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { unsigned int direct_qlen = ~0U; struct tc_htb_glob opt = { @@ -108,7 +108,7 @@ static int htb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl return 0; } -static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int ok = 0; struct tc_htb_opt opt = {}; @@ -178,7 +178,12 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str fprintf(stderr, "Double \"ceil\" spec\n"); return -1; } - if (get_rate64(&ceil64, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate64(&ceil64, *argv, dev)) { + explain1("ceil"); + return -1; + } + } else if (get_rate64(&ceil64, *argv)) { explain1("ceil"); return -1; } @@ -189,7 +194,12 @@ static int htb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, str fprintf(stderr, "Double \"rate\" spec\n"); return -1; } - if (get_rate64(&rate64, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate64(&rate64, *argv, dev)) { + explain1("rate"); + return -1; + } + } else if (get_rate64(&rate64, *argv)) { explain1("rate"); return -1; } diff --git a/tc/q_ingress.c b/tc/q_ingress.c index 31699a8..0ffd82c 100644 --- a/tc/q_ingress.c +++ b/tc/q_ingress.c @@ -21,7 +21,7 @@ static void explain(void) } static int ingress_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { while (argc > 0) { if (strcmp(*argv, "handle") == 0) { diff --git a/tc/q_mqprio.c b/tc/q_mqprio.c index 9979852..b568eea 100644 --- a/tc/q_mqprio.c +++ b/tc/q_mqprio.c @@ -33,7 +33,7 @@ static void explain(void) } static int mqprio_parse_opt(struct qdisc_util *qu, int argc, - char **argv, struct nlmsghdr *n) + char **argv, struct nlmsghdr *n, char *dev) { int idx; struct tc_mqprio_qopt opt = { diff --git a/tc/q_multiq.c b/tc/q_multiq.c index ce91fe8..f91ad3a 100644 --- a/tc/q_multiq.c +++ b/tc/q_multiq.c @@ -40,7 +40,7 @@ static void explain(void) } static int multiq_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct tc_multiq_qopt opt = {}; diff --git a/tc/q_netem.c b/tc/q_netem.c index 82eb46f..0b7b1df 100644 --- a/tc/q_netem.c +++ b/tc/q_netem.c @@ -167,7 +167,7 @@ static int get_ticks(__u32 *ticks, const char *str) } static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { int dist_size = 0; struct rtattr *tail; @@ -396,7 +396,12 @@ static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv, } else if (matches(*argv, "rate") == 0) { ++present[TCA_NETEM_RATE]; NEXT_ARG(); - if (get_rate64(&rate64, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate64(&rate64, *argv, dev)) { + explain1("rate"); + return -1; + } + } else if (get_rate64(&rate64, *argv)) { explain1("rate"); return -1; } diff --git a/tc/q_pie.c b/tc/q_pie.c index db72add..b331bb9 100644 --- a/tc/q_pie.c +++ b/tc/q_pie.c @@ -39,7 +39,7 @@ static void explain(void) #define BETA_MAX 32 static int pie_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { unsigned int limit = 0; unsigned int target = 0; diff --git a/tc/q_prio.c b/tc/q_prio.c index 677e25a..bdfc414 100644 --- a/tc/q_prio.c +++ b/tc/q_prio.c @@ -27,7 +27,7 @@ static void explain(void) fprintf(stderr, "Usage: ... prio bands NUMBER priomap P1 P2...[multiqueue]\n"); } -static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int prio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int pmap_mode = 0; int idx = 0; diff --git a/tc/q_qfq.c b/tc/q_qfq.c index fa270c8..91f683d 100644 --- a/tc/q_qfq.c +++ b/tc/q_qfq.c @@ -35,7 +35,7 @@ static void explain_class(void) } static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { if (argc > 0) { if (matches(*argv, "help") != 0) @@ -48,7 +48,7 @@ static int qfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, } static int qfq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct rtattr *tail; __u32 tmp; diff --git a/tc/q_red.c b/tc/q_red.c index 1564d6e..ddd78b0 100644 --- a/tc/q_red.c +++ b/tc/q_red.c @@ -32,7 +32,7 @@ static void explain(void) fprintf(stderr, " [ecn] [harddrop]\n"); } -static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { struct tc_red_qopt opt = {}; unsigned int burst = 0; @@ -83,7 +83,12 @@ static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl } } else if (strcmp(*argv, "bandwidth") == 0) { NEXT_ARG(); - if (get_rate(&rate, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate(&rate, *argv, dev)) { + fprintf(stderr, "Illegal \"bandwidth\"\n"); + return -1; + } + } else if (get_rate(&rate, *argv)) { fprintf(stderr, "Illegal \"bandwidth\"\n"); return -1; } diff --git a/tc/q_rr.c b/tc/q_rr.c index 71ce3ce..341d506 100644 --- a/tc/q_rr.c +++ b/tc/q_rr.c @@ -28,7 +28,7 @@ static void explain(void) } -static int rr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int rr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int pmap_mode = 0; int idx = 0; diff --git a/tc/q_sfb.c b/tc/q_sfb.c index d074e87..4f9fa5a 100644 --- a/tc/q_sfb.c +++ b/tc/q_sfb.c @@ -48,7 +48,7 @@ static int get_prob(__u32 *val, const char *arg) } static int sfb_parse_opt(struct qdisc_util *qu, int argc, char **argv, - struct nlmsghdr *n) + struct nlmsghdr *n, char *dev) { struct tc_sfb_qopt opt = { .rehash_interval = 600*1000, diff --git a/tc/q_sfq.c b/tc/q_sfq.c index a875abd..facf2ba 100644 --- a/tc/q_sfq.c +++ b/tc/q_sfq.c @@ -34,7 +34,7 @@ static void explain(void) fprintf(stderr, " [ ecn ] [ harddrop ]\n"); } -static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int sfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int ok = 0, red = 0; struct tc_sfq_qopt_v1 opt = {}; diff --git a/tc/q_tbf.c b/tc/q_tbf.c index 4955ee4..bd59e10 100644 --- a/tc/q_tbf.c +++ b/tc/q_tbf.c @@ -35,7 +35,7 @@ static void explain1(const char *arg, const char *val) } -static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { int ok = 0; struct tc_tbf_qopt opt = {}; @@ -125,7 +125,12 @@ static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl fprintf(stderr, "tbf: duplicate \"rate\" specification\n"); return -1; } - if (get_rate64(&rate64, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate64(&rate64, *argv, dev)) { + explain1("rate", *argv); + return -1; + } + } else if (get_rate64(&rate64, *argv)) { explain1("rate", *argv); return -1; } @@ -136,7 +141,12 @@ static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nl fprintf(stderr, "tbf: duplicate \"peakrate\" specification\n"); return -1; } - if (get_rate64(&prate64, *argv)) { + if (strchr(*argv, '%')) { + if (get_percent_rate64(&prate64, *argv, dev)) { + explain1("peakrate", *argv); + return -1; + } + } else if (get_rate64(&prate64, *argv)) { explain1("peakrate", *argv); return -1; } diff --git a/tc/tc.c b/tc/tc.c index fa71250..0943113 100644 --- a/tc/tc.c +++ b/tc/tc.c @@ -59,7 +59,7 @@ static int print_noqopt(struct qdisc_util *qu, FILE *f, return 0; } -static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) +static int parse_noqopt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, char *dev) { if (argc) { fprintf(stderr, "Unknown qdisc \"%s\", hence option \"%s\" is unparsable\n", qu->id, *argv); diff --git a/tc/tc_class.c b/tc/tc_class.c index c4a6a25..912c65f 100644 --- a/tc/tc_class.c +++ b/tc/tc_class.c @@ -128,7 +128,7 @@ static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv) fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k); return 1; } - if (q->parse_copt(q, argc, argv, &req.n)) + if (q->parse_copt(q, argc, argv, &req.n, d)) return 1; } else { if (argc) { diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c index fcb75f2..727d1bc 100644 --- a/tc/tc_qdisc.c +++ b/tc/tc_qdisc.c @@ -140,7 +140,7 @@ static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv) if (q) { if (q->parse_qopt) { - if (q->parse_qopt(q, argc, argv, &req.n)) + if (q->parse_qopt(q, argc, argv, &req.n, d)) return 1; } else if (argc) { fprintf(stderr, "qdisc '%s' does not support option parsing\n", k); diff --git a/tc/tc_util.c b/tc/tc_util.c index 472fc5d..f63add1 100644 --- a/tc/tc_util.c +++ b/tc/tc_util.c @@ -190,6 +190,72 @@ static const struct rate_suffix { { NULL } }; +int parse_percent_rate(char *rate, const char *str, char *dev) +{ + long max_rate_bits; + int ret, saved_errno; + double perc, rate_bits; + char *str_perc, *p; + + if (!dev[0]) { + fprintf(stderr, "No device specified; specify device to rate limit by percentage\n"); + return -1; + } + + if (read_prop(dev, "speed", &max_rate_bits)) + return -1; + + ret = sscanf(str, "%m[0-9.%]", &str_perc); + if (ret != 1) + goto malf; + + /* Make sure there's only one percent sign and it's at the end */ + perc = strtod(str_perc, &p); + if (*p != '%' || *(p++) != '\0') + goto malf; + + saved_errno = errno; + free(str_perc); + + if (perc > 100.0 || perc < 0.0 || saved_errno == ERANGE) { + fprintf(stderr, "Invalid rate specified; should be between [0,100]%% but is %s\n", str); + return -1; + } + + rate_bits = (perc * max_rate_bits) / 100.0; + + ret = snprintf(rate, 20, "%lf", rate_bits); + if (ret <= 0 || ret >= 20) { + fprintf(stderr, "Unable to parse calculated rate\n"); + return -1; + } + + return 0; + +malf: + fprintf(stderr, "Specified rate value could not be read or is malformed\n"); + return -1; +} + +int get_percent_rate(unsigned int *rate, const char *str, char *dev) +{ + char r_str[20]; + + if (parse_percent_rate(r_str, str, dev)) + return -1; + + return get_rate(rate, r_str); +} + +int get_percent_rate64(__u64 *rate, const char *str, char *dev) +{ + char r_str[20]; + + if (parse_percent_rate(r_str, str, dev)) + return -1; + + return get_rate64(rate, r_str); +} int get_rate(unsigned int *rate, const char *str) { diff --git a/tc/tc_util.h b/tc/tc_util.h index 583a21a..e79b44e 100644 --- a/tc/tc_util.h +++ b/tc/tc_util.h @@ -24,14 +24,14 @@ struct qdisc_util { struct qdisc_util *next; const char *id; int (*parse_qopt)(struct qdisc_util *qu, int argc, - char **argv, struct nlmsghdr *n); + char **argv, struct nlmsghdr *n, char *dev); int (*print_qopt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); int (*print_xstats)(struct qdisc_util *qu, FILE *f, struct rtattr *xstats); int (*parse_copt)(struct qdisc_util *qu, int argc, - char **argv, struct nlmsghdr *n); + char **argv, struct nlmsghdr *n, char *dev); int (*print_copt)(struct qdisc_util *qu, FILE *f, struct rtattr *opt); }; @@ -66,9 +66,13 @@ const char *get_tc_lib(void); struct qdisc_util *get_qdisc_kind(const char *str); struct filter_util *get_filter_kind(const char *str); +int read_prop(char *dev, char *prop, long *value); +int parse_percent_rate(char *rate, const char *str, char *dev); int get_qdisc_handle(__u32 *h, const char *str); int get_rate(unsigned int *rate, const char *str); +int get_percent_rate(unsigned int *rate, const char *str, char *dev); int get_rate64(__u64 *rate, const char *str); +int get_percent_rate64(__u64 *rate, const char *str, char *dev); int get_size(unsigned int *size, const char *str); int get_size_and_cell(unsigned int *size, int *cell_log, char *str); int get_time(unsigned int *time, const char *str); -- 1.9.1