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