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