summaryrefslogtreecommitdiffstats
path: root/src/expr
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:04:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:04:34 +0000
commit371c8a8bca2b6862226bc79c13d143e07c1d8fc3 (patch)
tree06c7dc8826596690422e79d5bde2f46c90492de3 /src/expr
parentInitial commit. (diff)
downloadlibnftnl-371c8a8bca2b6862226bc79c13d143e07c1d8fc3.tar.xz
libnftnl-371c8a8bca2b6862226bc79c13d143e07c1d8fc3.zip
Adding upstream version 1.2.6.upstream/1.2.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/expr.c303
-rw-r--r--src/expr/bitwise.c286
-rw-r--r--src/expr/byteorder.c224
-rw-r--r--src/expr/cmp.c206
-rw-r--r--src/expr/connlimit.c139
-rw-r--r--src/expr/counter.c137
-rw-r--r--src/expr/ct.c262
-rw-r--r--src/expr/data_reg.c219
-rw-r--r--src/expr/dup.c142
-rw-r--r--src/expr/dynset.c377
-rw-r--r--src/expr/exthdr.c271
-rw-r--r--src/expr/fib.c202
-rw-r--r--src/expr/flow_offload.c124
-rw-r--r--src/expr/fwd.c162
-rw-r--r--src/expr/hash.c230
-rw-r--r--src/expr/immediate.c233
-rw-r--r--src/expr/inner.c214
-rw-r--r--src/expr/last.c138
-rw-r--r--src/expr/limit.c206
-rw-r--r--src/expr/log.c257
-rw-r--r--src/expr/lookup.c210
-rw-r--r--src/expr/masq.c167
-rw-r--r--src/expr/match.c193
-rw-r--r--src/expr/meta.c221
-rw-r--r--src/expr/nat.c278
-rw-r--r--src/expr/numgen.c184
-rw-r--r--src/expr/objref.c209
-rw-r--r--src/expr/osf.c151
-rw-r--r--src/expr/payload.c250
-rw-r--r--src/expr/queue.c197
-rw-r--r--src/expr/quota.c151
-rw-r--r--src/expr/range.c217
-rw-r--r--src/expr/redir.c171
-rw-r--r--src/expr/reject.c138
-rw-r--r--src/expr/rt.c166
-rw-r--r--src/expr/socket.c169
-rw-r--r--src/expr/synproxy.c156
-rw-r--r--src/expr/target.c193
-rw-r--r--src/expr/tproxy.c174
-rw-r--r--src/expr/tunnel.c149
-rw-r--r--src/expr/xfrm.c200
-rw-r--r--src/expr_ops.c106
42 files changed, 8382 insertions, 0 deletions
diff --git a/src/expr.c b/src/expr.c
new file mode 100644
index 0000000..b4581f1
--- /dev/null
+++ b/src/expr.c
@@ -0,0 +1,303 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+
+EXPORT_SYMBOL(nftnl_expr_alloc);
+struct nftnl_expr *nftnl_expr_alloc(const char *name)
+{
+ struct nftnl_expr *expr;
+ struct expr_ops *ops;
+
+ ops = nftnl_expr_ops_lookup(name);
+ if (ops == NULL)
+ return NULL;
+
+ expr = calloc(1, sizeof(struct nftnl_expr) + ops->alloc_len);
+ if (expr == NULL)
+ return NULL;
+
+ /* Manually set expression name attribute */
+ expr->flags |= (1 << NFTNL_EXPR_NAME);
+ expr->ops = ops;
+
+ if (ops->init)
+ ops->init(expr);
+
+ return expr;
+}
+
+EXPORT_SYMBOL(nftnl_expr_free);
+void nftnl_expr_free(const struct nftnl_expr *expr)
+{
+ if (expr->ops->free)
+ expr->ops->free(expr);
+
+ xfree(expr);
+}
+
+EXPORT_SYMBOL(nftnl_expr_is_set);
+bool nftnl_expr_is_set(const struct nftnl_expr *expr, uint16_t type)
+{
+ return expr->flags & (1 << type);
+}
+
+EXPORT_SYMBOL(nftnl_expr_set);
+int nftnl_expr_set(struct nftnl_expr *expr, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ switch(type) {
+ case NFTNL_EXPR_NAME: /* cannot be modified */
+ return 0;
+ default:
+ if (expr->ops->set(expr, type, data, data_len) < 0)
+ return -1;
+ }
+ expr->flags |= (1 << type);
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u8);
+void
+nftnl_expr_set_u8(struct nftnl_expr *expr, uint16_t type, uint8_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint8_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u16);
+void
+nftnl_expr_set_u16(struct nftnl_expr *expr, uint16_t type, uint16_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint16_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u32);
+void
+nftnl_expr_set_u32(struct nftnl_expr *expr, uint16_t type, uint32_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u64);
+void
+nftnl_expr_set_u64(struct nftnl_expr *expr, uint16_t type, uint64_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_str);
+int nftnl_expr_set_str(struct nftnl_expr *expr, uint16_t type, const char *str)
+{
+ return nftnl_expr_set(expr, type, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get);
+const void *nftnl_expr_get(const struct nftnl_expr *expr,
+ uint16_t type, uint32_t *data_len)
+{
+ const void *ret;
+
+ if (!(expr->flags & (1 << type)))
+ return NULL;
+
+ switch(type) {
+ case NFTNL_EXPR_NAME:
+ *data_len = strlen(expr->ops->name) + 1;
+ ret = expr->ops->name;
+ break;
+ default:
+ ret = expr->ops->get(expr, type, data_len);
+ break;
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u8);
+uint8_t nftnl_expr_get_u8(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint8_t))
+ return 0;
+
+ return *((uint8_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u16);
+uint16_t nftnl_expr_get_u16(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint16_t))
+ return 0;
+
+ return *((uint16_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u32);
+uint32_t nftnl_expr_get_u32(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint32_t))
+ return 0;
+
+ return *((uint32_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u64);
+uint64_t nftnl_expr_get_u64(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint64_t))
+ return 0;
+
+ return *((uint64_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_str);
+const char *nftnl_expr_get_str(const struct nftnl_expr *expr, uint16_t type)
+{
+ uint32_t data_len;
+
+ return (const char *)nftnl_expr_get(expr, type, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_expr_build_payload);
+void nftnl_expr_build_payload(struct nlmsghdr *nlh, struct nftnl_expr *expr)
+{
+ struct nlattr *nest;
+
+ mnl_attr_put_strz(nlh, NFTA_EXPR_NAME, expr->ops->name);
+
+ if (!expr->ops->build)
+ return;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_EXPR_DATA);
+ expr->ops->build(nlh, expr);
+ mnl_attr_nest_end(nlh, nest);
+}
+
+static int nftnl_rule_parse_expr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_EXPR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_EXPR_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_EXPR_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+struct nftnl_expr *nftnl_expr_parse(struct nlattr *attr)
+{
+ struct nlattr *tb[NFTA_EXPR_MAX+1] = {};
+ struct nftnl_expr *expr;
+
+ if (mnl_attr_parse_nested(attr, nftnl_rule_parse_expr_cb, tb) < 0)
+ goto err1;
+
+ expr = nftnl_expr_alloc(mnl_attr_get_str(tb[NFTA_EXPR_NAME]));
+ if (expr == NULL)
+ goto err1;
+
+ if (tb[NFTA_EXPR_DATA] &&
+ expr->ops->parse &&
+ expr->ops->parse(expr, tb[NFTA_EXPR_DATA]) < 0)
+ goto err2;
+
+ return expr;
+
+err2:
+ xfree(expr);
+err1:
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_expr_snprintf);
+int nftnl_expr_snprintf(char *buf, size_t remain, const struct nftnl_expr *expr,
+ uint32_t type, uint32_t flags)
+{
+ int ret;
+ unsigned int offset = 0;
+
+ if (remain)
+ buf[0] = '\0';
+
+ if (!expr->ops->output || type != NFTNL_OUTPUT_DEFAULT)
+ return 0;
+
+ ret = expr->ops->output(buf + offset, remain, flags, expr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static int nftnl_expr_do_snprintf(char *buf, size_t size, const void *e,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_expr_snprintf(buf, size, e, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_expr_fprintf);
+int nftnl_expr_fprintf(FILE *fp, const struct nftnl_expr *expr, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, expr, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_expr_do_snprintf);
+}
diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
new file mode 100644
index 0000000..2d27233
--- /dev/null
+++ b/src/expr/bitwise.c
@@ -0,0 +1,286 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_bitwise {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ enum nft_bitwise_ops op;
+ unsigned int len;
+ union nftnl_data_reg mask;
+ union nftnl_data_reg xor;
+ union nftnl_data_reg data;
+};
+
+static int
+nftnl_expr_bitwise_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BITWISE_SREG:
+ memcpy(&bitwise->sreg, data, sizeof(bitwise->sreg));
+ break;
+ case NFTNL_EXPR_BITWISE_DREG:
+ memcpy(&bitwise->dreg, data, sizeof(bitwise->dreg));
+ break;
+ case NFTNL_EXPR_BITWISE_OP:
+ memcpy(&bitwise->op, data, sizeof(bitwise->op));
+ break;
+ case NFTNL_EXPR_BITWISE_LEN:
+ memcpy(&bitwise->len, data, sizeof(bitwise->len));
+ break;
+ case NFTNL_EXPR_BITWISE_MASK:
+ memcpy(&bitwise->mask.val, data, data_len);
+ bitwise->mask.len = data_len;
+ break;
+ case NFTNL_EXPR_BITWISE_XOR:
+ memcpy(&bitwise->xor.val, data, data_len);
+ bitwise->xor.len = data_len;
+ break;
+ case NFTNL_EXPR_BITWISE_DATA:
+ memcpy(&bitwise->data.val, data, data_len);
+ bitwise->data.len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_bitwise_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BITWISE_SREG:
+ *data_len = sizeof(bitwise->sreg);
+ return &bitwise->sreg;
+ case NFTNL_EXPR_BITWISE_DREG:
+ *data_len = sizeof(bitwise->dreg);
+ return &bitwise->dreg;
+ case NFTNL_EXPR_BITWISE_OP:
+ *data_len = sizeof(bitwise->op);
+ return &bitwise->op;
+ case NFTNL_EXPR_BITWISE_LEN:
+ *data_len = sizeof(bitwise->len);
+ return &bitwise->len;
+ case NFTNL_EXPR_BITWISE_MASK:
+ *data_len = bitwise->mask.len;
+ return &bitwise->mask.val;
+ case NFTNL_EXPR_BITWISE_XOR:
+ *data_len = bitwise->xor.len;
+ return &bitwise->xor.val;
+ case NFTNL_EXPR_BITWISE_DATA:
+ *data_len = bitwise->data.len;
+ return &bitwise->data.val;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_bitwise_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_BITWISE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_BITWISE_SREG:
+ case NFTA_BITWISE_DREG:
+ case NFTA_BITWISE_OP:
+ case NFTA_BITWISE_LEN:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_BITWISE_MASK:
+ case NFTA_BITWISE_XOR:
+ case NFTA_BITWISE_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_bitwise_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_SREG))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_SREG, htonl(bitwise->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_DREG))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_DREG, htonl(bitwise->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_OP))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_OP, htonl(bitwise->op));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_LEN))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_LEN, htonl(bitwise->len));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_MASK)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_MASK);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->mask.len,
+ bitwise->mask.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_XOR)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_XOR);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->xor.len,
+ bitwise->xor.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->data.len,
+ bitwise->data.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int
+nftnl_expr_bitwise_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_BITWISE_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_bitwise_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_BITWISE_SREG]) {
+ bitwise->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_SREG);
+ }
+ if (tb[NFTA_BITWISE_DREG]) {
+ bitwise->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_DREG);
+ }
+ if (tb[NFTA_BITWISE_OP]) {
+ bitwise->op = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_OP]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_OP);
+ }
+ if (tb[NFTA_BITWISE_LEN]) {
+ bitwise->len = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_LEN);
+ }
+ if (tb[NFTA_BITWISE_MASK]) {
+ ret = nftnl_parse_data(&bitwise->mask, tb[NFTA_BITWISE_MASK], NULL);
+ e->flags |= (1 << NFTA_BITWISE_MASK);
+ }
+ if (tb[NFTA_BITWISE_XOR]) {
+ ret = nftnl_parse_data(&bitwise->xor, tb[NFTA_BITWISE_XOR], NULL);
+ e->flags |= (1 << NFTA_BITWISE_XOR);
+ }
+ if (tb[NFTA_BITWISE_DATA]) {
+ ret = nftnl_parse_data(&bitwise->data, tb[NFTA_BITWISE_DATA], NULL);
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_DATA);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
+ const struct nftnl_expr_bitwise *bitwise)
+{
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u = ( reg %u & ",
+ bitwise->dreg, bitwise->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->mask,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, ") ^ ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->xor,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static int
+nftnl_expr_bitwise_snprintf_shift(char *buf, size_t remain, const char *op,
+ const struct nftnl_expr_bitwise *bitwise)
+{ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u = ( reg %u %s ",
+ bitwise->dreg, bitwise->sreg, op);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->data,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, ") ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static int
+nftnl_expr_bitwise_snprintf(char *buf, size_t size,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+ int err = -1;
+
+ switch (bitwise->op) {
+ case NFT_BITWISE_BOOL:
+ err = nftnl_expr_bitwise_snprintf_bool(buf, size, bitwise);
+ break;
+ case NFT_BITWISE_LSHIFT:
+ err = nftnl_expr_bitwise_snprintf_shift(buf, size, "<<", bitwise);
+ break;
+ case NFT_BITWISE_RSHIFT:
+ err = nftnl_expr_bitwise_snprintf_shift(buf, size, ">>", bitwise);
+ break;
+ }
+
+ return err;
+}
+
+struct expr_ops expr_ops_bitwise = {
+ .name = "bitwise",
+ .alloc_len = sizeof(struct nftnl_expr_bitwise),
+ .max_attr = NFTA_BITWISE_MAX,
+ .set = nftnl_expr_bitwise_set,
+ .get = nftnl_expr_bitwise_get,
+ .parse = nftnl_expr_bitwise_parse,
+ .build = nftnl_expr_bitwise_build,
+ .output = nftnl_expr_bitwise_snprintf,
+};
diff --git a/src/expr/byteorder.c b/src/expr/byteorder.c
new file mode 100644
index 0000000..89ed0a8
--- /dev/null
+++ b/src/expr/byteorder.c
@@ -0,0 +1,224 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_byteorder {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ enum nft_byteorder_ops op;
+ unsigned int len;
+ unsigned int size;
+};
+
+static int
+nftnl_expr_byteorder_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BYTEORDER_SREG:
+ memcpy(&byteorder->sreg, data, sizeof(byteorder->sreg));
+ break;
+ case NFTNL_EXPR_BYTEORDER_DREG:
+ memcpy(&byteorder->dreg, data, sizeof(byteorder->dreg));
+ break;
+ case NFTNL_EXPR_BYTEORDER_OP:
+ memcpy(&byteorder->op, data, sizeof(byteorder->op));
+ break;
+ case NFTNL_EXPR_BYTEORDER_LEN:
+ memcpy(&byteorder->len, data, sizeof(byteorder->len));
+ break;
+ case NFTNL_EXPR_BYTEORDER_SIZE:
+ memcpy(&byteorder->size, data, sizeof(byteorder->size));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_byteorder_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BYTEORDER_SREG:
+ *data_len = sizeof(byteorder->sreg);
+ return &byteorder->sreg;
+ case NFTNL_EXPR_BYTEORDER_DREG:
+ *data_len = sizeof(byteorder->dreg);
+ return &byteorder->dreg;
+ case NFTNL_EXPR_BYTEORDER_OP:
+ *data_len = sizeof(byteorder->op);
+ return &byteorder->op;
+ case NFTNL_EXPR_BYTEORDER_LEN:
+ *data_len = sizeof(byteorder->len);
+ return &byteorder->len;
+ case NFTNL_EXPR_BYTEORDER_SIZE:
+ *data_len = sizeof(byteorder->size);
+ return &byteorder->size;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_byteorder_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_BYTEORDER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_BYTEORDER_SREG:
+ case NFTA_BYTEORDER_DREG:
+ case NFTA_BYTEORDER_OP:
+ case NFTA_BYTEORDER_LEN:
+ case NFTA_BYTEORDER_SIZE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_byteorder_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_SREG)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_SREG,
+ htonl(byteorder->sreg));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_DREG)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_DREG,
+ htonl(byteorder->dreg));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_OP)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_OP,
+ htonl(byteorder->op));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_LEN)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_LEN,
+ htonl(byteorder->len));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_SIZE)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_SIZE,
+ htonl(byteorder->size));
+ }
+}
+
+static int
+nftnl_expr_byteorder_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_BYTEORDER_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_byteorder_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_BYTEORDER_SREG]) {
+ byteorder->sreg =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_SREG);
+ }
+ if (tb[NFTA_BYTEORDER_DREG]) {
+ byteorder->dreg =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_DREG);
+ }
+ if (tb[NFTA_BYTEORDER_OP]) {
+ byteorder->op =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_OP]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_OP);
+ }
+ if (tb[NFTA_BYTEORDER_LEN]) {
+ byteorder->len =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_LEN);
+ }
+ if (tb[NFTA_BYTEORDER_SIZE]) {
+ byteorder->size =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_SIZE]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_SIZE);
+ }
+
+ return ret;
+}
+
+static const char *expr_byteorder_str[] = {
+ [NFT_BYTEORDER_HTON] = "hton",
+ [NFT_BYTEORDER_NTOH] = "ntoh",
+};
+
+static const char *bo2str(uint32_t type)
+{
+ if (type > NFT_BYTEORDER_HTON)
+ return "unknown";
+
+ return expr_byteorder_str[type];
+}
+
+static inline int nftnl_str2ntoh(const char *op)
+{
+ if (strcmp(op, "ntoh") == 0)
+ return NFT_BYTEORDER_NTOH;
+ else if (strcmp(op, "hton") == 0)
+ return NFT_BYTEORDER_HTON;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int
+nftnl_expr_byteorder_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u = %s(reg %u, %u, %u) ",
+ byteorder->dreg, bo2str(byteorder->op),
+ byteorder->sreg, byteorder->size, byteorder->len);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_byteorder = {
+ .name = "byteorder",
+ .alloc_len = sizeof(struct nftnl_expr_byteorder),
+ .max_attr = NFTA_BYTEORDER_MAX,
+ .set = nftnl_expr_byteorder_set,
+ .get = nftnl_expr_byteorder_get,
+ .parse = nftnl_expr_byteorder_parse,
+ .build = nftnl_expr_byteorder_build,
+ .output = nftnl_expr_byteorder_snprintf,
+};
diff --git a/src/expr/cmp.c b/src/expr/cmp.c
new file mode 100644
index 0000000..f9d15bb
--- /dev/null
+++ b/src/expr/cmp.c
@@ -0,0 +1,206 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_cmp {
+ union nftnl_data_reg data;
+ enum nft_registers sreg;
+ enum nft_cmp_ops op;
+};
+
+static int
+nftnl_expr_cmp_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CMP_SREG:
+ memcpy(&cmp->sreg, data, sizeof(cmp->sreg));
+ break;
+ case NFTNL_EXPR_CMP_OP:
+ memcpy(&cmp->op, data, sizeof(cmp->op));
+ break;
+ case NFTNL_EXPR_CMP_DATA:
+ memcpy(&cmp->data.val, data, data_len);
+ cmp->data.len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_cmp_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CMP_SREG:
+ *data_len = sizeof(cmp->sreg);
+ return &cmp->sreg;
+ case NFTNL_EXPR_CMP_OP:
+ *data_len = sizeof(cmp->op);
+ return &cmp->op;
+ case NFTNL_EXPR_CMP_DATA:
+ *data_len = cmp->data.len;
+ return &cmp->data.val;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_cmp_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_CMP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CMP_SREG:
+ case NFTA_CMP_OP:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CMP_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_cmp_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CMP_SREG))
+ mnl_attr_put_u32(nlh, NFTA_CMP_SREG, htonl(cmp->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_CMP_OP))
+ mnl_attr_put_u32(nlh, NFTA_CMP_OP, htonl(cmp->op));
+ if (e->flags & (1 << NFTNL_EXPR_CMP_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CMP_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, cmp->data.len, cmp->data.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int
+nftnl_expr_cmp_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_CMP_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_cmp_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CMP_SREG]) {
+ cmp->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_SREG]));
+ e->flags |= (1 << NFTA_CMP_SREG);
+ }
+ if (tb[NFTA_CMP_OP]) {
+ cmp->op = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_OP]));
+ e->flags |= (1 << NFTA_CMP_OP);
+ }
+ if (tb[NFTA_CMP_DATA]) {
+ ret = nftnl_parse_data(&cmp->data, tb[NFTA_CMP_DATA], NULL);
+ e->flags |= (1 << NFTA_CMP_DATA);
+ }
+
+ return ret;
+}
+
+static const char *expr_cmp_str[] = {
+ [NFT_CMP_EQ] = "eq",
+ [NFT_CMP_NEQ] = "neq",
+ [NFT_CMP_LT] = "lt",
+ [NFT_CMP_LTE] = "lte",
+ [NFT_CMP_GT] = "gt",
+ [NFT_CMP_GTE] = "gte",
+};
+
+static const char *cmp2str(uint32_t op)
+{
+ if (op > NFT_CMP_GTE)
+ return "unknown";
+
+ return expr_cmp_str[op];
+}
+
+static inline int nftnl_str2cmp(const char *op)
+{
+ if (strcmp(op, "eq") == 0)
+ return NFT_CMP_EQ;
+ else if (strcmp(op, "neq") == 0)
+ return NFT_CMP_NEQ;
+ else if (strcmp(op, "lt") == 0)
+ return NFT_CMP_LT;
+ else if (strcmp(op, "lte") == 0)
+ return NFT_CMP_LTE;
+ else if (strcmp(op, "gt") == 0)
+ return NFT_CMP_GT;
+ else if (strcmp(op, "gte") == 0)
+ return NFT_CMP_GTE;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int
+nftnl_expr_cmp_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "%s reg %u ",
+ cmp2str(cmp->op), cmp->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &cmp->data,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_cmp = {
+ .name = "cmp",
+ .alloc_len = sizeof(struct nftnl_expr_cmp),
+ .max_attr = NFTA_CMP_MAX,
+ .set = nftnl_expr_cmp_set,
+ .get = nftnl_expr_cmp_get,
+ .parse = nftnl_expr_cmp_parse,
+ .build = nftnl_expr_cmp_build,
+ .output = nftnl_expr_cmp_snprintf,
+};
diff --git a/src/expr/connlimit.c b/src/expr/connlimit.c
new file mode 100644
index 0000000..549417b
--- /dev/null
+++ b/src/expr/connlimit.c
@@ -0,0 +1,139 @@
+/*
+ * (C) 2018 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_connlimit {
+ uint32_t count;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_connlimit_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CONNLIMIT_COUNT:
+ memcpy(&connlimit->count, data, sizeof(connlimit->count));
+ break;
+ case NFTNL_EXPR_CONNLIMIT_FLAGS:
+ memcpy(&connlimit->flags, data, sizeof(connlimit->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_connlimit_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CONNLIMIT_COUNT:
+ *data_len = sizeof(connlimit->count);
+ return &connlimit->count;
+ case NFTNL_EXPR_CONNLIMIT_FLAGS:
+ *data_len = sizeof(connlimit->flags);
+ return &connlimit->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_connlimit_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_CONNLIMIT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CONNLIMIT_COUNT:
+ case NFTA_CONNLIMIT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_connlimit_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CONNLIMIT_COUNT))
+ mnl_attr_put_u32(nlh, NFTA_CONNLIMIT_COUNT,
+ htonl(connlimit->count));
+ if (e->flags & (1 << NFTNL_EXPR_CONNLIMIT_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_CONNLIMIT_FLAGS,
+ htonl(connlimit->flags));
+}
+
+static int
+nftnl_expr_connlimit_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_CONNLIMIT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_connlimit_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CONNLIMIT_COUNT]) {
+ connlimit->count =
+ ntohl(mnl_attr_get_u32(tb[NFTA_CONNLIMIT_COUNT]));
+ e->flags |= (1 << NFTNL_EXPR_CONNLIMIT_COUNT);
+ }
+ if (tb[NFTA_CONNLIMIT_FLAGS]) {
+ connlimit->flags =
+ ntohl(mnl_attr_get_u32(tb[NFTA_CONNLIMIT_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_CONNLIMIT_FLAGS);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_connlimit_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "count %u flags %x ",
+ connlimit->count, connlimit->flags);
+}
+
+struct expr_ops expr_ops_connlimit = {
+ .name = "connlimit",
+ .alloc_len = sizeof(struct nftnl_expr_connlimit),
+ .max_attr = NFTA_CONNLIMIT_MAX,
+ .set = nftnl_expr_connlimit_set,
+ .get = nftnl_expr_connlimit_get,
+ .parse = nftnl_expr_connlimit_parse,
+ .build = nftnl_expr_connlimit_build,
+ .output = nftnl_expr_connlimit_snprintf,
+};
diff --git a/src/expr/counter.c b/src/expr/counter.c
new file mode 100644
index 0000000..d139a5f
--- /dev/null
+++ b/src/expr/counter.c
@@ -0,0 +1,137 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_counter {
+ uint64_t pkts;
+ uint64_t bytes;
+};
+
+static int
+nftnl_expr_counter_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CTR_BYTES:
+ memcpy(&ctr->bytes, data, sizeof(ctr->bytes));
+ break;
+ case NFTNL_EXPR_CTR_PACKETS:
+ memcpy(&ctr->pkts, data, sizeof(ctr->pkts));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_counter_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CTR_BYTES:
+ *data_len = sizeof(ctr->bytes);
+ return &ctr->bytes;
+ case NFTNL_EXPR_CTR_PACKETS:
+ *data_len = sizeof(ctr->pkts);
+ return &ctr->pkts;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_counter_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_COUNTER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_COUNTER_BYTES:
+ case NFTA_COUNTER_PACKETS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_counter_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CTR_BYTES))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes));
+ if (e->flags & (1 << NFTNL_EXPR_CTR_PACKETS))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts));
+}
+
+static int
+nftnl_expr_counter_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_COUNTER_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_counter_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_COUNTER_BYTES]) {
+ ctr->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES]));
+ e->flags |= (1 << NFTNL_EXPR_CTR_BYTES);
+ }
+ if (tb[NFTA_COUNTER_PACKETS]) {
+ ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS]));
+ e->flags |= (1 << NFTNL_EXPR_CTR_PACKETS);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_counter_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "pkts %"PRIu64" bytes %"PRIu64" ",
+ ctr->pkts, ctr->bytes);
+}
+
+struct expr_ops expr_ops_counter = {
+ .name = "counter",
+ .alloc_len = sizeof(struct nftnl_expr_counter),
+ .max_attr = NFTA_COUNTER_MAX,
+ .set = nftnl_expr_counter_set,
+ .get = nftnl_expr_counter_get,
+ .parse = nftnl_expr_counter_parse,
+ .build = nftnl_expr_counter_build,
+ .output = nftnl_expr_counter_snprintf,
+};
diff --git a/src/expr/ct.c b/src/expr/ct.c
new file mode 100644
index 0000000..f4a2aea
--- /dev/null
+++ b/src/expr/ct.c
@@ -0,0 +1,262 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_ct {
+ enum nft_ct_keys key;
+ enum nft_registers dreg;
+ enum nft_registers sreg;
+ uint8_t dir;
+};
+
+#define IP_CT_DIR_ORIGINAL 0
+#define IP_CT_DIR_REPLY 1
+
+static int
+nftnl_expr_ct_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CT_KEY:
+ memcpy(&ct->key, data, sizeof(ct->key));
+ break;
+ case NFTNL_EXPR_CT_DIR:
+ memcpy(&ct->dir, data, sizeof(ct->dir));
+ break;
+ case NFTNL_EXPR_CT_DREG:
+ memcpy(&ct->dreg, data, sizeof(ct->dreg));
+ break;
+ case NFTNL_EXPR_CT_SREG:
+ memcpy(&ct->sreg, data, sizeof(ct->sreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_ct_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CT_KEY:
+ *data_len = sizeof(ct->key);
+ return &ct->key;
+ case NFTNL_EXPR_CT_DIR:
+ *data_len = sizeof(ct->dir);
+ return &ct->dir;
+ case NFTNL_EXPR_CT_DREG:
+ *data_len = sizeof(ct->dreg);
+ return &ct->dreg;
+ case NFTNL_EXPR_CT_SREG:
+ *data_len = sizeof(ct->sreg);
+ return &ct->sreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_ct_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_CT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CT_KEY:
+ case NFTA_CT_DREG:
+ case NFTA_CT_SREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_DIRECTION:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_ct_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CT_KEY))
+ mnl_attr_put_u32(nlh, NFTA_CT_KEY, htonl(ct->key));
+ if (e->flags & (1 << NFTNL_EXPR_CT_DREG))
+ mnl_attr_put_u32(nlh, NFTA_CT_DREG, htonl(ct->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_CT_DIR))
+ mnl_attr_put_u8(nlh, NFTA_CT_DIRECTION, ct->dir);
+ if (e->flags & (1 << NFTNL_EXPR_CT_SREG))
+ mnl_attr_put_u32(nlh, NFTA_CT_SREG, htonl(ct->sreg));
+}
+
+static int
+nftnl_expr_ct_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_CT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_ct_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CT_KEY]) {
+ ct->key = ntohl(mnl_attr_get_u32(tb[NFTA_CT_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_CT_KEY);
+ }
+ if (tb[NFTA_CT_DREG]) {
+ ct->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_CT_DREG);
+ }
+ if (tb[NFTA_CT_SREG]) {
+ ct->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_CT_SREG);
+ }
+ if (tb[NFTA_CT_DIRECTION]) {
+ ct->dir = mnl_attr_get_u8(tb[NFTA_CT_DIRECTION]);
+ e->flags |= (1 << NFTNL_EXPR_CT_DIR);
+ }
+
+ return 0;
+}
+
+static const char *ctkey2str_array[NFT_CT_MAX + 1] = {
+ [NFT_CT_STATE] = "state",
+ [NFT_CT_DIRECTION] = "direction",
+ [NFT_CT_STATUS] = "status",
+ [NFT_CT_MARK] = "mark",
+ [NFT_CT_SECMARK] = "secmark",
+ [NFT_CT_EXPIRATION] = "expiration",
+ [NFT_CT_HELPER] = "helper",
+ [NFT_CT_L3PROTOCOL] = "l3protocol",
+ [NFT_CT_PROTOCOL] = "protocol",
+ [NFT_CT_SRC] = "src",
+ [NFT_CT_DST] = "dst",
+ [NFT_CT_PROTO_SRC] = "proto_src",
+ [NFT_CT_PROTO_DST] = "proto_dst",
+ [NFT_CT_LABELS] = "label",
+ [NFT_CT_PKTS] = "packets",
+ [NFT_CT_BYTES] = "bytes",
+ [NFT_CT_AVGPKT] = "avgpkt",
+ [NFT_CT_ZONE] = "zone",
+ [NFT_CT_EVENTMASK] = "event",
+ [NFT_CT_SRC_IP] = "src_ip",
+ [NFT_CT_DST_IP] = "dst_ip",
+ [NFT_CT_SRC_IP6] = "src_ip6",
+ [NFT_CT_DST_IP6] = "dst_ip6",
+ [NFT_CT_ID] = "id",
+};
+
+static const char *ctkey2str(uint32_t ctkey)
+{
+ if (ctkey >= NFT_CT_MAX)
+ return "unknown";
+
+ return ctkey2str_array[ctkey];
+}
+
+static inline int str2ctkey(const char *ctkey)
+{
+ int i;
+
+ for (i = 0; i < NFT_CT_MAX; i++) {
+ if (strcmp(ctkey2str_array[i], ctkey) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+static const char *ctdir2str(uint8_t ctdir)
+{
+ switch (ctdir) {
+ case IP_CT_DIR_ORIGINAL:
+ return "original";
+ case IP_CT_DIR_REPLY:
+ return "reply";
+ default:
+ return "unknown";
+ }
+}
+
+static inline int str2ctdir(const char *str, uint8_t *ctdir)
+{
+ if (strcmp(str, "original") == 0) {
+ *ctdir = IP_CT_DIR_ORIGINAL;
+ return 0;
+ }
+
+ if (strcmp(str, "reply") == 0) {
+ *ctdir = IP_CT_DIR_REPLY;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+nftnl_expr_ct_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_CT_SREG)) {
+ ret = snprintf(buf, remain, "set %s with reg %u ",
+ ctkey2str(ct->key), ct->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_CT_DREG)) {
+ ret = snprintf(buf, remain, "load %s => reg %u ",
+ ctkey2str(ct->key), ct->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_CT_DIR)) {
+ ret = snprintf(buf + offset, remain, ", dir %s ",
+ ctdir2str(ct->dir));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_ct = {
+ .name = "ct",
+ .alloc_len = sizeof(struct nftnl_expr_ct),
+ .max_attr = NFTA_CT_MAX,
+ .set = nftnl_expr_ct_set,
+ .get = nftnl_expr_ct_get,
+ .parse = nftnl_expr_ct_parse,
+ .build = nftnl_expr_ct_build,
+ .output = nftnl_expr_ct_snprintf,
+};
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
new file mode 100644
index 0000000..2633a77
--- /dev/null
+++ b/src/expr/data_reg.c
@@ -0,0 +1,219 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+#include "internal.h"
+
+static int
+nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
+ const union nftnl_data_reg *reg,
+ uint32_t flags)
+{
+ const char *pfx = flags & DATA_F_NOPFX ? "" : "0x";
+ int offset = 0, ret, i;
+
+
+
+ for (i = 0; i < div_round_up(reg->len, sizeof(uint32_t)); i++) {
+ ret = snprintf(buf + offset, remain,
+ "%s%.8x ", pfx, reg->val[i]);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static int
+nftnl_data_reg_verdict_snprintf_def(char *buf, size_t size,
+ const union nftnl_data_reg *reg,
+ uint32_t flags)
+{
+ int remain = size, offset = 0, ret = 0;
+
+ ret = snprintf(buf, size, "%s ", nftnl_verdict2str(reg->verdict));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (reg->chain != NULL) {
+ ret = snprintf(buf + offset, remain, "-> %s ", reg->chain);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+int nftnl_data_reg_snprintf(char *buf, size_t size,
+ const union nftnl_data_reg *reg,
+ uint32_t flags, int reg_type)
+{
+ switch(reg_type) {
+ case DATA_VALUE:
+ return nftnl_data_reg_value_snprintf_default(buf, size,
+ reg, flags);
+ case DATA_VERDICT:
+ case DATA_CHAIN:
+ return nftnl_data_reg_verdict_snprintf_def(buf, size,
+ reg, flags);
+ default:
+ return -1;
+ }
+}
+
+static int nftnl_data_parse_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_DATA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_DATA_VALUE:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DATA_VERDICT:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_verdict_parse_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_VERDICT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_VERDICT_CODE:
+ case NFTA_VERDICT_CHAIN_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_VERDICT_CHAIN:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_parse_verdict(union nftnl_data_reg *data, const struct nlattr *attr, int *type)
+{
+ struct nlattr *tb[NFTA_VERDICT_MAX+1];
+
+ if (mnl_attr_parse_nested(attr, nftnl_verdict_parse_cb, tb) < 0)
+ return -1;
+
+ if (!tb[NFTA_VERDICT_CODE])
+ return -1;
+
+ data->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE]));
+
+ switch(data->verdict) {
+ case NF_ACCEPT:
+ case NF_DROP:
+ case NF_QUEUE:
+ case NFT_CONTINUE:
+ case NFT_BREAK:
+ case NFT_RETURN:
+ if (type)
+ *type = DATA_VERDICT;
+ data->len = sizeof(data->verdict);
+ break;
+ case NFT_JUMP:
+ case NFT_GOTO:
+ if (!tb[NFTA_VERDICT_CHAIN])
+ return -1;
+
+ data->chain = strdup(mnl_attr_get_str(tb[NFTA_VERDICT_CHAIN]));
+ if (!data->chain)
+ return -1;
+
+ if (type)
+ *type = DATA_CHAIN;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+__nftnl_parse_data(union nftnl_data_reg *data, const struct nlattr *attr)
+{
+ void *orig = mnl_attr_get_payload(attr);
+ uint32_t data_len = mnl_attr_get_payload_len(attr);
+
+ if (data_len == 0)
+ return -1;
+
+ if (data_len > sizeof(data->val))
+ return -1;
+
+ memcpy(data->val, orig, data_len);
+ data->len = data_len;
+
+ return 0;
+}
+
+int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type)
+{
+ struct nlattr *tb[NFTA_DATA_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_data_parse_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_DATA_VALUE]) {
+ if (type)
+ *type = DATA_VALUE;
+
+ ret = __nftnl_parse_data(data, tb[NFTA_DATA_VALUE]);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_DATA_VERDICT])
+ ret = nftnl_parse_verdict(data, tb[NFTA_DATA_VERDICT], type);
+
+ return ret;
+}
+
+void nftnl_free_verdict(const union nftnl_data_reg *data)
+{
+ switch(data->verdict) {
+ case NFT_JUMP:
+ case NFT_GOTO:
+ xfree(data->chain);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/expr/dup.c b/src/expr/dup.c
new file mode 100644
index 0000000..a239ff3
--- /dev/null
+++ b/src/expr/dup.c
@@ -0,0 +1,142 @@
+/*
+ * (C) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+#include "expr_ops.h"
+#include "data_reg.h"
+
+struct nftnl_expr_dup {
+ enum nft_registers sreg_addr;
+ enum nft_registers sreg_dev;
+};
+
+static int nftnl_expr_dup_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_DUP_SREG_ADDR:
+ memcpy(&dup->sreg_addr, data, sizeof(dup->sreg_addr));
+ break;
+ case NFTNL_EXPR_DUP_SREG_DEV:
+ memcpy(&dup->sreg_dev, data, sizeof(dup->sreg_dev));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_dup_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_DUP_SREG_ADDR:
+ *data_len = sizeof(dup->sreg_addr);
+ return &dup->sreg_addr;
+ case NFTNL_EXPR_DUP_SREG_DEV:
+ *data_len = sizeof(dup->sreg_dev);
+ return &dup->sreg_dev;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_dup_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_DUP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_DUP_SREG_ADDR:
+ case NFTA_DUP_SREG_DEV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_dup_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR))
+ mnl_attr_put_u32(nlh, NFTA_DUP_SREG_ADDR, htonl(dup->sreg_addr));
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV))
+ mnl_attr_put_u32(nlh, NFTA_DUP_SREG_DEV, htonl(dup->sreg_dev));
+}
+
+static int nftnl_expr_dup_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_DUP_MAX + 1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_dup_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_DUP_SREG_ADDR]) {
+ dup->sreg_addr = ntohl(mnl_attr_get_u32(tb[NFTA_DUP_SREG_ADDR]));
+ e->flags |= (1 << NFTNL_EXPR_DUP_SREG_ADDR);
+ }
+ if (tb[NFTA_DUP_SREG_DEV]) {
+ dup->sreg_dev = ntohl(mnl_attr_get_u32(tb[NFTA_DUP_SREG_DEV]));
+ e->flags |= (1 << NFTNL_EXPR_DUP_SREG_DEV);
+ }
+
+ return ret;
+}
+
+static int nftnl_expr_dup_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR)) {
+ ret = snprintf(buf + offset, remain, "sreg_addr %u ", dup->sreg_addr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV)) {
+ ret = snprintf(buf + offset, remain, "sreg_dev %u ", dup->sreg_dev);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_dup = {
+ .name = "dup",
+ .alloc_len = sizeof(struct nftnl_expr_dup),
+ .max_attr = NFTA_DUP_MAX,
+ .set = nftnl_expr_dup_set,
+ .get = nftnl_expr_dup_get,
+ .parse = nftnl_expr_dup_parse,
+ .build = nftnl_expr_dup_build,
+ .output = nftnl_expr_dup_snprintf,
+};
diff --git a/src/expr/dynset.c b/src/expr/dynset.c
new file mode 100644
index 0000000..5bcf1c6
--- /dev/null
+++ b/src/expr/dynset.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2014, 2015 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+#include "data_reg.h"
+#include "expr_ops.h"
+
+struct nftnl_expr_dynset {
+ enum nft_registers sreg_key;
+ enum nft_registers sreg_data;
+ enum nft_dynset_ops op;
+ uint64_t timeout;
+ struct list_head expr_list;
+ char *set_name;
+ uint32_t set_id;
+ uint32_t dynset_flags;
+};
+
+static int
+nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr, *next;
+
+ switch (type) {
+ case NFTNL_EXPR_DYNSET_SREG_KEY:
+ memcpy(&dynset->sreg_key, data, sizeof(dynset->sreg_key));
+ break;
+ case NFTNL_EXPR_DYNSET_SREG_DATA:
+ memcpy(&dynset->sreg_data, data, sizeof(dynset->sreg_data));
+ break;
+ case NFTNL_EXPR_DYNSET_OP:
+ memcpy(&dynset->op, data, sizeof(dynset->op));
+ break;
+ case NFTNL_EXPR_DYNSET_TIMEOUT:
+ memcpy(&dynset->timeout, data, sizeof(dynset->timeout));
+ break;
+ case NFTNL_EXPR_DYNSET_SET_NAME:
+ dynset->set_name = strdup((const char *)data);
+ if (!dynset->set_name)
+ return -1;
+ break;
+ case NFTNL_EXPR_DYNSET_SET_ID:
+ memcpy(&dynset->set_id, data, sizeof(dynset->set_id));
+ break;
+ case NFTNL_EXPR_DYNSET_EXPR:
+ list_for_each_entry_safe(expr, next, &dynset->expr_list, head)
+ nftnl_expr_free(expr);
+
+ expr = (void *)data;
+ list_add(&expr->head, &dynset->expr_list);
+ break;
+ case NFTNL_EXPR_DYNSET_FLAGS:
+ memcpy(&dynset->dynset_flags, data, sizeof(dynset->dynset_flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr;
+
+ switch (type) {
+ case NFTNL_EXPR_DYNSET_SREG_KEY:
+ *data_len = sizeof(dynset->sreg_key);
+ return &dynset->sreg_key;
+ case NFTNL_EXPR_DYNSET_SREG_DATA:
+ *data_len = sizeof(dynset->sreg_data);
+ return &dynset->sreg_data;
+ case NFTNL_EXPR_DYNSET_OP:
+ *data_len = sizeof(dynset->op);
+ return &dynset->op;
+ case NFTNL_EXPR_DYNSET_TIMEOUT:
+ *data_len = sizeof(dynset->timeout);
+ return &dynset->timeout;
+ case NFTNL_EXPR_DYNSET_SET_NAME:
+ *data_len = strlen(dynset->set_name) + 1;
+ return dynset->set_name;
+ case NFTNL_EXPR_DYNSET_SET_ID:
+ *data_len = sizeof(dynset->set_id);
+ return &dynset->set_id;
+ case NFTNL_EXPR_DYNSET_EXPR:
+ list_for_each_entry(expr, &dynset->expr_list, head)
+ break;
+ return expr;
+ case NFTNL_EXPR_DYNSET_FLAGS:
+ *data_len = sizeof(dynset->dynset_flags);
+ return &dynset->dynset_flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_DYNSET_SREG_KEY:
+ case NFTA_DYNSET_SREG_DATA:
+ case NFTA_DYNSET_SET_ID:
+ case NFTA_DYNSET_OP:
+ case NFTA_DYNSET_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DYNSET_TIMEOUT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DYNSET_SET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DYNSET_EXPR:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nlattr *nest;
+ int num_exprs = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_KEY, htonl(dynset->sreg_key));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_DATA, htonl(dynset->sreg_data));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_OP))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_OP, htonl(dynset->op));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT))
+ mnl_attr_put_u64(nlh, NFTA_DYNSET_TIMEOUT, htobe64(dynset->timeout));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME))
+ mnl_attr_put_strz(nlh, NFTA_DYNSET_SET_NAME, dynset->set_name);
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_SET_ID, htonl(dynset->set_id));
+ if (!list_empty(&dynset->expr_list)) {
+ struct nftnl_expr *expr;
+
+ list_for_each_entry(expr, &dynset->expr_list, head)
+ num_exprs++;
+
+ if (num_exprs == 1) {
+ nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR);
+ list_for_each_entry(expr, &dynset->expr_list, head)
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest);
+ } else if (num_exprs > 1) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPRESSIONS);
+ list_for_each_entry(expr, &dynset->expr_list, head) {
+ nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ }
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_FLAGS,
+ htonl(dynset->dynset_flags));
+}
+
+EXPORT_SYMBOL(nftnl_expr_add_expr);
+void nftnl_expr_add_expr(struct nftnl_expr *e, uint32_t type,
+ struct nftnl_expr *expr)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+
+ list_add_tail(&expr->head, &dynset->expr_list);
+}
+
+EXPORT_SYMBOL(nftnl_expr_expr_foreach);
+int nftnl_expr_expr_foreach(const struct nftnl_expr *e,
+ int (*cb)(struct nftnl_expr *e, void *data),
+ void *data)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &dynset->expr_list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int
+nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_SET_MAX+1] = {};
+ struct nftnl_expr *expr, *next;
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_DYNSET_SREG_KEY]) {
+ dynset->sreg_key = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_KEY);
+ }
+ if (tb[NFTA_DYNSET_SREG_DATA]) {
+ dynset->sreg_data = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_DATA]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_DATA);
+ }
+ if (tb[NFTA_DYNSET_OP]) {
+ dynset->op = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_OP]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_OP);
+ }
+ if (tb[NFTA_DYNSET_TIMEOUT]) {
+ dynset->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_DYNSET_TIMEOUT]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_TIMEOUT);
+ }
+ if (tb[NFTA_DYNSET_SET_NAME]) {
+ dynset->set_name =
+ strdup(mnl_attr_get_str(tb[NFTA_DYNSET_SET_NAME]));
+ if (!dynset->set_name)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_NAME);
+ }
+ if (tb[NFTA_DYNSET_SET_ID]) {
+ dynset->set_id = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SET_ID]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_ID);
+ }
+ if (tb[NFTA_DYNSET_EXPR]) {
+ expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]);
+ if (expr == NULL)
+ return -1;
+
+ list_add(&expr->head, &dynset->expr_list);
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR);
+ } else if (tb[NFTA_DYNSET_EXPRESSIONS]) {
+ struct nlattr *attr2;
+
+ mnl_attr_for_each_nested(attr2, tb[NFTA_DYNSET_EXPRESSIONS]) {
+ if (mnl_attr_get_type(attr2) != NFTA_LIST_ELEM)
+ goto out_dynset_expr;
+
+ expr = nftnl_expr_parse(attr2);
+ if (!expr)
+ goto out_dynset_expr;
+
+ list_add_tail(&expr->head, &dynset->expr_list);
+ }
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPRESSIONS);
+ }
+ if (tb[NFTA_DYNSET_FLAGS]) {
+ dynset->dynset_flags = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_FLAGS);
+ }
+
+ return ret;
+out_dynset_expr:
+ list_for_each_entry_safe(expr, next, &dynset->expr_list, head)
+ nftnl_expr_free(expr);
+
+ return -1;
+}
+
+static const char *op2str_array[] = {
+ [NFT_DYNSET_OP_ADD] = "add",
+ [NFT_DYNSET_OP_UPDATE] = "update",
+ [NFT_DYNSET_OP_DELETE] = "delete",
+};
+
+static const char *op2str(enum nft_dynset_ops op)
+{
+ if (op > NFT_DYNSET_OP_DELETE)
+ return "unknown";
+ return op2str_array[op];
+}
+
+static int
+nftnl_expr_dynset_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr;
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "%s reg_key %u set %s ",
+ op2str(dynset->op), dynset->sreg_key, dynset->set_name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) {
+ ret = snprintf(buf + offset, remain, "sreg_data %u ",
+ dynset->sreg_data);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) {
+ ret = snprintf(buf + offset, remain, "timeout %"PRIu64"ms ",
+ dynset->timeout);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ list_for_each_entry(expr, &dynset->expr_list, head) {
+ ret = snprintf(buf + offset, remain, "expr [ %s ",
+ expr->ops->name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_expr_snprintf(buf + offset, remain, expr,
+ NFTNL_OUTPUT_DEFAULT,
+ NFTNL_OF_EVENT_ANY);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "] ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_dynset_init(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+
+ INIT_LIST_HEAD(&dynset->expr_list);
+}
+
+static void nftnl_expr_dynset_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr, *next;
+
+ xfree(dynset->set_name);
+ list_for_each_entry_safe(expr, next, &dynset->expr_list, head)
+ nftnl_expr_free(expr);
+}
+
+struct expr_ops expr_ops_dynset = {
+ .name = "dynset",
+ .alloc_len = sizeof(struct nftnl_expr_dynset),
+ .max_attr = NFTA_DYNSET_MAX,
+ .init = nftnl_expr_dynset_init,
+ .free = nftnl_expr_dynset_free,
+ .set = nftnl_expr_dynset_set,
+ .get = nftnl_expr_dynset_get,
+ .parse = nftnl_expr_dynset_parse,
+ .build = nftnl_expr_dynset_build,
+ .output = nftnl_expr_dynset_snprintf,
+};
diff --git a/src/expr/exthdr.c b/src/expr/exthdr.c
new file mode 100644
index 0000000..739c7ff
--- /dev/null
+++ b/src/expr/exthdr.c
@@ -0,0 +1,271 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+#ifndef IPPROTO_MH
+#define IPPROTO_MH 135
+#endif
+
+struct nftnl_expr_exthdr {
+ enum nft_registers dreg;
+ enum nft_registers sreg;
+ uint32_t offset;
+ uint32_t len;
+ uint8_t type;
+ uint32_t op;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_exthdr_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_EXTHDR_DREG:
+ memcpy(&exthdr->dreg, data, sizeof(exthdr->dreg));
+ break;
+ case NFTNL_EXPR_EXTHDR_TYPE:
+ memcpy(&exthdr->type, data, sizeof(exthdr->type));
+ break;
+ case NFTNL_EXPR_EXTHDR_OFFSET:
+ memcpy(&exthdr->offset, data, sizeof(exthdr->offset));
+ break;
+ case NFTNL_EXPR_EXTHDR_LEN:
+ memcpy(&exthdr->len, data, sizeof(exthdr->len));
+ break;
+ case NFTNL_EXPR_EXTHDR_OP:
+ memcpy(&exthdr->op, data, sizeof(exthdr->op));
+ break;
+ case NFTNL_EXPR_EXTHDR_FLAGS:
+ memcpy(&exthdr->flags, data, sizeof(exthdr->flags));
+ break;
+ case NFTNL_EXPR_EXTHDR_SREG:
+ memcpy(&exthdr->sreg, data, sizeof(exthdr->sreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_exthdr_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_EXTHDR_DREG:
+ *data_len = sizeof(exthdr->dreg);
+ return &exthdr->dreg;
+ case NFTNL_EXPR_EXTHDR_TYPE:
+ *data_len = sizeof(exthdr->type);
+ return &exthdr->type;
+ case NFTNL_EXPR_EXTHDR_OFFSET:
+ *data_len = sizeof(exthdr->offset);
+ return &exthdr->offset;
+ case NFTNL_EXPR_EXTHDR_LEN:
+ *data_len = sizeof(exthdr->len);
+ return &exthdr->len;
+ case NFTNL_EXPR_EXTHDR_OP:
+ *data_len = sizeof(exthdr->op);
+ return &exthdr->op;
+ case NFTNL_EXPR_EXTHDR_FLAGS:
+ *data_len = sizeof(exthdr->flags);
+ return &exthdr->flags;
+ case NFTNL_EXPR_EXTHDR_SREG:
+ *data_len = sizeof(exthdr->sreg);
+ return &exthdr->sreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_exthdr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_EXTHDR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_EXTHDR_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ case NFTA_EXTHDR_DREG:
+ case NFTA_EXTHDR_SREG:
+ case NFTA_EXTHDR_OFFSET:
+ case NFTA_EXTHDR_LEN:
+ case NFTA_EXTHDR_OP:
+ case NFTA_EXTHDR_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_exthdr_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_DREG, htonl(exthdr->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_SREG, htonl(exthdr->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_TYPE))
+ mnl_attr_put_u8(nlh, NFTA_EXTHDR_TYPE, exthdr->type);
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_LEN))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_LEN, htonl(exthdr->len));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OP))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_OP, htonl(exthdr->op));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_FLAGS, htonl(exthdr->flags));
+}
+
+static int
+nftnl_expr_exthdr_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_EXTHDR_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_exthdr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_EXTHDR_DREG]) {
+ exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_DREG);
+ }
+ if (tb[NFTA_EXTHDR_SREG]) {
+ exthdr->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_SREG);
+ }
+ if (tb[NFTA_EXTHDR_TYPE]) {
+ exthdr->type = mnl_attr_get_u8(tb[NFTA_EXTHDR_TYPE]);
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_TYPE);
+ }
+ if (tb[NFTA_EXTHDR_OFFSET]) {
+ exthdr->offset = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_OFFSET);
+ }
+ if (tb[NFTA_EXTHDR_LEN]) {
+ exthdr->len = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_LEN);
+ }
+ if (tb[NFTA_EXTHDR_OP]) {
+ exthdr->op = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OP]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_OP);
+ }
+ if (tb[NFTA_EXTHDR_FLAGS]) {
+ exthdr->flags = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_FLAGS);
+ }
+
+ return 0;
+}
+
+static const char *op2str(uint8_t op)
+{
+ switch (op) {
+ case NFT_EXTHDR_OP_TCPOPT:
+ return " tcpopt";
+ case NFT_EXTHDR_OP_IPV6:
+ return " ipv6";
+ case NFT_EXTHDR_OP_IPV4:
+ return " ipv4";
+ default:
+ return "";
+ }
+}
+
+static inline int str2exthdr_op(const char* str)
+{
+ if (!strcmp(str, "tcpopt"))
+ return NFT_EXTHDR_OP_TCPOPT;
+ if (!strcmp(str, "ipv4"))
+ return NFT_EXTHDR_OP_IPV4;
+
+ /* if str == "ipv6" or anything else */
+ return NFT_EXTHDR_OP_IPV6;
+}
+
+static inline int str2exthdr_type(const char *str)
+{
+ if (strcmp(str, "hopopts") == 0)
+ return IPPROTO_HOPOPTS;
+ else if (strcmp(str, "routing") == 0)
+ return IPPROTO_ROUTING;
+ else if (strcmp(str, "fragment") == 0)
+ return IPPROTO_FRAGMENT;
+ else if (strcmp(str, "dstopts") == 0)
+ return IPPROTO_DSTOPTS;
+ else if (strcmp(str, "mh") == 0)
+ return IPPROTO_MH;
+
+ return -1;
+}
+
+static int
+nftnl_expr_exthdr_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
+ return snprintf(buf, len, "load%s %ub @ %u + %u%s => reg %u ",
+ op2str(exthdr->op), exthdr->len, exthdr->type,
+ exthdr->offset,
+ exthdr->flags & NFT_EXTHDR_F_PRESENT ? " present" : "",
+ exthdr->dreg);
+ else if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
+ return snprintf(buf, len, "write%s reg %u => %ub @ %u + %u ",
+ op2str(exthdr->op), exthdr->sreg, exthdr->len, exthdr->type,
+ exthdr->offset);
+ else if (exthdr->op == NFT_EXTHDR_OP_TCPOPT && exthdr->len == 0)
+ return snprintf(buf, len, "reset tcpopt %u ", exthdr->type);
+ else
+ return snprintf(buf, len, "op %u len %u type %u offset %u ",
+ exthdr->op, exthdr->len, exthdr->type, exthdr->offset);
+
+}
+
+struct expr_ops expr_ops_exthdr = {
+ .name = "exthdr",
+ .alloc_len = sizeof(struct nftnl_expr_exthdr),
+ .max_attr = NFTA_EXTHDR_MAX,
+ .set = nftnl_expr_exthdr_set,
+ .get = nftnl_expr_exthdr_get,
+ .parse = nftnl_expr_exthdr_parse,
+ .build = nftnl_expr_exthdr_build,
+ .output = nftnl_expr_exthdr_snprintf,
+};
diff --git a/src/expr/fib.c b/src/expr/fib.c
new file mode 100644
index 0000000..957f929
--- /dev/null
+++ b/src/expr/fib.c
@@ -0,0 +1,202 @@
+/*
+ * (C) 2016 Red Hat GmbH
+ * Author: Florian Westphal <fw@strlen.de>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_fib {
+ uint32_t flags;
+ uint32_t result;
+ enum nft_registers dreg;
+};
+
+static int
+nftnl_expr_fib_set(struct nftnl_expr *e, uint16_t result,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ switch (result) {
+ case NFTNL_EXPR_FIB_RESULT:
+ memcpy(&fib->result, data, sizeof(fib->result));
+ break;
+ case NFTNL_EXPR_FIB_DREG:
+ memcpy(&fib->dreg, data, sizeof(fib->dreg));
+ break;
+ case NFTNL_EXPR_FIB_FLAGS:
+ memcpy(&fib->flags, data, sizeof(fib->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_fib_get(const struct nftnl_expr *e, uint16_t result,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ switch (result) {
+ case NFTNL_EXPR_FIB_RESULT:
+ *data_len = sizeof(fib->result);
+ return &fib->result;
+ case NFTNL_EXPR_FIB_DREG:
+ *data_len = sizeof(fib->dreg);
+ return &fib->dreg;
+ case NFTNL_EXPR_FIB_FLAGS:
+ *data_len = sizeof(fib->flags);
+ return &fib->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_fib_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FIB_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_FIB_RESULT:
+ case NFTA_FIB_DREG:
+ case NFTA_FIB_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_fib_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_FIB_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_FIB_FLAGS, htonl(fib->flags));
+ if (e->flags & (1 << NFTNL_EXPR_FIB_RESULT))
+ mnl_attr_put_u32(nlh, NFTA_FIB_RESULT, htonl(fib->result));
+ if (e->flags & (1 << NFTNL_EXPR_FIB_DREG))
+ mnl_attr_put_u32(nlh, NFTA_FIB_DREG, htonl(fib->dreg));
+}
+
+static int
+nftnl_expr_fib_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_FIB_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_fib_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FIB_RESULT]) {
+ fib->result = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_RESULT]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_RESULT);
+ }
+ if (tb[NFTA_FIB_DREG]) {
+ fib->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_DREG);
+ }
+ if (tb[NFTA_FIB_FLAGS]) {
+ fib->flags = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_FLAGS);
+ }
+ return ret;
+}
+
+static const char *fib_type[NFT_FIB_RESULT_MAX + 1] = {
+ [NFT_FIB_RESULT_OIF] = "oif",
+ [NFT_FIB_RESULT_OIFNAME] = "oifname",
+ [NFT_FIB_RESULT_ADDRTYPE] = "type",
+};
+
+static const char *fib_type_str(enum nft_fib_result r)
+{
+ if (r <= NFT_FIB_RESULT_MAX)
+ return fib_type[r];
+
+ return "unknown";
+}
+
+static int
+nftnl_expr_fib_snprintf(char *buf, size_t remain,
+ uint32_t printflags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+ uint32_t flags = fib->flags & ~NFTA_FIB_F_PRESENT;
+ uint32_t present_flag = fib->flags & NFTA_FIB_F_PRESENT;
+ int offset = 0, ret, i;
+ static const struct {
+ int bit;
+ const char *name;
+ } tab[] = {
+ { NFTA_FIB_F_SADDR, "saddr" },
+ { NFTA_FIB_F_DADDR, "daddr" },
+ { NFTA_FIB_F_MARK, "mark" },
+ { NFTA_FIB_F_IIF, "iif" },
+ { NFTA_FIB_F_OIF, "oif" },
+ };
+
+ for (i = 0; i < (sizeof(tab) / sizeof(tab[0])); i++) {
+ if (flags & tab[i].bit) {
+ ret = snprintf(buf + offset, remain, "%s ",
+ tab[i].name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ flags &= ~tab[i].bit;
+ if (flags) {
+ ret = snprintf(buf + offset, remain, ". ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+ }
+
+ if (flags) {
+ ret = snprintf(buf + offset, remain, "unknown 0x%" PRIx32,
+ flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain, "%s%s => reg %d ",
+ fib_type_str(fib->result),
+ present_flag ? " present" : "",
+ fib->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_fib = {
+ .name = "fib",
+ .alloc_len = sizeof(struct nftnl_expr_fib),
+ .max_attr = NFTA_FIB_MAX,
+ .set = nftnl_expr_fib_set,
+ .get = nftnl_expr_fib_get,
+ .parse = nftnl_expr_fib_parse,
+ .build = nftnl_expr_fib_build,
+ .output = nftnl_expr_fib_snprintf,
+};
diff --git a/src/expr/flow_offload.c b/src/expr/flow_offload.c
new file mode 100644
index 0000000..4fc0563
--- /dev/null
+++ b/src/expr/flow_offload.c
@@ -0,0 +1,124 @@
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+struct nftnl_expr_flow {
+ char *table_name;
+};
+
+static int nftnl_expr_flow_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_FLOW_TABLE_NAME:
+ flow->table_name = strdup((const char *)data);
+ if (!flow->table_name)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_flow_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_FLOW_TABLE_NAME:
+ *data_len = strlen(flow->table_name) + 1;
+ return flow->table_name;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_flow_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FLOW_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_FLOW_TABLE_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_flow_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_FLOW_TABLE_NAME))
+ mnl_attr_put_strz(nlh, NFTA_FLOW_TABLE_NAME, flow->table_name);
+}
+
+static int nftnl_expr_flow_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_FLOW_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_flow_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FLOW_TABLE_NAME]) {
+ flow->table_name =
+ strdup(mnl_attr_get_str(tb[NFTA_FLOW_TABLE_NAME]));
+ if (!flow->table_name)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_FLOW_TABLE_NAME);
+ }
+
+ return ret;
+}
+
+static int nftnl_expr_flow_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_flow *l = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "flowtable %s ", l->table_name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static void nftnl_expr_flow_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ xfree(flow->table_name);
+}
+
+struct expr_ops expr_ops_flow = {
+ .name = "flow_offload",
+ .alloc_len = sizeof(struct nftnl_expr_flow),
+ .max_attr = NFTA_FLOW_MAX,
+ .free = nftnl_expr_flow_free,
+ .set = nftnl_expr_flow_set,
+ .get = nftnl_expr_flow_get,
+ .parse = nftnl_expr_flow_parse,
+ .build = nftnl_expr_flow_build,
+ .output = nftnl_expr_flow_snprintf,
+};
diff --git a/src/expr/fwd.c b/src/expr/fwd.c
new file mode 100644
index 0000000..51f6612
--- /dev/null
+++ b/src/expr/fwd.c
@@ -0,0 +1,162 @@
+/*
+ * (C) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+#include "expr_ops.h"
+#include "data_reg.h"
+
+struct nftnl_expr_fwd {
+ enum nft_registers sreg_dev;
+ enum nft_registers sreg_addr;
+ uint32_t nfproto;
+};
+
+static int nftnl_expr_fwd_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_FWD_SREG_DEV:
+ memcpy(&fwd->sreg_dev, data, sizeof(fwd->sreg_dev));
+ break;
+ case NFTNL_EXPR_FWD_SREG_ADDR:
+ memcpy(&fwd->sreg_addr, data, sizeof(fwd->sreg_addr));
+ break;
+ case NFTNL_EXPR_FWD_NFPROTO:
+ memcpy(&fwd->nfproto, data, sizeof(fwd->nfproto));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_fwd_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_FWD_SREG_DEV:
+ *data_len = sizeof(fwd->sreg_dev);
+ return &fwd->sreg_dev;
+ case NFTNL_EXPR_FWD_SREG_ADDR:
+ *data_len = sizeof(fwd->sreg_addr);
+ return &fwd->sreg_addr;
+ case NFTNL_EXPR_FWD_NFPROTO:
+ *data_len = sizeof(fwd->nfproto);
+ return &fwd->nfproto;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_fwd_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FWD_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_FWD_SREG_DEV:
+ case NFTA_FWD_SREG_ADDR:
+ case NFTA_FWD_NFPROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_fwd_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV))
+ mnl_attr_put_u32(nlh, NFTA_FWD_SREG_DEV, htonl(fwd->sreg_dev));
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR))
+ mnl_attr_put_u32(nlh, NFTA_FWD_SREG_ADDR, htonl(fwd->sreg_addr));
+ if (e->flags & (1 << NFTNL_EXPR_FWD_NFPROTO))
+ mnl_attr_put_u32(nlh, NFTA_FWD_NFPROTO, htonl(fwd->nfproto));
+}
+
+static int nftnl_expr_fwd_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_FWD_MAX + 1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_fwd_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FWD_SREG_DEV]) {
+ fwd->sreg_dev = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_SREG_DEV]));
+ e->flags |= (1 << NFTNL_EXPR_FWD_SREG_DEV);
+ }
+ if (tb[NFTA_FWD_SREG_ADDR]) {
+ fwd->sreg_addr = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_SREG_ADDR]));
+ e->flags |= (1 << NFTNL_EXPR_FWD_SREG_ADDR);
+ }
+ if (tb[NFTA_FWD_NFPROTO]) {
+ fwd->nfproto = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_NFPROTO]));
+ e->flags |= (1 << NFTNL_EXPR_FWD_NFPROTO);
+ }
+
+ return ret;
+}
+
+static int nftnl_expr_fwd_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV)) {
+ ret = snprintf(buf + offset, remain, "sreg_dev %u ",
+ fwd->sreg_dev);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR)) {
+ ret = snprintf(buf + offset, remain, "sreg_addr %u ",
+ fwd->sreg_addr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_FWD_NFPROTO)) {
+ ret = snprintf(buf + offset, remain, "nfproto %u ",
+ fwd->nfproto);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_fwd = {
+ .name = "fwd",
+ .alloc_len = sizeof(struct nftnl_expr_fwd),
+ .max_attr = NFTA_FWD_MAX,
+ .set = nftnl_expr_fwd_set,
+ .get = nftnl_expr_fwd_get,
+ .parse = nftnl_expr_fwd_parse,
+ .build = nftnl_expr_fwd_build,
+ .output = nftnl_expr_fwd_snprintf,
+};
diff --git a/src/expr/hash.c b/src/expr/hash.c
new file mode 100644
index 0000000..6e2dd19
--- /dev/null
+++ b/src/expr/hash.c
@@ -0,0 +1,230 @@
+/*
+ * (C) 2016 by Laura Garcia <nevola@gmail.com>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_hash {
+ enum nft_hash_types type;
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ unsigned int len;
+ unsigned int modulus;
+ unsigned int seed;
+ unsigned int offset;
+};
+
+static int
+nftnl_expr_hash_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+ switch (type) {
+ case NFTNL_EXPR_HASH_SREG:
+ memcpy(&hash->sreg, data, sizeof(hash->sreg));
+ break;
+ case NFTNL_EXPR_HASH_DREG:
+ memcpy(&hash->dreg, data, sizeof(hash->dreg));
+ break;
+ case NFTNL_EXPR_HASH_LEN:
+ memcpy(&hash->len, data, sizeof(hash->len));
+ break;
+ case NFTNL_EXPR_HASH_MODULUS:
+ memcpy(&hash->modulus, data, sizeof(hash->modulus));
+ break;
+ case NFTNL_EXPR_HASH_SEED:
+ memcpy(&hash->seed, data, sizeof(hash->seed));
+ break;
+ case NFTNL_EXPR_HASH_OFFSET:
+ memcpy(&hash->offset, data, sizeof(hash->offset));
+ break;
+ case NFTNL_EXPR_HASH_TYPE:
+ memcpy(&hash->type, data, sizeof(hash->type));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_hash_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_HASH_SREG:
+ *data_len = sizeof(hash->sreg);
+ return &hash->sreg;
+ case NFTNL_EXPR_HASH_DREG:
+ *data_len = sizeof(hash->dreg);
+ return &hash->dreg;
+ case NFTNL_EXPR_HASH_LEN:
+ *data_len = sizeof(hash->len);
+ return &hash->len;
+ case NFTNL_EXPR_HASH_MODULUS:
+ *data_len = sizeof(hash->modulus);
+ return &hash->modulus;
+ case NFTNL_EXPR_HASH_SEED:
+ *data_len = sizeof(hash->seed);
+ return &hash->seed;
+ case NFTNL_EXPR_HASH_OFFSET:
+ *data_len = sizeof(hash->offset);
+ return &hash->offset;
+ case NFTNL_EXPR_HASH_TYPE:
+ *data_len = sizeof(hash->type);
+ return &hash->type;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_hash_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_HASH_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_HASH_SREG:
+ case NFTA_HASH_DREG:
+ case NFTA_HASH_LEN:
+ case NFTA_HASH_MODULUS:
+ case NFTA_HASH_SEED:
+ case NFTA_HASH_OFFSET:
+ case NFTA_HASH_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_hash_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_HASH_SREG))
+ mnl_attr_put_u32(nlh, NFTA_HASH_SREG, htonl(hash->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_DREG))
+ mnl_attr_put_u32(nlh, NFTA_HASH_DREG, htonl(hash->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_LEN))
+ mnl_attr_put_u32(nlh, NFTA_HASH_LEN, htonl(hash->len));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_MODULUS))
+ mnl_attr_put_u32(nlh, NFTA_HASH_MODULUS, htonl(hash->modulus));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_SEED))
+ mnl_attr_put_u32(nlh, NFTA_HASH_SEED, htonl(hash->seed));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_HASH_OFFSET, htonl(hash->offset));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_HASH_TYPE, htonl(hash->type));
+}
+
+static int
+nftnl_expr_hash_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_HASH_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_hash_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_HASH_SREG]) {
+ hash->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_SREG);
+ }
+ if (tb[NFTA_HASH_DREG]) {
+ hash->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_DREG);
+ }
+ if (tb[NFTA_HASH_LEN]) {
+ hash->len = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_LEN);
+ }
+ if (tb[NFTA_HASH_MODULUS]) {
+ hash->modulus = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_MODULUS]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_MODULUS);
+ }
+ if (tb[NFTA_HASH_SEED]) {
+ hash->seed = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_SEED]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_SEED);
+ }
+ if (tb[NFTA_HASH_OFFSET]) {
+ hash->offset = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_OFFSET);
+ }
+ if (tb[NFTA_HASH_TYPE]) {
+ hash->type = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_TYPE);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_hash_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ switch (hash->type) {
+ case NFT_HASH_SYM:
+ ret =
+ snprintf(buf, remain, "reg %u = symhash() %% mod %u ",
+ hash->dreg,
+ hash->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ case NFT_HASH_JENKINS:
+ default:
+ ret =
+ snprintf(buf, remain,
+ "reg %u = jhash(reg %u, %u, 0x%x) %% mod %u ",
+ hash->dreg, hash->sreg, hash->len, hash->seed,
+ hash->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ }
+
+ if (hash->offset) {
+ ret = snprintf(buf + offset, remain, "offset %u ",
+ hash->offset);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_hash = {
+ .name = "hash",
+ .alloc_len = sizeof(struct nftnl_expr_hash),
+ .max_attr = NFTA_HASH_MAX,
+ .set = nftnl_expr_hash_set,
+ .get = nftnl_expr_hash_get,
+ .parse = nftnl_expr_hash_parse,
+ .build = nftnl_expr_hash_build,
+ .output = nftnl_expr_hash_snprintf,
+};
diff --git a/src/expr/immediate.c b/src/expr/immediate.c
new file mode 100644
index 0000000..5d477a8
--- /dev/null
+++ b/src/expr/immediate.c
@@ -0,0 +1,233 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_immediate {
+ union nftnl_data_reg data;
+ enum nft_registers dreg;
+};
+
+static int
+nftnl_expr_immediate_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_IMM_DREG:
+ memcpy(&imm->dreg, data, sizeof(imm->dreg));
+ break;
+ case NFTNL_EXPR_IMM_DATA:
+ memcpy(&imm->data.val, data, data_len);
+ imm->data.len = data_len;
+ break;
+ case NFTNL_EXPR_IMM_VERDICT:
+ memcpy(&imm->data.verdict, data, sizeof(imm->data.verdict));
+ break;
+ case NFTNL_EXPR_IMM_CHAIN:
+ if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN))
+ xfree(imm->data.chain);
+
+ imm->data.chain = strdup(data);
+ if (!imm->data.chain)
+ return -1;
+ break;
+ case NFTNL_EXPR_IMM_CHAIN_ID:
+ memcpy(&imm->data.chain_id, data, sizeof(uint32_t));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_immediate_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_IMM_DREG:
+ *data_len = sizeof(imm->dreg);
+ return &imm->dreg;
+ case NFTNL_EXPR_IMM_DATA:
+ *data_len = imm->data.len;
+ return &imm->data.val;
+ case NFTNL_EXPR_IMM_VERDICT:
+ *data_len = sizeof(imm->data.verdict);
+ return &imm->data.verdict;
+ case NFTNL_EXPR_IMM_CHAIN:
+ *data_len = strlen(imm->data.chain)+1;
+ return imm->data.chain;
+ case NFTNL_EXPR_IMM_CHAIN_ID:
+ *data_len = sizeof(imm->data.chain_id);
+ return &imm->data.chain_id;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_immediate_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_IMMEDIATE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_IMMEDIATE_DREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_IMMEDIATE_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_immediate_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_IMM_DREG))
+ mnl_attr_put_u32(nlh, NFTA_IMMEDIATE_DREG, htonl(imm->dreg));
+
+ /* Sane configurations allows you to set ONLY one of these two below */
+ if (e->flags & (1 << NFTNL_EXPR_IMM_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, imm->data.len, imm->data.val);
+ mnl_attr_nest_end(nlh, nest);
+
+ } else if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA);
+ nest2 = mnl_attr_nest_start(nlh, NFTA_DATA_VERDICT);
+ mnl_attr_put_u32(nlh, NFTA_VERDICT_CODE, htonl(imm->data.verdict));
+ if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN))
+ mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, imm->data.chain);
+ if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN_ID)) {
+ mnl_attr_put_u32(nlh, NFTA_VERDICT_CHAIN_ID,
+ htonl(imm->data.chain_id));
+ }
+
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+}
+
+static int
+nftnl_expr_immediate_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_IMMEDIATE_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_immediate_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_IMMEDIATE_DREG]) {
+ imm->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_IMMEDIATE_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_IMM_DREG);
+ }
+ if (tb[NFTA_IMMEDIATE_DATA]) {
+ int type;
+
+ ret = nftnl_parse_data(&imm->data, tb[NFTA_IMMEDIATE_DATA], &type);
+ if (ret < 0)
+ return ret;
+
+ switch(type) {
+ case DATA_VALUE:
+ /* real immediate data to be loaded to destination */
+ e->flags |= (1 << NFTNL_EXPR_IMM_DATA);
+ break;
+ case DATA_VERDICT:
+ /* NF_ACCEPT, NF_DROP, NF_QUEUE and NFTNL_RETURN case */
+ e->flags |= (1 << NFTNL_EXPR_IMM_VERDICT);
+ break;
+ case DATA_CHAIN:
+ /* NFTNL_GOTO and NFTNL_JUMP case */
+ e->flags |= (1 << NFTNL_EXPR_IMM_VERDICT) |
+ (1 << NFTNL_EXPR_IMM_CHAIN);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_immediate_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u ", imm->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_IMM_DATA)) {
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
+ flags, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ } else if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) {
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
+ flags, DATA_VERDICT);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ } else if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN)) {
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
+ flags, DATA_CHAIN);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_immediate_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT))
+ nftnl_free_verdict(&imm->data);
+}
+
+struct expr_ops expr_ops_immediate = {
+ .name = "immediate",
+ .alloc_len = sizeof(struct nftnl_expr_immediate),
+ .max_attr = NFTA_IMMEDIATE_MAX,
+ .free = nftnl_expr_immediate_free,
+ .set = nftnl_expr_immediate_set,
+ .get = nftnl_expr_immediate_get,
+ .parse = nftnl_expr_immediate_parse,
+ .build = nftnl_expr_immediate_build,
+ .output = nftnl_expr_immediate_snprintf,
+};
diff --git a/src/expr/inner.c b/src/expr/inner.c
new file mode 100644
index 0000000..7daae4f
--- /dev/null
+++ b/src/expr/inner.c
@@ -0,0 +1,214 @@
+/*
+ * (C) 2012-2022 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_inner {
+ uint32_t type;
+ uint32_t flags;
+ uint32_t hdrsize;
+ struct nftnl_expr *expr;
+};
+
+static void nftnl_expr_inner_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+ if (inner->expr)
+ nftnl_expr_free(inner->expr);
+}
+
+static int
+nftnl_expr_inner_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_INNER_TYPE:
+ memcpy(&inner->type, data, sizeof(inner->type));
+ break;
+ case NFTNL_EXPR_INNER_FLAGS:
+ memcpy(&inner->flags, data, sizeof(inner->flags));
+ break;
+ case NFTNL_EXPR_INNER_HDRSIZE:
+ memcpy(&inner->hdrsize, data, sizeof(inner->hdrsize));
+ break;
+ case NFTNL_EXPR_INNER_EXPR:
+ if (inner->expr)
+ nftnl_expr_free(inner->expr);
+
+ inner->expr = (void *)data;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_inner_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_INNER_FLAGS:
+ *data_len = sizeof(inner->flags);
+ return &inner->flags;
+ case NFTNL_EXPR_INNER_TYPE:
+ *data_len = sizeof(inner->type);
+ return &inner->type;
+ case NFTNL_EXPR_INNER_HDRSIZE:
+ *data_len = sizeof(inner->hdrsize);
+ return &inner->hdrsize;
+ case NFTNL_EXPR_INNER_EXPR:
+ return inner->expr;
+ }
+ return NULL;
+}
+
+static void
+nftnl_expr_inner_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+ struct nlattr *nest;
+
+ mnl_attr_put_u32(nlh, NFTA_INNER_NUM, htonl(0));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_INNER_TYPE, htonl(inner->type));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_INNER_FLAGS, htonl(inner->flags));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_HDRSIZE))
+ mnl_attr_put_u32(nlh, NFTA_INNER_HDRSIZE, htonl(inner->hdrsize));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_EXPR)) {
+ nest = mnl_attr_nest_start(nlh, NFTA_INNER_EXPR);
+ nftnl_expr_build_payload(nlh, inner->expr);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int nftnl_inner_parse_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_INNER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_INNER_NUM:
+ case NFTA_INNER_TYPE:
+ case NFTA_INNER_HDRSIZE:
+ case NFTA_INNER_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_INNER_EXPR:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_expr_inner_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_INNER_MAX + 1] = {};
+ struct nftnl_expr *expr;
+ int err;
+
+ err = mnl_attr_parse_nested(attr, nftnl_inner_parse_cb, tb);
+ if (err < 0)
+ return err;
+
+ if (tb[NFTA_INNER_HDRSIZE]) {
+ inner->hdrsize =
+ ntohl(mnl_attr_get_u32(tb[NFTA_INNER_HDRSIZE]));
+ e->flags |= (1 << NFTNL_EXPR_INNER_HDRSIZE);
+ }
+ if (tb[NFTA_INNER_FLAGS]) {
+ inner->flags =
+ ntohl(mnl_attr_get_u32(tb[NFTA_INNER_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_INNER_FLAGS);
+ }
+ if (tb[NFTA_INNER_TYPE]) {
+ inner->type =
+ ntohl(mnl_attr_get_u32(tb[NFTA_INNER_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_INNER_TYPE);
+ }
+ if (tb[NFTA_INNER_EXPR]) {
+ expr = nftnl_expr_parse(tb[NFTA_INNER_EXPR]);
+ if (!expr)
+ return -1;
+
+ if (inner->expr)
+ nftnl_expr_free(inner->expr);
+
+ inner->expr = expr;
+ e->flags |= (1 << NFTNL_EXPR_INNER_EXPR);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_inner_snprintf(char *buf, size_t remain, uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+ uint32_t offset = 0;
+ int ret;
+
+ ret = snprintf(buf, remain, "type %u hdrsize %u flags %x [",
+ inner->type, inner->hdrsize, inner->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, " %s ", inner->expr->ops->name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_expr_snprintf(buf + offset, remain, inner->expr,
+ NFTNL_OUTPUT_DEFAULT, 0);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "] ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_inner = {
+ .name = "inner",
+ .alloc_len = sizeof(struct nftnl_expr_inner),
+ .max_attr = NFTA_INNER_MAX,
+ .free = nftnl_expr_inner_free,
+ .set = nftnl_expr_inner_set,
+ .get = nftnl_expr_inner_get,
+ .parse = nftnl_expr_inner_parse,
+ .build = nftnl_expr_inner_build,
+ .output = nftnl_expr_inner_snprintf,
+};
diff --git a/src/expr/last.c b/src/expr/last.c
new file mode 100644
index 0000000..641b713
--- /dev/null
+++ b/src/expr/last.c
@@ -0,0 +1,138 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_last {
+ uint64_t msecs;
+ uint32_t set;
+};
+
+static int nftnl_expr_last_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_LAST_MSECS:
+ memcpy(&last->msecs, data, sizeof(last->msecs));
+ break;
+ case NFTNL_EXPR_LAST_SET:
+ memcpy(&last->set, data, sizeof(last->set));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_last_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_LAST_MSECS:
+ *data_len = sizeof(last->msecs);
+ return &last->msecs;
+ case NFTNL_EXPR_LAST_SET:
+ *data_len = sizeof(last->set);
+ return &last->set;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_last_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_LAST_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LAST_MSECS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LAST_SET:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_last_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LAST_MSECS))
+ mnl_attr_put_u64(nlh, NFTA_LAST_MSECS, htobe64(last->msecs));
+ if (e->flags & (1 << NFTNL_EXPR_LAST_SET))
+ mnl_attr_put_u32(nlh, NFTA_LAST_SET, htonl(last->set));
+}
+
+static int
+nftnl_expr_last_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LAST_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_last_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LAST_MSECS]) {
+ last->msecs = be64toh(mnl_attr_get_u64(tb[NFTA_LAST_MSECS]));
+ e->flags |= (1 << NFTNL_EXPR_LAST_MSECS);
+ }
+ if (tb[NFTA_LAST_SET]) {
+ last->set = ntohl(mnl_attr_get_u32(tb[NFTA_LAST_SET]));
+ e->flags |= (1 << NFTNL_EXPR_LAST_SET);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_last_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ if (!last->set)
+ return snprintf(buf, len, "never ");
+
+ return snprintf(buf, len, "%"PRIu64" ", last->msecs);
+}
+
+struct expr_ops expr_ops_last = {
+ .name = "last",
+ .alloc_len = sizeof(struct nftnl_expr_last),
+ .max_attr = NFTA_LAST_MAX,
+ .set = nftnl_expr_last_set,
+ .get = nftnl_expr_last_get,
+ .parse = nftnl_expr_last_parse,
+ .build = nftnl_expr_last_build,
+ .output = nftnl_expr_last_snprintf,
+};
diff --git a/src/expr/limit.c b/src/expr/limit.c
new file mode 100644
index 0000000..1870e0e
--- /dev/null
+++ b/src/expr/limit.c
@@ -0,0 +1,206 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_limit {
+ uint64_t rate;
+ uint64_t unit;
+ uint32_t burst;
+ enum nft_limit_type type;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_limit_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LIMIT_RATE:
+ memcpy(&limit->rate, data, sizeof(limit->rate));
+ break;
+ case NFTNL_EXPR_LIMIT_UNIT:
+ memcpy(&limit->unit, data, sizeof(limit->unit));
+ break;
+ case NFTNL_EXPR_LIMIT_BURST:
+ memcpy(&limit->burst, data, sizeof(limit->burst));
+ break;
+ case NFTNL_EXPR_LIMIT_TYPE:
+ memcpy(&limit->type, data, sizeof(limit->type));
+ break;
+ case NFTNL_EXPR_LIMIT_FLAGS:
+ memcpy(&limit->flags, data, sizeof(limit->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_limit_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LIMIT_RATE:
+ *data_len = sizeof(uint64_t);
+ return &limit->rate;
+ case NFTNL_EXPR_LIMIT_UNIT:
+ *data_len = sizeof(uint64_t);
+ return &limit->unit;
+ case NFTNL_EXPR_LIMIT_BURST:
+ *data_len = sizeof(uint32_t);
+ return &limit->burst;
+ case NFTNL_EXPR_LIMIT_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &limit->type;
+ case NFTNL_EXPR_LIMIT_FLAGS:
+ *data_len = sizeof(uint32_t);
+ return &limit->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_limit_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_LIMIT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LIMIT_RATE:
+ case NFTA_LIMIT_UNIT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LIMIT_BURST:
+ case NFTA_LIMIT_TYPE:
+ case NFTA_LIMIT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_limit_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_RATE))
+ mnl_attr_put_u64(nlh, NFTA_LIMIT_RATE, htobe64(limit->rate));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_UNIT))
+ mnl_attr_put_u64(nlh, NFTA_LIMIT_UNIT, htobe64(limit->unit));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_BURST))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_BURST, htonl(limit->burst));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_TYPE, htonl(limit->type));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_FLAGS, htonl(limit->flags));
+}
+
+static int
+nftnl_expr_limit_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LIMIT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_limit_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LIMIT_RATE]) {
+ limit->rate = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_RATE]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_RATE);
+ }
+ if (tb[NFTA_LIMIT_UNIT]) {
+ limit->unit = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_UNIT]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_UNIT);
+ }
+ if (tb[NFTA_LIMIT_BURST]) {
+ limit->burst = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_BURST]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_BURST);
+ }
+ if (tb[NFTA_LIMIT_TYPE]) {
+ limit->type = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_TYPE);
+ }
+ if (tb[NFTA_LIMIT_FLAGS]) {
+ limit->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_FLAGS);
+ }
+
+ return 0;
+}
+
+static const char *get_unit(uint64_t u)
+{
+ switch (u) {
+ case 1: return "second";
+ case 60: return "minute";
+ case 60 * 60: return "hour";
+ case 60 * 60 * 24: return "day";
+ case 60 * 60 * 24 * 7: return "week";
+ }
+ return "error";
+}
+
+static const char *limit_to_type(enum nft_limit_type type)
+{
+ switch (type) {
+ default:
+ case NFT_LIMIT_PKTS:
+ return "packets";
+ case NFT_LIMIT_PKT_BYTES:
+ return "bytes";
+ }
+}
+
+static int
+nftnl_expr_limit_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "rate %"PRIu64"/%s burst %u type %s flags 0x%x ",
+ limit->rate, get_unit(limit->unit), limit->burst,
+ limit_to_type(limit->type), limit->flags);
+}
+
+struct expr_ops expr_ops_limit = {
+ .name = "limit",
+ .alloc_len = sizeof(struct nftnl_expr_limit),
+ .max_attr = NFTA_LIMIT_MAX,
+ .set = nftnl_expr_limit_set,
+ .get = nftnl_expr_limit_get,
+ .parse = nftnl_expr_limit_parse,
+ .build = nftnl_expr_limit_build,
+ .output = nftnl_expr_limit_snprintf,
+};
diff --git a/src/expr/log.c b/src/expr/log.c
new file mode 100644
index 0000000..180d839
--- /dev/null
+++ b/src/expr/log.c
@@ -0,0 +1,257 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_log.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_log {
+ uint32_t snaplen;
+ uint16_t group;
+ uint16_t qthreshold;
+ uint32_t level;
+ uint32_t flags;
+ const char *prefix;
+};
+
+static int nftnl_expr_log_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOG_PREFIX:
+ if (log->flags & (1 << NFTNL_EXPR_LOG_PREFIX))
+ xfree(log->prefix);
+
+ log->prefix = strdup(data);
+ if (!log->prefix)
+ return -1;
+ break;
+ case NFTNL_EXPR_LOG_GROUP:
+ memcpy(&log->group, data, sizeof(log->group));
+ break;
+ case NFTNL_EXPR_LOG_SNAPLEN:
+ memcpy(&log->snaplen, data, sizeof(log->snaplen));
+ break;
+ case NFTNL_EXPR_LOG_QTHRESHOLD:
+ memcpy(&log->qthreshold, data, sizeof(log->qthreshold));
+ break;
+ case NFTNL_EXPR_LOG_LEVEL:
+ memcpy(&log->level, data, sizeof(log->level));
+ break;
+ case NFTNL_EXPR_LOG_FLAGS:
+ memcpy(&log->flags, data, sizeof(log->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_log_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOG_PREFIX:
+ *data_len = strlen(log->prefix)+1;
+ return log->prefix;
+ case NFTNL_EXPR_LOG_GROUP:
+ *data_len = sizeof(log->group);
+ return &log->group;
+ case NFTNL_EXPR_LOG_SNAPLEN:
+ *data_len = sizeof(log->snaplen);
+ return &log->snaplen;
+ case NFTNL_EXPR_LOG_QTHRESHOLD:
+ *data_len = sizeof(log->qthreshold);
+ return &log->qthreshold;
+ case NFTNL_EXPR_LOG_LEVEL:
+ *data_len = sizeof(log->level);
+ return &log->level;
+ case NFTNL_EXPR_LOG_FLAGS:
+ *data_len = sizeof(log->flags);
+ return &log->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_log_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_LOG_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LOG_PREFIX:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LOG_GROUP:
+ case NFTA_LOG_QTHRESHOLD:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LOG_SNAPLEN:
+ case NFTA_LOG_LEVEL:
+ case NFTA_LOG_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_log_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LOG_PREFIX))
+ mnl_attr_put_strz(nlh, NFTA_LOG_PREFIX, log->prefix);
+ if (e->flags & (1 << NFTNL_EXPR_LOG_GROUP))
+ mnl_attr_put_u16(nlh, NFTA_LOG_GROUP, htons(log->group));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_SNAPLEN))
+ mnl_attr_put_u32(nlh, NFTA_LOG_SNAPLEN, htonl(log->snaplen));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_QTHRESHOLD))
+ mnl_attr_put_u16(nlh, NFTA_LOG_QTHRESHOLD, htons(log->qthreshold));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_LEVEL))
+ mnl_attr_put_u32(nlh, NFTA_LOG_LEVEL, htonl(log->level));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_LOG_FLAGS, htonl(log->flags));
+}
+
+static int
+nftnl_expr_log_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LOG_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_log_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LOG_PREFIX]) {
+ if (log->prefix)
+ xfree(log->prefix);
+
+ log->prefix = strdup(mnl_attr_get_str(tb[NFTA_LOG_PREFIX]));
+ if (!log->prefix)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_LOG_PREFIX);
+ }
+ if (tb[NFTA_LOG_GROUP]) {
+ log->group = ntohs(mnl_attr_get_u16(tb[NFTA_LOG_GROUP]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_GROUP);
+ }
+ if (tb[NFTA_LOG_SNAPLEN]) {
+ log->snaplen = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_SNAPLEN]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_SNAPLEN);
+ }
+ if (tb[NFTA_LOG_QTHRESHOLD]) {
+ log->qthreshold = ntohs(mnl_attr_get_u16(tb[NFTA_LOG_QTHRESHOLD]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_QTHRESHOLD);
+ }
+ if (tb[NFTA_LOG_LEVEL]) {
+ log->level = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_LEVEL]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_LEVEL);
+ }
+ if (tb[NFTA_LOG_FLAGS]) {
+ log->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_log_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_LOG_PREFIX)) {
+ ret = snprintf(buf, remain, "prefix %s ", log->prefix);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_LOG_GROUP)) {
+ ret = snprintf(buf + offset, remain,
+ "group %u snaplen %u qthreshold %u ",
+ log->group, log->snaplen, log->qthreshold);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ } else {
+ if (e->flags & (1 << NFTNL_EXPR_LOG_LEVEL)) {
+ ret = snprintf(buf + offset, remain, "level %u ",
+ log->level);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_LOG_FLAGS)) {
+ if (log->flags & NF_LOG_TCPSEQ) {
+ ret = snprintf(buf + offset, remain, "tcpseq ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_TCPOPT) {
+ ret = snprintf(buf + offset, remain, "tcpopt ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_IPOPT) {
+ ret = snprintf(buf + offset, remain, "ipopt ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_UID) {
+ ret = snprintf(buf + offset, remain, "uid ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_MACDECODE) {
+ ret = snprintf(buf + offset, remain,
+ "macdecode ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_log_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ xfree(log->prefix);
+}
+
+struct expr_ops expr_ops_log = {
+ .name = "log",
+ .alloc_len = sizeof(struct nftnl_expr_log),
+ .max_attr = NFTA_LOG_MAX,
+ .free = nftnl_expr_log_free,
+ .set = nftnl_expr_log_set,
+ .get = nftnl_expr_log_get,
+ .parse = nftnl_expr_log_parse,
+ .build = nftnl_expr_log_build,
+ .output = nftnl_expr_log_snprintf,
+};
diff --git a/src/expr/lookup.c b/src/expr/lookup.c
new file mode 100644
index 0000000..a06c338
--- /dev/null
+++ b/src/expr/lookup.c
@@ -0,0 +1,210 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+struct nftnl_expr_lookup {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ char *set_name;
+ uint32_t set_id;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_lookup_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOOKUP_SREG:
+ memcpy(&lookup->sreg, data, sizeof(lookup->sreg));
+ break;
+ case NFTNL_EXPR_LOOKUP_DREG:
+ memcpy(&lookup->dreg, data, sizeof(lookup->dreg));
+ break;
+ case NFTNL_EXPR_LOOKUP_SET:
+ lookup->set_name = strdup((const char *)data);
+ if (!lookup->set_name)
+ return -1;
+ break;
+ case NFTNL_EXPR_LOOKUP_SET_ID:
+ memcpy(&lookup->set_id, data, sizeof(lookup->set_id));
+ break;
+ case NFTNL_EXPR_LOOKUP_FLAGS:
+ memcpy(&lookup->flags, data, sizeof(lookup->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_lookup_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOOKUP_SREG:
+ *data_len = sizeof(lookup->sreg);
+ return &lookup->sreg;
+ case NFTNL_EXPR_LOOKUP_DREG:
+ *data_len = sizeof(lookup->dreg);
+ return &lookup->dreg;
+ case NFTNL_EXPR_LOOKUP_SET:
+ *data_len = strlen(lookup->set_name) + 1;
+ return lookup->set_name;
+ case NFTNL_EXPR_LOOKUP_SET_ID:
+ *data_len = sizeof(lookup->set_id);
+ return &lookup->set_id;
+ case NFTNL_EXPR_LOOKUP_FLAGS:
+ *data_len = sizeof(lookup->flags);
+ return &lookup->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_lookup_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_LOOKUP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LOOKUP_SREG:
+ case NFTA_LOOKUP_DREG:
+ case NFTA_LOOKUP_SET_ID:
+ case NFTA_LOOKUP_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LOOKUP_SET:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_lookup_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SREG))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_SREG, htonl(lookup->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_DREG))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_DREG, htonl(lookup->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SET))
+ mnl_attr_put_strz(nlh, NFTA_LOOKUP_SET, lookup->set_name);
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_SET_ID,
+ htonl(lookup->set_id));
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_FLAGS, htonl(lookup->flags));
+}
+
+static int
+nftnl_expr_lookup_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LOOKUP_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_lookup_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LOOKUP_SREG]) {
+ lookup->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_SREG);
+ }
+ if (tb[NFTA_LOOKUP_DREG]) {
+ lookup->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_DREG);
+ }
+ if (tb[NFTA_LOOKUP_SET]) {
+ lookup->set_name =
+ strdup(mnl_attr_get_str(tb[NFTA_LOOKUP_SET]));
+ if (!lookup->set_name)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_SET);
+ }
+ if (tb[NFTA_LOOKUP_SET_ID]) {
+ lookup->set_id =
+ ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_SET_ID]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_SET_ID);
+ }
+ if (tb[NFTA_LOOKUP_FLAGS]) {
+ lookup->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_FLAGS);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_lookup_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_lookup *l = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u set %s ", l->sreg, l->set_name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_DREG)) {
+ ret = snprintf(buf + offset, remain, "dreg %u ", l->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "0x%x ", l->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_lookup_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ xfree(lookup->set_name);
+}
+
+struct expr_ops expr_ops_lookup = {
+ .name = "lookup",
+ .alloc_len = sizeof(struct nftnl_expr_lookup),
+ .max_attr = NFTA_LOOKUP_MAX,
+ .free = nftnl_expr_lookup_free,
+ .set = nftnl_expr_lookup_set,
+ .get = nftnl_expr_lookup_get,
+ .parse = nftnl_expr_lookup_parse,
+ .build = nftnl_expr_lookup_build,
+ .output = nftnl_expr_lookup_snprintf,
+};
diff --git a/src/expr/masq.c b/src/expr/masq.c
new file mode 100644
index 0000000..e6e528d
--- /dev/null
+++ b/src/expr/masq.c
@@ -0,0 +1,167 @@
+/*
+ * (C) 2014 by Arturo Borrero Gonzalez <arturo@debian.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_masq {
+ uint32_t flags;
+ enum nft_registers sreg_proto_min;
+ enum nft_registers sreg_proto_max;
+};
+
+static int
+nftnl_expr_masq_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_MASQ_FLAGS:
+ memcpy(&masq->flags, data, sizeof(masq->flags));
+ break;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MIN:
+ memcpy(&masq->sreg_proto_min, data, sizeof(masq->sreg_proto_min));
+ break;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MAX:
+ memcpy(&masq->sreg_proto_max, data, sizeof(masq->sreg_proto_max));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_masq_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_MASQ_FLAGS:
+ *data_len = sizeof(masq->flags);
+ return &masq->flags;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MIN:
+ *data_len = sizeof(masq->sreg_proto_min);
+ return &masq->sreg_proto_min;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MAX:
+ *data_len = sizeof(masq->sreg_proto_max);
+ return &masq->sreg_proto_max;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_masq_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_MASQ_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_MASQ_REG_PROTO_MIN:
+ case NFTA_MASQ_REG_PROTO_MAX:
+ case NFTA_MASQ_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_masq_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_MASQ_FLAGS, htobe32(masq->flags));
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN))
+ mnl_attr_put_u32(nlh, NFTA_MASQ_REG_PROTO_MIN,
+ htobe32(masq->sreg_proto_min));
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX))
+ mnl_attr_put_u32(nlh, NFTA_MASQ_REG_PROTO_MAX,
+ htobe32(masq->sreg_proto_max));
+}
+
+static int
+nftnl_expr_masq_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_MASQ_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_masq_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_MASQ_FLAGS]) {
+ masq->flags = be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_MASQ_FLAGS);
+ }
+ if (tb[NFTA_MASQ_REG_PROTO_MIN]) {
+ masq->sreg_proto_min =
+ be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_REG_PROTO_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN);
+ }
+ if (tb[NFTA_MASQ_REG_PROTO_MAX]) {
+ masq->sreg_proto_max =
+ be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_REG_PROTO_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_masq_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+ int offset = 0, ret = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN)) {
+ ret = snprintf(buf + offset, remain, "proto_min reg %u ",
+ masq->sreg_proto_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX)) {
+ ret = snprintf(buf + offset, remain, "proto_max reg %u ",
+ masq->sreg_proto_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "flags 0x%x ", masq->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_masq = {
+ .name = "masq",
+ .alloc_len = sizeof(struct nftnl_expr_masq),
+ .max_attr = NFTA_MASQ_MAX,
+ .set = nftnl_expr_masq_set,
+ .get = nftnl_expr_masq_get,
+ .parse = nftnl_expr_masq_parse,
+ .build = nftnl_expr_masq_build,
+ .output = nftnl_expr_masq_snprintf,
+};
diff --git a/src/expr/match.c b/src/expr/match.c
new file mode 100644
index 0000000..f472add
--- /dev/null
+++ b/src/expr/match.c
@@ -0,0 +1,193 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+/* From include/linux/netfilter/x_tables.h */
+#define XT_EXTENSION_MAXNAMELEN 29
+
+struct nftnl_expr_match {
+ char name[XT_EXTENSION_MAXNAMELEN];
+ uint32_t rev;
+ uint32_t data_len;
+ const void *data;
+};
+
+static int
+nftnl_expr_match_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_match *mt = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_MT_NAME:
+ snprintf(mt->name, sizeof(mt->name), "%.*s", data_len,
+ (const char *)data);
+ break;
+ case NFTNL_EXPR_MT_REV:
+ memcpy(&mt->rev, data, sizeof(mt->rev));
+ break;
+ case NFTNL_EXPR_MT_INFO:
+ if (e->flags & (1 << NFTNL_EXPR_MT_INFO))
+ xfree(mt->data);
+
+ mt->data = data;
+ mt->data_len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_match_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_match *mt = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_MT_NAME:
+ *data_len = sizeof(mt->name);
+ return mt->name;
+ case NFTNL_EXPR_MT_REV:
+ *data_len = sizeof(mt->rev);
+ return &mt->rev;
+ case NFTNL_EXPR_MT_INFO:
+ *data_len = mt->data_len;
+ return mt->data;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_match_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_MATCH_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_MATCH_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_MATCH_REV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_MATCH_INFO:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_match_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_match *mt = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_MT_NAME))
+ mnl_attr_put_strz(nlh, NFTA_MATCH_NAME, mt->name);
+ if (e->flags & (1 << NFTNL_EXPR_MT_REV))
+ mnl_attr_put_u32(nlh, NFTA_MATCH_REV, htonl(mt->rev));
+ if (e->flags & (1 << NFTNL_EXPR_MT_INFO))
+ mnl_attr_put(nlh, NFTA_MATCH_INFO, mt->data_len, mt->data);
+}
+
+static int nftnl_expr_match_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_match *match = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_MATCH_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_match_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_MATCH_NAME]) {
+ snprintf(match->name, XT_EXTENSION_MAXNAMELEN, "%s",
+ mnl_attr_get_str(tb[NFTA_MATCH_NAME]));
+
+ match->name[XT_EXTENSION_MAXNAMELEN-1] = '\0';
+ e->flags |= (1 << NFTNL_EXPR_MT_NAME);
+ }
+
+ if (tb[NFTA_MATCH_REV]) {
+ match->rev = ntohl(mnl_attr_get_u32(tb[NFTA_MATCH_REV]));
+ e->flags |= (1 << NFTNL_EXPR_MT_REV);
+ }
+
+ if (tb[NFTA_MATCH_INFO]) {
+ uint32_t len = mnl_attr_get_payload_len(tb[NFTA_MATCH_INFO]);
+ void *match_data;
+
+ if (e->flags & (1 << NFTNL_EXPR_MT_INFO))
+ xfree(match->data);
+
+ match_data = calloc(1, len);
+ if (match_data == NULL)
+ return -1;
+
+ memcpy(match_data, mnl_attr_get_payload(tb[NFTA_MATCH_INFO]), len);
+
+ match->data = match_data;
+ match->data_len = len;
+
+ e->flags |= (1 << NFTNL_EXPR_MT_INFO);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_match_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_match *match = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "name %s rev %u ", match->name, match->rev);
+}
+
+static void nftnl_expr_match_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_match *match = nftnl_expr_data(e);
+
+ xfree(match->data);
+}
+
+struct expr_ops expr_ops_match = {
+ .name = "match",
+ .alloc_len = sizeof(struct nftnl_expr_match),
+ .max_attr = NFTA_MATCH_MAX,
+ .free = nftnl_expr_match_free,
+ .set = nftnl_expr_match_set,
+ .get = nftnl_expr_match_get,
+ .parse = nftnl_expr_match_parse,
+ .build = nftnl_expr_match_build,
+ .output = nftnl_expr_match_snprintf,
+};
diff --git a/src/expr/meta.c b/src/expr/meta.c
new file mode 100644
index 0000000..183f441
--- /dev/null
+++ b/src/expr/meta.c
@@ -0,0 +1,221 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+#ifndef NFT_META_MAX
+#define NFT_META_MAX (NFT_META_BRI_BROUTE + 1)
+#endif
+
+struct nftnl_expr_meta {
+ enum nft_meta_keys key;
+ enum nft_registers dreg;
+ enum nft_registers sreg;
+};
+
+static int
+nftnl_expr_meta_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_META_KEY:
+ memcpy(&meta->key, data, sizeof(meta->key));
+ break;
+ case NFTNL_EXPR_META_DREG:
+ memcpy(&meta->dreg, data, sizeof(meta->dreg));
+ break;
+ case NFTNL_EXPR_META_SREG:
+ memcpy(&meta->sreg, data, sizeof(meta->sreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_meta_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_META_KEY:
+ *data_len = sizeof(meta->key);
+ return &meta->key;
+ case NFTNL_EXPR_META_DREG:
+ *data_len = sizeof(meta->dreg);
+ return &meta->dreg;
+ case NFTNL_EXPR_META_SREG:
+ *data_len = sizeof(meta->sreg);
+ return &meta->sreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_meta_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_META_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_META_KEY:
+ case NFTA_META_DREG:
+ case NFTA_META_SREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_meta_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_META_KEY))
+ mnl_attr_put_u32(nlh, NFTA_META_KEY, htonl(meta->key));
+ if (e->flags & (1 << NFTNL_EXPR_META_DREG))
+ mnl_attr_put_u32(nlh, NFTA_META_DREG, htonl(meta->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_META_SREG))
+ mnl_attr_put_u32(nlh, NFTA_META_SREG, htonl(meta->sreg));
+}
+
+static int
+nftnl_expr_meta_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_META_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_meta_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_META_KEY]) {
+ meta->key = ntohl(mnl_attr_get_u32(tb[NFTA_META_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_META_KEY);
+ }
+ if (tb[NFTA_META_DREG]) {
+ meta->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_META_DREG);
+ }
+ if (tb[NFTA_META_SREG]) {
+ meta->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_META_SREG);
+ }
+
+ return 0;
+}
+
+static const char *meta_key2str_array[NFT_META_MAX] = {
+ [NFT_META_LEN] = "len",
+ [NFT_META_PROTOCOL] = "protocol",
+ [NFT_META_NFPROTO] = "nfproto",
+ [NFT_META_L4PROTO] = "l4proto",
+ [NFT_META_PRIORITY] = "priority",
+ [NFT_META_MARK] = "mark",
+ [NFT_META_IIF] = "iif",
+ [NFT_META_OIF] = "oif",
+ [NFT_META_IIFNAME] = "iifname",
+ [NFT_META_OIFNAME] = "oifname",
+ [NFT_META_IIFTYPE] = "iiftype",
+ [NFT_META_OIFTYPE] = "oiftype",
+ [NFT_META_SKUID] = "skuid",
+ [NFT_META_SKGID] = "skgid",
+ [NFT_META_NFTRACE] = "nftrace",
+ [NFT_META_RTCLASSID] = "rtclassid",
+ [NFT_META_SECMARK] = "secmark",
+ [NFT_META_BRI_IIFNAME] = "bri_iifname",
+ [NFT_META_BRI_OIFNAME] = "bri_oifname",
+ [NFT_META_PKTTYPE] = "pkttype",
+ [NFT_META_CPU] = "cpu",
+ [NFT_META_IIFGROUP] = "iifgroup",
+ [NFT_META_OIFGROUP] = "oifgroup",
+ [NFT_META_CGROUP] = "cgroup",
+ [NFT_META_PRANDOM] = "prandom",
+ [NFT_META_SECPATH] = "secpath",
+ [NFT_META_IIFKIND] = "iifkind",
+ [NFT_META_OIFKIND] = "oifkind",
+ [NFT_META_BRI_IIFPVID] = "bri_iifpvid",
+ [NFT_META_BRI_IIFVPROTO] = "bri_iifvproto",
+ [NFT_META_TIME_NS] = "time",
+ [NFT_META_TIME_DAY] = "day",
+ [NFT_META_TIME_HOUR] = "hour",
+ [NFT_META_SDIF] = "sdif",
+ [NFT_META_SDIFNAME] = "sdifname",
+ [NFT_META_BRI_BROUTE] = "broute",
+};
+
+static const char *meta_key2str(uint8_t key)
+{
+ if (key < NFT_META_MAX)
+ return meta_key2str_array[key];
+
+ return "unknown";
+}
+
+static inline int str2meta_key(const char *str)
+{
+ int i;
+
+ for (i = 0; i < NFT_META_MAX; i++) {
+ if (strcmp(str, meta_key2str_array[i]) == 0)
+ return i;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+static int
+nftnl_expr_meta_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_META_SREG)) {
+ return snprintf(buf, len, "set %s with reg %u ",
+ meta_key2str(meta->key), meta->sreg);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_META_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ meta_key2str(meta->key), meta->dreg);
+ }
+ return 0;
+}
+
+struct expr_ops expr_ops_meta = {
+ .name = "meta",
+ .alloc_len = sizeof(struct nftnl_expr_meta),
+ .max_attr = NFTA_META_MAX,
+ .set = nftnl_expr_meta_set,
+ .get = nftnl_expr_meta_get,
+ .parse = nftnl_expr_meta_parse,
+ .build = nftnl_expr_meta_build,
+ .output = nftnl_expr_meta_snprintf,
+};
diff --git a/src/expr/nat.c b/src/expr/nat.c
new file mode 100644
index 0000000..ca727be
--- /dev/null
+++ b/src/expr/nat.c
@@ -0,0 +1,278 @@
+/*
+ * (C) 2012-2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2012 Intel Corporation
+ *
+ * This program is free software; you can redistribute 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:
+ * Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_nat {
+ enum nft_registers sreg_addr_min;
+ enum nft_registers sreg_addr_max;
+ enum nft_registers sreg_proto_min;
+ enum nft_registers sreg_proto_max;
+ int family;
+ enum nft_nat_types type;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_nat_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_NAT_TYPE:
+ memcpy(&nat->type, data, sizeof(nat->type));
+ break;
+ case NFTNL_EXPR_NAT_FAMILY:
+ memcpy(&nat->family, data, sizeof(nat->family));
+ break;
+ case NFTNL_EXPR_NAT_REG_ADDR_MIN:
+ memcpy(&nat->sreg_addr_min, data, sizeof(nat->sreg_addr_min));
+ break;
+ case NFTNL_EXPR_NAT_REG_ADDR_MAX:
+ memcpy(&nat->sreg_addr_max, data, sizeof(nat->sreg_addr_max));
+ break;
+ case NFTNL_EXPR_NAT_REG_PROTO_MIN:
+ memcpy(&nat->sreg_proto_min, data, sizeof(nat->sreg_proto_min));
+ break;
+ case NFTNL_EXPR_NAT_REG_PROTO_MAX:
+ memcpy(&nat->sreg_proto_max, data, sizeof(nat->sreg_proto_max));
+ break;
+ case NFTNL_EXPR_NAT_FLAGS:
+ memcpy(&nat->flags, data, sizeof(nat->flags));
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static const void *
+nftnl_expr_nat_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_NAT_TYPE:
+ *data_len = sizeof(nat->type);
+ return &nat->type;
+ case NFTNL_EXPR_NAT_FAMILY:
+ *data_len = sizeof(nat->family);
+ return &nat->family;
+ case NFTNL_EXPR_NAT_REG_ADDR_MIN:
+ *data_len = sizeof(nat->sreg_addr_min);
+ return &nat->sreg_addr_min;
+ case NFTNL_EXPR_NAT_REG_ADDR_MAX:
+ *data_len = sizeof(nat->sreg_addr_max);
+ return &nat->sreg_addr_max;
+ case NFTNL_EXPR_NAT_REG_PROTO_MIN:
+ *data_len = sizeof(nat->sreg_proto_min);
+ return &nat->sreg_proto_min;
+ case NFTNL_EXPR_NAT_REG_PROTO_MAX:
+ *data_len = sizeof(nat->sreg_proto_max);
+ return &nat->sreg_proto_max;
+ case NFTNL_EXPR_NAT_FLAGS:
+ *data_len = sizeof(nat->flags);
+ return &nat->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_nat_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_NAT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_NAT_TYPE:
+ case NFTA_NAT_FAMILY:
+ case NFTA_NAT_REG_ADDR_MIN:
+ case NFTA_NAT_REG_ADDR_MAX:
+ case NFTA_NAT_REG_PROTO_MIN:
+ case NFTA_NAT_REG_PROTO_MAX:
+ case NFTA_NAT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_expr_nat_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_NAT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_nat_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_NAT_TYPE]) {
+ nat->type = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_TYPE);
+ }
+ if (tb[NFTA_NAT_FAMILY]) {
+ nat->family = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_FAMILY]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_FAMILY);
+ }
+ if (tb[NFTA_NAT_REG_ADDR_MIN]) {
+ nat->sreg_addr_min =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_ADDR_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN);
+ }
+ if (tb[NFTA_NAT_REG_ADDR_MAX]) {
+ nat->sreg_addr_max =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_ADDR_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX);
+ }
+ if (tb[NFTA_NAT_REG_PROTO_MIN]) {
+ nat->sreg_proto_min =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_PROTO_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN);
+ }
+ if (tb[NFTA_NAT_REG_PROTO_MAX]) {
+ nat->sreg_proto_max =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_PROTO_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX);
+ }
+ if (tb[NFTA_NAT_FLAGS]) {
+ nat->flags = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_FLAGS);
+ }
+
+ return 0;
+}
+
+static void
+nftnl_expr_nat_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_NAT_TYPE, htonl(nat->type));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_FAMILY))
+ mnl_attr_put_u32(nlh, NFTA_NAT_FAMILY, htonl(nat->family));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_ADDR_MIN,
+ htonl(nat->sreg_addr_min));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_ADDR_MAX,
+ htonl(nat->sreg_addr_max));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_PROTO_MIN,
+ htonl(nat->sreg_proto_min));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_PROTO_MAX,
+ htonl(nat->sreg_proto_max));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_NAT_FLAGS, htonl(nat->flags));
+}
+
+static inline const char *nat2str(uint16_t nat)
+{
+ switch (nat) {
+ case NFT_NAT_SNAT:
+ return "snat";
+ case NFT_NAT_DNAT:
+ return "dnat";
+ default:
+ return "unknown";
+ }
+}
+
+static inline int nftnl_str2nat(const char *nat)
+{
+ if (strcmp(nat, "snat") == 0)
+ return NFT_NAT_SNAT;
+ else if (strcmp(nat, "dnat") == 0)
+ return NFT_NAT_DNAT;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int
+nftnl_expr_nat_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+ int offset = 0, ret = 0;
+
+ ret = snprintf(buf, remain, "%s ", nat2str(nat->type));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "%s ",
+ nftnl_family2str(nat->family));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN)) {
+ ret = snprintf(buf + offset, remain,
+ "addr_min reg %u ", nat->sreg_addr_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX)) {
+ ret = snprintf(buf + offset, remain,
+ "addr_max reg %u ", nat->sreg_addr_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN)) {
+ ret = snprintf(buf + offset, remain,
+ "proto_min reg %u ", nat->sreg_proto_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX)) {
+ ret = snprintf(buf + offset, remain,
+ "proto_max reg %u ", nat->sreg_proto_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "flags 0x%x ", nat->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_nat = {
+ .name = "nat",
+ .alloc_len = sizeof(struct nftnl_expr_nat),
+ .max_attr = NFTA_NAT_MAX,
+ .set = nftnl_expr_nat_set,
+ .get = nftnl_expr_nat_get,
+ .parse = nftnl_expr_nat_parse,
+ .build = nftnl_expr_nat_build,
+ .output = nftnl_expr_nat_snprintf,
+};
diff --git a/src/expr/numgen.c b/src/expr/numgen.c
new file mode 100644
index 0000000..d4020a6
--- /dev/null
+++ b/src/expr/numgen.c
@@ -0,0 +1,184 @@
+/*
+ * (C) 2016 by Laura Garcia <nevola@gmail.com>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_ng {
+ enum nft_registers dreg;
+ unsigned int modulus;
+ enum nft_ng_types type;
+ unsigned int offset;
+};
+
+static int
+nftnl_expr_ng_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_NG_DREG:
+ memcpy(&ng->dreg, data, sizeof(ng->dreg));
+ break;
+ case NFTNL_EXPR_NG_MODULUS:
+ memcpy(&ng->modulus, data, sizeof(ng->modulus));
+ break;
+ case NFTNL_EXPR_NG_TYPE:
+ memcpy(&ng->type, data, sizeof(ng->type));
+ break;
+ case NFTNL_EXPR_NG_OFFSET:
+ memcpy(&ng->offset, data, sizeof(ng->offset));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_ng_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_NG_DREG:
+ *data_len = sizeof(ng->dreg);
+ return &ng->dreg;
+ case NFTNL_EXPR_NG_MODULUS:
+ *data_len = sizeof(ng->modulus);
+ return &ng->modulus;
+ case NFTNL_EXPR_NG_TYPE:
+ *data_len = sizeof(ng->type);
+ return &ng->type;
+ case NFTNL_EXPR_NG_OFFSET:
+ *data_len = sizeof(ng->offset);
+ return &ng->offset;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_ng_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_NG_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_NG_DREG:
+ case NFTA_NG_MODULUS:
+ case NFTA_NG_TYPE:
+ case NFTA_NG_OFFSET:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_ng_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_NG_DREG))
+ mnl_attr_put_u32(nlh, NFTA_NG_DREG, htonl(ng->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_NG_MODULUS))
+ mnl_attr_put_u32(nlh, NFTA_NG_MODULUS, htonl(ng->modulus));
+ if (e->flags & (1 << NFTNL_EXPR_NG_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_NG_TYPE, htonl(ng->type));
+ if (e->flags & (1 << NFTNL_EXPR_NG_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_NG_OFFSET, htonl(ng->offset));
+}
+
+static int
+nftnl_expr_ng_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_NG_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_ng_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_NG_DREG]) {
+ ng->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_NG_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_NG_DREG);
+ }
+ if (tb[NFTA_NG_MODULUS]) {
+ ng->modulus = ntohl(mnl_attr_get_u32(tb[NFTA_NG_MODULUS]));
+ e->flags |= (1 << NFTNL_EXPR_NG_MODULUS);
+ }
+ if (tb[NFTA_NG_TYPE]) {
+ ng->type = ntohl(mnl_attr_get_u32(tb[NFTA_NG_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_NG_TYPE);
+ }
+ if (tb[NFTA_NG_OFFSET]) {
+ ng->offset = ntohl(mnl_attr_get_u32(tb[NFTA_NG_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_NG_OFFSET);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_ng_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ switch (ng->type) {
+ case NFT_NG_INCREMENTAL:
+ ret = snprintf(buf, remain, "reg %u = inc mod %u ",
+ ng->dreg, ng->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ case NFT_NG_RANDOM:
+ ret = snprintf(buf, remain, "reg %u = random mod %u ",
+ ng->dreg, ng->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ default:
+ return 0;
+ }
+
+ if (ng->offset) {
+ ret = snprintf(buf + offset, remain, "offset %u ", ng->offset);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_ng = {
+ .name = "numgen",
+ .alloc_len = sizeof(struct nftnl_expr_ng),
+ .max_attr = NFTA_NG_MAX,
+ .set = nftnl_expr_ng_set,
+ .get = nftnl_expr_ng_get,
+ .parse = nftnl_expr_ng_parse,
+ .build = nftnl_expr_ng_build,
+ .output = nftnl_expr_ng_snprintf,
+};
diff --git a/src/expr/objref.c b/src/expr/objref.c
new file mode 100644
index 0000000..ad0688f
--- /dev/null
+++ b/src/expr/objref.c
@@ -0,0 +1,209 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_objref {
+ struct {
+ uint32_t type;
+ const char *name;
+ } imm;
+ struct {
+ uint32_t sreg;
+ const char *name;
+ uint32_t id;
+ } set;
+};
+
+static int nftnl_expr_objref_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OBJREF_IMM_TYPE:
+ memcpy(&objref->imm.type, data, sizeof(objref->imm.type));
+ break;
+ case NFTNL_EXPR_OBJREF_IMM_NAME:
+ objref->imm.name = strdup(data);
+ if (!objref->imm.name)
+ return -1;
+ break;
+ case NFTNL_EXPR_OBJREF_SET_SREG:
+ memcpy(&objref->set.sreg, data, sizeof(objref->set.sreg));
+ break;
+ case NFTNL_EXPR_OBJREF_SET_NAME:
+ objref->set.name = strdup(data);
+ if (!objref->set.name)
+ return -1;
+ break;
+ case NFTNL_EXPR_OBJREF_SET_ID:
+ memcpy(&objref->set.id, data, sizeof(objref->set.id));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_objref_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OBJREF_IMM_TYPE:
+ *data_len = sizeof(objref->imm.type);
+ return &objref->imm.type;
+ case NFTNL_EXPR_OBJREF_IMM_NAME:
+ *data_len = strlen(objref->imm.name) + 1;
+ return objref->imm.name;
+ case NFTNL_EXPR_OBJREF_SET_SREG:
+ *data_len = sizeof(objref->set.sreg);
+ return &objref->set.sreg;
+ case NFTNL_EXPR_OBJREF_SET_NAME:
+ *data_len = strlen(objref->set.name) + 1;
+ return objref->set.name;
+ case NFTNL_EXPR_OBJREF_SET_ID:
+ *data_len = sizeof(objref->set.id);
+ return &objref->set.id;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_objref_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_OBJREF_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_OBJREF_IMM_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJREF_IMM_NAME:
+ case NFTA_OBJREF_SET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJREF_SET_SREG:
+ case NFTA_OBJREF_SET_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_objref_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_IMM_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_OBJREF_IMM_TYPE,
+ htonl(objref->imm.type));
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_IMM_NAME))
+ mnl_attr_put_str(nlh, NFTA_OBJREF_IMM_NAME, objref->imm.name);
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_SREG))
+ mnl_attr_put_u32(nlh, NFTA_OBJREF_SET_SREG,
+ htonl(objref->set.sreg));
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_NAME))
+ mnl_attr_put_str(nlh, NFTA_OBJREF_SET_NAME, objref->set.name);
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_OBJREF_SET_ID,
+ htonl(objref->set.id));
+}
+
+static int nftnl_expr_objref_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_OBJREF_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_objref_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_OBJREF_IMM_TYPE]) {
+ objref->imm.type =
+ ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_IMM_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_IMM_TYPE);
+ }
+ if (tb[NFTA_OBJREF_IMM_NAME]) {
+ objref->imm.name =
+ strdup(mnl_attr_get_str(tb[NFTA_OBJREF_IMM_NAME]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_IMM_NAME);
+ }
+ if (tb[NFTA_OBJREF_SET_SREG]) {
+ objref->set.sreg =
+ ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_SET_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_SREG);
+ }
+ if (tb[NFTA_OBJREF_SET_NAME]) {
+ objref->set.name =
+ strdup(mnl_attr_get_str(tb[NFTA_OBJREF_SET_NAME]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_NAME);
+ }
+ if (tb[NFTA_OBJREF_SET_ID]) {
+ objref->set.id =
+ ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_SET_ID]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_ID);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_objref_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_SREG))
+ return snprintf(buf, len, "sreg %u set %s ",
+ objref->set.sreg, objref->set.name);
+ else
+ return snprintf(buf, len, "type %u name %s ",
+ objref->imm.type, objref->imm.name);
+}
+
+static void nftnl_expr_objref_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ xfree(objref->imm.name);
+ xfree(objref->set.name);
+}
+
+struct expr_ops expr_ops_objref = {
+ .name = "objref",
+ .alloc_len = sizeof(struct nftnl_expr_objref),
+ .max_attr = NFTA_OBJREF_MAX,
+ .free = nftnl_expr_objref_free,
+ .set = nftnl_expr_objref_set,
+ .get = nftnl_expr_objref_get,
+ .parse = nftnl_expr_objref_parse,
+ .build = nftnl_expr_objref_build,
+ .output = nftnl_expr_objref_snprintf,
+};
diff --git a/src/expr/osf.c b/src/expr/osf.c
new file mode 100644
index 0000000..f15a722
--- /dev/null
+++ b/src/expr/osf.c
@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+#define OSF_GENRE_SIZE 32
+
+struct nftnl_expr_osf {
+ enum nft_registers dreg;
+ uint8_t ttl;
+ uint32_t flags;
+};
+
+static int nftnl_expr_osf_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OSF_DREG:
+ memcpy(&osf->dreg, data, sizeof(osf->dreg));
+ break;
+ case NFTNL_EXPR_OSF_TTL:
+ memcpy(&osf->ttl, data, sizeof(osf->ttl));
+ break;
+ case NFTNL_EXPR_OSF_FLAGS:
+ memcpy(&osf->flags, data, sizeof(osf->flags));
+ break;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_osf_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OSF_DREG:
+ *data_len = sizeof(osf->dreg);
+ return &osf->dreg;
+ case NFTNL_EXPR_OSF_TTL:
+ *data_len = sizeof(osf->ttl);
+ return &osf->ttl;
+ case NFTNL_EXPR_OSF_FLAGS:
+ *data_len = sizeof(osf->flags);
+ return &osf->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_osf_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_OSF_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTNL_EXPR_OSF_DREG:
+ case NFTNL_EXPR_OSF_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+
+ case NFTNL_EXPR_OSF_TTL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_osf_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_OSF_DREG))
+ mnl_attr_put_u32(nlh, NFTNL_EXPR_OSF_DREG, htonl(osf->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_OSF_TTL))
+ mnl_attr_put_u8(nlh, NFTNL_EXPR_OSF_TTL, osf->ttl);
+ if (e->flags & (1 << NFTNL_EXPR_OSF_FLAGS))
+ if (osf->flags)
+ mnl_attr_put_u32(nlh, NFTNL_EXPR_OSF_FLAGS, htonl(osf->flags));
+}
+
+static int
+nftnl_expr_osf_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_OSF_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_osf_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_OSF_DREG]) {
+ osf->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_OSF_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_OSF_DREG);
+ }
+
+ if (tb[NFTA_OSF_TTL]) {
+ osf->ttl = mnl_attr_get_u8(tb[NFTA_OSF_TTL]);
+ e->flags |= (1 << NFTNL_EXPR_OSF_TTL);
+ }
+
+ if (tb[NFTA_OSF_FLAGS]) {
+ osf->flags = ntohl(mnl_attr_get_u32(tb[NFTA_OSF_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_OSF_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_osf_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_OSF_DREG)) {
+ ret = snprintf(buf, len, "dreg %u ", osf->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, len, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_osf = {
+ .name = "osf",
+ .alloc_len = sizeof(struct nftnl_expr_osf),
+ .max_attr = NFTA_OSF_MAX,
+ .set = nftnl_expr_osf_set,
+ .get = nftnl_expr_osf_get,
+ .parse = nftnl_expr_osf_parse,
+ .build = nftnl_expr_osf_build,
+ .output = nftnl_expr_osf_snprintf,
+};
diff --git a/src/expr/payload.c b/src/expr/payload.c
new file mode 100644
index 0000000..c633e33
--- /dev/null
+++ b/src/expr/payload.c
@@ -0,0 +1,250 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_payload {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ enum nft_payload_bases base;
+ uint32_t offset;
+ uint32_t len;
+ uint32_t csum_type;
+ uint32_t csum_offset;
+ uint32_t csum_flags;
+};
+
+static int
+nftnl_expr_payload_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_PAYLOAD_SREG:
+ memcpy(&payload->sreg, data, sizeof(payload->sreg));
+ break;
+ case NFTNL_EXPR_PAYLOAD_DREG:
+ memcpy(&payload->dreg, data, sizeof(payload->dreg));
+ break;
+ case NFTNL_EXPR_PAYLOAD_BASE:
+ memcpy(&payload->base, data, sizeof(payload->base));
+ break;
+ case NFTNL_EXPR_PAYLOAD_OFFSET:
+ memcpy(&payload->offset, data, sizeof(payload->offset));
+ break;
+ case NFTNL_EXPR_PAYLOAD_LEN:
+ memcpy(&payload->len, data, sizeof(payload->len));
+ break;
+ case NFTNL_EXPR_PAYLOAD_CSUM_TYPE:
+ memcpy(&payload->csum_type, data, sizeof(payload->csum_type));
+ break;
+ case NFTNL_EXPR_PAYLOAD_CSUM_OFFSET:
+ memcpy(&payload->csum_offset, data, sizeof(payload->csum_offset));
+ break;
+ case NFTNL_EXPR_PAYLOAD_FLAGS:
+ memcpy(&payload->csum_flags, data, sizeof(payload->csum_flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_payload_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_PAYLOAD_SREG:
+ *data_len = sizeof(payload->sreg);
+ return &payload->sreg;
+ case NFTNL_EXPR_PAYLOAD_DREG:
+ *data_len = sizeof(payload->dreg);
+ return &payload->dreg;
+ case NFTNL_EXPR_PAYLOAD_BASE:
+ *data_len = sizeof(payload->base);
+ return &payload->base;
+ case NFTNL_EXPR_PAYLOAD_OFFSET:
+ *data_len = sizeof(payload->offset);
+ return &payload->offset;
+ case NFTNL_EXPR_PAYLOAD_LEN:
+ *data_len = sizeof(payload->len);
+ return &payload->len;
+ case NFTNL_EXPR_PAYLOAD_CSUM_TYPE:
+ *data_len = sizeof(payload->csum_type);
+ return &payload->csum_type;
+ case NFTNL_EXPR_PAYLOAD_CSUM_OFFSET:
+ *data_len = sizeof(payload->csum_offset);
+ return &payload->csum_offset;
+ case NFTNL_EXPR_PAYLOAD_FLAGS:
+ *data_len = sizeof(payload->csum_flags);
+ return &payload->csum_flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_payload_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_PAYLOAD_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_PAYLOAD_SREG:
+ case NFTA_PAYLOAD_DREG:
+ case NFTA_PAYLOAD_BASE:
+ case NFTA_PAYLOAD_OFFSET:
+ case NFTA_PAYLOAD_LEN:
+ case NFTA_PAYLOAD_CSUM_TYPE:
+ case NFTA_PAYLOAD_CSUM_OFFSET:
+ case NFTA_PAYLOAD_CSUM_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_payload_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_SREG))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_SREG, htonl(payload->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_DREG))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_BASE))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_LEN))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_CSUM_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_TYPE,
+ htonl(payload->csum_type));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_CSUM_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_OFFSET,
+ htonl(payload->csum_offset));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_FLAGS,
+ htonl(payload->csum_flags));
+}
+
+static int
+nftnl_expr_payload_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_payload_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_PAYLOAD_SREG]) {
+ payload->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_SREG);
+ }
+ if (tb[NFTA_PAYLOAD_DREG]) {
+ payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_DREG);
+ }
+ if (tb[NFTA_PAYLOAD_BASE]) {
+ payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_BASE);
+ }
+ if (tb[NFTA_PAYLOAD_OFFSET]) {
+ payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_OFFSET);
+ }
+ if (tb[NFTA_PAYLOAD_LEN]) {
+ payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_LEN);
+ }
+ if (tb[NFTA_PAYLOAD_CSUM_TYPE]) {
+ payload->csum_type = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_CSUM_TYPE);
+ }
+ if (tb[NFTA_PAYLOAD_CSUM_OFFSET]) {
+ payload->csum_offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_CSUM_OFFSET);
+ }
+ if (tb[NFTA_PAYLOAD_CSUM_FLAGS]) {
+ payload->csum_flags = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_FLAGS);
+ }
+ return 0;
+}
+
+static const char *base2str_array[NFT_PAYLOAD_TUN_HEADER + 1] = {
+ [NFT_PAYLOAD_LL_HEADER] = "link",
+ [NFT_PAYLOAD_NETWORK_HEADER] = "network",
+ [NFT_PAYLOAD_TRANSPORT_HEADER] = "transport",
+ [NFT_PAYLOAD_INNER_HEADER] = "inner",
+ [NFT_PAYLOAD_TUN_HEADER] = "tunnel",
+};
+
+static const char *base2str(enum nft_payload_bases base)
+{
+ if (base > NFT_PAYLOAD_INNER_HEADER)
+ return "unknown";
+
+ return base2str_array[base];
+}
+
+static int
+nftnl_expr_payload_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ if (payload->sreg)
+ return snprintf(buf, len, "write reg %u => %ub @ %s header + %u csum_type %u csum_off %u csum_flags 0x%x ",
+ payload->sreg,
+ payload->len, base2str(payload->base),
+ payload->offset, payload->csum_type,
+ payload->csum_offset,
+ payload->csum_flags);
+ else
+ return snprintf(buf, len, "load %ub @ %s header + %u => reg %u ",
+ payload->len, base2str(payload->base),
+ payload->offset, payload->dreg);
+}
+
+struct expr_ops expr_ops_payload = {
+ .name = "payload",
+ .alloc_len = sizeof(struct nftnl_expr_payload),
+ .max_attr = NFTA_PAYLOAD_MAX,
+ .set = nftnl_expr_payload_set,
+ .get = nftnl_expr_payload_get,
+ .parse = nftnl_expr_payload_parse,
+ .build = nftnl_expr_payload_build,
+ .output = nftnl_expr_payload_snprintf,
+};
diff --git a/src/expr/queue.c b/src/expr/queue.c
new file mode 100644
index 0000000..de287f2
--- /dev/null
+++ b/src/expr/queue.c
@@ -0,0 +1,197 @@
+/*
+ * (C) 2013 by Eric Leblond <eric@regit.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_queue {
+ enum nft_registers sreg_qnum;
+ uint16_t queuenum;
+ uint16_t queues_total;
+ uint16_t flags;
+};
+
+static int nftnl_expr_queue_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_QUEUE_NUM:
+ memcpy(&queue->queuenum, data, sizeof(queue->queuenum));
+ break;
+ case NFTNL_EXPR_QUEUE_TOTAL:
+ memcpy(&queue->queues_total, data, sizeof(queue->queues_total));
+ break;
+ case NFTNL_EXPR_QUEUE_FLAGS:
+ memcpy(&queue->flags, data, sizeof(queue->flags));
+ break;
+ case NFTNL_EXPR_QUEUE_SREG_QNUM:
+ memcpy(&queue->sreg_qnum, data, sizeof(queue->sreg_qnum));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_queue_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_QUEUE_NUM:
+ *data_len = sizeof(queue->queuenum);
+ return &queue->queuenum;
+ case NFTNL_EXPR_QUEUE_TOTAL:
+ *data_len = sizeof(queue->queues_total);
+ return &queue->queues_total;
+ case NFTNL_EXPR_QUEUE_FLAGS:
+ *data_len = sizeof(queue->flags);
+ return &queue->flags;
+ case NFTNL_EXPR_QUEUE_SREG_QNUM:
+ *data_len = sizeof(queue->sreg_qnum);
+ return &queue->sreg_qnum;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_queue_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_QUEUE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_QUEUE_NUM:
+ case NFTA_QUEUE_TOTAL:
+ case NFTA_QUEUE_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_QUEUE_SREG_QNUM:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_queue_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_NUM))
+ mnl_attr_put_u16(nlh, NFTA_QUEUE_NUM, htons(queue->queuenum));
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_TOTAL))
+ mnl_attr_put_u16(nlh, NFTA_QUEUE_TOTAL, htons(queue->queues_total));
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_FLAGS))
+ mnl_attr_put_u16(nlh, NFTA_QUEUE_FLAGS, htons(queue->flags));
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM))
+ mnl_attr_put_u32(nlh, NFTA_QUEUE_SREG_QNUM, htonl(queue->sreg_qnum));
+}
+
+static int
+nftnl_expr_queue_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_QUEUE_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_queue_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_QUEUE_NUM]) {
+ queue->queuenum = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_NUM]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_NUM);
+ }
+ if (tb[NFTA_QUEUE_TOTAL]) {
+ queue->queues_total = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_TOTAL]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_TOTAL);
+ }
+ if (tb[NFTA_QUEUE_FLAGS]) {
+ queue->flags = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_FLAGS);
+ }
+ if (tb[NFTA_QUEUE_SREG_QNUM]) {
+ queue->sreg_qnum = ntohl(mnl_attr_get_u32(tb[NFTA_QUEUE_SREG_QNUM]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_SREG_QNUM);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_queue_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+ uint16_t total_queues;
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_NUM)) {
+ total_queues = queue->queuenum + queue->queues_total - 1;
+
+ ret = snprintf(buf + offset, remain, "num %u", queue->queuenum);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (queue->queues_total && total_queues != queue->queuenum) {
+ ret = snprintf(buf + offset, remain, "-%u", total_queues);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain, " ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM)) {
+ ret = snprintf(buf + offset, remain, "sreg_qnum %u ",
+ queue->sreg_qnum);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_FLAGS)) {
+ if (queue->flags & (NFT_QUEUE_FLAG_BYPASS)) {
+ ret = snprintf(buf + offset, remain, "bypass ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (queue->flags & (NFT_QUEUE_FLAG_CPU_FANOUT)) {
+ ret = snprintf(buf + offset, remain, "fanout ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+ return offset;
+}
+
+struct expr_ops expr_ops_queue = {
+ .name = "queue",
+ .alloc_len = sizeof(struct nftnl_expr_queue),
+ .max_attr = NFTA_QUEUE_MAX,
+ .set = nftnl_expr_queue_set,
+ .get = nftnl_expr_queue_get,
+ .parse = nftnl_expr_queue_parse,
+ .build = nftnl_expr_queue_build,
+ .output = nftnl_expr_queue_snprintf,
+};
diff --git a/src/expr/quota.c b/src/expr/quota.c
new file mode 100644
index 0000000..835729c
--- /dev/null
+++ b/src/expr/quota.c
@@ -0,0 +1,151 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_quota {
+ uint64_t bytes;
+ uint64_t consumed;
+ uint32_t flags;
+};
+
+static int nftnl_expr_quota_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_QUOTA_BYTES:
+ memcpy(&quota->bytes, data, sizeof(quota->bytes));
+ break;
+ case NFTNL_EXPR_QUOTA_CONSUMED:
+ memcpy(&quota->consumed, data, sizeof(quota->consumed));
+ break;
+ case NFTNL_EXPR_QUOTA_FLAGS:
+ memcpy(&quota->flags, data, sizeof(quota->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_quota_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_QUOTA_BYTES:
+ *data_len = sizeof(quota->bytes);
+ return &quota->bytes;
+ case NFTNL_EXPR_QUOTA_CONSUMED:
+ *data_len = sizeof(quota->consumed);
+ return &quota->consumed;
+ case NFTNL_EXPR_QUOTA_FLAGS:
+ *data_len = sizeof(quota->flags);
+ return &quota->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_quota_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_QUOTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_QUOTA_BYTES:
+ case NFTA_QUOTA_CONSUMED:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_QUOTA_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_quota_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_QUOTA_BYTES))
+ mnl_attr_put_u64(nlh, NFTA_QUOTA_BYTES, htobe64(quota->bytes));
+ if (e->flags & (1 << NFTNL_EXPR_QUOTA_CONSUMED))
+ mnl_attr_put_u64(nlh, NFTA_QUOTA_CONSUMED, htobe64(quota->consumed));
+ if (e->flags & (1 << NFTNL_EXPR_QUOTA_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_QUOTA_FLAGS, htonl(quota->flags));
+}
+
+static int
+nftnl_expr_quota_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_QUOTA_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_quota_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_QUOTA_BYTES]) {
+ quota->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_BYTES]));
+ e->flags |= (1 << NFTNL_EXPR_QUOTA_BYTES);
+ }
+ if (tb[NFTA_QUOTA_CONSUMED]) {
+ quota->consumed = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_CONSUMED]));
+ e->flags |= (1 << NFTNL_EXPR_QUOTA_CONSUMED);
+ }
+ if (tb[NFTA_QUOTA_FLAGS]) {
+ quota->flags = ntohl(mnl_attr_get_u32(tb[NFTA_QUOTA_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_QUOTA_FLAGS);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_quota_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ return snprintf(buf, len,
+ "bytes %"PRIu64" consumed %"PRIu64" flags %u ",
+ quota->bytes, quota->consumed, quota->flags);
+}
+
+struct expr_ops expr_ops_quota = {
+ .name = "quota",
+ .alloc_len = sizeof(struct nftnl_expr_quota),
+ .max_attr = NFTA_QUOTA_MAX,
+ .set = nftnl_expr_quota_set,
+ .get = nftnl_expr_quota_get,
+ .parse = nftnl_expr_quota_parse,
+ .build = nftnl_expr_quota_build,
+ .output = nftnl_expr_quota_snprintf,
+};
diff --git a/src/expr/range.c b/src/expr/range.c
new file mode 100644
index 0000000..473add8
--- /dev/null
+++ b/src/expr/range.c
@@ -0,0 +1,217 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_range {
+ union nftnl_data_reg data_from;
+ union nftnl_data_reg data_to;
+ enum nft_registers sreg;
+ enum nft_range_ops op;
+};
+
+static int nftnl_expr_range_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_RANGE_SREG:
+ memcpy(&range->sreg, data, sizeof(range->sreg));
+ break;
+ case NFTNL_EXPR_RANGE_OP:
+ memcpy(&range->op, data, sizeof(range->op));
+ break;
+ case NFTNL_EXPR_RANGE_FROM_DATA:
+ memcpy(&range->data_from.val, data, data_len);
+ range->data_from.len = data_len;
+ break;
+ case NFTNL_EXPR_RANGE_TO_DATA:
+ memcpy(&range->data_to.val, data, data_len);
+ range->data_to.len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_range_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_RANGE_SREG:
+ *data_len = sizeof(range->sreg);
+ return &range->sreg;
+ case NFTNL_EXPR_RANGE_OP:
+ *data_len = sizeof(range->op);
+ return &range->op;
+ case NFTNL_EXPR_RANGE_FROM_DATA:
+ *data_len = range->data_from.len;
+ return &range->data_from.val;
+ case NFTNL_EXPR_RANGE_TO_DATA:
+ *data_len = range->data_to.len;
+ return &range->data_to.val;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_range_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_RANGE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_RANGE_SREG:
+ case NFTA_RANGE_OP:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_RANGE_FROM_DATA:
+ case NFTA_RANGE_TO_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_range_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_SREG))
+ mnl_attr_put_u32(nlh, NFTA_RANGE_SREG, htonl(range->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_OP))
+ mnl_attr_put_u32(nlh, NFTA_RANGE_OP, htonl(range->op));
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_FROM_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RANGE_FROM_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, range->data_from.len,
+ range->data_from.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_TO_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RANGE_TO_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, range->data_to.len,
+ range->data_to.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int
+nftnl_expr_range_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_RANGE_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_range_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_RANGE_SREG]) {
+ range->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_RANGE_SREG]));
+ e->flags |= (1 << NFTA_RANGE_SREG);
+ }
+ if (tb[NFTA_RANGE_OP]) {
+ range->op = ntohl(mnl_attr_get_u32(tb[NFTA_RANGE_OP]));
+ e->flags |= (1 << NFTA_RANGE_OP);
+ }
+ if (tb[NFTA_RANGE_FROM_DATA]) {
+ ret = nftnl_parse_data(&range->data_from,
+ tb[NFTA_RANGE_FROM_DATA], NULL);
+ e->flags |= (1 << NFTA_RANGE_FROM_DATA);
+ }
+ if (tb[NFTA_RANGE_TO_DATA]) {
+ ret = nftnl_parse_data(&range->data_to,
+ tb[NFTA_RANGE_TO_DATA], NULL);
+ e->flags |= (1 << NFTA_RANGE_TO_DATA);
+ }
+
+ return ret;
+}
+
+static const char *expr_range_str[] = {
+ [NFT_RANGE_EQ] = "eq",
+ [NFT_RANGE_NEQ] = "neq",
+};
+
+static const char *range2str(uint32_t op)
+{
+ if (op > NFT_RANGE_NEQ)
+ return "unknown";
+
+ return expr_range_str[op];
+}
+
+static inline int nftnl_str2range(const char *op)
+{
+ if (strcmp(op, "eq") == 0)
+ return NFT_RANGE_EQ;
+ else if (strcmp(op, "neq") == 0)
+ return NFT_RANGE_NEQ;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int nftnl_expr_range_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "%s reg %u ",
+ range2str(range->op), range->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_from,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_to,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_range = {
+ .name = "range",
+ .alloc_len = sizeof(struct nftnl_expr_range),
+ .max_attr = NFTA_RANGE_MAX,
+ .set = nftnl_expr_range_set,
+ .get = nftnl_expr_range_get,
+ .parse = nftnl_expr_range_parse,
+ .build = nftnl_expr_range_build,
+ .output = nftnl_expr_range_snprintf,
+};
diff --git a/src/expr/redir.c b/src/expr/redir.c
new file mode 100644
index 0000000..87c2acc
--- /dev/null
+++ b/src/expr/redir.c
@@ -0,0 +1,171 @@
+/*
+ * (C) 2014 by Arturo Borrero Gonzalez <arturo@debian.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_redir {
+ enum nft_registers sreg_proto_min;
+ enum nft_registers sreg_proto_max;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_redir_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_REDIR_REG_PROTO_MIN:
+ memcpy(&redir->sreg_proto_min, data, sizeof(redir->sreg_proto_min));
+ break;
+ case NFTNL_EXPR_REDIR_REG_PROTO_MAX:
+ memcpy(&redir->sreg_proto_max, data, sizeof(redir->sreg_proto_max));
+ break;
+ case NFTNL_EXPR_REDIR_FLAGS:
+ memcpy(&redir->flags, data, sizeof(redir->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_redir_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_REDIR_REG_PROTO_MIN:
+ *data_len = sizeof(redir->sreg_proto_min);
+ return &redir->sreg_proto_min;
+ case NFTNL_EXPR_REDIR_REG_PROTO_MAX:
+ *data_len = sizeof(redir->sreg_proto_max);
+ return &redir->sreg_proto_max;
+ case NFTNL_EXPR_REDIR_FLAGS:
+ *data_len = sizeof(redir->flags);
+ return &redir->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_redir_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_REDIR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_REDIR_REG_PROTO_MIN:
+ case NFTA_REDIR_REG_PROTO_MAX:
+ case NFTA_REDIR_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_redir_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN))
+ mnl_attr_put_u32(nlh, NFTA_REDIR_REG_PROTO_MIN,
+ htobe32(redir->sreg_proto_min));
+ if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX))
+ mnl_attr_put_u32(nlh, NFTA_REDIR_REG_PROTO_MAX,
+ htobe32(redir->sreg_proto_max));
+ if (e->flags & (1 << NFTNL_EXPR_REDIR_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_REDIR_FLAGS, htobe32(redir->flags));
+}
+
+static int
+nftnl_expr_redir_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_REDIR_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_redir_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_REDIR_REG_PROTO_MIN]) {
+ redir->sreg_proto_min =
+ ntohl(mnl_attr_get_u32(tb[NFTA_REDIR_REG_PROTO_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN);
+ }
+ if (tb[NFTA_REDIR_REG_PROTO_MAX]) {
+ redir->sreg_proto_max =
+ ntohl(mnl_attr_get_u32(tb[NFTA_REDIR_REG_PROTO_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX);
+ }
+ if (tb[NFTA_REDIR_FLAGS]) {
+ redir->flags = be32toh(mnl_attr_get_u32(tb[NFTA_REDIR_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_REDIR_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_redir_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ int ret, offset = 0;
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_REG_PROTO_MIN)) {
+ ret = snprintf(buf + offset, remain, "proto_min reg %u ",
+ redir->sreg_proto_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_REG_PROTO_MAX)) {
+ ret = snprintf(buf + offset, remain, "proto_max reg %u ",
+ redir->sreg_proto_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "flags 0x%x ",
+ redir->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_redir = {
+ .name = "redir",
+ .alloc_len = sizeof(struct nftnl_expr_redir),
+ .max_attr = NFTA_REDIR_MAX,
+ .set = nftnl_expr_redir_set,
+ .get = nftnl_expr_redir_get,
+ .parse = nftnl_expr_redir_parse,
+ .build = nftnl_expr_redir_build,
+ .output = nftnl_expr_redir_snprintf,
+};
diff --git a/src/expr/reject.c b/src/expr/reject.c
new file mode 100644
index 0000000..c7c9441
--- /dev/null
+++ b/src/expr/reject.c
@@ -0,0 +1,138 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_reject {
+ uint32_t type;
+ uint8_t icmp_code;
+};
+
+static int nftnl_expr_reject_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_REJECT_TYPE:
+ memcpy(&reject->type, data, sizeof(reject->type));
+ break;
+ case NFTNL_EXPR_REJECT_CODE:
+ memcpy(&reject->icmp_code, data, sizeof(reject->icmp_code));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_reject_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_REJECT_TYPE:
+ *data_len = sizeof(reject->type);
+ return &reject->type;
+ case NFTNL_EXPR_REJECT_CODE:
+ *data_len = sizeof(reject->icmp_code);
+ return &reject->icmp_code;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_reject_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_REJECT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_REJECT_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_REJECT_ICMP_CODE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_reject_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_REJECT_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_REJECT_TYPE, htonl(reject->type));
+ if (e->flags & (1 << NFTNL_EXPR_REJECT_CODE))
+ mnl_attr_put_u8(nlh, NFTA_REJECT_ICMP_CODE, reject->icmp_code);
+}
+
+static int
+nftnl_expr_reject_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_REJECT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_reject_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_REJECT_TYPE]) {
+ reject->type = ntohl(mnl_attr_get_u32(tb[NFTA_REJECT_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_REJECT_TYPE);
+ }
+ if (tb[NFTA_REJECT_ICMP_CODE]) {
+ reject->icmp_code = mnl_attr_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
+ e->flags |= (1 << NFTNL_EXPR_REJECT_CODE);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_reject_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "type %u code %u ",
+ reject->type, reject->icmp_code);
+}
+
+struct expr_ops expr_ops_reject = {
+ .name = "reject",
+ .alloc_len = sizeof(struct nftnl_expr_reject),
+ .max_attr = NFTA_REJECT_MAX,
+ .set = nftnl_expr_reject_set,
+ .get = nftnl_expr_reject_get,
+ .parse = nftnl_expr_reject_parse,
+ .build = nftnl_expr_reject_build,
+ .output = nftnl_expr_reject_snprintf,
+};
diff --git a/src/expr/rt.c b/src/expr/rt.c
new file mode 100644
index 0000000..695a658
--- /dev/null
+++ b/src/expr/rt.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.com>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_rt {
+ enum nft_rt_keys key;
+ enum nft_registers dreg;
+};
+
+static int
+nftnl_expr_rt_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_RT_KEY:
+ memcpy(&rt->key, data, sizeof(rt->key));
+ break;
+ case NFTNL_EXPR_RT_DREG:
+ memcpy(&rt->dreg, data, sizeof(rt->dreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_rt_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_RT_KEY:
+ *data_len = sizeof(rt->key);
+ return &rt->key;
+ case NFTNL_EXPR_RT_DREG:
+ *data_len = sizeof(rt->dreg);
+ return &rt->dreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_rt_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_RT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_RT_KEY:
+ case NFTA_RT_DREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_rt_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_RT_KEY))
+ mnl_attr_put_u32(nlh, NFTA_RT_KEY, htonl(rt->key));
+ if (e->flags & (1 << NFTNL_EXPR_RT_DREG))
+ mnl_attr_put_u32(nlh, NFTA_RT_DREG, htonl(rt->dreg));
+}
+
+static int
+nftnl_expr_rt_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_RT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_rt_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_RT_KEY]) {
+ rt->key = ntohl(mnl_attr_get_u32(tb[NFTA_RT_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_RT_KEY);
+ }
+ if (tb[NFTA_RT_DREG]) {
+ rt->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_RT_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_RT_DREG);
+ }
+
+ return 0;
+}
+
+static const char *rt_key2str_array[NFT_RT_MAX + 1] = {
+ [NFT_RT_CLASSID] = "classid",
+ [NFT_RT_NEXTHOP4] = "nexthop4",
+ [NFT_RT_NEXTHOP6] = "nexthop6",
+ [NFT_RT_TCPMSS] = "tcpmss",
+ [NFT_RT_XFRM] = "ipsec",
+};
+
+static const char *rt_key2str(uint8_t key)
+{
+ if (key <= NFT_RT_MAX)
+ return rt_key2str_array[key];
+
+ return "unknown";
+}
+
+static inline int str2rt_key(const char *str)
+{
+ int i;
+
+ for (i = 0; i < NFT_RT_MAX; i++) {
+ if (strcmp(str, rt_key2str_array[i]) == 0)
+ return i;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+static int
+nftnl_expr_rt_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_RT_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ rt_key2str(rt->key), rt->dreg);
+ }
+ return 0;
+}
+
+struct expr_ops expr_ops_rt = {
+ .name = "rt",
+ .alloc_len = sizeof(struct nftnl_expr_rt),
+ .max_attr = NFTA_RT_MAX,
+ .set = nftnl_expr_rt_set,
+ .get = nftnl_expr_rt_get,
+ .parse = nftnl_expr_rt_parse,
+ .build = nftnl_expr_rt_build,
+ .output = nftnl_expr_rt_snprintf,
+};
diff --git a/src/expr/socket.c b/src/expr/socket.c
new file mode 100644
index 0000000..83045c0
--- /dev/null
+++ b/src/expr/socket.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2018 Máté Eckl <ecklm94@gmail.com>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_socket {
+ enum nft_socket_keys key;
+ enum nft_registers dreg;
+ uint32_t level;
+};
+
+static int
+nftnl_expr_socket_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_SOCKET_KEY:
+ memcpy(&socket->key, data, sizeof(socket->key));
+ break;
+ case NFTNL_EXPR_SOCKET_DREG:
+ memcpy(&socket->dreg, data, sizeof(socket->dreg));
+ break;
+ case NFTNL_EXPR_SOCKET_LEVEL:
+ memcpy(&socket->level, data, sizeof(socket->level));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_socket_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_SOCKET_KEY:
+ *data_len = sizeof(socket->key);
+ return &socket->key;
+ case NFTNL_EXPR_SOCKET_DREG:
+ *data_len = sizeof(socket->dreg);
+ return &socket->dreg;
+ case NFTNL_EXPR_SOCKET_LEVEL:
+ *data_len = sizeof(socket->level);
+ return &socket->level;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_socket_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SOCKET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_SOCKET_KEY:
+ case NFTA_SOCKET_DREG:
+ case NFTA_SOCKET_LEVEL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_socket_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_KEY))
+ mnl_attr_put_u32(nlh, NFTA_SOCKET_KEY, htonl(socket->key));
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG))
+ mnl_attr_put_u32(nlh, NFTA_SOCKET_DREG, htonl(socket->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_LEVEL))
+ mnl_attr_put_u32(nlh, NFTA_SOCKET_LEVEL, htonl(socket->level));
+}
+
+static int
+nftnl_expr_socket_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_SOCKET_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_socket_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SOCKET_KEY]) {
+ socket->key = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_SOCKET_KEY);
+ }
+ if (tb[NFTA_SOCKET_DREG]) {
+ socket->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_SOCKET_DREG);
+ }
+ if (tb[NFTA_SOCKET_LEVEL]) {
+ socket->level = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_LEVEL]));
+ e->flags |= (1 << NFTNL_EXPR_SOCKET_LEVEL);
+ }
+
+ return 0;
+}
+
+static const char *socket_key2str_array[NFT_SOCKET_MAX + 1] = {
+ [NFT_SOCKET_TRANSPARENT] = "transparent",
+ [NFT_SOCKET_MARK] = "mark",
+ [NFT_SOCKET_WILDCARD] = "wildcard",
+ [NFT_SOCKET_CGROUPV2] = "cgroupv2",
+};
+
+static const char *socket_key2str(uint8_t key)
+{
+ if (key < NFT_SOCKET_MAX + 1)
+ return socket_key2str_array[key];
+
+ return "unknown";
+}
+
+static int
+nftnl_expr_socket_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ socket_key2str(socket->key), socket->dreg);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_LEVEL))
+ return snprintf(buf, len, "level %u ", socket->level);
+
+ return 0;
+}
+
+struct expr_ops expr_ops_socket = {
+ .name = "socket",
+ .alloc_len = sizeof(struct nftnl_expr_socket),
+ .max_attr = NFTA_SOCKET_MAX,
+ .set = nftnl_expr_socket_set,
+ .get = nftnl_expr_socket_get,
+ .parse = nftnl_expr_socket_parse,
+ .build = nftnl_expr_socket_build,
+ .output = nftnl_expr_socket_snprintf,
+};
diff --git a/src/expr/synproxy.c b/src/expr/synproxy.c
new file mode 100644
index 0000000..47fcaef
--- /dev/null
+++ b/src/expr/synproxy.c
@@ -0,0 +1,156 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_synproxy {
+ uint16_t mss;
+ uint8_t wscale;
+ uint32_t flags;
+};
+
+static int nftnl_expr_synproxy_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_SYNPROXY_MSS:
+ memcpy(&synproxy->mss, data, sizeof(synproxy->mss));
+ break;
+ case NFTNL_EXPR_SYNPROXY_WSCALE:
+ memcpy(&synproxy->wscale, data, sizeof(synproxy->wscale));
+ break;
+ case NFTNL_EXPR_SYNPROXY_FLAGS:
+ memcpy(&synproxy->flags, data, sizeof(synproxy->flags));
+ break;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_synproxy_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_SYNPROXY_MSS:
+ *data_len = sizeof(synproxy->mss);
+ return &synproxy->mss;
+ case NFTNL_EXPR_SYNPROXY_WSCALE:
+ *data_len = sizeof(synproxy->wscale);
+ return &synproxy->wscale;
+ case NFTNL_EXPR_SYNPROXY_FLAGS:
+ *data_len = sizeof(synproxy->flags);
+ return &synproxy->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_synproxy_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SYNPROXY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTNL_EXPR_SYNPROXY_MSS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+
+ case NFTNL_EXPR_SYNPROXY_WSCALE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+
+ case NFTNL_EXPR_SYNPROXY_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_synproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_MSS))
+ mnl_attr_put_u16(nlh, NFTNL_EXPR_SYNPROXY_MSS,
+ htons(synproxy->mss));
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_WSCALE))
+ mnl_attr_put_u8(nlh, NFTNL_EXPR_SYNPROXY_WSCALE,
+ synproxy->wscale);
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_FLAGS))
+ mnl_attr_put_u32(nlh, NFTNL_EXPR_SYNPROXY_FLAGS,
+ htonl(synproxy->flags));
+}
+
+static int
+nftnl_expr_synproxy_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_SYNPROXY_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_synproxy_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SYNPROXY_MSS]) {
+ synproxy->mss = ntohs(mnl_attr_get_u16(tb[NFTA_SYNPROXY_MSS]));
+ e->flags |= (1 << NFTNL_EXPR_SYNPROXY_MSS);
+ }
+
+ if (tb[NFTA_SYNPROXY_WSCALE]) {
+ synproxy->wscale = mnl_attr_get_u8(tb[NFTA_SYNPROXY_WSCALE]);
+ e->flags |= (1 << NFTNL_EXPR_SYNPROXY_WSCALE);
+ }
+
+ if (tb[NFTA_SYNPROXY_FLAGS]) {
+ synproxy->flags = ntohl(mnl_attr_get_u32(tb[NFTA_SYNPROXY_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_SYNPROXY_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_synproxy_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_MSS) &&
+ e->flags & (1 << NFTNL_EXPR_SYNPROXY_WSCALE)) {
+ ret = snprintf(buf, len, "mss %u wscale %u ", synproxy->mss,
+ synproxy->wscale);
+ SNPRINTF_BUFFER_SIZE(ret, len, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_synproxy = {
+ .name = "synproxy",
+ .alloc_len = sizeof(struct nftnl_expr_synproxy),
+ .max_attr = NFTA_SYNPROXY_MAX,
+ .set = nftnl_expr_synproxy_set,
+ .get = nftnl_expr_synproxy_get,
+ .parse = nftnl_expr_synproxy_parse,
+ .build = nftnl_expr_synproxy_build,
+ .output = nftnl_expr_synproxy_snprintf,
+};
diff --git a/src/expr/target.c b/src/expr/target.c
new file mode 100644
index 0000000..2a3fe8a
--- /dev/null
+++ b/src/expr/target.c
@@ -0,0 +1,193 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+/* From include/linux/netfilter/x_tables.h */
+#define XT_EXTENSION_MAXNAMELEN 29
+
+struct nftnl_expr_target {
+ char name[XT_EXTENSION_MAXNAMELEN];
+ uint32_t rev;
+ uint32_t data_len;
+ const void *data;
+};
+
+static int
+nftnl_expr_target_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_target *tg = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TG_NAME:
+ snprintf(tg->name, sizeof(tg->name), "%.*s", data_len,
+ (const char *) data);
+ break;
+ case NFTNL_EXPR_TG_REV:
+ memcpy(&tg->rev, data, sizeof(tg->rev));
+ break;
+ case NFTNL_EXPR_TG_INFO:
+ if (e->flags & (1 << NFTNL_EXPR_TG_INFO))
+ xfree(tg->data);
+
+ tg->data = data;
+ tg->data_len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_target_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_target *tg = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TG_NAME:
+ *data_len = sizeof(tg->name);
+ return tg->name;
+ case NFTNL_EXPR_TG_REV:
+ *data_len = sizeof(tg->rev);
+ return &tg->rev;
+ case NFTNL_EXPR_TG_INFO:
+ *data_len = tg->data_len;
+ return tg->data;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_target_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TARGET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TARGET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TARGET_REV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TARGET_INFO:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_target_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_target *tg = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TG_NAME))
+ mnl_attr_put_strz(nlh, NFTA_TARGET_NAME, tg->name);
+ if (e->flags & (1 << NFTNL_EXPR_TG_REV))
+ mnl_attr_put_u32(nlh, NFTA_TARGET_REV, htonl(tg->rev));
+ if (e->flags & (1 << NFTNL_EXPR_TG_INFO))
+ mnl_attr_put(nlh, NFTA_TARGET_INFO, tg->data_len, tg->data);
+}
+
+static int nftnl_expr_target_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_target *target = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_TARGET_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_target_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TARGET_NAME]) {
+ snprintf(target->name, XT_EXTENSION_MAXNAMELEN, "%s",
+ mnl_attr_get_str(tb[NFTA_TARGET_NAME]));
+
+ target->name[XT_EXTENSION_MAXNAMELEN-1] = '\0';
+ e->flags |= (1 << NFTNL_EXPR_TG_NAME);
+ }
+
+ if (tb[NFTA_TARGET_REV]) {
+ target->rev = ntohl(mnl_attr_get_u32(tb[NFTA_TARGET_REV]));
+ e->flags |= (1 << NFTNL_EXPR_TG_REV);
+ }
+
+ if (tb[NFTA_TARGET_INFO]) {
+ uint32_t len = mnl_attr_get_payload_len(tb[NFTA_TARGET_INFO]);
+ void *target_data;
+
+ if (target->data)
+ xfree(target->data);
+
+ target_data = calloc(1, len);
+ if (target_data == NULL)
+ return -1;
+
+ memcpy(target_data, mnl_attr_get_payload(tb[NFTA_TARGET_INFO]), len);
+
+ target->data = target_data;
+ target->data_len = len;
+
+ e->flags |= (1 << NFTNL_EXPR_TG_INFO);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_target_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_target *target = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "name %s rev %u ", target->name, target->rev);
+}
+
+static void nftnl_expr_target_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_target *target = nftnl_expr_data(e);
+
+ xfree(target->data);
+}
+
+struct expr_ops expr_ops_target = {
+ .name = "target",
+ .alloc_len = sizeof(struct nftnl_expr_target),
+ .max_attr = NFTA_TARGET_MAX,
+ .free = nftnl_expr_target_free,
+ .set = nftnl_expr_target_set,
+ .get = nftnl_expr_target_get,
+ .parse = nftnl_expr_target_parse,
+ .build = nftnl_expr_target_build,
+ .output = nftnl_expr_target_snprintf,
+};
diff --git a/src/expr/tproxy.c b/src/expr/tproxy.c
new file mode 100644
index 0000000..bd5ffbf
--- /dev/null
+++ b/src/expr/tproxy.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2018 Máté Eckl <ecklm94@gmail.com>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_tproxy {
+ enum nft_registers sreg_addr;
+ enum nft_registers sreg_port;
+ int family;
+};
+
+static int
+nftnl_expr_tproxy_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TPROXY_FAMILY:
+ memcpy(&tproxy->family, data, sizeof(tproxy->family));
+ break;
+ case NFTNL_EXPR_TPROXY_REG_ADDR:
+ memcpy(&tproxy->sreg_addr, data, sizeof(tproxy->sreg_addr));
+ break;
+ case NFTNL_EXPR_TPROXY_REG_PORT:
+ memcpy(&tproxy->sreg_port, data, sizeof(tproxy->sreg_port));
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static const void *
+nftnl_expr_tproxy_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TPROXY_FAMILY:
+ *data_len = sizeof(tproxy->family);
+ return &tproxy->family;
+ case NFTNL_EXPR_TPROXY_REG_ADDR:
+ *data_len = sizeof(tproxy->sreg_addr);
+ return &tproxy->sreg_addr;
+ case NFTNL_EXPR_TPROXY_REG_PORT:
+ *data_len = sizeof(tproxy->sreg_port);
+ return &tproxy->sreg_port;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_tproxy_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TPROXY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TPROXY_FAMILY:
+ case NFTA_TPROXY_REG_ADDR:
+ case NFTA_TPROXY_REG_PORT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_expr_tproxy_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_TPROXY_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_tproxy_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TPROXY_FAMILY]) {
+ tproxy->family = ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_FAMILY]));
+ e->flags |= (1 << NFTNL_EXPR_TPROXY_FAMILY);
+ }
+ if (tb[NFTA_TPROXY_REG_ADDR]) {
+ tproxy->sreg_addr =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_REG_ADDR]));
+ e->flags |= (1 << NFTNL_EXPR_TPROXY_REG_ADDR);
+ }
+ if (tb[NFTA_TPROXY_REG_PORT]) {
+ tproxy->sreg_port =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_REG_PORT]));
+ e->flags |= (1 << NFTNL_EXPR_TPROXY_REG_PORT);
+ }
+
+ return 0;
+}
+
+static void
+nftnl_expr_tproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_FAMILY))
+ mnl_attr_put_u32(nlh, NFTA_TPROXY_FAMILY, htonl(tproxy->family));
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR))
+ mnl_attr_put_u32(nlh, NFTA_TPROXY_REG_ADDR,
+ htonl(tproxy->sreg_addr));
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT))
+ mnl_attr_put_u32(nlh, NFTA_TPROXY_REG_PORT,
+ htonl(tproxy->sreg_port));
+}
+
+static int
+nftnl_expr_tproxy_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+ int offset = 0, ret = 0;
+
+ if (tproxy->family != NFTA_TPROXY_UNSPEC) {
+ ret = snprintf(buf + offset, remain, "%s ",
+ nftnl_family2str(tproxy->family));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR)) {
+ ret = snprintf(buf + offset, remain,
+ "addr reg %u ", tproxy->sreg_addr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT)) {
+ ret = snprintf(buf + offset, remain,
+ "port reg %u ", tproxy->sreg_port);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_tproxy = {
+ .name = "tproxy",
+ .alloc_len = sizeof(struct nftnl_expr_tproxy),
+ .max_attr = NFTA_TPROXY_MAX,
+ .set = nftnl_expr_tproxy_set,
+ .get = nftnl_expr_tproxy_get,
+ .parse = nftnl_expr_tproxy_parse,
+ .build = nftnl_expr_tproxy_build,
+ .output = nftnl_expr_tproxy_snprintf,
+};
diff --git a/src/expr/tunnel.c b/src/expr/tunnel.c
new file mode 100644
index 0000000..a00f620
--- /dev/null
+++ b/src/expr/tunnel.c
@@ -0,0 +1,149 @@
+/*
+ * (C) 2018 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_tunnel {
+ enum nft_tunnel_keys key;
+ enum nft_registers dreg;
+};
+
+static int nftnl_expr_tunnel_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TUNNEL_KEY:
+ memcpy(&tunnel->key, data, sizeof(tunnel->key));
+ break;
+ case NFTNL_EXPR_TUNNEL_DREG:
+ memcpy(&tunnel->dreg, data, sizeof(tunnel->dreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_tunnel_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TUNNEL_KEY:
+ *data_len = sizeof(tunnel->key);
+ return &tunnel->key;
+ case NFTNL_EXPR_TUNNEL_DREG:
+ *data_len = sizeof(tunnel->dreg);
+ return &tunnel->dreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_tunnel_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TUNNEL_KEY:
+ case NFTA_TUNNEL_DREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_tunnel_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TUNNEL_KEY))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY, htonl(tunnel->key));
+ if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_DREG, htonl(tunnel->dreg));
+}
+
+static int
+nftnl_expr_tunnel_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_TUNNEL_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_tunnel_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY]) {
+ tunnel->key = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_TUNNEL_KEY);
+ }
+ if (tb[NFTA_TUNNEL_DREG]) {
+ tunnel->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_TUNNEL_DREG);
+ }
+
+ return 0;
+}
+
+static const char *tunnel_key2str_array[NFT_TUNNEL_MAX + 1] = {
+ [NFT_TUNNEL_PATH] = "path",
+ [NFT_TUNNEL_ID] = "id",
+};
+
+static const char *tunnel_key2str(uint8_t key)
+{
+ if (key <= NFT_TUNNEL_MAX)
+ return tunnel_key2str_array[key];
+
+ return "unknown";
+}
+
+static int
+nftnl_expr_tunnel_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ tunnel_key2str(tunnel->key), tunnel->dreg);
+ }
+ return 0;
+}
+
+struct expr_ops expr_ops_tunnel = {
+ .name = "tunnel",
+ .alloc_len = sizeof(struct nftnl_expr_tunnel),
+ .max_attr = NFTA_TUNNEL_MAX,
+ .set = nftnl_expr_tunnel_set,
+ .get = nftnl_expr_tunnel_get,
+ .parse = nftnl_expr_tunnel_parse,
+ .build = nftnl_expr_tunnel_build,
+ .output = nftnl_expr_tunnel_snprintf,
+};
diff --git a/src/expr/xfrm.c b/src/expr/xfrm.c
new file mode 100644
index 0000000..2db00d5
--- /dev/null
+++ b/src/expr/xfrm.c
@@ -0,0 +1,200 @@
+/*
+ * This program is free software; you can redistribute 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/xfrm.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_xfrm {
+ enum nft_registers dreg;
+ enum nft_xfrm_keys key;
+ uint32_t spnum;
+ uint8_t dir;
+};
+
+static int
+nftnl_expr_xfrm_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_XFRM_KEY:
+ memcpy(&x->key, data, sizeof(x->key));
+ break;
+ case NFTNL_EXPR_XFRM_DIR:
+ memcpy(&x->dir, data, sizeof(x->dir));
+ break;
+ case NFTNL_EXPR_XFRM_SPNUM:
+ memcpy(&x->spnum, data, sizeof(x->spnum));
+ break;
+ case NFTNL_EXPR_XFRM_DREG:
+ memcpy(&x->dreg, data, sizeof(x->dreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_xfrm_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_XFRM_KEY:
+ *data_len = sizeof(x->key);
+ return &x->key;
+ case NFTNL_EXPR_XFRM_DIR:
+ *data_len = sizeof(x->dir);
+ return &x->dir;
+ case NFTNL_EXPR_XFRM_SPNUM:
+ *data_len = sizeof(x->spnum);
+ return &x->spnum;
+ case NFTNL_EXPR_XFRM_DREG:
+ *data_len = sizeof(x->dreg);
+ return &x->dreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_xfrm_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_XFRM_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_XFRM_DREG:
+ case NFTA_XFRM_KEY:
+ case NFTA_XFRM_SPNUM:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_XFRM_DIR:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_xfrm_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_KEY))
+ mnl_attr_put_u32(nlh, NFTA_XFRM_KEY, htonl(x->key));
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_DIR))
+ mnl_attr_put_u8(nlh, NFTA_XFRM_DIR, x->dir);
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_SPNUM))
+ mnl_attr_put_u32(nlh, NFTA_XFRM_SPNUM, htonl(x->spnum));
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG))
+ mnl_attr_put_u32(nlh, NFTA_XFRM_DREG, htonl(x->dreg));
+}
+
+static int
+nftnl_expr_xfrm_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_XFRM_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_xfrm_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_XFRM_KEY]) {
+ x->key = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_XFRM_KEY);
+ }
+ if (tb[NFTA_XFRM_DIR]) {
+ x->dir = mnl_attr_get_u8(tb[NFTA_XFRM_DIR]);
+ e->flags |= (1 << NFTNL_EXPR_XFRM_DIR);
+ }
+ if (tb[NFTA_XFRM_SPNUM]) {
+ x->spnum = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_SPNUM]));
+ e->flags |= (1 << NFTNL_EXPR_XFRM_SPNUM);
+ }
+ if (tb[NFTA_XFRM_DREG]) {
+ x->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_XFRM_DREG);
+ }
+ return 0;
+}
+
+static const char *xfrmkey2str_array[] = {
+ [NFT_XFRM_KEY_DADDR_IP4] = "daddr4",
+ [NFT_XFRM_KEY_SADDR_IP4] = "saddr4",
+ [NFT_XFRM_KEY_DADDR_IP6] = "daddr6",
+ [NFT_XFRM_KEY_SADDR_IP6] = "saddr6",
+ [NFT_XFRM_KEY_REQID] = "reqid",
+ [NFT_XFRM_KEY_SPI] = "spi",
+};
+
+static const char *xfrmkey2str(uint32_t key)
+{
+ if (key >= sizeof(xfrmkey2str_array) / sizeof(xfrmkey2str_array[0]))
+ return "unknown";
+
+ return xfrmkey2str_array[key];
+}
+
+static const char *xfrmdir2str_array[] = {
+ [XFRM_POLICY_IN] = "in",
+ [XFRM_POLICY_OUT] = "out",
+};
+
+static const char *xfrmdir2str(uint8_t dir)
+{
+ if (dir >= sizeof(xfrmdir2str_array) / sizeof(xfrmdir2str_array[0]))
+ return "unknown";
+
+ return xfrmdir2str_array[dir];
+}
+
+static int
+nftnl_expr_xfrm_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG)) {
+ ret = snprintf(buf, remain, "load %s %u %s => reg %u ",
+ xfrmdir2str(x->dir),
+ x->spnum,
+ xfrmkey2str(x->key), x->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ return offset;
+}
+
+struct expr_ops expr_ops_xfrm = {
+ .name = "xfrm",
+ .alloc_len = sizeof(struct nftnl_expr_xfrm),
+ .max_attr = NFTA_XFRM_MAX,
+ .set = nftnl_expr_xfrm_set,
+ .get = nftnl_expr_xfrm_get,
+ .parse = nftnl_expr_xfrm_parse,
+ .build = nftnl_expr_xfrm_build,
+ .output = nftnl_expr_xfrm_snprintf,
+};
diff --git a/src/expr_ops.c b/src/expr_ops.c
new file mode 100644
index 0000000..b85f472
--- /dev/null
+++ b/src/expr_ops.c
@@ -0,0 +1,106 @@
+#include <string.h>
+#include <linux_list.h>
+
+#include "expr_ops.h"
+
+/* Unfortunately, __attribute__((constructor)) breaks library static linking */
+extern struct expr_ops expr_ops_bitwise;
+extern struct expr_ops expr_ops_byteorder;
+extern struct expr_ops expr_ops_cmp;
+extern struct expr_ops expr_ops_connlimit;
+extern struct expr_ops expr_ops_counter;
+extern struct expr_ops expr_ops_ct;
+extern struct expr_ops expr_ops_dup;
+extern struct expr_ops expr_ops_exthdr;
+extern struct expr_ops expr_ops_fwd;
+extern struct expr_ops expr_ops_immediate;
+extern struct expr_ops expr_ops_inner;
+extern struct expr_ops expr_ops_last;
+extern struct expr_ops expr_ops_limit;
+extern struct expr_ops expr_ops_log;
+extern struct expr_ops expr_ops_lookup;
+extern struct expr_ops expr_ops_masq;
+extern struct expr_ops expr_ops_match;
+extern struct expr_ops expr_ops_meta;
+extern struct expr_ops expr_ops_ng;
+extern struct expr_ops expr_ops_nat;
+extern struct expr_ops expr_ops_tproxy;
+extern struct expr_ops expr_ops_objref;
+extern struct expr_ops expr_ops_payload;
+extern struct expr_ops expr_ops_range;
+extern struct expr_ops expr_ops_redir;
+extern struct expr_ops expr_ops_reject;
+extern struct expr_ops expr_ops_rt;
+extern struct expr_ops expr_ops_queue;
+extern struct expr_ops expr_ops_quota;
+extern struct expr_ops expr_ops_target;
+extern struct expr_ops expr_ops_dynset;
+extern struct expr_ops expr_ops_hash;
+extern struct expr_ops expr_ops_fib;
+extern struct expr_ops expr_ops_flow;
+extern struct expr_ops expr_ops_socket;
+extern struct expr_ops expr_ops_synproxy;
+extern struct expr_ops expr_ops_tunnel;
+extern struct expr_ops expr_ops_osf;
+extern struct expr_ops expr_ops_xfrm;
+
+static struct expr_ops expr_ops_notrack = {
+ .name = "notrack",
+};
+
+static struct expr_ops *expr_ops[] = {
+ &expr_ops_bitwise,
+ &expr_ops_byteorder,
+ &expr_ops_cmp,
+ &expr_ops_connlimit,
+ &expr_ops_counter,
+ &expr_ops_ct,
+ &expr_ops_dup,
+ &expr_ops_exthdr,
+ &expr_ops_fwd,
+ &expr_ops_immediate,
+ &expr_ops_inner,
+ &expr_ops_last,
+ &expr_ops_limit,
+ &expr_ops_log,
+ &expr_ops_lookup,
+ &expr_ops_masq,
+ &expr_ops_match,
+ &expr_ops_meta,
+ &expr_ops_ng,
+ &expr_ops_nat,
+ &expr_ops_tproxy,
+ &expr_ops_notrack,
+ &expr_ops_payload,
+ &expr_ops_range,
+ &expr_ops_redir,
+ &expr_ops_reject,
+ &expr_ops_rt,
+ &expr_ops_queue,
+ &expr_ops_quota,
+ &expr_ops_target,
+ &expr_ops_dynset,
+ &expr_ops_hash,
+ &expr_ops_fib,
+ &expr_ops_objref,
+ &expr_ops_flow,
+ &expr_ops_socket,
+ &expr_ops_synproxy,
+ &expr_ops_tunnel,
+ &expr_ops_osf,
+ &expr_ops_xfrm,
+ NULL,
+};
+
+struct expr_ops *nftnl_expr_ops_lookup(const char *name)
+{
+ int i = 0;
+
+ while (expr_ops[i] != NULL) {
+ if (strcmp(expr_ops[i]->name, name) == 0)
+ return expr_ops[i];
+
+ i++;
+ }
+ return NULL;
+}