netstat.c revision 50477
1/*-
2 * Copyright (c) 1980, 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35/*
36static char sccsid[] = "@(#)netstat.c	8.1 (Berkeley) 6/6/93";
37*/
38static const char rcsid[] =
39  "$FreeBSD: head/usr.bin/systat/netstat.c 50477 1999-08-28 01:08:13Z peter $";
40#endif /* not lint */
41
42/*
43 * netstat
44 */
45#include <sys/param.h>
46#include <sys/queue.h>
47#include <sys/socket.h>
48#include <sys/socketvar.h>
49#include <sys/protosw.h>
50
51#include <netinet/in.h>
52#include <arpa/inet.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
79static void enter __P((struct inpcb *, struct socket *, int, char *));
80static char *inetname __P((struct in_addr));
81static 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
86WINDOW *
87opennetstat()
88{
89	sethostent(1);
90	setnetent(1);
91	return (subwin(stdscr, LINES-5-1, 0, 5, 0));
92}
93
94struct 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
111static struct {
112	struct	netinfo *ni_forw, *ni_prev;
113} netcb;
114
115static	int aflag = 0;
116static	int nflag = 0;
117static	int lastrow = 1;
118static	void enter(), inetprint();
119static	char *inetname();
120
121void
122closenetstat(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
143static struct nlist namelist[] = {
144#define	X_TCB	0
145	{ "_tcb" },
146#define	X_UDB	1
147	{ "_udb" },
148	{ "" },
149};
150
151int
152initnetstat()
153{
154	if (kvm_nlist(kd, namelist)) {
155		nlisterr(namelist);
156		return(0);
157	}
158	if (namelist[X_TCB].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
167void
168fetchnetstat()
169{
170	register struct inpcb *next;
171	register struct netinfo *p;
172	struct inpcbhead head;
173	struct inpcb inpcb;
174	struct socket sockb;
175	struct tcpcb tcpcb;
176	void *off;
177	int istcp;
178
179	if (namelist[X_TCB].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_TCB);
185		istcp = 1;
186	}
187	else if (protos&UDP) {
188		off = NPTR(X_UDB);
189		istcp = 0;
190	}
191	else {
192		error("No protocols to display");
193		return;
194	}
195again:
196	KREAD(off, &head, sizeof (struct inpcbhead));
197	for (next = head.lh_first; next != NULL; next = inpcb.inp_list.le_next) {
198		KREAD(next, &inpcb, sizeof (inpcb));
199		if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
200			continue;
201		if (nhosts && !checkhost(&inpcb))
202			continue;
203		if (nports && !checkport(&inpcb))
204			continue;
205		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
206		if (istcp) {
207			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
208			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
209		} else
210			enter(&inpcb, &sockb, 0, "udp");
211	}
212	if (istcp && (protos&UDP)) {
213		istcp = 0;
214		off = NPTR(X_UDB);
215		goto again;
216	}
217}
218
219static void
220enter(inp, so, state, proto)
221	register struct inpcb *inp;
222	register struct socket *so;
223	int state;
224	char *proto;
225{
226	register struct netinfo *p;
227
228	/*
229	 * Only take exact matches, any sockets with
230	 * previously unbound addresses will be deleted
231	 * below in the display routine because they
232	 * will appear as ``not seen'' in the kernel
233	 * data structures.
234	 */
235	for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
236		if (!streq(proto, p->ni_proto))
237			continue;
238		if (p->ni_lport != inp->inp_lport ||
239		    p->ni_laddr.s_addr != inp->inp_laddr.s_addr)
240			continue;
241		if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr &&
242		    p->ni_fport == inp->inp_fport)
243			break;
244	}
245	if (p == (struct netinfo *)&netcb) {
246		if ((p = malloc(sizeof(*p))) == NULL) {
247			error("Out of memory");
248			return;
249		}
250		p->ni_prev = (struct netinfo *)&netcb;
251		p->ni_forw = netcb.ni_forw;
252		netcb.ni_forw->ni_prev = p;
253		netcb.ni_forw = p;
254		p->ni_line = -1;
255		p->ni_laddr = inp->inp_laddr;
256		p->ni_lport = inp->inp_lport;
257		p->ni_faddr = inp->inp_faddr;
258		p->ni_fport = inp->inp_fport;
259		p->ni_proto = proto;
260		p->ni_flags = NIF_LACHG|NIF_FACHG;
261	}
262	p->ni_rcvcc = so->so_rcv.sb_cc;
263	p->ni_sndcc = so->so_snd.sb_cc;
264	p->ni_state = state;
265	p->ni_seen = 1;
266}
267
268/* column locations */
269#define	LADDR	0
270#define	FADDR	LADDR+23
271#define	PROTO	FADDR+23
272#define	RCVCC	PROTO+6
273#define	SNDCC	RCVCC+7
274#define	STATE	SNDCC+7
275
276
277void
278labelnetstat()
279{
280	if (namelist[X_TCB].n_type == 0)
281		return;
282	wmove(wnd, 0, 0); wclrtobot(wnd);
283	mvwaddstr(wnd, 0, LADDR, "Local Address");
284	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
285	mvwaddstr(wnd, 0, PROTO, "Proto");
286	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
287	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
288	mvwaddstr(wnd, 0, STATE, "(state)");
289}
290
291void
292shownetstat()
293{
294	register struct netinfo *p, *q;
295
296	/*
297	 * First, delete any connections that have gone
298	 * away and adjust the position of connections
299	 * below to reflect the deleted line.
300	 */
301	p = netcb.ni_forw;
302	while (p != (struct netinfo *)&netcb) {
303		if (p->ni_line == -1 || p->ni_seen) {
304			p = p->ni_forw;
305			continue;
306		}
307		wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
308		q = netcb.ni_forw;
309		for (; q != (struct netinfo *)&netcb; q = q->ni_forw)
310			if (q != p && q->ni_line > p->ni_line) {
311				q->ni_line--;
312				/* this shouldn't be necessary */
313				q->ni_flags |= NIF_LACHG|NIF_FACHG;
314			}
315		lastrow--;
316		q = p->ni_forw;
317		p->ni_prev->ni_forw = p->ni_forw;
318		p->ni_forw->ni_prev = p->ni_prev;
319		free(p);
320		p = q;
321	}
322	/*
323	 * Update existing connections and add new ones.
324	 */
325	for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
326		if (p->ni_line == -1) {
327			/*
328			 * Add a new entry if possible.
329			 */
330			if (lastrow > YMAX(wnd))
331				continue;
332			p->ni_line = lastrow++;
333			p->ni_flags |= NIF_LACHG|NIF_FACHG;
334		}
335		if (p->ni_flags & NIF_LACHG) {
336			wmove(wnd, p->ni_line, LADDR);
337			inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto);
338			p->ni_flags &= ~NIF_LACHG;
339		}
340		if (p->ni_flags & NIF_FACHG) {
341			wmove(wnd, p->ni_line, FADDR);
342			inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto);
343			p->ni_flags &= ~NIF_FACHG;
344		}
345		mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto);
346		mvwprintw(wnd, p->ni_line, RCVCC, "%6d", p->ni_rcvcc);
347		mvwprintw(wnd, p->ni_line, SNDCC, "%6d", p->ni_sndcc);
348		if (streq(p->ni_proto, "tcp"))
349			if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
350				mvwprintw(wnd, p->ni_line, STATE, "%d",
351				    p->ni_state);
352			else
353				mvwaddstr(wnd, p->ni_line, STATE,
354				    tcpstates[p->ni_state]);
355		wclrtoeol(wnd);
356	}
357	if (lastrow < YMAX(wnd)) {
358		wmove(wnd, lastrow, 0); wclrtobot(wnd);
359		wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd);	/* XXX */
360	}
361}
362
363/*
364 * Pretty print an Internet address (net address + port).
365 * If the nflag was specified, use numbers instead of names.
366 */
367static void
368inetprint(in, port, proto)
369	register struct in_addr *in;
370	int port;
371	char *proto;
372{
373	struct servent *sp = 0;
374	char line[80], *cp, *index();
375
376	snprintf(line, sizeof(line), "%.*s.", 16, inetname(*in));
377	cp = index(line, '\0');
378	if (!nflag && port)
379		sp = getservbyport(port, proto);
380	if (sp || port == 0)
381		snprintf(cp, sizeof(line) - (cp - line), "%.8s",
382		    sp ? sp->s_name : "*");
383	else
384		snprintf(cp, sizeof(line) - (cp - line), "%d",
385		    ntohs((u_short)port));
386	/* pad to full column to clear any garbage */
387	cp = index(line, '\0');
388	while (cp - line < 22)
389		*cp++ = ' ';
390	line[22] = '\0';
391	waddstr(wnd, line);
392}
393
394/*
395 * Construct an Internet address representation.
396 * If the nflag has been supplied, give
397 * numeric value, otherwise try for symbolic name.
398 */
399static char *
400inetname(in)
401	struct in_addr in;
402{
403	char *cp = 0;
404	static char line[50];
405	struct hostent *hp;
406	struct netent *np;
407
408	if (!nflag && in.s_addr != INADDR_ANY) {
409		int net = inet_netof(in);
410		int lna = inet_lnaof(in);
411
412		if (lna == INADDR_ANY) {
413			np = getnetbyaddr(net, AF_INET);
414			if (np)
415				cp = np->n_name;
416		}
417		if (cp == 0) {
418			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
419			if (hp)
420				cp = hp->h_name;
421		}
422	}
423	if (in.s_addr == INADDR_ANY)
424		strcpy(line, "*");
425	else if (cp)
426		snprintf(line, sizeof(line), "%s", cp);
427	else {
428		in.s_addr = ntohl(in.s_addr);
429#define C(x)	((x) & 0xff)
430		snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
431			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
432	}
433	return (line);
434}
435
436int
437cmdnetstat(cmd, args)
438	char *cmd, *args;
439{
440	register struct netinfo *p;
441
442	if (prefix(cmd, "all")) {
443		aflag = !aflag;
444		goto fixup;
445	}
446	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
447		int new;
448
449		new = prefix(cmd, "numbers");
450		if (new == nflag)
451			return (1);
452		p = netcb.ni_forw;
453		for (; p != (struct netinfo *)&netcb; p = p->ni_forw) {
454			if (p->ni_line == -1)
455				continue;
456			p->ni_flags |= NIF_LACHG|NIF_FACHG;
457		}
458		nflag = new;
459		goto redisplay;
460	}
461	if (!netcmd(cmd, args))
462		return (0);
463fixup:
464	fetchnetstat();
465redisplay:
466	shownetstat();
467	refresh();
468	return (1);
469}
470