--- /dev/null
+/*
+ * em_cmp.c Simle coparison 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_cmp.h>
+
+extern struct ematch_util cmp_ematch_util;
+
+static void cmp_print_usage(FILE *fd)
+{
+ fprintf(fd,
+ "Usage: cmp(ALIGN at OFFSET [ ATTRS ] { eq | lt | gt } VALUE)\n" \
+ "where: ALIGN := { u8 | u16 | u32 }\n" \
+ " ATTRS := [ layer LAYER ] [ mask MASK ] [ trans ]\n" \
+ " LAYER := { link | header | next-header | 0..%d }\n" \
+ "\n" \
+ "Example: cmp(u16 at 3 layer 2 mask 0xff00 gt 20)\n",
+ TCF_LAYER_MAX);
+}
+
+static int cmp_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+ struct bstr *args)
+{
+ struct bstr *a;
+ int align, opnd = 0;
+ unsigned long offset = 0, layer = TCF_LAYER_NETWORK, mask = 0, value = 0;
+ int offset_present = 0, value_present = 0;
+ struct tcf_em_cmp cmp;
+
+ memset(&cmp, 0, sizeof(cmp));
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+ em_parse_error(EINVAL, args, CARG, &cmp_ematch_util, FMT ,##ARGS)
+
+ if (args == NULL)
+ return PARSE_ERR(args, "cmp: missing arguments");
+
+ if (!bstrcmp(args, "u8"))
+ align = TCF_EM_ALIGN_U8;
+ else if (!bstrcmp(args, "u16"))
+ align = TCF_EM_ALIGN_U16;
+ else if (!bstrcmp(args, "u32"))
+ align = TCF_EM_ALIGN_U32;
+ else
+ return PARSE_ERR(args, "cmp: invalid alignment");
+
+ for (a = bstr_next(args); a; a = bstr_next(a)) {
+ if (!bstrcmp(a, "at")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ offset = bstrtoul(a);
+ if (offset == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid offset, " \
+ "must be numeric");
+
+ offset_present = 1;
+ } else if (!bstrcmp(a, "layer")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ layer = parse_layer(a);
+ if (layer == INT_MAX) {
+ layer = bstrtoul(a);
+ if (layer == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid " \
+ "layer");
+ }
+
+ if (layer > TCF_LAYER_MAX)
+ return PARSE_ERR(a, "cmp: illegal layer, " \
+ "must be in 0..%d", TCF_LAYER_MAX);
+ } else if (!bstrcmp(a, "mask")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ mask = bstrtoul(a);
+ if (mask == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid mask");
+ } else if (!bstrcmp(a, "trans")) {
+ cmp.flags |= TCF_EM_CMP_TRANS;
+ } else if (!bstrcmp(a, "eq") || !bstrcmp(a, "gt") ||
+ !bstrcmp(a, "lt")) {
+
+ 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;
+
+ if (a->next == NULL)
+ return PARSE_ERR(a, "cmp: missing argument");
+ a = bstr_next(a);
+
+ value = bstrtoul(a);
+ if (value == ULONG_MAX)
+ return PARSE_ERR(a, "cmp: invalid value");
+
+ value_present = 1;
+ } else
+ return PARSE_ERR(a, "nbyte: unknown parameter");
+ }
+
+ if (offset_present == 0 || value_present == 0)
+ return PARSE_ERR(a, "cmp: offset and value required");
+
+ cmp.val = (__u32) value;
+ cmp.mask = (__u32) mask;
+ cmp.off = (__u16) offset;
+ cmp.align = (__u8) align;
+ cmp.layer = (__u8) layer;
+ cmp.opnd = (__u8) opnd;
+
+ addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+ addraw_l(n, MAX_MSG, &cmp, sizeof(cmp));
+
+#undef PARSE_ERR
+ return 0;
+}
+
+static int cmp_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+ int data_len)
+{
+ struct tcf_em_cmp *cmp = data;
+
+ if (data_len < sizeof(*cmp)) {
+ fprintf(stderr, "CMP header size mismatch\n");
+ return -1;
+ }
+
+ if (cmp->align == TCF_EM_ALIGN_U8)
+ fprintf(fd, "u8 ");
+ else if (cmp->align == TCF_EM_ALIGN_U16)
+ fprintf(fd, "u16 ");
+ else if (cmp->align == TCF_EM_ALIGN_U16)
+ fprintf(fd, "u32 ");
+
+ fprintf(fd, "at %d layer %d ", cmp->off, cmp->layer);
+
+ if (cmp->mask)
+ fprintf(fd, "mask 0x%x ", cmp->mask);
+
+ if (cmp->flags & TCF_EM_CMP_TRANS)
+ fprintf(fd, "trans ");
+
+ if (cmp->opnd == TCF_EM_OPND_EQ)
+ fprintf(fd, "eq ");
+ else if (cmp->opnd == TCF_EM_OPND_LT)
+ fprintf(fd, "lt ");
+ else if (cmp->opnd == TCF_EM_OPND_GT)
+ fprintf(fd, "gt ");
+
+ fprintf(fd, "%d", cmp->val);
+
+ return 0;
+}
+
+struct ematch_util cmp_ematch_util = {
+ .kind = "cmp",
+ .kind_num = TCF_EM_CMP,
+ .parse_eopt = cmp_parse_eopt,
+ .print_eopt = cmp_print_eopt,
+ .print_usage = cmp_print_usage
+};
--- /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(INDEV, "indev", "iv",
+ "Device the packet came in"),
+ __A(REALDEV, "realdev", "iv",
+ "Underlying real device"),
+
+ __A(SECTION, "Packet attributes", "", ""),
+ __A(PRIORITY, "priority", "i",
+ "Priority of packet"),
+ __A(PROTOCOL, "protocol", "i",
+ "Link layer protocol"),
+ __A(SECURITY, "security", "i",
+ "Security level"),
+ __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(TCVERDICT, "tc_verdict", "i", "TC Verdict"),
+ __A(TCCLASSID, "tc_classid", "i", "TC ClassID"),
+
+ __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
+};
--- /dev/null
+/*
+ * em_nbyte.c N-Byte 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_nbyte.h>
+
+extern struct ematch_util nbyte_ematch_util;
+
+static void nbyte_print_usage(FILE *fd)
+{
+ fprintf(fd,
+ "Usage: nbyte(NEEDLE at OFFSET [layer LAYER])\n" \
+ "where: NEEDLE := { string | \"c-escape-sequence\" }\n" \
+ " OFFSET := int\n" \
+ " LAYER := { link | header | next-header | 0..%d }\n" \
+ "\n" \
+ "Example: nbyte(\"ababa\" at 12 layer 1)\n",
+ TCF_LAYER_MAX);
+}
+
+static int nbyte_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+ struct bstr *args)
+{
+ struct bstr *a;
+ struct bstr *needle = args;
+ unsigned long offset = 0, layer = TCF_LAYER_NETWORK;
+ int offset_present = 0;
+ struct tcf_em_nbyte nb;
+
+ memset(&nb, 0, sizeof(nb));
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+ em_parse_error(EINVAL, args, CARG, &nbyte_ematch_util, FMT ,##ARGS)
+
+ if (args == NULL)
+ return PARSE_ERR(args, "nbyte: missing arguments");
+
+ if (needle->len <= 0)
+ return PARSE_ERR(args, "nbyte: needle length is 0");
+
+ for (a = bstr_next(args); a; a = bstr_next(a)) {
+ if (!bstrcmp(a, "at")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "nbyte: missing argument");
+ a = bstr_next(a);
+
+ offset = bstrtoul(a);
+ if (offset == ULONG_MAX)
+ return PARSE_ERR(a, "nbyte: invalid offset, " \
+ "must be numeric");
+
+ offset_present = 1;
+ } else if (!bstrcmp(a, "layer")) {
+ if (a->next == NULL)
+ return PARSE_ERR(a, "nbyte: missing argument");
+ a = bstr_next(a);
+
+ layer = parse_layer(a);
+ if (layer == INT_MAX) {
+ layer = bstrtoul(a);
+ if (layer == ULONG_MAX)
+ return PARSE_ERR(a, "nbyte: invalid " \
+ "layer");
+ }
+
+ if (layer > TCF_LAYER_MAX)
+ return PARSE_ERR(a, "nbyte: illegal layer, " \
+ "must be in 0..%d", TCF_LAYER_MAX);
+ } else
+ return PARSE_ERR(a, "nbyte: unknown parameter");
+ }
+
+ if (offset_present == 0)
+ return PARSE_ERR(a, "nbyte: offset required");
+
+ nb.len = needle->len;
+ nb.layer = (__u8) layer;
+ nb.off = (__u16) offset;
+
+ addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+ addraw_l(n, MAX_MSG, &nb, sizeof(nb));
+ addraw_l(n, MAX_MSG, needle->data, needle->len);
+
+#undef PARSE_ERR
+ return 0;
+}
+
+static int nbyte_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+ int data_len)
+{
+ int i;
+ struct tcf_em_nbyte *nb = data;
+ __u8 *needle;
+
+ if (data_len < sizeof(*nb)) {
+ fprintf(stderr, "NByte header size mismatch\n");
+ return -1;
+ }
+
+ if (data_len < sizeof(*nb) + nb->len) {
+ fprintf(stderr, "NByte payload size mismatch\n");
+ return -1;
+ }
+
+ needle = data + sizeof(*nb);
+
+ for (i = 0; i < nb->len; i++)
+ fprintf(fd, "%02x ", needle[i]);
+
+ fprintf(fd, "\"");
+ for (i = 0; i < nb->len; i++)
+ fprintf(fd, "%c", isprint(needle[i]) ? needle[i] : '.');
+ fprintf(fd, "\" at %d layer %d", nb->off, nb->layer);
+
+ return 0;
+}
+
+struct ematch_util nbyte_ematch_util = {
+ .kind = "nbyte",
+ .kind_num = TCF_EM_NBYTE,
+ .parse_eopt = nbyte_parse_eopt,
+ .print_eopt = nbyte_print_eopt,
+ .print_usage = nbyte_print_usage
+};
--- /dev/null
+/*
+ * em_u32.c U32 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"
+
+extern struct ematch_util u32_ematch_util;
+
+static void u32_print_usage(FILE *fd)
+{
+ fprintf(fd,
+ "Usage: u32(ALIGN VALUE MASK at [ nexthdr+ ] OFFSET)\n" \
+ "where: ALIGN := { u8 | u16 | u32 }\n" \
+ "\n" \
+ "Example: u32(u16 0x1122 0xffff at nexthdr+4)\n");
+}
+
+static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
+ struct bstr *args)
+{
+ struct bstr *a;
+ int align, nh_len;
+ unsigned long key, mask, offmask = 0, offset;
+ struct tc_u32_key u_key;
+
+ memset(&u_key, 0, sizeof(u_key));
+
+#define PARSE_ERR(CARG, FMT, ARGS...) \
+ em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS)
+
+ if (args == NULL)
+ return PARSE_ERR(args, "u32: missing arguments");
+
+ if (!bstrcmp(args, "u8"))
+ align = 1;
+ else if (!bstrcmp(args, "u16"))
+ align = 2;
+ else if (!bstrcmp(args, "u32"))
+ align = 4;
+ else
+ return PARSE_ERR(args, "u32: invalid alignment");
+
+ a = bstr_next(args);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing key");
+
+ key = bstrtoul(a);
+ if (key == ULONG_MAX)
+ return PARSE_ERR(a, "u32: invalid key, must be numeric");
+
+ a = bstr_next(a);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing mask");
+
+ mask = bstrtoul(a);
+ if (mask == ULONG_MAX)
+ return PARSE_ERR(a, "u32: invalid mask, must be numeric");
+
+ a = bstr_next(a);
+ if (a == NULL || bstrcmp(a, "at") != 0)
+ return PARSE_ERR(a, "u32: missing \"at\"");
+
+ a = bstr_next(a);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing offset");
+
+ nh_len = strlen("nexthdr+");
+ if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) {
+ char buf[a->len - nh_len + 1];
+ offmask = -1;
+ memcpy(buf, a->data + nh_len, a->len - nh_len);
+ offset = strtoul(buf, NULL, 0);
+ } else if (!bstrcmp(a, "nexthdr+")) {
+ a = bstr_next(a);
+ if (a == NULL)
+ return PARSE_ERR(a, "u32: missing offset");
+ offset = bstrtoul(a);
+ } else
+ offset = bstrtoul(a);
+
+ if (offset == ULONG_MAX)
+ return PARSE_ERR(a, "u32: invalid offset");
+
+ if (a->next)
+ return PARSE_ERR(a->next, "u32: unexpected trailer");
+
+ switch (align) {
+ case 1:
+ if (key > 0xFF)
+ return PARSE_ERR(a, "Illegal key (>0xFF)");
+ if (mask > 0xFF)
+ return PARSE_ERR(a, "Illegal mask (>0xFF)");
+
+ key <<= 24 - ((offset & 3) * 8);
+ mask <<= 24 - ((offset & 3) * 8);
+ offset &= ~3;
+ break;
+
+ case 2:
+ if (key > 0xFFFF)
+ return PARSE_ERR(a, "Illegal key (>0xFFFF)");
+ if (mask > 0xFFFF)
+ return PARSE_ERR(a, "Illegal mask (>0xFFFF)");
+
+ if ((offset & 3) == 0) {
+ key <<= 16;
+ mask <<= 16;
+ }
+ offset &= ~3;
+ break;
+ }
+
+ key = htonl(key);
+ mask = htonl(mask);
+
+ if (offset % 4)
+ return PARSE_ERR(a, "u32: invalid offset alignment, " \
+ "must be aligned to 4.");
+
+ key &= mask;
+
+ u_key.mask = mask;
+ u_key.val = key;
+ u_key.off = offset;
+ u_key.offmask = offmask;
+
+ addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+ addraw_l(n, MAX_MSG, &u_key, sizeof(u_key));
+
+#undef PARSE_ERR
+ return 0;
+}
+
+static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+ int data_len)
+{
+ struct tc_u32_key *u_key = data;
+
+ if (data_len < sizeof(*u_key)) {
+ fprintf(stderr, "U32 header size mismatch\n");
+ return -1;
+ }
+
+ fprintf(fd, "%08x/%08x at %s%d",
+ (unsigned int) ntohl(u_key->val),
+ (unsigned int) ntohl(u_key->mask),
+ u_key->offmask ? "nexthdr+" : "",
+ u_key->off);
+
+ return 0;
+}
+
+struct ematch_util u32_ematch_util = {
+ .kind = "u32",
+ .kind_num = TCF_EM_U32,
+ .parse_eopt = u32_parse_eopt,
+ .print_eopt = u32_print_eopt,
+ .print_usage = u32_print_usage
+};