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