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