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