summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2010-03-19 14:55:32 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2010-03-19 14:55:32 +0100
commit74e7bb8b033640f7c4692c4ea0d5c231e3137b7b (patch)
tree4cc2125d00b83b5ac1b076da2c8e3eff7b08ddc3
initial libmnl import
-rw-r--r--Make_global.am7
-rw-r--r--Makefile.am14
-rw-r--r--README9
-rwxr-xr-xautogen.sh53
-rw-r--r--configure.in18
-rw-r--r--examples/Makefile.am24
-rw-r--r--examples/genl-family-get.c143
-rw-r--r--examples/rtnl-link-dump.c84
-rw-r--r--examples/rtnl-link-event.c70
-rw-r--r--examples/rtnl-link-set.c76
-rw-r--r--examples/rtnl-route-dump.c192
-rw-r--r--include/Makefile.am1
-rw-r--r--include/libmnl/Makefile.am1
-rw-r--r--include/libmnl/libmnl.h120
-rw-r--r--libmnl.pc.in15
-rw-r--r--src/Makefile.am9
-rw-r--r--src/attr.c212
-rw-r--r--src/callback.c119
-rw-r--r--src/msg.c213
-rw-r--r--src/socket.c255
20 files changed, 1635 insertions, 0 deletions
diff --git a/Make_global.am b/Make_global.am
new file mode 100644
index 0000000..6f3ac2b
--- /dev/null
+++ b/Make_global.am
@@ -0,0 +1,7 @@
+# This is _NOT_ the library release version, it's an API version.
+# Please read Chapter 6 "Library interface versions" of the libtool
+# documentation before making any modification
+# http://sources.redhat.com/autobook/autobook/autobook_91.html
+LIBVERSION=0:0:0
+
+INCLUDES=$(all_includes) -I$(top_srcdir)/include
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..baeae1a
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,14 @@
+include $(top_srcdir)/Make_global.am
+
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src include examples
+DIST_SUBDIRS = src include examples
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libmnl.pc
+
+$(OBJECTS): libtool
+libtool: $(LIBTOOL_DEPS)
+ $(SHELL) ./config.status --recheck
diff --git a/README b/README
new file mode 100644
index 0000000..9bd9414
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+libmnl
+
+Features:
+- Minimalistic Netlink library
+- Low-level: you have to know how Netlink works.
+- Simplicity: as opposed to complexity.
+- Very low abstraction: try to leak as less Netlink details as possible.
+- Decoupling: the main bricks are decoupled to avoid inter-dependencies
+(eg. socket handling and callbacks are not attached).
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..c167563
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+include ()
+{
+ # If we keep a copy of the kernel header in the SVN tree, we'll have
+ # to worry about synchronization issues forever. Instead, we just copy
+ # the headers that we need from the lastest kernel version at autogen
+ # stage.
+
+ INCLUDEDIR=${KERNEL_DIR:-/lib/modules/`uname -r`/build}/include/linux
+
+ if [ -f $INCLUDEDIR/netfilter/nfnetlink.h ]
+ then
+ TARGET=include/libnfnetlink/linux_nfnetlink.h
+ echo "Copying nfnetlink.h to linux_nfnetlink.h"
+ cp $INCLUDEDIR/netfilter/nfnetlink.h $TARGET
+ TMP=`mktemp`
+ sed 's/__be16/u_int16_t/g' $TARGET > $TMP
+ cp $TMP $TARGET
+ sed 's/#include <linux\/netfilter\/nfnetlink_compat\.h>/#include <libnfnetlink\/linux_nfnetlink_compat\.h>/g' $TARGET > $TMP
+ cp $TMP $TARGET
+ else
+ echo "can't find nfnetlink.h kernel file in $INCLUDEDIR"
+ exit 1
+ fi
+
+ if [ -f $INCLUDEDIR/netfilter/nfnetlink_compat.h ]
+ then
+ TARGET=include/libnfnetlink/linux_nfnetlink_compat.h
+ echo "Copying nfnetlink_compat.h to linux_nfnetlink_compat.h"
+ cp $INCLUDEDIR/netfilter/nfnetlink_compat.h $TARGET
+ else
+ echo "can't find nfnetlink.h kernel file in $INCLUDEDIR, ignoring"
+ fi
+}
+
+run ()
+{
+ echo "running: $*"
+ eval $*
+
+ if test $? != 0 ; then
+ echo "error: while running '$*'"
+ exit 1
+ fi
+}
+
+[ "x$1" = "xdistrib" ] && include
+run aclocal
+#run autoheader
+run libtoolize -f
+run automake -a
+run autoconf
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..4a8f1a2
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,18 @@
+dnl Process this file with autoconf to create configure.
+
+AC_INIT
+AC_CANONICAL_SYSTEM
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE(libmnl, 1.0.0-beta0)
+
+AC_PROG_CC
+AC_EXEEXT
+AM_PROG_LIBTOOL
+AC_SUBST(LIBTOOL_DEPS)
+
+case $target in
+*-*-linux*) ;;
+*) AC_MSG_ERROR([Linux only, dude!]);;
+esac
+
+AC_OUTPUT(Makefile src/Makefile include/Makefile include/libmnl/Makefile examples/Makefile libmnl.pc)
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..92c5342
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,24 @@
+include $(top_srcdir)/Make_global.am
+
+check_PROGRAMS = rtnl-link-dump rtnl-link-event rtnl-link-set \
+ rtnl-route-dump genl-family-get
+
+rtnl_link_dump_SOURCES = rtnl-link-dump.c
+rtnl_link_dump_LDADD = ../src/libmnl.la
+rtnl_link_dump_LDFLAGS = -dynamic -ldl
+
+rtnl_link_event_SOURCES = rtnl-link-event.c
+rtnl_link_event_LDADD = ../src/libmnl.la
+rtnl_link_event_LDFLAGS = -dynamic -ldl
+
+rtnl_link_set_SOURCES = rtnl-link-set.c
+rtnl_link_set_LDADD = ../src/libmnl.la
+rtnl_link_set_LDFLAGS = -dynamic -ldl
+
+rtnl_route_dump_SOURCES = rtnl-route-dump.c
+rtnl_route_dump_LDADD = ../src/libmnl.la
+rtnl_route_dump_LDFLAGS = -dynamic -ldl
+
+genl_family_get_SOURCES = genl-family-get.c
+genl_family_get_LDADD = ../src/libmnl.la
+genl_family_get_LDFLAGS = -dynamic -ldl
diff --git a/examples/genl-family-get.c b/examples/genl-family-get.c
new file mode 100644
index 0000000..3e741cb
--- /dev/null
+++ b/examples/genl-family-get.c
@@ -0,0 +1,143 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/genetlink.h>
+
+static void parse_genl_mc_grps(struct nlattr *nested)
+{
+ struct nlattr *pos;
+ int len;
+
+ mnl_attr_for_each_nested(pos, nested, len) {
+ struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1];
+
+ mnl_attr_parse_nested(pos, tb, CTRL_ATTR_MCAST_GRP_MAX);
+ if (tb[CTRL_ATTR_MCAST_GRP_ID]) {
+ printf("id-0x%x ",
+ mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]));
+ }
+ if (tb[CTRL_ATTR_MCAST_GRP_NAME]) {
+ printf("name: %s ",
+ mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]));
+ }
+ printf("\n");
+ }
+}
+
+static void parse_genl_family_ops(struct nlattr *nested)
+{
+ struct nlattr *pos;
+ int len;
+
+ mnl_attr_for_each_nested(pos, nested, len) {
+ struct nlattr *tb[CTRL_ATTR_OP_MAX+1];
+
+ mnl_attr_parse_nested(pos, tb, CTRL_ATTR_OP_MAX);
+ if (tb[CTRL_ATTR_OP_ID]) {
+ printf("id-0x%x ",
+ mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]));
+ }
+ if (tb[CTRL_ATTR_OP_MAX]) {
+ printf("flags ");
+ }
+ printf("\n");
+ }
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[CTRL_ATTR_MAX+1];
+ struct genlmsghdr *genl = mnl_nlmsg_get_data(nlh);
+
+ mnl_attr_parse_at_offset(nlh, sizeof(*genl), tb, CTRL_ATTR_MAX);
+ if (tb[CTRL_ATTR_FAMILY_NAME]) {
+ printf("name=%s\t",
+ mnl_attr_get_str(tb[CTRL_ATTR_FAMILY_NAME]));
+ }
+ if (tb[CTRL_ATTR_FAMILY_ID]) {
+ printf("id=%u\t",
+ mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]));
+ }
+ if (tb[CTRL_ATTR_VERSION]) {
+ printf("version=%u\t",
+ mnl_attr_get_u32(tb[CTRL_ATTR_VERSION]));
+ }
+ if (tb[CTRL_ATTR_HDRSIZE]) {
+ printf("hdrsize=%u\t",
+ mnl_attr_get_u32(tb[CTRL_ATTR_HDRSIZE]));
+ }
+ if (tb[CTRL_ATTR_MAXATTR]) {
+ printf("maxattr=%u\t",
+ mnl_attr_get_u32(tb[CTRL_ATTR_MAXATTR]));
+ }
+ if (tb[CTRL_ATTR_OPS]) {
+ printf("\nops:\n");
+ parse_genl_family_ops(tb[CTRL_ATTR_OPS]);
+ }
+ if (tb[CTRL_ATTR_MCAST_GROUPS]) {
+ printf("\ngrps:\n");
+ parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS]);
+ }
+ return MNL_CB_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[getpagesize()];
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+ int ret;
+ unsigned int seq;
+
+ if (argc != 2) {
+ printf("%s [family name]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = GENL_ID_CTRL;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+
+ genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genl->cmd = CTRL_CMD_GETFAMILY;
+ genl->version = 1;
+
+ mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
+ mnl_attr_put_str_null(nlh, CTRL_ATTR_FAMILY_NAME, 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_sendto(nl, nlh, mnl_nlmsg_get_len(nlh)) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, 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/rtnl-link-dump.c b/examples/rtnl-link-dump.c
new file mode 100644
index 0000000..7cf061d
--- /dev/null
+++ b/examples/rtnl-link-dump.c
@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[IFLA_MAX+1];
+ struct ifinfomsg *ifm = mnl_nlmsg_get_data(nlh);
+ int len = mnl_nlmsg_get_len(nlh);
+ struct nlattr *attr;
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_parse_at_offset(nlh, sizeof(*ifm), tb, IFLA_MAX);
+ if (tb[IFLA_MTU]) {
+ printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
+ }
+ if (tb[IFLA_IFNAME]) {
+ printf("name=%s", mnl_attr_get_str(tb[IFLA_IFNAME]));
+ }
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main()
+{
+ struct mnl_socket *nl;
+ char buf[getpagesize()];
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rt;
+ int ret;
+ unsigned int seq;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq = time(NULL);
+ rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+ rt->rtgen_family = AF_PACKET;
+
+ 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);
+ }
+
+ if (mnl_socket_sendto(nl, nlh, mnl_nlmsg_get_len(nlh)) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, 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-link-event.c b/examples/rtnl-link-event.c
new file mode 100644
index 0000000..ed5a577
--- /dev/null
+++ b/examples/rtnl-link-event.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[IFLA_MAX+1];
+ struct ifinfomsg *ifm = mnl_nlmsg_get_data(nlh);
+ int len = mnl_nlmsg_get_len(nlh);
+ struct nlattr *attr;
+
+ printf("index=%d type=%d flags=%d family=%d ",
+ ifm->ifi_index, ifm->ifi_type,
+ ifm->ifi_flags, ifm->ifi_family);
+
+ if (ifm->ifi_flags & IFF_RUNNING)
+ printf("[RUNNING] ");
+ else
+ printf("[NOT RUNNING] ");
+
+ mnl_attr_parse_at_offset(nlh, sizeof(*ifm), tb, IFLA_MAX);
+ if (tb[IFLA_MTU]) {
+ printf("mtu=%d ", mnl_attr_get_u32(tb[IFLA_MTU]));
+ }
+ if (tb[IFLA_IFNAME]) {
+ printf("name=%s", mnl_attr_get_str(tb[IFLA_IFNAME]));
+ }
+ printf("\n");
+ return MNL_CB_OK;
+}
+
+int main()
+{
+ struct mnl_socket *nl;
+ char buf[getpagesize()];
+ struct nlmsghdr *nlh = (struct nlmsghdr *) buf;
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ perror("mnl_socket_open");
+ exit(EXIT_FAILURE);
+ }
+
+ if (mnl_socket_bind(nl, RTMGRP_LINK, 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, 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/rtnl-link-set.c b/examples/rtnl-link-set.c
new file mode 100644
index 0000000..36bf355
--- /dev/null
+++ b/examples/rtnl-link-set.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nl;
+ char buf[getpagesize()];
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+ int ret;
+ unsigned int seq, oper;
+
+ if (argc != 3) {
+ printf("Usage: %s [ifname] [up|down]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strncasecmp(argv[2], "up") == 0)
+ oper = IF_OPER_UP;
+ else if (strncasecmp(argv[2], "down") == 0)
+ oper = IF_OPER_DOWN;
+ else {
+ fprintf(stderr, "%s is not `up' nor `down'\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = RTM_SETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_seq = seq = time(NULL);
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_PACKET;
+
+ mnl_attr_put_u8(nlh, IFLA_OPERSTATE, oper);
+ mnl_attr_put_str(nlh, IFLA_IFNAME, argv[1]);
+
+ 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);
+ }
+
+ mnl_nlmsg_print(nlh);
+
+ if (mnl_socket_sendto(nl, nlh, mnl_nlmsg_get_len(nlh)) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ if (ret == -1) {
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_cb_run(buf, ret, seq, NULL, NULL);
+ if (ret == -1){
+ perror("callback");
+ exit(EXIT_FAILURE);
+ }
+
+ mnl_socket_close(nl);
+
+ return 0;
+}
diff --git a/examples/rtnl-route-dump.c b/examples/rtnl-route-dump.c
new file mode 100644
index 0000000..e8f3a0c
--- /dev/null
+++ b/examples/rtnl-route-dump.c
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/if.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+
+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]) {
+ printf("dst=%s ", inet_ntoa(mnl_attr_get_u32(tb[RTA_DST])));
+ }
+ if (tb[RTA_SRC]) {
+ printf("src=%s ", inet_ntoa(mnl_attr_get_u32(tb[RTA_SRC])));
+ }
+ 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]) {
+ printf("prefsrc=%s ",
+ inet_ntoa(mnl_attr_get_u32(tb[RTA_PREFSRC])));
+ }
+ if (tb[RTA_GATEWAY]) {
+ printf("gw=%s ", inet_ntoa(mnl_attr_get_u32(tb[RTA_GATEWAY])));
+ }
+ if (tb[RTA_METRICS]) {
+ int i;
+ struct nlattr *tbx[RTAX_MAX+1] = {};
+
+ mnl_attr_parse_nested(tb[RTA_METRICS], tbx, RTAX_MAX);
+
+ for (i=0; i<RTAX_MAX; i++) {
+ if (tbx[i]) {
+ printf("metrics[%d]=%u ",
+ mnl_attr_get_u32(tbx[i]));
+ }
+ }
+ }
+ printf("\n");
+}
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+ /* parse() ya está inicializando este array, qué hacer ? */
+ struct nlattr *tb[RTA_MAX+1] = {};
+ struct rtmsg *rm = mnl_nlmsg_get_data(nlh);
+ int len = mnl_nlmsg_get_len(nlh);
+ struct nlattr *attr;
+
+ /* 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\n", rm->rtm_flags);
+
+ mnl_attr_parse_at_offset(nlh, sizeof(*rm), tb, RTA_MAX);
+
+ switch(rm->rtm_family) {
+ case AF_INET:
+ attributes_show_ipv4(tb);
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+int main()
+{
+ struct mnl_socket *nl;
+ char buf[getpagesize()];
+ struct nlmsghdr *nlh;
+ struct rtmsg *rtm;
+ int ret;
+ unsigned int seq;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ 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 = AF_INET;
+
+ 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);
+ }
+
+ if (mnl_socket_sendto(nl, nlh, mnl_nlmsg_get_len(nlh)) < 0) {
+ perror("mnl_socket_send");
+ exit(EXIT_FAILURE);
+ }
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, 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/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..976b9f8
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = libmnl
diff --git a/include/libmnl/Makefile.am b/include/libmnl/Makefile.am
new file mode 100644
index 0000000..b03f68a
--- /dev/null
+++ b/include/libmnl/Makefile.am
@@ -0,0 +1 @@
+pkginclude_HEADERS = libmnl.h
diff --git a/include/libmnl/libmnl.h b/include/libmnl/libmnl.h
new file mode 100644
index 0000000..55f48c1
--- /dev/null
+++ b/include/libmnl/libmnl.h
@@ -0,0 +1,120 @@
+#ifndef _LIBMNL_H_
+#define _LIBMNL_H_
+
+#include <sys/socket.h> /* for sa_family_t */
+#include <linux/netlink.h>
+
+/*
+ * generic netlink socket API
+ */
+
+#define MNL_SOCKET_AUTOPID 0
+
+struct mnl_socket;
+
+extern struct mnl_socket *mnl_socket_open(int type);
+extern int mnl_socket_bind(struct mnl_socket *nl, int groups, int pid);
+extern int mnl_socket_close(struct mnl_socket *nl);
+extern int mnl_socket_get_fd(const struct mnl_socket *nl);
+extern unsigned int mnl_socket_get_portid(const struct mnl_socket *nl);
+extern int mnl_socket_sendto(struct mnl_socket *nl, const void *req, int siz);
+extern int mnl_socket_sendmsg(struct mnl_socket *nl, struct msghdr *msg, int flags);
+extern int mnl_socket_recvfrom(struct mnl_socket *nl, void *buf, int siz);
+extern int mnl_socket_recvmsg(const struct mnl_socket *nl, struct msghdr *msg, int flags);
+extern int mnl_socket_setsockopt(struct mnl_socket *nl, int type, void *buf, socklen_t len);
+extern int mnl_socket_getsockopt(struct mnl_socket *nl, int type, void *buf, socklen_t *len);
+
+/*
+ * generic netlink message API
+ */
+
+#define MNL_ALIGNTO 4
+#define MNL_NLMSG_HDRLEN mnl_align(sizeof(struct nlmsghdr))
+
+extern int mnl_align(int len);
+extern size_t mnl_nlmsg_size(int len);
+extern size_t mnl_nlmsg_total_size(int len);
+extern size_t mnl_nlmsg_payload_size(const struct nlmsghdr *nlh);
+
+/* Netlink message header builder */
+extern struct nlmsghdr *mnl_nlmsg_put_header(void *buf);
+extern void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, int size);
+
+/* Netlink message iterators */
+extern int mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len);
+extern struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len);
+
+/* Netlink sequence tracking */
+extern int mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq);
+
+/* Netlink header getters */
+extern u_int16_t mnl_nlmsg_get_len(const struct nlmsghdr *nlh);
+extern void *mnl_nlmsg_get_data(const struct nlmsghdr *nlh);
+extern void *mnl_nlmsg_get_data_offset(const struct nlmsghdr *nlh, int offset);
+extern void *mnl_nlmsg_get_tail(const struct nlmsghdr *nlh);
+
+/* Netlink dump message */
+extern void mnl_nlmsg_print(const struct nlmsghdr *nlh);
+
+/*
+ * generic netlink attributes API
+ */
+#define MNL_ATTR_HDRLEN mnl_align(sizeof(struct nlattr))
+
+/* TLV attribute getters */
+extern u_int16_t mnl_attr_get_type(const struct nlattr *attr);
+extern u_int16_t mnl_attr_get_len(const struct nlattr *attr);
+extern u_int16_t mnl_attr_get_payload_len(const struct nlattr *attr);
+extern void *mnl_attr_get_data(const struct nlattr *attr);
+extern u_int8_t mnl_attr_get_u8(const struct nlattr *attr);
+extern u_int16_t mnl_attr_get_u16(const struct nlattr *attr);
+extern u_int32_t mnl_attr_get_u32(const struct nlattr *attr);
+extern u_int64_t mnl_attr_get_u64(const struct nlattr *attr);
+extern const char *mnl_attr_get_str(const struct nlattr *attr);
+
+/* TLV attribute putters */
+extern void mnl_attr_put(struct nlmsghdr *nlh, int type, size_t len, const void *data);
+extern void mnl_attr_put_u8(struct nlmsghdr *nlh, int type, u_int8_t data);
+extern void mnl_attr_put_u16(struct nlmsghdr *nlh, int type, u_int16_t data);
+extern void mnl_attr_put_u32(struct nlmsghdr *nlh, int type, u_int32_t data);
+extern void mnl_attr_put_u64(struct nlmsghdr *nlh, int type, u_int64_t data);
+extern void mnl_attr_put_str(struct nlmsghdr *nlh, int type, const void *data);
+extern void mnl_attr_put_str_null(struct nlmsghdr *nlh, int type, const void *data);
+
+/* TLV attribute parsers */
+extern int mnl_attr_parse(const struct nlmsghdr *nlh, struct nlattr *tb[], int max);
+extern int mnl_attr_parse_at_offset(const struct nlmsghdr *nlh, int offset, struct nlattr *tb[], int max);
+extern int mnl_attr_parse_nested(const struct nlattr *attr, struct nlattr *tb[], int max);
+extern int mnl_attr_ok(const struct nlattr *attr, int len);
+extern struct nlattr *mnl_attr_next(const struct nlattr *attr, int *len);
+
+#define mnl_attr_for_each_nested(pos, head, len) \
+ for (pos = mnl_attr_get_data(head), len = mnl_attr_get_len(head); \
+ mnl_attr_ok(pos, len); \
+ pos = mnl_attr_next(pos, &(len)))
+
+/*
+ * callback API
+ */
+#define MNL_CB_ERROR -1
+#define MNL_CB_STOP 0
+#define MNL_CB_OK 1
+
+typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
+
+extern int mnl_cb_run(const char *buf, int numbytes, unsigned int seq,
+ mnl_cb_t cb_data, void *data);
+
+extern int mnl_cb_run2(const char *buf, int numbytes, unsigned int seq,
+ mnl_cb_t cb_data, void *data,
+ mnl_cb_t *cb_ctl_array, int cb_ctl_array_len);
+
+/*
+ * other declarations
+ */
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#endif
diff --git a/libmnl.pc.in b/libmnl.pc.in
new file mode 100644
index 0000000..8a24315
--- /dev/null
+++ b/libmnl.pc.in
@@ -0,0 +1,15 @@
+# libmnl pkg-config file
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libmnl
+Description: Minimalistic Netlink communication library
+URL: http://netfilter.org/projects/libmnl/
+Version: @VERSION@
+Requires:
+Conflicts:
+Libs: -L${libdir} -lmnl
+Cflags: -I${includedir}
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..1d3713f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,9 @@
+include $(top_srcdir)/Make_global.am
+
+AM_CFLAGS=-fPIC -Wall
+LIBS=
+
+lib_LTLIBRARIES = libmnl.la
+
+libmnl_la_LDFLAGS = -Wc,-nostartfiles -version-info $(LIBVERSION)
+libmnl_la_SOURCES = socket.c callback.c msg.c attr.c
diff --git a/src/attr.c b/src/attr.c
new file mode 100644
index 0000000..a724be0
--- /dev/null
+++ b/src/attr.c
@@ -0,0 +1,212 @@
+/*
+ * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <libmnl/libmnl.h>
+#include <string.h>
+
+/**
+ * Netlink attribute:
+ *
+ * |<-- 2 bytes -->|<-- 2 bytes -->|<-- variable -->|
+ * -------------------------------------------------
+ * | length | type | value |
+ * -------------------------------------------------
+ */
+
+/**
+ * mnl_attr_get_type - get the attribute type of a netlink message
+ *
+ * This function returns the attribute type.
+ */
+u_int16_t mnl_attr_get_type(const struct nlattr *attr)
+{
+ return attr->nla_type & NLA_TYPE_MASK;
+}
+
+/**
+ * mnl_attr_get_len - get the attribute length
+ *
+ * This function returns the attribute length.
+ */
+u_int16_t mnl_attr_get_len(const struct nlattr *attr)
+{
+ return attr->nla_len;
+}
+
+/**
+ * mnl_attr_get_payload_len - get the attribute payload length
+ *
+ * This function returns the attribute payload length.
+ */
+u_int16_t mnl_attr_get_payload_len(const struct nlattr *attr)
+{
+ return attr->nla_len - MNL_ATTR_HDRLEN;
+}
+
+/**
+ * mnl_attr_get_data - get pointer to the attribute payload
+ *
+ * This function return a pointer to the attribute payload
+ */
+void *mnl_attr_get_data(const struct nlattr *attr)
+{
+ return (void *)attr + MNL_ATTR_HDRLEN;
+}
+
+/**
+ * mnl_attr_ok - check a there is room for an attribute
+ * @nlh: attribute that we want to check
+ * @len: remaining bytes in a buffer that contains the attribute
+ *
+ * This function is used to check that a buffer that contains an attribute
+ * has enough room for the attribute that it stores, ie. this function can
+ * be used to verify that an attribute is not malformed nor truncated.
+ */
+int mnl_attr_ok(const struct nlattr *attr, int len)
+{
+ return len >= sizeof(struct nlattr) &&
+ attr->nla_len >= sizeof(struct nlattr) &&
+ attr->nla_len <= len;
+}
+
+/**
+ * mnl_attr_next - get the next attribute in the payload of a netlink message
+ * @attr: pointer to the current attribute
+ * @len: pointer to the current remaining bytes in the buffer
+ *
+ * This function returns a pointer to the next attribute that is in the
+ * payload of a netlink message.
+ */
+struct nlattr *mnl_attr_next(const struct nlattr *attr, int *len)
+{
+ *len -= mnl_align(attr->nla_len);
+ return (struct nlattr *)((void *)attr + mnl_align(attr->nla_len));
+}
+
+/**
+ * mnl_attr_parse - returns an array with the attributes in a message
+ * @tb: array of pointers to the attribute found
+ * @tb_size: size of the attribute array
+ * @attr: first attribute in the stream
+ * @len: remaining bytes in the buffer that contain attributes
+ *
+ * This function returns a table of pointers to the attributes that has been
+ * found in a netlink payload. This function return 0 on sucess, and >0 to
+ * indicate the number of bytes the remaining bytes.
+ */
+int mnl_attr_parse_at_offset(const struct nlmsghdr *nlh, int offset,
+ struct nlattr *tb[], int max)
+{
+ struct nlattr *attr = mnl_nlmsg_get_data_offset(nlh, offset);
+ int len = mnl_nlmsg_get_len(nlh);
+
+ memset(tb, 0, sizeof(struct nlattr *) * (max + 1));
+
+ while (mnl_attr_ok(attr, len)) {
+ if (mnl_attr_get_type(attr) <= max)
+ tb[mnl_attr_get_type(attr)] = attr;
+ attr = mnl_attr_next(attr, &len);
+ }
+ return len;
+}
+
+int mnl_attr_parse(const struct nlmsghdr *nlh, struct nlattr *tb[], int max)
+{
+ return mnl_attr_parse_at_offset(nlh, 0, tb, max);
+}
+
+int mnl_attr_parse_nested(const struct nlattr *nested,
+ struct nlattr *tb[], int max)
+{
+ struct nlattr *attr = mnl_attr_get_data(nested);
+ int len = mnl_attr_get_payload_len(nested);
+
+ memset(tb, 0, sizeof(struct nlattr *) * (max + 1));
+
+ while (mnl_attr_ok(attr, len)) {
+ if (mnl_attr_get_type(attr) <= max)
+ tb[mnl_attr_get_type(attr)] = attr;
+ attr = mnl_attr_next(attr, &len);
+ }
+ return len;
+}
+
+u_int8_t mnl_attr_get_u8(const struct nlattr *attr)
+{
+ return *((u_int8_t *)mnl_attr_get_data(attr));
+}
+
+u_int16_t mnl_attr_get_u16(const struct nlattr *attr)
+{
+ return *((u_int16_t *)mnl_attr_get_data(attr));
+}
+
+u_int32_t mnl_attr_get_u32(const struct nlattr *attr)
+{
+ return *((u_int32_t *)mnl_attr_get_data(attr));
+}
+
+/**
+ * mnl_attr_get_u64 - returns an arra
+ * @attr: netlink attribute
+ *
+ * This function returns the payload of a 64-bits attribute. This function
+ * is align-safe since accessing 64-bits Netlink attributes is a common
+ * source of alignment issues.
+ */
+u_int64_t mnl_attr_get_u64(const struct nlattr *attr)
+{
+ u_int64_t tmp;
+ memcpy(&tmp, mnl_attr_get_data(attr), sizeof(tmp));
+ return tmp;
+}
+
+const char *mnl_attr_get_str(const struct nlattr *attr)
+{
+ return (const char *)mnl_attr_get_data(attr);
+}
+
+void mnl_attr_put(struct nlmsghdr *nlh, int type, size_t len, const void *data)
+{
+ struct nlattr *attr = mnl_nlmsg_get_tail(nlh);
+ int payload_len = mnl_align(sizeof(struct nlattr)) + len;
+
+ attr->nla_type = type;
+ attr->nla_len = payload_len;
+ memcpy(mnl_attr_get_data(attr), data, len);
+ nlh->nlmsg_len += mnl_align(payload_len);
+}
+
+void mnl_attr_put_u8(struct nlmsghdr *nlh, int type, u_int8_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(u_int8_t), &data);
+}
+
+void mnl_attr_put_u16(struct nlmsghdr *nlh, int type, u_int16_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(u_int16_t), &data);
+}
+
+void mnl_attr_put_u32(struct nlmsghdr *nlh, int type, u_int32_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(u_int32_t), &data);
+}
+
+void mnl_attr_put_u64(struct nlmsghdr *nlh, int type, u_int64_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(u_int64_t), &data);
+}
+
+void mnl_attr_put_str(struct nlmsghdr *nlh, int type, const void *data)
+{
+ mnl_attr_put(nlh, type, strlen(data), data);
+}
+
+void mnl_attr_put_str_null(struct nlmsghdr *nlh, int type, const void *data)
+{
+ mnl_attr_put(nlh, type, strlen(data)+1, data);
+}
diff --git a/src/callback.c b/src/callback.c
new file mode 100644
index 0000000..3fc883e
--- /dev/null
+++ b/src/callback.c
@@ -0,0 +1,119 @@
+/*
+ * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data)
+{
+ return MNL_CB_OK;
+}
+
+static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_data(nlh);
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+ /* Netlink subsystems returns the errno value with different signess */
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data)
+{
+ return MNL_CB_STOP;
+}
+
+static mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_NOOP] = mnl_cb_noop,
+ [NLMSG_ERROR] = mnl_cb_error,
+ [NLMSG_DONE] = mnl_cb_stop,
+ [NLMSG_OVERRUN] = mnl_cb_noop,
+};
+
+/**
+ * mnl_cb_run2 - callback runqueue for netlink messages
+ * @buf: buffer that contains the netlink messages
+ * @numbytes: number of bytes stored in the buffer
+ * @seq: sequence number that we expect to receive (use zero to skip)
+ * @cb_data: callback handler for data messages
+ * @data: pointer to data that will be passed to the data callback handler
+ * @cb_ctl_array: array of custom callback handlers from control messages
+ * @cb_ctl_array_len: length of the array of custom control callback handlers
+ *
+ * You can set the cb_ctl_array to NULL if you want to use the default control
+ * callback handlers, in that case, the parameter cb_ctl_array_len is not
+ * checked.
+ *
+ * This function returns -1 in case of error, 0 if we have received a
+ * NLMSG_DONE message or the callback has explicitly returned MNL_CB_STOP.
+ */
+int mnl_cb_run2(const char *buf, int numbytes, unsigned int seq,
+ mnl_cb_t cb_data, void *data,
+ mnl_cb_t *cb_ctl_array, int cb_ctl_array_len)
+{
+ int ret = MNL_CB_OK;
+ struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
+
+ while (mnl_nlmsg_ok(nlh, numbytes)) {
+ /* perform sequence tracking */
+ if (!mnl_nlmsg_seq_ok(nlh, seq)) {
+ errno = EILSEQ;
+ return -1;
+ }
+
+ /* netlink data message handling */
+ if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
+ if (cb_data){
+ ret = cb_data(nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (nlh->nlmsg_type < cb_ctl_array_len) {
+ if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
+ ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (default_cb_array[nlh->nlmsg_type]) {
+ ret = default_cb_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ nlh = mnl_nlmsg_next(nlh, &numbytes);
+ }
+out:
+ return ret <= MNL_CB_ERROR ? -1 : 0;
+}
+
+/**
+ * mnl_cb_run - callback runqueue for netlink messages (simplified version)
+ * @buf: buffer that contains the netlink messages
+ * @numbytes: number of bytes stored in the buffer
+ * @seq: sequence number that we expect to receive (use zero to skip)
+ * @cb_data: callback handler for data messages
+ * @data: pointer to data that will be passed to the data callback handler
+ *
+ * This function is like mnl_cb_run2() but it does not allow you to set
+ * the control callback handlers.
+ *
+ * This function returns -1 in case of error, 0 if we have received a
+ * NLMSG_DONE message or the callback has explicitly returned MNL_CB_STOP.
+ */
+
+int mnl_cb_run(const char *buf, int numbytes, unsigned int seq,
+ mnl_cb_t cb_data, void *data)
+{
+ return mnl_cb_run2(buf, numbytes, seq, cb_data, data, NULL, 0);
+}
diff --git a/src/msg.c b/src/msg.c
new file mode 100644
index 0000000..02f812b
--- /dev/null
+++ b/src/msg.c
@@ -0,0 +1,213 @@
+/*
+ * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <libmnl/libmnl.h>
+
+/**
+ * mnl_align - align a value to four bytes
+ * @value: the value that we want to get aligned
+ *
+ * This function returns the value passed aligned to four bytes. Netlink
+ * message headers and its attributes are always aligned to four bytes.
+ */
+int mnl_align(int value)
+{
+ return (value + MNL_ALIGNTO - 1) & ~(MNL_ALIGNTO - 1);
+}
+
+/**
+ * mnl_nlmsg_size - get size of the netlink messages (without alignment)
+ * @len: length of the netlink message
+ *
+ * This function returns the size of a netlink message (header plus payload)
+ * without alignment.
+ */
+size_t mnl_nlmsg_size(int len)
+{
+ return len + mnl_align(MNL_NLMSG_HDRLEN);
+}
+
+/**
+ * mnl_nlmsg_aligned_size - get size of the netlink messages (with alignment)
+ * @len: length of the netlink message
+ *
+ * This function returns the size of a netlink message (header plus payload)
+ * with alignment.
+ */
+size_t mnl_nlmsg_aligned_size(int len)
+{
+ return mnl_align(mnl_nlmsg_size(len));
+}
+
+/**
+ * mnl_nlmsg_payload_size - get the size of the payload
+ * @nlh: pointer to the header of the netlink message
+ *
+ * This function returns the size of the netlink payload
+ */
+size_t mnl_nlmsg_payload_size(const struct nlmsghdr *nlh)
+{
+ return nlh->nlmsg_len - MNL_NLMSG_HDRLEN;
+}
+
+/**
+ * mnl_nlmsg_put_header - prepare room for Netlink header
+ * @buf: memory already allocated to store the Netlink message
+ *
+ * This function sets to zero the room that is required to put a Netlink
+ * header in the memory buffer passed as parameter. This function also
+ * initializes the nlmsg_len field. This function returns a pointer to the
+ * Netlink header structure.
+ */
+struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
+{
+ int len = mnl_align(sizeof(struct nlmsghdr));
+ struct nlmsghdr *nlh = buf;
+
+ memset(buf, 0, len);
+ nlh->nlmsg_len = len;
+ return nlh;
+}
+
+/**
+ * mnl_nlmsg_put_extra_header - prepare room for an extra header
+ * @nlh: pointer to Netlink header
+ * @size: size of the extra header that we want to put
+ *
+ * This function sets to zero the room that is required to put the extra
+ * header after the initial Netlink header. This function also increases
+ * the nlmsg_len field. This function returns a pointer to the extra
+ * header.
+ */
+void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, int size)
+{
+ char *ptr = (char *)nlh + nlh->nlmsg_len;
+ nlh->nlmsg_len += mnl_align(size);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+/**
+ * mnl_nlmsg_get_len - get the length field from the netlink message
+ * @nlh: pointer to a netlink header
+ *
+ * This function returns the length of the netlink message by return the field
+ * nlmsg_len of the message.
+ */
+u_int16_t mnl_nlmsg_get_len(const struct nlmsghdr *nlh)
+{
+ return nlh->nlmsg_len;
+}
+
+/**
+ * mnl_nlmsg_get_data - get a pointer to the payload of the netlink message
+ * @nlh: pointer to a netlink header
+ *
+ * This function returns a pointer to the payload of the netlink message.
+ */
+void *mnl_nlmsg_get_data(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN;
+}
+
+/**
+ * mnl_nlmsg_get_data_offset - get a pointer to the payload of the message
+ * @nlh: pointer to a netlink header
+ * @offset: offset to the payload of the attributes TLV set
+ *
+ * This function returns a pointer to the payload of the netlink message plus
+ * a given offset.
+ */
+void *mnl_nlmsg_get_data_offset(const struct nlmsghdr *nlh, int offset)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN + mnl_align(offset);
+}
+
+/**
+ * mnl_nlmsg_ok - check a there is room for netlink message
+ * @nlh: netlink message that we want to check
+ * @len: remaining bytes in a buffer that contains the netlink message
+ *
+ * This function is used to check that a buffer that contains a netlink
+ * message has enough room for the netlink message that it stores, ie. this
+ * function can be used to verify that a netlink message is not malformed nor
+ * truncated.
+ */
+int mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
+{
+ return len >= sizeof(struct nlmsghdr) &&
+ nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
+ nlh->nlmsg_len <= len;
+}
+
+/**
+ * mnl_nlmsg_next - get the next netlink message in a multipart message
+ * @nlh: current netlink message that we are handling
+ * @len: pointer to the current remaining bytes in the buffer
+ *
+ * This function returns a pointer to the next netlink message that is part
+ * of a multi-part netlink message. Netlink can batches messages into a buffer
+ * so that the receiver has to iterate over the whole set of netlink
+ * messages.
+ */
+struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)
+{
+ *len -= mnl_align(nlh->nlmsg_len);
+ return (struct nlmsghdr *)((void *)nlh + mnl_align(nlh->nlmsg_len));
+}
+
+void *mnl_nlmsg_get_tail(const struct nlmsghdr *nlh)
+{
+ return (struct nlmsghdr *)((void *)nlh + mnl_align(nlh->nlmsg_len));
+}
+
+/**
+ * mnl_nlmsg_seq_ok - perform sequence tracking
+ * @nlh: current netlink message that we are handling
+ * @seq: last sequence number used to send a message
+ *
+ * This functions returns 1 if the sequence tracking is fulfilled, otherwise
+ * 0 is returned. If seq is 0, then the sequence tracking is skipped. This
+ * value is generally used by the kernel for asynchronous notifications,
+ * for that reason, this library consider that it is reserved.
+ */
+int mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)
+{
+ return seq ? nlh->nlmsg_seq == seq : 1;
+}
+
+/* XXX: rework this, please */
+void mnl_nlmsg_print(const struct nlmsghdr *nlh)
+{
+ int i;
+
+ printf("========= netlink header ==========\n");
+ printf("length(32 bits)=%.08u\n", nlh->nlmsg_len);
+ printf("type(16 bits)=%.04u flags(16 bits)=%.04x\n",
+ nlh->nlmsg_type, nlh->nlmsg_flags);
+ printf("sequence number(32 bits)=%.08x\n", nlh->nlmsg_seq);
+ printf("port ID(32 bits)=%.08u\n", nlh->nlmsg_pid);
+ printf("===================================\n");
+
+ for (i=sizeof(struct nlmsghdr); i<mnl_nlmsg_get_len(nlh); i+=4) {
+ char *b = (char *) nlh;
+
+ printf("(%.3d) %.2x %.2x %.2x %.2x | ", i,
+ 0xff & b[i], 0xff & b[i+1],
+ 0xff & b[i+2], 0xff & b[i+3]);
+
+ printf("%c %c %c %c\n",
+ isalnum(b[i]) ? b[i] : 0,
+ isalnum(b[i+1]) ? b[i+1] : 0,
+ isalnum(b[i+2]) ? b[i+2] : 0,
+ isalnum(b[i+3]) ? b[i+3] : 0);
+ }
+}
diff --git a/src/socket.c b/src/socket.c
new file mode 100644
index 0000000..b439b8f
--- /dev/null
+++ b/src/socket.c
@@ -0,0 +1,255 @@
+/*
+ * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <libmnl/libmnl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+struct mnl_socket {
+ int fd;
+ struct sockaddr_nl addr;
+};
+
+/**
+ * mnl_socket_get_fd - obtain file descriptor from netlink socket
+ * @nl: netlink socket obtained via mnl_socket_open()
+ *
+ * This function returns the file descriptor of a given netlink socket.
+ */
+int mnl_socket_get_fd(const struct mnl_socket *nl)
+{
+ return nl->fd;
+}
+
+/**
+ * mnl_socket_get_portid - obtain Netlink PortID from netlink socket
+ * @nl: netlink socket obtained via mnl_socket_open()
+ *
+ * This function returns the Netlink PortID of a given netlink socket.
+ * It's a common mistake to assume that this PortID equals the process ID
+ * which is not always true. This is the case if you open more than one
+ * socket that is binded to the same Netlink subsystem.
+ */
+unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
+{
+ return nl->addr.nl_pid;
+}
+
+/**
+ * mnl_socket_open - open a netlink socket
+ * @unit: the netlink socket unit (see NETLINK_* constants)
+ *
+ * On error, it returns -1 and errno is appropriately set. Otherwise, it
+ * returns a valid pointer to the mnl_socket structure.
+ */
+struct mnl_socket *mnl_socket_open(int unit)
+{
+ struct mnl_socket *nl;
+
+ nl = calloc(sizeof(struct mnl_socket), 1);
+ if (nl == NULL)
+ return NULL;
+
+ nl->fd = socket(AF_NETLINK, SOCK_RAW, unit);
+ if (nl->fd == -1)
+ return NULL;
+
+ return nl;
+}
+
+/**
+ * mnl_socket_bind - bind netlink socket
+ * @nl: netlink socket obtained via mnl_socket_open()
+ * @groups: the group of message you're interested in
+ * @pid: the port ID you want to use (use zero for automatic selection)
+ *
+ * On error, this function returns -1 and errno is appropriately set. On
+ * success, 0 is returned.
+ */
+int mnl_socket_bind(struct mnl_socket *nl, int groups, int pid)
+{
+ int ret;
+ socklen_t addr_len;
+
+ nl->addr.nl_family = AF_NETLINK;
+ nl->addr.nl_groups = groups;
+
+ ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
+ if (ret < 0)
+ return ret;
+
+ addr_len = sizeof(nl->addr);
+ ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
+ if (ret < 0)
+ return ret;
+
+ if (addr_len != sizeof(nl->addr)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (nl->addr.nl_family != AF_NETLINK) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * mnl_socket_sendto - send a netlink message of a certain size
+ * @nl: netlink socket obtained via mnl_socket_open()
+ * @buf: buffer containing the netlink message to be sent
+ * @bufsiz: number of bytes in the buffer that you want to send
+ *
+ * On error, it returns -1 and errno is appropriately set. Otherwise, it
+ * returns the number of bytes sent.
+ */
+int mnl_socket_sendto(struct mnl_socket *nl, const void *buf, int len)
+{
+ struct sockaddr_nl snl = {
+ .nl_family = AF_NETLINK
+ };
+ return sendto(nl->fd, buf, len, 0,
+ (struct sockaddr *) &snl, sizeof(snl));
+}
+
+/**
+ * mnl_socket_sendmsg - send a netlink message of a certain size
+ * @nl: netlink socket obtained via mnl_socket_open()
+ * @msg: pointer to struct msghdr (must be initialized appropriately)
+ * @flags: flags passed to sendmsg()
+ *
+ * On error, it returns -1 and errno is appropriately set. Otherwise, it
+ * returns the number of bytes sent.
+ */
+int
+mnl_socket_sendmsg(struct mnl_socket *nl, struct msghdr *msg, int flags)
+{
+ return sendmsg(nl->fd, msg, flags);
+}
+
+/**
+ * mnl_socket_recvfrom - receive a netlink message
+ * @nl: netlink socket obtained via mnl_socket_open()
+ * @buf: buffer that you want to use to store the netlink message
+ * @bufsiz: size of the buffer passed to store the netlink message
+ *
+ * On error, it returns -1 and errno is appropriately set. If errno is set
+ * to ENOSPC, it means that the buffer that you have passed to store the
+ * netlink message is small so you have received a truncated message. Make
+ * sure your program set a buffer big enough to store the netlink message.
+ */
+int mnl_socket_recvfrom(struct mnl_socket *nl, void *buf, int bufsiz)
+{
+ int ret;
+ struct sockaddr_nl addr;
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = bufsiz,
+ };
+ struct msghdr msg = {
+ .msg_name = (void *)&addr,
+ .msg_namelen = sizeof(struct sockaddr_nl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ ret = recvmsg(nl->fd, &msg, 0);
+ if (ret == -1)
+ return ret;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ errno = ENOSPC;
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return ret;
+}
+
+/**
+ * mnl_socket_recvmsg- receive a netlink message
+ * @nl: netlink socket obtained via mnl_socket_open()
+ * @msg: pointer to struct msghdr (must be initialized appropriately)
+ * @flags: flags passed to recvmsg()
+ *
+ * On error, this function returns -1 and errno is appropriately set.
+ * On sucess, this function returns the number of bytes received.
+ */
+int
+mnl_socket_recvmsg(const struct mnl_socket *nl, struct msghdr *msg, int flags)
+{
+ return recvmsg(nl->fd, msg, flags);
+}
+
+/**
+ * mnl_socket_close - close a given netlink socket
+ * @nl: netlink socket obtained via mnl_socket_open()
+ *
+ * On error, this function returns -1 and errno is appropriately set.
+ * On success, it returns 0.
+ */
+int mnl_socket_close(struct mnl_socket *nl)
+{
+ int ret = close(nl->fd);
+ free(nl);
+ nl = NULL;
+ return ret;
+}
+
+/**
+ * mnl_socket_setsockopt - set Netlink socket option
+ * @nl: netlink socket obtained via mnl_socket_open()
+ * @type: type of Netlink socket options
+ * @buf: the buffer that contains the data about this option
+ * @len: the size of the buffer passed
+ *
+ * This function allows you to set some Netlink socket option. As of writing
+ * this, the existing options are:
+ *
+ * #define NETLINK_ADD_MEMBERSHIP 1
+ * #define NETLINK_DROP_MEMBERSHIP 2
+ * #define NETLINK_PKTINFO 3
+ * #define NETLINK_BROADCAST_ERROR 4
+ * #define NETLINK_NO_ENOBUFS 5
+ *
+ * In the early days, Netlink only supported 32 groups expressed in a
+ * 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast
+ * groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to
+ * join a given multicast group. This function internally calls setsockopt()
+ * to join a given netlink multicast group. You can still use mnl_bind()
+ * and the 32-bit mask to join a set of Netlink multicast groups.
+ *
+ * On error, this function returns -1 and errno is appropriately set.
+ */
+int mnl_socket_setsockopt(struct mnl_socket *nl, int type,
+ void *buf, socklen_t len)
+{
+ return setsockopt(nl->fd, SOL_NETLINK, type, buf, len);
+}
+
+/**
+ * mnl_socket_getsockopt - get a Netlink socket option
+ * @nl: netlink socket obtained via mnl_socket_open()
+ * @type: type of Netlink socket options
+ * @buf: pointer to the buffer to store the value of this option
+ * @len: size of the information written in the buffer
+ *
+ * On error, this function returns -1 and errno is appropriately set.
+ */
+int mnl_socket_getsockopt(struct mnl_socket *nl, int type,
+ void *buf, socklen_t *len)
+{
+ return getsockopt(nl->fd, SOL_NETLINK, type, buf, len);
+}