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 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[MAXFLOWNAMELEN]; 51 datalink_id_t linkid; 52 uint64_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, MAXFLOWNAMELEN) 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, MAXFLOWNAMELEN); 124 flist->linkid = linkid; 125 return (flist); 126 } 127 128 static void 129 print_flow_stats(dladm_handle_t handle, 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[MAXLINKNAMELEN]; 149 150 (void) dladm_datalink_id2info(handle, fcurr->linkid, 151 NULL, NULL, 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(dladm_handle_t handle, 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[MAXLINKNAMELEN]; 230 231 (void) dladm_datalink_id2info(handle, fcurr->linkid, 232 NULL, NULL, 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(dladm_handle_t handle, datalink_id_t linkid, void *arg) 262 { 263 dladm_status_t status; 264 265 status = dladm_walk_flow(flow_kstats, handle, linkid, arg, B_FALSE); 266 if (status == DLADM_STATUS_OK) 267 return (DLADM_WALK_CONTINUE); 268 else 269 return (DLADM_WALK_TERMINATE); 270 } 271 272 /*ARGSUSED*/ 273 static int 274 link_kstats(dladm_handle_t handle, datalink_id_t linkid, void *arg) 275 { 276 kstat_ctl_t *kcp = (kstat_ctl_t *)arg; 277 struct flowlist *flist; 278 pktsum_t currstats, *prevstats, *diffstats; 279 kstat_t *ksp; 280 char linkname[MAXLINKNAMELEN]; 281 282 /* find the flist entry */ 283 flist = findstat(NULL, linkid); 284 if (flist != NULL) { 285 prevstats = &flist->prevstats; 286 diffstats = &flist->diffstats; 287 } else { 288 return (DLADM_WALK_CONTINUE); 289 } 290 291 /* lookup kstat entry */ 292 (void) dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL, 293 linkname, sizeof (linkname)); 294 295 if (linkname == NULL) { 296 warn("no linkname for linkid"); 297 return (DLADM_WALK_TERMINATE); 298 } 299 300 ksp = dladm_kstat_lookup(kcp, NULL, -1, linkname, "net"); 301 302 if (ksp == NULL) 303 return (DLADM_WALK_TERMINATE); 304 else 305 flist->display = B_TRUE; 306 307 /* read packet and byte stats */ 308 dladm_get_stats(kcp, ksp, &currstats); 309 310 if (flist->ifspeed == 0) 311 (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, 312 &flist->ifspeed); 313 314 if (flist->first == B_TRUE) 315 flist->first = B_FALSE; 316 else 317 dladm_stats_diff(diffstats, &currstats, prevstats); 318 319 bcopy(&currstats, prevstats, sizeof (*prevstats)); 320 321 return (DLADM_WALK_CONTINUE); 322 } 323 324 /*ARGSUSED*/ 325 static void 326 sig_break(int s) 327 { 328 handle_break = 1; 329 } 330 331 /*ARGSUSED*/ 332 static void 333 sig_resize(int s) 334 { 335 handle_resize = 1; 336 } 337 338 static void 339 curses_init() 340 { 341 maxx = maxx; /* lint */ 342 maxy = maxy; /* lint */ 343 344 /* Install signal handlers */ 345 (void) signal(SIGINT, sig_break); 346 (void) signal(SIGQUIT, sig_break); 347 (void) signal(SIGTERM, sig_break); 348 (void) signal(SIGWINCH, sig_resize); 349 350 /* Initialize ncurses */ 351 (void) initscr(); 352 (void) cbreak(); 353 (void) noecho(); 354 (void) curs_set(0); 355 timeout(0); 356 getmaxyx(stdscr, maxy, maxx); 357 } 358 359 static void 360 curses_fin() 361 { 362 (void) printw("\n"); 363 (void) curs_set(1); 364 (void) nocbreak(); 365 (void) endwin(); 366 367 free(stattable); 368 } 369 370 static void 371 stat_report(dladm_handle_t handle, kstat_ctl_t *kcp, datalink_id_t linkid, 372 const char *flowname, int opt) 373 { 374 375 double dlt, ikbs, okbs, ipks, opks; 376 377 struct flowlist *fstable = stattable; 378 379 if ((opt != LINK_REPORT) && (opt != FLOW_REPORT)) 380 return; 381 382 /* Handle window resizes */ 383 if (handle_resize) { 384 (void) endwin(); 385 (void) initscr(); 386 (void) cbreak(); 387 (void) noecho(); 388 (void) curs_set(0); 389 timeout(0); 390 getmaxyx(stdscr, maxy, maxx); 391 redraw = 1; 392 handle_resize = 0; 393 } 394 395 /* Print title */ 396 (void) erase(); 397 (void) attron(A_BOLD); 398 (void) move(0, 0); 399 if (opt == FLOW_REPORT) 400 (void) printw("%-15.15s", "Flow"); 401 (void) printw("%-10.10s", "Link"); 402 (void) printw("%9.9s %9.9s %9.9s %9.9s ", 403 "iKb/s", "oKb/s", "iPk/s", "oPk/s"); 404 if (opt == LINK_REPORT) 405 (void) printw(" %6.6s", "%Util"); 406 (void) printw("\n"); 407 (void) attroff(A_BOLD); 408 409 (void) move(2, 0); 410 411 /* Print stats for each link or flow */ 412 bzero(&totalstats, sizeof (totalstats)); 413 if (opt == LINK_REPORT) { 414 /* Display all links */ 415 if (linkid == DATALINK_ALL_LINKID) { 416 (void) dladm_walk_datalink_id(link_kstats, handle, 417 (void *)kcp, DATALINK_CLASS_ALL, 418 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 419 /* Display 1 link */ 420 } else { 421 (void) link_kstats(handle, linkid, kcp); 422 } 423 print_link_stats(handle, fstable); 424 425 } else if (opt == FLOW_REPORT) { 426 /* Display 1 flow */ 427 if (flowname != NULL) { 428 dladm_flow_attr_t fattr; 429 if (dladm_flow_info(handle, flowname, &fattr) != 430 DLADM_STATUS_OK) 431 return; 432 (void) flow_kstats(&fattr, kcp); 433 /* Display all flows on all links */ 434 } else if (linkid == DATALINK_ALL_LINKID) { 435 (void) dladm_walk_datalink_id(link_flowstats, handle, 436 (void *)kcp, DATALINK_CLASS_ALL, 437 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 438 /* Display all flows on a link */ 439 } else if (linkid != DATALINK_INVALID_LINKID) { 440 (void) dladm_walk_flow(flow_kstats, handle, linkid, kcp, 441 B_FALSE); 442 } 443 print_flow_stats(handle, fstable); 444 445 /* Print totals */ 446 (void) attron(A_BOLD); 447 dlt = (double)totalstats.snaptime / (double)NANOSEC; 448 ikbs = totalstats.rbytes / dlt / 1024; 449 okbs = totalstats.obytes / dlt / 1024; 450 ipks = totalstats.ipackets / dlt; 451 opks = totalstats.opackets / dlt; 452 (void) printw("\n%-25.25s", "Totals"); 453 (void) printw("%9.2f %9.2f %9.2f %9.2f ", 454 ikbs, okbs, ipks, opks); 455 (void) attroff(A_BOLD); 456 } 457 458 if (redraw) 459 (void) clearok(stdscr, 1); 460 461 if (refresh() == ERR) 462 return; 463 464 if (redraw) { 465 (void) clearok(stdscr, 0); 466 redraw = 0; 467 } 468 } 469 470 /* Exported functions */ 471 472 /* 473 * Continuously display link or flow statstics using a libcurses 474 * based display. 475 */ 476 477 void 478 dladm_continuous(dladm_handle_t handle, datalink_id_t linkid, 479 const char *flowname, int interval, int opt) 480 { 481 kstat_ctl_t *kcp; 482 483 if ((kcp = kstat_open()) == NULL) { 484 warn("kstat open operation failed"); 485 return; 486 } 487 488 curses_init(); 489 490 for (;;) { 491 492 if (handle_break) 493 break; 494 495 stat_report(handle, kcp, linkid, flowname, opt); 496 497 (void) sleep(max(1, interval)); 498 } 499 500 (void) curses_fin(); 501 (void) kstat_close(kcp); 502 } 503 504 /* 505 * dladm_kstat_lookup() is a modified version of kstat_lookup which 506 * adds the class as a selector. 507 */ 508 509 kstat_t * 510 dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance, 511 const char *name, const char *class) 512 { 513 kstat_t *ksp = NULL; 514 515 for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { 516 if ((module == NULL || strcmp(ksp->ks_module, module) == 0) && 517 (instance == -1 || ksp->ks_instance == instance) && 518 (name == NULL || strcmp(ksp->ks_name, name) == 0) && 519 (class == NULL || strcmp(ksp->ks_class, class) == 0)) 520 return (ksp); 521 } 522 523 errno = ENOENT; 524 return (NULL); 525 } 526 527 /* 528 * dladm_get_stats() populates the supplied pktsum_t structure with 529 * the input and output packet and byte kstats from the kstat_t 530 * found with dladm_kstat_lookup. 531 */ 532 void 533 dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats) 534 { 535 536 if (kstat_read(kcp, ksp, NULL) == -1) 537 return; 538 539 stats->snaptime = gethrtime(); 540 541 if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64, 542 &stats->ipackets) < 0) { 543 if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64, 544 &stats->ipackets) < 0) 545 return; 546 } 547 548 if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64, 549 &stats->opackets) < 0) { 550 if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64, 551 &stats->opackets) < 0) 552 return; 553 } 554 555 if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64, 556 &stats->rbytes) < 0) { 557 if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64, 558 &stats->rbytes) < 0) 559 return; 560 } 561 562 if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64, 563 &stats->obytes) < 0) { 564 if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64, 565 &stats->obytes) < 0) 566 return; 567 } 568 569 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32, 570 &stats->ierrors) < 0) { 571 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64, 572 &stats->ierrors) < 0) 573 return; 574 } 575 576 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32, 577 &stats->oerrors) < 0) { 578 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64, 579 &stats->oerrors) < 0) 580 return; 581 } 582 } 583 584 int 585 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) 586 { 587 kstat_named_t *knp; 588 589 if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) 590 return (-1); 591 592 if (knp->data_type != type) 593 return (-1); 594 595 switch (type) { 596 case KSTAT_DATA_UINT64: 597 *(uint64_t *)buf = knp->value.ui64; 598 break; 599 case KSTAT_DATA_UINT32: 600 *(uint32_t *)buf = knp->value.ui32; 601 break; 602 default: 603 return (-1); 604 } 605 606 return (0); 607 } 608 609 dladm_status_t 610 dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid, 611 const char *name, uint8_t type, void *val) 612 { 613 kstat_ctl_t *kcp; 614 char module[DLPI_LINKNAME_MAX]; 615 uint_t instance; 616 char link[DLPI_LINKNAME_MAX]; 617 dladm_status_t status; 618 uint32_t flags, media; 619 kstat_t *ksp; 620 dladm_phys_attr_t dpap; 621 622 if ((kcp = kstat_open()) == NULL) { 623 warn("kstat_open operation failed"); 624 return (-1); 625 } 626 627 if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, 628 &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK) 629 return (status); 630 631 if (media != DL_ETHER) 632 return (DLADM_STATUS_LINKINVAL); 633 634 status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST); 635 636 if (status != DLADM_STATUS_OK) 637 return (status); 638 639 status = dladm_parselink(dpap.dp_dev, module, &instance); 640 641 if (status != DLADM_STATUS_OK) 642 return (status); 643 644 /* 645 * The kstat query could fail if the underlying MAC 646 * driver was already detached. 647 */ 648 if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && 649 (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) 650 goto bail; 651 652 if (kstat_read(kcp, ksp, NULL) == -1) 653 goto bail; 654 655 if (dladm_kstat_value(ksp, name, type, val) < 0) 656 goto bail; 657 658 (void) kstat_close(kcp); 659 return (DLADM_STATUS_OK); 660 661 bail: 662 (void) kstat_close(kcp); 663 return (dladm_errno2status(errno)); 664 } 665 666 /* Compute sum of 2 pktsums (s1 = s2 + s3) */ 667 void 668 dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 669 { 670 s1->rbytes = s2->rbytes + s3->rbytes; 671 s1->ipackets = s2->ipackets + s3->ipackets; 672 s1->ierrors = s2->ierrors + s3->ierrors; 673 s1->obytes = s2->obytes + s3->obytes; 674 s1->opackets = s2->opackets + s3->opackets; 675 s1->oerrors = s2->oerrors + s3->oerrors; 676 s1->snaptime = s2->snaptime; 677 } 678 679 /* Compute differences between 2 pktsums (s1 = s2 - s3) */ 680 void 681 dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 682 { 683 s1->rbytes = s2->rbytes - s3->rbytes; 684 s1->ipackets = s2->ipackets - s3->ipackets; 685 s1->ierrors = s2->ierrors - s3->ierrors; 686 s1->obytes = s2->obytes - s3->obytes; 687 s1->opackets = s2->opackets - s3->opackets; 688 s1->oerrors = s2->oerrors - s3->oerrors; 689 s1->snaptime = s2->snaptime - s3->snaptime; 690 } 691