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