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(void) 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(WINDOW *w) 126 { 127 struct netinfo *p; 128 129 endhostent(); 130 endnetent(); 131 TAILQ_FOREACH(p, &netcb, chain) { 132 if (p->ni_line != -1) 133 lastrow--; 134 p->ni_line = -1; 135 } 136 if (w != NULL) { 137 wclear(w); 138 wrefresh(w); 139 delwin(w); 140 } 141 } 142 143 static const char *miblist[] = { 144 "net.inet.tcp.pcblist", 145 "net.inet.udp.pcblist" 146 }; 147 148 static char tcb[] = "tcb", udb[] = "udb"; 149 150 struct nlist namelist[] = { 151 #define X_TCB 0 152 { .n_name = tcb }, 153 #define X_UDB 1 154 { .n_name = udb }, 155 { .n_name = NULL }, 156 }; 157 158 int 159 initnetstat(void) 160 { 161 protos = TCP|UDP; 162 return(1); 163 } 164 165 void 166 fetchnetstat(void) 167 { 168 if (use_kvm) 169 fetchnetstat_kvm(); 170 else 171 fetchnetstat_sysctl(); 172 } 173 174 static void 175 fetchnetstat_kvm(void) 176 { 177 struct inpcb *next; 178 struct netinfo *p; 179 struct inpcbhead head; 180 struct inpcb inpcb; 181 struct socket sockb; 182 struct tcpcb tcpcb; 183 void *off; 184 int istcp; 185 186 if (namelist[X_TCB].n_value == 0) 187 return; 188 TAILQ_FOREACH(p, &netcb, chain) 189 p->ni_seen = 0; 190 if (protos&TCP) { 191 off = NPTR(X_TCB); 192 istcp = 1; 193 } 194 else if (protos&UDP) { 195 off = NPTR(X_UDB); 196 istcp = 0; 197 } 198 else { 199 error("No protocols to display"); 200 return; 201 } 202 again: 203 KREAD(off, &head, sizeof (struct inpcbhead)); 204 LIST_FOREACH(next, &head, inp_list) { 205 KREAD(next, &inpcb, sizeof (inpcb)); 206 next = &inpcb; 207 if (!aflag) { 208 if (inpcb.inp_vflag & INP_IPV4) { 209 if (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 210 continue; 211 } 212 #ifdef INET6 213 else if (inpcb.inp_vflag & INP_IPV6) { 214 if (memcmp(&inpcb.in6p_laddr, 215 &in6addr_any, sizeof(in6addr_any)) == 0) 216 continue; 217 } 218 #endif 219 } 220 if (nhosts && !checkhost(&inpcb)) 221 continue; 222 if (nports && !checkport(&inpcb)) 223 continue; 224 if (istcp) { 225 if (inpcb.inp_vflag & INP_TIMEWAIT) { 226 bzero(&sockb, sizeof(sockb)); 227 enter_kvm(&inpcb, &sockb, TCPS_TIME_WAIT, 228 "tcp"); 229 } else { 230 KREAD(inpcb.inp_socket, &sockb, 231 sizeof (sockb)); 232 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 233 enter_kvm(&inpcb, &sockb, tcpcb.t_state, 234 "tcp"); 235 } 236 } else 237 enter_kvm(&inpcb, &sockb, 0, "udp"); 238 } 239 if (istcp && (protos&UDP)) { 240 istcp = 0; 241 off = NPTR(X_UDB); 242 goto again; 243 } 244 } 245 246 static void 247 fetchnetstat_sysctl(void) 248 { 249 struct netinfo *p; 250 int idx; 251 struct xinpgen *inpg; 252 char *cur, *end; 253 struct inpcb *inpcb; 254 struct xinpcb *xip = NULL; 255 struct xtcpcb *xtp = NULL; 256 int plen; 257 size_t lsz; 258 259 TAILQ_FOREACH(p, &netcb, chain) 260 p->ni_seen = 0; 261 if (protos&TCP) { 262 idx = 0; 263 } else if (protos&UDP) { 264 idx = 1; 265 } else { 266 error("No protocols to display"); 267 return; 268 } 269 270 for (;idx < 2; idx++) { 271 if (idx == 1 && !(protos&UDP)) 272 break; 273 inpg = (struct xinpgen *)sysctl_dynread(miblist[idx], &lsz); 274 if (inpg == NULL) { 275 error("sysctl(%s...) failed", miblist[idx]); 276 continue; 277 } 278 /* 279 * We currently do no require a consistent pcb list. 280 * Try to be robust in case of struct size changes 281 */ 282 cur = ((char *)inpg) + inpg->xig_len; 283 /* There is also a trailing struct xinpgen */ 284 end = ((char *)inpg) + lsz - inpg->xig_len; 285 if (end <= cur) { 286 free(inpg); 287 continue; 288 } 289 if (idx == 0) { /* TCP */ 290 xtp = (struct xtcpcb *)cur; 291 plen = xtp->xt_len; 292 } else { 293 xip = (struct xinpcb *)cur; 294 plen = xip->xi_len; 295 } 296 while (cur + plen <= end) { 297 if (idx == 0) { /* TCP */ 298 xtp = (struct xtcpcb *)cur; 299 inpcb = &xtp->xt_inp; 300 } else { 301 xip = (struct xinpcb *)cur; 302 inpcb = &xip->xi_inp; 303 } 304 cur += plen; 305 306 if (!aflag) { 307 if (inpcb->inp_vflag & INP_IPV4) { 308 if (inet_lnaof(inpcb->inp_laddr) == 309 INADDR_ANY) 310 continue; 311 } 312 #ifdef INET6 313 else if (inpcb->inp_vflag & INP_IPV6) { 314 if (memcmp(&inpcb->in6p_laddr, 315 &in6addr_any, sizeof(in6addr_any)) 316 == 0) 317 continue; 318 } 319 #endif 320 } 321 if (nhosts && !checkhost(inpcb)) 322 continue; 323 if (nports && !checkport(inpcb)) 324 continue; 325 if (idx == 0) /* TCP */ 326 enter_sysctl(inpcb, &xtp->xt_socket, 327 xtp->xt_tp.t_state, "tcp"); 328 else /* UDP */ 329 enter_sysctl(inpcb, &xip->xi_socket, 0, "udp"); 330 } 331 free(inpg); 332 } 333 } 334 335 static void 336 enter_kvm(struct inpcb *inp, struct socket *so, int state, const char *proto) 337 { 338 struct netinfo *p; 339 340 if ((p = enter(inp, state, proto)) != NULL) { 341 p->ni_rcvcc = so->so_rcv.sb_cc; 342 p->ni_sndcc = so->so_snd.sb_cc; 343 } 344 } 345 346 static void 347 enter_sysctl(struct inpcb *inp, struct xsocket *so, int state, const char *proto) 348 { 349 struct netinfo *p; 350 351 if ((p = enter(inp, state, proto)) != NULL) { 352 p->ni_rcvcc = so->so_rcv.sb_cc; 353 p->ni_sndcc = so->so_snd.sb_cc; 354 } 355 } 356 357 358 static struct netinfo * 359 enter(struct inpcb *inp, int state, const char *proto) 360 { 361 struct netinfo *p; 362 struct sockaddr_storage lsa, fsa; 363 struct sockaddr_in *sa4; 364 #ifdef INET6 365 struct sockaddr_in6 *sa6; 366 #endif 367 368 memset(&lsa, 0, sizeof(lsa)); 369 memset(&fsa, 0, sizeof(fsa)); 370 if (inp->inp_vflag & INP_IPV4) { 371 sa4 = (struct sockaddr_in *)&lsa; 372 sa4->sin_addr = inp->inp_laddr; 373 sa4->sin_port = inp->inp_lport; 374 sa4->sin_family = AF_INET; 375 sa4->sin_len = sizeof(struct sockaddr_in); 376 377 sa4 = (struct sockaddr_in *)&fsa; 378 sa4->sin_addr = inp->inp_faddr; 379 sa4->sin_port = inp->inp_fport; 380 sa4->sin_family = AF_INET; 381 sa4->sin_len = sizeof(struct sockaddr_in); 382 } 383 #ifdef INET6 384 else if (inp->inp_vflag & INP_IPV6) { 385 sa6 = (struct sockaddr_in6 *)&lsa; 386 memcpy(&sa6->sin6_addr, &inp->in6p_laddr, 387 sizeof(struct in6_addr)); 388 sa6->sin6_port = inp->inp_lport; 389 sa6->sin6_family = AF_INET6; 390 sa6->sin6_len = sizeof(struct sockaddr_in6); 391 392 sa6 = (struct sockaddr_in6 *)&fsa; 393 memcpy(&sa6->sin6_addr, &inp->in6p_faddr, 394 sizeof(struct in6_addr)); 395 sa6->sin6_port = inp->inp_fport; 396 sa6->sin6_family = AF_INET6; 397 sa6->sin6_len = sizeof(struct sockaddr_in6); 398 } 399 #endif 400 else 401 return NULL; 402 403 /* 404 * Only take exact matches, any sockets with 405 * previously unbound addresses will be deleted 406 * below in the display routine because they 407 * will appear as ``not seen'' in the kernel 408 * data structures. 409 */ 410 TAILQ_FOREACH(p, &netcb, chain) { 411 if (!streq(proto, p->ni_proto)) 412 continue; 413 if (p->ni_lsa.ss_family != lsa.ss_family || 414 memcmp(&p->ni_lsa, &lsa, lsa.ss_len) != 0) 415 continue; 416 if (p->ni_fsa.ss_family == fsa.ss_family && 417 memcmp(&p->ni_fsa, &fsa, fsa.ss_len) == 0) 418 break; 419 } 420 if (p == NULL) { 421 if ((p = malloc(sizeof(*p))) == NULL) { 422 error("Out of memory"); 423 return NULL; 424 } 425 TAILQ_INSERT_HEAD(&netcb, p, chain); 426 p->ni_line = -1; 427 memcpy(&p->ni_lsa, &lsa, lsa.ss_len); 428 memcpy(&p->ni_fsa, &fsa, fsa.ss_len); 429 p->ni_proto = strdup(proto); 430 p->ni_flags = NIF_LACHG|NIF_FACHG; 431 } 432 p->ni_state = state; 433 p->ni_seen = 1; 434 return p; 435 } 436 437 /* column locations */ 438 #define LADDR 0 439 #define FADDR LADDR+23 440 #define PROTO FADDR+23 441 #define RCVCC PROTO+6 442 #define SNDCC RCVCC+7 443 #define STATE SNDCC+7 444 445 446 void 447 labelnetstat(void) 448 { 449 if (use_kvm && namelist[X_TCB].n_type == 0) 450 return; 451 wmove(wnd, 0, 0); wclrtobot(wnd); 452 mvwaddstr(wnd, 0, LADDR, "Local Address"); 453 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 454 mvwaddstr(wnd, 0, PROTO, "Proto"); 455 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 456 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 457 mvwaddstr(wnd, 0, STATE, "(state)"); 458 } 459 460 void 461 shownetstat(void) 462 { 463 struct netinfo *p, *q; 464 char proto[6]; 465 const char *family = ""; 466 467 /* 468 * First, delete any connections that have gone 469 * away and adjust the position of connections 470 * below to reflect the deleted line. 471 */ 472 p = TAILQ_FIRST(&netcb); 473 while (p != NULL) { 474 if (p->ni_line == -1 || p->ni_seen) { 475 p = TAILQ_NEXT(p, chain); 476 continue; 477 } 478 wmove(wnd, p->ni_line, 0); wdeleteln(wnd); 479 TAILQ_FOREACH(q, &netcb, chain) 480 if (q != p && q->ni_line > p->ni_line) { 481 q->ni_line--; 482 /* this shouldn't be necessary */ 483 q->ni_flags |= NIF_LACHG|NIF_FACHG; 484 } 485 lastrow--; 486 q = TAILQ_NEXT(p, chain); 487 TAILQ_REMOVE(&netcb, p, chain); 488 free(p); 489 p = q; 490 } 491 /* 492 * Update existing connections and add new ones. 493 */ 494 TAILQ_FOREACH(p, &netcb, chain) { 495 if (p->ni_line == -1) { 496 /* 497 * Add a new entry if possible. 498 */ 499 if (lastrow > YMAX(wnd)) 500 continue; 501 p->ni_line = lastrow++; 502 p->ni_flags |= NIF_LACHG|NIF_FACHG; 503 } 504 if (p->ni_flags & NIF_LACHG) { 505 wmove(wnd, p->ni_line, LADDR); 506 inetprint((struct sockaddr *)&p->ni_lsa, p->ni_proto); 507 p->ni_flags &= ~NIF_LACHG; 508 } 509 if (p->ni_flags & NIF_FACHG) { 510 wmove(wnd, p->ni_line, FADDR); 511 inetprint((struct sockaddr *)&p->ni_fsa, p->ni_proto); 512 p->ni_flags &= ~NIF_FACHG; 513 } 514 #ifdef INET6 515 family = (p->ni_lsa.ss_family == AF_INET) ? "4" : "6"; 516 #endif 517 snprintf(proto, sizeof(proto), "%s%s", p->ni_proto, family); 518 mvwaddstr(wnd, p->ni_line, PROTO, proto); 519 mvwprintw(wnd, p->ni_line, RCVCC, "%6u", p->ni_rcvcc); 520 mvwprintw(wnd, p->ni_line, SNDCC, "%6u", p->ni_sndcc); 521 if (streq(p->ni_proto, "tcp")) { 522 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES) 523 mvwprintw(wnd, p->ni_line, STATE, "%d", 524 p->ni_state); 525 else 526 mvwaddstr(wnd, p->ni_line, STATE, 527 tcpstates[p->ni_state]); 528 } 529 wclrtoeol(wnd); 530 } 531 if (lastrow < YMAX(wnd)) { 532 wmove(wnd, lastrow, 0); wclrtobot(wnd); 533 wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ 534 } 535 } 536 537 /* 538 * Pretty print an Internet address (net address + port). 539 * If the nflag was specified, use numbers instead of names. 540 */ 541 static void 542 inetprint(struct sockaddr *sa, const char *proto) 543 { 544 struct servent *sp = 0; 545 char line[80], *cp; 546 int port; 547 548 switch (sa->sa_family) { 549 case AF_INET: 550 port = ((struct sockaddr_in *)sa)->sin_port; 551 break; 552 #ifdef INET6 553 case AF_INET6: 554 port = ((struct sockaddr_in6 *)sa)->sin6_port; 555 break; 556 #endif 557 default: 558 port = 0; 559 break; 560 } 561 snprintf(line, sizeof(line), "%.*s.", 16, inetname(sa)); 562 cp = index(line, '\0'); 563 if (!nflag && port) 564 sp = getservbyport(port, proto); 565 if (sp || port == 0) 566 snprintf(cp, sizeof(line) - (cp - line), "%.8s", 567 sp ? sp->s_name : "*"); 568 else 569 snprintf(cp, sizeof(line) - (cp - line), "%d", 570 ntohs((u_short)port)); 571 /* pad to full column to clear any garbage */ 572 cp = index(line, '\0'); 573 while (cp - line < 22) 574 *cp++ = ' '; 575 line[22] = '\0'; 576 waddstr(wnd, line); 577 } 578 579 /* 580 * Construct an Internet address representation. 581 * If the nflag has been supplied, give 582 * numeric value, otherwise try for symbolic name. 583 */ 584 static char * 585 inetname(struct sockaddr *sa) 586 { 587 char *cp = 0; 588 static char line[NI_MAXHOST]; 589 struct hostent *hp; 590 struct netent *np; 591 struct in_addr in; 592 593 #ifdef INET6 594 if (sa->sa_family == AF_INET6) { 595 if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr, 596 &in6addr_any, sizeof(in6addr_any)) == 0) 597 strcpy(line, "*"); 598 else 599 getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, 600 nflag ? NI_NUMERICHOST : 0); 601 return (line); 602 } 603 #endif 604 605 in = ((struct sockaddr_in *)sa)->sin_addr; 606 if (!nflag && in.s_addr != INADDR_ANY) { 607 int net = inet_netof(in); 608 int lna = inet_lnaof(in); 609 610 if (lna == INADDR_ANY) { 611 np = getnetbyaddr(net, AF_INET); 612 if (np) 613 cp = np->n_name; 614 } 615 if (cp == 0) { 616 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 617 if (hp) 618 cp = hp->h_name; 619 } 620 } 621 if (in.s_addr == INADDR_ANY) 622 strcpy(line, "*"); 623 else if (cp) 624 snprintf(line, sizeof(line), "%s", cp); 625 else { 626 in.s_addr = ntohl(in.s_addr); 627 #define C(x) ((x) & 0xff) 628 snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24), 629 C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); 630 } 631 return (line); 632 } 633 634 int 635 cmdnetstat(const char *cmd, const char *args) 636 { 637 if (prefix(cmd, "all")) { 638 aflag = !aflag; 639 goto fixup; 640 } 641 if (prefix(cmd, "numbers") || prefix(cmd, "names")) { 642 struct netinfo *p; 643 int new; 644 645 new = prefix(cmd, "numbers"); 646 if (new == nflag) 647 return (1); 648 TAILQ_FOREACH(p, &netcb, chain) { 649 if (p->ni_line == -1) 650 continue; 651 p->ni_flags |= NIF_LACHG|NIF_FACHG; 652 } 653 nflag = new; 654 goto redisplay; 655 } 656 if (!netcmd(cmd, args)) 657 return (0); 658 fixup: 659 fetchnetstat(); 660 redisplay: 661 shownetstat(); 662 refresh(); 663 return (1); 664 } 665