xref: /openbsd/usr.bin/systat/netstat.c (revision 404b540a)
1 /*	$OpenBSD: netstat.c,v 1.32 2008/12/07 02:56:06 canacar Exp $	*/
2 /*	$NetBSD: netstat.c,v 1.3 1995/06/18 23:53:07 cgd Exp $	*/
3 
4 /*-
5  * Copyright (c) 1980, 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * netstat
35  */
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <sys/socketvar.h>
39 #include <sys/mbuf.h>
40 #include <sys/protosw.h>
41 
42 #include <netinet/in.h>
43 #include <net/route.h>
44 #include <netinet/in_systm.h>
45 #include <netinet/ip.h>
46 #include <netinet/in_pcb.h>
47 #include <netinet/ip_icmp.h>
48 #include <netinet/icmp_var.h>
49 #include <netinet/ip_var.h>
50 #include <netinet/tcp.h>
51 #include <netinet/tcpip.h>
52 #include <netinet/tcp_seq.h>
53 #define TCPSTATES
54 #include <netinet/tcp_fsm.h>
55 #include <netinet/tcp_timer.h>
56 #include <netinet/tcp_var.h>
57 #include <netinet/tcp_debug.h>
58 #include <netinet/udp.h>
59 #include <netinet/udp_var.h>
60 #include <arpa/inet.h>
61 
62 #include <netdb.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <err.h>
66 #include <nlist.h>
67 #include <paths.h>
68 #include "systat.h"
69 #include "engine.h"
70 
71 struct netinfo {
72 	union {
73 		struct	in_addr nif_laddr;	/* local address */
74 		struct	in6_addr nif_laddr6;	/* local address */
75 	} l;
76 	union {
77 		struct	in_addr	nif_faddr;	/* foreign address */
78 		struct	in6_addr nif_faddr6;	/* foreign address */
79 	} f;
80 	char	*nif_proto;		/* protocol */
81 	long	nif_rcvcc;		/* rcv buffer character count */
82 	long	nif_sndcc;		/* snd buffer character count */
83 	short	nif_lport;		/* local port */
84 	short	nif_fport;		/* foreign port */
85 	short	nif_state;		/* tcp state */
86 	short	nif_family;
87 };
88 
89 #define nif_laddr  l.nif_laddr
90 #define nif_laddr6 l.nif_laddr6
91 #define nif_faddr  f.nif_faddr
92 #define nif_faddr6 f.nif_faddr6
93 
94 static void enter(struct inpcb *, struct socket *, int, char *);
95 static const char *inetname(struct in_addr);
96 static void inetprint(struct in_addr *, int, char *, field_def *);
97 static const char *inet6name(struct in6_addr *);
98 static void inet6print(struct in6_addr *, int, char *, field_def *);
99 static void shownetstat(struct netinfo *p);
100 
101 void print_ns(void);
102 int read_ns(void);
103 int select_ns(void);
104 int ns_keyboard_callback(int);
105 
106 #define	streq(a,b)	(strcmp(a,b)==0)
107 
108 static	int aflag = 0;
109 
110 static struct nlist namelist[] = {
111 #define	X_TCBTABLE	0		/* no sysctl */
112 	{ "_tcbtable" },
113 #define	X_UDBTABLE	1		/* no sysctl */
114 	{ "_udbtable" },
115 	{ "" },
116 };
117 #define ADD_ALLOC  1000
118 
119 
120 int protos;
121 
122 struct netinfo *netinfos = NULL;
123 size_t num_ns = 0;
124 static size_t num_alloc = 0;
125 
126 
127 field_def fields_ns[] = {
128 	{"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
129 	{"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
130 	{"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
131 	{"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
132 	{"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
133 	{"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0},
134 };
135 
136 #define FIELD_ADDR(x) (&fields_ns[x])
137 
138 #define FLD_NS_LOCAL	FIELD_ADDR(0)
139 #define FLD_NS_FOREIGN	FIELD_ADDR(1)
140 #define FLD_NS_PROTO	FIELD_ADDR(2)
141 #define FLD_NS_RECV_Q	FIELD_ADDR(3)
142 #define FLD_NS_SEND_Q	FIELD_ADDR(4)
143 #define FLD_NS_STATE	FIELD_ADDR(5)
144 
145 /* Define views */
146 field_def *view_ns_0[] = {
147 	FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO,
148 	FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL
149 };
150 
151 /* Define view managers */
152 struct view_manager netstat_mgr = {
153 	"Netstat", select_ns, read_ns, NULL, print_header,
154 	print_ns, ns_keyboard_callback, NULL, NULL
155 };
156 
157 field_view views_ns[] = {
158 	{view_ns_0, "netstat", '0', &netstat_mgr},
159 	{NULL, NULL, 0, NULL}
160 };
161 
162 
163 
164 
165 struct netinfo *
166 next_ns(void)
167 {
168 	if (num_alloc <= num_ns) {
169 		struct netinfo *ni;
170 		size_t a = num_alloc + ADD_ALLOC;
171 		if (a < num_alloc)
172 			return NULL;
173 		ni = realloc(netinfos, a * sizeof(*ni));
174 		if (ni == NULL)
175 			return NULL;
176 		netinfos = ni;
177 		num_alloc = a;
178 	}
179 
180 	return &netinfos[num_ns++];
181 }
182 
183 static void
184 enter(struct inpcb *inp, struct socket *so, int state, char *proto)
185 {
186 	struct netinfo *p;
187 
188 	p = next_ns();
189 	if (p == NULL) {
190 		error("Out of Memory!");
191 		return;
192 	}
193 
194 	p->nif_lport = inp->inp_lport;
195 	p->nif_fport = inp->inp_fport;
196 	p->nif_proto = proto;
197 
198 	if (inp->inp_flags & INP_IPV6) {
199 		p->nif_laddr6 = inp->inp_laddr6;
200 		p->nif_faddr6 = inp->inp_faddr6;
201 		p->nif_family = AF_INET6;
202 	} else {
203 		p->nif_laddr = inp->inp_laddr;
204 		p->nif_faddr = inp->inp_faddr;
205 		p->nif_family = AF_INET;
206 	}
207 
208 	p->nif_rcvcc = so->so_rcv.sb_cc;
209 	p->nif_sndcc = so->so_snd.sb_cc;
210 	p->nif_state = state;
211 }
212 
213 
214 /* netstat callback functions */
215 
216 int
217 select_ns(void)
218 {
219 	static int init = 0;
220 	if (kd == NULL) {
221 		num_disp = 1;
222 		return (0);
223 	}
224 
225 	if (!init) {
226 		sethostent(1);
227 		setnetent(1);
228 		init = 1;
229 	}
230 
231 	num_disp = num_ns;
232 	return (0);
233 }
234 
235 int
236 read_ns(void)
237 {
238 	struct inpcbtable pcbtable;
239 	struct inpcb *head, *prev, *next;
240 	struct inpcb inpcb;
241 	struct socket sockb;
242 	struct tcpcb tcpcb;
243 	void *off;
244 	int istcp;
245 
246 	if (kd == NULL) {
247 		return (0);
248 	}
249 
250 	num_ns = 0;
251 
252 	if (namelist[X_TCBTABLE].n_value == 0)
253 		return 0;
254 
255 	if (protos & TCP) {
256 		off = NPTR(X_TCBTABLE);
257 		istcp = 1;
258 	} else if (protos & UDP) {
259 		off = NPTR(X_UDBTABLE);
260 		istcp = 0;
261 	} else {
262 		error("No protocols to display");
263 		return 0;
264 	}
265 
266 again:
267 	KREAD(off, &pcbtable, sizeof (struct inpcbtable));
268 
269 	prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
270 	next = CIRCLEQ_FIRST(&pcbtable.inpt_queue);
271 
272 	while (next != head) {
273 		KREAD(next, &inpcb, sizeof (inpcb));
274 		if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) {
275 			error("Kernel state in transition");
276 			return 0;
277 		}
278 		prev = next;
279 		next = CIRCLEQ_NEXT(&inpcb, inp_queue);
280 
281 		if (!aflag) {
282 			if (!(inpcb.inp_flags & INP_IPV6) &&
283 			    inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
284 				continue;
285 			if ((inpcb.inp_flags & INP_IPV6) &&
286 			    IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6))
287 				continue;
288 		}
289 		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
290 		if (istcp) {
291 			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
292 			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
293 		} else
294 			enter(&inpcb, &sockb, 0, "udp");
295 	}
296 	if (istcp && (protos & UDP)) {
297 		istcp = 0;
298 		off = NPTR(X_UDBTABLE);
299 		goto again;
300 	}
301 
302 	num_disp = num_ns;
303 	return 0;
304 }
305 
306 void
307 print_ns(void)
308 {
309 	int n, count = 0;
310 
311 	if (kd == NULL) {
312 		print_fld_str(FLD_NS_LOCAL, "Failed to initialize KVM!");
313 		print_fld_str(FLD_NS_FOREIGN, "Failed to initialize KVM!");
314 		end_line();
315 		return;
316 	}
317 
318 	for (n = dispstart; n < num_disp; n++) {
319 		shownetstat(netinfos + n);
320 		count++;
321 		if (maxprint > 0 && count >= maxprint)
322 			break;
323 	}
324 }
325 
326 
327 int
328 initnetstat(void)
329 {
330 	field_view *v;
331 	int ret;
332 
333 	if (kd) {
334 		if ((ret = kvm_nlist(kd, namelist)) == -1)
335 			errx(1, "%s", kvm_geterr(kd));
336 		else if (ret)
337 			nlisterr(namelist);
338 
339 		if (namelist[X_TCBTABLE].n_value == 0) {
340 			error("No symbols in namelist");
341 			return(0);
342 		}
343 	}
344 	protos = TCP|UDP;
345 
346 	for (v = views_ns; v->name != NULL; v++)
347 		add_view(v);
348 
349 	return(1);
350 }
351 
352 static void
353 shownetstat(struct netinfo *p)
354 {
355 	switch (p->nif_family) {
356 	case AF_INET:
357 		inetprint(&p->nif_laddr, p->nif_lport,
358 			  p->nif_proto, FLD_NS_LOCAL);
359 		inetprint(&p->nif_faddr, p->nif_fport,
360 			  p->nif_proto, FLD_NS_FOREIGN);
361 		break;
362 	case AF_INET6:
363 		inet6print(&p->nif_laddr6, p->nif_lport,
364 			   p->nif_proto, FLD_NS_LOCAL);
365 		inet6print(&p->nif_faddr6, p->nif_fport,
366 			   p->nif_proto, FLD_NS_FOREIGN);
367 		break;
368 	}
369 
370 	tb_start();
371 	tbprintf("%s", p->nif_proto);
372 	if (p->nif_family == AF_INET6)
373 		tbprintf("6");
374 
375 	print_fld_tb(FLD_NS_PROTO);
376 
377 	print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc);
378 	print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc);
379 
380 	if (streq(p->nif_proto, "tcp")) {
381 		if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES)
382 			print_fld_uint(FLD_NS_STATE, p->nif_state);
383 		else
384 			print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]);
385 	}
386 	end_line();
387 }
388 
389 /*
390  * Pretty print an Internet address (net address + port).
391  * If the nflag was specified, use numbers instead of names.
392  */
393 static void
394 inetprint(struct in_addr *in, int port, char *proto, field_def *fld)
395 {
396 	struct servent *sp = 0;
397 
398 	tb_start();
399 	tbprintf("%s", inetname(*in));
400 
401 	if (!nflag && port)
402 		sp = getservbyport(port, proto);
403 	if (sp || port == 0)
404 		tbprintf(":%s", sp ? sp->s_name : "*");
405 	else
406 		tbprintf(":%d", ntohs((u_short)port));
407 
408 	print_fld_tb(fld);
409 }
410 
411 static void
412 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld)
413 {
414 	struct servent *sp = 0;
415 
416 	tb_start();
417 
418 	tbprintf("%s", inet6name(in6));
419 	if (!nflag && port)
420 		sp = getservbyport(port, proto);
421 	if (sp || port == 0)
422 		tbprintf(":%s", sp ? sp->s_name : "*");
423 	else
424 		tbprintf(":%d", ntohs((u_short)port));
425 
426 	print_fld_tb(fld);
427 }
428 
429 static const char *
430 inet6name(struct in6_addr *in6)
431 {
432 	static char line[NI_MAXHOST];
433 	struct sockaddr_in6 sin6;
434 	int flags;
435 
436 	flags = nflag ? NI_NUMERICHOST : 0;
437 	if (IN6_IS_ADDR_UNSPECIFIED(in6))
438 		return "*";
439 	memset(&sin6, 0, sizeof(sin6));
440 	sin6.sin6_family = AF_INET6;
441 	sin6.sin6_len = sizeof(struct sockaddr_in6);
442 	sin6.sin6_addr = *in6;
443 	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
444 	    line, sizeof(line), NULL, 0, flags) == 0)
445 		return line;
446 	return "?";
447 }
448 
449 static const char *
450 inetname(struct in_addr in)
451 {
452 	static char line[NI_MAXHOST];
453 	struct sockaddr_in si;
454 	int flags, e;
455 
456 	flags = nflag ? NI_NUMERICHOST : 0;
457 	if (in.s_addr == INADDR_ANY)
458 		return "*";
459 
460 	memset(&si, 0, sizeof(si));
461 	si.sin_family = AF_INET;
462 	si.sin_len = sizeof(struct sockaddr_in);
463 	si.sin_addr = in;
464 
465 	e = getnameinfo((struct sockaddr *)&si, si.sin_len,
466 			line, sizeof(line), NULL, 0, flags);
467 
468 	if (e == 0)
469 		return line;
470 
471 	error("Lookup: %s", gai_strerror(e));
472 
473 	return "?";
474 }
475 
476 int
477 kvm_ckread(void *a, void *b, size_t l)
478 {
479 	if (kvm_read(kd, (u_long)a, b, l) != l) {
480 		if (verbose)
481 			error("error reading kmem at %x\n", a);
482 		return (0);
483 	} else
484 		return (1);
485 }
486 
487 
488 int
489 ns_keyboard_callback(int ch)
490 {
491 	switch (ch) {
492 	case 'n':
493 		nflag = !nflag;
494 		gotsig_alarm = 1;
495 		break;
496 	case 't':
497 		protos ^= TCP;
498 		gotsig_alarm = 1;
499 		break;
500 	case 'u':
501 		protos ^= UDP;
502 		gotsig_alarm = 1;
503 		break;
504 	default:
505 		return keyboard_callback(ch);
506 	};
507 
508 	return 1;
509 }
510 
511