summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Harpin <development@landsofshadow.co.uk>2015-10-18 11:48:55 +0100
committerAlex Harpin <development@landsofshadow.co.uk>2015-10-18 11:48:55 +0100
commitcebada14c32e6603380a913fb852854c46605d1d (patch)
tree88396b80b0a558866002809642876792f3c60de9
parent0bb90c44862ce3f9018656beadfbc2aac4a0d517 (diff)
parent1891e0e2cefced50e7bfdacd50942cefe5bf73ba (diff)
downloadlibmnl-cebada14c32e6603380a913fb852854c46605d1d.tar.gz
libmnl-cebada14c32e6603380a913fb852854c46605d1d.zip
Merge remote-tracking branch 'source/master' into upstreamupstream
-rw-r--r--configure.ac4
-rw-r--r--examples/Makefile.am2
-rw-r--r--examples/genl/Makefile.am6
-rw-r--r--examples/genl/genl-family-get.c2
-rw-r--r--examples/genl/genl-group-events.c63
-rw-r--r--examples/kobject/Makefile.am6
-rw-r--r--examples/kobject/kobject-event.c49
-rw-r--r--examples/netfilter/Makefile.am10
-rw-r--r--examples/netfilter/nf-log.c17
-rw-r--r--examples/netfilter/nf-queue.c24
-rw-r--r--examples/netfilter/nfct-create-batch.c6
-rw-r--r--examples/netfilter/nfct-daemon.c364
-rw-r--r--examples/netfilter/nfct-dump.c319
-rw-r--r--examples/rtnl/Makefile.am12
-rw-r--r--examples/rtnl/rtnl-addr-dump.c133
-rw-r--r--examples/rtnl/rtnl-link-dump.c24
-rw-r--r--examples/rtnl/rtnl-link-dump2.c4
-rw-r--r--examples/rtnl/rtnl-link-dump3.c2
-rw-r--r--examples/rtnl/rtnl-link-event.c2
-rw-r--r--examples/rtnl/rtnl-link-set.c6
-rw-r--r--examples/rtnl/rtnl-route-add.c83
-rw-r--r--examples/rtnl/rtnl-route-dump.c21
-rw-r--r--examples/rtnl/rtnl-route-event.c341
-rw-r--r--include/libmnl/libmnl.h17
-rw-r--r--include/linux/Makefile.am3
-rw-r--r--include/linux/netfilter/Makefile.am1
-rw-r--r--include/linux/netfilter/nfnetlink_conntrack.h252
-rw-r--r--include/linux/netlink.h15
-rw-r--r--include/linux/socket.h21
-rw-r--r--src/attr.c6
-rw-r--r--src/callback.c9
-rw-r--r--src/libmnl.map5
-rw-r--r--src/nlmsg.c6
-rw-r--r--src/socket.c73
34 files changed, 1800 insertions, 108 deletions
diff --git a/configure.ac b/configure.ac
index 718ab1c..313a015 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,7 @@ AC_DISABLE_STATIC
LT_INIT
CHECK_GCC_FVISIBILITY
case "$host" in
-*-*-linux*) ;;
+*-*-linux* | *-*-uclinux*) ;;
*) AC_MSG_ERROR([Linux only, dude!]);;
esac
@@ -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 examples/Makefile examples/genl/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 libmnl.pc doxygen.cfg])
AC_OUTPUT
diff --git a/examples/Makefile.am b/examples/Makefile.am
index f6c2f69..e5cb052 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1 +1 @@
-SUBDIRS = genl netfilter rtnl
+SUBDIRS = genl kobject netfilter rtnl
diff --git a/examples/genl/Makefile.am b/examples/genl/Makefile.am
index deaf18d..b4b7954 100644
--- a/examples/genl/Makefile.am
+++ b/examples/genl/Makefile.am
@@ -1,6 +1,10 @@
include $(top_srcdir)/Make_global.am
-check_PROGRAMS = genl-family-get
+check_PROGRAMS = genl-family-get \
+ genl-group-events
genl_family_get_SOURCES = genl-family-get.c
genl_family_get_LDADD = ../../src/libmnl.la
+
+genl_group_events_SOURCES = genl-group-events.c
+genl_group_events_LDADD = ../../src/libmnl.la
diff --git a/examples/genl/genl-family-get.c b/examples/genl/genl-family-get.c
index 50f7ea3..ba8de12 100644
--- a/examples/genl/genl-family-get.c
+++ b/examples/genl/genl-family-get.c
@@ -219,7 +219,7 @@ int main(int argc, char *argv[])
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
diff --git a/examples/genl/genl-group-events.c b/examples/genl/genl-group-events.c
new file mode 100644
index 0000000..d5f0a18
--- /dev/null
+++ b/examples/genl/genl-group-events.c
@@ -0,0 +1,63 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+
+static int group;
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ printf("received event type=%d from genetlink group %d\n",
+ nlh->nlmsg_type, group);
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ if (argc != 2) {
+ printf("%s [group]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ group = atoi(argv[1]);
+
+ nl = mnl_socket_open(NETLINK_GENERIC);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_setsockopt(nl, NETLINK_ADD_MEMBERSHIP, &group,
+ sizeof(int)) < 0) {
+ perror("mnl_socket_setsockopt");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret <= 0)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/kobject/Makefile.am b/examples/kobject/Makefile.am
new file mode 100644
index 0000000..9197f7a
--- /dev/null
+++ b/examples/kobject/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = kobject-event
+
+kobject_event_SOURCES = kobject-event.c
+kobject_event_LDADD = ../../src/libmnl.la
diff --git a/examples/kobject/kobject-event.c b/examples/kobject/kobject-event.c
new file mode 100644
index 0000000..97debdf
--- /dev/null
+++ b/examples/kobject/kobject-event.c
@@ -0,0 +1,49 @@
+/* This example is placed in the public domain. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_KOBJECT_UEVENT);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ /* There is one single group in kobject over netlink */
+ if (mnl_socket_bind(nl, (1<<0), MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ int i;
+
+ /* kobject uses a string based protocol, with no initial
+ * netlink header.
+ */
+ for (i=0; i<ret; i++)
+ printf("%c", buf[i]);
+
+ printf("\n");
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/netfilter/Makefile.am b/examples/netfilter/Makefile.am
index 98458fd..4bae05f 100644
--- a/examples/netfilter/Makefile.am
+++ b/examples/netfilter/Makefile.am
@@ -2,8 +2,10 @@ include $(top_srcdir)/Make_global.am
check_PROGRAMS = nf-queue \
nf-log \
+ nfct-dump \
nfct-event \
- nfct-create-batch
+ nfct-create-batch \
+ nfct-daemon
nf_queue_SOURCES = nf-queue.c
nf_queue_LDADD = ../../src/libmnl.la
@@ -11,6 +13,12 @@ nf_queue_LDADD = ../../src/libmnl.la
nf_log_SOURCES = nf-log.c
nf_log_LDADD = ../../src/libmnl.la
+nfct_dump_SOURCES = nfct-dump.c
+nfct_dump_LDADD = ../../src/libmnl.la
+
+nfct_daemon_SOURCES = nfct-daemon.c
+nfct_daemon_LDADD = ../../src/libmnl.la
+
nfct_event_SOURCES = nfct-event.c
nfct_event_LDADD = ../../src/libmnl.la
diff --git a/examples/netfilter/nf-log.c b/examples/netfilter/nf-log.c
index a862912..4383b66 100644
--- a/examples/netfilter/nf-log.c
+++ b/examples/netfilter/nf-log.c
@@ -9,11 +9,6 @@
#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)
@@ -39,14 +34,14 @@ static int parse_attr_cb(const struct nlattr *attr, void *data)
case NFULA_TIMESTAMP:
if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
sizeof(struct nfulnl_msg_packet_timestamp)) < 0) {
- perror("mnl_attr_validate");
+ 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_validate");
+ perror("mnl_attr_validate2");
return MNL_CB_ERROR;
}
break;
@@ -174,28 +169,28 @@ int main(int argc, char *argv[])
nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_UNBIND);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
nlh = nflog_build_cfg_pf_request(buf, NFULNL_CFG_CMD_PF_BIND);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
nlh = nflog_build_cfg_request(buf, NFULNL_CFG_CMD_BIND, qnum);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
nlh = nflog_build_cfg_params(buf, NFULNL_COPY_PACKET, 0xFFFF, qnum);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
diff --git a/examples/netfilter/nf-queue.c b/examples/netfilter/nf-queue.c
index c66611f..957e365 100644
--- a/examples/netfilter/nf-queue.c
+++ b/examples/netfilter/nf-queue.c
@@ -9,11 +9,6 @@
#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)
@@ -39,14 +34,14 @@ static int parse_attr_cb(const struct nlattr *attr, void *data)
case NFQA_TIMESTAMP:
if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
- perror("mnl_attr_validate");
+ 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_validate");
+ perror("mnl_attr_validate2");
return MNL_CB_ERROR;
}
break;
@@ -67,9 +62,10 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data)
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);
}
- printf("packet received (id=%u hw=0x%04x hook=%u)\n",
- id, ntohs(ph->hw_protocol), ph->hook);
return MNL_CB_OK + id;
}
@@ -188,28 +184,28 @@ int main(int argc, char *argv[])
nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_UNBIND);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
nlh = nfq_build_cfg_pf_request(buf, NFQNL_CFG_CMD_PF_BIND);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
@@ -230,7 +226,7 @@ int main(int argc, char *argv[])
id = ret - MNL_CB_OK;
nlh = nfq_build_verdict(buf, id, queue_num, NF_ACCEPT);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
diff --git a/examples/netfilter/nfct-create-batch.c b/examples/netfilter/nfct-create-batch.c
index dd6623f..4675789 100644
--- a/examples/netfilter/nfct-create-batch.c
+++ b/examples/netfilter/nfct-create-batch.c
@@ -67,7 +67,7 @@ static void put_msg(char *buf, uint16_t i, int seq)
static int cb_err(const struct nlmsghdr *nlh, void *data)
{
- struct nlmsgerr *err = (void *)(nlh + 1);
+ struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
if (err->error != 0)
printf("message with seq %u has failed: %s\n",
nlh->nlmsg_seq, strerror(-err->error));
@@ -87,7 +87,7 @@ send_batch(struct mnl_socket *nl, struct mnl_nlmsg_batch *b, int portid)
ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b), len);
if (ret == -1) {
- perror("mnl_socket_recvfrom");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
@@ -116,7 +116,7 @@ send_batch(struct mnl_socket *nl, struct mnl_nlmsg_batch *b, int portid)
NULL, NULL, cb_ctl_array,
MNL_ARRAY_SIZE(cb_ctl_array));
if (ret == -1) {
- perror("mnl_cb_run");
+ perror("mnl_cb_run2");
exit(EXIT_FAILURE);
}
diff --git a/examples/netfilter/nfct-daemon.c b/examples/netfilter/nfct-daemon.c
new file mode 100644
index 0000000..a97c2ec
--- /dev/null
+++ b/examples/netfilter/nfct-daemon.c
@@ -0,0 +1,364 @@
+/* A very simple skeleton code that implements a daemon that collects
+ * conntrack statistics from ctnetlink.
+ *
+ * This example is placed in the public domain.
+ */
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+#include <sys/queue.h>
+
+struct nstats {
+ LIST_ENTRY(nstats) list;
+
+ uint8_t family;
+
+ union {
+ struct in_addr ip;
+ struct in6_addr ip6;
+ };
+ uint64_t pkts, bytes;
+};
+
+static LIST_HEAD(nstats_head, nstats) nstats_head;
+
+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 parse_counters(const struct nlattr *nest, struct nstats *ns)
+{
+ struct nlattr *tb[CTA_COUNTERS_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_counters_cb, tb);
+ if (tb[CTA_COUNTERS_PACKETS])
+ ns->pkts += be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS]));
+
+ if (tb[CTA_COUNTERS_BYTES])
+ ns->bytes += 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 parse_ip(const struct nlattr *nest, struct nstats *ns)
+{
+ 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]);
+ ns->ip = *in;
+ ns->family = AF_INET;
+ }
+ if (tb[CTA_IP_V6_SRC]) {
+ struct in6_addr *in = mnl_attr_get_payload(tb[CTA_IP_V6_SRC]);
+ ns->ip6 = *in;
+ ns->family = AF_INET6;
+ }
+}
+
+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;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void parse_tuple(const struct nlattr *nest, struct nstats *ns)
+{
+ struct nlattr *tb[CTA_TUPLE_MAX+1] = {};
+
+ mnl_attr_parse_nested(nest, parse_tuple_cb, tb);
+ if (tb[CTA_TUPLE_IP])
+ parse_ip(tb[CTA_TUPLE_IP], ns);
+}
+
+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;
+ }
+ 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);
+ struct nstats ns = {}, *cur, *new;
+
+ mnl_attr_parse(nlh, sizeof(*nfg), data_attr_cb, tb);
+ if (tb[CTA_TUPLE_ORIG])
+ parse_tuple(tb[CTA_TUPLE_ORIG], &ns);
+
+ if (tb[CTA_COUNTERS_ORIG])
+ parse_counters(tb[CTA_COUNTERS_ORIG], &ns);
+
+ if (tb[CTA_COUNTERS_REPLY])
+ parse_counters(tb[CTA_COUNTERS_REPLY], &ns);
+
+ /* Look up for existing statistics object ... */
+ LIST_FOREACH(cur, &nstats_head, list) {
+ if (memcmp(&ns.ip6, &cur->ip6, sizeof(struct in6_addr)) == 0) {
+ /* ... and sum counters */
+ cur->pkts += ns.pkts;
+ cur->bytes += ns.bytes;
+ return MNL_CB_OK;
+ }
+ }
+
+ /* ... if it does not exist, add new stats object */
+ new = calloc(1, sizeof(struct nstats));
+ if (!new)
+ return MNL_CB_OK;
+
+ new->family = ns.family;
+ new->ip6 = ns.ip6;
+ new->pkts = ns.pkts;
+ new->bytes = ns.bytes;
+
+ LIST_INSERT_HEAD(&nstats_head, new, list);
+
+ return MNL_CB_OK;
+}
+
+static int handle(struct mnl_socket *nl)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ /* It only happens if NETLINK_NO_ENOBUFS is not set, it means
+ * we are leaking statistics.
+ */
+ if (errno == ENOBUFS) {
+ fprintf(stderr, "The daemon has hit ENOBUFS, you can "
+ "increase the size of your receiver "
+ "buffer to mitigate this or enable "
+ "reliable delivery.\n");
+ } else {
+ perror("mnl_socket_recvfrom");
+ }
+ return -1;
+ }
+
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret == -1) {
+ perror("mnl_cb_run");
+ return -1;
+ } else if (ret <= MNL_CB_STOP)
+ return 0;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+ struct nstats *cur;
+ struct timeval tv = {};
+ int ret, secs, on = 1, buffersize = (1 << 22);
+
+ if (argc != 2) {
+ printf("Usage: %s <poll-secs>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ secs = atoi(argv[1]);
+
+ LIST_INIT(&nstats_head);
+
+ printf("Polling every %d seconds from kernel...\n", secs);
+
+ /* Set high priority for this process, less chances to overrun
+ * the netlink receiver buffer since the scheduler gives this process
+ * more chances to run.
+ */
+ nice(-20);
+
+ /* Open netlink socket to operate with netfilter */
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Subscribe to destroy events to avoid leaking counters. The same
+ * socket is used to periodically atomically dump and reset counters.
+ */
+ if (mnl_socket_bind(nl, NF_NETLINK_CONNTRACK_DESTROY,
+ MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Set netlink receiver buffer to 16 MBytes, to avoid packet drops */
+ setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE,
+ &buffersize, sizeof(socklen_t));
+
+ /* The two tweaks below enable reliable event delivery, packets may
+ * be dropped if the netlink receiver buffer overruns. This happens ...
+ *
+ * a) if the kernel spams this user-space process until the receiver
+ * is filled.
+ *
+ * or:
+ *
+ * b) if the user-space process does not pull messages from the
+ * receiver buffer so often.
+ */
+ mnl_socket_setsockopt(nl, NETLINK_BROADCAST_ERROR, &on, sizeof(int));
+ mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &on, sizeof(int));
+
+ nlh = mnl_nlmsg_put_header(buf);
+ /* Counters are atomically zeroed in each dump */
+ nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) |
+ IPCTNL_MSG_CT_GET_CTRZERO;
+ nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = AF_INET;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = 0;
+
+ /* Filter by mark: We only want to dump entries whose mark is zero */
+ mnl_attr_put_u32(nlh, CTA_MARK, htonl(0));
+ mnl_attr_put_u32(nlh, CTA_MARK_MASK, htonl(0xffffffff));
+
+ while (1) {
+ int fd_max = mnl_socket_get_fd(nl);
+ fd_set readfds;
+
+ /* Every N seconds ... */
+ if (tv.tv_sec == 0 && tv.tv_usec == 0) {
+ /* ... request a fresh dump of the table from kernel */
+ ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
+ if (ret == -1) {
+ perror("mnl_socket_sendto");
+ return -1;
+ }
+ tv.tv_sec = secs;
+ tv.tv_usec = 0;
+
+ /* print the content of the list */
+ LIST_FOREACH(cur, &nstats_head, list) {
+ char out[INET6_ADDRSTRLEN];
+
+ if (inet_ntop(cur->family, &cur->ip, out, sizeof(out)))
+ printf("src=%s ", out);
+
+ printf("counters %"PRIu64" %"PRIu64"\n",
+ cur->pkts, cur->bytes);
+ }
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(mnl_socket_get_fd(nl), &readfds);
+
+ ret = select(fd_max+1, &readfds, NULL, NULL, &tv);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+
+ perror("select");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Handled event and periodic atomic-dump-and-reset messages */
+ if (FD_ISSET(mnl_socket_get_fd(nl), &readfds)) {
+ if (handle(nl) < 0)
+ return EXIT_FAILURE;
+ }
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/netfilter/nfct-dump.c b/examples/netfilter/nfct-dump.c
new file mode 100644
index 0000000..114af61
--- /dev/null
+++ b/examples/netfilter/nfct-dump.c
@@ -0,0 +1,319 @@
+/* This example is placed in the public domain. */
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <inttypes.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;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+ uint32_t seq, portid;
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ 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;
+
+ ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
+ if (ret == -1) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+ portid = mnl_socket_get_portid(nl);
+
+ while (1) {
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret == -1) {
+ perror("mnl_cb_run");
+ exit(EXIT_FAILURE);
+ } else if (ret <= MNL_CB_STOP)
+ break;
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/rtnl/Makefile.am b/examples/rtnl/Makefile.am
index 3cc8995..24769b6 100644
--- a/examples/rtnl/Makefile.am
+++ b/examples/rtnl/Makefile.am
@@ -1,10 +1,15 @@
include $(top_srcdir)/Make_global.am
-check_PROGRAMS = rtnl-link-dump rtnl-link-dump2 rtnl-link-dump3 \
+check_PROGRAMS = rtnl-addr-dump \
+ rtnl-link-dump rtnl-link-dump2 rtnl-link-dump3 \
rtnl-link-event \
rtnl-link-set \
rtnl-route-add \
- rtnl-route-dump
+ rtnl-route-dump \
+ rtnl-route-event
+
+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
@@ -26,3 +31,6 @@ rtnl_link_set_LDADD = ../../src/libmnl.la
rtnl_route_dump_SOURCES = rtnl-route-dump.c
rtnl_route_dump_LDADD = ../../src/libmnl.la
+
+rtnl_route_event_SOURCES = rtnl-route-event.c
+rtnl_route_event_LDADD = ../../src/libmnl.la
diff --git a/examples/rtnl/rtnl-addr-dump.c b/examples/rtnl/rtnl-addr-dump.c
new file mode 100644
index 0000000..6b4e52e
--- /dev/null
+++ b/examples/rtnl/rtnl-addr-dump.c
@@ -0,0 +1,133 @@
+/* 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;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+ unsigned int seq, portid;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <inet|inet6>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ 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));
+ if (strcmp(argv[1], "inet") == 0)
+ rt->rtgen_family = AF_INET;
+ else if (strcmp(argv[1], "inet6") == 0)
+ rt->rtgen_family = AF_INET6;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ 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);
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/rtnl/rtnl-link-dump.c b/examples/rtnl/rtnl-link-dump.c
index 159f3b6..f5d6312 100644
--- a/examples/rtnl/rtnl-link-dump.c
+++ b/examples/rtnl/rtnl-link-dump.c
@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
+#include <arpa/inet.h>
#include <libmnl/libmnl.h>
#include <linux/if.h>
@@ -19,6 +20,12 @@ static int data_attr_cb(const struct nlattr *attr, void *data)
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");
@@ -27,7 +34,7 @@ static int data_attr_cb(const struct nlattr *attr, void *data)
break;
case IFLA_IFNAME:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
- perror("mnl_attr_validate2");
+ perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
@@ -55,7 +62,18 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
}
if (tb[IFLA_IFNAME]) {
- printf("name=%s", mnl_attr_get_str(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;
@@ -90,7 +108,7 @@ int main(void)
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
diff --git a/examples/rtnl/rtnl-link-dump2.c b/examples/rtnl/rtnl-link-dump2.c
index 78f31a8..b3ca3fa 100644
--- a/examples/rtnl/rtnl-link-dump2.c
+++ b/examples/rtnl/rtnl-link-dump2.c
@@ -25,7 +25,7 @@ static int data_attr_cb(const struct nlattr *attr, void *data)
break;
case IFLA_IFNAME:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
- perror("mnl_attr_validate2");
+ perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
printf("name=%s ", mnl_attr_get_str(attr));
@@ -81,7 +81,7 @@ int main(void)
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
diff --git a/examples/rtnl/rtnl-link-dump3.c b/examples/rtnl/rtnl-link-dump3.c
index a6dce6f..2521214 100644
--- a/examples/rtnl/rtnl-link-dump3.c
+++ b/examples/rtnl/rtnl-link-dump3.c
@@ -81,7 +81,7 @@ int main(void)
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
diff --git a/examples/rtnl/rtnl-link-event.c b/examples/rtnl/rtnl-link-event.c
index 90bb8e5..123fb88 100644
--- a/examples/rtnl/rtnl-link-event.c
+++ b/examples/rtnl/rtnl-link-event.c
@@ -26,7 +26,7 @@ static int data_attr_cb(const struct nlattr *attr, void *data)
break;
case IFLA_IFNAME:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
- perror("mnl_attr_validate2");
+ perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
diff --git a/examples/rtnl/rtnl-link-set.c b/examples/rtnl/rtnl-link-set.c
index dad8856..6086c37 100644
--- a/examples/rtnl/rtnl-link-set.c
+++ b/examples/rtnl/rtnl-link-set.c
@@ -62,19 +62,19 @@ int main(int argc, char *argv[])
sizeof(struct ifinfomsg));
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
- perror("read");
+ perror("mnl_socket_recvfrom");
exit(EXIT_FAILURE);
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret == -1){
- perror("callback");
+ perror("mnl_cb_run");
exit(EXIT_FAILURE);
}
diff --git a/examples/rtnl/rtnl-route-add.c b/examples/rtnl/rtnl-route-add.c
index 6d166da..97578cd 100644
--- a/examples/rtnl/rtnl-route-add.c
+++ b/examples/rtnl/rtnl-route-add.c
@@ -14,63 +14,83 @@
int main(int argc, char *argv[])
{
+ struct mnl_socket *nl;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ struct rtmsg *rtm;
+ uint32_t prefix, seq, portid;
+ union {
+ in_addr_t ip;
+ struct in6_addr ip6;
+ } dst;
+ union {
+ in_addr_t ip;
+ struct in6_addr ip6;
+ } gw;
+ int iface, ret, family = AF_INET;
+
if (argc <= 3) {
printf("Usage: %s iface destination cidr [gateway]\n", argv[0]);
printf("Example: %s eth0 10.0.1.12 32 10.0.1.11\n", argv[0]);
+ printf(" %s eth0 ffff::10.0.1.12 128 fdff::1\n", argv[0]);
exit(EXIT_FAILURE);
}
- int iface;
iface = if_nametoindex(argv[1]);
if (iface == 0) {
- printf("Bad interface name\n");
+ perror("if_nametoindex");
exit(EXIT_FAILURE);
}
- in_addr_t dst;
if (!inet_pton(AF_INET, argv[2], &dst)) {
- printf("Bad destination\n");
- exit(EXIT_FAILURE);
+ if (!inet_pton(AF_INET6, argv[2], &dst)) {
+ perror("inet_pton");
+ exit(EXIT_FAILURE);
+ }
+ family = AF_INET6;
}
- uint32_t mask;
- if (sscanf(argv[3], "%u", &mask) == 0) {
- printf("Bad CIDR\n");
+ if (sscanf(argv[3], "%u", &prefix) == 0) {
+ perror("sscanf");
exit(EXIT_FAILURE);
}
- in_addr_t gw;
- if (argc >= 5 && !inet_pton(AF_INET, argv[4], &gw)) {
- printf("Bad gateway\n");
+ if (argc == 5 && !inet_pton(family, argv[4], &gw)) {
+ perror("inet_pton");
exit(EXIT_FAILURE);
}
- struct mnl_socket *nl;
- char buf[MNL_SOCKET_BUFFER_SIZE];
- struct nlmsghdr *nlh;
- struct rtmsg *rtm;
-
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_NEWROUTE;
- nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
- nlh->nlmsg_seq = time(NULL);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
- rtm->rtm_family = AF_INET;
- rtm->rtm_dst_len = mask;
+ rtm->rtm_family = family;
+ rtm->rtm_dst_len = prefix;
rtm->rtm_src_len = 0;
rtm->rtm_tos = 0;
- rtm->rtm_protocol = RTPROT_BOOT;
+ rtm->rtm_protocol = RTPROT_STATIC;
rtm->rtm_table = RT_TABLE_MAIN;
rtm->rtm_type = RTN_UNICAST;
/* is there any gateway? */
rtm->rtm_scope = (argc == 4) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
rtm->rtm_flags = 0;
- mnl_attr_put_u32(nlh, RTA_DST, dst);
+ if (family == AF_INET)
+ mnl_attr_put_u32(nlh, RTA_DST, dst.ip);
+ else
+ mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), &dst);
+
mnl_attr_put_u32(nlh, RTA_OIF, iface);
- if (argc >= 5)
- mnl_attr_put_u32(nlh, RTA_GATEWAY, gw);
+ if (argc == 5) {
+ if (family == AF_INET)
+ mnl_attr_put_u32(nlh, RTA_GATEWAY, gw.ip);
+ else {
+ mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr),
+ &gw.ip6);
+ }
+ }
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL) {
@@ -82,9 +102,22 @@ int main(int argc, char *argv[])
perror("mnl_socket_bind");
exit(EXIT_FAILURE);
}
+ portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret < 0) {
+ perror("mnl_socket_recvfrom");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
+ if (ret < 0) {
+ perror("mnl_cb_run");
exit(EXIT_FAILURE);
}
diff --git a/examples/rtnl/rtnl-route-dump.c b/examples/rtnl/rtnl-route-dump.c
index a798515..17da80b 100644
--- a/examples/rtnl/rtnl-route-dump.c
+++ b/examples/rtnl/rtnl-route-dump.c
@@ -13,6 +13,8 @@
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;
@@ -21,6 +23,8 @@ static int data_attr_cb2(const struct nlattr *attr, void *data)
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
+
+ tb[mnl_attr_get_type(attr)] = attr;
return MNL_CB_OK;
}
@@ -51,6 +55,9 @@ static void attributes_show_ipv4(struct nlattr *tb[])
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] = {};
@@ -64,7 +71,6 @@ static void attributes_show_ipv4(struct nlattr *tb[])
}
}
}
- printf("\n");
}
/* like inet_ntoa(), not reentrant */
@@ -102,6 +108,9 @@ static void attributes_show_ipv6(struct nlattr *tb[])
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] = {};
@@ -115,7 +124,6 @@ static void attributes_show_ipv6(struct nlattr *tb[])
}
}
}
- printf("\n");
}
static int data_ipv4_attr_cb(const struct nlattr *attr, void *data)
@@ -135,6 +143,7 @@ static int data_ipv4_attr_cb(const struct nlattr *attr, void *data)
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;
@@ -164,6 +173,7 @@ static int data_ipv6_attr_cb(const struct nlattr *attr, void *data)
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;
@@ -175,7 +185,7 @@ static int data_ipv6_attr_cb(const struct nlattr *attr, void *data)
case RTA_GATEWAY:
if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
sizeof(struct in6_addr)) < 0) {
- perror("mnl_attr_validate");
+ perror("mnl_attr_validate2");
return MNL_CB_ERROR;
}
break;
@@ -269,7 +279,7 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
* RTM_F_EQUALIZE = 0x400: Multipath equalizer: NI
* RTM_F_PREFIX = 0x800: Prefix addresses
*/
- printf("flags=%x\n", rm->rtm_flags);
+ printf("flags=%x ", rm->rtm_flags);
switch(rm->rtm_family) {
case AF_INET:
@@ -282,6 +292,7 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
break;
}
+ printf("\n");
return MNL_CB_OK;
}
@@ -323,7 +334,7 @@ int main(int argc, char *argv[])
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- perror("mnl_socket_send");
+ perror("mnl_socket_sendto");
exit(EXIT_FAILURE);
}
diff --git a/examples/rtnl/rtnl-route-event.c b/examples/rtnl/rtnl-route-event.c
new file mode 100644
index 0000000..6cad9f0
--- /dev/null
+++ b/examples/rtnl/rtnl-route-event.c
@@ -0,0 +1,341 @@
+/* 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_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);
+
+ switch(nlh->nlmsg_type) {
+ case RTM_NEWROUTE:
+ printf("[NEW] ");
+ break;
+ case RTM_DELROUTE:
+ printf("[DEL] ");
+ break;
+ }
+
+ /* 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;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE,
+ MNL_SOCKET_AUTOPID) < 0) {
+ perror("mnl_socket_bind");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 0, 0, data_cb, NULL);
+ if (ret <= MNL_CB_STOP)
+ break;
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1) {
+ perror("error");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/include/libmnl/libmnl.h b/include/libmnl/libmnl.h
index a647fd9..5adb13c 100644
--- a/include/libmnl/libmnl.h
+++ b/include/libmnl/libmnl.h
@@ -1,14 +1,9 @@
#ifndef _LIBMNL_H_
#define _LIBMNL_H_
-#ifdef __cplusplus
-# include <cstdio>
-# include <cstdint>
-#else
-# include <stdbool.h> /* not in C++ */
-# include <stdio.h>
-# include <stdint.h>
-#endif
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h> /* for sa_family_t */
#include <linux/netlink.h>
@@ -22,11 +17,13 @@ extern "C" {
*/
#define MNL_SOCKET_AUTOPID 0
-#define MNL_SOCKET_BUFFER_SIZE (getpagesize() < 8192L ? getpagesize() : 8192L)
+#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L)
struct mnl_socket;
-extern struct mnl_socket *mnl_socket_open(int type);
+extern struct mnl_socket *mnl_socket_open(int bus);
+extern struct mnl_socket *mnl_socket_open2(int bus, int flags);
+extern struct mnl_socket *mnl_socket_fdopen(int fd);
extern int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid);
extern int mnl_socket_close(struct mnl_socket *nl);
extern int mnl_socket_get_fd(const struct mnl_socket *nl);
diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am
index 2d02887..08c600b 100644
--- a/include/linux/Makefile.am
+++ b/include/linux/Makefile.am
@@ -1 +1,2 @@
-noinst_HEADERS = netlink.h
+SUBDIRS = netfilter
+noinst_HEADERS = netlink.h socket.h
diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am
new file mode 100644
index 0000000..64a975e
--- /dev/null
+++ b/include/linux/netfilter/Makefile.am
@@ -0,0 +1 @@
+noinst_HEADERS = nfnetlink_conntrack.h
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
new file mode 100644
index 0000000..08fabc6
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -0,0 +1,252 @@
+#ifndef _IPCONNTRACK_NETLINK_H
+#define _IPCONNTRACK_NETLINK_H
+#include <linux/netfilter/nfnetlink.h>
+
+enum cntl_msg_types {
+ IPCTNL_MSG_CT_NEW,
+ IPCTNL_MSG_CT_GET,
+ IPCTNL_MSG_CT_DELETE,
+ IPCTNL_MSG_CT_GET_CTRZERO,
+ IPCTNL_MSG_CT_GET_STATS_CPU,
+ IPCTNL_MSG_CT_GET_STATS,
+ IPCTNL_MSG_CT_GET_DYING,
+ IPCTNL_MSG_CT_GET_UNCONFIRMED,
+
+ IPCTNL_MSG_MAX
+};
+
+enum ctnl_exp_msg_types {
+ IPCTNL_MSG_EXP_NEW,
+ IPCTNL_MSG_EXP_GET,
+ IPCTNL_MSG_EXP_DELETE,
+ IPCTNL_MSG_EXP_GET_STATS_CPU,
+
+ IPCTNL_MSG_EXP_MAX
+};
+
+
+enum ctattr_type {
+ CTA_UNSPEC,
+ CTA_TUPLE_ORIG,
+ CTA_TUPLE_REPLY,
+ CTA_STATUS,
+ CTA_PROTOINFO,
+ CTA_HELP,
+ CTA_NAT_SRC,
+#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */
+ CTA_TIMEOUT,
+ CTA_MARK,
+ CTA_COUNTERS_ORIG,
+ CTA_COUNTERS_REPLY,
+ CTA_USE,
+ CTA_ID,
+ CTA_NAT_DST,
+ CTA_TUPLE_MASTER,
+ CTA_NAT_SEQ_ADJ_ORIG,
+ CTA_NAT_SEQ_ADJ_REPLY,
+ CTA_SECMARK, /* obsolete */
+ CTA_ZONE,
+ CTA_SECCTX,
+ CTA_TIMESTAMP,
+ CTA_MARK_MASK,
+ CTA_LABELS,
+ CTA_LABELS_MASK,
+ __CTA_MAX
+};
+#define CTA_MAX (__CTA_MAX - 1)
+
+enum ctattr_tuple {
+ CTA_TUPLE_UNSPEC,
+ CTA_TUPLE_IP,
+ CTA_TUPLE_PROTO,
+ __CTA_TUPLE_MAX
+};
+#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
+
+enum ctattr_ip {
+ CTA_IP_UNSPEC,
+ CTA_IP_V4_SRC,
+ CTA_IP_V4_DST,
+ CTA_IP_V6_SRC,
+ CTA_IP_V6_DST,
+ __CTA_IP_MAX
+};
+#define CTA_IP_MAX (__CTA_IP_MAX - 1)
+
+enum ctattr_l4proto {
+ CTA_PROTO_UNSPEC,
+ CTA_PROTO_NUM,
+ CTA_PROTO_SRC_PORT,
+ CTA_PROTO_DST_PORT,
+ CTA_PROTO_ICMP_ID,
+ CTA_PROTO_ICMP_TYPE,
+ CTA_PROTO_ICMP_CODE,
+ CTA_PROTO_ICMPV6_ID,
+ CTA_PROTO_ICMPV6_TYPE,
+ CTA_PROTO_ICMPV6_CODE,
+ __CTA_PROTO_MAX
+};
+#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
+
+enum ctattr_protoinfo {
+ CTA_PROTOINFO_UNSPEC,
+ CTA_PROTOINFO_TCP,
+ CTA_PROTOINFO_DCCP,
+ CTA_PROTOINFO_SCTP,
+ __CTA_PROTOINFO_MAX
+};
+#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
+
+enum ctattr_protoinfo_tcp {
+ CTA_PROTOINFO_TCP_UNSPEC,
+ CTA_PROTOINFO_TCP_STATE,
+ CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
+ CTA_PROTOINFO_TCP_WSCALE_REPLY,
+ CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+ CTA_PROTOINFO_TCP_FLAGS_REPLY,
+ __CTA_PROTOINFO_TCP_MAX
+};
+#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
+
+enum ctattr_protoinfo_dccp {
+ CTA_PROTOINFO_DCCP_UNSPEC,
+ CTA_PROTOINFO_DCCP_STATE,
+ CTA_PROTOINFO_DCCP_ROLE,
+ CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
+ __CTA_PROTOINFO_DCCP_MAX,
+};
+#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1)
+
+enum ctattr_protoinfo_sctp {
+ CTA_PROTOINFO_SCTP_UNSPEC,
+ CTA_PROTOINFO_SCTP_STATE,
+ CTA_PROTOINFO_SCTP_VTAG_ORIGINAL,
+ CTA_PROTOINFO_SCTP_VTAG_REPLY,
+ __CTA_PROTOINFO_SCTP_MAX
+};
+#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_MAX - 1)
+
+enum ctattr_counters {
+ CTA_COUNTERS_UNSPEC,
+ CTA_COUNTERS_PACKETS, /* 64bit counters */
+ CTA_COUNTERS_BYTES, /* 64bit counters */
+ CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */
+ CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */
+ __CTA_COUNTERS_MAX
+};
+#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
+
+enum ctattr_tstamp {
+ CTA_TIMESTAMP_UNSPEC,
+ CTA_TIMESTAMP_START,
+ CTA_TIMESTAMP_STOP,
+ __CTA_TIMESTAMP_MAX
+};
+#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1)
+
+enum ctattr_nat {
+ CTA_NAT_UNSPEC,
+ CTA_NAT_V4_MINIP,
+#define CTA_NAT_MINIP CTA_NAT_V4_MINIP
+ CTA_NAT_V4_MAXIP,
+#define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP
+ CTA_NAT_PROTO,
+ CTA_NAT_V6_MINIP,
+ CTA_NAT_V6_MAXIP,
+ __CTA_NAT_MAX
+};
+#define CTA_NAT_MAX (__CTA_NAT_MAX - 1)
+
+enum ctattr_protonat {
+ CTA_PROTONAT_UNSPEC,
+ CTA_PROTONAT_PORT_MIN,
+ CTA_PROTONAT_PORT_MAX,
+ __CTA_PROTONAT_MAX
+};
+#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
+
+enum ctattr_natseq {
+ CTA_NAT_SEQ_UNSPEC,
+ CTA_NAT_SEQ_CORRECTION_POS,
+ CTA_NAT_SEQ_OFFSET_BEFORE,
+ CTA_NAT_SEQ_OFFSET_AFTER,
+ __CTA_NAT_SEQ_MAX
+};
+#define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1)
+
+enum ctattr_expect {
+ CTA_EXPECT_UNSPEC,
+ CTA_EXPECT_MASTER,
+ CTA_EXPECT_TUPLE,
+ CTA_EXPECT_MASK,
+ CTA_EXPECT_TIMEOUT,
+ CTA_EXPECT_ID,
+ CTA_EXPECT_HELP_NAME,
+ CTA_EXPECT_ZONE,
+ CTA_EXPECT_FLAGS,
+ CTA_EXPECT_CLASS,
+ CTA_EXPECT_NAT,
+ CTA_EXPECT_FN,
+ __CTA_EXPECT_MAX
+};
+#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1)
+
+enum ctattr_expect_nat {
+ CTA_EXPECT_NAT_UNSPEC,
+ CTA_EXPECT_NAT_DIR,
+ CTA_EXPECT_NAT_TUPLE,
+ __CTA_EXPECT_NAT_MAX
+};
+#define CTA_EXPECT_NAT_MAX (__CTA_EXPECT_NAT_MAX - 1)
+
+enum ctattr_help {
+ CTA_HELP_UNSPEC,
+ CTA_HELP_NAME,
+ CTA_HELP_INFO,
+ __CTA_HELP_MAX
+};
+#define CTA_HELP_MAX (__CTA_HELP_MAX - 1)
+
+enum ctattr_secctx {
+ CTA_SECCTX_UNSPEC,
+ CTA_SECCTX_NAME,
+ __CTA_SECCTX_MAX
+};
+#define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1)
+
+enum ctattr_stats_cpu {
+ CTA_STATS_UNSPEC,
+ CTA_STATS_SEARCHED,
+ CTA_STATS_FOUND,
+ CTA_STATS_NEW,
+ CTA_STATS_INVALID,
+ CTA_STATS_IGNORE,
+ CTA_STATS_DELETE,
+ CTA_STATS_DELETE_LIST,
+ CTA_STATS_INSERT,
+ CTA_STATS_INSERT_FAILED,
+ CTA_STATS_DROP,
+ CTA_STATS_EARLY_DROP,
+ CTA_STATS_ERROR,
+ CTA_STATS_SEARCH_RESTART,
+ __CTA_STATS_MAX,
+};
+#define CTA_STATS_MAX (__CTA_STATS_MAX - 1)
+
+enum ctattr_stats_global {
+ CTA_STATS_GLOBAL_UNSPEC,
+ CTA_STATS_GLOBAL_ENTRIES,
+ __CTA_STATS_GLOBAL_MAX,
+};
+#define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1)
+
+enum ctattr_expect_stats {
+ CTA_STATS_EXP_UNSPEC,
+ CTA_STATS_EXP_NEW,
+ CTA_STATS_EXP_CREATE,
+ CTA_STATS_EXP_DELETE,
+ __CTA_STATS_EXP_MAX,
+};
+#define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1)
+
+#endif /* _IPCONNTRACK_NETLINK_H */
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index f55ed8c..ced0e1a 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -1,14 +1,14 @@
#ifndef __LINUX_NETLINK_H
#define __LINUX_NETLINK_H
-#include <linux/socket.h> /* for sa_family_t */
+#include <linux/socket.h> /* for __kernel_sa_family_t */
#include <linux/types.h>
#define NETLINK_ROUTE 0 /* Routing/device hook */
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
-#define NETLINK_FIREWALL 3 /* Firewalling hook */
-#define NETLINK_INET_DIAG 4 /* INET socket monitoring */
+#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
+#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
@@ -24,11 +24,15 @@
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
+#define NETLINK_RDMA 20
+#define NETLINK_CRYPTO 21 /* Crypto layer */
+
+#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
#define MAX_LINKS 32
struct sockaddr_nl {
- sa_family_t nl_family; /* AF_NETLINK */
+ __kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
@@ -48,6 +52,7 @@ struct nlmsghdr {
#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */
#define NLM_F_ECHO 8 /* Echo this request */
+#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */
/* Modifiers to GET request */
#define NLM_F_ROOT 0x100 /* specify tree root */
@@ -145,4 +150,4 @@ struct nlattr {
#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
-#endif /* __LINUX_NETLINK_H */
+#endif /* __LINUX_NETLINK_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
new file mode 100644
index 0000000..8c1e501
--- /dev/null
+++ b/include/linux/socket.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_SOCKET_H
+#define _LINUX_SOCKET_H
+
+/*
+ * Desired design of maximum size and alignment (see RFC2553)
+ */
+#define _K_SS_MAXSIZE 128 /* Implementation specific max size */
+#define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *))
+ /* Implementation specific desired alignment */
+
+typedef unsigned short __kernel_sa_family_t;
+
+struct __kernel_sockaddr_storage {
+ __kernel_sa_family_t ss_family; /* address family */
+ /* Following field(s) are implementation specific */
+ char __data[_K_SS_MAXSIZE - sizeof(unsigned short)];
+ /* space to achieve desired size, */
+ /* _SS_MAXSIZE value minus size of ss_family */
+} __attribute__ ((aligned(_K_SS_ALIGNSIZE))); /* force desired alignment */
+
+#endif /* _LINUX_SOCKET_H */
diff --git a/src/attr.c b/src/attr.c
index 1136c50..c551d0b 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -89,7 +89,7 @@ EXPORT_SYMBOL(mnl_attr_get_payload);
* truncated.
*
* This function does not set errno in case of error since it is intended
- * for iterations. Thus, it returns 1 on success and 0 on error.
+ * for iterations. Thus, it returns true on success and false on error.
*
* The len parameter may be negative in the case of malformed messages during
* attribute iteration, that is why we use a signed integer.
@@ -105,7 +105,6 @@ EXPORT_SYMBOL(mnl_attr_ok);
/**
* mnl_attr_next - get the next attribute in the payload of a netlink message
* \param attr pointer to the current attribute
- * \param len length of the remaining bytes in the buffer (passed by reference).
*
* This function returns a pointer to the next attribute after the one passed
* as parameter. You have to use mnl_attr_ok() to ensure that the next
@@ -200,6 +199,7 @@ static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
[MNL_TYPE_U16] = sizeof(uint16_t),
[MNL_TYPE_U32] = sizeof(uint32_t),
[MNL_TYPE_U64] = sizeof(uint64_t),
+ [MNL_TYPE_MSECS] = sizeof(uint64_t),
};
/**
@@ -427,7 +427,6 @@ EXPORT_SYMBOL(mnl_attr_put);
* mnl_attr_put_u8 - add 8-bit unsigned integer attribute to netlink message
* \param nlh pointer to the netlink message
* \param type netlink attribute type
- * \param len netlink attribute payload size
* \param data 8-bit unsigned integer data that is stored by the new attribute
*
* This function updates the length field of the Netlink message (nlmsg_len)
@@ -568,7 +567,6 @@ EXPORT_SYMBOL(mnl_attr_put_check);
* \param nlh pointer to the netlink message
* \param buflen size of buffer which stores the message
* \param type netlink attribute type
- * \param len netlink attribute payload size
* \param data 8-bit unsigned integer data that is stored by the new attribute
*
* This function first checks that the data can be added to the message
diff --git a/src/callback.c b/src/callback.c
index 6337acc..f023401 100644
--- a/src/callback.c
+++ b/src/callback.c
@@ -65,6 +65,12 @@ __mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
return -1;
}
+ /* dump was interrupted */
+ if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
/* netlink data message handling */
if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
if (cb_data){
@@ -117,7 +123,8 @@ out:
* This function propagates the callback return value. On error, it returns
* -1 and errno is explicitly set. If the portID is not the expected, errno
* is set to ESRCH. If the sequence number is not the expected, errno is set
- * to EPROTO.
+ * to EPROTO. If the dump was interrupted, errno is set to EINTR and you should
+ * request a new fresh dump again.
*/
int
mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
diff --git a/src/libmnl.map b/src/libmnl.map
index dbc332e..e5920e5 100644
--- a/src/libmnl.map
+++ b/src/libmnl.map
@@ -72,3 +72,8 @@ local: *;
LIBMNL_1.1 {
mnl_attr_parse_payload;
} LIBMNL_1.0;
+
+LIBMNL_1.2 {
+ mnl_socket_open2;
+ mnl_socket_fdopen;
+} LIBMNL_1.1;
diff --git a/src/nlmsg.c b/src/nlmsg.c
index 000829e..fd2f698 100644
--- a/src/nlmsg.c
+++ b/src/nlmsg.c
@@ -150,7 +150,7 @@ EXPORT_SYMBOL(mnl_nlmsg_get_payload_offset);
* truncated.
*
* This function does not set errno in case of error since it is intended
- * for iterations. Thus, it returns 1 on success and 0 on error.
+ * for iterations. Thus, it returns true on success and false on error.
*
* The len parameter may become negative in malformed messages during message
* iteration, that is why we use a signed integer.
@@ -222,7 +222,7 @@ EXPORT_SYMBOL(mnl_nlmsg_seq_ok);
/**
* mnl_nlmsg_portid_ok - perform portID origin check
* \param nlh current netlink message that we are handling
- * \param seq netlink portid that we want to check
+ * \param portid netlink portid that we want to check
*
* This functions returns true if the origin is fulfilled, otherwise
* false is returned. We skip the tracking for netlink message whose portID
@@ -462,7 +462,7 @@ EXPORT_SYMBOL(mnl_nlmsg_batch_start);
* mnl_nlmsg_batch_stop - release a batch
* \param b pointer to batch
*
- * This function returns the amount of data that is part of this batch.
+ * This function releases the batch allocated by mnl_nlmsg_batch_start().
*/
void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b)
{
diff --git a/src/socket.c b/src/socket.c
index 6d54563..d63ab87 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -103,30 +103,87 @@ unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
}
EXPORT_SYMBOL(mnl_socket_get_portid);
+static struct mnl_socket *__mnl_socket_open(int bus, int flags)
+{
+ struct mnl_socket *nl;
+
+ nl = calloc(1, sizeof(struct mnl_socket));
+ if (nl == NULL)
+ return NULL;
+
+ nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
+ if (nl->fd == -1) {
+ free(nl);
+ return NULL;
+ }
+
+ return nl;
+}
+
/**
* mnl_socket_open - open a netlink socket
* \param bus the netlink socket bus ID (see NETLINK_* constants)
*
- * On error, it returns -1 and errno is appropriately set. Otherwise, it
+ * On error, it returns NULL and errno is appropriately set. Otherwise, it
* returns a valid pointer to the mnl_socket structure.
*/
struct mnl_socket *mnl_socket_open(int bus)
{
+ return __mnl_socket_open(bus, 0);
+}
+EXPORT_SYMBOL(mnl_socket_open);
+
+/**
+ * mnl_socket_open2 - open a netlink socket with appropriate flags
+ * \param bus the netlink socket bus ID (see NETLINK_* constants)
+ * \param flags the netlink socket flags (see SOCK_* constants in socket(2))
+ *
+ * This is similar to mnl_socket_open(), but allows to set flags like
+ * SOCK_CLOEXEC at socket creation time (useful for multi-threaded programs
+ * performing exec calls).
+ *
+ * On error, it returns NULL and errno is appropriately set. Otherwise, it
+ * returns a valid pointer to the mnl_socket structure.
+ */
+struct mnl_socket *mnl_socket_open2(int bus, int flags)
+{
+ return __mnl_socket_open(bus, flags);
+}
+EXPORT_SYMBOL(mnl_socket_open2);
+
+/**
+ * mnl_socket_fdopen - associates a mnl_socket object with pre-existing socket.
+ * \param fd pre-existing socket descriptor.
+ *
+ * On error, it returns NULL and errno is appropriately set. Otherwise, it
+ * returns a valid pointer to the mnl_socket structure. It also sets the portID
+ * if the socket fd is already bound and it is AF_NETLINK.
+ *
+ * Note that mnl_socket_get_portid() returns 0 if this function is used with
+ * non-netlink socket.
+ */
+struct mnl_socket *mnl_socket_fdopen(int fd)
+{
+ int ret;
struct mnl_socket *nl;
+ struct sockaddr_nl addr;
+ socklen_t addr_len = sizeof(struct sockaddr_nl);
- nl = calloc(sizeof(struct mnl_socket), 1);
- if (nl == NULL)
+ ret = getsockname(fd, (struct sockaddr *) &addr, &addr_len);
+ if (ret == -1)
return NULL;
- nl->fd = socket(AF_NETLINK, SOCK_RAW, bus);
- if (nl->fd == -1) {
- free(nl);
+ nl = calloc(1, sizeof(struct mnl_socket));
+ if (nl == NULL)
return NULL;
- }
+
+ nl->fd = fd;
+ if (addr.nl_family == AF_NETLINK)
+ nl->addr = addr;
return nl;
}
-EXPORT_SYMBOL(mnl_socket_open);
+EXPORT_SYMBOL(mnl_socket_fdopen);
/**
* mnl_socket_bind - bind netlink socket