From 74e7bb8b033640f7c4692c4ea0d5c231e3137b7b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 19 Mar 2010 14:55:32 +0100 Subject: initial libmnl import --- Make_global.am | 7 ++ Makefile.am | 14 +++ README | 9 ++ autogen.sh | 53 ++++++++++ configure.in | 18 ++++ examples/Makefile.am | 24 +++++ examples/genl-family-get.c | 143 +++++++++++++++++++++++++ examples/rtnl-link-dump.c | 84 +++++++++++++++ examples/rtnl-link-event.c | 70 +++++++++++++ examples/rtnl-link-set.c | 76 ++++++++++++++ examples/rtnl-route-dump.c | 192 ++++++++++++++++++++++++++++++++++ include/Makefile.am | 1 + include/libmnl/Makefile.am | 1 + include/libmnl/libmnl.h | 120 +++++++++++++++++++++ libmnl.pc.in | 15 +++ src/Makefile.am | 9 ++ src/attr.c | 212 +++++++++++++++++++++++++++++++++++++ src/callback.c | 119 +++++++++++++++++++++ src/msg.c | 213 +++++++++++++++++++++++++++++++++++++ src/socket.c | 255 +++++++++++++++++++++++++++++++++++++++++++++ 20 files changed, 1635 insertions(+) create mode 100644 Make_global.am create mode 100644 Makefile.am create mode 100644 README create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 examples/Makefile.am create mode 100644 examples/genl-family-get.c create mode 100644 examples/rtnl-link-dump.c create mode 100644 examples/rtnl-link-event.c create mode 100644 examples/rtnl-link-set.c create mode 100644 examples/rtnl-route-dump.c create mode 100644 include/Makefile.am create mode 100644 include/libmnl/Makefile.am create mode 100644 include/libmnl/libmnl.h create mode 100644 libmnl.pc.in create mode 100644 src/Makefile.am create mode 100644 src/attr.c create mode 100644 src/callback.c create mode 100644 src/msg.c create mode 100644 src/socket.c 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 /#include /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 +#include +#include + +#include +#include + +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 +#include +#include + +#include +#include +#include +#include + +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 +#include +#include + +#include +#include +#include +#include + +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 +#include +#include + +#include +#include +#include +#include + +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 +#include +#include + +#include +#include +#include +#include + +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; irtm_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 /* for sa_family_t */ +#include + +/* + * 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 + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include +#include + +/** + * 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 + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include +#include + +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 + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include + +/** + * 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 + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + */ + +#include +#include +#include +#include +#include +#include +#include + +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); +} -- cgit v1.2.3