diff mbox

[libmnl,5/9] examples: add for mmap nonmin1

Message ID 87mwkc8zjr.wl%chamaken@gmail.com
State Superseded
Headers show

Commit Message

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

---
 configure.ac                            |   2 +-
 examples/mmap/Makefile.am               |   2 +-
 examples/mmap/nonmin1/Makefile.am       |  18 ++
 examples/mmap/nonmin1/nf-log.c          | 250 +++++++++++++++++++++
 examples/mmap/nonmin1/nfct-dump.c       | 336 ++++++++++++++++++++++++++++
 examples/mmap/nonmin1/rtnl-addr-dump.c  | 159 +++++++++++++
 examples/mmap/nonmin1/rtnl-route-dump.c | 383 ++++++++++++++++++++++++++++++++
 7 files changed, 1148 insertions(+), 2 deletions(-)
 create mode 100644 examples/mmap/nonmin1/Makefile.am
 create mode 100644 examples/mmap/nonmin1/nf-log.c
 create mode 100644 examples/mmap/nonmin1/nfct-dump.c
 create mode 100644 examples/mmap/nonmin1/rtnl-addr-dump.c
 create mode 100644 examples/mmap/nonmin1/rtnl-route-dump.c
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index 1b0b2cd..08350b9 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 examples/mmap/Makefile examples/mmap/min/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 examples/mmap/nonmin1/Makefile libmnl.pc doxygen.cfg])
 AC_OUTPUT
diff --git a/examples/mmap/Makefile.am b/examples/mmap/Makefile.am
index a532ec0..1a7ee6f 100644
--- a/examples/mmap/Makefile.am
+++ b/examples/mmap/Makefile.am
@@ -1 +1 @@ 
-SUBDIRS = min
+SUBDIRS = min nonmin1
diff --git a/examples/mmap/nonmin1/Makefile.am b/examples/mmap/nonmin1/Makefile.am
new file mode 100644
index 0000000..59c7792
--- /dev/null
+++ b/examples/mmap/nonmin1/Makefile.am
@@ -0,0 +1,18 @@ 
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = nf-log \
+		 nfct-dump \
+		 rtnl-addr-dump \
+		 rtnl-route-dump
+
+nf_log_SOURCES = nf-log.c
+nf_log_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_route_dump_SOURCES = rtnl-route-dump.c
+rtnl_route_dump_LDADD = ../../../src/libmnl.la
diff --git a/examples/mmap/nonmin1/nf-log.c b/examples/mmap/nonmin1/nf-log.c
new file mode 100644
index 0000000..e11347d
--- /dev/null
+++ b/examples/mmap/nonmin1/nf-log.c
@@ -0,0 +1,250 @@ 
+/* 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;
+}
+
+int main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	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_ring_send(nlm) < 0) {
+		perror("mnl_ring_send");
+		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_ring_send(nlm) < 0) {
+		perror("mnl_ring_send");
+		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_ring_send(nlm) < 0) {
+		perror("mnl_ring_send");
+		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_ring_send(nlm) < 0) {
+		perror("mnl_ring_send");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		hdr = mnl_ring_poll_rxframe(nlm);
+		if (hdr == NULL) {
+			perror("mnl_ring_poll_rxframe");
+			exit(EXIT_FAILURE);
+		}
+		ret = mnl_cb_run(MNL_MMAP_MSGHDR(hdr), hdr->nm_len, 0, portid, log_cb, NULL);
+		if (ret < 0)
+			break;
+		hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+		mnl_ring_advance(nlm, MNL_RING_RX);
+	}
+
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/nonmin1/nfct-dump.c b/examples/mmap/nonmin1/nfct-dump.c
new file mode 100644
index 0000000..c7131e8
--- /dev/null
+++ b/examples/mmap/nonmin1/nfct-dump.c
@@ -0,0 +1,336 @@ 
+/* 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;
+}
+
+int main(void)
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct nfgenmsg *nfh;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	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_ring_send(nlm) < 0) {
+		perror("mnl_socket_send");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		hdr = mnl_ring_poll_rxframe(nlm);
+		if (hdr == NULL) {
+			perror("mnl_ring_poll_rxframe");
+			exit(EXIT_FAILURE);
+		}
+		ret = mnl_cb_run(MNL_MMAP_MSGHDR(hdr), hdr->nm_len, 0, portid, data_cb, NULL);
+		if (ret <= MNL_CB_STOP)
+			break;
+		hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+		mnl_ring_advance(nlm, MNL_RING_RX);
+	}
+
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/nonmin1/rtnl-addr-dump.c b/examples/mmap/nonmin1/rtnl-addr-dump.c
new file mode 100644
index 0000000..4fb4d03
--- /dev/null
+++ b/examples/mmap/nonmin1/rtnl-addr-dump.c
@@ -0,0 +1,159 @@ 
+/* 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 <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;
+}
+
+int main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	struct nlmsghdr *nlh;
+	struct rtgenmsg *rt;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	struct nl_mmap_hdr *hdr;
+	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_ring_send(nlm) < 0) {
+		perror("mnl_ring_send");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		hdr = mnl_ring_poll_rxframe(nlm);
+		if (hdr == NULL) {
+			perror("mnl_ring_poll_rxframe");
+			exit(EXIT_FAILURE);
+		}
+		ret = mnl_cb_run(MNL_MMAP_MSGHDR(hdr), hdr->nm_len, seq, portid, data_cb, NULL);
+		if (ret <= MNL_CB_STOP)
+			break;
+		hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+		mnl_ring_advance(nlm, MNL_RING_RX);
+	}
+
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}
diff --git a/examples/mmap/nonmin1/rtnl-route-dump.c b/examples/mmap/nonmin1/rtnl-route-dump.c
new file mode 100644
index 0000000..56ec448
--- /dev/null
+++ b/examples/mmap/nonmin1/rtnl-route-dump.c
@@ -0,0 +1,383 @@ 
+/* 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 <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;
+}
+
+int main(int argc, char *argv[])
+{
+	struct mnl_socket *nl;
+	struct mnl_ring_socket *nlm;
+	struct nl_mmap_hdr *hdr;
+	struct nlmsghdr *nlh;
+	struct rtmsg *rtm;
+	struct nl_mmap_req nlmr = MNL_MMAP_DEFAULT_REQ;
+	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((void *)hdr + NL_MMAP_HDRLEN);
+	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_ring_send(nlm) < 0) {
+		perror("mnl_ring_send");
+		exit(EXIT_FAILURE);
+	}
+	mnl_ring_advance(nlm, MNL_RING_TX);
+
+	while (1) {
+		hdr = mnl_ring_poll_rxframe(nlm);
+		if (hdr == NULL) {
+			perror("mnl_ring_poll_rxframe");
+			exit(EXIT_FAILURE);
+		}
+		ret = mnl_cb_run((void *)hdr + NL_MMAP_HDRLEN, hdr->nm_len, seq, portid, data_cb, NULL);
+		if (ret <= MNL_CB_STOP)
+			break;
+		hdr->nm_status = NL_MMAP_STATUS_UNUSED;
+		mnl_ring_advance(nlm, MNL_RING_RX);
+	}
+
+	if (ret == -1) {
+		perror("error");
+		exit(EXIT_FAILURE);
+	}
+
+	mnl_ring_unmap(nlm);
+	mnl_socket_close(nl);
+
+	return 0;
+}