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