summaryrefslogtreecommitdiff
path: root/src/pmdas/solaris/netmib2.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
committerIgor Pashev <pashev.igor@gmail.com>2014-10-26 12:33:50 +0400
commit47e6e7c84f008a53061e661f31ae96629bc694ef (patch)
tree648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/solaris/netmib2.c
downloadpcp-debian.tar.gz
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/solaris/netmib2.c')
-rw-r--r--src/pmdas/solaris/netmib2.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/pmdas/solaris/netmib2.c b/src/pmdas/solaris/netmib2.c
new file mode 100644
index 0000000..aab417a
--- /dev/null
+++ b/src/pmdas/solaris/netmib2.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2009 Max Matveev. All rights reserved.
+ *
+ * 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.
+ */
+
+/* Extract network-related information from the kernel using MIB2
+ * interfaces. MIB2 structures are described by RFC 4113, 4293,
+ * 4001. IPv6 specific MIB structures are described in RFC 2465, 2466.
+ */
+
+#include <fcntl.h>
+#include <stropts.h>
+#include <inet/mib2.h>
+#include <sys/tihdr.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "netmib2.h"
+
+static int afd = -1;
+static int data_valid;
+static int netif_added;
+
+nm2_udp_stats_t nm2_udp;
+
+static nm2_netif_stats_t *
+netif_cache_inst(const char *ifname)
+{
+ pmInDom indom = indomtab[NETIF_INDOM].it_indom;
+ nm2_netif_stats_t *ist;
+ int rv;
+
+ if (pmdaCacheLookupName(indom, ifname, &rv,
+ (void **)&ist) != PMDA_CACHE_ACTIVE) {
+ ist = malloc(sizeof(*ist));
+ if (ist == NULL) {
+ __pmNotifyErr(LOG_WARNING,
+ "Out of memory for stats on network interface '%s'\n",
+ ifname);
+ return NULL;
+ }
+
+ rv = pmdaCacheStore(indom, PMDA_CACHE_ADD, ifname, ist);
+ if (rv < 0) {
+ __pmNotifyErr(LOG_WARNING,
+ "Cannot create instance for '%s': %s\n",
+ ifname, pmErrStr(rv));
+ free(ist);
+ return NULL;
+ }
+ netif_added++;
+ }
+
+ return ist;
+}
+
+static void
+ipv4_stats (const void *data, int sz)
+{
+ const mib2_ipAddrEntry_t *ipa = data;
+
+ while (sz > 0) {
+ nm2_netif_stats_t *ist = netif_cache_inst(ipa->ipAdEntIfIndex.o_bytes);
+
+ if (ist) {
+ ist->mtu = ipa->ipAdEntInfo.ae_mtu;
+ /* We get byte count and other stuff from Traffic stats */
+ }
+ sz -= sizeof(*ipa);
+ ipa++;
+ }
+}
+
+static void
+ipv4_ifstats(const void *data, int sz)
+{
+ const mib2_ipIfStatsEntry_t *ips = data;
+
+ nm2_udp.noports = 0;
+ nm2_udp.overflows = 0;
+
+ while (sz > 0) {
+ /* index 0 is a pseudo-interface */
+ if (ips->ipIfStatsIfIndex) {
+ nm2_netif_stats_t *ist;
+ char name[64];
+
+ if ((if_indextoname(ips->ipIfStatsIfIndex, name) != NULL) &&
+ ((ist = netif_cache_inst(name)) != NULL)) {
+
+ ist->ibytes = ips->ipIfStatsHCInOctets;
+ ist->obytes = ips->ipIfStatsHCOutOctets;
+ ist->ipackets = ips->ipIfStatsHCInReceives;
+ ist->opackets = ips->ipIfStatsHCOutTransmits;
+ ist->imcast = ips->ipIfStatsHCInMcastPkts;
+ ist->omcast = ips->ipIfStatsHCOutMcastPkts;
+ ist->ibcast = ips->ipIfStatsHCInBcastPkts;
+ ist->obcast = ips->ipIfStatsHCOutBcastPkts;
+ ist->delivered = ips->ipIfStatsHCInDelivers;
+ ist->idrops = ips->ipIfStatsInDiscards;
+ ist->odrops = ips->ipIfStatsOutDiscards;
+ ist->ierrors =
+ + (uint64_t)ips->ipIfStatsInHdrErrors
+ + ips->ipIfStatsInTooBigErrors
+ + ips->ipIfStatsInNoRoutes
+ + ips->ipIfStatsInAddrErrors
+ + ips->ipIfStatsInUnknownProtos
+ + ips->ipIfStatsInTruncatedPkts;
+
+ ist->oerrors = ips->ipIfStatsOutFragFails;
+ }
+ }
+
+ nm2_udp.noports += ips->udpNoPorts;
+ nm2_udp.overflows += ips->udpInOverflows;
+
+ sz -= sizeof(*ips);
+ ips++;
+ }
+}
+
+void
+netmib2_refresh(void)
+{
+ struct strbuf ctrl;
+ struct opthdr *oh;
+ uint64_t buf[64]; /* Arbitrary size, just large enough to fit req + opthdr */
+ struct T_optmgmt_req *omreq = (struct T_optmgmt_req *)buf;
+ struct T_optmgmt_ack *omack = (struct T_optmgmt_ack *)buf;
+
+ omreq->PRIM_type = T_SVR4_OPTMGMT_REQ;
+ omreq->OPT_offset = sizeof (*omreq);
+ omreq->OPT_length = sizeof (*oh);
+ omreq->MGMT_flags = T_CURRENT;
+
+ oh = (struct opthdr *)(omreq + 1);
+ oh->level = /*EXPER_IP_AND_TESTHIDDEN*/MIB2_IP;
+ oh->name = 0;
+ oh->len = 0;
+
+ ctrl.buf = (char *)buf;
+ ctrl.len = omreq->OPT_length + omreq->OPT_offset;
+
+ data_valid = 0;
+
+ if (putmsg(afd, &ctrl, NULL, 0) == -1) {
+ __pmNotifyErr(LOG_ERR, "Failed to push message down stream: %s\n",
+ osstrerror());
+ return;
+ }
+
+ oh = (struct opthdr *)(omack + 1);
+ ctrl.maxlen = sizeof(buf);
+
+ netif_added = 0;
+
+ for (;;) {
+ int flags = 0;
+ struct strbuf data;
+ int rv;
+
+ rv = getmsg(afd, &ctrl, NULL, &flags);
+ if (rv < 0) {
+ __pmNotifyErr(LOG_ERR, "netmib2: failed to get a response: %s\n",
+ osstrerror());
+ break;
+ }
+
+ if ((rv == 0) && (ctrl.len >= sizeof(*omack)) &&
+ (omack->PRIM_type == T_OPTMGMT_ACK) &&
+ (omack->MGMT_flags == T_SUCCESS) && (oh->len == 0)) {
+ data_valid = 1;
+ break;
+ }
+
+ if ((rv != MOREDATA) || (ctrl.len < sizeof(*omack)) ||
+ (omack->PRIM_type != T_OPTMGMT_ACK) ||
+ (omack->MGMT_flags != T_SUCCESS)) {
+ __pmNotifyErr(LOG_ERR, "netmib2: Unexpected message received\n");
+ break;
+ }
+
+ memset(&data, 0, sizeof(data));
+ data.buf = malloc(oh->len);
+ if (data.buf == NULL) {
+ __pmNotifyErr(LOG_ERR, "netmib2: Out of memory\n");
+ break;
+ }
+
+ data.maxlen = oh->len;
+ flags = 0;
+
+ rv = getmsg(afd, NULL, &data, &flags);
+ if (rv) {
+ __pmNotifyErr(LOG_ERR,
+ "net2mib: Failed to get additional data: %s\n",
+ osstrerror());
+ break;
+ }
+
+ switch (oh->level) {
+ case MIB2_IP:
+ switch(oh->name) {
+ case 0: /* Overall statistic */
+ break;
+
+ case MIB2_IP_ADDR:
+ ipv4_stats(data.buf, data.len);
+ break;
+
+ case MIB2_IP_TRAFFIC_STATS:
+ ipv4_ifstats(data.buf, data.len);
+ break;
+ }
+ break;
+
+ case MIB2_IP6:
+ break;
+
+ case MIB2_UDP:
+ if (oh->name == 0) {
+ mib2_udp_t *m2u = (mib2_udp_t *)data.buf;
+
+#ifdef EXPER_IP_AND_TESTHIDDEN
+ nm2_udp.ipackets = m2u->udpHCInDatagrams;
+ nm2_udp.opackets = m2u->udpHCOutDatagrams;
+#else
+ nm2_udp.ipackets = m2u->udpInDatagrams;
+ nm2_udp.opackets = m2u->udpOutDatagrams;
+#endif
+ nm2_udp.ierrors = m2u->udpInErrors;
+ nm2_udp.oerrors = m2u->udpOutErrors;
+ }
+ break;
+
+ case MIB2_TCP:
+ break;
+ }
+
+ free(data.buf);
+ }
+
+ if (netif_added) {
+ pmdaCacheOp(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_SAVE);
+ }
+}
+
+int
+netmib2_fetch(pmdaMetric *pm, int inst, pmAtomValue *av)
+{
+ char *fsname;
+ metricdesc_t *md = pm->m_user;
+ char *ist;
+
+ if (pm->m_desc.indom == PM_INDOM_NULL) {
+ switch (pm->m_desc.type) {
+ case PM_TYPE_U32:
+ av->ul = *(uint32_t *)md->md_offset;
+ return 1;
+
+ case PM_TYPE_U64:
+ av->ull = *(uint64_t *)md->md_offset;
+ return 1;
+ }
+
+ return PM_ERR_APPVERSION;
+ }
+
+ if (pmdaCacheLookup(indomtab[NETIF_INDOM].it_indom, inst, &fsname,
+ (void **)&ist) != PMDA_CACHE_ACTIVE)
+ return PM_ERR_INST;
+
+ if (ist) {
+ switch (pm->m_desc.type) {
+ case PM_TYPE_U32:
+ av->ul = *(uint32_t *)(ist + md->md_offset);
+ return 1;
+
+ case PM_TYPE_U64:
+ av->ull = *(uint64_t *)(ist + md->md_offset);
+ return 1;
+ }
+
+ return PM_ERR_APPVERSION;
+ }
+
+ /* Even if we've copied the values don't admit they're good unless
+ * the update was problem-free. */
+ return data_valid;
+}
+
+void
+netmib2_init(int first)
+{
+ char *mods[] = {"tcp", "udp", "icmp"};
+ int i;
+
+ if (afd >= 0)
+ return;
+
+ afd = open("/dev/arp", O_RDWR);
+ if (afd < 0) {
+ __pmNotifyErr(LOG_ERR, "Cannot open /dev/arp: %s\n", osstrerror());
+ return;
+ }
+
+ for (i = 0; i < 3; i++ ) {
+ if (ioctl(afd, I_PUSH, mods[i]) < 0) {
+ __pmNotifyErr(LOG_ERR, "Cannot push %s into /dev/arp: %s\n",
+ mods[i], osstrerror());
+ close(afd);
+ afd = -1;
+ return;
+ }
+ }
+
+ pmdaCacheOp(indomtab[NETIF_INDOM].it_indom, PMDA_CACHE_LOAD);
+ netmib2_refresh();
+}