summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-08-03 17:31:48 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2012-10-11 15:15:02 +0200
commit00c179b4c9f9f9394cc6f79e5f2a68410631fd72 (patch)
tree00057636d125f3c89e2b9485c67c4283a98d91b2 /src
initial version of libnftables
It adds support for table, chain and rule handling. This also includes expression handling for each rule. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am21
-rw-r--r--src/chain.c484
-rw-r--r--src/expr.c189
-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
-rw-r--r--src/expr_ops.c36
-rw-r--r--src/expr_ops.h24
-rw-r--r--src/internal.h32
-rw-r--r--src/libnftables.map80
-rw-r--r--src/rule.c452
-rw-r--r--src/table.c212
18 files changed, 3000 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..b1c1044
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,21 @@
+include $(top_srcdir)/Make_global.am
+lib_LTLIBRARIES = libnftables.la
+
+libnftables_la_LIBADD = ${LIBMNL_LIBS}
+libnftables_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnftables.map \
+ -version-info $(LIBVERSION)
+libnftables_la_SOURCES = table.c \
+ chain.c \
+ rule.c \
+ expr.c \
+ expr_ops.c \
+ expr/cmp.c \
+ expr/counter.c \
+ expr/data_reg.c \
+ expr/immediate.c \
+ expr/match.c \
+ expr/meta.c \
+ expr/payload.c \
+ expr/target.c \
+ libnftables.map \
+ internal.h
diff --git a/src/chain.c b/src/chain.c
new file mode 100644
index 0000000..fd95a64
--- /dev/null
+++ b/src/chain.c
@@ -0,0 +1,484 @@
+/*
+ * (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 <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftables/chain.h>
+
+struct nft_chain {
+ struct list_head head;
+
+ char *name;
+ char *table;
+ uint8_t family;
+ uint32_t policy;
+ uint32_t hooknum;
+ int32_t prio;
+ uint32_t use;
+ uint64_t packets;
+ uint64_t bytes;
+ uint32_t flags;
+};
+
+struct nft_chain *nft_chain_alloc(void)
+{
+ return calloc(1, sizeof(struct nft_chain));
+}
+EXPORT_SYMBOL(nft_chain_alloc);
+
+void nft_chain_free(struct nft_chain *c)
+{
+ if (c->name != NULL)
+ free(c->name);
+ if (c->table != NULL)
+ free(c->table);
+
+ free(c);
+}
+EXPORT_SYMBOL(nft_chain_free);
+
+void nft_chain_attr_set(struct nft_chain *c, uint16_t attr, void *data)
+{
+ switch(attr) {
+ case NFT_CHAIN_ATTR_NAME:
+ if (c->name)
+ free(c->name);
+
+ c->name = strdup(data);
+ break;
+ case NFT_CHAIN_ATTR_TABLE:
+ if (c->table)
+ free(c->table);
+
+ c->table = strdup(data);
+ break;
+ case NFT_CHAIN_ATTR_HOOKNUM:
+ memcpy(&c->hooknum, data, sizeof(c->hooknum));
+ break;
+ case NFT_CHAIN_ATTR_PRIO:
+ memcpy(&c->prio, data, sizeof(c->prio));
+ break;
+ case NFT_CHAIN_ATTR_POLICY:
+ c->policy = *((uint32_t *)data);
+ break;
+ case NFT_CHAIN_ATTR_USE:
+ /* cannot be set, ignore it */
+ return;
+ case NFT_CHAIN_ATTR_BYTES:
+ c->bytes = *((uint64_t *)data);
+ break;
+ case NFT_CHAIN_ATTR_PACKETS:
+ c->bytes = *((uint64_t *)data);
+ break;
+ default:
+ return;
+ }
+ c->flags |= (1 << attr);
+}
+EXPORT_SYMBOL(nft_chain_attr_set);
+
+void nft_chain_attr_set_u32(struct nft_chain *c, uint16_t attr, uint32_t data)
+{
+ nft_chain_attr_set(c, attr, &data);
+}
+EXPORT_SYMBOL(nft_chain_attr_set_u32);
+
+void nft_chain_attr_set_s32(struct nft_chain *c, uint16_t attr, int32_t data)
+{
+ nft_chain_attr_set(c, attr, &data);
+}
+EXPORT_SYMBOL(nft_chain_attr_set_s32);
+
+void nft_chain_attr_set_u64(struct nft_chain *c, uint16_t attr, uint64_t data)
+{
+ nft_chain_attr_set(c, attr, &data);
+}
+EXPORT_SYMBOL(nft_chain_attr_set_u64);
+
+void *nft_chain_attr_get(struct nft_chain *c, uint16_t attr)
+{
+ switch(attr) {
+ case NFT_CHAIN_ATTR_NAME:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_NAME))
+ return c->name;
+ else
+ return NULL;
+ break;
+ case NFT_CHAIN_ATTR_TABLE:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_TABLE))
+ return c->table;
+ else
+ return NULL;
+ case NFT_CHAIN_ATTR_HOOKNUM:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_HOOKNUM))
+ return &c->hooknum;
+ else
+ return NULL;
+ break;
+ case NFT_CHAIN_ATTR_PRIO:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_PRIO))
+ return &c->prio;
+ else
+ return NULL;
+ break;
+ case NFT_CHAIN_ATTR_POLICY:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_POLICY))
+ return &c->policy;
+ else
+ return NULL;
+ break;
+ case NFT_CHAIN_ATTR_USE:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_USE))
+ return &c->use;
+ else
+ return NULL;
+ break;
+ case NFT_CHAIN_ATTR_BYTES:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_BYTES))
+ return &c->bytes;
+ else
+ return NULL;
+ break;
+ case NFT_CHAIN_ATTR_PACKETS:
+ if (c->flags & (1 << NFT_CHAIN_ATTR_PACKETS))
+ return &c->packets;
+ else
+ return NULL;
+ break;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(nft_chain_attr_get);
+
+const char *nft_chain_attr_get_str(struct nft_chain *c, uint16_t attr)
+{
+ return nft_chain_attr_get(c, attr);
+}
+EXPORT_SYMBOL(nft_chain_attr_get_str);
+
+uint32_t nft_chain_attr_get_u32(struct nft_chain *c, uint16_t attr)
+{
+ uint32_t *val = nft_chain_attr_get(c, attr);
+ return val ? *val : 0;
+}
+EXPORT_SYMBOL(nft_chain_attr_get_u32);
+
+int32_t nft_chain_attr_get_s32(struct nft_chain *c, uint16_t attr)
+{
+ int32_t *val = nft_chain_attr_get(c, attr);
+ return val ? *val : 0;
+}
+EXPORT_SYMBOL(nft_chain_attr_get_s32);
+
+uint64_t nft_chain_attr_get_u64(struct nft_chain *c, uint16_t attr)
+{
+ uint64_t *val = nft_chain_attr_get(c, attr);
+ return val ? *val : 0;
+}
+EXPORT_SYMBOL(nft_chain_attr_get_u64);
+
+struct nlmsghdr *
+nft_chain_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family,
+ uint16_t type, uint32_t seq)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | cmd;
+ nlh->nlmsg_flags = NLM_F_REQUEST | type;
+ nlh->nlmsg_seq = seq;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = family;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ return nlh;
+}
+EXPORT_SYMBOL(nft_chain_nlmsg_build_hdr);
+
+void nft_chain_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nft_chain *c)
+{
+ if (c->flags & (1 << NFT_CHAIN_ATTR_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, c->table);
+ if (c->flags & (1 << NFT_CHAIN_ATTR_NAME))
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, c->name);
+ if ((c->flags & (1 << NFT_CHAIN_ATTR_HOOKNUM)) &&
+ (c->flags & (1 << NFT_CHAIN_ATTR_PRIO))) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
+ mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(c->hooknum));
+ mnl_attr_put_u32(nlh, NFTA_HOOK_PRIORITY, htonl(c->prio));
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (c->flags & (1 << NFT_CHAIN_ATTR_POLICY))
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(c->policy));
+ if ((c->flags & (1 << NFT_CHAIN_ATTR_PACKETS)) &&
+ (c->flags & (1 << NFT_CHAIN_ATTR_BYTES))) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_COUNTERS);
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, be64toh(c->packets));
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, be64toh(c->bytes));
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+EXPORT_SYMBOL(nft_chain_nlmsg_build_payload);
+
+static int nft_chain_parse_attr_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_CHAIN_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CHAIN_NAME:
+ case NFTA_CHAIN_TABLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_CHAIN_HOOK:
+ case NFTA_CHAIN_COUNTERS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_CHAIN_POLICY:
+ case NFTA_CHAIN_USE:
+ 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 int nft_chain_parse_counters_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 int nft_chain_parse_counters(struct nlattr *attr, struct nft_chain *c)
+{
+ struct nlattr *tb[NFTA_COUNTER_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nft_chain_parse_counters_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_COUNTER_PACKETS]) {
+ c->packets = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_PACKETS);
+ }
+ if (tb[NFTA_COUNTER_BYTES]) {
+ c->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_BYTES);
+ }
+
+ return 0;
+}
+static int nft_chain_parse_hook_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_HOOK_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_HOOK_HOOKNUM:
+ case NFTA_HOOK_PRIORITY:
+ 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 int nft_chain_parse_hook(struct nlattr *attr, struct nft_chain *c)
+{
+ struct nlattr *tb[NFTA_HOOK_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nft_chain_parse_hook_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_HOOK_HOOKNUM]) {
+ c->hooknum = ntohl(mnl_attr_get_u32(tb[NFTA_HOOK_HOOKNUM]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_HOOKNUM);
+ }
+ if (tb[NFTA_HOOK_PRIORITY]) {
+ c->prio = ntohl(mnl_attr_get_u32(tb[NFTA_HOOK_PRIORITY]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_PRIO);
+ }
+
+ return 0;
+}
+
+int nft_chain_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_chain *c)
+{
+ struct nlattr *tb[NFTA_CHAIN_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret = 0;
+
+ mnl_attr_parse(nlh, sizeof(*nfg), nft_chain_parse_attr_cb, tb);
+ if (tb[NFTA_CHAIN_NAME]) {
+ c->name = strdup(mnl_attr_get_str(tb[NFTA_CHAIN_NAME]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_NAME);
+ }
+ if (tb[NFTA_CHAIN_TABLE]) {
+ c->table = strdup(mnl_attr_get_str(tb[NFTA_CHAIN_TABLE]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_TABLE);
+ }
+ if (tb[NFTA_CHAIN_HOOK])
+ ret = nft_chain_parse_hook(tb[NFTA_CHAIN_HOOK], c);
+ if (tb[NFTA_CHAIN_POLICY]) {
+ c->policy = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_POLICY]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_POLICY);
+ }
+ if (tb[NFTA_CHAIN_USE]) {
+ c->use = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_USE]));
+ c->flags |= (1 << NFT_CHAIN_ATTR_USE);
+ }
+ if (tb[NFTA_CHAIN_COUNTERS])
+ ret = nft_chain_parse_counters(tb[NFTA_CHAIN_COUNTERS], c);
+
+ c->family = nfg->nfgen_family;
+
+ return ret;
+}
+EXPORT_SYMBOL(nft_chain_nlmsg_parse);
+
+int nft_chain_snprintf(char *buf, size_t size, struct nft_chain *c,
+ uint32_t type, uint32_t flags)
+{
+ return snprintf(buf, size, "family=%u table=%s chain=%s "
+ "hook=%u prio=%d policy=%d use=%d "
+ "packets=%lu bytes=%lu\n",
+ c->family, c->table, c->name, c->hooknum,
+ c->prio, c->policy, c->use, c->packets, c->bytes);
+}
+EXPORT_SYMBOL(nft_chain_snprintf);
+
+struct nft_chain_list {
+ struct list_head list;
+};
+
+struct nft_chain_list *nft_chain_list_alloc(void)
+{
+ struct nft_chain_list *list;
+
+ list = calloc(1, sizeof(struct nft_chain_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+EXPORT_SYMBOL(nft_chain_list_alloc);
+
+void nft_chain_list_free(struct nft_chain_list *list)
+{
+ struct nft_chain *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &list->list, head) {
+ list_del(&r->head);
+ nft_chain_free(r);
+ }
+ free(list);
+}
+EXPORT_SYMBOL(nft_chain_list_free);
+
+void nft_chain_list_add(struct nft_chain *r, struct nft_chain_list *list)
+{
+ list_add_tail(&r->head, &list->list);
+}
+EXPORT_SYMBOL(nft_chain_list_add);
+
+struct nft_chain_list_iter {
+ struct nft_chain_list *list;
+ struct nft_chain *cur;
+};
+
+struct nft_chain_list_iter *nft_chain_list_iter_create(struct nft_chain_list *l)
+{
+ struct nft_chain_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nft_chain_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ iter->cur = list_entry(l->list.next, struct nft_chain, head);
+
+ return iter;
+}
+EXPORT_SYMBOL(nft_chain_list_iter_create);
+
+struct nft_chain *nft_chain_list_iter_next(struct nft_chain_list_iter *iter)
+{
+ struct nft_chain *r = iter->cur;
+
+ /* get next chain, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nft_chain, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return r;
+}
+EXPORT_SYMBOL(nft_chain_list_iter_next);
+
+void nft_chain_list_iter_destroy(struct nft_chain_list_iter *iter)
+{
+ free(iter);
+}
+EXPORT_SYMBOL(nft_chain_list_iter_destroy);
diff --git a/src/expr.c b/src/expr.c
new file mode 100644
index 0000000..0b06aed
--- /dev/null
+++ b/src/expr.c
@@ -0,0 +1,189 @@
+/*
+ * (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 "expr_ops.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftables/expr.h>
+
+#include "linux_list.h"
+
+struct nft_rule_expr *nft_rule_expr_alloc(const char *name)
+{
+ struct nft_rule_expr *expr;
+ struct expr_ops *ops;
+
+ ops = nft_expr_ops_lookup(name);
+ if (ops == NULL)
+ return NULL;
+
+ expr = calloc(1, sizeof(struct nft_rule_expr) + ops->alloc_len);
+ if (expr == NULL)
+ return NULL;
+
+ /* Manually set expression name attribute */
+ expr->flags |= (1 << NFT_RULE_EXPR_ATTR_NAME);
+ expr->ops = ops;
+
+ return expr;
+}
+EXPORT_SYMBOL(nft_rule_expr_alloc);
+
+void nft_rule_expr_free(struct nft_rule_expr *expr)
+{
+ free(expr);
+}
+EXPORT_SYMBOL(nft_rule_expr_free);
+
+void
+nft_rule_expr_set(struct nft_rule_expr *expr, uint16_t type,
+ const void *data, size_t data_len)
+{
+ switch(type) {
+ case NFT_RULE_EXPR_ATTR_NAME: /* cannot be modified */
+ return;
+ default:
+ if (expr->ops->set(expr, type, data, data_len) < 0)
+ return;
+ }
+ expr->flags |= (1 << type);
+}
+EXPORT_SYMBOL(nft_rule_expr_set);
+
+void
+nft_rule_expr_set_u8(struct nft_rule_expr *expr, uint16_t type, uint8_t data)
+{
+ nft_rule_expr_set(expr, type, &data, sizeof(uint8_t));
+}
+EXPORT_SYMBOL(nft_rule_expr_set_u8);
+
+void
+nft_rule_expr_set_u32(struct nft_rule_expr *expr, uint16_t type, uint32_t data)
+{
+ nft_rule_expr_set(expr, type, &data, sizeof(uint32_t));
+}
+EXPORT_SYMBOL(nft_rule_expr_set_u32);
+
+void
+nft_rule_expr_set_u64(struct nft_rule_expr *expr, uint16_t type, uint64_t data)
+{
+ nft_rule_expr_set(expr, type, &data, sizeof(uint64_t));
+}
+EXPORT_SYMBOL(nft_rule_expr_set_u64);
+
+void
+nft_rule_expr_set_str(struct nft_rule_expr *expr, uint16_t type, char *str)
+{
+ nft_rule_expr_set(expr, type, str, strlen(str)+1);
+}
+EXPORT_SYMBOL(nft_rule_expr_set_str);
+
+const void *nft_rule_expr_get(struct nft_rule_expr *expr, uint16_t type, size_t *data_len)
+{
+ const void *ret = NULL;
+
+ switch(type) {
+ case NFT_RULE_EXPR_ATTR_NAME:
+ if (!(expr->flags & (1 << NFT_RULE_EXPR_ATTR_NAME)))
+ return NULL;
+
+ ret = expr->ops->name;
+ break;
+ default:
+ ret = expr->ops->get(expr, type, data_len);
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(nft_rule_expr_get);
+
+uint8_t nft_rule_expr_get_u8(struct nft_rule_expr *expr, uint16_t type)
+{
+ const void *data;
+ size_t data_len;
+
+ data = nft_rule_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(nft_rule_expr_get_u8);
+
+uint32_t nft_rule_expr_get_u32(struct nft_rule_expr *expr, uint16_t type)
+{
+ const void *data;
+ size_t data_len;
+
+ data = nft_rule_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(nft_rule_expr_get_u32);
+
+uint64_t nft_rule_expr_get_u64(struct nft_rule_expr *expr, uint16_t type)
+{
+ const void *data;
+ size_t data_len;
+
+ data = nft_rule_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(nft_rule_expr_get_u64);
+
+const char *nft_rule_expr_get_str(struct nft_rule_expr *expr, uint16_t type)
+{
+ size_t data_len;
+
+ return (const char *)nft_rule_expr_get(expr, type, &data_len);
+}
+EXPORT_SYMBOL(nft_rule_expr_get_str);
+
+void
+nft_rule_expr_build_payload(struct nlmsghdr *nlh, struct nft_rule_expr *expr)
+{
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ mnl_attr_put_strz(nlh, NFTA_EXPR_NAME, expr->ops->name);
+
+ nest2 = mnl_attr_nest_start(nlh, NFTA_EXPR_DATA);
+ expr->ops->build(nlh, expr);
+ mnl_attr_nest_end(nlh, nest2);
+
+ mnl_attr_nest_end(nlh, nest1);
+}
+EXPORT_SYMBOL(nft_rule_expr_build_payload);
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,
+};
diff --git a/src/expr_ops.c b/src/expr_ops.c
new file mode 100644
index 0000000..2b21d96
--- /dev/null
+++ b/src/expr_ops.c
@@ -0,0 +1,36 @@
+#include <string.h>
+
+#include "expr_ops.h"
+
+extern struct expr_ops expr_ops_cmp;
+extern struct expr_ops expr_ops_counter;
+extern struct expr_ops expr_ops_immediate;
+extern struct expr_ops expr_ops_match;
+extern struct expr_ops expr_ops_meta;
+extern struct expr_ops expr_ops_payload;
+extern struct expr_ops expr_ops_target;
+
+struct expr_ops *expr_ops[] = {
+ &expr_ops_cmp,
+ &expr_ops_counter,
+ &expr_ops_immediate,
+ &expr_ops_match,
+ &expr_ops_meta,
+ &expr_ops_payload,
+ &expr_ops_target,
+ NULL,
+};
+
+struct expr_ops *nft_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;
+}
diff --git a/src/expr_ops.h b/src/expr_ops.h
new file mode 100644
index 0000000..2322958
--- /dev/null
+++ b/src/expr_ops.h
@@ -0,0 +1,24 @@
+#ifndef _EXPR_OPS_H_
+#define _EXPR_OPS_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+
+struct nlattr;
+struct nlmsghdr;
+struct nft_rule_expr;
+
+struct expr_ops {
+ char *name;
+ size_t alloc_len;
+ int max_attr;
+ int (*set)(struct nft_rule_expr *e, uint16_t type, const void *data, size_t data_len);
+ const void *(*get)(struct nft_rule_expr *e, uint16_t type, size_t *data_len);
+ int (*parse)(struct nft_rule_expr *e, struct nlattr *attr);
+ void (*build)(struct nlmsghdr *nlh, struct nft_rule_expr *e);
+ int (*snprintf)(char *buf, size_t len, struct nft_rule_expr *e);
+};
+
+struct expr_ops *nft_expr_ops_lookup(const char *name);
+
+#endif
diff --git a/src/internal.h b/src/internal.h
new file mode 100644
index 0000000..5ba7c76
--- /dev/null
+++ b/src/internal.h
@@ -0,0 +1,32 @@
+#ifndef INTERNAL_H
+#define INTERNAL_H 1
+
+#include "config.h"
+#ifdef HAVE_VISIBILITY_HIDDEN
+# define __visible __attribute__((visibility("default")))
+# define EXPORT_SYMBOL(x) typeof(x) (x) __visible
+#else
+# define EXPORT_SYMBOL
+#endif
+
+#include "linux_list.h"
+
+#include <stdint.h>
+
+struct expr_ops;
+
+struct nft_rule_expr {
+ struct list_head head;
+ uint32_t flags;
+ struct expr_ops *ops;
+ uint8_t data[];
+};
+
+#define SNPRINTF_BUFFER_SIZE(ret, size, len, offset) \
+ size += ret; \
+ if (ret > len) \
+ ret = len; \
+ offset += ret; \
+ len -= ret;
+
+#endif
diff --git a/src/libnftables.map b/src/libnftables.map
new file mode 100644
index 0000000..4024764
--- /dev/null
+++ b/src/libnftables.map
@@ -0,0 +1,80 @@
+LIBNFTABLES_1.0 {
+global:
+ nft_table_alloc;
+ nft_table_free;
+ nft_table_attr_set;
+ nft_table_attr_get;
+ nft_table_snprintf;
+ nft_table_nlmsg_build_hdr;
+ nft_table_nlmsg_build_payload;
+ nft_table_nlmsg_parse;
+ nft_table_list_alloc;
+ nft_table_list_free;
+ nft_table_list_add;
+ nft_table_list_iter_create;
+ nft_table_list_iter_next;
+ nft_table_list_iter_destroy;
+
+ nft_chain_alloc;
+ nft_chain_free;
+ nft_chain_attr_set;
+ nft_chain_attr_set_u32;
+ nft_chain_attr_set_s32;
+ nft_chain_attr_set_u64;
+ nft_chain_attr_get;
+ nft_chain_attr_get_u32;
+ nft_chain_attr_get_s32;
+ nft_chain_attr_get_u64;
+ nft_chain_attr_get_str;
+ nft_chain_snprintf;
+ nft_chain_nlmsg_build_hdr;
+ nft_chain_nlmsg_build_payload;
+ nft_chain_nlmsg_parse;
+ nft_chain_list_alloc;
+ nft_chain_list_free;
+ nft_chain_list_add;
+ nft_chain_list_iter_create;
+ nft_chain_list_iter_next;
+ nft_chain_list_iter_destroy;
+
+ nft_rule_alloc;
+ nft_rule_free;
+ nft_rule_attr_set;
+ nft_rule_attr_set_u16;
+ nft_rule_attr_set_str;
+ nft_rule_attr_get;
+ nft_rule_attr_get_u16;
+ nft_rule_attr_get_str;
+ nft_rule_snprintf;
+ nft_rule_nlmsg_build_hdr;
+ nft_rule_nlmsg_build_payload;
+ nft_rule_nlmsg_parse;
+ nft_rule_add_expr;
+
+ nft_rule_expr_iter_create;
+ nft_rule_expr_iter_next;
+ nft_rule_expr_iter_destroy;
+
+ nft_rule_expr_alloc;
+ nft_rule_expr_set;
+ nft_rule_expr_set_u8;
+ nft_rule_expr_set_u32;
+ nft_rule_expr_set_u64;
+ nft_rule_expr_set_str;
+ nft_rule_expr_get;
+ nft_rule_expr_get_u8;
+ nft_rule_expr_get_u32;
+ nft_rule_expr_get_u64;
+ nft_rule_expr_get_str;
+ nft_rule_expr_free;
+
+ nft_rule_list_alloc;
+ nft_rule_list_free;
+ nft_rule_list_add;
+ nft_rule_list_iter_create;
+ nft_rule_list_iter_cur;
+ nft_rule_list_iter_next;
+ nft_rule_list_iter_destroy;
+
+local: *;
+};
diff --git a/src/rule.c b/src/rule.c
new file mode 100644
index 0000000..b0f7bb3
--- /dev/null
+++ b/src/rule.c
@@ -0,0 +1,452 @@
+/*
+ * (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 <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftables/rule.h>
+#include <libnftables/expr.h>
+
+#include "linux_list.h"
+#include "expr_ops.h"
+
+struct nft_rule {
+ struct list_head head;
+
+ uint32_t flags;
+ char *table;
+ char *chain;
+ uint8_t family;
+ uint16_t handle;
+
+ struct list_head expr_list;
+};
+
+struct nft_rule *nft_rule_alloc(void)
+{
+ struct nft_rule *r;
+
+ r = calloc(1, sizeof(struct nft_rule));
+ if (r == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&r->expr_list);
+
+ return r;
+}
+EXPORT_SYMBOL(nft_rule_alloc);
+
+void nft_rule_free(struct nft_rule *r)
+{
+ if (r->table != NULL)
+ free(r->table);
+ if (r->chain != NULL)
+ free(r->chain);
+
+ free(r);
+}
+EXPORT_SYMBOL(nft_rule_free);
+
+void nft_rule_attr_set(struct nft_rule *r, uint16_t attr, void *data)
+{
+ switch(attr) {
+ case NFT_RULE_ATTR_TABLE:
+ if (r->table)
+ free(r->table);
+
+ r->table = strdup(data);
+ break;
+ case NFT_RULE_ATTR_CHAIN:
+ if (r->chain)
+ free(r->chain);
+
+ r->chain = strdup(data);
+ break;
+ case NFT_RULE_ATTR_HANDLE:
+ r->handle = *((uint16_t *)data);
+ break;
+ default:
+ return;
+ }
+ r->flags |= (1 << attr);
+}
+EXPORT_SYMBOL(nft_rule_attr_set);
+
+void nft_rule_attr_set_u16(struct nft_rule *r, uint16_t attr, uint16_t val)
+{
+ nft_rule_attr_set(r, attr, &val);
+}
+EXPORT_SYMBOL(nft_rule_attr_set_u16);
+
+void nft_rule_attr_set_str(struct nft_rule *r, uint16_t attr, char *str)
+{
+ nft_rule_attr_set(r, attr, str);
+}
+EXPORT_SYMBOL(nft_rule_attr_set_str);
+
+void *nft_rule_attr_get(struct nft_rule *r, uint16_t attr)
+{
+ switch(attr) {
+ case NFT_RULE_ATTR_TABLE:
+ if (r->flags & (1 << NFT_RULE_ATTR_TABLE))
+ return r->table;
+ else
+ return NULL;
+ break;
+ case NFT_RULE_ATTR_CHAIN:
+ if (r->flags & (1 << NFT_RULE_ATTR_CHAIN))
+ return r->chain;
+ else
+ return NULL;
+ case NFT_RULE_ATTR_HANDLE:
+ if (r->flags & (1 << NFT_RULE_ATTR_HANDLE))
+ return &r->handle;
+ else
+ return NULL;
+ break;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(nft_rule_attr_get);
+
+const char *nft_rule_attr_get_str(struct nft_rule *r, uint16_t attr)
+{
+ return nft_rule_attr_get(r, attr);
+}
+EXPORT_SYMBOL(nft_rule_attr_get_str);
+
+uint16_t nft_rule_attr_get_u16(struct nft_rule *r, uint16_t attr)
+{
+ uint16_t val = *((uint32_t *)nft_rule_attr_get(r, attr));
+ return val;
+}
+EXPORT_SYMBOL(nft_rule_attr_get_u16);
+
+struct nlmsghdr *
+nft_rule_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family,
+ uint16_t type, uint32_t seq)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | cmd;
+ nlh->nlmsg_flags = NLM_F_REQUEST | type;
+ nlh->nlmsg_seq = seq;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = family;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ return nlh;
+}
+EXPORT_SYMBOL(nft_rule_nlmsg_build_hdr);
+
+void nft_rule_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_rule *r)
+{
+ struct nft_rule_expr *expr;
+ struct nlattr *nest;
+
+ if (r->flags & (1 << NFT_RULE_ATTR_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, r->table);
+ if (r->flags & (1 << NFT_RULE_ATTR_CHAIN))
+ mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, r->chain);
+ if (r->flags & (1 << NFT_RULE_ATTR_HANDLE))
+ mnl_attr_put_u16(nlh, NFTA_RULE_HANDLE, htons(r->handle));
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS);
+ list_for_each_entry(expr, &r->expr_list, head) {
+ nft_rule_expr_build_payload(nlh, expr);
+ }
+ mnl_attr_nest_end(nlh, nest);
+}
+EXPORT_SYMBOL(nft_rule_nlmsg_build_payload);
+
+void nft_rule_add_expr(struct nft_rule *r, struct nft_rule_expr *expr)
+{
+ list_add_tail(&expr->head, &r->expr_list);
+}
+EXPORT_SYMBOL(nft_rule_add_expr);
+
+static int nft_rule_parse_attr_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_RULE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_RULE_TABLE:
+ case NFTA_RULE_CHAIN:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_RULE_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nft_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) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_EXPR_DATA:
+ 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_rule_parse_expr2(struct nlattr *attr, struct nft_rule *r)
+{
+ struct nlattr *tb[NFTA_EXPR_MAX+1] = {};
+ struct nft_rule_expr *expr;
+
+ if (mnl_attr_parse_nested(attr, nft_rule_parse_expr_cb, tb) < 0)
+ return -1;
+
+ expr = nft_rule_expr_alloc(mnl_attr_get_str(tb[NFTA_EXPR_NAME]));
+ if (expr == NULL)
+ return -1;
+
+ if (tb[NFTA_EXPR_DATA]) {
+ if (expr->ops->parse(expr, tb[NFTA_EXPR_DATA]) < 0) {
+ free(expr);
+ return -1;
+ }
+ }
+ list_add_tail(&expr->head, &r->expr_list);
+
+ return 0;
+}
+
+static int nft_rule_parse_expr(struct nlattr *nest, struct nft_rule *r)
+{
+ struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, nest) {
+ if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM)
+ return -1;
+
+ nft_rule_parse_expr2(attr, r);
+ }
+ return 0;
+}
+
+int nft_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_rule *r)
+{
+ struct nlattr *tb[NFTA_RULE_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret = 0;
+
+ mnl_attr_parse(nlh, sizeof(*nfg), nft_rule_parse_attr_cb, tb);
+ if (tb[NFTA_RULE_TABLE]) {
+ r->table = strdup(mnl_attr_get_str(tb[NFTA_RULE_TABLE]));
+ r->flags |= (1 << NFT_RULE_ATTR_TABLE);
+ }
+ if (tb[NFTA_RULE_CHAIN]) {
+ r->chain = strdup(mnl_attr_get_str(tb[NFTA_RULE_CHAIN]));
+ r->flags |= (1 << NFT_RULE_ATTR_CHAIN);
+ }
+ if (tb[NFTA_RULE_HANDLE]) {
+ r->handle = ntohs(mnl_attr_get_u16(tb[NFTA_RULE_HANDLE]));
+ r->flags |= (1 << NFT_RULE_ATTR_HANDLE);
+ }
+ if (tb[NFTA_RULE_EXPRESSIONS])
+ ret = nft_rule_parse_expr(tb[NFTA_RULE_EXPRESSIONS], r);
+
+ r->family = nfg->nfgen_family;
+
+ return ret;
+}
+EXPORT_SYMBOL(nft_rule_nlmsg_parse);
+
+int nft_rule_snprintf(char *buf, size_t size, struct nft_rule *r,
+ uint32_t type, uint32_t flags)
+{
+ int ret;
+ struct nft_rule_expr *expr;
+ int len = size, offset = 0;
+
+ ret = snprintf(buf, size, "family=%u table=%s chain=%s handle=%u ",
+ r->family, r->table, r->chain, r->handle);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ list_for_each_entry(expr, &r->expr_list, head) {
+ ret = snprintf(buf+offset, len, "%s ", expr->ops->name);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ ret = expr->ops->snprintf(buf+offset, len, expr);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+ ret = snprintf(buf+offset-1, len, "\n");
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ return ret;
+}
+EXPORT_SYMBOL(nft_rule_snprintf);
+
+struct nft_rule_expr_iter {
+ struct nft_rule *r;
+ struct nft_rule_expr *cur;
+};
+
+struct nft_rule_expr_iter *nft_rule_expr_iter_create(struct nft_rule *r)
+{
+ struct nft_rule_expr_iter *iter;
+
+ iter = calloc(1, sizeof(struct nft_rule_expr_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->r = r;
+ iter->cur = list_entry(r->expr_list.next, struct nft_rule_expr, head);
+
+ return iter;
+}
+EXPORT_SYMBOL(nft_rule_expr_iter_create);
+
+struct nft_rule_expr *nft_rule_expr_iter_next(struct nft_rule_expr_iter *iter)
+{
+ struct nft_rule_expr *expr = iter->cur;
+
+ /* get next expression, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nft_rule_expr, head);
+ if (&iter->cur->head == iter->r->expr_list.next)
+ return NULL;
+
+ return expr;
+}
+EXPORT_SYMBOL(nft_rule_expr_iter_next);
+
+void nft_rule_expr_iter_destroy(struct nft_rule_expr_iter *iter)
+{
+ free(iter);
+}
+EXPORT_SYMBOL(nft_rule_expr_iter_destroy);
+
+struct nft_rule_list {
+ struct list_head list;
+};
+
+struct nft_rule_list *nft_rule_list_alloc(void)
+{
+ struct nft_rule_list *list;
+
+ list = calloc(1, sizeof(struct nft_rule_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+EXPORT_SYMBOL(nft_rule_list_alloc);
+
+void nft_rule_list_free(struct nft_rule_list *list)
+{
+ struct nft_rule *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &list->list, head) {
+ list_del(&r->head);
+ nft_rule_free(r);
+ }
+ free(list);
+}
+EXPORT_SYMBOL(nft_rule_list_free);
+
+void nft_rule_list_add(struct nft_rule *r, struct nft_rule_list *list)
+{
+ list_add_tail(&r->head, &list->list);
+}
+EXPORT_SYMBOL(nft_rule_list_add);
+
+struct nft_rule_list_iter {
+ struct nft_rule_list *list;
+ struct nft_rule *cur;
+};
+
+struct nft_rule_list_iter *nft_rule_list_iter_create(struct nft_rule_list *l)
+{
+ struct nft_rule_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nft_rule_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ iter->cur = list_entry(l->list.next, struct nft_rule, head);
+
+ return iter;
+}
+EXPORT_SYMBOL(nft_rule_list_iter_create);
+
+struct nft_rule *nft_rule_list_iter_cur(struct nft_rule_list_iter *iter)
+{
+ return iter->cur;
+}
+EXPORT_SYMBOL(nft_rule_list_iter_cur);
+
+struct nft_rule *nft_rule_list_iter_next(struct nft_rule_list_iter *iter)
+{
+ struct nft_rule *r = iter->cur;
+
+ /* get next rule, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nft_rule, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return r;
+}
+EXPORT_SYMBOL(nft_rule_list_iter_next);
+
+void nft_rule_list_iter_destroy(struct nft_rule_list_iter *iter)
+{
+ free(iter);
+}
+EXPORT_SYMBOL(nft_rule_list_iter_destroy);
diff --git a/src/table.c b/src/table.c
new file mode 100644
index 0000000..040bf9a
--- /dev/null
+++ b/src/table.c
@@ -0,0 +1,212 @@
+/*
+ * (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 <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftables/table.h>
+
+struct nft_table {
+ struct list_head head;
+
+ char *name;
+ uint8_t family;
+ uint32_t flags; /* FIXME missing */
+};
+
+struct nft_table *nft_table_alloc(void)
+{
+ return calloc(1, sizeof(struct nft_table));
+}
+EXPORT_SYMBOL(nft_table_alloc);
+
+void nft_table_free(struct nft_table *t)
+{
+ if (t->name != NULL)
+ free(t->name);
+
+ free(t);
+}
+EXPORT_SYMBOL(nft_table_free);
+
+void nft_table_attr_set(struct nft_table *t, uint16_t attr, void *data)
+{
+ switch(attr) {
+ case NFT_TABLE_ATTR_NAME:
+ if (t->name)
+ free(t->name);
+
+ t->name = strdup(data);
+ break;
+ }
+}
+EXPORT_SYMBOL(nft_table_attr_set);
+
+const void *nft_table_attr_get(struct nft_table *t, uint16_t attr)
+{
+ switch(attr) {
+ case NFT_TABLE_ATTR_NAME:
+ return t->name;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(nft_table_attr_get);
+
+struct nlmsghdr *
+nft_table_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family,
+ uint16_t type, uint32_t seq)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | cmd;
+ nlh->nlmsg_flags = NLM_F_REQUEST | type;
+ nlh->nlmsg_seq = seq;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = family;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ return nlh;
+}
+EXPORT_SYMBOL(nft_table_nlmsg_build_hdr);
+
+void nft_table_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nft_table *t)
+{
+ if (t->name != NULL)
+ mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, t->name);
+}
+EXPORT_SYMBOL(nft_table_nlmsg_build_payload);
+
+static int nft_table_parse_attr_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_TABLE_MAX) < 0)
+ return MNL_CB_OK;
+
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+int nft_table_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_table *t)
+{
+ struct nlattr *tb[NFTA_TABLE_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+
+ mnl_attr_parse(nlh, sizeof(*nfg), nft_table_parse_attr_cb, tb);
+ if (tb[NFTA_TABLE_NAME])
+ t->name = strdup(mnl_attr_get_str(tb[NFTA_TABLE_NAME]));
+
+ t->family = nfg->nfgen_family;
+
+ return 0;
+}
+EXPORT_SYMBOL(nft_table_nlmsg_parse);
+
+int nft_table_snprintf(char *buf, size_t size, struct nft_table *t,
+ uint32_t type, uint32_t flags)
+{
+ return snprintf(buf, size, "table=%s family=%u\n", t->name, t->family);
+}
+EXPORT_SYMBOL(nft_table_snprintf);
+
+struct nft_table_list {
+ struct list_head list;
+};
+
+struct nft_table_list *nft_table_list_alloc(void)
+{
+ struct nft_table_list *list;
+
+ list = calloc(1, sizeof(struct nft_table_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+EXPORT_SYMBOL(nft_table_list_alloc);
+
+void nft_table_list_free(struct nft_table_list *list)
+{
+ struct nft_table *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &list->list, head) {
+ list_del(&r->head);
+ nft_table_free(r);
+ }
+ free(list);
+}
+EXPORT_SYMBOL(nft_table_list_free);
+
+void nft_table_list_add(struct nft_table *r, struct nft_table_list *list)
+{
+ list_add_tail(&r->head, &list->list);
+}
+EXPORT_SYMBOL(nft_table_list_add);
+
+struct nft_table_list_iter {
+ struct nft_table_list *list;
+ struct nft_table *cur;
+};
+
+struct nft_table_list_iter *nft_table_list_iter_create(struct nft_table_list *l)
+{
+ struct nft_table_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nft_table_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ iter->cur = list_entry(l->list.next, struct nft_table, head);
+
+ return iter;
+}
+EXPORT_SYMBOL(nft_table_list_iter_create);
+
+struct nft_table *nft_table_list_iter_next(struct nft_table_list_iter *iter)
+{
+ struct nft_table *r = iter->cur;
+
+ /* get next table, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nft_table, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return r;
+}
+EXPORT_SYMBOL(nft_table_list_iter_next);
+
+void nft_table_list_iter_destroy(struct nft_table_list_iter *iter)
+{
+ free(iter);
+}
+EXPORT_SYMBOL(nft_table_list_iter_destroy);