added files
[bcm963xx.git] / userapps / opensource / net-snmp / apps / snmpnetstat / inet6.c
1 /****************************************************************
2         Copyright 1989, 1991, 1992 by Carnegie Mellon University
3
4                       All Rights Reserved
5
6 Permission to use, copy, modify, and distribute this software and its 
7 documentation for any purpose and without fee is hereby granted, 
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in 
10 supporting documentation, and that the name of CMU not be
11 used in advertising or publicity pertaining to distribution of the
12 software without specific, written prior permission.  
13
14 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 ******************************************************************/
22 /*
23  * Copyright (c) 1983,1988 Regents of the University of California.
24  * All rights reserved.
25  *
26  * Redistribution and use in source and binary forms are permitted
27  * provided that this notice is preserved and that due credit is given
28  * to the University of California at Berkeley. The name of the University
29  * may not be used to endorse or promote products derived from this
30  * software without specific prior written permission. This software
31  * is provided ``as is'' without express or implied warranty.
32  */
33
34 #include <net-snmp/net-snmp-config.h>
35
36 #ifdef INET6
37
38 #if HAVE_STDLIB_H
39 #include <stdlib.h>
40 #endif
41 #if HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #if HAVE_STRING_H
45 #include <string.h>
46 #else
47 #include <strings.h>
48 #endif
49
50 #include <stdio.h>
51
52 #if HAVE_SYS_PARAM_H
53 #include <sys/param.h>
54 #endif
55 #if HAVE_SYS_SELECT_H
56 #include <sys/select.h>
57 #endif
58 #if HAVE_NETINET_IN_H
59 #include <netinet/in.h>
60 #endif
61 #if HAVE_ARPA_INET_H
62 #include <arpa/inet.h>
63 #endif
64 #if HAVE_WINSOCK_H
65 #include <winsock.h>
66 #include "winstub.h"
67 #else
68 #include <sys/socket.h>
69 #include <netdb.h>
70 #endif
71
72 #include "main.h"
73 #include <net-snmp/net-snmp-includes.h>
74 #include "netstat.h"
75
76 static char    *inet6name(struct in6_addr *);
77
78 struct stat_table {
79     int             entry;      /* entry number in table */
80     /*
81      * format string to printf(description, value, plural(value)); 
82      */
83     /*
84      * warning: the %d must be before the %s 
85      */
86     char            description[80];
87 };
88
89 #if DEBUGGING_INFORMATION
90         /*
91          * The following tables provide useful debugging information.
92          * This isn't normally needed or easily accessible,
93          *   but could potentially be very useful.
94          * Rather than delete it totally, it's commented out
95          */
96 static oid      oid_ipstats[] = { 1, 3, 6, 1, 2, 1, 4, 0, 0 };
97 struct stat_table ip_stattab[] = {
98     {3, "%d total datagram%s received"},
99     {4, "%d datagram%s with header errors"},
100     {5, "%d datagram%s with an invalid destination address"},
101     {6, "%d datagram%s forwarded"},
102     {7, "%d datagram%s with unknown protocol"},
103     {8, "%d datagram%s discarded"},
104     {9, "%d datagram%s delivered"},
105     {10, "%d output datagram request%s"},
106     {11, "%d output datagram%s discarded"},
107     {12, "%d datagram%s with no route"},
108     {14, "%d fragment%s received"},
109     {15, "%d datagram%s reassembled"},
110     {16, "%d reassembly failure%s"},
111     {17, "%d datagram%s fragmented"},
112     {18, "%d fragmentation failure%s"},
113     {19, "%d fragment%s created"}
114 };
115
116 static oid      oid_udpstats[] = { 1, 3, 6, 1, 2, 1, 7, 0, 0 };
117 struct stat_table udp_stattab[] = {
118     {1, "%d total datagram%s received"},
119     {2, "%d datagram%s to invalid port"},
120     {3, "%d datagram%s dropped due to errors"},
121     {4, "%d output datagram request%s"}
122 };
123
124 static oid      oid_tcpstats[] = { 1, 3, 6, 1, 2, 1, 6, 0, 0 };
125 struct stat_table tcp_stattab[] = {
126     {5, "%d active open%s"},
127     {6, "%d passive open%s"},
128     {7, "%d failed attempt%s"},
129     {8, "%d reset%s of established connections"},
130     {9, "%d current established connection%s"},
131     {10, "%d segment%s received"},
132     {11, "%d segment%s sent"},
133     {12, "%d segment%s retransmitted"}
134 };
135
136 static oid      oid_icmpstats[] = { 1, 3, 6, 1, 2, 1, 5, 0, 0 };
137 struct stat_table icmp_stattab[] = {
138     {1, "%d total message%s received"},
139     {2, "%d message%s dropped due to errors"},
140     {14, "%d ouput message request%s"},
141     {15, "%d output message%s discarded"}
142 };
143
144 struct stat_table icmp_inhistogram[] = {
145     {3, "Destination unreachable: %d"},
146     {4, "Time Exceeded: %d"},
147     {5, "Parameter Problem: %d"},
148     {6, "Source Quench: %d"},
149     {7, "Redirect: %d"},
150     {8, "Echo Request: %d"},
151     {9, "Echo Reply: %d"},
152     {10, "Timestamp Request: %d"},
153     {11, "Timestamp Reply: %d"},
154     {12, "Address Mask Request: %d"},
155     {13, "Addrss Mask Reply:%d"},
156 };
157
158 struct stat_table icmp_outhistogram[] = {
159     {16, "Destination unreachable: %d"},
160     {17, "Time Exceeded: %d"},
161     {18, "Parameter Problem: %d"},
162     {19, "Source Quench: %d"},
163     {20, "Redirect: %d"},
164     {21, "Echo Request: %d"},
165     {22, "Echo Reply: %d"},
166     {23, "Timestamp Request: %d"},
167     {24, "Timestamp Reply: %d"},
168     {25, "Address Mask Request: %d"},
169     {26, "Addrss Mask Reply:%d"},
170 };
171 #endif
172
173 struct tcpconn_entry {
174     oid             instance[16 + 1 + 16 + 1 + 1];
175     struct in6_addr localAddress;
176     int             locAddrSet;
177     u_short         localPort;
178     int             locPortSet;
179     struct in6_addr remoteAddress;
180     int             remAddrSet;
181     u_short         remotePort;
182     int             remPortSet;
183     int             state;
184     int             stateSet;
185     struct tcpconn_entry *next;
186 };
187
188 struct udp_entry {
189     oid             instance[16 + 1 + 1];
190     struct in6_addr localAddress;
191     int             locAddrSet;
192     u_short         localPort;
193     int             locPortSet;
194     struct udp_entry *next;
195 };
196
197 #define TCPCONN_LOCADDR 1
198 #define TCPCONN_LOCPORT 2
199 #define TCPCONN_REMADDR 3
200 #define TCPCONN_REMPORT 4
201 #define TCPCONN_IFINDEX 5
202 #define TCPCONN_STATE   6
203
204 static oid      oid_tcpconntable[] = { 1, 3, 6, 1, 3, 86, 1, 16, 1 };
205 #define TCP_ENTRY 9
206
207 #define UDP_LOCADDR     1
208 #define UDP_LOCPORT     2
209 #define UDP_IFINDEX     3
210
211 static oid      oid_udptable[] = { 1, 3, 6, 1, 3, 87, 1, 1, 1 };
212 #define UDP_ENTRY 9
213
214 static const char *tcpstates[] = {
215     "",
216     "CLOSED",
217     "LISTEN",
218     "SYNSENT",
219     "SYNRECEIVED",
220     "ESTABLISHED",
221     "FINWAIT1",
222     "FINWAIT2",
223     "CLOSEWAIT",
224     "LASTACK",
225     "CLOSING",
226     "TIMEWAIT"
227 };
228 #define TCP_NSTATES 11
229
230 static int      validUShortAssign(unsigned short *, int, const char *);
231
232 /*
233  * Print a summary of connections related to an Internet
234  * protocol (currently only TCP).  For TCP, also give state of connection.
235  */
236 void
237 protopr6(const char *name)
238 {
239     struct tcpconn_entry *tcpconn = NULL, *tcplast = NULL, *tp, *newtp;
240     struct udp_entry *udpconn = NULL, *udplast = NULL, *up, *newup;
241     netsnmp_pdu    *request = NULL, *response = NULL;
242     netsnmp_variable_list *vp;
243     oid            *instance;
244     int             first, status;
245
246     response = NULL;
247     if (strncmp(name, "tcp", 3) == 0) {
248         request = snmp_pdu_create(SNMP_MSG_GETNEXT);
249         snmp_add_null_var(request, oid_tcpconntable,
250                           sizeof(oid_tcpconntable) / sizeof(oid));
251         status = STAT_SUCCESS;
252     } else
253         status = STAT_TIMEOUT;
254     while (status == STAT_SUCCESS) {
255         if (response)
256             snmp_free_pdu(response);
257         response = NULL;
258         status = snmp_synch_response(Session, request, &response);
259         if (status != STAT_SUCCESS
260             || response->errstat != SNMP_ERR_NOERROR) {
261             snmp_perror("SNMP request failed");
262             break;
263         }
264         vp = response->variables;
265         if (!vp)
266             break;
267         if (vp->name_length != 46 ||
268             memcmp(vp->name, oid_tcpconntable, sizeof(oid_tcpconntable))) {
269             break;
270         }
271
272         request = snmp_pdu_create(SNMP_MSG_GETNEXT);
273         snmp_add_null_var(request, vp->name, vp->name_length);
274
275         instance = vp->name + 10;
276         for (tp = tcpconn; tp != NULL; tp = tp->next) {
277             if (!memcmp(instance, tp->instance, sizeof(tp->instance)))
278                 break;
279         }
280         if (tp == NULL) {
281             tp = (struct tcpconn_entry *) calloc(1,
282                                                  sizeof(struct
283                                                         tcpconn_entry));
284             if (tp == NULL)
285                 break;
286             if (tcplast != NULL)
287                 tcplast->next = tp;
288             tcplast = tp;
289             if (tcpconn == NULL)
290                 tcpconn = tp;
291             memmove(tp->instance, instance, sizeof(tp->instance));
292         }
293
294         if (vp->name[TCP_ENTRY] == TCPCONN_STATE) {
295             tp->state = *vp->val.integer;
296             tp->stateSet = 1;
297         }
298
299         if (vp->name[TCP_ENTRY] == TCPCONN_LOCADDR) {
300             memmove(&tp->localAddress, vp->val.string,
301                     sizeof(struct in6_addr));
302             tp->locAddrSet = 1;
303         }
304
305         if (vp->name[TCP_ENTRY] == TCPCONN_LOCPORT) {
306             if (validUShortAssign(&tp->localPort, *vp->val.integer,
307                                   "TCPCONN_LOCPORT"))
308                 tp->locPortSet = 1;
309         }
310
311         if (vp->name[TCP_ENTRY] == TCPCONN_REMADDR) {
312             memmove(&tp->remoteAddress, vp->val.string,
313                     sizeof(struct in6_addr));
314             tp->remAddrSet = 1;
315         }
316
317         if (vp->name[TCP_ENTRY] == TCPCONN_REMPORT) {
318             if (validUShortAssign(&tp->remotePort, *vp->val.integer,
319                                   "TCPCONN_REMPORT"))
320                 tp->remPortSet = 1;
321         }
322     }
323     if (response)
324         snmp_free_pdu(response);
325     response = NULL;
326
327     for (first = 1, tp = tcpconn, newtp = NULL; tp != NULL; tp = tp->next) {
328         if (newtp)
329             free(newtp);
330         newtp = tp;
331         if (!(tp->stateSet && tp->locAddrSet
332               && tp->locPortSet && tp->remAddrSet && tp->remPortSet)) {
333             printf("incomplete entry\n");
334             continue;
335         }
336         if (!aflag && tp->state == MIB_TCPCONNSTATE_LISTEN)
337             continue;
338         if (first) {
339             printf("Active Internet (%s) Connections", name);
340             if (aflag)
341                 printf(" (including servers)");
342             putchar('\n');
343             printf("%-5.5s %-28.28s %-28.28s %s\n",
344                    "Proto", "Local Address", "Foreign Address", "(state)");
345             first = 0;
346         }
347         printf("%-5.5s ", name);
348         inet6print(&tp->localAddress, tp->localPort, name);
349         inet6print(&tp->remoteAddress, tp->remotePort, name);
350         if (tp->state < 1 || tp->state > TCP_NSTATES)
351             printf(" %d", tp->state);
352         else
353             printf(" %s", tcpstates[tp->state]);
354         putchar('\n');
355     }
356     if (newtp)
357         free(newtp);
358
359     response = NULL;
360     if (strncmp(name, "udp", 3) == 0) {
361         request = snmp_pdu_create(SNMP_MSG_GETNEXT);
362         snmp_add_null_var(request, oid_udptable,
363                           sizeof(oid_udptable) / sizeof(oid));
364         status = STAT_SUCCESS;
365     } else
366         status = STAT_TIMEOUT;
367     while (status == STAT_SUCCESS) {
368         if (response)
369             snmp_free_pdu(response);
370         response = NULL;
371         status = snmp_synch_response(Session, request, &response);
372         if (status != STAT_SUCCESS
373             || response->errstat != SNMP_ERR_NOERROR) {
374             fprintf(stderr, "SNMP request failed\n");
375             break;
376         }
377         vp = response->variables;
378         if (!vp)
379             break;
380         if (vp->name_length != 28 ||
381             memcmp(vp->name, oid_udptable, sizeof(oid_udptable))) {
382             break;
383         }
384
385         request = snmp_pdu_create(SNMP_MSG_GETNEXT);
386         snmp_add_null_var(request, vp->name, vp->name_length);
387
388         instance = vp->name + 10;
389         for (up = udpconn; up != NULL; up = up->next) {
390             if (!memcmp(instance, up->instance, sizeof(up->instance)))
391                 break;
392         }
393         if (up == NULL) {
394             up = (struct udp_entry *) calloc(1, sizeof(struct udp_entry));
395             if (up == NULL)
396                 break;
397             if (udplast != NULL)
398                 udplast->next = up;
399             udplast = up;
400             if (udpconn == NULL)
401                 udpconn = up;
402             memmove(up->instance, instance, sizeof(up->instance));
403         }
404
405         if (vp->name[UDP_ENTRY] == UDP_LOCADDR) {
406             memmove(&up->localAddress, vp->val.string,
407                     sizeof(struct in6_addr));
408             up->locAddrSet = 1;
409         }
410
411         if (vp->name[UDP_ENTRY] == UDP_LOCPORT) {
412             if (validUShortAssign(&up->localPort, *vp->val.integer,
413                                   "UDP_LOCPORT"))
414                 up->locPortSet = 1;
415         }
416     }
417     if (response)
418         snmp_free_pdu(response);
419     response = NULL;
420
421     for (first = 1, up = udpconn, newup = NULL; up != NULL; up = up->next) {
422         if (newup)
423             free(newup);
424         newup = up;
425         if (!(up->locAddrSet && up->locPortSet)) {
426             printf("incomplete entry\n");
427             continue;
428         }
429         if (first) {
430             printf("Active Internet (%s) Connections", name);
431             putchar('\n');
432             printf("%-5.5s %-28.28s\n", "Proto", "Local Address");
433             first = 0;
434         }
435         printf("%-5.5s ", name);
436         inet6print(&up->localAddress, up->localPort, name);
437         putchar('\n');
438     }
439     if (newup)
440         free(newup);
441
442 }
443
444 static int
445 validUShortAssign(unsigned short *pushort, int ival, const char *errstr)
446 {
447     u_long          ulval = (u_long) ival;
448     if (ival > 65535) {
449         printf("Warning: %s value %ld (0x%lx) is not a port address\n",
450                errstr, ulval, ulval);
451         return 0;
452     }
453     *pushort = (unsigned short) ulval;
454     return 1;
455 }
456
457
458 #if DEBUGGING_INFORMATION
459         /*
460          * The following routines print out useful debugging information.
461          * This isn't normally needed or easily accessible,
462          *   but could potentially be very useful.
463          * Rather than delete it totally, it's commented out
464          */
465 /*
466  * Dump UDP statistics structure.
467  */
468 void
469 udp_stats(void)
470 {
471     oid             varname[MAX_OID_LEN], *udpentry;
472     int             varname_len;
473     netsnmp_variable_list *var;
474     int             count;
475     struct stat_table *sp = udp_stattab;
476
477     memmove(varname, oid_udpstats, sizeof(oid_udpstats));
478     varname_len = sizeof(oid_udpstats) / sizeof(oid);
479     udpentry = varname + 7;
480     printf("udp:\n");
481     count = sizeof(udp_stattab) / sizeof(struct stat_table);
482     while (count--) {
483         *udpentry = sp->entry;
484         var = getvarbyname(Session, varname, varname_len);
485         if (var && var->val.integer) {
486             putchar('\t');
487             printf(sp->description, *var->val.integer,
488                    plural((int) *var->val.integer));
489             putchar('\n');
490         }
491         if (var)
492             snmp_free_var(var);
493         sp++;
494     }
495
496 }
497
498 /*
499  * Dump TCP statistics structure.
500  */
501 void
502 tcp_stats(void)
503 {
504     oid             varname[MAX_OID_LEN], *tcpentry;
505     int             varname_len;
506     netsnmp_variable_list *var;
507     int             count;
508     struct stat_table *sp = tcp_stattab;
509
510     memmove(varname, oid_tcpstats, sizeof(oid_tcpstats));
511     varname_len = sizeof(oid_tcpstats) / sizeof(oid);
512     tcpentry = varname + 7;
513     printf("tcp:\n");
514     count = sizeof(tcp_stattab) / sizeof(struct stat_table);
515     while (count--) {
516         *tcpentry = sp->entry;
517         var = getvarbyname(Session, varname, varname_len);
518         if (var && var->val.integer) {
519             putchar('\t');
520             printf(sp->description, *var->val.integer,
521                    plural((int) *var->val.integer));
522             putchar('\n');
523         }
524         if (var)
525             snmp_free_var(var);
526         sp++;
527     }
528
529 }
530
531 /*
532  * Dump IP statistics structure.
533  */
534 void
535 ip_stats(void)
536 {
537     oid             varname[MAX_OID_LEN], *ipentry;
538     int             varname_len;
539     netsnmp_variable_list *var;
540     int             count;
541     struct stat_table *sp = ip_stattab;
542
543     memmove(varname, oid_ipstats, sizeof(oid_ipstats));
544     varname_len = sizeof(oid_ipstats) / sizeof(oid);
545     ipentry = varname + 7;
546     printf("ip:\n");
547     count = sizeof(ip_stattab) / sizeof(struct stat_table);
548     while (count--) {
549         *ipentry = sp->entry;
550         var = getvarbyname(Session, varname, varname_len);
551         if (var && var->val.integer) {
552             putchar('\t');
553             printf(sp->description, *var->val.integer,
554                    plural((int) *var->val.integer));
555             putchar('\n');
556         }
557         if (var)
558             snmp_free_var(var);
559         sp++;
560     }
561
562 }
563
564 /*
565  * Dump ICMP statistics.
566  */
567 void
568 icmp_stats(void)
569 {
570     oid             varname[MAX_OID_LEN], *icmpentry;
571     int             varname_len;
572     netsnmp_variable_list *var;
573     int             count, first;
574     struct stat_table *sp;
575
576     memmove(varname, oid_icmpstats, sizeof(oid_icmpstats));
577     varname_len = sizeof(oid_icmpstats) / sizeof(oid);
578     icmpentry = varname + 7;
579     printf("icmp:\n");
580     sp = icmp_stattab;
581     count = sizeof(icmp_stattab) / sizeof(struct stat_table);
582     while (count--) {
583         *icmpentry = sp->entry;
584         var = getvarbyname(Session, varname, varname_len);
585         if (var && var->val.integer) {
586             putchar('\t');
587             printf(sp->description, *var->val.integer,
588                    plural((int) *var->val.integer));
589             putchar('\n');
590         }
591         if (var)
592             snmp_free_var(var);
593         sp++;
594     }
595
596     sp = icmp_outhistogram;
597     first = 1;
598     count = sizeof(icmp_outhistogram) / sizeof(struct stat_table);
599     while (count--) {
600         *icmpentry = sp->entry;
601         var = getvarbyname(Session, varname, varname_len);
602         if (var && var->val.integer && *var->val.integer != 0) {
603             if (first) {
604                 printf("\tOutput Histogram:\n");
605                 first = 0;
606             }
607             printf("\t\t");
608             printf(sp->description, *var->val.integer,
609                    plural((int) *var->val.integer));
610             putchar('\n');
611         }
612         if (var)
613             snmp_free_var(var);
614         sp++;
615     }
616
617     sp = icmp_inhistogram;
618     first = 1;
619     count = sizeof(icmp_inhistogram) / sizeof(struct stat_table);
620     while (count--) {
621         *icmpentry = sp->entry;
622         var = getvarbyname(Session, varname, varname_len);
623         if (var && var->val.integer && *var->val.integer != 0) {
624             if (first) {
625                 printf("\tInput Histogram:\n");
626                 first = 0;
627             }
628             printf("\t\t");
629             printf(sp->description, *var->val.integer,
630                    plural((int) *var->val.integer));
631             putchar('\n');
632         }
633         if (var)
634             snmp_free_var(var);
635         sp++;
636     }
637 }
638 #endif
639
640 /*
641  * Pretty print an Internet address (net address + port).
642  * If the nflag was specified, use numbers instead of names.
643  */
644 void
645 inet6print(struct in6_addr *in, u_short port, const char *proto)
646 {
647     struct servent *sp = 0;
648     char            line[80], *cp;
649     int             width;
650
651     sprintf(line, "%.*s.", 22, inet6name(in));
652     cp = strchr(line, '\0');
653     if (!nflag && port)
654         sp = getservbyport(htons(port), proto);
655     if (sp || port == 0)
656         sprintf(cp, "%.8s", sp ? sp->s_name : "*");
657     else
658         sprintf(cp, "%d", port);
659     width = 28;
660     printf(" %-*.*s", width, width, line);
661 }
662
663 /*
664  * Construct an Internet address representation.
665  * If the nflag has been supplied, give 
666  * numeric value, otherwise try for symbolic name.
667  */
668 static char    *
669 inet6name(struct in6_addr *in)
670 {
671     register char  *cp;
672     static char     line[50];
673     struct hostent *hp;
674     static char     domain[MAXHOSTNAMELEN + 1];
675     static int      first = 1;
676
677     if (first && !nflag) {
678         first = 0;
679         if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
680             (cp = strchr(domain, '.')))
681             (void) strcpy(domain, cp + 1);
682         else
683             domain[0] = 0;
684     }
685     cp = 0;
686     if (!nflag && !IN6_IS_ADDR_UNSPECIFIED(in)) {
687         hp = gethostbyaddr((char *) in, sizeof(*in), AF_INET6);
688         if (hp) {
689             if ((cp = strchr(hp->h_name, '.')) && !strcmp(cp + 1, domain))
690                 *cp = 0;
691             cp = hp->h_name;
692         }
693     }
694     if (IN6_IS_ADDR_UNSPECIFIED(in))
695         strcpy(line, "*");
696     else if (cp) {
697         strncpy(line, cp, sizeof(line));
698         line[ sizeof(line)-1 ] = 0;
699     } else
700         inet_ntop(AF_INET6, in, line, sizeof(line));
701     return (line);
702 }
703
704 #endif                          /* INET6 */