#include <sys/param.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <netgraph/socket/ng_socket.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <netdb.h>
#include <nlist.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "netstat.h"
static struct nlist nl[] = {
#define N_IFNET 0
{ .n_name = "_ifnet" },
#define N_UNIXSW 1
{ .n_name = "_localsw" },
#define N_RTREE 2
{ .n_name = "_rt_tables"},
#define N_CLTPSTAT 3
{ .n_name = "_cltpstat"},
#define N_MRTSTAT 4
{ .n_name = "_mrtstat" },
#define N_MFCTABLE 5
{ .n_name = "_mfctable" },
#define N_VIFTABLE 6
{ .n_name = "_viftable" },
#define N_NGSOCKS 7
{ .n_name = "_ngsocklist"},
#define N_IP6STAT 8
{ .n_name = "_ip6stat" },
#define N_ICMP6STAT 9
{ .n_name = "_icmp6stat" },
#define N_PIM6STAT 10
{ .n_name = "_pim6stat" },
#define N_MRT6PROTO 11
{ .n_name = "_ip6_mrtproto" },
#define N_MRT6STAT 12
{ .n_name = "_mrt6stat" },
#define N_MF6CTABLE 13
{ .n_name = "_mf6ctable" },
#define N_MIF6TABLE 14
{ .n_name = "_mif6table" },
#define N_MBSTAT 15
{ .n_name = "_mbstat" },
#define N_MBTYPES 16
{ .n_name = "_mbtypes" },
#define N_NMBCLUSTERS 17
{ .n_name = "_nmbclusters" },
#define N_NMBUFS 18
{ .n_name = "_nmbufs" },
#define N_NCPUS 19
{ .n_name = "_ncpus" },
#define N_CARPSTAT 20
{ .n_name = "_carpstats" },
#define N_NMBJCLUSTERS 21
{ .n_name = "_nmbjclusters" },
{ .n_name = NULL },
};
struct protox {
u_char pr_index;
u_char pr_sindex;
u_char pr_wanted;
void (*pr_cblocks)(u_long, const char *, int);
void (*pr_stats)(u_long, const char *, int);
void (*pr_istats)(char *);
const char *pr_name;
u_int pr_usesysctl;
} protox[] = {
{ -1, -1, 1, protopr,
tcp_stats, NULL, "tcp", IPPROTO_TCP },
{ -1, -1, 1, protopr,
udp_stats, NULL, "udp", IPPROTO_UDP },
{ -1, -1, 1, protopr,
NULL, NULL, "divert",IPPROTO_DIVERT },
{ -1, -1, 1, protopr,
ip_stats, NULL, "ip", IPPROTO_RAW },
{ -1, -1, 1, protopr,
icmp_stats, NULL, "icmp", IPPROTO_ICMP },
{ -1, -1, 1, protopr,
igmp_stats, NULL, "igmp", IPPROTO_IGMP },
{ -1, N_CARPSTAT, 1, 0,
carp_stats, NULL, "carp", 0},
{ -1, -1, 0, 0,
0, NULL, NULL, 0}
};
#ifdef INET6
struct protox ip6protox[] = {
{ -1, -1, 1, protopr,
tcp_stats, NULL, "tcp", IPPROTO_TCP },
{ -1, -1, 1, protopr,
udp_stats, NULL, "udp", IPPROTO_UDP },
{ -1, N_IP6STAT, 1, protopr,
ip6_stats, ip6_ifstats, "ip6", IPPROTO_RAW },
{ -1, N_ICMP6STAT, 1, protopr,
icmp6_stats, icmp6_ifstats, "icmp6",IPPROTO_ICMPV6 },
#ifdef notyet
{ -1, N_PIM6STAT, 1, 0,
pim6_stats, NULL, "pim6", 0 },
#endif
{ -1, -1, 1, 0,
rip6_stats, NULL, "rip6", 0 },
{ -1, -1, 1, protopr,
pim_stats, NULL, "pim", IPPROTO_PIM },
{ -1, -1, 0, 0,
0, NULL, 0, 0 }
};
#endif
struct protox netgraphprotox[] = {
{ N_NGSOCKS, -1, 1, netgraphprotopr,
NULL, NULL, "ctrl", 0 },
{ N_NGSOCKS, -1, 1, netgraphprotopr,
NULL, NULL, "data", 0 },
{ -1, -1, 0, 0,
0, NULL, NULL, 0 }
};
struct protox *protoprotox[] = {
protox,
#ifdef INET6
ip6protox,
#endif
NULL };
static void printproto (struct protox *, const char *, u_long);
static void usage (void);
static struct protox *name2protox (char *);
static struct protox *knownname (char *);
static kvm_t *kvmd;
static char *nlistf = NULL, *memf = NULL;
int Aflag;
int aflag;
int bflag;
int cpuflag = -1;
int dflag;
int gflag;
int hflag;
int iflag;
int Lflag;
int mflag;
int Pflag;
int numeric_addr;
int numeric_port;
static int pflag;
int rflag;
int sflag;
int tflag;
int Bflag;
int Wflag;
int zflag;
int interval;
char *interface;
int unit;
int af;
int
main(int argc, char **argv)
{
struct protox *tp = NULL;
int ch;
int n;
size_t nsz;
af = AF_UNSPEC;
while ((ch = getopt(argc, argv, "46Aabc:df:ghI:iLlM:mN:nPp:rSsBtuWw:z")) != -1)
switch(ch) {
case '4':
af = AF_INET;
break;
#ifdef INET6
case '6':
af = AF_INET6;
break;
#endif
case 'A':
Aflag = 1;
break;
case 'a':
aflag = 1;
break;
case 'b':
bflag = 1;
break;
case 'c':
nsz = sizeof(n);
sysctlbyname("net.netisr.ncpus", &n, &nsz, NULL, 0);
cpuflag = strtol(optarg, NULL, 0);
if (cpuflag < 0 || cpuflag >= n) {
errx(1, "cpu%d does not have network data",
cpuflag);
}
break;
case 'd':
dflag = 1;
break;
case 'f':
if (strcmp(optarg, "inet") == 0)
af = AF_INET;
#ifdef INET6
else if (strcmp(optarg, "inet6") == 0)
af = AF_INET6;
#endif
else if (strcmp(optarg, "unix") == 0)
af = AF_UNIX;
else if (strcmp(optarg, "ng") == 0
|| strcmp(optarg, "netgraph") == 0)
af = AF_NETGRAPH;
else if (strcmp(optarg, "link") == 0)
af = AF_LINK;
else if (strcmp(optarg, "mpls") == 0)
af = AF_MPLS;
else {
errx(1, "%s: unknown address family", optarg);
}
break;
case 'g':
gflag = 1;
break;
case 'h':
hflag = 1;
break;
case 'I': {
char *cp;
iflag = 1;
for (cp = interface = optarg; isalpha(*cp); cp++)
continue;
unit = atoi(cp);
break;
}
case 'i':
iflag = 1;
break;
case 'L':
Lflag = 1;
break;
case 'M':
memf = optarg;
break;
case 'm':
mflag = 1;
break;
case 'N':
nlistf = optarg;
break;
case 'n':
numeric_addr = numeric_port = 1;
break;
case 'P':
Pflag = 1;
break;
case 'p':
if ((tp = name2protox(optarg)) == NULL) {
errx(1,
"%s: unknown or uninstrumented protocol",
optarg);
}
pflag = 1;
break;
case 'r':
rflag = 1;
break;
case 's':
++sflag;
break;
case 'S':
numeric_addr = 1;
break;
case 'B':
Bflag = 1;
break;
case 't':
tflag = 1;
break;
case 'u':
af = AF_UNIX;
break;
case 'W':
case 'l':
Wflag = 1;
break;
case 'w':
interval = atoi(optarg);
iflag = 1;
break;
case 'z':
zflag = 1;
break;
case '?':
default:
usage();
}
argv += optind;
argc -= optind;
#define BACKWARD_COMPATIBILITY
#ifdef BACKWARD_COMPATIBILITY
if (*argv) {
if (isdigit(**argv)) {
interval = atoi(*argv);
if (interval <= 0)
usage();
++argv;
iflag = 1;
}
if (*argv) {
nlistf = *argv;
if (*++argv)
memf = *argv;
}
}
#endif
if (nlistf != NULL || memf != NULL)
setgid(getgid());
if (mflag) {
if (memf != NULL) {
if (kread(0, 0, 0) == 0)
mbpr(nl[N_MBSTAT].n_value,
nl[N_MBTYPES].n_value,
nl[N_NMBCLUSTERS].n_value,
nl[N_NMBJCLUSTERS].n_value,
nl[N_NMBUFS].n_value,
nl[N_NCPUS].n_value);
} else {
mbpr(0, 0, 0, 0, 0, 0);
}
exit(0);
}
#if 0
sethostent(1);
setnetent(1);
#else
#endif
if (iflag && !sflag) {
kread(0, 0, 0);
intpr(interval, nl[N_IFNET].n_value, NULL, nl[N_NCPUS].n_value);
exit(0);
}
if (rflag) {
kread(0, 0, 0);
if (sflag)
rt_stats();
else
routepr(nl[N_RTREE].n_value);
exit(0);
}
if (gflag) {
kread(0, 0, 0);
if (sflag) {
if (af == AF_INET || af == AF_UNSPEC)
mrt_stats(nl[N_MRTSTAT].n_value);
#ifdef INET6
if (af == AF_INET6 || af == AF_UNSPEC)
mrt6_stats(nl[N_MRT6STAT].n_value);
#endif
} else {
if (af == AF_INET || af == AF_UNSPEC)
mroutepr(nl[N_MFCTABLE].n_value,
nl[N_VIFTABLE].n_value);
#ifdef INET6
if (af == AF_INET6 || af == AF_UNSPEC)
mroute6pr(nl[N_MF6CTABLE].n_value,
nl[N_MIF6TABLE].n_value);
#endif
}
exit(0);
}
kread(0, 0, 0);
if (tp) {
printproto(tp, tp->pr_name, nl[N_NCPUS].n_value);
exit(0);
}
if (af == AF_INET || af == AF_UNSPEC)
for (tp = protox; tp->pr_name; tp++)
printproto(tp, tp->pr_name, nl[N_NCPUS].n_value);
#ifdef INET6
if (af == AF_INET6 || af == AF_UNSPEC)
for (tp = ip6protox; tp->pr_name; tp++)
printproto(tp, tp->pr_name, nl[N_NCPUS].n_value);
#endif
if (af == AF_NETGRAPH || af == AF_UNSPEC)
for (tp = netgraphprotox; tp->pr_name; tp++)
printproto(tp, tp->pr_name, nl[N_NCPUS].n_value);
if ((af == AF_UNIX || af == AF_UNSPEC) && !Lflag && !sflag)
unixpr();
exit(0);
}
static void
printproto(struct protox *tp, const char *name, u_long ncpusaddr)
{
void (*pr)(u_long, const char *, int);
u_long off;
if (sflag) {
if (iflag) {
if (tp->pr_istats)
intpr(interval, nl[N_IFNET].n_value,
tp->pr_istats, ncpusaddr);
else if (pflag)
printf("%s: no per-interface stats routine\n",
tp->pr_name);
return;
}
else {
pr = tp->pr_stats;
if (!pr) {
if (pflag)
printf("%s: no stats routine\n",
tp->pr_name);
return;
}
off = tp->pr_usesysctl ? tp->pr_usesysctl
: nl[tp->pr_sindex].n_value;
}
} else {
pr = tp->pr_cblocks;
if (!pr) {
if (pflag)
printf("%s: no PCB routine\n", tp->pr_name);
return;
}
off = tp->pr_usesysctl ? tp->pr_usesysctl
: nl[tp->pr_index].n_value;
}
if (pr != NULL && (off || af != AF_UNSPEC))
(*pr)(off, name, af);
}
int
kread(u_long addr, char *buf, int size)
{
if (kvmd == NULL) {
kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
if (kvmd != NULL) {
if (kvm_nlist(kvmd, nl) < 0) {
if (nlistf)
errx(1, "%s: kvm_nlist: %s", nlistf,
kvm_geterr(kvmd));
else
errx(1, "kvm_nlist: %s", kvm_geterr(kvmd));
}
if (nl[0].n_type == 0) {
if (nlistf)
errx(1, "%s: no namelist", nlistf);
else
errx(1, "no namelist");
}
} else {
warnx("kvm not available");
return (-1);
}
}
if (!buf)
return (0);
if (kvm_read(kvmd, addr, buf, size) != size) {
warnx("%s", kvm_geterr(kvmd));
return (-1);
}
return (0);
}
const char *
plural(int n)
{
return (n != 1 ? "s" : "");
}
const char *
plurales(int n)
{
return (n != 1 ? "es" : "");
}
static struct protox *
knownname(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);
}
static struct protox *
name2protox(char *name)
{
struct protox *tp;
char **alias;
struct protoent *p;
if ((tp = knownname(name)) != NULL)
return (tp);
setprotoent(1);
while ((p = getprotoent()) != NULL) {
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, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
"usage: netstat [-AaLnPSW] [-c cpu] [-f protocol_family | -p protocol]\n"
" [-M core] [-N system]",
" netstat -i | -I interface [-aBbdhnt] [-f address_family]\n"
" [-M core] [-N system]",
" netstat -w wait [-I interface] [-dh] [-M core] [-N system]",
" netstat -s [-s] [-z] [-f protocol_family | -p protocol] [-M core]",
" netstat -i | -I interface -s [-f protocol_family | -p protocol]\n"
" [-M core] [-N system]",
" netstat -m [-M core] [-N system]",
" netstat -r [-AanW] [-f address_family] [-M core] [-N system]",
" netstat -rs [-s] [-M core] [-N system]",
" netstat -g [-W] [-f address_family] [-M core] [-N system]",
" netstat -gs [-s] [-f address_family] [-M core] [-N system]");
exit(1);
}