summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--examples/Makefile.am12
-rw-r--r--examples/nft-set-elem-add.c114
-rw-r--r--examples/nft-set-elem-del.c114
-rw-r--r--examples/nft-set-elem-get.c115
-rw-r--r--include/libnftables/set.h41
-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
10 files changed, 868 insertions, 18 deletions
diff --git a/examples/Makefile.am b/examples/Makefile.am
index d239196..1c39e12 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -14,6 +14,9 @@ check_PROGRAMS = nft-table-add \
nft-set-add \
nft-set-get \
nft-set-del \
+ nft-set-elem-add \
+ nft-set-elem-get \
+ nft-set-elem-del \
nft-compat-get
nft_table_add_SOURCES = nft-table-add.c
@@ -58,5 +61,14 @@ nft_set_del_LDADD = ../src/libnftables.la ${LIBMNL_LIBS}
nft_set_get_SOURCES = nft-set-get.c
nft_set_get_LDADD = ../src/libnftables.la ${LIBMNL_LIBS}
+nft_set_elem_add_SOURCES = nft-set-elem-add.c
+nft_set_elem_add_LDADD = ../src/libnftables.la ${LIBMNL_LIBS}
+
+nft_set_elem_del_SOURCES = nft-set-elem-del.c
+nft_set_elem_del_LDADD = ../src/libnftables.la ${LIBMNL_LIBS}
+
+nft_set_elem_get_SOURCES = nft-set-elem-get.c
+nft_set_elem_get_LDADD = ../src/libnftables.la ${LIBMNL_LIBS}
+
nft_compat_get_SOURCES = nft-compat-get.c
nft_compat_get_LDADD = ../src/libnftables.la ${LIBMNL_LIBS}
diff --git a/examples/nft-set-elem-add.c b/examples/nft-set-elem-add.c
new file mode 100644
index 0000000..992b09c
--- /dev/null
+++ b/examples/nft-set-elem-add.c
@@ -0,0 +1,114 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftables/set.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq, family, data;
+ struct nft_set *s;
+ struct nft_set_elem *e;
+ int ret;
+
+ if (argc != 4) {
+ fprintf(stderr, "%s <family> <table> <set>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ s = nft_set_alloc();
+ if (s == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ seq = time(NULL);
+ if (strcmp(argv[1], "ip") == 0)
+ family = AF_INET;
+ else if (strcmp(argv[1], "ip6") == 0)
+ family = AF_INET6;
+ else if (strcmp(argv[1], "bridge") == 0)
+ family = AF_BRIDGE;
+ else {
+ fprintf(stderr, "Unknown family: ip, ip6, bridge\n");
+ exit(EXIT_FAILURE);
+ }
+
+ nft_set_attr_set(s, NFT_SET_ATTR_TABLE, argv[2]);
+ nft_set_attr_set(s, NFT_SET_ATTR_NAME, argv[3]);
+
+ /* Add to dummy elements to set */
+ e = nft_set_elem_alloc();
+ if (e == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ data = 0x1;
+ nft_set_elem_attr_set(e, NFT_SET_ELEM_ATTR_KEY, &data, sizeof(data));
+ nft_set_elem_add(s, e);
+
+ e = nft_set_elem_alloc();
+ if (e == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+ data = 0x2;
+ nft_set_elem_attr_set(e, NFT_SET_ELEM_ATTR_KEY, &data, sizeof(data));
+ nft_set_elem_add(s, e);
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_NEWSETELEM, family,
+ NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK, seq);
+ nft_set_elems_nlmsg_build_payload(nlh, s);
+ nft_set_free(s);
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+ mnl_socket_close(nl);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/nft-set-elem-del.c b/examples/nft-set-elem-del.c
new file mode 100644
index 0000000..5ec1183
--- /dev/null
+++ b/examples/nft-set-elem-del.c
@@ -0,0 +1,114 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftables/set.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq, family, data;
+ struct nft_set *s;
+ struct nft_set_elem *e;
+ int ret;
+
+ if (argc != 4) {
+ fprintf(stderr, "%s <family> <table> <set>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ s = nft_set_alloc();
+ if (s == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ seq = time(NULL);
+ if (strcmp(argv[1], "ip") == 0)
+ family = AF_INET;
+ else if (strcmp(argv[1], "ip6") == 0)
+ family = AF_INET6;
+ else if (strcmp(argv[1], "bridge") == 0)
+ family = AF_BRIDGE;
+ else {
+ fprintf(stderr, "Unknown family: ip, ip6, bridge\n");
+ exit(EXIT_FAILURE);
+ }
+
+ nft_set_attr_set(s, NFT_SET_ATTR_TABLE, argv[2]);
+ nft_set_attr_set(s, NFT_SET_ATTR_NAME, argv[3]);
+
+ /* Add to dummy elements to set */
+ e = nft_set_elem_alloc();
+ if (e == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+
+ data = 0x1;
+ nft_set_elem_attr_set(e, NFT_SET_ELEM_ATTR_KEY, &data, sizeof(data));
+ nft_set_elem_add(s, e);
+
+ e = nft_set_elem_alloc();
+ if (e == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+ data = 0x2;
+ nft_set_elem_attr_set(e, NFT_SET_ELEM_ATTR_KEY, &data, sizeof(data));
+ nft_set_elem_add(s, e);
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_DELSETELEM, family,
+ NLM_F_ACK, seq);
+ nft_set_elems_nlmsg_build_payload(nlh, s);
+ nft_set_free(s);
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+ mnl_socket_close(nl);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/nft-set-elem-get.c b/examples/nft-set-elem-get.c
new file mode 100644
index 0000000..3c45ead
--- /dev/null
+++ b/examples/nft-set-elem-get.c
@@ -0,0 +1,115 @@
+/*
+ * (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 software has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftables/set.h>
+
+static int set_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_set *t;
+ char buf[4096];
+
+ t = nft_set_alloc();
+ if (t == NULL) {
+ perror("OOM");
+ goto err;
+ }
+
+ if (nft_set_elems_nlmsg_parse(nlh, t) < 0) {
+ perror("nft_set_nlmsg_parse");
+ goto err_free;
+ }
+
+ nft_set_snprintf(buf, sizeof(buf), t, 0, 0);
+ printf("%s", buf);
+
+err_free:
+ nft_set_free(t);
+err:
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq, family;
+ struct nft_set *t = NULL;
+ int ret;
+
+ if (argc != 4) {
+ fprintf(stderr, "%s <family> <table> <set>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ t = nft_set_alloc();
+ if (t == NULL) {
+ perror("OOM");
+ exit(EXIT_FAILURE);
+ }
+ seq = time(NULL);
+ if (strcmp(argv[1], "ip") == 0)
+ family = AF_INET;
+ else if (strcmp(argv[1], "ip6") == 0)
+ family = AF_INET6;
+ else if (strcmp(argv[2], "bridge") == 0)
+ family = AF_BRIDGE;
+ else {
+ fprintf(stderr, "Unknown family: ip, ip6, bridge\n");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM, family,
+ NLM_F_DUMP|NLM_F_ACK, seq);
+ nft_set_attr_set(t, NFT_SET_ATTR_NAME, argv[3]);
+ nft_set_attr_set(t, NFT_SET_ATTR_TABLE, argv[2]);
+ nft_set_elems_nlmsg_build_payload(nlh, t);
+ nft_set_free(t);
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, set_cb, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+ mnl_socket_close(nl);
+
+ return EXIT_SUCCESS;
+}
diff --git a/include/libnftables/set.h b/include/libnftables/set.h
index e56fb50..53c067b 100644
--- a/include/libnftables/set.h
+++ b/include/libnftables/set.h
@@ -27,6 +27,7 @@ uint32_t nft_set_attr_get_u32(struct nft_set *s, uint16_t attr);
struct nlmsghdr *nft_set_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family, uint16_t type, uint32_t seq);
void nft_set_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_set *s);
int nft_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s);
+int nft_set_elems_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set *s);
int nft_set_snprintf(char *buf, size_t size, struct nft_set *s, uint32_t type, uint32_t flags);
@@ -42,4 +43,44 @@ struct nft_set *nft_set_list_iter_cur(struct nft_set_list_iter *iter);
struct nft_set *nft_set_list_iter_next(struct nft_set_list_iter *iter);
void nft_set_list_iter_destroy(struct nft_set_list_iter *iter);
+/*
+ * Set elements
+ */
+
+enum {
+ NFT_SET_ELEM_ATTR_FLAGS,
+ NFT_SET_ELEM_ATTR_KEY,
+ NFT_SET_ELEM_ATTR_VERDICT,
+ NFT_SET_ELEM_ATTR_CHAIN,
+};
+
+struct nft_set_elem;
+
+struct nft_set_elem *nft_set_elem_alloc(void);
+void nft_set_elem_free(struct nft_set_elem *s);
+
+void nft_set_elem_add(struct nft_set *s, struct nft_set_elem *elem);
+
+void nft_set_elem_attr_set(struct nft_set_elem *s, uint16_t attr, const void *data, size_t data_len);
+void nft_set_elem_attr_set_u32(struct nft_set_elem *s, uint16_t attr, uint32_t val);
+void nft_set_elem_attr_set_str(struct nft_set_elem *s, uint16_t attr, char *str);
+
+void *nft_set_elem_attr_get(struct nft_set_elem *s, uint16_t attr, size_t *data_len);
+const char *nft_set_elem_attr_get_str(struct nft_set_elem *s, uint16_t attr);
+uint32_t nft_set_elem_attr_get_u32(struct nft_set_elem *s, uint16_t attr);
+
+struct nlmsghdr *nft_set_elem_nlmsg_build_hdr(char *buf, uint16_t cmd, uint16_t family, uint16_t type, uint32_t seq);
+void nft_set_elems_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_set *s);
+void nft_set_elem_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_set_elem *e);
+
+int nft_set_elem_nlmsg_parse(const struct nlmsghdr *nlh, struct nft_set_elem *s);
+
+int nft_set_elem_snprintf(char *buf, size_t size, struct nft_set_elem *s, uint32_t type, uint32_t flags);
+
+struct nft_set_elems_iter;
+struct nft_set_elems_iter *nft_set_elems_iter_create(struct nft_set *s);
+struct nft_set_elem *nft_set_elems_iter_cur(struct nft_set_elems_iter *iter);
+struct nft_set_elem *nft_set_elems_iter_next(struct nft_set_elems_iter *iter);
+void nft_set_elems_iter_destroy(struct nft_set_elems_iter *iter);
+
#endif
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);