| From: Nick Hainke <vincent@systemli.org> |
| Date: Mon, 7 Dec 2020 19:29:54 +0100 |
| Subject: [PATCH] snmp6: add ipv6 statistics |
| |
| ChangeLog: snmp6 plugin: Add plugin for parsing IPv6 statistics |
| |
| We would like to have pure ipv6 interface statistics. To get them, |
| we parse the snmp6 interface. |
| |
| Signed-off-by: Nick Hainke <vincent@systemli.org> |
| --- |
| Makefile.am | 8 +++ |
| README | 4 ++ |
| configure.ac | 2 + |
| src/collectd.conf.in | 6 ++ |
| src/snmp6.c | 135 +++++++++++++++++++++++++++++++++++++++++++ |
| src/types.db | 2 + |
| 6 files changed, 157 insertions(+) |
| create mode 100644 src/snmp6.c |
| |
| --- a/Makefile.am |
| +++ b/Makefile.am |
| @@ -1964,6 +1964,14 @@ TESTS += test_plugin_snmp_agent |
| |
| endif |
| |
| +if BUILD_PLUGIN_SNMP6 |
| +pkglib_LTLIBRARIES += snmp6.la |
| +snmp6_la_SOURCES = src/snmp6.c |
| +snmp6_la_CFLAGS = $(AM_CFLAGS) |
| +snmp6_la_LDFLAGS = $(PLUGIN_LDFLAGS) |
| +snmp6_la_LIBADD = libignorelist.la |
| +endif # BUILD_PLUGIN_SNMP6 |
| + |
| if BUILD_PLUGIN_STATSD |
| pkglib_LTLIBRARIES += statsd.la |
| statsd_la_SOURCES = src/statsd.c |
| --- a/README |
| +++ b/README |
| @@ -422,6 +422,10 @@ Features |
| network devices such as switches, routers, thermometers, rack monitoring |
| servers, etc. See collectd-snmp(5). |
| |
| + - snmp6 |
| + Read values from SNMP6 (Simple Network Management Protocol). Supports pure |
| + IPv6 interface statistics. |
| + |
| - statsd |
| Acts as a StatsD server, reading values sent over the network from StatsD |
| clients and calculating rates and other aggregates out of these values. |
| --- a/configure.ac |
| +++ b/configure.ac |
| @@ -7162,6 +7162,7 @@ AC_PLUGIN([slurm], [$with_ |
| AC_PLUGIN([smart], [$plugin_smart], [SMART statistics]) |
| AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin]) |
| AC_PLUGIN([snmp_agent], [$with_libnetsnmpagent], [SNMP agent plugin]) |
| +AC_PLUGIN([snmp6], [yes], [IPv6 Interface traffic statistics via snmp6]) |
| AC_PLUGIN([statsd], [yes], [StatsD plugin]) |
| AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics]) |
| AC_PLUGIN([synproxy], [$plugin_synproxy], [Synproxy stats plugin]) |
| @@ -7611,6 +7612,7 @@ AC_MSG_RESULT([ slurm . . . . . . . . |
| AC_MSG_RESULT([ smart . . . . . . . . $enable_smart]) |
| AC_MSG_RESULT([ snmp . . . . . . . . $enable_snmp]) |
| AC_MSG_RESULT([ snmp_agent . . . . . $enable_snmp_agent]) |
| +AC_MSG_RESULT([ snmp6 . . . . . . . . $enable_snmp6]) |
| AC_MSG_RESULT([ statsd . . . . . . . $enable_statsd]) |
| AC_MSG_RESULT([ swap . . . . . . . . $enable_swap]) |
| AC_MSG_RESULT([ synproxy . . . . . . $enable_synproxy]) |
| --- a/src/collectd.conf.in |
| +++ b/src/collectd.conf.in |
| @@ -207,6 +207,7 @@ |
| #@BUILD_PLUGIN_SMART_TRUE@LoadPlugin smart |
| #@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp |
| #@BUILD_PLUGIN_SNMP_AGENT_TRUE@LoadPlugin snmp_agent |
| +#@BUILD_PLUGIN_SNMP6_TRUE@LoadPlugin snmp6 |
| #@BUILD_PLUGIN_STATSD_TRUE@LoadPlugin statsd |
| #@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap |
| #@BUILD_PLUGIN_SYSEVENT_TRUE@LoadPlugin sysevent |
| @@ -1718,6 +1719,11 @@ |
| # </Table> |
| #</Plugin> |
| |
| +#<Plugin snmp6> |
| +# Interface "eth0" |
| +# IgnoreSelected false |
| +#</Plugin> |
| + |
| #<Plugin statsd> |
| # Host "::" |
| # Port "8125" |
| --- /dev/null |
| +++ b/src/snmp6.c |
| @@ -0,0 +1,135 @@ |
| +/* |
| + This Plugin is based opn the interface.c Plugin. |
| +*/ |
| +#include <stdint.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#include <errno.h> |
| +#include <stdbool.h> |
| +#include <stdio.h> |
| + |
| +#include <net/if.h> |
| +#include <sys/types.h> |
| +#include <ifaddrs.h> |
| + |
| +#include "plugin.h" |
| +#include "utils/cmds/putval.h" |
| +#include "utils/common/common.h" |
| +#include "utils/ignorelist/ignorelist.h" |
| + |
| +static const char *config_keys[] = { |
| + "Interface", |
| + "IgnoreSelected", |
| +}; |
| +static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); |
| + |
| +static ignorelist_t *ignorelist; |
| + |
| +static int snmp6_config(const char *key, const char *value) { |
| + if (ignorelist == NULL) |
| + ignorelist = ignorelist_create(/* invert = */ 1); |
| + |
| + if (strcasecmp(key, "Interface") == 0) { |
| + ignorelist_add(ignorelist, value); |
| + } else if (strcasecmp(key, "IgnoreSelected") == 0) { |
| + int invert = 1; |
| + if (IS_TRUE(value)) |
| + invert = 0; |
| + ignorelist_set_invert(ignorelist, invert); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +/* Copied from interface.c */ |
| +static void snmp6_submit(const char *dev, const char *type, derive_t rx, |
| + derive_t tx) { |
| + value_list_t vl = VALUE_LIST_INIT; |
| + value_t values[] = { |
| + {.derive = rx}, |
| + {.derive = tx}, |
| + }; |
| + |
| + vl.values = values; |
| + vl.values_len = STATIC_ARRAY_SIZE(values); |
| + sstrncpy(vl.plugin, "snmp6", sizeof(vl.plugin)); |
| + sstrncpy(vl.plugin_instance, dev, sizeof(vl.plugin_instance)); |
| + sstrncpy(vl.type, type, sizeof(vl.type)); |
| + |
| + plugin_dispatch_values(&vl); |
| +} /* void if_submit */ |
| + |
| +int snmp_read(char *ifname) { |
| + FILE *fh; |
| + char buffer[1024]; |
| + char *fields[2]; |
| + int numfields; |
| + int currline = 0; |
| + derive_t data[76]; |
| + char procpath[1024]; |
| + int offset = 0; |
| + |
| + if (ignorelist_match(ignorelist, ifname) != 0) |
| + return 0; |
| + |
| + if (strncmp("all", ifname, strlen("all")) == 0) { |
| + snprintf(procpath, 1024, "/proc/net/snmp6"); |
| + offset = 1; |
| + } else { |
| + snprintf(procpath, 1024, "/proc/net/dev_snmp6/%s", ifname); |
| + } |
| + |
| + if ((fh = fopen(procpath, "r")) == NULL) { |
| + WARNING("snmp6 plugin: try opening %s : fopen: %s", procpath, STRERRNO); |
| + return -1; |
| + } |
| + |
| + while (fgets(buffer, 1024, fh) != NULL) { |
| + numfields = strsplit(buffer, fields, 2); |
| + |
| + if (numfields < 2) |
| + return -1; |
| + |
| + data[currline++] = atoll(fields[1]); |
| + } |
| + |
| + fclose(fh); |
| + |
| + if (currline < 28) { |
| + return -1; |
| + } |
| + |
| + snmp6_submit(ifname, "if_octets", data[23 - offset], data[24 - offset]); |
| + snmp6_submit(ifname, "if_octets_mcast", data[25 - offset], data[26 - offset]); |
| + snmp6_submit(ifname, "if_octets_bcast", data[27 - offset], data[28 - offset]); |
| + return 0; |
| +} |
| + |
| +int read_all_interfaces(void) { |
| +#ifndef HAVE_IFADDRS_H |
| + return -1; |
| +#else |
| + |
| + // getifaddrs is not working all the time (e.g. wireguard interfaces) |
| + // instead we use if_nameindex() syscall as suggested in: |
| + // https://stackoverflow.com/a/45796495/8474618 |
| + struct if_nameindex *if_nidxs, *intf; |
| + |
| + if_nidxs = if_nameindex(); |
| + |
| + if (if_nidxs != NULL) { |
| + for (intf = if_nidxs; intf->if_index != 0 || intf->if_name != NULL; intf++) { |
| + snmp_read(intf->if_name); |
| + } |
| + if_freenameindex(if_nidxs); |
| + } |
| + |
| + snmp_read("all"); |
| + return 0; |
| +#endif |
| +} |
| + |
| +void module_register(void) { |
| + plugin_register_config("snmp6", snmp6_config, config_keys, config_keys_num); |
| + plugin_register_read("snmp6", read_all_interfaces); |
| +} /* void module_register */ |
| --- a/src/types.db |
| +++ b/src/types.db |
| @@ -132,6 +132,8 @@ if_dropped rx:DERIVE:0:U, t |
| if_errors rx:DERIVE:0:U, tx:DERIVE:0:U |
| if_multicast value:DERIVE:0:U |
| if_octets rx:DERIVE:0:U, tx:DERIVE:0:U |
| +if_octets_mcast rx:DERIVE:0:U, tx:DERIVE:0:U |
| +if_octets_bcast rx:DERIVE:0:U, tx:DERIVE:0:U |
| if_packets rx:DERIVE:0:U, tx:DERIVE:0:U |
| if_rx_dropped value:DERIVE:0:U |
| if_rx_errors value:DERIVE:0:U |