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