From: shemminger Date: Thu, 23 Jun 2005 20:25:16 +0000 (+0000) Subject: Add new extended match files. X-Git-Tag: ss-050808~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=311b41454dc445639924c691a949bd15fbfab0cb;p=thirdparty%2Fiproute2.git Add new extended match files. --- diff --git a/tc/em_cmp.c b/tc/em_cmp.c new file mode 100644 index 000000000..c636c53ad --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m_ematch.h" +#include + +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 000000000..86186c143 --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m_ematch.h" +#include + +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 000000000..e0ed5baf5 --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m_ematch.h" +#include + +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 000000000..b8857f16d --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000..80ab0da9c --- /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); + } + +\" { + BEGIN(INITIAL); + yylval->b = bstr_new(strbuf, strbuf_index); + yylval->b->quoted = 1; + return ATTRIBUTE; + } + +\\[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); + } + +\\[0-9]+ { /* catch wrong octal escape seq. */ + fprintf(stderr, "error: invalid octale escape sequence\n"); + return ERROR; + } + +\\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); + } + +\\n strbuf_append_char('\n'); +\\r strbuf_append_char('\r'); +\\t strbuf_append_char('\t'); +\\v strbuf_append_char('\v'); +\\b strbuf_append_char('\b'); +\\f strbuf_append_char('\f'); +\\a strbuf_append_char('\a'); + +\\(.|\n) strbuf_append_char(yytext[1]); +[^\\\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 000000000..b4e4a3e2d --- /dev/null +++ b/tc/emp_ematch.y @@ -0,0 +1,102 @@ +%{ + #include + #include + #include + #include + #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 ERROR +%token ATTRIBUTE +%token AND OR NOT +%type invert relation +%type match expr +%type 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); + } +