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