1 /*- This is a -*- C -*- compatible code file
3 * Code for SUNOS5_INSTRUMENTATION
5 * This file contains includes of standard and local system header files,
6 * includes of other application header files, global variable definitions,
7 * static variable definitions, static function prototypes, and function
10 * This file contains function to obtain statistics from SunOS 5.x kernel
14 #include <net-snmp/net-snmp-config.h>
17 * Includes of standard ANSI C header files
26 * Includes of system header files (wrapped in duplicate include prevention)
31 #include <sys/types.h>
33 #include <sys/fcntl.h>
38 #include <sys/sockio.h>
39 #include <sys/socket.h>
40 #include <sys/stream.h>
41 #include <sys/stropts.h>
42 #include <sys/tihdr.h>
43 #include <sys/tiuser.h>
44 #include <inet/common.h>
45 #include <inet/mib2.h>
48 #include <netinet/in.h>
51 * Includes of local application header files
54 #include <net-snmp/net-snmp-includes.h>
55 #include <net-snmp/agent/net-snmp-agent-includes.h>
57 #include "kernel_sunos5.h"
59 kstat_ctl_t *kstat_fd = 0;
62 * Global variable definitions (with initialization)
66 * Static variable definitions (with initialization)
70 mibcache Mibcache[MIBCACHE_SIZE] = {
71 {MIB_SYSTEM, 0, (void *) -1, 0, 0, 0, 0},
72 {MIB_INTERFACES, 10 * sizeof(mib2_ifEntry_t), (void *) -1, 0, 10, 0,
74 {MIB_AT, 0, (void *) -1, 0, 0, 0, 0},
75 {MIB_IP, sizeof(mib2_ip_t), (void *) -1, 0, 20, 0, 0},
76 {MIB_IP_ADDR, 20 * sizeof(mib2_ipAddrEntry_t), (void *) -1, 0, 20, 0,
78 {MIB_IP_ROUTE, 200 * sizeof(mib2_ipRouteEntry_t), (void *) -1, 0, 10,
80 {MIB_IP_NET, 100 * sizeof(mib2_ipNetToMediaEntry_t), (void *) -1, 0,
82 {MIB_ICMP, sizeof(mib2_icmp_t), (void *) -1, 0, 20, 0, 0},
83 {MIB_TCP, sizeof(mib2_tcp_t), (void *) -1, 0, 20, 0, 0},
84 {MIB_TCP_CONN, 1000 * sizeof(mib2_tcpConnEntry_t), (void *) -1, 0, 15,
86 {MIB_UDP, sizeof(mib2_udp_t), (void *) -1, 0, 15, 0, 0},
87 {MIB_UDP_LISTEN, 1000 * sizeof(mib2_udpEntry_t), (void *) -1, 0, 15, 0,
89 {MIB_EGP, 0, (void *) -1, 0, 0, 0, 0},
90 {MIB_CMOT, 0, (void *) -1, 0, 0, 0, 0},
91 {MIB_TRANSMISSION, 0, (void *) -1, 0, 0, 0, 0},
92 {MIB_SNMP, 0, (void *) -1, 0, 0, 0, 0},
97 mibmap Mibmap[MIBCACHE_SIZE] = {
99 {MIB2_INTERFACES, 0,},
102 {MIB2_IP, MIB2_IP_20,},
103 {MIB2_IP, MIB2_IP_21,},
104 {MIB2_IP, MIB2_IP_22,},
107 {MIB2_TCP, MIB2_TCP_13,},
109 {MIB2_UDP, MIB2_UDP_5},
112 {MIB2_TRANSMISSION, 0,},
117 static int sd = -1; /* /dev/ip stream descriptor. */
120 * Static function prototypes (use void as argument type if there are none)
124 getentry(req_e req_type, void *bufaddr, size_t len, size_t entrysize,
125 void *resp, int (*comp)(void *, void *), void *arg);
128 getmib(int groupname, int subgroupname, void *statbuf, size_t size,
129 size_t entrysize, req_e req_type, void *resp, size_t *length,
130 int (*comp)(void *, void *), void *arg);
133 getif(mib2_ifEntry_t *ifbuf, size_t size, req_e req_type, mib2_ifEntry_t *resp,
134 size_t *length, int (*comp)(void *, void *), void *arg);
137 Name_cmp(void *, void *);
140 init_mibcache_element(mibcache * cp);
142 #define STREAM_DEV "/dev/ip"
143 #define BUFSIZE 40960 /* Buffer for messages (should be modulo(pagesize) */
146 * Function definitions
156 * I profiled snmpd using Quantify on a Solaris 7 box, and it turned out that
157 * the calls to time() in getMibstat() were taking 18% of the total execution
158 * time of snmpd when doing simple walks over the whole tree. I guess it must
159 * be difficult for Sun hardware to tell the time or something ;-). Anyway,
160 * this seemed like it was negating the point of having the cache, so I have
161 * changed the code so that it runs a periodic alarm to age the cache entries
162 * instead. The meaning of the cache_ttl and cache_time members has changed to
163 * support this. cache_ttl is now the value that cache_time gets reset to when
164 * we fetch a value from the kernel; cache_time then ticks down to zero in
165 * steps of period (see below). When it reaches zero, the cache entry is no
166 * longer valid and we fetch a new one. The effect of this is the same as the
167 * previous code, but more efficient (because it's not calling time() for every
168 * variable fetched) when you are walking the tables. jbpn, 20020226.
172 kernel_sunos5_cache_age(unsigned int regnumber, void *data)
174 int i = 0, period = (int)data;
176 for (i = 0; i < MIBCACHE_SIZE; i++) {
177 DEBUGMSGTL(("kernel_sunos5", "cache[%d] time %ld ttl %d\n", i,
178 Mibcache[i].cache_time, Mibcache[i].cache_ttl));
179 if (Mibcache[i].cache_time < period) {
180 Mibcache[i].cache_time = 0;
182 Mibcache[i].cache_time -= period;
188 init_kernel_sunos5(void)
191 const int period = 5;
194 creg = snmp_alarm_register(period, SA_REPEAT, kernel_sunos5_cache_age,
196 DEBUGMSGTL(("kernel_sunos5", "registered alarm %d with period %ds\n",
202 * Get various kernel statistics using undocumented Solaris kstat interface.
203 * We need it mainly for getting network interface statistics, although it is
204 * generic enough to be used for any purpose. It knows about kstat_headers
205 * module names and by the name of the statistics it tries to figure out the
206 * rest of necessary information. Returns 0 in case of success and < 0 if
207 * there were any errors.
210 * NOTE: To use this function correctly you have to know the actual type of the
211 * value to be returned, so you may build the test program, figure out the type
212 * and use it. Exposing kstat data types to upper layers doesn't seem to be
213 * reasonable. In any case I'd expect more reasonable kstat interface. :-(
218 getKstatInt(const char *classname, const char *statname,
219 const char *varname, int *value)
224 kstat_named_t *named;
225 int ret = 0; /* fail unless ... */
228 kstat_fd = kstat_open();
230 snmp_log(LOG_ERR, "kstat_open(): failed\n");
233 if ((ksc = kstat_fd) == NULL) {
236 ks = kstat_lookup(ksc, classname, -1, statname);
240 kid = kstat_read(ksc, ks, NULL);
244 named = kstat_data_lookup(ks, varname);
249 ret = 1; /* maybe successful */
250 switch (named->data_type) {
251 #ifdef KSTAT_DATA_INT32 /* Solaris 2.6 and up */
252 case KSTAT_DATA_INT32:
253 *value = named->value.i32;
255 case KSTAT_DATA_UINT32:
256 *value = named->value.ui32;
258 case KSTAT_DATA_INT64:
259 *value = named->value.i64;
261 case KSTAT_DATA_UINT64:
262 *value = named->value.ui64;
265 case KSTAT_DATA_LONG:
266 *value = named->value.l;
268 case KSTAT_DATA_ULONG:
269 *value = named->value.ul;
271 case KSTAT_DATA_LONGLONG:
272 *value = named->value.ll;
274 case KSTAT_DATA_ULONGLONG:
275 *value = named->value.ull;
279 DEBUGMSGTL(("kernel_sunos5",
280 "non-int type in kstat data: \"%s\" \"%s\" \"%s\" %d\n",
281 classname, statname, varname, named->data_type));
290 getKstat(const char *statname, const char *varname, void *value)
293 kstat_t *ks, *kstat_data;
296 char module_name[64];
298 u_longlong_t val; /* The largest value */
301 if (value == NULL) { /* Pretty useless but ... */
308 kstat_fd = kstat_open();
310 snmp_log(LOG_ERR, "kstat_open(): failed\n");
313 if ((ksc = kstat_fd) == NULL) {
315 goto Return; /* kstat errors */
317 if (statname == NULL || varname == NULL) {
323 * First, get "kstat_headers" statistics. It should
324 * contain all available modules.
327 if ((ks = kstat_lookup(ksc, "unix", 0, "kstat_headers")) == NULL) {
329 goto Return; /* kstat errors */
331 if (kstat_read(ksc, ks, NULL) <= 0) {
333 goto Return; /* kstat errors */
335 kstat_data = ks->ks_data;
338 * Now, look for the name of our stat in the headers buf
340 for (i = 0; i < ks->ks_ndata; i++) {
341 DEBUGMSGTL(("kernel_sunos5",
342 "module: %s instance: %d name: %s class: %s type: %d flags: %x\n",
343 kstat_data[i].ks_module, kstat_data[i].ks_instance,
344 kstat_data[i].ks_name, kstat_data[i].ks_class,
345 kstat_data[i].ks_type, kstat_data[i].ks_flags));
346 if (strcmp(statname, kstat_data[i].ks_name) == 0) {
347 strcpy(module_name, kstat_data[i].ks_module);
348 instance = kstat_data[i].ks_instance;
353 if (i == ks->ks_ndata) {
355 goto Return; /* Not found */
359 * Get the named statistics
361 if ((ks = kstat_lookup(ksc, module_name, instance, statname)) == NULL) {
363 goto Return; /* kstat errors */
366 if (kstat_read(ksc, ks, NULL) <= 0) {
368 goto Return; /* kstat errors */
371 * This function expects only name/value type of statistics, so if it is
372 * not the case return an error
374 if (ks->ks_type != KSTAT_TYPE_NAMED) {
376 goto Return; /* Invalid stat type */
379 for (i = 0, d = KSTAT_NAMED_PTR(ks); i < ks->ks_ndata; i++, d++) {
380 DEBUGMSGTL(("kernel_sunos5", "variable: \"%s\" (type %d)\n",
381 d->name, d->data_type));
383 if (strcmp(d->name, varname) == 0) {
384 switch (d->data_type) {
385 case KSTAT_DATA_CHAR:
386 *(char *)v = (int)d->value.c;
387 DEBUGMSGTL(("kernel_sunos5", "value: %d\n", (int)d->value.c));
389 #ifdef KSTAT_DATA_INT32 /* Solaris 2.6 and up */
390 case KSTAT_DATA_INT32:
391 *(Counter *)v = d->value.i32;
392 DEBUGMSGTL(("kernel_sunos5", "value: %d\n", d->value.i32));
394 case KSTAT_DATA_UINT32:
395 *(Counter *)v = d->value.ui32;
396 DEBUGMSGTL(("kernel_sunos5", "value: %u\n", d->value.ui32));
398 case KSTAT_DATA_INT64:
399 *(int64_t *)v = d->value.i64;
400 DEBUGMSGTL(("kernel_sunos5", "value: %ld\n", d->value.i64));
402 case KSTAT_DATA_UINT64:
403 *(uint64_t *)v = d->value.ui64;
404 DEBUGMSGTL(("kernel_sunos5", "value: %lu\n", d->value.ui64));
407 case KSTAT_DATA_LONG:
408 *(Counter *)v = d->value.l;
409 DEBUGMSGTL(("kernel_sunos5", "value: %ld\n", d->value.l));
411 case KSTAT_DATA_ULONG:
412 *(Counter *)v = d->value.ul;
413 DEBUGMSGTL(("kernel_sunos5", "value: %lu\n", d->value.ul));
415 case KSTAT_DATA_LONGLONG:
416 *(Counter *)v = d->value.ll;
417 DEBUGMSGTL(("kernel_sunos5", "value: %lld\n",
420 case KSTAT_DATA_ULONGLONG:
421 *(Counter *)v = d->value.ull;
422 DEBUGMSGTL(("kernel_sunos5", "value: %llu\n",
423 (unsigned long)d->value.ull));
426 case KSTAT_DATA_FLOAT:
427 *(float *)v = d->value.f;
428 DEBUGMSGTL(("kernel_sunos5", "value: %f\n", d->value.f));
430 case KSTAT_DATA_DOUBLE:
431 *(double *)v = d->value.d;
432 DEBUGMSGTL(("kernel_sunos5", "value: %f\n", d->value.d));
435 DEBUGMSGTL(("kernel_sunos5",
436 "UNKNOWN TYPE %d (stat \"%s\" var \"%s\")\n",
437 d->data_type, statname, varname));
439 goto Return; /* Invalid data type */
441 ret = 0; /* Success */
445 ret = -4; /* Name not found */
452 * get MIB-II statistics. It maintaines a simple cache which buffers the last
453 * read block of MIB statistics (which may contain the whole table). It calls
454 * *comp to compare every entry with an entry pointed by arg. *comp should
455 * return 0 if comparison is successful. Req_type may be GET_FIRST, GET_EXACT,
456 * GET_NEXT. If search is successful getMibstat returns 0, otherwise 1.
459 getMibstat(mibgroup_e grid, void *resp, size_t entrysize,
460 req_e req_type, int (*comp) (void *, void *), void *arg)
462 int ret, rc = -1, mibgr, mibtb, cache_valid;
465 found_e result = NOT_FOUND;
469 * We assume that Mibcache is initialized in mibgroup_e enum order so we
470 * don't check the validity of index here.
473 DEBUGMSGTL(("kernel_sunos5", "getMibstat (%d, *, %d, %d, *, *)\n",
474 grid, entrysize, req_type));
475 cachep = &Mibcache[grid];
476 mibgr = Mibmap[grid].group;
477 mibtb = Mibmap[grid].table;
479 if (cachep->cache_addr == (void *) -1) /* Hasn't been initialized yet */
480 init_mibcache_element(cachep);
481 if (cachep->cache_size == 0) { /* Memory allocation problems */
482 cachep->cache_addr = resp; /* So use caller supplied address instead of cache */
483 cachep->cache_size = entrysize;
484 cachep->cache_last_found = 0;
486 if (req_type != GET_NEXT)
487 cachep->cache_last_found = 0;
489 cache_valid = (cachep->cache_time > 0);
491 DEBUGMSGTL(("kernel_sunos5","... cache_valid %d time %ld ttl %d now %ld\n",
492 cache_valid, cachep->cache_time, cachep->cache_ttl,
498 if (cachep->cache_comp != (void *)comp || cachep->cache_arg != arg) {
499 cache_valid = 0; /* Nope. */
505 * Entry is valid, let's try to find a match
508 if (req_type == GET_NEXT) {
509 result = getentry(req_type,
510 (void *)((char *)cachep->cache_addr +
511 (cachep->cache_last_found * entrysize)),
512 cachep->cache_length -
513 (cachep->cache_last_found * entrysize),
514 entrysize, &ep, comp, arg);
516 result = getentry(req_type, cachep->cache_addr,
517 cachep->cache_length, entrysize, &ep, comp,
522 if ((cache_valid == 0) || (result == NOT_FOUND) ||
523 (result == NEED_NEXT && cachep->cache_flags & CACHE_MOREDATA)) {
525 * Either the cache is old, or we haven't found anything, or need the
526 * next item which hasn't been read yet. In any case, fill the cache
527 * up and try to find our entry.
530 if (grid == MIB_INTERFACES) {
531 rc = getif((mib2_ifEntry_t *) cachep->cache_addr,
532 cachep->cache_size, req_type,
533 (mib2_ifEntry_t *) & ep, &length, comp, arg);
535 rc = getmib(mibgr, mibtb, cachep->cache_addr,
536 cachep->cache_size, entrysize, req_type, &ep,
540 if (rc >= 0) { /* Cache has been filled up */
541 cachep->cache_time = cachep->cache_ttl;
542 cachep->cache_length = length;
543 if (rc == 1) /* Found but there are more unread data */
544 cachep->cache_flags |= CACHE_MOREDATA;
546 cachep->cache_flags &= ~CACHE_MOREDATA;
547 cachep->cache_comp = (void *) comp;
548 cachep->cache_arg = arg;
550 cachep->cache_comp = NULL;
551 cachep->cache_arg = NULL;
554 DEBUGMSGTL(("kernel_sunos5", "... result %d rc %d\n", result, rc));
556 if (result == FOUND || rc == 0 || rc == 1) {
558 * Entry has been found, deliver it
561 memcpy(resp, ep, entrysize);
564 cachep->cache_last_found =
565 ((char *)ep - (char *)cachep->cache_addr) / entrysize;
567 ret = 1; /* Not found */
569 DEBUGMSGTL(("kernel_sunos5", "... getMibstat returns %d\n", ret));
574 * Get a MIB-II entry from the buffer buffaddr, which satisfies the criterion,
575 * computed by (*comp), which gets arg as the first argument and pointer to the
576 * current position in the buffer as the second. If found entry is pointed by
581 getentry(req_e req_type, void *bufaddr, size_t len,
582 size_t entrysize, void *resp, int (*comp)(void *, void *),
585 void *bp = bufaddr, **rp = resp;
586 int previous_found = 0;
589 * Here we have to perform address arithmetic with pointer to void. Ugly...
592 for (; len != 0; len -= entrysize, bp = (char *) bp + entrysize) {
593 if (rp != (void *) NULL) {
597 if (req_type == GET_FIRST || (req_type == GET_NEXT && previous_found)){
601 if ((*comp)(arg, bp) == 0) {
602 if (req_type == GET_EXACT) {
604 } else { /* GET_NEXT */
611 if (previous_found) {
619 * Initialize a cache element. It allocates the memory and sets the time stamp
620 * to invalidate the element.
623 init_mibcache_element(mibcache * cp)
625 if (cp == (mibcache *)NULL) {
628 if (cp->cache_size) {
629 cp->cache_addr = malloc(cp->cache_size);
632 cp->cache_comp = NULL;
633 cp->cache_arg = NULL;
637 * Get MIB-II statistics from the Solaris kernel. It uses undocumented
638 * interface to TCP/IP streams modules, which provides extended MIB-II for the
639 * following groups: ip, icmp, tcp, udp, egp.
642 * Usage: groupname, subgroupname are from <inet/mib2.h>,
643 * size%sizeof(statbuf) == 0,
644 * entrysize should be exact size of MIB-II entry,
646 * GET_FIRST - get the first entry in the buffer
647 * GET_EXACT - get exact match
648 * GET_NEXT - get next entry after the exact match
650 * (*comp) is a compare function, provided by the caller, which gets arg as the
651 * first argument and pointer to the current entry as th second. If compared,
652 * should return 0 and found entry will be pointed by resp.
654 * If search is successful and no more data to read, it returns 0,
655 * if successful and there is more data -- 1,
656 * if not found and end of data -- 2, any other errors -- < 0
657 * (negative error numbers are pretty random).
659 * NOTE: needs to be protected by a mutex in reentrant environment
663 getmib(int groupname, int subgroupname, void *statbuf, size_t size,
664 size_t entrysize, req_e req_type, void *resp,
665 size_t *length, int (*comp)(void *, void *), void *arg)
667 int rc, ret = 0, flags;
669 struct strbuf strbuf;
670 struct T_optmgmt_req *tor = (struct T_optmgmt_req *) buf;
671 struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *) buf;
672 struct T_error_ack *tea = (struct T_error_ack *) buf;
674 found_e result = FOUND;
676 DEBUGMSGTL(("kernel_sunos5", "...... getmib (%d, %d, ...)\n",
677 groupname, subgroupname));
680 * Open the stream driver and push all MIB-related modules
683 if (sd == -1) { /* First time */
684 if ((sd = open(STREAM_DEV, O_RDWR)) == -1) {
688 if (ioctl(sd, I_PUSH, "arp") == -1) {
692 if (ioctl(sd, I_PUSH, "tcp") == -1) {
696 if (ioctl(sd, I_PUSH, "udp") == -1) {
700 DEBUGMSGTL(("kernel_sunos5", "...... modules pushed OK\n"));
704 * First, use bigger buffer, to accelerate skipping unwanted messages
708 strbuf.maxlen = BUFSIZE;
710 tor->PRIM_type = T_OPTMGMT_REQ;
711 tor->OPT_offset = sizeof(struct T_optmgmt_req);
712 tor->OPT_length = sizeof(struct opthdr);
714 tor->MGMT_flags = MI_T_CURRENT; /* Solaris < 2.6 */
716 tor->MGMT_flags = T_CURRENT; /* Solaris 2.6 */
718 req = (struct opthdr *)(tor + 1);
719 req->level = groupname;
720 req->name = subgroupname;
722 strbuf.len = tor->OPT_length + tor->OPT_offset;
724 if ((rc = putmsg(sd, &strbuf, NULL, flags))) {
729 req = (struct opthdr *) (toa + 1);
732 if ((rc = getmsg(sd, &strbuf, NULL, &flags)) == -1) {
736 if (rc == 0 && strbuf.len >= sizeof(struct T_optmgmt_ack) &&
737 toa->PRIM_type == T_OPTMGMT_ACK &&
738 toa->MGMT_flags == T_SUCCESS && req->len == 0) {
742 if (strbuf.len >= sizeof(struct T_error_ack) &&
743 tea->PRIM_type == T_ERROR_ACK) {
745 ret = -((tea->TLI_error == TSYSERR) ? tea->UNIX_error : EPROTO);
748 if (rc != MOREDATA || strbuf.len < sizeof(struct T_optmgmt_ack) ||
749 toa->PRIM_type != T_OPTMGMT_ACK ||
750 toa->MGMT_flags != T_SUCCESS) {
751 ret = -ENOMSG; /* No more messages */
756 * The order in which we get the statistics is determined by the kernel
757 * and not by the group name, so we have to loop until we get the
758 * required statistics.
761 if (req->level != groupname || req->name != subgroupname) {
762 strbuf.maxlen = BUFSIZE;
765 rc = getmsg(sd, NULL, &strbuf, &flags);
766 } while (rc == MOREDATA);
771 * Now when we found our stat, switch buffer to a caller-provided
772 * one. Manipulating the size of it one can control performance,
773 * reducing the number of getmsg calls
776 strbuf.buf = statbuf;
777 strbuf.maxlen = size;
781 rc = getmsg(sd, NULL, &strbuf, &flags);
793 if (req_type == GET_NEXT && result == NEED_NEXT)
795 * End of buffer, so "next" is the first item in the next
798 req_type = GET_FIRST;
799 result = getentry(req_type, (void *) strbuf.buf, strbuf.len,
800 entrysize, resp, comp, arg);
801 *length = strbuf.len; /* To use in caller for cacheing */
804 } while (rc == MOREDATA && result != FOUND);
806 if (result == FOUND) { /* Search is successful */
807 if (rc != MOREDATA) {
808 ret = 0; /* Found and no more data */
810 ret = 1; /* Found and there is another unread data block */
813 } else { /* Restore buffers, continue search */
815 strbuf.maxlen = BUFSIZE;
819 ioctl(sd, I_FLUSH, FLUSHRW);
820 DEBUGMSGTL(("kernel_sunos5", "...... getmib returns %d\n", ret));
825 * Get info for interfaces group. Mimics getmib interface as much as possible
826 * to be substituted later if SunSoft decides to extend its mib2 interface.
829 getif(mib2_ifEntry_t *ifbuf, size_t size, req_e req_type,
830 mib2_ifEntry_t *resp, size_t *length, int (*comp)(void *, void *),
835 static char *buf = NULL;
836 static int bufsize = 0;
837 struct ifconf ifconf;
840 mib2_ipNetToMediaEntry_t Media;
841 int nentries = size / sizeof(mib2_ifEntry_t);
842 found_e result = NOT_FOUND;
844 if ((ifsd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
850 buf = malloc(bufsize);
857 ifconf.ifc_buf = buf;
858 ifconf.ifc_len = bufsize;
859 while (ioctl(ifsd, SIOCGIFCONF, &ifconf) == -1) {
862 buf = malloc(bufsize);
867 ifconf.ifc_buf = buf;
868 ifconf.ifc_len = bufsize;
872 for (i = 0, ifp = (mib2_ifEntry_t *) ifbuf, ifrp = ifconf.ifc_req;
873 ((char *) ifrp < ((char *) ifconf.ifc_buf + ifconf.ifc_len))
874 && (i < nentries); i++, ifp++, ifrp++, idx++) {
876 DEBUGMSGTL(("kernel_sunos5", "...... getif %s\n", ifrp->ifr_name));
878 if (ioctl(ifsd, SIOCGIFFLAGS, ifrp) < 0) {
880 DEBUGMSGTL(("kernel_sunos5", "...... SIOCGIFFLAGS failed\n"));
884 memset(ifp, 0, sizeof(mib2_ifEntry_t));
886 ifp->ifDescr.o_length = strlen(ifrp->ifr_name);
887 strcpy(ifp->ifDescr.o_bytes, ifrp->ifr_name);
888 ifp->ifAdminStatus = (ifrp->ifr_flags & IFF_RUNNING) ? 1 : 2;
889 ifp->ifOperStatus = (ifrp->ifr_flags & IFF_UP) ? 1 : 2;
890 ifp->ifLastChange = 0; /* Who knows ... */
892 if (ioctl(ifsd, SIOCGIFMTU, ifrp) < 0) {
894 DEBUGMSGTL(("kernel_sunos5", "...... SIOCGIFMTU failed\n"));
897 ifp->ifMtu = ifrp->ifr_metric;
901 if ((getKstat(ifrp->ifr_name, "ifspeed", &ifp->ifSpeed) == 0) &&
902 (ifp->ifSpeed != 0)) {
904 * check for SunOS patch with half implemented ifSpeed
906 if (ifp->ifSpeed < 10000) {
907 ifp->ifSpeed *= 1000000;
909 } else if (getKstat(ifrp->ifr_name, "ifSpeed", &ifp->ifSpeed) == 0) {
915 switch (ifrp->ifr_name[0]) {
916 case 'l': /* le / lo / lane (ATM LAN Emulation) */
917 if (ifrp->ifr_name[1] == 'o') {
919 ifp->ifSpeed = 127000000;
921 } else if (ifrp->ifr_name[1] == 'e') {
923 ifp->ifSpeed = 10000000;
925 } else if (ifrp->ifr_name[1] == 'a') {
927 ifp->ifSpeed = 155000000;
932 case 'g': /* ge (gigabit ethernet card) */
934 ifp->ifSpeed = 1000000000;
938 case 'h': /* hme (SBus card) */
939 case 'e': /* eri (PCI card) */
941 case 'd': /* dmfe -- found on netra X1 */
943 ifp->ifSpeed = 100000000;
947 case 'f': /* fa (Fore ATM */
949 ifp->ifSpeed = 155000000;
953 case 'q': /* qe (QuadEther)/qa (Fore ATM)/qfe (QuadFastEther)*/
954 if (ifrp->ifr_name[1] == 'a') {
956 ifp->ifSpeed = 155000000;
958 } else if (ifrp->ifr_name[1] == 'e') {
960 ifp->ifSpeed = 10000000;
962 } else if (ifrp->ifr_name[1] == 'f') {
964 ifp->ifSpeed = 100000000;
970 if (!strchr(ifrp->ifr_name, ':')) {
973 if (getKstat(ifrp->ifr_name, "ipackets", &ifp->ifInUcastPkts) < 0){
978 if (getKstat(ifrp->ifr_name, "rbytes", &ifp->ifInOctets) < 0) {
979 ifp->ifInOctets = ifp->ifInUcastPkts * 308; /* XXX */
982 if (getKstat(ifrp->ifr_name, "opackets",&ifp->ifOutUcastPkts) < 0){
987 if (getKstat(ifrp->ifr_name, "obytes", &ifp->ifOutOctets) < 0) {
988 ifp->ifOutOctets = ifp->ifOutUcastPkts * 308; /* XXX */
991 if (ifp->ifType == 24) /* Loopback */
994 if (getKstat(ifrp->ifr_name, "ierrors", &ifp->ifInErrors) < 0) {
999 if (getKstat(ifrp->ifr_name, "oerrors", &ifp->ifOutErrors) < 0) {
1004 if (getKstat(ifrp->ifr_name, "brdcstrcv",&ifp->ifInNUcastPkts)==0&&
1005 getKstat(ifrp->ifr_name, "multircv", &l_tmp) == 0) {
1006 ifp->ifInNUcastPkts += l_tmp;
1009 if (getKstat(ifrp->ifr_name,"brdcstxmt",&ifp->ifOutNUcastPkts)==0&&
1010 getKstat(ifrp->ifr_name, "multixmt", &l_tmp) == 0) {
1011 ifp->ifOutNUcastPkts += l_tmp;
1016 * An attempt to determine the physical address of the interface.
1017 * There should be a more elegant solution using DLPI, but "the margin
1018 * is too small to put it here ..."
1021 if (ioctl(ifsd, SIOCGIFADDR, ifrp) < 0) {
1026 if (getMibstat(MIB_IP_NET, &Media, sizeof(mib2_ipNetToMediaEntry_t),
1027 GET_EXACT, &Name_cmp, ifrp) == 0) {
1028 ifp->ifPhysAddress = Media.ipNetToMediaPhysAddress;
1032 if ((req_type == GET_NEXT) && (result == NEED_NEXT)) {
1034 * End of buffer, so "next" is the first item in the next buffer
1036 req_type = GET_FIRST;
1039 result = getentry(req_type, (void *) ifbuf, size, sizeof(mib2_ifEntry_t),
1040 (void *)resp, comp, arg);
1042 if ((result != FOUND) && (i == nentries) &&
1043 ((char *)ifrp < (char *)ifconf.ifc_buf + ifconf.ifc_len)) {
1045 * We reached the end of supplied buffer, but there is
1046 * some more stuff to read, so continue.
1048 ifconf.ifc_len -= i * sizeof(struct ifreq);
1049 ifconf.ifc_req = ifrp;
1053 if (result != FOUND) {
1056 if ((char *)ifrp < (char *)ifconf.ifc_buf + ifconf.ifc_len) {
1057 ret = 1; /* Found and more data to fetch */
1059 ret = 0; /* Found and no more data */
1061 *length = i * sizeof(mib2_ifEntry_t); /* Actual cache length */
1070 * Always TRUE. May be used as a comparison function in getMibstat
1071 * to obtain the whole table (GET_FIRST should be used)
1074 Get_everything(void *x, void *y)
1076 return 0; /* Always TRUE */
1080 * Compare name and IP address of the interface to ARP table entry.
1081 * Needed to obtain the physical address of the interface in getif.
1084 Name_cmp(void *ifrp, void *ep)
1086 struct sockaddr_in *s = (struct sockaddr_in *)
1087 &(((struct ifreq *)ifrp)->ifr_addr);
1088 mib2_ipNetToMediaEntry_t *Ep = (mib2_ipNetToMediaEntry_t *)ep;
1090 if ((strncmp(Ep->ipNetToMediaIfIndex.o_bytes,
1091 ((struct ifreq *)ifrp)->ifr_name,
1092 Ep->ipNetToMediaIfIndex.o_length) == 0) &&
1093 (s->sin_addr.s_addr == Ep->ipNetToMediaNetAddress)) {
1106 #ifdef _GETKSTAT_TEST
1109 main(int argc, char **argv)
1115 snmp_log(LOG_ERR, "Usage: %s stat_name var_name\n", argv[0]);
1119 snmp_set_do_debugging(1);
1120 rc = getKstat(argv[1], argv[2], &val);
1123 snmp_log(LOG_ERR, "%s = %lu\n", argv[2], val);
1125 snmp_log(LOG_ERR, "rc =%d\n", rc);
1128 #endif /*_GETKSTAT_TEST */
1130 #ifdef _GETMIBSTAT_TEST
1133 ip20comp(void *ifname, void *ipp)
1135 return (strncmp((char *) ifname,
1136 ((mib2_ipAddrEntry_t *) ipp)->ipAdEntIfIndex.o_bytes,
1137 ((mib2_ipAddrEntry_t *) ipp)->ipAdEntIfIndex.
1142 ARP_Cmp_Addr(void *addr, void *ep)
1144 DEBUGMSGTL(("kernel_sunos5", "ARP: %lx <> %lx\n",
1145 ((mib2_ipNetToMediaEntry_t *) ep)->ipNetToMediaNetAddress,
1146 *(IpAddress *) addr));
1147 if (((mib2_ipNetToMediaEntry_t *) ep)->ipNetToMediaNetAddress ==
1148 *(IpAddress *)addr) {
1156 IF_cmp(void *addr, void *ep)
1158 if (((mib2_ifEntry_t *)ep)->ifIndex ==((mib2_ifEntry_t *)addr)->ifIndex) {
1166 main(int argc, char **argv)
1169 mib2_ipAddrEntry_t ipbuf, *ipp = &ipbuf;
1170 mib2_ipNetToMediaEntry_t entry, *ep = &entry;
1171 mib2_ifEntry_t ifstat;
1173 IpAddress LastAddr = 0;
1177 "Usage: %s if_name req_type (0 first, 1 exact, 2 next) \n",
1182 switch (atoi(argv[2])) {
1184 req_type = GET_FIRST;
1187 req_type = GET_EXACT;
1190 req_type = GET_NEXT;
1194 snmp_set_do_debugging(0);
1196 getMibstat(MIB_INTERFACES, &ifstat, sizeof(mib2_ifEntry_t),
1197 req_type, &IF_cmp, &idx)) == 0) {
1198 idx = ifstat.ifIndex;
1199 DEBUGMSGTL(("kernel_sunos5", "Ifname = %s\n",
1200 ifstat.ifDescr.o_bytes));
1201 req_type = GET_NEXT;
1203 rc = getMibstat(MIB_IP_ADDR, &ipbuf, sizeof(mib2_ipAddrEntry_t),
1204 req_type, ip20comp, argv[1]);
1207 DEBUGMSGTL(("kernel_sunos5", "mtu = %ld\n",
1208 ipp->ipAdEntInfo.ae_mtu));
1210 DEBUGMSGTL(("kernel_sunos5", "rc =%d\n", rc));
1213 getMibstat(MIB_IP_NET, &entry,
1214 sizeof(mib2_ipNetToMediaEntry_t), req_type,
1215 &ARP_Cmp_Addr, &LastAddr)) == 0) {
1216 LastAddr = ep->ipNetToMediaNetAddress;
1217 DEBUGMSGTL(("kernel_sunos5", "Ipaddr = %lX\n", (u_long) LastAddr));
1218 req_type = GET_NEXT;
1222 #endif /*_GETMIBSTAT_TEST */
1227 * These variables describe the formatting of this file. If you don't like the
1228 * template defaults, feel free to change them here (not in your .emacs file).
1231 * comment-column: 32
1233 * c-continued-statement-offset: 4
1234 * c-brace-offset: -4
1235 * c-argdecl-indent: 0
1236 * c-label-offset: -4
1238 * fill-prefix: " * "