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