From fa0b947c29d14d2883043d560900270e35cb84fd Mon Sep 17 00:00:00 2001 From: Cyril Feraudet Date: Tue, 4 Oct 2011 15:00:04 +0200 Subject: [PATCH] ethstat plugin: Collect performance statistics from NICs. Hi all, For my needs at work I wrote a plugin for collectd and I wish to share it with you. This plugins, temporary called ethstat and wrote in C, collect lot of performance counter about network interface as "ethtool -S ethX" do by asking NIC driver module. At the moment this plugin work only under Linux. Any help would be great for make it working under Solaris, AIX and co ... If you think this plugin may be included to collectd sources, what is the procedure to do it ? Regards, Cyril Feraudet To be added to configuration file to make it work : LoadPlugin ethstat Iface "eth0" #Iface "eth1" #Iface "eth2" #Iface "eth3" Signed-off-by: Florian Forster --- configure.in | 2 + src/Makefile.am | 8 +++ src/ethstat.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ethstat.h | 78 +++++++++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 src/ethstat.c create mode 100644 src/ethstat.h diff --git a/configure.in b/configure.in index 0a0a8f8c..0d992e1d 100644 --- a/configure.in +++ b/configure.in @@ -4745,6 +4745,7 @@ AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics]) AC_PLUGIN([dns], [$with_libpcap], [DNS traffic analysis]) AC_PLUGIN([email], [yes], [EMail statistics]) AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics]) +AC_PLUGIN([ethstat], [yes], [Stats from NIC driver]) AC_PLUGIN([exec], [yes], [Execution of external programs]) AC_PLUGIN([filecount], [yes], [Count files in directories]) AC_PLUGIN([fscache], [$plugin_fscache], [fscache statistics]) @@ -5076,6 +5077,7 @@ Configuration: dns . . . . . . . . . $enable_dns email . . . . . . . . $enable_email entropy . . . . . . . $enable_entropy + ethstat . . . . . . . $enable_ethstat exec . . . . . . . . $enable_exec filecount . . . . . . $enable_filecount fscache . . . . . . . $enable_fscache diff --git a/src/Makefile.am b/src/Makefile.am index 05f94d9e..5c2eb35d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -378,6 +378,14 @@ collectd_LDADD += "-dlopen" exec.la collectd_DEPENDENCIES += exec.la endif +if BUILD_PLUGIN_ETHSTAT +pkglib_LTLIBRARIES += ethstat.la +ethstat_la_SOURCES = ethstat.c +ethstat_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" ethstat.la +collectd_DEPENDENCIES += ethstat.la +endif + if BUILD_PLUGIN_FILECOUNT pkglib_LTLIBRARIES += filecount.la filecount_la_SOURCES = filecount.c diff --git a/src/ethstat.c b/src/ethstat.c new file mode 100644 index 00000000..fbe52c27 --- /dev/null +++ b/src/ethstat.c @@ -0,0 +1,178 @@ +/** + * collectd - src/ethstat.c + * Copyright (C) 2011 Cyril Feraudet + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * 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: + * Cyril Feraudet + **/ + +#include +#include +#include +#include +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ethstat.h" + +# if KERNEL_LINUX + + +static int ethstat_config (const char *key, const char *value) +{ + if (strcasecmp (key, "Iface") == 0) { + ifacelist[ifacenumber] = malloc(strlen(value) + 1); + strcpy(ifacelist[ifacenumber++], value); + INFO("ethstat: Registred iface %s", value); + } + return (0); +} + + +static void ethstat_submit_value (char *devname, char *counter, unsigned long long value) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].counter = value; + + vl.values = values; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, devname, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, "derive", sizeof (vl.type)); + sstrncpy (vl.type_instance, counter, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} + + +static int getstats(char *devname, struct ifreq *ifr) { + int fd; + struct ethtool_drvinfo drvinfo; + struct ethtool_gstrings *strings; + struct ethtool_stats *stats; + unsigned int n_stats, sz_str, sz_stats, i; + int err; + + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + ERROR("ethstat - %s : Cannot get control socket", devname); + return 1; + } + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr->ifr_data = (caddr_t)&drvinfo; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + ERROR("ethstat - %s : Cannot get driver information", devname); + return 1; + } + + + n_stats = drvinfo.n_stats; + if (n_stats < 1) { + ERROR("ethstat - %s : No stats available", devname); + return 1; + } + + sz_str = n_stats * ETH_GSTRING_LEN; + sz_stats = n_stats * sizeof(u64); + + strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings)); + stats = calloc(1, sz_stats + sizeof(struct ethtool_stats)); + if (!strings || !stats) { + ERROR("ethstat - %s No memory available", devname); + return 1; + } + + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = ETH_SS_STATS; + strings->len = n_stats; + ifr->ifr_data = (caddr_t) strings; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + ERROR("ethstat - %s : Cannot get stats strings information", devname); + free(strings); + free(stats); + return 96; + } + + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = n_stats; + ifr->ifr_data = (caddr_t) stats; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + ERROR("ethstat - %s : Cannot get stats information", devname); + free(strings); + free(stats); + return 97; + } + + for (i = 0; i < n_stats; i++) { + DEBUG("ethstat - %s : %s: %llu", + devname, + &strings->data[i * ETH_GSTRING_LEN], + stats->data[i]); + ethstat_submit_value ( + devname, + (char*)&strings->data[i * ETH_GSTRING_LEN], + stats->data[i]); + } + free(strings); + free(stats); + + + return 0; +} + + +static int ethstat_read(void) +{ + struct ifreq ifr; + int i; + + for (i = 0 ; i < ifacenumber ; i++) { + DEBUG("ethstat - Processing : %s\n", ifacelist[i]); + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, ifacelist[i]); + getstats(ifacelist[i], &ifr); + } + return 0; +} + +void module_register (void) +{ + ifacelist = malloc(sizeof(char*)); + plugin_register_config ("ethstat", ethstat_config, + config_keys, config_keys_num); + plugin_register_read ("ethstat", ethstat_read); +} + +#endif diff --git a/src/ethstat.h b/src/ethstat.h new file mode 100644 index 00000000..d544db51 --- /dev/null +++ b/src/ethstat.h @@ -0,0 +1,78 @@ +/** + * collectd - src/ethstat.h + * Copyright (C) 2011 Cyril Feraudet + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * 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: + * Cyril Feraudet + **/ + +#define ETHTOOL_BUSINFO_LEN 32 +#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ +#define ETH_GSTRING_LEN 32 +#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ +#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ + +enum ethtool_stringset { + ETH_SS_TEST = 0, + ETH_SS_STATS, +}; + +typedef unsigned long long u64; +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned int __u32; +typedef unsigned long long __u64; + + +static const char *config_keys[] = +{ + "Iface" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static char **ifacelist; +static int ifacenumber = 0; +struct ethtool_drvinfo { + __u32 cmd; + char driver[32]; /* driver short name, "tulip", "eepro100" */ + char version[32]; /* driver version string */ + char fw_version[32]; /* firmware version string, if applicable */ + char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ + /* For PCI devices, use pci_name(pci_dev). */ + char reserved1[32]; + char reserved2[16]; + __u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ + __u32 testinfo_len; + __u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ + __u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ +}; + +struct ethtool_gstrings { + __u32 cmd; /* ETHTOOL_GSTRINGS */ + __u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ + __u32 len; /* number of strings in the string set */ + __u8 data[0]; +}; + +struct ethtool_stats { + __u32 cmd; /* ETHTOOL_GSTATS */ + __u32 n_stats; /* number of u64's being returned */ + __u64 data[0]; +}; + + + -- 2.11.0