Commit | Line | Data |
---|---|---|
6a4aa0a8 KB |
1 | /*- |
2 | * Copyright (c) 1980, 1992 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
1e3c68be | 5 | * %sccs.include.redist.c% |
07ed1e09 KM |
6 | */ |
7 | ||
9796f3aa | 8 | #ifndef lint |
1e3c68be | 9 | static char sccsid[] = "@(#)netstat.c 5.12 (Berkeley) %G%"; |
6a4aa0a8 | 10 | #endif /* not lint */ |
9796f3aa SL |
11 | |
12 | /* | |
13 | * netstat | |
14 | */ | |
6a4aa0a8 | 15 | #include <sys/param.h> |
9796f3aa SL |
16 | #include <sys/socket.h> |
17 | #include <sys/socketvar.h> | |
18 | #include <sys/mbuf.h> | |
19 | #include <sys/protosw.h> | |
20 | ||
6a4aa0a8 | 21 | #include <netinet/in.h> |
9796f3aa | 22 | #include <net/route.h> |
9796f3aa | 23 | #include <netinet/in_systm.h> |
9796f3aa | 24 | #include <netinet/ip.h> |
5062575e | 25 | #include <netinet/in_pcb.h> |
9796f3aa SL |
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> | |
6a4aa0a8 KB |
39 | |
40 | #include <netdb.h> | |
41 | #include <stdlib.h> | |
42 | #include <string.h> | |
43 | #include <nlist.h> | |
eef33ea7 | 44 | #include <paths.h> |
6a4aa0a8 KB |
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 *)); | |
9796f3aa SL |
51 | |
52 | #define streq(a,b) (strcmp(a,b)==0) | |
edfbb71e | 53 | #define YMAX(w) ((w)->maxy-1) |
9796f3aa SL |
54 | |
55 | WINDOW * | |
56 | opennetstat() | |
57 | { | |
9796f3aa SL |
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; | |
591f9018 | 87 | static void enter(), inetprint(); |
9796f3aa SL |
88 | static char *inetname(); |
89 | ||
6a4aa0a8 | 90 | void |
9796f3aa SL |
91 | closenetstat(w) |
92 | WINDOW *w; | |
93 | { | |
c0b7e584 | 94 | register struct netinfo *p; |
9796f3aa SL |
95 | |
96 | endhostent(); | |
97 | endnetent(); | |
a4e6a87f | 98 | p = (struct netinfo *)netcb.ni_forw; |
9796f3aa SL |
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 | ||
86923762 | 112 | static struct nlist namelist[] = { |
9796f3aa SL |
113 | #define X_TCB 0 |
114 | { "_tcb" }, | |
115 | #define X_UDB 1 | |
116 | { "_udb" }, | |
eef33ea7 | 117 | { "" }, |
9796f3aa SL |
118 | }; |
119 | ||
6a4aa0a8 | 120 | int |
9796f3aa SL |
121 | initnetstat() |
122 | { | |
86923762 KB |
123 | if (kvm_nlist(kd, namelist)) { |
124 | nlisterr(namelist); | |
6a4aa0a8 KB |
125 | return(0); |
126 | } | |
86923762 | 127 | if (namelist[X_TCB].n_value == 0) { |
9796f3aa | 128 | error("No symbols in namelist"); |
c0b7e584 | 129 | return(0); |
9796f3aa SL |
130 | } |
131 | netcb.ni_forw = netcb.ni_prev = (struct netinfo *)&netcb; | |
833d578b | 132 | protos = TCP|UDP; |
c0b7e584 | 133 | return(1); |
9796f3aa SL |
134 | } |
135 | ||
6a4aa0a8 | 136 | void |
9796f3aa SL |
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; | |
142cb8a1 | 144 | void *off; |
9796f3aa SL |
145 | int istcp; |
146 | ||
86923762 | 147 | if (namelist[X_TCB].n_value == 0) |
9796f3aa SL |
148 | return; |
149 | for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) | |
150 | p->ni_seen = 0; | |
142cb8a1 MT |
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 | } | |
591f9018 CT |
159 | else { |
160 | error("No protocols to display"); | |
161 | return; | |
162 | } | |
9796f3aa | 163 | again: |
142cb8a1 MT |
164 | KREAD(off, &inpcb, sizeof (struct inpcb)); |
165 | prev = off; | |
166 | for (; inpcb.inp_next != off; prev = next) { | |
9796f3aa | 167 | next = inpcb.inp_next; |
142cb8a1 | 168 | KREAD(next, &inpcb, sizeof (inpcb)); |
9796f3aa SL |
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; | |
833d578b | 178 | if (nhosts && !checkhost(&inpcb)) |
9796f3aa | 179 | continue; |
833d578b | 180 | if (nports && !checkport(&inpcb)) |
9796f3aa | 181 | continue; |
142cb8a1 | 182 | KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); |
9796f3aa | 183 | if (istcp) { |
142cb8a1 | 184 | KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); |
9796f3aa SL |
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; | |
142cb8a1 | 191 | off = NPTR(X_UDB); |
9796f3aa SL |
192 | goto again; |
193 | } | |
194 | } | |
195 | ||
591f9018 | 196 | static void |
9796f3aa SL |
197 | enter(inp, so, state, proto) |
198 | register struct inpcb *inp; | |
199 | register struct socket *so; | |
200 | int state; | |
201 | char *proto; | |
202 | { | |
833d578b | 203 | register struct netinfo *p; |
9796f3aa SL |
204 | |
205 | /* | |
833d578b SL |
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. | |
9796f3aa SL |
211 | */ |
212 | for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) { | |
213 | if (!streq(proto, p->ni_proto)) | |
214 | continue; | |
833d578b SL |
215 | if (p->ni_lport != inp->inp_lport || |
216 | p->ni_laddr.s_addr != inp->inp_laddr.s_addr) | |
9796f3aa | 217 | continue; |
833d578b SL |
218 | if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr && |
219 | p->ni_fport == inp->inp_fport) | |
220 | break; | |
9796f3aa | 221 | } |
833d578b | 222 | if (p == (struct netinfo *)&netcb) { |
a4e6a87f | 223 | if ((p = malloc(sizeof(*p))) == NULL) { |
833d578b SL |
224 | error("Out of memory"); |
225 | return; | |
226 | } | |
a4e6a87f KB |
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; | |
833d578b SL |
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; | |
9796f3aa | 238 | } |
833d578b SL |
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; | |
9796f3aa SL |
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 | ||
6a4aa0a8 KB |
253 | |
254 | void | |
9796f3aa SL |
255 | labelnetstat() |
256 | { | |
86923762 | 257 | if (namelist[X_TCB].n_type == 0) |
eef33ea7 KB |
258 | return; |
259 | wmove(wnd, 0, 0); wclrtobot(wnd); | |
9796f3aa SL |
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 | ||
6a4aa0a8 | 268 | void |
9796f3aa SL |
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) | |
833d578b | 287 | if (q != p && q->ni_line > p->ni_line) { |
9796f3aa | 288 | q->ni_line--; |
833d578b SL |
289 | /* this shouldn't be necessary */ |
290 | q->ni_flags |= NIF_LACHG|NIF_FACHG; | |
291 | } | |
9796f3aa SL |
292 | lastrow--; |
293 | q = p->ni_forw; | |
a4e6a87f KB |
294 | p->ni_prev->ni_forw = p->ni_forw; |
295 | p->ni_forw->ni_prev = p->ni_prev; | |
296 | free(p); | |
9796f3aa SL |
297 | p = q; |
298 | } | |
9796f3aa SL |
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 | */ | |
833d578b | 307 | if (lastrow > YMAX(wnd)) |
9796f3aa SL |
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 | } | |
833d578b SL |
334 | if (lastrow < YMAX(wnd)) { |
335 | wmove(wnd, lastrow, 0); wclrtobot(wnd); | |
336 | wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ | |
337 | } | |
9796f3aa SL |
338 | } |
339 | ||
340 | /* | |
341 | * Pretty print an Internet address (net address + port). | |
342 | * If the nflag was specified, use numbers instead of names. | |
343 | */ | |
591f9018 | 344 | static void |
9796f3aa SL |
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) { | |
142cb8a1 | 393 | hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); |
9796f3aa SL |
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 | ||
6a4aa0a8 | 411 | int |
9796f3aa | 412 | cmdnetstat(cmd, args) |
eef33ea7 | 413 | char *cmd, *args; |
9796f3aa SL |
414 | { |
415 | register struct netinfo *p; | |
9796f3aa | 416 | |
9796f3aa SL |
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) | |
833d578b | 426 | return (1); |
9796f3aa SL |
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 | } | |
833d578b SL |
436 | if (!netcmd(cmd, args)) |
437 | return (0); | |
9796f3aa SL |
438 | fixup: |
439 | fetchnetstat(); | |
440 | redisplay: | |
441 | shownetstat(); | |
442 | refresh(); | |
9796f3aa SL |
443 | return (1); |
444 | } |