xref: /openbsd-src/usr.bin/systat/if.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: if.c,v 1.8 2006/11/28 19:21:15 reyk Exp $ */
2 /*
3  * Copyright (c) 2004 Markus Friedl <markus@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/param.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/sysctl.h>
21 #include <net/if.h>
22 #include <net/if_dl.h>
23 #include <net/route.h>
24 
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "systat.h"
29 #include "extern.h"
30 
31 static  enum state { BOOT, TIME, RUN } state = TIME;
32 
33 struct ifcount {
34 	u_long		ifc_ib;			/* input bytes */
35 	u_long		ifc_ip;			/* input packets */
36 	u_long		ifc_ie;			/* input errors */
37 	u_long		ifc_ob;			/* output bytes */
38 	u_long		ifc_op;			/* output packets */
39 	u_long		ifc_oe;			/* output errors */
40 	u_long		ifc_co;			/* collisions */
41 	int		ifc_flags;		/* up / down */
42 	int		ifc_state;		/* link state */
43 } sum;
44 
45 struct ifstat {
46 	char		ifs_name[IFNAMSIZ];	/* interface name */
47 	struct ifcount	ifs_cur;
48 	struct ifcount	ifs_old;
49 	struct ifcount	ifs_now;
50 } *ifstats;
51 
52 static	int nifs = 0;
53 extern	u_int naptime;
54 
55 const char	*showlinkstate(int);
56 
57 WINDOW *
58 openifstat(void)
59 {
60 
61 	return (subwin(stdscr, LINES-5-1, 0, 5, 0));
62 }
63 
64 void
65 closeifstat(WINDOW *w)
66 {
67 
68 	if (w == NULL)
69 		return;
70 	wclear(w);
71 	wrefresh(w);
72 	delwin(w);
73 }
74 
75 int
76 initifstat(void)
77 {
78 
79 	fetchifstat();
80 	return(1);
81 }
82 
83 #define UPDATE(x, y) do { \
84 		ifs->ifs_now.x = ifm.y; \
85 		ifs->ifs_cur.x = ifs->ifs_now.x - ifs->ifs_old.x; \
86 		if (state == TIME) {\
87 			ifs->ifs_old.x = ifs->ifs_now.x; \
88 			ifs->ifs_cur.x /= naptime; \
89 		} \
90 		sum.x += ifs->ifs_cur.x; \
91 	} while(0)
92 
93 
94 void
95 rt_getaddrinfo(struct sockaddr *sa, int addrs, struct sockaddr **info)
96 {
97 	int i;
98 
99 	for (i = 0; i < RTAX_MAX; i++) {
100 		if (addrs & (1 << i)) {
101 			info[i] = sa;
102 			sa = (struct sockaddr *) ((char *)(sa) +
103 			    roundup(sa->sa_len, sizeof(long)));
104 		} else
105 			info[i] = NULL;
106 	}
107 }
108 
109 void
110 fetchifstat(void)
111 {
112 	struct ifstat *newstats, *ifs;
113 	struct if_msghdr ifm;
114 	struct sockaddr *info[RTAX_MAX];
115 	struct sockaddr_dl *sdl;
116 	char *buf, *next, *lim;
117 	int mib[6];
118 	size_t need;
119 
120 	mib[0] = CTL_NET;
121 	mib[1] = AF_ROUTE;
122 	mib[2] = 0;
123 	mib[3] = 0;
124 	mib[4] = NET_RT_IFLIST;
125 	mib[5] = 0;
126 
127 	if (sysctl(mib, 6, NULL, &need, NULL, 0) == -1)
128 		return;
129 	if ((buf = malloc(need)) == NULL)
130 		return;
131 	if (sysctl(mib, 6, buf, &need, NULL, 0) == -1) {
132 		free(buf);
133 		return;
134 	}
135 
136 	bzero(&sum, sizeof(sum));
137 
138 	lim = buf + need;
139 	for (next = buf; next < lim; next += ifm.ifm_msglen) {
140 		bcopy(next, &ifm, sizeof ifm);
141 		if (ifm.ifm_type != RTM_IFINFO ||
142 		   !(ifm.ifm_addrs & RTA_IFP))
143 			continue;
144 		if (ifm.ifm_index >= nifs) {
145 			if ((newstats = realloc(ifstats, (ifm.ifm_index + 4) *
146 			    sizeof(struct ifstat))) == NULL)
147 				continue;
148 			ifstats = newstats;
149 			for (; nifs < ifm.ifm_index + 4; nifs++)
150 				ifstats[nifs].ifs_name[0] = '\0';
151 		}
152 		ifs = &ifstats[ifm.ifm_index];
153 		if (ifs->ifs_name[0] == '\0') {
154 			bzero(&info, sizeof(info));
155 			rt_getaddrinfo(
156 			    (struct sockaddr *)((struct if_msghdr *)next + 1),
157 			    ifm.ifm_addrs, info);
158 			if ((sdl = (struct sockaddr_dl *)info[RTAX_IFP])) {
159 				if (sdl->sdl_family == AF_LINK &&
160 				    sdl->sdl_nlen > 0) {
161 					bcopy(sdl->sdl_data, ifs->ifs_name,
162 					    sdl->sdl_nlen);
163 					ifs->ifs_name[sdl->sdl_nlen] = '\0';
164 				}
165 			}
166 			if (ifs->ifs_name[0] == '\0')
167 				continue;
168 		}
169 		UPDATE(ifc_ip, ifm_data.ifi_ipackets);
170 		UPDATE(ifc_ib, ifm_data.ifi_ibytes);
171 		UPDATE(ifc_ie, ifm_data.ifi_ierrors);
172 		UPDATE(ifc_op, ifm_data.ifi_opackets);
173 		UPDATE(ifc_ob, ifm_data.ifi_obytes);
174 		UPDATE(ifc_oe, ifm_data.ifi_oerrors);
175 		UPDATE(ifc_co, ifm_data.ifi_collisions);
176 		ifs->ifs_cur.ifc_flags = ifm.ifm_flags;
177 		ifs->ifs_cur.ifc_state = ifm.ifm_data.ifi_link_state;
178 	}
179 	free(buf);
180 }
181 
182 #define INSET 0
183 
184 void
185 labelifstat(void)
186 {
187 
188 	wmove(wnd, 0, 0);
189 	wclrtobot(wnd);
190 
191 	mvwaddstr(wnd, 1, INSET, "Iface");
192 	mvwaddstr(wnd, 1, INSET+9, "State");
193 	mvwaddstr(wnd, 1, INSET+19, "Ibytes");
194 	mvwaddstr(wnd, 1, INSET+29, "Ipkts");
195 	mvwaddstr(wnd, 1, INSET+36, "Ierrs");
196 	mvwaddstr(wnd, 1, INSET+48, "Obytes");
197 	mvwaddstr(wnd, 1, INSET+58, "Opkts");
198 	mvwaddstr(wnd, 1, INSET+65, "Oerrs");
199 	mvwaddstr(wnd, 1, INSET+74, "Colls");
200 }
201 
202 #define FMT "%-8.8s %2s%2s  %10lu %8lu %6lu   %10lu %8lu %6lu   %6lu "
203 
204 const char *
205 showlinkstate(int state)
206 {
207 	switch (state) {
208 	case LINK_STATE_UP:
209 	case LINK_STATE_HALF_DUPLEX:
210 	case LINK_STATE_FULL_DUPLEX:
211 		return (":U");
212 	case LINK_STATE_DOWN:
213 		return (":D");
214 	case LINK_STATE_UNKNOWN:
215 		return ("");
216 	}
217 }
218 
219 void
220 showifstat(void)
221 {
222 	int row;
223 	struct ifstat *ifs;
224 
225 	row = 2;
226 	wmove(wnd, 0, 0);
227 	wclrtoeol(wnd);
228 	for (ifs = ifstats; ifs < ifstats + nifs; ifs++) {
229 		if (ifs->ifs_name[0] == '\0')
230 			continue;
231 		mvwprintw(wnd, row++, INSET, FMT,
232 		    ifs->ifs_name,
233 		    ifs->ifs_cur.ifc_flags & IFF_UP ? "up" : "dn",
234 		    showlinkstate(ifs->ifs_cur.ifc_state),
235 		    ifs->ifs_cur.ifc_ib,
236 		    ifs->ifs_cur.ifc_ip,
237 		    ifs->ifs_cur.ifc_ie,
238 		    ifs->ifs_cur.ifc_ob,
239 		    ifs->ifs_cur.ifc_op,
240 		    ifs->ifs_cur.ifc_oe,
241 		    ifs->ifs_cur.ifc_co);
242 	}
243 	mvwprintw(wnd, row++, INSET, FMT,
244 	    "Totals",
245 	    "", "",
246 	    sum.ifc_ib,
247 	    sum.ifc_ip,
248 	    sum.ifc_ie,
249 	    sum.ifc_ob,
250 	    sum.ifc_op,
251 	    sum.ifc_oe,
252 	    sum.ifc_co);
253 }
254 
255 int
256 cmdifstat(char *cmd, char *args)
257 {
258 	struct ifstat *ifs;
259 
260 	if (prefix(cmd, "run")) {
261 		if (state != RUN)
262 			for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
263 				ifs->ifs_old = ifs->ifs_now;
264 		state = RUN;
265 		return (1);
266 	}
267 	if (prefix(cmd, "boot")) {
268 		state = BOOT;
269 		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
270 			bzero(&ifs->ifs_old, sizeof(ifs->ifs_old));
271 		return (1);
272 	}
273 	if (prefix(cmd, "time")) {
274 		state = TIME;
275 		return (1);
276 	}
277 	if (prefix(cmd, "zero")) {
278 		if (state == RUN)
279 			for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
280 				ifs->ifs_old = ifs->ifs_now;
281 		return (1);
282 	}
283 	return (1);
284 }
285