iproute2/tc/m_gate.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
   2/* Copyright 2020 NXP */
   3
   4#include <stdio.h>
   5#include <stdlib.h>
   6#include <unistd.h>
   7#include <string.h>
   8#include <linux/if_ether.h>
   9#include "utils.h"
  10#include "rt_names.h"
  11#include "tc_util.h"
  12#include "list.h"
  13#include <linux/tc_act/tc_gate.h>
  14
  15struct gate_entry {
  16        struct list_head list;
  17        uint8_t gate_state;
  18        uint32_t interval;
  19        int32_t ipv;
  20        int32_t maxoctets;
  21};
  22
  23#define CLOCKID_INVALID (-1)
  24static const struct clockid_table {
  25        const char *name;
  26        clockid_t clockid;
  27} clockt_map[] = {
  28        { "REALTIME", CLOCK_REALTIME },
  29        { "TAI", CLOCK_TAI },
  30        { "BOOTTIME", CLOCK_BOOTTIME },
  31        { "MONOTONIC", CLOCK_MONOTONIC },
  32        { NULL }
  33};
  34
  35static void explain(void)
  36{
  37        fprintf(stderr,
  38                "Usage: gate [ priority PRIO-SPEC ] [ base-time BASE-TIME ]\n"
  39                "       [ cycle-time CYCLE-TIME ]\n"
  40                "       [ cycle-time-ext CYCLE-TIME-EXT ]\n"
  41                "       [ clockid CLOCKID ] [flags FLAGS]\n"
  42                "       [ sched-entry GATE0 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
  43                "       [ sched-entry GATE1 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
  44                "       ......\n"
  45                "       [ sched-entry GATEn INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n"
  46                "       [ CONTROL ]\n"
  47                "       GATEn := open | close\n"
  48                "       INTERVAL : nanoseconds period of gate slot\n"
  49                "       INTERNAL-PRIO-VALUE : internal priority decide which\n"
  50                "                             rx queue number direct to.\n"
  51                "                             default to be -1 which means wildcard.\n"
  52                "       MAX-OCTETS : maximum number of MSDU octets that are\n"
  53                "                    permitted to pas the gate during the\n"
  54                "                    specified TimeInterval.\n"
  55                "                    default to be -1 which means wildcard.\n"
  56                "       CONTROL := pipe | drop | continue | pass |\n"
  57                "                  goto chain <CHAIN_INDEX>\n");
  58}
  59
  60static void usage(void)
  61{
  62        explain();
  63        exit(-1);
  64}
  65
  66static void explain_entry_format(void)
  67{
  68        fprintf(stderr, "Usage: sched-entry <open | close> <interval> [ <interval ipv> <octets max bytes> ]\n");
  69}
  70
  71static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
  72                      int tca_id, struct nlmsghdr *n);
  73static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg);
  74
  75struct action_util gate_action_util = {
  76        .id = "gate",
  77        .parse_aopt = parse_gate,
  78        .print_aopt = print_gate,
  79};
  80
  81static int get_clockid(__s32 *val, const char *arg)
  82{
  83        const struct clockid_table *c;
  84
  85        if (strcasestr(arg, "CLOCK_") != NULL)
  86                arg += sizeof("CLOCK_") - 1;
  87
  88        for (c = clockt_map; c->name; c++) {
  89                if (strcasecmp(c->name, arg) == 0) {
  90                        *val = c->clockid;
  91                        return 0;
  92                }
  93        }
  94
  95        return -1;
  96}
  97
  98static const char *get_clock_name(clockid_t clockid)
  99{
 100        const struct clockid_table *c;
 101
 102        for (c = clockt_map; c->name; c++) {
 103                if (clockid == c->clockid)
 104                        return c->name;
 105        }
 106
 107        return "invalid";
 108}
 109
 110static int get_gate_state(__u8 *val, const char *arg)
 111{
 112        if (!strcasecmp("OPEN", arg)) {
 113                *val = 1;
 114                return 0;
 115        }
 116
 117        if (!strcasecmp("CLOSE", arg)) {
 118                *val = 0;
 119                return 0;
 120        }
 121
 122        return -1;
 123}
 124
 125static struct gate_entry *create_gate_entry(uint8_t gate_state,
 126                                            uint32_t interval,
 127                                            int32_t ipv,
 128                                            int32_t maxoctets)
 129{
 130        struct gate_entry *e;
 131
 132        e = calloc(1, sizeof(*e));
 133        if (!e)
 134                return NULL;
 135
 136        e->gate_state = gate_state;
 137        e->interval = interval;
 138        e->ipv = ipv;
 139        e->maxoctets = maxoctets;
 140
 141        return e;
 142}
 143
 144static int add_gate_list(struct list_head *gate_entries, struct nlmsghdr *n)
 145{
 146        struct gate_entry *e;
 147
 148        list_for_each_entry(e, gate_entries, list) {
 149                struct rtattr *a;
 150
 151                a = addattr_nest(n, 1024, TCA_GATE_ONE_ENTRY | NLA_F_NESTED);
 152
 153                if (e->gate_state)
 154                        addattr(n, MAX_MSG, TCA_GATE_ENTRY_GATE);
 155
 156                addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_INTERVAL,
 157                          &e->interval, sizeof(e->interval));
 158                addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_IPV,
 159                          &e->ipv, sizeof(e->ipv));
 160                addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_MAX_OCTETS,
 161                          &e->maxoctets, sizeof(e->maxoctets));
 162
 163                addattr_nest_end(n, a);
 164        }
 165
 166        return 0;
 167}
 168
 169static void free_entries(struct list_head *gate_entries)
 170{
 171        struct gate_entry *e, *n;
 172
 173        list_for_each_entry_safe(e, n, gate_entries, list) {
 174                list_del(&e->list);
 175                free(e);
 176        }
 177}
 178
 179static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p,
 180                      int tca_id, struct nlmsghdr *n)
 181{
 182        struct tc_gate parm = {.action = TC_ACT_PIPE};
 183        struct list_head gate_entries;
 184        __s32 clockid = CLOCKID_INVALID;
 185        struct rtattr *tail, *nle;
 186        char **argv = *argv_p;
 187        int argc = *argc_p;
 188        __s64 base_time = 0;
 189        __s64 cycle_time = 0;
 190        __s64 cycle_time_ext = 0;
 191        int entry_num = 0;
 192        char *invalidarg;
 193        __u32 flags = 0;
 194        int prio = -1;
 195
 196        int err;
 197
 198        if (matches(*argv, "gate") != 0)
 199                return -1;
 200
 201        NEXT_ARG();
 202        if (argc <= 0)
 203                return -1;
 204
 205        INIT_LIST_HEAD(&gate_entries);
 206
 207        while (argc > 0) {
 208                if (matches(*argv, "index") == 0) {
 209                        NEXT_ARG();
 210                        if (get_u32(&parm.index, *argv, 10)) {
 211                                invalidarg = "index";
 212                                goto err_arg;
 213                        }
 214                } else if (matches(*argv, "priority") == 0) {
 215                        NEXT_ARG();
 216                        if (get_s32(&prio, *argv, 0)) {
 217                                invalidarg = "priority";
 218                                goto err_arg;
 219                        }
 220                } else if (matches(*argv, "base-time") == 0) {
 221                        NEXT_ARG();
 222                        if (get_s64(&base_time, *argv, 10) &&
 223                            get_time64(&base_time, *argv)) {
 224                                invalidarg = "base-time";
 225                                goto err_arg;
 226                        }
 227                } else if (matches(*argv, "cycle-time") == 0) {
 228                        NEXT_ARG();
 229                        if (get_s64(&cycle_time, *argv, 10) &&
 230                            get_time64(&cycle_time, *argv)) {
 231                                invalidarg = "cycle-time";
 232                                goto err_arg;
 233                        }
 234                } else if (matches(*argv, "cycle-time-ext") == 0) {
 235                        NEXT_ARG();
 236                        if (get_s64(&cycle_time_ext, *argv, 10) &&
 237                            get_time64(&cycle_time_ext, *argv)) {
 238                                invalidarg = "cycle-time-ext";
 239                                goto err_arg;
 240                        }
 241                } else if (matches(*argv, "clockid") == 0) {
 242                        NEXT_ARG();
 243                        if (get_clockid(&clockid, *argv)) {
 244                                invalidarg = "clockid";
 245                                goto err_arg;
 246                        }
 247                } else if (matches(*argv, "flags") == 0) {
 248                        NEXT_ARG();
 249                        if (get_u32(&flags, *argv, 0)) {
 250                                invalidarg = "flags";
 251                                goto err_arg;
 252                        }
 253                } else if (matches(*argv, "sched-entry") == 0) {
 254                        unsigned int maxoctets_uint = 0;
 255                        int32_t maxoctets = -1;
 256                        struct gate_entry *e;
 257                        uint8_t gate_state = 0;
 258                        __s64 interval_s64 = 0;
 259                        uint32_t interval = 0;
 260                        int32_t ipv = -1;
 261
 262                        if (!NEXT_ARG_OK()) {
 263                                explain_entry_format();
 264                                fprintf(stderr, "\"sched-entry\" is incomplete\n");
 265                                free_entries(&gate_entries);
 266                                return -1;
 267                        }
 268
 269                        NEXT_ARG();
 270
 271                        if (get_gate_state(&gate_state, *argv)) {
 272                                explain_entry_format();
 273                                fprintf(stderr, "\"sched-entry\" is incomplete\n");
 274                                free_entries(&gate_entries);
 275                                return -1;
 276                        }
 277
 278                        if (!NEXT_ARG_OK()) {
 279                                explain_entry_format();
 280                                fprintf(stderr, "\"sched-entry\" is incomplete\n");
 281                                free_entries(&gate_entries);
 282                                return -1;
 283                        }
 284
 285                        NEXT_ARG();
 286
 287                        if (get_u32(&interval, *argv, 0) &&
 288                            get_time64(&interval_s64, *argv)) {
 289                                explain_entry_format();
 290                                fprintf(stderr, "\"sched-entry\" is incomplete\n");
 291                                free_entries(&gate_entries);
 292                                return -1;
 293                        }
 294
 295                        if (interval_s64 > UINT_MAX) {
 296                                fprintf(stderr, "\"interval\" is too large\n");
 297                                free_entries(&gate_entries);
 298                                return -1;
 299                        } else if (interval_s64) {
 300                                interval = interval_s64;
 301                        }
 302
 303                        if (!NEXT_ARG_OK())
 304                                goto create_entry;
 305
 306                        NEXT_ARG();
 307
 308                        if (get_s32(&ipv, *argv, 0)) {
 309                                PREV_ARG();
 310                                goto create_entry;
 311                        }
 312
 313                        if (!gate_state)
 314                                ipv = -1;
 315
 316                        if (!NEXT_ARG_OK())
 317                                goto create_entry;
 318
 319                        NEXT_ARG();
 320
 321                        if (get_s32(&maxoctets, *argv, 0) &&
 322                            get_size(&maxoctets_uint, *argv))
 323                                PREV_ARG();
 324
 325                        if (maxoctets_uint > INT_MAX) {
 326                                fprintf(stderr, "\"maxoctets\" is too large\n");
 327                                free_entries(&gate_entries);
 328                                return -1;
 329                        } else if (maxoctets_uint ) {
 330                                maxoctets = maxoctets_uint;
 331                        }
 332
 333                        if (!gate_state)
 334                                maxoctets = -1;
 335
 336create_entry:
 337                        e = create_gate_entry(gate_state, interval,
 338                                              ipv, maxoctets);
 339                        if (!e) {
 340                                fprintf(stderr, "gate: not enough memory\n");
 341                                free_entries(&gate_entries);
 342                                return -1;
 343                        }
 344
 345                        list_add_tail(&e->list, &gate_entries);
 346                        entry_num++;
 347                } else if (matches(*argv, "help") == 0) {
 348                        usage();
 349                } else {
 350                        break;
 351                }
 352
 353                argc--;
 354                argv++;
 355        }
 356
 357        parse_action_control_dflt(&argc, &argv, &parm.action,
 358                                  false, TC_ACT_PIPE);
 359
 360        if (!entry_num && !parm.index) {
 361                fprintf(stderr, "gate: must add at least one entry\n");
 362                return -1;
 363        }
 364
 365        tail = addattr_nest(n, MAX_MSG, tca_id | NLA_F_NESTED);
 366        addattr_l(n, MAX_MSG, TCA_GATE_PARMS, &parm, sizeof(parm));
 367
 368        if (prio != -1)
 369                addattr_l(n, MAX_MSG, TCA_GATE_PRIORITY, &prio, sizeof(prio));
 370
 371        if (flags)
 372                addattr_l(n, MAX_MSG, TCA_GATE_FLAGS, &flags, sizeof(flags));
 373
 374        if (base_time)
 375                addattr_l(n, MAX_MSG, TCA_GATE_BASE_TIME,
 376                          &base_time, sizeof(base_time));
 377
 378        if (cycle_time)
 379                addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME,
 380                          &cycle_time, sizeof(cycle_time));
 381
 382        if (cycle_time_ext)
 383                addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME_EXT,
 384                          &cycle_time_ext, sizeof(cycle_time_ext));
 385
 386        if (clockid != CLOCKID_INVALID)
 387                addattr_l(n, MAX_MSG, TCA_GATE_CLOCKID,
 388                          &clockid, sizeof(clockid));
 389
 390        nle = addattr_nest(n, MAX_MSG, TCA_GATE_ENTRY_LIST | NLA_F_NESTED);
 391        err = add_gate_list(&gate_entries, n);
 392        if (err < 0) {
 393                fprintf(stderr, "Could not add entries to netlink message\n");
 394                free_entries(&gate_entries);
 395                return -1;
 396        }
 397
 398        addattr_nest_end(n, nle);
 399        addattr_nest_end(n, tail);
 400        free_entries(&gate_entries);
 401        *argc_p = argc;
 402        *argv_p = argv;
 403
 404        return 0;
 405err_arg:
 406        invarg(invalidarg, *argv);
 407        free_entries(&gate_entries);
 408
 409        return -1;
 410}
 411
 412static int print_gate_list(struct rtattr *list)
 413{
 414        struct rtattr *item;
 415        int rem;
 416
 417        rem = RTA_PAYLOAD(list);
 418
 419        print_string(PRINT_FP, NULL, "%s", _SL_);
 420        print_string(PRINT_FP, NULL, "\tschedule:%s", _SL_);
 421        open_json_array(PRINT_JSON, "schedule");
 422
 423        for (item = RTA_DATA(list);
 424             RTA_OK(item, rem);
 425             item = RTA_NEXT(item, rem)) {
 426                struct rtattr *tb[TCA_GATE_ENTRY_MAX + 1];
 427                __u32 index = 0, interval = 0;
 428                __u8 gate_state = 0;
 429                __s32 ipv = -1, maxoctets = -1;
 430                SPRINT_BUF(buf);
 431
 432                parse_rtattr_nested(tb, TCA_GATE_ENTRY_MAX, item);
 433
 434                if (tb[TCA_GATE_ENTRY_INDEX])
 435                        index = rta_getattr_u32(tb[TCA_GATE_ENTRY_INDEX]);
 436
 437                if (tb[TCA_GATE_ENTRY_GATE])
 438                        gate_state = 1;
 439
 440                if (tb[TCA_GATE_ENTRY_INTERVAL])
 441                        interval = rta_getattr_u32(tb[TCA_GATE_ENTRY_INTERVAL]);
 442
 443                if (tb[TCA_GATE_ENTRY_IPV])
 444                        ipv = rta_getattr_s32(tb[TCA_GATE_ENTRY_IPV]);
 445
 446                if (tb[TCA_GATE_ENTRY_MAX_OCTETS])
 447                        maxoctets = rta_getattr_s32(tb[TCA_GATE_ENTRY_MAX_OCTETS]);
 448
 449                open_json_object(NULL);
 450                print_uint(PRINT_ANY, "number", "\t number %4u", index);
 451                print_string(PRINT_ANY, "gate_state", "\tgate-state %s ",
 452                             gate_state ? "open" : "close");
 453
 454                print_uint(PRINT_JSON, "interval", NULL, interval);
 455
 456                memset(buf, 0, sizeof(buf));
 457                print_string(PRINT_FP, NULL, "\tinterval %s",
 458                             sprint_time64(interval, buf));
 459
 460                if (ipv != -1) {
 461                        print_uint(PRINT_ANY, "ipv", "\t ipv %-10u", ipv);
 462                } else {
 463                        print_int(PRINT_JSON, "ipv", NULL, ipv);
 464                        print_string(PRINT_FP, NULL, "\t ipv %s", "wildcard");
 465                }
 466
 467                if (maxoctets != -1) {
 468                        print_size(PRINT_ANY, "max_octets", "\t max-octets %s",
 469                                   maxoctets);
 470                } else {
 471                        print_string(PRINT_FP, NULL,
 472                                     "\t max-octets %s", "wildcard");
 473                        print_int(PRINT_JSON, "max_octets", NULL, maxoctets);
 474                }
 475
 476                close_json_object();
 477                print_string(PRINT_FP, NULL, "%s", _SL_);
 478        }
 479
 480        close_json_array(PRINT_ANY, "");
 481
 482        return 0;
 483}
 484
 485static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg)
 486{
 487        struct tc_gate *parm;
 488        struct rtattr *tb[TCA_GATE_MAX + 1];
 489        __s32 clockid = CLOCKID_INVALID;
 490        __s64 base_time = 0;
 491        __s64 cycle_time = 0;
 492        __s64 cycle_time_ext = 0;
 493        SPRINT_BUF(buf);
 494        int prio = -1;
 495
 496        if (arg == NULL)
 497                return -1;
 498
 499        parse_rtattr_nested(tb, TCA_GATE_MAX, arg);
 500
 501        if (!tb[TCA_GATE_PARMS]) {
 502                fprintf(stderr, "Missing gate parameters\n");
 503                return -1;
 504        }
 505
 506        print_string(PRINT_FP, NULL, "%s", "\n");
 507
 508        parm = RTA_DATA(tb[TCA_GATE_PARMS]);
 509
 510        if (tb[TCA_GATE_PRIORITY])
 511                prio = rta_getattr_s32(tb[TCA_GATE_PRIORITY]);
 512
 513        if (prio != -1) {
 514                print_int(PRINT_ANY, "priority", "\tpriority %-8d", prio);
 515        } else {
 516                print_string(PRINT_FP, NULL, "\tpriority %s", "wildcard");
 517                print_int(PRINT_JSON, "priority", NULL, prio);
 518        }
 519
 520        if (tb[TCA_GATE_CLOCKID])
 521                clockid = rta_getattr_s32(tb[TCA_GATE_CLOCKID]);
 522        print_string(PRINT_ANY, "clockid", "\tclockid %s",
 523                     get_clock_name(clockid));
 524
 525        if (tb[TCA_GATE_FLAGS]) {
 526                __u32 flags;
 527
 528                flags = rta_getattr_u32(tb[TCA_GATE_FLAGS]);
 529                print_0xhex(PRINT_ANY, "flags", "\tflags %#x", flags);
 530        }
 531
 532        print_string(PRINT_FP, NULL, "%s", "\n");
 533
 534        if (tb[TCA_GATE_BASE_TIME])
 535                base_time = rta_getattr_s64(tb[TCA_GATE_BASE_TIME]);
 536
 537        memset(buf, 0, sizeof(buf));
 538        print_string(PRINT_FP, NULL, "\tbase-time %s",
 539                     sprint_time64(base_time, buf));
 540        print_lluint(PRINT_JSON, "base_time", NULL, base_time);
 541
 542        if (tb[TCA_GATE_CYCLE_TIME])
 543                cycle_time = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME]);
 544
 545        memset(buf, 0, sizeof(buf));
 546        print_string(PRINT_FP, NULL,
 547                     "\tcycle-time %s", sprint_time64(cycle_time, buf));
 548        print_lluint(PRINT_JSON, "cycle_time", NULL, cycle_time);
 549
 550        if (tb[TCA_GATE_CYCLE_TIME_EXT])
 551                cycle_time_ext = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME_EXT]);
 552
 553        memset(buf, 0, sizeof(buf));
 554        print_string(PRINT_FP, NULL, "\tcycle-time-ext %s",
 555                     sprint_time64(cycle_time_ext, buf));
 556        print_lluint(PRINT_JSON, "cycle_time_ext", NULL, cycle_time_ext);
 557
 558        if (tb[TCA_GATE_ENTRY_LIST])
 559                print_gate_list(tb[TCA_GATE_ENTRY_LIST]);
 560
 561        print_action_control(f, "\t", parm->action, "");
 562
 563        print_uint(PRINT_ANY, "index", "\n\t index %u", parm->index);
 564        print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt);
 565        print_int(PRINT_ANY, "bind", " bind %d", parm->bindcnt);
 566
 567        if (show_stats) {
 568                if (tb[TCA_GATE_TM]) {
 569                        struct tcf_t *tm = RTA_DATA(tb[TCA_GATE_TM]);
 570
 571                        print_tm(f, tm);
 572                }
 573        }
 574
 575        print_string(PRINT_FP, NULL, "%s", "\n");
 576
 577        return 0;
 578}
 579