1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <strings.h> 29 #include <err.h> 30 #include <errno.h> 31 #include <kstat.h> 32 #include <unistd.h> 33 #include <signal.h> 34 #include <sys/dld.h> 35 36 #include <libdllink.h> 37 #include <libdlflow.h> 38 #include <libdlstat.h> 39 40 /* 41 * x86 <sys/regs> ERR conflicts with <curses.h> ERR. 42 * Include curses.h last. 43 */ 44 #if defined(ERR) 45 #undef ERR 46 #endif 47 #include <curses.h> 48 49 struct flowlist { 50 char flowname[MAXNAMELEN]; 51 datalink_id_t linkid; 52 uint_t ifspeed; 53 boolean_t first; 54 boolean_t display; 55 pktsum_t prevstats; 56 pktsum_t diffstats; 57 }; 58 59 static int maxx, maxy, redraw = 0; 60 static volatile uint_t handle_resize = 0, handle_break = 0; 61 62 pktsum_t totalstats; 63 struct flowlist *stattable = NULL; 64 static int statentry = -1, maxstatentries = 0; 65 66 #define STATGROWSIZE 16 67 68 69 /* 70 * Search for flowlist entry in stattable which matches 71 * the flowname and linkide. If no match is found, use 72 * next available slot. If no slots are available, 73 * reallocate table with more slots. 74 * 75 * Return: *flowlist of matching flow 76 * NULL if realloc fails 77 */ 78 79 static struct flowlist * 80 findstat(const char *flowname, datalink_id_t linkid) 81 { 82 int match = 0; 83 struct flowlist *flist; 84 85 /* Look for match in the stattable */ 86 for (match = 0, flist = stattable; 87 match <= statentry; 88 match++, flist++) { 89 90 if (flist == NULL) 91 break; 92 /* match the flowname */ 93 if (flowname != NULL) { 94 if (strncmp(flowname, flist->flowname, MAXNAMELEN) 95 == NULL) 96 return (flist); 97 /* match the linkid */ 98 } else { 99 if (linkid == flist->linkid) 100 return (flist); 101 } 102 } 103 104 /* 105 * No match found in the table. Store statistics in the next slot. 106 * If necessary, make room for this entry. 107 */ 108 statentry++; 109 if ((maxstatentries == 0) || (maxstatentries == statentry)) { 110 maxstatentries += STATGROWSIZE; 111 stattable = realloc(stattable, 112 maxstatentries * sizeof (struct flowlist)); 113 if (stattable == NULL) { 114 perror("realloc"); 115 return (struct flowlist *)(NULL); 116 } 117 } 118 flist = &stattable[statentry]; 119 bzero(flist, sizeof (struct flowlist)); 120 flist->first = B_TRUE; 121 122 if (flowname != NULL) 123 (void) strncpy(flist->flowname, flowname, MAXNAMELEN); 124 flist->linkid = linkid; 125 return (flist); 126 } 127 128 static void 129 print_flow_stats(struct flowlist *flist) 130 { 131 struct flowlist *fcurr; 132 double ikbs, okbs; 133 double ipks, opks; 134 double dlt; 135 int fcount; 136 static boolean_t first = B_TRUE; 137 138 if (first) { 139 first = B_FALSE; 140 (void) printw("please wait...\n"); 141 return; 142 } 143 144 for (fcount = 0, fcurr = flist; 145 fcount <= statentry; 146 fcount++, fcurr++) { 147 if (fcurr->flowname && fcurr->display) { 148 char linkname[MAXNAMELEN]; 149 150 (void) dladm_datalink_id2info(fcurr->linkid, NULL, NULL, 151 NULL, linkname, sizeof (linkname)); 152 dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; 153 ikbs = fcurr->diffstats.rbytes * 8 / dlt / 1024; 154 okbs = fcurr->diffstats.obytes * 8 / dlt / 1024; 155 ipks = fcurr->diffstats.ipackets / dlt; 156 opks = fcurr->diffstats.opackets / dlt; 157 (void) printw("%-15.15s", fcurr->flowname); 158 (void) printw("%-10.10s", linkname); 159 (void) printw("%9.2f %9.2f %9.2f %9.2f ", 160 ikbs, okbs, ipks, opks); 161 (void) printw("\n"); 162 } 163 } 164 } 165 166 /*ARGSUSED*/ 167 static int 168 flow_kstats(dladm_flow_attr_t *attr, void *arg) 169 { 170 kstat_ctl_t *kcp = (kstat_ctl_t *)arg; 171 kstat_t *ksp; 172 struct flowlist *flist; 173 pktsum_t currstats, *prevstats, *diffstats; 174 175 flist = findstat(attr->fa_flowname, attr->fa_linkid); 176 if (flist != NULL) { 177 prevstats = &flist->prevstats; 178 diffstats = &flist->diffstats; 179 } else { 180 return (DLADM_STATUS_FAILED); 181 } 182 183 /* lookup kstat entry */ 184 ksp = dladm_kstat_lookup(kcp, NULL, -1, attr->fa_flowname, "flow"); 185 186 if (ksp == NULL) 187 return (DLADM_WALK_TERMINATE); 188 else 189 flist->display = B_TRUE; 190 191 dladm_get_stats(kcp, ksp, &currstats); 192 if (flist->ifspeed == 0) 193 (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, 194 &flist->ifspeed); 195 196 if (flist->first) 197 flist->first = B_FALSE; 198 else { 199 dladm_stats_diff(diffstats, &currstats, prevstats); 200 dladm_stats_total(&totalstats, diffstats, &totalstats); 201 } 202 203 bcopy(&currstats, prevstats, sizeof (pktsum_t)); 204 return (DLADM_WALK_CONTINUE); 205 } 206 207 static void 208 print_link_stats(struct flowlist *flist) 209 { 210 struct flowlist *fcurr; 211 double ikbs, okbs; 212 double ipks, opks; 213 double util; 214 double dlt; 215 int fcount; 216 static boolean_t first = B_TRUE; 217 218 if (first) { 219 first = B_FALSE; 220 (void) printw("please wait...\n"); 221 return; 222 } 223 224 for (fcount = 0, fcurr = flist; 225 fcount <= statentry; 226 fcount++, fcurr++) { 227 if ((fcurr->linkid != DATALINK_INVALID_LINKID) && 228 fcurr->display) { 229 char linkname[MAXNAMELEN]; 230 231 (void) dladm_datalink_id2info(fcurr->linkid, NULL, NULL, 232 NULL, linkname, sizeof (linkname)); 233 dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; 234 ikbs = (double)fcurr->diffstats.rbytes * 8 / dlt / 1024; 235 okbs = (double)fcurr->diffstats.obytes * 8 / dlt / 1024; 236 ipks = (double)fcurr->diffstats.ipackets / dlt; 237 opks = (double)fcurr->diffstats.opackets / dlt; 238 (void) printw("%-10.10s", linkname); 239 (void) printw("%9.2f %9.2f %9.2f %9.2f ", 240 ikbs, okbs, ipks, opks); 241 if (fcurr->ifspeed != 0) 242 util = ((ikbs + okbs) * 1024) * 243 100/ fcurr->ifspeed; 244 else 245 util = (double)0; 246 (void) attron(A_BOLD); 247 (void) printw(" %6.2f", util); 248 (void) attroff(A_BOLD); 249 (void) printw("\n"); 250 } 251 } 252 } 253 254 /* 255 * This function is called through the dladm_walk_datalink_id() walker and 256 * calls the dladm_walk_flow() walker. 257 */ 258 259 /*ARGSUSED*/ 260 static int 261 link_flowstats(datalink_id_t linkid, void *arg) 262 { 263 return (dladm_walk_flow(flow_kstats, linkid, arg, B_FALSE)); 264 } 265 266 /*ARGSUSED*/ 267 static int 268 link_kstats(datalink_id_t linkid, void *arg) 269 { 270 kstat_ctl_t *kcp = (kstat_ctl_t *)arg; 271 struct flowlist *flist; 272 pktsum_t currstats, *prevstats, *diffstats; 273 kstat_t *ksp; 274 char linkname[MAXNAMELEN]; 275 276 /* find the flist entry */ 277 flist = findstat(NULL, linkid); 278 if (flist != NULL) { 279 prevstats = &flist->prevstats; 280 diffstats = &flist->diffstats; 281 } else { 282 return (DLADM_WALK_CONTINUE); 283 } 284 285 /* lookup kstat entry */ 286 (void) dladm_datalink_id2info(linkid, NULL, NULL, NULL, linkname, 287 sizeof (linkname)); 288 289 if (linkname == NULL) { 290 warn("no linkname for linkid"); 291 return (DLADM_WALK_TERMINATE); 292 } 293 294 ksp = dladm_kstat_lookup(kcp, NULL, -1, linkname, "net"); 295 296 if (ksp == NULL) 297 return (DLADM_WALK_TERMINATE); 298 else 299 flist->display = B_TRUE; 300 301 /* read packet and byte stats */ 302 dladm_get_stats(kcp, ksp, &currstats); 303 304 if (flist->ifspeed == 0) 305 (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, 306 &flist->ifspeed); 307 308 if (flist->first == B_TRUE) 309 flist->first = B_FALSE; 310 else 311 dladm_stats_diff(diffstats, &currstats, prevstats); 312 313 bcopy(&currstats, prevstats, sizeof (*prevstats)); 314 315 return (DLADM_WALK_CONTINUE); 316 } 317 318 /*ARGSUSED*/ 319 static void 320 sig_break(int s) 321 { 322 handle_break = 1; 323 } 324 325 /*ARGSUSED*/ 326 static void 327 sig_resize(int s) 328 { 329 handle_resize = 1; 330 } 331 332 static void 333 curses_init() 334 { 335 maxx = maxx; /* lint */ 336 maxy = maxy; /* lint */ 337 338 /* Install signal handlers */ 339 (void) signal(SIGINT, sig_break); 340 (void) signal(SIGQUIT, sig_break); 341 (void) signal(SIGTERM, sig_break); 342 (void) signal(SIGWINCH, sig_resize); 343 344 /* Initialize ncurses */ 345 (void) initscr(); 346 (void) cbreak(); 347 (void) noecho(); 348 (void) curs_set(0); 349 timeout(0); 350 getmaxyx(stdscr, maxy, maxx); 351 } 352 353 static void 354 curses_fin() 355 { 356 (void) printw("\n"); 357 (void) curs_set(1); 358 (void) nocbreak(); 359 (void) endwin(); 360 361 free(stattable); 362 } 363 364 static void 365 stat_report(kstat_ctl_t *kcp, datalink_id_t linkid, const char *flowname, 366 int opt) 367 { 368 369 double dlt, ikbs, okbs, ipks, opks; 370 371 struct flowlist *fstable = stattable; 372 373 if ((opt != LINK_REPORT) && (opt != FLOW_REPORT)) 374 return; 375 376 /* Handle window resizes */ 377 if (handle_resize) { 378 (void) endwin(); 379 (void) initscr(); 380 (void) cbreak(); 381 (void) noecho(); 382 (void) curs_set(0); 383 timeout(0); 384 getmaxyx(stdscr, maxy, maxx); 385 redraw = 1; 386 handle_resize = 0; 387 } 388 389 /* Print title */ 390 (void) erase(); 391 (void) attron(A_BOLD); 392 (void) move(0, 0); 393 if (opt == FLOW_REPORT) 394 (void) printw("%-15.15s", "Flow"); 395 (void) printw("%-10.10s", "Link"); 396 (void) printw("%9.9s %9.9s %9.9s %9.9s ", 397 "iKb/s", "oKb/s", "iPk/s", "oPk/s"); 398 if (opt == LINK_REPORT) 399 (void) printw(" %6.6s", "%Util"); 400 (void) printw("\n"); 401 (void) attroff(A_BOLD); 402 403 (void) move(2, 0); 404 405 /* Print stats for each link or flow */ 406 bzero(&totalstats, sizeof (totalstats)); 407 if (opt == LINK_REPORT) { 408 /* Display all links */ 409 if (linkid == DATALINK_ALL_LINKID) { 410 (void) dladm_walk_datalink_id(link_kstats, 411 (void *)kcp, DATALINK_CLASS_ALL, 412 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 413 /* Display 1 link */ 414 } else { 415 (void) link_kstats(linkid, kcp); 416 } 417 print_link_stats(fstable); 418 419 } else if (opt == FLOW_REPORT) { 420 /* Display 1 flow */ 421 if (flowname != NULL) { 422 dladm_flow_attr_t fattr; 423 if (dladm_flow_info(flowname, &fattr) != 424 DLADM_STATUS_OK) 425 return; 426 (void) flow_kstats(&fattr, kcp); 427 /* Display all flows on all links */ 428 } else if (linkid == DATALINK_ALL_LINKID) { 429 (void) dladm_walk_datalink_id(link_flowstats, 430 (void *)kcp, DATALINK_CLASS_ALL, 431 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 432 /* Display all flows on a link */ 433 } else if (linkid != DATALINK_INVALID_LINKID) { 434 (void) dladm_walk_flow(flow_kstats, linkid, kcp, 435 B_FALSE); 436 } 437 print_flow_stats(fstable); 438 439 /* Print totals */ 440 (void) attron(A_BOLD); 441 dlt = (double)totalstats.snaptime / (double)NANOSEC; 442 ikbs = totalstats.rbytes / dlt / 1024; 443 okbs = totalstats.obytes / dlt / 1024; 444 ipks = totalstats.ipackets / dlt; 445 opks = totalstats.opackets / dlt; 446 (void) printw("\n%-25.25s", "Totals"); 447 (void) printw("%9.2f %9.2f %9.2f %9.2f ", 448 ikbs, okbs, ipks, opks); 449 (void) attroff(A_BOLD); 450 } 451 452 if (redraw) 453 (void) clearok(stdscr, 1); 454 455 if (refresh() == ERR) 456 return; 457 458 if (redraw) { 459 (void) clearok(stdscr, 0); 460 redraw = 0; 461 } 462 } 463 464 /* Exported functions */ 465 466 /* 467 * Continuously display link or flow statstics using a libcurses 468 * based display. 469 */ 470 471 void 472 dladm_continuous(datalink_id_t linkid, const char *flowname, int interval, 473 int opt) 474 { 475 kstat_ctl_t *kcp; 476 477 if ((kcp = kstat_open()) == NULL) { 478 warn("kstat open operation failed"); 479 return; 480 } 481 482 curses_init(); 483 484 for (;;) { 485 486 if (handle_break) 487 break; 488 489 stat_report(kcp, linkid, flowname, opt); 490 491 (void) sleep(max(1, interval)); 492 } 493 494 (void) curses_fin(); 495 (void) kstat_close(kcp); 496 } 497 498 /* 499 * dladm_kstat_lookup() is a modified version of kstat_lookup which 500 * adds the class as a selector. 501 */ 502 503 kstat_t * 504 dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance, 505 const char *name, const char *class) 506 { 507 kstat_t *ksp = NULL; 508 509 for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { 510 if ((module == NULL || strcmp(ksp->ks_module, module) == 0) && 511 (instance == -1 || ksp->ks_instance == instance) && 512 (name == NULL || strcmp(ksp->ks_name, name) == 0) && 513 (class == NULL || strcmp(ksp->ks_class, class) == 0)) 514 return (ksp); 515 } 516 517 errno = ENOENT; 518 return (NULL); 519 } 520 521 /* 522 * dladm_get_stats() populates the supplied pktsum_t structure with 523 * the input and output packet and byte kstats from the kstat_t 524 * found with dladm_kstat_lookup. 525 */ 526 void 527 dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats) 528 { 529 530 if (kstat_read(kcp, ksp, NULL) == -1) 531 return; 532 533 stats->snaptime = gethrtime(); 534 535 if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64, 536 &stats->ipackets) < 0) { 537 if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64, 538 &stats->ipackets) < 0) 539 return; 540 } 541 542 if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64, 543 &stats->opackets) < 0) { 544 if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64, 545 &stats->opackets) < 0) 546 return; 547 } 548 549 if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64, 550 &stats->rbytes) < 0) { 551 if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64, 552 &stats->rbytes) < 0) 553 return; 554 } 555 556 if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64, 557 &stats->obytes) < 0) { 558 if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64, 559 &stats->obytes) < 0) 560 return; 561 } 562 563 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32, 564 &stats->ierrors) < 0) { 565 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64, 566 &stats->ierrors) < 0) 567 return; 568 } 569 570 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32, 571 &stats->oerrors) < 0) { 572 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64, 573 &stats->oerrors) < 0) 574 return; 575 } 576 } 577 578 int 579 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) 580 { 581 kstat_named_t *knp; 582 583 if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) 584 return (-1); 585 586 if (knp->data_type != type) 587 return (-1); 588 589 switch (type) { 590 case KSTAT_DATA_UINT64: 591 *(uint64_t *)buf = knp->value.ui64; 592 break; 593 case KSTAT_DATA_UINT32: 594 *(uint32_t *)buf = knp->value.ui32; 595 break; 596 default: 597 return (-1); 598 } 599 600 return (0); 601 } 602 603 dladm_status_t 604 dladm_get_single_mac_stat(datalink_id_t linkid, const char *name, uint8_t type, 605 void *val) 606 { 607 kstat_ctl_t *kcp; 608 char module[DLPI_LINKNAME_MAX]; 609 uint_t instance; 610 char link[DLPI_LINKNAME_MAX]; 611 dladm_status_t status; 612 uint32_t flags, media; 613 kstat_t *ksp; 614 dladm_phys_attr_t dpap; 615 616 if ((kcp = kstat_open()) == NULL) { 617 warn("kstat_open operation failed"); 618 return (-1); 619 } 620 621 if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media, 622 link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK) 623 return (status); 624 625 if (media != DL_ETHER) 626 return (DLADM_STATUS_LINKINVAL); 627 628 status = dladm_phys_info(linkid, &dpap, DLADM_OPT_PERSIST); 629 630 if (status != DLADM_STATUS_OK) 631 return (status); 632 633 status = dladm_parselink(dpap.dp_dev, module, &instance); 634 635 if (status != DLADM_STATUS_OK) 636 return (status); 637 638 /* 639 * The kstat query could fail if the underlying MAC 640 * driver was already detached. 641 */ 642 if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && 643 (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) 644 goto bail; 645 646 if (kstat_read(kcp, ksp, NULL) == -1) 647 goto bail; 648 649 if (dladm_kstat_value(ksp, name, type, val) < 0) 650 goto bail; 651 652 (void) kstat_close(kcp); 653 return (DLADM_STATUS_OK); 654 655 bail: 656 (void) kstat_close(kcp); 657 return (dladm_errno2status(errno)); 658 } 659 660 /* Compute sum of 2 pktsums (s1 = s2 + s3) */ 661 void 662 dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 663 { 664 s1->rbytes = s2->rbytes + s3->rbytes; 665 s1->ipackets = s2->ipackets + s3->ipackets; 666 s1->ierrors = s2->ierrors + s3->ierrors; 667 s1->obytes = s2->obytes + s3->obytes; 668 s1->opackets = s2->opackets + s3->opackets; 669 s1->oerrors = s2->oerrors + s3->oerrors; 670 s1->snaptime = s2->snaptime; 671 } 672 673 /* Compute differences between 2 pktsums (s1 = s2 - s3) */ 674 void 675 dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 676 { 677 s1->rbytes = s2->rbytes - s3->rbytes; 678 s1->ipackets = s2->ipackets - s3->ipackets; 679 s1->ierrors = s2->ierrors - s3->ierrors; 680 s1->obytes = s2->obytes - s3->obytes; 681 s1->opackets = s2->opackets - s3->opackets; 682 s1->oerrors = s2->oerrors - s3->oerrors; 683 s1->snaptime = s2->snaptime - s3->snaptime; 684 } 685