Commit | Line | Data |
---|---|---|
9796f3aa | 1 | #ifndef lint |
833d578b | 2 | static char sccsid[] = "@(#)netstat.c 1.2 (Berkeley) %G%"; |
9796f3aa SL |
3 | #endif |
4 | ||
5 | /* | |
6 | * netstat | |
7 | */ | |
8 | #include "systat.h" | |
9 | ||
9796f3aa SL |
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> | |
9796f3aa SL |
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) | |
833d578b | 34 | #define YMAX(w) ((w)->_maxy-1) |
9796f3aa SL |
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 | ||
9796f3aa SL |
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; | |
833d578b | 110 | protos = TCP|UDP; |
9796f3aa SL |
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; | |
833d578b SL |
127 | if (protos == 0) |
128 | error("No protocols to display"); | |
9796f3aa SL |
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; | |
833d578b | 150 | if (nhosts && !checkhost(&inpcb)) |
9796f3aa | 151 | continue; |
833d578b | 152 | if (nports && !checkport(&inpcb)) |
9796f3aa SL |
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 | { | |
833d578b | 177 | register struct netinfo *p; |
9796f3aa SL |
178 | |
179 | /* | |
833d578b SL |
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. | |
9796f3aa SL |
185 | */ |
186 | for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) { | |
187 | if (!streq(proto, p->ni_proto)) | |
188 | continue; | |
833d578b SL |
189 | if (p->ni_lport != inp->inp_lport || |
190 | p->ni_laddr.s_addr != inp->inp_laddr.s_addr) | |
9796f3aa | 191 | continue; |
833d578b SL |
192 | if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr && |
193 | p->ni_fport == inp->inp_fport) | |
194 | break; | |
9796f3aa | 195 | } |
833d578b SL |
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; | |
9796f3aa | 210 | } |
833d578b SL |
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; | |
9796f3aa SL |
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) | |
833d578b | 257 | if (q != p && q->ni_line > p->ni_line) { |
9796f3aa | 258 | q->ni_line--; |
833d578b SL |
259 | /* this shouldn't be necessary */ |
260 | q->ni_flags |= NIF_LACHG|NIF_FACHG; | |
261 | } | |
9796f3aa SL |
262 | lastrow--; |
263 | q = p->ni_forw; | |
264 | remque(p); | |
265 | free((char *)p); | |
266 | p = q; | |
267 | } | |
9796f3aa SL |
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 | */ | |
833d578b | 276 | if (lastrow > YMAX(wnd)) |
9796f3aa SL |
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 | } | |
833d578b SL |
303 | if (lastrow < YMAX(wnd)) { |
304 | wmove(wnd, lastrow, 0); wclrtobot(wnd); | |
305 | wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */ | |
306 | } | |
9796f3aa SL |
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; | |
9796f3aa | 384 | |
9796f3aa SL |
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) | |
833d578b | 394 | return (1); |
9796f3aa SL |
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 | } | |
833d578b SL |
404 | if (!netcmd(cmd, args)) |
405 | return (0); | |
9796f3aa SL |
406 | fixup: |
407 | fetchnetstat(); | |
408 | redisplay: | |
409 | shownetstat(); | |
410 | refresh(); | |
9796f3aa SL |
411 | return (1); |
412 | } |