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