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$"); 37 38 #ifdef lint 39 static 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 <paths.h> 78 #include <stdlib.h> 79 #include <string.h> 80 81 #include "systat.h" 82 #include "extern.h" 83 84 static struct netinfo *enter(struct inpcb *, int, const char *); 85 static void enter_kvm(struct inpcb *, struct socket *, int, const char *); 86 static void enter_sysctl(struct inpcb *, struct xsocket *, int, const char *); 87 static void fetchnetstat_kvm(void); 88 static void fetchnetstat_sysctl(void); 89 static char *inetname(struct in_addr); 90 static void inetprint(struct in_addr *, int, const char *); 91 92 #define streq(a,b) (strcmp(a,b)==0) 93 #define YMAX(w) ((w)->_maxy-1) 94 95 WINDOW * 96 opennetstat() 97 { 98 sethostent(1); 99 setnetent(1); 100 return (subwin(stdscr, LINES-5-1, 0, 5, 0)); 101 } 102 103 struct netinfo { 104 TAILQ_ENTRY(netinfo) chain; 105 short ni_line; /* line on screen */ 106 short ni_seen; /* 0 when not present in list */ 107 short ni_flags; 108 #define NIF_LACHG 0x1 /* local address changed */ 109 #define NIF_FACHG 0x2 /* foreign address changed */ 110 short ni_state; /* tcp state */ 111 const char *ni_proto; /* protocol */ 112 struct in_addr ni_laddr; /* local address */ 113 long ni_lport; /* local port */ 114 struct in_addr ni_faddr; /* foreign address */ 115 long ni_fport; /* foreign port */ 116 u_int ni_rcvcc; /* rcv buffer character count */ 117 u_int ni_sndcc; /* snd buffer character count */ 118 }; 119 120 TAILQ_HEAD(netinfohead, netinfo) netcb = TAILQ_HEAD_INITIALIZER(netcb); 121 122 static int aflag = 0; 123 static int nflag = 0; 124 static int lastrow = 1; 125 126 void 127 closenetstat(w) 128 WINDOW *w; 129 { 130 struct netinfo *p; 131 132 endhostent(); 133 endnetent(); 134 TAILQ_FOREACH(p, &netcb, chain) { 135 if (p->ni_line != -1) 136 lastrow--; 137 p->ni_line = -1; 138 } 139 if (w != NULL) { 140 wclear(w); 141 wrefresh(w); 142 delwin(w); 143 } 144 } 145 146 static const char *miblist[] = { 147 "net.inet.tcp.pcblist", 148 "net.inet.udp.pcblist" 149 }; 150 151 struct nlist namelist[] = { 152 #define X_TCB 0 153 { "tcb" }, 154 #define X_UDB 1 155 { "udb" }, 156 { "" }, 157 }; 158 159 int 160 initnetstat() 161 { 162 protos = TCP|UDP; 163 return(1); 164 } 165 166 void 167 fetchnetstat() 168 { 169 if (use_kvm) 170 fetchnetstat_kvm(); 171 else 172 fetchnetstat_sysctl(); 173 } 174 175 static void 176 fetchnetstat_kvm() 177 { 178 struct inpcb *next; 179 struct netinfo *p; 180 struct inpcbhead head; 181 struct inpcb inpcb; 182 struct socket sockb; 183 struct tcpcb tcpcb; 184 void *off; 185 int istcp; 186 187 if (namelist[X_TCB].n_value == 0) 188 return; 189 TAILQ_FOREACH(p, &netcb, chain) 190 p->ni_seen = 0; 191 if (protos&TCP) { 192 off = NPTR(X_TCB); 193 istcp = 1; 194 } 195 else if (protos&UDP) { 196 off = NPTR(X_UDB); 197 istcp = 0; 198 } 199 else { 200 error("No protocols to display"); 201 return; 202 } 203 again: 204 KREAD(off, &head, sizeof (struct inpcbhead)); 205 LIST_FOREACH(next, &head, inp_list) { 206 KREAD(next, &inpcb, sizeof (inpcb)); 207 next = &inpcb; 208 if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 209 continue; 210 if (nhosts && !checkhost(&inpcb)) 211 continue; 212 if (nports && !checkport(&inpcb)) 213 continue; 214 if (istcp) { 215 if (inpcb.inp_vflag & INP_TIMEWAIT) { 216 bzero(&sockb, sizeof(sockb)); 217 enter_kvm(&inpcb, &sockb, TCPS_TIME_WAIT, 218 "tcp"); 219 } else { 220 KREAD(inpcb.inp_socket, &sockb, 221 sizeof (sockb)); 222 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 223 enter_kvm(&inpcb, &sockb, tcpcb.t_state, 224 "tcp"); 225 } 226 } else 227 enter_kvm(&inpcb, &sockb, 0, "udp"); 228 } 229 if (istcp && (protos&UDP)) { 230 istcp = 0; 231 off = NPTR(X_UDB); 232 goto again; 233 } 234 } 235 236 static void 237 fetchnetstat_sysctl() 238 { 239 struct netinfo *p; 240 int idx; 241 struct xinpgen *inpg; 242 char *cur, *end; 243 struct inpcb *inpcb; 244 struct xinpcb *xip; 245 struct xtcpcb *xtp; 246 int plen; 247 size_t lsz; 248 249 TAILQ_FOREACH(p, &netcb, chain) 250 p->ni_seen = 0; 251 if (protos&TCP) { 252 idx = 0; 253 } else if (protos&UDP) { 254 idx = 1; 255 } else { 256 error("No protocols to display"); 257 return; 258 } 259 260 for (;idx < 2; idx++) { 261 if (idx == 1 && !(protos&UDP)) 262 break; 263 inpg = (struct xinpgen *)sysctl_dynread(miblist[idx], &lsz); 264 if (inpg == NULL) { 265 error("sysctl(%s...) failed", miblist[idx]); 266 continue; 267 } 268 /* 269 * We currently do no require a consistent pcb list. 270 * Try to be robust in case of struct size changes 271 */ 272 cur = ((char *)inpg) + inpg->xig_len; 273 /* There is also a trailing struct xinpgen */ 274 end = ((char *)inpg) + lsz - inpg->xig_len; 275 if (end <= cur) { 276 free(inpg); 277 continue; 278 } 279 if (idx == 0) { /* TCP */ 280 xtp = (struct xtcpcb *)cur; 281 plen = xtp->xt_len; 282 } else { 283 xip = (struct xinpcb *)cur; 284 plen = xip->xi_len; 285 } 286 while (cur + plen <= end) { 287 if (idx == 0) { /* TCP */ 288 xtp = (struct xtcpcb *)cur; 289 inpcb = &xtp->xt_inp; 290 } else { 291 xip = (struct xinpcb *)cur; 292 inpcb = &xip->xi_inp; 293 } 294 cur += plen; 295 296 if (!aflag && inet_lnaof(inpcb->inp_laddr) == 297 INADDR_ANY) 298 continue; 299 if (nhosts && !checkhost(inpcb)) 300 continue; 301 if (nports && !checkport(inpcb)) 302 continue; 303 if (idx == 0) /* TCP */ 304 enter_sysctl(inpcb, &xtp->xt_socket, 305 xtp->xt_tp.t_state, "tcp"); 306 else /* UDP */ 307 enter_sysctl(inpcb, &xip->xi_socket, 0, "udp"); 308 } 309 free(inpg); 310 } 311 } 312 313 static void 314 enter_kvm(inp, so, state, proto) 315 struct inpcb *inp; 316 struct socket *so; 317 int state; 318 const char *proto; 319 { 320 struct netinfo *p; 321 322 if ((p = enter(inp, state, proto)) != NULL) { 323 p->ni_rcvcc = so->so_rcv.sb_cc; 324 p->ni_sndcc = so->so_snd.sb_cc; 325 } 326 } 327 328 static void 329 enter_sysctl(inp, so, state, proto) 330 struct inpcb *inp; 331 struct xsocket *so; 332 int state; 333 const char *proto; 334 { 335 struct netinfo *p; 336 337 if ((p = enter(inp, state, proto)) != NULL) { 338 p->ni_rcvcc = so->so_rcv.sb_cc; 339 p->ni_sndcc = so->so_snd.sb_cc; 340 } 341 } 342 343 344 static struct netinfo * 345 enter(inp, state, proto) 346 struct inpcb *inp; 347 int state; 348 const char *proto; 349 { 350 struct netinfo *p; 351 352 /* 353 * Only take exact matches, any sockets with 354 * previously unbound addresses will be deleted 355 * below in the display routine because they 356 * will appear as ``not seen'' in the kernel 357 * data structures. 358 */ 359 TAILQ_FOREACH(p, &netcb, chain) { 360 if (!streq(proto, p->ni_proto)) 361 continue; 362 if (p->ni_lport != inp->inp_lport || 363 p->ni_laddr.s_addr != inp->inp_laddr.s_addr) 364 continue; 365 if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr && 366 p->ni_fport == inp->inp_fport) 367 break; 368 } 369 if (p == NULL) { 370 if ((p = malloc(sizeof(*p))) == NULL) { 371 error("Out of memory"); 372 return NULL; 373 } 374 TAILQ_INSERT_HEAD(&netcb, p, chain); 375 p->ni_line = -1; 376 p->ni_laddr = inp->inp_laddr; 377 p->ni_lport = inp->inp_lport; 378 p->ni_faddr = inp->inp_faddr; 379 p->ni_fport = inp->inp_fport; 380 p->ni_proto = strdup(proto); 381 p->ni_flags = NIF_LACHG|NIF_FACHG; 382 } 383 p->ni_state = state; 384 p->ni_seen = 1; 385 return p; 386 } 387 388 /* column locations */ 389 #define LADDR 0 390 #define FADDR LADDR+23 391 #define PROTO FADDR+23 392 #define RCVCC PROTO+6 393 #define SNDCC RCVCC+7 394 #define STATE SNDCC+7 395 396 397 void 398 labelnetstat() 399 { 400 if (use_kvm && namelist[X_TCB].n_type == 0) 401 return; 402 wmove(wnd, 0, 0); wclrtobot(wnd); 403 mvwaddstr(wnd, 0, LADDR, "Local Address"); 404 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 405 mvwaddstr(wnd, 0, PROTO, "Proto"); 406 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 407 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 408 mvwaddstr(wnd, 0, STATE, "(state)"); 409 } 410 411 void 412 shownetstat() 413 { 414 struct netinfo *p, *q; 415 416 /* 417 * First, delete any connections that have gone 418 * away and adjust the position of connections 419 * below to reflect the deleted line. 420 */ 421 p = TAILQ_FIRST(&netcb); 422 while (p != NULL) { 423 if (p->ni_line == -1 || p->ni_seen) { 424 p = TAILQ_NEXT(p, chain); 425 continue; 426 } 427 wmove(wnd, p->ni_line, 0); wdeleteln(wnd); 428 TAILQ_FOREACH(q, &netcb, chain) 429 if (q != p && q->ni_line > p->ni_line) { 430 q->ni_line--; 431 /* this shouldn't be necessary */ 432 q->ni_flags |= NIF_LACHG|NIF_FACHG; 433 } 434 lastrow--; 435 q = TAILQ_NEXT(p, chain); 436 TAILQ_REMOVE(&netcb, p, chain); 437 free(p); 438 p = q; 439 } 440 /* 441 * Update existing connections and add new ones. 442 */ 443 TAILQ_FOREACH(p, &netcb, chain) { 444 if (p->ni_line == -1) { 445 /* 446 * Add a new entry if possible. 447 */ 448 if (lastrow > YMAX(wnd)) 449 continue; 450 p->ni_line = lastrow++; 451 p->ni_flags |= NIF_LACHG|NIF_FACHG; 452 } 453 if (p->ni_flags & NIF_LACHG) { 454 wmove(wnd, p->ni_line, LADDR); 455 inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto); 456 p->ni_flags &= ~NIF_LACHG; 457 } 458 if (p->ni_flags & NIF_FACHG) { 459 wmove(wnd, p->ni_line, FADDR); 460 inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto); 461 p->ni_flags &= ~NIF_FACHG; 462 } 463 mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto); 464 mvwprintw(wnd, p->ni_line, RCVCC, "%6u", p->ni_rcvcc); 465 mvwprintw(wnd, p->ni_line, SNDCC, "%6u", p->ni_sndcc); 466 if (streq(p->ni_proto, "tcp")) { 467 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES) 468 mvwprintw(wnd, p->ni_line, STATE, "%d", 469 p->ni_state); 470 else 471 mvwaddstr(wnd, p->ni_line, STATE, 472 tcpstates[p->ni_state]); 473 } 474 wclrtoeol(wnd); 475 } 476 if (lastrow < YMAX(wnd)) { 477 wmove(wnd, lastrow, 0); wclrtobot(wnd); 478 wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ 479 } 480 } 481 482 /* 483 * Pretty print an Internet address (net address + port). 484 * If the nflag was specified, use numbers instead of names. 485 */ 486 static void 487 inetprint(in, port, proto) 488 struct in_addr *in; 489 int port; 490 const char *proto; 491 { 492 struct servent *sp = 0; 493 char line[80], *cp; 494 495 snprintf(line, sizeof(line), "%.*s.", 16, inetname(*in)); 496 cp = index(line, '\0'); 497 if (!nflag && port) 498 sp = getservbyport(port, proto); 499 if (sp || port == 0) 500 snprintf(cp, sizeof(line) - (cp - line), "%.8s", 501 sp ? sp->s_name : "*"); 502 else 503 snprintf(cp, sizeof(line) - (cp - line), "%d", 504 ntohs((u_short)port)); 505 /* pad to full column to clear any garbage */ 506 cp = index(line, '\0'); 507 while (cp - line < 22) 508 *cp++ = ' '; 509 line[22] = '\0'; 510 waddstr(wnd, line); 511 } 512 513 /* 514 * Construct an Internet address representation. 515 * If the nflag has been supplied, give 516 * numeric value, otherwise try for symbolic name. 517 */ 518 static char * 519 inetname(in) 520 struct in_addr in; 521 { 522 char *cp = 0; 523 static char line[50]; 524 struct hostent *hp; 525 struct netent *np; 526 527 if (!nflag && in.s_addr != INADDR_ANY) { 528 int net = inet_netof(in); 529 int lna = inet_lnaof(in); 530 531 if (lna == INADDR_ANY) { 532 np = getnetbyaddr(net, AF_INET); 533 if (np) 534 cp = np->n_name; 535 } 536 if (cp == 0) { 537 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 538 if (hp) 539 cp = hp->h_name; 540 } 541 } 542 if (in.s_addr == INADDR_ANY) 543 strcpy(line, "*"); 544 else if (cp) 545 snprintf(line, sizeof(line), "%s", cp); 546 else { 547 in.s_addr = ntohl(in.s_addr); 548 #define C(x) ((x) & 0xff) 549 snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24), 550 C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); 551 } 552 return (line); 553 } 554 555 int 556 cmdnetstat(cmd, args) 557 const char *cmd, *args; 558 { 559 if (prefix(cmd, "all")) { 560 aflag = !aflag; 561 goto fixup; 562 } 563 if (prefix(cmd, "numbers") || prefix(cmd, "names")) { 564 struct netinfo *p; 565 int new; 566 567 new = prefix(cmd, "numbers"); 568 if (new == nflag) 569 return (1); 570 TAILQ_FOREACH(p, &netcb, chain) { 571 if (p->ni_line == -1) 572 continue; 573 p->ni_flags |= NIF_LACHG|NIF_FACHG; 574 } 575 nflag = new; 576 goto redisplay; 577 } 578 if (!netcmd(cmd, args)) 579 return (0); 580 fixup: 581 fetchnetstat(); 582 redisplay: 583 shownetstat(); 584 refresh(); 585 return (1); 586 } 587