Home | History | Annotate | Line # | Download | only in systat
netstat.c revision 1.7
      1 /*	$NetBSD: netstat.c,v 1.7 1997/07/21 07:05:06 mrg Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1980, 1992, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 #if 0
     39 static char sccsid[] = "@(#)netstat.c	8.1 (Berkeley) 6/6/93";
     40 #endif
     41 __RCSID("$NetBSD: netstat.c,v 1.7 1997/07/21 07:05:06 mrg Exp $");
     42 #endif /* not lint */
     43 
     44 /*
     45  * netstat
     46  */
     47 #include <sys/param.h>
     48 #include <sys/socket.h>
     49 #include <sys/socketvar.h>
     50 #include <sys/mbuf.h>
     51 #include <sys/protosw.h>
     52 
     53 #include <netinet/in.h>
     54 
     55 #include <arpa/inet.h>
     56 #include <net/route.h>
     57 
     58 #include <netinet/in_systm.h>
     59 #include <netinet/ip.h>
     60 #include <netinet/in_pcb.h>
     61 #include <netinet/ip_icmp.h>
     62 #include <netinet/icmp_var.h>
     63 #include <netinet/ip_var.h>
     64 #include <netinet/tcp.h>
     65 #include <netinet/tcpip.h>
     66 #include <netinet/tcp_seq.h>
     67 #define TCPSTATES
     68 #include <netinet/tcp_fsm.h>
     69 #include <netinet/tcp_timer.h>
     70 #include <netinet/tcp_var.h>
     71 #include <netinet/tcp_debug.h>
     72 #include <netinet/udp.h>
     73 #include <netinet/udp_var.h>
     74 
     75 #include <netdb.h>
     76 #include <stdlib.h>
     77 #include <string.h>
     78 #include <nlist.h>
     79 #include <paths.h>
     80 #include "systat.h"
     81 #include "extern.h"
     82 
     83 static void enter __P((struct inpcb *, struct socket *, int, char *));
     84 static char *inetname __P((struct in_addr));
     85 static void inetprint __P((struct in_addr *, int, char *));
     86 
     87 #define	streq(a,b)	(strcmp(a,b)==0)
     88 
     89 WINDOW *
     90 opennetstat()
     91 {
     92 	sethostent(1);
     93 	setnetent(1);
     94 	return (subwin(stdscr, LINES-5-1, 0, 5, 0));
     95 }
     96 
     97 struct netinfo {
     98 	struct	netinfo *ni_forw, *ni_prev;
     99 	short	ni_line;		/* line on screen */
    100 	short	ni_seen;		/* 0 when not present in list */
    101 	short	ni_flags;
    102 #define	NIF_LACHG	0x1		/* local address changed */
    103 #define	NIF_FACHG	0x2		/* foreign address changed */
    104 	short	ni_state;		/* tcp state */
    105 	char	*ni_proto;		/* protocol */
    106 	struct	in_addr ni_laddr;	/* local address */
    107 	long	ni_lport;		/* local port */
    108 	struct	in_addr	ni_faddr;	/* foreign address */
    109 	long	ni_fport;		/* foreign port */
    110 	long	ni_rcvcc;		/* rcv buffer character count */
    111 	long	ni_sndcc;		/* snd buffer character count */
    112 };
    113 
    114 static struct {
    115 	struct	netinfo *ni_forw, *ni_prev;
    116 } netcb;
    117 
    118 static	int aflag = 0;
    119 static	int nflag = 0;
    120 static	int lastrow = 1;
    121 static	void enter __P((struct inpcb *, struct socket *, int, char *));
    122 static	void inetprint __P((struct in_addr *, int, char *));
    123 static	char *inetname __P((struct in_addr));
    124 
    125 void
    126 closenetstat(w)
    127 	WINDOW *w;
    128 {
    129 	register struct netinfo *p;
    130 
    131 	endhostent();
    132 	endnetent();
    133 	p = (struct netinfo *)netcb.ni_forw;
    134 	while (p != (struct netinfo *)&netcb) {
    135 		if (p->ni_line != -1)
    136 			lastrow--;
    137 		p->ni_line = -1;
    138 		p = p->ni_forw;
    139 	}
    140 	if (w != NULL) {
    141 		wclear(w);
    142 		wrefresh(w);
    143 		delwin(w);
    144 	}
    145 }
    146 
    147 static struct nlist namelist[] = {
    148 #define	X_TCBTABLE	0
    149 	{ "_tcbtable" },
    150 #define	X_UDBTABLE	1
    151 	{ "_udbtable" },
    152 	{ "" },
    153 };
    154 
    155 int
    156 initnetstat()
    157 {
    158 	if (kvm_nlist(kd, namelist)) {
    159 		nlisterr(namelist);
    160 		return(0);
    161 	}
    162 	if (namelist[X_TCBTABLE].n_value == 0) {
    163 		error("No symbols in namelist");
    164 		return(0);
    165 	}
    166 	netcb.ni_forw = netcb.ni_prev = (struct netinfo *)&netcb;
    167 	protos = TCP|UDP;
    168 	return(1);
    169 }
    170 
    171 void
    172 fetchnetstat()
    173 {
    174 	struct inpcbtable pcbtable;
    175 	register struct inpcb *head, *prev, *next;
    176 	register struct netinfo *p;
    177 	struct inpcb inpcb;
    178 	struct socket sockb;
    179 	struct tcpcb tcpcb;
    180 	void *off;
    181 	int istcp;
    182 
    183 	if (namelist[X_TCBTABLE].n_value == 0)
    184 		return;
    185 	for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw)
    186 		p->ni_seen = 0;
    187 	if (protos&TCP) {
    188 		off = NPTR(X_TCBTABLE);
    189 		istcp = 1;
    190 	}
    191 	else if (protos&UDP) {
    192 		off = NPTR(X_UDBTABLE);
    193 		istcp = 0;
    194 	}
    195 	else {
    196 		error("No protocols to display");
    197 		return;
    198 	}
    199 again:
    200 	KREAD(off, &pcbtable, sizeof (struct inpcbtable));
    201 	prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
    202 	next = pcbtable.inpt_queue.cqh_first;
    203 	while (next != head) {
    204 		KREAD(next, &inpcb, sizeof (inpcb));
    205 		if (inpcb.inp_queue.cqe_prev != prev) {
    206 printf("prev = %p, head = %p, next = %p, inpcb...prev = %p\n", prev, head, next, inpcb.inp_queue.cqe_prev);
    207 			p = netcb.ni_forw;
    208 			for (; p != (struct netinfo *)&netcb; p = p->ni_forw)
    209 				p->ni_seen = 1;
    210 			error("Kernel state in transition");
    211 			return;
    212 		}
    213 		prev = next;
    214 		next = inpcb.inp_queue.cqe_next;
    215 
    216 		if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
    217 			continue;
    218 		if (nhosts && !checkhost(&inpcb))
    219 			continue;
    220 		if (nports && !checkport(&inpcb))
    221 			continue;
    222 		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
    223 		if (istcp) {
    224 			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
    225 			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
    226 		} else
    227 			enter(&inpcb, &sockb, 0, "udp");
    228 	}
    229 	if (istcp && (protos&UDP)) {
    230 		istcp = 0;
    231 		off = NPTR(X_UDBTABLE);
    232 		goto again;
    233 	}
    234 }
    235 
    236 static void
    237 enter(inp, so, state, proto)
    238 	struct inpcb *inp;
    239 	struct socket *so;
    240 	int state;
    241 	char *proto;
    242 {
    243 	register struct netinfo *p;
    244 
    245 	/*
    246 	 * Only take exact matches, any sockets with
    247 	 * previously unbound addresses will be deleted
    248 	 * below in the display routine because they
    249 	 * will appear as ``not seen'' in the kernel
    250 	 * data structures.
    251 	 */
    252 	for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
    253 		if (!streq(proto, p->ni_proto))
    254 			continue;
    255 		if (p->ni_lport != inp->inp_lport ||
    256 		    p->ni_laddr.s_addr != inp->inp_laddr.s_addr)
    257 			continue;
    258 		if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr &&
    259 		    p->ni_fport == inp->inp_fport)
    260 			break;
    261 	}
    262 	if (p == (struct netinfo *)&netcb) {
    263 		if ((p = malloc(sizeof(*p))) == NULL) {
    264 			error("Out of memory");
    265 			return;
    266 		}
    267 		p->ni_prev = (struct netinfo *)&netcb;
    268 		p->ni_forw = netcb.ni_forw;
    269 		netcb.ni_forw->ni_prev = p;
    270 		netcb.ni_forw = p;
    271 		p->ni_line = -1;
    272 		p->ni_laddr = inp->inp_laddr;
    273 		p->ni_lport = inp->inp_lport;
    274 		p->ni_faddr = inp->inp_faddr;
    275 		p->ni_fport = inp->inp_fport;
    276 		p->ni_proto = proto;
    277 		p->ni_flags = NIF_LACHG|NIF_FACHG;
    278 	}
    279 	p->ni_rcvcc = so->so_rcv.sb_cc;
    280 	p->ni_sndcc = so->so_snd.sb_cc;
    281 	p->ni_state = state;
    282 	p->ni_seen = 1;
    283 }
    284 
    285 /* column locations */
    286 #define	LADDR	0
    287 #define	FADDR	LADDR+23
    288 #define	PROTO	FADDR+23
    289 #define	RCVCC	PROTO+6
    290 #define	SNDCC	RCVCC+7
    291 #define	STATE	SNDCC+7
    292 
    293 
    294 void
    295 labelnetstat()
    296 {
    297 	if (namelist[X_TCBTABLE].n_type == 0)
    298 		return;
    299 	wmove(wnd, 0, 0); wclrtobot(wnd);
    300 	mvwaddstr(wnd, 0, LADDR, "Local Address");
    301 	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
    302 	mvwaddstr(wnd, 0, PROTO, "Proto");
    303 	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
    304 	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
    305 	mvwaddstr(wnd, 0, STATE, "(state)");
    306 }
    307 
    308 void
    309 shownetstat()
    310 {
    311 	register struct netinfo *p, *q;
    312 
    313 	/*
    314 	 * First, delete any connections that have gone
    315 	 * away and adjust the position of connections
    316 	 * below to reflect the deleted line.
    317 	 */
    318 	p = netcb.ni_forw;
    319 	while (p != (struct netinfo *)&netcb) {
    320 		if (p->ni_line == -1 || p->ni_seen) {
    321 			p = p->ni_forw;
    322 			continue;
    323 		}
    324 		wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
    325 		q = netcb.ni_forw;
    326 		for (; q != (struct netinfo *)&netcb; q = q->ni_forw)
    327 			if (q != p && q->ni_line > p->ni_line) {
    328 				q->ni_line--;
    329 				/* this shouldn't be necessary */
    330 				q->ni_flags |= NIF_LACHG|NIF_FACHG;
    331 			}
    332 		lastrow--;
    333 		q = p->ni_forw;
    334 		p->ni_prev->ni_forw = p->ni_forw;
    335 		p->ni_forw->ni_prev = p->ni_prev;
    336 		free(p);
    337 		p = q;
    338 	}
    339 	/*
    340 	 * Update existing connections and add new ones.
    341 	 */
    342 	for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
    343 		if (p->ni_line == -1) {
    344 			/*
    345 			 * Add a new entry if possible.
    346 			 */
    347 			if (lastrow > getmaxy(wnd))
    348 				continue;
    349 			p->ni_line = lastrow++;
    350 			p->ni_flags |= NIF_LACHG|NIF_FACHG;
    351 		}
    352 		if (p->ni_flags & NIF_LACHG) {
    353 			wmove(wnd, p->ni_line, LADDR);
    354 			inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto);
    355 			p->ni_flags &= ~NIF_LACHG;
    356 		}
    357 		if (p->ni_flags & NIF_FACHG) {
    358 			wmove(wnd, p->ni_line, FADDR);
    359 			inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto);
    360 			p->ni_flags &= ~NIF_FACHG;
    361 		}
    362 		mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto);
    363 		mvwprintw(wnd, p->ni_line, RCVCC, "%6d", p->ni_rcvcc);
    364 		mvwprintw(wnd, p->ni_line, SNDCC, "%6d", p->ni_sndcc);
    365 		if (streq(p->ni_proto, "tcp"))
    366 			if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
    367 				mvwprintw(wnd, p->ni_line, STATE, "%d",
    368 				    p->ni_state);
    369 			else
    370 				mvwaddstr(wnd, p->ni_line, STATE,
    371 				    tcpstates[p->ni_state]);
    372 		wclrtoeol(wnd);
    373 	}
    374 	if (lastrow < getmaxy(wnd)) {
    375 		wmove(wnd, lastrow, 0); wclrtobot(wnd);
    376 		wmove(wnd, getmaxy(wnd), 0); wdeleteln(wnd);	/* XXX */
    377 	}
    378 }
    379 
    380 /*
    381  * Pretty print an Internet address (net address + port).
    382  * If the nflag was specified, use numbers instead of names.
    383  */
    384 static void
    385 inetprint(in, port, proto)
    386 	struct in_addr *in;
    387 	int port;
    388 	char *proto;
    389 {
    390 	struct servent *sp = 0;
    391 	char line[80], *cp;
    392 
    393 	sprintf(line, "%.*s.", 16, inetname(*in));
    394 	cp = index(line, '\0');
    395 	if (!nflag && port)
    396 		sp = getservbyport(port, proto);
    397 	if (sp || port == 0)
    398 		sprintf(cp, "%.8s", sp ? sp->s_name : "*");
    399 	else
    400 		sprintf(cp, "%d", ntohs((u_short)port));
    401 	/* pad to full column to clear any garbage */
    402 	cp = index(line, '\0');
    403 	while (cp - line < 22)
    404 		*cp++ = ' ';
    405 	*cp = '\0';
    406 	waddstr(wnd, line);
    407 }
    408 
    409 /*
    410  * Construct an Internet address representation.
    411  * If the nflag has been supplied, give
    412  * numeric value, otherwise try for symbolic name.
    413  */
    414 static char *
    415 inetname(in)
    416 	struct in_addr in;
    417 {
    418 	char *cp = 0;
    419 	static char line[50];
    420 	struct hostent *hp;
    421 	struct netent *np;
    422 
    423 	if (!nflag && in.s_addr != INADDR_ANY) {
    424 		int net = inet_netof(in);
    425 		int lna = inet_lnaof(in);
    426 
    427 		if (lna == INADDR_ANY) {
    428 			np = getnetbyaddr(net, AF_INET);
    429 			if (np)
    430 				cp = np->n_name;
    431 		}
    432 		if (cp == 0) {
    433 			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
    434 			if (hp)
    435 				cp = hp->h_name;
    436 		}
    437 	}
    438 	if (in.s_addr == INADDR_ANY)
    439 		strcpy(line, "*");
    440 	else if (cp)
    441 		strcpy(line, cp);
    442 	else {
    443 		in.s_addr = ntohl(in.s_addr);
    444 #define C(x)	((x) & 0xff)
    445 		sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
    446 			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
    447 	}
    448 	return (line);
    449 }
    450 
    451 int
    452 cmdnetstat(cmd, args)
    453 	char *cmd, *args;
    454 {
    455 	register struct netinfo *p;
    456 
    457 	if (prefix(cmd, "all")) {
    458 		aflag = !aflag;
    459 		goto fixup;
    460 	}
    461 	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
    462 		int new;
    463 
    464 		new = prefix(cmd, "numbers");
    465 		if (new == nflag)
    466 			return (1);
    467 		p = netcb.ni_forw;
    468 		for (; p != (struct netinfo *)&netcb; p = p->ni_forw) {
    469 			if (p->ni_line == -1)
    470 				continue;
    471 			p->ni_flags |= NIF_LACHG|NIF_FACHG;
    472 		}
    473 		nflag = new;
    474 		wclear(wnd);
    475 		labelnetstat();
    476 		goto redisplay;
    477 	}
    478 	if (!netcmd(cmd, args))
    479 		return (0);
    480 fixup:
    481 	fetchnetstat();
    482 redisplay:
    483 	shownetstat();
    484 	refresh();
    485 	return (1);
    486 }
    487