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