From: Florian Forster Date: Wed, 13 May 2009 14:22:22 +0000 (+0200) Subject: olsrd plugin: Add a plugin for the “optimized link-state routing daemon”. X-Git-Tag: collectd-4.8.0~110^2~5 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=7a12fd28b4cbbf5c9e0967710373de002b8f9ad9;p=collectd.git olsrd plugin: Add a plugin for the “optimized link-state routing daemon”. This daemon is often used to route in mesh networks, i. e. highly dynamic wireless networks. The plugin connects to the TCP port opened by the `txtinfo' plugin of olsrd and parses its output. Available statistics are: * links: How many direct connections exist and which quality do they have? * routes: Number of nets/hosts a node can contact and the average metric to each network. * topology: Link quality of the entire topology known to the router. Each statistic can be configured to be collected as * No: Values are ignored. * Summary: Only a fixed number of statistics are collected. This number does *not* depend on the number of nodes in the network. * Detail: One (or more) values for each link/node, i. e. the number of values depends on the number of hosts and may be *huge*. No documentation exists yet. The plugin has not yet been tested. --- diff --git a/configure.in b/configure.in index c9d76299..d0edfa80 100644 --- a/configure.in +++ b/configure.in @@ -3646,6 +3646,7 @@ AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications]) AC_PLUGIN([notify_email], [$with_libesmtp], [Email notifier]) AC_PLUGIN([ntpd], [yes], [NTPd statistics]) AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics]) +AC_PLUGIN([olsrd], [yes], [olsrd statistics]) AC_PLUGIN([onewire], [$with_libowcapi], [OneWire sensor statistics]) AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics]) AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin]) @@ -3931,6 +3932,7 @@ Configuration: notify_email . . . . $enable_notify_email ntpd . . . . . . . . $enable_ntpd nut . . . . . . . . . $enable_nut + olsrd . . . . . . . . $enable_olsrd onewire . . . . . . . $enable_onewire openvpn . . . . . . . $enable_openvpn oracle . . . . . . . $enable_oracle diff --git a/src/Makefile.am b/src/Makefile.am index 8b5fa546..1a4cea6e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -653,6 +653,18 @@ collectd_LDADD += "-dlopen" nut.la collectd_DEPENDENCIES += nut.la endif +if BUILD_PLUGIN_OLSRD +pkglib_LTLIBRARIES += olsrd.la +olsrd_la_SOURCES = olsrd.c +olsrd_la_LDFLAGS = -module -avoid-version +olsrd_la_LIBADD = +if BUILD_WITH_LIBSOCKET +olsrd_la_LIBADD += -lsocket +endif +collectd_LDADD += "-dlopen" olsrd.la +collectd_DEPENDENCIES += olsrd.la +endif + if BUILD_PLUGIN_ONEWIRE pkglib_LTLIBRARIES += onewire.la onewire_la_SOURCES = onewire.c diff --git a/src/olsrd.c b/src/olsrd.c new file mode 100644 index 00000000..8b73214d --- /dev/null +++ b/src/olsrd.c @@ -0,0 +1,626 @@ +/** + * collectd - src/olsrd.c + * Copyright (C) 2009 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 + * Free Software Foundation; only version 2 of the License is applicable. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#include +#include +#include + +#define OLSRD_DEFAULT_NODE "localhost" +#define OLSRD_DEFAULT_SERVICE "2006" + +static const char *config_keys[] = +{ + "Host", + "Port", + "CollectLinks", + "CollectRoutes", + "CollectTopology" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static char *config_node = NULL; +static char *config_service = NULL; + +#define OLSRD_WANT_NOT 0 +#define OLSRD_WANT_SUMMARY 1 +#define OLSRD_WANT_DETAIL 2 +static int config_want_links = OLSRD_WANT_DETAIL; +static int config_want_routes = OLSRD_WANT_SUMMARY; +static int config_want_topology = OLSRD_WANT_SUMMARY; + +static const char *olsrd_get_node (void) /* {{{ */ +{ + if (config_node != NULL) + return (config_node); + return (OLSRD_DEFAULT_NODE); +} /* }}} const char *olsrd_get_node */ + +static const char *olsrd_get_service (void) /* {{{ */ +{ + if (config_service != NULL) + return (config_service); + return (OLSRD_DEFAULT_SERVICE); +} /* }}} const char *olsrd_get_service */ + +static void olsrd_set_node (const char *node) /* {{{ */ +{ + char *tmp; + if (node == NULL) + return; + tmp = strdup (node); + if (tmp == NULL) + return; + config_node = tmp; +} /* }}} void olsrd_set_node */ + +static void olsrd_set_service (const char *service) /* {{{ */ +{ + char *tmp; + if (service == NULL) + return; + tmp = strdup (service); + if (tmp == NULL) + return; + config_service = tmp; +} /* }}} void olsrd_set_service */ + +static void olsrd_set_detail (int *varptr, const char *detail, /* {{{ */ + const char *key) +{ + if (strcasecmp ("No", detail) == 0) + *varptr = OLSRD_WANT_NOT; + else if (strcasecmp ("Summary", detail) == 0) + *varptr = OLSRD_WANT_SUMMARY; + else if (strcasecmp ("Detail", detail) == 0) + *varptr = OLSRD_WANT_DETAIL; + else + { + ERROR ("olsrd plugin: Invalid argument given to the `%s' configuration " + "option: `%s'. Expected: `No', `Summary', or `Detail'.", + key, detail); + } +} /* }}} void olsrd_set_detail */ + +/* Strip trailing newline characters. Returns length of string. */ +static size_t strchomp (char *buffer) /* {{{ */ +{ + size_t buffer_len; + + buffer_len = strlen (buffer); + while ((buffer_len > 0) + && ((buffer[buffer_len - 1] == '\r') + || (buffer[buffer_len - 1] == '\n'))) + { + buffer_len--; + buffer[buffer_len] = 0; + } + + return (buffer_len); +} /* }}} size_t strchomp */ + +static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */ +{ + size_t i; + char *ptr; + char *saveptr; + + i = 0; + ptr = string; + saveptr = NULL; + while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL) + { + ptr = NULL; + i++; + + if (i >= size) + break; + } + + return (i); +} /* }}} size_t strtabsplit */ + +static FILE *olsrd_connect (void) /* {{{ */ +{ + struct addrinfo ai_hints; + struct addrinfo *ai_list, *ai_ptr; + int ai_return; + + FILE *fh; + + memset (&ai_hints, 0, sizeof (ai_hints)); + ai_hints.ai_flags = 0; +#ifdef AI_ADDRCONFIG + ai_hints.ai_flags |= AI_ADDRCONFIG; +#endif + ai_hints.ai_family = PF_UNSPEC; + ai_hints.ai_socktype = SOCK_STREAM; + ai_hints.ai_protocol = IPPROTO_TCP; + + ai_list = NULL; + ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (), + &ai_hints, &ai_list); + if (ai_return != 0) + { + ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s", + olsrd_get_node (), olsrd_get_service (), + gai_strerror (ai_return)); + return (NULL); + } + + fh = NULL; + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + int fd; + int status; + char errbuf[1024]; + + fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); + if (fd < 0) + { + ERROR ("olsrd plugin: socket failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + continue; + } + + status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); + if (status != 0) + { + ERROR ("olsrd plugin: connect failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + close (fd); + continue; + } + + fh = fdopen (fd, "r+"); + if (fh == NULL) + { + ERROR ("olsrd plugin: fdopen failed."); + close (fd); + continue; + } + + break; + } /* for (ai_ptr) */ + + freeaddrinfo (ai_list); + + return (fh); +} /* }}} FILE *olsrd_connect */ + +static int olsrd_cb_ignore (int lineno, /* {{{ */ + size_t fields_num, char **fields) +{ + return (0); +} /* }}} int olsrd_cb_ignore */ + +static int olsrd_cb_links (int lineno, /* {{{ */ + size_t fields_num, char **fields) +{ + /* Fields: + * 0 = Local IP + * 1 = Remote IP + * 2 = Hyst. + * 3 = LQ + * 4 = NLQ + * 5 = Cost */ + + static uint32_t links_num; + static double lq_sum; + static uint32_t lq_num; + static double nlq_sum; + static uint32_t nlq_num; + + char link_name[DATA_MAX_NAME_LEN]; + double lq; /* tx */ + double nlq; /* rx */ + + char *endptr; + + if (config_want_links == OLSRD_WANT_NOT) + return (0); + + /* Special handling of the first line. */ + if (lineno <= 0) + { + links_num = 0; + lq_sum = 0.0; + lq_num = 0; + nlq_sum = 0.0; + nlq_num = 0; + + return (0); + } + + /* Special handling of the last line. */ + if (fields_num == 0) + { + DEBUG ("olsrd plugin: fields_num = %"PRIu32";", links_num); + + lq = NAN; + if (lq_num > 0) + lq = lq_sum / ((double) lq_num); + DEBUG ("olsrd plugin: Average LQ: %g", lq); + + nlq = NAN; + if (nlq_num > 0) + nlq = nlq_sum / ((double) nlq_num); + DEBUG ("olsrd plugin: Average NLQ: %g", nlq); + + return (0); + } + + if (fields_num != 6) + return (-1); + + links_num++; + + memset (link_name, 0, sizeof (link_name)); + ssnprintf (link_name, sizeof (link_name), "%s-%s", fields[0], fields[1]); + + errno = 0; + endptr = NULL; + lq = strtod (fields[3], &endptr); + if ((errno != 0) || (endptr == fields[3])) + { + ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]); + } + else + { + if (!isnan (lq)) + { + lq_sum += lq; + lq_num++; + } + + if (config_want_links == OLSRD_WANT_DETAIL) + { + DEBUG ("olsrd plugin: %s: LQ = %g.", link_name, lq); + } + } + + errno = 0; + endptr = NULL; + nlq = strtod (fields[4], &endptr); + if ((errno != 0) || (endptr == fields[4])) + { + ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]); + } + else + { + if (!isnan (nlq)) + { + nlq_sum += nlq; + nlq_num++; + } + + if (config_want_links == OLSRD_WANT_DETAIL) + { + DEBUG ("olsrd plugin: %s: NLQ = %g.", link_name, lq); + } + } + + return (0); +} /* }}} int olsrd_cb_links */ + +static int olsrd_cb_routes (int lineno, /* {{{ */ + size_t fields_num, char **fields) +{ + /* Fields: + * 0 = Destination + * 1 = Gateway IP + * 2 = Metric + * 3 = ETX + * 4 = Interface */ + + static uint32_t metric_sum; + static uint32_t metric_num; + + static double etx_sum; + static uint32_t etx_num; + + uint32_t metric; + double etx; + char *endptr; + + if (config_want_routes == OLSRD_WANT_NOT) + return (0); + + /* Special handling of the first line */ + if (lineno <= 0) + { + metric_num = 0; + metric_sum = 0; + return (0); + } + + /* Special handling after the last line */ + if (fields_num == 0) + { + double metric_avg; + + metric_avg = NAN; + if (metric_num > 0) + metric_avg = ((double) metric_sum) / ((double) metric_num); + + etx = NAN; + if (etx_num > 0) + etx = etx_sum / ((double) etx_sum); + + DEBUG ("olsrd plugin: Number of routes: %"PRIu32"; Average metric: %g", + metric_num, metric_avg); + return (0); + } + + if (fields_num != 5) + return (-1); + + errno = 0; + endptr = NULL; + metric = (uint32_t) strtoul (fields[2], &endptr, 0); + if ((errno != 0) || (endptr == fields[2])) + { + ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]); + } + else + { + metric_num++; + metric_sum += metric; + + if (config_want_routes == OLSRD_WANT_DETAIL) + { + DEBUG ("olsrd plugin: Route with metric %"PRIu32".", metric); + } + } + + errno = 0; + endptr = NULL; + etx = strtod (fields[3], &endptr); + if ((errno != 0) || (endptr == fields[3])) + { + ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]); + } + else + { + if (!isnan (etx)) + { + etx_sum += etx; + etx_num++; + } + + if (config_want_routes == OLSRD_WANT_DETAIL) + { + DEBUG ("olsrd plugin: Route with ETX %g.", etx); + } + } + + return (0); +} /* }}} int olsrd_cb_routes */ + +static int olsrd_cb_topology (int lineno, /* {{{ */ + size_t fields_num, char **fields) +{ + /* Fields: + * 0 = Dest. IP + * 1 = Last hop IP + * 2 = LQ + * 3 = NLQ + * 4 = Cost */ + + static double lq_sum; + static uint32_t lq_num; + + static uint32_t links_num; + + char link_name[DATA_MAX_NAME_LEN]; + double lq; + char *endptr; + + if (config_want_topology == OLSRD_WANT_NOT) + return (0); + + /* Special handling of the first line */ + if (lineno <= 0) + { + lq_sum = 0.0; + lq_num = 0; + links_num = 0; + + return (0); + } + + /* Special handling after the last line */ + if (fields_num == 0) + { + lq = NAN; + if (lq_num > 0) + lq = lq_sum / ((double) lq_sum); + + DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num); + DEBUG ("olsrd plugin: Average link quality: %g", lq); + + return (0); + } + + if (fields_num != 5) + return (-1); + + memset (link_name, 0, sizeof (link_name)); + ssnprintf (link_name, sizeof (link_name), "%s-%s", fields[0], fields[1]); + links_num++; + + errno = 0; + endptr = NULL; + lq = strtod (fields[2], &endptr); + if ((errno != 0) || (endptr == fields[2])) + { + ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]); + } + else + { + if (!isnan (lq)) + { + lq_sum += lq; + lq_num++; + } + + if (config_want_topology == OLSRD_WANT_DETAIL) + { + DEBUG ("olsrd plugin: link_name = %s; lq = %g;", link_name, lq); + } + } + + if (config_want_topology == OLSRD_WANT_DETAIL) + { + double nlq; + + errno = 0; + endptr = NULL; + nlq = strtod (fields[3], &endptr); + if ((errno != 0) || (endptr == fields[3])) + { + ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]); + } + else + { + DEBUG ("olsrd plugin: link_name = %s; nlq = %g;", link_name, nlq); + } + } + + return (0); +} /* }}} int olsrd_cb_topology */ + +static int olsrd_read_table (FILE *fh, /* {{{ */ + int (*callback) (int lineno, size_t fields_num, char **fields)) +{ + char buffer[1024]; + size_t buffer_len; + + char *fields[32]; + size_t fields_num; + + int lineno; + + lineno = 0; + while (fgets (buffer, sizeof (buffer), fh) != NULL) + { + /* An empty line ends the table. */ + buffer_len = strchomp (buffer); + if (buffer_len <= 0) + { + (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL); + break; + } + + fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields)); + + (*callback) (lineno, fields_num, fields); + lineno++; + } /* while (fgets) */ + + return (0); +} /* }}} int olsrd_read_table */ + +static int olsrd_config (const char *key, const char *value) /* {{{ */ +{ + if (strcasecmp ("Host", key) == 0) + olsrd_set_node (value); + else if (strcasecmp ("Port", key) == 0) + olsrd_set_service (value); + else if (strcasecmp ("CollectLinks", key) == 0) + olsrd_set_detail (&config_want_links, value, key); + else if (strcasecmp ("CollectRoutes", key) == 0) + olsrd_set_detail (&config_want_routes, value, key); + else if (strcasecmp ("CollectTopology", key) == 0) + olsrd_set_detail (&config_want_topology, value, key); + else + { + ERROR ("olsrd plugin: Unknown configuration option given: %s", key); + return (-1); + } + + return (0); +} /* }}} int olsrd_config */ + +static int olsrd_read (void) /* {{{ */ +{ + FILE *fh; + char buffer[1024]; + size_t buffer_len; + + fh = olsrd_connect (); + if (fh == NULL) + return (-1); + + while (fgets (buffer, sizeof (buffer), fh) != NULL) + { + buffer_len = strchomp (buffer); + if (buffer_len <= 0) + continue; + + if (strcmp ("Table: Links", buffer) == 0) + olsrd_read_table (fh, olsrd_cb_links); + else if (strcmp ("Table: Neighbors", buffer) == 0) + olsrd_read_table (fh, olsrd_cb_ignore); + else if (strcmp ("Table: Topology", buffer) == 0) + olsrd_read_table (fh, olsrd_cb_topology); + else if (strcmp ("Table: HNA", buffer) == 0) + olsrd_read_table (fh, olsrd_cb_ignore); + else if (strcmp ("Table: MID", buffer) == 0) + olsrd_read_table (fh, olsrd_cb_ignore); + else if (strcmp ("Table: Routes", buffer) == 0) + olsrd_read_table (fh, olsrd_cb_routes); + else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0) + || (strcmp ("Content-type: text/plain", buffer) == 0)) + { + /* ignore */ + } + else + { + DEBUG ("olsrd plugin: Unable to handle line: %s", buffer); + } + } /* while (fgets) */ + + fclose (fh); + + return (0); +} /* }}} int olsrd_read */ + +static int olsrd_shutdown (void) /* {{{ */ +{ + sfree (config_node); + sfree (config_service); + + return (0); +} /* }}} int olsrd_shutdown */ + +void module_register (void) +{ + plugin_register_config ("olsrd", olsrd_config, + config_keys, config_keys_num); + plugin_register_read ("olsrd", olsrd_read); + plugin_register_shutdown ("olsrd", olsrd_shutdown); +} /* void module_register */ + +/* vim: set sw=2 sts=2 et fdm=marker : */