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