xref: /openbsd/usr.bin/systat/if.c (revision cca36db2)
1 /*	$OpenBSD: if.c,v 1.20 2011/04/05 07:35:32 mpf 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 
18 #include <sys/param.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/sysctl.h>
22 #include <net/if.h>
23 #include <net/if_dl.h>
24 #include <net/route.h>
25 #include <sys/sockio.h>
26 #include <sys/ioctl.h>
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "systat.h"
33 
34 static  enum state { BOOT, TIME, RUN } state = TIME;
35 
36 struct ifstat {
37 	char		ifs_name[IFNAMSIZ];	/* interface name */
38 	char		ifs_description[IFDESCRSIZE];
39 	struct ifcount	ifs_cur;
40 	struct ifcount	ifs_old;
41 	struct ifcount	ifs_now;
42 	char		ifs_flag;
43 } *ifstats;
44 
45 static	int nifs = 0;
46 static int num_ifs = 0;
47 static int show_bits = 0;
48 
49 void print_if(void);
50 int read_if(void);
51 int select_if(void);
52 int if_keyboard_callback(int);
53 
54 void fetchifstat(void);
55 static void showifstat(struct ifstat *);
56 static void showtotal(void);
57 static void rt_getaddrinfo(struct sockaddr *, int, struct sockaddr **);
58 
59 
60 /* Define fields */
61 field_def fields_if[] = {
62 	{"IFACE", 8, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
63 	{"STATE", 4, 6, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
64 	{"IPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
65 	{"IBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
66 	{"IERRS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
67 	{"OPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
68 	{"OBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
69 	{"OERRS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
70 	{"COLLS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
71 	{"DESC", 14, 64, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
72 };
73 
74 
75 #define FLD_IF_IFACE	FIELD_ADDR(fields_if,0)
76 #define FLD_IF_STATE	FIELD_ADDR(fields_if,1)
77 #define FLD_IF_IPKTS	FIELD_ADDR(fields_if,2)
78 #define FLD_IF_IBYTES	FIELD_ADDR(fields_if,3)
79 #define FLD_IF_IERRS	FIELD_ADDR(fields_if,4)
80 #define FLD_IF_OPKTS	FIELD_ADDR(fields_if,5)
81 #define FLD_IF_OBYTES	FIELD_ADDR(fields_if,6)
82 #define FLD_IF_OERRS	FIELD_ADDR(fields_if,7)
83 #define FLD_IF_COLLS	FIELD_ADDR(fields_if,8)
84 #define FLD_IF_DESC	FIELD_ADDR(fields_if,9)
85 
86 
87 /* Define views */
88 field_def *view_if_0[] = {
89 	FLD_IF_IFACE, FLD_IF_STATE, FLD_IF_DESC, FLD_IF_IPKTS,
90 	FLD_IF_IBYTES, FLD_IF_IERRS, FLD_IF_OPKTS, FLD_IF_OBYTES,
91 	FLD_IF_OERRS, FLD_IF_COLLS, NULL
92 };
93 
94 /* Define view managers */
95 
96 struct view_manager ifstat_mgr = {
97 	"Ifstat", select_if, read_if, NULL, print_header,
98 	print_if, if_keyboard_callback, NULL, NULL
99 };
100 
101 field_view views_if[] = {
102 	{view_if_0, "ifstat", '1', &ifstat_mgr},
103 	{NULL, NULL, 0, NULL}
104 };
105 
106 
107 int
108 initifstat(void)
109 {
110 	field_view *v;
111 	read_if();
112 	for (v = views_if; v->name != NULL; v++)
113 		add_view(v);
114 
115 	return(1);
116 }
117 
118 #define UPDATE(x, y) do { \
119 		ifs->ifs_now.x = ifm.y; \
120 		ifs->ifs_cur.x = ifs->ifs_now.x - ifs->ifs_old.x; \
121 		if (state == TIME) {\
122 			ifs->ifs_old.x = ifs->ifs_now.x; \
123 			ifs->ifs_cur.x /= naptime; \
124 		} \
125 		sum.x += ifs->ifs_cur.x; \
126 	} while(0)
127 
128 
129 void
130 rt_getaddrinfo(struct sockaddr *sa, int addrs, struct sockaddr **info)
131 {
132 	int i;
133 
134 	for (i = 0; i < RTAX_MAX; i++) {
135 		if (addrs & (1 << i)) {
136 			info[i] = sa;
137 			sa = (struct sockaddr *) ((char *)(sa) +
138 			    roundup(sa->sa_len, sizeof(long)));
139 		} else
140 			info[i] = NULL;
141 	}
142 }
143 
144 
145 
146 int
147 select_if(void)
148 {
149 	num_disp = num_ifs + 1;
150 	return (0);
151 }
152 
153 int
154 read_if(void)
155 {
156 	fetchifstat();
157 	num_disp = num_ifs + 1;
158 
159 	return 0;
160 }
161 
162 void
163 print_if(void)
164 {
165 	int n, i, count = 0;
166 
167 	for (n = 0, i = 0; n < nifs; n++) {
168 		if (ifstats[n].ifs_name[0] == '\0')
169 			continue;
170 		if (i++ < dispstart)
171 			continue;
172 		if (i == num_disp)
173 			break;
174 		showifstat(ifstats + n);
175 		if (maxprint > 0 && ++count >= maxprint)
176 			return;
177 	}
178 	showtotal();
179 }
180 
181 
182 void
183 fetchifstat(void)
184 {
185 	struct ifstat *newstats, *ifs;
186 	struct if_msghdr ifm;
187 	struct sockaddr *info[RTAX_MAX];
188 	struct sockaddr_dl *sdl;
189 	char *buf, *next, *lim;
190 	int mib[6], i;
191 	size_t need;
192 
193 	mib[0] = CTL_NET;
194 	mib[1] = AF_ROUTE;
195 	mib[2] = 0;
196 	mib[3] = 0;
197 	mib[4] = NET_RT_IFLIST;
198 	mib[5] = 0;
199 
200 	if (sysctl(mib, 6, NULL, &need, NULL, 0) == -1)
201 		return;
202 	if ((buf = malloc(need)) == NULL)
203 		return;
204 	if (sysctl(mib, 6, buf, &need, NULL, 0) == -1) {
205 		free(buf);
206 		return;
207 	}
208 
209 	bzero(&sum, sizeof(sum));
210 	num_ifs = 0;
211 
212 	lim = buf + need;
213 	for (next = buf; next < lim; next += ifm.ifm_msglen) {
214 		bcopy(next, &ifm, sizeof ifm);
215 		if (ifm.ifm_version != RTM_VERSION ||
216 		    ifm.ifm_type != RTM_IFINFO ||
217 		    !(ifm.ifm_addrs & RTA_IFP))
218 			continue;
219 		if (ifm.ifm_index >= nifs) {
220 			if ((newstats = realloc(ifstats, (ifm.ifm_index + 4)
221 			    * sizeof(struct ifstat))) == NULL)
222 				continue;
223 			ifstats = newstats;
224 			for (; nifs < ifm.ifm_index + 4; nifs++)
225 				bzero(&ifstats[nifs], sizeof(*ifstats));
226 		}
227 		ifs = &ifstats[ifm.ifm_index];
228 		if (ifs->ifs_name[0] == '\0') {
229 			bzero(&info, sizeof(info));
230 			rt_getaddrinfo(
231 			    (struct sockaddr *)((struct if_msghdr *)next + 1),
232 			    ifm.ifm_addrs, info);
233 			sdl = (struct sockaddr_dl *)info[RTAX_IFP];
234 
235 			if (sdl && sdl->sdl_family == AF_LINK &&
236 			    sdl->sdl_nlen > 0) {
237 				struct ifreq ifrdesc;
238 				char ifdescr[IFDESCRSIZE];
239 				int s;
240 
241 				bcopy(sdl->sdl_data, ifs->ifs_name,
242 				      sdl->sdl_nlen);
243 				ifs->ifs_name[sdl->sdl_nlen] = '\0';
244 
245 				/* Get the interface description */
246 				memset(&ifrdesc, 0, sizeof(ifrdesc));
247 				strlcpy(ifrdesc.ifr_name, ifs->ifs_name,
248 					sizeof(ifrdesc.ifr_name));
249 				ifrdesc.ifr_data = (caddr_t)&ifdescr;
250 
251 				s = socket(AF_INET, SOCK_DGRAM, 0);
252 				if (s != -1) {
253 					if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0)
254 						strlcpy(ifs->ifs_description,
255 						    ifrdesc.ifr_data,
256 						    sizeof(ifs->ifs_description));
257 					close(s);
258 				}
259 			}
260 			if (ifs->ifs_name[0] == '\0')
261 				continue;
262 		}
263 		num_ifs++;
264 		UPDATE(ifc_ip, ifm_data.ifi_ipackets);
265 		UPDATE(ifc_ib, ifm_data.ifi_ibytes);
266 		UPDATE(ifc_ie, ifm_data.ifi_ierrors);
267 		UPDATE(ifc_op, ifm_data.ifi_opackets);
268 		UPDATE(ifc_ob, ifm_data.ifi_obytes);
269 		UPDATE(ifc_oe, ifm_data.ifi_oerrors);
270 		UPDATE(ifc_co, ifm_data.ifi_collisions);
271 		ifs->ifs_cur.ifc_flags = ifm.ifm_flags;
272 		ifs->ifs_cur.ifc_state = ifm.ifm_data.ifi_link_state;
273 		ifs->ifs_flag++;
274 	}
275 
276 	/* remove unreferenced interfaces */
277 	for (i = 0; i < nifs; i++) {
278 		ifs = &ifstats[i];
279 		if (ifs->ifs_flag)
280 			ifs->ifs_flag = 0;
281 		else
282 			ifs->ifs_name[0] = '\0';
283 	}
284 
285 	free(buf);
286 }
287 
288 
289 static void
290 showifstat(struct ifstat *ifs)
291 {
292 	int conv = show_bits ? 8 : 1;
293 	int div = show_bits ? 1000 : 1024;
294 
295 	print_fld_str(FLD_IF_IFACE, ifs->ifs_name);
296 
297 	tb_start();
298 	tbprintf("%s", ifs->ifs_cur.ifc_flags & IFF_UP ?
299 		 "up" : "dn");
300 
301 	switch (ifs->ifs_cur.ifc_state) {
302 	case LINK_STATE_UP:
303 	case LINK_STATE_HALF_DUPLEX:
304 	case LINK_STATE_FULL_DUPLEX:
305 		tbprintf(":U");
306 		break;
307 	case LINK_STATE_DOWN:
308 		tbprintf (":D");
309 		break;
310 	}
311 
312 	print_fld_tb(FLD_IF_STATE);
313 
314 	print_fld_str(FLD_IF_DESC, ifs->ifs_description);
315 
316 	print_fld_sdiv(FLD_IF_IBYTES, ifs->ifs_cur.ifc_ib * conv, div);
317 	print_fld_size(FLD_IF_IPKTS, ifs->ifs_cur.ifc_ip);
318 	print_fld_size(FLD_IF_IERRS, ifs->ifs_cur.ifc_ie);
319 
320 	print_fld_sdiv(FLD_IF_OBYTES, ifs->ifs_cur.ifc_ob * conv, div);
321 	print_fld_size(FLD_IF_OPKTS, ifs->ifs_cur.ifc_op);
322 	print_fld_size(FLD_IF_OERRS, ifs->ifs_cur.ifc_oe);
323 
324 	print_fld_size(FLD_IF_COLLS, ifs->ifs_cur.ifc_co);
325 
326 	end_line();
327 }
328 
329 static void
330 showtotal(void)
331 {
332 	int conv = show_bits ? 8 : 1;
333 	int div = show_bits ? 1000 : 1024;
334 
335 	print_fld_str(FLD_IF_IFACE, "Totals");
336 
337 	print_fld_sdiv(FLD_IF_IBYTES, sum.ifc_ib * conv, div);
338 	print_fld_size(FLD_IF_IPKTS, sum.ifc_ip);
339 	print_fld_size(FLD_IF_IERRS, sum.ifc_ie);
340 
341 	print_fld_sdiv(FLD_IF_OBYTES, sum.ifc_ob * conv, div);
342 	print_fld_size(FLD_IF_OPKTS, sum.ifc_op);
343 	print_fld_size(FLD_IF_OERRS, sum.ifc_oe);
344 
345 	print_fld_size(FLD_IF_COLLS, sum.ifc_co);
346 
347 	end_line();
348 
349 }
350 
351 int
352 if_keyboard_callback(int ch)
353 {
354 	struct ifstat *ifs;
355 
356 	switch (ch) {
357 	case 'r':
358 		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
359 			ifs->ifs_old = ifs->ifs_now;
360 		state = RUN;
361 		gotsig_alarm = 1;
362 
363 		break;
364 	case 'b':
365 		state = BOOT;
366 		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
367 			bzero(&ifs->ifs_old, sizeof(ifs->ifs_old));
368 		gotsig_alarm = 1;
369 		break;
370 	case 'B':
371 		show_bits = !show_bits;
372 		if (show_bits) {
373 			FLD_IF_IBYTES->title = "IBITS";
374 			FLD_IF_OBYTES->title = "OBITS";
375 		} else {
376 			FLD_IF_IBYTES->title = "IBYTES";
377 			FLD_IF_OBYTES->title = "OBYTES";
378 		}
379 		gotsig_alarm = 1;
380 		break;
381 	case 't':
382 		state = TIME;
383 		gotsig_alarm = 1;
384 		break;
385 	default:
386 		return keyboard_callback(ch);
387 	};
388 
389 	return 1;
390 }
391 
392