summaryrefslogtreecommitdiffstats
path: root/src/expr
diff options
context:
space:
mode:
Diffstat (limited to 'src/expr')
-rw-r--r--src/expr/cmp.c194
-rw-r--r--src/expr/counter.c145
-rw-r--r--src/expr/data_reg.c158
-rw-r--r--src/expr/data_reg.h23
-rw-r--r--src/expr/immediate.c215
-rw-r--r--src/expr/match.c203
-rw-r--r--src/expr/meta.c144
-rw-r--r--src/expr/payload.c185
-rw-r--r--src/expr/target.c203
9 files changed, 1470 insertions, 0 deletions
diff --git a/src/expr/cmp.c b/src/expr/cmp.c
new file mode 100644
index 0000000..cabaddd
--- /dev/null
+++ b/src/expr/cmp.c
@@ -0,0 +1,194 @@
+/*
+ * (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 <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftables/expr.h>
+#include "expr_ops.h"
+#include "data_reg.h"
+
+struct nft_expr_cmp {
+ union nft_data_reg data;
+ uint8_t sreg; /* enum nft_registers */
+ uint8_t op; /* enum nft_cmp_ops */
+};
+
+static int
+nft_rule_expr_cmp_set(struct nft_rule_expr *e, uint16_t type,
+ const void *data, size_t data_len)
+{
+ struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_CMP_SREG:
+ cmp->sreg = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_CMP_OP:
+ cmp->op = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_CMP_DATA:
+ memcpy(&cmp->data.val, data, data_len);
+ cmp->data.len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nft_rule_expr_cmp_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len)
+{
+ struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_CMP_SREG:
+ if (e->flags & (1 << NFT_EXPR_CMP_SREG)) {
+ *data_len = sizeof(cmp->sreg);
+ return &cmp->sreg;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_CMP_OP:
+ if (e->flags & (1 << NFT_EXPR_CMP_OP)) {
+ *data_len = sizeof(cmp->op);
+ return &cmp->op;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_CMP_DATA:
+ if (e->flags & (1 << NFT_EXPR_CMP_DATA)) {
+ *data_len = cmp->data.len;
+ return &cmp->data.val;
+ } else
+ return NULL;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int nft_rule_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) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_CMP_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nft_rule_expr_cmp_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
+{
+ struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data;
+
+ if (e->flags & (1 << NFT_EXPR_CMP_SREG))
+ mnl_attr_put_u32(nlh, NFTA_CMP_SREG, htonl(cmp->sreg));
+ if (e->flags & (1 << NFT_EXPR_CMP_OP))
+ mnl_attr_put_u32(nlh, NFTA_CMP_OP, htonl(cmp->op));
+ if (e->flags & (1 << NFT_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
+nft_rule_expr_cmp_parse(struct nft_rule_expr *e, struct nlattr *attr)
+{
+ struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data;
+ struct nlattr *tb[NFTA_CMP_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nft_rule_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 = nft_parse_data(&cmp->data, tb[NFTA_CMP_DATA], NULL);
+ e->flags |= (1 << NFTA_CMP_DATA);
+ }
+
+ return ret;
+}
+
+static 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 int
+nft_rule_expr_cmp_snprintf(char *buf, size_t size, struct nft_rule_expr *e)
+{
+ struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data;
+ int len = size, offset = 0, ret, i;
+
+ ret = snprintf(buf, len, "sreg=%u op=%s data=",
+ cmp->sreg, expr_cmp_str[cmp->op]);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ for (i=0; i<cmp->data.len/sizeof(uint32_t); i++) {
+ ret = snprintf(buf+offset, len, "%.8x ", cmp->data.val[i]);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+ return offset;
+}
+
+struct expr_ops expr_ops_cmp = {
+ .name = "cmp",
+ .alloc_len = sizeof(struct nft_expr_cmp),
+ .max_attr = NFTA_CMP_MAX,
+ .set = nft_rule_expr_cmp_set,
+ .get = nft_rule_expr_cmp_get,
+ .parse = nft_rule_expr_cmp_parse,
+ .build = nft_rule_expr_cmp_build,
+ .snprintf = nft_rule_expr_cmp_snprintf,
+};
diff --git a/src/expr/counter.c b/src/expr/counter.c
new file mode 100644
index 0000000..26b7d6f
--- /dev/null
+++ b/src/expr/counter.c
@@ -0,0 +1,145 @@
+/*
+ * (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 <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftables/expr.h>
+#include "expr_ops.h"
+
+struct nft_expr_counter {
+ uint64_t pkts;
+ uint64_t bytes;
+};
+
+static int
+nft_rule_expr_counter_set(struct nft_rule_expr *e, uint16_t type,
+ const void *data, size_t data_len)
+{
+ struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_CTR_BYTES:
+ ctr->bytes = *((uint64_t *)data);
+ break;
+ case NFT_EXPR_CTR_PACKETS:
+ ctr->pkts = *((uint64_t *)data);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nft_rule_expr_counter_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len)
+{
+ struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_CTR_BYTES:
+ if (e->flags & (1 << NFT_EXPR_CTR_BYTES)) {
+ *data_len = sizeof(ctr->bytes);
+ return &ctr->bytes;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_CTR_PACKETS:
+ if (e->flags & (1 << NFT_EXPR_CTR_PACKETS)) {
+ *data_len = sizeof(ctr->pkts);
+ return &ctr->pkts;
+ } else
+ return NULL;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int nft_rule_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) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nft_rule_expr_counter_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
+{
+ struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data;
+
+ if (e->flags & (1 << NFT_EXPR_CTR_BYTES))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes));
+ if (e->flags & (1 << NFT_EXPR_CTR_PACKETS))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts));
+}
+
+static int
+nft_rule_expr_counter_parse(struct nft_rule_expr *e, struct nlattr *attr)
+{
+ struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data;
+ struct nlattr *tb[NFTA_COUNTER_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nft_rule_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 << NFT_EXPR_CTR_BYTES);
+ }
+ if (tb[NFTA_COUNTER_PACKETS]) {
+ ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS]));
+ e->flags |= (1 << NFT_EXPR_CTR_PACKETS);
+ }
+
+ return 0;
+}
+
+static int
+nft_rule_expr_counter_snprintf(char *buf, size_t len, struct nft_rule_expr *e)
+{
+ struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data;
+
+ return snprintf(buf, len, "pkts=%lu bytes=%lu ",
+ ctr->pkts, ctr->bytes);
+}
+
+struct expr_ops expr_ops_counter = {
+ .name = "counter",
+ .alloc_len = sizeof(struct nft_expr_counter),
+ .max_attr = NFTA_COUNTER_MAX,
+ .set = nft_rule_expr_counter_set,
+ .get = nft_rule_expr_counter_get,
+ .parse = nft_rule_expr_counter_parse,
+ .build = nft_rule_expr_counter_build,
+ .snprintf = nft_rule_expr_counter_snprintf,
+};
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
new file mode 100644
index 0000000..5b14695
--- /dev/null
+++ b/src/expr/data_reg.c
@@ -0,0 +1,158 @@
+/*
+ * (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 <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftables/expr.h>
+#include "expr_ops.h"
+#include "data_reg.h"
+#include "internal.h"
+
+static int nft_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) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_DATA_VERDICT:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nft_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:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_VERDICT_CHAIN:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nft_parse_verdict(union nft_data_reg *data, const struct nlattr *attr, int *type)
+{
+ struct nlattr *tb[NFTA_VERDICT_MAX+1];
+
+ if (mnl_attr_parse_nested(attr, nft_verdict_parse_cb, tb) < 0) {
+ perror("mnl_attr_parse_nested");
+ 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 (type)
+ *type = DATA_CHAIN;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+__nft_parse_data(union nft_data_reg *data, const struct nlattr *attr)
+{
+ void *orig = mnl_attr_get_payload(attr);
+ size_t data_len = mnl_attr_get_payload_len(attr);
+
+ if (data_len == 0)
+ return -1;
+
+ if (data_len > sizeof(uint32_t) * 4)
+ return -1;
+
+ memcpy(data->val, orig, data_len);
+ data->len = data_len;
+
+ return 0;
+}
+
+int nft_parse_data(union nft_data_reg *data, struct nlattr *attr, int *type)
+{
+ struct nlattr *tb[NFTA_DATA_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nft_data_parse_cb, tb) < 0) {
+ perror("mnl_attr_parse_nested");
+ return -1;
+ }
+ if (tb[NFTA_DATA_VALUE]) {
+ if (type)
+ *type = DATA_VALUE;
+
+ ret = __nft_parse_data(data, tb[NFTA_DATA_VALUE]);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_DATA_VERDICT])
+ ret = nft_parse_verdict(data, tb[NFTA_DATA_VERDICT], type);
+
+ return ret;
+}
diff --git a/src/expr/data_reg.h b/src/expr/data_reg.h
new file mode 100644
index 0000000..00eab63
--- /dev/null
+++ b/src/expr/data_reg.h
@@ -0,0 +1,23 @@
+#ifndef _DATA_H_
+#define _DATA_H_
+
+enum {
+ DATA_VALUE,
+ DATA_VERDICT,
+ DATA_CHAIN,
+};
+
+union nft_data_reg {
+ struct {
+ uint32_t val[4];
+ size_t len;
+ };
+ struct {
+ int verdict;
+ char *chain;
+ };
+};
+
+int nft_parse_data(union nft_data_reg *data, struct nlattr *attr, int *type);
+
+#endif
diff --git a/src/expr/immediate.c b/src/expr/immediate.c
new file mode 100644
index 0000000..68c27ec
--- /dev/null
+++ b/src/expr/immediate.c
@@ -0,0 +1,215 @@
+/*
+ * (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 "internal.h"
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftables/expr.h>
+#include "expr_ops.h"
+#include "data_reg.h"
+
+struct nft_expr_immediate {
+ union nft_data_reg data;
+ enum nft_registers dreg;
+};
+
+static int
+nft_rule_expr_immediate_set(struct nft_rule_expr *e, uint16_t type,
+ const void *data, size_t data_len)
+{
+ struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_IMM_DREG:
+ imm->dreg = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_IMM_DATA:
+ memcpy(&imm->data.val, data, data_len);
+ imm->data.len = data_len;
+ break;
+ case NFT_EXPR_IMM_VERDICT:
+ imm->data.verdict = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_IMM_CHAIN:
+ if (imm->data.chain)
+ free(imm->data.chain);
+
+ imm->data.chain = strdup(data);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nft_rule_expr_immediate_get(struct nft_rule_expr *e, uint16_t type,
+ size_t *data_len)
+{
+ struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_IMM_DREG:
+ if (e->flags & (1 << NFT_EXPR_IMM_DREG)) {
+ *data_len = sizeof(imm->dreg);
+ return &imm->dreg;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_IMM_DATA:
+ if (e->flags & (1 << NFT_EXPR_IMM_DATA)) {
+ *data_len = imm->data.len;
+ return &imm->data.val;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_IMM_VERDICT:
+ if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) {
+ *data_len = sizeof(imm->data.verdict);
+ return &imm->data.verdict;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_IMM_CHAIN:
+ if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) {
+ *data_len = strlen(imm->data.chain)+1;
+ return imm->data.chain;
+ } else
+ return NULL;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int nft_rule_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) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_IMMEDIATE_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nft_rule_expr_immediate_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
+{
+ struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data;
+
+ if (e->flags & (1 << NFT_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 << NFT_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 << NFT_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 << NFT_EXPR_IMM_CHAIN))
+ mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, imm->data.chain);
+
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+}
+
+static int
+nft_rule_expr_immediate_parse(struct nft_rule_expr *e, struct nlattr *attr)
+{
+ struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data;
+ struct nlattr *tb[NFTA_IMMEDIATE_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nft_rule_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 << NFT_EXPR_IMM_DREG);
+ }
+ if (tb[NFTA_IMMEDIATE_DATA]) {
+ int type;
+
+ ret = nft_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 << NFT_EXPR_IMM_DATA);
+ break;
+ case DATA_VERDICT:
+ /* NF_ACCEPT, NF_DROP, NF_QUEUE and NFT_RETURN case */
+ e->flags |= (1 << NFT_EXPR_IMM_VERDICT);
+ break;
+ case DATA_CHAIN:
+ /* NFT_GOTO and NFT_JUMP case */
+ e->flags |= (1 << NFT_EXPR_IMM_VERDICT) |
+ (1 << NFT_EXPR_IMM_CHAIN);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nft_rule_expr_immediate_snprintf(char *buf, size_t len, struct nft_rule_expr *e)
+{
+ struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data;
+
+ return snprintf(buf, len, "dreg=%u data=%u ",
+ imm->dreg, imm->data.val[0]);
+}
+
+struct expr_ops expr_ops_immediate = {
+ .name = "immediate",
+ .alloc_len = sizeof(struct nft_expr_immediate),
+ .max_attr = NFTA_IMMEDIATE_MAX,
+ .set = nft_rule_expr_immediate_set,
+ .get = nft_rule_expr_immediate_get,
+ .parse = nft_rule_expr_immediate_parse,
+ .build = nft_rule_expr_immediate_build,
+ .snprintf = nft_rule_expr_immediate_snprintf,
+};
diff --git a/src/expr/match.c b/src/expr/match.c
new file mode 100644
index 0000000..a9e2dee
--- /dev/null
+++ b/src/expr/match.c
@@ -0,0 +1,203 @@
+/*
+ * (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 <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/x_tables.h>
+
+#include <libnftables/expr.h>
+
+#include "expr_ops.h"
+
+struct nft_expr_match {
+ char name[XT_EXTENSION_MAXNAMELEN];
+ uint32_t rev;
+ uint32_t data_len;
+ const void *data;
+};
+
+static int
+nft_rule_expr_match_set(struct nft_rule_expr *e, uint16_t type,
+ const void *data, size_t data_len)
+{
+ struct nft_expr_match *mt = (struct nft_expr_match *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_MT_NAME:
+ memcpy(mt->name, data, XT_EXTENSION_MAXNAMELEN);
+ mt->name[XT_EXTENSION_MAXNAMELEN-1] = '\0';
+ break;
+ case NFT_EXPR_MT_REV:
+ mt->rev = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_MT_INFO:
+ if (mt->data)
+ free((void *)mt->data);
+
+ mt->data = data;
+ mt->data_len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nft_rule_expr_match_get(struct nft_rule_expr *e, uint16_t type,
+ size_t *data_len)
+{
+ struct nft_expr_match *mt = (struct nft_expr_match *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_MT_NAME:
+ if (e->flags & (1 << NFT_EXPR_MT_NAME)) {
+ *data_len = sizeof(mt->name);
+ return mt->name;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_MT_REV:
+ if (e->flags & (1 << NFT_EXPR_MT_REV)) {
+ *data_len = sizeof(mt->rev);
+ return &mt->rev;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_MT_INFO:
+ if (e->flags & (1 << NFT_EXPR_MT_INFO)) {
+ *data_len = mt->data_len;
+ return mt->data;
+ } else
+ return NULL;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int nft_rule_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) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_MATCH_REV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_MATCH_INFO:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nft_rule_expr_match_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
+{
+ struct nft_expr_match *mt = (struct nft_expr_match *)e->data;
+
+ if (e->flags & (1 << NFT_EXPR_MT_NAME))
+ mnl_attr_put_strz(nlh, NFTA_MATCH_NAME, mt->name);
+ if (e->flags & (1 << NFT_EXPR_MT_REV))
+ mnl_attr_put_u32(nlh, NFTA_MATCH_REV, htonl(mt->rev));
+ if (e->flags & (1 << NFT_EXPR_MT_INFO))
+ mnl_attr_put(nlh, NFTA_MATCH_INFO, XT_ALIGN(mt->data_len), mt->data);
+}
+
+static int nft_rule_expr_match_parse(struct nft_rule_expr *e, struct nlattr *attr)
+{
+ struct nft_expr_match *match = (struct nft_expr_match *)e->data;
+ struct nlattr *tb[NFTA_MATCH_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nft_rule_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 << NFTA_MATCH_NAME);
+ }
+
+ if (tb[NFTA_MATCH_REV]) {
+ match->rev = ntohl(mnl_attr_get_u32(tb[NFTA_MATCH_REV]));
+ e->flags |= (1 << NFTA_MATCH_REV);
+ }
+
+ if (tb[NFTA_MATCH_INFO]) {
+ uint32_t len = mnl_attr_get_payload_len(tb[NFTA_MATCH_INFO]);
+ void *match_data;
+
+ if (match->data)
+ free((void *) 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 << NFTA_MATCH_INFO);
+ }
+
+ return 0;
+}
+
+static int
+nft_rule_expr_match_snprintf(char *buf, size_t len, struct nft_rule_expr *e)
+{
+ struct nft_expr_match *match = (struct nft_expr_match *)e->data;
+
+ return snprintf(buf, len, "name=%s rev=%u ",
+ match->name, match->rev);
+}
+
+struct expr_ops expr_ops_match = {
+ .name = "match",
+ .alloc_len = sizeof(struct nft_expr_match),
+ .max_attr = NFTA_MATCH_MAX,
+ .set = nft_rule_expr_match_set,
+ .get = nft_rule_expr_match_get,
+ .parse = nft_rule_expr_match_parse,
+ .build = nft_rule_expr_match_build,
+ .snprintf = nft_rule_expr_match_snprintf,
+};
diff --git a/src/expr/meta.c b/src/expr/meta.c
new file mode 100644
index 0000000..0251e43
--- /dev/null
+++ b/src/expr/meta.c
@@ -0,0 +1,144 @@
+/*
+ * (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 <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftables/expr.h>
+#include "expr_ops.h"
+
+struct nft_expr_meta {
+ uint8_t key; /* enum nft_meta_keys */
+ uint8_t dreg; /* enum nft_registers */
+};
+
+static int
+nft_rule_expr_meta_set(struct nft_rule_expr *e, uint16_t type,
+ const void *data, size_t data_len)
+{
+ struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_META_KEY:
+ meta->key = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_META_DREG:
+ meta->dreg = *((uint32_t *)data);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nft_rule_expr_meta_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len)
+{
+ struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_META_KEY:
+ if (e->flags & (1 << NFT_EXPR_META_KEY)) {
+ *data_len = sizeof(meta->key);
+ return &meta->key;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_META_DREG:
+ if (e->flags & (1 << NFT_EXPR_META_DREG)) {
+ *data_len = sizeof(meta->dreg);
+ return &meta->dreg;
+ } else
+ return NULL;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int nft_rule_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:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nft_rule_expr_meta_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
+{
+ struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data;
+
+ if (e->flags & (1 << NFT_EXPR_META_KEY))
+ mnl_attr_put_u32(nlh, NFTA_META_KEY, htonl(meta->key));
+ if (e->flags & (1 << NFT_EXPR_META_DREG))
+ mnl_attr_put_u32(nlh, NFTA_META_DREG, htonl(meta->dreg));
+}
+
+static int
+nft_rule_expr_meta_parse(struct nft_rule_expr *e, struct nlattr *attr)
+{
+ struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data;
+ struct nlattr *tb[NFTA_META_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nft_rule_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 << NFT_EXPR_META_KEY);
+ }
+ if (tb[NFTA_META_DREG]) {
+ meta->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_DREG]));
+ e->flags |= (1 << NFT_EXPR_META_DREG);
+ }
+
+ return 0;
+}
+
+static int
+nft_rule_expr_meta_snprintf(char *buf, size_t len, struct nft_rule_expr *e)
+{
+ struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data;
+
+ return snprintf(buf, len, "dreg=%u key=%u ", meta->dreg, meta->key);
+}
+
+struct expr_ops expr_ops_meta = {
+ .name = "meta",
+ .alloc_len = sizeof(struct nft_expr_meta),
+ .max_attr = NFTA_META_MAX,
+ .set = nft_rule_expr_meta_set,
+ .get = nft_rule_expr_meta_get,
+ .parse = nft_rule_expr_meta_parse,
+ .build = nft_rule_expr_meta_build,
+ .snprintf = nft_rule_expr_meta_snprintf,
+};
diff --git a/src/expr/payload.c b/src/expr/payload.c
new file mode 100644
index 0000000..746a127
--- /dev/null
+++ b/src/expr/payload.c
@@ -0,0 +1,185 @@
+/*
+ * (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 <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftables/expr.h>
+
+#include "expr_ops.h"
+
+struct nft_expr_payload {
+ enum nft_registers dreg;
+ enum nft_payload_bases base;
+ unsigned int offset;
+ unsigned int len;
+};
+
+static int
+nft_rule_expr_payload_set(struct nft_rule_expr *e, uint16_t type,
+ const void *data, size_t data_len)
+{
+ struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_PAYLOAD_DREG:
+ payload->dreg = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_PAYLOAD_BASE:
+ payload->base = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_PAYLOAD_OFFSET:
+ payload->offset = *((unsigned int *)data);
+ break;
+ case NFT_EXPR_PAYLOAD_LEN:
+ payload->len = *((unsigned int *)data);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nft_rule_expr_payload_get(struct nft_rule_expr *e, uint16_t type, size_t *data_len)
+{
+ struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_PAYLOAD_DREG:
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG)) {
+ *data_len = sizeof(payload->dreg);
+ return &payload->dreg;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_PAYLOAD_BASE:
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE)) {
+ *data_len = sizeof(payload->base);
+ return &payload->base;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_PAYLOAD_OFFSET:
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET)) {
+ *data_len = sizeof(payload->offset);
+ return &payload->offset;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_PAYLOAD_LEN:
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN)) {
+ *data_len = sizeof(payload->len);
+ return &payload->len;
+ } else
+ return NULL;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int nft_rule_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_DREG:
+ case NFTA_PAYLOAD_BASE:
+ case NFTA_PAYLOAD_OFFSET:
+ case NFTA_PAYLOAD_LEN:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nft_rule_expr_payload_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
+{
+ struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data;
+
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg));
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base));
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset));
+ if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len));
+}
+
+static int
+nft_rule_expr_payload_parse(struct nft_rule_expr *e, struct nlattr *attr)
+{
+ struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data;
+ struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nft_rule_expr_payload_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_PAYLOAD_DREG]) {
+ payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG]));
+ e->flags |= (1 << NFT_EXPR_PAYLOAD_DREG);
+ }
+ if (tb[NFTA_PAYLOAD_BASE]) {
+ payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE]));
+ e->flags |= (1 << NFT_EXPR_PAYLOAD_BASE);
+ }
+ if (tb[NFTA_PAYLOAD_OFFSET]) {
+ payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET]));
+ e->flags |= (1 << NFT_EXPR_PAYLOAD_OFFSET);
+ }
+ if (tb[NFTA_PAYLOAD_LEN]) {
+ payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN]));
+ e->flags |= (1 << NFT_EXPR_PAYLOAD_LEN);
+ }
+
+ return 0;
+}
+
+static int
+nft_rule_expr_payload_snprintf(char *buf, size_t len, struct nft_rule_expr *e)
+{
+ struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data;
+
+ return snprintf(buf, len, "dreg=%u base=%u offset=%u len=%u ",
+ payload->dreg, payload->base,
+ payload->offset, payload->len);
+}
+
+struct expr_ops expr_ops_payload = {
+ .name = "payload",
+ .alloc_len = sizeof(struct nft_expr_payload),
+ .max_attr = NFTA_PAYLOAD_MAX,
+ .set = nft_rule_expr_payload_set,
+ .get = nft_rule_expr_payload_get,
+ .parse = nft_rule_expr_payload_parse,
+ .build = nft_rule_expr_payload_build,
+ .snprintf = nft_rule_expr_payload_snprintf,
+};
diff --git a/src/expr/target.c b/src/expr/target.c
new file mode 100644
index 0000000..fccf64b
--- /dev/null
+++ b/src/expr/target.c
@@ -0,0 +1,203 @@
+/*
+ * (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 <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/x_tables.h>
+
+#include <libnftables/expr.h>
+
+#include "expr_ops.h"
+
+struct nft_expr_target {
+ char name[XT_EXTENSION_MAXNAMELEN];
+ uint32_t rev;
+ uint32_t data_len;
+ const void *data;
+};
+
+static int
+nft_rule_expr_target_set(struct nft_rule_expr *e, uint16_t type,
+ const void *data, size_t data_len)
+{
+ struct nft_expr_target *tg = (struct nft_expr_target *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_TG_NAME:
+ memcpy(tg->name, data, XT_EXTENSION_MAXNAMELEN);
+ tg->name[XT_EXTENSION_MAXNAMELEN-1] = '\0';
+ break;
+ case NFT_EXPR_TG_REV:
+ tg->rev = *((uint32_t *)data);
+ break;
+ case NFT_EXPR_TG_INFO:
+ if (tg->data)
+ free((void *)tg->data);
+
+ tg->data = data;
+ tg->data_len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nft_rule_expr_target_get(struct nft_rule_expr *e, uint16_t type,
+ size_t *data_len)
+{
+ struct nft_expr_target *tg = (struct nft_expr_target *)e->data;
+
+ switch(type) {
+ case NFT_EXPR_TG_NAME:
+ if (e->flags & (1 << NFT_EXPR_TG_NAME)) {
+ *data_len = sizeof(tg->name);
+ return tg->name;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_TG_REV:
+ if (e->flags & (1 << NFT_EXPR_TG_REV)) {
+ *data_len = sizeof(tg->rev);
+ return &tg->rev;
+ } else
+ return NULL;
+ break;
+ case NFT_EXPR_TG_INFO:
+ if (e->flags & (1 << NFT_EXPR_TG_INFO)) {
+ *data_len = tg->data_len;
+ return tg->data;
+ } else
+ return NULL;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+static int nft_rule_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) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_TARGET_REV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_TARGET_INFO:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nft_rule_expr_target_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
+{
+ struct nft_expr_target *tg = (struct nft_expr_target *)e->data;
+
+ if (e->flags & (1 << NFT_EXPR_TG_NAME))
+ mnl_attr_put_strz(nlh, NFTA_TARGET_NAME, tg->name);
+ if (e->flags & (1 << NFT_EXPR_TG_REV))
+ mnl_attr_put_u32(nlh, NFTA_TARGET_REV, htonl(tg->rev));
+ if (e->flags & (1 << NFT_EXPR_TG_INFO))
+ mnl_attr_put(nlh, NFTA_TARGET_INFO, XT_ALIGN(tg->data_len), tg->data);
+}
+
+static int nft_rule_expr_target_parse(struct nft_rule_expr *e, struct nlattr *attr)
+{
+ struct nft_expr_target *target = (struct nft_expr_target *)e->data;
+ struct nlattr *tb[NFTA_TARGET_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nft_rule_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 << NFT_EXPR_TG_NAME);
+ }
+
+ if (tb[NFTA_TARGET_REV]) {
+ target->rev = ntohl(mnl_attr_get_u32(tb[NFTA_TARGET_REV]));
+ e->flags |= (1 << NFT_EXPR_TG_REV);
+ }
+
+ if (tb[NFTA_TARGET_INFO]) {
+ uint32_t len = mnl_attr_get_len(tb[NFTA_TARGET_INFO]);
+ void *target_data;
+
+ if (target->data)
+ free((void *) 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 << NFT_EXPR_TG_INFO);
+ }
+
+ return 0;
+}
+
+static int
+nft_rule_expr_target_snprintf(char *buf, size_t len, struct nft_rule_expr *e)
+{
+ struct nft_expr_target *target = (struct nft_expr_target *)e->data;
+
+ return snprintf(buf, len, "name=%s rev=%u ",
+ target->name, target->rev);
+}
+
+struct expr_ops expr_ops_target = {
+ .name = "target",
+ .alloc_len = sizeof(struct nft_expr_target),
+ .max_attr = NFTA_TARGET_MAX,
+ .set = nft_rule_expr_target_set,
+ .get = nft_rule_expr_target_get,
+ .parse = nft_rule_expr_target_parse,
+ .build = nft_rule_expr_target_build,
+ .snprintf = nft_rule_expr_target_snprintf,
+};