summaryrefslogtreecommitdiff
path: root/agent/mibgroup/etherlike-mib/data_access/dot3stats_linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/etherlike-mib/data_access/dot3stats_linux.c')
-rw-r--r--agent/mibgroup/etherlike-mib/data_access/dot3stats_linux.c928
1 files changed, 928 insertions, 0 deletions
diff --git a/agent/mibgroup/etherlike-mib/data_access/dot3stats_linux.c b/agent/mibgroup/etherlike-mib/data_access/dot3stats_linux.c
new file mode 100644
index 0000000..00f4bc3
--- /dev/null
+++ b/agent/mibgroup/etherlike-mib/data_access/dot3stats_linux.c
@@ -0,0 +1,928 @@
+/*
+ * standard Net-SNMP includes
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+
+/*
+ * include our parent header
+ */
+#include "etherlike-mib/dot3StatsTable/dot3StatsTable.h"
+#include "etherlike-mib/dot3StatsTable/dot3StatsTable_data_access.h"
+#include "etherlike-mib/dot3StatsTable/ioctl_imp_common.h"
+
+/*
+ * @retval 0 success
+ * @retval -1 getifaddrs failed
+ * @retval -2 memory allocation failed
+ */
+
+struct ifname *
+dot3stats_interface_name_list_get (struct ifname *list_head, int *retval)
+{
+ struct ifaddrs *addrs = NULL, *p = NULL;
+ struct ifname *nameptr1=NULL, *nameptr2 = NULL;
+
+ DEBUGMSGTL(("access:dot3StatsTable:interface_name_list_get",
+ "called\n"));
+
+ if ((getifaddrs(&addrs)) < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_name_list_get",
+ "getifaddrs failed\n"));
+ snmp_log (LOG_ERR, "access:dot3StatsTable,interface_name_list_get, getifaddrs failed\n");
+ *retval = -1;
+ return NULL;
+ }
+
+ for (p = addrs; p; p = p->ifa_next) {
+
+ if (!list_head) {
+ if ( (list_head = (struct ifname *) malloc (sizeof(struct ifname))) < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_name_list_get",
+ "memory allocation failed\n"));
+ snmp_log (LOG_ERR, "access:dot3StatsTable,interface_name_list_get, memory allocation failed\n");
+ freeifaddrs(addrs);
+ *retval = -2;
+ return NULL;
+ }
+ memset(list_head, 0, sizeof (struct ifname));
+ strlcpy(list_head->name, p->ifa_name, IF_NAMESIZE);
+ continue;
+ }
+
+ for (nameptr1 = list_head; nameptr1; nameptr2 = nameptr1, nameptr1 = nameptr1->ifn_next)
+ if (!strncmp(p->ifa_name, nameptr1->name, IF_NAMESIZE))
+ break;
+
+ if (nameptr1)
+ continue;
+
+ if ( (nameptr2->ifn_next = (struct ifname *) malloc (sizeof(struct ifname))) < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_name_list_get",
+ "memory allocation failed\n"));
+ snmp_log (LOG_ERR, "access:dot3StatsTable,interface_name_list_get, memory allocation failed\n");
+ dot3stats_interface_name_list_free (list_head);
+ freeifaddrs(addrs);
+ *retval = -2;
+ return NULL;
+ }
+ nameptr2 = nameptr2->ifn_next;
+ memset(nameptr2, 0, sizeof (struct ifname));
+ strlcpy(nameptr2->name, p->ifa_name, IF_NAMESIZE);
+ continue;
+
+ }
+
+ freeifaddrs(addrs);
+ return list_head;
+}
+
+/*
+ * @retval 0 success
+ * @retval -1 invalid pointer
+ */
+
+int
+dot3stats_interface_name_list_free (struct ifname *list_head)
+{
+ struct ifname *nameptr1 = NULL, *nameptr2 = NULL;
+
+ DEBUGMSGTL(("access:dot3StatsTable:interface_name_list_free",
+ "called\n"));
+
+ if (!list_head) {
+ snmp_log (LOG_ERR, "access:dot3StatsTable:interface_name_list_free: invalid pointer list_head");
+ DEBUGMSGTL(("access:dot3StatsTable:interface_name_list_free",
+ "invalid pointer list_head\n"));
+ return -1;
+ }
+
+ for (nameptr1 = list_head; nameptr1; nameptr1 = nameptr2) {
+ nameptr2 = nameptr1->ifn_next;
+ free (nameptr1);
+ }
+
+ return 0;
+}
+
+/*
+ * @retval 0 : not found
+ * @retval !0 : ifIndex
+ */
+
+int
+dot3stats_interface_ioctl_ifindex_get (int fd, const char *name) {
+#ifndef SIOCGIFINDEX
+ return 0;
+#else
+ struct ifreq ifrq;
+ int rc = 0;
+
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_ifindex_get", "called\n"));
+
+ rc = _dot3Stats_ioctl_get(fd, SIOCGIFINDEX, &ifrq, name);
+ if (rc < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_ifindex_get",
+ "error on interface '%s'\n", name));
+ snmp_log (LOG_ERR, "access:dot3StatsTable:interface_ioctl_ifindex_get, error on interface '%s'\n", name);
+ return 0;
+
+ }
+
+ return ifrq.ifr_ifindex;
+#endif /* SIOCGIFINDEX */
+}
+
+# if HAVE_LINUX_RTNETLINK_H /* { NETLINK */
+/*
+ * The following code is based upon code I got from Stephen Hemminger
+ */
+#include <linux/rtnetlink.h>
+#include <errno.h>
+
+struct rtnl_handle {
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+struct ifstat_ent {
+ struct ifstat_ent *next;
+ const char *name;
+ int ifindex;
+ struct rtnl_link_stats stats;
+};
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *, struct nlmsghdr *n, void *);
+
+struct rtnl_dump_filter_arg
+{
+ rtnl_filter_t filter;
+ void *arg1;
+ rtnl_filter_t junk;
+ void *arg2;
+};
+
+static struct ifstat_ent *kern_db;
+static const int rcvbuf_size = 1024 * 1024;
+
+static int
+rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+
+ memset(rth, 0, sizeof(*rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (rth->fd < 0) {
+ snmp_log(LOG_ERR, "Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ snmp_log(LOG_ERR, "SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf_size,sizeof(rcvbuf_size)) < 0) {
+ snmp_log(LOG_ERR, "SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ snmp_log(LOG_ERR, "Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ snmp_log(LOG_ERR, "Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ snmp_log(LOG_ERR, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ snmp_log(LOG_ERR, "Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+static int
+rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+static void
+rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd != -1)
+ close(rth->fd);
+ rth->fd = -1;
+
+ return;
+}
+
+static int
+rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return send(rth->fd, (void*)&req, sizeof(req), 0);
+}
+
+static int
+parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len))
+ {
+ if (rta->rta_type <= max)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+
+ if (len)
+ snmp_log(LOG_ERR, "parse_rtattr: !!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+
+ return 0;
+}
+
+static int
+get_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *m, void *arg)
+{
+ struct ifinfomsg *ifi = NLMSG_DATA(m);
+ struct rtattr * tb[IFLA_MAX+1];
+ int len = m->nlmsg_len;
+ struct ifstat_ent *n;
+
+ if (m->nlmsg_type != RTM_NEWLINK)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0)
+ return -1;
+
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+ if (tb[IFLA_IFNAME] == NULL || tb[IFLA_STATS] == NULL)
+ return 0;
+
+ n = malloc(sizeof(*n));
+ memset(n, 0, sizeof(*n));
+
+ n->ifindex = ifi->ifi_index;
+ n->name = strdup(RTA_DATA(tb[IFLA_IFNAME]));
+ memcpy(&n->stats, RTA_DATA(tb[IFLA_STATS]), sizeof(n->stats));
+ n->next = kern_db;
+ kern_db = n;
+ return 0;
+}
+
+static int
+rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ const struct rtnl_dump_filter_arg *a;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ for (a = arg; a->filter; a++) {
+ struct nlmsghdr *h = (struct nlmsghdr*)buf;
+
+ while (NLMSG_OK(h, status)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (a->junk) {
+ err = a->junk(&nladdr, h, a->arg2);
+ if (err < 0)
+ return err;
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE)
+ return 0;
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = a->filter(&nladdr, h, a->arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, status);
+ }
+ } while (0);
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+static int
+rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2)
+{
+ const struct rtnl_dump_filter_arg a[2] = {
+ { .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
+ { .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL }
+ };
+
+ return rtnl_dump_filter_l(rth, a);
+}
+
+int
+_dot3Stats_netlink_get_errorcntrs(dot3StatsTable_rowreq_ctx *rowreq_ctx, const char *name)
+{
+ struct rtnl_handle rth;
+ struct ifstat_ent *ke;
+ int done;
+
+ if (rtnl_open(&rth, 0) < 0)
+ {
+ snmp_log(LOG_ERR, "_dot3Stats_netlink_get_errorcntrs: rtnl_open() failed\n");
+ return 1;
+ }
+
+ if (rtnl_wilddump_request(&rth, AF_INET, RTM_GETLINK) < 0)
+ {
+ snmp_log(LOG_ERR, "_dot3Stats_netlink_get_errorcntrs: Cannot send dump request");
+ rtnl_close(&rth);
+ return 1;
+ }
+
+ if (rtnl_dump_filter(&rth, get_nlmsg, NULL, NULL, NULL) < 0)
+ {
+ snmp_log(LOG_ERR, "_dot3Stats_netlink_get_errorcntrs: Dump terminated\n");
+ rtnl_close(&rth);
+ return 1;
+ }
+
+ rtnl_close(&rth);
+
+ /*
+ * Now scan kern_db for this if's data
+ * While doing so, we'll throw away the kern db.
+ */
+ done = 0;
+
+ while ((ke = kern_db) != NULL)
+ {
+ if (strcmp(ke->name, name) == 0)
+ {
+ dot3StatsTable_data *data = &rowreq_ctx->data;
+
+ snmp_log(LOG_ERR, "IFLA_STATS for %s\n", name);
+
+ data->dot3StatsFCSErrors = ke->stats.rx_crc_errors;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFCSERRORS_FLAG;
+
+ data->dot3StatsDeferredTransmissions = ke->stats.tx_dropped;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSDEFERREDTRANSMISSIONS_FLAG;
+
+ data->dot3StatsInternalMacTransmitErrors = ke->stats.tx_fifo_errors;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACTRANSMITERRORS_FLAG;
+
+ data->dot3StatsCarrierSenseErrors = ke->stats.tx_carrier_errors;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSCARRIERSENSEERRORS_FLAG;
+
+ data->dot3StatsFrameTooLongs = ke->stats.rx_frame_errors;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFRAMETOOLONGS_FLAG;
+
+ data->dot3StatsInternalMacReceiveErrors = ke->stats.rx_fifo_errors;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACRECEIVEERRORS_FLAG;
+
+ done = 1;
+ }
+ kern_db = ke->next;
+ free(ke);
+ }
+
+ return !done;
+}
+# else /* }{ */
+int
+_dot3Stats_netlink_get_errorcntrs(dot3StatsTable_rowreq_ctx *rowreq_ctx, const char *name)
+{
+ return 1;
+}
+# endif /* } */
+
+
+/*
+ * NAME: getulongfromsysclassnetstatistics
+ * PURPOSE: To get a single statistics value from /sys/class/net/<ifname>/statistics/<ctrname>
+ * ARGUMENTS: ifname: interface name
+ * ctrname: counter name
+ * valuep: where to store value
+ * RETURNS: 0 if value not available
+ * non-0 if value available
+ */
+static int
+getulongfromsysclassnetstatistics(const char *ifname, const char *ctrname, u_long *valuep)
+{
+ char path[256];
+ FILE *fp;
+ int rv;
+
+ if (ifname == NULL || ctrname == NULL || valuep == NULL)
+ return 0;
+
+ snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/%s", ifname, ctrname);
+ fp = fopen(path, "rt");
+ if (fp == NULL)
+ return 0;
+
+ rv = 1;
+ if (fscanf(fp, "%lu", valuep) != 1)
+ rv = 0;
+
+ fclose(fp);
+
+ return rv;
+}
+
+/*
+ * NAME: interface_dot3stats_get_errorcounters
+ * PURPOSE: To get ethernet error statistics
+ * ARGUMENTS: rowreq_ctx: where to store the value(s)
+ * name: interface name
+ * RETURNS: nothing. fields not set if data not available
+ */
+void
+interface_dot3stats_get_errorcounters (dot3StatsTable_rowreq_ctx *rowreq_ctx, const char *name)
+{
+ u_long value;
+ dot3StatsTable_data *data = &rowreq_ctx->data;
+ FILE *dev;
+ const char NETDEV_FILE[] = "/proc/net/dev";
+
+ if (_dot3Stats_netlink_get_errorcntrs(rowreq_ctx, name) == 0)
+ {
+ snmp_log(LOG_NOTICE, "interface_dot3stats_get_errorcounters: got data from IFLA_STATS\n");
+ return;
+ }
+
+ if ((dev = fopen(NETDEV_FILE, "r")) != NULL)
+ {
+ char line[256], *lp, *next;
+ size_t namelen = strlen(name);
+ unsigned int value;
+ unsigned int column;
+
+ while (fgets(line, sizeof(line), dev) != NULL)
+ {
+ /* br0:68395635 1038214 0 0 0 0 0 939411 25626606 90708 0 0 0 0 0 0 */
+ lp = line;
+ while (*lp == ' ' || *lp == '\t')
+ lp++;
+ if (strncmp(lp, name, namelen) != 0 || lp[namelen] != ':')
+ continue;
+ lp += namelen + 1; /* Skip name and colon */
+
+ column = 1;
+ while (1)
+ {
+ value = strtoul(lp, &next, 10);
+ if (next == lp)
+ break; /* no more data */
+ switch (column)
+ {
+ case 3:
+ data->dot3StatsFCSErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFCSERRORS_FLAG;
+ break;
+ case 12:
+ data->dot3StatsDeferredTransmissions = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSDEFERREDTRANSMISSIONS_FLAG;
+ break;
+ case 13:
+ data->dot3StatsInternalMacTransmitErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACTRANSMITERRORS_FLAG;
+ break;
+ case 15:
+ data->dot3StatsCarrierSenseErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSCARRIERSENSEERRORS_FLAG;
+ break;
+ case 6:
+ data->dot3StatsFrameTooLongs = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFRAMETOOLONGS_FLAG;
+ break;
+ case 5:
+ data->dot3StatsInternalMacReceiveErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACRECEIVEERRORS_FLAG;
+ break;
+ case 14:
+ data->dot3StatsSingleCollisionFrames = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSSINGLECOLLISIONFRAMES_FLAG;
+ break;
+ }
+ column++;
+ lp = next;
+ }
+ break;
+ }
+
+ fclose(dev);
+ }
+
+ if (!(rowreq_ctx->column_exists_flags & COLUMN_DOT3STATSFCSERRORS_FLAG) &&
+ getulongfromsysclassnetstatistics(name, "rx_errors", &value)) {
+ data->dot3StatsFCSErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFCSERRORS_FLAG;
+ }
+ if (!(rowreq_ctx->column_exists_flags & COLUMN_DOT3STATSDEFERREDTRANSMISSIONS_FLAG) &&
+ getulongfromsysclassnetstatistics(name, "tx_dropped", &value)) {
+ data->dot3StatsDeferredTransmissions = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSDEFERREDTRANSMISSIONS_FLAG;
+ }
+ if (!(rowreq_ctx->column_exists_flags & COLUMN_DOT3STATSINTERNALMACTRANSMITERRORS_FLAG) &&
+ getulongfromsysclassnetstatistics(name, "tx_fifo_errors", &value)) {
+ data->dot3StatsInternalMacTransmitErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACTRANSMITERRORS_FLAG;
+ }
+ if (!(rowreq_ctx->column_exists_flags & COLUMN_DOT3STATSCARRIERSENSEERRORS_FLAG) &&
+ getulongfromsysclassnetstatistics(name, "tx_carrier_errors", &value)) {
+ data->dot3StatsCarrierSenseErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSCARRIERSENSEERRORS_FLAG;
+ }
+ if (!(rowreq_ctx->column_exists_flags & COLUMN_DOT3STATSFRAMETOOLONGS_FLAG) &&
+ getulongfromsysclassnetstatistics(name, "rx_frame_errors", &value)) {
+ data->dot3StatsFrameTooLongs = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFRAMETOOLONGS_FLAG;
+ }
+ if (!(rowreq_ctx->column_exists_flags & COLUMN_DOT3STATSINTERNALMACRECEIVEERRORS_FLAG) &&
+ getulongfromsysclassnetstatistics(name, "rx_fifo_errors", &value)) {
+ data->dot3StatsInternalMacReceiveErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACRECEIVEERRORS_FLAG;
+ }
+
+ return;
+}
+
+/*
+ * @retval 0 success
+ * @retval -1 cannot get ETHTOOL_DRVINFO failed
+ * @retval -2 nstats zero - no statistcs available
+ * @retval -3 memory allocation for holding the statistics failed
+ * @retval -4 cannot get ETHTOOL_GSTRINGS information
+ * @retval -5 cannot get ETHTOOL_GSTATS information
+ * @retval -6 function not supported if HAVE_LINUX_ETHTOOL_H not defined
+ */
+
+
+int
+interface_ioctl_dot3stats_get (dot3StatsTable_rowreq_ctx *rowreq_ctx, int fd, const char *name) {
+
+#ifdef HAVE_LINUX_ETHTOOL_H
+ dot3StatsTable_data *data = &rowreq_ctx->data;
+ struct ethtool_drvinfo driver_info;
+ struct ethtool_gstrings *eth_strings;
+ struct ethtool_stats *eth_stats;
+ struct ifreq ifr;
+ unsigned int nstats, size_str, i;
+ int err;
+
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_get",
+ "called\n"));
+
+ memset(&ifr, 0, sizeof(ifr));
+ strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+ memset(&driver_info, 0, sizeof (driver_info));
+ driver_info.cmd = ETHTOOL_GDRVINFO;
+ ifr.ifr_data = (char *)&driver_info;
+
+ err = _dot3Stats_ioctl_get(fd, SIOCETHTOOL, &ifr, name);
+ if (err < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_get",
+ "ETHTOOL_GETDRVINFO failed for interface |%s| \n", name));
+ return -1;
+ }
+
+ nstats = driver_info.n_stats;
+ if (nstats < 1) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_get",
+ "no stats available for interface |%s| \n", name));
+ return -2;
+ }
+
+ size_str = nstats * ETH_GSTRING_LEN;
+
+ eth_strings = malloc(size_str + sizeof (struct ethtool_gstrings));
+ if (!eth_strings) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_get",
+ "no memory available\n"));
+ snmp_log (LOG_ERR, "access:dot3StatsTable,interface_ioctl_dot3Stats_get, no memory available\n");
+
+ return -3;
+ }
+ memset (eth_strings, 0, (size_str + sizeof (struct ethtool_gstrings)));
+
+ eth_stats = malloc (size_str + sizeof (struct ethtool_stats));
+ if (!eth_stats) {
+ free (eth_strings);
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_get",
+ "no memory available\n"));
+ snmp_log (LOG_ERR, "access:dot3StatsTable,interface_ioctl_dot3Stats_get, no memory available\n");
+
+ return -3;
+ }
+ memset (eth_stats, 0, (size_str + sizeof (struct ethtool_stats)));
+
+ eth_strings->cmd = ETHTOOL_GSTRINGS;
+ eth_strings->string_set = ETH_SS_STATS;
+ eth_strings->len = nstats;
+ ifr.ifr_data = (char *) eth_strings;
+ err = _dot3Stats_ioctl_get(fd, SIOCETHTOOL, &ifr, name);
+ if (err < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_get",
+ "cannot get stats strings information for interface |%s| \n", name));
+ snmp_log (LOG_ERR, "access:dot3StatsTable,interface_ioctl_dot3Stats_get, cannot get stats strings information for interface |%s| \n", name);
+
+ free(eth_strings);
+ free(eth_stats);
+ return -4;
+ }
+
+ eth_stats->cmd = ETHTOOL_GSTATS;
+ eth_stats->n_stats = nstats;
+ ifr.ifr_data = (char *) eth_stats;
+ err = _dot3Stats_ioctl_get(fd, SIOCETHTOOL, &ifr, name);
+ if (err < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_get",
+ "cannot get stats strings information for interface |%s| \n", name));
+ snmp_log (LOG_ERR, "access:dot3StatsTable,interface_ioctl_dot3Stats_get, cannot get stats information for interface |%s| \n", name);
+
+ free(eth_strings);
+ free(eth_stats);
+ return -5;
+ }
+
+ for (i = 0; i < nstats; i++) {
+ char s[ETH_GSTRING_LEN];
+
+ strlcpy(s, (const char *) &eth_strings->data[i * ETH_GSTRING_LEN],
+ sizeof(s));
+
+ if (DOT3STATSALIGNMENTERRORS(s)) {
+ data->dot3StatsAlignmentErrors = (u_long)eth_stats->data[i];
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSALIGNMENTERRORS_FLAG;
+ }
+
+ if (DOT3STATSMULTIPLECOLLISIONFRAMES(s)) {
+ data->dot3StatsMultipleCollisionFrames = (u_long)eth_stats->data[i];
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSMULTIPLECOLLISIONFRAMES_FLAG;
+ }
+
+ if (DOT3STATSLATECOLLISIONS(s)) {
+ data->dot3StatsLateCollisions = (u_long)eth_stats->data[i];
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSLATECOLLISIONS_FLAG;
+ }
+
+ if (DOT3STATSSINGLECOLLISIONFRAMES(s)) {
+ data->dot3StatsSingleCollisionFrames = (u_long)eth_stats->data[i];
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSSINGLECOLLISIONFRAMES_FLAG;
+ }
+
+ if (DOT3STATSEXCESSIVECOLLISIONS(s)) {
+ data->dot3StatsExcessiveCollisions = (u_long)eth_stats->data[i];
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSEXCESSIVECOLLISIONS_FLAG;
+ }
+ }
+
+ free(eth_strings);
+ free(eth_stats);
+
+ return 0;
+#else
+ return -6;
+#endif
+}
+
+
+/*
+ * @retval 0 success
+ * @retval -1 ETHTOOL_GSET failed
+ * @retval -2 function not supported if HAVE_LINUX_ETHTOOL_H not defined
+ */
+
+int
+interface_ioctl_dot3stats_duplex_get(dot3StatsTable_rowreq_ctx *rowreq_ctx, int fd, const char* name) {
+
+#ifdef HAVE_LINUX_ETHTOOL_H
+ dot3StatsTable_data *data = &rowreq_ctx->data;
+ struct ethtool_cmd edata;
+ struct ifreq ifr;
+ int err;
+
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_duplex_get",
+ "called\n"));
+
+ memset(&edata, 0, sizeof (edata));
+ memset(&ifr, 0, sizeof (ifr));
+ edata.cmd = ETHTOOL_GSET;
+ ifr.ifr_data = (char *)&edata;
+
+ err = _dot3Stats_ioctl_get (fd, SIOCETHTOOL, &ifr, name);
+ if (err < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_duplex_get",
+ "ETHTOOL_GSET failed\n"));
+
+ return -1;
+ }
+
+ if (err == 0) {
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSDUPLEXSTATUS_FLAG;
+ switch (edata.duplex) {
+ case DUPLEX_HALF:
+ data->dot3StatsDuplexStatus = (u_long) DOT3STATSDUPLEXSTATUS_HALFDUPLEX;
+ break;
+ case DUPLEX_FULL:
+ data->dot3StatsDuplexStatus = (u_long) DOT3STATSDUPLEXSTATUS_FULLDUPLEX;
+ break;
+ default:
+ data->dot3StatsDuplexStatus = (u_long) DOT3STATSDUPLEXSTATUS_UNKNOWN;
+ break;
+ };
+ }
+
+ DEBUGMSGTL(("access:dot3StatsTable:interface_ioctl_dot3Stats_duplex_get",
+ "ETHTOOL_GSET processed\n"));
+ return err;
+#else
+ return -2;
+#endif
+}
+
+
+/*
+ * NAME: interface_sysclassnet_dot3stats_get
+ * PURPOSE: To get ethernet statistics from /sys/class/net/...
+ * ARGUMENTS: rowreq_ctx: where to store the value(s)
+ * name: interface name
+ * RETURNS: nothing. fields not set if data not available
+ */
+void
+interface_sysclassnet_dot3stats_get (dot3StatsTable_rowreq_ctx *rowreq_ctx, const char *name)
+{
+ u_long value;
+ dot3StatsTable_data *data = &rowreq_ctx->data;
+
+ if (getulongfromsysclassnetstatistics(name, "rx_errors", &value)) {
+ data->dot3StatsFCSErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFCSERRORS_FLAG;
+ }
+ if (getulongfromsysclassnetstatistics(name, "tx_dropped", &value)) {
+ data->dot3StatsDeferredTransmissions = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSDEFERREDTRANSMISSIONS_FLAG;
+ }
+ if (getulongfromsysclassnetstatistics(name, "tx_fifo_errors", &value)) {
+ data->dot3StatsInternalMacTransmitErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACTRANSMITERRORS_FLAG;
+ }
+ if (getulongfromsysclassnetstatistics(name, "tx_carrier_errors", &value)) {
+ data->dot3StatsCarrierSenseErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSCARRIERSENSEERRORS_FLAG;
+ }
+ if (getulongfromsysclassnetstatistics(name, "rx_frame_errors", &value)) {
+ data->dot3StatsFrameTooLongs = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSFRAMETOOLONGS_FLAG;
+ }
+ if (getulongfromsysclassnetstatistics(name, "rx_fifo_errors", &value)) {
+ data->dot3StatsInternalMacReceiveErrors = value;
+ rowreq_ctx->column_exists_flags |= COLUMN_DOT3STATSINTERNALMACRECEIVEERRORS_FLAG;
+ }
+
+ return;
+}
+
+
+
+/* ioctl wrapper
+ *
+ * @param fd : socket fd to use w/ioctl, or -1 to open/close one
+ * @param which
+ * @param ifrq
+ * param ifentry : ifentry to update
+ * @param name
+ *
+ * @retval 0 : success
+ * @retval -1 : invalid parameters
+ * @retval -2 : couldn't create socket
+ * @retval -3 : ioctl call failed
+ */
+int
+_dot3Stats_ioctl_get(int fd, int which, struct ifreq *ifrq, const char* name)
+{
+ int ourfd = -1, rc = 0;
+
+ DEBUGMSGTL(("access:dot3StatsTable:ioctl", "_dot3Stats_ioctl_get\n"));
+
+ /*
+ * sanity checks
+ */
+ if(NULL == name) {
+ DEBUGMSGTL(("access:dot3StatsTable:ioctl",
+ "_dot3Stats_ioctl_get invalid ifname '%s'\n", name));
+ snmp_log (LOG_ERR, "access:dot3StatsTable:ioctl, _dot3Stats_ioctl_get error on interface '%s'\n", name);
+ return -1;
+ }
+
+ /*
+ * create socket for ioctls
+ */
+ if(fd < 0) {
+ fd = ourfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if(ourfd < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:ioctl",
+ "dot3Stats_ioctl_get couldn't create a socket\n"));
+ snmp_log (LOG_ERR, "access:dot3StatsTable:ioctl, _dot3Stats_ioctl_get error on interface '%s'\n", name);
+
+ return -2;
+ }
+ }
+
+ strlcpy(ifrq->ifr_name, name, sizeof(ifrq->ifr_name));
+ rc = ioctl(fd, which, ifrq);
+ if (rc < 0) {
+ DEBUGMSGTL(("access:dot3StatsTable:ioctl",
+ "dot3Stats_ioctl_get ioctl %d returned %d\n", which, rc));
+ rc = -3;
+ }
+
+ if(ourfd >= 0)
+ close(ourfd);
+
+ return rc;
+}
+
+