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