xref: /openbsd-src/usr.bin/systat/netstat.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$OpenBSD: netstat.c,v 1.20 2001/12/07 09:18:08 deraadt 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.20 2001/12/07 09:18:08 deraadt 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 <err.h>
77 #include <nlist.h>
78 #include <paths.h>
79 #include "systat.h"
80 #include "extern.h"
81 
82 static void enter __P((struct inpcb *, struct socket *, int, char *));
83 static const char *inetname __P((struct in_addr));
84 static void inetprint __P((struct in_addr *, int, char *));
85 #ifdef INET6
86 static const char *inet6name __P((struct in6_addr *));
87 static void inet6print __P((struct in6_addr *, int, char *));
88 #endif
89 
90 #define	streq(a,b)	(strcmp(a,b)==0)
91 #define	YMAX(w)		((w)->_maxy-1)
92 
93 WINDOW *
94 opennetstat()
95 {
96 	sethostent(1);
97 	setnetent(1);
98 	return (subwin(stdscr, LINES-5-1, 0, 5, 0));
99 }
100 
101 struct netinfo {
102 	struct	netinfo *nif_forw, *nif_prev;
103 	int	nif_family;
104 	short	nif_line;		/* line on screen */
105 	short	nif_seen;		/* 0 when not present in list */
106 	short	nif_flags;
107 #define	NIF_LACHG	0x1		/* local address changed */
108 #define	NIF_FACHG	0x2		/* foreign address changed */
109 	short	nif_state;		/* tcp state */
110 	char	*nif_proto;		/* protocol */
111 	struct	in_addr nif_laddr;	/* local address */
112 #ifdef INET6
113 	struct	in6_addr nif_laddr6;	/* local address */
114 #endif
115 	long	nif_lport;		/* local port */
116 	struct	in_addr	nif_faddr;	/* foreign address */
117 #ifdef INET6
118 	struct	in6_addr nif_faddr6;	/* foreign address */
119 #endif
120 	long	nif_fport;		/* foreign port */
121 	long	nif_rcvcc;		/* rcv buffer character count */
122 	long	nif_sndcc;		/* snd buffer character count */
123 };
124 
125 static struct {
126 	struct	netinfo *nif_forw, *nif_prev;
127 } netcb;
128 
129 static	int aflag = 0;
130 static	int nflag = 0;
131 static	int lastrow = 1;
132 
133 void
134 closenetstat(w)
135 	WINDOW *w;
136 {
137 	struct netinfo *p;
138 
139 	endhostent();
140 	endnetent();
141 	p = (struct netinfo *)netcb.nif_forw;
142 	while (p != (struct netinfo *)&netcb) {
143 		if (p->nif_line != -1)
144 			lastrow--;
145 		p->nif_line = -1;
146 		p = p->nif_forw;
147 	}
148 	if (w != NULL) {
149 		wclear(w);
150 		wrefresh(w);
151 		delwin(w);
152 	}
153 }
154 
155 static struct nlist namelist[] = {
156 #define	X_TCBTABLE	0
157 	{ "_tcbtable" },
158 #define	X_UDBTABLE	1
159 	{ "_udbtable" },
160 	{ "" },
161 };
162 
163 int
164 initnetstat()
165 {
166 	int ret;
167 
168 	if ((ret = kvm_nlist(kd, namelist)) == -1)
169 		errx(1, "%s", kvm_geterr(kd));
170 	else if (ret)
171 		nlisterr(namelist);
172 	if (namelist[X_TCBTABLE].n_value == 0) {
173 		error("No symbols in namelist");
174 		return(0);
175 	}
176 	netcb.nif_forw = netcb.nif_prev = (struct netinfo *)&netcb;
177 	protos = TCP|UDP;
178 	return(1);
179 }
180 
181 void
182 fetchnetstat()
183 {
184 	struct inpcbtable pcbtable;
185 	struct inpcb *head, *prev, *next;
186 	struct netinfo *p;
187 	struct inpcb inpcb;
188 	struct socket sockb;
189 	struct tcpcb tcpcb;
190 	void *off;
191 	int istcp;
192 
193 	if (namelist[X_TCBTABLE].n_value == 0)
194 		return;
195 	for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw)
196 		p->nif_seen = 0;
197 	if (protos&TCP) {
198 		off = NPTR(X_TCBTABLE);
199 		istcp = 1;
200 	} else if (protos&UDP) {
201 		off = NPTR(X_UDBTABLE);
202 		istcp = 0;
203 	} else {
204 		error("No protocols to display");
205 		return;
206 	}
207 again:
208 	KREAD(off, &pcbtable, sizeof (struct inpcbtable));
209 	prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
210 	next = pcbtable.inpt_queue.cqh_first;
211 	while (next != head) {
212 		KREAD(next, &inpcb, sizeof (inpcb));
213 		if (inpcb.inp_queue.cqe_prev != prev) {
214 printf("prev = %p, head = %p, next = %p, inpcb...prev = %p\n", prev, head, next, inpcb.inp_queue.cqe_prev);
215 			p = netcb.nif_forw;
216 			for (; p != (struct netinfo *)&netcb; p = p->nif_forw)
217 				p->nif_seen = 1;
218 			error("Kernel state in transition");
219 			return;
220 		}
221 		prev = next;
222 		next = inpcb.inp_queue.cqe_next;
223 
224 #ifndef INET6
225 		if (inpcb.inp_flags & INP_IPV6)
226 			continue;
227 #endif
228 
229 		if (!aflag) {
230 			if (!(inpcb.inp_flags & INP_IPV6) &&
231 			    inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
232 				continue;
233 #ifdef INET6
234 			if ((inpcb.inp_flags & INP_IPV6) &&
235 			    IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6))
236 				continue;
237 #endif
238 		}
239 		if (nhosts && !checkhost(&inpcb))
240 			continue;
241 		if (nports && !checkport(&inpcb))
242 			continue;
243 		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
244 		if (istcp) {
245 			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
246 			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
247 		} else
248 			enter(&inpcb, &sockb, 0, "udp");
249 	}
250 	if (istcp && (protos&UDP)) {
251 		istcp = 0;
252 		off = NPTR(X_UDBTABLE);
253 		goto again;
254 	}
255 }
256 
257 static void
258 enter(inp, so, state, proto)
259 	struct inpcb *inp;
260 	struct socket *so;
261 	int state;
262 	char *proto;
263 {
264 	struct netinfo *p;
265 
266 	/*
267 	 * Only take exact matches, any sockets with
268 	 * previously unbound addresses will be deleted
269 	 * below in the display routine because they
270 	 * will appear as ``not seen'' in the kernel
271 	 * data structures.
272 	 */
273 	for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw) {
274 #ifdef INET6
275 		if (p->nif_family == AF_INET && (inp->inp_flags & INP_IPV6))
276 			continue;
277 		if (p->nif_family == AF_INET6 && !(inp->inp_flags & INP_IPV6))
278 			continue;
279 #endif
280 		if (!streq(proto, p->nif_proto))
281 			continue;
282 		if (p->nif_family == AF_INET) {
283 			if (p->nif_lport != inp->inp_lport ||
284 			    p->nif_laddr.s_addr != inp->inp_laddr.s_addr)
285 				continue;
286 			if (p->nif_faddr.s_addr == inp->inp_faddr.s_addr &&
287 			    p->nif_fport == inp->inp_fport)
288 				break;
289 
290 		}
291 #ifdef INET6
292 		else if (p->nif_family == AF_INET6) {
293 			if (p->nif_lport != inp->inp_lport ||
294 			    !IN6_ARE_ADDR_EQUAL(&p->nif_laddr6, &inp->inp_laddr6))
295 				continue;
296 			if (IN6_ARE_ADDR_EQUAL(&p->nif_faddr6, &inp->inp_faddr6) &&
297 			    p->nif_fport == inp->inp_fport)
298 				break;
299 		}
300 #endif
301 		else
302 			continue;
303 	}
304 	if (p == (struct netinfo *)&netcb) {
305 		if ((p = malloc(sizeof(*p))) == NULL) {
306 			error("Out of memory");
307 			return;
308 		}
309 		p->nif_prev = (struct netinfo *)&netcb;
310 		p->nif_forw = netcb.nif_forw;
311 		netcb.nif_forw->nif_prev = p;
312 		netcb.nif_forw = p;
313 		p->nif_line = -1;
314 		p->nif_lport = inp->inp_lport;
315 		p->nif_fport = inp->inp_fport;
316 		p->nif_proto = proto;
317 		p->nif_flags = NIF_LACHG|NIF_FACHG;
318 #ifdef INET6
319 		if (inp->inp_flags & INP_IPV6) {
320 			p->nif_laddr6 = inp->inp_laddr6;
321 			p->nif_faddr6 = inp->inp_faddr6;
322 			p->nif_family = AF_INET6;
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 	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 		}
451 		wclrtoeol(wnd);
452 	}
453 	if (lastrow < YMAX(wnd)) {
454 		wmove(wnd, lastrow, 0); wclrtobot(wnd);
455 		wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd);	/* XXX */
456 	}
457 }
458 
459 /*
460  * Pretty print an Internet address (net address + port).
461  * If the nflag was specified, use numbers instead of names.
462  */
463 static void
464 inetprint(in, port, proto)
465 	struct in_addr *in;
466 	int port;
467 	char *proto;
468 {
469 	struct servent *sp = 0;
470 	char line[80], *cp;
471 
472 	snprintf(line, sizeof line, "%.*s.", 16, inetname(*in));
473 	cp = strchr(line, '\0');
474 	if (!nflag && port)
475 		sp = getservbyport(port, proto);
476 	if (sp || port == 0)
477 		snprintf(cp, sizeof line - strlen(cp), "%.8s",
478 		    sp ? sp->s_name : "*");
479 	else
480 		snprintf(cp, sizeof line - strlen(cp), "%d",
481 		    ntohs((u_short)port));
482 	/* pad to full column to clear any garbage */
483 	cp = strchr(line, '\0');
484 	while (cp - line < 22 && cp - line < sizeof line-1)
485 		*cp++ = ' ';
486 	*cp = '\0';
487 	waddstr(wnd, line);
488 }
489 
490 #ifdef INET6
491 static void
492 inet6print(in6, port, proto)
493 	struct in6_addr *in6;
494 	int port;
495 	char *proto;
496 {
497 	struct servent *sp = 0;
498 	char line[80], *cp;
499 
500 	snprintf(line, sizeof line, "%.*s.", 16, inet6name(in6));
501 	cp = strchr(line, '\0');
502 	if (!nflag && port)
503 		sp = getservbyport(port, proto);
504 	if (sp || port == 0)
505 		snprintf(cp, sizeof line - strlen(cp), "%.8s",
506 		    sp ? sp->s_name : "*");
507 	else
508 		snprintf(cp, sizeof line - strlen(cp), "%d",
509 		    ntohs((u_short)port));
510 	/* pad to full column to clear any garbage */
511 	cp = strchr(line, '\0');
512 	while (cp - line < 22 && cp - line < sizeof line-1)
513 		*cp++ = ' ';
514 	*cp = '\0';
515 	waddstr(wnd, line);
516 }
517 #endif
518 
519 /*
520  * Construct an Internet address representation.
521  * If the nflag has been supplied, give
522  * numeric value, otherwise try for symbolic name.
523  */
524 static const char *
525 inetname(in)
526 	struct in_addr in;
527 {
528 	char *cp = 0;
529 	static char line[50];
530 	struct hostent *hp;
531 	struct netent *np;
532 
533 	if (!nflag && in.s_addr != INADDR_ANY) {
534 		int net = inet_netof(in);
535 		int lna = inet_lnaof(in);
536 
537 		if (lna == INADDR_ANY) {
538 			np = getnetbyaddr(net, AF_INET);
539 			if (np)
540 				cp = np->n_name;
541 		}
542 		if (cp == 0) {
543 			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
544 			if (hp)
545 				cp = hp->h_name;
546 		}
547 	}
548 	if (in.s_addr == INADDR_ANY) {
549 		strlcpy(line, "*", sizeof line);
550 	} else if (cp) {
551 		strlcpy(line, cp, sizeof line);
552 	} else {
553 		in.s_addr = ntohl(in.s_addr);
554 #define C(x)	((x) & 0xff)
555 		snprintf(line, sizeof line, "%u.%u.%u.%u", C(in.s_addr >> 24),
556 		    C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
557 	}
558 	return (line);
559 }
560 
561 #ifdef INET6
562 static const char *
563 inet6name(in6)
564 	struct in6_addr *in6;
565 {
566 	static char line[NI_MAXHOST];
567 	struct sockaddr_in6 sin6;
568 	int flags;
569 
570 	if (nflag)
571 		flags = NI_NUMERICHOST;
572 	else
573 		flags = 0;
574 	if (IN6_IS_ADDR_UNSPECIFIED(in6))
575 		return "*";
576 	memset(&sin6, 0, sizeof(sin6));
577 	sin6.sin6_family = AF_INET6;
578 	sin6.sin6_len = sizeof(struct sockaddr_in6);
579 	sin6.sin6_addr = *in6;
580 	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
581 	    line, sizeof(line), NULL, 0, flags) == 0)
582 		return line;
583 	return "?";
584 }
585 #endif
586 
587 int
588 cmdnetstat(cmd, args)
589 	char *cmd, *args;
590 {
591 	struct netinfo *p;
592 
593 	if (prefix(cmd, "all")) {
594 		aflag = !aflag;
595 		goto fixup;
596 	}
597 	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
598 		int new;
599 
600 		new = prefix(cmd, "numbers");
601 		if (new == nflag)
602 			return (1);
603 		p = netcb.nif_forw;
604 		for (; p != (struct netinfo *)&netcb; p = p->nif_forw) {
605 			if (p->nif_line == -1)
606 				continue;
607 			p->nif_flags |= NIF_LACHG|NIF_FACHG;
608 		}
609 		nflag = new;
610 		wclear(wnd);
611 		labelnetstat();
612 		goto redisplay;
613 	}
614 	if (!netcmd(cmd, args))
615 		return (0);
616 fixup:
617 	fetchnetstat();
618 redisplay:
619 	shownetstat();
620 	refresh();
621 	return (1);
622 }
623