Add new extended match files.
This commit is contained in:
parent
7314173cf9
commit
311b41454d
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
%%
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue