xref: /openbsd-src/usr.bin/systat/if.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: if.c,v 1.25 2019/07/04 01:39:44 dlg 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>	/* roundup */
19 #include <sys/signal.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 const char ifails[] = "IFAILS";
60 const char ofails[] = "OFAILS";
61 
62 #define IF_ERR_SUM	0
63 #define IF_ERR_ERRORS	1
64 #define IF_ERR_QDROPS	2
65 
66 struct if_err_view {
67 	const char *iname;
68 	const char *oname;
69 	uint64_t (*icount)(const struct ifcount *);
70 	uint64_t (*ocount)(const struct ifcount *);
71 };
72 
73 static uint64_t if_err_ifails(const struct ifcount *);
74 static uint64_t if_err_ofails(const struct ifcount *);
75 static uint64_t if_err_ierrors(const struct ifcount *);
76 static uint64_t if_err_oerrors(const struct ifcount *);
77 static uint64_t if_err_iqdrops(const struct ifcount *);
78 static uint64_t if_err_oqdrops(const struct ifcount *);
79 
80 static const struct if_err_view if_err_views[] = {
81 	[IF_ERR_SUM] =    {
82 		.iname = ifails,
83 		.oname = ofails,
84 		.icount = if_err_ifails,
85 		.ocount = if_err_ofails,
86 	},
87 	[IF_ERR_ERRORS] = {
88 		.iname = "IERRS",
89 		.oname = "OERRS",
90 		.icount = if_err_ierrors,
91 		.ocount = if_err_oerrors,
92 	},
93 	[IF_ERR_QDROPS] = {
94 		.iname = "IQDROPS",
95 		.oname = "OQDROPS",
96 		.icount = if_err_iqdrops,
97 		.ocount = if_err_oqdrops,
98 	},
99 };
100 
101 static const struct if_err_view *if_err_view = &if_err_views[IF_ERR_SUM];
102 
103 /* Define fields */
104 field_def fields_if[] = {
105 	{"IFACE", 8, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
106 	{"STATE", 4, 6, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
107 	{"IPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
108 	{"IBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
109 	{ifails, 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
110 	{"OPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
111 	{"OBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
112 	{ofails, 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
113 	{"COLLS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
114 	{"DESC", 14, 64, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
115 };
116 
117 
118 #define FLD_IF_IFACE	FIELD_ADDR(fields_if,0)
119 #define FLD_IF_STATE	FIELD_ADDR(fields_if,1)
120 #define FLD_IF_IPKTS	FIELD_ADDR(fields_if,2)
121 #define FLD_IF_IBYTES	FIELD_ADDR(fields_if,3)
122 #define FLD_IF_IERRS	FIELD_ADDR(fields_if,4)
123 #define FLD_IF_OPKTS	FIELD_ADDR(fields_if,5)
124 #define FLD_IF_OBYTES	FIELD_ADDR(fields_if,6)
125 #define FLD_IF_OERRS	FIELD_ADDR(fields_if,7)
126 #define FLD_IF_COLLS	FIELD_ADDR(fields_if,8)
127 #define FLD_IF_DESC	FIELD_ADDR(fields_if,9)
128 
129 
130 /* Define views */
131 field_def *view_if_0[] = {
132 	FLD_IF_IFACE, FLD_IF_STATE, FLD_IF_DESC, FLD_IF_IPKTS,
133 	FLD_IF_IBYTES, FLD_IF_IERRS, FLD_IF_OPKTS, FLD_IF_OBYTES,
134 	FLD_IF_OERRS, FLD_IF_COLLS, NULL
135 };
136 
137 /* Define view managers */
138 
139 struct view_manager ifstat_mgr = {
140 	"Ifstat", select_if, read_if, NULL, print_header,
141 	print_if, if_keyboard_callback, NULL, NULL
142 };
143 
144 field_view views_if[] = {
145 	{view_if_0, "ifstat", '1', &ifstat_mgr},
146 	{NULL, NULL, 0, NULL}
147 };
148 
149 
150 int
151 initifstat(void)
152 {
153 	field_view *v;
154 	read_if();
155 	for (v = views_if; v->name != NULL; v++)
156 		add_view(v);
157 
158 	return(1);
159 }
160 
161 #define UPDATE(x, y) do { \
162 		ifs->ifs_now.x = ifm.y; \
163 		ifs->ifs_cur.x = ifs->ifs_now.x - ifs->ifs_old.x; \
164 		if (state == TIME) {\
165 			ifs->ifs_old.x = ifs->ifs_now.x; \
166 			ifs->ifs_cur.x /= naptime; \
167 		} \
168 		sum.x += ifs->ifs_cur.x; \
169 	} while(0)
170 
171 
172 void
173 rt_getaddrinfo(struct sockaddr *sa, int addrs, struct sockaddr **info)
174 {
175 	int i;
176 
177 	for (i = 0; i < RTAX_MAX; i++) {
178 		if (addrs & (1 << i)) {
179 			info[i] = sa;
180 			sa = (struct sockaddr *) ((char *)(sa) +
181 			    roundup(sa->sa_len, sizeof(long)));
182 		} else
183 			info[i] = NULL;
184 	}
185 }
186 
187 
188 
189 int
190 select_if(void)
191 {
192 	num_disp = num_ifs + 1;
193 	return (0);
194 }
195 
196 int
197 read_if(void)
198 {
199 	fetchifstat();
200 	num_disp = num_ifs + 1;
201 
202 	return 0;
203 }
204 
205 void
206 print_if(void)
207 {
208 	int n, i, count = 0;
209 
210 	for (n = 0, i = 0; n < nifs; n++) {
211 		if (ifstats[n].ifs_name[0] == '\0')
212 			continue;
213 		if (i++ < dispstart)
214 			continue;
215 		if (i == num_disp)
216 			break;
217 		showifstat(ifstats + n);
218 		if (maxprint > 0 && ++count >= maxprint)
219 			return;
220 	}
221 	showtotal();
222 }
223 
224 
225 void
226 fetchifstat(void)
227 {
228 	struct ifstat *newstats, *ifs;
229 	struct if_msghdr ifm;
230 	struct sockaddr *info[RTAX_MAX];
231 	struct sockaddr_dl *sdl;
232 	char *buf, *next, *lim;
233 	int mib[6], i;
234 	size_t need;
235 
236 	mib[0] = CTL_NET;
237 	mib[1] = PF_ROUTE;
238 	mib[2] = 0;
239 	mib[3] = 0;
240 	mib[4] = NET_RT_IFLIST;
241 	mib[5] = 0;
242 
243 	if (sysctl(mib, 6, NULL, &need, NULL, 0) == -1)
244 		return;
245 	if ((buf = malloc(need)) == NULL)
246 		return;
247 	if (sysctl(mib, 6, buf, &need, NULL, 0) == -1) {
248 		free(buf);
249 		return;
250 	}
251 
252 	bzero(&sum, sizeof(sum));
253 	num_ifs = 0;
254 
255 	lim = buf + need;
256 	for (next = buf; next < lim; next += ifm.ifm_msglen) {
257 		bcopy(next, &ifm, sizeof ifm);
258 		if (ifm.ifm_version != RTM_VERSION ||
259 		    ifm.ifm_type != RTM_IFINFO ||
260 		    !(ifm.ifm_addrs & RTA_IFP))
261 			continue;
262 		if (ifm.ifm_index >= nifs) {
263 			if ((newstats = reallocarray(ifstats, ifm.ifm_index + 4,
264 			    sizeof(struct ifstat))) == NULL)
265 				continue;
266 			ifstats = newstats;
267 			for (; nifs < ifm.ifm_index + 4; nifs++)
268 				bzero(&ifstats[nifs], sizeof(*ifstats));
269 		}
270 		ifs = &ifstats[ifm.ifm_index];
271 		if (ifs->ifs_name[0] == '\0') {
272 			bzero(&info, sizeof(info));
273 			rt_getaddrinfo(
274 			    (struct sockaddr *)((struct if_msghdr *)next + 1),
275 			    ifm.ifm_addrs, info);
276 			sdl = (struct sockaddr_dl *)info[RTAX_IFP];
277 
278 			if (sdl && sdl->sdl_family == AF_LINK &&
279 			    sdl->sdl_nlen > 0) {
280 				struct ifreq ifrdesc;
281 				char ifdescr[IFDESCRSIZE];
282 				int s;
283 
284 				bcopy(sdl->sdl_data, ifs->ifs_name,
285 				      sdl->sdl_nlen);
286 				ifs->ifs_name[sdl->sdl_nlen] = '\0';
287 
288 				/* Get the interface description */
289 				memset(&ifrdesc, 0, sizeof(ifrdesc));
290 				strlcpy(ifrdesc.ifr_name, ifs->ifs_name,
291 					sizeof(ifrdesc.ifr_name));
292 				ifrdesc.ifr_data = (caddr_t)&ifdescr;
293 
294 				s = socket(AF_INET, SOCK_DGRAM, 0);
295 				if (s != -1) {
296 					if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0)
297 						strlcpy(ifs->ifs_description,
298 						    ifrdesc.ifr_data,
299 						    sizeof(ifs->ifs_description));
300 					close(s);
301 				}
302 			}
303 			if (ifs->ifs_name[0] == '\0')
304 				continue;
305 		}
306 		num_ifs++;
307 		UPDATE(ifc_ip, ifm_data.ifi_ipackets);
308 		UPDATE(ifc_ib, ifm_data.ifi_ibytes);
309 		UPDATE(ifc_ie, ifm_data.ifi_ierrors);
310 		UPDATE(ifc_iq, ifm_data.ifi_iqdrops);
311 		UPDATE(ifc_op, ifm_data.ifi_opackets);
312 		UPDATE(ifc_ob, ifm_data.ifi_obytes);
313 		UPDATE(ifc_oe, ifm_data.ifi_oerrors);
314 		UPDATE(ifc_oq, ifm_data.ifi_oqdrops);
315 		UPDATE(ifc_co, ifm_data.ifi_collisions);
316 		ifs->ifs_cur.ifc_flags = ifm.ifm_flags;
317 		ifs->ifs_cur.ifc_state = ifm.ifm_data.ifi_link_state;
318 		ifs->ifs_flag++;
319 	}
320 
321 	/* remove unreferenced interfaces */
322 	for (i = 0; i < nifs; i++) {
323 		ifs = &ifstats[i];
324 		if (ifs->ifs_flag)
325 			ifs->ifs_flag = 0;
326 		else
327 			ifs->ifs_name[0] = '\0';
328 	}
329 
330 	free(buf);
331 }
332 
333 
334 static void
335 showifstat(struct ifstat *ifs)
336 {
337 	int conv = show_bits ? 8 : 1;
338 	int div = show_bits ? 1000 : 1024;
339 
340 	print_fld_str(FLD_IF_IFACE, ifs->ifs_name);
341 
342 	tb_start();
343 	tbprintf("%s", ifs->ifs_cur.ifc_flags & IFF_UP ?
344 		 "up" : "dn");
345 
346 	switch (ifs->ifs_cur.ifc_state) {
347 	case LINK_STATE_UP:
348 	case LINK_STATE_HALF_DUPLEX:
349 	case LINK_STATE_FULL_DUPLEX:
350 		tbprintf(":U");
351 		break;
352 	case LINK_STATE_DOWN:
353 		tbprintf (":D");
354 		break;
355 	}
356 
357 	print_fld_tb(FLD_IF_STATE);
358 
359 	print_fld_str(FLD_IF_DESC, ifs->ifs_description);
360 
361 	print_fld_sdiv(FLD_IF_IBYTES, ifs->ifs_cur.ifc_ib * conv, div);
362 	print_fld_size(FLD_IF_IPKTS, ifs->ifs_cur.ifc_ip);
363 	print_fld_size(FLD_IF_IERRS, if_err_view->icount(&ifs->ifs_cur));
364 
365 	print_fld_sdiv(FLD_IF_OBYTES, ifs->ifs_cur.ifc_ob * conv, div);
366 	print_fld_size(FLD_IF_OPKTS, ifs->ifs_cur.ifc_op);
367 	print_fld_size(FLD_IF_OERRS, if_err_view->ocount(&ifs->ifs_cur));
368 
369 	print_fld_size(FLD_IF_COLLS, ifs->ifs_cur.ifc_co);
370 
371 	end_line();
372 }
373 
374 static void
375 showtotal(void)
376 {
377 	int conv = show_bits ? 8 : 1;
378 	int div = show_bits ? 1000 : 1024;
379 
380 	print_fld_str(FLD_IF_IFACE, "Totals");
381 
382 	print_fld_sdiv(FLD_IF_IBYTES, sum.ifc_ib * conv, div);
383 	print_fld_size(FLD_IF_IPKTS, sum.ifc_ip);
384 	print_fld_size(FLD_IF_IERRS, if_err_view->icount(&sum));
385 
386 	print_fld_sdiv(FLD_IF_OBYTES, sum.ifc_ob * conv, div);
387 	print_fld_size(FLD_IF_OPKTS, sum.ifc_op);
388 	print_fld_size(FLD_IF_OERRS, if_err_view->ocount(&sum));
389 
390 	print_fld_size(FLD_IF_COLLS, sum.ifc_co);
391 
392 	end_line();
393 
394 }
395 
396 static uint64_t
397 if_err_ifails(const struct ifcount *ifc)
398 {
399 	return (ifc->ifc_ie + ifc->ifc_iq);
400 }
401 
402 static uint64_t
403 if_err_ofails(const struct ifcount *ifc)
404 {
405 	return (ifc->ifc_oe + ifc->ifc_oq);
406 }
407 
408 static uint64_t
409 if_err_ierrors(const struct ifcount *ifc)
410 {
411 	return (ifc->ifc_ie);
412 }
413 
414 static uint64_t
415 if_err_oerrors(const struct ifcount *ifc)
416 {
417 	return (ifc->ifc_oe);
418 }
419 
420 static uint64_t
421 if_err_iqdrops(const struct ifcount *ifc)
422 {
423 	return (ifc->ifc_iq);
424 }
425 
426 static uint64_t
427 if_err_oqdrops(const struct ifcount *ifc)
428 {
429 	return (ifc->ifc_oq);
430 }
431 
432 static void
433 if_set_errs(unsigned int v)
434 {
435 	if_err_view = &if_err_views[v];
436 	FLD_IF_IERRS->title = if_err_view->iname;
437 	FLD_IF_OERRS->title = if_err_view->oname;
438 	gotsig_alarm = 1;
439 }
440 
441 int
442 if_keyboard_callback(int ch)
443 {
444 	struct ifstat *ifs;
445 
446 	switch (ch) {
447 	case 'd':
448 		if_set_errs(IF_ERR_QDROPS);
449 		break;
450 	case 'e':
451 		if_set_errs(IF_ERR_ERRORS);
452 		break;
453 	case 'f':
454 		if_set_errs(IF_ERR_SUM);
455 		break;
456 
457 	case 'r':
458 		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
459 			ifs->ifs_old = ifs->ifs_now;
460 		state = RUN;
461 		gotsig_alarm = 1;
462 
463 		break;
464 	case 'b':
465 		state = BOOT;
466 		for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
467 			bzero(&ifs->ifs_old, sizeof(ifs->ifs_old));
468 		gotsig_alarm = 1;
469 		break;
470 	case 'B':
471 		show_bits = !show_bits;
472 		if (show_bits) {
473 			FLD_IF_IBYTES->title = "IBITS";
474 			FLD_IF_OBYTES->title = "OBITS";
475 		} else {
476 			FLD_IF_IBYTES->title = "IBYTES";
477 			FLD_IF_OBYTES->title = "OBYTES";
478 		}
479 		gotsig_alarm = 1;
480 		break;
481 	case 't':
482 		state = TIME;
483 		gotsig_alarm = 1;
484 		break;
485 	default:
486 		return keyboard_callback(ch);
487 	};
488 
489 	return 1;
490 }
491 
492