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