xref: /openbsd/usr.bin/systat/netstat.c (revision 479c151d)
1 /*	$OpenBSD: netstat.c,v 1.47 2024/09/20 02:00:46 jsg 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 
37 #include <kvm.h>
38 #include <sys/types.h>
39 #include <sys/sysctl.h>
40 #include <sys/socket.h>
41 #define _KERNEL
42 #include <sys/file.h>
43 #undef _KERNEL
44 
45 #include <netinet/in.h>
46 #include <netinet/tcp.h>
47 #include <netinet/tcp_seq.h>
48 #define TCPSTATES
49 #include <netinet/tcp_fsm.h>
50 #include <arpa/inet.h>
51 
52 #include <netdb.h>
53 #include <signal.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <err.h>
57 #include <nlist.h>
58 #include <paths.h>
59 #include "systat.h"
60 #include "engine.h"
61 
62 #define	TCP	0x1
63 #define	UDP	0x2
64 #define	OTHER	0x4
65 
66 struct netinfo {
67 	union {
68 		struct	in_addr nif_laddr;	/* local address */
69 		struct	in6_addr nif_laddr6;	/* local address */
70 	} l;
71 	union {
72 		struct	in_addr	nif_faddr;	/* foreign address */
73 		struct	in6_addr nif_faddr6;	/* foreign address */
74 	} f;
75 	long	nif_rcvcc;		/* rcv buffer character count */
76 	long	nif_sndcc;		/* snd buffer character count */
77 	short	nif_lport;		/* local port */
78 	short	nif_fport;		/* foreign port */
79 	short	nif_state;		/* tcp state */
80 	short	nif_family;
81 	short	nif_proto;		/* protocol */
82 	short	nif_ipproto;
83 };
84 
85 #define nif_laddr  l.nif_laddr
86 #define nif_laddr6 l.nif_laddr6
87 #define nif_faddr  f.nif_faddr
88 #define nif_faddr6 f.nif_faddr6
89 
90 static void enter(struct kinfo_file *);
91 static int kf_comp(const void *, const void *);
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 #define ADD_ALLOC  1000
106 
107 int protos;
108 
109 struct netinfo *netinfos = NULL;
110 size_t num_ns = 0;
111 static size_t num_alloc = 0;
112 
113 
114 field_def fields_ns[] = {
115 	{"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
116 	{"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
117 	{"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
118 	{"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
119 	{"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
120 	{"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0},
121 };
122 
123 #define FLD_NS_LOCAL	FIELD_ADDR(fields_ns,0)
124 #define FLD_NS_FOREIGN	FIELD_ADDR(fields_ns,1)
125 #define FLD_NS_PROTO	FIELD_ADDR(fields_ns,2)
126 #define FLD_NS_RECV_Q	FIELD_ADDR(fields_ns,3)
127 #define FLD_NS_SEND_Q	FIELD_ADDR(fields_ns,4)
128 #define FLD_NS_STATE	FIELD_ADDR(fields_ns,5)
129 
130 /* Define views */
131 field_def *view_ns_0[] = {
132 	FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO,
133 	FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL
134 };
135 
136 /* Define view managers */
137 struct view_manager netstat_mgr = {
138 	"Netstat", select_ns, read_ns, NULL, print_header,
139 	print_ns, ns_keyboard_callback, NULL, NULL
140 };
141 
142 field_view views_ns[] = {
143 	{view_ns_0, "netstat", '0', &netstat_mgr},
144 	{NULL, NULL, 0, NULL}
145 };
146 
147 
148 
149 
150 struct netinfo *
next_ns(void)151 next_ns(void)
152 {
153 	if (num_alloc <= num_ns) {
154 		struct netinfo *ni;
155 		size_t a = num_alloc + ADD_ALLOC;
156 		if (a < num_alloc)
157 			return NULL;
158 		ni = reallocarray(netinfos, a, sizeof(*ni));
159 		if (ni == NULL)
160 			return NULL;
161 		netinfos = ni;
162 		num_alloc = a;
163 	}
164 
165 	return &netinfos[num_ns++];
166 }
167 
168 static void
enter(struct kinfo_file * kf)169 enter(struct kinfo_file *kf)
170 {
171 #define s6_addr32 __u6_addr.__u6_addr32
172 	struct netinfo *p;
173 
174 	/* first filter out unwanted sockets */
175 	if (kf->so_family != AF_INET && kf->so_family != AF_INET6)
176 		return;
177 
178 	switch (kf->so_protocol) {
179 	case IPPROTO_TCP:
180 		if ((protos & TCP) == 0)
181 			return;
182 		break;
183 	case IPPROTO_UDP:
184 		if ((protos & UDP) == 0)
185 			return;
186 		break;
187 	default:
188 		if ((protos & OTHER) == 0)
189 			return;
190 		break;
191 	}
192 
193 	if (!aflag) {
194 		struct in6_addr faddr6;
195 
196 		switch (kf->so_family) {
197 		case AF_INET:
198 			if (kf->inp_faddru[0] == INADDR_ANY)
199 				return;
200 			break;
201 		case AF_INET6:
202 			faddr6.s6_addr32[0] = kf->inp_faddru[0];
203 			faddr6.s6_addr32[1] = kf->inp_faddru[1];
204 			faddr6.s6_addr32[2] = kf->inp_faddru[2];
205 			faddr6.s6_addr32[3] = kf->inp_faddru[3];
206 			if (IN6_IS_ADDR_UNSPECIFIED(&faddr6))
207 				return;
208 			break;
209 		}
210 	}
211 
212 	/* finally enter the socket to the table */
213 	p = next_ns();
214 	if (p == NULL) {
215 		error("Out of Memory!");
216 		return;
217 	}
218 
219 	p->nif_lport = kf->inp_lport;
220 	p->nif_fport = kf->inp_fport;
221 	p->nif_proto = kf->so_protocol;
222 	p->nif_ipproto = kf->inp_proto;
223 
224 	switch (kf->so_family) {
225 	case AF_INET:
226 		p->nif_family = AF_INET;
227 		p->nif_laddr.s_addr = kf->inp_laddru[0];
228 		p->nif_faddr.s_addr = kf->inp_faddru[0];
229 		break;
230 	case AF_INET6:
231 		p->nif_family = AF_INET6;
232 		p->nif_laddr6.s6_addr32[0] = kf->inp_laddru[0];
233 		p->nif_laddr6.s6_addr32[1] = kf->inp_laddru[1];
234 		p->nif_laddr6.s6_addr32[2] = kf->inp_laddru[2];
235 		p->nif_laddr6.s6_addr32[3] = kf->inp_laddru[3];
236 		p->nif_faddr6.s6_addr32[0] = kf->inp_faddru[0];
237 		p->nif_faddr6.s6_addr32[1] = kf->inp_faddru[1];
238 		p->nif_faddr6.s6_addr32[2] = kf->inp_faddru[2];
239 		p->nif_faddr6.s6_addr32[3] = kf->inp_faddru[3];
240 		break;
241 	}
242 
243 	p->nif_rcvcc = kf->so_rcv_cc;
244 	p->nif_sndcc = kf->so_snd_cc;
245 	p->nif_state = kf->t_state;
246 #undef s6_addr32
247 }
248 
249 
250 /* netstat callback functions */
251 
252 int
select_ns(void)253 select_ns(void)
254 {
255 	num_disp = num_ns;
256 	return (0);
257 }
258 
259 static int type_map[] = { -1, 2, 3, 1, 4, 5 };
260 
261 static int
kf_comp(const void * a,const void * b)262 kf_comp(const void *a, const void *b)
263 {
264 	const struct kinfo_file *ka = a, *kb = b;
265 
266 	if (ka->so_family != kb->so_family) {
267 		/* AF_INET < AF_INET6 < AF_LOCAL */
268 		if (ka->so_family == AF_INET)
269 			return (-1);
270 		if (ka->so_family == AF_LOCAL)
271 			return (1);
272 		if (kb->so_family == AF_LOCAL)
273 			return (-1);
274 		return (1);
275 	}
276 	if (ka->so_family == AF_LOCAL) {
277 		if (type_map[ka->so_type] < type_map[kb->so_type])
278 			return (-1);
279 		if (type_map[ka->so_type] > type_map[kb->so_type])
280 			return (1);
281 	} else if (ka->so_family == AF_INET || ka->so_family == AF_INET6) {
282 		if (ka->so_protocol < kb->so_protocol)
283 			return (-1);
284 		if (ka->so_protocol > kb->so_protocol)
285 			return (1);
286 		if (ka->so_type == SOCK_DGRAM || ka->so_type == SOCK_STREAM) {
287 			/* order sockets by remote port desc */
288 			if (ka->inp_fport > kb->inp_fport)
289 				return (-1);
290 			if (ka->inp_fport < kb->inp_fport)
291 				return (1);
292 		} else if (ka->so_type == SOCK_RAW) {
293 			if (ka->inp_proto > kb->inp_proto)
294 				return (-1);
295 			if (ka->inp_proto < kb->inp_proto)
296 				return (1);
297 		}
298 	}
299 	return (0);
300 }
301 
302 
303 int
read_ns(void)304 read_ns(void)
305 {
306 	struct kinfo_file *kf;
307 	int i, fcnt;
308 
309 	if (kd == NULL) {
310 		error("Failed to initialize KVM!");
311 		return (0);
312 	}
313 	kf = kvm_getfiles(kd, KERN_FILE_BYFILE, DTYPE_SOCKET,
314 	    sizeof(*kf), &fcnt);
315 	if (kf == NULL) {
316 		error("Out of Memory!");
317 		return (0);
318 	}
319 
320 	/* sort sockets by AF, proto and type */
321 	qsort(kf, fcnt, sizeof(*kf), kf_comp);
322 
323 	num_ns = 0;
324 
325 	for (i = 0; i < fcnt; i++)
326 		enter(&kf[i]);
327 
328 	num_disp = num_ns;
329 	return 0;
330 }
331 
332 void
print_ns(void)333 print_ns(void)
334 {
335 	int n, count = 0;
336 
337 	for (n = dispstart; n < num_disp; n++) {
338 		shownetstat(netinfos + n);
339 		count++;
340 		if (maxprint > 0 && count >= maxprint)
341 			break;
342 	}
343 }
344 
345 
346 int
initnetstat(void)347 initnetstat(void)
348 {
349 	field_view *v;
350 
351 	protos = TCP|UDP|OTHER;
352 	for (v = views_ns; v->name != NULL; v++)
353 		add_view(v);
354 
355 	return(1);
356 }
357 
358 static void
shownetstat(struct netinfo * p)359 shownetstat(struct netinfo *p)
360 {
361 	char *proto = NULL;
362 
363 	switch (p->nif_proto) {
364 	case IPPROTO_TCP:
365 		proto = "tcp";
366 		break;
367 	case IPPROTO_UDP:
368 		proto = "udp";
369 		break;
370 	}
371 
372 	switch (p->nif_family) {
373 	case AF_INET:
374 		inetprint(&p->nif_laddr, p->nif_lport,
375 			  proto, FLD_NS_LOCAL);
376 		inetprint(&p->nif_faddr, p->nif_fport,
377 			  proto, FLD_NS_FOREIGN);
378 		break;
379 	case AF_INET6:
380 		inet6print(&p->nif_laddr6, p->nif_lport,
381 			   proto, FLD_NS_LOCAL);
382 		inet6print(&p->nif_faddr6, p->nif_fport,
383 			   proto, FLD_NS_FOREIGN);
384 		break;
385 	}
386 
387 	tb_start();
388 	switch (p->nif_proto) {
389 	case IPPROTO_TCP:
390 	case IPPROTO_UDP:
391 		tbprintf("%s", proto);
392 		if (p->nif_family == AF_INET6)
393 			tbprintf("6");
394 		break;
395 	case IPPROTO_DIVERT:
396 		tbprintf("divert");
397 		if (p->nif_family == AF_INET6)
398 			tbprintf("6");
399 		break;
400 	default:
401 		tbprintf("%d", p->nif_ipproto);
402 		break;
403 	}
404 
405 	print_fld_tb(FLD_NS_PROTO);
406 
407 	print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc);
408 	print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc);
409 
410 	if (p->nif_proto == IPPROTO_TCP) {
411 		if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES)
412 			print_fld_uint(FLD_NS_STATE, p->nif_state);
413 		else
414 			print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]);
415 	}
416 	end_line();
417 }
418 
419 /*
420  * Pretty print an Internet address (net address + port).
421  * If the nflag was specified, use numbers instead of names.
422  */
423 static void
inetprint(struct in_addr * in,int port,char * proto,field_def * fld)424 inetprint(struct in_addr *in, int port, char *proto, field_def *fld)
425 {
426 	struct servent *sp = 0;
427 
428 	tb_start();
429 	tbprintf("%s", inetname(*in));
430 
431 	if (!nflag && port)
432 		sp = getservbyport(port, proto);
433 	if (sp || port == 0)
434 		tbprintf(":%s", sp ? sp->s_name : "*");
435 	else
436 		tbprintf(":%d", ntohs((u_short)port));
437 
438 	print_fld_tb(fld);
439 }
440 
441 static void
inet6print(struct in6_addr * in6,int port,char * proto,field_def * fld)442 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld)
443 {
444 	struct servent *sp = 0;
445 
446 	tb_start();
447 
448 	tbprintf("%s", inet6name(in6));
449 	if (!nflag && port)
450 		sp = getservbyport(port, proto);
451 	if (sp || port == 0)
452 		tbprintf(":%s", sp ? sp->s_name : "*");
453 	else
454 		tbprintf(":%d", ntohs((u_short)port));
455 
456 	print_fld_tb(fld);
457 }
458 
459 int
ns_keyboard_callback(int ch)460 ns_keyboard_callback(int ch)
461 {
462 	switch (ch) {
463 	case 'a':
464 		aflag = !aflag;
465 		gotsig_alarm = 1;
466 		break;
467 	case 'n':
468 		nflag = !nflag;
469 		gotsig_alarm = 1;
470 		break;
471 	case 'o':
472 		protos ^= OTHER;
473 		gotsig_alarm = 1;
474 		break;
475 	case 'r':
476 		aflag = 0;
477 		nflag = 1;
478 		protos = TCP|UDP;
479 		gotsig_alarm = 1;
480 		break;
481 	case 't':
482 		protos ^= TCP;
483 		gotsig_alarm = 1;
484 		break;
485 	case 'u':
486 		protos ^= UDP;
487 		gotsig_alarm = 1;
488 		break;
489 	default:
490 		return keyboard_callback(ch);
491 	}
492 
493 	return 1;
494 }
495 
496