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