iproute2/tc/q_red.c
<<
>>
Prefs
   1/*
   2 * q_red.c              RED.
   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#include <math.h>
  22
  23#include "utils.h"
  24#include "tc_util.h"
  25#include "tc_qevent.h"
  26
  27#include "tc_red.h"
  28
  29static void explain(void)
  30{
  31        fprintf(stderr,
  32                "Usage: ... red limit BYTES [min BYTES] [max BYTES] avpkt BYTES [burst PACKETS]\n"
  33                "               [adaptive] [probability PROBABILITY] [bandwidth KBPS]\n"
  34                "               [ecn] [harddrop] [nodrop]\n"
  35                "               [qevent early_drop block IDX] [qevent mark block IDX]\n");
  36}
  37
  38#define RED_SUPPORTED_FLAGS (TC_RED_HISTORIC_FLAGS | TC_RED_NODROP)
  39
  40static struct qevent_plain qe_early_drop = {};
  41static struct qevent_plain qe_mark = {};
  42static struct qevent_util qevents[] = {
  43        QEVENT("early_drop", plain, &qe_early_drop, TCA_RED_EARLY_DROP_BLOCK),
  44        QEVENT("mark", plain, &qe_mark, TCA_RED_MARK_BLOCK),
  45        {},
  46};
  47
  48static int red_parse_opt(struct qdisc_util *qu, int argc, char **argv,
  49                         struct nlmsghdr *n, const char *dev)
  50{
  51        struct nla_bitfield32 flags_bf = {
  52                .selector = RED_SUPPORTED_FLAGS,
  53        };
  54        struct tc_red_qopt opt = {};
  55        unsigned int burst = 0;
  56        unsigned int avpkt = 0;
  57        double probability = 0.02;
  58        unsigned int rate = 0;
  59        int parm;
  60        __u8 sbuf[256];
  61        __u32 max_P;
  62        struct rtattr *tail;
  63
  64        qevents_init(qevents);
  65
  66        while (argc > 0) {
  67                if (strcmp(*argv, "limit") == 0) {
  68                        NEXT_ARG();
  69                        if (get_size(&opt.limit, *argv)) {
  70                                fprintf(stderr, "Illegal \"limit\"\n");
  71                                return -1;
  72                        }
  73                } else if (strcmp(*argv, "min") == 0) {
  74                        NEXT_ARG();
  75                        if (get_size(&opt.qth_min, *argv)) {
  76                                fprintf(stderr, "Illegal \"min\"\n");
  77                                return -1;
  78                        }
  79                } else if (strcmp(*argv, "max") == 0) {
  80                        NEXT_ARG();
  81                        if (get_size(&opt.qth_max, *argv)) {
  82                                fprintf(stderr, "Illegal \"max\"\n");
  83                                return -1;
  84                        }
  85                } else if (strcmp(*argv, "burst") == 0) {
  86                        NEXT_ARG();
  87                        if (get_unsigned(&burst, *argv, 0)) {
  88                                fprintf(stderr, "Illegal \"burst\"\n");
  89                                return -1;
  90                        }
  91                } else if (strcmp(*argv, "avpkt") == 0) {
  92                        NEXT_ARG();
  93                        if (get_size(&avpkt, *argv)) {
  94                                fprintf(stderr, "Illegal \"avpkt\"\n");
  95                                return -1;
  96                        }
  97                } else if (strcmp(*argv, "probability") == 0) {
  98                        NEXT_ARG();
  99                        if (sscanf(*argv, "%lg", &probability) != 1) {
 100                                fprintf(stderr, "Illegal \"probability\"\n");
 101                                return -1;
 102                        }
 103                } else if (strcmp(*argv, "bandwidth") == 0) {
 104                        NEXT_ARG();
 105                        if (strchr(*argv, '%')) {
 106                                if (get_percent_rate(&rate, *argv, dev)) {
 107                                        fprintf(stderr, "Illegal \"bandwidth\"\n");
 108                                        return -1;
 109                                }
 110                        } else if (get_rate(&rate, *argv)) {
 111                                fprintf(stderr, "Illegal \"bandwidth\"\n");
 112                                return -1;
 113                        }
 114                } else if (strcmp(*argv, "ecn") == 0) {
 115                        flags_bf.value |= TC_RED_ECN;
 116                } else if (strcmp(*argv, "harddrop") == 0) {
 117                        flags_bf.value |= TC_RED_HARDDROP;
 118                } else if (strcmp(*argv, "nodrop") == 0) {
 119                        flags_bf.value |= TC_RED_NODROP;
 120                } else if (strcmp(*argv, "adaptative") == 0) {
 121                        flags_bf.value |= TC_RED_ADAPTATIVE;
 122                } else if (strcmp(*argv, "adaptive") == 0) {
 123                        flags_bf.value |= TC_RED_ADAPTATIVE;
 124                } else if (matches(*argv, "qevent") == 0) {
 125                        NEXT_ARG();
 126                        if (qevent_parse(qevents, &argc, &argv))
 127                                return -1;
 128                        continue;
 129                } else if (strcmp(*argv, "help") == 0) {
 130                        explain();
 131                        return -1;
 132                } else {
 133                        fprintf(stderr, "What is \"%s\"?\n", *argv);
 134                        explain();
 135                        return -1;
 136                }
 137                argc--; argv++;
 138        }
 139
 140        if (!opt.limit || !avpkt) {
 141                fprintf(stderr, "RED: Required parameter (limit, avpkt) is missing\n");
 142                return -1;
 143        }
 144        /* Compute default min/max thresholds based on
 145         * Sally Floyd's recommendations:
 146         * http://www.icir.org/floyd/REDparameters.txt
 147         */
 148        if (!opt.qth_max)
 149                opt.qth_max = opt.qth_min ? opt.qth_min * 3 : opt.limit / 4;
 150        if (!opt.qth_min)
 151                opt.qth_min = opt.qth_max / 3;
 152        if (!burst)
 153                burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt);
 154        if (!rate) {
 155                get_rate(&rate, "10Mbit");
 156                fprintf(stderr, "RED: set bandwidth to 10Mbit\n");
 157        }
 158        if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) {
 159                fprintf(stderr, "RED: failed to calculate EWMA constant.\n");
 160                return -1;
 161        }
 162        if (parm >= 10)
 163                fprintf(stderr, "RED: WARNING. Burst %u seems to be too large.\n", burst);
 164        opt.Wlog = parm;
 165        if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) {
 166                fprintf(stderr, "RED: failed to calculate probability.\n");
 167                return -1;
 168        }
 169        opt.Plog = parm;
 170        if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0) {
 171                fprintf(stderr, "RED: failed to calculate idle damping table.\n");
 172                return -1;
 173        }
 174        opt.Scell_log = parm;
 175
 176        tail = addattr_nest(n, 1024, TCA_OPTIONS);
 177        addattr_l(n, 1024, TCA_RED_PARMS, &opt, sizeof(opt));
 178        addattr_l(n, 1024, TCA_RED_STAB, sbuf, 256);
 179        max_P = probability * pow(2, 32);
 180        addattr_l(n, 1024, TCA_RED_MAX_P, &max_P, sizeof(max_P));
 181        addattr_l(n, 1024, TCA_RED_FLAGS, &flags_bf, sizeof(flags_bf));
 182        if (qevents_dump(qevents, n))
 183                return -1;
 184        addattr_nest_end(n, tail);
 185        return 0;
 186}
 187
 188static int red_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 189{
 190        struct rtattr *tb[TCA_RED_MAX + 1];
 191        struct nla_bitfield32 *flags_bf;
 192        struct tc_red_qopt *qopt;
 193        __u32 max_P = 0;
 194
 195        if (opt == NULL)
 196                return 0;
 197
 198        parse_rtattr_nested(tb, TCA_RED_MAX, opt);
 199
 200        if (tb[TCA_RED_PARMS] == NULL)
 201                return -1;
 202        qopt = RTA_DATA(tb[TCA_RED_PARMS]);
 203        if (RTA_PAYLOAD(tb[TCA_RED_PARMS])  < sizeof(*qopt))
 204                return -1;
 205
 206        if (tb[TCA_RED_MAX_P] &&
 207            RTA_PAYLOAD(tb[TCA_RED_MAX_P]) >= sizeof(__u32))
 208                max_P = rta_getattr_u32(tb[TCA_RED_MAX_P]);
 209
 210        if (tb[TCA_RED_FLAGS] &&
 211            RTA_PAYLOAD(tb[TCA_RED_FLAGS]) >= sizeof(*flags_bf)) {
 212                flags_bf = RTA_DATA(tb[TCA_RED_FLAGS]);
 213                qopt->flags = flags_bf->value;
 214        }
 215
 216        print_size(PRINT_ANY, "limit", "limit %s ", qopt->limit);
 217        print_size(PRINT_ANY, "min", "min %s ", qopt->qth_min);
 218        print_size(PRINT_ANY, "max", "max %s ", qopt->qth_max);
 219
 220        tc_red_print_flags(qopt->flags);
 221
 222        if (show_details) {
 223                print_uint(PRINT_ANY, "ewma", "ewma %u ", qopt->Wlog);
 224                if (max_P)
 225                        print_float(PRINT_ANY, "probability",
 226                                    "probability %lg ", max_P / pow(2, 32));
 227                else
 228                        print_uint(PRINT_ANY, "Plog", "Plog %u ", qopt->Plog);
 229                print_uint(PRINT_ANY, "Scell_log", "Scell_log %u",
 230                           qopt->Scell_log);
 231        }
 232
 233        qevents_init(qevents);
 234        if (qevents_read(qevents, tb))
 235                return -1;
 236        qevents_print(qevents, f);
 237        return 0;
 238}
 239
 240static int red_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
 241{
 242#ifdef TC_RED_ECN
 243        struct tc_red_xstats *st;
 244
 245        if (xstats == NULL)
 246                return 0;
 247
 248        if (RTA_PAYLOAD(xstats) < sizeof(*st))
 249                return -1;
 250
 251        st = RTA_DATA(xstats);
 252        print_uint(PRINT_ANY, "marked", "  marked %u ", st->marked);
 253        print_uint(PRINT_ANY, "early", "early %u ", st->early);
 254        print_uint(PRINT_ANY, "pdrop", "pdrop %u ", st->pdrop);
 255        print_uint(PRINT_ANY, "other", "other %u ", st->other);
 256#endif
 257        return 0;
 258}
 259
 260static int red_has_block(struct qdisc_util *qu, struct rtattr *opt, __u32 block_idx, bool *p_has)
 261{
 262        struct rtattr *tb[TCA_RED_MAX + 1];
 263
 264        if (opt == NULL)
 265                return 0;
 266
 267        parse_rtattr_nested(tb, TCA_RED_MAX, opt);
 268
 269        qevents_init(qevents);
 270        if (qevents_read(qevents, tb))
 271                return -1;
 272
 273        *p_has = qevents_have_block(qevents, block_idx);
 274        return 0;
 275}
 276
 277struct qdisc_util red_qdisc_util = {
 278        .id             = "red",
 279        .parse_qopt     = red_parse_opt,
 280        .print_qopt     = red_print_opt,
 281        .print_xstats   = red_print_xstats,
 282        .has_block      = red_has_block,
 283};
 284