summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2013-02-02 12:17:31 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2013-02-05 13:55:02 +0100
commitd2a0036dd3e16d40f4039a11677241827ea057c5 (patch)
tree757e14859d225f34ad5499e554957476f9647785 /src
parentd07d9c1911739037be2db1d54a59aadf5c2d4adc (diff)
set: add support to add elements to sets
This patch includes iterators and several examples. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/internal.h27
-rw-r--r--src/libnftables.map22
-rw-r--r--src/set.c41
-rw-r--r--src/set_elem.c399
5 files changed, 472 insertions, 18 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d14251a..7e51104 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ libnftables_la_SOURCES = table.c \
chain.c \
rule.c \
set.c \
+ set_elem.c \
expr.c \
expr_ops.c \
expr/bitwise.c \
diff --git a/src/internal.h b/src/internal.h
index 5ba7c76..f5717ed 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -22,6 +22,33 @@ struct nft_rule_expr {
uint8_t data[];
};
+struct nlattr;
+
+struct nft_set {
+ struct list_head head;
+
+ uint32_t set_flags;
+ char *table;
+ char *name;
+ uint32_t key_type;
+ size_t key_len;
+ uint32_t data_type;
+ size_t data_len;
+ struct list_head element_list;
+
+ uint32_t flags;
+};
+
+#include "expr/data_reg.h"
+
+struct nft_set_elem {
+ struct list_head head;
+ uint32_t set_elem_flags;
+ union nft_data_reg key;
+ union nft_data_reg data;
+ uint32_t flags;
+};
+
#define SNPRINTF_BUFFER_SIZE(ret, size, len, offset) \
size += ret; \
if (ret > len) \
diff --git a/src/libnftables.map b/src/libnftables.map
index 6fc316f..957e3b6 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -104,5 +104,27 @@ global:
nft_set_list_iter_next;
nft_set_list_iter_destroy;
+ nft_set_elem_alloc;
+ nft_set_elem_free;
+ nft_set_elem_add;
+ nft_set_elem_attr_set;
+ nft_set_elem_attr_set_u32;
+ nft_set_elem_attr_set_str;
+ nft_set_elem_attr_get;
+ nft_set_elem_attr_get_str;
+ nft_set_elem_attr_get_u32;
+ nft_set_elem_nlmsg_build_hdr;
+ nft_set_elem_nlmsg_build_payload;
+ nft_set_elem_nlmsg_parse;
+ nft_set_elem_snprintf;
+
+ nft_set_elems_nlmsg_build_payload;
+ nft_set_elems_nlmsg_parse;
+
+ nft_set_elems_iter_create;
+ nft_set_elems_iter_cur;
+ nft_set_elems_iter_next;
+ nft_set_elems_iter_destroy;
+
local: *;
};
diff --git a/src/set.c b/src/set.c
index 54cc55d..f285b34 100644
--- a/src/set.c
+++ b/src/set.c
@@ -26,20 +26,6 @@
#include "linux_list.h"
#include "expr/data_reg.h"
-struct nft_set {
- struct list_head head;
-
- uint32_t set_flags;
- char *table;
- char *name;
- uint32_t key_type;
- size_t key_len;
- uint32_t data_type;
- size_t data_len;
-
- uint32_t flags;
-};
-
struct nft_set *nft_set_alloc(void)
{
struct nft_set *s;
@@ -48,17 +34,24 @@ struct nft_set *nft_set_alloc(void)
if (s == NULL)
return NULL;
+ INIT_LIST_HEAD(&s->element_list);
return s;
}
EXPORT_SYMBOL(nft_set_alloc);
void nft_set_free(struct nft_set *s)
{
+ struct nft_set_elem *elem, *tmp;
+
if (s->table != NULL)
free(s->table);
if (s->name != NULL)
free(s->name);
+ list_for_each_entry_safe(elem, tmp, &s->element_list, head) {
+ list_del(&elem->head);
+ nft_set_elem_free(elem);
+ }
free(s);
}
EXPORT_SYMBOL(nft_set_free);
@@ -275,18 +268,30 @@ int nft_set_snprintf(char *buf, size_t size, struct nft_set *s,
{
int ret;
int len = size, offset = 0;
+ struct nft_set_elem *elem;
- ret = snprintf(buf, size, "set=%s table=%s flags=%x ",
+ ret = snprintf(buf, size, "set=%s table=%s flags=%x\n",
s->name, s->table, s->set_flags);
SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- ret = snprintf(buf+offset-1, len, "\n");
- SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ list_for_each_entry(elem, &s->element_list, head) {
+ ret = snprintf(buf+offset, size, "\t");
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- return ret;
+ ret = nft_set_elem_snprintf(buf+offset, size, elem, type, flags);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ return offset;
}
EXPORT_SYMBOL(nft_set_snprintf);
+void nft_set_elem_add(struct nft_set *s, struct nft_set_elem *elem)
+{
+ list_add_tail(&elem->head, &s->element_list);
+}
+EXPORT_SYMBOL(nft_set_elem_add);
+
struct nft_set_list {
struct list_head list;
};
diff --git a/src/set_elem.c b/src/set_elem.c
new file mode 100644
index 0000000..980faed
--- /dev/null
+++ b/src/set_elem.c
@@ -0,0 +1,399 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <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/set.h>
+
+#include "linux_list.h"
+#include "expr/data_reg.h"
+
+struct nft_set_elem *nft_set_elem_alloc(void)
+{
+ struct nft_set_elem *s;
+
+ s = calloc(1, sizeof(struct nft_set_elem));
+ if (s == NULL)
+ return NULL;
+
+ return s;
+}
+EXPORT_SYMBOL(nft_set_elem_alloc);
+
+void nft_set_elem_free(struct nft_set_elem *s)
+{
+ free(s);
+}
+EXPORT_SYMBOL(nft_set_elem_free);
+
+void nft_set_elem_attr_set(struct nft_set_elem *s, uint16_t attr,
+ const void *data, size_t data_len)
+{
+ switch(attr) {
+ case NFT_SET_ELEM_ATTR_FLAGS:
+ s->set_elem_flags = *((uint32_t *)data);
+ break;
+ case NFT_SET_ELEM_ATTR_KEY: /* NFTA_SET_ELEM_KEY */
+ memcpy(&s->key.val, data, data_len);
+ s->key.len = data_len;
+ break;
+ case NFT_SET_ELEM_ATTR_VERDICT: /* NFTA_SET_ELEM_DATA */
+ s->data.verdict = *((uint32_t *)data);
+ break;
+ case NFT_SET_ELEM_ATTR_CHAIN: /* NFTA_SET_ELEM_DATA */
+ if (s->data.chain)
+ free(s->data.chain);
+
+ s->data.chain = strdup(data);
+ break;
+ default:
+ return;
+ }
+ s->flags |= (1 << attr);
+}
+EXPORT_SYMBOL(nft_set_elem_attr_set);
+
+void nft_set_elem_attr_set_u32(struct nft_set_elem *s, uint16_t attr, uint32_t val)
+{
+ nft_set_elem_attr_set(s, attr, &val, sizeof(uint32_t));
+}
+EXPORT_SYMBOL(nft_set_elem_attr_set_u32);
+
+void *nft_set_elem_attr_get(struct nft_set_elem *s, uint16_t attr, size_t *data_len)
+{
+ switch(attr) {
+ case NFT_SET_ELEM_ATTR_FLAGS:
+ if (s->flags & (1 << NFT_SET_ELEM_ATTR_FLAGS))
+ return &s->set_elem_flags;
+ break;
+ case NFT_SET_ELEM_ATTR_KEY: /* NFTA_SET_ELEM_KEY */
+ if (s->flags & (1 << NFT_SET_ELEM_ATTR_KEY)) {
+ *data_len = s->key.len;
+ return &s->key.val;
+ }
+ break;
+ case NFT_SET_ELEM_ATTR_VERDICT: /* NFTA_SET_ELEM_DATA */
+ if (s->flags & (1 << NFT_SET_ELEM_ATTR_VERDICT))
+ return &s->data.verdict;
+ break;
+ case NFT_SET_ELEM_ATTR_CHAIN: /* NFTA_SET_ELEM_DATA */
+ if (s->flags & (1 << NFT_SET_ELEM_ATTR_CHAIN))
+ return &s->data.chain;
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(nft_set_elem_attr_get);
+
+const char *nft_set_elem_attr_get_str(struct nft_set_elem *s, uint16_t attr)
+{
+ size_t size;
+
+ return nft_set_elem_attr_get(s, attr, &size);
+}
+EXPORT_SYMBOL(nft_set_elem_attr_get_str);
+
+uint32_t nft_set_elem_attr_get_u32(struct nft_set_elem *s, uint16_t attr)
+{
+ size_t size;
+ uint32_t val = *((uint32_t *)nft_set_elem_attr_get(s, attr, &size));
+ return val;
+}
+EXPORT_SYMBOL(nft_set_elem_attr_get_u32);
+
+struct nlmsghdr *
+nft_set_elem_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_set_elem_nlmsg_build_hdr);
+
+void nft_set_elem_nlmsg_build_payload(struct nlmsghdr *nlh,
+ struct nft_set_elem *e)
+{
+ if (e->flags & (1 << NFT_SET_ELEM_ATTR_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_SET_ELEM_FLAGS, htonl(e->set_elem_flags));
+ if (e->flags & (1 << NFT_SET_ELEM_ATTR_KEY)) {
+ struct nlattr *nest1;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_KEY);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, e->key.len, e->key.val);
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ if (e->flags & (1 << NFT_SET_ELEM_ATTR_VERDICT)) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_DATA);
+ nest2 = mnl_attr_nest_start(nlh, NFTA_DATA_VERDICT);
+ mnl_attr_put_u32(nlh, NFTA_VERDICT_CODE, htonl(e->data.verdict));
+ if (e->flags & (1 << NFT_SET_ELEM_ATTR_CHAIN))
+ mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, e->data.chain);
+
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+}
+
+void nft_set_elems_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_set *s)
+{
+ struct nft_set_elem *elem;
+ struct nlattr *nest1;
+ int i = 0;
+
+ if (s->flags & (1 << NFT_SET_ATTR_NAME))
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_SET, s->name);
+ if (s->flags & (1 << NFT_SET_ATTR_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE, s->table);
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS);
+ list_for_each_entry(elem, &s->element_list, head) {
+ struct nlattr *nest2;
+
+ nest2 = mnl_attr_nest_start(nlh, ++i);
+ nft_set_elem_nlmsg_build_payload(nlh, elem);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ mnl_attr_nest_end(nlh, nest1);
+}
+EXPORT_SYMBOL(nft_set_elems_nlmsg_build_payload);
+
+static int nft_set_elem_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_SET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_SET_ELEM_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_SET_ELEM_KEY:
+ case NFTA_SET_ELEM_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_set_elems_parse2(struct nft_set *s, const struct nlattr *nest)
+{
+ struct nlattr *tb[NFTA_SET_ELEM_MAX+1] = {};
+ struct nft_set_elem *e;
+ int ret = 0, type;
+
+ e = nft_set_elem_alloc();
+ if (e == NULL)
+ return -1;
+
+ mnl_attr_parse_nested(nest, nft_set_elem_parse_attr_cb, tb);
+ if (tb[NFTA_SET_ELEM_FLAGS]) {
+ e->set_elem_flags =
+ ntohl(mnl_attr_get_u32(tb[NFTA_SET_ELEM_FLAGS]));
+ e->flags |= (1 << NFT_SET_ELEM_ATTR_KEY);
+ }
+ if (tb[NFTA_SET_ELEM_KEY]) {
+ ret = nft_parse_data(&e->key, tb[NFTA_SET_ELEM_KEY], &type);
+ e->flags |= (1 << NFT_SET_ELEM_ATTR_KEY);
+ }
+ if (tb[NFTA_SET_ELEM_DATA]) {
+ ret = nft_parse_data(&e->data, tb[NFTA_SET_ELEM_DATA], &type);
+ switch(type) {
+ case DATA_VERDICT:
+ s->flags |= (1 << NFT_SET_ELEM_ATTR_VERDICT);
+ break;
+ case DATA_CHAIN:
+ s->flags |= (1 << NFT_SET_ELEM_ATTR_CHAIN);
+ break;
+ }
+ }
+ if (ret < 0)
+ free(e);
+
+ /* Add this new element to this set */
+ list_add_tail(&e->head, &s->element_list);
+
+ return ret;
+}
+EXPORT_SYMBOL(nft_set_elem_nlmsg_parse);
+
+static int
+nft_set_elem_list_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_SET_ELEM_LIST_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_SET_ELEM_LIST_TABLE:
+ case NFTA_SET_ELEM_LIST_SET:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+ perror("mnl_attr_validate");
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NFTA_SET_ELEM_LIST_ELEMENTS:
+ 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_set_elems_parse(struct nft_set *s, const struct nlattr *nest)
+{
+ struct nlattr *attr;
+ int ret = 0;
+
+ mnl_attr_for_each_nested(attr, nest) {
+ if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM)
+ return -1;
+
+ ret = nft_set_elems_parse2(s, attr);
+ }
+ return ret;
+}
+
+int nft_set_elems_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s)
+{
+ struct nlattr *tb[NFTA_SET_ELEM_LIST_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret = 0;
+
+ mnl_attr_parse(nlh, sizeof(*nfg), nft_set_elem_list_parse_attr_cb, tb);
+ if (tb[NFTA_SET_ELEM_LIST_TABLE]) {
+ s->table =
+ strdup(mnl_attr_get_str(tb[NFTA_SET_ELEM_LIST_TABLE]));
+ s->flags |= (1 << NFT_SET_ATTR_TABLE);
+ }
+ if (tb[NFTA_SET_ELEM_LIST_SET]) {
+ s->name =
+ strdup(mnl_attr_get_str(tb[NFTA_SET_ELEM_LIST_SET]));
+ s->flags |= (1 << NFT_SET_ATTR_NAME);
+ }
+ if (tb[NFTA_SET_ELEM_LIST_ELEMENTS])
+ ret = nft_set_elems_parse(s, tb[NFTA_SET_ELEM_LIST_ELEMENTS]);
+
+ return ret;
+}
+EXPORT_SYMBOL(nft_set_elems_nlmsg_parse);
+
+int nft_set_elem_snprintf(char *buf, size_t size, struct nft_set_elem *e,
+ uint32_t type, uint32_t flags)
+{
+ int ret, len = size, offset = 0, i;
+
+ ret = snprintf(buf, size, "flags=%u key=", e->set_elem_flags);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ for (i=0; i<e->key.len/sizeof(uint32_t); i++) {
+ ret = snprintf(buf+offset, len, "%.8x ", e->key.val[i]);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ ret = snprintf(buf+offset, size, "data=");
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ for (i=0; i<e->data.len/sizeof(uint32_t); i++) {
+ ret = snprintf(buf+offset, len, "%.8x ", e->data.val[i]);
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ }
+ ret = snprintf(buf+offset, size, "\n");
+ SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+ return offset;
+}
+EXPORT_SYMBOL(nft_set_elem_snprintf);
+
+struct nft_set_elems_iter {
+ struct list_head *list;
+ struct nft_set_elem *cur;
+};
+
+struct nft_set_elems_iter *nft_set_elems_iter_create(struct nft_set *s)
+{
+ struct nft_set_elems_iter *iter;
+
+ iter = calloc(1, sizeof(struct nft_set_elems_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = &s->element_list;
+ iter->cur = list_entry(s->element_list.next, struct nft_set_elem, head);
+
+ return iter;
+}
+EXPORT_SYMBOL(nft_set_elems_iter_create);
+
+struct nft_set_elem *nft_set_elems_iter_cur(struct nft_set_elems_iter *iter)
+{
+ return iter->cur;
+}
+EXPORT_SYMBOL(nft_set_elems_iter_cur);
+
+struct nft_set_elem *nft_set_elems_iter_next(struct nft_set_elems_iter *iter)
+{
+ struct nft_set_elem *s = iter->cur;
+
+ iter->cur = list_entry(iter->cur->head.next, struct nft_set_elem, head);
+ if (&iter->cur->head == iter->list->next)
+ return NULL;
+
+ return s;
+}
+EXPORT_SYMBOL(nft_set_elems_iter_next);
+
+void nft_set_elems_iter_destroy(struct nft_set_elems_iter *iter)
+{
+ free(iter);
+}
+EXPORT_SYMBOL(nft_set_elems_iter_destroy);