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