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