iproute2/tc/em_meta.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * em_meta.c            Metadata Ematch
   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 <errno.h>
  17
  18#include "m_ematch.h"
  19#include <linux/tc_ematch/tc_em_meta.h>
  20
  21extern struct ematch_util meta_ematch_util;
  22
  23static void meta_print_usage(FILE *fd)
  24{
  25        fprintf(fd,
  26            "Usage: meta(OBJECT { eq | lt | gt } OBJECT)\n" \
  27            "where: OBJECT  := { META_ID | VALUE }\n" \
  28            "       META_ID := id [ shift SHIFT ] [ mask MASK ]\n" \
  29            "\n" \
  30            "Example: meta(nf_mark gt 24)\n" \
  31            "         meta(indev shift 1 eq \"ppp\")\n" \
  32            "         meta(tcindex mask 0xf0 eq 0xf0)\n" \
  33            "\n" \
  34            "For a list of meta identifiers, use meta(list).\n");
  35}
  36
  37static const struct meta_entry {
  38        int             id;
  39        char *kind;
  40        char *mask;
  41        char *desc;
  42} meta_table[] = {
  43#define TCF_META_ID_SECTION 0
  44#define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc }
  45        __A(SECTION,            "Generic", "", ""),
  46        __A(RANDOM,             "random",       "i",
  47                                "Random value (32 bit)"),
  48        __A(LOADAVG_0,          "loadavg_1",    "i",
  49                                "Load average in last minute"),
  50        __A(LOADAVG_1,          "loadavg_5",    "i",
  51                                "Load average in last 5 minutes"),
  52        __A(LOADAVG_2,          "loadavg_15",   "i",
  53                                "Load average in last 15 minutes"),
  54
  55        __A(SECTION,            "Interfaces", "", ""),
  56        __A(DEV,                "dev",          "iv",
  57                                "Device the packet is on"),
  58        __A(SECTION,            "Packet attributes", "", ""),
  59        __A(PRIORITY,           "priority",     "i",
  60                                "Priority of packet"),
  61        __A(PROTOCOL,           "protocol",     "i",
  62                                "Link layer protocol"),
  63        __A(PKTTYPE,            "pkt_type",     "i",
  64                                "Packet type (uni|multi|broad|...)cast"),
  65        __A(PKTLEN,             "pkt_len",      "i",
  66                                "Length of packet"),
  67        __A(DATALEN,            "data_len",     "i",
  68                                "Length of data in packet"),
  69        __A(MACLEN,             "mac_len",      "i",
  70                                "Length of link layer header"),
  71
  72        __A(SECTION,            "Netfilter", "", ""),
  73        __A(NFMARK,             "nf_mark",      "i",
  74                                "Netfilter mark"),
  75        __A(NFMARK,             "fwmark",       "i",
  76                                "Alias for nf_mark"),
  77
  78        __A(SECTION,            "Traffic Control", "", ""),
  79        __A(TCINDEX,            "tc_index",     "i",    "TC Index"),
  80        __A(SECTION,            "Routing", "", ""),
  81        __A(RTCLASSID,          "rt_classid",   "i",
  82                                "Routing ClassID (cls_route)"),
  83        __A(RTIIF,              "rt_iif",       "i",
  84                                "Incoming interface index"),
  85        __A(VLAN_TAG,           "vlan",         "i",    "Vlan tag"),
  86
  87        __A(SECTION,            "Sockets", "", ""),
  88        __A(SK_FAMILY,          "sk_family",    "i",    "Address family"),
  89        __A(SK_STATE,           "sk_state",     "i",    "State"),
  90        __A(SK_REUSE,           "sk_reuse",     "i",    "Reuse Flag"),
  91        __A(SK_BOUND_IF,        "sk_bind_if",   "iv",   "Bound interface"),
  92        __A(SK_REFCNT,          "sk_refcnt",    "i",    "Reference counter"),
  93        __A(SK_SHUTDOWN,        "sk_shutdown",  "i",    "Shutdown mask"),
  94        __A(SK_PROTO,           "sk_proto",     "i",    "Protocol"),
  95        __A(SK_TYPE,            "sk_type",      "i",    "Type"),
  96        __A(SK_RCVBUF,          "sk_rcvbuf",    "i",    "Receive buffer size"),
  97        __A(SK_RMEM_ALLOC,      "sk_rmem",      "i",    "RMEM"),
  98        __A(SK_WMEM_ALLOC,      "sk_wmem",      "i",    "WMEM"),
  99        __A(SK_OMEM_ALLOC,      "sk_omem",      "i",    "OMEM"),
 100        __A(SK_WMEM_QUEUED,     "sk_wmem_queue", "i",   "WMEM queue"),
 101        __A(SK_SND_QLEN,        "sk_snd_queue", "i",    "Send queue length"),
 102        __A(SK_RCV_QLEN,        "sk_rcv_queue", "i",    "Receive queue length"),
 103        __A(SK_ERR_QLEN,        "sk_err_queue", "i",    "Error queue length"),
 104        __A(SK_FORWARD_ALLOCS,  "sk_fwd_alloc", "i",    "Forward allocations"),
 105        __A(SK_SNDBUF,          "sk_sndbuf",    "i",    "Send buffer size"),
 106#undef __A
 107};
 108
 109static inline int map_type(char k)
 110{
 111        switch (k) {
 112                case 'i': return TCF_META_TYPE_INT;
 113                case 'v': return TCF_META_TYPE_VAR;
 114        }
 115
 116        fprintf(stderr, "BUG: Unknown map character '%c'\n", k);
 117        return INT_MAX;
 118}
 119
 120static const struct meta_entry *lookup_meta_entry(struct bstr *kind)
 121{
 122        int i;
 123
 124        for (i = 0; i < ARRAY_SIZE(meta_table); i++)
 125                if (!bstrcmp(kind, meta_table[i].kind) &&
 126                    meta_table[i].id != 0)
 127                        return &meta_table[i];
 128
 129        return NULL;
 130}
 131
 132static const struct meta_entry *lookup_meta_entry_byid(int id)
 133{
 134        int i;
 135
 136        for (i = 0; i < ARRAY_SIZE(meta_table); i++)
 137                if (meta_table[i].id == id)
 138                        return &meta_table[i];
 139
 140        return NULL;
 141}
 142
 143static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val,
 144                              struct tcf_meta_val *hdr)
 145{
 146        __u32 t;
 147
 148        switch (TCF_META_TYPE(hdr->kind)) {
 149                case TCF_META_TYPE_INT:
 150                        t = val;
 151                        addattr_l(n, MAX_MSG, tlv, &t, sizeof(t));
 152                        break;
 153
 154                case TCF_META_TYPE_VAR:
 155                        if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) {
 156                                struct bstr *a = (struct bstr *) val;
 157
 158                                addattr_l(n, MAX_MSG, tlv, a->data, a->len);
 159                        }
 160                        break;
 161        }
 162}
 163
 164static inline int is_compatible(struct tcf_meta_val *what,
 165                                struct tcf_meta_val *needed)
 166{
 167        const struct meta_entry *entry;
 168        char *p;
 169
 170        entry = lookup_meta_entry_byid(TCF_META_ID(what->kind));
 171
 172        if (entry == NULL)
 173                return 0;
 174
 175        for (p = entry->mask; p; p++)
 176                if (map_type(*p) == TCF_META_TYPE(needed->kind))
 177                        return 1;
 178
 179        return 0;
 180}
 181
 182static void list_meta_ids(FILE *fd)
 183{
 184        int i;
 185
 186        fprintf(fd,
 187            "--------------------------------------------------------\n" \
 188            "  ID               Type       Description\n" \
 189            "--------------------------------------------------------");
 190
 191        for (i = 0; i < ARRAY_SIZE(meta_table); i++) {
 192                if (meta_table[i].id == TCF_META_ID_SECTION) {
 193                        fprintf(fd, "\n%s:\n", meta_table[i].kind);
 194                } else {
 195                        char *p = meta_table[i].mask;
 196                        char buf[64] = {0};
 197
 198                        fprintf(fd, "  %-16s ", meta_table[i].kind);
 199
 200                        while (*p) {
 201                                int type = map_type(*p);
 202
 203                                switch (type) {
 204                                        case TCF_META_TYPE_INT:
 205                                                strcat(buf, "INT");
 206                                                break;
 207
 208                                        case TCF_META_TYPE_VAR:
 209                                                strcat(buf, "VAR");
 210                                                break;
 211                                }
 212
 213                                if (*(++p))
 214                                        strcat(buf, ",");
 215                        }
 216
 217                        fprintf(fd, "%-10s %s\n", buf, meta_table[i].desc);
 218                }
 219        }
 220
 221        fprintf(fd,
 222            "--------------------------------------------------------\n");
 223}
 224
 225#undef TCF_META_ID_SECTION
 226
 227#define PARSE_FAILURE ((void *) (-1))
 228
 229#define PARSE_ERR(CARG, FMT, ARGS...) \
 230        em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT, ##ARGS)
 231
 232static inline int can_adopt(struct tcf_meta_val *val)
 233{
 234        return !!TCF_META_ID(val->kind);
 235}
 236
 237static inline int overwrite_type(struct tcf_meta_val *src,
 238                                 struct tcf_meta_val *dst)
 239{
 240        return (TCF_META_TYPE(dst->kind) << 12) | TCF_META_ID(src->kind);
 241}
 242
 243
 244static inline struct bstr *
 245parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj,
 246             unsigned long *dst, struct tcf_meta_val *left)
 247{
 248        const struct meta_entry *entry;
 249        unsigned long num;
 250        struct bstr *a;
 251
 252        if (arg->quoted) {
 253                obj->kind = TCF_META_TYPE_VAR << 12;
 254                obj->kind |= TCF_META_ID_VALUE;
 255                *dst = (unsigned long) arg;
 256                return bstr_next(arg);
 257        }
 258
 259        num = bstrtoul(arg);
 260        if (num != ULONG_MAX) {
 261                obj->kind = TCF_META_TYPE_INT << 12;
 262                obj->kind |= TCF_META_ID_VALUE;
 263                *dst = (unsigned long) num;
 264                return bstr_next(arg);
 265        }
 266
 267        entry = lookup_meta_entry(arg);
 268
 269        if (entry == NULL) {
 270                PARSE_ERR(arg, "meta: unknown meta id\n");
 271                return PARSE_FAILURE;
 272        }
 273
 274        obj->kind = entry->id | (map_type(entry->mask[0]) << 12);
 275
 276        if (left) {
 277                struct tcf_meta_val *right = obj;
 278
 279                if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind))
 280                        goto compatible;
 281
 282                if (can_adopt(left) && !can_adopt(right)) {
 283                        if (is_compatible(left, right))
 284                                left->kind = overwrite_type(left, right);
 285                        else
 286                                goto not_compatible;
 287                } else if (can_adopt(right) && !can_adopt(left)) {
 288                        if (is_compatible(right, left))
 289                                right->kind = overwrite_type(right, left);
 290                        else
 291                                goto not_compatible;
 292                } else if (can_adopt(left) && can_adopt(right)) {
 293                        if (is_compatible(left, right))
 294                                left->kind = overwrite_type(left, right);
 295                        else if (is_compatible(right, left))
 296                                right->kind = overwrite_type(right, left);
 297                        else
 298                                goto not_compatible;
 299                } else
 300                        goto not_compatible;
 301        }
 302
 303compatible:
 304
 305        a = bstr_next(arg);
 306
 307        while (a) {
 308                if (!bstrcmp(a, "shift")) {
 309                        unsigned long shift;
 310
 311                        if (a->next == NULL) {
 312                                PARSE_ERR(a, "meta: missing argument");
 313                                return PARSE_FAILURE;
 314                        }
 315                        a = bstr_next(a);
 316
 317                        shift = bstrtoul(a);
 318                        if (shift == ULONG_MAX) {
 319                                PARSE_ERR(a, "meta: invalid shift, must " \
 320                                    "be numeric");
 321                                return PARSE_FAILURE;
 322                        }
 323
 324                        obj->shift = (__u8) shift;
 325                        a = bstr_next(a);
 326                } else if (!bstrcmp(a, "mask")) {
 327                        unsigned long mask;
 328
 329                        if (a->next == NULL) {
 330                                PARSE_ERR(a, "meta: missing argument");
 331                                return PARSE_FAILURE;
 332                        }
 333                        a = bstr_next(a);
 334
 335                        mask = bstrtoul(a);
 336                        if (mask == ULONG_MAX) {
 337                                PARSE_ERR(a, "meta: invalid mask, must be " \
 338                                    "numeric");
 339                                return PARSE_FAILURE;
 340                        }
 341                        *dst = (unsigned long) mask;
 342                        a = bstr_next(a);
 343                } else
 344                        break;
 345        }
 346
 347        return a;
 348
 349not_compatible:
 350        PARSE_ERR(arg, "lvalue and rvalue are not compatible.");
 351        return PARSE_FAILURE;
 352}
 353
 354static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
 355                           struct bstr *args)
 356{
 357        int opnd;
 358        struct bstr *a;
 359        struct tcf_meta_hdr meta_hdr = {};
 360        unsigned long lvalue = 0, rvalue = 0;
 361
 362        if (args == NULL)
 363                return PARSE_ERR(args, "meta: missing arguments");
 364
 365        if (!bstrcmp(args, "list")) {
 366                list_meta_ids(stderr);
 367                return -1;
 368        }
 369
 370        a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL);
 371        if (a == PARSE_FAILURE)
 372                return -1;
 373        else if (a == NULL)
 374                return PARSE_ERR(args, "meta: missing operand");
 375
 376        if (!bstrcmp(a, "eq"))
 377                opnd = TCF_EM_OPND_EQ;
 378        else if (!bstrcmp(a, "gt"))
 379                opnd = TCF_EM_OPND_GT;
 380        else if (!bstrcmp(a, "lt"))
 381                opnd = TCF_EM_OPND_LT;
 382        else
 383                return PARSE_ERR(a, "meta: invalid operand");
 384
 385        meta_hdr.left.op = (__u8) opnd;
 386
 387        if (a->next == NULL)
 388                return PARSE_ERR(args, "meta: missing rvalue");
 389        a = bstr_next(a);
 390
 391        a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left);
 392        if (a == PARSE_FAILURE)
 393                return -1;
 394        else if (a != NULL)
 395                return PARSE_ERR(a, "meta: unexpected trailer");
 396
 397
 398        addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
 399
 400        addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr));
 401
 402        dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left);
 403        dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right);
 404
 405        return 0;
 406}
 407#undef PARSE_ERR
 408
 409static inline void print_binary(FILE *fd, unsigned char *str, int len)
 410{
 411        int i;
 412
 413        for (i = 0; i < len; i++)
 414                if (!isprint(str[i]))
 415                        goto binary;
 416
 417        for (i = 0; i < len; i++)
 418                fprintf(fd, "%c", str[i]);
 419        return;
 420
 421binary:
 422        for (i = 0; i < len; i++)
 423                fprintf(fd, "%02x ", str[i]);
 424
 425        fprintf(fd, "\"");
 426        for (i = 0; i < len; i++)
 427                fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.');
 428        fprintf(fd, "\"");
 429}
 430
 431static inline int print_value(FILE *fd, int type, struct rtattr *rta)
 432{
 433        if (rta == NULL) {
 434                fprintf(stderr, "Missing value TLV\n");
 435                return -1;
 436        }
 437
 438        switch (type) {
 439                case TCF_META_TYPE_INT:
 440                        if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
 441                                fprintf(stderr, "meta int type value TLV " \
 442                                    "size mismatch.\n");
 443                                return -1;
 444                        }
 445                        fprintf(fd, "%d", rta_getattr_u32(rta));
 446                        break;
 447
 448                case TCF_META_TYPE_VAR:
 449                        print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta));
 450                        break;
 451        }
 452
 453        return 0;
 454}
 455
 456static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta)
 457{
 458        int id = TCF_META_ID(obj->kind);
 459        int type = TCF_META_TYPE(obj->kind);
 460        const struct meta_entry *entry;
 461
 462        if (id == TCF_META_ID_VALUE)
 463                return print_value(fd, type, rta);
 464
 465        entry = lookup_meta_entry_byid(id);
 466
 467        if (entry == NULL)
 468                fprintf(fd, "[unknown meta id %d]", id);
 469        else
 470                fprintf(fd, "%s", entry->kind);
 471
 472        if (obj->shift)
 473                fprintf(fd, " shift %d", obj->shift);
 474
 475        switch (type) {
 476                case TCF_META_TYPE_INT:
 477                        if (rta) {
 478                                if (RTA_PAYLOAD(rta) < sizeof(__u32))
 479                                        goto size_mismatch;
 480
 481                                if (rta_getattr_u32(rta))
 482                                        fprintf(fd, " mask 0x%08x",
 483                                                rta_getattr_u32(rta));
 484                        }
 485                        break;
 486        }
 487
 488        return 0;
 489
 490size_mismatch:
 491        fprintf(stderr, "meta int type mask TLV size mismatch\n");
 492        return -1;
 493}
 494
 495
 496static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 497                           int data_len)
 498{
 499        struct rtattr *tb[TCA_EM_META_MAX+1];
 500        struct tcf_meta_hdr *meta_hdr;
 501
 502        if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0)
 503                return -1;
 504
 505        if (tb[TCA_EM_META_HDR] == NULL) {
 506                fprintf(stderr, "Missing meta header\n");
 507                return -1;
 508        }
 509
 510        if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) {
 511                fprintf(stderr, "Meta header size mismatch\n");
 512                return -1;
 513        }
 514
 515        meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]);
 516
 517        if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0)
 518                return -1;
 519
 520        switch (meta_hdr->left.op) {
 521                case TCF_EM_OPND_EQ:
 522                        fprintf(fd, " eq ");
 523                        break;
 524                case TCF_EM_OPND_LT:
 525                        fprintf(fd, " lt ");
 526                        break;
 527                case TCF_EM_OPND_GT:
 528                        fprintf(fd, " gt ");
 529                        break;
 530        }
 531
 532        return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]);
 533}
 534
 535struct ematch_util meta_ematch_util = {
 536        .kind = "meta",
 537        .kind_num = TCF_EM_META,
 538        .parse_eopt = meta_parse_eopt,
 539        .print_eopt = meta_print_eopt,
 540        .print_usage = meta_print_usage
 541};
 542