xref: /openbsd/usr.bin/systat/netstat.c (revision 91f110e0)
1 /*	$OpenBSD: netstat.c,v 1.39 2013/12/25 01:46:00 tedu 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 void inetprint(struct in_addr *, int, char *, field_def *);
96 static void inet6print(struct in6_addr *, int, char *, field_def *);
97 static void shownetstat(struct netinfo *p);
98 
99 void print_ns(void);
100 int read_ns(void);
101 int select_ns(void);
102 int ns_keyboard_callback(int);
103 
104 #define	streq(a,b)	(strcmp(a,b)==0)
105 
106 static	int aflag = 0;
107 
108 static struct nlist namelist[] = {
109 #define	X_TCBTABLE	0		/* no sysctl */
110 	{ "_tcbtable" },
111 #define	X_UDBTABLE	1		/* no sysctl */
112 	{ "_udbtable" },
113 	{ "" },
114 };
115 #define ADD_ALLOC  1000
116 
117 
118 int protos;
119 
120 struct netinfo *netinfos = NULL;
121 size_t num_ns = 0;
122 static size_t num_alloc = 0;
123 
124 
125 field_def fields_ns[] = {
126 	{"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
127 	{"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
128 	{"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
129 	{"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
130 	{"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
131 	{"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0},
132 };
133 
134 #define FLD_NS_LOCAL	FIELD_ADDR(fields_ns,0)
135 #define FLD_NS_FOREIGN	FIELD_ADDR(fields_ns,1)
136 #define FLD_NS_PROTO	FIELD_ADDR(fields_ns,2)
137 #define FLD_NS_RECV_Q	FIELD_ADDR(fields_ns,3)
138 #define FLD_NS_SEND_Q	FIELD_ADDR(fields_ns,4)
139 #define FLD_NS_STATE	FIELD_ADDR(fields_ns,5)
140 
141 /* Define views */
142 field_def *view_ns_0[] = {
143 	FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO,
144 	FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL
145 };
146 
147 /* Define view managers */
148 struct view_manager netstat_mgr = {
149 	"Netstat", select_ns, read_ns, NULL, print_header,
150 	print_ns, ns_keyboard_callback, NULL, NULL
151 };
152 
153 field_view views_ns[] = {
154 	{view_ns_0, "netstat", '0', &netstat_mgr},
155 	{NULL, NULL, 0, NULL}
156 };
157 
158 
159 
160 
161 struct netinfo *
162 next_ns(void)
163 {
164 	if (num_alloc <= num_ns) {
165 		struct netinfo *ni;
166 		size_t a = num_alloc + ADD_ALLOC;
167 		if (a < num_alloc)
168 			return NULL;
169 		ni = realloc(netinfos, a * sizeof(*ni));
170 		if (ni == NULL)
171 			return NULL;
172 		netinfos = ni;
173 		num_alloc = a;
174 	}
175 
176 	return &netinfos[num_ns++];
177 }
178 
179 static void
180 enter(struct inpcb *inp, struct socket *so, int state, char *proto)
181 {
182 	struct netinfo *p;
183 
184 	p = next_ns();
185 	if (p == NULL) {
186 		error("Out of Memory!");
187 		return;
188 	}
189 
190 	p->nif_lport = inp->inp_lport;
191 	p->nif_fport = inp->inp_fport;
192 	p->nif_proto = proto;
193 
194 	if (inp->inp_flags & INP_IPV6) {
195 		p->nif_laddr6 = inp->inp_laddr6;
196 		p->nif_faddr6 = inp->inp_faddr6;
197 		p->nif_family = AF_INET6;
198 	} else {
199 		p->nif_laddr = inp->inp_laddr;
200 		p->nif_faddr = inp->inp_faddr;
201 		p->nif_family = AF_INET;
202 	}
203 
204 	p->nif_rcvcc = so->so_rcv.sb_cc;
205 	p->nif_sndcc = so->so_snd.sb_cc;
206 	p->nif_state = state;
207 }
208 
209 
210 /* netstat callback functions */
211 
212 int
213 select_ns(void)
214 {
215 	static int init = 0;
216 	if (kd == NULL) {
217 		num_disp = 1;
218 		return (0);
219 	}
220 
221 	if (!init) {
222 		sethostent(1);
223 		setnetent(1);
224 		init = 1;
225 	}
226 
227 	num_disp = num_ns;
228 	return (0);
229 }
230 
231 int
232 read_ns(void)
233 {
234 	struct inpcbtable pcbtable;
235 	struct inpcb *next, *prev;
236 	struct inpcb inpcb, prevpcb;
237 	struct socket sockb;
238 	struct tcpcb tcpcb;
239 	void *off;
240 	int istcp;
241 
242 	if (kd == NULL) {
243 		return (0);
244 	}
245 
246 	num_ns = 0;
247 
248 	if (namelist[X_TCBTABLE].n_value == 0)
249 		return 0;
250 
251 	if (protos & TCP) {
252 		off = NPTR(X_TCBTABLE);
253 		istcp = 1;
254 	} else if (protos & UDP) {
255 		off = NPTR(X_UDBTABLE);
256 		istcp = 0;
257 	} else {
258 		error("No protocols to display");
259 		return 0;
260 	}
261 
262 again:
263 	KREAD(off, &pcbtable, sizeof (struct inpcbtable));
264 
265 	prev = NULL;
266 	next = TAILQ_FIRST(&pcbtable.inpt_queue);
267 
268 	while (next != NULL) {
269 		KREAD(next, &inpcb, sizeof (inpcb));
270 		if (prev != NULL) {
271 			KREAD(prev, &prevpcb, sizeof (prevpcb));
272 			if (TAILQ_NEXT(&prevpcb, inp_queue) != next) {
273 				error("Kernel state in transition");
274 				return 0;
275 			}
276 		}
277 		prev = next;
278 		next = TAILQ_NEXT(&inpcb, inp_queue);
279 
280 		if (!aflag) {
281 			if (!(inpcb.inp_flags & INP_IPV6) &&
282 			    inet_lnaof(inpcb.inp_faddr) == INADDR_ANY)
283 				continue;
284 			if ((inpcb.inp_flags & INP_IPV6) &&
285 			    IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_faddr6))
286 				continue;
287 		}
288 		KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
289 		if (istcp) {
290 			KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
291 			if (!aflag && tcpcb.t_state <= TCPS_LISTEN)
292 				continue;
293 			enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
294 		} else
295 			enter(&inpcb, &sockb, 0, "udp");
296 	}
297 	if (istcp && (protos & UDP)) {
298 		istcp = 0;
299 		off = NPTR(X_UDBTABLE);
300 		goto again;
301 	}
302 
303 	num_disp = num_ns;
304 	return 0;
305 }
306 
307 void
308 print_ns(void)
309 {
310 	int n, count = 0;
311 
312 	if (kd == NULL) {
313 		print_fld_str(FLD_NS_LOCAL, "Failed to initialize KVM!");
314 		print_fld_str(FLD_NS_FOREIGN, "Failed to initialize KVM!");
315 		end_line();
316 		return;
317 	}
318 
319 	for (n = dispstart; n < num_disp; n++) {
320 		shownetstat(netinfos + n);
321 		count++;
322 		if (maxprint > 0 && count >= maxprint)
323 			break;
324 	}
325 }
326 
327 
328 int
329 initnetstat(void)
330 {
331 	field_view *v;
332 	int ret;
333 
334 	if (kd) {
335 		if ((ret = kvm_nlist(kd, namelist)) == -1)
336 			errx(1, "%s", kvm_geterr(kd));
337 		else if (ret)
338 			nlisterr(namelist);
339 
340 		if (namelist[X_TCBTABLE].n_value == 0) {
341 			error("No symbols in namelist");
342 			return(0);
343 		}
344 	}
345 	protos = TCP|UDP;
346 
347 	for (v = views_ns; v->name != NULL; v++)
348 		add_view(v);
349 
350 	return(1);
351 }
352 
353 static void
354 shownetstat(struct netinfo *p)
355 {
356 	switch (p->nif_family) {
357 	case AF_INET:
358 		inetprint(&p->nif_laddr, p->nif_lport,
359 			  p->nif_proto, FLD_NS_LOCAL);
360 		inetprint(&p->nif_faddr, p->nif_fport,
361 			  p->nif_proto, FLD_NS_FOREIGN);
362 		break;
363 	case AF_INET6:
364 		inet6print(&p->nif_laddr6, p->nif_lport,
365 			   p->nif_proto, FLD_NS_LOCAL);
366 		inet6print(&p->nif_faddr6, p->nif_fport,
367 			   p->nif_proto, FLD_NS_FOREIGN);
368 		break;
369 	}
370 
371 	tb_start();
372 	tbprintf("%s", p->nif_proto);
373 	if (p->nif_family == AF_INET6)
374 		tbprintf("6");
375 
376 	print_fld_tb(FLD_NS_PROTO);
377 
378 	print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc);
379 	print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc);
380 
381 	if (streq(p->nif_proto, "tcp")) {
382 		if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES)
383 			print_fld_uint(FLD_NS_STATE, p->nif_state);
384 		else
385 			print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]);
386 	}
387 	end_line();
388 }
389 
390 /*
391  * Pretty print an Internet address (net address + port).
392  * If the nflag was specified, use numbers instead of names.
393  */
394 static void
395 inetprint(struct in_addr *in, int port, char *proto, field_def *fld)
396 {
397 	struct servent *sp = 0;
398 
399 	tb_start();
400 	tbprintf("%s", inetname(*in));
401 
402 	if (!nflag && port)
403 		sp = getservbyport(port, proto);
404 	if (sp || port == 0)
405 		tbprintf(":%s", sp ? sp->s_name : "*");
406 	else
407 		tbprintf(":%d", ntohs((u_short)port));
408 
409 	print_fld_tb(fld);
410 }
411 
412 static void
413 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld)
414 {
415 	struct servent *sp = 0;
416 
417 	tb_start();
418 
419 	tbprintf("%s", inet6name(in6));
420 	if (!nflag && port)
421 		sp = getservbyport(port, proto);
422 	if (sp || port == 0)
423 		tbprintf(":%s", sp ? sp->s_name : "*");
424 	else
425 		tbprintf(":%d", ntohs((u_short)port));
426 
427 	print_fld_tb(fld);
428 }
429 
430 int
431 kvm_ckread(void *a, void *b, size_t l)
432 {
433 	if (kvm_read(kd, (u_long)a, b, l) != l) {
434 		if (verbose)
435 			error("error reading kmem\n");
436 		return (0);
437 	} else
438 		return (1);
439 }
440 
441 
442 int
443 ns_keyboard_callback(int ch)
444 {
445 	switch (ch) {
446 	case 'a':
447 		aflag = !aflag;
448 		gotsig_alarm = 1;
449 		break;
450 	case 'n':
451 		nflag = !nflag;
452 		gotsig_alarm = 1;
453 		break;
454 	case 'r':
455 		aflag = 0;
456 		nflag = 1;
457 		protos = TCP|UDP;
458 		gotsig_alarm = 1;
459 		break;
460 	case 't':
461 		protos ^= TCP;
462 		gotsig_alarm = 1;
463 		break;
464 	case 'u':
465 		protos ^= UDP;
466 		gotsig_alarm = 1;
467 		break;
468 	default:
469 		return keyboard_callback(ch);
470 	};
471 
472 	return 1;
473 }
474 
475