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