iproute2/tc/m_ematch.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * m_ematch.c           Extended Matches
   4 *
   5 * Authors:     Thomas Graf <tgraf@suug.ch>
   6 */
   7
   8#include <stdio.h>
   9#include <stdlib.h>
  10#include <unistd.h>
  11#include <fcntl.h>
  12#include <sys/socket.h>
  13#include <netinet/in.h>
  14#include <arpa/inet.h>
  15#include <string.h>
  16#include <dlfcn.h>
  17#include <stdarg.h>
  18#include <errno.h>
  19
  20#include "utils.h"
  21#include "tc_util.h"
  22#include "m_ematch.h"
  23
  24#define EMATCH_MAP_USR CONF_USR_DIR "/ematch_map"
  25#define EMATCH_MAP_ETC CONF_ETC_DIR "/ematch_map"
  26
  27static struct ematch_util *ematch_list;
  28
  29/* export to bison parser */
  30int ematch_argc;
  31char **ematch_argv;
  32char *ematch_err;
  33struct ematch *ematch_root;
  34
  35static int begin_argc;
  36static char **begin_argv;
  37
  38static void bstr_print(FILE *fd, const struct bstr *b, int ascii);
  39
  40static inline void map_warning(int num, char *kind)
  41{
  42        fprintf(stderr,
  43            "Error: Unable to find ematch \"%s\" in %s or %s\n" \
  44            "Please assign a unique ID to the ematch kind the suggested " \
  45            "entry is:\n" \
  46            "\t%d\t%s\n",
  47            kind, EMATCH_MAP_ETC, EMATCH_MAP_USR, num, kind);
  48}
  49
  50static int lookup_map(__u16 num, char *dst, int len, const char *file)
  51{
  52        int err = -EINVAL;
  53        char buf[512];
  54        FILE *fd = fopen(file, "r");
  55
  56        if (fd == NULL)
  57                return -errno;
  58
  59        while (fgets(buf, sizeof(buf), fd)) {
  60                char namebuf[512], *p = buf;
  61                int id;
  62
  63                while (*p == ' ' || *p == '\t')
  64                        p++;
  65                if (*p == '#' || *p == '\n' || *p == 0)
  66                        continue;
  67
  68                if (sscanf(p, "%d %s", &id, namebuf) != 2) {
  69                        fprintf(stderr, "ematch map %s corrupted at %s\n",
  70                            file, p);
  71                        goto out;
  72                }
  73
  74                if (id == num) {
  75                        if (dst)
  76                                strncpy(dst, namebuf, len - 1);
  77                        err = 0;
  78                        goto out;
  79                }
  80        }
  81
  82        err = -ENOENT;
  83out:
  84        fclose(fd);
  85        return err;
  86}
  87
  88static int lookup_map_id(char *kind, int *dst, const char *file)
  89{
  90        int err = -EINVAL;
  91        char buf[512];
  92        FILE *fd = fopen(file, "r");
  93
  94        if (fd == NULL)
  95                return -errno;
  96
  97        while (fgets(buf, sizeof(buf), fd)) {
  98                char namebuf[512], *p = buf;
  99                int id;
 100
 101                while (*p == ' ' || *p == '\t')
 102                        p++;
 103                if (*p == '#' || *p == '\n' || *p == 0)
 104                        continue;
 105
 106                if (sscanf(p, "%d %s", &id, namebuf) != 2) {
 107                        fprintf(stderr, "ematch map %s corrupted at %s\n",
 108                            file, p);
 109                        goto out;
 110                }
 111
 112                if (!strcasecmp(namebuf, kind)) {
 113                        if (dst)
 114                                *dst = id;
 115                        err = 0;
 116                        goto out;
 117                }
 118        }
 119
 120        err = -ENOENT;
 121        *dst = 0;
 122out:
 123        fclose(fd);
 124        return err;
 125}
 126
 127static struct ematch_util *get_ematch_kind(char *kind)
 128{
 129        static void *body;
 130        void *dlh;
 131        char buf[256];
 132        struct ematch_util *e;
 133
 134        for (e = ematch_list; e; e = e->next) {
 135                if (strcmp(e->kind, kind) == 0)
 136                        return e;
 137        }
 138
 139        snprintf(buf, sizeof(buf), "em_%s.so", kind);
 140        dlh = dlopen(buf, RTLD_LAZY);
 141        if (dlh == NULL) {
 142                dlh = body;
 143                if (dlh == NULL) {
 144                        dlh = body = dlopen(NULL, RTLD_LAZY);
 145                        if (dlh == NULL)
 146                                return NULL;
 147                }
 148        }
 149
 150        snprintf(buf, sizeof(buf), "%s_ematch_util", kind);
 151        e = dlsym(dlh, buf);
 152        if (e == NULL)
 153                return NULL;
 154
 155        e->next = ematch_list;
 156        ematch_list = e;
 157
 158        return e;
 159}
 160
 161static struct ematch_util *get_ematch_kind_num(__u16 kind)
 162{
 163        char name[513];
 164        int ret;
 165
 166        ret = lookup_map(kind, name, sizeof(name), EMATCH_MAP_ETC);
 167        if (ret == -ENOENT)
 168                ret = lookup_map(kind, name, sizeof(name), EMATCH_MAP_USR);
 169        if (ret < 0)
 170                return NULL;
 171
 172        return get_ematch_kind(name);
 173}
 174
 175static int em_parse_call(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
 176                         struct ematch_util *e, struct ematch *t)
 177{
 178        if (e->parse_eopt_argv) {
 179                int argc = 0, i = 0, ret;
 180                struct bstr *args;
 181                char **argv;
 182
 183                for (args = t->args; args; args = bstr_next(args))
 184                        argc++;
 185                argv = calloc(argc, sizeof(char *));
 186                if (!argv)
 187                        return -1;
 188                for (args = t->args; args; args = bstr_next(args))
 189                        argv[i++] = args->data;
 190
 191                ret = e->parse_eopt_argv(n, hdr, argc, argv);
 192
 193                free(argv);
 194                return ret;
 195        }
 196
 197        return e->parse_eopt(n, hdr, t->args->next);
 198}
 199
 200static int parse_tree(struct nlmsghdr *n, struct ematch *tree)
 201{
 202        int index = 1;
 203        struct ematch *t;
 204
 205        for (t = tree; t; t = t->next) {
 206                struct rtattr *tail;
 207                struct tcf_ematch_hdr hdr = { .flags = t->relation };
 208
 209                if (t->inverted)
 210                        hdr.flags |= TCF_EM_INVERT;
 211
 212                tail = addattr_nest(n, MAX_MSG, index++);
 213
 214                if (t->child) {
 215                        __u32 r = t->child_ref;
 216
 217                        addraw_l(n, MAX_MSG, &hdr, sizeof(hdr));
 218                        addraw_l(n, MAX_MSG, &r, sizeof(r));
 219                } else {
 220                        int num = 0, err;
 221                        char buf[64];
 222                        struct ematch_util *e;
 223
 224                        if (t->args == NULL)
 225                                return -1;
 226
 227                        strncpy(buf, (char *) t->args->data, sizeof(buf)-1);
 228                        e = get_ematch_kind(buf);
 229                        if (e == NULL) {
 230                                fprintf(stderr, "Unknown ematch \"%s\"\n",
 231                                    buf);
 232                                return -1;
 233                        }
 234
 235                        err = lookup_map_id(buf, &num, EMATCH_MAP_ETC);
 236                        if (err == -ENOENT)
 237                                err = lookup_map_id(buf, &num, EMATCH_MAP_USR);
 238                        if (err < 0) {
 239                                if (err == -ENOENT)
 240                                        map_warning(e->kind_num, buf);
 241                                return err;
 242                        }
 243
 244                        hdr.kind = num;
 245                        if (em_parse_call(n, &hdr, e, t) < 0)
 246                                return -1;
 247                }
 248
 249                addattr_nest_end(n, tail);
 250        }
 251
 252        return 0;
 253}
 254
 255static int flatten_tree(struct ematch *head, struct ematch *tree)
 256{
 257        int i, count = 0;
 258        struct ematch *t;
 259
 260        for (;;) {
 261                count++;
 262
 263                if (tree->child) {
 264                        for (t = head; t->next; t = t->next);
 265                        t->next = tree->child;
 266                        count += flatten_tree(head, tree->child);
 267                }
 268
 269                if (tree->relation == 0)
 270                        break;
 271
 272                tree = tree->next;
 273        }
 274
 275        for (i = 0, t = head; t; t = t->next, i++)
 276                t->index = i;
 277
 278        for (t = head; t; t = t->next)
 279                if (t->child)
 280                        t->child_ref = t->child->index;
 281
 282        return count;
 283}
 284
 285__attribute__((format(printf, 5, 6)))
 286int em_parse_error(int err, struct bstr *args, struct bstr *carg,
 287                   struct ematch_util *e, char *fmt, ...)
 288{
 289        va_list a;
 290
 291        va_start(a, fmt);
 292        vfprintf(stderr, fmt, a);
 293        va_end(a);
 294
 295        if (ematch_err)
 296                fprintf(stderr, ": %s\n... ", ematch_err);
 297        else
 298                fprintf(stderr, "\n... ");
 299
 300        while (ematch_argc < begin_argc) {
 301                if (ematch_argc == (begin_argc - 1))
 302                        fprintf(stderr, ">>%s<< ", *begin_argv);
 303                else
 304                        fprintf(stderr, "%s ", *begin_argv);
 305                begin_argv++;
 306                begin_argc--;
 307        }
 308
 309        fprintf(stderr, "...\n");
 310
 311        if (args) {
 312                fprintf(stderr, "... %s(", e->kind);
 313                while (args) {
 314                        fprintf(stderr, "%s", args == carg ? ">>" : "");
 315                        bstr_print(stderr, args, 1);
 316                        fprintf(stderr, "%s%s", args == carg ? "<<" : "",
 317                            args->next ? " " : "");
 318                        args = args->next;
 319                }
 320                fprintf(stderr, ")...\n");
 321
 322        }
 323
 324        if (e == NULL) {
 325                fprintf(stderr,
 326                    "Usage: EXPR\n" \
 327                    "where: EXPR  := TERM [ { and | or } EXPR ]\n" \
 328                    "       TERM  := [ not ] { MATCH | '(' EXPR ')' }\n" \
 329                    "       MATCH := module '(' ARGS ')'\n" \
 330                    "       ARGS := ARG1 ARG2 ...\n" \
 331                    "\n" \
 332                    "Example: a(x y) and not (b(x) or c(x y z))\n");
 333        } else
 334                e->print_usage(stderr);
 335
 336        return -err;
 337}
 338
 339static inline void free_ematch_err(void)
 340{
 341        if (ematch_err) {
 342                free(ematch_err);
 343                ematch_err = NULL;
 344        }
 345}
 346
 347extern int ematch_parse(void);
 348
 349int parse_ematch(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
 350{
 351        begin_argc = ematch_argc = *argc_p;
 352        begin_argv = ematch_argv = *argv_p;
 353
 354        if (ematch_parse()) {
 355                int err = em_parse_error(EINVAL, NULL, NULL, NULL,
 356                    "Parse error");
 357                free_ematch_err();
 358                return err;
 359        }
 360
 361        free_ematch_err();
 362
 363        /* undo look ahead by parser */
 364        ematch_argc++;
 365        ematch_argv--;
 366
 367        if (ematch_root) {
 368                struct rtattr *tail, *tail_list;
 369
 370                struct tcf_ematch_tree_hdr hdr = {
 371                        .nmatches = flatten_tree(ematch_root, ematch_root),
 372                        .progid = TCF_EM_PROG_TC
 373                };
 374
 375                tail = addattr_nest(n, MAX_MSG, tca_id);
 376                addattr_l(n, MAX_MSG, TCA_EMATCH_TREE_HDR, &hdr, sizeof(hdr));
 377
 378                tail_list = addattr_nest(n, MAX_MSG, TCA_EMATCH_TREE_LIST);
 379
 380                if (parse_tree(n, ematch_root) < 0)
 381                        return -1;
 382
 383                addattr_nest_end(n, tail_list);
 384                addattr_nest_end(n, tail);
 385        }
 386
 387        *argc_p = ematch_argc;
 388        *argv_p = ematch_argv;
 389
 390        return 0;
 391}
 392
 393static int print_ematch_seq(FILE *fd, struct rtattr **tb, int start,
 394                            int prefix)
 395{
 396        int n, i = start;
 397        struct tcf_ematch_hdr *hdr;
 398        int dlen;
 399        void *data;
 400
 401        for (;;) {
 402                if (tb[i] == NULL)
 403                        return -1;
 404
 405                dlen = RTA_PAYLOAD(tb[i]) - sizeof(*hdr);
 406                data = (void *) RTA_DATA(tb[i]) + sizeof(*hdr);
 407
 408                if (dlen < 0)
 409                        return -1;
 410
 411                hdr = RTA_DATA(tb[i]);
 412
 413                if (hdr->flags & TCF_EM_INVERT)
 414                        fprintf(fd, "NOT ");
 415
 416                if (hdr->kind == 0) {
 417                        __u32 ref;
 418
 419                        if (dlen < sizeof(__u32))
 420                                return -1;
 421
 422                        ref = *(__u32 *) data;
 423                        fprintf(fd, "(\n");
 424                        for (n = 0; n <= prefix; n++)
 425                                fprintf(fd, "  ");
 426                        if (print_ematch_seq(fd, tb, ref + 1, prefix + 1) < 0)
 427                                return -1;
 428                        for (n = 0; n < prefix; n++)
 429                                fprintf(fd, "  ");
 430                        fprintf(fd, ") ");
 431
 432                } else {
 433                        struct ematch_util *e;
 434
 435                        e = get_ematch_kind_num(hdr->kind);
 436                        if (e == NULL)
 437                                fprintf(fd, "[unknown ematch %d]\n",
 438                                    hdr->kind);
 439                        else {
 440                                fprintf(fd, "%s(", e->kind);
 441                                if (e->print_eopt(fd, hdr, data, dlen) < 0)
 442                                        return -1;
 443                                fprintf(fd, ")\n");
 444                        }
 445                        if (hdr->flags & TCF_EM_REL_MASK)
 446                                for (n = 0; n < prefix; n++)
 447                                        fprintf(fd, "  ");
 448                }
 449
 450                switch (hdr->flags & TCF_EM_REL_MASK) {
 451                        case TCF_EM_REL_AND:
 452                                fprintf(fd, "AND ");
 453                                break;
 454
 455                        case TCF_EM_REL_OR:
 456                                fprintf(fd, "OR ");
 457                                break;
 458
 459                        default:
 460                                return 0;
 461                }
 462
 463                i++;
 464        }
 465
 466        return 0;
 467}
 468
 469static int print_ematch_list(FILE *fd, struct tcf_ematch_tree_hdr *hdr,
 470                             struct rtattr *rta)
 471{
 472        int err = -1;
 473        struct rtattr **tb;
 474
 475        tb = malloc((hdr->nmatches + 1) * sizeof(struct rtattr *));
 476        if (tb == NULL)
 477                return -1;
 478
 479        if (hdr->nmatches > 0) {
 480                if (parse_rtattr_nested(tb, hdr->nmatches, rta) < 0)
 481                        goto errout;
 482
 483                fprintf(fd, "\n  ");
 484                if (print_ematch_seq(fd, tb, 1, 1) < 0)
 485                        goto errout;
 486        }
 487
 488        err = 0;
 489errout:
 490        free(tb);
 491        return err;
 492}
 493
 494int print_ematch(FILE *fd, const struct rtattr *rta)
 495{
 496        struct rtattr *tb[TCA_EMATCH_TREE_MAX+1];
 497        struct tcf_ematch_tree_hdr *hdr;
 498
 499        if (parse_rtattr_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0)
 500                return -1;
 501
 502        if (tb[TCA_EMATCH_TREE_HDR] == NULL) {
 503                fprintf(stderr, "Missing ematch tree header\n");
 504                return -1;
 505        }
 506
 507        if (tb[TCA_EMATCH_TREE_LIST] == NULL) {
 508                fprintf(stderr, "Missing ematch tree list\n");
 509                return -1;
 510        }
 511
 512        if (RTA_PAYLOAD(tb[TCA_EMATCH_TREE_HDR]) < sizeof(*hdr)) {
 513                fprintf(stderr, "Ematch tree header size mismatch\n");
 514                return -1;
 515        }
 516
 517        hdr = RTA_DATA(tb[TCA_EMATCH_TREE_HDR]);
 518
 519        return print_ematch_list(fd, hdr, tb[TCA_EMATCH_TREE_LIST]);
 520}
 521
 522struct bstr *bstr_alloc(const char *text)
 523{
 524        struct bstr *b = calloc(1, sizeof(*b));
 525
 526        if (b == NULL)
 527                return NULL;
 528
 529        b->data = strdup(text);
 530        if (b->data == NULL) {
 531                free(b);
 532                return NULL;
 533        }
 534
 535        b->len = strlen(text);
 536
 537        return b;
 538}
 539
 540unsigned long bstrtoul(const struct bstr *b)
 541{
 542        char *inv = NULL;
 543        unsigned long l;
 544        char buf[b->len+1];
 545
 546        memcpy(buf, b->data, b->len);
 547        buf[b->len] = '\0';
 548
 549        l = strtoul(buf, &inv, 0);
 550        if (l == ULONG_MAX || inv == buf)
 551                return ULONG_MAX;
 552
 553        return l;
 554}
 555
 556static void bstr_print(FILE *fd, const struct bstr *b, int ascii)
 557{
 558        int i;
 559        char *s = b->data;
 560
 561        if (ascii)
 562                for (i = 0; i < b->len; i++)
 563                    fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
 564        else {
 565                for (i = 0; i < b->len; i++)
 566                    fprintf(fd, "%02x", s[i]);
 567                fprintf(fd, "\"");
 568                for (i = 0; i < b->len; i++)
 569                    fprintf(fd, "%c", isprint(s[i]) ? s[i] : '.');
 570                fprintf(fd, "\"");
 571        }
 572}
 573