summaryrefslogtreecommitdiffstats
path: root/src/set.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/set.c1066
1 files changed, 1066 insertions, 0 deletions
diff --git a/src/set.c b/src/set.c
new file mode 100644
index 0000000..c46f827
--- /dev/null
+++ b/src/set.c
@@ -0,0 +1,1066 @@
+/*
+ * (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 <inttypes.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/set.h>
+#include <libnftnl/expr.h>
+
+EXPORT_SYMBOL(nftnl_set_alloc);
+struct nftnl_set *nftnl_set_alloc(void)
+{
+ struct nftnl_set *s;
+
+ s = calloc(1, sizeof(struct nftnl_set));
+ if (s == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&s->element_list);
+ INIT_LIST_HEAD(&s->expr_list);
+ return s;
+}
+
+EXPORT_SYMBOL(nftnl_set_free);
+void nftnl_set_free(const struct nftnl_set *s)
+{
+ struct nftnl_set_elem *elem, *tmp;
+ struct nftnl_expr *expr, *next;
+
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ xfree(s->table);
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ xfree(s->name);
+ if (s->flags & (1 << NFTNL_SET_USERDATA))
+ xfree(s->user.data);
+
+ list_for_each_entry_safe(expr, next, &s->expr_list, head)
+ nftnl_expr_free(expr);
+
+ list_for_each_entry_safe(elem, tmp, &s->element_list, head) {
+ list_del(&elem->head);
+ nftnl_set_elem_free(elem);
+ }
+ xfree(s);
+}
+
+EXPORT_SYMBOL(nftnl_set_is_set);
+bool nftnl_set_is_set(const struct nftnl_set *s, uint16_t attr)
+{
+ return s->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_set_unset);
+void nftnl_set_unset(struct nftnl_set *s, uint16_t attr)
+{
+ struct nftnl_expr *expr, *tmp;
+
+ if (!(s->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_SET_TABLE:
+ xfree(s->table);
+ break;
+ case NFTNL_SET_NAME:
+ xfree(s->name);
+ break;
+ case NFTNL_SET_HANDLE:
+ case NFTNL_SET_FLAGS:
+ case NFTNL_SET_KEY_TYPE:
+ case NFTNL_SET_KEY_LEN:
+ case NFTNL_SET_DATA_TYPE:
+ case NFTNL_SET_DATA_LEN:
+ case NFTNL_SET_OBJ_TYPE:
+ case NFTNL_SET_FAMILY:
+ case NFTNL_SET_ID:
+ case NFTNL_SET_POLICY:
+ case NFTNL_SET_DESC_SIZE:
+ case NFTNL_SET_DESC_CONCAT:
+ case NFTNL_SET_TIMEOUT:
+ case NFTNL_SET_GC_INTERVAL:
+ break;
+ case NFTNL_SET_USERDATA:
+ xfree(s->user.data);
+ break;
+ case NFTNL_SET_EXPR:
+ case NFTNL_SET_EXPRESSIONS:
+ list_for_each_entry_safe(expr, tmp, &s->expr_list, head)
+ nftnl_expr_free(expr);
+ break;
+ default:
+ return;
+ }
+
+ s->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_set_validate[NFTNL_SET_MAX + 1] = {
+ [NFTNL_SET_HANDLE] = sizeof(uint64_t),
+ [NFTNL_SET_FLAGS] = sizeof(uint32_t),
+ [NFTNL_SET_KEY_TYPE] = sizeof(uint32_t),
+ [NFTNL_SET_KEY_LEN] = sizeof(uint32_t),
+ [NFTNL_SET_DATA_TYPE] = sizeof(uint32_t),
+ [NFTNL_SET_DATA_LEN] = sizeof(uint32_t),
+ [NFTNL_SET_OBJ_TYPE] = sizeof(uint32_t),
+ [NFTNL_SET_FAMILY] = sizeof(uint32_t),
+ [NFTNL_SET_POLICY] = sizeof(uint32_t),
+ [NFTNL_SET_DESC_SIZE] = sizeof(uint32_t),
+ [NFTNL_SET_TIMEOUT] = sizeof(uint64_t),
+ [NFTNL_SET_GC_INTERVAL] = sizeof(uint32_t),
+};
+
+EXPORT_SYMBOL(nftnl_set_set_data);
+int nftnl_set_set_data(struct nftnl_set *s, uint16_t attr, const void *data,
+ uint32_t data_len)
+{
+ struct nftnl_expr *expr, *tmp;
+
+ nftnl_assert_attr_exists(attr, NFTNL_SET_MAX);
+ nftnl_assert_validate(data, nftnl_set_validate, attr, data_len);
+
+ switch(attr) {
+ case NFTNL_SET_TABLE:
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ xfree(s->table);
+
+ s->table = strdup(data);
+ if (!s->table)
+ return -1;
+ break;
+ case NFTNL_SET_NAME:
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ xfree(s->name);
+
+ s->name = strdup(data);
+ if (!s->name)
+ return -1;
+ break;
+ case NFTNL_SET_HANDLE:
+ memcpy(&s->handle, data, sizeof(s->handle));
+ break;
+ case NFTNL_SET_FLAGS:
+ memcpy(&s->set_flags, data, sizeof(s->set_flags));
+ break;
+ case NFTNL_SET_KEY_TYPE:
+ memcpy(&s->key_type, data, sizeof(s->key_type));
+ break;
+ case NFTNL_SET_KEY_LEN:
+ memcpy(&s->key_len, data, sizeof(s->key_len));
+ break;
+ case NFTNL_SET_DATA_TYPE:
+ memcpy(&s->data_type, data, sizeof(s->data_type));
+ break;
+ case NFTNL_SET_DATA_LEN:
+ memcpy(&s->data_len, data, sizeof(s->data_len));
+ break;
+ case NFTNL_SET_OBJ_TYPE:
+ memcpy(&s->obj_type, data, sizeof(s->obj_type));
+ break;
+ case NFTNL_SET_FAMILY:
+ memcpy(&s->family, data, sizeof(s->family));
+ break;
+ case NFTNL_SET_ID:
+ memcpy(&s->id, data, sizeof(s->id));
+ break;
+ case NFTNL_SET_POLICY:
+ memcpy(&s->policy, data, sizeof(s->policy));
+ break;
+ case NFTNL_SET_DESC_SIZE:
+ memcpy(&s->desc.size, data, sizeof(s->desc.size));
+ break;
+ case NFTNL_SET_DESC_CONCAT:
+ memcpy(&s->desc.field_len, data, data_len);
+ while (s->desc.field_len[++s->desc.field_count]);
+ break;
+ case NFTNL_SET_TIMEOUT:
+ memcpy(&s->timeout, data, sizeof(s->timeout));
+ break;
+ case NFTNL_SET_GC_INTERVAL:
+ memcpy(&s->gc_interval, data, sizeof(s->gc_interval));
+ break;
+ case NFTNL_SET_USERDATA:
+ if (s->flags & (1 << NFTNL_SET_USERDATA))
+ xfree(s->user.data);
+
+ s->user.data = malloc(data_len);
+ if (!s->user.data)
+ return -1;
+ memcpy(s->user.data, data, data_len);
+ s->user.len = data_len;
+ break;
+ case NFTNL_SET_EXPR:
+ list_for_each_entry_safe(expr, tmp, &s->expr_list, head)
+ nftnl_expr_free(expr);
+
+ expr = (void *)data;
+ list_add(&expr->head, &s->expr_list);
+ break;
+ }
+ s->flags |= (1 << attr);
+ return 0;
+}
+
+int nftnl_set_set(struct nftnl_set *s, uint16_t attr, const void *data) __visible;
+int nftnl_set_set(struct nftnl_set *s, uint16_t attr, const void *data)
+{
+ return nftnl_set_set_data(s, attr, data, nftnl_set_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_set_set_u32);
+void nftnl_set_set_u32(struct nftnl_set *s, uint16_t attr, uint32_t val)
+{
+ nftnl_set_set_data(s, attr, &val, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_set_set_u64);
+void nftnl_set_set_u64(struct nftnl_set *s, uint16_t attr, uint64_t val)
+{
+ nftnl_set_set_data(s, attr, &val, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_set_set_str);
+int nftnl_set_set_str(struct nftnl_set *s, uint16_t attr, const char *str)
+{
+ return nftnl_set_set_data(s, attr, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_set_get_data);
+const void *nftnl_set_get_data(const struct nftnl_set *s, uint16_t attr,
+ uint32_t *data_len)
+{
+ struct nftnl_expr *expr;
+
+ if (!(s->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_SET_TABLE:
+ *data_len = strlen(s->table) + 1;
+ return s->table;
+ case NFTNL_SET_NAME:
+ *data_len = strlen(s->name) + 1;
+ return s->name;
+ case NFTNL_SET_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &s->handle;
+ case NFTNL_SET_FLAGS:
+ *data_len = sizeof(uint32_t);
+ return &s->set_flags;
+ case NFTNL_SET_KEY_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &s->key_type;
+ case NFTNL_SET_KEY_LEN:
+ *data_len = sizeof(uint32_t);
+ return &s->key_len;
+ case NFTNL_SET_DATA_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &s->data_type;
+ case NFTNL_SET_DATA_LEN:
+ *data_len = sizeof(uint32_t);
+ return &s->data_len;
+ case NFTNL_SET_OBJ_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &s->obj_type;
+ case NFTNL_SET_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &s->family;
+ case NFTNL_SET_ID:
+ *data_len = sizeof(uint32_t);
+ return &s->id;
+ case NFTNL_SET_POLICY:
+ *data_len = sizeof(uint32_t);
+ return &s->policy;
+ case NFTNL_SET_DESC_SIZE:
+ *data_len = sizeof(uint32_t);
+ return &s->desc.size;
+ case NFTNL_SET_DESC_CONCAT:
+ *data_len = s->desc.field_count;
+ return s->desc.field_len;
+ case NFTNL_SET_TIMEOUT:
+ *data_len = sizeof(uint64_t);
+ return &s->timeout;
+ case NFTNL_SET_GC_INTERVAL:
+ *data_len = sizeof(uint32_t);
+ return &s->gc_interval;
+ case NFTNL_SET_USERDATA:
+ *data_len = s->user.len;
+ return s->user.data;
+ case NFTNL_SET_EXPR:
+ list_for_each_entry(expr, &s->expr_list, head)
+ break;
+ return expr;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_set_get);
+const void *nftnl_set_get(const struct nftnl_set *s, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_set_get_data(s, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_set_get_str);
+const char *nftnl_set_get_str(const struct nftnl_set *s, uint16_t attr)
+{
+ return nftnl_set_get(s, attr);
+}
+
+EXPORT_SYMBOL(nftnl_set_get_u32);
+uint32_t nftnl_set_get_u32(const struct nftnl_set *s, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint32_t *val = nftnl_set_get_data(s, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint32_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_set_get_u64);
+uint64_t nftnl_set_get_u64(const struct nftnl_set *s, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint64_t *val = nftnl_set_get_data(s, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint64_t));
+
+ return val ? *val : 0;
+}
+
+struct nftnl_set *nftnl_set_clone(const struct nftnl_set *set)
+{
+ struct nftnl_set *newset;
+ struct nftnl_set_elem *elem, *newelem;
+
+ newset = nftnl_set_alloc();
+ if (newset == NULL)
+ return NULL;
+
+ memcpy(newset, set, sizeof(*set));
+
+ if (set->flags & (1 << NFTNL_SET_TABLE)) {
+ newset->table = strdup(set->table);
+ if (!newset->table)
+ goto err;
+ }
+ if (set->flags & (1 << NFTNL_SET_NAME)) {
+ newset->name = strdup(set->name);
+ if (!newset->name)
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&newset->element_list);
+ list_for_each_entry(elem, &set->element_list, head) {
+ newelem = nftnl_set_elem_clone(elem);
+ if (newelem == NULL)
+ goto err;
+
+ list_add_tail(&newelem->head, &newset->element_list);
+ }
+
+ return newset;
+err:
+ nftnl_set_free(newset);
+ return NULL;
+}
+
+static void nftnl_set_nlmsg_build_desc_size_payload(struct nlmsghdr *nlh,
+ struct nftnl_set *s)
+{
+ mnl_attr_put_u32(nlh, NFTA_SET_DESC_SIZE, htonl(s->desc.size));
+}
+
+static void nftnl_set_nlmsg_build_desc_concat_payload(struct nlmsghdr *nlh,
+ struct nftnl_set *s)
+{
+ struct nlattr *nest;
+ int i;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_SET_DESC_CONCAT);
+ for (i = 0; i < NFT_REG32_COUNT && i < s->desc.field_count; i++) {
+ struct nlattr *nest_elem;
+
+ nest_elem = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ mnl_attr_put_u32(nlh, NFTA_SET_FIELD_LEN,
+ htonl(s->desc.field_len[i]));
+ mnl_attr_nest_end(nlh, nest_elem);
+ }
+ mnl_attr_nest_end(nlh, nest);
+}
+
+static void
+nftnl_set_nlmsg_build_desc_payload(struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_SET_DESC);
+
+ if (s->flags & (1 << NFTNL_SET_DESC_SIZE))
+ nftnl_set_nlmsg_build_desc_size_payload(nlh, s);
+ if (s->flags & (1 << NFTNL_SET_DESC_CONCAT))
+ nftnl_set_nlmsg_build_desc_concat_payload(nlh, s);
+
+ mnl_attr_nest_end(nlh, nest);
+}
+
+EXPORT_SYMBOL(nftnl_set_nlmsg_build_payload);
+void nftnl_set_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ int num_exprs = 0;
+
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_SET_TABLE, s->table);
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ mnl_attr_put_strz(nlh, NFTA_SET_NAME, s->name);
+ if (s->flags & (1 << NFTNL_SET_HANDLE))
+ mnl_attr_put_u64(nlh, NFTA_SET_HANDLE, htobe64(s->handle));
+ if (s->flags & (1 << NFTNL_SET_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_SET_FLAGS, htonl(s->set_flags));
+ if (s->flags & (1 << NFTNL_SET_KEY_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_SET_KEY_TYPE, htonl(s->key_type));
+ if (s->flags & (1 << NFTNL_SET_KEY_LEN))
+ mnl_attr_put_u32(nlh, NFTA_SET_KEY_LEN, htonl(s->key_len));
+ /* These are only used to map matching -> action (1:1) */
+ if (s->flags & (1 << NFTNL_SET_DATA_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_SET_DATA_TYPE, htonl(s->data_type));
+ if (s->flags & (1 << NFTNL_SET_DATA_LEN))
+ mnl_attr_put_u32(nlh, NFTA_SET_DATA_LEN, htonl(s->data_len));
+ if (s->flags & (1 << NFTNL_SET_OBJ_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_SET_OBJ_TYPE, htonl(s->obj_type));
+ if (s->flags & (1 << NFTNL_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_SET_ID, htonl(s->id));
+ if (s->flags & (1 << NFTNL_SET_POLICY))
+ mnl_attr_put_u32(nlh, NFTA_SET_POLICY, htonl(s->policy));
+ if (s->flags & (1 << NFTNL_SET_DESC_SIZE | 1 << NFTNL_SET_DESC_CONCAT))
+ nftnl_set_nlmsg_build_desc_payload(nlh, s);
+ if (s->flags & (1 << NFTNL_SET_TIMEOUT))
+ mnl_attr_put_u64(nlh, NFTA_SET_TIMEOUT, htobe64(s->timeout));
+ if (s->flags & (1 << NFTNL_SET_GC_INTERVAL))
+ mnl_attr_put_u32(nlh, NFTA_SET_GC_INTERVAL, htonl(s->gc_interval));
+ if (s->flags & (1 << NFTNL_SET_USERDATA))
+ mnl_attr_put(nlh, NFTA_SET_USERDATA, s->user.len, s->user.data);
+ if (!list_empty(&s->expr_list)) {
+ struct nftnl_expr *expr;
+
+ list_for_each_entry(expr, &s->expr_list, head)
+ num_exprs++;
+
+ if (num_exprs == 1) {
+ struct nlattr *nest1;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_EXPR);
+ list_for_each_entry(expr, &s->expr_list, head)
+ nftnl_expr_build_payload(nlh, expr);
+
+ mnl_attr_nest_end(nlh, nest1);
+ } else if (num_exprs > 1) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_EXPRESSIONS);
+ list_for_each_entry(expr, &s->expr_list, head) {
+ nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ }
+}
+
+EXPORT_SYMBOL(nftnl_set_add_expr);
+void nftnl_set_add_expr(struct nftnl_set *s, struct nftnl_expr *expr)
+{
+ list_add_tail(&expr->head, &s->expr_list);
+}
+
+EXPORT_SYMBOL(nftnl_set_expr_foreach);
+int nftnl_set_expr_foreach(const struct nftnl_set *s,
+ int (*cb)(struct nftnl_expr *e, void *data),
+ void *data)
+{
+ struct nftnl_expr *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &s->expr_list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int nftnl_set_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_TABLE:
+ case NFTA_SET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_FLAGS:
+ case NFTA_SET_KEY_TYPE:
+ case NFTA_SET_KEY_LEN:
+ case NFTA_SET_DATA_TYPE:
+ case NFTA_SET_DATA_LEN:
+ case NFTA_SET_ID:
+ case NFTA_SET_POLICY:
+ case NFTA_SET_GC_INTERVAL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_USERDATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_TIMEOUT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_DESC:
+ case NFTA_SET_EXPR:
+ case NFTA_SET_EXPRESSIONS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_set_desc_concat_field_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ struct nftnl_set *s = data;
+
+ if (type != NFTA_SET_FIELD_LEN)
+ return MNL_CB_OK;
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U32))
+ return MNL_CB_ERROR;
+
+ s->desc.field_len[s->desc.field_count] = ntohl(mnl_attr_get_u32(attr));
+ s->desc.field_count++;
+
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_set_desc_concat_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ struct nftnl_set *s = data;
+
+ if (type != NFTA_LIST_ELEM)
+ return MNL_CB_OK;
+
+ return mnl_attr_parse_nested(attr,
+ nftnl_set_desc_concat_field_parse_attr_cb,
+ s);
+}
+
+static int nftnl_set_desc_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr), err;
+ struct nftnl_set *s = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_SET_DESC_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_SET_DESC_SIZE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ abi_breakage();
+ break;
+ }
+
+ s->desc.size = ntohl(mnl_attr_get_u32(attr));
+ s->flags |= (1 << NFTNL_SET_DESC_SIZE);
+ break;
+ case NFTA_SET_DESC_CONCAT:
+ err = mnl_attr_parse_nested(attr,
+ nftnl_set_desc_concat_parse_attr_cb,
+ s);
+ if (err != MNL_CB_OK)
+ abi_breakage();
+
+ s->flags |= (1 << NFTNL_SET_DESC_CONCAT);
+ break;
+ default:
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int nftnl_set_desc_parse(struct nftnl_set *s, const struct nlattr *attr)
+{
+ return mnl_attr_parse_nested(attr, nftnl_set_desc_parse_attr_cb, s);
+}
+
+EXPORT_SYMBOL(nftnl_set_nlmsg_parse);
+int nftnl_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ struct nlattr *tb[NFTA_SET_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ struct nftnl_expr *expr, *next;
+ int ret;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_set_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SET_TABLE]) {
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ xfree(s->table);
+ s->table = strdup(mnl_attr_get_str(tb[NFTA_SET_TABLE]));
+ if (!s->table)
+ return -1;
+ s->flags |= (1 << NFTNL_SET_TABLE);
+ }
+ if (tb[NFTA_SET_NAME]) {
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ xfree(s->name);
+ s->name = strdup(mnl_attr_get_str(tb[NFTA_SET_NAME]));
+ if (!s->name)
+ return -1;
+ s->flags |= (1 << NFTNL_SET_NAME);
+ }
+ if (tb[NFTA_SET_HANDLE]) {
+ s->handle = be64toh(mnl_attr_get_u64(tb[NFTA_SET_HANDLE]));
+ s->flags |= (1 << NFTNL_SET_HANDLE);
+ }
+ if (tb[NFTA_SET_FLAGS]) {
+ s->set_flags = ntohl(mnl_attr_get_u32(tb[NFTA_SET_FLAGS]));
+ s->flags |= (1 << NFTNL_SET_FLAGS);
+ }
+ if (tb[NFTA_SET_KEY_TYPE]) {
+ s->key_type = ntohl(mnl_attr_get_u32(tb[NFTA_SET_KEY_TYPE]));
+ s->flags |= (1 << NFTNL_SET_KEY_TYPE);
+ }
+ if (tb[NFTA_SET_KEY_LEN]) {
+ s->key_len = ntohl(mnl_attr_get_u32(tb[NFTA_SET_KEY_LEN]));
+ s->flags |= (1 << NFTNL_SET_KEY_LEN);
+ }
+ if (tb[NFTA_SET_DATA_TYPE]) {
+ s->data_type = ntohl(mnl_attr_get_u32(tb[NFTA_SET_DATA_TYPE]));
+ s->flags |= (1 << NFTNL_SET_DATA_TYPE);
+ }
+ if (tb[NFTA_SET_DATA_LEN]) {
+ s->data_len = ntohl(mnl_attr_get_u32(tb[NFTA_SET_DATA_LEN]));
+ s->flags |= (1 << NFTNL_SET_DATA_LEN);
+ }
+ if (tb[NFTA_SET_OBJ_TYPE]) {
+ s->obj_type = ntohl(mnl_attr_get_u32(tb[NFTA_SET_OBJ_TYPE]));
+ s->flags |= (1 << NFTNL_SET_OBJ_TYPE);
+ }
+ if (tb[NFTA_SET_ID]) {
+ s->id = ntohl(mnl_attr_get_u32(tb[NFTA_SET_ID]));
+ s->flags |= (1 << NFTNL_SET_ID);
+ }
+ if (tb[NFTA_SET_POLICY]) {
+ s->policy = ntohl(mnl_attr_get_u32(tb[NFTA_SET_POLICY]));
+ s->flags |= (1 << NFTNL_SET_POLICY);
+ }
+ if (tb[NFTA_SET_TIMEOUT]) {
+ s->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_SET_TIMEOUT]));
+ s->flags |= (1 << NFTNL_SET_TIMEOUT);
+ }
+ if (tb[NFTA_SET_GC_INTERVAL]) {
+ s->gc_interval = ntohl(mnl_attr_get_u32(tb[NFTA_SET_GC_INTERVAL]));
+ s->flags |= (1 << NFTNL_SET_GC_INTERVAL);
+ }
+ if (tb[NFTA_SET_USERDATA]) {
+ ret = nftnl_set_set_data(s, NFTNL_SET_USERDATA,
+ mnl_attr_get_payload(tb[NFTA_SET_USERDATA]),
+ mnl_attr_get_payload_len(tb[NFTA_SET_USERDATA]));
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_SET_DESC]) {
+ ret = nftnl_set_desc_parse(s, tb[NFTA_SET_DESC]);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_SET_EXPR]) {
+ expr = nftnl_expr_parse(tb[NFTA_SET_EXPR]);
+ if (!expr)
+ goto out_set_expr;
+
+ list_add(&expr->head, &s->expr_list);
+ s->flags |= (1 << NFTNL_SET_EXPR);
+ } else if (tb[NFTA_SET_EXPRESSIONS]) {
+ struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, tb[NFTA_SET_EXPRESSIONS]) {
+ if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM)
+ goto out_set_expr;
+
+ expr = nftnl_expr_parse(attr);
+ if (expr == NULL)
+ goto out_set_expr;
+
+ list_add_tail(&expr->head, &s->expr_list);
+ }
+ s->flags |= (1 << NFTNL_SET_EXPRESSIONS);
+ }
+
+ s->family = nfg->nfgen_family;
+ s->flags |= (1 << NFTNL_SET_FAMILY);
+
+ return 0;
+out_set_expr:
+ list_for_each_entry_safe(expr, next, &s->expr_list, head)
+ nftnl_expr_free(expr);
+
+ return -1;
+}
+
+static int nftnl_set_do_parse(struct nftnl_set *s, enum nftnl_parse_type type,
+ const void *data, struct nftnl_parse_err *err,
+ enum nftnl_parse_input input)
+{
+ int ret;
+ struct nftnl_parse_err perr = {};
+
+ switch (type) {
+ case NFTNL_PARSE_JSON:
+ case NFTNL_PARSE_XML:
+ default:
+ ret = -1;
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (err != NULL)
+ *err = perr;
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_set_parse);
+int nftnl_set_parse(struct nftnl_set *s, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ return nftnl_set_do_parse(s, type, data, err, NFTNL_PARSE_BUFFER);
+}
+
+EXPORT_SYMBOL(nftnl_set_parse_file);
+int nftnl_set_parse_file(struct nftnl_set *s, enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ return nftnl_set_do_parse(s, type, fp, err, NFTNL_PARSE_FILE);
+}
+
+static int nftnl_set_snprintf_default(char *buf, size_t remain,
+ const struct nftnl_set *s,
+ uint32_t type, uint32_t flags)
+{
+ struct nftnl_set_elem *elem;
+ int ret, offset = 0;
+
+ ret = snprintf(buf, remain, "%s %s %x",
+ s->name, s->table, s->set_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (s->flags & (1 << NFTNL_SET_TIMEOUT)) {
+ ret = snprintf(buf + offset, remain, " timeout %"PRIu64"ms",
+ s->timeout);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (s->flags & (1 << NFTNL_SET_GC_INTERVAL)) {
+ ret = snprintf(buf + offset, remain, " gc_interval %ums",
+ s->gc_interval);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (s->flags & (1 << NFTNL_SET_POLICY)) {
+ ret = snprintf(buf + offset, remain, " policy %u", s->policy);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (s->flags & (1 << NFTNL_SET_DESC_SIZE)) {
+ ret = snprintf(buf + offset, remain, " size %u", s->desc.size);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ /* Empty set? Skip printinf of elements */
+ if (list_empty(&s->element_list))
+ return offset;
+
+ ret = snprintf(buf + offset, remain, "\n");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ list_for_each_entry(elem, &s->element_list, head) {
+ ret = snprintf(buf + offset, remain, "\t");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_set_elem_snprintf_default(buf + offset, remain,
+ elem);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static int nftnl_set_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_set *s, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t inner_flags = flags;
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ /* prevent set_elems to print as events */
+ inner_flags &= ~NFTNL_OF_EVENT_ANY;
+
+ ret = nftnl_set_snprintf_default(buf + offset, remain, s, type,
+ inner_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_set_snprintf);
+int nftnl_set_snprintf(char *buf, size_t size, const struct nftnl_set *s,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_set_cmd_snprintf(buf, size, s, nftnl_flag2cmd(flags), type,
+ flags);
+}
+
+static int nftnl_set_do_snprintf(char *buf, size_t size, const void *s,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_set_snprintf(buf, size, s, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_set_fprintf);
+int nftnl_set_fprintf(FILE *fp, const struct nftnl_set *s, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, s, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_set_do_snprintf);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_add);
+void nftnl_set_elem_add(struct nftnl_set *s, struct nftnl_set_elem *elem)
+{
+ list_add_tail(&elem->head, &s->element_list);
+}
+
+#define SET_NAME_HSIZE 512
+
+struct nftnl_set_list {
+ struct list_head list;
+ struct hlist_head name_hash[SET_NAME_HSIZE];
+};
+
+EXPORT_SYMBOL(nftnl_set_list_alloc);
+struct nftnl_set_list *nftnl_set_list_alloc(void)
+{
+ struct nftnl_set_list *list;
+ int i;
+
+ list = calloc(1, sizeof(struct nftnl_set_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+ for (i = 0; i < SET_NAME_HSIZE; i++)
+ INIT_HLIST_HEAD(&list->name_hash[i]);
+
+ return list;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_free);
+void nftnl_set_list_free(struct nftnl_set_list *list)
+{
+ struct nftnl_set *s, *tmp;
+
+ list_for_each_entry_safe(s, tmp, &list->list, head) {
+ list_del(&s->head);
+ hlist_del(&s->hnode);
+ nftnl_set_free(s);
+ }
+ xfree(list);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_is_empty);
+int nftnl_set_list_is_empty(const struct nftnl_set_list *list)
+{
+ return list_empty(&list->list);
+}
+
+static uint32_t djb_hash(const char *key)
+{
+ uint32_t i, hash = 5381;
+
+ for (i = 0; i < strlen(key); i++)
+ hash = ((hash << 5) + hash) + key[i];
+
+ return hash;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_add);
+void nftnl_set_list_add(struct nftnl_set *s, struct nftnl_set_list *list)
+{
+ int key = djb_hash(s->name) % SET_NAME_HSIZE;
+
+ hlist_add_head(&s->hnode, &list->name_hash[key]);
+ list_add(&s->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_add_tail);
+void nftnl_set_list_add_tail(struct nftnl_set *s, struct nftnl_set_list *list)
+{
+ int key = djb_hash(s->name) % SET_NAME_HSIZE;
+
+ hlist_add_head(&s->hnode, &list->name_hash[key]);
+ list_add_tail(&s->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_del);
+void nftnl_set_list_del(struct nftnl_set *s)
+{
+ list_del(&s->head);
+ hlist_del(&s->hnode);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_foreach);
+int nftnl_set_list_foreach(struct nftnl_set_list *set_list,
+ int (*cb)(struct nftnl_set *t, void *data), void *data)
+{
+ struct nftnl_set *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &set_list->list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct nftnl_set_list_iter {
+ const struct nftnl_set_list *list;
+ struct nftnl_set *cur;
+};
+
+EXPORT_SYMBOL(nftnl_set_list_iter_create);
+struct nftnl_set_list_iter *
+nftnl_set_list_iter_create(const struct nftnl_set_list *l)
+{
+ struct nftnl_set_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_set_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ if (nftnl_set_list_is_empty(l))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(l->list.next, struct nftnl_set, head);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_iter_cur);
+struct nftnl_set *
+nftnl_set_list_iter_cur(const struct nftnl_set_list_iter *iter)
+{
+ return iter->cur;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_iter_next);
+struct nftnl_set *nftnl_set_list_iter_next(struct nftnl_set_list_iter *iter)
+{
+ struct nftnl_set *s = iter->cur;
+
+ if (s == NULL)
+ return NULL;
+
+ /* get next rule, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_set, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return s;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_iter_destroy);
+void nftnl_set_list_iter_destroy(const struct nftnl_set_list_iter *iter)
+{
+ xfree(iter);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_lookup_byname);
+struct nftnl_set *
+nftnl_set_list_lookup_byname(struct nftnl_set_list *set_list, const char *set)
+{
+ int key = djb_hash(set) % SET_NAME_HSIZE;
+ struct hlist_node *n;
+ struct nftnl_set *s;
+
+ hlist_for_each_entry(s, n, &set_list->name_hash[key], hnode) {
+ if (!strcmp(set, s->name))
+ return s;
+ }
+ return NULL;
+}
+
+int nftnl_set_lookup_id(struct nftnl_expr *e,
+ struct nftnl_set_list *set_list, uint32_t *set_id)
+{
+ const char *set_name;
+ struct nftnl_set *s;
+
+ set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
+ if (set_name == NULL)
+ return 0;
+
+ s = nftnl_set_list_lookup_byname(set_list, set_name);
+ if (s == NULL)
+ return 0;
+
+ *set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
+ return 1;
+}