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