| /*- This is a -*- C -*- compatible code file |
| * |
| * RCS $Id$ |
| * |
| * Code for SUNOS5_INSTRUMENTATION |
| * |
| * This file contains includes of standard and local system header files, |
| * includes of other application header files, global variable definitions, |
| * static variable definitions, static function prototypes, and function |
| * definitions. |
| * |
| * This file contains function to obtain statistics from SunOS 5.x kernel |
| * |
| */ |
| #include <config.h> |
| #ifdef solaris2 |
| #define _SYS_PROC_H |
| /*- |
| * Includes of standard ANSI C header files |
| */ |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| |
| /*- |
| * Includes of system header files (wrapped in duplicate include prevention) |
| */ |
| |
| #include <fcntl.h> |
| #include <stropts.h> |
| #include <sys/types.h> |
| #define _SYS_USER_H |
| #include <kvm.h> |
| #include <nlist.h> |
| #include <sys/fcntl.h> |
| #include <kstat.h> |
| #include <errno.h> |
| #include <time.h> |
| |
| #include <sys/sockio.h> |
| #include <sys/socket.h> |
| #include <sys/stream.h> |
| #include <sys/stropts.h> |
| #include <sys/tihdr.h> |
| #include <sys/tiuser.h> |
| #include <inet/common.h> |
| #include <inet/mib2.h> |
| #include <inet/ip.h> |
| #include <net/if.h> |
| #include <netinet/in.h> |
| |
| /*- |
| * Includes of local application header files |
| */ |
| |
| #include "kernel_sunos5.h" |
| |
| /*- |
| * Global variable definitions (with initialization) |
| */ |
| |
| /*- |
| * Static variable definitions (with initialization) |
| */ |
| |
| static |
| mibcache Mibcache[MIBCACHE_SIZE] = { |
| {MIB_SYSTEM, 0, (void *)-1, 0, 0, 0, 0}, |
| {MIB_INTERFACES, 10*sizeof(mib2_ifEntry_t), (void *)-1, 0, 10, 0, 0}, |
| {MIB_AT, 0, (void *)-1, 0, 0, 0, 0}, |
| {MIB_IP, sizeof(mib2_ip_t), (void *)-1, 0, 20, 0, 0}, |
| {MIB_IP_ADDR, 20*sizeof(mib2_ipAddrEntry_t), (void *)-1, 0, 20, 0, 0}, |
| {MIB_IP_ROUTE, 200*sizeof(mib2_ipRouteEntry_t), (void *)-1, 0, 10, 0, 0}, |
| {MIB_IP_NET, 20*sizeof(mib2_ipNetToMediaEntry_t), (void *)-1, 0, 100, 0, 0}, |
| {MIB_ICMP, sizeof(mib2_icmp_t), (void *)-1, 0, 20, 0, 0}, |
| {MIB_TCP, sizeof(mib2_tcp_t), (void *)-1, 0, 20, 0, 0}, |
| {MIB_TCP_CONN, 1000*sizeof(mib2_tcpConnEntry_t), (void *)-1, 0, 15, 0, 0}, |
| {MIB_UDP, sizeof(mib2_udp_t), (void *)-1, 0, 15, 0, 0}, |
| {MIB_UDP_LISTEN, 100*sizeof(mib2_udpEntry_t), (void *)-1, 0, 15, 0, 0}, |
| {MIB_EGP, 0, (void *)-1, 0, 0, 0, 0}, |
| {MIB_CMOT, 0, (void *)-1, 0, 0, 0, 0}, |
| {MIB_TRANSMISSION, 0, (void *)-1, 0, 0, 0, 0}, |
| {MIB_SNMP, 0, (void *)-1, 0, 0, 0, 0}, |
| {0}, |
| }; |
| |
| static |
| mibmap Mibmap[MIBCACHE_SIZE] = { |
| {MIB2_SYSTEM, 0,}, |
| {MIB2_INTERFACES, 0,}, |
| {MIB2_AT, 0,}, |
| {MIB2_IP, 0,}, |
| {MIB2_IP, MIB2_IP_20,}, |
| {MIB2_IP, MIB2_IP_21,}, |
| {MIB2_IP, MIB2_IP_22,}, |
| {MIB2_ICMP, 0,}, |
| {MIB2_TCP, 0,}, |
| {MIB2_TCP, MIB2_TCP_13,}, |
| {MIB2_UDP, 0,}, |
| {MIB2_UDP, MIB2_UDP_5}, |
| {MIB2_EGP, 0,}, |
| {MIB2_CMOT, 0,}, |
| {MIB2_TRANSMISSION, 0,}, |
| {MIB2_SNMP, 0,}, |
| {0}, |
| }; |
| |
| static int sd = -1; /* /dev/ip stream descriptor. */ |
| |
| /*- |
| * Static function prototypes (use void as argument type if there are none) |
| */ |
| |
| static found_e |
| getentry(req_e req_type, void *bufaddr, int len, int entrysize, |
| void *resp, int (*comp)(void *, void *), void *arg); |
| |
| static int |
| getmib(int groupname, int subgroupname, void *statbuf, size_t size, int entrysize, |
| req_e req_type, void *resp, int *length, int (*comp)(void *, void *), void *arg); |
| |
| static int |
| getif(mib2_ifEntry_t *ifbuf, size_t size, req_e req_type, mib2_ifEntry_t *resp, |
| int *length, int (*comp)(void *, void *), void *arg); |
| |
| static int |
| Name_cmp(void *, void *); |
| |
| static void |
| init_mibcache_element(mibcache *cp); |
| |
| #define STREAM_DEV "/dev/ip" |
| #define BUFSIZE 40960 /* Buffer for messages (should be modulo(pagesize) */ |
| |
| /*- |
| * Function definitions |
| */ |
| |
| #ifdef _STDC_COMPAT |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| #endif |
| |
| /* Get various kernel statistics using undocumented Solaris kstat interface. |
| We need it mainly for getting network interface statistics, |
| although it is generic enough to be used for any purpose. |
| It knows about kstat_headers module names and by the name of the statistics |
| it tries to figure out the rest of necessary information. |
| Returns 0 in case of success and < 0 if there were any errors. |
| |
| NOTE: To use this function correctly you have to know the actual |
| type of the value to be returned, so you may build the test program, |
| figure out the type and use it. Exposing kstat data types to upper layers |
| doesn't seem to be reasonable. In any case I'd expect more reasonable kstat |
| interface. :-(*/ |
| |
| |
| int |
| getKstat(char *statname, char *varname, void *value) |
| { |
| kstat_ctl_t *ksc; |
| kstat_t *ks, *kstat_data; |
| kstat_named_t *d; |
| size_t i, instance; |
| char module_name[64]; |
| int ret; |
| u_longlong_t val; /* The largest value */ |
| void *v; |
| |
| if (value == NULL) /* Pretty useless but ... */ |
| v = (void *)&val; |
| else |
| v = value; |
| if ((ksc = kstat_open()) == NULL) { |
| ret = -10; |
| goto Return; /* kstat errors */ |
| } |
| if (statname == NULL || varname == NULL) { |
| ret = -20; |
| goto Return; |
| } |
| /* First, get "kstat_headers" statistics. It should |
| contain all available modules. */ |
| if ((ks = kstat_lookup(ksc, "unix", 0, "kstat_headers")) == NULL) { |
| ret = -10; |
| goto Return; /* kstat errors */ |
| } |
| if (kstat_read(ksc, ks, NULL) <= 0) { |
| ret = -10; |
| goto Return; /* kstat errors */ |
| } |
| kstat_data = ks->ks_data; |
| |
| /* Now, look for the name of our stat in the headers buf */ |
| for (i = 0; i < ks->ks_ndata; i++) { |
| #ifdef DODEBUG |
| printf ("module: %s instance: %d name: %s class: %s type: %d flags: %x\n", |
| kstat_data[i].ks_module, kstat_data[i].ks_instance, |
| kstat_data[i].ks_name, kstat_data[i].ks_class, |
| kstat_data[i].ks_type, kstat_data[i].ks_flags); |
| #endif |
| if (strcmp(statname, kstat_data[i].ks_name) == 0) { |
| strcpy(module_name, kstat_data[i].ks_module); |
| instance = kstat_data[i].ks_instance; |
| break; |
| } |
| } |
| if (i == ks->ks_ndata) { |
| ret = -1; |
| goto Return; /* Not found */ |
| } |
| /* Get the named statistics */ |
| if ((ks = kstat_lookup(ksc, module_name, instance, statname)) == NULL) { |
| ret = -10; |
| goto Return; /* kstat errors */ |
| } |
| if (kstat_read(ksc, ks, NULL) <= 0) { |
| ret = -10; |
| goto Return; /* kstat errors */ |
| } |
| /* This function expects only name/value type of statistics, |
| so if it is not the case return an error */ |
| if (ks->ks_type != KSTAT_TYPE_NAMED) { |
| ret = -2; |
| goto Return; /* Invalid stat type */ |
| } |
| for (i = 0, d = KSTAT_NAMED_PTR(ks); i < ks->ks_ndata; i++, d++) { |
| #ifdef DODEBUG |
| printf ("variable: %s %d\n", d->name, d->data_type); |
| #endif |
| if (strcmp(d->name, varname) == 0) { |
| switch (d->data_type) { |
| case KSTAT_DATA_CHAR: |
| *(char *)v = (int)d->value.c; |
| #ifdef DODEBUG |
| printf ("value: %d\n", (int)d->value.c); |
| #endif |
| break; |
| case KSTAT_DATA_LONG: |
| *(long *)v = d->value.l; |
| #ifdef DODEBUG |
| printf ("value: %ld\n", d->value.l); |
| #endif |
| break; |
| case KSTAT_DATA_ULONG: |
| *(ulong_t *)v = d->value.ul; |
| #ifdef DODEBUG |
| printf ("value: %lu\n", d->value.ul); |
| #endif |
| break; |
| case KSTAT_DATA_LONGLONG: |
| *(longlong_t *)v = d->value.ll; |
| #ifdef DODEBUG |
| printf ("value: %ld\n", (long)d->value.ll); |
| #endif |
| break; |
| case KSTAT_DATA_ULONGLONG: |
| *(u_longlong_t *)v = d->value.ull; |
| #ifdef DODEBUG |
| printf ("value: %lu\n", (unsigned long)d->value.ul); |
| #endif |
| break; |
| case KSTAT_DATA_FLOAT: |
| *(float *)v = d->value.f; |
| #ifdef DODEBUG |
| printf ("value: %f\n", d->value.f); |
| #endif |
| break; |
| case KSTAT_DATA_DOUBLE: |
| *(double *)v = d->value.d; |
| #ifdef DODEBUG |
| printf ("value: %f\n", d->value.d); |
| #endif |
| break; |
| default: |
| ret = -3; |
| goto Return; /* Invalid data type */ |
| } |
| ret = 0; /* Success */ |
| goto Return; |
| } |
| } |
| ret = -4; /* Name not found */ |
| Return: |
| if (ksc != NULL) |
| kstat_close(ksc); |
| return (ret); |
| } |
| |
| /* |
| * get MIB-II statistics. It maintaines a simple cache which buffers |
| * the last read block of MIB statistics (which may contain the whole |
| * table). It calls *comp to compare every entry with an entry pointed |
| * by arg. *comp should return 0 if comparison is successful. |
| * Req_type may be GET_FIRST, GET_EXACT, GET_NEXT. |
| * If search is successful getMibstat returns 0, otherwise 1. |
| */ |
| int |
| getMibstat(mibgroup_e grid, void *resp, int entrysize, |
| req_e req_type, int (*comp)(void *, void *), void *arg) |
| { |
| int ret, rc=-1, mibgr, mibtb, cache_valid, length; |
| mibcache *cachep; |
| found_e result = NOT_FOUND; |
| void* ep; |
| |
| /* |
| * We assume that Mibcache is initialized in mibgroup_e enum order |
| * so we don't check the validity of index here. |
| */ |
| #ifdef DODEBUG |
| printf ("getMibstat (%d, *, %d, %d, *, *)\n", grid, entrysize, req_type); |
| #endif |
| cachep = &Mibcache[grid]; |
| mibgr = Mibmap[grid].group; |
| mibtb = Mibmap[grid].table; |
| |
| if (cachep->cache_addr == (void *)-1) /* Hasn't been initialized yet */ |
| init_mibcache_element(cachep); |
| if (cachep->cache_size == 0) { /* Memory allocation problems */ |
| cachep->cache_addr = resp; /* So use caller supplied address instead of cache */ |
| cachep->cache_size = entrysize; |
| cachep->cache_last_found = 0; |
| } |
| if (req_type != GET_NEXT) |
| cachep->cache_last_found = 0; |
| cache_valid = (time(NULL) - cachep->cache_time) > cachep->cache_ttl ? 0 : 1; |
| #ifdef DODEBUG |
| printf ("... cache_valid %d time %ld ttl %d now %ld\n", |
| cache_valid, cachep->cache_time, cachep->cache_ttl, time (NULL)); |
| #endif |
| if (cache_valid) { |
| /* Is it really? */ |
| if (cachep->cache_comp != (void *) comp || |
| cachep->cache_arg != arg) |
| cache_valid = 0; /* Nope. */ |
| } |
| |
| if (cache_valid) { |
| /* Entry is valid, let's try to find a match */ |
| if (req_type == GET_NEXT) { |
| result = getentry(req_type, |
| (void *) ((char *) cachep->cache_addr + (cachep->cache_last_found * entrysize)), |
| cachep->cache_length - (cachep->cache_last_found * entrysize), |
| entrysize, &ep, comp, arg); |
| } else { |
| result = getentry(req_type, cachep->cache_addr, cachep->cache_length, |
| entrysize, &ep, comp, arg); |
| } |
| } |
| if ((cache_valid == 0) |
| || (result == NOT_FOUND) |
| || ((result == NEED_NEXT) && cachep->cache_flags&CACHE_MOREDATA)) { |
| /* |
| * Either the cache is old, or we haven't found |
| * anything, or need the next item which hasn't been read yet. |
| * In any case, fill the cache up and try to find our entry. |
| */ |
| if (grid == MIB_INTERFACES) |
| rc = getif((mib2_ifEntry_t *)cachep->cache_addr, cachep->cache_size, |
| req_type, (mib2_ifEntry_t *)&ep, &length, comp, arg); |
| else |
| rc = getmib(mibgr, mibtb, cachep->cache_addr, cachep->cache_size, entrysize, |
| req_type, &ep, &length, comp, arg); |
| if (rc >= 0) { /* Cache has been filled up */ |
| cachep->cache_time = time(NULL); |
| cachep->cache_length = length; |
| if (rc == 1) /* Found but there are more unread data */ |
| cachep->cache_flags |= CACHE_MOREDATA; |
| else |
| cachep->cache_flags &= ~CACHE_MOREDATA; |
| cachep->cache_comp = (void *) comp; |
| cachep->cache_arg = arg; |
| } else { |
| cachep->cache_comp = NULL; |
| cachep->cache_arg = NULL; |
| } |
| } |
| #ifdef DODEBUG |
| printf ("... result %d rc %d\n", result, rc); |
| #endif |
| if (result == FOUND || rc == 0 || rc == 1) { |
| /* Entry has been found, deliver it */ |
| if (resp != (void *)NULL) |
| (void)memcpy(resp, ep, entrysize); |
| ret = 0; |
| cachep->cache_last_found = ((char *) ep - (char *) cachep->cache_addr) / entrysize; |
| } else |
| ret = 1; /* Not found */ |
| #ifdef DODEBUG |
| printf ("... getMibstat returns %d\n", ret); |
| #endif |
| return (ret); |
| } |
| |
| /* Get a MIB-II entry from the buffer buffaddr, which satisfies |
| * the criterion, computed by (*comp), which gets arg as the first |
| * argument and pointer to the current position in the buffer as the |
| * second. If found entry is pointed by resp. |
| */ |
| |
| static found_e |
| getentry(req_e req_type, void *bufaddr, int len, int entrysize, |
| void *resp, int (*comp)(void *, void *), void *arg) |
| { |
| void *bp = bufaddr, **rp = resp; |
| int previous_found = 0; |
| |
| /* Here we have to perform address arithmetic with |
| pointer to void. Ugly... */ |
| for (; len != 0; len -= entrysize, bp = (char *)bp + entrysize) { |
| if (rp != (void *)NULL) |
| *rp = bp; |
| if ((req_type == GET_FIRST) || |
| ((req_type == GET_NEXT) && previous_found)) { |
| return (FOUND); |
| } |
| if ((*comp)(arg, bp) == 0) |
| if (req_type == GET_EXACT) { |
| return (FOUND); |
| } else { /* GET_NEXT */ |
| previous_found++; |
| continue; |
| } |
| } |
| if (previous_found) |
| return (NEED_NEXT); |
| else |
| return (NOT_FOUND); |
| } |
| |
| /* |
| * Initialize a cache element. It allocates the memory |
| * and sets the time stamp to invalidate the element. |
| */ |
| static void |
| init_mibcache_element(mibcache *cp) |
| { |
| if (cp == (mibcache *)NULL) |
| return; |
| if (cp->cache_size) |
| cp->cache_addr = malloc(cp->cache_size); |
| cp->cache_time = time(NULL) - 100*cp->cache_ttl; /* In the past */ |
| cp->cache_comp = NULL; |
| cp->cache_arg = NULL; |
| } |
| |
| /* Get MIB-II statistics from the Solaris kernel. |
| It uses undocumented interface to TCP/IP streams modules, which |
| provides extended MIB-II for the following groups: |
| ip, icmp, tcp, udp, egp. |
| |
| Usage: groupname, subgroupname are from <inet/mib2.h>, size%sizeof(statbuf) == 0, |
| entrysize should be exact size of MIB-II entry, |
| req_type: |
| GET_FIRST - get the first entry in the buffer |
| GET_EXACT - get exact match |
| GET_NEXT - get next entry after the exact match |
| |
| (*comp) is a compare function, provided by the caller, which gets arg as the |
| first argument and pointer to the current entry as th second. If compared, should |
| return 0 and found entry will be pointed by resp. |
| |
| If search is successful and no more data to read, it returns 0, |
| if successful and there is more data -- 1, |
| if not found and end of data -- 2, any other errors -- < 0 |
| (negative error numbers are pretty random). |
| |
| NOTE: needs to be protected by a mutex in reentrant environment */ |
| |
| static int |
| getmib(int groupname, int subgroupname, void *statbuf, size_t size, int entrysize, |
| req_e req_type, void *resp, int *length, int (*comp)(void *, void *), void *arg) |
| { |
| int rc, ret = 0, flags; |
| char buf[BUFSIZE]; |
| struct strbuf strbuf; |
| struct T_optmgmt_req *tor = (struct T_optmgmt_req *) buf; |
| struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *) buf; |
| struct T_error_ack *tea = (struct T_error_ack *) buf; |
| struct opthdr *req; |
| found_e result = FOUND; |
| |
| #ifdef DODEBUG |
| printf ("...... getmib (%d, %d, ...)\n", groupname, subgroupname); |
| #endif |
| |
| /* Open the stream driver and push all MIB-related modules */ |
| |
| if (sd == -1) { /* First time */ |
| if ((sd = open(STREAM_DEV, O_RDWR)) == -1) { |
| ret = -1; |
| goto Return; |
| } |
| if (ioctl(sd, I_PUSH, "arp") == -1) { |
| ret = -1; |
| goto Return; |
| } |
| if (ioctl(sd, I_PUSH, "tcp") == -1) { |
| ret = -1; |
| goto Return; |
| } |
| if (ioctl(sd, I_PUSH, "udp") == -1) { |
| ret = -1; |
| goto Return; |
| } |
| #ifdef DODEBUG |
| printf ("...... modules pushed OK\n"); |
| #endif |
| } |
| |
| /* First, use bigger buffer, to accelerate skipping |
| unwanted messages */ |
| |
| strbuf.buf = buf; |
| strbuf.maxlen = BUFSIZE; |
| |
| tor->PRIM_type = T_OPTMGMT_REQ; |
| tor->OPT_offset = sizeof(struct T_optmgmt_req); |
| tor->OPT_length = sizeof(struct opthdr); |
| tor->MGMT_flags = MI_T_CURRENT; |
| req = (struct opthdr *) (tor + 1); |
| req->level = groupname; |
| req->name = subgroupname; |
| req->len = 0; |
| strbuf.len = tor->OPT_length + tor->OPT_offset; |
| flags = 0; |
| if ((rc = putmsg(sd, &strbuf, NULL, flags))) { |
| ret = -2; |
| goto Return; |
| } |
| req = (struct opthdr *) (toa + 1); |
| for (;;) { |
| flags = 0; |
| if ((rc = getmsg(sd, &strbuf, NULL, &flags)) == -1) { |
| ret = -EIO; |
| break; |
| } |
| if (rc == 0 |
| && strbuf.len >= sizeof(struct T_optmgmt_ack) |
| && toa->PRIM_type == T_OPTMGMT_ACK |
| && toa->MGMT_flags == T_SUCCESS |
| && req->len == 0) { |
| ret = 2; |
| break; |
| } |
| if (strbuf.len >= sizeof(struct T_error_ack) |
| && tea->PRIM_type == T_ERROR_ACK) { |
| ret = -(tea->TLI_error == TSYSERR) ? tea->UNIX_error : EPROTO;/* Protocol error */ |
| break; |
| } |
| if (rc != MOREDATA |
| || strbuf.len < sizeof(struct T_optmgmt_ack) |
| || toa->PRIM_type != T_OPTMGMT_ACK |
| || toa->MGMT_flags != T_SUCCESS) { |
| ret = -ENOMSG; /* No more messages */ |
| break; |
| } |
| /* The order in which we get the statistics is determined by the |
| kernel and not by the group name, so we have to loop until |
| we get the required statistics. */ |
| if (req->level != groupname || req->name != subgroupname) { |
| strbuf.maxlen = BUFSIZE; |
| strbuf.buf = buf; |
| do { |
| rc = getmsg(sd, NULL, &strbuf, &flags); |
| } while (rc == MOREDATA) ; |
| continue; |
| } |
| /* Now when we found our stat, switch buffer to a caller-provided |
| one. Manipulating the size of it one can control performance, |
| reducing the number of getmsg calls */ |
| strbuf.buf = statbuf; |
| strbuf.maxlen = size; |
| strbuf.len = 0; |
| flags = 0; |
| do { |
| rc = getmsg(sd, NULL, &strbuf, &flags); |
| switch (rc) { |
| case -1: |
| rc = -ENOSR; |
| goto Return; |
| |
| default: |
| rc = -ENODATA; |
| goto Return; |
| |
| case MOREDATA: |
| case 0: |
| if ((req_type == GET_NEXT) && (result == NEED_NEXT)) |
| /* End of buffer, so "next" is the first item in the next buffer */ |
| req_type = GET_FIRST; |
| result = getentry(req_type, (void *)strbuf.buf, strbuf.len, entrysize, |
| resp, comp, arg); |
| *length = strbuf.len; /* To use in caller for cacheing */ |
| break; |
| } |
| } while ((rc == MOREDATA) && (result != FOUND)); |
| if (result == FOUND) { /* Search is successful */ |
| if (rc != MOREDATA) |
| ret = 0; /* Found and no more data */ |
| else |
| ret = 1; /* Found and there is another unread data block */ |
| break; |
| } else { /* Restore buffers, continue search */ |
| strbuf.buf = buf; |
| strbuf.maxlen = BUFSIZE; |
| } |
| } |
| Return: |
| (void)ioctl(sd, I_FLUSH, FLUSHRW); |
| #ifdef DODEBUG |
| printf ("...... getmib returns %d\n", ret); |
| #endif |
| return(ret); |
| } |
| |
| /* |
| * Get info for interfaces group. Mimics getmib interface as much as possible |
| * to be substituted later if SunSoft decides to extend its mib2 interface. |
| */ |
| static int |
| getif(mib2_ifEntry_t *ifbuf, size_t size, req_e req_type, |
| mib2_ifEntry_t *resp, int *length, int (*comp)(void *, void *), void *arg) |
| { |
| int i, ret, idx = 1; |
| int sd; |
| char buf[1024]; |
| struct ifconf ifconf; |
| struct ifreq *ifrp; |
| mib2_ifEntry_t *ifp; |
| mib2_ipNetToMediaEntry_t Media; |
| int nentries = size/sizeof(mib2_ifEntry_t); |
| found_e result = NOT_FOUND; |
| |
| if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) |
| return (-1); |
| ifconf.ifc_buf = buf; |
| ifconf.ifc_len = sizeof(buf); |
| if (ioctl(sd, SIOCGIFCONF, &ifconf) == -1) { |
| ret = -1; |
| goto Return; |
| } |
| Again: |
| for (i = 0, ifp = (mib2_ifEntry_t *)ifbuf, ifrp = ifconf.ifc_req; |
| ((char *)ifrp < ((char *)ifconf.ifc_buf + ifconf.ifc_len)) && (i < nentries); |
| i++, ifp++, ifrp++, idx++) { |
| #ifdef DODEBUG |
| printf ("...... getif %s\n", ifrp->ifr_name); |
| #endif |
| if (ioctl(sd, SIOCGIFFLAGS, ifrp) < 0) { |
| ret = -1; |
| #ifdef DODEBUG |
| printf ("...... SIOCGIFFLAGS failed\n"); |
| #endif |
| goto Return; |
| } |
| (void)memset(ifp, 0, sizeof(mib2_ifEntry_t)); |
| ifp->ifIndex = idx; |
| ifp->ifDescr.o_length = strlen(ifrp->ifr_name); |
| (void)strcpy(ifp->ifDescr.o_bytes, ifrp->ifr_name); |
| ifp->ifAdminStatus = (ifrp->ifr_flags & IFF_RUNNING) ? 1 : 2; |
| ifp->ifOperStatus = (ifrp->ifr_flags & IFF_UP) ? 1 : 2; |
| ifp->ifLastChange = 0; /* Who knows ... */ |
| if (ioctl(sd, SIOCGIFMTU, ifrp) < 0) { |
| ret = -1; |
| #ifdef DODEBUG |
| printf ("...... SIOCGIFMTU failed\n"); |
| #endif |
| goto Return; |
| } |
| ifp->ifMtu = ifrp->ifr_metric; |
| ifp->ifSpeed = 10000000; /* Best guess */ |
| if (!strchr (ifrp->ifr_name, ':')) { |
| if (getKstat(ifrp->ifr_name, "ipackets", &ifp->ifInUcastPkts) < 0) { |
| ret = -1; |
| goto Return; |
| } |
| ifp->ifInOctets = ifp->ifInUcastPkts * 308; /* XXX */ |
| if (getKstat(ifrp->ifr_name, "opackets", &ifp->ifOutUcastPkts) < 0) { |
| ret = -1; |
| goto Return; |
| } |
| ifp->ifOutOctets = ifp->ifOutUcastPkts * 308; /* XXX */ |
| if (strcmp(ifrp->ifr_name, "lo0") == 0) { /* No other stat for lo0 */ |
| ifp->ifType = 24; /* Loopback */ |
| continue; |
| } |
| ifp->ifType = (ifp->ifMtu == 1500)? 6 : 1; /* Best guess */ |
| if (getKstat(ifrp->ifr_name, "ierrors", &ifp->ifInErrors) < 0) { |
| ret = -1; |
| goto Return; |
| } |
| if (getKstat(ifrp->ifr_name, "oerrors", &ifp->ifOutErrors) < 0) { |
| ret = -1; |
| goto Return; |
| } |
| } |
| /* |
| * An attempt to determine the physical address of the interface. |
| * There should be a more elegant solution using DLPI, but |
| * "the margin is too small to put it here ..." |
| */ |
| if (ioctl(sd, SIOCGIFADDR, ifrp) < 0) { |
| ret = -1; |
| goto Return; |
| } |
| if (getMibstat(MIB_IP_NET, &Media, sizeof(mib2_ipNetToMediaEntry_t), |
| GET_EXACT, &Name_cmp, ifrp) == 0) |
| ifp->ifPhysAddress = Media.ipNetToMediaPhysAddress; |
| } |
| if ((req_type == GET_NEXT) && (result == NEED_NEXT)) |
| /* End of buffer, so "next" is the first item in the next buffer */ |
| req_type = GET_FIRST; |
| result = getentry(req_type, (void *)ifbuf, size, sizeof(mib2_ifEntry_t), |
| (void *)resp, comp, arg); |
| if ((result != FOUND) && |
| (i == nentries) && |
| ((char *)ifrp < (char *)ifconf.ifc_buf + ifconf.ifc_len)) { |
| /* |
| * We reached the end of supplied buffer, but there is |
| * some more stuff to read, so continue. |
| */ |
| ifconf.ifc_len -= i * sizeof(struct ifreq); |
| ifconf.ifc_req = ifrp; |
| goto Again; |
| } |
| if (result != FOUND) |
| ret = 2; |
| else { |
| if ((char *)ifrp < (char *)ifconf.ifc_buf + ifconf.ifc_len) |
| ret = 1; /* Found and more data to fetch */ |
| else |
| ret = 0; /* Found and no more data */ |
| *length = i * sizeof(mib2_ifEntry_t); /* Actual cache length */ |
| } |
| Return: |
| close(sd); |
| return (ret); /* DONE */ |
| } |
| |
| /* Always TRUE. May be used as a comparison function in getMibstat |
| to obtain the whole table (GET_FIRST should be used) */ |
| /*ARGSUSED */ |
| int |
| Get_everything(void *x, void *y) |
| { |
| return (0); /* Always TRUE */ |
| } |
| |
| /* |
| * Compare name and IP address of the interface to ARP table entry. |
| * Needed to obtain the physical address of the interface in getif. |
| */ |
| static int |
| Name_cmp(void *ifrp, void *ep) |
| { |
| struct sockaddr_in *s = (struct sockaddr_in *)&(((struct ifreq *)ifrp)->ifr_addr); |
| mib2_ipNetToMediaEntry_t *Ep = (mib2_ipNetToMediaEntry_t *)ep; |
| |
| if ((strncmp(Ep->ipNetToMediaIfIndex.o_bytes, |
| ((struct ifreq *)ifrp)->ifr_name, |
| Ep->ipNetToMediaIfIndex.o_length) == 0) && |
| (s->sin_addr.s_addr == Ep->ipNetToMediaNetAddress)) |
| return (0); |
| else |
| return (1); |
| } |
| |
| #ifdef _STDC_COMPAT |
| #ifdef __cplusplus |
| } |
| #endif |
| #endif |
| |
| #ifdef _GETKSTAT_TEST |
| #include <stdio.h> |
| |
| void |
| main (int argc, char **argv) |
| { |
| int rc = 0; |
| u_long val = 0; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Usage: %s stat_name var_name\n", argv[0]); |
| exit(1); |
| } |
| |
| rc = getKstat(argv[1], argv[2], &val); |
| |
| if (rc == 0) |
| fprintf(stderr, "%s = %lu\n", argv[2], val); |
| else |
| fprintf(stderr, "rc =%d\n", rc); |
| } |
| #endif /*_GETKSTAT_TEST */ |
| |
| #ifdef _GETMIBSTAT_TEST |
| #include <stdio.h> |
| |
| int |
| ip20comp(void *ifname, void *ipp) |
| { |
| return(strncmp((char *)ifname, |
| ((mib2_ipAddrEntry_t *)ipp)->ipAdEntIfIndex.o_bytes, |
| ((mib2_ipAddrEntry_t *)ipp)->ipAdEntIfIndex.o_length)); |
| } |
| |
| int |
| ARP_Cmp_Addr(void *addr, void *ep) |
| { |
| if (((mib2_ipNetToMediaEntry_t *)ep)->ipNetToMediaNetAddress == |
| *(IpAddress *)addr) |
| return (0); |
| else |
| return (1); |
| } |
| int |
| IF_cmp(void *addr, void *ep) |
| { |
| if (((mib2_ifEntry_t *)ep)->ifIndex == ((mib2_ifEntry_t *)addr)->ifIndex) |
| return (0); |
| else |
| return (1); |
| } |
| |
| void |
| main (int argc, char **argv) |
| { |
| int rc = 0, i, idx; |
| mib2_ipAddrEntry_t ipbuf, *ipp = &ipbuf; |
| mib2_ipNetToMediaEntry_t entry, *ep = &entry; |
| mib2_ifEntry_t ifstat; |
| req_e req_type; |
| IpAddress LastAddr = -1; |
| |
| if (argc != 3) { |
| fprintf(stderr, "Usage: %s if_name req_type (0 first, 1 exact, 2 next) \n", |
| argv[0]); |
| exit(1); |
| } |
| switch (atoi(argv[2])) { |
| case 0: |
| req_type = GET_FIRST; |
| break; |
| case 1: |
| req_type = GET_EXACT; |
| break; |
| case 2: |
| req_type = GET_NEXT; |
| break; |
| }; |
| while ((rc = getMibstat(MIB_INTERFACES, &ifstat, sizeof(mib2_ifEntry_t), |
| req_type, &IF_cmp, &idx)) == 0) { |
| idx = ifstat.ifIndex; |
| fprintf(stdout, "Ifname = %s\n", ifstat.ifDescr.o_bytes); |
| req_type = GET_NEXT; |
| } |
| rc = getMibstat(MIB_IP_ADDR, &ipbuf, sizeof(mib2_ipAddrEntry_t), |
| req_type, ip20comp, argv[1]); |
| |
| if (rc == 0) |
| fprintf(stdout, "mtu = %d\n", ipp->ipAdEntInfo.ae_mtu); |
| else |
| fprintf(stderr, "rc =%d\n", rc); |
| |
| while ((rc = getMibstat(MIB_IP_NET, &entry, sizeof(mib2_ipNetToMediaEntry_t), |
| req_type, &ARP_Cmp_Addr, &LastAddr)) == 0) { |
| LastAddr = ep->ipNetToMediaNetAddress; |
| fprintf(stdout, "Ipaddr = %X\n", (u_long)LastAddr); |
| req_type = GET_NEXT; |
| } |
| |
| } |
| #endif /*_GETMIBSTAT_TEST */ |
| #endif /* SUNOS5 */ |
| |
| |
| /*- |
| * These variables describe the formatting of this file. If you don't like the |
| * template defaults, feel free to change them here (not in your .emacs file). |
| * |
| * Local Variables: |
| * comment-column: 32 |
| * c-indent-level: 4 |
| * c-continued-statement-offset: 4 |
| * c-brace-offset: -4 |
| * c-argdecl-indent: 0 |
| * c-label-offset: -4 |
| * fill-column: 79 |
| * fill-prefix: " * " |
| * End: |
| */ |