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