major rework; fixup netstat stuff; pull disk cmds into common file; change
[unix-history] / usr / src / usr.bin / systat / netstat.c
CommitLineData
9796f3aa 1#ifndef lint
833d578b 2static 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
36WINDOW *
37opennetstat()
38{
39
40 sethostent(1);
41 setnetent(1);
42 return (subwin(stdscr, LINES-5-1, 0, 5, 0));
43}
44
45struct 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
62static struct {
63 struct netinfo *ni_forw, *ni_prev;
64} netcb;
65
66static int aflag = 0;
67static int nflag = 0;
68static int lastrow = 1;
69static char *inetname();
70
71closenetstat(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
92static struct nlist nlst[] = {
93#define X_TCB 0
94 { "_tcb" },
95#define X_UDB 1
96 { "_udb" },
97 { "" },
98};
99
9796f3aa
SL
100initnetstat()
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
113fetchnetstat()
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;
133again:
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
170static
171enter(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
225labelnetstat()
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
239shownetstat()
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 */
313static
314inetprint(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 */
343static char *
344inetname(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
380cmdnetstat(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
406fixup:
407 fetchnetstat();
408redisplay:
409 shownetstat();
410 refresh();
9796f3aa
SL
411 return (1);
412}