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