Add new extended match files.
diff --git a/tc/em_cmp.c b/tc/em_cmp.c
new file mode 100644
index 0000000..c636c53
--- /dev/null
+++ b/tc/em_cmp.c
@@ -0,0 +1,188 @@
+/*
+ * 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
+};
diff --git a/tc/em_meta.c b/tc/em_meta.c
new file mode 100644
index 0000000..86186c1
--- /dev/null
+++ b/tc/em_meta.c
@@ -0,0 +1,560 @@
+/*
+ * 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
+};
diff --git a/tc/em_nbyte.c b/tc/em_nbyte.c
new file mode 100644
index 0000000..e0ed5ba
--- /dev/null
+++ b/tc/em_nbyte.c
@@ -0,0 +1,144 @@
+/*
+ * 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
+};
diff --git a/tc/em_u32.c b/tc/em_u32.c
new file mode 100644
index 0000000..b8857f1
--- /dev/null
+++ b/tc/em_u32.c
@@ -0,0 +1,178 @@
+/*
+ * 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
+};
diff --git a/tc/emp_ematch.l b/tc/emp_ematch.l
new file mode 100644
index 0000000..80ab0da
--- /dev/null
+++ b/tc/emp_ematch.l
@@ -0,0 +1,143 @@
+%{
+ #include "emp_ematch.yacc.h"
+ #include "m_ematch.h"
+
+ extern int ematch_argc;
+ extern char **ematch_argv;
+
+ #define NEXT_EM_ARG() do { ematch_argc--; ematch_argv++; } while(0);
+
+ #define YY_INPUT(buf, result, max_size) \
+ { \
+ next: \
+ if (ematch_argc <= 0) \
+ result = YY_NULL; \
+ else if (**ematch_argv == '\0') { \
+ NEXT_EM_ARG(); \
+ goto next; \
+ } else { \
+ if (max_size <= strlen(*ematch_argv) + 1) { \
+ fprintf(stderr, "match argument too long.\n"); \
+ result = YY_NULL; \
+ } else { \
+ strcpy(buf, *ematch_argv); \
+ result = strlen(*ematch_argv) + 1; \
+ buf[result-1] = ' '; \
+ buf[result] = '\0'; \
+ NEXT_EM_ARG(); \
+ } \
+ } \
+ }
+
+ static void __attribute__ ((unused)) yyunput (int c,char *buf_ptr );
+ static void __attribute__ ((unused)) yy_push_state (int new_state );
+ static void __attribute__ ((unused)) yy_pop_state (void);
+ static int __attribute__ ((unused)) yy_top_state (void );
+%}
+
+%x str
+
+%option 8bit stack warn bison-bridge noyywrap prefix="ematch_"
+%%
+
+ static unsigned char *strbuf;
+ static unsigned int strbuf_size;
+ static unsigned int strbuf_index;
+
+ static inline void strbuf_enlarge(void)
+ {
+ strbuf_size += 512;
+ strbuf = realloc(strbuf, strbuf_size);
+ }
+
+ static inline void strbuf_append_char(unsigned char c)
+ {
+ while (strbuf_index >= strbuf_size)
+ strbuf_enlarge();
+ strbuf[strbuf_index++] = c;
+ }
+
+ static inline void strbuf_append_charp(unsigned char *s)
+ {
+ while (strbuf_index >= strbuf_size)
+ strbuf_enlarge();
+ memcpy(strbuf + strbuf_index, s, strlen(s));
+ strbuf_index += strlen(s);
+ }
+
+[ \t\r\n]+
+
+\" {
+ if (strbuf == NULL) {
+ strbuf_size = 512;
+ strbuf = calloc(1, strbuf_size);
+ if (strbuf == NULL)
+ return ERROR;
+ }
+ strbuf_index = 0;
+
+ BEGIN(str);
+ }
+
+<str>\" {
+ BEGIN(INITIAL);
+ yylval->b = bstr_new(strbuf, strbuf_index);
+ yylval->b->quoted = 1;
+ return ATTRIBUTE;
+ }
+
+<str>\\[0-7]{1,3} { /* octal escape sequence */
+ int res;
+
+ sscanf(yytext + 1, "%o", &res);
+ if (res > 0xFF) {
+ fprintf(stderr, "error: octal escape sequence" \
+ " out of range\n");
+ return ERROR;
+ }
+ strbuf_append_char((unsigned char) res);
+ }
+
+<str>\\[0-9]+ { /* catch wrong octal escape seq. */
+ fprintf(stderr, "error: invalid octale escape sequence\n");
+ return ERROR;
+ }
+
+<str>\\x[0-9a-fA-F]{1,2} {
+ int res;
+
+ sscanf(yytext + 2, "%x", &res);
+
+ if (res > 0xFF) {
+ fprintf(stderr, "error: hexadecimal escape " \
+ "sequence out of range\n");
+ return ERROR;
+ }
+ strbuf_append_char((unsigned char) res);
+ }
+
+<str>\\n strbuf_append_char('\n');
+<str>\\r strbuf_append_char('\r');
+<str>\\t strbuf_append_char('\t');
+<str>\\v strbuf_append_char('\v');
+<str>\\b strbuf_append_char('\b');
+<str>\\f strbuf_append_char('\f');
+<str>\\a strbuf_append_char('\a');
+
+<str>\\(.|\n) strbuf_append_char(yytext[1]);
+<str>[^\\\n\"]+ strbuf_append_charp(yytext);
+
+[aA][nN][dD] return AND;
+[oO][rR] return OR;
+[nN][oO][tT] return NOT;
+"(" |
+")" {
+ return yylval->i = *yytext;
+ }
+[^ \t\r\n()]+ {
+ yylval->b = bstr_alloc(yytext);
+ if (yylval->b == NULL)
+ return ERROR;
+ return ATTRIBUTE;
+ }
+%%
diff --git a/tc/emp_ematch.y b/tc/emp_ematch.y
new file mode 100644
index 0000000..b4e4a3e
--- /dev/null
+++ b/tc/emp_ematch.y
@@ -0,0 +1,102 @@
+%{
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <malloc.h>
+ #include <string.h>
+ #include "m_ematch.h"
+%}
+
+%locations
+%pure_parser
+%token-table
+%error-verbose
+%name-prefix="ematch_"
+
+%union {
+ unsigned int i;
+ struct bstr *b;
+ struct ematch *e;
+}
+
+%{
+ extern int yylex(YYSTYPE *, YYLTYPE *);
+ extern void yyerror(char *s);
+ extern struct ematch *ematch_root;
+ extern char *ematch_err;
+%}
+
+%token <i> ERROR
+%token <b> ATTRIBUTE
+%token <i> AND OR NOT
+%type <i> invert relation
+%type <e> match expr
+%type <b> args
+%right AND OR
+%start input
+%%
+input:
+ /* empty */
+ | expr
+ { ematch_root = $1; }
+ | expr error
+ {
+ ematch_root = $1;
+ YYACCEPT;
+ }
+ ;
+
+expr:
+ match
+ { $$ = $1; }
+ | match relation expr
+ {
+ $1->relation = $2;
+ $1->next = $3;
+ $$ = $1;
+ }
+ ;
+
+match:
+ invert ATTRIBUTE '(' args ')'
+ {
+ $2->next = $4;
+ $$ = new_ematch($2, $1);
+ if ($$ == NULL)
+ YYABORT;
+ }
+ | invert '(' expr ')'
+ {
+ $$ = new_ematch(NULL, $1);
+ if ($$ == NULL)
+ YYABORT;
+ $$->child = $3;
+ }
+ ;
+
+args:
+ ATTRIBUTE
+ { $$ = $1; }
+ | ATTRIBUTE args
+ { $1->next = $2; }
+ ;
+
+relation:
+ AND
+ { $$ = TCF_EM_REL_AND; }
+ | OR
+ { $$ = TCF_EM_REL_OR; }
+ ;
+
+invert:
+ /* empty */
+ { $$ = 0; }
+ | NOT
+ { $$ = 1; }
+ ;
+%%
+
+ void yyerror(char *s)
+ {
+ ematch_err = strdup(s);
+ }
+