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