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