1 /* $OpenBSD: netstat.c,v 1.13 2000/05/24 13:17:08 itojun 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.13 2000/05/24 13:17:08 itojun 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 <nlist.h> 77 #include <paths.h> 78 #include "systat.h" 79 #include "extern.h" 80 81 static void enter __P((struct inpcb *, struct socket *, int, char *)); 82 static const char *inetname __P((struct in_addr)); 83 static void inetprint __P((struct in_addr *, int, char *)); 84 #ifdef INET6 85 static const char *inet6name __P((struct in6_addr *)); 86 static void inet6print __P((struct in6_addr *, int, char *)); 87 #endif 88 89 #define streq(a,b) (strcmp(a,b)==0) 90 #define YMAX(w) ((w)->_maxy-1) 91 92 WINDOW * 93 opennetstat() 94 { 95 sethostent(1); 96 setnetent(1); 97 return (subwin(stdscr, LINES-5-1, 0, 5, 0)); 98 } 99 100 struct netinfo { 101 struct netinfo *nif_forw, *nif_prev; 102 int nif_family; 103 short nif_line; /* line on screen */ 104 short nif_seen; /* 0 when not present in list */ 105 short nif_flags; 106 #define NIF_LACHG 0x1 /* local address changed */ 107 #define NIF_FACHG 0x2 /* foreign address changed */ 108 short nif_state; /* tcp state */ 109 char *nif_proto; /* protocol */ 110 struct in_addr nif_laddr; /* local address */ 111 #ifdef INET6 112 struct in6_addr nif_laddr6; /* local address */ 113 #endif 114 long nif_lport; /* local port */ 115 struct in_addr nif_faddr; /* foreign address */ 116 #ifdef INET6 117 struct in6_addr nif_faddr6; /* foreign address */ 118 #endif 119 long nif_fport; /* foreign port */ 120 long nif_rcvcc; /* rcv buffer character count */ 121 long nif_sndcc; /* snd buffer character count */ 122 }; 123 124 static struct { 125 struct netinfo *nif_forw, *nif_prev; 126 } netcb; 127 128 static int aflag = 0; 129 static int nflag = 0; 130 static int lastrow = 1; 131 132 void 133 closenetstat(w) 134 WINDOW *w; 135 { 136 register struct netinfo *p; 137 138 endhostent(); 139 endnetent(); 140 p = (struct netinfo *)netcb.nif_forw; 141 while (p != (struct netinfo *)&netcb) { 142 if (p->nif_line != -1) 143 lastrow--; 144 p->nif_line = -1; 145 p = p->nif_forw; 146 } 147 if (w != NULL) { 148 wclear(w); 149 wrefresh(w); 150 delwin(w); 151 } 152 } 153 154 static struct nlist namelist[] = { 155 #define X_TCBTABLE 0 156 { "_tcbtable" }, 157 #define X_UDBTABLE 1 158 { "_udbtable" }, 159 { "" }, 160 }; 161 162 int 163 initnetstat() 164 { 165 if (kvm_nlist(kd, namelist)) { 166 nlisterr(namelist); 167 return(0); 168 } 169 if (namelist[X_TCBTABLE].n_value == 0) { 170 error("No symbols in namelist"); 171 return(0); 172 } 173 netcb.nif_forw = netcb.nif_prev = (struct netinfo *)&netcb; 174 protos = TCP|UDP; 175 return(1); 176 } 177 178 void 179 fetchnetstat() 180 { 181 struct inpcbtable pcbtable; 182 register struct inpcb *head, *prev, *next; 183 register struct netinfo *p; 184 struct inpcb inpcb; 185 struct socket sockb; 186 struct tcpcb tcpcb; 187 void *off; 188 int istcp; 189 190 if (namelist[X_TCBTABLE].n_value == 0) 191 return; 192 for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw) 193 p->nif_seen = 0; 194 if (protos&TCP) { 195 off = NPTR(X_TCBTABLE); 196 istcp = 1; 197 } 198 else if (protos&UDP) { 199 off = NPTR(X_UDBTABLE); 200 istcp = 0; 201 } 202 else { 203 error("No protocols to display"); 204 return; 205 } 206 again: 207 KREAD(off, &pcbtable, sizeof (struct inpcbtable)); 208 prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue; 209 next = pcbtable.inpt_queue.cqh_first; 210 while (next != head) { 211 KREAD(next, &inpcb, sizeof (inpcb)); 212 if (inpcb.inp_queue.cqe_prev != prev) { 213 printf("prev = %x, head = %x, next = %x, inpcb...prev = %x\n", prev, head, next, inpcb.inp_queue.cqe_prev); 214 p = netcb.nif_forw; 215 for (; p != (struct netinfo *)&netcb; p = p->nif_forw) 216 p->nif_seen = 1; 217 error("Kernel state in transition"); 218 return; 219 } 220 prev = next; 221 next = inpcb.inp_queue.cqe_next; 222 223 #ifndef INET6 224 if (inpcb.inp_flags & INP_IPV6) 225 continue; 226 #endif 227 228 if (!aflag) { 229 if (!(inpcb.inp_flags & INP_IPV6) 230 && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 231 continue; 232 #ifdef INET6 233 if ((inpcb.inp_flags & INP_IPV6) 234 && IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6)) 235 continue; 236 #endif 237 } 238 if (nhosts && !checkhost(&inpcb)) 239 continue; 240 if (nports && !checkport(&inpcb)) 241 continue; 242 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); 243 if (istcp) { 244 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 245 enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); 246 } else 247 enter(&inpcb, &sockb, 0, "udp"); 248 } 249 if (istcp && (protos&UDP)) { 250 istcp = 0; 251 off = NPTR(X_UDBTABLE); 252 goto again; 253 } 254 } 255 256 static void 257 enter(inp, so, state, proto) 258 register struct inpcb *inp; 259 register struct socket *so; 260 int state; 261 char *proto; 262 { 263 register struct netinfo *p; 264 265 /* 266 * Only take exact matches, any sockets with 267 * previously unbound addresses will be deleted 268 * below in the display routine because they 269 * will appear as ``not seen'' in the kernel 270 * data structures. 271 */ 272 for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw) { 273 #ifdef INET6 274 if (p->nif_family == AF_INET && (inp->inp_flags & INP_IPV6)) 275 continue; 276 if (p->nif_family == AF_INET6 && !(inp->inp_flags & INP_IPV6)) 277 continue; 278 #endif 279 if (!streq(proto, p->nif_proto)) 280 continue; 281 if (p->nif_family == AF_INET) { 282 if (p->nif_lport != inp->inp_lport || 283 p->nif_laddr.s_addr != inp->inp_laddr.s_addr) 284 continue; 285 if (p->nif_faddr.s_addr == inp->inp_faddr.s_addr && 286 p->nif_fport == inp->inp_fport) 287 break; 288 289 } 290 #ifdef INET6 291 else if (p->nif_family == AF_INET6) { 292 if (p->nif_lport != inp->inp_lport || 293 !IN6_ARE_ADDR_EQUAL(&p->nif_laddr6, &inp->inp_laddr6)) 294 continue; 295 if (IN6_ARE_ADDR_EQUAL(&p->nif_faddr6, &inp->inp_faddr6) && 296 p->nif_fport == inp->inp_fport) 297 break; 298 } 299 #endif 300 else 301 continue; 302 } 303 if (p == (struct netinfo *)&netcb) { 304 if ((p = malloc(sizeof(*p))) == NULL) { 305 error("Out of memory"); 306 return; 307 } 308 p->nif_prev = (struct netinfo *)&netcb; 309 p->nif_forw = netcb.nif_forw; 310 netcb.nif_forw->nif_prev = p; 311 netcb.nif_forw = p; 312 p->nif_line = -1; 313 p->nif_lport = inp->inp_lport; 314 p->nif_fport = inp->inp_fport; 315 p->nif_proto = proto; 316 p->nif_flags = NIF_LACHG|NIF_FACHG; 317 #ifdef INET6 318 if (inp->inp_flags & INP_IPV6) { 319 p->nif_laddr6 = inp->inp_laddr6; 320 p->nif_faddr6 = inp->inp_faddr6; 321 p->nif_family = AF_INET6; 322 } 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 register 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 wclrtoeol(wnd); 451 } 452 if (lastrow < YMAX(wnd)) { 453 wmove(wnd, lastrow, 0); wclrtobot(wnd); 454 wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ 455 } 456 } 457 458 /* 459 * Pretty print an Internet address (net address + port). 460 * If the nflag was specified, use numbers instead of names. 461 */ 462 static void 463 inetprint(in, port, proto) 464 register struct in_addr *in; 465 int port; 466 char *proto; 467 { 468 struct servent *sp = 0; 469 char line[80], *cp; 470 471 snprintf(line, sizeof line, "%.*s.", 16, inetname(*in)); 472 cp = strchr(line, '\0'); 473 if (!nflag && port) 474 sp = getservbyport(port, proto); 475 if (sp || port == 0) 476 snprintf(cp, sizeof line - strlen(cp), "%.8s", 477 sp ? sp->s_name : "*"); 478 else 479 snprintf(cp, sizeof line - strlen(cp), "%d", 480 ntohs((u_short)port)); 481 /* pad to full column to clear any garbage */ 482 cp = strchr(line, '\0'); 483 while (cp - line < 22 && cp - line < sizeof line-1) 484 *cp++ = ' '; 485 *cp = '\0'; 486 waddstr(wnd, line); 487 } 488 489 #ifdef INET6 490 static void 491 inet6print(in6, port, proto) 492 register struct in6_addr *in6; 493 int port; 494 char *proto; 495 { 496 struct servent *sp = 0; 497 char line[80], *cp; 498 499 snprintf(line, sizeof line, "%.*s.", 16, inet6name(in6)); 500 cp = strchr(line, '\0'); 501 if (!nflag && port) 502 sp = getservbyport(port, proto); 503 if (sp || port == 0) 504 snprintf(cp, sizeof line - strlen(cp), "%.8s", 505 sp ? sp->s_name : "*"); 506 else 507 snprintf(cp, sizeof line - strlen(cp), "%d", 508 ntohs((u_short)port)); 509 /* pad to full column to clear any garbage */ 510 cp = strchr(line, '\0'); 511 while (cp - line < 22 && cp - line < sizeof line-1) 512 *cp++ = ' '; 513 *cp = '\0'; 514 waddstr(wnd, line); 515 } 516 #endif 517 518 /* 519 * Construct an Internet address representation. 520 * If the nflag has been supplied, give 521 * numeric value, otherwise try for symbolic name. 522 */ 523 static const char * 524 inetname(in) 525 struct in_addr in; 526 { 527 char *cp = 0; 528 static char line[50]; 529 struct hostent *hp; 530 struct netent *np; 531 532 if (!nflag && in.s_addr != INADDR_ANY) { 533 int net = inet_netof(in); 534 int lna = inet_lnaof(in); 535 536 if (lna == INADDR_ANY) { 537 np = getnetbyaddr(net, AF_INET); 538 if (np) 539 cp = np->n_name; 540 } 541 if (cp == 0) { 542 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 543 if (hp) 544 cp = hp->h_name; 545 } 546 } 547 if (in.s_addr == INADDR_ANY) { 548 strncpy(line, "*", sizeof line-1); 549 line[sizeof line-1] = '\0'; 550 } else if (cp) { 551 strncpy(line, cp, sizeof line-1); 552 line[sizeof line-1] = '\0'; 553 } else { 554 in.s_addr = ntohl(in.s_addr); 555 #define C(x) ((x) & 0xff) 556 snprintf(line, sizeof line, "%u.%u.%u.%u", C(in.s_addr >> 24), 557 C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); 558 } 559 return (line); 560 } 561 562 #ifdef INET6 563 static const char * 564 inet6name(in6) 565 struct in6_addr *in6; 566 { 567 static char line[NI_MAXHOST]; 568 struct sockaddr_in6 sin6; 569 int flags; 570 571 if (nflag) 572 flags = NI_NUMERICHOST; 573 else 574 flags = 0; 575 if (IN6_IS_ADDR_UNSPECIFIED(in6)) 576 return "*"; 577 memset(&sin6, 0, sizeof(sin6)); 578 sin6.sin6_family = AF_INET6; 579 sin6.sin6_len = sizeof(struct sockaddr_in6); 580 sin6.sin6_addr = *in6; 581 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 582 line, sizeof(line), NULL, 0, flags) == 0) 583 return line; 584 return "?"; 585 } 586 #endif 587 588 int 589 cmdnetstat(cmd, args) 590 char *cmd, *args; 591 { 592 register struct netinfo *p; 593 594 if (prefix(cmd, "all")) { 595 aflag = !aflag; 596 goto fixup; 597 } 598 if (prefix(cmd, "numbers") || prefix(cmd, "names")) { 599 int new; 600 601 new = prefix(cmd, "numbers"); 602 if (new == nflag) 603 return (1); 604 p = netcb.nif_forw; 605 for (; p != (struct netinfo *)&netcb; p = p->nif_forw) { 606 if (p->nif_line == -1) 607 continue; 608 p->nif_flags |= NIF_LACHG|NIF_FACHG; 609 } 610 nflag = new; 611 wclear(wnd); 612 labelnetstat(); 613 goto redisplay; 614 } 615 if (!netcmd(cmd, args)) 616 return (0); 617 fixup: 618 fetchnetstat(); 619 redisplay: 620 shownetstat(); 621 refresh(); 622 return (1); 623 } 624