Patchwork [libmnl,3/9] examples: add for min mmap

login
register
mail settings
Submitter Ken-ichirou MATSUZAWA
Date Dec. 7, 2013, 3:11 p.m.
Message ID <87ppp88znf.wl%chamaken@gmail.com>
Download mbox | patch
Permalink /patch/298690/
State Superseded
Headers show

Comments

Ken-ichirou MATSUZAWA - Dec. 7, 2013, 3:11 p.m.
Signed-off-by: Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp>

---
 configure.ac                        |   2 +-
 examples/Makefile.am                |   2 +-
 examples/mmap/Makefile.am           |   1 +
 examples/mmap/min/Makefile.am       |  26 +++
 examples/mmap/min/nf-log.c          | 292 ++++++++++++++++++++++++
 examples/mmap/min/nf-queue.c        | 333 ++++++++++++++++++++++++++++
 examples/mmap/min/nfct-dump.c       | 378 +++++++++++++++++++++++++++++++
 examples/mmap/min/rtnl-addr-dump.c  | 204 +++++++++++++++++
 examples/mmap/min/rtnl-link-dump.c  | 199 +++++++++++++++++
 examples/mmap/min/rtnl-route-dump.c | 427 ++++++++++++++++++++++++++++++++++++
 10 files changed, 1862 insertions(+), 2 deletions(-)
 create mode 100644 examples/mmap/Makefile.am
 create mode 100644 examples/mmap/min/Makefile.am
 create mode 100644 examples/mmap/min/nf-log.c
 create mode 100644 examples/mmap/min/nf-queue.c
 create mode 100644 examples/mmap/min/nfct-dump.c
 create mode 100644 examples/mmap/min/rtnl-addr-dump.c
 create mode 100644 examples/mmap/min/rtnl-link-dump.c
 create mode 100644 examples/mmap/min/rtnl-route-dump.c

Patch

diff --git a/configure.ac b/configure.ac
index 313a015..1b0b2cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,5 +27,5 @@  regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
 	-Wformat=2 -pipe"
 AC_SUBST([regular_CPPFLAGS])
 AC_SUBST([regular_CFLAGS])
-AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/libmnl/Makefile include/linux/Makefile include/linux/netfilter/Makefile examples/Makefile examples/genl/Makefile examples/kobject/Makefile examples/netfilter/Makefile examples/rtnl/Makefile libmnl.pc doxygen.cfg])
+AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/libmnl/Makefile include/linux/Makefile include/linux/netfilter/Makefile examples/Makefile examples/genl/Makefile examples/kobject/Makefile examples/netfilter/Makefile examples/rtnl/Makefile examples/mmap/Makefile examples/mmap/min/Makefile libmnl.pc doxygen.cfg])
 AC_OUTPUT
