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