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