1 /*- 2 * Copyright (c) 1980, 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 36 __FBSDID("$FreeBSD$"); 37 38 #ifdef lint 39 static const char sccsid[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93"; 40 #endif 41 42 /* 43 * netstat 44 */ 45 #include <sys/param.h> 46 #include <sys/queue.h> 47 #include <sys/socket.h> 48 #include <sys/socketvar.h> 49 #include <sys/protosw.h> 50 51 #include <netinet/in.h> 52 #include <arpa/inet.h> 53 #include <net/route.h> 54 #include <netinet/in_systm.h> 55 #include <netinet/ip.h> 56 #ifdef INET6 57 #include <netinet/ip6.h> 58 #endif 59 #include <netinet/in_pcb.h> 60 #include <netinet/ip_icmp.h> 61 #include <netinet/icmp_var.h> 62 #include <netinet/ip_var.h> 63 #include <netinet/tcp.h> 64 #include <netinet/tcpip.h> 65 #include <netinet/tcp_seq.h> 66 #include <netinet/tcp_var.h> 67 #define TCPSTATES 68 #include <netinet/tcp_fsm.h> 69 #include <netinet/tcp_timer.h> 70 #include <netinet/tcp_var.h> 71 #include <netinet/tcp_debug.h> 72 #include <netinet/udp.h> 73 #include <netinet/udp_var.h> 74 75 #include <netdb.h> 76 #include <nlist.h> 77 #include <paths.h> 78 #include <stdlib.h> 79 #include <string.h> 80 81 #include "systat.h" 82 #include "extern.h" 83 84 static struct netinfo *enter(struct inpcb *, int, const char *); 85 static void enter_kvm(struct inpcb *, struct socket *, int, const char *); 86 static void enter_sysctl(struct inpcb *, struct xsocket *, int, const char *); 87 static void fetchnetstat_kvm(void); 88 static void fetchnetstat_sysctl(void); 89 static char *inetname(struct sockaddr *); 90 static void inetprint(struct sockaddr *, const char *); 91 92 #define streq(a,b) (strcmp(a,b)==0) 93 #define YMAX(w) ((w)->_maxy-1) 94 95 WINDOW * 96 opennetstat() 97 { 98 sethostent(1); 99 setnetent(1); 100 return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); 101 } 102 103 struct netinfo { 104 TAILQ_ENTRY(netinfo) chain; 105 short ni_line; /* line on screen */ 106 short ni_seen; /* 0 when not present in list */ 107 short ni_flags; 108 #define NIF_LACHG 0x1 /* local address changed */ 109 #define NIF_FACHG 0x2 /* foreign address changed */ 110 short ni_state; /* tcp state */ 111 const char *ni_proto; /* protocol */ 112 struct sockaddr_storage ni_lsa; /* local address */ 113 struct sockaddr_storage ni_fsa; /* foreign address */ 114 u_int ni_rcvcc; /* rcv buffer character count */ 115 u_int ni_sndcc; /* snd buffer character count */ 116 }; 117 118 TAILQ_HEAD(netinfohead, netinfo) netcb = TAILQ_HEAD_INITIALIZER(netcb); 119 120 static int aflag = 0; 121 static int nflag = 0; 122 static int lastrow = 1; 123 124 void 125 closenetstat(w) 126 WINDOW *w; 127 { 128 struct netinfo *p; 129 130 endhostent(); 131 endnetent(); 132 TAILQ_FOREACH(p, &netcb, chain) { 133 if (p->ni_line != -1) 134 lastrow--; 135 p->ni_line = -1; 136 } 137 if (w != NULL) { 138 wclear(w); 139 wrefresh(w); 140 delwin(w); 141 } 142 } 143 144 static const char *miblist[] = { 145 "net.inet.tcp.pcblist", 146 "net.inet.udp.pcblist" 147 }; 148 149 static char tcb[] = "tcb", udb[] = "udb"; 150 151 struct nlist namelist[] = { 152 #define X_TCB 0 153 { .n_name = tcb }, 154 #define X_UDB 1 155 { .n_name = udb }, 156 { .n_name = NULL }, 157 }; 158 159 int 160 initnetstat() 161 { 162 protos = TCP|UDP; 163 return(1); 164 } 165 166 void 167 fetchnetstat() 168 { 169 if (use_kvm) 170 fetchnetstat_kvm(); 171 else 172 fetchnetstat_sysctl(); 173 } 174 175 static void 176 fetchnetstat_kvm() 177 { 178 struct inpcb *next; 179 struct netinfo *p; 180 struct inpcbhead head; 181 struct inpcb inpcb; 182 struct socket sockb; 183 struct tcpcb tcpcb; 184 void *off; 185 int istcp; 186 187 if (namelist[X_TCB].n_value == 0) 188 return; 189 TAILQ_FOREACH(p, &netcb, chain) 190 p->ni_seen = 0; 191 if (protos&TCP) { 192 off = NPTR(X_TCB); 193 istcp = 1; 194 } 195 else if (protos&UDP) { 196 off = NPTR(X_UDB); 197 istcp = 0; 198 } 199 else { 200 error("No protocols to display"); 201 return; 202 } 203 again: 204 KREAD(off, &head, sizeof (struct inpcbhead)); 205 LIST_FOREACH(next, &head, inp_list) { 206 KREAD(next, &inpcb, sizeof (inpcb)); 207 next = &inpcb; 208 if (!aflag) { 209 if (inpcb.inp_vflag & INP_IPV4) { 210 if (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 211 continue; 212 } 213 #ifdef INET6 214 else if (inpcb.inp_vflag & INP_IPV6) { 215 if (memcmp(&inpcb.in6p_laddr, 216 &in6addr_any, sizeof(in6addr_any)) == 0) 217 continue; 218 } 219 #endif 220 } 221 if (nhosts && !checkhost(&inpcb)) 222 continue; 223 if (nports && !checkport(&inpcb)) 224 continue; 225 if (istcp) { 226 if (inpcb.inp_vflag & INP_TIMEWAIT) { 227 bzero(&sockb, sizeof(sockb)); 228 enter_kvm(&inpcb, &sockb, TCPS_TIME_WAIT, 229 "tcp"); 230 } else { 231 KREAD(inpcb.inp_socket, &sockb, 232 sizeof (sockb)); 233 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 234 enter_kvm(&inpcb, &sockb, tcpcb.t_state, 235 "tcp"); 236 } 237 } else 238 enter_kvm(&inpcb, &sockb, 0, "udp"); 239 } 240 if (istcp && (protos&UDP)) { 241 istcp = 0; 242 off = NPTR(X_UDB); 243 goto again; 244 } 245 } 246 247 static void 248 fetchnetstat_sysctl() 249 { 250 struct netinfo *p; 251 int idx; 252 struct xinpgen *inpg; 253 char *cur, *end; 254 struct inpcb *inpcb; 255 struct xinpcb *xip = NULL; 256 struct xtcpcb *xtp = NULL; 257 int plen; 258 size_t lsz; 259 260 TAILQ_FOREACH(p, &netcb, chain) 261 p->ni_seen = 0; 262 if (protos&TCP) { 263 idx = 0; 264 } else if (protos&UDP) { 265 idx = 1; 266 } else { 267 error("No protocols to display"); 268 return; 269 } 270 271 for (;idx < 2; idx++) { 272 if (idx == 1 && !(protos&UDP)) 273 break; 274 inpg = (struct xinpgen *)sysctl_dynread(miblist[idx], &lsz); 275 if (inpg == NULL) { 276 error("sysctl(%s...) failed", miblist[idx]); 277 continue; 278 } 279 /* 280 * We currently do no require a consistent pcb list. 281 * Try to be robust in case of struct size changes 282 */ 283 cur = ((char *)inpg) + inpg->xig_len; 284 /* There is also a trailing struct xinpgen */ 285 end = ((char *)inpg) + lsz - inpg->xig_len; 286 if (end <= cur) { 287 free(inpg); 288 continue; 289 } 290 if (idx == 0) { /* TCP */ 291 xtp = (struct xtcpcb *)cur; 292 plen = xtp->xt_len; 293 } else { 294 xip = (struct xinpcb *)cur; 295 plen = xip->xi_len; 296 } 297 while (cur + plen <= end) { 298 if (idx == 0) { /* TCP */ 299 xtp = (struct xtcpcb *)cur; 300 inpcb = &xtp->xt_inp; 301 } else { 302 xip = (struct xinpcb *)cur; 303 inpcb = &xip->xi_inp; 304 } 305 cur += plen; 306 307 if (!aflag) { 308 if (inpcb->inp_vflag & INP_IPV4) { 309 if (inet_lnaof(inpcb->inp_laddr) == 310 INADDR_ANY) 311 continue; 312 } 313 #ifdef INET6 314 else if (inpcb->inp_vflag & INP_IPV6) { 315 if (memcmp(&inpcb->in6p_laddr, 316 &in6addr_any, sizeof(in6addr_any)) 317 == 0) 318 continue; 319 } 320 #endif 321 } 322 if (nhosts && !checkhost(inpcb)) 323 continue; 324 if (nports && !checkport(inpcb)) 325 continue; 326 if (idx == 0) /* TCP */ 327 enter_sysctl(inpcb, &xtp->xt_socket, 328 xtp->xt_tp.t_state, "tcp"); 329 else /* UDP */ 330 enter_sysctl(inpcb, &xip->xi_socket, 0, "udp"); 331 } 332 free(inpg); 333 } 334 } 335 336 static void 337 enter_kvm(inp, so, state, proto) 338 struct inpcb *inp; 339 struct socket *so; 340 int state; 341 const char *proto; 342 { 343 struct netinfo *p; 344 345 if ((p = enter(inp, state, proto)) != NULL) { 346 p->ni_rcvcc = so->so_rcv.sb_cc; 347 p->ni_sndcc = so->so_snd.sb_cc; 348 } 349 } 350 351 static void 352 enter_sysctl(inp, so, state, proto) 353 struct inpcb *inp; 354 struct xsocket *so; 355 int state; 356 const char *proto; 357 { 358 struct netinfo *p; 359 360 if ((p = enter(inp, state, proto)) != NULL) { 361 p->ni_rcvcc = so->so_rcv.sb_cc; 362 p->ni_sndcc = so->so_snd.sb_cc; 363 } 364 } 365 366 367 static struct netinfo * 368 enter(inp, state, proto) 369 struct inpcb *inp; 370 int state; 371 const char *proto; 372 { 373 struct netinfo *p; 374 struct sockaddr_storage lsa, fsa; 375 struct sockaddr_in *sa4; 376 #ifdef INET6 377 struct sockaddr_in6 *sa6; 378 #endif 379 380 memset(&lsa, 0, sizeof(lsa)); 381 memset(&fsa, 0, sizeof(fsa)); 382 if (inp->inp_vflag & INP_IPV4) { 383 sa4 = (struct sockaddr_in *)&lsa; 384 sa4->sin_addr = inp->inp_laddr; 385 sa4->sin_port = inp->inp_lport; 386 sa4->sin_family = AF_INET; 387 sa4->sin_len = sizeof(struct sockaddr_in); 388 389 sa4 = (struct sockaddr_in *)&fsa; 390 sa4->sin_addr = inp->inp_faddr; 391 sa4->sin_port = inp->inp_fport; 392 sa4->sin_family = AF_INET; 393 sa4->sin_len = sizeof(struct sockaddr_in); 394 } 395 #ifdef INET6 396 else if (inp->inp_vflag & INP_IPV6) { 397 sa6 = (struct sockaddr_in6 *)&lsa; 398 memcpy(&sa6->sin6_addr, &inp->in6p_laddr, 399 sizeof(struct in6_addr)); 400 sa6->sin6_port = inp->inp_lport; 401 sa6->sin6_family = AF_INET6; 402 sa6->sin6_len = sizeof(struct sockaddr_in6); 403 404 sa6 = (struct sockaddr_in6 *)&fsa; 405 memcpy(&sa6->sin6_addr, &inp->in6p_faddr, 406 sizeof(struct in6_addr)); 407 sa6->sin6_port = inp->inp_fport; 408 sa6->sin6_family = AF_INET6; 409 sa6->sin6_len = sizeof(struct sockaddr_in6); 410 } 411 #endif 412 else 413 return NULL; 414 415 /* 416 * Only take exact matches, any sockets with 417 * previously unbound addresses will be deleted 418 * below in the display routine because they 419 * will appear as ``not seen'' in the kernel 420 * data structures. 421 */ 422 TAILQ_FOREACH(p, &netcb, chain) { 423 if (!streq(proto, p->ni_proto)) 424 continue; 425 if (p->ni_lsa.ss_family != lsa.ss_family || 426 memcmp(&p->ni_lsa, &lsa, lsa.ss_len) != 0) 427 continue; 428 if (p->ni_fsa.ss_family == fsa.ss_family && 429 memcmp(&p->ni_fsa, &fsa, fsa.ss_len) == 0) 430 break; 431 } 432 if (p == NULL) { 433 if ((p = malloc(sizeof(*p))) == NULL) { 434 error("Out of memory"); 435 return NULL; 436 } 437 TAILQ_INSERT_HEAD(&netcb, p, chain); 438 p->ni_line = -1; 439 memcpy(&p->ni_lsa, &lsa, lsa.ss_len); 440 memcpy(&p->ni_fsa, &fsa, fsa.ss_len); 441 p->ni_proto = strdup(proto); 442 p->ni_flags = NIF_LACHG|NIF_FACHG; 443 } 444 p->ni_state = state; 445 p->ni_seen = 1; 446 return p; 447 } 448 449 /* column locations */ 450 #define LADDR 0 451 #define FADDR LADDR+23 452 #define PROTO FADDR+23 453 #define RCVCC PROTO+6 454 #define SNDCC RCVCC+7 455 #define STATE SNDCC+7 456 457 458 void 459 labelnetstat() 460 { 461 if (use_kvm && namelist[X_TCB].n_type == 0) 462 return; 463 wmove(wnd, 0, 0); wclrtobot(wnd); 464 mvwaddstr(wnd, 0, LADDR, "Local Address"); 465 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 466 mvwaddstr(wnd, 0, PROTO, "Proto"); 467 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 468 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 469 mvwaddstr(wnd, 0, STATE, "(state)"); 470 } 471 472 void 473 shownetstat() 474 { 475 struct netinfo *p, *q; 476 char proto[6]; 477 const char *family = ""; 478 479 /* 480 * First, delete any connections that have gone 481 * away and adjust the position of connections 482 * below to reflect the deleted line. 483 */ 484 p = TAILQ_FIRST(&netcb); 485 while (p != NULL) { 486 if (p->ni_line == -1 || p->ni_seen) { 487 p = TAILQ_NEXT(p, chain); 488 continue; 489 } 490 wmove(wnd, p->ni_line, 0); wdeleteln(wnd); 491 TAILQ_FOREACH(q, &netcb, chain) 492 if (q != p && q->ni_line > p->ni_line) { 493 q->ni_line--; 494 /* this shouldn't be necessary */ 495 q->ni_flags |= NIF_LACHG|NIF_FACHG; 496 } 497 lastrow--; 498 q = TAILQ_NEXT(p, chain); 499 TAILQ_REMOVE(&netcb, p, chain); 500 free(p); 501 p = q; 502 } 503 /* 504 * Update existing connections and add new ones. 505 */ 506 TAILQ_FOREACH(p, &netcb, chain) { 507 if (p->ni_line == -1) { 508 /* 509 * Add a new entry if possible. 510 */ 511 if (lastrow > YMAX(wnd)) 512 continue; 513 p->ni_line = lastrow++; 514 p->ni_flags |= NIF_LACHG|NIF_FACHG; 515 } 516 if (p->ni_flags & NIF_LACHG) { 517 wmove(wnd, p->ni_line, LADDR); 518 inetprint((struct sockaddr *)&p->ni_lsa, p->ni_proto); 519 p->ni_flags &= ~NIF_LACHG; 520 } 521 if (p->ni_flags & NIF_FACHG) { 522 wmove(wnd, p->ni_line, FADDR); 523 inetprint((struct sockaddr *)&p->ni_fsa, p->ni_proto); 524 p->ni_flags &= ~NIF_FACHG; 525 } 526 #ifdef INET6 527 family = (p->ni_lsa.ss_family == AF_INET) ? "4" : "6"; 528 #endif 529 snprintf(proto, sizeof(proto), "%s%s", p->ni_proto, family); 530 mvwaddstr(wnd, p->ni_line, PROTO, proto); 531 mvwprintw(wnd, p->ni_line, RCVCC, "%6u", p->ni_rcvcc); 532 mvwprintw(wnd, p->ni_line, SNDCC, "%6u", p->ni_sndcc); 533 if (streq(p->ni_proto, "tcp")) { 534 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES) 535 mvwprintw(wnd, p->ni_line, STATE, "%d", 536 p->ni_state); 537 else 538 mvwaddstr(wnd, p->ni_line, STATE, 539 tcpstates[p->ni_state]); 540 } 541 wclrtoeol(wnd); 542 } 543 if (lastrow < YMAX(wnd)) { 544 wmove(wnd, lastrow, 0); wclrtobot(wnd); 545 wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ 546 } 547 } 548 549 /* 550 * Pretty print an Internet address (net address + port). 551 * If the nflag was specified, use numbers instead of names. 552 */ 553 static void 554 inetprint(sa, proto) 555 struct sockaddr *sa; 556 const char *proto; 557 { 558 struct servent *sp = 0; 559 char line[80], *cp; 560 int port; 561 562 switch (sa->sa_family) { 563 case AF_INET: 564 port = ((struct sockaddr_in *)sa)->sin_port; 565 break; 566 #ifdef INET6 567 case AF_INET6: 568 port = ((struct sockaddr_in6 *)sa)->sin6_port; 569 break; 570 #endif 571 default: 572 port = 0; 573 break; 574 } 575 snprintf(line, sizeof(line), "%.*s.", 16, inetname(sa)); 576 cp = index(line, '\0'); 577 if (!nflag && port) 578 sp = getservbyport(port, proto); 579 if (sp || port == 0) 580 snprintf(cp, sizeof(line) - (cp - line), "%.8s", 581 sp ? sp->s_name : "*"); 582 else 583 snprintf(cp, sizeof(line) - (cp - line), "%d", 584 ntohs((u_short)port)); 585 /* pad to full column to clear any garbage */ 586 cp = index(line, '\0'); 587 while (cp - line < 22) 588 *cp++ = ' '; 589 line[22] = '\0'; 590 waddstr(wnd, line); 591 } 592 593 /* 594 * Construct an Internet address representation. 595 * If the nflag has been supplied, give 596 * numeric value, otherwise try for symbolic name. 597 */ 598 static char * 599 inetname(sa) 600 struct sockaddr *sa; 601 { 602 char *cp = 0; 603 static char line[NI_MAXHOST]; 604 struct hostent *hp; 605 struct netent *np; 606 struct in_addr in; 607 608 #ifdef INET6 609 if (sa->sa_family == AF_INET6) { 610 if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr, 611 &in6addr_any, sizeof(in6addr_any)) == 0) 612 strcpy(line, "*"); 613 else 614 getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, 615 nflag ? NI_NUMERICHOST : 0); 616 return (line); 617 } 618 #endif 619 620 in = ((struct sockaddr_in *)sa)->sin_addr; 621 if (!nflag && in.s_addr != INADDR_ANY) { 622 int net = inet_netof(in); 623 int lna = inet_lnaof(in); 624 625 if (lna == INADDR_ANY) { 626 np = getnetbyaddr(net, AF_INET); 627 if (np) 628 cp = np->n_name; 629 } 630 if (cp == 0) { 631 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 632 if (hp) 633 cp = hp->h_name; 634 } 635 } 636 if (in.s_addr == INADDR_ANY) 637 strcpy(line, "*"); 638 else if (cp) 639 snprintf(line, sizeof(line), "%s", cp); 640 else { 641 in.s_addr = ntohl(in.s_addr); 642 #define C(x) ((x) & 0xff) 643 snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24), 644 C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); 645 } 646 return (line); 647 } 648 649 int 650 cmdnetstat(cmd, args) 651 const char *cmd, *args; 652 { 653 if (prefix(cmd, "all")) { 654 aflag = !aflag; 655 goto fixup; 656 } 657 if (prefix(cmd, "numbers") || prefix(cmd, "names")) { 658 struct netinfo *p; 659 int new; 660 661 new = prefix(cmd, "numbers"); 662 if (new == nflag) 663 return (1); 664 TAILQ_FOREACH(p, &netcb, chain) { 665 if (p->ni_line == -1) 666 continue; 667 p->ni_flags |= NIF_LACHG|NIF_FACHG; 668 } 669 nflag = new; 670 goto redisplay; 671 } 672 if (!netcmd(cmd, args)) 673 return (0); 674 fixup: 675 fetchnetstat(); 676 redisplay: 677 shownetstat(); 678 refresh(); 679 return (1); 680 } 681