+++ /dev/null
-/*
- * em_meta.c Metadata Ematch
- *
- * This program is free software; you can distribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Thomas Graf <tgraf@suug.ch>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <syslog.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <string.h>
-#include <dlfcn.h>
-#include <errno.h>
-
-#include "m_ematch.h"
-#include <linux/tc_ematch/tc_em_meta.h>
-
-extern struct ematch_util meta_ematch_util;
-
-static void meta_print_usage(FILE *fd)
-{
- fprintf(fd,
- "Usage: meta(OBJECT { eq | lt | gt } OBJECT)\n" \
- "where: OBJECT := { META_ID | VALUE }\n" \
- " META_ID := id [ shift SHIFT ] [ mask MASK ]\n" \
- "\n" \
- "Example: meta(nfmark gt 24)\n" \
- " meta(indev shift 1 eq \"ppp\"\n" \
- " meta(tcindex mask 0xf0 eq 0xf0)\n" \
- " meta(dev eq indev)\n" \
- "\n" \
- "For a list of meta identifiers, use meta(list).\n");
-}
-
-struct meta_entry {
- int id;
- char * kind;
- char * mask;
- char * desc;
-} meta_table[] = {
-#define TCF_META_ID_SECTION 0
-#define __A(id, name, mask, desc) { TCF_META_ID_##id, name, mask, desc }
- __A(SECTION, "Generic", "", ""),
- __A(RANDOM, "random", "i",
- "Random value (32 bit)"),
- __A(LOADAVG_0, "loadavg_1", "i",
- "Load average in last minute"),
- __A(LOADAVG_1, "loadavg_5", "i",
- "Load average in last 5 minutes"),
- __A(LOADAVG_2, "loadavg_15", "i",
- "Load average in last 15 minutes"),
-
- __A(SECTION, "Interfaces", "", ""),
- __A(DEV, "dev", "iv",
- "Device the packet is on"),
- __A(SECTION, "Packet attributes", "", ""),
- __A(PRIORITY, "priority", "i",
- "Priority of packet"),
- __A(PROTOCOL, "protocol", "i",
- "Link layer protocol"),
- __A(PKTTYPE, "pkt_type", "i",
- "Packet type (uni|multi|broad|...)cast"),
- __A(PKTLEN, "pkt_len", "i",
- "Length of packet"),
- __A(DATALEN, "data_len", "i",
- "Length of data in packet"),
- __A(MACLEN, "mac_len", "i",
- "Length of link layer header"),
-
- __A(SECTION, "Netfilter", "", ""),
- __A(NFMARK, "nf_mark", "i",
- "Netfilter mark"),
- __A(NFMARK, "fwmark", "i",
- "Alias for nf_mark"),
-
- __A(SECTION, "Traffic Control", "", ""),
- __A(TCINDEX, "tc_index", "i", "TC Index"),
- __A(SECTION, "Routing", "", ""),
- __A(RTCLASSID, "rt_classid", "i",
- "Routing ClassID (cls_route)"),
- __A(RTIIF, "rt_iif", "i",
- "Incoming interface index"),
-
- __A(SECTION, "Sockets", "", ""),
- __A(SK_FAMILY, "sk_family", "i", "Address family"),
- __A(SK_STATE, "sk_state", "i", "State"),
- __A(SK_REUSE, "sk_reuse", "i", "Reuse Flag"),
- __A(SK_BOUND_IF, "sk_bind_if", "iv", "Bound interface"),
- __A(SK_REFCNT, "sk_refcnt", "i", "Reference counter"),
- __A(SK_SHUTDOWN, "sk_shutdown", "i", "Shutdown mask"),
- __A(SK_PROTO, "sk_proto", "i", "Protocol"),
- __A(SK_TYPE, "sk_type", "i", "Type"),
- __A(SK_RCVBUF, "sk_rcvbuf", "i", "Receive buffer size"),
- __A(SK_RMEM_ALLOC, "sk_rmem", "i", "RMEM"),
- __A(SK_WMEM_ALLOC, "sk_wmem", "i", "WMEM"),
- __A(SK_OMEM_ALLOC, "sk_omem", "i", "OMEM"),
- __A(SK_WMEM_QUEUED, "sk_wmem_queue","i", "WMEM queue"),
- __A(SK_SND_QLEN, "sk_snd_queue", "i", "Send queue length"),
- __A(SK_RCV_QLEN, "sk_rcv_queue", "i", "Receive queue length"),
- __A(SK_ERR_QLEN, "sk_err_queue", "i", "Error queue length"),
- __A(SK_FORWARD_ALLOCS, "sk_fwd_alloc", "i", "Forward allocations"),
- __A(SK_SNDBUF, "sk_sndbuf", "i", "Send buffer size"),
-#undef __A
-};
-
-static inline int map_type(char k)
-{
- switch (k) {
- case 'i': return TCF_META_TYPE_INT;
- case 'v': return TCF_META_TYPE_VAR;
- }
-
- fprintf(stderr, "BUG: Unknown map character '%c'\n", k);
- return INT_MAX;
-}
-
-static struct meta_entry * lookup_meta_entry(struct bstr *kind)
-{
- int i;
-
- for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++)
- if (!bstrcmp(kind, meta_table[i].kind) &&
- meta_table[i].id != 0)
- return &meta_table[i];
-
- return NULL;
-}
-
-static struct meta_entry * lookup_meta_entry_byid(int id)
-{
- int i;
-
- for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++)
- if (meta_table[i].id == id)
- return &meta_table[i];
-
- return NULL;
-}
-
-static inline void dump_value(struct nlmsghdr *n, int tlv, unsigned long val,
- struct tcf_meta_val *hdr)
-{
- __u32 t;
-
- switch (TCF_META_TYPE(hdr->kind)) {
- case TCF_META_TYPE_INT:
- t = val;
- addattr_l(n, MAX_MSG, tlv, &t, sizeof(t));
- break;
-
- case TCF_META_TYPE_VAR:
- if (TCF_META_ID(hdr->kind) == TCF_META_ID_VALUE) {
- struct bstr *a = (struct bstr *) val;
- addattr_l(n, MAX_MSG, tlv, a->data, a->len);
- }
- break;
- }
-}
-
-static inline int is_compatible(struct tcf_meta_val *what,
- struct tcf_meta_val *needed)
-{
- char *p;
- struct meta_entry *entry;
-
- entry = lookup_meta_entry_byid(TCF_META_ID(what->kind));
-
- if (entry == NULL)
- return 0;
-
- for (p = entry->mask; p; p++)
- if (map_type(*p) == TCF_META_TYPE(needed->kind))
- return 1;
-
- return 0;
-}
-
-static void list_meta_ids(FILE *fd)
-{
- int i;
-
- fprintf(fd,
- "--------------------------------------------------------\n" \
- " ID Type Description\n" \
- "--------------------------------------------------------");
-
- for (i = 0; i < (sizeof(meta_table)/sizeof(meta_table[0])); i++) {
- if (meta_table[i].id == TCF_META_ID_SECTION) {
- fprintf(fd, "\n%s:\n", meta_table[i].kind);
- } else {
- char *p = meta_table[i].mask;
- char buf[64] = {0};
-
- fprintf(fd, " %-16s ", meta_table[i].kind);
-
- while (*p) {
- int type = map_type(*p);
-
- switch (type) {
- case TCF_META_TYPE_INT:
- strcat(buf, "INT");
- break;
-
- case TCF_META_TYPE_VAR:
- strcat(buf, "VAR");
- break;
- }
-
- if (*(++p))
- strcat(buf, ",");
- }
-
- fprintf(fd, "%-10s %s\n", buf, meta_table[i].desc);
- }
- }
-
- fprintf(fd,
- "--------------------------------------------------------\n");
-}
-
-#undef TCF_META_ID_SECTION
-
-#define PARSE_FAILURE ((void *) (-1))
-
-#define PARSE_ERR(CARG, FMT, ARGS...) \
- em_parse_error(EINVAL, args, CARG, &meta_ematch_util, FMT ,##ARGS)
-
-static inline int can_adopt(struct tcf_meta_val *val)
-{
- return !!TCF_META_ID(val->kind);
-}
-
-static inline int overwrite_type(struct tcf_meta_val *src,
- struct tcf_meta_val *dst)
-{
- return (TCF_META_TYPE(dst->kind) << 12) | TCF_META_ID(src->kind);
-}
-
-
-static inline struct bstr *
-parse_object(struct bstr *args, struct bstr *arg, struct tcf_meta_val *obj,
- unsigned long *dst, struct tcf_meta_val *left)
-{
- struct meta_entry *entry;
- unsigned long num;
- struct bstr *a;
-
- if (arg->quoted) {
- obj->kind = TCF_META_TYPE_VAR << 12;
- obj->kind |= TCF_META_ID_VALUE;
- *dst = (unsigned long) arg;
- return bstr_next(arg);
- }
-
- num = bstrtoul(arg);
- if (num != LONG_MAX) {
- obj->kind = TCF_META_TYPE_INT << 12;
- obj->kind |= TCF_META_ID_VALUE;
- *dst = (unsigned long) num;
- return bstr_next(arg);
- }
-
- entry = lookup_meta_entry(arg);
-
- if (entry == NULL) {
- PARSE_ERR(arg, "meta: unknown meta id\n");
- return PARSE_FAILURE;
- }
-
- obj->kind = entry->id | (map_type(entry->mask[0]) << 12);
-
- if (left) {
- struct tcf_meta_val *right = obj;
-
- if (TCF_META_TYPE(right->kind) == TCF_META_TYPE(left->kind))
- goto compatible;
-
- if (can_adopt(left) && !can_adopt(right)) {
- if (is_compatible(left, right))
- left->kind = overwrite_type(left, right);
- else
- goto not_compatible;
- } else if (can_adopt(right) && !can_adopt(left)) {
- if (is_compatible(right, left))
- right->kind = overwrite_type(right, left);
- else
- goto not_compatible;
- } else if (can_adopt(left) && can_adopt(right)) {
- if (is_compatible(left, right))
- left->kind = overwrite_type(left, right);
- else if (is_compatible(right, left))
- right->kind = overwrite_type(right, left);
- else
- goto not_compatible;
- } else
- goto not_compatible;
- }
-
-compatible:
-
- a = bstr_next(arg);
-
- while(a) {
- if (!bstrcmp(a, "shift")) {
- unsigned long shift;
-
- if (a->next == NULL) {
- PARSE_ERR(a, "meta: missing argument");
- return PARSE_FAILURE;
- }
- a = bstr_next(a);
-
- shift = bstrtoul(a);
- if (shift == LONG_MAX) {
- PARSE_ERR(a, "meta: invalid shift, must " \
- "be numeric");
- return PARSE_FAILURE;
- }
-
- obj->shift = (__u8) shift;
- a = bstr_next(a);
- } else if (!bstrcmp(a, "mask")) {
- unsigned long mask;
-
- if (a->next == NULL) {
- PARSE_ERR(a, "meta: missing argument");
- return PARSE_FAILURE;
- }
- a = bstr_next(a);
-
- mask = bstrtoul(a);
- if (mask == LONG_MAX) {
- PARSE_ERR(a, "meta: invalid mask, must be " \
- "numeric");
- return PARSE_FAILURE;
- }
- *dst = (unsigned long) mask;
- a = bstr_next(a);
- } else
- break;
- }
-
- return a;
-
-not_compatible:
- PARSE_ERR(arg, "lvalue and rvalue are not compatible.");
- return PARSE_FAILURE;
-}
-
-static int meta_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
- struct bstr *args)
-{
- int opnd;
- struct bstr *a;
- struct tcf_meta_hdr meta_hdr;
- unsigned long lvalue = 0, rvalue = 0;
-
- memset(&meta_hdr, 0, sizeof(meta_hdr));
-
- if (args == NULL)
- return PARSE_ERR(args, "meta: missing arguments");
-
- if (!bstrcmp(args, "list")) {
- list_meta_ids(stderr);
- return -1;
- }
-
- a = parse_object(args, args, &meta_hdr.left, &lvalue, NULL);
- if (a == PARSE_FAILURE)
- return -1;
- else if (a == NULL)
- return PARSE_ERR(args, "meta: missing operand");
-
- if (!bstrcmp(a, "eq"))
- opnd = TCF_EM_OPND_EQ;
- else if (!bstrcmp(a, "gt"))
- opnd = TCF_EM_OPND_GT;
- else if (!bstrcmp(a, "lt"))
- opnd = TCF_EM_OPND_LT;
- else
- return PARSE_ERR(a, "meta: invalid operand");
-
- meta_hdr.left.op = (__u8) opnd;
-
- if (a->next == NULL)
- return PARSE_ERR(args, "meta: missing rvalue");
- a = bstr_next(a);
-
- a = parse_object(args, a, &meta_hdr.right, &rvalue, &meta_hdr.left);
- if (a == PARSE_FAILURE)
- return -1;
- else if (a != NULL)
- return PARSE_ERR(a, "meta: unexpected trailer");
-
-
- addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
-
- addattr_l(n, MAX_MSG, TCA_EM_META_HDR, &meta_hdr, sizeof(meta_hdr));
-
- if (lvalue)
- dump_value(n, TCA_EM_META_LVALUE, lvalue, &meta_hdr.left);
-
- if (rvalue)
- dump_value(n, TCA_EM_META_RVALUE, rvalue, &meta_hdr.right);
-
- return 0;
-}
-#undef PARSE_ERR
-
-static inline void print_binary(FILE *fd, unsigned char *str, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- if (!isprint(str[i]))
- goto binary;
-
- for (i = 0; i < len; i++)
- fprintf(fd, "%c", str[i]);
- return;
-
-binary:
- for (i = 0; i < len; i++)
- fprintf(fd, "%02x ", str[i]);
-
- fprintf(fd, "\"");
- for (i = 0; i < len; i++)
- fprintf(fd, "%c", isprint(str[i]) ? str[i] : '.');
- fprintf(fd, "\"");
-}
-
-static inline int print_value(FILE *fd, int type, struct rtattr *rta)
-{
- if (rta == NULL) {
- fprintf(stderr, "Missing value TLV\n");
- return -1;
- }
-
- switch(type) {
- case TCF_META_TYPE_INT:
- if (RTA_PAYLOAD(rta) < sizeof(__u32)) {
- fprintf(stderr, "meta int type value TLV " \
- "size mismatch.\n");
- return -1;
- }
- fprintf(fd, "%d", *(__u32 *) RTA_DATA(rta));
- break;
-
- case TCF_META_TYPE_VAR:
- print_binary(fd, RTA_DATA(rta), RTA_PAYLOAD(rta));
- break;
- }
-
- return 0;
-}
-
-static int print_object(FILE *fd, struct tcf_meta_val *obj, struct rtattr *rta)
-{
- int id = TCF_META_ID(obj->kind);
- int type = TCF_META_TYPE(obj->kind);
- struct meta_entry *entry;
-
- if (id == TCF_META_ID_VALUE)
- return print_value(fd, type, rta);
-
- entry = lookup_meta_entry_byid(id);
-
- if (entry == NULL)
- fprintf(fd, "[unknown meta id %d]", id);
- else
- fprintf(fd, "%s", entry->kind);
-
- if (obj->shift)
- fprintf(fd, " shift %d", obj->shift);
-
- switch (type) {
- case TCF_META_TYPE_INT:
- if (rta) {
- if (RTA_PAYLOAD(rta) < sizeof(__u32))
- goto size_mismatch;
-
- fprintf(fd, " mask 0x%08x",
- *(__u32*) RTA_DATA(rta));
- }
- break;
- }
-
- return 0;
-
-size_mismatch:
- fprintf(stderr, "meta int type mask TLV size mismatch\n");
- return -1;
-}
-
-
-static int meta_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
- int data_len)
-{
- struct rtattr *tb[TCA_EM_META_MAX+1];
- struct tcf_meta_hdr *meta_hdr;
-
- if (parse_rtattr(tb, TCA_EM_META_MAX, data, data_len) < 0)
- return -1;
-
- if (tb[TCA_EM_META_HDR] == NULL) {
- fprintf(stderr, "Missing meta header\n");
- return -1;
- }
-
- if (RTA_PAYLOAD(tb[TCA_EM_META_HDR]) < sizeof(*meta_hdr)) {
- fprintf(stderr, "Meta header size mismatch\n");
- return -1;
- }
-
- meta_hdr = RTA_DATA(tb[TCA_EM_META_HDR]);
-
- if (print_object(fd, &meta_hdr->left, tb[TCA_EM_META_LVALUE]) < 0)
- return -1;
-
- switch (meta_hdr->left.op) {
- case TCF_EM_OPND_EQ:
- fprintf(fd, " eq ");
- break;
- case TCF_EM_OPND_LT:
- fprintf(fd, " lt ");
- break;
- case TCF_EM_OPND_GT:
- fprintf(fd, " gt ");
- break;
- }
-
- return print_object(fd, &meta_hdr->right, tb[TCA_EM_META_RVALUE]);
-}
-
-struct ematch_util meta_ematch_util = {
- .kind = "meta",
- .kind_num = TCF_EM_META,
- .parse_eopt = meta_parse_eopt,
- .print_eopt = meta_print_eopt,
- .print_usage = meta_print_usage
-};