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