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