1 /* $OpenBSD: if.c,v 1.27 2021/11/29 06:39:23 deraadt 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 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 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 194 select_if(void) 195 { 196 num_disp = num_ifs + 1; 197 return (0); 198 } 199 200 int 201 read_if(void) 202 { 203 fetchifstat(); 204 num_disp = num_ifs + 1; 205 206 return 0; 207 } 208 209 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 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 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 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 401 if_err_ifails(const struct ifcount *ifc) 402 { 403 return (ifc->ifc_ie + ifc->ifc_iq); 404 } 405 406 static uint64_t 407 if_err_ofails(const struct ifcount *ifc) 408 { 409 return (ifc->ifc_oe + ifc->ifc_oq); 410 } 411 412 static uint64_t 413 if_err_ierrors(const struct ifcount *ifc) 414 { 415 return (ifc->ifc_ie); 416 } 417 418 static uint64_t 419 if_err_oerrors(const struct ifcount *ifc) 420 { 421 return (ifc->ifc_oe); 422 } 423 424 static uint64_t 425 if_err_iqdrops(const struct ifcount *ifc) 426 { 427 return (ifc->ifc_iq); 428 } 429 430 static uint64_t 431 if_err_oqdrops(const struct ifcount *ifc) 432 { 433 return (ifc->ifc_oq); 434 } 435 436 static void 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 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