collectd: add snmp6 interface
authorNick Hainke <vincent@systemli.org>
Fri, 11 Dec 2020 20:53:02 +0000 (21:53 +0100)
committerHannu Nyman <hannu.nyman@iki.fi>
Sat, 9 Jan 2021 08:12:20 +0000 (10:12 +0200)
Add collectd plugin allowing to collect IPv6 interface statistics.

Signed-off-by: Nick Hainke <vincent@systemli.org>
utils/collectd/Makefile
utils/collectd/patches/931-snmp6-add-ipv6-statistics.patch [new file with mode: 0644]

index 01cc944e1029bcbc453560d25c5b1974ca326ee6..040e58f5c04974914be04d3924a46e810d3944b4 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=collectd
 PKG_VERSION:=5.12.0
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=https://collectd.org/files/ \
@@ -185,6 +185,7 @@ COLLECTD_PLUGINS_SELECTED:= \
        rrdtool \
        sensors \
        snmp \
+       snmp6 \
        syslog \
        table \
        tail \
@@ -450,6 +451,7 @@ $(eval $(call BuildPlugin,routeros,MikroTik RouterOS input,routeros,+PACKAGE_col
 $(eval $(call BuildPlugin,rrdtool,RRDtool output,rrdtool,+PACKAGE_collectd-mod-rrdtool:librrd1))
 $(eval $(call BuildPlugin,sensors,lm_sensors input,sensors,+PACKAGE_collectd-mod-sensors:libsensors))
 $(eval $(call BuildPlugin,snmp,SNMP input,snmp,+PACKAGE_collectd-mod-snmp:libnetsnmp))
+$(eval $(call BuildPlugin,snmp6,snmp6 input,snmp6,))
 $(eval $(call BuildPlugin,syslog,syslog output,syslog,))
 $(eval $(call BuildPlugin,tail,tail input,tail,))
 $(eval $(call BuildPlugin,tail-csv,tail CSV input,tail_csv,))
diff --git a/utils/collectd/patches/931-snmp6-add-ipv6-statistics.patch b/utils/collectd/patches/931-snmp6-add-ipv6-statistics.patch
new file mode 100644 (file)
index 0000000..ed27502
--- /dev/null
@@ -0,0 +1,239 @@
+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.
++*/
++
++#if HAVE_LINUX_IF_H
++#include <linux/if.h>
++#elif HAVE_NET_IF_H
++#include <net/if.h>
++#endif
++
++#include <ifaddrs.h>
++
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include <errno.h>
++#include <stdbool.h>
++#include <stdio.h>
++#include <sys/types.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;
++struct ifaddrs *if_list;
++
++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},
++  };
++
++  if (ignorelist_match(ignorelist, dev) != 0)
++    return;
++
++  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[16];
++  int numfields;
++  int currline = 0;
++  derive_t data[76];
++  char procpath[1024];
++  int offset = 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("interface plugin: fopen: %s", STRERRNO);
++    return -1;
++  }
++
++  while (fgets(buffer, 1024, fh) != NULL) {
++    numfields = strsplit(buffer, fields, 16);
++
++    if (numfields < 2)
++      continue;
++
++    data[currline++] = atoll(fields[1]);
++  }
++
++  fclose(fh);
++
++  if (currline < 25) {
++    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
++  if (getifaddrs(&if_list) != 0)
++    return -1;
++
++  for (struct ifaddrs *if_ptr = if_list; if_ptr != NULL;
++       if_ptr = if_ptr->ifa_next) {
++    snmp_read(if_ptr->ifa_name);
++  }
++  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