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 79void parse_ports(const char *); 80int get_num(const char *, const char **, const char **); 81void get_sockets(const char *); 82void get_files(void); 83int sort_files(const void *, const void *); 84void sysctl_sucker(int *, u_int, void **, size_t *); 85void socket_add_hash(struct kinfo_pcb *, int); 86int isconnected(struct kinfo_pcb *); 87int islistening(struct kinfo_pcb *); 88struct kinfo_pcb *pick_socket(struct kinfo_file *); 89int get_proc(struct kinfo_proc2 *, int); 90int print_socket(struct kinfo_file *, struct kinfo_pcb *, 91 struct kinfo_proc2 *); 92void print_addr(int, int, int, struct sockaddr *); 93 94LIST_HEAD(socklist, sockitem); 95#define HASHSIZE 1009 96struct socklist sockhash[HASHSIZE]; 97struct sockitem { 98 LIST_ENTRY(sockitem) s_list; 99 struct kinfo_pcb *s_sock; 100}; 101 102struct kinfo_file *flist; 103size_t flistc; 104 105int pf_list, only, nonames; 106bitstr_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 116int 117main(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 229void 230parse_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 271int 272get_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 290void 291get_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 317void 318get_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 343int 344sort_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 354void 355sysctl_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 386void 387socket_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 406int 407isconnected(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 419int 420islistening(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 455struct kinfo_pcb * 456pick_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 526int 527get_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 548int 549print_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 646void 647print_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