summaryrefslogtreecommitdiffstats
path: root/src/mnl.c
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2013-06-13 16:38:33 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2013-06-24 11:50:00 +0200
commitaae836a7aa628af4d4d5dd97d0eefa898e8f5245 (patch)
tree0a5999838de7e19bba5c2bfb7172fec8d70c2da5 /src/mnl.c
parentf171d1cf8a6530fe0ac204d4a6520fe0e2a70da4 (diff)
src: use libnftables
This patch migrates nft to use the libnftables library, that is used by the iptables over nftables compat utility as well. Most of the conversion was pretty straight forward. Some small significant changes happened in the handling of set element and immediate data abstraction that libnl provides. libnftables is a bit more granular since it splits the struct nfnl_nft_data into three attributes: verdict, chain and plain data (used in maps). I have added a new file src/mnl.c that contains the low level netlink communication that now resides in nftables source tree instead of the library. This should help to implement the batching support using libmnl in follow up patches. I also spent some significant amount of time running my tests to make sure that we don't increase the number of bugs that we already have (I plan to provide a list of those that I have detected and diagnosed, so anyone else can help us to fix them). As a side effect, this change should also prepare the ground for JSON and XML support anytime soon. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/mnl.c')
-rw-r--r--src/mnl.c478
1 files changed, 478 insertions, 0 deletions
diff --git a/src/mnl.c b/src/mnl.c
new file mode 100644
index 00000000..ea9637c8
--- /dev/null
+++ b/src/mnl.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2013 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <libmnl/libmnl.h>
+#include <libnftables/table.h>
+#include <libnftables/chain.h>
+#include <libnftables/rule.h>
+#include <libnftables/expr.h>
+#include <libnftables/set.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <mnl.h>
+#include <errno.h>
+#include <utils.h>
+
+static int seq;
+
+static int
+mnl_talk(struct mnl_socket *nf_sock, const void *data, unsigned int len,
+ int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t portid = mnl_socket_get_portid(nf_sock);
+ int ret;
+
+ if (mnl_socket_sendto(nf_sock, data, len) < 0)
+ return -1;
+
+ ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, cb, cb_data);
+ if (ret <= 0)
+ goto out;
+
+ ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf));
+ }
+out:
+ if (ret < 0 && errno == EAGAIN)
+ return 0;
+
+ return ret;
+}
+
+/*
+ * Rule
+ */
+int mnl_nft_rule_add(struct mnl_socket *nf_sock, struct nft_rule *nlr,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWRULE,
+ nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY),
+ NLM_F_APPEND|NLM_F_ACK|NLM_F_CREATE, seq);
+ nft_rule_nlmsg_build_payload(nlh, nlr);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_rule_delete(struct mnl_socket *nf_sock, struct nft_rule *nlr,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_DELRULE,
+ nft_rule_attr_get_u32(nlr, NFT_RULE_ATTR_FAMILY),
+ NLM_F_ACK, seq);
+ nft_rule_nlmsg_build_payload(nlh, nlr);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int rule_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_rule_list *nlr_list = data;
+ struct nft_rule *r;
+
+ r = nft_rule_alloc();
+ if (r == NULL)
+ memory_allocation_error();
+
+ if (nft_rule_nlmsg_parse(nlh, r) < 0)
+ goto err_free;
+
+ nft_rule_list_add(r, nlr_list);
+ return MNL_CB_OK;
+
+err_free:
+ nft_rule_free(r);
+ return MNL_CB_OK;
+}
+
+struct nft_rule_list *mnl_nft_rule_dump(struct mnl_socket *nf_sock, int family)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_rule_list *nlr_list;
+ int ret;
+
+ nlr_list = nft_rule_list_alloc();
+ if (nlr_list == NULL)
+ memory_allocation_error();
+
+ nlh = nft_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
+ NLM_F_DUMP, seq);
+
+ ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, rule_cb, nlr_list);
+ if (ret < 0)
+ goto err;
+
+ return nlr_list;
+err:
+ nft_rule_list_free(nlr_list);
+ return NULL;
+}
+
+/*
+ * Chain
+ */
+int mnl_nft_chain_add(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+ unsigned int flags)
+
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWCHAIN,
+ nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY),
+ NLM_F_ACK|flags, seq);
+ nft_chain_nlmsg_build_payload(nlh, nlc);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_chain_delete(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_DELCHAIN,
+ nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY),
+ NLM_F_ACK, seq);
+ nft_chain_nlmsg_build_payload(nlh, nlc);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int chain_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_chain_list *nlc_list = data;
+ struct nft_chain *c;
+
+ c = nft_chain_alloc();
+ if (c == NULL)
+ memory_allocation_error();
+
+ if (nft_chain_nlmsg_parse(nlh, c) < 0)
+ goto err_free;
+
+ nft_chain_list_add(c, nlc_list);
+ return MNL_CB_OK;
+
+err_free:
+ nft_chain_free(c);
+ return MNL_CB_OK;
+}
+
+struct nft_chain_list *mnl_nft_chain_dump(struct mnl_socket *nf_sock, int family)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_chain_list *nlc_list;
+ int ret;
+
+ nlc_list = nft_chain_list_alloc();
+ if (nlc_list == NULL)
+ memory_allocation_error();
+
+ nlh = nft_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family,
+ NLM_F_DUMP, seq);
+
+ ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, chain_cb, nlc_list);
+ if (ret < 0)
+ goto err;
+
+ return nlc_list;
+err:
+ nft_chain_list_free(nlc_list);
+ return NULL;
+}
+
+static int chain_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ nft_chain_nlmsg_parse(nlh, data);
+ return MNL_CB_OK;
+}
+
+int mnl_nft_chain_get(struct mnl_socket *nf_sock, struct nft_chain *nlc,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
+ nft_chain_attr_get_u32(nlc, NFT_CHAIN_ATTR_FAMILY),
+ NLM_F_ACK|flags, seq);
+ nft_chain_nlmsg_build_payload(nlh, nlc);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, chain_get_cb, nlc);
+}
+
+/*
+ * Table
+ */
+int mnl_nft_table_add(struct mnl_socket *nf_sock, struct nft_table *nlt,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_NEWTABLE,
+ nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY),
+ NLM_F_EXCL|NLM_F_ACK, seq);
+ nft_table_nlmsg_build_payload(nlh, nlt);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_table_delete(struct mnl_socket *nf_sock, struct nft_table *nlt,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_DELTABLE,
+ nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY),
+ NLM_F_ACK, seq);
+ nft_table_nlmsg_build_payload(nlh, nlt);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int table_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_table_list *nlt_list = data;
+ struct nft_table *t;
+
+ t = nft_table_alloc();
+ if (t == NULL)
+ memory_allocation_error();
+
+ if (nft_table_nlmsg_parse(nlh, t) < 0)
+ goto err_free;
+
+ nft_table_list_add(t, nlt_list);
+ return MNL_CB_OK;
+
+err_free:
+ nft_table_free(t);
+ return MNL_CB_OK;
+}
+
+struct nft_table_list *mnl_nft_table_dump(struct mnl_socket *nf_sock, int family)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_table_list *nlt_list;
+ int ret;
+
+ nlt_list = nft_table_list_alloc();
+ if (nlt_list == NULL)
+ memory_allocation_error();
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family,
+ NLM_F_DUMP, seq);
+
+ ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, table_cb, nlt_list);
+ if (ret < 0)
+ goto err;
+
+ return nlt_list;
+err:
+ nft_table_list_free(nlt_list);
+ return NULL;
+}
+
+static int table_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_table *t = data;
+
+ nft_table_nlmsg_parse(nlh, t);
+ return MNL_CB_OK;
+}
+
+int mnl_nft_table_get(struct mnl_socket *nf_sock, struct nft_table *nlt,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_table_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE,
+ nft_table_attr_get_u32(nlt, NFT_TABLE_ATTR_FAMILY),
+ NLM_F_ACK, seq);
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, table_get_cb, nlt);
+}
+
+/*
+ * Set
+ */
+static int set_add_cb(const struct nlmsghdr *nlh, void *data)
+{
+ nft_set_nlmsg_parse(nlh, data);
+ return MNL_CB_OK;
+}
+
+int mnl_nft_set_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_NEWSET,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ flags|NLM_F_CREATE|NLM_F_ACK, seq);
+ nft_set_nlmsg_build_payload(nlh, nls);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_add_cb, nls);
+}
+
+int mnl_nft_set_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_DELSET,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ flags|NLM_F_ACK, seq);
+ nft_set_nlmsg_build_payload(nlh, nls);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int set_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_set_list *nls_list = data;
+ struct nft_set *s;
+
+ s = nft_set_alloc();
+ if (s == NULL)
+ memory_allocation_error();
+
+ if (nft_set_nlmsg_parse(nlh, s) < 0)
+ goto err_free;
+
+ nft_set_list_add(s, nls_list);
+ return MNL_CB_OK;
+
+err_free:
+ nft_set_free(s);
+ return MNL_CB_OK;
+}
+
+struct nft_set_list *
+mnl_nft_set_dump(struct mnl_socket *nf_sock, int family, const char *table)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nft_set *s;
+ struct nft_set_list *nls_list;
+ int ret;
+
+ s = nft_set_alloc();
+ if (s == NULL)
+ memory_allocation_error();
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
+ NLM_F_DUMP|NLM_F_ACK, seq);
+ nft_set_attr_set(s, NFT_SET_ATTR_TABLE, table);
+ nft_set_nlmsg_build_payload(nlh, s);
+ nft_set_free(s);
+
+ nls_list = nft_set_list_alloc();
+ if (nls_list == NULL)
+ memory_allocation_error();
+
+ ret = mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_cb, nls_list);
+ if (ret < 0)
+ goto err;
+
+ return nls_list;
+err:
+ nft_set_list_free(nls_list);
+ return NULL;
+}
+
+static int set_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nft_set *s = data;
+
+ nft_set_nlmsg_parse(nlh, s);
+ return MNL_CB_OK;
+}
+
+int mnl_nft_set_get(struct mnl_socket *nf_sock, struct nft_set *nls)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ NLM_F_ACK, seq);
+ nft_set_nlmsg_build_payload(nlh, nls);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_get_cb, nls);
+}
+
+/*
+ * Set elements
+ */
+int mnl_nft_setelem_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_NEWSETELEM,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK, seq);
+ nft_set_elems_nlmsg_build_payload(nlh, nls);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+int mnl_nft_setelem_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
+ unsigned int flags)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_DELSETELEM,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ NLM_F_ACK, seq);
+ nft_set_elems_nlmsg_build_payload(nlh, nls);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL);
+}
+
+static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
+{
+ nft_set_elems_nlmsg_parse(nlh, data);
+ return MNL_CB_OK;
+}
+
+int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+
+ nlh = nft_set_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM,
+ nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY),
+ NLM_F_DUMP|NLM_F_ACK, seq);
+ nft_set_nlmsg_build_payload(nlh, nls);
+
+ return mnl_talk(nf_sock, nlh, nlh->nlmsg_len, set_elem_cb, nls);
+}