diff --git a/examples/Makefile.am b/examples/Makefile.am
index e5cb052..aad6f13 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1 +1 @@ 
-SUBDIRS = genl kobject netfilter rtnl
+SUBDIRS = genl kobject netfilter rtnl mmap
diff --git a/examples/mmap/Makefile.am b/examples/mmap/Makefile.am
new file mode 100644
index 0000000..a532ec0
--- /dev/null
+++ b/examples/mmap/Makefile.am
@@ -0,0 +1 @@ 
+SUBDIRS = min
diff --git a/examples/mmap/min/Makefile.am b/examples/mmap/min/Makefile.am
new file mode 100644
index 0000000..5b172c2
--- /dev/null
+++ b/examples/mmap/min/Makefile.am
@@ -0,0 +1,26 @@ 
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = nf-log \
+		 nf-queue \
+		 nfct-dump \
+		 rtnl-addr-dump \
+		 rtnl-link-dump \
+		 rtnl-route-dump
+
+nf_log_SOURCES = nf-log.c
+nf_log_LDADD = ../../../src/libmnl.la
+
+nf_queue_SOURCES = nf-queue.c
+nf_queue_LDADD = ../../../src/libmnl.la
+
+nfct_dump_SOURCES = nfct-dump.c
+nfct_dump_LDADD = ../../../src/libmnl.la
+
+rtnl_addr_dump_SOURCES = rtnl-addr-dump.c
+rtnl_addr_dump_LDADD = ../../../src/libmnl.la
+
+rtnl_link_dump_SOURCES = rtnl-link-dump.c
+rtnl_link_dump_LDADD = ../../../src/libmnl.la
+
+rtnl_route_dump_SOURCES = rtnl-route-dump.c
+rtnl_route_dump_LDADD = ../../../src/libmnl.la
diff --git a/examples/mmap/min/nf-log.c b/examples/mmap/min/nf-log.c
new file mode 100644
index 0000000..5dbf359
--- /dev/null
+++ b/examples/mmap/min/nf-log.c
@@ -0,0 +1,292 @@ 
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#ifndef aligned_be64
+#define aligned_be64 u_int64_t __attribute__((aligned(8)))
+#endif
+
+#include <linux/netfilter/nfnetlink_log.h>
+
+static int parse_attr_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* skip unsupported attribute in user-space */
+	if (mnl_attr_type_valid(attr, NFULA_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case NFULA_MARK:
+	case NFULA_IFINDEX_INDEV:
+	case NFULA_IFINDEX_OUTDEV:
+	case NFULA_IFINDEX_PHYSINDEV:
+	case NFULA_IFINDEX_PHYSOUTDEV:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case NFULA_TIMESTAMP:
+		if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+		    sizeof(struct nfulnl_msg_packet_timestamp)) < 0) {
+			perror("mnl_attr_validate2");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case NFULA_HWADDR:
+		if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+		    sizeof(struct nfulnl_msg_packet_hw)) < 0) {
+			perror("mnl_attr_validate2");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case NFULA_PREFIX:
+		if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case NFULA_PAYLOAD:
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int log_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[NFULA_MAX+1] = {};
+	struct nfulnl_msg_packet_hdr *ph = NULL;
+	const char *prefix = NULL;
+	uint32_t mark = 0;
+
+	mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
+	if (tb[NFULA_PACKET_HDR])
+		ph = mnl_attr_get_payload(tb[NFULA_PACKET_HDR]);
+	if (tb[NFULA_PREFIX])
+		prefix = mnl_attr_get_str(tb[NFULA_PREFIX]);
+	if (tb[NFULA_MARK])
+		mark = ntohl(mnl_attr_get_u32(tb[NFULA_MARK]));
+
+	printf("log received (prefix=\"%s\" hw=0x%04x hook=%u mark=%u)\n",
+		prefix ? prefix : "", ntohs(ph->hw_protocol), ph->hook,
+		mark);
+
+	return MNL_CB_OK;
+}
+
+static struct nlmsghdr *
+nflog_build_cfg_pf_request(char *buf, uint8_t command)
+{
+	struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+	nlh->nlmsg_type	= (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+
+	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_INET;
+	nfg->version = NFNETLINK_V0;
+
+	struct nfulnl_msg_config_cmd cmd = {
+		.command = command,
+	};
+	mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd);
+
+	return nlh;
+}
+
+static struct nlmsghdr *
+nflog_build_cfg_request(char *buf, uint8_t command, int qnum)
+{
+	struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+	nlh->nlmsg_type	= (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+
+	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_INET;
+	nfg->version = NFNETLINK_V0;
+	nfg->res_id = htons(qnum);
+
+	struct nfulnl_msg_config_cmd cmd = {
+		.command = command,
+	};
+	mnl_attr_put(nlh, NFULA_CFG_CMD, sizeof(cmd), &cmd);
+
+	return nlh;
+}
+
+static struct nlmsghdr *
+nflog_build_cfg_params(char *buf, uint8_t mode, int range, int qnum)
+{
+	struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+	nlh->nlmsg_type	= (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+
+	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_UNSPEC;
+	nfg->version = NFNETLINK_V0;
+	nfg->res_id = htons(qnum);
+
+	struct nfulnl_msg_config_mode params = {
+		.copy_range = htonl(range),
+		.copy_mode = mode,
+	};
+	mnl_attr_put(nlh, NFULA_CFG_MODE, sizeof(params), &params);
+
+	return nlh;
+}
+
+static int mnl_socket_poll(struct mnl_socket *nl)
+{
+	struct pollfd pfds[1];
+
+	while (1) {
+		pfds[0].fd	= mnl_socket_get_fd(nl);
+		pfds[0].events	= POLLIN | POLLERR;
+		pfds[0].revents = 0;
+
+		if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
+			return -1;
+
+		if (pfds[0].revents & POLLIN)
+			return 0;
+		if (pfds[0].revents & POLLERR)
+			return -1;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	ssize_t len;
+	void *ptr;
+	int ret;
+	unsigned int portid, qnum;
+
+	if (argc != 2) {
+		printf("Usage: %s [queue_num]\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	qnum = atoi(argv[1]);
+
+	nl = mnl_socket_open(NETLINK_NETFILTER);
+	if (nl == NULL) {
+		perror("mnl_socket_open");
+		exit(EXIT_FAILURE);
+	}
+
+	nlm = mnl_ring_map(nl, &nlmr, &nlmr);
+	if (nlm == NULL) {
+		perror("mnl_ring_map");
+		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);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	nlh = nflog_build_cfg_pf_request(MNL_MMAP_MSGHDR(hdr), NFULNL_CFG_CMD_PF_UNBIND);
+	hdr->nm_len = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	/*
+	 * nframes++;
+	 * assert(nframes < nlmr.nm_frame_nr);
+	 */
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	nlh = nflog_build_cfg_pf_request(MNL_MMAP_MSGHDR(hdr), NFULNL_CFG_CMD_PF_BIND);
+	hdr->nm_len = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	nlh = nflog_build_cfg_request(MNL_MMAP_MSGHDR(hdr), NFULNL_CFG_CMD_BIND, qnum);
+	hdr->nm_len = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	nlh = nflog_build_cfg_params(MNL_MMAP_MSGHDR(hdr), NFULNL_COPY_PACKET, 0xFFFF, qnum);
+	hdr->nm_len = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		ret = mnl_socket_poll(nl);
+		if (ret < 0) {
+			perror("mnl_socket_poll");
+			exit(EXIT_FAILURE);
+		}
+
+		while (1) {
+			hdr = mnl_ring_get_frame(nlm, MNL_RING_RX);
+
+			if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
+				ptr = MNL_MMAP_MSGHDR(hdr);
+				len = hdr->nm_len;
+				if (len == 0)
+					goto next;
+			} else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+				len = recv(mnl_socket_get_fd(nl),
+					   buf, sizeof(buf), MSG_DONTWAIT);
+				if (len <= 0)
+					break;
+				ptr = buf;
+			} else
+				break;
+
+			ret = mnl_cb_run(ptr, len, 0, portid, log_cb, NULL);
+			if (ret < 0)
+				goto end;
+next:
+			hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+			mnl_ring_advance(nlm, MNL_RING_RX);
+		}
+	}
+end:
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/min/nf-queue.c b/examples/mmap/min/nf-queue.c
new file mode 100644
index 0000000..7efda6e
--- /dev/null
+++ b/examples/mmap/min/nf-queue.c
@@ -0,0 +1,333 @@ 
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <poll.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#ifndef aligned_be64
+#define aligned_be64 u_int64_t __attribute__((aligned(8)))
+#endif
+
+#include <linux/netfilter/nfnetlink_queue.h>
+
+static int parse_attr_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* skip unsupported attribute in user-space */
+	if (mnl_attr_type_valid(attr, NFQA_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case NFQA_MARK:
+	case NFQA_IFINDEX_INDEV:
+	case NFQA_IFINDEX_OUTDEV:
+	case NFQA_IFINDEX_PHYSINDEV:
+	case NFQA_IFINDEX_PHYSOUTDEV:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case NFQA_TIMESTAMP:
+		if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+		    sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
+			perror("mnl_attr_validate2");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case NFQA_HWADDR:
+		if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
+		    sizeof(struct nfqnl_msg_packet_hw)) < 0) {
+			perror("mnl_attr_validate2");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case NFQA_PAYLOAD:
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int queue_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[NFQA_MAX+1] = {};
+	struct nfqnl_msg_packet_hdr *ph = NULL;
+	uint32_t id = 0;
+
+	mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
+	if (tb[NFQA_PACKET_HDR]) {
+		ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]);
+		id = ntohl(ph->packet_id);
+
+		printf("packet received (id=%u hw=0x%04x hook=%u)\n",
+		       id, ntohs(ph->hw_protocol), ph->hook);
+	}
+
+	return MNL_CB_OK + id;
+}
+
+static struct nlmsghdr *
+nfq_build_cfg_pf_request(struct mnl_ring_socket *nlm, uint8_t command)
+{
+	struct nl_mmap_hdr *hdr;
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
+		return NULL;
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	struct nlmsghdr *nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type	= (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+
+	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_UNSPEC;
+	nfg->version = NFNETLINK_V0;
+
+	struct nfqnl_msg_config_cmd cmd = {
+		.command = command,
+		.pf = htons(AF_INET),
+	};
+	mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
+
+	hdr->nm_len    = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	return nlh;
+}
+
+static struct nlmsghdr *
+nfq_build_cfg_request(struct mnl_ring_socket *nlm, uint8_t command, int queue_num)
+{
+	struct nl_mmap_hdr *hdr;
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
+		return NULL;
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	struct nlmsghdr *nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type	= (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+
+	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_UNSPEC;
+	nfg->version = NFNETLINK_V0;
+	nfg->res_id = htons(queue_num);
+
+	struct nfqnl_msg_config_cmd cmd = {
+		.command = command,
+		.pf = htons(AF_INET),
+	};
+	mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
+
+	hdr->nm_len    = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	return nlh;
+}
+
+static struct nlmsghdr *
+nfq_build_cfg_params(struct mnl_ring_socket *nlm, uint8_t mode, int range, int queue_num)
+{
+	struct nl_mmap_hdr *hdr;
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
+		return NULL;
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	struct nlmsghdr *nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type	= (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+
+	struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_UNSPEC;
+	nfg->version = NFNETLINK_V0;
+	nfg->res_id = htons(queue_num);
+
+	struct nfqnl_msg_config_params params = {
+		.copy_range = htonl(range),
+		.copy_mode = mode,
+	};
+	mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), &params);
+
+	hdr->nm_len    = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	return nlh;
+}
+
+static struct nlmsghdr *
+nfq_build_verdict(struct mnl_ring_socket *nlm, int id, int queue_num, int verd)
+{
+	struct nl_mmap_hdr *hdr;
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
+		return NULL;
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	struct nlmsghdr *nlh;
+	struct nfgenmsg *nfg;
+
+	nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT;
+	nlh->nlmsg_flags = NLM_F_REQUEST;
+	nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
+	nfg->nfgen_family = AF_UNSPEC;
+	nfg->version = NFNETLINK_V0;
+	nfg->res_id = htons(queue_num);
+
+	struct nfqnl_msg_verdict_hdr vh = {
+		.verdict = htonl(verd),
+		.id = htonl(id),
+	};
+	mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
+
+	hdr->nm_len    = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	return nlh;
+}
+
+static int mnl_socket_poll(struct mnl_socket *nl)
+{
+	struct pollfd pfds[1];
+
+	while (1) {
+		pfds[0].fd	= mnl_socket_get_fd(nl);
+		pfds[0].events	= POLLIN | POLLERR;
+		pfds[0].revents = 0;
+
+		if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
+			return -1;
+
+		if (pfds[0].revents & POLLIN)
+			return 0;
+		if (pfds[0].revents & POLLERR)
+			return -1;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	char buf[16384];
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct nl_mmap_req req = MNL_MMAP_DEFAULT_REQ;
+	ssize_t len;
+	void *ptr;
+	int ret;
+	unsigned int portid, queue_num;
+
+	if (argc != 2) {
+		printf("Usage: %s [queue_num]\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	queue_num = atoi(argv[1]);
+
+	nl = mnl_socket_open(NETLINK_NETFILTER);
+	if (nl == NULL) {
+		perror("mnl_socket_open");
+		exit(EXIT_FAILURE);
+	}
+
+	nlm = mnl_ring_map(nl, &req, &req);
+	if (nlm == NULL) {
+		perror("mnl_ring_mmap");
+		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);
+
+	nlh = nfq_build_cfg_pf_request(nlm, NFQNL_CFG_CMD_PF_UNBIND);
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+
+	nlh = nfq_build_cfg_pf_request(nlm, NFQNL_CFG_CMD_PF_BIND);
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+
+	nlh = nfq_build_cfg_request(nlm, NFQNL_CFG_CMD_BIND, queue_num);
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+
+	nlh = nfq_build_cfg_params(nlm, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+
+	while (1) {
+		uint32_t id;
+
+		ret = mnl_socket_poll(nl);
+		if (ret < 0) {
+			perror("mnl_socket_poll");
+			exit(EXIT_FAILURE);
+		}
+
+		while (1) {
+			hdr = mnl_ring_get_frame(nlm, MNL_RING_RX);
+
+			if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
+				ptr = MNL_MMAP_MSGHDR(hdr);
+				len = hdr->nm_len;
+				if (len == 0)
+					goto release;
+			} else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+				len = recv(mnl_socket_get_fd(nl),
+					   buf, sizeof(buf), MSG_DONTWAIT);
+				if (len <= 0)
+					break;
+				ptr = buf;
+			} else
+				break;
+
+			ret = mnl_cb_run(ptr, len, 0, portid, queue_cb, NULL);
+			if (ret < 0){
+				perror("mnl_cb_run");
+				exit(EXIT_FAILURE);
+			}
+
+			id = ret - MNL_CB_OK;
+			nlh = nfq_build_verdict(nlm, id, queue_num, NF_ACCEPT);
+			if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+				perror("mnl_socket_sendto");
+				exit(EXIT_FAILURE);
+			}
+release:
+			hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+			mnl_ring_advance(nlm, MNL_RING_RX);
+		}
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/min/nfct-dump.c b/examples/mmap/min/nfct-dump.c
new file mode 100644
index 0000000..2b87caa
--- /dev/null
+++ b/examples/mmap/min/nfct-dump.c
@@ -0,0 +1,378 @@ 
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+static int parse_counters_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, CTA_COUNTERS_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case CTA_COUNTERS_PACKETS:
+	case CTA_COUNTERS_BYTES:
+		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static void print_counters(const struct nlattr *nest)
+{
+	struct nlattr *tb[CTA_COUNTERS_MAX+1] = {};
+
+	mnl_attr_parse_nested(nest, parse_counters_cb, tb);
+	if (tb[CTA_COUNTERS_PACKETS]) {
+		printf("packets=%"PRIu64" ",
+		       be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])));
+	}
+	if (tb[CTA_COUNTERS_BYTES]) {
+		printf("bytes=%"PRIu64" ",
+		       be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES])));
+	}
+}
+
+static int parse_ip_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, CTA_IP_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case CTA_IP_V4_SRC:
+	case CTA_IP_V4_DST:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case CTA_IP_V6_SRC:
+	case CTA_IP_V6_DST:
+		if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
+				       sizeof(struct in6_addr)) < 0) {
+			perror("mnl_attr_validate2");
+			return MNL_CB_ERROR;
+		}
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static void print_ip(const struct nlattr *nest)
+{
+	struct nlattr *tb[CTA_IP_MAX+1] = {};
+
+	mnl_attr_parse_nested(nest, parse_ip_cb, tb);
+	if (tb[CTA_IP_V4_SRC]) {
+		struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_SRC]);
+		printf("src=%s ", inet_ntoa(*in));
+	}
+	if (tb[CTA_IP_V4_DST]) {
+		struct in_addr *in = mnl_attr_get_payload(tb[CTA_IP_V4_DST]);
+		printf("dst=%s ", inet_ntoa(*in));
+	}
+	if (tb[CTA_IP_V6_SRC]) {
+		struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]);
+		char out[INET6_ADDRSTRLEN];
+
+		if (!inet_ntop(AF_INET6, in, out, sizeof(out)))
+			printf("src=%s ", out);
+	}
+	if (tb[CTA_IP_V6_DST]) {
+		struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_DST]);
+		char out[INET6_ADDRSTRLEN];
+
+		if (!inet_ntop(AF_INET6, in, out, sizeof(out)))
+			printf("dst=%s ", out);
+	}
+}
+
+static int parse_proto_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, CTA_PROTO_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case CTA_PROTO_NUM:
+	case CTA_PROTO_ICMP_TYPE:
+	case CTA_PROTO_ICMP_CODE:
+		if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case CTA_PROTO_SRC_PORT:
+	case CTA_PROTO_DST_PORT:
+	case CTA_PROTO_ICMP_ID:
+		if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static void print_proto(const struct nlattr *nest)
+{
+	struct nlattr *tb[CTA_PROTO_MAX+1] = {};
+
+	mnl_attr_parse_nested(nest, parse_proto_cb, tb);
+	if (tb[CTA_PROTO_NUM]) {
+		printf("proto=%u ", mnl_attr_get_u8(tb[CTA_PROTO_NUM]));
+	}
+	if (tb[CTA_PROTO_SRC_PORT]) {
+		printf("sport=%u ",
+			ntohs(mnl_attr_get_u16(tb[CTA_PROTO_SRC_PORT])));
+	}
+	if (tb[CTA_PROTO_DST_PORT]) {
+		printf("dport=%u ",
+			ntohs(mnl_attr_get_u16(tb[CTA_PROTO_DST_PORT])));
+	}
+	if (tb[CTA_PROTO_ICMP_ID]) {
+		printf("id=%u ",
+			ntohs(mnl_attr_get_u16(tb[CTA_PROTO_ICMP_ID])));
+	}
+	if (tb[CTA_PROTO_ICMP_TYPE]) {
+		printf("type=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_TYPE]));
+	}
+	if (tb[CTA_PROTO_ICMP_CODE]) {
+		printf("code=%u ", mnl_attr_get_u8(tb[CTA_PROTO_ICMP_CODE]));
+	}
+}
+
+static int parse_tuple_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, CTA_TUPLE_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case CTA_TUPLE_IP:
+		if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case CTA_TUPLE_PROTO:
+		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 void print_tuple(const struct nlattr *nest)
+{
+	struct nlattr *tb[CTA_TUPLE_MAX+1] = {};
+
+	mnl_attr_parse_nested(nest, parse_tuple_cb, tb);
+	if (tb[CTA_TUPLE_IP]) {
+		print_ip(tb[CTA_TUPLE_IP]);
+	}
+	if (tb[CTA_TUPLE_PROTO]) {
+		print_proto(tb[CTA_TUPLE_PROTO]);
+	}
+}
+
+static int data_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, CTA_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case CTA_TUPLE_ORIG:
+	case CTA_COUNTERS_ORIG:
+	case CTA_COUNTERS_REPLY:
+		if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case CTA_TIMEOUT:
+	case CTA_MARK:
+	case CTA_SECMARK:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[CTA_MAX+1] = {};
+	struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb);
+	if (tb[CTA_TUPLE_ORIG])
+		print_tuple(tb[CTA_TUPLE_ORIG]);
+
+	if (tb[CTA_MARK])
+		printf("mark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_MARK])));
+
+	if (tb[CTA_SECMARK])
+		printf("secmark=%u ", ntohl(mnl_attr_get_u32(tb[CTA_SECMARK])));
+
+	if (tb[CTA_COUNTERS_ORIG]) {
+		printf("original ");
+		print_counters(tb[CTA_COUNTERS_ORIG]);
+	}
+
+	if (tb[CTA_COUNTERS_REPLY]) {
+		printf("reply ");
+		print_counters(tb[CTA_COUNTERS_REPLY]);
+	}
+
+	printf("\n");
+	return MNL_CB_OK;
+}
+
+static int mnl_socket_poll(struct mnl_socket *nl)
+{
+	struct pollfd pfds[1];
+
+	while (1) {
+		pfds[0].fd	= mnl_socket_get_fd(nl);
+		pfds[0].events	= POLLIN | POLLERR;
+		pfds[0].revents = 0;
+
+		if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
+			return -1;
+
+		if (pfds[0].revents & POLLIN)
+			return 0;
+		if (pfds[0].revents & POLLERR)
+			return -1;
+	}
+}
+
+int main(void)
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct nfgenmsg *nfh;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	ssize_t len;
+	void *ptr;
+	int ret;
+	uint32_t seq, portid;
+
+	nl = mnl_socket_open(NETLINK_NETFILTER);
+	if (nl == NULL) {
+		perror("mnl_socket_open");
+		exit(EXIT_FAILURE);
+	}
+
+	nlm = mnl_ring_map(nl, &nlmr, &nlmr);
+	if (nlm == NULL) {
+		perror("mnl_ring_map");
+		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);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET;
+	nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+	nlh->nlmsg_seq = seq = time(NULL);
+
+	nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+	nfh->nfgen_family = AF_INET;
+	nfh->version = NFNETLINK_V0;
+	nfh->res_id = 0;
+
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	hdr->nm_len = nlh->nlmsg_len;
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		ret = mnl_socket_poll(nl);
+		if (ret < 0) {
+			perror("mnl_socket_poll");
+			exit(EXIT_FAILURE);
+		}
+
+		while (1) {
+			hdr = mnl_ring_get_frame(nlm, MNL_RING_RX);
+
+			if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
+				ptr = MNL_MMAP_MSGHDR(hdr);
+				len = hdr->nm_len;
+				if (len == 0)
+					goto next;
+			} else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+				len = recv(mnl_socket_get_fd(nl),
+					   buf, sizeof(buf), MSG_DONTWAIT);
+				if (len <= 0)
+					break;
+				ptr = buf;
+			} else
+				break;
+
+			ret = mnl_cb_run(ptr, len, 0, portid, data_cb, NULL);
+			if (ret <= 0)
+				goto end;
+next:
+			hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+			mnl_ring_advance(nlm, MNL_RING_RX);
+		}
+	}
+end:
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/min/rtnl-addr-dump.c b/examples/mmap/min/rtnl-addr-dump.c
new file mode 100644
index 0000000..9679608
--- /dev/null
+++ b/examples/mmap/min/rtnl-addr-dump.c
@@ -0,0 +1,204 @@ 
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* skip unsupported attribute in user-space */
+	if (mnl_attr_type_valid(attr, IFA_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case IFA_ADDRESS:
+		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[IFLA_MAX+1] = {};
+	struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh);
+
+	printf("index=%d family=%d ", ifa->ifa_index, ifa->ifa_family);
+
+	mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb);
+	printf("addr=");
+	if (tb[IFA_ADDRESS]) {
+		void *addr = mnl_attr_get_payload(tb[IFA_ADDRESS]);
+		char out[INET6_ADDRSTRLEN];
+
+		if (inet_ntop(ifa->ifa_family, addr, out, sizeof(out)))
+			printf("%s ", out);
+	}
+	printf("scope=");
+	switch(ifa->ifa_scope) {
+	case 0:
+		printf("global ");
+		break;
+	case 200:
+		printf("site ");
+		break;
+	case 253:
+		printf("link ");
+		break;
+	case 254:
+		printf("host ");
+		break;
+	case 255:
+		printf("nowhere ");
+		break;
+	default:
+		printf("%d ", ifa->ifa_scope);
+		break;
+	}
+
+	printf("\n");
+	return MNL_CB_OK;
+}
+
+static int mnl_socket_poll(struct mnl_socket *nl)
+{
+	struct pollfd pfds[1];
+
+	while (1) {
+		pfds[0].fd	= mnl_socket_get_fd(nl);
+		pfds[0].events	= POLLIN | POLLERR;
+		pfds[0].revents = 0;
+
+		if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
+			return -1;
+
+		if (pfds[0].revents & POLLIN)
+			return 0;
+		if (pfds[0].revents & POLLERR)
+			return -1;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct rtgenmsg *rt;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	ssize_t len;
+	void *ptr;
+	int ret;
+	unsigned int seq, portid;
+	unsigned char family = AF_INET;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	if (strcmp(argv[1], "inet") == 0)
+		family = AF_INET;
+	else if (strcmp(argv[1], "inet6") == 0)
+		family = AF_INET6;
+
+	nl = mnl_socket_open(NETLINK_ROUTE);
+	if (nl == NULL) {
+		perror("mnl_socket_open");
+		exit(EXIT_FAILURE);
+	}
+
+	nlm = mnl_ring_map(nl, &nlmr, &nlmr);
+	if (nlm == NULL) {
+		perror("mnl_ring_map");
+		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);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	if (hdr == NULL) {
+		perror("mnl_ring_get_frame - MNL_RING_TX");
+		exit(EXIT_FAILURE);
+	}
+
+	nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type	= RTM_GETADDR;
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	nlh->nlmsg_seq = seq = time(NULL);
+	rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+	rt->rtgen_family = family;
+
+	hdr->nm_len = nlh->nlmsg_len;
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		ret = mnl_socket_poll(nl);
+		if (ret < 0) {
+			perror("mnl_socket_poll");
+			exit(EXIT_FAILURE);
+		}
+
+		while (1) {
+			hdr = mnl_ring_get_frame(nlm, MNL_RING_RX);
+
+			if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
+				ptr = MNL_MMAP_MSGHDR(hdr);
+				len = hdr->nm_len;
+				if (len == 0)
+					goto next;
+			} else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+				len = recv(mnl_socket_get_fd(nl),
+					   buf, sizeof(buf), MSG_DONTWAIT);
+				if (len <= 0)
+					break;
+				ptr = buf;
+			} else
+				break;
+
+			ret = mnl_cb_run(ptr, len, seq, portid, data_cb, NULL);
+			if (ret <= MNL_CB_STOP)
+				goto end;
+next:
+			hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+			mnl_ring_advance(nlm, MNL_RING_RX);
+		}
+	}
+end:
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/min/rtnl-link-dump.c b/examples/mmap/min/rtnl-link-dump.c
new file mode 100644
index 0000000..8eea80d
--- /dev/null
+++ b/examples/mmap/min/rtnl-link-dump.c
@@ -0,0 +1,199 @@ 
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* skip unsupported attribute in user-space */
+	if (mnl_attr_type_valid(attr, IFLA_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case IFLA_ADDRESS:
+		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case IFLA_MTU:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case IFLA_IFNAME:
+		if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	}
+	tb[type] = attr;
+	return MNL_CB_OK;
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[IFLA_MAX+1] = {};
+	struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh);
+
+	printf("index=%d type=%d flags=%d family=%d ",
+		ifm->ifi_index, ifm->ifi_type,
+		ifm->ifi_flags, ifm->ifi_family);
+
+	if (ifm->ifi_flags & IFF_RUNNING)
+		printf("[RUNNING] ");
+	else
+		printf("[NOT RUNNING] ");
+
+	mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb);
+	if (tb[IFLA_MTU]) {
+		printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
+	}
+	if (tb[IFLA_IFNAME]) {
+		printf("name=%s ", mnl_attr_get_str(tb[IFLA_IFNAME]));
+	}
+	if (tb[IFLA_ADDRESS]) {
+		uint8_t *hwaddr = mnl_attr_get_payload(tb[IFLA_ADDRESS]);
+		int i;
+
+		printf("hwaddr=");
+		for (i=0; i<mnl_attr_get_payload_len(tb[IFLA_ADDRESS]); i++) {
+			printf("%.2x", hwaddr[i] & 0xff);
+			if (i+1 != mnl_attr_get_payload_len(tb[IFLA_ADDRESS]))
+				printf(":");
+		}
+	}
+	printf("\n");
+	return MNL_CB_OK;
+}
+
+static int mnl_socket_poll(struct mnl_socket *nl)
+{
+	struct pollfd pfds[1];
+
+	while (1) {
+		pfds[0].fd	= mnl_socket_get_fd(nl);
+		pfds[0].events	= POLLIN | POLLERR;
+		pfds[0].revents = 0;
+
+		if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
+			return -1;
+
+		if (pfds[0].revents & POLLIN)
+			return 0;
+		if (pfds[0].revents & POLLERR)
+			return -1;
+	}
+}
+
+int main(void)
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct rtgenmsg *rt;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	ssize_t len;
+	void *ptr;
+	int ret;
+	unsigned int seq, portid;
+
+	nl = mnl_socket_open(NETLINK_ROUTE);
+	if (nl == NULL) {
+		perror("mnl_socket_open");
+		exit(EXIT_FAILURE);
+	}
+
+	nlm = mnl_ring_map(nl, &nlmr, &nlmr);
+	if (nlm == NULL) {
+		perror("mnl_ring_map");
+		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);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	if (hdr == NULL) {
+		perror("mnl_ring_get_frame - MNL_RING_TX");
+		exit(EXIT_FAILURE);
+	}
+
+	nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type	= RTM_GETLINK;
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	nlh->nlmsg_seq = seq = time(NULL);
+	rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+	rt->rtgen_family = AF_PACKET;
+
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	hdr->nm_len = nlh->nlmsg_len;
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		ret = mnl_socket_poll(nl);
+		if (ret < 0) {
+			perror("mnl_socket_poll");
+			exit(EXIT_FAILURE);
+		}
+
+		while (1) {
+			hdr = mnl_ring_get_frame(nlm, MNL_RING_RX);
+
+			if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
+				ptr = MNL_MMAP_MSGHDR(hdr);
+				len = hdr->nm_len;
+				if (len == 0)
+					goto next;
+			} else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+				len = recv(mnl_socket_get_fd(nl),
+					   buf, sizeof(buf), MSG_DONTWAIT);
+				if (len <= 0)
+					break;
+				ptr = buf;
+			} else
+				break;
+
+			ret = mnl_cb_run(ptr, len, seq, portid, data_cb, NULL);
+			if (ret <= MNL_CB_STOP)
+				goto end;
+next:
+			hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+			mnl_ring_advance(nlm, MNL_RING_RX);
+		}
+	}
+end:
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/min/rtnl-route-dump.c b/examples/mmap/min/rtnl-route-dump.c
new file mode 100644
index 0000000..aecb6e8
--- /dev/null
+++ b/examples/mmap/min/rtnl-route-dump.c
@@ -0,0 +1,427 @@ 
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_attr_cb2(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+
+	/* skip unsupported attribute in user-space */
+	if (mnl_attr_type_valid(attr, RTAX_MAX) < 0)
+		return MNL_CB_OK;
+
+	if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+		perror("mnl_attr_validate");
+		return MNL_CB_ERROR;
+	}
+
+	tb[mnl_attr_get_type(attr)] = attr;
+	return MNL_CB_OK;
+}
+
+static void attributes_show_ipv4(struct nlattr *tb[])
+{
+	if (tb[RTA_TABLE]) {
+		printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE]));
+	}
+	if (tb[RTA_DST]) {
+		struct in_addr *addr = mnl_attr_get_payload(tb[RTA_DST]);
+		printf("dst=%s ", inet_ntoa(*addr));
+	}
+	if (tb[RTA_SRC]) {
+		struct in_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]);
+		printf("src=%s ", inet_ntoa(*addr));
+	}
+	if (tb[RTA_OIF]) {
+		printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF]));
+	}
+	if (tb[RTA_FLOW]) {
+		printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW]));
+	}
+	if (tb[RTA_PREFSRC]) {
+		struct in_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]);
+		printf("prefsrc=%s ", inet_ntoa(*addr));
+	}
+	if (tb[RTA_GATEWAY]) {
+		struct in_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]);
+		printf("gw=%s ", inet_ntoa(*addr));
+	}
+	if (tb[RTA_PRIORITY]) {
+		printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY]));
+	}
+	if (tb[RTA_METRICS]) {
+		int i;
+		struct nlattr *tbx[RTAX_MAX+1] = {};
+
+		mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx);
+
+		for (i=0; i<RTAX_MAX; i++) {
+			if (tbx[i]) {
+				printf("metrics[%d]=%u ",
+					i, mnl_attr_get_u32(tbx[i]));
+			}
+		}
+	}
+}
+
+/* like inet_ntoa(), not reentrant */
+static const char *inet6_ntoa(struct in6_addr in6)
+{
+	static char buf[INET6_ADDRSTRLEN];
+
+	return inet_ntop(AF_INET6, &in6.s6_addr, buf, sizeof(buf));
+}
+
+static void attributes_show_ipv6(struct nlattr *tb[])
+{
+	if (tb[RTA_TABLE]) {
+		printf("table=%u ", mnl_attr_get_u32(tb[RTA_TABLE]));
+	}
+	if (tb[RTA_DST]) {
+		struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_DST]);
+		printf("dst=%s ", inet6_ntoa(*addr));
+	}
+	if (tb[RTA_SRC]) {
+		struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_SRC]);
+		printf("src=%s ", inet6_ntoa(*addr));
+	}
+	if (tb[RTA_OIF]) {
+		printf("oif=%u ", mnl_attr_get_u32(tb[RTA_OIF]));
+	}
+	if (tb[RTA_FLOW]) {
+		printf("flow=%u ", mnl_attr_get_u32(tb[RTA_FLOW]));
+	}
+	if (tb[RTA_PREFSRC]) {
+		struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_PREFSRC]);
+		printf("prefsrc=%s ", inet6_ntoa(*addr));
+	}
+	if (tb[RTA_GATEWAY]) {
+		struct in6_addr *addr = mnl_attr_get_payload(tb[RTA_GATEWAY]);
+		printf("gw=%s ", inet6_ntoa(*addr));
+	}
+	if (tb[RTA_PRIORITY]) {
+		printf("prio=%u ", mnl_attr_get_u32(tb[RTA_PRIORITY]));
+	}
+	if (tb[RTA_METRICS]) {
+		int i;
+		struct nlattr *tbx[RTAX_MAX+1] = {};
+
+		mnl_attr_parse_nested(tb[RTA_METRICS], data_attr_cb2, tbx);
+
+		for (i=0; i<RTAX_MAX; i++) {
+			if (tbx[i]) {
+				printf("metrics[%d]=%u ",
+					i, mnl_attr_get_u32(tbx[i]));
+			}
+		}
+	}
+}
+
+static int data_ipv4_attr_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* skip unsupported attribute in user-space */
+	if (mnl_attr_type_valid(attr, RTA_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case RTA_TABLE:
+	case RTA_DST:
+	case RTA_SRC:
+	case RTA_OIF:
+	case RTA_FLOW:
+	case RTA_PREFSRC:
+	case RTA_GATEWAY:
+	case RTA_PRIORITY:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case RTA_METRICS:
+		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 data_ipv6_attr_cb(const struct nlattr *attr, void *data)
+{
+	const struct nlattr **tb = data;
+	int type = mnl_attr_get_type(attr);
+
+	/* skip unsupported attribute in user-space */
+	if (mnl_attr_type_valid(attr, RTA_MAX) < 0)
+		return MNL_CB_OK;
+
+	switch(type) {
+	case RTA_TABLE:
+	case RTA_OIF:
+	case RTA_FLOW:
+	case RTA_PRIORITY:
+		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+			perror("mnl_attr_validate");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case RTA_DST:
+	case RTA_SRC:
+	case RTA_PREFSRC:
+	case RTA_GATEWAY:
+		if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
+					sizeof(struct in6_addr)) < 0) {
+			perror("mnl_attr_validate2");
+			return MNL_CB_ERROR;
+		}
+		break;
+	case RTA_METRICS:
+		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 data_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[RTA_MAX+1] = {};
+	struct rtmsg *rm = mnl_nlmsg_get_payload(nlh);
+
+	/* protocol family = AF_INET | AF_INET6 */
+	printf("family=%u ", rm->rtm_family);
+
+	/* destination CIDR, eg. 24 or 32 for IPv4 */
+	printf("dst_len=%u ", rm->rtm_dst_len);
+
+	/* source CIDR */
+	printf("src_len=%u ", rm->rtm_src_len);
+
+	/* type of service (TOS), eg. 0 */
+	printf("tos=%u ", rm->rtm_tos);
+
+	/* table id:
+	 *	RT_TABLE_UNSPEC		= 0
+	 *
+	 *	... user defined values ...
+	 *
+	 *	RT_TABLE_COMPAT		= 252
+	 *	RT_TABLE_DEFAULT	= 253
+	 *	RT_TABLE_MAIN		= 254
+	 *	RT_TABLE_LOCAL		= 255
+	 *	RT_TABLE_MAX		= 0xFFFFFFFF
+	 *
+	 * Synonimous attribute: RTA_TABLE.
+	 */
+	printf("table=%u ", rm->rtm_table);
+
+	/* type:
+	 *	RTN_UNSPEC	= 0
+	 *	RTN_UNICAST	= 1
+	 *	RTN_LOCAL	= 2
+	 *	RTN_BROADCAST	= 3
+	 *	RTN_ANYCAST	= 4
+	 *	RTN_MULTICAST	= 5
+	 *	RTN_BLACKHOLE	= 6
+	 *	RTN_UNREACHABLE	= 7
+	 *	RTN_PROHIBIT	= 8
+	 *	RTN_THROW	= 9
+	 *	RTN_NAT		= 10
+	 *	RTN_XRESOLVE	= 11
+	 *	__RTN_MAX	= 12
+	 */
+	printf("type=%u ", rm->rtm_type);
+
+	/* scope:
+	 *	RT_SCOPE_UNIVERSE	= 0   : everywhere in the universe
+	 *
+	 *	... user defined values ...
+	 *
+	 *	RT_SCOPE_SITE		= 200
+	 *	RT_SCOPE_LINK		= 253 : destination attached to link
+	 *	RT_SCOPE_HOST		= 254 : local address
+	 *	RT_SCOPE_NOWHERE	= 255 : not existing destination
+	 */
+	printf("scope=%u ", rm->rtm_scope);
+
+	/* protocol:
+	 *	RTPROT_UNSPEC	= 0
+	 *	RTPROT_REDIRECT = 1
+	 *	RTPROT_KERNEL	= 2 : route installed by kernel
+	 *	RTPROT_BOOT	= 3 : route installed during boot
+	 *	RTPROT_STATIC	= 4 : route installed by administrator
+	 *
+	 * Values >= RTPROT_STATIC are not interpreted by kernel, they are
+	 * just user-defined.
+	 */
+	printf("proto=%u ", rm->rtm_protocol);
+
+	/* flags:
+	 *	RTM_F_NOTIFY	= 0x100: notify user of route change
+	 *	RTM_F_CLONED	= 0x200: this route is cloned
+	 *	RTM_F_EQUALIZE	= 0x400: Multipath equalizer: NI
+	 *	RTM_F_PREFIX	= 0x800: Prefix addresses
+	 */
+	printf("flags=%x ", rm->rtm_flags);
+
+	switch(rm->rtm_family) {
+	case AF_INET:
+		mnl_attr_parse(nlh, sizeof(*rm), data_ipv4_attr_cb, tb);
+		attributes_show_ipv4(tb);
+		break;
+	case AF_INET6:
+		mnl_attr_parse(nlh, sizeof(*rm), data_ipv6_attr_cb, tb);
+		attributes_show_ipv6(tb);
+		break;
+	}
+
+	printf("\n");
+	return MNL_CB_OK;
+}
+
+static int mnl_socket_poll(struct mnl_socket *nl)
+{
+	struct pollfd pfds[1];
+
+	while (1) {
+		pfds[0].fd	= mnl_socket_get_fd(nl);
+		pfds[0].events	= POLLIN | POLLERR;
+		pfds[0].revents = 0;
+
+		if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
+			return -1;
+
+		if (pfds[0].revents & POLLIN)
+			return 0;
+		if (pfds[0].revents & POLLERR)
+			return -1;
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct rtmsg *rtm;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	ssize_t len;
+	void *ptr;
+	int ret;
+	unsigned int seq, portid;
+	unsigned char family = AF_INET;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (strcmp(argv[1], "inet") == 0)
+		family = AF_INET;
+	else if (strcmp(argv[1], "inet6") == 0)
+		family = AF_INET6;
+
+	nl = mnl_socket_open(NETLINK_ROUTE);
+	if (nl == NULL) {
+		perror("mnl_socket_open");
+		exit(EXIT_FAILURE);
+	}
+
+	nlm = mnl_ring_map(nl, &nlmr, &nlmr);
+	if (nlm == NULL) {
+		perror("mnl_ring_map");
+		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);
+
+	hdr = mnl_ring_get_frame(nlm, MNL_RING_TX);
+	if (hdr == NULL) {
+		perror("mnl_ring_get_frame - MNL_RING_TX");
+		exit(EXIT_FAILURE);
+	}
+
+	nlh = mnl_nlmsg_put_header(MNL_MMAP_MSGHDR(hdr));
+	nlh->nlmsg_type = RTM_GETROUTE;
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	nlh->nlmsg_seq = seq = time(NULL);
+	rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
+	rtm->rtm_family = family;
+
+	hdr->nm_status = NL_MMAP_STATUS_VALID;
+	hdr->nm_len = nlh->nlmsg_len;
+
+	if (mnl_socket_sendto(nl, NULL, 0) < 0) {
+		perror("mnl_socket_sendto");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		ret = mnl_socket_poll(nl);
+		if (ret < 0) {
+			perror("mnl_socket_poll");
+			exit(EXIT_FAILURE);
+		}
+
+		while (1) {
+			hdr = mnl_ring_get_frame(nlm, MNL_RING_RX);
+
+			if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
+				ptr = MNL_MMAP_MSGHDR(hdr);
+				len = hdr->nm_len;
+				if (len == 0)
+					goto next;
+			} else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
+				len = recv(mnl_socket_get_fd(nl),
+					   buf, sizeof(buf), MSG_DONTWAIT);
+				if (len <= 0)
+					break;
+				ptr = buf;
+			} else
+				break;
+
+			ret = mnl_cb_run(ptr, len, seq, portid, data_cb, NULL);
+			if (ret <= MNL_CB_STOP)
+				goto end;
+next:
+			hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+			mnl_ring_advance(nlm, MNL_RING_RX);
+		}
+	}
+end:
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}