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