1 /* $NetBSD: sockstat.c,v 1.19 2017/01/14 01:01:48 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.19 2017/01/14 01:01:48 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 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((u_int)**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 = 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 = 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