X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fnetlink.c;h=40b12e1b048c4861c408a8af51e9df3415d900c1;hb=fcd73a95fdf5ef40a68c58389f46efafc20d3a04;hp=b15768e719d5c7ea847848ea63db4142e703c45c;hpb=de0fdb208de123fe753c5fcf03533833777a5b4a;p=collectd.git diff --git a/src/netlink.c b/src/netlink.c index b15768e7..40b12e1b 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -1,6 +1,6 @@ /** * collectd - src/netlink.c - * Copyright (C) 2007 Florian octo Forster + * Copyright (C) 2007-2010 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" @@ -35,13 +35,10 @@ # include #endif -#if HAVE_LIBNETLINK_H -# include -#elif HAVE_IPROUTE_LIBNETLINK_H -# include -#elif HAVE_LINUX_LIBNETLINK_H -# include -#endif +#include +#include +//#include +//#include typedef struct ir_ignorelist_s { @@ -54,7 +51,7 @@ typedef struct ir_ignorelist_s static int ir_ignorelist_invert = 1; static ir_ignorelist_t *ir_ignorelist_head = NULL; -static struct rtnl_handle rth; +static struct mnl_socket *nl; static char **iflist = NULL; static size_t iflist_len = 0; @@ -161,12 +158,12 @@ static int check_ignorelist (const char *dev, } /* int check_ignorelist */ static void submit_one (const char *dev, const char *type, - const char *type_instance, counter_t value) + const char *type_instance, derive_t value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = 1; @@ -183,13 +180,13 @@ static void submit_one (const char *dev, const char *type, static void submit_two (const char *dev, const char *type, const char *type_instance, - counter_t rx, counter_t tx) + derive_t rx, derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; vl.values_len = 2; @@ -204,46 +201,8 @@ static void submit_two (const char *dev, const char *type, plugin_dispatch_values (&vl); } /* void submit_two */ -static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa, - struct nlmsghdr *nmh, void __attribute__((unused)) *args) +static int update_iflist(struct ifinfomsg *msg) { - struct ifinfomsg *msg; - int msg_len; - struct rtattr *attrs[IFLA_MAX + 1]; - struct rtnl_link_stats *stats; - - const char *dev; - - if (nmh->nlmsg_type != RTM_NEWLINK) - { - ERROR ("netlink plugin: link_filter: Don't know how to handle type %i.", - nmh->nlmsg_type); - return (-1); - } - - msg = NLMSG_DATA (nmh); - - msg_len = nmh->nlmsg_len - sizeof (struct ifinfomsg); - if (msg_len < 0) - { - ERROR ("netlink plugin: link_filter: msg_len = %i < 0;", msg_len); - return (-1); - } - - memset (attrs, '\0', sizeof (attrs)); - if (parse_rtattr (attrs, IFLA_MAX, IFLA_RTA (msg), msg_len) != 0) - { - ERROR ("netlink plugin: link_filter: parse_rtattr failed."); - return (-1); - } - - if (attrs[IFLA_IFNAME] == NULL) - { - ERROR ("netlink plugin: link_filter: attrs[IFLA_IFNAME] == NULL"); - return (-1); - } - dev = RTA_DATA (attrs[IFLA_IFNAME]); - /* Update the `iflist'. It's used to know which interfaces exist and query * them later for qdiscs and classes. */ if ((msg->ifi_index >= 0) && ((size_t) msg->ifi_index >= iflist_len)) @@ -253,7 +212,7 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa, temp = (char **) realloc (iflist, (msg->ifi_index + 1) * sizeof (char *)); if (temp == NULL) { - ERROR ("netlink plugin: link_filter: realloc failed."); + ERROR ("netlink plugin: update_iflist: realloc failed."); return (-1); } @@ -269,12 +228,13 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa, iflist[msg->ifi_index] = strdup (dev); } - if (attrs[IFLA_STATS] == NULL) - { - DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev); - return (0); - } - stats = RTA_DATA (attrs[IFLA_STATS]); + return (0); +} + + +static void check_ignorelist_and_submit(const char *dev, + struct rtnl_link_stats *stats) +{ if (check_ignorelist (dev, "interface", NULL) == 0) { @@ -311,158 +271,248 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa, DEBUG ("netlink plugin: Ignoring %s/if_detail.", dev); } - return (0); -} /* int link_filter */ +} -static int qos_filter (const struct sockaddr_nl __attribute__((unused)) *sa, - struct nlmsghdr *nmh, void *args) +static int link_filter_cb (const struct nlmsghdr *nlh, + void __attribute__((unused)) *args) { - struct tcmsg *msg; - int msg_len; - struct rtattr *attrs[TCA_MAX + 1]; + struct ifinfomsg *ifm = mnl_nlmsg_get_payload (nlh); + struct nlattr *attr; + struct rtnl_link_stats *stats = NULL; + const char *dev = NULL; - int wanted_ifindex = *((int *) args); + if (nlh->nlmsg_type != RTM_NEWLINK) + { + ERROR ("netlink plugin: link_filter_cb: Don't know how to handle type %i.", + nlh->nlmsg_type); + return MNL_CB_ERROR; + } - const char *dev; + mnl_attr_for_each (attr, nlh, sizeof (*ifm)) + { + if (mnl_attr_get_type (attr) != IFLA_IFNAME) + continue; - /* char *type_instance; */ - char *tc_type; - char tc_inst[DATA_MAX_NAME_LEN]; + if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0) + { + ERROR ("netlink plugin: link_filter_cb: IFLA_IFNAME mnl_attr_validate failed."); + return MNL_CB_ERROR; + } - if (nmh->nlmsg_type == RTM_NEWQDISC) - tc_type = "qdisc"; - else if (nmh->nlmsg_type == RTM_NEWTCLASS) - tc_type = "class"; - else if (nmh->nlmsg_type == RTM_NEWTFILTER) - tc_type = "filter"; - else + dev = mnl_attr_get_str (attr); + if (update_iflist (ifm) < 0) + return MNL_CB_ERROR; + break; + } + + if (dev == NULL) { - ERROR ("netlink plugin: qos_filter: Don't know how to handle type %i.", - nmh->nlmsg_type); - return (-1); + ERROR ("netlink plugin: link_filter_cb: dev == NULL"); + return MNL_CB_ERROR; } - msg = NLMSG_DATA (nmh); + mnl_attr_for_each (attr, nlh, sizeof (*ifm)) + { + if (mnl_attr_get_type (attr) != IFLA_STATS) + continue; + + if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof(*stats)) < 0) + { + ERROR ("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 failed."); + return MNL_CB_ERROR; + } + stats = mnl_attr_get_payload(attr); + + check_ignorelist_and_submit(dev, stats); + break; + } - msg_len = nmh->nlmsg_len - sizeof (struct tcmsg); - if (msg_len < 0) + if (stats == NULL) { - ERROR ("netlink plugin: qos_filter: msg_len = %i < 0;", msg_len); - return (-1); + DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev); + return MNL_CB_OK; } - if (msg->tcm_ifindex != wanted_ifindex) + return MNL_CB_OK; +} /* int link_filter_cb */ + +static int qos_attr_cb (const struct nlattr *attr, void *data) +{ + struct gnet_stats_basic *bs = *(struct gnet_stats_basic **)data; + + /* skip unsupported attribute in user-space */ + if (mnl_attr_type_valid(attr, TCA_STATS_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_get_type(attr) == TCA_STATS_BASIC) { - DEBUG ("netlink plugin: qos_filter: Got %s for interface #%i, " + if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*bs)) < 0) + { + ERROR ("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 failed."); + return MNL_CB_ERROR; + } + bs = mnl_attr_get_payload(attr); + return MNL_CB_STOP; + } + + return MNL_CB_OK; +} /* qos_attr_cb */ + +static int qos_filter_cb (constr struct nlmsghdr *nlh, void *args) +{ + struct tcmsg *tm = mnl_nlmsg_get_payload(nlh); + int wanted_ifindex = *((int *) args); + + const char *dev; + const char *kind = NULL; + struct gnet_stats_basic *bs = NULL; + struct tc_stats *ts = NULL; + + /* char *type_instance; */ + char *tc_type; + char tc_inst[DATA_MAX_NAME_LEN]; + + if (tm->tcm_ifindex != wanted_ifindex) + { + DEBUG ("netlink plugin: qos_filter_cb: Got %s for interface #%i, " "but expected #%i.", tc_type, msg->tcm_ifindex, wanted_ifindex); - return (0); + return MNL_CB_OK; + } + + if (nlh->nlmsg_type == RTM_NEWQDISC) + tc_type = "qdisc"; + else if (nlh->nlmsg_type == RTM_NEWTCLASS) + tc_type = "class"; + else if (nlh->nlmsg_type == RTM_NEWTFILTER) + tc_type = "filter"; + else + { + ERROR ("netlink plugin: qos_filter_cb: Don't know how to handle type %i.", + nlh->nlmsg_type); + return MNL_CB_ERROR; } - if ((msg->tcm_ifindex >= 0) - && ((size_t) msg->tcm_ifindex >= iflist_len)) + if ((tm->tcm_ifindex >= 0) + && ((size_t) tm->tcm_ifindex >= iflist_len)) { - ERROR ("netlink plugin: qos_filter: msg->tcm_ifindex = %i " + ERROR ("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i " ">= iflist_len = %zu", - msg->tcm_ifindex, iflist_len); - return (-1); + tm->tcm_ifindex, iflist_len); + return MNL_CB_ERROR; } dev = iflist[msg->tcm_ifindex]; if (dev == NULL) { - ERROR ("netlink plugin: qos_filter: iflist[%i] == NULL", + ERROR ("netlink plugin: qos_filter_cb: iflist[%i] == NULL", msg->tcm_ifindex); - return (-1); + return MNL_CB_ERROR; } - memset (attrs, '\0', sizeof (attrs)); - if (parse_rtattr (attrs, TCA_MAX, TCA_RTA (msg), msg_len) != 0) + mnl_attr_for_each (attr, nlh, sizeof (*tm)) { - ERROR ("netlink plugin: qos_filter: parse_rtattr failed."); - return (-1); + if (mnl_attr_get_type(attr) != TCA_KIND) + continue; + + if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0) + { + ERROR ("netlink plugin: qos_filter_cb: TCA_KIND mnl_attr_validate failed."); + return MNL_CB_ERROR; + } + + kind = mnl_attr_get_str(attr); + break; } - if (attrs[TCA_KIND] == NULL) + if (kind == NULL) { - ERROR ("netlink plugin: qos_filter: attrs[TCA_KIND] == NULL"); + ERROR ("netlink plugin: qos_filter_cb: kind == NULL"); return (-1); } { /* The the ID */ uint32_t numberic_id; - numberic_id = msg->tcm_handle; + numberic_id = tm->tcm_handle; if (strcmp (tc_type, "filter") == 0) - numberic_id = msg->tcm_parent; + numberic_id = tm->tcm_parent; ssnprintf (tc_inst, sizeof (tc_inst), "%s-%x:%x", - (const char *) RTA_DATA (attrs[TCA_KIND]), + kind, numberic_id >> 16, numberic_id & 0x0000FFFF); } - DEBUG ("netlink plugin: qos_filter: got %s for %s (%i).", + DEBUG ("netlink plugin: qos_filter_cb: got %s for %s (%i).", tc_type, dev, msg->tcm_ifindex); - + if (check_ignorelist (dev, tc_type, tc_inst)) - return (0); + return MNL_CB_OK; #if HAVE_TCA_STATS2 - if (attrs[TCA_STATS2]) + mnl_attr_for_each (attr, nlh, sizeof (*tm)) { - struct rtattr *attrs_stats[TCA_STATS_MAX + 1]; - - memset (attrs_stats, '\0', sizeof (attrs_stats)); - parse_rtattr_nested (attrs_stats, TCA_STATS_MAX, attrs[TCA_STATS2]); + if (mnl_attr_get_type(attr) != TCA_STATS2) + continue; - if (attrs_stats[TCA_STATS_BASIC]) + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) { - struct gnet_stats_basic bs; - char type_instance[DATA_MAX_NAME_LEN]; + ERROR ("netlink plugin: qos_filter_cb: TCA_STATS2 mnl_attr_validate failed."); + return MNL_CB_ERROR; + } - ssnprintf (type_instance, sizeof (type_instance), "%s-%s", - tc_type, tc_inst); + mnl_attr_parse_nested(attr, qos_attr_cb, &bs); - memset (&bs, '\0', sizeof (bs)); - memcpy (&bs, RTA_DATA (attrs_stats[TCA_STATS_BASIC]), - MIN (RTA_PAYLOAD (attrs_stats[TCA_STATS_BASIC]), sizeof(bs))); + break; + } - submit_one (dev, "ipt_bytes", type_instance, bs.bytes); - submit_one (dev, "ipt_packets", type_instance, bs.packets); - } + if (bs != NULL) + { + char type_instance[DATA_MAX_NAME_LEN]; + + ssnprintf (type_instance, sizeof (type_instance), "%s-%s", + tc_type, tc_inst); + + submit_one (dev, "ipt_bytes", type_instance, bs->bytes); + submit_one (dev, "ipt_packets", type_instance, bs->packets); } #endif /* TCA_STATS2 */ -#if HAVE_TCA_STATS && HAVE_TCA_STATS2 - else -#endif + #if HAVE_TCA_STATS - if (attrs[TCA_STATS] != NULL) + mnl_attr_for_each (attr, nlh, sizeof (*tm)) + { + if (mnl_attr_get_type(attr) != TCA_STATS) + continue; + + if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*ts)) < 0) + { + ERROR ("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 failed."); + return MNL_CB_ERROR; + } + ts = mnl_attr_get_payload(attr); + break; + } + + if (bs == NULL && ts != NULL) { - struct tc_stats ts; char type_instance[DATA_MAX_NAME_LEN]; ssnprintf (type_instance, sizeof (type_instance), "%s-%s", tc_type, tc_inst); - memset(&ts, '\0', sizeof (ts)); - memcpy(&ts, RTA_DATA (attrs[TCA_STATS]), - MIN (RTA_PAYLOAD (attrs[TCA_STATS]), sizeof (ts))); - - submit_one (dev, "ipt_bytes", type_instance, ts.bytes); - submit_one (dev, "ipt_packets", type_instance, ts.packets); + submit_one (dev, "ipt_bytes", type_instance, ts->bytes); + submit_one (dev, "ipt_packets", type_instance, ts->packets); } #endif /* TCA_STATS */ -#if HAVE_TCA_STATS || HAVE_TCA_STATS2 - else + +#if !(HAVE_TCA_STATS && HAVE_TCA_STATS2) + DEBUG ("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor " + "TCA_STATS."); #endif - { - DEBUG ("netlink plugin: qos_filter: Have neither TCA_STATS2 nor " - "TCA_STATS."); - } - return (0); -} /* int qos_filter */ + return MNL_CB_OK; +} /* int qos_filter_cb */ static int ir_config (const char *key, const char *value) { @@ -526,9 +576,7 @@ static int ir_config (const char *key, const char *value) } else { - if ((strcasecmp (fields[0], "yes") == 0) - || (strcasecmp (fields[0], "true") == 0) - || (strcasecmp (fields[0], "on") == 0)) + if (IS_TRUE (fields[0])) ir_ignorelist_invert = 0; else ir_ignorelist_invert = 1; @@ -543,11 +591,16 @@ static int ir_config (const char *key, const char *value) static int ir_init (void) { - memset (&rth, '\0', sizeof (rth)); + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + { + ERROR ("netlink plugin: ir_init: mnl_socket_open failed."); + return (-1); + } - if (rtnl_open (&rth, 0) != 0) + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - ERROR ("netlink plugin: ir_init: rtnl_open failed."); + ERROR ("netlink plugin: ir_init: mnl_socket_bind failed."); return (-1); } @@ -556,33 +609,50 @@ static int ir_init (void) static int ir_read (void) { - struct ifinfomsg im; - struct tcmsg tm; - int ifindex; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int ret; + unsigned int seq, portid; static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER }; static const char *type_name[] = { "qdisc", "class", "filter" }; - memset (&im, '\0', sizeof (im)); - im.ifi_type = AF_UNSPEC; + portid = mnl_socket_get_portid(nl); - if (rtnl_dump_request (&rth, RTM_GETLINK, &im, sizeof (im)) < 0) + 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 (*rt)); + rt->rtgen_family = AF_PACKET; + + if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0) { - ERROR ("netlink plugin: ir_read: rtnl_dump_request failed."); + ERROR ("netlink plugin: ir_read: rtnl_wilddump_request failed."); return (-1); } - if (rtnl_dump_filter (&rth, link_filter, /* arg1 = */ NULL, - NULL, NULL) != 0) + ret = mnl_socket_recvfrom (nl, buf, sizeof (buf)); + while (ret > 0) + { + ret = mnl_cb_run (buf, ret, seq, portid, link_filter_cb, NULL); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom (nl, buf, sizeof(buf)); + } + if (ret < 0) { - ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed."); + ERROR ("netlink plugin: ir_read: mnl_socket_recvfrom failed."); return (-1); } - /* `link_filter' will update `iflist' which is used here to iterate over all - * interfaces. */ + /* `link_filter_cb' will update `iflist' which is used here to iterate + * over all interfaces. */ for (ifindex = 0; (size_t) ifindex < iflist_len; ifindex++) { + struct tcmsg *tm; + int ifindex; size_t type_index; if (iflist[ifindex] == NULL) @@ -600,22 +670,34 @@ static int ir_read (void) DEBUG ("netlink plugin: ir_read: querying %s from %s (%i).", type_name[type_index], iflist[ifindex], ifindex); - memset (&tm, '\0', sizeof (tm)); - tm.tcm_family = AF_UNSPEC; - tm.tcm_ifindex = ifindex; + nlh = mnl_nlmsg_put_header (buf); + nlh->nlmsg_type = type_id[type_index]; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time (NULL); + tm = mnl_nlmsg_put_extra_header (nlh, sizeof (*tm)); + tm->tcm_family = AF_PACKET; + tm->tcm_ifindex = ifindex; - if (rtnl_dump_request (&rth, type_id[type_index], &tm, sizeof (tm)) < 0) + if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0) { - ERROR ("netlink plugin: ir_read: rtnl_dump_request failed."); + ERROR ("netlink plugin: ir_read: mnl_socket_sendto failed."); continue; } - if (rtnl_dump_filter (&rth, qos_filter, (void *) &ifindex, - NULL, NULL) != 0) + ret = mnl_socket_recvfrom (nl, buf, sizeof (buf)); + while (ret > 0) { - ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed."); - continue; + ret = mnl_cb_run (buf, ret, seq, portid, qos_filter_cb, &ifindex); + if (ret <= MNL_CB_STOP) + break; + ret = mnl_socket_recvfrom (nl, buf, sizeof (buf)); } + if (ret < 0) + { + ERROR ("netlink plugin: ir_read:mnl_socket_recvfrom failed."); + continue; + } + } /* for (type_index) */ } /* for (if_index) */ @@ -624,12 +706,12 @@ static int ir_read (void) static int ir_shutdown (void) { - if ((rth.fd != 0) || (rth.seq != 0) || (rth.dump != 0)) + if (nl) { - rtnl_close(&rth); - memset (&rth, '\0', sizeof (rth)); + mnl_socket_close(nl); + nl = NULL; } - + return (0); } /* int ir_shutdown */