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