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