1/* $OpenBSD: if.c,v 1.81 2023/03/08 04:43:12 guenther Exp $ */ 2/* $NetBSD: if.c,v 1.16.4.2 1996/06/07 21:46:46 thorpej Exp $ */ 3 4/* 5 * Copyright (c) 1983, 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include <sys/types.h> 34#include <sys/ioctl.h> 35#include <sys/protosw.h> 36#include <sys/socket.h> 37#include <sys/sysctl.h> 38 39#include <net/if.h> 40#include <net/if_dl.h> 41#include <net/if_types.h> 42#include <net/route.h> 43#include <netinet/in.h> 44#include <netinet/in_var.h> 45#include <netinet/if_ether.h> 46#include <arpa/inet.h> 47 48#include <err.h> 49#include <limits.h> 50#include <signal.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <string.h> 54#include <unistd.h> 55#include <util.h> 56 57#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 58 59#include "netstat.h" 60 61static void print_addr(struct sockaddr *, struct sockaddr **, struct if_data *); 62static void sidewaysintpr(u_int, int); 63static void catchalarm(int); 64static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 65static void fetchifs(void); 66 67struct iftot; 68 69struct if_show_err { 70 const char *name; 71 const char *iname; 72 const char *oname; 73 uint64_t (*count)(uint64_t, uint64_t); 74}; 75 76static uint64_t if_show_fails(uint64_t, uint64_t); 77static uint64_t if_show_errors(uint64_t, uint64_t); 78static uint64_t if_show_qdrops(uint64_t, uint64_t); 79 80static const struct if_show_err if_show_errs[] = { 81 [IF_SHOW_FAIL] = { "fails", "Ifail", "Ofail", if_show_fails }, 82 [IF_SHOW_ERRS] = { "errs", "Ierrs", "Oerrs", if_show_errors }, 83 [IF_SHOW_DROP] = { "drops", "Idrop", "Odrop", if_show_qdrops }, 84}; 85static const struct if_show_err *if_errs = if_show_errs; 86 87/* 88 * Print a description of the network interfaces. 89 */ 90void 91intpr(int interval, int repeatcount) 92{ 93 struct if_msghdr ifm; 94 int mib[6] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0 }; 95 char name[IFNAMSIZ + 1]; /* + 1 for the '*' */ 96 char *buf = NULL, *next, *lim, *cp; 97 struct rt_msghdr *rtm; 98 struct ifa_msghdr *ifam; 99 struct if_data *ifd; 100 struct sockaddr *sa, *rti_info[RTAX_MAX]; 101 struct sockaddr_dl *sdl; 102 u_int64_t total = 0; 103 size_t len; 104 105 if_errs = &if_show_errs[dflag]; 106 107 if (interval) { 108 sidewaysintpr((unsigned)interval, repeatcount); 109 return; 110 } 111 112 len = get_sysctl(mib, 6, &buf); 113 114 printf("%-7.7s %-5.5s %-11.11s %-17.17s ", 115 "Name", "Mtu", "Network", "Address"); 116 if (bflag) 117 printf("%10.10s %10.10s", "Ibytes", "Obytes"); 118 else { 119 printf("%8.8s %5.5s %8.8s %5.5s %5.5s", 120 "Ipkts", if_errs->iname, 121 "Opkts", if_errs->oname, "Colls"); 122 } 123 putchar('\n'); 124 125 lim = buf + len; 126 for (next = buf; next < lim; next += rtm->rtm_msglen) { 127 rtm = (struct rt_msghdr *)next; 128 if (rtm->rtm_version != RTM_VERSION) 129 continue; 130 switch (rtm->rtm_type) { 131 case RTM_IFINFO: 132 total = 0; 133 bcopy(next, &ifm, sizeof ifm); 134 ifd = &ifm.ifm_data; 135 136 sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); 137 get_rtaddrs(ifm.ifm_addrs, sa, rti_info); 138 139 sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; 140 if (sdl == NULL || sdl->sdl_family != AF_LINK) 141 continue; 142 bzero(name, sizeof(name)); 143 if (sdl->sdl_nlen >= IFNAMSIZ) 144 memcpy(name, sdl->sdl_data, IFNAMSIZ - 1); 145 else if (sdl->sdl_nlen > 0) 146 memcpy(name, sdl->sdl_data, sdl->sdl_nlen); 147 148 if (interface != 0 && strcmp(name, interface) != 0) 149 continue; 150 151 /* mark inactive interfaces with a '*' */ 152 cp = strchr(name, '\0'); 153 if ((ifm.ifm_flags & IFF_UP) == 0) 154 *cp++ = '*'; 155 *cp = '\0'; 156 157 if (qflag) { 158 total = ifd->ifi_ibytes + ifd->ifi_obytes + 159 ifd->ifi_ipackets + 160 ifd->ifi_opackets + 161 ifd->ifi_collisions; 162 total += if_errs->count(ifd->ifi_ierrors, 163 ifd->ifi_iqdrops); 164 total += if_errs->count(ifd->ifi_oerrors, 165 ifd->ifi_oqdrops); 166 if (total == 0) 167 continue; 168 } 169 170 printf("%-7s %-5d ", name, ifd->ifi_mtu); 171 print_addr(rti_info[RTAX_IFP], rti_info, ifd); 172 break; 173 case RTM_NEWADDR: 174 if (qflag && total == 0) 175 continue; 176 if (interface != 0 && strcmp(name, interface) != 0) 177 continue; 178 179 ifam = (struct ifa_msghdr *)next; 180 if ((ifam->ifam_addrs & (RTA_NETMASK | RTA_IFA | 181 RTA_BRD)) == 0) 182 break; 183 184 sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); 185 get_rtaddrs(ifam->ifam_addrs, sa, rti_info); 186 187 printf("%-7s %-5d ", name, ifd->ifi_mtu); 188 print_addr(rti_info[RTAX_IFA], rti_info, ifd); 189 break; 190 } 191 } 192 free(buf); 193} 194 195static void 196print_addr(struct sockaddr *sa, struct sockaddr **rtinfo, struct if_data *ifd) 197{ 198 struct sockaddr_dl *sdl; 199 struct sockaddr_in *sin; 200 struct sockaddr_in6 *sin6; 201 char *cp; 202 int m, n; 203 204 switch (sa->sa_family) { 205 case AF_UNSPEC: 206 printf("%-11.11s ", "none"); 207 printf("%-17.17s ", "none"); 208 break; 209 case AF_INET: 210 sin = (struct sockaddr_in *)sa; 211 cp = netname4(sin->sin_addr.s_addr, 212 ((struct sockaddr_in *)rtinfo[RTAX_NETMASK])->sin_addr.s_addr); 213 if (vflag) 214 n = strlen(cp) < 11 ? 11 : strlen(cp); 215 else 216 n = 11; 217 printf("%-*.*s ", n, n, cp); 218 cp = routename4(sin->sin_addr.s_addr); 219 if (vflag) 220 n = strlen(cp) < 17 ? 17 : strlen(cp); 221 else 222 n = 17; 223 printf("%-*.*s ", n, n, cp); 224 225 break; 226 case AF_INET6: 227 sin6 = (struct sockaddr_in6 *)sa; 228#ifdef __KAME__ 229 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) && 230 sin6->sin6_scope_id == 0) { 231 sin6->sin6_scope_id = 232 ntohs(*(u_int16_t *) 233 &sin6->sin6_addr.s6_addr[2]); 234 sin6->sin6_addr.s6_addr[2] = 0; 235 sin6->sin6_addr.s6_addr[3] = 0; 236 } 237#endif 238 cp = netname6(sin6, 239 (struct sockaddr_in6 *)rtinfo[RTAX_NETMASK]); 240 if (vflag) 241 n = strlen(cp) < 11 ? 11 : strlen(cp); 242 else 243 n = 11; 244 printf("%-*.*s ", n, n, cp); 245 cp = routename6(sin6); 246 if (vflag) 247 n = strlen(cp) < 17 ? 17 : strlen(cp); 248 else 249 n = 17; 250 printf("%-*.*s ", n, n, cp); 251 break; 252 case AF_LINK: 253 sdl = (struct sockaddr_dl *)sa; 254 m = printf("%-11.11s ", "<Link>"); 255 if (sdl->sdl_type == IFT_ETHER || 256 sdl->sdl_type == IFT_CARP || 257 sdl->sdl_type == IFT_FDDI || 258 sdl->sdl_type == IFT_ISO88025) 259 printf("%-17.17s ", 260 ether_ntoa((struct ether_addr *)LLADDR(sdl))); 261 else { 262 cp = (char *)LLADDR(sdl); 263 n = sdl->sdl_alen; 264 goto hexprint; 265 } 266 break; 267 default: 268 m = printf("(%d)", sa->sa_family); 269 for (cp = sa->sa_len + (char *)sa; 270 --cp > sa->sa_data && (*cp == 0);) {} 271 n = cp - sa->sa_data + 1; 272 cp = sa->sa_data; 273hexprint: 274 while (--n >= 0) 275 m += printf("%x%c", *cp++ & 0xff, 276 n > 0 ? '.' : ' '); 277 m = 30 - m; 278 while (m-- > 0) 279 putchar(' '); 280 break; 281 } 282 if (bflag) { 283 if (hflag) { 284 char ibytes[FMT_SCALED_STRSIZE]; 285 char obytes[FMT_SCALED_STRSIZE]; 286 fmt_scaled(ifd->ifi_ibytes, ibytes); 287 fmt_scaled(ifd->ifi_obytes, obytes); 288 printf("%10s %10s", ibytes, obytes); 289 } else 290 printf("%10llu %10llu", 291 ifd->ifi_ibytes, ifd->ifi_obytes); 292 } else 293 printf("%8llu %5llu %8llu %5llu %5llu", 294 ifd->ifi_ipackets, 295 if_errs->count(ifd->ifi_ierrors, ifd->ifi_iqdrops), 296 ifd->ifi_opackets, 297 if_errs->count(ifd->ifi_oerrors, ifd->ifi_oqdrops), 298 ifd->ifi_collisions); 299 putchar('\n'); 300} 301 302struct iftot { 303 char ift_name[IFNAMSIZ]; /* interface name */ 304 u_int64_t ift_ip; /* input packets */ 305 u_int64_t ift_ib; /* input bytes */ 306 u_int64_t ift_ie; /* input errors */ 307 u_int64_t ift_iq; /* input qdrops */ 308 u_int64_t ift_op; /* output packets */ 309 u_int64_t ift_ob; /* output bytes */ 310 u_int64_t ift_oe; /* output errors */ 311 u_int64_t ift_oq; /* output qdrops */ 312 u_int64_t ift_co; /* collisions */ 313} ip_cur, ip_old, sum_cur, sum_old; 314 315volatile sig_atomic_t signalled; /* set if alarm goes off "early" */ 316 317/* 318 * Print a running summary of interface statistics. 319 * Repeat display every interval seconds, showing statistics 320 * collected over that interval. Assumes that interval is non-zero. 321 * First line printed at top of screen is always cumulative. 322 */ 323static void 324sidewaysintpr(unsigned int interval, int repeatcount) 325{ 326 sigset_t emptyset; 327 int line; 328 char ibytes[FMT_SCALED_STRSIZE]; 329 char obytes[FMT_SCALED_STRSIZE]; 330 331 fetchifs(); 332 if (ip_cur.ift_name[0] == '\0') { 333 fprintf(stderr, "%s: %s: unknown interface\n", 334 __progname, interface); 335 exit(1); 336 } 337 338 (void)signal(SIGALRM, catchalarm); 339 signalled = 0; 340 (void)alarm(interval); 341banner: 342 if (bflag) 343 printf("%7.7s in %8.8s %6.6s out %5.5s", 344 ip_cur.ift_name, " ", 345 ip_cur.ift_name, " "); 346 else 347 printf("%5.5s in %5.5s%5.5s out %5.5s %5.5s", 348 ip_cur.ift_name, " ", 349 ip_cur.ift_name, " ", " "); 350 351 if (bflag) 352 printf(" %7.7s in %8.8s %6.6s out %5.5s", 353 "total", " ", "total", " "); 354 else 355 printf(" %5.5s in %5.5s%5.5s out %5.5s %5.5s", 356 "total", " ", "total", " ", " "); 357 putchar('\n'); 358 if (bflag) 359 printf("%10.10s %8.8s %10.10s %5.5s", 360 "bytes", " ", "bytes", " "); 361 else 362 printf("%8.8s %5.5s %8.8s %5.5s %5.5s", 363 "packets", if_errs->name, 364 "packets", if_errs->name, "colls"); 365 366 if (bflag) 367 printf("%10.10s %8.8s %10.10s %5.5s", 368 "bytes", " ", "bytes", " "); 369 else 370 printf(" %8.8s %5.5s %8.8s %5.5s %5.5s", 371 "packets", "errs", "packets", "errs", "colls"); 372 putchar('\n'); 373 fflush(stdout); 374 line = 0; 375 bzero(&ip_old, sizeof(ip_old)); 376 bzero(&sum_old, sizeof(sum_old)); 377loop: 378 bzero(&sum_cur, sizeof(sum_cur)); 379 380 fetchifs(); 381 382 if (bflag) { 383 if (hflag) { 384 fmt_scaled(ip_cur.ift_ib - ip_old.ift_ib, ibytes); 385 fmt_scaled(ip_cur.ift_ob - ip_old.ift_ob, obytes); 386 printf("%10s %8.8s %10s %5.5s", 387 ibytes, " ", obytes, " "); 388 } else 389 printf("%10llu %8.8s %10llu %5.5s", 390 ip_cur.ift_ib - ip_old.ift_ib, " ", 391 ip_cur.ift_ob - ip_old.ift_ob, " "); 392 } else 393 printf("%8llu %5llu %8llu %5llu %5llu", 394 ip_cur.ift_ip - ip_old.ift_ip, 395 if_errs->count(ip_cur.ift_ie - ip_old.ift_ie, 396 ip_cur.ift_iq - ip_old.ift_iq), 397 ip_cur.ift_op - ip_old.ift_op, 398 if_errs->count(ip_cur.ift_oe - ip_old.ift_oe, 399 ip_cur.ift_oq - ip_old.ift_oq), 400 ip_cur.ift_co - ip_old.ift_co); 401 402 ip_old = ip_cur; 403 404 if (bflag) { 405 if (hflag) { 406 fmt_scaled(sum_cur.ift_ib - sum_old.ift_ib, ibytes); 407 fmt_scaled(sum_cur.ift_ob - sum_old.ift_ob, obytes); 408 printf("%10s %8.8s %10s %5.5s", 409 ibytes, " ", obytes, " "); 410 } else 411 printf("%10llu %8.8s %10llu %5.5s", 412 sum_cur.ift_ib - sum_old.ift_ib, " ", 413 sum_cur.ift_ob - sum_old.ift_ob, " "); 414 } else 415 printf("%8llu %5llu %8llu %5llu %5llu", 416 sum_cur.ift_ip - sum_old.ift_ip, 417 if_errs->count(sum_cur.ift_ie - sum_old.ift_ie, 418 sum_cur.ift_iq - sum_old.ift_iq), 419 sum_cur.ift_op - sum_old.ift_op, 420 if_errs->count(sum_cur.ift_oe - sum_old.ift_oe, 421 sum_cur.ift_oq - sum_old.ift_oq), 422 sum_cur.ift_co - sum_old.ift_co); 423 424 sum_old = sum_cur; 425 426 putchar('\n'); 427 fflush(stdout); 428 if (repeatcount && --repeatcount == 0) 429 return; 430 line++; 431 sigemptyset(&emptyset); 432 if (!signalled) 433 sigsuspend(&emptyset); 434 signalled = 0; 435 (void)alarm(interval); 436 if (line == 21 && isatty(STDOUT_FILENO)) 437 goto banner; 438 goto loop; 439} 440 441/* 442 * Called if an interval expires before sidewaysintpr has completed a loop. 443 * Sets a flag to not wait for the alarm. 444 */ 445static void 446catchalarm(int signo) 447{ 448 signalled = 1; 449} 450 451static void 452get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 453{ 454 int i; 455 456 for (i = 0; i < RTAX_MAX; i++) { 457 if (addrs & (1 << i)) { 458 rti_info[i] = sa; 459 sa = (struct sockaddr *)((char *)(sa) + 460 roundup(sa->sa_len, sizeof(long))); 461 } else 462 rti_info[i] = NULL; 463 } 464} 465 466 467static int 468isegress(char *name) 469{ 470 static int s = -1; 471 int len; 472 struct ifgroupreq ifgr; 473 struct ifg_req *ifg; 474 int rv = 0; 475 476 if (s == -1) { 477 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 478 return 0; 479 } 480 481 memset(&ifgr, 0, sizeof(ifgr)); 482 strlcpy(ifgr.ifgr_name, name, IFNAMSIZ); 483 484 if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { 485 return 0; 486 } 487 488 len = ifgr.ifgr_len; 489 ifgr.ifgr_groups = calloc(len, 1); 490 if (ifgr.ifgr_groups == NULL) 491 err(1, "getifgroups"); 492 if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) 493 err(1, "SIOCGIFGROUP"); 494 495 ifg = ifgr.ifgr_groups; 496 for (; ifg && len >= sizeof(struct ifg_req); ifg++) { 497 len -= sizeof(struct ifg_req); 498 if (strcmp(ifg->ifgrq_group, IFG_EGRESS) == 0) 499 rv = 1; 500 } 501 502 free(ifgr.ifgr_groups); 503 return rv; 504} 505 506static void 507fetchifs(void) 508{ 509 struct if_msghdr ifm; 510 int mib[6] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0 }; 511 struct rt_msghdr *rtm; 512 struct if_data *ifd; 513 struct sockaddr *sa, *rti_info[RTAX_MAX]; 514 struct sockaddr_dl *sdl; 515 char *buf = NULL, *next, *lim; 516 char name[IFNAMSIZ]; 517 size_t len; 518 int takeit = 0; 519 int foundone = 0; 520 521 len = get_sysctl(mib, 6, &buf); 522 523 memset(&ip_cur, 0, sizeof(ip_cur)); 524 lim = buf + len; 525 for (next = buf; next < lim; next += rtm->rtm_msglen) { 526 rtm = (struct rt_msghdr *)next; 527 if (rtm->rtm_version != RTM_VERSION) 528 continue; 529 switch (rtm->rtm_type) { 530 case RTM_IFINFO: 531 bcopy(next, &ifm, sizeof ifm); 532 ifd = &ifm.ifm_data; 533 534 sa = (struct sockaddr *)(next + rtm->rtm_hdrlen); 535 get_rtaddrs(ifm.ifm_addrs, sa, rti_info); 536 537 sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; 538 if (sdl == NULL || sdl->sdl_family != AF_LINK) 539 continue; 540 bzero(name, sizeof(name)); 541 if (sdl->sdl_nlen >= IFNAMSIZ) 542 memcpy(name, sdl->sdl_data, IFNAMSIZ - 1); 543 else if (sdl->sdl_nlen > 0) 544 memcpy(name, sdl->sdl_data, sdl->sdl_nlen); 545 546 if (interface != NULL && !strcmp(name, interface)) { 547 takeit = 1; 548 } else if (interface == NULL && foundone == 0 && 549 isegress(name)) { 550 takeit = 1; 551 foundone = 1; 552 } else 553 takeit = 0; 554 if (takeit) { 555 strlcpy(ip_cur.ift_name, name, 556 sizeof(ip_cur.ift_name)); 557 ip_cur.ift_ip = ifd->ifi_ipackets; 558 ip_cur.ift_ib = ifd->ifi_ibytes; 559 ip_cur.ift_ie = ifd->ifi_ierrors; 560 ip_cur.ift_iq = ifd->ifi_iqdrops; 561 ip_cur.ift_op = ifd->ifi_opackets; 562 ip_cur.ift_ob = ifd->ifi_obytes; 563 ip_cur.ift_oe = ifd->ifi_oerrors; 564 ip_cur.ift_oq = ifd->ifi_oqdrops; 565 ip_cur.ift_co = ifd->ifi_collisions; 566 } 567 568 sum_cur.ift_ip += ifd->ifi_ipackets; 569 sum_cur.ift_ib += ifd->ifi_ibytes; 570 sum_cur.ift_ie += ifd->ifi_ierrors; 571 sum_cur.ift_iq += ifd->ifi_iqdrops; 572 sum_cur.ift_op += ifd->ifi_opackets; 573 sum_cur.ift_ob += ifd->ifi_obytes; 574 sum_cur.ift_oe += ifd->ifi_oerrors; 575 sum_cur.ift_oq += ifd->ifi_oqdrops; 576 sum_cur.ift_co += ifd->ifi_collisions; 577 break; 578 } 579 } 580 if (interface == NULL && foundone == 0) { 581 strlcpy(ip_cur.ift_name, name, 582 sizeof(ip_cur.ift_name)); 583 ip_cur.ift_ip = ifd->ifi_ipackets; 584 ip_cur.ift_ib = ifd->ifi_ibytes; 585 ip_cur.ift_ie = ifd->ifi_ierrors; 586 ip_cur.ift_iq = ifd->ifi_iqdrops; 587 ip_cur.ift_op = ifd->ifi_opackets; 588 ip_cur.ift_ob = ifd->ifi_obytes; 589 ip_cur.ift_oe = ifd->ifi_oerrors; 590 ip_cur.ift_oq = ifd->ifi_oqdrops; 591 ip_cur.ift_co = ifd->ifi_collisions; 592 } 593 free(buf); 594} 595 596static uint64_t 597if_show_fails(uint64_t errors, uint64_t qdrops) 598{ 599 return (errors + qdrops); 600} 601 602static uint64_t 603if_show_errors(uint64_t errors, uint64_t qdrops) 604{ 605 return (errors); 606} 607 608static uint64_t 609if_show_qdrops(uint64_t errors, uint64_t qdrops) 610{ 611 return (qdrops); 612} 613