/* $OpenBSD: main.c,v 1.52 2005/02/10 14:25:08 itojun Exp $ */ /* $NetBSD: main.c,v 1.9 1996/05/07 02:55:02 thorpej Exp $ */ /* * Copyright (c) 1983, 1988, 1993 * Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1983, 1988, 1993\n\ Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifdef INHERITED_CODE #ifndef lint #if 0 static char sccsid[] = "from: @(#)main.c 8.4 (Berkeley) 3/1/94"; #else static char *rcsid = "$OpenBSD: main.c,v 1.52 2005/02/10 14:25:08 itojun Exp $"; #endif #endif /* not lint */ #endif #include #include #include #if HAVE_NETDB_H #include #endif #include "main.h" #include "netstat.h" #if HAVE_WINSOCK_H #include "winstub.h" #endif int aflag; /* show all sockets (including servers) */ int bflag; /* show bytes instead of packets */ int dflag; /* show i/f dropped packets */ int gflag; /* show group (multicast) routing or stats */ int iflag; /* show interfaces */ int lflag; /* show routing table with use and ref */ int Lflag; /* Legacy mibs */ int mflag; /* show memory stats */ int nflag; /* show addresses numerically */ int oflag; /* Open/Net-BSD style octet output */ int pflag; /* show given protocol */ int qflag; /* only display non-zero values for output */ int rflag; /* show routing tables (or routing stats) */ int Sflag; /* show source address in routing table */ int sflag; /* show protocol statistics */ int tflag; /* show i/f watchdog timers */ int vflag; /* be verbose */ int interval; /* repeat interval for i/f stats */ char *intrface; /* desired i/f for stats, or NULL for all i/fs */ int af; /* address family */ int max_getbulk = 32; /* specifies the max-repeaters value to use with GETBULK requests */ char *progname = NULL; const char *pname; /* * struct nlist nl[] - Omitted */ typedef void (stringfun)(const char*); struct protox { /* pr_index/pr_sindex - Omitted */ int pr_wanted; /* 1 if wanted, 0 otherwise */ stringfun *pr_cblocks; /* control blocks printing routine */ stringfun *pr_stats; /* statistics printing routine */ const char *pr_name; /* well-known name */ }; struct protox protox[] = { { 1, tcpprotopr, tcp_stats, "tcp" }, { 1, udpprotopr, udp_stats, "udp" }, { 1, (stringfun*)0, ip_stats, "ip" }, /* protopr Omitted */ { 1, (stringfun*)0, icmp_stats, "icmp" }, /* igmp/ah/esp/ipencap/etherip/ipcomp/carp/pfsync/pim - Omitted */ { 0, (stringfun*)0, (stringfun*)0, NULL } }; struct protox ip6protox[] = { { 1, tcp6protopr, (stringfun*)0, "tcp6" }, { 1, udp6protopr, (stringfun*)0, "udp6" }, { 1, (stringfun*)0, ip6_stats, "ip6" },/* ip6protopr Omitted */ { 1, (stringfun*)0, icmp6_stats, "icmp6" }, /* pim6/rip6 - Omitted */ { 0, (stringfun*)0, (stringfun*)0, NULL } }; struct protox ipxprotox[] = { { 1, tcpxprotopr, tcp_stats, "tcp" }, { 1, udpxprotopr, udp_stats, "udp" }, { 1, (stringfun*)0, ipx_stats, "ip" },/* ip6protopr Omitted */ { 1, (stringfun*)0, ipx_stats, "ip6" },/* ip6protopr Omitted */ { 1, (stringfun*)0, icmpx_stats, "icmp" }, { 1, (stringfun*)0, icmpx_stats, "icmp6" }, { 0, (stringfun*)0, (stringfun*)0, NULL } }; /* {ipx,ns,atalk}protox Omitted */ struct protox *protoprotox[] = { protox, ip6protox, NULL }; static void printproto(struct protox *, const char *); static void usage(void); static struct protox *name2protox(const char *); static struct protox *knownname(const char *); netsnmp_session *ss; struct protox *tp = NULL; /* for printing cblocks & stats */ static void optProc( int argc, char *const *argv, int opt ) { switch (opt) { case 'C': while (*optarg) { switch (*optarg++) { case 'a': aflag = 1; break; case 'b': bflag = 1; break; case 'd': dflag = 1; break; case 'f': if (!*optarg) optarg = argv[optind++]; if (strcmp(optarg, "inet") == 0) af = AF_INET; else if (strcmp(optarg, "inet6") == 0) af = AF_INET6; /* else if (strcmp(optarg, "local") == 0) af = AF_LOCAL; else if (strcmp(optarg, "unix") == 0) af = AF_UNIX; else if (strcmp(optarg, "ipx") == 0) af = AF_IPX; else if (strcmp(optarg, "ns") == 0) af = AF_NS; else if (strcmp(optarg, "encap") == 0) af = PF_KEY; else if (strcmp(optarg, "atalk") == 0) af = AF_APPLETALK; */ else { (void)fprintf(stderr, "%s: %s: unknown address family\n", progname, optarg); exit(1); } return; case 'g': gflag = 1; break; case 'I': iflag = 1; if (!*optarg) optarg = argv[optind++]; intrface = optarg; return; case 'i': iflag = 1; break; case 'L': Lflag = 1; break; /* case 'L': FreeBSD: Display listen queue lengths NetBSD: Suppress link-level routes */ /* case 'l': OpenBSD: Wider IPv6 display Linux: Listening sockets only lflag = 1; break; case 'M': *BSD: Memory image Linux: Masqueraded connections memf = optarg; break; */ case 'm': mflag = 1; break; /* case 'N': *BSD: Kernel image nlistf = optarg; break; */ case 'n': nflag = 1; break; case 'o': oflag = 1; break; /* case 'P': NetBSD: OpenBSD: dump PCB block */ case 'p': if (!*optarg) optarg = argv[optind++]; if ((tp = name2protox(optarg)) == NULL) { (void)fprintf(stderr, "%s: %s: unknown protocol\n", progname, optarg); exit(1); } pflag = 1; pname = tp->pr_name; return; /* case 'q': NetBSD: IRQ information OpenBSD: Suppress inactive I/Fs qflag = 1; break; */ case 'r': rflag = 1; break; case 'R': if (optind < argc) { if (argv[optind]) { max_getbulk = atoi(argv[optind]); if (max_getbulk == 0) { usage(); fprintf(stderr, "Bad -CR option: %s\n", argv[optind]); exit(1); } } } else { usage(); fprintf(stderr, "Bad -CR option: no argument given\n"); exit(1); } optind++; break; case 'S': /* FreeBSD: NetBSD: Semi-numeric display OpenBSD: Show route source selector */ Sflag = 1; break; case 's': ++sflag; break; /* case 't': FreeBSD: OpenBSD: Display watchdog timers tflag = 1; break; case 'u': OpenBSD: unix sockets only af = AF_UNIX; break; */ case 'v': vflag = 1; break; case 'w': if (!*optarg) optarg = argv[optind++]; interval = atoi(optarg); iflag = 1; return; case '?': default: usage(); exit(1); } } break; /* End of '-Cx' switch */ /* * Backward compatability for the main display modes * (where this doesn't clash with standard SNMP flags) */ case 'i': iflag = 1; break; case 'R': rflag = 1; /* -r sets the retry count */ break; case 's': ++sflag; break; } } int main(int argc, char *argv[]) { netsnmp_session session; struct protoent *p; char *cp; int exit_code = 1; SOCK_STARTUP; af = AF_UNSPEC; cp = strrchr( argv[0], '/' ); if (cp) progname = cp+1; else progname = argv[0]; switch (snmp_parse_args( argc, argv, &session, "C:iRs", optProc)) { case NETSNMP_PARSE_ARGS_ERROR: goto out; case NETSNMP_PARSE_ARGS_SUCCESS_EXIT: exit_code = 0; goto out; case NETSNMP_PARSE_ARGS_ERROR_USAGE: usage(); goto out; default: break; } /* * Check argc vs optind ?? */ argv += optind; argc -= optind; /* * Open an SNMP session. */ ss = snmp_open(&session); if (ss == NULL) { /* * diagnose snmp_open errors with the input netsnmp_session pointer */ snmp_sess_perror("snmpnetstat", &session); goto out; } /* * Omitted: * Privilege handling * "Backward Compatibility" * Kernel namelis handling */ #if 0 if (mflag) { /* mbpr(nl[N_MBSTAT].n_value, nl[N_MBPOOL].n_value, nl[N_MCLPOOL].n_value); */ exit_code = 0; goto out; } if (pflag) { printproto(tp, tp->pr_name); exit_code = 0; goto out; } #endif /* * Keep file descriptors open to avoid overhead * of open/close on each call to get* routines. */ sethostent(1); setnetent(1); if (iflag) { intpr(interval); exit_code = 0; goto out; } if (rflag) { /* if (sflag) rt_stats(); else */ if (Lflag || routexpr(af) == 0) { if (route4pr(af) == 0 && af == AF_INET) routepr(); route6pr(af); } exit_code = 0; goto out; } /* if (gflag) { if (sflag) { if (af == AF_INET || af == AF_UNSPEC) mrt_stats(nl[N_MRTPROTO].n_value, nl[N_MRTSTAT].n_value); #ifdef NETSNMP_ENABLE_IPV6 if (af == AF_INET6 || af == AF_UNSPEC) mrt6_stats(nl[N_MRT6PROTO].n_value, nl[N_MRT6STAT].n_value); #endif } else { if (af == AF_INET || af == AF_UNSPEC) mroutepr(nl[N_MRTPROTO].n_value, nl[N_MFCHASHTBL].n_value, nl[N_MFCHASH].n_value, nl[N_VIFTABLE].n_value); #ifdef NETSNMP_ENABLE_IPV6 if (af == AF_INET6 || af == AF_UNSPEC) mroute6pr(nl[N_MRT6PROTO].n_value, nl[N_MF6CTABLE].n_value, nl[N_MIF6TABLE].n_value); #endif } exit_code = 0; goto out; } */ setservent(1); if (Lflag) { switch (af) { case AF_UNSPEC: setprotoent(1); /* ugh, this is O(MN) ... why do we do this? */ while ((p = getprotoent())) { for (tp = protox; tp->pr_name; tp++) if (strcmp(tp->pr_name, p->p_name) == 0) if (tp->pr_name && tp->pr_wanted) printproto(tp, p->p_name); } endprotoent(); break; case AF_INET: for (tp = protox; tp->pr_name; tp++) printproto(tp, tp->pr_name); /* FALL THROUGH */ case AF_INET6: for (tp = ip6protox; tp->pr_name; tp++) printproto(tp, tp->pr_name); } } else { for (tp = ipxprotox; tp->pr_name; tp++) if (!pname || strcmp(pname,tp->pr_name) == 0) printproto(tp, pname); } /* if (af == AF_IPX || af == AF_UNSPEC) for (tp = ipxprotox; tp->pr_name; tp++) printproto(tp, tp->pr_name); if (af == AF_NS || af == AF_UNSPEC) for (tp = nsprotox; tp->pr_name; tp++) printproto(tp, tp->pr_name); if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) unixpr(nl[N_UNIXSW].n_value); if (af == AF_APPLETALK || af == AF_UNSPEC) for (tp = atalkprotox; tp->pr_name; tp++) printproto(tp, tp->pr_name); */ exit_code = 0; out: SOCK_CLEANUP; return exit_code; } /* * Print out protocol statistics or control blocks (per sflag). * Namelist checks - Omitted */ static void printproto(struct protox *tp, const char *name) { void (*pr)(const char *); if (sflag) { pr = tp->pr_stats; } else { pr = tp->pr_cblocks; } if (pr != NULL) (*pr)(name); } /* * Read kernel memory - Omitted */ const char * plural(int n) { return (n != 1 ? "s" : ""); } /* * Find the protox for the given "well-known" name. */ static struct protox * knownname(const char *name) { struct protox **tpp, *tp; for (tpp = protoprotox; *tpp; tpp++) for (tp = *tpp; tp->pr_name; tp++) if (strcmp(tp->pr_name, name) == 0) return (tp); return (NULL); } /* * Find the protox corresponding to name. */ static struct protox * name2protox(const char *name) { struct protox *tp; char **alias; /* alias from p->aliases */ struct protoent *p; /* * Try to find the name in the list of "well-known" names. If that * fails, check if name is an alias for an Internet protocol. */ if ((tp = knownname(name))) return (tp); setprotoent(1); /* make protocol lookup cheaper */ while ((p = getprotoent())) { /* netsnmp_assert: name not same as p->name */ for (alias = p->p_aliases; *alias; alias++) if (strcmp(name, *alias) == 0) { endprotoent(); return (knownname(p->p_name)); } } endprotoent(); return (NULL); } static void usage(void) { (void)fprintf(stderr, "usage: %s [snmp_opts] [-Canv] [-Cf address_family]\n", progname); (void)fprintf(stderr, " %s [snmp_opts] [-Cibodnv] [-CI interface] [-Cw wait]\n", progname); (void)fprintf(stderr, " %s [snmp_opts] [-Cs[s]] [-Cp protocol]\n", progname); (void)fprintf(stderr, " %s [snmp_opts] [-Crnv] [-Cf address_family]\n", progname); }