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