1 /* $OpenBSD: netstat.c,v 1.16 2001/07/28 05:36:18 pvalchev 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.16 2001/07/28 05:36:18 pvalchev 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 register 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 register struct inpcb *head, *prev, *next; 186 register 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 } 201 else if (protos&UDP) { 202 off = NPTR(X_UDBTABLE); 203 istcp = 0; 204 } 205 else { 206 error("No protocols to display"); 207 return; 208 } 209 again: 210 KREAD(off, &pcbtable, sizeof (struct inpcbtable)); 211 prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue; 212 next = pcbtable.inpt_queue.cqh_first; 213 while (next != head) { 214 KREAD(next, &inpcb, sizeof (inpcb)); 215 if (inpcb.inp_queue.cqe_prev != prev) { 216 printf("prev = %p, head = %p, next = %p, inpcb...prev = %p\n", prev, head, next, inpcb.inp_queue.cqe_prev); 217 p = netcb.nif_forw; 218 for (; p != (struct netinfo *)&netcb; p = p->nif_forw) 219 p->nif_seen = 1; 220 error("Kernel state in transition"); 221 return; 222 } 223 prev = next; 224 next = inpcb.inp_queue.cqe_next; 225 226 #ifndef INET6 227 if (inpcb.inp_flags & INP_IPV6) 228 continue; 229 #endif 230 231 if (!aflag) { 232 if (!(inpcb.inp_flags & INP_IPV6) 233 && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 234 continue; 235 #ifdef INET6 236 if ((inpcb.inp_flags & INP_IPV6) 237 && IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6)) 238 continue; 239 #endif 240 } 241 if (nhosts && !checkhost(&inpcb)) 242 continue; 243 if (nports && !checkport(&inpcb)) 244 continue; 245 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); 246 if (istcp) { 247 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 248 enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); 249 } else 250 enter(&inpcb, &sockb, 0, "udp"); 251 } 252 if (istcp && (protos&UDP)) { 253 istcp = 0; 254 off = NPTR(X_UDBTABLE); 255 goto again; 256 } 257 } 258 259 static void 260 enter(inp, so, state, proto) 261 register struct inpcb *inp; 262 register struct socket *so; 263 int state; 264 char *proto; 265 { 266 register struct netinfo *p; 267 268 /* 269 * Only take exact matches, any sockets with 270 * previously unbound addresses will be deleted 271 * below in the display routine because they 272 * will appear as ``not seen'' in the kernel 273 * data structures. 274 */ 275 for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw) { 276 #ifdef INET6 277 if (p->nif_family == AF_INET && (inp->inp_flags & INP_IPV6)) 278 continue; 279 if (p->nif_family == AF_INET6 && !(inp->inp_flags & INP_IPV6)) 280 continue; 281 #endif 282 if (!streq(proto, p->nif_proto)) 283 continue; 284 if (p->nif_family == AF_INET) { 285 if (p->nif_lport != inp->inp_lport || 286 p->nif_laddr.s_addr != inp->inp_laddr.s_addr) 287 continue; 288 if (p->nif_faddr.s_addr == inp->inp_faddr.s_addr && 289 p->nif_fport == inp->inp_fport) 290 break; 291 292 } 293 #ifdef INET6 294 else if (p->nif_family == AF_INET6) { 295 if (p->nif_lport != inp->inp_lport || 296 !IN6_ARE_ADDR_EQUAL(&p->nif_laddr6, &inp->inp_laddr6)) 297 continue; 298 if (IN6_ARE_ADDR_EQUAL(&p->nif_faddr6, &inp->inp_faddr6) && 299 p->nif_fport == inp->inp_fport) 300 break; 301 } 302 #endif 303 else 304 continue; 305 } 306 if (p == (struct netinfo *)&netcb) { 307 if ((p = malloc(sizeof(*p))) == NULL) { 308 error("Out of memory"); 309 return; 310 } 311 p->nif_prev = (struct netinfo *)&netcb; 312 p->nif_forw = netcb.nif_forw; 313 netcb.nif_forw->nif_prev = p; 314 netcb.nif_forw = p; 315 p->nif_line = -1; 316 p->nif_lport = inp->inp_lport; 317 p->nif_fport = inp->inp_fport; 318 p->nif_proto = proto; 319 p->nif_flags = NIF_LACHG|NIF_FACHG; 320 #ifdef INET6 321 if (inp->inp_flags & INP_IPV6) { 322 p->nif_laddr6 = inp->inp_laddr6; 323 p->nif_faddr6 = inp->inp_faddr6; 324 p->nif_family = AF_INET6; 325 } 326 else 327 #endif 328 { 329 p->nif_laddr = inp->inp_laddr; 330 p->nif_faddr = inp->inp_faddr; 331 p->nif_family = AF_INET; 332 } 333 } 334 p->nif_rcvcc = so->so_rcv.sb_cc; 335 p->nif_sndcc = so->so_snd.sb_cc; 336 p->nif_state = state; 337 p->nif_seen = 1; 338 } 339 340 /* column locations */ 341 #define LADDR 0 342 #define FADDR LADDR+23 343 #define PROTO FADDR+23 344 #define RCVCC PROTO+6 345 #define SNDCC RCVCC+7 346 #define STATE SNDCC+7 347 348 349 void 350 labelnetstat() 351 { 352 if (namelist[X_TCBTABLE].n_type == 0) 353 return; 354 wmove(wnd, 0, 0); wclrtobot(wnd); 355 mvwaddstr(wnd, 0, LADDR, "Local Address"); 356 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 357 mvwaddstr(wnd, 0, PROTO, "Proto"); 358 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 359 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 360 mvwaddstr(wnd, 0, STATE, "(state)"); 361 } 362 363 void 364 shownetstat() 365 { 366 register struct netinfo *p, *q; 367 368 /* 369 * First, delete any connections that have gone 370 * away and adjust the position of connections 371 * below to reflect the deleted line. 372 */ 373 p = netcb.nif_forw; 374 while (p != (struct netinfo *)&netcb) { 375 if (p->nif_line == -1 || p->nif_seen) { 376 p = p->nif_forw; 377 continue; 378 } 379 wmove(wnd, p->nif_line, 0); wdeleteln(wnd); 380 q = netcb.nif_forw; 381 for (; q != (struct netinfo *)&netcb; q = q->nif_forw) 382 if (q != p && q->nif_line > p->nif_line) { 383 q->nif_line--; 384 /* this shouldn't be necessary */ 385 q->nif_flags |= NIF_LACHG|NIF_FACHG; 386 } 387 lastrow--; 388 q = p->nif_forw; 389 p->nif_prev->nif_forw = p->nif_forw; 390 p->nif_forw->nif_prev = p->nif_prev; 391 free(p); 392 p = q; 393 } 394 /* 395 * Update existing connections and add new ones. 396 */ 397 for (p = netcb.nif_forw; p != (struct netinfo *)&netcb; p = p->nif_forw) { 398 if (p->nif_line == -1) { 399 /* 400 * Add a new entry if possible. 401 */ 402 if (lastrow > YMAX(wnd)) 403 continue; 404 p->nif_line = lastrow++; 405 p->nif_flags |= NIF_LACHG|NIF_FACHG; 406 } 407 if (p->nif_flags & NIF_LACHG) { 408 wmove(wnd, p->nif_line, LADDR); 409 switch (p->nif_family) { 410 case AF_INET: 411 inetprint(&p->nif_laddr, p->nif_lport, 412 p->nif_proto); 413 break; 414 #ifdef INET6 415 case AF_INET6: 416 inet6print(&p->nif_laddr6, p->nif_lport, 417 p->nif_proto); 418 break; 419 #endif 420 } 421 p->nif_flags &= ~NIF_LACHG; 422 } 423 if (p->nif_flags & NIF_FACHG) { 424 wmove(wnd, p->nif_line, FADDR); 425 switch (p->nif_family) { 426 case AF_INET: 427 inetprint(&p->nif_faddr, p->nif_fport, 428 p->nif_proto); 429 break; 430 #ifdef INET6 431 case AF_INET6: 432 inet6print(&p->nif_faddr6, p->nif_fport, 433 p->nif_proto); 434 break; 435 #endif 436 } 437 p->nif_flags &= ~NIF_FACHG; 438 } 439 mvwaddstr(wnd, p->nif_line, PROTO, p->nif_proto); 440 #ifdef INET6 441 if (p->nif_family == AF_INET6) 442 waddstr(wnd, "6"); 443 #endif 444 mvwprintw(wnd, p->nif_line, RCVCC, "%6d", p->nif_rcvcc); 445 mvwprintw(wnd, p->nif_line, SNDCC, "%6d", p->nif_sndcc); 446 if (streq(p->nif_proto, "tcp")) 447 if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES) 448 mvwprintw(wnd, p->nif_line, STATE, "%d", 449 p->nif_state); 450 else 451 mvwaddstr(wnd, p->nif_line, STATE, 452 tcpstates[p->nif_state]); 453 wclrtoeol(wnd); 454 } 455 if (lastrow < YMAX(wnd)) { 456 wmove(wnd, lastrow, 0); wclrtobot(wnd); 457 wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ 458 } 459 } 460 461 /* 462 * Pretty print an Internet address (net address + port). 463 * If the nflag was specified, use numbers instead of names. 464 */ 465 static void 466 inetprint(in, port, proto) 467 register struct in_addr *in; 468 int port; 469 char *proto; 470 { 471 struct servent *sp = 0; 472 char line[80], *cp; 473 474 snprintf(line, sizeof line, "%.*s.", 16, inetname(*in)); 475 cp = strchr(line, '\0'); 476 if (!nflag && port) 477 sp = getservbyport(port, proto); 478 if (sp || port == 0) 479 snprintf(cp, sizeof line - strlen(cp), "%.8s", 480 sp ? sp->s_name : "*"); 481 else 482 snprintf(cp, sizeof line - strlen(cp), "%d", 483 ntohs((u_short)port)); 484 /* pad to full column to clear any garbage */ 485 cp = strchr(line, '\0'); 486 while (cp - line < 22 && cp - line < sizeof line-1) 487 *cp++ = ' '; 488 *cp = '\0'; 489 waddstr(wnd, line); 490 } 491 492 #ifdef INET6 493 static void 494 inet6print(in6, port, proto) 495 register struct in6_addr *in6; 496 int port; 497 char *proto; 498 { 499 struct servent *sp = 0; 500 char line[80], *cp; 501 502 snprintf(line, sizeof line, "%.*s.", 16, inet6name(in6)); 503 cp = strchr(line, '\0'); 504 if (!nflag && port) 505 sp = getservbyport(port, proto); 506 if (sp || port == 0) 507 snprintf(cp, sizeof line - strlen(cp), "%.8s", 508 sp ? sp->s_name : "*"); 509 else 510 snprintf(cp, sizeof line - strlen(cp), "%d", 511 ntohs((u_short)port)); 512 /* pad to full column to clear any garbage */ 513 cp = strchr(line, '\0'); 514 while (cp - line < 22 && cp - line < sizeof line-1) 515 *cp++ = ' '; 516 *cp = '\0'; 517 waddstr(wnd, line); 518 } 519 #endif 520 521 /* 522 * Construct an Internet address representation. 523 * If the nflag has been supplied, give 524 * numeric value, otherwise try for symbolic name. 525 */ 526 static const char * 527 inetname(in) 528 struct in_addr in; 529 { 530 char *cp = 0; 531 static char line[50]; 532 struct hostent *hp; 533 struct netent *np; 534 535 if (!nflag && in.s_addr != INADDR_ANY) { 536 int net = inet_netof(in); 537 int lna = inet_lnaof(in); 538 539 if (lna == INADDR_ANY) { 540 np = getnetbyaddr(net, AF_INET); 541 if (np) 542 cp = np->n_name; 543 } 544 if (cp == 0) { 545 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 546 if (hp) 547 cp = hp->h_name; 548 } 549 } 550 if (in.s_addr == INADDR_ANY) { 551 strlcpy(line, "*", sizeof line); 552 } else if (cp) { 553 strlcpy(line, cp, sizeof line); 554 } else { 555 in.s_addr = ntohl(in.s_addr); 556 #define C(x) ((x) & 0xff) 557 snprintf(line, sizeof line, "%u.%u.%u.%u", C(in.s_addr >> 24), 558 C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); 559 } 560 return (line); 561 } 562 563 #ifdef INET6 564 static const char * 565 inet6name(in6) 566 struct in6_addr *in6; 567 { 568 static char line[NI_MAXHOST]; 569 struct sockaddr_in6 sin6; 570 int flags; 571 572 if (nflag) 573 flags = NI_NUMERICHOST; 574 else 575 flags = 0; 576 if (IN6_IS_ADDR_UNSPECIFIED(in6)) 577 return "*"; 578 memset(&sin6, 0, sizeof(sin6)); 579 sin6.sin6_family = AF_INET6; 580 sin6.sin6_len = sizeof(struct sockaddr_in6); 581 sin6.sin6_addr = *in6; 582 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 583 line, sizeof(line), NULL, 0, flags) == 0) 584 return line; 585 return "?"; 586 } 587 #endif 588 589 int 590 cmdnetstat(cmd, args) 591 char *cmd, *args; 592 { 593 register struct netinfo *p; 594 595 if (prefix(cmd, "all")) { 596 aflag = !aflag; 597 goto fixup; 598 } 599 if (prefix(cmd, "numbers") || prefix(cmd, "names")) { 600 int new; 601 602 new = prefix(cmd, "numbers"); 603 if (new == nflag) 604 return (1); 605 p = netcb.nif_forw; 606 for (; p != (struct netinfo *)&netcb; p = p->nif_forw) { 607 if (p->nif_line == -1) 608 continue; 609 p->nif_flags |= NIF_LACHG|NIF_FACHG; 610 } 611 nflag = new; 612 wclear(wnd); 613 labelnetstat(); 614 goto redisplay; 615 } 616 if (!netcmd(cmd, args)) 617 return (0); 618 fixup: 619 fetchnetstat(); 620 redisplay: 621 shownetstat(); 622 refresh(); 623 return (1); 624 } 625