]> granicus.if.org Git - libnl/commitdiff
Support for meta match
authorThomas Graf <tgraf@suug.ch>
Sun, 31 Oct 2010 22:13:01 +0000 (23:13 +0100)
committerThomas Graf <tgraf@suug.ch>
Sun, 31 Oct 2010 22:13:01 +0000 (23:13 +0100)
Syntax:
meta(pktlen > 768)

include/linux/tc_ematch/tc_em_meta.h [new file with mode: 0644]
include/netlink/route/cls/ematch.h
include/netlink/route/cls/ematch/meta.h [new file with mode: 0644]
lib/Makefile.am
lib/route/cls/ematch.c
lib/route/cls/ematch/meta.c [new file with mode: 0644]
lib/route/cls/ematch_grammar.l
lib/route/cls/ematch_syntax.y

diff --git a/include/linux/tc_ematch/tc_em_meta.h b/include/linux/tc_ematch/tc_em_meta.h
new file mode 100644 (file)
index 0000000..fe815e2
--- /dev/null
@@ -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
index 52c4750eb8142cbee1bc0b82b9e1934ef3746c91..13f9c323dfc71c80a5029f77850fabe2850b2629 100644 (file)
@@ -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 (file)
index 0000000..2fe5899
--- /dev/null
@@ -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
index 7c869dcec752e6caafb464965b7da4b2a3a7cbac..6cfce33168c959acbf0c6c0516b5d11c168a5310 100644 (file)
@@ -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 \
        \
index 76c34be4187c0ca0b38c691a6c1bf27c9c9b99dd..e2719c2aa38d8c97b4733e48f09dc1ff5ce1d4ff 100644 (file)
@@ -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 (file)
index 0000000..5752c75
--- /dev/null
@@ -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);
+}
+
+/** @} */
index 998e86795728bf78ff473e5cb78727030f0914f0..07e7e8c6aab8fc061c239ec8d70bf8b6896d8807 100644 (file)
@@ -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)
index 26642a3946b530c0cb68542eb2b0d275f9e72594..61c91d11b6a9f67966afcb2a4d5da340d745de2e 100644 (file)
 #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
 
@@ -178,6 +239,24 @@ ematch:
                                rtnl_pktloc_put($6);
                        }
 
+                       $$ = 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 */
@@ -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; }
        ;