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