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