diff options
-rw-r--r-- | include/linux/tc_ematch/tc_em_meta.h | 89 | ||||
-rw-r--r-- | include/netlink/route/cls/ematch.h | 1 | ||||
-rw-r--r-- | include/netlink/route/cls/ematch/meta.h | 41 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/route/cls/ematch.c | 14 | ||||
-rw-r--r-- | lib/route/cls/ematch/meta.c | 291 | ||||
-rw-r--r-- | lib/route/cls/ematch_grammar.l | 56 | ||||
-rw-r--r-- | lib/route/cls/ematch_syntax.y | 154 |
8 files changed, 644 insertions, 3 deletions
diff --git a/include/linux/tc_ematch/tc_em_meta.h b/include/linux/tc_ematch/tc_em_meta.h new file mode 100644 index 0000000..fe815e2 --- /dev/null +++ b/include/linux/tc_ematch/tc_em_meta.h @@ -0,0 +1,89 @@ +#ifndef __LINUX_TC_EM_META_H +#define __LINUX_TC_EM_META_H + +enum { + TCA_EM_META_UNSPEC, + TCA_EM_META_HDR, + TCA_EM_META_LVALUE, + TCA_EM_META_RVALUE, + __TCA_EM_META_MAX +}; +#define TCA_EM_META_MAX (__TCA_EM_META_MAX - 1) + +struct tcf_meta_val { + __u16 kind; + __u8 shift; + __u8 op; +}; + +#define TCF_META_TYPE_MASK (0xf << 12) +#define TCF_META_TYPE(kind) (((kind) & TCF_META_TYPE_MASK) >> 12) +#define TCF_META_ID_MASK 0x7ff +#define TCF_META_ID(kind) ((kind) & TCF_META_ID_MASK) + +enum { + TCF_META_TYPE_VAR, + TCF_META_TYPE_INT, + __TCF_META_TYPE_MAX +}; +#define TCF_META_TYPE_MAX (__TCF_META_TYPE_MAX - 1) + +enum { + TCF_META_ID_VALUE, + TCF_META_ID_RANDOM, + TCF_META_ID_LOADAVG_0, + TCF_META_ID_LOADAVG_1, + TCF_META_ID_LOADAVG_2, + TCF_META_ID_DEV, + TCF_META_ID_PRIORITY, + TCF_META_ID_PROTOCOL, + TCF_META_ID_PKTTYPE, + TCF_META_ID_PKTLEN, + TCF_META_ID_DATALEN, + TCF_META_ID_MACLEN, + TCF_META_ID_NFMARK, + TCF_META_ID_TCINDEX, + TCF_META_ID_RTCLASSID, + TCF_META_ID_RTIIF, + TCF_META_ID_SK_FAMILY, + TCF_META_ID_SK_STATE, + TCF_META_ID_SK_REUSE, + TCF_META_ID_SK_BOUND_IF, + TCF_META_ID_SK_REFCNT, + TCF_META_ID_SK_SHUTDOWN, + TCF_META_ID_SK_PROTO, + TCF_META_ID_SK_TYPE, + TCF_META_ID_SK_RCVBUF, + TCF_META_ID_SK_RMEM_ALLOC, + TCF_META_ID_SK_WMEM_ALLOC, + TCF_META_ID_SK_OMEM_ALLOC, + TCF_META_ID_SK_WMEM_QUEUED, + TCF_META_ID_SK_RCV_QLEN, + TCF_META_ID_SK_SND_QLEN, + TCF_META_ID_SK_ERR_QLEN, + TCF_META_ID_SK_FORWARD_ALLOCS, + TCF_META_ID_SK_SNDBUF, + TCF_META_ID_SK_ALLOCS, + TCF_META_ID_SK_ROUTE_CAPS, + TCF_META_ID_SK_HASH, + TCF_META_ID_SK_LINGERTIME, + TCF_META_ID_SK_ACK_BACKLOG, + TCF_META_ID_SK_MAX_ACK_BACKLOG, + TCF_META_ID_SK_PRIO, + TCF_META_ID_SK_RCVLOWAT, + TCF_META_ID_SK_RCVTIMEO, + TCF_META_ID_SK_SNDTIMEO, + TCF_META_ID_SK_SENDMSG_OFF, + TCF_META_ID_SK_WRITE_PENDING, + TCF_META_ID_VLAN_TAG, + TCF_META_ID_RXHASH, + __TCF_META_ID_MAX +}; +#define TCF_META_ID_MAX (__TCF_META_ID_MAX - 1) + +struct tcf_meta_hdr { + struct tcf_meta_val left; + struct tcf_meta_val right; +}; + +#endif diff --git a/include/netlink/route/cls/ematch.h b/include/netlink/route/cls/ematch.h index 52c4750..13f9c32 100644 --- a/include/netlink/route/cls/ematch.h +++ b/include/netlink/route/cls/ematch.h @@ -86,6 +86,7 @@ extern int rtnl_ematch_parse_expr(const char *, char **, extern char * rtnl_ematch_offset2txt(uint8_t, uint16_t, char *, size_t); +extern char * rtnl_ematch_opnd2txt(uint8_t, char *, size_t); #ifdef __cplusplus } diff --git a/include/netlink/route/cls/ematch/meta.h b/include/netlink/route/cls/ematch/meta.h new file mode 100644 index 0000000..2fe5899 --- /dev/null +++ b/include/netlink/route/cls/ematch/meta.h @@ -0,0 +1,41 @@ +/* + * netlink/route/cls/ematch/meta.h Metadata Match + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch> + */ + +#ifndef NETLINK_CLS_EMATCH_META_H_ +#define NETLINK_CLS_EMATCH_META_H_ + +#include <netlink/netlink.h> +#include <netlink/route/cls/ematch.h> +#include <linux/tc_ematch/tc_em_meta.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_meta_value; + +extern struct rtnl_meta_value * rtnl_meta_value_alloc_int(uint64_t); +extern struct rtnl_meta_value * rtnl_meta_value_alloc_var(void *, size_t); +extern struct rtnl_meta_value * rtnl_meta_value_alloc_id(uint8_t, uint16_t, + uint8_t, uint64_t); +extern void rtnl_meta_value_put(struct rtnl_meta_value *); + +extern void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *, + struct rtnl_meta_value *); +void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *, + struct rtnl_meta_value *); +extern void rtnl_ematch_meta_set_operand(struct rtnl_ematch *, uint8_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 7c869dc..6cfce33 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -56,6 +56,7 @@ libnl_route_la_SOURCES = \ route/cls/ematch.c \ route/cls/ematch/container.c route/cls/ematch/cmp.c \ route/cls/ematch/nbyte.c route/cls/ematch/text.c \ + route/cls/ematch/meta.c \ \ route/link/api.c route/link/vlan.c \ \ diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c index 76c34be..e2719c2 100644 --- a/lib/route/cls/ematch.c +++ b/lib/route/cls/ematch.c @@ -682,4 +682,18 @@ char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t l return buf; } +static const char *operand_txt[] = { + [TCF_EM_OPND_EQ] = "=", + [TCF_EM_OPND_LT] = "<", + [TCF_EM_OPND_GT] = ">", +}; + +char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len) +{ + snprintf(buf, len, "%s", + opnd <= ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?"); + + return buf; +} + /** @} */ diff --git a/lib/route/cls/ematch/meta.c b/lib/route/cls/ematch/meta.c new file mode 100644 index 0000000..5752c75 --- /dev/null +++ b/lib/route/cls/ematch/meta.c @@ -0,0 +1,291 @@ +/* + * lib/route/cls/ematch/meta.c Metadata Match + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch> + */ + +/** + * @ingroup ematch + * @defgroup em_meta Metadata Match + * + * @{ + */ + +#include <netlink-local.h> +#include <netlink-tc.h> +#include <netlink/netlink.h> +#include <netlink/route/cls/ematch.h> +#include <netlink/route/cls/ematch/meta.h> + +struct rtnl_meta_value +{ + uint8_t mv_type; + uint8_t mv_shift; + uint16_t mv_id; + size_t mv_len; +}; + +struct meta_data +{ + struct rtnl_meta_value * left; + struct rtnl_meta_value * right; + uint8_t opnd; +}; + +static struct rtnl_meta_value *meta_alloc(uint8_t type, uint16_t id, + uint8_t shift, void *data, + size_t len) +{ + struct rtnl_meta_value *value; + + if (!(value = calloc(1, sizeof(*value) + len))) + return NULL; + + value->mv_type = type; + value->mv_id = id; + value->mv_shift = shift; + value->mv_len = len; + + memcpy(value + 1, data, len); + + return value; +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_int(uint64_t value) +{ + return meta_alloc(TCF_META_TYPE_INT, TCF_META_ID_VALUE, 0, &value, 8); +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_var(void *data, size_t len) +{ + return meta_alloc(TCF_META_TYPE_VAR, TCF_META_ID_VALUE, 0, data, len); +} + +struct rtnl_meta_value *rtnl_meta_value_alloc_id(uint8_t type, uint16_t id, + uint8_t shift, uint64_t mask) +{ + size_t masklen = 0; + + if (id > TCF_META_ID_MAX) + return NULL; + + if (mask) { + if (type == TCF_META_TYPE_VAR) + return NULL; + + masklen = 8; + } + + return meta_alloc(type, id, shift, &mask, masklen); +} + +void rtnl_meta_value_put(struct rtnl_meta_value *mv) +{ + free(mv); +} + +void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->left = v; +} + +void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->right = v; +} + +void rtnl_ematch_meta_set_operand(struct rtnl_ematch *e, uint8_t opnd) +{ + struct meta_data *m = rtnl_ematch_data(e); + m->opnd = opnd; +} + +static struct nla_policy meta_policy[TCA_EM_META_MAX+1] = { + [TCA_EM_META_HDR] = { .minlen = sizeof(struct tcf_meta_hdr) }, + [TCA_EM_META_LVALUE] = { .minlen = 1, }, + [TCA_EM_META_RVALUE] = { .minlen = 1, }, +}; + +static int meta_parse(struct rtnl_ematch *e, void *data, size_t len) +{ + struct meta_data *m = rtnl_ematch_data(e); + struct nlattr *tb[TCA_EM_META_MAX+1]; + struct rtnl_meta_value *v; + struct tcf_meta_hdr *hdr; + void *vdata = NULL; + size_t vlen = 0; + int err; + + if ((err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy)) < 0) + return err; + + if (!tb[TCA_EM_META_HDR]) + return -NLE_MISSING_ATTR; + + hdr = nla_data(tb[TCA_EM_META_HDR]); + + if (tb[TCA_EM_META_LVALUE]) { + vdata = nla_data(tb[TCA_EM_META_LVALUE]); + vlen = nla_len(tb[TCA_EM_META_LVALUE]); + } + + v = meta_alloc(TCF_META_TYPE(hdr->left.kind), + TCF_META_ID(hdr->left.kind), + hdr->left.shift, vdata, vlen); + if (!v) + return -NLE_NOMEM; + + m->left = v; + + vlen = 0; + if (tb[TCA_EM_META_RVALUE]) { + vdata = nla_data(tb[TCA_EM_META_RVALUE]); + vlen = nla_len(tb[TCA_EM_META_RVALUE]); + } + + v = meta_alloc(TCF_META_TYPE(hdr->right.kind), + TCF_META_ID(hdr->right.kind), + hdr->right.shift, vdata, vlen); + if (!v) { + rtnl_meta_value_put(m->left); + return -NLE_NOMEM; + } + + m->right = v; + m->opnd = hdr->left.op; + + return 0; +} + +static struct trans_tbl meta_int[] = { + __ADD(TCF_META_ID_VLAN_TAG, vlan) + __ADD(TCF_META_ID_PKTLEN, pktlen) +}; + +static char *int_id2str(int id, char *buf, size_t size) +{ + return __type2str(id, buf, size, meta_int, ARRAY_SIZE(meta_int)); +} + +static struct trans_tbl meta_var[] = { + __ADD(TCF_META_ID_DEV,devname) + __ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if) +}; + +static char *var_id2str(int id, char *buf, size_t size) +{ + return __type2str(id, buf, size, meta_var, ARRAY_SIZE(meta_var)); +} + +static void dump_value(struct rtnl_meta_value *v, struct nl_dump_params *p) +{ + char buf[32]; + + switch (v->mv_type) { + case TCF_META_TYPE_INT: + if (v->mv_id == TCF_META_ID_VALUE) { + nl_dump(p, "%u", + *(uint32_t *) (v + 1)); + } else { + nl_dump(p, "%s", + int_id2str(v->mv_id, buf, sizeof(buf))); + + if (v->mv_shift) + nl_dump(p, " >> %u", v->mv_shift); + + if (v->mv_len == 4) + nl_dump(p, " & %#x", *(uint32_t *) (v + 1)); + else if (v->mv_len == 8) + nl_dump(p, " & %#x", *(uint64_t *) (v + 1)); + } + break; + + case TCF_META_TYPE_VAR: + if (v->mv_id == TCF_META_ID_VALUE) { + nl_dump(p, "%s", (char *) (v + 1)); + } else { + nl_dump(p, "%s", + var_id2str(v->mv_id, buf, sizeof(buf))); + + if (v->mv_shift) + nl_dump(p, " >> %u", v->mv_shift); + } + break; + } +} + +static void meta_dump(struct rtnl_ematch *e, struct nl_dump_params *p) +{ + struct meta_data *m = rtnl_ematch_data(e); + char buf[32]; + + nl_dump(p, "meta("); + dump_value(m->left, p); + + nl_dump(p, " %s ", rtnl_ematch_opnd2txt(m->opnd, buf, sizeof(buf))); + + dump_value(m->right, p); + nl_dump(p, ")"); +} + +static int meta_fill(struct rtnl_ematch *e, struct nl_msg *msg) +{ + struct meta_data *m = rtnl_ematch_data(e); + struct tcf_meta_hdr hdr; + + if (!(m->left && m->right)) + return -NLE_MISSING_ATTR; + + memset(&hdr, 0, sizeof(hdr)); + hdr.left.kind = (m->left->mv_type << 12) & TCF_META_TYPE_MASK; + hdr.left.kind |= m->left->mv_id & TCF_META_ID_MASK; + hdr.left.shift = m->left->mv_shift; + hdr.left.op = m->opnd; + hdr.right.kind = (m->right->mv_type << 12) & TCF_META_TYPE_MASK; + hdr.right.kind |= m->right->mv_id & TCF_META_ID_MASK; + + NLA_PUT(msg, TCA_EM_META_HDR, sizeof(hdr), &hdr); + + if (m->left->mv_len) + NLA_PUT(msg, TCA_EM_META_LVALUE, m->left->mv_len, (m->left + 1)); + + if (m->right->mv_len) + NLA_PUT(msg, TCA_EM_META_RVALUE, m->right->mv_len, (m->right + 1)); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +static void meta_free(struct rtnl_ematch *e) +{ + struct meta_data *m = rtnl_ematch_data(e); + free(m->left); + free(m->right); +} + +static struct rtnl_ematch_ops meta_ops = { + .eo_kind = TCF_EM_META, + .eo_name = "meta", + .eo_minlen = sizeof(struct tcf_meta_hdr), + .eo_datalen = sizeof(struct meta_data), + .eo_parse = meta_parse, + .eo_dump = meta_dump, + .eo_fill = meta_fill, + .eo_free = meta_free, +}; + +static void __init meta_init(void) +{ + rtnl_ematch_register(&meta_ops); +} + +/** @} */ diff --git a/lib/route/cls/ematch_grammar.l b/lib/route/cls/ematch_grammar.l index 998e867..07e7e8c 100644 --- a/lib/route/cls/ematch_grammar.l +++ b/lib/route/cls/ematch_grammar.l @@ -78,10 +78,14 @@ lt | [cC][mM][pP] { yylval->i = TCF_EM_CMP; return EMATCH_CMP; } [pP][aA][tT][tT][eE][rR][nN] { yylval->i = TCF_EM_NBYTE; return EMATCH_NBYTE; } [tT][eE][xX][tT] { yylval->i = TCF_EM_TEXT; return EMATCH_TEXT; } +[mM][eE][tT][aA] { yylval->i = TCF_EM_META; return EMATCH_META; } "(" return KW_OPEN; ")" return KW_CLOSE; -[mM][aA][sS][kK] return KW_MASK; +[mM][aA][sS][kK] | +"&" return KW_MASK; +[sS][hH][iI][fF][tT] | +">>" return KW_SHIFT; [aA][tT] return KW_AT; "+" return KW_PLUS; [fF][rR][oO][mM] return KW_FROM; @@ -99,6 +103,56 @@ lt | [tT][rR][aA][nN][sS][pP][oO][rR][tT] | [tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; } +random return META_RANDOM; +loadavg_0 return META_LOADAVG_0; +loadavg_1 return META_LOADAVG_1; +loadavg_2 return META_LOADAVG_2; +dev return META_DEV; +prio return META_PRIO; +proto return META_PROTO; +pkttype return META_PKTTYPE; +pktlen return META_PKTLEN; +datalen return META_DATALEN; +maclen return META_MACLEN; +mark return META_MARK; +tcindex return META_TCINDEX; +rtclassid return META_RTCLASSID; +rtiif return META_RTIIF; +sk_family return META_SK_FAMILY; +sk_state return META_SK_STATE; +sk_reuse return META_SK_REUSE; +sk_refcnt return META_SK_REFCNT; +sk_rcvbuf return META_SK_RCVBUF; +sk_sndbuf return META_SK_SNDBUF; +sk_shutdown return META_SK_SHUTDOWN; +sk_proto return META_SK_PROTO; +sk_type return META_SK_TYPE; +sk_rmem_alloc return META_SK_RMEM_ALLOC; +sk_wmem_alloc return META_SK_WMEM_ALLOC; +sk_wmem_queued return META_SK_WMEM_QUEUED; +sk_rcv_qlen return META_SK_RCV_QLEN; +sk_snd_qlen return META_SK_SND_QLEN; +sk_err_qlen return META_SK_ERR_QLEN; +sk_forward_allocs return META_SK_FORWARD_ALLOCS; +sk_allocs return META_SK_ALLOCS; +sk_route_caps return META_SK_ROUTE_CAPS; +sk_hash return META_SK_HASH; +sk_lingertime return META_SK_LINGERTIME; +sk_ack_backlog return META_SK_ACK_BACKLOG; +sk_max_ack_backlog return META_SK_MAX_ACK_BACKLOG; +sk_prio return META_SK_PRIO; +sk_rcvlowat return META_SK_RCVLOWAT; +sk_rcvtimeo return META_SK_RCVTIMEO; +sk_sndtimeo return META_SK_SNDTIMEO; +sk_sendmsg_off return META_SK_SENDMSG_OFF; +sk_write_pending return META_SK_WRITE_PENDING; +vlan return META_VLAN; +rxhash return META_RXHASH; + +devname return META_DEVNAME; +sk_bound_if return META_SK_BOUND_IF; + + [^ \t\r\n+()=<>&|\"]+ { yylval->s = strdup(yytext); if (yylval->s == NULL) diff --git a/lib/route/cls/ematch_syntax.y b/lib/route/cls/ematch_syntax.y index 26642a3..61c91d1 100644 --- a/lib/route/cls/ematch_syntax.y +++ b/lib/route/cls/ematch_syntax.y @@ -19,6 +19,12 @@ #include <netlink/route/cls/ematch/cmp.h> #include <netlink/route/cls/ematch/nbyte.h> #include <netlink/route/cls/ematch/text.h> +#include <netlink/route/cls/ematch/meta.h> + +#define META_ALLOC rtnl_meta_value_alloc_id +#define META_ID(name) TCF_META_ID_##name +#define META_INT TCF_META_TYPE_INT +#define META_VAR TCF_META_TYPE_VAR %} %error-verbose @@ -35,7 +41,9 @@ struct ematch_quoted q; struct rtnl_ematch * e; struct rtnl_pktloc * loc; + struct rtnl_meta_value *mv; uint32_t i; + uint64_t i64; char * s; } @@ -54,29 +62,82 @@ static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const %token <i> KW_CLOSE ")" %token <i> KW_PLUS "+" %token <i> KW_MASK "mask" +%token <i> KW_SHIFT ">>" %token <i> KW_AT "at" %token <i> EMATCH_CMP "cmp" %token <i> EMATCH_NBYTE "pattern" %token <i> EMATCH_TEXT "text" +%token <i> EMATCH_META "meta" %token <i> KW_EQ "=" %token <i> KW_GT ">" %token <i> KW_LT "<" %token <i> KW_FROM "from" %token <i> KW_TO "to" +%token <i> META_RANDOM "random" +%token <i> META_LOADAVG_0 "loadavg_0" +%token <i> META_LOADAVG_1 "loadavg_1" +%token <i> META_LOADAVG_2 "loadavg_2" +%token <i> META_DEV "dev" +%token <i> META_PRIO "prio" +%token <i> META_PROTO "proto" +%token <i> META_PKTTYPE "pkttype" +%token <i> META_PKTLEN "pktlen" +%token <i> META_DATALEN "datalen" +%token <i> META_MACLEN "maclen" +%token <i> META_MARK "mark" +%token <i> META_TCINDEX "tcindex" +%token <i> META_RTCLASSID "rtclassid" +%token <i> META_RTIIF "rtiif" +%token <i> META_SK_FAMILY "sk_family" +%token <i> META_SK_STATE "sk_state" +%token <i> META_SK_REUSE "sk_reuse" +%token <i> META_SK_REFCNT "sk_refcnt" +%token <i> META_SK_RCVBUF "sk_rcvbuf" +%token <i> META_SK_SNDBUF "sk_sndbuf" +%token <i> META_SK_SHUTDOWN "sk_shutdown" +%token <i> META_SK_PROTO "sk_proto" +%token <i> META_SK_TYPE "sk_type" +%token <i> META_SK_RMEM_ALLOC "sk_rmem_alloc" +%token <i> META_SK_WMEM_ALLOC "sk_wmem_alloc" +%token <i> META_SK_WMEM_QUEUED "sk_wmem_queued" +%token <i> META_SK_RCV_QLEN "sk_rcv_qlen" +%token <i> META_SK_SND_QLEN "sk_snd_qlen" +%token <i> META_SK_ERR_QLEN "sk_err_qlen" +%token <i> META_SK_FORWARD_ALLOCS "sk_forward_allocs" +%token <i> META_SK_ALLOCS "sk_allocs" +%token <i> META_SK_ROUTE_CAPS "sk_route_caps" +%token <i> META_SK_HASH "sk_hash" +%token <i> META_SK_LINGERTIME "sk_lingertime" +%token <i> META_SK_ACK_BACKLOG "sk_ack_backlog" +%token <i> META_SK_MAX_ACK_BACKLOG "sk_max_ack_backlog" +%token <i> META_SK_PRIO "sk_prio" +%token <i> META_SK_RCVLOWAT "sk_rcvlowat" +%token <i> META_SK_RCVTIMEO "sk_rcvtimeo" +%token <i> META_SK_SNDTIMEO "sk_sndtimeo" +%token <i> META_SK_SENDMSG_OFF "sk_sendmsg_off" +%token <i> META_SK_WRITE_PENDING "sk_write_pending" +%token <i> META_VLAN "vlan" +%token <i> META_RXHASH "rxhash" +%token <i> META_DEVNAME "devname" +%token <i> META_SK_BOUND_IF "sk_bound_if" + %token <s> STR %token <q> QUOTED -%type <i> mask align operand +%type <i> align operand shift meta_int_id meta_var_id +%type <i64> mask %type <e> expr match ematch %type <cmp> cmp_expr cmp_match %type <loc> pktloc text_from text_to %type <q> pattern +%type <mv> meta_value %destructor { free($$); NL_DBG(2, "string destructor\n"); } <s> %destructor { rtnl_pktloc_put($$); NL_DBG(2, "pktloc destructor\n"); } <loc> %destructor { free($$.data); NL_DBG(2, "quoted destructor\n"); } <q> +%destructor { rtnl_meta_value_put($$); NL_DBG(2, "meta value destructor\n"); } <mv> %start input @@ -180,6 +241,24 @@ ematch: $$ = e; } + | EMATCH_META "(" meta_value operand meta_value ")" + { + struct rtnl_ematch *e; + + if (!(e = rtnl_ematch_alloc())) { + asprintf(errp, "Unable to allocate ematch object"); + YYABORT; + } + + if (rtnl_ematch_set_kind(e, TCF_EM_META) < 0) + BUG(); + + rtnl_ematch_meta_set_lvalue(e, $3); + rtnl_ematch_meta_set_rvalue(e, $5); + rtnl_ematch_meta_set_operand(e, $4); + + $$ = e; + } /* CONTAINER */ | "(" expr ")" { @@ -249,6 +328,70 @@ text_to: { $$ = $2; } ; +meta_value: + QUOTED + { $$ = rtnl_meta_value_alloc_var($1.data, $1.len); } + | NUMBER + { $$ = rtnl_meta_value_alloc_int($1); } + | meta_int_id shift mask + { $$ = META_ALLOC(META_INT, $1, $2, $3); } + | meta_var_id shift + { $$ = META_ALLOC(META_VAR, $1, $2, 0); } + ; + +meta_int_id: + META_RANDOM { $$ = META_ID(RANDOM); } + |META_LOADAVG_0 { $$ = META_ID(LOADAVG_0); } + |META_LOADAVG_1 { $$ = META_ID(LOADAVG_1); } + |META_LOADAVG_2 { $$ = META_ID(LOADAVG_2); } + | META_DEV { $$ = META_ID(DEV); } + | META_PRIO { $$ = META_ID(PRIORITY); } + | META_PROTO { $$ = META_ID(PROTOCOL); } + | META_PKTTYPE { $$ = META_ID(PKTTYPE); } + | META_PKTLEN { $$ = META_ID(PKTLEN); } + | META_DATALEN { $$ = META_ID(DATALEN); } + | META_MACLEN { $$ = META_ID(MACLEN); } + | META_MARK { $$ = META_ID(NFMARK); } + | META_TCINDEX { $$ = META_ID(TCINDEX); } + | META_RTCLASSID { $$ = META_ID(RTCLASSID); } + | META_RTIIF { $$ = META_ID(RTIIF); } + | META_SK_FAMILY { $$ = META_ID(SK_FAMILY); } + | META_SK_STATE { $$ = META_ID(SK_STATE); } + | META_SK_REUSE { $$ = META_ID(SK_REUSE); } + | META_SK_REFCNT { $$ = META_ID(SK_REFCNT); } + | META_SK_RCVBUF { $$ = META_ID(SK_RCVBUF); } + | META_SK_SNDBUF { $$ = META_ID(SK_SNDBUF); } + | META_SK_SHUTDOWN { $$ = META_ID(SK_SHUTDOWN); } + | META_SK_PROTO { $$ = META_ID(SK_PROTO); } + | META_SK_TYPE { $$ = META_ID(SK_TYPE); } + | META_SK_RMEM_ALLOC { $$ = META_ID(SK_RMEM_ALLOC); } + | META_SK_WMEM_ALLOC { $$ = META_ID(SK_WMEM_ALLOC); } + | META_SK_WMEM_QUEUED { $$ = META_ID(SK_WMEM_QUEUED); } + | META_SK_RCV_QLEN { $$ = META_ID(SK_RCV_QLEN); } + | META_SK_SND_QLEN { $$ = META_ID(SK_SND_QLEN); } + | META_SK_ERR_QLEN { $$ = META_ID(SK_ERR_QLEN); } + | META_SK_FORWARD_ALLOCS { $$ = META_ID(SK_FORWARD_ALLOCS); } + | META_SK_ALLOCS { $$ = META_ID(SK_ALLOCS); } + | META_SK_ROUTE_CAPS { $$ = META_ID(SK_ROUTE_CAPS); } + | META_SK_HASH { $$ = META_ID(SK_HASH); } + | META_SK_LINGERTIME { $$ = META_ID(SK_LINGERTIME); } + | META_SK_ACK_BACKLOG { $$ = META_ID(SK_ACK_BACKLOG); } + | META_SK_MAX_ACK_BACKLOG { $$ = META_ID(SK_MAX_ACK_BACKLOG); } + | META_SK_PRIO { $$ = META_ID(SK_PRIO); } + | META_SK_RCVLOWAT { $$ = META_ID(SK_RCVLOWAT); } + | META_SK_RCVTIMEO { $$ = META_ID(SK_RCVTIMEO); } + | META_SK_SNDTIMEO { $$ = META_ID(SK_SNDTIMEO); } + | META_SK_SENDMSG_OFF { $$ = META_ID(SK_SENDMSG_OFF); } + | META_SK_WRITE_PENDING { $$ = META_ID(SK_WRITE_PENDING); } + | META_VLAN { $$ = META_ID(VLAN_TAG); } + | META_RXHASH { $$ = META_ID(RXHASH); } + ; + +meta_var_id: + META_DEVNAME { $$ = META_ID(DEV); } + | META_SK_BOUND_IF { $$ = META_ID(SK_BOUND_IF); } + ; + /* * pattern */ @@ -333,7 +476,14 @@ align: mask: /* empty */ { $$ = 0; } - | "mask" NUMBER + | KW_MASK NUMBER + { $$ = $2; } + ; + +shift: + /* empty */ + { $$ = 0; } + | KW_SHIFT NUMBER { $$ = $2; } ; |