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