and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / kernel_sunos5.c
1 /*- This is a -*- C -*- compatible code file
2  *
3  * Code for SUNOS5_INSTRUMENTATION
4  *
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
8  * definitions.
9  *
10  * This file contains function to obtain statistics from SunOS 5.x kernel
11  *
12  */
13
14 #include <net-snmp/net-snmp-config.h>
15 #ifdef solaris2
16 /*-
17  * Includes of standard ANSI C header files 
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24
25 /*-
26  * Includes of system header files (wrapped in duplicate include prevention)
27  */
28
29 #include <fcntl.h>
30 #include <stropts.h>
31 #include <sys/types.h>
32 #include <kvm.h>
33 #include <sys/fcntl.h>
34 #include <kstat.h>
35 #include <errno.h>
36 #include <time.h>
37
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>
46 #include <inet/ip.h>
47 #include <net/if.h>
48 #include <netinet/in.h>
49
50 /*-
51  * Includes of local application header files 
52  */
53
54 #include <net-snmp/net-snmp-includes.h>
55 #include <net-snmp/agent/net-snmp-agent-includes.h>
56
57 #include "kernel_sunos5.h"
58
59 kstat_ctl_t    *kstat_fd = 0;
60
61 /*-
62  * Global variable definitions (with initialization)
63  */
64
65 /*-
66  * Static variable definitions (with initialization)
67  */
68
69 static
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,
73      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,
77      0},
78     {MIB_IP_ROUTE, 200 * sizeof(mib2_ipRouteEntry_t), (void *) -1, 0, 10,
79      0, 0},
80     {MIB_IP_NET, 100 * sizeof(mib2_ipNetToMediaEntry_t), (void *) -1, 0,
81      100, 0, 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,
85      0, 0},
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,
88      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},
93     {0},
94 };
95
96 static
97 mibmap          Mibmap[MIBCACHE_SIZE] = {
98     {MIB2_SYSTEM, 0,},
99     {MIB2_INTERFACES, 0,},
100     {MIB2_AT, 0,},
101     {MIB2_IP, 0,},
102     {MIB2_IP, MIB2_IP_20,},
103     {MIB2_IP, MIB2_IP_21,},
104     {MIB2_IP, MIB2_IP_22,},
105     {MIB2_ICMP, 0,},
106     {MIB2_TCP, 0,},
107     {MIB2_TCP, MIB2_TCP_13,},
108     {MIB2_UDP, 0,},
109     {MIB2_UDP, MIB2_UDP_5},
110     {MIB2_EGP, 0,},
111     {MIB2_CMOT, 0,},
112     {MIB2_TRANSMISSION, 0,},
113     {MIB2_SNMP, 0,},
114     {0},
115 };
116
117 static int      sd = -1;        /* /dev/ip stream descriptor. */
118
119 /*-
120  * Static function prototypes (use void as argument type if there are none)
121  */
122
123 static found_e
124 getentry(req_e req_type, void *bufaddr, size_t len, size_t entrysize,
125          void *resp, int (*comp)(void *, void *), void *arg);
126
127 static int
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);
131
132 static int
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);
135
136 static int
137 Name_cmp(void *, void *);
138
139 static void
140 init_mibcache_element(mibcache * cp);
141
142 #define STREAM_DEV      "/dev/ip"
143 #define BUFSIZE         40960   /* Buffer for  messages (should be modulo(pagesize) */
144
145 /*-
146  * Function definitions
147  */
148
149 #ifdef _STDC_COMPAT
150 #ifdef __cplusplus
151 extern          "C" {
152 #endif
153 #endif
154
155 /*
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.
169  */
170
171 static void
172 kernel_sunos5_cache_age(unsigned int regnumber, void *data)
173 {
174     int i = 0, period = (int)data;
175
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;
181         } else {
182             Mibcache[i].cache_time -= period;
183         }
184     }
185 }
186
187 void
188 init_kernel_sunos5(void)
189 {
190     static int creg   = 0;
191     const  int period = 5;
192
193     if (creg == 0) {
194         creg = snmp_alarm_register(period, SA_REPEAT, kernel_sunos5_cache_age,
195                                    (void *)period);
196         DEBUGMSGTL(("kernel_sunos5", "registered alarm %d with period %ds\n", 
197                     creg, period));
198     }
199 }
200
201 /*
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.
208  
209  * 
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. :-(
214  */
215
216
217 int
218 getKstatInt(const char *classname, const char *statname, 
219             const char *varname, int *value)
220 {
221     kstat_ctl_t    *ksc;
222     kstat_t        *ks;
223     kid_t           kid;
224     kstat_named_t  *named;
225     int             ret = 0;        /* fail unless ... */
226
227     if (kstat_fd == 0) {
228         kstat_fd = kstat_open();
229         if (kstat_fd == 0) {
230             snmp_log(LOG_ERR, "kstat_open(): failed\n");
231         }
232     }
233     if ((ksc = kstat_fd) == NULL) {
234         goto Return;
235     }
236     ks = kstat_lookup(ksc, classname, -1, statname);
237     if (ks == NULL) {
238         goto Return;
239     }
240     kid = kstat_read(ksc, ks, NULL);
241     if (kid == -1) {
242         goto Return;
243     }
244     named = kstat_data_lookup(ks, varname);
245     if (named == NULL) {
246         goto Return;
247     }
248
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;
254         break;
255     case KSTAT_DATA_UINT32:
256         *value = named->value.ui32;
257         break;
258     case KSTAT_DATA_INT64:
259         *value = named->value.i64;
260         break;
261     case KSTAT_DATA_UINT64:
262         *value = named->value.ui64;
263         break;
264 #else
265     case KSTAT_DATA_LONG:
266         *value = named->value.l;
267         break;
268     case KSTAT_DATA_ULONG:
269         *value = named->value.ul;
270         break;
271     case KSTAT_DATA_LONGLONG:
272         *value = named->value.ll;
273         break;
274     case KSTAT_DATA_ULONGLONG:
275         *value = named->value.ull;
276         break;
277 #endif
278     default:
279         DEBUGMSGTL(("kernel_sunos5", 
280                     "non-int type in kstat data: \"%s\" \"%s\" \"%s\" %d\n",
281                     classname, statname, varname, named->data_type));
282         ret = 0;            /* fail */
283         break;
284     }
285  Return:
286     return ret;
287 }
288
289 int
290 getKstat(const char *statname, const char *varname, void *value)
291 {
292     kstat_ctl_t    *ksc;
293     kstat_t        *ks, *kstat_data;
294     kstat_named_t  *d;
295     size_t          i, instance;
296     char            module_name[64];
297     int             ret;
298     u_longlong_t    val;    /* The largest value */
299     void           *v;
300
301     if (value == NULL) {      /* Pretty useless but ... */
302         v = (void *) &val;
303     } else {
304         v = value;
305     }
306
307     if (kstat_fd == 0) {
308         kstat_fd = kstat_open();
309         if (kstat_fd == 0) {
310             snmp_log(LOG_ERR, "kstat_open(): failed\n");
311         }
312     }
313     if ((ksc = kstat_fd) == NULL) {
314         ret = -10;
315         goto Return;        /* kstat errors */
316     }
317     if (statname == NULL || varname == NULL) {
318         ret = -20;
319         goto Return;
320     }
321
322     /*
323      * First, get "kstat_headers" statistics. It should
324      * contain all available modules. 
325      */
326
327     if ((ks = kstat_lookup(ksc, "unix", 0, "kstat_headers")) == NULL) {
328         ret = -10;
329         goto Return;        /* kstat errors */
330     }
331     if (kstat_read(ksc, ks, NULL) <= 0) {
332         ret = -10;
333         goto Return;        /* kstat errors */
334     }
335     kstat_data = ks->ks_data;
336     
337     /*
338      * Now, look for the name of our stat in the headers buf 
339      */
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;
349             break;
350         }
351     }
352     
353     if (i == ks->ks_ndata) {
354         ret = -1;
355         goto Return;        /* Not found */
356     }
357     
358     /*
359      * Get the named statistics 
360      */
361     if ((ks = kstat_lookup(ksc, module_name, instance, statname)) == NULL) {
362         ret = -10;
363         goto Return;        /* kstat errors */
364     }
365
366     if (kstat_read(ksc, ks, NULL) <= 0) {
367         ret = -10;
368         goto Return;        /* kstat errors */
369     }
370     /*
371      * This function expects only name/value type of statistics, so if it is
372      * not the case return an error
373      */
374     if (ks->ks_type != KSTAT_TYPE_NAMED) {
375         ret = -2;
376         goto Return;        /* Invalid stat type */
377     }
378     
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));
382
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));
388                 break;
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));
393                 break;
394             case KSTAT_DATA_UINT32:
395                 *(Counter *)v = d->value.ui32;
396                 DEBUGMSGTL(("kernel_sunos5", "value: %u\n", d->value.ui32));
397                 break;
398             case KSTAT_DATA_INT64:
399                 *(int64_t *)v = d->value.i64;
400                 DEBUGMSGTL(("kernel_sunos5", "value: %ld\n", d->value.i64));
401                 break;
402             case KSTAT_DATA_UINT64:
403                 *(uint64_t *)v = d->value.ui64;
404                 DEBUGMSGTL(("kernel_sunos5", "value: %lu\n", d->value.ui64));
405                 break;
406 #else
407             case KSTAT_DATA_LONG:
408                 *(Counter *)v = d->value.l;
409                 DEBUGMSGTL(("kernel_sunos5", "value: %ld\n", d->value.l));
410                 break;
411             case KSTAT_DATA_ULONG:
412                 *(Counter *)v = d->value.ul;
413                 DEBUGMSGTL(("kernel_sunos5", "value: %lu\n", d->value.ul));
414                 break;
415             case KSTAT_DATA_LONGLONG:
416                 *(Counter *)v = d->value.ll;
417                 DEBUGMSGTL(("kernel_sunos5", "value: %lld\n",
418                             (long)d->value.ll));
419                 break;
420             case KSTAT_DATA_ULONGLONG:
421                 *(Counter *)v = d->value.ull;
422                 DEBUGMSGTL(("kernel_sunos5", "value: %llu\n",
423                             (unsigned long)d->value.ull));
424                 break;
425 #endif
426             case KSTAT_DATA_FLOAT:
427                 *(float *)v = d->value.f;
428                 DEBUGMSGTL(("kernel_sunos5", "value: %f\n", d->value.f));
429                 break;
430             case KSTAT_DATA_DOUBLE:
431                 *(double *)v = d->value.d;
432                 DEBUGMSGTL(("kernel_sunos5", "value: %f\n", d->value.d));
433                 break;
434             default:
435                 DEBUGMSGTL(("kernel_sunos5",
436                             "UNKNOWN TYPE %d (stat \"%s\" var \"%s\")\n",
437                             d->data_type, statname, varname));
438                 ret = -3;
439                 goto Return;        /* Invalid data type */
440             }
441             ret = 0;        /* Success  */
442             goto Return;
443         }
444     }
445     ret = -4;               /* Name not found */
446  Return:
447     return ret;
448 }
449
450
451 /*
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.
457  */
458 int
459 getMibstat(mibgroup_e grid, void *resp, size_t entrysize,
460            req_e req_type, int (*comp) (void *, void *), void *arg)
461 {
462     int             ret, rc = -1, mibgr, mibtb, cache_valid;
463     size_t          length;
464     mibcache       *cachep;
465     found_e         result = NOT_FOUND;
466     void           *ep;
467
468     /*
469      * We assume that Mibcache is initialized in mibgroup_e enum order so we
470      * don't check the validity of index here.
471      */
472
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;
478
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;
485     }
486     if (req_type != GET_NEXT)
487         cachep->cache_last_found = 0;
488
489     cache_valid = (cachep->cache_time > 0);
490
491     DEBUGMSGTL(("kernel_sunos5","... cache_valid %d time %ld ttl %d now %ld\n",
492                 cache_valid, cachep->cache_time, cachep->cache_ttl,
493                 time(NULL)));
494     if (cache_valid) {
495         /*
496          * Is it really? 
497          */
498         if (cachep->cache_comp != (void *)comp || cachep->cache_arg != arg) {
499             cache_valid = 0;        /* Nope. */
500         }
501     }
502
503     if (cache_valid) {
504         /*
505          * Entry is valid, let's try to find a match 
506          */
507
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);
515             } else {
516                 result = getentry(req_type, cachep->cache_addr,
517                                   cachep->cache_length, entrysize, &ep, comp,
518                                   arg);
519             }
520     }
521
522     if ((cache_valid == 0) || (result == NOT_FOUND) ||
523         (result == NEED_NEXT && cachep->cache_flags & CACHE_MOREDATA)) {
524         /*
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.
528          */
529
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);
534         } else {
535             rc = getmib(mibgr, mibtb, cachep->cache_addr,
536                         cachep->cache_size, entrysize, req_type, &ep,
537                         &length, comp, arg);
538         }
539
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;
545             else
546                 cachep->cache_flags &= ~CACHE_MOREDATA;
547             cachep->cache_comp = (void *) comp;
548             cachep->cache_arg = arg;
549         } else {
550             cachep->cache_comp = NULL;
551             cachep->cache_arg = NULL;
552         }
553     }
554     DEBUGMSGTL(("kernel_sunos5", "... result %d rc %d\n", result, rc));
555     
556     if (result == FOUND || rc == 0 || rc == 1) {
557         /*
558          * Entry has been found, deliver it 
559          */
560         if (resp != NULL) {
561             memcpy(resp, ep, entrysize);
562         }
563         ret = 0;
564         cachep->cache_last_found =
565             ((char *)ep - (char *)cachep->cache_addr) / entrysize;
566     } else {
567         ret = 1;            /* Not found */
568     }
569     DEBUGMSGTL(("kernel_sunos5", "... getMibstat returns %d\n", ret));
570     return ret;
571 }
572
573 /*
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
577  * resp.
578  */
579
580 static found_e
581 getentry(req_e req_type, void *bufaddr, size_t len,
582          size_t entrysize, void *resp, int (*comp)(void *, void *),
583          void *arg)
584 {
585     void *bp = bufaddr, **rp = resp;
586     int previous_found = 0;
587     
588     /*
589      * Here we have to perform address arithmetic with pointer to void. Ugly...
590      */
591
592     for (; len != 0; len -= entrysize, bp = (char *) bp + entrysize) {
593         if (rp != (void *) NULL) {
594             *rp = bp;
595         }
596
597         if (req_type == GET_FIRST || (req_type == GET_NEXT && previous_found)){
598             return FOUND;
599         }
600
601         if ((*comp)(arg, bp) == 0) {
602             if (req_type == GET_EXACT) {
603                 return FOUND;
604             } else {        /* GET_NEXT */
605                 previous_found++;
606                 continue;
607             }
608         }
609     }
610
611     if (previous_found) {
612         return NEED_NEXT;
613     } else {
614         return NOT_FOUND;
615     }
616 }
617
618 /*
619  * Initialize a cache element. It allocates the memory and sets the time stamp
620  * to invalidate the element.
621  */
622 static void
623 init_mibcache_element(mibcache * cp)
624 {
625     if (cp == (mibcache *)NULL) {
626         return;
627     }
628     if (cp->cache_size) {
629         cp->cache_addr = malloc(cp->cache_size);
630     }
631     cp->cache_time = 0;
632     cp->cache_comp = NULL;
633     cp->cache_arg = NULL;
634 }
635
636 /*
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.
640  
641  * 
642  * Usage: groupname, subgroupname are from <inet/mib2.h>, 
643  *        size%sizeof(statbuf) == 0,
644  *        entrysize should be exact size of MIB-II entry,
645  *        req_type:
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
649  * 
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.
653  * 
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).
658  * 
659  * NOTE: needs to be protected by a mutex in reentrant environment 
660  */
661
662 static int
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)
666 {
667     int             rc, ret = 0, flags;
668     char            buf[BUFSIZE];
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;
673     struct opthdr  *req;
674     found_e         result = FOUND;
675
676     DEBUGMSGTL(("kernel_sunos5", "...... getmib (%d, %d, ...)\n",
677                 groupname, subgroupname));
678
679     /*
680      * Open the stream driver and push all MIB-related modules 
681      */
682
683     if (sd == -1) {         /* First time */
684         if ((sd = open(STREAM_DEV, O_RDWR)) == -1) {
685             ret = -1;
686             goto Return;
687         }
688         if (ioctl(sd, I_PUSH, "arp") == -1) {
689             ret = -1;
690             goto Return;
691         }
692         if (ioctl(sd, I_PUSH, "tcp") == -1) {
693             ret = -1;
694             goto Return;
695         }
696         if (ioctl(sd, I_PUSH, "udp") == -1) {
697             ret = -1;
698             goto Return;
699         }
700         DEBUGMSGTL(("kernel_sunos5", "...... modules pushed OK\n"));
701     }
702
703     /*
704      * First, use bigger buffer, to accelerate skipping unwanted messages
705      */
706
707     strbuf.buf = buf;
708     strbuf.maxlen = BUFSIZE;
709     
710     tor->PRIM_type = T_OPTMGMT_REQ;
711     tor->OPT_offset = sizeof(struct T_optmgmt_req);
712     tor->OPT_length = sizeof(struct opthdr);
713 #ifdef MI_T_CURRENT
714     tor->MGMT_flags = MI_T_CURRENT; /* Solaris < 2.6 */
715 #else
716     tor->MGMT_flags = T_CURRENT;    /* Solaris 2.6 */
717 #endif
718     req = (struct opthdr *)(tor + 1);
719     req->level = groupname;
720     req->name = subgroupname;
721     req->len = 0;
722     strbuf.len = tor->OPT_length + tor->OPT_offset;
723     flags = 0;
724     if ((rc = putmsg(sd, &strbuf, NULL, flags))) {
725         ret = -2;
726         goto Return;
727     }
728
729     req = (struct opthdr *) (toa + 1);
730     for (;;) {
731         flags = 0;
732         if ((rc = getmsg(sd, &strbuf, NULL, &flags)) == -1) {
733             ret = -EIO;
734             break;
735         }
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) {
739             ret = 2;
740             break;
741         }
742         if (strbuf.len >= sizeof(struct T_error_ack) &&
743             tea->PRIM_type == T_ERROR_ACK) {
744             /* Protocol error */
745             ret = -((tea->TLI_error == TSYSERR) ? tea->UNIX_error : EPROTO);
746             break;
747         }
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 */
752             break;
753         }
754
755         /*
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.
759          */
760
761         if (req->level != groupname || req->name != subgroupname) {
762             strbuf.maxlen = BUFSIZE;
763             strbuf.buf = buf;
764             do {
765                 rc = getmsg(sd, NULL, &strbuf, &flags);
766             } while (rc == MOREDATA);
767             continue;
768         }
769         
770         /*
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
774          */
775
776         strbuf.buf = statbuf;
777         strbuf.maxlen = size;
778         strbuf.len = 0;
779         flags = 0;
780         do {
781             rc = getmsg(sd, NULL, &strbuf, &flags);
782             switch (rc) {
783             case -1:
784                 rc = -ENOSR;
785                 goto Return;
786
787             default:
788                 rc = -ENODATA;
789                 goto Return;
790
791             case MOREDATA:
792             case 0:
793                 if (req_type == GET_NEXT && result == NEED_NEXT)
794                     /*
795                      * End of buffer, so "next" is the first item in the next
796                      * buffer  
797                      */
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 */
802                 break;
803             }
804         } while (rc == MOREDATA && result != FOUND);
805
806         if (result == FOUND) {      /* Search is successful */
807             if (rc != MOREDATA) {
808                 ret = 0;    /* Found and no more data */
809             } else {
810                 ret = 1;    /* Found and there is another unread data block */
811             }
812             break;
813         } else {            /* Restore buffers, continue search */
814             strbuf.buf = buf;
815             strbuf.maxlen = BUFSIZE;
816         }
817     }
818  Return:
819     ioctl(sd, I_FLUSH, FLUSHRW);
820     DEBUGMSGTL(("kernel_sunos5", "...... getmib returns %d\n", ret));
821     return ret;
822 }
823   
824 /*
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.
827  */
828 static int
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 *),
831       void *arg)
832 {
833     int             i, ret, idx = 1;
834     int             ifsd;
835     static char    *buf = NULL;
836     static int      bufsize = 0;
837     struct ifconf   ifconf;
838     struct ifreq   *ifrp;
839     mib2_ifEntry_t *ifp;
840     mib2_ipNetToMediaEntry_t Media;
841     int             nentries = size / sizeof(mib2_ifEntry_t);
842     found_e         result = NOT_FOUND;
843
844     if ((ifsd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
845         return -1;
846     }
847
848     if (!buf) {
849         bufsize = 10240;
850         buf = malloc(bufsize);
851         if (!buf) {
852             ret = -1;
853             goto Return;
854         }
855     }
856
857     ifconf.ifc_buf = buf;
858     ifconf.ifc_len = bufsize;
859     while (ioctl(ifsd, SIOCGIFCONF, &ifconf) == -1) {
860         bufsize += 10240;
861         free(buf);
862         buf = malloc(bufsize);
863         if (!buf) {
864             ret = -1;
865             goto Return;
866         }
867         ifconf.ifc_buf = buf;
868         ifconf.ifc_len = bufsize;
869     }
870
871  Again:
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++) {
875
876         DEBUGMSGTL(("kernel_sunos5", "...... getif %s\n", ifrp->ifr_name));
877
878         if (ioctl(ifsd, SIOCGIFFLAGS, ifrp) < 0) {
879             ret = -1;
880             DEBUGMSGTL(("kernel_sunos5", "...... SIOCGIFFLAGS failed\n"));
881             goto Return;
882         }
883
884         memset(ifp, 0, sizeof(mib2_ifEntry_t));
885         ifp->ifIndex = idx;
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 ...  */
891
892         if (ioctl(ifsd, SIOCGIFMTU, ifrp) < 0) {
893             ret = -1;
894             DEBUGMSGTL(("kernel_sunos5", "...... SIOCGIFMTU failed\n"));
895             goto Return;
896         }
897         ifp->ifMtu = ifrp->ifr_metric;
898         ifp->ifType = 1;
899         ifp->ifSpeed = 0;
900
901         if ((getKstat(ifrp->ifr_name, "ifspeed", &ifp->ifSpeed) == 0) &&
902             (ifp->ifSpeed != 0)) {
903             /*
904              * check for SunOS patch with half implemented ifSpeed 
905              */
906             if (ifp->ifSpeed < 10000) {
907                     ifp->ifSpeed *= 1000000;
908             }
909         } else if (getKstat(ifrp->ifr_name, "ifSpeed", &ifp->ifSpeed) == 0) {
910             /*
911              * this is good 
912              */
913         }
914
915         switch (ifrp->ifr_name[0]) {
916         case 'l':          /* le / lo / lane (ATM LAN Emulation) */
917             if (ifrp->ifr_name[1] == 'o') {
918                 if (!ifp->ifSpeed)
919                     ifp->ifSpeed = 127000000;
920                 ifp->ifType = 24;
921             } else if (ifrp->ifr_name[1] == 'e') {
922                 if (!ifp->ifSpeed)
923                     ifp->ifSpeed = 10000000;
924                 ifp->ifType = 6;
925             } else if (ifrp->ifr_name[1] == 'a') {
926                 if (!ifp->ifSpeed)
927                     ifp->ifSpeed = 155000000;
928                 ifp->ifType = 37;
929             }
930             break;
931
932         case 'g':          /* ge (gigabit ethernet card)  */
933             if (!ifp->ifSpeed)
934                 ifp->ifSpeed = 1000000000;
935             ifp->ifType = 6;
936             break;
937
938         case 'h':          /* hme (SBus card) */
939         case 'e':          /* eri (PCI card) */
940         case 'b':          /* be */
941         case 'd':          /* dmfe -- found on netra X1 */
942             if (!ifp->ifSpeed)
943                 ifp->ifSpeed = 100000000;
944             ifp->ifType = 6;
945             break;
946
947         case 'f':          /* fa (Fore ATM */
948             if (!ifp->ifSpeed)
949                 ifp->ifSpeed = 155000000;
950             ifp->ifType = 37;
951             break;
952
953         case 'q':         /* qe (QuadEther)/qa (Fore ATM)/qfe (QuadFastEther)*/
954             if (ifrp->ifr_name[1] == 'a') {
955                 if (!ifp->ifSpeed)
956                     ifp->ifSpeed = 155000000;
957                 ifp->ifType = 37;
958             } else if (ifrp->ifr_name[1] == 'e') {
959                 if (!ifp->ifSpeed)
960                     ifp->ifSpeed = 10000000;
961                 ifp->ifType = 6;
962             } else if (ifrp->ifr_name[1] == 'f') {
963                 if (!ifp->ifSpeed)
964                     ifp->ifSpeed = 100000000;
965                 ifp->ifType = 6;
966             }
967             break;
968         }
969
970         if (!strchr(ifrp->ifr_name, ':')) {
971             Counter l_tmp;
972
973             if (getKstat(ifrp->ifr_name, "ipackets", &ifp->ifInUcastPkts) < 0){
974                 ret = -1;
975                 goto Return;
976             }
977             
978             if (getKstat(ifrp->ifr_name, "rbytes", &ifp->ifInOctets) < 0) {
979                     ifp->ifInOctets = ifp->ifInUcastPkts * 308; /* XXX */
980             }
981             
982             if (getKstat(ifrp->ifr_name, "opackets",&ifp->ifOutUcastPkts) < 0){
983                 ret = -1;
984                 goto Return;
985             }
986             
987             if (getKstat(ifrp->ifr_name, "obytes", &ifp->ifOutOctets) < 0) {
988                 ifp->ifOutOctets = ifp->ifOutUcastPkts * 308;       /* XXX */
989             }
990
991             if (ifp->ifType == 24)  /* Loopback */
992                 continue;
993
994             if (getKstat(ifrp->ifr_name, "ierrors", &ifp->ifInErrors) < 0) {
995                 ret = -1;
996                 goto Return;
997             }
998
999             if (getKstat(ifrp->ifr_name, "oerrors", &ifp->ifOutErrors) < 0) {
1000                 ret = -1;
1001                 goto Return;
1002             }
1003
1004             if (getKstat(ifrp->ifr_name, "brdcstrcv",&ifp->ifInNUcastPkts)==0&&
1005                 getKstat(ifrp->ifr_name, "multircv", &l_tmp) == 0) {
1006                 ifp->ifInNUcastPkts += l_tmp;
1007             }
1008
1009             if (getKstat(ifrp->ifr_name,"brdcstxmt",&ifp->ifOutNUcastPkts)==0&&
1010                 getKstat(ifrp->ifr_name, "multixmt", &l_tmp) == 0) {
1011                 ifp->ifOutNUcastPkts += l_tmp;
1012             }
1013         }
1014
1015         /*
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 ..."
1019          */
1020
1021         if (ioctl(ifsd, SIOCGIFADDR, ifrp) < 0) {
1022             ret = -1;
1023             goto Return;
1024         }
1025
1026         if (getMibstat(MIB_IP_NET, &Media, sizeof(mib2_ipNetToMediaEntry_t),
1027                        GET_EXACT, &Name_cmp, ifrp) == 0) {
1028             ifp->ifPhysAddress = Media.ipNetToMediaPhysAddress;
1029         }
1030     }
1031
1032     if ((req_type == GET_NEXT) && (result == NEED_NEXT)) {
1033             /*
1034              * End of buffer, so "next" is the first item in the next buffer 
1035              */
1036             req_type = GET_FIRST;
1037     }
1038
1039     result = getentry(req_type, (void *) ifbuf, size, sizeof(mib2_ifEntry_t),
1040                       (void *)resp, comp, arg);
1041
1042     if ((result != FOUND) && (i == nentries) && 
1043         ((char *)ifrp < (char *)ifconf.ifc_buf + ifconf.ifc_len)) {
1044         /*
1045          * We reached the end of supplied buffer, but there is
1046          * some more stuff to read, so continue.
1047          */
1048         ifconf.ifc_len -= i * sizeof(struct ifreq);
1049         ifconf.ifc_req = ifrp;
1050         goto Again;
1051     }
1052
1053     if (result != FOUND) {
1054             ret = 2;
1055     } else {
1056         if ((char *)ifrp < (char *)ifconf.ifc_buf + ifconf.ifc_len) {
1057             ret = 1;        /* Found and more data to fetch */
1058         } else {
1059             ret = 0;        /* Found and no more data */
1060         }
1061         *length = i * sizeof(mib2_ifEntry_t);       /* Actual cache length */
1062     }
1063
1064  Return:
1065     close(ifsd);
1066     return ret;
1067 }
1068
1069 /*
1070  * Always TRUE. May be used as a comparison function in getMibstat
1071  * to obtain the whole table (GET_FIRST should be used) 
1072  */
1073 int
1074 Get_everything(void *x, void *y)
1075 {
1076     return 0;             /* Always TRUE */
1077 }
1078
1079 /*
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.
1082  */
1083 static int
1084 Name_cmp(void *ifrp, void *ep)
1085 {
1086     struct sockaddr_in *s = (struct sockaddr_in *)
1087                                            &(((struct ifreq *)ifrp)->ifr_addr);
1088     mib2_ipNetToMediaEntry_t *Ep = (mib2_ipNetToMediaEntry_t *)ep;
1089
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)) {
1094         return 0;
1095     } else {
1096         return 1;
1097     }
1098 }       
1099
1100 #ifdef _STDC_COMPAT
1101 #ifdef __cplusplus
1102 }
1103 #endif
1104 #endif
1105
1106 #ifdef _GETKSTAT_TEST
1107
1108 int
1109 main(int argc, char **argv)
1110 {
1111     int             rc = 0;
1112     u_long          val = 0;
1113
1114     if (argc != 3) {
1115         snmp_log(LOG_ERR, "Usage: %s stat_name var_name\n", argv[0]);
1116         exit(1);
1117     }
1118
1119     snmp_set_do_debugging(1);
1120     rc = getKstat(argv[1], argv[2], &val);
1121
1122     if (rc == 0)
1123         snmp_log(LOG_ERR, "%s = %lu\n", argv[2], val);
1124     else
1125         snmp_log(LOG_ERR, "rc =%d\n", rc);
1126     return 0;
1127 }
1128 #endif /*_GETKSTAT_TEST */
1129
1130 #ifdef _GETMIBSTAT_TEST
1131
1132 int
1133 ip20comp(void *ifname, void *ipp)
1134 {
1135     return (strncmp((char *) ifname,
1136                     ((mib2_ipAddrEntry_t *) ipp)->ipAdEntIfIndex.o_bytes,
1137                     ((mib2_ipAddrEntry_t *) ipp)->ipAdEntIfIndex.
1138                     o_length));
1139 }
1140
1141 int
1142 ARP_Cmp_Addr(void *addr, void *ep)
1143 {
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) {
1149         return 0;
1150     } else {
1151         return 1;
1152     }
1153 }
1154
1155 int
1156 IF_cmp(void *addr, void *ep)
1157 {
1158     if (((mib2_ifEntry_t *)ep)->ifIndex ==((mib2_ifEntry_t *)addr)->ifIndex) {
1159         return 0;
1160     } else {
1161         return 1;
1162     }
1163 }
1164
1165 int
1166 main(int argc, char **argv)
1167 {
1168     int             rc = 0, i, idx;
1169     mib2_ipAddrEntry_t ipbuf, *ipp = &ipbuf;
1170     mib2_ipNetToMediaEntry_t entry, *ep = &entry;
1171     mib2_ifEntry_t  ifstat;
1172     req_e           req_type;
1173     IpAddress       LastAddr = 0;
1174
1175     if (argc != 3) {
1176         snmp_log(LOG_ERR,
1177                  "Usage: %s if_name req_type (0 first, 1 exact, 2 next) \n",
1178                  argv[0]);
1179         exit(1);
1180     }
1181
1182     switch (atoi(argv[2])) {
1183     case 0:
1184         req_type = GET_FIRST;
1185         break;
1186     case 1:
1187         req_type = GET_EXACT;
1188         break;
1189     case 2:
1190         req_type = GET_NEXT;
1191         break;
1192     };
1193
1194     snmp_set_do_debugging(0);
1195     while ((rc =
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;
1202     }
1203     rc = getMibstat(MIB_IP_ADDR, &ipbuf, sizeof(mib2_ipAddrEntry_t),
1204                     req_type, ip20comp, argv[1]);
1205
1206     if (rc == 0)
1207         DEBUGMSGTL(("kernel_sunos5", "mtu = %ld\n",
1208                     ipp->ipAdEntInfo.ae_mtu));
1209     else
1210         DEBUGMSGTL(("kernel_sunos5", "rc =%d\n", rc));
1211
1212     while ((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;
1219     }
1220     return 0;
1221 }
1222 #endif /*_GETMIBSTAT_TEST */
1223 #endif                          /* SUNOS5 */
1224
1225
1226 /*-
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).
1229  *
1230  * Local Variables:
1231  * comment-column: 32
1232  * c-indent-level: 4
1233  * c-continued-statement-offset: 4
1234  * c-brace-offset: -4
1235  * c-argdecl-indent: 0
1236  * c-label-offset: -4
1237  * fill-column: 79
1238  * fill-prefix: " * "
1239  * End:
1240  */