summaryrefslogtreecommitdiffstats
path: root/lib/mnl.c
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-04-22 16:52:29 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2010-04-22 16:52:29 +0200
commit8e0608d31d988333ff04f3faaa6e851c0ecdbc6e (patch)
treeb042fc732d7c784d298ed42496f88a2f164f413c /lib/mnl.c
parent1e6e8bd9a62aa7cd72e13db9355badc96df18ee8 (diff)
Fourth stage to ipset-5
Add new userspace files: include/, lib/ and plus new files in src/.
Diffstat (limited to 'lib/mnl.c')
-rw-r--r--lib/mnl.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/lib/mnl.c b/lib/mnl.c
new file mode 100644
index 0000000..5662a47
--- /dev/null
+++ b/lib/mnl.c
@@ -0,0 +1,157 @@
+/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
+ *
+ * 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.
+ */
+#include <assert.h> /* assert */
+#include <errno.h> /* errno */
+#include <stdlib.h> /* calloc, free */
+#include <time.h> /* time */
+
+#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
+#include <libipset/session.h> /* ipset_session_handle */
+#include <libipset/ui.h> /* IPSET_ENV_EXIST */
+#include <libipset/utils.h> /* UNUSED */
+#include <libipset/mnl.h> /* prototypes */
+
+#ifndef NFNL_SUBSYS_IPSET
+#define NFNL_SUBSYS_IPSET 6
+#endif
+
+struct ipset_handle {
+ struct mnl_socket *h;
+ unsigned int seq;
+ unsigned int portid;
+ mnl_cb_t *cb_ctl;
+ void *data;
+};
+
+/* Netlink flags of the commands */
+static uint16_t cmdflags[] = {
+ [IPSET_CMD_CREATE-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|NLM_F_EXCL,
+ [IPSET_CMD_DESTROY-1] = NLM_F_REQUEST|NLM_F_ACK,
+ [IPSET_CMD_FLUSH-1] = NLM_F_REQUEST|NLM_F_ACK,
+ [IPSET_CMD_RENAME-1] = NLM_F_REQUEST|NLM_F_ACK,
+ [IPSET_CMD_SWAP-1] = NLM_F_REQUEST|NLM_F_ACK,
+ [IPSET_CMD_LIST-1] = NLM_F_REQUEST|NLM_F_ROOT|NLM_F_MATCH|NLM_F_DUMP,
+ [IPSET_CMD_SAVE-1] = NLM_F_REQUEST|NLM_F_ROOT|NLM_F_MATCH|NLM_F_DUMP,
+ [IPSET_CMD_ADD-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
+ [IPSET_CMD_DEL-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
+ [IPSET_CMD_TEST-1] = NLM_F_REQUEST|NLM_F_ACK,
+ [IPSET_CMD_HEADER-1] = NLM_F_REQUEST,
+ [IPSET_CMD_TYPE-1] = NLM_F_REQUEST,
+ [IPSET_CMD_PROTOCOL-1] = NLM_F_REQUEST,
+};
+
+int
+ipset_get_nlmsg_type(const struct nlmsghdr *nlh)
+{
+ return nlh->nlmsg_type & ~(NFNL_SUBSYS_IPSET << 8);
+}
+
+static void
+ipset_mnl_fill_hdr(struct ipset_handle *handle, enum ipset_cmd cmd,
+ void *buffer, size_t len UNUSED, uint8_t envflags)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfg;
+
+ assert(handle);
+ assert(buffer);
+ assert(cmd > IPSET_CMD_NONE && cmd < IPSET_MSG_MAX);
+
+ nlh = mnl_nlmsg_put_header(buffer);
+ nlh->nlmsg_type = cmd | (NFNL_SUBSYS_IPSET << 8);
+ nlh->nlmsg_flags = cmdflags[cmd - 1];
+ if (envflags & IPSET_ENV_EXIST)
+ nlh->nlmsg_flags &= ~NLM_F_EXCL;
+ nlh->nlmsg_seq = handle->seq = time(NULL);
+
+ nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfg->nfgen_family = AF_INET;
+ nfg->version = NFNETLINK_V0;
+ nfg->res_id = htons(0);
+}
+
+static int
+ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len)
+{
+ struct nlmsghdr *nlh = buffer;
+ int ret;
+
+ assert(handle);
+ assert(buffer);
+
+ if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0)
+ return -ECOMM;
+
+ D("message sent");
+ ret = mnl_socket_recvfrom(handle->h, buffer, len);
+ D("message received, ret: %d", ret);
+ while (ret > 0) {
+ ret = mnl_cb_run2(buffer, ret,
+ handle->seq, handle->portid,
+ handle->cb_ctl[NLMSG_MIN_TYPE],
+ handle->data,
+ handle->cb_ctl, NLMSG_MIN_TYPE);
+ D("nfln_cb_run2, ret: %d", ret);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(handle->h, buffer, len);
+ D("message received, ret: %d", ret);
+ }
+ return ret > 0 ? 0 : ret;
+}
+
+static struct ipset_handle *
+ipset_mnl_init(mnl_cb_t *cb_ctl, void *data)
+{
+ struct ipset_handle *handle;
+
+ assert(cb_ctl);
+ assert(data);
+
+ handle = calloc(1, sizeof(*handle));
+ if (!handle)
+ return NULL;
+
+ handle->h = mnl_socket_open(NETLINK_NETFILTER);
+ if (!handle->h)
+ goto free_handle;
+
+ if (mnl_socket_bind(handle->h, 0, MNL_SOCKET_AUTOPID) < 0)
+ goto close_nl;
+
+ handle->portid = mnl_socket_get_portid(handle->h);
+ handle->cb_ctl = cb_ctl;
+ handle->data = data;
+
+ return handle;
+
+close_nl:
+ mnl_socket_close(handle->h);
+free_handle:
+ free(handle);
+
+ return NULL;
+}
+
+static int
+ipset_mnl_fini(struct ipset_handle *handle)
+{
+ assert(handle);
+
+ if (handle->h)
+ mnl_socket_close(handle->h);
+
+ free(handle);
+ return 0;
+}
+
+const struct ipset_transport ipset_mnl_transport = {
+ .init = ipset_mnl_init,
+ .fini = ipset_mnl_fini,
+ .fill_hdr = ipset_mnl_fill_hdr,
+ .query = ipset_mnl_query,
+};