iproute2/tc/q_tbf.c
<<
>>
Prefs
   1/*
   2 * q_tbf.c              TBF.
   3 *
   4 *              This program is free software; you can redistribute it and/or
   5 *              modify it under the terms of the GNU General Public License
   6 *              as published by the Free Software Foundation; either version
   7 *              2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  10 *
  11 */
  12
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <unistd.h>
  16#include <fcntl.h>
  17#include <sys/socket.h>
  18#include <netinet/in.h>
  19#include <arpa/inet.h>
  20#include <string.h>
  21
  22#include "utils.h"
  23#include "tc_util.h"
  24
  25static void explain(void)
  26{
  27        fprintf(stderr,
  28                "Usage: ... tbf limit BYTES burst BYTES[/BYTES] rate KBPS [ mtu BYTES[/BYTES] ]\n"
  29                "       [ peakrate KBPS ] [ latency TIME ] "
  30                "[ overhead BYTES ] [ linklayer TYPE ]\n");
  31}
  32
  33static void explain1(const char *arg, const char *val)
  34{
  35        fprintf(stderr, "tbf: illegal value for \"%s\": \"%s\"\n", arg, val);
  36}
  37
  38
  39static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv,
  40                         struct nlmsghdr *n, const char *dev)
  41{
  42        int ok = 0;
  43        struct tc_tbf_qopt opt = {};
  44        __u32 rtab[256];
  45        __u32 ptab[256];
  46        unsigned buffer = 0, mtu = 0, mpu = 0, latency = 0;
  47        int Rcell_log =  -1, Pcell_log = -1;
  48        unsigned short overhead = 0;
  49        unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
  50        struct rtattr *tail;
  51        __u64 rate64 = 0, prate64 = 0;
  52
  53        while (argc > 0) {
  54                if (matches(*argv, "limit") == 0) {
  55                        NEXT_ARG();
  56                        if (opt.limit) {
  57                                fprintf(stderr, "tbf: duplicate \"limit\" specification\n");
  58                                return -1;
  59                        }
  60                        if (latency) {
  61                                fprintf(stderr, "tbf: specifying both \"latency\" and \"limit\" is not allowed\n");
  62                                return -1;
  63                        }
  64                        if (get_size(&opt.limit, *argv)) {
  65                                explain1("limit", *argv);
  66                                return -1;
  67                        }
  68                        ok++;
  69                } else if (matches(*argv, "latency") == 0) {
  70                        NEXT_ARG();
  71                        if (latency) {
  72                                fprintf(stderr, "tbf: duplicate \"latency\" specification\n");
  73                                return -1;
  74                        }
  75                        if (opt.limit) {
  76                                fprintf(stderr, "tbf: specifying both \"limit\" and \"/latency\" is not allowed\n");
  77                                return -1;
  78                        }
  79                        if (get_time(&latency, *argv)) {
  80                                explain1("latency", *argv);
  81                                return -1;
  82                        }
  83                        ok++;
  84                } else if (matches(*argv, "burst") == 0 ||
  85                        strcmp(*argv, "buffer") == 0 ||
  86                        strcmp(*argv, "maxburst") == 0) {
  87                        const char *parm_name = *argv;
  88
  89                        NEXT_ARG();
  90                        if (buffer) {
  91                                fprintf(stderr, "tbf: duplicate \"buffer/burst/maxburst\" specification\n");
  92                                return -1;
  93                        }
  94                        if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
  95                                explain1(parm_name, *argv);
  96                                return -1;
  97                        }
  98                        ok++;
  99                } else if (strcmp(*argv, "mtu") == 0 ||
 100                           strcmp(*argv, "minburst") == 0) {
 101                        const char *parm_name = *argv;
 102
 103                        NEXT_ARG();
 104                        if (mtu) {
 105                                fprintf(stderr, "tbf: duplicate \"mtu/minburst\" specification\n");
 106                                return -1;
 107                        }
 108                        if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) {
 109                                explain1(parm_name, *argv);
 110                                return -1;
 111                        }
 112                        ok++;
 113                } else if (strcmp(*argv, "mpu") == 0) {
 114                        NEXT_ARG();
 115                        if (mpu) {
 116                                fprintf(stderr, "tbf: duplicate \"mpu\" specification\n");
 117                                return -1;
 118                        }
 119                        if (get_size(&mpu, *argv)) {
 120                                explain1("mpu", *argv);
 121                                return -1;
 122                        }
 123                        ok++;
 124                } else if (strcmp(*argv, "rate") == 0) {
 125                        NEXT_ARG();
 126                        if (rate64) {
 127                                fprintf(stderr, "tbf: duplicate \"rate\" specification\n");
 128                                return -1;
 129                        }
 130                        if (strchr(*argv, '%')) {
 131                                if (get_percent_rate64(&rate64, *argv, dev)) {
 132                                        explain1("rate", *argv);
 133                                        return -1;
 134                                }
 135                        } else if (get_rate64(&rate64, *argv)) {
 136                                explain1("rate", *argv);
 137                                return -1;
 138                        }
 139                        ok++;
 140                } else if (matches(*argv, "peakrate") == 0) {
 141                        NEXT_ARG();
 142                        if (prate64) {
 143                                fprintf(stderr, "tbf: duplicate \"peakrate\" specification\n");
 144                                return -1;
 145                        }
 146                        if (strchr(*argv, '%')) {
 147                                if (get_percent_rate64(&prate64, *argv, dev)) {
 148                                        explain1("peakrate", *argv);
 149                                        return -1;
 150                                }
 151                        } else if (get_rate64(&prate64, *argv)) {
 152                                explain1("peakrate", *argv);
 153                                return -1;
 154                        }
 155                        ok++;
 156                } else if (matches(*argv, "overhead") == 0) {
 157                        NEXT_ARG();
 158                        if (overhead) {
 159                                fprintf(stderr, "tbf: duplicate \"overhead\" specification\n");
 160                                return -1;
 161                        }
 162                        if (get_u16(&overhead, *argv, 10)) {
 163                                explain1("overhead", *argv); return -1;
 164                        }
 165                } else if (matches(*argv, "linklayer") == 0) {
 166                        NEXT_ARG();
 167                        if (get_linklayer(&linklayer, *argv)) {
 168                                explain1("linklayer", *argv); return -1;
 169                        }
 170                } else if (strcmp(*argv, "help") == 0) {
 171                        explain();
 172                        return -1;
 173                } else {
 174                        fprintf(stderr, "tbf: unknown parameter \"%s\"\n", *argv);
 175                        explain();
 176                        return -1;
 177                }
 178                argc--; argv++;
 179        }
 180
 181        int verdict = 0;
 182
 183        /* Be nice to the user: try to emit all error messages in
 184         * one go rather than reveal one more problem when a
 185         * previous one has been fixed.
 186         */
 187        if (rate64 == 0) {
 188                fprintf(stderr, "tbf: the \"rate\" parameter is mandatory.\n");
 189                verdict = -1;
 190        }
 191        if (!buffer) {
 192                fprintf(stderr, "tbf: the \"burst\" parameter is mandatory.\n");
 193                verdict = -1;
 194        }
 195        if (prate64) {
 196                if (!mtu) {
 197                        fprintf(stderr, "tbf: when \"peakrate\" is specified, \"mtu\" must also be specified.\n");
 198                        verdict = -1;
 199                }
 200        }
 201
 202        if (opt.limit == 0 && latency == 0) {
 203                fprintf(stderr, "tbf: either \"limit\" or \"latency\" is required.\n");
 204                verdict = -1;
 205        }
 206
 207        if (verdict != 0) {
 208                explain();
 209                return verdict;
 210        }
 211
 212        opt.rate.rate = (rate64 >= (1ULL << 32)) ? ~0U : rate64;
 213        opt.peakrate.rate = (prate64 >= (1ULL << 32)) ? ~0U : prate64;
 214
 215        if (opt.limit == 0) {
 216                double lim = rate64*(double)latency/TIME_UNITS_PER_SEC + buffer;
 217
 218                if (prate64) {
 219                        double lim2 = prate64*(double)latency/TIME_UNITS_PER_SEC + mtu;
 220
 221                        if (lim2 < lim)
 222                                lim = lim2;
 223                }
 224                opt.limit = lim;
 225        }
 226
 227        opt.rate.mpu      = mpu;
 228        opt.rate.overhead = overhead;
 229        if (tc_calc_rtable(&opt.rate, rtab, Rcell_log, mtu, linklayer) < 0) {
 230                fprintf(stderr, "tbf: failed to calculate rate table.\n");
 231                return -1;
 232        }
 233        opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer);
 234
 235        if (opt.peakrate.rate) {
 236                opt.peakrate.mpu      = mpu;
 237                opt.peakrate.overhead = overhead;
 238                if (tc_calc_rtable(&opt.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) {
 239                        fprintf(stderr, "tbf: failed to calculate peak rate table.\n");
 240                        return -1;
 241                }
 242                opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu);
 243        }
 244
 245        tail = addattr_nest(n, 1024, TCA_OPTIONS);
 246        addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt));
 247        addattr_l(n, 2124, TCA_TBF_BURST, &buffer, sizeof(buffer));
 248        if (rate64 >= (1ULL << 32))
 249                addattr_l(n, 2124, TCA_TBF_RATE64, &rate64, sizeof(rate64));
 250        addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024);
 251        if (opt.peakrate.rate) {
 252                if (prate64 >= (1ULL << 32))
 253                        addattr_l(n, 3124, TCA_TBF_PRATE64, &prate64, sizeof(prate64));
 254                addattr_l(n, 3224, TCA_TBF_PBURST, &mtu, sizeof(mtu));
 255                addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024);
 256        }
 257        addattr_nest_end(n, tail);
 258        return 0;
 259}
 260
 261static int tbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 262{
 263        struct rtattr *tb[TCA_TBF_MAX+1];
 264        struct tc_tbf_qopt *qopt;
 265        unsigned int linklayer;
 266        double buffer, mtu;
 267        double latency, lat2;
 268        __u64 rate64 = 0, prate64 = 0;
 269
 270        SPRINT_BUF(b1);
 271        SPRINT_BUF(b2);
 272        SPRINT_BUF(b3);
 273
 274        if (opt == NULL)
 275                return 0;
 276
 277        parse_rtattr_nested(tb, TCA_TBF_MAX, opt);
 278
 279        if (tb[TCA_TBF_PARMS] == NULL)
 280                return -1;
 281
 282        qopt = RTA_DATA(tb[TCA_TBF_PARMS]);
 283        if (RTA_PAYLOAD(tb[TCA_TBF_PARMS])  < sizeof(*qopt))
 284                return -1;
 285        rate64 = qopt->rate.rate;
 286        if (tb[TCA_TBF_RATE64] &&
 287            RTA_PAYLOAD(tb[TCA_TBF_RATE64]) >= sizeof(rate64))
 288                rate64 = rta_getattr_u64(tb[TCA_TBF_RATE64]);
 289        tc_print_rate(PRINT_ANY, "rate", "rate %s ", rate64);
 290        buffer = tc_calc_xmitsize(rate64, qopt->buffer);
 291        if (show_details) {
 292                sprintf(b1, "%s/%u",  sprint_size(buffer, b2),
 293                        1 << qopt->rate.cell_log);
 294                print_string(PRINT_ANY, "burst", "burst %s ", b1);
 295                print_size(PRINT_ANY, "mpu", "mpu %s ", qopt->rate.mpu);
 296        } else {
 297                print_size(PRINT_ANY, "burst", "burst %s ", buffer);
 298        }
 299        if (show_raw)
 300                print_hex(PRINT_ANY, "burst_raw", "[%08x] ", qopt->buffer);
 301        prate64 = qopt->peakrate.rate;
 302        if (tb[TCA_TBF_PRATE64] &&
 303            RTA_PAYLOAD(tb[TCA_TBF_PRATE64]) >= sizeof(prate64))
 304                prate64 = rta_getattr_u64(tb[TCA_TBF_PRATE64]);
 305        if (prate64) {
 306                tc_print_rate(PRINT_FP, "peakrate", "peakrate %s ", prate64);
 307                if (qopt->mtu || qopt->peakrate.mpu) {
 308                        mtu = tc_calc_xmitsize(prate64, qopt->mtu);
 309                        if (show_details) {
 310                                sprintf(b1, "%s/%u",  sprint_size(mtu, b2),
 311                                        1 << qopt->peakrate.cell_log);
 312                                print_string(PRINT_ANY, "mtu", "mtu %s ", b1);
 313                                print_size(PRINT_ANY, "mpu", "mpu %s ",
 314                                           qopt->peakrate.mpu);
 315                        } else {
 316                                print_size(PRINT_ANY, "minburst",
 317                                           "minburst %s ", mtu);
 318                        }
 319                        if (show_raw)
 320                                print_hex(PRINT_ANY, "mtu_raw", "[%08x] ",
 321                                            qopt->mtu);
 322                }
 323        }
 324
 325        latency = TIME_UNITS_PER_SEC * (qopt->limit / (double)rate64) -
 326                  tc_core_tick2time(qopt->buffer);
 327        if (prate64) {
 328                lat2 = TIME_UNITS_PER_SEC * (qopt->limit / (double)prate64) -
 329                       tc_core_tick2time(qopt->mtu);
 330
 331                if (lat2 > latency)
 332                        latency = lat2;
 333        }
 334        if (latency >= 0.0) {
 335                print_u64(PRINT_JSON, "lat", NULL, latency);
 336                print_string(PRINT_FP, NULL, "lat %s ",
 337                             sprint_time(latency, b1));
 338        }
 339        if (show_raw || latency < 0.0)
 340                print_size(PRINT_ANY, "limit", "limit %s ", qopt->limit);
 341        if (qopt->rate.overhead)
 342                print_int(PRINT_ANY, "overhead", "overhead %d ",
 343                          qopt->rate.overhead);
 344        linklayer = (qopt->rate.linklayer & TC_LINKLAYER_MASK);
 345        if (linklayer > TC_LINKLAYER_ETHERNET || show_details)
 346                print_string(PRINT_ANY, "linklayer", "linklayer %s ",
 347                             sprint_linklayer(linklayer, b3));
 348
 349        return 0;
 350}
 351
 352struct qdisc_util tbf_qdisc_util = {
 353        .id             = "tbf",
 354        .parse_qopt     = tbf_parse_opt,
 355        .print_qopt     = tbf_print_opt,
 356};
 357