Annotation of src/usr.bin/systat/netstat.c, Revision 1.6
1.6 ! millert 1: /* $OpenBSD: netstat.c,v 1.5 1996/08/06 18:48:15 deraadt Exp $ */
1.1 deraadt 2: /* $NetBSD: netstat.c,v 1.3 1995/06/18 23:53:07 cgd Exp $ */
3:
4: /*-
5: * Copyright (c) 1980, 1992, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36:
37: #ifndef lint
38: #if 0
39: static char sccsid[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93";
40: #endif
1.6 ! millert 41: static char rcsid[] = "$OpenBSD: netstat.c,v 1.5 1996/08/06 18:48:15 deraadt Exp $";
1.1 deraadt 42: #endif /* not lint */
43:
44: /*
45: * netstat
46: */
47: #include <sys/param.h>
48: #include <sys/socket.h>
49: #include <sys/socketvar.h>
50: #include <sys/mbuf.h>
51: #include <sys/protosw.h>
52:
53: #include <netinet/in.h>
54: #include <net/route.h>
55: #include <netinet/in_systm.h>
56: #include <netinet/ip.h>
57: #include <netinet/in_pcb.h>
58: #include <netinet/ip_icmp.h>
59: #include <netinet/icmp_var.h>
60: #include <netinet/ip_var.h>
61: #include <netinet/tcp.h>
62: #include <netinet/tcpip.h>
63: #include <netinet/tcp_seq.h>
64: #define TCPSTATES
65: #include <netinet/tcp_fsm.h>
66: #include <netinet/tcp_timer.h>
67: #include <netinet/tcp_var.h>
68: #include <netinet/tcp_debug.h>
69: #include <netinet/udp.h>
70: #include <netinet/udp_var.h>
71:
72: #include <netdb.h>
73: #include <stdlib.h>
74: #include <string.h>
75: #include <nlist.h>
76: #include <paths.h>
77: #include "systat.h"
78: #include "extern.h"
79:
80: static void enter __P((struct inpcb *, struct socket *, int, char *));
81: static char *inetname __P((struct in_addr));
82: static void inetprint __P((struct in_addr *, int, char *));
83:
84: #define streq(a,b) (strcmp(a,b)==0)
1.2 mickey 85: #define YMAX(w) ((w)->_maxy-1)
1.1 deraadt 86:
87: WINDOW *
88: opennetstat()
89: {
90: sethostent(1);
91: setnetent(1);
92: return (subwin(stdscr, LINES-5-1, 0, 5, 0));
93: }
94:
95: struct netinfo {
96: struct netinfo *ni_forw, *ni_prev;
97: short ni_line; /* line on screen */
98: short ni_seen; /* 0 when not present in list */
99: short ni_flags;
100: #define NIF_LACHG 0x1 /* local address changed */
101: #define NIF_FACHG 0x2 /* foreign address changed */
102: short ni_state; /* tcp state */
103: char *ni_proto; /* protocol */
104: struct in_addr ni_laddr; /* local address */
105: long ni_lport; /* local port */
106: struct in_addr ni_faddr; /* foreign address */
107: long ni_fport; /* foreign port */
108: long ni_rcvcc; /* rcv buffer character count */
109: long ni_sndcc; /* snd buffer character count */
110: };
111:
112: static struct {
113: struct netinfo *ni_forw, *ni_prev;
114: } netcb;
115:
116: static int aflag = 0;
117: static int nflag = 0;
118: static int lastrow = 1;
119: static void enter(), inetprint();
120: static char *inetname();
121:
122: void
123: closenetstat(w)
124: WINDOW *w;
125: {
126: register struct netinfo *p;
127:
128: endhostent();
129: endnetent();
130: p = (struct netinfo *)netcb.ni_forw;
131: while (p != (struct netinfo *)&netcb) {
132: if (p->ni_line != -1)
133: lastrow--;
134: p->ni_line = -1;
135: p = p->ni_forw;
136: }
137: if (w != NULL) {
138: wclear(w);
139: wrefresh(w);
140: delwin(w);
141: }
142: }
143:
144: static struct nlist namelist[] = {
145: #define X_TCBTABLE 0
146: { "_tcbtable" },
147: #define X_UDBTABLE 1
148: { "_udbtable" },
149: { "" },
150: };
151:
152: int
153: initnetstat()
154: {
155: if (kvm_nlist(kd, namelist)) {
156: nlisterr(namelist);
157: return(0);
158: }
159: if (namelist[X_TCBTABLE].n_value == 0) {
160: error("No symbols in namelist");
161: return(0);
162: }
163: netcb.ni_forw = netcb.ni_prev = (struct netinfo *)&netcb;
164: protos = TCP|UDP;
165: return(1);
166: }
167:
168: void
169: fetchnetstat()
170: {
171: struct inpcbtable pcbtable;
172: register struct inpcb *head, *prev, *next;
173: register struct netinfo *p;
174: struct inpcb inpcb;
175: struct socket sockb;
176: struct tcpcb tcpcb;
177: void *off;
178: int istcp;
179:
180: if (namelist[X_TCBTABLE].n_value == 0)
181: return;
182: for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw)
183: p->ni_seen = 0;
184: if (protos&TCP) {
185: off = NPTR(X_TCBTABLE);
186: istcp = 1;
187: }
188: else if (protos&UDP) {
189: off = NPTR(X_UDBTABLE);
190: istcp = 0;
191: }
192: else {
193: error("No protocols to display");
194: return;
195: }
196: again:
197: KREAD(off, &pcbtable, sizeof (struct inpcbtable));
198: prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
199: next = pcbtable.inpt_queue.cqh_first;
200: while (next != head) {
201: KREAD(next, &inpcb, sizeof (inpcb));
202: if (inpcb.inp_queue.cqe_prev != prev) {
203: printf("prev = %x, head = %x, next = %x, inpcb...prev = %x\n", prev, head, next, inpcb.inp_queue.cqe_prev);
204: p = netcb.ni_forw;
205: for (; p != (struct netinfo *)&netcb; p = p->ni_forw)
206: p->ni_seen = 1;
207: error("Kernel state in transition");
208: return;
209: }
210: prev = next;
211: next = inpcb.inp_queue.cqe_next;
212:
213: if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
214: continue;
215: if (nhosts && !checkhost(&inpcb))
216: continue;
217: if (nports && !checkport(&inpcb))
218: continue;
219: KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
220: if (istcp) {
221: KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
222: enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
223: } else
224: enter(&inpcb, &sockb, 0, "udp");
225: }
226: if (istcp && (protos&UDP)) {
227: istcp = 0;
228: off = NPTR(X_UDBTABLE);
229: goto again;
230: }
231: }
232:
233: static void
234: enter(inp, so, state, proto)
235: register struct inpcb *inp;
236: register struct socket *so;
237: int state;
238: char *proto;
239: {
240: register struct netinfo *p;
241:
242: /*
243: * Only take exact matches, any sockets with
244: * previously unbound addresses will be deleted
245: * below in the display routine because they
246: * will appear as ``not seen'' in the kernel
247: * data structures.
248: */
249: for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
250: if (!streq(proto, p->ni_proto))
251: continue;
252: if (p->ni_lport != inp->inp_lport ||
253: p->ni_laddr.s_addr != inp->inp_laddr.s_addr)
254: continue;
255: if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr &&
256: p->ni_fport == inp->inp_fport)
257: break;
258: }
259: if (p == (struct netinfo *)&netcb) {
260: if ((p = malloc(sizeof(*p))) == NULL) {
261: error("Out of memory");
262: return;
263: }
264: p->ni_prev = (struct netinfo *)&netcb;
265: p->ni_forw = netcb.ni_forw;
266: netcb.ni_forw->ni_prev = p;
267: netcb.ni_forw = p;
268: p->ni_line = -1;
269: p->ni_laddr = inp->inp_laddr;
270: p->ni_lport = inp->inp_lport;
271: p->ni_faddr = inp->inp_faddr;
272: p->ni_fport = inp->inp_fport;
273: p->ni_proto = proto;
274: p->ni_flags = NIF_LACHG|NIF_FACHG;
275: }
276: p->ni_rcvcc = so->so_rcv.sb_cc;
277: p->ni_sndcc = so->so_snd.sb_cc;
278: p->ni_state = state;
279: p->ni_seen = 1;
280: }
281:
282: /* column locations */
283: #define LADDR 0
284: #define FADDR LADDR+23
285: #define PROTO FADDR+23
286: #define RCVCC PROTO+6
287: #define SNDCC RCVCC+7
288: #define STATE SNDCC+7
289:
290:
291: void
292: labelnetstat()
293: {
294: if (namelist[X_TCBTABLE].n_type == 0)
295: return;
296: wmove(wnd, 0, 0); wclrtobot(wnd);
297: mvwaddstr(wnd, 0, LADDR, "Local Address");
298: mvwaddstr(wnd, 0, FADDR, "Foreign Address");
299: mvwaddstr(wnd, 0, PROTO, "Proto");
300: mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
301: mvwaddstr(wnd, 0, SNDCC, "Send-Q");
302: mvwaddstr(wnd, 0, STATE, "(state)");
303: }
304:
305: void
306: shownetstat()
307: {
308: register struct netinfo *p, *q;
309:
310: /*
311: * First, delete any connections that have gone
312: * away and adjust the position of connections
313: * below to reflect the deleted line.
314: */
315: p = netcb.ni_forw;
316: while (p != (struct netinfo *)&netcb) {
317: if (p->ni_line == -1 || p->ni_seen) {
318: p = p->ni_forw;
319: continue;
320: }
321: wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
322: q = netcb.ni_forw;
323: for (; q != (struct netinfo *)&netcb; q = q->ni_forw)
324: if (q != p && q->ni_line > p->ni_line) {
325: q->ni_line--;
326: /* this shouldn't be necessary */
327: q->ni_flags |= NIF_LACHG|NIF_FACHG;
328: }
329: lastrow--;
330: q = p->ni_forw;
331: p->ni_prev->ni_forw = p->ni_forw;
332: p->ni_forw->ni_prev = p->ni_prev;
333: free(p);
334: p = q;
335: }
336: /*
337: * Update existing connections and add new ones.
338: */
339: for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
340: if (p->ni_line == -1) {
341: /*
342: * Add a new entry if possible.
343: */
344: if (lastrow > YMAX(wnd))
345: continue;
346: p->ni_line = lastrow++;
347: p->ni_flags |= NIF_LACHG|NIF_FACHG;
348: }
349: if (p->ni_flags & NIF_LACHG) {
350: wmove(wnd, p->ni_line, LADDR);
351: inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto);
352: p->ni_flags &= ~NIF_LACHG;
353: }
354: if (p->ni_flags & NIF_FACHG) {
355: wmove(wnd, p->ni_line, FADDR);
356: inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto);
357: p->ni_flags &= ~NIF_FACHG;
358: }
359: mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto);
360: mvwprintw(wnd, p->ni_line, RCVCC, "%6d", p->ni_rcvcc);
361: mvwprintw(wnd, p->ni_line, SNDCC, "%6d", p->ni_sndcc);
362: if (streq(p->ni_proto, "tcp"))
363: if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
364: mvwprintw(wnd, p->ni_line, STATE, "%d",
365: p->ni_state);
366: else
367: mvwaddstr(wnd, p->ni_line, STATE,
368: tcpstates[p->ni_state]);
369: wclrtoeol(wnd);
370: }
371: if (lastrow < YMAX(wnd)) {
372: wmove(wnd, lastrow, 0); wclrtobot(wnd);
373: wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */
374: }
375: }
376:
377: /*
378: * Pretty print an Internet address (net address + port).
379: * If the nflag was specified, use numbers instead of names.
380: */
381: static void
382: inetprint(in, port, proto)
383: register struct in_addr *in;
384: int port;
385: char *proto;
386: {
387: struct servent *sp = 0;
1.6 ! millert 388: char line[80], *cp;
1.1 deraadt 389:
390: sprintf(line, "%.*s.", 16, inetname(*in));
1.6 ! millert 391: cp = strchr(line, '\0');
1.1 deraadt 392: if (!nflag && port)
393: sp = getservbyport(port, proto);
394: if (sp || port == 0)
395: sprintf(cp, "%.8s", sp ? sp->s_name : "*");
396: else
397: sprintf(cp, "%d", ntohs((u_short)port));
398: /* pad to full column to clear any garbage */
1.6 ! millert 399: cp = strchr(line, '\0');
1.1 deraadt 400: while (cp - line < 22)
401: *cp++ = ' ';
402: *cp = '\0';
403: waddstr(wnd, line);
404: }
405:
406: /*
407: * Construct an Internet address representation.
408: * If the nflag has been supplied, give
409: * numeric value, otherwise try for symbolic name.
410: */
411: static char *
412: inetname(in)
413: struct in_addr in;
414: {
415: char *cp = 0;
416: static char line[50];
417: struct hostent *hp;
418: struct netent *np;
419:
420: if (!nflag && in.s_addr != INADDR_ANY) {
421: int net = inet_netof(in);
422: int lna = inet_lnaof(in);
423:
424: if (lna == INADDR_ANY) {
425: np = getnetbyaddr(net, AF_INET);
426: if (np)
427: cp = np->n_name;
428: }
429: if (cp == 0) {
430: hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
431: if (hp)
432: cp = hp->h_name;
433: }
434: }
435: if (in.s_addr == INADDR_ANY)
436: strcpy(line, "*");
437: else if (cp)
1.5 deraadt 438: strncpy(line, cp, sizeof line);
1.1 deraadt 439: else {
440: in.s_addr = ntohl(in.s_addr);
441: #define C(x) ((x) & 0xff)
442: sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
443: C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
444: }
445: return (line);
446: }
447:
448: int
449: cmdnetstat(cmd, args)
450: char *cmd, *args;
451: {
452: register struct netinfo *p;
453:
454: if (prefix(cmd, "all")) {
455: aflag = !aflag;
456: goto fixup;
457: }
458: if (prefix(cmd, "numbers") || prefix(cmd, "names")) {
459: int new;
460:
461: new = prefix(cmd, "numbers");
462: if (new == nflag)
463: return (1);
464: p = netcb.ni_forw;
465: for (; p != (struct netinfo *)&netcb; p = p->ni_forw) {
466: if (p->ni_line == -1)
467: continue;
468: p->ni_flags |= NIF_LACHG|NIF_FACHG;
469: }
470: nflag = new;
1.4 deraadt 471: wclear(wnd);
472: labelnetstat();
1.1 deraadt 473: goto redisplay;
474: }
475: if (!netcmd(cmd, args))
476: return (0);
477: fixup:
478: fetchnetstat();
479: redisplay:
480: shownetstat();
481: refresh();
482: return (1);
483: }