xref: /openbsd/usr.bin/systat/netstat.c (revision 133306f0)
1 /*	$OpenBSD: netstat.c,v 1.13 2000/05/24 13:17:08 itojun Exp $	*/
2 /*	$NetBSD: netstat.c,v 1.3 1995/06/18 23:53:07 cgd Exp $	*/
3 
4 /*-
5  * Copyright (c) 1980, 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)netstat.c	8.1 (Berkeley) 6/6/93";
40 #endif
41 static char rcsid[] = "$OpenBSD: netstat.c,v 1.13 2000/05/24 13:17:08 itojun 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 #include <net/route.h>
55 #include <netinet/in_systm.h>
56 #include <netinet/ip.h>
57 #include <netinet/in_pcb.h>
58 #include <netinet/ip_icmp.h>
59 #include <netinet/icmp_var.h>
60 #include <netinet/ip_var.h>
61 #include <netinet/tcp.h>
62 #include <netinet/tcpip.h>
63 #include <netinet/tcp_seq.h>
64 #define TCPSTATES
65 #include <netinet/tcp_fsm.h>
66 #include <netinet/tcp_timer.h>
67 #include <netinet/tcp_var.h>
68 #include <netinet/tcp_debug.h>
69 #include <netinet/udp.h>
70 #include <netinet/udp_var.h>
71 #include <arpa/inet.h>
72 
73 #include <netdb.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <nlist.h>
77 #include <paths.h>
78 #include "systat.h"
79 #include "extern.h"
80 
81 static void enter __P((struct inpcb *, struct socket *, int, char *));
82 static const char *inetname __P((struct in_addr));
83 static void inetprint __P((struct in_addr *, int, char *));
84 #ifdef INET6
85 static const char *inet6name __P((struct in6_addr *));
86 static void inet6print __P((struct in6_addr *, int, char *));
87 #endif
88 
89 #define	streq(a,b)	(strcmp(a,b)==0)
90 #define	YMAX(w)		((w)->_maxy-1)
91 
92 WINDOW *
93 opennetstat()
94 {
95 	sethostent(1);
96 	setnetent(1);
97 	return (subwin(stdscr, LINES-5-1, 0, 5, 0));
98 }
99 
100 struct netinfo {
101 	struct	netinfo *nif_forw, *nif_prev;
102 	int	nif_family;
103 	short	nif_line;		/* line on screen */
104 	short	nif_seen;		/* 0 when not present in list */
105 	short	nif_flags;
106 #define	NIF_LACHG	0x1		/* local address changed */
107 #define	NIF_FACHG	0x2		/* foreign address changed */
108 	short	nif_state;		/* tcp state */
109 	char	*nif_proto;		/* protocol */
110 	struct	in_addr nif_laddr;	/* local address */
111 #ifdef INET6
112 	struct	in6_addr nif_laddr6;	/* local address */
113 #endif
114 	long	nif_lport;		/* local port */
115 	struct	in_addr	nif_faddr;	/* foreign address */
116 #ifdef INET6
117 	struct	in6_addr nif_faddr6;	/* foreign address */
118 #endif
119 	long	nif_fport;		/* foreign port */
120 	long	nif_rcvcc;		/* rcv buffer character count */
121 	long	nif_sndcc;		/* snd buffer character count */
122 };
123 
124 static struct {
125 	struct	netinfo *nif_forw, *nif_prev;
126 } netcb;
127 
128 static	int aflag = 0;
129 static	int nflag = 0;
130 static	int lastrow = 1;
131 
132 void
133 closenetstat(w)
134         WINDOW *w;
135 {
136 	register struct netinfo *p;
137 
138 	endhostent();
139 	endnetent();
140 	p = (struct netinfo *)netcb.nif_forw;
141 	while (p != (struct netinfo *)&netcb) {
142 		if (p->nif_line != -1)
143 			lastrow--;
144 		p->nif_line = -1;
145 		p = p->nif_forw;
146 	}
147         if (w != NULL) {
148 		wclear(w);
149 		wrefresh(w);
150 		delwin(w);
151 	}
152 }
153 
154 static struct nlist namelist[] = {
155 #define	X_TCBTABLE	0
156 	{ "_tcbtable" },
157 #define	X_UDBTABLE	1
158 	{ "_udbtable" },
159 	{ "" },
160 };
161 
162 int
163 initnetstat()
164 {
165 	if (kvm_nlist(kd, namelist)) {
166 		nlisterr(namelist);
167 		return(0);
168 	}
169 	if (namelist[X_TCBTABLE].n_value == 0) {
170 		error("No symbols in namelist");
171 		return(0);
172 	}
173 	netcb.nif_forw = netcb.nif_prev = (struct netinfo *)&netcb;
174 	protos = TCP|UDP;
175 	return(1);
176 }
177 
178 void
179 fetchnetstat()
180 {
181 	struct inpcbtable pcbtable;
182 	register struct inpcb *head, *prev, *next;
183 	register struct netinfo *p;
184 	struct inpcb inpcb;
185 	struct socket sockb;
186 	struct tcpcb tcpcb;
187 	void *off;
188 	int istcp;
189 
190 	if (namelist[X_TCBTABLE].n_value == 0)
191 		return;
192 	for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw)
193 		p->nif_seen = 0;
194 	if (protos&TCP) {
195 		off = NPTR(X_TCBTABLE);
196 		istcp = 1;
197 	}
198 	else if (protos&UDP) {
199 		off = NPTR(X_UDBTABLE);
200 		istcp = 0;
201 	}
202 	else {
203 		error("No protocols to display");
204 		return;
205 	}
206 again:
207 	KREAD(off, &pcbtable, sizeof (struct inpcbtable));
208 	prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
209 	next = pcbtable.inpt_queue.cqh_first;
210 	while (next != head) {
211 		KREAD(next, &inpcb, sizeof (inpcb));
212 		if (inpcb.inp_queue.cqe_prev != prev) {
213 printf("prev = %x, head = %x, next = %x, inpcb...prev = %x\n", prev, head, next, inpcb.inp_queue.cqe_prev);
214 			p = netcb.nif_forw;
215 			for (; p != (struct netinfo *)&netcb; p = p->nif_forw)
216 				p->nif_seen = 1;
217 			error("Kernel state in transition");
218 			return;
219 		}
220 		prev = next;
221 		next = inpcb.inp_queue.cqe_next;
222 
223 #ifndef INET6
224 		if (inpcb.inp_flags & INP_IPV6)
225 			continue;
226 #endif
227 
228 		if (!aflag) {
229 			if (!(inpcb.inp_flags & INP_IPV6)
230 			 && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
231 				continue;
232 #ifdef INET6
233 			if ((inpcb.inp_flags & INP_IPV6)
234 			 && IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6))
235 				continue;
236 #endif
237 		}
238 		if (nhosts && !checkhost(&inpcb))
239 			continue;
240 		if (nports && !checkport(&inpcb))
241 			continue;
242 		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
243 		if (istcp) {
244 			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
245 			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
246 		} else
247 			enter(&inpcb, &sockb, 0, "udp");
248 	}
249 	if (istcp && (protos&UDP)) {
250 		istcp = 0;
251 		off = NPTR(X_UDBTABLE);
252 		goto again;
253 	}
254 }
255 
256 static void
257 enter(inp, so, state, proto)
258 	register struct inpcb *inp;
259 	register struct socket *so;
260 	int state;
261 	char *proto;
262 {
263 	register struct netinfo *p;
264 
265 	/*
266 	 * Only take exact matches, any sockets with
267 	 * previously unbound addresses will be deleted
268 	 * below in the display routine because they
269 	 * will appear as ``not seen'' in the kernel
270 	 * data structures.
271 	 */
272 	for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw) {
273 #ifdef INET6
274 		if (p->nif_family == AF_INET && (inp->inp_flags & INP_IPV6))
275 			continue;
276 		if (p->nif_family == AF_INET6 && !(inp->inp_flags & INP_IPV6))
277 			continue;
278 #endif
279 		if (!streq(proto, p->nif_proto))
280 			continue;
281 		if (p->nif_family == AF_INET) {
282 			if (p->nif_lport != inp->inp_lport ||
283 			    p->nif_laddr.s_addr != inp->inp_laddr.s_addr)
284 				continue;
285 			if (p->nif_faddr.s_addr == inp->inp_faddr.s_addr &&
286 			    p->nif_fport == inp->inp_fport)
287 				break;
288 
289 		}
290 #ifdef INET6
291 		else if (p->nif_family == AF_INET6) {
292 			if (p->nif_lport != inp->inp_lport ||
293 			    !IN6_ARE_ADDR_EQUAL(&p->nif_laddr6, &inp->inp_laddr6))
294 				continue;
295 			if (IN6_ARE_ADDR_EQUAL(&p->nif_faddr6, &inp->inp_faddr6) &&
296 			    p->nif_fport == inp->inp_fport)
297 				break;
298 		}
299 #endif
300 		else
301 			continue;
302 	}
303 	if (p == (struct netinfo *)&netcb) {
304 		if ((p = malloc(sizeof(*p))) == NULL) {
305 			error("Out of memory");
306 			return;
307 		}
308 		p->nif_prev = (struct netinfo *)&netcb;
309 		p->nif_forw = netcb.nif_forw;
310 		netcb.nif_forw->nif_prev = p;
311 		netcb.nif_forw = p;
312 		p->nif_line = -1;
313 		p->nif_lport = inp->inp_lport;
314 		p->nif_fport = inp->inp_fport;
315 		p->nif_proto = proto;
316 		p->nif_flags = NIF_LACHG|NIF_FACHG;
317 #ifdef INET6
318 		if (inp->inp_flags & INP_IPV6) {
319 			p->nif_laddr6 = inp->inp_laddr6;
320 			p->nif_faddr6 = inp->inp_faddr6;
321 			p->nif_family = AF_INET6;
322 		}
323 		else
324 #endif
325 		{
326 			p->nif_laddr = inp->inp_laddr;
327 			p->nif_faddr = inp->inp_faddr;
328 			p->nif_family = AF_INET;
329 		}
330 	}
331 	p->nif_rcvcc = so->so_rcv.sb_cc;
332 	p->nif_sndcc = so->so_snd.sb_cc;
333 	p->nif_state = state;
334 	p->nif_seen = 1;
335 }
336 
337 /* column locations */
338 #define	LADDR	0
339 #define	FADDR	LADDR+23
340 #define	PROTO	FADDR+23
341 #define	RCVCC	PROTO+6
342 #define	SNDCC	RCVCC+7
343 #define	STATE	SNDCC+7
344 
345 
346 void
347 labelnetstat()
348 {
349 	if (namelist[X_TCBTABLE].n_type == 0)
350 		return;
351 	wmove(wnd, 0, 0); wclrtobot(wnd);
352 	mvwaddstr(wnd, 0, LADDR, "Local Address");
353 	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
354 	mvwaddstr(wnd, 0, PROTO, "Proto");
355 	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
356 	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
357 	mvwaddstr(wnd, 0, STATE, "(state)");
358 }
359 
360 void
361 shownetstat()
362 {
363 	register struct netinfo *p, *q;
364 
365 	/*
366 	 * First, delete any connections that have gone
367 	 * away and adjust the position of connections
368 	 * below to reflect the deleted line.
369 	 */
370 	p = netcb.nif_forw;
371 	while (p != (struct netinfo *)&netcb) {
372 		if (p->nif_line == -1 || p->nif_seen) {
373 			p = p->nif_forw;
374 			continue;
375 		}
376 		wmove(wnd, p->nif_line, 0); wdeleteln(wnd);
377 		q = netcb.nif_forw;
378 		for (; q != (struct netinfo *)&netcb; q = q->nif_forw)
379 			if (q != p && q->nif_line > p->nif_line) {
380 				q->nif_line--;
381 				/* this shouldn't be necessary */
382 				q->nif_flags |= NIF_LACHG|NIF_FACHG;
383 			}
384 		lastrow--;
385 		q = p->nif_forw;
386 		p->nif_prev->nif_forw = p->nif_forw;
387 		p->nif_forw->nif_prev = p->nif_prev;
388 		free(p);
389 		p = q;
390 	}
391 	/*
392 	 * Update existing connections and add new ones.
393 	 */
394 	for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw) {
395 		if (p->nif_line == -1) {
396 			/*
397 			 * Add a new entry if possible.
398 			 */
399 			if (lastrow > YMAX(wnd))
400 				continue;
401 			p->nif_line = lastrow++;
402 			p->nif_flags |= NIF_LACHG|NIF_FACHG;
403 		}
404 		if (p->nif_flags & NIF_LACHG) {
405 			wmove(wnd, p->nif_line, LADDR);
406 			switch (p->nif_family) {
407 			case AF_INET:
408 				inetprint(&p->nif_laddr, p->nif_lport,
409 					p->nif_proto);
410 				break;
411 #ifdef INET6
412 			case AF_INET6:
413 				inet6print(&p->nif_laddr6, p->nif_lport,
414 					p->nif_proto);
415 				break;
416 #endif
417 			}
418 			p->nif_flags &= ~NIF_LACHG;
419 		}
420 		if (p->nif_flags & NIF_FACHG) {
421 			wmove(wnd, p->nif_line, FADDR);
422 			switch (p->nif_family) {
423 			case AF_INET:
424 				inetprint(&p->nif_faddr, p->nif_fport,
425 					p->nif_proto);
426 				break;
427 #ifdef INET6
428 			case AF_INET6:
429 				inet6print(&p->nif_faddr6, p->nif_fport,
430 					p->nif_proto);
431 				break;
432 #endif
433 			}
434 			p->nif_flags &= ~NIF_FACHG;
435 		}
436 		mvwaddstr(wnd, p->nif_line, PROTO, p->nif_proto);
437 #ifdef INET6
438 		if (p->nif_family == AF_INET6)
439 			waddstr(wnd, "6");
440 #endif
441 		mvwprintw(wnd, p->nif_line, RCVCC, "%6d", p->nif_rcvcc);
442 		mvwprintw(wnd, p->nif_line, SNDCC, "%6d", p->nif_sndcc);
443 		if (streq(p->nif_proto, "tcp"))
444 			if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES)
445 				mvwprintw(wnd, p->nif_line, STATE, "%d",
446 				    p->nif_state);
447 			else
448 				mvwaddstr(wnd, p->nif_line, STATE,
449 				    tcpstates[p->nif_state]);
450 		wclrtoeol(wnd);
451 	}
452 	if (lastrow < YMAX(wnd)) {
453 		wmove(wnd, lastrow, 0); wclrtobot(wnd);
454 		wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd);	/* XXX */
455 	}
456 }
457 
458 /*
459  * Pretty print an Internet address (net address + port).
460  * If the nflag was specified, use numbers instead of names.
461  */
462 static void
463 inetprint(in, port, proto)
464 	register struct in_addr *in;
465 	int port;
466 	char *proto;
467 {
468 	struct servent *sp = 0;
469 	char line[80], *cp;
470 
471 	snprintf(line, sizeof line, "%.*s.", 16, inetname(*in));
472 	cp = strchr(line, '\0');
473 	if (!nflag && port)
474 		sp = getservbyport(port, proto);
475 	if (sp || port == 0)
476 		snprintf(cp, sizeof line - strlen(cp), "%.8s",
477 		    sp ? sp->s_name : "*");
478 	else
479 		snprintf(cp, sizeof line - strlen(cp), "%d",
480 		    ntohs((u_short)port));
481 	/* pad to full column to clear any garbage */
482 	cp = strchr(line, '\0');
483 	while (cp - line < 22 && cp - line < sizeof line-1)
484 		*cp++ = ' ';
485 	*cp = '\0';
486 	waddstr(wnd, line);
487 }
488 
489 #ifdef INET6
490 static void
491 inet6print(in6, port, proto)
492 	register struct in6_addr *in6;
493 	int port;
494 	char *proto;
495 {
496 	struct servent *sp = 0;
497 	char line[80], *cp;
498 
499 	snprintf(line, sizeof line, "%.*s.", 16, inet6name(in6));
500 	cp = strchr(line, '\0');
501 	if (!nflag && port)
502 		sp = getservbyport(port, proto);
503 	if (sp || port == 0)
504 		snprintf(cp, sizeof line - strlen(cp), "%.8s",
505 		    sp ? sp->s_name : "*");
506 	else
507 		snprintf(cp, sizeof line - strlen(cp), "%d",
508 		    ntohs((u_short)port));
509 	/* pad to full column to clear any garbage */
510 	cp = strchr(line, '\0');
511 	while (cp - line < 22 && cp - line < sizeof line-1)
512 		*cp++ = ' ';
513 	*cp = '\0';
514 	waddstr(wnd, line);
515 }
516 #endif
517 
518 /*
519  * Construct an Internet address representation.
520  * If the nflag has been supplied, give
521  * numeric value, otherwise try for symbolic name.
522  */
523 static const char *
524 inetname(in)
525 	struct in_addr in;
526 {
527 	char *cp = 0;
528 	static char line[50];
529 	struct hostent *hp;
530 	struct netent *np;
531 
532 	if (!nflag && in.s_addr != INADDR_ANY) {
533 		int net = inet_netof(in);
534 		int lna = inet_lnaof(in);
535 
536 		if (lna == INADDR_ANY) {
537 			np = getnetbyaddr(net, AF_INET);
538 			if (np)
539 				cp = np->n_name;
540 		}
541 		if (cp == 0) {
542 			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
543 			if (hp)
544 				cp = hp->h_name;
545 		}
546 	}
547 	if (in.s_addr == INADDR_ANY) {
548 		strncpy(line, "*", sizeof line-1);
549 		line[sizeof line-1] = '\0';
550 	} else if (cp) {
551 		strncpy(line, cp, sizeof line-1);
552 		line[sizeof line-1] = '\0';
553 	} else {
554 		in.s_addr = ntohl(in.s_addr);
555 #define C(x)	((x) & 0xff)
556 		snprintf(line, sizeof line, "%u.%u.%u.%u", C(in.s_addr >> 24),
557 			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
558 	}
559 	return (line);
560 }
561 
562 #ifdef INET6
563 static const char *
564 inet6name(in6)
565 	struct in6_addr *in6;
566 {
567 	static char line[NI_MAXHOST];
568 	struct sockaddr_in6 sin6;
569 	int flags;
570 
571 	if (nflag)
572 		flags = NI_NUMERICHOST;
573 	else
574 		flags = 0;
575 	if (IN6_IS_ADDR_UNSPECIFIED(in6))
576 		return "*";
577 	memset(&sin6, 0, sizeof(sin6));
578 	sin6.sin6_family = AF_INET6;
579 	sin6.sin6_len = sizeof(struct sockaddr_in6);
580 	sin6.sin6_addr = *in6;
581 	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
582 			line, sizeof(line), NULL, 0, flags) == 0)
583 		return line;
584 	return "?";
585 }
586 #endif
587 
588 int
589 cmdnetstat(cmd, args)
590 	char *cmd, *args;
591 {
592 	register struct netinfo *p;
593 
594 	if (prefix(cmd, "all")) {
595 		aflag = !aflag;
596 		goto fixup;
597 	}
598 	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
599 		int new;
600 
601 		new = prefix(cmd, "numbers");
602 		if (new == nflag)
603 			return (1);
604 		p = netcb.nif_forw;
605 		for (; p != (struct netinfo *)&netcb; p = p->nif_forw) {
606 			if (p->nif_line == -1)
607 				continue;
608 			p->nif_flags |= NIF_LACHG|NIF_FACHG;
609 		}
610 		nflag = new;
611 		wclear(wnd);
612 		labelnetstat();
613 		goto redisplay;
614 	}
615 	if (!netcmd(cmd, args))
616 		return (0);
617 fixup:
618 	fetchnetstat();
619 redisplay:
620 	shownetstat();
621 	refresh();
622 	return (1);
623 }
624