netstat.c revision 70523
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 70523 2000-12-30 21:17:03Z phk $";
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	TAILQ_ENTRY(netinfo) chain;
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
111TAILQ_HEAD(netinfohead, netinfo) netcb = TAILQ_HEAD_INITIALIZER(netcb);
112
113static	int aflag = 0;
114static	int nflag = 0;
115static	int lastrow = 1;
116static	void enter(), inetprint();
117static	char *inetname();
118
119void
120closenetstat(w)
121        WINDOW *w;
122{
123	register struct netinfo *p;
124
125	endhostent();
126	endnetent();
127	TAILQ_FOREACH(p, *netcb, chain) {
128		if (p->ni_line != -1)
129			lastrow--;
130		p->ni_line = -1;
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	protos = TCP|UDP;
159	return(1);
160}
161
162void
163fetchnetstat()
164{
165	register struct inpcb *next;
166	register struct netinfo *p;
167	struct inpcbhead head;
168	struct inpcb inpcb;
169	struct socket sockb;
170	struct tcpcb tcpcb;
171	void *off;
172	int istcp;
173
174	if (namelist[X_TCB].n_value == 0)
175		return;
176	TAILQ_FOREACH(p, &netcb, chain)
177		p->ni_seen = 0;
178	if (protos&TCP) {
179		off = NPTR(X_TCB);
180		istcp = 1;
181	}
182	else if (protos&UDP) {
183		off = NPTR(X_UDB);
184		istcp = 0;
185	}
186	else {
187		error("No protocols to display");
188		return;
189	}
190again:
191	KREAD(off, &head, sizeof (struct inpcbhead));
192	LIST_FOREACH(next, &head, inp_list) {
193		KREAD(next, &inpcb, sizeof (inpcb));
194		if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
195			continue;
196		if (nhosts && !checkhost(&inpcb))
197			continue;
198		if (nports && !checkport(&inpcb))
199			continue;
200		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
201		if (istcp) {
202			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
203			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
204		} else
205			enter(&inpcb, &sockb, 0, "udp");
206	}
207	if (istcp && (protos&UDP)) {
208		istcp = 0;
209		off = NPTR(X_UDB);
210		goto again;
211	}
212}
213
214static void
215enter(inp, so, state, proto)
216	register struct inpcb *inp;
217	register struct socket *so;
218	int state;
219	char *proto;
220{
221	register struct netinfo *p;
222
223	/*
224	 * Only take exact matches, any sockets with
225	 * previously unbound addresses will be deleted
226	 * below in the display routine because they
227	 * will appear as ``not seen'' in the kernel
228	 * data structures.
229	 */
230	TAILQ_FOREACH(p, &netcb, chain) {
231		if (!streq(proto, p->ni_proto))
232			continue;
233		if (p->ni_lport != inp->inp_lport ||
234		    p->ni_laddr.s_addr != inp->inp_laddr.s_addr)
235			continue;
236		if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr &&
237		    p->ni_fport == inp->inp_fport)
238			break;
239	}
240	if (p == NULL) {
241		if ((p = malloc(sizeof(*p))) == NULL) {
242			error("Out of memory");
243			return;
244		}
245		TAILQ_INSERT_HEAD(&netcb, p, chain);
246		p->ni_line = -1;
247		p->ni_laddr = inp->inp_laddr;
248		p->ni_lport = inp->inp_lport;
249		p->ni_faddr = inp->inp_faddr;
250		p->ni_fport = inp->inp_fport;
251		p->ni_proto = proto;
252		p->ni_flags = NIF_LACHG|NIF_FACHG;
253	}
254	p->ni_rcvcc = so->so_rcv.sb_cc;
255	p->ni_sndcc = so->so_snd.sb_cc;
256	p->ni_state = state;
257	p->ni_seen = 1;
258}
259
260/* column locations */
261#define	LADDR	0
262#define	FADDR	LADDR+23
263#define	PROTO	FADDR+23
264#define	RCVCC	PROTO+6
265#define	SNDCC	RCVCC+7
266#define	STATE	SNDCC+7
267
268
269void
270labelnetstat()
271{
272	if (namelist[X_TCB].n_type == 0)
273		return;
274	wmove(wnd, 0, 0); wclrtobot(wnd);
275	mvwaddstr(wnd, 0, LADDR, "Local Address");
276	mvwaddstr(wnd, 0, FADDR, "Foreign Address");
277	mvwaddstr(wnd, 0, PROTO, "Proto");
278	mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
279	mvwaddstr(wnd, 0, SNDCC, "Send-Q");
280	mvwaddstr(wnd, 0, STATE, "(state)");
281}
282
283void
284shownetstat()
285{
286	register struct netinfo *p, *q;
287
288	/*
289	 * First, delete any connections that have gone
290	 * away and adjust the position of connections
291	 * below to reflect the deleted line.
292	 */
293	TAILQ_FOREACH(p, &netcb, chain) {
294		if (p->ni_line == -1 || p->ni_seen)
295			continue;
296		wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
297		TAILQ_FOREACH(q, &netcb, chain)
298			if (q != p && q->ni_line > p->ni_line) {
299				q->ni_line--;
300				/* this shouldn't be necessary */
301				q->ni_flags |= NIF_LACHG|NIF_FACHG;
302			}
303		lastrow--;
304		q = TAILQ_PREV(p);
305		TAILQ_REMOVE(&netcb, p, chain);
306		free(p);
307		p = q;
308	}
309	/*
310	 * Update existing connections and add new ones.
311	 */
312	TAILQ_FOREACH(p, &netcb, chain) {
313		if (p->ni_line == -1) {
314			/*
315			 * Add a new entry if possible.
316			 */
317			if (lastrow > YMAX(wnd))
318				continue;
319			p->ni_line = lastrow++;
320			p->ni_flags |= NIF_LACHG|NIF_FACHG;
321		}
322		if (p->ni_flags & NIF_LACHG) {
323			wmove(wnd, p->ni_line, LADDR);
324			inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto);
325			p->ni_flags &= ~NIF_LACHG;
326		}
327		if (p->ni_flags & NIF_FACHG) {
328			wmove(wnd, p->ni_line, FADDR);
329			inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto);
330			p->ni_flags &= ~NIF_FACHG;
331		}
332		mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto);
333		mvwprintw(wnd, p->ni_line, RCVCC, "%6d", p->ni_rcvcc);
334		mvwprintw(wnd, p->ni_line, SNDCC, "%6d", p->ni_sndcc);
335		if (streq(p->ni_proto, "tcp"))
336			if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
337				mvwprintw(wnd, p->ni_line, STATE, "%d",
338				    p->ni_state);
339			else
340				mvwaddstr(wnd, p->ni_line, STATE,
341				    tcpstates[p->ni_state]);
342		wclrtoeol(wnd);
343	}
344	if (lastrow < YMAX(wnd)) {
345		wmove(wnd, lastrow, 0); wclrtobot(wnd);
346		wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd);	/* XXX */
347	}
348}
349
350/*
351 * Pretty print an Internet address (net address + port).
352 * If the nflag was specified, use numbers instead of names.
353 */
354static void
355inetprint(in, port, proto)
356	register struct in_addr *in;
357	int port;
358	char *proto;
359{
360	struct servent *sp = 0;
361	char line[80], *cp, *index();
362
363	snprintf(line, sizeof(line), "%.*s.", 16, inetname(*in));
364	cp = index(line, '\0');
365	if (!nflag && port)
366		sp = getservbyport(port, proto);
367	if (sp || port == 0)
368		snprintf(cp, sizeof(line) - (cp - line), "%.8s",
369		    sp ? sp->s_name : "*");
370	else
371		snprintf(cp, sizeof(line) - (cp - line), "%d",
372		    ntohs((u_short)port));
373	/* pad to full column to clear any garbage */
374	cp = index(line, '\0');
375	while (cp - line < 22)
376		*cp++ = ' ';
377	line[22] = '\0';
378	waddstr(wnd, line);
379}
380
381/*
382 * Construct an Internet address representation.
383 * If the nflag has been supplied, give
384 * numeric value, otherwise try for symbolic name.
385 */
386static char *
387inetname(in)
388	struct in_addr in;
389{
390	char *cp = 0;
391	static char line[50];
392	struct hostent *hp;
393	struct netent *np;
394
395	if (!nflag && in.s_addr != INADDR_ANY) {
396		int net = inet_netof(in);
397		int lna = inet_lnaof(in);
398
399		if (lna == INADDR_ANY) {
400			np = getnetbyaddr(net, AF_INET);
401			if (np)
402				cp = np->n_name;
403		}
404		if (cp == 0) {
405			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
406			if (hp)
407				cp = hp->h_name;
408		}
409	}
410	if (in.s_addr == INADDR_ANY)
411		strcpy(line, "*");
412	else if (cp)
413		snprintf(line, sizeof(line), "%s", cp);
414	else {
415		in.s_addr = ntohl(in.s_addr);
416#define C(x)	((x) & 0xff)
417		snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
418			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
419	}
420	return (line);
421}
422
423int
424cmdnetstat(cmd, args)
425	char *cmd, *args;
426{
427	if (prefix(cmd, "all")) {
428		aflag = !aflag;
429		goto fixup;
430	}
431	if  (prefix(cmd, "numbers") || prefix(cmd, "names")) {
432		struct netinfo *p;
433		int new;
434
435		new = prefix(cmd, "numbers");
436		if (new == nflag)
437			return (1);
438		TAILQ_FOREACH(p, &netcb, chain) {
439			if (p->ni_line == -1)
440				continue;
441			p->ni_flags |= NIF_LACHG|NIF_FACHG;
442		}
443		nflag = new;
444		goto redisplay;
445	}
446	if (!netcmd(cmd, args))
447		return (0);
448fixup:
449	fetchnetstat();
450redisplay:
451	shownetstat();
452	refresh();
453	return (1);
454}
455