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 static char sccsid[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93"; 36 #endif /* not lint */ 37 38 /* 39 * netstat 40 */ 41 #include <sys/param.h> 42 #include <sys/socket.h> 43 #include <sys/socketvar.h> 44 #include <sys/mbuf.h> 45 #include <sys/protosw.h> 46 47 #include <netinet/in.h> 48 #include <net/route.h> 49 #include <netinet/in_systm.h> 50 #include <netinet/ip.h> 51 #include <netinet/in_pcb.h> 52 #include <netinet/ip_icmp.h> 53 #include <netinet/icmp_var.h> 54 #include <netinet/ip_var.h> 55 #include <netinet/tcp.h> 56 #include <netinet/tcpip.h> 57 #include <netinet/tcp_seq.h> 58 #define TCPSTATES 59 #include <netinet/tcp_fsm.h> 60 #include <netinet/tcp_timer.h> 61 #include <netinet/tcp_var.h> 62 #include <netinet/tcp_debug.h> 63 #include <netinet/udp.h> 64 #include <netinet/udp_var.h> 65 66 #include <netdb.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <nlist.h> 70 #include <paths.h> 71 #include "systat.h" 72 #include "extern.h" 73 74 static void enter __P((struct inpcb *, struct socket *, int, char *)); 75 static char *inetname __P((struct in_addr)); 76 static void inetprint __P((struct in_addr *, int, char *)); 77 78 #define streq(a,b) (strcmp(a,b)==0) 79 #define YMAX(w) ((w)->maxy-1) 80 81 WINDOW * 82 opennetstat() 83 { 84 sethostent(1); 85 setnetent(1); 86 return (subwin(stdscr, LINES-5-1, 0, 5, 0)); 87 } 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(), inetprint(); 114 static char *inetname(); 115 116 void 117 closenetstat(w) 118 WINDOW *w; 119 { 120 register struct netinfo *p; 121 122 endhostent(); 123 endnetent(); 124 p = (struct netinfo *)netcb.ni_forw; 125 while (p != (struct netinfo *)&netcb) { 126 if (p->ni_line != -1) 127 lastrow--; 128 p->ni_line = -1; 129 p = p->ni_forw; 130 } 131 if (w != NULL) { 132 wclear(w); 133 wrefresh(w); 134 delwin(w); 135 } 136 } 137 138 static struct nlist namelist[] = { 139 #define X_TCB 0 140 { "_tcb" }, 141 #define X_UDB 1 142 { "_udb" }, 143 { "" }, 144 }; 145 146 int 147 initnetstat() 148 { 149 if (kvm_nlist(kd, namelist)) { 150 nlisterr(namelist); 151 return(0); 152 } 153 if (namelist[X_TCB].n_value == 0) { 154 error("No symbols in namelist"); 155 return(0); 156 } 157 netcb.ni_forw = netcb.ni_prev = (struct netinfo *)&netcb; 158 protos = TCP|UDP; 159 return(1); 160 } 161 162 void 163 fetchnetstat() 164 { 165 register struct inpcb *prev, *next; 166 register struct netinfo *p; 167 struct inpcb inpcb; 168 struct socket sockb; 169 struct tcpcb tcpcb; 170 void *off; 171 int istcp; 172 173 if (namelist[X_TCB].n_value == 0) 174 return; 175 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) 176 p->ni_seen = 0; 177 if (protos&TCP) { 178 off = NPTR(X_TCB); 179 istcp = 1; 180 } 181 else if (protos&UDP) { 182 off = NPTR(X_UDB); 183 istcp = 0; 184 } 185 else { 186 error("No protocols to display"); 187 return; 188 } 189 again: 190 KREAD(off, &inpcb, sizeof (struct inpcb)); 191 prev = off; 192 for (; inpcb.inp_next != off; prev = next) { 193 next = inpcb.inp_next; 194 KREAD(next, &inpcb, sizeof (inpcb)); 195 if (inpcb.inp_prev != prev) { 196 p = netcb.ni_forw; 197 for (; p != (struct netinfo *)&netcb; p = p->ni_forw) 198 p->ni_seen = 1; 199 error("Kernel state in transition"); 200 return; 201 } 202 if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 203 continue; 204 if (nhosts && !checkhost(&inpcb)) 205 continue; 206 if (nports && !checkport(&inpcb)) 207 continue; 208 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); 209 if (istcp) { 210 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 211 enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); 212 } else 213 enter(&inpcb, &sockb, 0, "udp"); 214 } 215 if (istcp && (protos&UDP)) { 216 istcp = 0; 217 off = NPTR(X_UDB); 218 goto again; 219 } 220 } 221 222 static void 223 enter(inp, so, state, proto) 224 register struct inpcb *inp; 225 register struct socket *so; 226 int state; 227 char *proto; 228 { 229 register struct netinfo *p; 230 231 /* 232 * Only take exact matches, any sockets with 233 * previously unbound addresses will be deleted 234 * below in the display routine because they 235 * will appear as ``not seen'' in the kernel 236 * data structures. 237 */ 238 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) { 239 if (!streq(proto, p->ni_proto)) 240 continue; 241 if (p->ni_lport != inp->inp_lport || 242 p->ni_laddr.s_addr != inp->inp_laddr.s_addr) 243 continue; 244 if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr && 245 p->ni_fport == inp->inp_fport) 246 break; 247 } 248 if (p == (struct netinfo *)&netcb) { 249 if ((p = malloc(sizeof(*p))) == NULL) { 250 error("Out of memory"); 251 return; 252 } 253 p->ni_prev = (struct netinfo *)&netcb; 254 p->ni_forw = netcb.ni_forw; 255 netcb.ni_forw->ni_prev = p; 256 netcb.ni_forw = p; 257 p->ni_line = -1; 258 p->ni_laddr = inp->inp_laddr; 259 p->ni_lport = inp->inp_lport; 260 p->ni_faddr = inp->inp_faddr; 261 p->ni_fport = inp->inp_fport; 262 p->ni_proto = proto; 263 p->ni_flags = NIF_LACHG|NIF_FACHG; 264 } 265 p->ni_rcvcc = so->so_rcv.sb_cc; 266 p->ni_sndcc = so->so_snd.sb_cc; 267 p->ni_state = state; 268 p->ni_seen = 1; 269 } 270 271 /* column locations */ 272 #define LADDR 0 273 #define FADDR LADDR+23 274 #define PROTO FADDR+23 275 #define RCVCC PROTO+6 276 #define SNDCC RCVCC+7 277 #define STATE SNDCC+7 278 279 280 void 281 labelnetstat() 282 { 283 if (namelist[X_TCB].n_type == 0) 284 return; 285 wmove(wnd, 0, 0); wclrtobot(wnd); 286 mvwaddstr(wnd, 0, LADDR, "Local Address"); 287 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 288 mvwaddstr(wnd, 0, PROTO, "Proto"); 289 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 290 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 291 mvwaddstr(wnd, 0, STATE, "(state)"); 292 } 293 294 void 295 shownetstat() 296 { 297 register struct netinfo *p, *q; 298 299 /* 300 * First, delete any connections that have gone 301 * away and adjust the position of connections 302 * below to reflect the deleted line. 303 */ 304 p = netcb.ni_forw; 305 while (p != (struct netinfo *)&netcb) { 306 if (p->ni_line == -1 || p->ni_seen) { 307 p = p->ni_forw; 308 continue; 309 } 310 wmove(wnd, p->ni_line, 0); wdeleteln(wnd); 311 q = netcb.ni_forw; 312 for (; q != (struct netinfo *)&netcb; q = q->ni_forw) 313 if (q != p && q->ni_line > p->ni_line) { 314 q->ni_line--; 315 /* this shouldn't be necessary */ 316 q->ni_flags |= NIF_LACHG|NIF_FACHG; 317 } 318 lastrow--; 319 q = p->ni_forw; 320 p->ni_prev->ni_forw = p->ni_forw; 321 p->ni_forw->ni_prev = p->ni_prev; 322 free(p); 323 p = q; 324 } 325 /* 326 * Update existing connections and add new ones. 327 */ 328 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) { 329 if (p->ni_line == -1) { 330 /* 331 * Add a new entry if possible. 332 */ 333 if (lastrow > YMAX(wnd)) 334 continue; 335 p->ni_line = lastrow++; 336 p->ni_flags |= NIF_LACHG|NIF_FACHG; 337 } 338 if (p->ni_flags & NIF_LACHG) { 339 wmove(wnd, p->ni_line, LADDR); 340 inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto); 341 p->ni_flags &= ~NIF_LACHG; 342 } 343 if (p->ni_flags & NIF_FACHG) { 344 wmove(wnd, p->ni_line, FADDR); 345 inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto); 346 p->ni_flags &= ~NIF_FACHG; 347 } 348 mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto); 349 mvwprintw(wnd, p->ni_line, RCVCC, "%6d", p->ni_rcvcc); 350 mvwprintw(wnd, p->ni_line, SNDCC, "%6d", p->ni_sndcc); 351 if (streq(p->ni_proto, "tcp")) 352 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES) 353 mvwprintw(wnd, p->ni_line, STATE, "%d", 354 p->ni_state); 355 else 356 mvwaddstr(wnd, p->ni_line, STATE, 357 tcpstates[p->ni_state]); 358 wclrtoeol(wnd); 359 } 360 if (lastrow < YMAX(wnd)) { 361 wmove(wnd, lastrow, 0); wclrtobot(wnd); 362 wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ 363 } 364 } 365 366 /* 367 * Pretty print an Internet address (net address + port). 368 * If the nflag was specified, use numbers instead of names. 369 */ 370 static void 371 inetprint(in, port, proto) 372 register struct in_addr *in; 373 int port; 374 char *proto; 375 { 376 struct servent *sp = 0; 377 char line[80], *cp, *index(); 378 379 sprintf(line, "%.*s.", 16, inetname(*in)); 380 cp = index(line, '\0'); 381 if (!nflag && port) 382 sp = getservbyport(port, proto); 383 if (sp || port == 0) 384 sprintf(cp, "%.8s", sp ? sp->s_name : "*"); 385 else 386 sprintf(cp, "%d", ntohs((u_short)port)); 387 /* pad to full column to clear any garbage */ 388 cp = index(line, '\0'); 389 while (cp - line < 22) 390 *cp++ = ' '; 391 *cp = '\0'; 392 waddstr(wnd, line); 393 } 394 395 /* 396 * Construct an Internet address representation. 397 * If the nflag has been supplied, give 398 * numeric value, otherwise try for symbolic name. 399 */ 400 static char * 401 inetname(in) 402 struct in_addr in; 403 { 404 char *cp = 0; 405 static char line[50]; 406 struct hostent *hp; 407 struct netent *np; 408 409 if (!nflag && in.s_addr != INADDR_ANY) { 410 int net = inet_netof(in); 411 int lna = inet_lnaof(in); 412 413 if (lna == INADDR_ANY) { 414 np = getnetbyaddr(net, AF_INET); 415 if (np) 416 cp = np->n_name; 417 } 418 if (cp == 0) { 419 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 420 if (hp) 421 cp = hp->h_name; 422 } 423 } 424 if (in.s_addr == INADDR_ANY) 425 strcpy(line, "*"); 426 else if (cp) 427 strcpy(line, cp); 428 else { 429 in.s_addr = ntohl(in.s_addr); 430 #define C(x) ((x) & 0xff) 431 sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), 432 C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); 433 } 434 return (line); 435 } 436 437 int 438 cmdnetstat(cmd, args) 439 char *cmd, *args; 440 { 441 register struct netinfo *p; 442 443 if (prefix(cmd, "all")) { 444 aflag = !aflag; 445 goto fixup; 446 } 447 if (prefix(cmd, "numbers") || prefix(cmd, "names")) { 448 int new; 449 450 new = prefix(cmd, "numbers"); 451 if (new == nflag) 452 return (1); 453 p = netcb.ni_forw; 454 for (; p != (struct netinfo *)&netcb; p = p->ni_forw) { 455 if (p->ni_line == -1) 456 continue; 457 p->ni_flags |= NIF_LACHG|NIF_FACHG; 458 } 459 nflag = new; 460 goto redisplay; 461 } 462 if (!netcmd(cmd, args)) 463 return (0); 464 fixup: 465 fetchnetstat(); 466 redisplay: 467 shownetstat(); 468 refresh(); 469 return (1); 470 } 471