1 /* $NetBSD: netstat.c,v 1.10 1998/08/25 20:59:40 ross Exp $ */ 2 3 /*- 4 * Copyright (c) 1980, 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93"; 40 #endif 41 __RCSID("$NetBSD: netstat.c,v 1.10 1998/08/25 20:59:40 ross 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 55 #include <arpa/inet.h> 56 #include <net/route.h> 57 58 #include <netinet/in_systm.h> 59 #include <netinet/ip.h> 60 #include <netinet/in_pcb.h> 61 #include <netinet/ip_icmp.h> 62 #include <netinet/icmp_var.h> 63 #include <netinet/ip_var.h> 64 #include <netinet/tcp.h> 65 #include <netinet/tcpip.h> 66 #include <netinet/tcp_seq.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 <stdlib.h> 77 #include <string.h> 78 #include <nlist.h> 79 #include <paths.h> 80 #include "systat.h" 81 #include "extern.h" 82 83 static void enter __P((struct inpcb *, struct socket *, int, char *)); 84 static char *inetname __P((struct in_addr)); 85 static void inetprint __P((struct in_addr *, int, char *)); 86 87 #define streq(a,b) (strcmp(a,b)==0) 88 89 struct netinfo { 90 struct netinfo *ni_forw, *ni_prev; 91 short ni_line; /* line on screen */ 92 short ni_seen; /* 0 when not present in list */ 93 short ni_flags; 94 #define NIF_LACHG 0x1 /* local address changed */ 95 #define NIF_FACHG 0x2 /* foreign address changed */ 96 short ni_state; /* tcp state */ 97 char *ni_proto; /* protocol */ 98 struct in_addr ni_laddr; /* local address */ 99 long ni_lport; /* local port */ 100 struct in_addr ni_faddr; /* foreign address */ 101 long ni_fport; /* foreign port */ 102 long ni_rcvcc; /* rcv buffer character count */ 103 long ni_sndcc; /* snd buffer character count */ 104 }; 105 106 static struct { 107 struct netinfo *ni_forw, *ni_prev; 108 } netcb; 109 110 static int aflag = 0; 111 static int nflag = 0; 112 static int lastrow = 1; 113 static void enter __P((struct inpcb *, struct socket *, int, char *)); 114 static void inetprint __P((struct in_addr *, int, char *)); 115 static char *inetname __P((struct in_addr)); 116 117 WINDOW * 118 opennetstat() 119 { 120 121 sethostent(1); 122 setnetent(1); 123 return (subwin(stdscr, LINES-5-1, 0, 5, 0)); 124 } 125 126 void 127 closenetstat(w) 128 WINDOW *w; 129 { 130 struct netinfo *p; 131 132 endhostent(); 133 endnetent(); 134 p = (struct netinfo *)netcb.ni_forw; 135 while (p != (struct netinfo *)&netcb) { 136 if (p->ni_line != -1) 137 lastrow--; 138 p->ni_line = -1; 139 p = p->ni_forw; 140 } 141 if (w != NULL) { 142 wclear(w); 143 wrefresh(w); 144 delwin(w); 145 } 146 } 147 148 static struct nlist namelist[] = { 149 #define X_TCBTABLE 0 150 { "_tcbtable" }, 151 #define X_UDBTABLE 1 152 { "_udbtable" }, 153 { "" }, 154 }; 155 156 int 157 initnetstat() 158 { 159 if (kvm_nlist(kd, namelist)) { 160 nlisterr(namelist); 161 return(0); 162 } 163 if (namelist[X_TCBTABLE].n_value == 0) { 164 error("No symbols in namelist"); 165 return(0); 166 } 167 netcb.ni_forw = netcb.ni_prev = (struct netinfo *)&netcb; 168 protos = TCP|UDP; 169 return(1); 170 } 171 172 void 173 fetchnetstat() 174 { 175 struct inpcbtable pcbtable; 176 struct inpcb *head, *prev, *next; 177 struct netinfo *p; 178 struct inpcb inpcb; 179 struct socket sockb; 180 struct tcpcb tcpcb; 181 void *off; 182 int istcp; 183 184 if (namelist[X_TCBTABLE].n_value == 0) 185 return; 186 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) 187 p->ni_seen = 0; 188 if (protos&TCP) { 189 off = NPTR(X_TCBTABLE); 190 istcp = 1; 191 } 192 else if (protos&UDP) { 193 off = NPTR(X_UDBTABLE); 194 istcp = 0; 195 } 196 else { 197 error("No protocols to display"); 198 return; 199 } 200 again: 201 KREAD(off, &pcbtable, sizeof (struct inpcbtable)); 202 prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue; 203 next = pcbtable.inpt_queue.cqh_first; 204 while (next != head) { 205 KREAD(next, &inpcb, sizeof (inpcb)); 206 if (inpcb.inp_queue.cqe_prev != prev) { 207 printf("prev = %p, head = %p, next = %p, inpcb...prev = %p\n", prev, head, next, inpcb.inp_queue.cqe_prev); 208 p = netcb.ni_forw; 209 for (; p != (struct netinfo *)&netcb; p = p->ni_forw) 210 p->ni_seen = 1; 211 error("Kernel state in transition"); 212 return; 213 } 214 prev = next; 215 next = inpcb.inp_queue.cqe_next; 216 217 if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 218 continue; 219 if (nhosts && !checkhost(&inpcb)) 220 continue; 221 if (nports && !checkport(&inpcb)) 222 continue; 223 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); 224 if (istcp) { 225 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 226 enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); 227 } else 228 enter(&inpcb, &sockb, 0, "udp"); 229 } 230 if (istcp && (protos&UDP)) { 231 istcp = 0; 232 off = NPTR(X_UDBTABLE); 233 goto again; 234 } 235 } 236 237 static void 238 enter(inp, so, state, proto) 239 struct inpcb *inp; 240 struct socket *so; 241 int state; 242 char *proto; 243 { 244 struct netinfo *p; 245 246 /* 247 * Only take exact matches, any sockets with 248 * previously unbound addresses will be deleted 249 * below in the display routine because they 250 * will appear as ``not seen'' in the kernel 251 * data structures. 252 */ 253 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) { 254 if (!streq(proto, p->ni_proto)) 255 continue; 256 if (p->ni_lport != inp->inp_lport || 257 p->ni_laddr.s_addr != inp->inp_laddr.s_addr) 258 continue; 259 if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr && 260 p->ni_fport == inp->inp_fport) 261 break; 262 } 263 if (p == (struct netinfo *)&netcb) { 264 if ((p = malloc(sizeof(*p))) == NULL) { 265 error("Out of memory"); 266 return; 267 } 268 p->ni_prev = (struct netinfo *)&netcb; 269 p->ni_forw = netcb.ni_forw; 270 netcb.ni_forw->ni_prev = p; 271 netcb.ni_forw = p; 272 p->ni_line = -1; 273 p->ni_laddr = inp->inp_laddr; 274 p->ni_lport = inp->inp_lport; 275 p->ni_faddr = inp->inp_faddr; 276 p->ni_fport = inp->inp_fport; 277 p->ni_proto = proto; 278 p->ni_flags = NIF_LACHG|NIF_FACHG; 279 } 280 p->ni_rcvcc = so->so_rcv.sb_cc; 281 p->ni_sndcc = so->so_snd.sb_cc; 282 p->ni_state = state; 283 p->ni_seen = 1; 284 } 285 286 /* column locations */ 287 #define LADDR 0 288 #define FADDR LADDR+23 289 #define PROTO FADDR+23 290 #define RCVCC PROTO+6 291 #define SNDCC RCVCC+7 292 #define STATE SNDCC+7 293 294 void 295 labelnetstat() 296 { 297 298 if (namelist[X_TCBTABLE].n_type == 0) 299 return; 300 wmove(wnd, 0, 0); wclrtobot(wnd); 301 mvwaddstr(wnd, 0, LADDR, "Local Address"); 302 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 303 mvwaddstr(wnd, 0, PROTO, "Proto"); 304 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 305 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 306 mvwaddstr(wnd, 0, STATE, "(state)"); 307 } 308 309 void 310 shownetstat() 311 { 312 struct netinfo *p, *q; 313 314 /* 315 * First, delete any connections that have gone 316 * away and adjust the position of connections 317 * below to reflect the deleted line. 318 */ 319 p = netcb.ni_forw; 320 while (p != (struct netinfo *)&netcb) { 321 if (p->ni_line == -1 || p->ni_seen) { 322 p = p->ni_forw; 323 continue; 324 } 325 wmove(wnd, p->ni_line, 0); wdeleteln(wnd); 326 q = netcb.ni_forw; 327 for (; q != (struct netinfo *)&netcb; q = q->ni_forw) 328 if (q != p && q->ni_line > p->ni_line) { 329 q->ni_line--; 330 /* this shouldn't be necessary */ 331 q->ni_flags |= NIF_LACHG|NIF_FACHG; 332 } 333 lastrow--; 334 q = p->ni_forw; 335 p->ni_prev->ni_forw = p->ni_forw; 336 p->ni_forw->ni_prev = p->ni_prev; 337 free(p); 338 p = q; 339 } 340 /* 341 * Update existing connections and add new ones. 342 */ 343 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) { 344 if (p->ni_line == -1) { 345 /* 346 * Add a new entry if possible. 347 */ 348 if (lastrow > getmaxy(wnd)) 349 continue; 350 p->ni_line = lastrow++; 351 p->ni_flags |= NIF_LACHG|NIF_FACHG; 352 } 353 if (p->ni_flags & NIF_LACHG) { 354 wmove(wnd, p->ni_line, LADDR); 355 inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto); 356 p->ni_flags &= ~NIF_LACHG; 357 } 358 if (p->ni_flags & NIF_FACHG) { 359 wmove(wnd, p->ni_line, FADDR); 360 inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto); 361 p->ni_flags &= ~NIF_FACHG; 362 } 363 mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto); 364 mvwprintw(wnd, p->ni_line, RCVCC, "%6d", p->ni_rcvcc); 365 mvwprintw(wnd, p->ni_line, SNDCC, "%6d", p->ni_sndcc); 366 if (streq(p->ni_proto, "tcp")) { 367 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES) 368 mvwprintw(wnd, p->ni_line, STATE, "%d", 369 p->ni_state); 370 else 371 mvwaddstr(wnd, p->ni_line, STATE, 372 tcpstates[p->ni_state]); 373 } 374 wclrtoeol(wnd); 375 } 376 if (lastrow < getmaxy(wnd)) { 377 wmove(wnd, lastrow, 0); wclrtobot(wnd); 378 wmove(wnd, getmaxy(wnd), 0); wdeleteln(wnd); /* XXX */ 379 } 380 } 381 382 /* 383 * Pretty print an Internet address (net address + port). 384 * If the nflag was specified, use numbers instead of names. 385 */ 386 static void 387 inetprint(in, port, proto) 388 struct in_addr *in; 389 int port; 390 char *proto; 391 { 392 struct servent *sp = 0; 393 char line[80], *cp; 394 395 (void)snprintf(line, sizeof line, "%.*s.", 16, inetname(*in)); 396 cp = strchr(line, '\0'); 397 if (!nflag && port) 398 sp = getservbyport(port, proto); 399 if (sp || port == 0) 400 (void)snprintf(cp, line + sizeof line - cp, "%.8s", 401 sp ? sp->s_name : "*"); 402 else 403 (void)snprintf(cp, line + sizeof line - cp, "%d", 404 ntohs((u_short)port)); 405 /* pad to full column to clear any garbage */ 406 cp = strchr(line, '\0'); 407 while (cp - line < 22) 408 *cp++ = ' '; 409 *cp = '\0'; 410 waddstr(wnd, line); 411 } 412 413 /* 414 * Construct an Internet address representation. 415 * If the nflag has been supplied, give 416 * numeric value, otherwise try for symbolic name. 417 */ 418 static char * 419 inetname(in) 420 struct in_addr in; 421 { 422 char *cp = 0; 423 static char line[50]; 424 struct hostent *hp; 425 struct netent *np; 426 427 if (!nflag && in.s_addr != INADDR_ANY) { 428 int net = inet_netof(in); 429 int lna = inet_lnaof(in); 430 431 if (lna == INADDR_ANY) { 432 np = getnetbyaddr(net, AF_INET); 433 if (np) 434 cp = np->n_name; 435 } 436 if (cp == 0) { 437 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 438 if (hp) 439 cp = hp->h_name; 440 } 441 } 442 if (in.s_addr == INADDR_ANY) 443 strncpy(line, "*", sizeof(line) - 1); 444 else if (cp) 445 strncpy(line, cp, sizeof(line) - 1); 446 else { 447 in.s_addr = ntohl(in.s_addr); 448 #define C(x) ((x) & 0xff) 449 (void)snprintf(line, sizeof line, "%u.%u.%u.%u", 450 C(in.s_addr >> 24), C(in.s_addr >> 16), 451 C(in.s_addr >> 8), C(in.s_addr)); 452 #undef C 453 } 454 line[sizeof(line) - 1] = '\0'; 455 return (line); 456 } 457 458 int 459 cmdnetstat(cmd, args) 460 char *cmd, *args; 461 { 462 struct netinfo *p; 463 464 if (prefix(cmd, "all")) { 465 aflag = !aflag; 466 goto fixup; 467 } 468 if (prefix(cmd, "numbers") || prefix(cmd, "names")) { 469 int new; 470 471 new = prefix(cmd, "numbers"); 472 if (new == nflag) 473 return (1); 474 p = netcb.ni_forw; 475 for (; p != (struct netinfo *)&netcb; p = p->ni_forw) { 476 if (p->ni_line == -1) 477 continue; 478 p->ni_flags |= NIF_LACHG|NIF_FACHG; 479 } 480 nflag = new; 481 wclear(wnd); 482 labelnetstat(); 483 goto redisplay; 484 } 485 if (!netcmd(cmd, args)) 486 return (0); 487 fixup: 488 fetchnetstat(); 489 redisplay: 490 shownetstat(); 491 refresh(); 492 return (1); 493 } 494