18275SEric Cheng /* 28275SEric Cheng * CDDL HEADER START 38275SEric Cheng * 48275SEric Cheng * The contents of this file are subject to the terms of the 58275SEric Cheng * Common Development and Distribution License (the "License"). 68275SEric Cheng * You may not use this file except in compliance with the License. 78275SEric Cheng * 88275SEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98275SEric Cheng * or http://www.opensolaris.org/os/licensing. 108275SEric Cheng * See the License for the specific language governing permissions 118275SEric Cheng * and limitations under the License. 128275SEric Cheng * 138275SEric Cheng * When distributing Covered Code, include this CDDL HEADER in each 148275SEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158275SEric Cheng * If applicable, add the following below this CDDL HEADER, with the 168275SEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying 178275SEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner] 188275SEric Cheng * 198275SEric Cheng * CDDL HEADER END 208275SEric Cheng */ 218275SEric Cheng /* 228558SGirish.Moodalbail@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 238275SEric Cheng * Use is subject to license terms. 248275SEric Cheng */ 258275SEric Cheng 268275SEric Cheng #include <stdio.h> 278275SEric Cheng #include <stdlib.h> 288275SEric Cheng #include <strings.h> 298275SEric Cheng #include <err.h> 308275SEric Cheng #include <errno.h> 31*9421SMichael.Lim@Sun.COM #include <fcntl.h> 328275SEric Cheng #include <kstat.h> 33*9421SMichael.Lim@Sun.COM #include <limits.h> 348275SEric Cheng #include <unistd.h> 358275SEric Cheng #include <signal.h> 368275SEric Cheng #include <sys/dld.h> 379107Sjames.d.carlson@sun.com #include <sys/ddi.h> 388275SEric Cheng 398275SEric Cheng #include <libdllink.h> 408275SEric Cheng #include <libdlflow.h> 418275SEric Cheng #include <libdlstat.h> 428275SEric Cheng 438275SEric Cheng /* 448275SEric Cheng * x86 <sys/regs> ERR conflicts with <curses.h> ERR. 458275SEric Cheng * Include curses.h last. 468275SEric Cheng */ 478275SEric Cheng #if defined(ERR) 488275SEric Cheng #undef ERR 498275SEric Cheng #endif 508275SEric Cheng #include <curses.h> 518275SEric Cheng 528275SEric Cheng struct flowlist { 538558SGirish.Moodalbail@Sun.COM char flowname[MAXFLOWNAMELEN]; 54*9421SMichael.Lim@Sun.COM char linkname[MAXLINKNAMELEN]; 558275SEric Cheng datalink_id_t linkid; 56*9421SMichael.Lim@Sun.COM int fd; 578957SMichael.Lim@Sun.COM uint64_t ifspeed; 588275SEric Cheng boolean_t first; 598275SEric Cheng boolean_t display; 608275SEric Cheng pktsum_t prevstats; 618275SEric Cheng pktsum_t diffstats; 628275SEric Cheng }; 638275SEric Cheng 648275SEric Cheng static int maxx, maxy, redraw = 0; 658275SEric Cheng static volatile uint_t handle_resize = 0, handle_break = 0; 668275SEric Cheng 678275SEric Cheng pktsum_t totalstats; 688275SEric Cheng struct flowlist *stattable = NULL; 698275SEric Cheng static int statentry = -1, maxstatentries = 0; 708275SEric Cheng 718275SEric Cheng #define STATGROWSIZE 16 728275SEric Cheng 738275SEric Cheng /* 748275SEric Cheng * Search for flowlist entry in stattable which matches 758275SEric Cheng * the flowname and linkide. If no match is found, use 768275SEric Cheng * next available slot. If no slots are available, 778275SEric Cheng * reallocate table with more slots. 788275SEric Cheng * 798275SEric Cheng * Return: *flowlist of matching flow 808275SEric Cheng * NULL if realloc fails 818275SEric Cheng */ 828275SEric Cheng 838275SEric Cheng static struct flowlist * 848275SEric Cheng findstat(const char *flowname, datalink_id_t linkid) 858275SEric Cheng { 868275SEric Cheng int match = 0; 878275SEric Cheng struct flowlist *flist; 888275SEric Cheng 898275SEric Cheng /* Look for match in the stattable */ 908275SEric Cheng for (match = 0, flist = stattable; 918275SEric Cheng match <= statentry; 928275SEric Cheng match++, flist++) { 938275SEric Cheng 948275SEric Cheng if (flist == NULL) 958275SEric Cheng break; 968275SEric Cheng /* match the flowname */ 978275SEric Cheng if (flowname != NULL) { 988558SGirish.Moodalbail@Sun.COM if (strncmp(flowname, flist->flowname, MAXFLOWNAMELEN) 998275SEric Cheng == NULL) 1008275SEric Cheng return (flist); 1018275SEric Cheng /* match the linkid */ 1028275SEric Cheng } else { 1038275SEric Cheng if (linkid == flist->linkid) 1048275SEric Cheng return (flist); 1058275SEric Cheng } 1068275SEric Cheng } 1078275SEric Cheng 1088275SEric Cheng /* 1098275SEric Cheng * No match found in the table. Store statistics in the next slot. 1108275SEric Cheng * If necessary, make room for this entry. 1118275SEric Cheng */ 1128275SEric Cheng statentry++; 1138275SEric Cheng if ((maxstatentries == 0) || (maxstatentries == statentry)) { 1148275SEric Cheng maxstatentries += STATGROWSIZE; 1158275SEric Cheng stattable = realloc(stattable, 1168275SEric Cheng maxstatentries * sizeof (struct flowlist)); 1178275SEric Cheng if (stattable == NULL) { 1188275SEric Cheng perror("realloc"); 1198275SEric Cheng return (struct flowlist *)(NULL); 1208275SEric Cheng } 1218275SEric Cheng } 1228275SEric Cheng flist = &stattable[statentry]; 1238275SEric Cheng bzero(flist, sizeof (struct flowlist)); 1248275SEric Cheng 1258275SEric Cheng if (flowname != NULL) 1268558SGirish.Moodalbail@Sun.COM (void) strncpy(flist->flowname, flowname, MAXFLOWNAMELEN); 1278275SEric Cheng flist->linkid = linkid; 128*9421SMichael.Lim@Sun.COM flist->fd = INT32_MAX; 129*9421SMichael.Lim@Sun.COM 1308275SEric Cheng return (flist); 1318275SEric Cheng } 1328275SEric Cheng 133*9421SMichael.Lim@Sun.COM /*ARGSUSED*/ 1348275SEric Cheng static void 1358453SAnurag.Maskey@Sun.COM print_flow_stats(dladm_handle_t handle, struct flowlist *flist) 1368275SEric Cheng { 1378275SEric Cheng struct flowlist *fcurr; 1388275SEric Cheng double ikbs, okbs; 1398275SEric Cheng double ipks, opks; 1408275SEric Cheng double dlt; 1418275SEric Cheng int fcount; 1428275SEric Cheng static boolean_t first = B_TRUE; 1438275SEric Cheng 1448275SEric Cheng if (first) { 1458275SEric Cheng first = B_FALSE; 1468275SEric Cheng (void) printw("please wait...\n"); 1478275SEric Cheng return; 1488275SEric Cheng } 1498275SEric Cheng 1508275SEric Cheng for (fcount = 0, fcurr = flist; 1518275SEric Cheng fcount <= statentry; 1528275SEric Cheng fcount++, fcurr++) { 1538275SEric Cheng if (fcurr->flowname && fcurr->display) { 1548275SEric Cheng dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; 1558275SEric Cheng ikbs = fcurr->diffstats.rbytes * 8 / dlt / 1024; 1568275SEric Cheng okbs = fcurr->diffstats.obytes * 8 / dlt / 1024; 1578275SEric Cheng ipks = fcurr->diffstats.ipackets / dlt; 1588275SEric Cheng opks = fcurr->diffstats.opackets / dlt; 1598275SEric Cheng (void) printw("%-15.15s", fcurr->flowname); 160*9421SMichael.Lim@Sun.COM (void) printw("%-10.10s", fcurr->linkname); 1618275SEric Cheng (void) printw("%9.2f %9.2f %9.2f %9.2f ", 1628275SEric Cheng ikbs, okbs, ipks, opks); 1638275SEric Cheng (void) printw("\n"); 1648275SEric Cheng } 1658275SEric Cheng } 1668275SEric Cheng } 1678275SEric Cheng 1688275SEric Cheng /*ARGSUSED*/ 1698275SEric Cheng static int 170*9421SMichael.Lim@Sun.COM flow_kstats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 1718275SEric Cheng { 1728275SEric Cheng kstat_ctl_t *kcp = (kstat_ctl_t *)arg; 1738275SEric Cheng kstat_t *ksp; 1748275SEric Cheng struct flowlist *flist; 1758275SEric Cheng pktsum_t currstats, *prevstats, *diffstats; 1768275SEric Cheng 1778275SEric Cheng flist = findstat(attr->fa_flowname, attr->fa_linkid); 178*9421SMichael.Lim@Sun.COM if (flist == NULL) 179*9421SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 180*9421SMichael.Lim@Sun.COM 181*9421SMichael.Lim@Sun.COM flist->display = B_FALSE; 182*9421SMichael.Lim@Sun.COM prevstats = &flist->prevstats; 183*9421SMichael.Lim@Sun.COM diffstats = &flist->diffstats; 184*9421SMichael.Lim@Sun.COM 185*9421SMichael.Lim@Sun.COM (void) dladm_datalink_id2info(handle, attr->fa_linkid, NULL, NULL, 186*9421SMichael.Lim@Sun.COM NULL, flist->linkname, sizeof (flist->linkname)); 187*9421SMichael.Lim@Sun.COM 1888275SEric Cheng 1898275SEric Cheng /* lookup kstat entry */ 1908275SEric Cheng ksp = dladm_kstat_lookup(kcp, NULL, -1, attr->fa_flowname, "flow"); 1918275SEric Cheng 1928275SEric Cheng if (ksp == NULL) 193*9421SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 1948275SEric Cheng 195*9421SMichael.Lim@Sun.COM /* read packet and byte stats */ 1968275SEric Cheng dladm_get_stats(kcp, ksp, &currstats); 197*9421SMichael.Lim@Sun.COM 1988275SEric Cheng if (flist->ifspeed == 0) 1998275SEric Cheng (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, 2008275SEric Cheng &flist->ifspeed); 2018275SEric Cheng 202*9421SMichael.Lim@Sun.COM if (flist->first) { 2038275SEric Cheng flist->first = B_FALSE; 204*9421SMichael.Lim@Sun.COM } else { 2058275SEric Cheng dladm_stats_diff(diffstats, &currstats, prevstats); 206*9421SMichael.Lim@Sun.COM if (diffstats->snaptime == 0) 207*9421SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 2088275SEric Cheng dladm_stats_total(&totalstats, diffstats, &totalstats); 2098275SEric Cheng } 2108275SEric Cheng 2118275SEric Cheng bcopy(&currstats, prevstats, sizeof (pktsum_t)); 212*9421SMichael.Lim@Sun.COM flist->display = B_TRUE; 213*9421SMichael.Lim@Sun.COM 2148275SEric Cheng return (DLADM_WALK_CONTINUE); 2158275SEric Cheng } 2168275SEric Cheng 217*9421SMichael.Lim@Sun.COM /*ARGSUSED*/ 2188275SEric Cheng static void 2198453SAnurag.Maskey@Sun.COM print_link_stats(dladm_handle_t handle, struct flowlist *flist) 2208275SEric Cheng { 2218275SEric Cheng struct flowlist *fcurr; 2228275SEric Cheng double ikbs, okbs; 2238275SEric Cheng double ipks, opks; 2248275SEric Cheng double util; 2258275SEric Cheng double dlt; 2268275SEric Cheng int fcount; 2278275SEric Cheng static boolean_t first = B_TRUE; 2288275SEric Cheng 2298275SEric Cheng if (first) { 2308275SEric Cheng first = B_FALSE; 2318275SEric Cheng (void) printw("please wait...\n"); 2328275SEric Cheng return; 2338275SEric Cheng } 2348275SEric Cheng 2358275SEric Cheng for (fcount = 0, fcurr = flist; 2368275SEric Cheng fcount <= statentry; 2378275SEric Cheng fcount++, fcurr++) { 2388275SEric Cheng if ((fcurr->linkid != DATALINK_INVALID_LINKID) && 2398275SEric Cheng fcurr->display) { 2408275SEric Cheng dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; 2418275SEric Cheng ikbs = (double)fcurr->diffstats.rbytes * 8 / dlt / 1024; 2428275SEric Cheng okbs = (double)fcurr->diffstats.obytes * 8 / dlt / 1024; 2438275SEric Cheng ipks = (double)fcurr->diffstats.ipackets / dlt; 2448275SEric Cheng opks = (double)fcurr->diffstats.opackets / dlt; 245*9421SMichael.Lim@Sun.COM (void) printw("%-10.10s", fcurr->linkname); 2468275SEric Cheng (void) printw("%9.2f %9.2f %9.2f %9.2f ", 2478275SEric Cheng ikbs, okbs, ipks, opks); 2488275SEric Cheng if (fcurr->ifspeed != 0) 2498275SEric Cheng util = ((ikbs + okbs) * 1024) * 2508275SEric Cheng 100/ fcurr->ifspeed; 2518275SEric Cheng else 2528275SEric Cheng util = (double)0; 2538275SEric Cheng (void) attron(A_BOLD); 2548275SEric Cheng (void) printw(" %6.2f", util); 2558275SEric Cheng (void) attroff(A_BOLD); 2568275SEric Cheng (void) printw("\n"); 2578275SEric Cheng } 2588275SEric Cheng } 2598275SEric Cheng } 2608275SEric Cheng 2618275SEric Cheng /* 2628275SEric Cheng * This function is called through the dladm_walk_datalink_id() walker and 2638275SEric Cheng * calls the dladm_walk_flow() walker. 2648275SEric Cheng */ 2658275SEric Cheng 2668275SEric Cheng /*ARGSUSED*/ 2678275SEric Cheng static int 2688453SAnurag.Maskey@Sun.COM link_flowstats(dladm_handle_t handle, datalink_id_t linkid, void *arg) 2698275SEric Cheng { 2708621SMichael.Lim@Sun.COM dladm_status_t status; 2718621SMichael.Lim@Sun.COM 2728621SMichael.Lim@Sun.COM status = dladm_walk_flow(flow_kstats, handle, linkid, arg, B_FALSE); 2738621SMichael.Lim@Sun.COM if (status == DLADM_STATUS_OK) 2748621SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 2758621SMichael.Lim@Sun.COM else 2768621SMichael.Lim@Sun.COM return (DLADM_WALK_TERMINATE); 2778275SEric Cheng } 2788275SEric Cheng 2798275SEric Cheng /*ARGSUSED*/ 2808275SEric Cheng static int 2818453SAnurag.Maskey@Sun.COM link_kstats(dladm_handle_t handle, datalink_id_t linkid, void *arg) 2828275SEric Cheng { 283*9421SMichael.Lim@Sun.COM kstat_ctl_t *kcp = (kstat_ctl_t *)arg; 284*9421SMichael.Lim@Sun.COM struct flowlist *flist; 285*9421SMichael.Lim@Sun.COM pktsum_t currstats, *prevstats, *diffstats; 286*9421SMichael.Lim@Sun.COM datalink_class_t class; 287*9421SMichael.Lim@Sun.COM kstat_t *ksp; 288*9421SMichael.Lim@Sun.COM char dnlink[MAXPATHLEN]; 2898275SEric Cheng 2908275SEric Cheng /* find the flist entry */ 2918275SEric Cheng flist = findstat(NULL, linkid); 292*9421SMichael.Lim@Sun.COM flist->display = B_FALSE; 2938275SEric Cheng if (flist != NULL) { 2948275SEric Cheng prevstats = &flist->prevstats; 2958275SEric Cheng diffstats = &flist->diffstats; 2968275SEric Cheng } else { 2978275SEric Cheng return (DLADM_WALK_CONTINUE); 2988275SEric Cheng } 2998275SEric Cheng 300*9421SMichael.Lim@Sun.COM if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, 301*9421SMichael.Lim@Sun.COM flist->linkname, sizeof (flist->linkname)) != DLADM_STATUS_OK) 302*9421SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 3038275SEric Cheng 304*9421SMichael.Lim@Sun.COM if (flist->fd == INT32_MAX) { 305*9421SMichael.Lim@Sun.COM if (class == DATALINK_CLASS_PHYS) { 306*9421SMichael.Lim@Sun.COM (void) snprintf(dnlink, MAXPATHLEN, "/dev/net/%s", 307*9421SMichael.Lim@Sun.COM flist->linkname); 308*9421SMichael.Lim@Sun.COM if ((flist->fd = open(dnlink, O_RDWR)) < 0) 309*9421SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 310*9421SMichael.Lim@Sun.COM } else { 311*9421SMichael.Lim@Sun.COM flist->fd = -1; 312*9421SMichael.Lim@Sun.COM } 313*9421SMichael.Lim@Sun.COM (void) kstat_chain_update(kcp); 3148275SEric Cheng } 3158275SEric Cheng 316*9421SMichael.Lim@Sun.COM /* lookup kstat entry */ 317*9421SMichael.Lim@Sun.COM ksp = dladm_kstat_lookup(kcp, NULL, -1, flist->linkname, "net"); 3188275SEric Cheng 3198275SEric Cheng if (ksp == NULL) 320*9421SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 3218275SEric Cheng 3228275SEric Cheng /* read packet and byte stats */ 3238275SEric Cheng dladm_get_stats(kcp, ksp, &currstats); 3248275SEric Cheng 3258275SEric Cheng if (flist->ifspeed == 0) 3268275SEric Cheng (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, 3278275SEric Cheng &flist->ifspeed); 3288275SEric Cheng 329*9421SMichael.Lim@Sun.COM if (flist->first) { 3308275SEric Cheng flist->first = B_FALSE; 331*9421SMichael.Lim@Sun.COM } else { 3328275SEric Cheng dladm_stats_diff(diffstats, &currstats, prevstats); 333*9421SMichael.Lim@Sun.COM if (diffstats->snaptime == 0) 334*9421SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 335*9421SMichael.Lim@Sun.COM } 3368275SEric Cheng 3378275SEric Cheng bcopy(&currstats, prevstats, sizeof (*prevstats)); 338*9421SMichael.Lim@Sun.COM flist->display = B_TRUE; 3398275SEric Cheng 3408275SEric Cheng return (DLADM_WALK_CONTINUE); 3418275SEric Cheng } 3428275SEric Cheng 343*9421SMichael.Lim@Sun.COM static void 344*9421SMichael.Lim@Sun.COM closedevnet() 345*9421SMichael.Lim@Sun.COM { 346*9421SMichael.Lim@Sun.COM int index = 0; 347*9421SMichael.Lim@Sun.COM struct flowlist *flist; 348*9421SMichael.Lim@Sun.COM 349*9421SMichael.Lim@Sun.COM /* Close all open /dev/net/ files */ 350*9421SMichael.Lim@Sun.COM for (flist = stattable; index <= maxstatentries; index++, flist++) { 351*9421SMichael.Lim@Sun.COM if (flist->linkid == DATALINK_INVALID_LINKID) 352*9421SMichael.Lim@Sun.COM break; 353*9421SMichael.Lim@Sun.COM if (flist->fd != -1 && flist->fd != INT32_MAX) 354*9421SMichael.Lim@Sun.COM (void) close(flist->fd); 355*9421SMichael.Lim@Sun.COM } 356*9421SMichael.Lim@Sun.COM } 357*9421SMichael.Lim@Sun.COM 3588275SEric Cheng /*ARGSUSED*/ 3598275SEric Cheng static void 3608275SEric Cheng sig_break(int s) 3618275SEric Cheng { 3628275SEric Cheng handle_break = 1; 3638275SEric Cheng } 3648275SEric Cheng 3658275SEric Cheng /*ARGSUSED*/ 3668275SEric Cheng static void 3678275SEric Cheng sig_resize(int s) 3688275SEric Cheng { 3698275SEric Cheng handle_resize = 1; 3708275SEric Cheng } 3718275SEric Cheng 3728275SEric Cheng static void 3738275SEric Cheng curses_init() 3748275SEric Cheng { 3758275SEric Cheng maxx = maxx; /* lint */ 3768275SEric Cheng maxy = maxy; /* lint */ 3778275SEric Cheng 3788275SEric Cheng /* Install signal handlers */ 3798275SEric Cheng (void) signal(SIGINT, sig_break); 3808275SEric Cheng (void) signal(SIGQUIT, sig_break); 3818275SEric Cheng (void) signal(SIGTERM, sig_break); 3828275SEric Cheng (void) signal(SIGWINCH, sig_resize); 3838275SEric Cheng 3848275SEric Cheng /* Initialize ncurses */ 3858275SEric Cheng (void) initscr(); 3868275SEric Cheng (void) cbreak(); 3878275SEric Cheng (void) noecho(); 3888275SEric Cheng (void) curs_set(0); 3898275SEric Cheng timeout(0); 3908275SEric Cheng getmaxyx(stdscr, maxy, maxx); 3918275SEric Cheng } 3928275SEric Cheng 3938275SEric Cheng static void 3948275SEric Cheng curses_fin() 3958275SEric Cheng { 3968275SEric Cheng (void) printw("\n"); 3978275SEric Cheng (void) curs_set(1); 3988275SEric Cheng (void) nocbreak(); 3998275SEric Cheng (void) endwin(); 4008275SEric Cheng 4018275SEric Cheng free(stattable); 4028275SEric Cheng } 4038275SEric Cheng 4048275SEric Cheng static void 4058453SAnurag.Maskey@Sun.COM stat_report(dladm_handle_t handle, kstat_ctl_t *kcp, datalink_id_t linkid, 4068453SAnurag.Maskey@Sun.COM const char *flowname, int opt) 4078275SEric Cheng { 4088275SEric Cheng 4098275SEric Cheng double dlt, ikbs, okbs, ipks, opks; 4108275SEric Cheng 4118275SEric Cheng struct flowlist *fstable = stattable; 4128275SEric Cheng 4138275SEric Cheng if ((opt != LINK_REPORT) && (opt != FLOW_REPORT)) 4148275SEric Cheng return; 4158275SEric Cheng 4168275SEric Cheng /* Handle window resizes */ 4178275SEric Cheng if (handle_resize) { 4188275SEric Cheng (void) endwin(); 4198275SEric Cheng (void) initscr(); 4208275SEric Cheng (void) cbreak(); 4218275SEric Cheng (void) noecho(); 4228275SEric Cheng (void) curs_set(0); 4238275SEric Cheng timeout(0); 4248275SEric Cheng getmaxyx(stdscr, maxy, maxx); 4258275SEric Cheng redraw = 1; 4268275SEric Cheng handle_resize = 0; 4278275SEric Cheng } 4288275SEric Cheng 4298275SEric Cheng /* Print title */ 4308275SEric Cheng (void) erase(); 4318275SEric Cheng (void) attron(A_BOLD); 4328275SEric Cheng (void) move(0, 0); 4338275SEric Cheng if (opt == FLOW_REPORT) 4348275SEric Cheng (void) printw("%-15.15s", "Flow"); 4358275SEric Cheng (void) printw("%-10.10s", "Link"); 4368275SEric Cheng (void) printw("%9.9s %9.9s %9.9s %9.9s ", 4378275SEric Cheng "iKb/s", "oKb/s", "iPk/s", "oPk/s"); 4388275SEric Cheng if (opt == LINK_REPORT) 4398275SEric Cheng (void) printw(" %6.6s", "%Util"); 4408275SEric Cheng (void) printw("\n"); 4418275SEric Cheng (void) attroff(A_BOLD); 4428275SEric Cheng 4438275SEric Cheng (void) move(2, 0); 4448275SEric Cheng 4458275SEric Cheng /* Print stats for each link or flow */ 4468275SEric Cheng bzero(&totalstats, sizeof (totalstats)); 4478275SEric Cheng if (opt == LINK_REPORT) { 4488275SEric Cheng /* Display all links */ 4498275SEric Cheng if (linkid == DATALINK_ALL_LINKID) { 4508453SAnurag.Maskey@Sun.COM (void) dladm_walk_datalink_id(link_kstats, handle, 4518275SEric Cheng (void *)kcp, DATALINK_CLASS_ALL, 4528275SEric Cheng DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 4538275SEric Cheng /* Display 1 link */ 4548275SEric Cheng } else { 4558453SAnurag.Maskey@Sun.COM (void) link_kstats(handle, linkid, kcp); 4568275SEric Cheng } 4578453SAnurag.Maskey@Sun.COM print_link_stats(handle, fstable); 4588275SEric Cheng 4598275SEric Cheng } else if (opt == FLOW_REPORT) { 4608275SEric Cheng /* Display 1 flow */ 4618275SEric Cheng if (flowname != NULL) { 4628275SEric Cheng dladm_flow_attr_t fattr; 4638453SAnurag.Maskey@Sun.COM if (dladm_flow_info(handle, flowname, &fattr) != 4648275SEric Cheng DLADM_STATUS_OK) 4658275SEric Cheng return; 466*9421SMichael.Lim@Sun.COM (void) flow_kstats(handle, &fattr, kcp); 4678275SEric Cheng /* Display all flows on all links */ 4688275SEric Cheng } else if (linkid == DATALINK_ALL_LINKID) { 4698453SAnurag.Maskey@Sun.COM (void) dladm_walk_datalink_id(link_flowstats, handle, 4708275SEric Cheng (void *)kcp, DATALINK_CLASS_ALL, 4718275SEric Cheng DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 4728275SEric Cheng /* Display all flows on a link */ 4738275SEric Cheng } else if (linkid != DATALINK_INVALID_LINKID) { 4748453SAnurag.Maskey@Sun.COM (void) dladm_walk_flow(flow_kstats, handle, linkid, kcp, 4758275SEric Cheng B_FALSE); 4768275SEric Cheng } 4778453SAnurag.Maskey@Sun.COM print_flow_stats(handle, fstable); 4788275SEric Cheng 4798275SEric Cheng /* Print totals */ 4808275SEric Cheng (void) attron(A_BOLD); 4818275SEric Cheng dlt = (double)totalstats.snaptime / (double)NANOSEC; 4828275SEric Cheng ikbs = totalstats.rbytes / dlt / 1024; 4838275SEric Cheng okbs = totalstats.obytes / dlt / 1024; 4848275SEric Cheng ipks = totalstats.ipackets / dlt; 4858275SEric Cheng opks = totalstats.opackets / dlt; 4868275SEric Cheng (void) printw("\n%-25.25s", "Totals"); 4878275SEric Cheng (void) printw("%9.2f %9.2f %9.2f %9.2f ", 4888275SEric Cheng ikbs, okbs, ipks, opks); 4898275SEric Cheng (void) attroff(A_BOLD); 4908275SEric Cheng } 4918275SEric Cheng 4928275SEric Cheng if (redraw) 4938275SEric Cheng (void) clearok(stdscr, 1); 4948275SEric Cheng 4958275SEric Cheng if (refresh() == ERR) 4968275SEric Cheng return; 4978275SEric Cheng 4988275SEric Cheng if (redraw) { 4998275SEric Cheng (void) clearok(stdscr, 0); 5008275SEric Cheng redraw = 0; 5018275SEric Cheng } 5028275SEric Cheng } 5038275SEric Cheng 5048275SEric Cheng /* Exported functions */ 5058275SEric Cheng 5068275SEric Cheng /* 5078275SEric Cheng * Continuously display link or flow statstics using a libcurses 5088275SEric Cheng * based display. 5098275SEric Cheng */ 5108275SEric Cheng 5118275SEric Cheng void 5128453SAnurag.Maskey@Sun.COM dladm_continuous(dladm_handle_t handle, datalink_id_t linkid, 5138453SAnurag.Maskey@Sun.COM const char *flowname, int interval, int opt) 5148275SEric Cheng { 5158275SEric Cheng kstat_ctl_t *kcp; 5168275SEric Cheng 5178275SEric Cheng if ((kcp = kstat_open()) == NULL) { 5188275SEric Cheng warn("kstat open operation failed"); 5198275SEric Cheng return; 5208275SEric Cheng } 5218275SEric Cheng 5228275SEric Cheng curses_init(); 5238275SEric Cheng 5248275SEric Cheng for (;;) { 5258275SEric Cheng 5268275SEric Cheng if (handle_break) 5278275SEric Cheng break; 5288275SEric Cheng 5298453SAnurag.Maskey@Sun.COM stat_report(handle, kcp, linkid, flowname, opt); 5308275SEric Cheng 5318275SEric Cheng (void) sleep(max(1, interval)); 5328275SEric Cheng } 5338275SEric Cheng 534*9421SMichael.Lim@Sun.COM closedevnet(); 535*9421SMichael.Lim@Sun.COM curses_fin(); 5368275SEric Cheng (void) kstat_close(kcp); 5378275SEric Cheng } 5388275SEric Cheng 5398275SEric Cheng /* 5408275SEric Cheng * dladm_kstat_lookup() is a modified version of kstat_lookup which 5418275SEric Cheng * adds the class as a selector. 5428275SEric Cheng */ 5438275SEric Cheng 5448275SEric Cheng kstat_t * 5458275SEric Cheng dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance, 5468275SEric Cheng const char *name, const char *class) 5478275SEric Cheng { 5488275SEric Cheng kstat_t *ksp = NULL; 5498275SEric Cheng 5508275SEric Cheng for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { 5518275SEric Cheng if ((module == NULL || strcmp(ksp->ks_module, module) == 0) && 5528275SEric Cheng (instance == -1 || ksp->ks_instance == instance) && 5538275SEric Cheng (name == NULL || strcmp(ksp->ks_name, name) == 0) && 5548275SEric Cheng (class == NULL || strcmp(ksp->ks_class, class) == 0)) 5558275SEric Cheng return (ksp); 5568275SEric Cheng } 5578275SEric Cheng 5588275SEric Cheng errno = ENOENT; 5598275SEric Cheng return (NULL); 5608275SEric Cheng } 5618275SEric Cheng 5628275SEric Cheng /* 5638275SEric Cheng * dladm_get_stats() populates the supplied pktsum_t structure with 5648275SEric Cheng * the input and output packet and byte kstats from the kstat_t 5658275SEric Cheng * found with dladm_kstat_lookup. 5668275SEric Cheng */ 5678275SEric Cheng void 5688275SEric Cheng dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats) 5698275SEric Cheng { 5708275SEric Cheng 5718275SEric Cheng if (kstat_read(kcp, ksp, NULL) == -1) 5728275SEric Cheng return; 5738275SEric Cheng 5748275SEric Cheng stats->snaptime = gethrtime(); 5758275SEric Cheng 5768275SEric Cheng if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64, 5778275SEric Cheng &stats->ipackets) < 0) { 5788275SEric Cheng if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64, 5798275SEric Cheng &stats->ipackets) < 0) 5808275SEric Cheng return; 5818275SEric Cheng } 5828275SEric Cheng 5838275SEric Cheng if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64, 5848275SEric Cheng &stats->opackets) < 0) { 5858275SEric Cheng if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64, 5868275SEric Cheng &stats->opackets) < 0) 5878275SEric Cheng return; 5888275SEric Cheng } 5898275SEric Cheng 5908275SEric Cheng if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64, 5918275SEric Cheng &stats->rbytes) < 0) { 5928275SEric Cheng if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64, 5938275SEric Cheng &stats->rbytes) < 0) 5948275SEric Cheng return; 5958275SEric Cheng } 5968275SEric Cheng 5978275SEric Cheng if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64, 5988275SEric Cheng &stats->obytes) < 0) { 5998275SEric Cheng if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64, 6008275SEric Cheng &stats->obytes) < 0) 6018275SEric Cheng return; 6028275SEric Cheng } 6038275SEric Cheng 6048275SEric Cheng if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32, 6058275SEric Cheng &stats->ierrors) < 0) { 6068275SEric Cheng if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64, 6078275SEric Cheng &stats->ierrors) < 0) 6088275SEric Cheng return; 6098275SEric Cheng } 6108275SEric Cheng 6118275SEric Cheng if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32, 6128275SEric Cheng &stats->oerrors) < 0) { 6138275SEric Cheng if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64, 6148275SEric Cheng &stats->oerrors) < 0) 6158275SEric Cheng return; 6168275SEric Cheng } 6178275SEric Cheng } 6188275SEric Cheng 6198275SEric Cheng int 6208275SEric Cheng dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) 6218275SEric Cheng { 6228275SEric Cheng kstat_named_t *knp; 6238275SEric Cheng 6248275SEric Cheng if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) 6258275SEric Cheng return (-1); 6268275SEric Cheng 6278275SEric Cheng if (knp->data_type != type) 6288275SEric Cheng return (-1); 6298275SEric Cheng 6308275SEric Cheng switch (type) { 6318275SEric Cheng case KSTAT_DATA_UINT64: 6328275SEric Cheng *(uint64_t *)buf = knp->value.ui64; 6338275SEric Cheng break; 6348275SEric Cheng case KSTAT_DATA_UINT32: 6358275SEric Cheng *(uint32_t *)buf = knp->value.ui32; 6368275SEric Cheng break; 6378275SEric Cheng default: 6388275SEric Cheng return (-1); 6398275SEric Cheng } 6408275SEric Cheng 6418275SEric Cheng return (0); 6428275SEric Cheng } 6438275SEric Cheng 6448275SEric Cheng dladm_status_t 6458453SAnurag.Maskey@Sun.COM dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid, 6468453SAnurag.Maskey@Sun.COM const char *name, uint8_t type, void *val) 6478275SEric Cheng { 6488275SEric Cheng kstat_ctl_t *kcp; 6498275SEric Cheng char module[DLPI_LINKNAME_MAX]; 6508275SEric Cheng uint_t instance; 6518275SEric Cheng char link[DLPI_LINKNAME_MAX]; 6528275SEric Cheng dladm_status_t status; 6538275SEric Cheng uint32_t flags, media; 6548275SEric Cheng kstat_t *ksp; 6558275SEric Cheng dladm_phys_attr_t dpap; 6568275SEric Cheng 6578275SEric Cheng if ((kcp = kstat_open()) == NULL) { 6588275SEric Cheng warn("kstat_open operation failed"); 6598275SEric Cheng return (-1); 6608275SEric Cheng } 6618275SEric Cheng 6628453SAnurag.Maskey@Sun.COM if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, 6638453SAnurag.Maskey@Sun.COM &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK) 6648275SEric Cheng return (status); 6658275SEric Cheng 6668275SEric Cheng if (media != DL_ETHER) 6678275SEric Cheng return (DLADM_STATUS_LINKINVAL); 6688275SEric Cheng 6698453SAnurag.Maskey@Sun.COM status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST); 6708275SEric Cheng 6718275SEric Cheng if (status != DLADM_STATUS_OK) 6728275SEric Cheng return (status); 6738275SEric Cheng 6748275SEric Cheng status = dladm_parselink(dpap.dp_dev, module, &instance); 6758275SEric Cheng 6768275SEric Cheng if (status != DLADM_STATUS_OK) 6778275SEric Cheng return (status); 6788275SEric Cheng 6798275SEric Cheng /* 6808275SEric Cheng * The kstat query could fail if the underlying MAC 6818275SEric Cheng * driver was already detached. 6828275SEric Cheng */ 6838275SEric Cheng if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && 6848275SEric Cheng (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) 6858275SEric Cheng goto bail; 6868275SEric Cheng 6878275SEric Cheng if (kstat_read(kcp, ksp, NULL) == -1) 6888275SEric Cheng goto bail; 6898275SEric Cheng 6908275SEric Cheng if (dladm_kstat_value(ksp, name, type, val) < 0) 6918275SEric Cheng goto bail; 6928275SEric Cheng 6938275SEric Cheng (void) kstat_close(kcp); 6948275SEric Cheng return (DLADM_STATUS_OK); 6958275SEric Cheng 6968275SEric Cheng bail: 6978275SEric Cheng (void) kstat_close(kcp); 6988275SEric Cheng return (dladm_errno2status(errno)); 6998275SEric Cheng } 7008275SEric Cheng 7018275SEric Cheng /* Compute sum of 2 pktsums (s1 = s2 + s3) */ 7028275SEric Cheng void 7038275SEric Cheng dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 7048275SEric Cheng { 7058275SEric Cheng s1->rbytes = s2->rbytes + s3->rbytes; 7068275SEric Cheng s1->ipackets = s2->ipackets + s3->ipackets; 7078275SEric Cheng s1->ierrors = s2->ierrors + s3->ierrors; 7088275SEric Cheng s1->obytes = s2->obytes + s3->obytes; 7098275SEric Cheng s1->opackets = s2->opackets + s3->opackets; 7108275SEric Cheng s1->oerrors = s2->oerrors + s3->oerrors; 7118275SEric Cheng s1->snaptime = s2->snaptime; 7128275SEric Cheng } 7138275SEric Cheng 7149220SPrakash.Jalan@Sun.COM #define DIFF_STAT(s2, s3) ((s2) > (s3) ? (s2 - s3) : 0) 7159220SPrakash.Jalan@Sun.COM 7169220SPrakash.Jalan@Sun.COM 7178275SEric Cheng /* Compute differences between 2 pktsums (s1 = s2 - s3) */ 7188275SEric Cheng void 7198275SEric Cheng dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 7208275SEric Cheng { 7219220SPrakash.Jalan@Sun.COM s1->rbytes = DIFF_STAT(s2->rbytes, s3->rbytes); 7229220SPrakash.Jalan@Sun.COM s1->ipackets = DIFF_STAT(s2->ipackets, s3->ipackets); 7239220SPrakash.Jalan@Sun.COM s1->ierrors = DIFF_STAT(s2->ierrors, s3->ierrors); 7249220SPrakash.Jalan@Sun.COM s1->obytes = DIFF_STAT(s2->obytes, s3->obytes); 7259220SPrakash.Jalan@Sun.COM s1->opackets = DIFF_STAT(s2->opackets, s3->opackets); 7269220SPrakash.Jalan@Sun.COM s1->oerrors = DIFF_STAT(s2->oerrors, s3->oerrors); 7279220SPrakash.Jalan@Sun.COM s1->snaptime = DIFF_STAT(s2->snaptime, s3->snaptime); 7288275SEric Cheng } 729