xref: /netbsd-src/usr.bin/sockstat/sockstat.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: sockstat.c,v 1.14 2008/04/29 06:53:03 martin Exp $ */
2 
3 /*
4  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Brown.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: sockstat.c,v 1.14 2008/04/29 06:53:03 martin Exp $");
35 #endif
36 
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/sysctl.h>
40 #include <sys/socket.h>
41 #include <sys/socketvar.h>
42 #include <sys/un.h>
43 #include <netinet/in.h>
44 #include <net/route.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/ip.h>
47 #include <netinet/in_pcb.h>
48 #include <netinet/in_pcb_hdr.h>
49 #include <netinet/tcp_fsm.h>
50 
51 #define _KERNEL
52 /* want DTYPE_* defines */
53 #include <sys/file.h>
54 #undef _KERNEL
55 
56 #include <arpa/inet.h>
57 
58 #include <bitstring.h>
59 #include <ctype.h>
60 #include <err.h>
61 #include <errno.h>
62 #include <netdb.h>
63 #include <pwd.h>
64 #include <stdio.h>
65 #include <strings.h>
66 #include <stdlib.h>
67 #include <unistd.h>
68 #include <util.h>
69 
70 #define satosun(sa)	((struct sockaddr_un *)(sa))
71 #define satosin(sa)	((struct sockaddr_in *)(sa))
72 #ifdef INET6
73 #define satosin6(sa)	((struct sockaddr_in6 *)(sa))
74 #endif
75 
76 void	parse_ports(const char *);
77 int	get_num(const char *, const char **, const char **);
78 void	get_sockets(const char *);
79 void	get_files(void);
80 int	sort_files(const void *, const void *);
81 void	sysctl_sucker(int *, u_int, void **, size_t *);
82 void	socket_add_hash(struct kinfo_pcb *, int);
83 int	isconnected(struct kinfo_pcb *);
84 int	islistening(struct kinfo_pcb *);
85 struct kinfo_pcb *pick_socket(struct kinfo_file *);
86 int	get_proc(struct kinfo_proc2 *, int);
87 int	print_socket(struct kinfo_file *, struct kinfo_pcb *,
88 		     struct kinfo_proc2 *);
89 void	print_addr(int, int, int, struct sockaddr *);
90 
91 LIST_HEAD(socklist, sockitem);
92 #define HASHSIZE 1009
93 struct socklist sockhash[HASHSIZE];
94 struct sockitem {
95 	LIST_ENTRY(sockitem) s_list;
96 	struct kinfo_pcb *s_sock;
97 };
98 
99 struct kinfo_file *flist;
100 u_int nfiles;
101 
102 int pf_list, only, nonames;
103 bitstr_t *portmap;
104 
105 #define PF_LIST_INET	1
106 #ifdef INET6
107 #define PF_LIST_INET6	2
108 #endif
109 #define PF_LIST_LOCAL	4
110 #define ONLY_CONNECTED	1
111 #define ONLY_LISTEN	2
112 
113 int
114 main(int argc, char *argv[])
115 {
116 	struct kinfo_pcb *kp;
117 	int i, ch;
118 	struct kinfo_proc2 p;
119 
120 	pf_list = only = 0;
121 
122 #ifdef INET6
123 	while ((ch = getopt(argc, argv, "46cf:lnp:u")) != - 1) {
124 #else
125 	while ((ch = getopt(argc, argv, "4cf:lnp:u")) != - 1) {
126 #endif
127 		switch (ch) {
128 		case '4':
129 			pf_list |= PF_LIST_INET;
130 			break;
131 #ifdef INET6
132 		case '6':
133 			pf_list |= PF_LIST_INET6;
134 			break;
135 #endif
136 		case 'c':
137 			only |= ONLY_CONNECTED;
138 			break;
139 		case 'f':
140 			if (strcasecmp(optarg, "inet") == 0)
141 				pf_list |= PF_LIST_INET;
142 #ifdef INET6
143 			else if (strcasecmp(optarg, "inet6") == 0)
144 				pf_list |= PF_LIST_INET6;
145 #endif
146 			else if (strcasecmp(optarg, "local") == 0)
147 				pf_list |= PF_LIST_LOCAL;
148 			else if (strcasecmp(optarg, "unix") == 0)
149 				pf_list |= PF_LIST_LOCAL;
150 			else
151 				errx(1, "%s: unsupported protocol family",
152 				    optarg);
153 			break;
154 		case 'l':
155 			only |= ONLY_LISTEN;
156 			break;
157 		case 'n':
158 			nonames++;
159 			break;
160 		case 'p':
161 			parse_ports(optarg);
162 			break;
163 		case 'u':
164 			pf_list |= PF_LIST_LOCAL;
165 			break;
166 		default:
167 			/* usage(); */
168 			exit(1);
169 		}
170 	}
171 	argc -= optind;
172 	argv += optind;
173 
174 	if ((portmap != NULL) && (pf_list == 0)) {
175 		pf_list = PF_LIST_INET;
176 #ifdef INET6
177 		pf_list |= PF_LIST_INET6;
178 #endif
179 	}
180 	if (pf_list == 0) {
181 		pf_list = PF_LIST_INET | PF_LIST_LOCAL;
182 #ifdef INET6
183 		pf_list |= PF_LIST_INET6;
184 #endif
185 	}
186 	if ((portmap != NULL) && (pf_list & PF_LIST_LOCAL))
187 		errx(1, "local domain sockets do not have ports");
188 
189 	if (pf_list & PF_LIST_INET) {
190 		get_sockets("net.inet.tcp.pcblist");
191 		get_sockets("net.inet.udp.pcblist");
192 		if (portmap == NULL)
193 			get_sockets("net.inet.raw.pcblist");
194 	}
195 
196 #ifdef INET6
197 	if (pf_list & PF_LIST_INET6) {
198 		get_sockets("net.inet6.tcp6.pcblist");
199 		get_sockets("net.inet6.udp6.pcblist");
200 		if (portmap == NULL)
201 			get_sockets("net.inet6.raw6.pcblist");
202 	}
203 #endif
204 
205 	if (pf_list & PF_LIST_LOCAL) {
206 		get_sockets("net.local.stream.pcblist");
207 		get_sockets("net.local.dgram.pcblist");
208 	}
209 
210 	get_files();
211 
212 	p.p_pid = 0;
213 	for (i = 0; i < nfiles; i++)
214 		if ((kp = pick_socket(&flist[i])) != NULL &&
215 		    get_proc(&p, flist[i].ki_pid) == 0)
216 			print_socket(&flist[i], kp, &p);
217 
218 	return (0);
219 }
220 
221 void
222 parse_ports(const char *l)
223 {
224 	struct servent *srv;
225 	const char *s, *e;
226 	long i, j;
227 
228 	if (portmap == NULL) {
229 		portmap = bit_alloc(65536);
230 		if (portmap == NULL)
231 			err(1, "malloc");
232 	}
233 
234 	if ((srv = getservbyname(l, NULL)) != NULL) {
235 		bit_set(portmap, ntohs(srv->s_port));
236 		return;
237 	}
238 
239 	s = e = l;
240 	while (*s != '\0') {
241 		i = get_num(l, &s, &e);
242 		switch (*e) {
243 		case ',':
244 			e++;
245 		case '\0':
246 			bit_set(portmap, i);
247 			s = e;
248 			continue;
249 		case '-':
250 			s = ++e;
251 			j = get_num(l, &s, &e);
252 			for (; i <= j; i++)
253 				bit_set(portmap, i);
254 			break;
255 		default:
256 			errno = EINVAL;
257 			err(1, "%s", l);
258 		}
259 	}
260 }
261 
262 int
263 get_num(const char *l, const char **s, const char **e)
264 {
265 	long x;
266 	char *t;
267 
268 	while (isdigit((u_int)**e))
269 		(*e)++;
270 	if (*s != *e) {
271 		errno = 0;
272 		x = strtol(*s, &t, 0);
273 		if (errno == 0 && x >= 0 && x <= 65535 && t == *e)
274 			return (x);
275 	}
276 
277 	errno = EINVAL;
278 	err(1, "%s", l);
279 }
280 
281 void
282 get_sockets(const char *mib)
283 {
284 	void *v;
285 	size_t sz;
286 	int rc, n, name[CTL_MAXNAME];
287 	u_int namelen;
288 
289 	sz = CTL_MAXNAME;
290 	rc = sysctlnametomib(mib, &name[0], &sz);
291 	if (rc == -1) {
292 		if (errno == ENOENT)
293 			return;
294 		err(1, "sysctlnametomib: %s", mib);
295 	}
296 	namelen = sz;
297 
298 	name[namelen++] = PCB_ALL;
299 	name[namelen++] = 0;		/* XXX all pids */
300 	name[namelen++] = sizeof(struct kinfo_pcb);
301 	name[namelen++] = INT_MAX;	/* all of them */
302 
303 	sysctl_sucker(&name[0], namelen, &v, &sz);
304 	n = sz / sizeof(struct kinfo_pcb);
305 	socket_add_hash(v, n);
306 }
307 
308 void
309 get_files(void)
310 {
311 	void *v;
312 	size_t sz;
313 	int rc, name[CTL_MAXNAME];
314 	u_int namelen;
315 
316 	sz = CTL_MAXNAME;
317 	rc = sysctlnametomib("kern.file2", &name[0], &sz);
318 	if (rc == -1)
319 		err(1, "sysctlnametomib");
320 	namelen = sz;
321 
322 	name[namelen++] = KERN_FILE_BYPID;
323 	name[namelen++] = 0;		/* XXX all pids */
324 	name[namelen++] = sizeof(struct kinfo_file);
325 	name[namelen++] = INT_MAX;	/* all of them */
326 
327 	sysctl_sucker(&name[0], namelen, &v, &sz);
328 	flist = v;
329 	nfiles = sz / sizeof(struct kinfo_file);
330 
331 	qsort(flist, nfiles, sizeof(*flist), sort_files);
332 }
333 
334 int
335 sort_files(const void *a, const void *b)
336 {
337 	const struct kinfo_file *ka = a, *kb = b;
338 
339 	if (ka->ki_pid == kb->ki_pid)
340 		return (ka->ki_fd - kb->ki_fd);
341 
342 	return (ka->ki_pid - kb->ki_pid);
343 }
344 
345 void
346 sysctl_sucker(int *name, u_int namelen, void **vp, size_t *szp)
347 {
348 	int rc;
349 	void *v;
350 	size_t sz;
351 
352 	/* printf("name %p, namelen %u\n", name, namelen); */
353 
354 	v = NULL;
355 	sz = 0;
356 	do {
357 		rc = sysctl(&name[0], namelen, v, &sz, NULL, 0);
358 		if (rc == -1 && errno != ENOMEM)
359 			err(1, "sysctl");
360 		if (rc == -1 && v != NULL) {
361 			free(v);
362 			v = NULL;
363 		}
364 		if (v == NULL) {
365 			v = malloc(sz);
366 			rc = -1;
367 		}
368 		if (v == NULL)
369 			err(1, "malloc");
370 	} while (rc == -1);
371 
372 	*vp = v;
373 	*szp = sz;
374 	/* printf("got %zu at %p\n", sz, v); */
375 }
376 
377 void
378 socket_add_hash(struct kinfo_pcb *kp, int n)
379 {
380 	struct sockitem *si;
381 	int hash, i;
382 
383 	if (n == 0)
384 		return;
385 
386 	si = malloc(sizeof(*si) * n);
387 	if (si== NULL)
388 		err(1, "malloc");
389 
390 	for (i = 0; i < n; i++) {
391 		si[i].s_sock = &kp[i];
392 		hash = (int)(kp[i].ki_sockaddr % HASHSIZE);
393 		LIST_INSERT_HEAD(&sockhash[hash], &si[i], s_list);
394 	}
395 }
396 
397 int
398 isconnected(struct kinfo_pcb *kp)
399 {
400 
401 	if ((kp->ki_sostate & SS_ISCONNECTED) ||
402 	    (kp->ki_prstate >= INP_CONNECTED) ||
403 	    (kp->ki_tstate > TCPS_LISTEN) ||
404 	    (kp->ki_conn != 0))
405 		return (1);
406 
407 	return (0);
408 }
409 
410 int
411 islistening(struct kinfo_pcb *kp)
412 {
413 
414 	if (isconnected(kp))
415 		return (0);
416 
417 	if (kp->ki_tstate == TCPS_LISTEN)
418 		return (1);
419 
420 	switch (kp->ki_family) {
421 	case PF_INET:
422 		if (kp->ki_type == SOCK_RAW ||
423 		    (kp->ki_type == SOCK_DGRAM &&
424 		     ntohs(satosin(&kp->ki_src)->sin_port) != 0))
425 			return (1);
426 		break;
427 #ifdef INET6
428 	case PF_INET6:
429 		if (kp->ki_type == SOCK_RAW ||
430 		    (kp->ki_type == SOCK_DGRAM &&
431 		     ntohs(satosin6(&kp->ki_src)->sin6_port) != 0))
432 			return (1);
433 		break;
434 #endif
435 	case PF_LOCAL:
436 		if (satosun(&kp->ki_src)->sun_path[0] != '\0')
437 			return (1);
438 		break;
439 	default:
440 		break;
441 	}
442 
443 	return (0);
444 }
445 
446 struct kinfo_pcb *
447 pick_socket(struct kinfo_file *f)
448 {
449 	struct sockitem *si;
450 	struct kinfo_pcb *kp;
451 	int hash;
452 
453 	if (f->ki_ftype != DTYPE_SOCKET)
454 		return (NULL);
455 
456 	hash = (int)(f->ki_fdata % HASHSIZE);
457 	LIST_FOREACH(si, &sockhash[hash], s_list) {
458 		if (si->s_sock->ki_sockaddr == f->ki_fdata)
459 			break;
460 	}
461 	if (si == NULL)
462 		return (NULL);
463 
464 	kp = si->s_sock;
465 
466 	if (only) {
467 		if (isconnected(kp)) {
468 			/*
469 			 * connected but you didn't say you wanted
470 			 * connected sockets
471 			 */
472 			if (!(only & ONLY_CONNECTED))
473 				return (NULL);
474 		}
475 		else if (islistening(kp)) {
476 			/*
477 			 * listening but you didn't ask for listening
478 			 * sockets
479 			 */
480 			if (!(only & ONLY_LISTEN))
481 				return (NULL);
482 		}
483 		else
484 			/*
485 			 * neither connected nor listening, so you
486 			 * don't get it
487 			 */
488 			return (NULL);
489 	}
490 
491 	if (portmap) {
492 		switch (kp->ki_family) {
493 		case AF_INET:
494 			if (!bit_test(portmap,
495 				      ntohs(satosin(&kp->ki_src)->sin_port)) &&
496 			    !bit_test(portmap,
497 				      ntohs(satosin(&kp->ki_dst)->sin_port)))
498 				return (NULL);
499 			break;
500 #ifdef INET6
501 		case AF_INET6:
502 			if (!bit_test(portmap,
503 			    ntohs(satosin6(&kp->ki_src)->sin6_port)) &&
504 			    !bit_test(portmap,
505 				      ntohs(satosin6(&kp->ki_dst)->sin6_port)))
506 				return (NULL);
507 			break;
508 #endif
509 		default:
510 			return (NULL);
511 		}
512 	}
513 
514 	return (kp);
515 }
516 
517 int
518 get_proc(struct kinfo_proc2 *p, int pid)
519 {
520 	int name[6];
521 	u_int namelen;
522 	size_t sz;
523 
524 	if (p->p_pid == pid)
525 		return (0);
526 
527 	sz = sizeof(*p);
528 	namelen = 0;
529 	name[namelen++] = CTL_KERN;
530 	name[namelen++] = KERN_PROC2;
531 	name[namelen++] = KERN_PROC_PID;
532 	name[namelen++] = pid;
533 	name[namelen++] = sz;
534 	name[namelen++] = 1;
535 
536 	return (sysctl(&name[0], namelen, p, &sz, NULL, 0));
537 }
538 
539 int
540 print_socket(struct kinfo_file *kf, struct kinfo_pcb *kp, struct kinfo_proc2 *p)
541 {
542 	static int first = 1;
543 	struct passwd *pw;
544 	const char *t;
545 	char proto[22];
546 
547 	if (first) {
548 		printf("%-8s " "%-10s "   "%-5s " "%-2s " "%-6s "
549 		       "%-21s "         "%s\n",
550 		       "USER", "COMMAND", "PID",  "FD",   "PROTO",
551 		       "LOCAL ADDRESS", "FOREIGN ADDRESS");
552 		first = 0;
553 	}
554 
555 	if ((pw = getpwuid(p->p_uid)) != NULL)
556 		printf("%-8s ", pw->pw_name);
557 	else
558 		printf("%-8d ", (int)p->p_uid);
559 
560 	printf("%-10.10s ", p->p_comm);
561 	printf("%-5d ", (int)kf->ki_pid);
562 	printf("%2d ", (int)kf->ki_fd);
563 
564 	snprintf(proto, sizeof(proto), "%d/%d", kp->ki_family, kp->ki_protocol);
565 
566 	switch (kp->ki_family) {
567 	case PF_INET:
568 		switch (kp->ki_protocol) {
569 		case IPPROTO_TCP:	t = "tcp";	break;
570 		case IPPROTO_UDP:	t = "udp";	break;
571 		case IPPROTO_RAW:	t = "raw";	break;
572 		default:		t = proto;	break;
573 		}
574 		break;
575 #ifdef INET6
576 	case PF_INET6:
577 		switch (kp->ki_protocol) {
578 		case IPPROTO_TCP:	t = "tcp6";	break;
579 		case IPPROTO_UDP:	t = "udp6";	break;
580 		case IPPROTO_RAW:	t = "raw6";	break;
581 		default:		t = proto;	break;
582 		}
583 		break;
584 #endif
585 	case PF_LOCAL:
586 		switch (kp->ki_type) {
587 		case SOCK_STREAM:	t = "stream";	break;
588 		case SOCK_DGRAM:	t = "dgram";	break;
589 		case SOCK_RAW:		t = "raw";	break;
590 		case SOCK_RDM:		t = "rdm";	break;
591 		case SOCK_SEQPACKET:	t = "seq";	break;
592 		default:		t = proto;	break;
593 		}
594 		break;
595 	default:
596 		snprintf(proto, sizeof(proto), "%d/%d/%d",
597 			 kp->ki_family, kp->ki_type, kp->ki_protocol);
598 		t = proto;
599 		break;
600 	}
601 
602 	printf("%-6s ", t);
603 
604 /*
605 	if (kp->ki_family == PF_LOCAL) {
606 		if (kp->ki_src.sa_len > 2) {
607 			print_addr(0, kp->ki_type, kp->ki_pflags, &kp->ki_src);
608 			if (kp->ki_dst.sa_family == PF_LOCAL)
609 				printf(" ");
610 		}
611 		if (kp->ki_dst.sa_family == PF_LOCAL)
612 			printf("-> ");
613 	}
614 	else */{
615 		print_addr(21, kp->ki_type, kp->ki_pflags, &kp->ki_src);
616 		printf(" ");
617 	}
618 
619 	if (isconnected(kp))
620 		print_addr(0, kp->ki_type, kp->ki_pflags, &kp->ki_dst);
621 	else if (kp->ki_family == PF_INET
622 #ifdef INET6
623 	    || kp->ki_family == PF_INET6
624 #endif
625 	    )
626 		printf("%-*s", 0, "*.*");
627 	/* else if (kp->ki_src.sa_len == 2)
628 	   printf("%-*s", 0, "-"); */
629 	else
630 		printf("-");
631 
632 	printf("\n");
633 
634 	return (0);
635 }
636 
637 void
638 print_addr(int l, int t, int f, struct sockaddr *sa)
639 {
640 	char sabuf[256], pbuf[32];
641 	int r = 0;
642 
643 	if (!(f & INP_ANONPORT))
644 		f = 0;
645 	else
646 		f = NI_NUMERICSERV;
647 	if (t == SOCK_DGRAM)
648 		f |= NI_DGRAM;
649 	if (nonames)
650 		f |= NI_NUMERICHOST|NI_NUMERICSERV;
651 
652 	getnameinfo(sa, sa->sa_len, sabuf, sizeof(sabuf),
653 		    pbuf, sizeof(pbuf), f);
654 
655 	switch (sa->sa_family) {
656 	case PF_UNSPEC:
657 		r = printf("(PF_UNSPEC)");
658 		break;
659 	case PF_INET: {
660 		struct sockaddr_in *si = satosin(sa);
661 		if (si->sin_addr.s_addr != INADDR_ANY)
662 			r = printf("%s.%s", sabuf, pbuf);
663 		else if (ntohs(si->sin_port) != 0)
664 			r = printf("*.%s", pbuf);
665 		else
666 			r = printf("*.*");
667 		break;
668 	}
669 #ifdef INET6
670 	case PF_INET6: {
671 		struct sockaddr_in6 *si6 = satosin6(sa);
672 		if (!IN6_IS_ADDR_UNSPECIFIED(&si6->sin6_addr))
673 			r = printf("%s.%s", sabuf, pbuf);
674 		else if (ntohs(si6->sin6_port) != 0)
675 			r = printf("*.%s", pbuf);
676 		else
677 			r = printf("*.*");
678 		break;
679 	}
680 #endif
681 	case PF_LOCAL: {
682 		struct sockaddr_un *sun = satosun(sa);
683 		r = printf("%s", sun->sun_path);
684 		if (r == 0)
685 			r = printf("-");
686 		break;
687 	}
688 	default:
689 		break;
690 	}
691 
692 	if (r > 0)
693 		l -= r;
694 	if (l > 0)
695 		printf("%*s", l, "");
696 }
697