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