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