Annotation of src/usr.bin/systat/if.c, Revision 1.25
1.25 ! dlg 1: /* $OpenBSD: if.c,v 1.24 2019/03/04 21:27:35 dlg Exp $ */
1.1 markus 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: */
1.15 jasper 17:
1.23 deraadt 18: #include <sys/param.h> /* roundup */
19: #include <sys/signal.h>
1.1 markus 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>
1.13 deraadt 25: #include <sys/sockio.h>
1.15 jasper 26: #include <sys/ioctl.h>
1.1 markus 27:
28: #include <stdlib.h>
1.5 deraadt 29: #include <string.h>
1.15 jasper 30: #include <unistd.h>
1.1 markus 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 */
1.13 deraadt 38: char ifs_description[IFDESCRSIZE];
1.1 markus 39: struct ifcount ifs_cur;
40: struct ifcount ifs_old;
41: struct ifcount ifs_now;
1.17 canacar 42: char ifs_flag;
1.1 markus 43: } *ifstats;
44:
45: static int nifs = 0;
1.12 canacar 46: static int num_ifs = 0;
1.20 mpf 47: static int show_bits = 0;
1.1 markus 48:
1.12 canacar 49: void print_if(void);
50: int read_if(void);
51: int select_if(void);
52: int if_keyboard_callback(int);
53:
1.16 deraadt 54: void fetchifstat(void);
1.12 canacar 55: static void showifstat(struct ifstat *);
56: static void showtotal(void);
1.18 lum 57: static void rt_getaddrinfo(struct sockaddr *, int, struct sockaddr **);
1.12 canacar 58:
1.24 dlg 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];
1.12 canacar 102:
103: /* Define fields */
104: field_def fields_if[] = {
105: {"IFACE", 8, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.13 deraadt 106: {"STATE", 4, 6, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.12 canacar 107: {"IPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
108: {"IBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.24 dlg 109: {ifails, 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.12 canacar 110: {"OPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
111: {"OBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.24 dlg 112: {ofails, 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.12 canacar 113: {"COLLS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.13 deraadt 114: {"DESC", 14, 64, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.12 canacar 115: };
116:
117:
1.19 jasper 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)
1.12 canacar 128:
129:
130: /* Define views */
131: field_def *view_if_0[] = {
1.13 deraadt 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
1.12 canacar 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: };
1.7 claudio 148:
1.1 markus 149:
150: int
151: initifstat(void)
152: {
1.12 canacar 153: field_view *v;
154: read_if();
155: for (v = views_if; v->name != NULL; v++)
156: add_view(v);
1.1 markus 157:
158: return(1);
159: }
160:
161: #define UPDATE(x, y) do { \
1.2 deraadt 162: ifs->ifs_now.x = ifm.y; \
1.1 markus 163: ifs->ifs_cur.x = ifs->ifs_now.x - ifs->ifs_old.x; \
1.4 dlg 164: if (state == TIME) {\
165: ifs->ifs_old.x = ifs->ifs_now.x; \
166: ifs->ifs_cur.x /= naptime; \
167: } \
1.1 markus 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:
1.12 canacar 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:
1.1 markus 205: void
1.12 canacar 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:
1.16 deraadt 225: void
1.1 markus 226: fetchifstat(void)
227: {
228: struct ifstat *newstats, *ifs;
1.2 deraadt 229: struct if_msghdr ifm;
1.1 markus 230: struct sockaddr *info[RTAX_MAX];
231: struct sockaddr_dl *sdl;
232: char *buf, *next, *lim;
1.17 canacar 233: int mib[6], i;
1.1 markus 234: size_t need;
235:
236: mib[0] = CTL_NET;
1.21 guenther 237: mib[1] = PF_ROUTE;
1.1 markus 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));
1.12 canacar 253: num_ifs = 0;
1.1 markus 254:
255: lim = buf + need;
1.2 deraadt 256: for (next = buf; next < lim; next += ifm.ifm_msglen) {
257: bcopy(next, &ifm, sizeof ifm);
1.14 claudio 258: if (ifm.ifm_version != RTM_VERSION ||
259: ifm.ifm_type != RTM_IFINFO ||
260: !(ifm.ifm_addrs & RTA_IFP))
1.1 markus 261: continue;
1.2 deraadt 262: if (ifm.ifm_index >= nifs) {
1.22 doug 263: if ((newstats = reallocarray(ifstats, ifm.ifm_index + 4,
264: sizeof(struct ifstat))) == NULL)
1.1 markus 265: continue;
266: ifstats = newstats;
1.2 deraadt 267: for (; nifs < ifm.ifm_index + 4; nifs++)
1.12 canacar 268: bzero(&ifstats[nifs], sizeof(*ifstats));
1.1 markus 269: }
1.2 deraadt 270: ifs = &ifstats[ifm.ifm_index];
1.1 markus 271: if (ifs->ifs_name[0] == '\0') {
272: bzero(&info, sizeof(info));
1.2 deraadt 273: rt_getaddrinfo(
274: (struct sockaddr *)((struct if_msghdr *)next + 1),
275: ifm.ifm_addrs, info);
1.13 deraadt 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);
1.3 dlg 301: }
1.1 markus 302: }
303: if (ifs->ifs_name[0] == '\0')
304: continue;
305: }
1.12 canacar 306: num_ifs++;
1.1 markus 307: UPDATE(ifc_ip, ifm_data.ifi_ipackets);
308: UPDATE(ifc_ib, ifm_data.ifi_ibytes);
309: UPDATE(ifc_ie, ifm_data.ifi_ierrors);
1.24 dlg 310: UPDATE(ifc_iq, ifm_data.ifi_iqdrops);
1.1 markus 311: UPDATE(ifc_op, ifm_data.ifi_opackets);
312: UPDATE(ifc_ob, ifm_data.ifi_obytes);
313: UPDATE(ifc_oe, ifm_data.ifi_oerrors);
1.24 dlg 314: UPDATE(ifc_oq, ifm_data.ifi_oqdrops);
1.1 markus 315: UPDATE(ifc_co, ifm_data.ifi_collisions);
1.7 claudio 316: ifs->ifs_cur.ifc_flags = ifm.ifm_flags;
317: ifs->ifs_cur.ifc_state = ifm.ifm_data.ifi_link_state;
1.17 canacar 318: ifs->ifs_flag++;
1.1 markus 319: }
1.17 canacar 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:
1.1 markus 330: free(buf);
331: }
332:
333:
1.12 canacar 334: static void
335: showifstat(struct ifstat *ifs)
1.1 markus 336: {
1.20 mpf 337: int conv = show_bits ? 8 : 1;
338: int div = show_bits ? 1000 : 1024;
339:
1.12 canacar 340: print_fld_str(FLD_IF_IFACE, ifs->ifs_name);
1.1 markus 341:
1.12 canacar 342: tb_start();
343: tbprintf("%s", ifs->ifs_cur.ifc_flags & IFF_UP ?
344: "up" : "dn");
1.7 claudio 345:
1.12 canacar 346: switch (ifs->ifs_cur.ifc_state) {
1.7 claudio 347: case LINK_STATE_UP:
1.8 reyk 348: case LINK_STATE_HALF_DUPLEX:
349: case LINK_STATE_FULL_DUPLEX:
1.12 canacar 350: tbprintf(":U");
351: break;
1.7 claudio 352: case LINK_STATE_DOWN:
1.12 canacar 353: tbprintf (":D");
354: break;
1.7 claudio 355: }
1.12 canacar 356:
357: print_fld_tb(FLD_IF_STATE);
1.13 deraadt 358:
359: print_fld_str(FLD_IF_DESC, ifs->ifs_description);
1.12 canacar 360:
1.20 mpf 361: print_fld_sdiv(FLD_IF_IBYTES, ifs->ifs_cur.ifc_ib * conv, div);
1.12 canacar 362: print_fld_size(FLD_IF_IPKTS, ifs->ifs_cur.ifc_ip);
1.24 dlg 363: print_fld_size(FLD_IF_IERRS, if_err_view->icount(&ifs->ifs_cur));
1.12 canacar 364:
1.20 mpf 365: print_fld_sdiv(FLD_IF_OBYTES, ifs->ifs_cur.ifc_ob * conv, div);
1.12 canacar 366: print_fld_size(FLD_IF_OPKTS, ifs->ifs_cur.ifc_op);
1.24 dlg 367: print_fld_size(FLD_IF_OERRS, if_err_view->ocount(&ifs->ifs_cur));
1.12 canacar 368:
369: print_fld_size(FLD_IF_COLLS, ifs->ifs_cur.ifc_co);
370:
371: end_line();
1.7 claudio 372: }
1.6 deraadt 373:
1.12 canacar 374: static void
375: showtotal(void)
1.1 markus 376: {
1.20 mpf 377: int conv = show_bits ? 8 : 1;
378: int div = show_bits ? 1000 : 1024;
379:
1.12 canacar 380: print_fld_str(FLD_IF_IFACE, "Totals");
381:
1.20 mpf 382: print_fld_sdiv(FLD_IF_IBYTES, sum.ifc_ib * conv, div);
1.12 canacar 383: print_fld_size(FLD_IF_IPKTS, sum.ifc_ip);
1.24 dlg 384: print_fld_size(FLD_IF_IERRS, if_err_view->icount(&sum));
1.12 canacar 385:
1.20 mpf 386: print_fld_sdiv(FLD_IF_OBYTES, sum.ifc_ob * conv, div);
1.12 canacar 387: print_fld_size(FLD_IF_OPKTS, sum.ifc_op);
1.24 dlg 388: print_fld_size(FLD_IF_OERRS, if_err_view->ocount(&sum));
1.12 canacar 389:
390: print_fld_size(FLD_IF_COLLS, sum.ifc_co);
391:
392: end_line();
1.1 markus 393:
394: }
395:
1.24 dlg 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;
1.25 ! dlg 437: FLD_IF_OERRS->title = if_err_view->oname;
1.24 dlg 438: gotsig_alarm = 1;
439: }
440:
1.1 markus 441: int
1.12 canacar 442: if_keyboard_callback(int ch)
1.1 markus 443: {
444: struct ifstat *ifs;
445:
1.12 canacar 446: switch (ch) {
1.24 dlg 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:
1.12 canacar 457: case 'r':
458: for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
459: ifs->ifs_old = ifs->ifs_now;
1.1 markus 460: state = RUN;
1.12 canacar 461: gotsig_alarm = 1;
462:
463: break;
464: case 'b':
1.1 markus 465: state = BOOT;
466: for (ifs = ifstats; ifs < ifstats + nifs; ifs++)
467: bzero(&ifs->ifs_old, sizeof(ifs->ifs_old));
1.20 mpf 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: }
1.12 canacar 479: gotsig_alarm = 1;
480: break;
481: case 't':
1.1 markus 482: state = TIME;
1.12 canacar 483: gotsig_alarm = 1;
484: break;
485: default:
486: return keyboard_callback(ch);
487: };
488:
489: return 1;
1.1 markus 490: }
1.12 canacar 491: