| /* |
| * 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; |
| |
| DEBUGMSGTL(("access:dot3StatsTable", "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, 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) |
| { |
| DEBUGMSGTL(("access:dot3StatsTable", "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 *) ð_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", name)); |
| 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; |
| } |
| |
| |