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> 318275SEric Cheng #include <kstat.h> 328275SEric Cheng #include <unistd.h> 338275SEric Cheng #include <signal.h> 348275SEric Cheng #include <sys/dld.h> 35*9107Sjames.d.carlson@sun.com #include <sys/ddi.h> 368275SEric Cheng 378275SEric Cheng #include <libdllink.h> 388275SEric Cheng #include <libdlflow.h> 398275SEric Cheng #include <libdlstat.h> 408275SEric Cheng 418275SEric Cheng /* 428275SEric Cheng * x86 <sys/regs> ERR conflicts with <curses.h> ERR. 438275SEric Cheng * Include curses.h last. 448275SEric Cheng */ 458275SEric Cheng #if defined(ERR) 468275SEric Cheng #undef ERR 478275SEric Cheng #endif 488275SEric Cheng #include <curses.h> 498275SEric Cheng 508275SEric Cheng struct flowlist { 518558SGirish.Moodalbail@Sun.COM char flowname[MAXFLOWNAMELEN]; 528275SEric Cheng datalink_id_t linkid; 538957SMichael.Lim@Sun.COM uint64_t ifspeed; 548275SEric Cheng boolean_t first; 558275SEric Cheng boolean_t display; 568275SEric Cheng pktsum_t prevstats; 578275SEric Cheng pktsum_t diffstats; 588275SEric Cheng }; 598275SEric Cheng 608275SEric Cheng static int maxx, maxy, redraw = 0; 618275SEric Cheng static volatile uint_t handle_resize = 0, handle_break = 0; 628275SEric Cheng 638275SEric Cheng pktsum_t totalstats; 648275SEric Cheng struct flowlist *stattable = NULL; 658275SEric Cheng static int statentry = -1, maxstatentries = 0; 668275SEric Cheng 678275SEric Cheng #define STATGROWSIZE 16 688275SEric Cheng 698275SEric Cheng 708275SEric Cheng /* 718275SEric Cheng * Search for flowlist entry in stattable which matches 728275SEric Cheng * the flowname and linkide. If no match is found, use 738275SEric Cheng * next available slot. If no slots are available, 748275SEric Cheng * reallocate table with more slots. 758275SEric Cheng * 768275SEric Cheng * Return: *flowlist of matching flow 778275SEric Cheng * NULL if realloc fails 788275SEric Cheng */ 798275SEric Cheng 808275SEric Cheng static struct flowlist * 818275SEric Cheng findstat(const char *flowname, datalink_id_t linkid) 828275SEric Cheng { 838275SEric Cheng int match = 0; 848275SEric Cheng struct flowlist *flist; 858275SEric Cheng 868275SEric Cheng /* Look for match in the stattable */ 878275SEric Cheng for (match = 0, flist = stattable; 888275SEric Cheng match <= statentry; 898275SEric Cheng match++, flist++) { 908275SEric Cheng 918275SEric Cheng if (flist == NULL) 928275SEric Cheng break; 938275SEric Cheng /* match the flowname */ 948275SEric Cheng if (flowname != NULL) { 958558SGirish.Moodalbail@Sun.COM if (strncmp(flowname, flist->flowname, MAXFLOWNAMELEN) 968275SEric Cheng == NULL) 978275SEric Cheng return (flist); 988275SEric Cheng /* match the linkid */ 998275SEric Cheng } else { 1008275SEric Cheng if (linkid == flist->linkid) 1018275SEric Cheng return (flist); 1028275SEric Cheng } 1038275SEric Cheng } 1048275SEric Cheng 1058275SEric Cheng /* 1068275SEric Cheng * No match found in the table. Store statistics in the next slot. 1078275SEric Cheng * If necessary, make room for this entry. 1088275SEric Cheng */ 1098275SEric Cheng statentry++; 1108275SEric Cheng if ((maxstatentries == 0) || (maxstatentries == statentry)) { 1118275SEric Cheng maxstatentries += STATGROWSIZE; 1128275SEric Cheng stattable = realloc(stattable, 1138275SEric Cheng maxstatentries * sizeof (struct flowlist)); 1148275SEric Cheng if (stattable == NULL) { 1158275SEric Cheng perror("realloc"); 1168275SEric Cheng return (struct flowlist *)(NULL); 1178275SEric Cheng } 1188275SEric Cheng } 1198275SEric Cheng flist = &stattable[statentry]; 1208275SEric Cheng bzero(flist, sizeof (struct flowlist)); 1218275SEric Cheng flist->first = B_TRUE; 1228275SEric Cheng 1238275SEric Cheng if (flowname != NULL) 1248558SGirish.Moodalbail@Sun.COM (void) strncpy(flist->flowname, flowname, MAXFLOWNAMELEN); 1258275SEric Cheng flist->linkid = linkid; 1268275SEric Cheng return (flist); 1278275SEric Cheng } 1288275SEric Cheng 1298275SEric Cheng static void 1308453SAnurag.Maskey@Sun.COM print_flow_stats(dladm_handle_t handle, struct flowlist *flist) 1318275SEric Cheng { 1328275SEric Cheng struct flowlist *fcurr; 1338275SEric Cheng double ikbs, okbs; 1348275SEric Cheng double ipks, opks; 1358275SEric Cheng double dlt; 1368275SEric Cheng int fcount; 1378275SEric Cheng static boolean_t first = B_TRUE; 1388275SEric Cheng 1398275SEric Cheng if (first) { 1408275SEric Cheng first = B_FALSE; 1418275SEric Cheng (void) printw("please wait...\n"); 1428275SEric Cheng return; 1438275SEric Cheng } 1448275SEric Cheng 1458275SEric Cheng for (fcount = 0, fcurr = flist; 1468275SEric Cheng fcount <= statentry; 1478275SEric Cheng fcount++, fcurr++) { 1488275SEric Cheng if (fcurr->flowname && fcurr->display) { 1498558SGirish.Moodalbail@Sun.COM char linkname[MAXLINKNAMELEN]; 1508275SEric Cheng 1518453SAnurag.Maskey@Sun.COM (void) dladm_datalink_id2info(handle, fcurr->linkid, 1528453SAnurag.Maskey@Sun.COM NULL, NULL, NULL, linkname, sizeof (linkname)); 1538275SEric Cheng dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; 1548275SEric Cheng ikbs = fcurr->diffstats.rbytes * 8 / dlt / 1024; 1558275SEric Cheng okbs = fcurr->diffstats.obytes * 8 / dlt / 1024; 1568275SEric Cheng ipks = fcurr->diffstats.ipackets / dlt; 1578275SEric Cheng opks = fcurr->diffstats.opackets / dlt; 1588275SEric Cheng (void) printw("%-15.15s", fcurr->flowname); 1598275SEric Cheng (void) printw("%-10.10s", linkname); 1608275SEric Cheng (void) printw("%9.2f %9.2f %9.2f %9.2f ", 1618275SEric Cheng ikbs, okbs, ipks, opks); 1628275SEric Cheng (void) printw("\n"); 1638275SEric Cheng } 1648275SEric Cheng } 1658275SEric Cheng } 1668275SEric Cheng 1678275SEric Cheng /*ARGSUSED*/ 1688275SEric Cheng static int 1698275SEric Cheng flow_kstats(dladm_flow_attr_t *attr, void *arg) 1708275SEric Cheng { 1718275SEric Cheng kstat_ctl_t *kcp = (kstat_ctl_t *)arg; 1728275SEric Cheng kstat_t *ksp; 1738275SEric Cheng struct flowlist *flist; 1748275SEric Cheng pktsum_t currstats, *prevstats, *diffstats; 1758275SEric Cheng 1768275SEric Cheng flist = findstat(attr->fa_flowname, attr->fa_linkid); 1778275SEric Cheng if (flist != NULL) { 1788275SEric Cheng prevstats = &flist->prevstats; 1798275SEric Cheng diffstats = &flist->diffstats; 1808275SEric Cheng } else { 1818275SEric Cheng return (DLADM_STATUS_FAILED); 1828275SEric Cheng } 1838275SEric Cheng 1848275SEric Cheng /* lookup kstat entry */ 1858275SEric Cheng ksp = dladm_kstat_lookup(kcp, NULL, -1, attr->fa_flowname, "flow"); 1868275SEric Cheng 1878275SEric Cheng if (ksp == NULL) 1888275SEric Cheng return (DLADM_WALK_TERMINATE); 1898275SEric Cheng else 1908275SEric Cheng flist->display = B_TRUE; 1918275SEric Cheng 1928275SEric Cheng dladm_get_stats(kcp, ksp, &currstats); 1938275SEric Cheng if (flist->ifspeed == 0) 1948275SEric Cheng (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, 1958275SEric Cheng &flist->ifspeed); 1968275SEric Cheng 1978275SEric Cheng if (flist->first) 1988275SEric Cheng flist->first = B_FALSE; 1998275SEric Cheng else { 2008275SEric Cheng dladm_stats_diff(diffstats, &currstats, prevstats); 2018275SEric Cheng dladm_stats_total(&totalstats, diffstats, &totalstats); 2028275SEric Cheng } 2038275SEric Cheng 2048275SEric Cheng bcopy(&currstats, prevstats, sizeof (pktsum_t)); 2058275SEric Cheng return (DLADM_WALK_CONTINUE); 2068275SEric Cheng } 2078275SEric Cheng 2088275SEric Cheng static void 2098453SAnurag.Maskey@Sun.COM print_link_stats(dladm_handle_t handle, struct flowlist *flist) 2108275SEric Cheng { 2118275SEric Cheng struct flowlist *fcurr; 2128275SEric Cheng double ikbs, okbs; 2138275SEric Cheng double ipks, opks; 2148275SEric Cheng double util; 2158275SEric Cheng double dlt; 2168275SEric Cheng int fcount; 2178275SEric Cheng static boolean_t first = B_TRUE; 2188275SEric Cheng 2198275SEric Cheng if (first) { 2208275SEric Cheng first = B_FALSE; 2218275SEric Cheng (void) printw("please wait...\n"); 2228275SEric Cheng return; 2238275SEric Cheng } 2248275SEric Cheng 2258275SEric Cheng for (fcount = 0, fcurr = flist; 2268275SEric Cheng fcount <= statentry; 2278275SEric Cheng fcount++, fcurr++) { 2288275SEric Cheng if ((fcurr->linkid != DATALINK_INVALID_LINKID) && 2298275SEric Cheng fcurr->display) { 2308558SGirish.Moodalbail@Sun.COM char linkname[MAXLINKNAMELEN]; 2318275SEric Cheng 2328453SAnurag.Maskey@Sun.COM (void) dladm_datalink_id2info(handle, fcurr->linkid, 2338453SAnurag.Maskey@Sun.COM NULL, NULL, NULL, linkname, sizeof (linkname)); 2348275SEric Cheng dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; 2358275SEric Cheng ikbs = (double)fcurr->diffstats.rbytes * 8 / dlt / 1024; 2368275SEric Cheng okbs = (double)fcurr->diffstats.obytes * 8 / dlt / 1024; 2378275SEric Cheng ipks = (double)fcurr->diffstats.ipackets / dlt; 2388275SEric Cheng opks = (double)fcurr->diffstats.opackets / dlt; 2398275SEric Cheng (void) printw("%-10.10s", linkname); 2408275SEric Cheng (void) printw("%9.2f %9.2f %9.2f %9.2f ", 2418275SEric Cheng ikbs, okbs, ipks, opks); 2428275SEric Cheng if (fcurr->ifspeed != 0) 2438275SEric Cheng util = ((ikbs + okbs) * 1024) * 2448275SEric Cheng 100/ fcurr->ifspeed; 2458275SEric Cheng else 2468275SEric Cheng util = (double)0; 2478275SEric Cheng (void) attron(A_BOLD); 2488275SEric Cheng (void) printw(" %6.2f", util); 2498275SEric Cheng (void) attroff(A_BOLD); 2508275SEric Cheng (void) printw("\n"); 2518275SEric Cheng } 2528275SEric Cheng } 2538275SEric Cheng } 2548275SEric Cheng 2558275SEric Cheng /* 2568275SEric Cheng * This function is called through the dladm_walk_datalink_id() walker and 2578275SEric Cheng * calls the dladm_walk_flow() walker. 2588275SEric Cheng */ 2598275SEric Cheng 2608275SEric Cheng /*ARGSUSED*/ 2618275SEric Cheng static int 2628453SAnurag.Maskey@Sun.COM link_flowstats(dladm_handle_t handle, datalink_id_t linkid, void *arg) 2638275SEric Cheng { 2648621SMichael.Lim@Sun.COM dladm_status_t status; 2658621SMichael.Lim@Sun.COM 2668621SMichael.Lim@Sun.COM status = dladm_walk_flow(flow_kstats, handle, linkid, arg, B_FALSE); 2678621SMichael.Lim@Sun.COM if (status == DLADM_STATUS_OK) 2688621SMichael.Lim@Sun.COM return (DLADM_WALK_CONTINUE); 2698621SMichael.Lim@Sun.COM else 2708621SMichael.Lim@Sun.COM return (DLADM_WALK_TERMINATE); 2718275SEric Cheng } 2728275SEric Cheng 2738275SEric Cheng /*ARGSUSED*/ 2748275SEric Cheng static int 2758453SAnurag.Maskey@Sun.COM link_kstats(dladm_handle_t handle, datalink_id_t linkid, void *arg) 2768275SEric Cheng { 2778275SEric Cheng kstat_ctl_t *kcp = (kstat_ctl_t *)arg; 2788275SEric Cheng struct flowlist *flist; 2798275SEric Cheng pktsum_t currstats, *prevstats, *diffstats; 2808275SEric Cheng kstat_t *ksp; 2818558SGirish.Moodalbail@Sun.COM char linkname[MAXLINKNAMELEN]; 2828275SEric Cheng 2838275SEric Cheng /* find the flist entry */ 2848275SEric Cheng flist = findstat(NULL, linkid); 2858275SEric Cheng if (flist != NULL) { 2868275SEric Cheng prevstats = &flist->prevstats; 2878275SEric Cheng diffstats = &flist->diffstats; 2888275SEric Cheng } else { 2898275SEric Cheng return (DLADM_WALK_CONTINUE); 2908275SEric Cheng } 2918275SEric Cheng 2928275SEric Cheng /* lookup kstat entry */ 2938453SAnurag.Maskey@Sun.COM (void) dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL, 2948453SAnurag.Maskey@Sun.COM linkname, sizeof (linkname)); 2958275SEric Cheng 2968275SEric Cheng if (linkname == NULL) { 2978275SEric Cheng warn("no linkname for linkid"); 2988275SEric Cheng return (DLADM_WALK_TERMINATE); 2998275SEric Cheng } 3008275SEric Cheng 3018275SEric Cheng ksp = dladm_kstat_lookup(kcp, NULL, -1, linkname, "net"); 3028275SEric Cheng 3038275SEric Cheng if (ksp == NULL) 3048275SEric Cheng return (DLADM_WALK_TERMINATE); 3058275SEric Cheng else 3068275SEric Cheng flist->display = B_TRUE; 3078275SEric Cheng 3088275SEric Cheng /* read packet and byte stats */ 3098275SEric Cheng dladm_get_stats(kcp, ksp, &currstats); 3108275SEric Cheng 3118275SEric Cheng if (flist->ifspeed == 0) 3128275SEric Cheng (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, 3138275SEric Cheng &flist->ifspeed); 3148275SEric Cheng 3158275SEric Cheng if (flist->first == B_TRUE) 3168275SEric Cheng flist->first = B_FALSE; 3178275SEric Cheng else 3188275SEric Cheng dladm_stats_diff(diffstats, &currstats, prevstats); 3198275SEric Cheng 3208275SEric Cheng bcopy(&currstats, prevstats, sizeof (*prevstats)); 3218275SEric Cheng 3228275SEric Cheng return (DLADM_WALK_CONTINUE); 3238275SEric Cheng } 3248275SEric Cheng 3258275SEric Cheng /*ARGSUSED*/ 3268275SEric Cheng static void 3278275SEric Cheng sig_break(int s) 3288275SEric Cheng { 3298275SEric Cheng handle_break = 1; 3308275SEric Cheng } 3318275SEric Cheng 3328275SEric Cheng /*ARGSUSED*/ 3338275SEric Cheng static void 3348275SEric Cheng sig_resize(int s) 3358275SEric Cheng { 3368275SEric Cheng handle_resize = 1; 3378275SEric Cheng } 3388275SEric Cheng 3398275SEric Cheng static void 3408275SEric Cheng curses_init() 3418275SEric Cheng { 3428275SEric Cheng maxx = maxx; /* lint */ 3438275SEric Cheng maxy = maxy; /* lint */ 3448275SEric Cheng 3458275SEric Cheng /* Install signal handlers */ 3468275SEric Cheng (void) signal(SIGINT, sig_break); 3478275SEric Cheng (void) signal(SIGQUIT, sig_break); 3488275SEric Cheng (void) signal(SIGTERM, sig_break); 3498275SEric Cheng (void) signal(SIGWINCH, sig_resize); 3508275SEric Cheng 3518275SEric Cheng /* Initialize ncurses */ 3528275SEric Cheng (void) initscr(); 3538275SEric Cheng (void) cbreak(); 3548275SEric Cheng (void) noecho(); 3558275SEric Cheng (void) curs_set(0); 3568275SEric Cheng timeout(0); 3578275SEric Cheng getmaxyx(stdscr, maxy, maxx); 3588275SEric Cheng } 3598275SEric Cheng 3608275SEric Cheng static void 3618275SEric Cheng curses_fin() 3628275SEric Cheng { 3638275SEric Cheng (void) printw("\n"); 3648275SEric Cheng (void) curs_set(1); 3658275SEric Cheng (void) nocbreak(); 3668275SEric Cheng (void) endwin(); 3678275SEric Cheng 3688275SEric Cheng free(stattable); 3698275SEric Cheng } 3708275SEric Cheng 3718275SEric Cheng static void 3728453SAnurag.Maskey@Sun.COM stat_report(dladm_handle_t handle, kstat_ctl_t *kcp, datalink_id_t linkid, 3738453SAnurag.Maskey@Sun.COM const char *flowname, int opt) 3748275SEric Cheng { 3758275SEric Cheng 3768275SEric Cheng double dlt, ikbs, okbs, ipks, opks; 3778275SEric Cheng 3788275SEric Cheng struct flowlist *fstable = stattable; 3798275SEric Cheng 3808275SEric Cheng if ((opt != LINK_REPORT) && (opt != FLOW_REPORT)) 3818275SEric Cheng return; 3828275SEric Cheng 3838275SEric Cheng /* Handle window resizes */ 3848275SEric Cheng if (handle_resize) { 3858275SEric Cheng (void) endwin(); 3868275SEric Cheng (void) initscr(); 3878275SEric Cheng (void) cbreak(); 3888275SEric Cheng (void) noecho(); 3898275SEric Cheng (void) curs_set(0); 3908275SEric Cheng timeout(0); 3918275SEric Cheng getmaxyx(stdscr, maxy, maxx); 3928275SEric Cheng redraw = 1; 3938275SEric Cheng handle_resize = 0; 3948275SEric Cheng } 3958275SEric Cheng 3968275SEric Cheng /* Print title */ 3978275SEric Cheng (void) erase(); 3988275SEric Cheng (void) attron(A_BOLD); 3998275SEric Cheng (void) move(0, 0); 4008275SEric Cheng if (opt == FLOW_REPORT) 4018275SEric Cheng (void) printw("%-15.15s", "Flow"); 4028275SEric Cheng (void) printw("%-10.10s", "Link"); 4038275SEric Cheng (void) printw("%9.9s %9.9s %9.9s %9.9s ", 4048275SEric Cheng "iKb/s", "oKb/s", "iPk/s", "oPk/s"); 4058275SEric Cheng if (opt == LINK_REPORT) 4068275SEric Cheng (void) printw(" %6.6s", "%Util"); 4078275SEric Cheng (void) printw("\n"); 4088275SEric Cheng (void) attroff(A_BOLD); 4098275SEric Cheng 4108275SEric Cheng (void) move(2, 0); 4118275SEric Cheng 4128275SEric Cheng /* Print stats for each link or flow */ 4138275SEric Cheng bzero(&totalstats, sizeof (totalstats)); 4148275SEric Cheng if (opt == LINK_REPORT) { 4158275SEric Cheng /* Display all links */ 4168275SEric Cheng if (linkid == DATALINK_ALL_LINKID) { 4178453SAnurag.Maskey@Sun.COM (void) dladm_walk_datalink_id(link_kstats, handle, 4188275SEric Cheng (void *)kcp, DATALINK_CLASS_ALL, 4198275SEric Cheng DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 4208275SEric Cheng /* Display 1 link */ 4218275SEric Cheng } else { 4228453SAnurag.Maskey@Sun.COM (void) link_kstats(handle, linkid, kcp); 4238275SEric Cheng } 4248453SAnurag.Maskey@Sun.COM print_link_stats(handle, fstable); 4258275SEric Cheng 4268275SEric Cheng } else if (opt == FLOW_REPORT) { 4278275SEric Cheng /* Display 1 flow */ 4288275SEric Cheng if (flowname != NULL) { 4298275SEric Cheng dladm_flow_attr_t fattr; 4308453SAnurag.Maskey@Sun.COM if (dladm_flow_info(handle, flowname, &fattr) != 4318275SEric Cheng DLADM_STATUS_OK) 4328275SEric Cheng return; 4338275SEric Cheng (void) flow_kstats(&fattr, kcp); 4348275SEric Cheng /* Display all flows on all links */ 4358275SEric Cheng } else if (linkid == DATALINK_ALL_LINKID) { 4368453SAnurag.Maskey@Sun.COM (void) dladm_walk_datalink_id(link_flowstats, handle, 4378275SEric Cheng (void *)kcp, DATALINK_CLASS_ALL, 4388275SEric Cheng DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 4398275SEric Cheng /* Display all flows on a link */ 4408275SEric Cheng } else if (linkid != DATALINK_INVALID_LINKID) { 4418453SAnurag.Maskey@Sun.COM (void) dladm_walk_flow(flow_kstats, handle, linkid, kcp, 4428275SEric Cheng B_FALSE); 4438275SEric Cheng } 4448453SAnurag.Maskey@Sun.COM print_flow_stats(handle, fstable); 4458275SEric Cheng 4468275SEric Cheng /* Print totals */ 4478275SEric Cheng (void) attron(A_BOLD); 4488275SEric Cheng dlt = (double)totalstats.snaptime / (double)NANOSEC; 4498275SEric Cheng ikbs = totalstats.rbytes / dlt / 1024; 4508275SEric Cheng okbs = totalstats.obytes / dlt / 1024; 4518275SEric Cheng ipks = totalstats.ipackets / dlt; 4528275SEric Cheng opks = totalstats.opackets / dlt; 4538275SEric Cheng (void) printw("\n%-25.25s", "Totals"); 4548275SEric Cheng (void) printw("%9.2f %9.2f %9.2f %9.2f ", 4558275SEric Cheng ikbs, okbs, ipks, opks); 4568275SEric Cheng (void) attroff(A_BOLD); 4578275SEric Cheng } 4588275SEric Cheng 4598275SEric Cheng if (redraw) 4608275SEric Cheng (void) clearok(stdscr, 1); 4618275SEric Cheng 4628275SEric Cheng if (refresh() == ERR) 4638275SEric Cheng return; 4648275SEric Cheng 4658275SEric Cheng if (redraw) { 4668275SEric Cheng (void) clearok(stdscr, 0); 4678275SEric Cheng redraw = 0; 4688275SEric Cheng } 4698275SEric Cheng } 4708275SEric Cheng 4718275SEric Cheng /* Exported functions */ 4728275SEric Cheng 4738275SEric Cheng /* 4748275SEric Cheng * Continuously display link or flow statstics using a libcurses 4758275SEric Cheng * based display. 4768275SEric Cheng */ 4778275SEric Cheng 4788275SEric Cheng void 4798453SAnurag.Maskey@Sun.COM dladm_continuous(dladm_handle_t handle, datalink_id_t linkid, 4808453SAnurag.Maskey@Sun.COM const char *flowname, int interval, int opt) 4818275SEric Cheng { 4828275SEric Cheng kstat_ctl_t *kcp; 4838275SEric Cheng 4848275SEric Cheng if ((kcp = kstat_open()) == NULL) { 4858275SEric Cheng warn("kstat open operation failed"); 4868275SEric Cheng return; 4878275SEric Cheng } 4888275SEric Cheng 4898275SEric Cheng curses_init(); 4908275SEric Cheng 4918275SEric Cheng for (;;) { 4928275SEric Cheng 4938275SEric Cheng if (handle_break) 4948275SEric Cheng break; 4958275SEric Cheng 4968453SAnurag.Maskey@Sun.COM stat_report(handle, kcp, linkid, flowname, opt); 4978275SEric Cheng 4988275SEric Cheng (void) sleep(max(1, interval)); 4998275SEric Cheng } 5008275SEric Cheng 5018275SEric Cheng (void) curses_fin(); 5028275SEric Cheng (void) kstat_close(kcp); 5038275SEric Cheng } 5048275SEric Cheng 5058275SEric Cheng /* 5068275SEric Cheng * dladm_kstat_lookup() is a modified version of kstat_lookup which 5078275SEric Cheng * adds the class as a selector. 5088275SEric Cheng */ 5098275SEric Cheng 5108275SEric Cheng kstat_t * 5118275SEric Cheng dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance, 5128275SEric Cheng const char *name, const char *class) 5138275SEric Cheng { 5148275SEric Cheng kstat_t *ksp = NULL; 5158275SEric Cheng 5168275SEric Cheng for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { 5178275SEric Cheng if ((module == NULL || strcmp(ksp->ks_module, module) == 0) && 5188275SEric Cheng (instance == -1 || ksp->ks_instance == instance) && 5198275SEric Cheng (name == NULL || strcmp(ksp->ks_name, name) == 0) && 5208275SEric Cheng (class == NULL || strcmp(ksp->ks_class, class) == 0)) 5218275SEric Cheng return (ksp); 5228275SEric Cheng } 5238275SEric Cheng 5248275SEric Cheng errno = ENOENT; 5258275SEric Cheng return (NULL); 5268275SEric Cheng } 5278275SEric Cheng 5288275SEric Cheng /* 5298275SEric Cheng * dladm_get_stats() populates the supplied pktsum_t structure with 5308275SEric Cheng * the input and output packet and byte kstats from the kstat_t 5318275SEric Cheng * found with dladm_kstat_lookup. 5328275SEric Cheng */ 5338275SEric Cheng void 5348275SEric Cheng dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats) 5358275SEric Cheng { 5368275SEric Cheng 5378275SEric Cheng if (kstat_read(kcp, ksp, NULL) == -1) 5388275SEric Cheng return; 5398275SEric Cheng 5408275SEric Cheng stats->snaptime = gethrtime(); 5418275SEric Cheng 5428275SEric Cheng if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64, 5438275SEric Cheng &stats->ipackets) < 0) { 5448275SEric Cheng if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64, 5458275SEric Cheng &stats->ipackets) < 0) 5468275SEric Cheng return; 5478275SEric Cheng } 5488275SEric Cheng 5498275SEric Cheng if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64, 5508275SEric Cheng &stats->opackets) < 0) { 5518275SEric Cheng if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64, 5528275SEric Cheng &stats->opackets) < 0) 5538275SEric Cheng return; 5548275SEric Cheng } 5558275SEric Cheng 5568275SEric Cheng if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64, 5578275SEric Cheng &stats->rbytes) < 0) { 5588275SEric Cheng if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64, 5598275SEric Cheng &stats->rbytes) < 0) 5608275SEric Cheng return; 5618275SEric Cheng } 5628275SEric Cheng 5638275SEric Cheng if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64, 5648275SEric Cheng &stats->obytes) < 0) { 5658275SEric Cheng if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64, 5668275SEric Cheng &stats->obytes) < 0) 5678275SEric Cheng return; 5688275SEric Cheng } 5698275SEric Cheng 5708275SEric Cheng if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32, 5718275SEric Cheng &stats->ierrors) < 0) { 5728275SEric Cheng if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64, 5738275SEric Cheng &stats->ierrors) < 0) 5748275SEric Cheng return; 5758275SEric Cheng } 5768275SEric Cheng 5778275SEric Cheng if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32, 5788275SEric Cheng &stats->oerrors) < 0) { 5798275SEric Cheng if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64, 5808275SEric Cheng &stats->oerrors) < 0) 5818275SEric Cheng return; 5828275SEric Cheng } 5838275SEric Cheng } 5848275SEric Cheng 5858275SEric Cheng int 5868275SEric Cheng dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) 5878275SEric Cheng { 5888275SEric Cheng kstat_named_t *knp; 5898275SEric Cheng 5908275SEric Cheng if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) 5918275SEric Cheng return (-1); 5928275SEric Cheng 5938275SEric Cheng if (knp->data_type != type) 5948275SEric Cheng return (-1); 5958275SEric Cheng 5968275SEric Cheng switch (type) { 5978275SEric Cheng case KSTAT_DATA_UINT64: 5988275SEric Cheng *(uint64_t *)buf = knp->value.ui64; 5998275SEric Cheng break; 6008275SEric Cheng case KSTAT_DATA_UINT32: 6018275SEric Cheng *(uint32_t *)buf = knp->value.ui32; 6028275SEric Cheng break; 6038275SEric Cheng default: 6048275SEric Cheng return (-1); 6058275SEric Cheng } 6068275SEric Cheng 6078275SEric Cheng return (0); 6088275SEric Cheng } 6098275SEric Cheng 6108275SEric Cheng dladm_status_t 6118453SAnurag.Maskey@Sun.COM dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid, 6128453SAnurag.Maskey@Sun.COM const char *name, uint8_t type, void *val) 6138275SEric Cheng { 6148275SEric Cheng kstat_ctl_t *kcp; 6158275SEric Cheng char module[DLPI_LINKNAME_MAX]; 6168275SEric Cheng uint_t instance; 6178275SEric Cheng char link[DLPI_LINKNAME_MAX]; 6188275SEric Cheng dladm_status_t status; 6198275SEric Cheng uint32_t flags, media; 6208275SEric Cheng kstat_t *ksp; 6218275SEric Cheng dladm_phys_attr_t dpap; 6228275SEric Cheng 6238275SEric Cheng if ((kcp = kstat_open()) == NULL) { 6248275SEric Cheng warn("kstat_open operation failed"); 6258275SEric Cheng return (-1); 6268275SEric Cheng } 6278275SEric Cheng 6288453SAnurag.Maskey@Sun.COM if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, 6298453SAnurag.Maskey@Sun.COM &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK) 6308275SEric Cheng return (status); 6318275SEric Cheng 6328275SEric Cheng if (media != DL_ETHER) 6338275SEric Cheng return (DLADM_STATUS_LINKINVAL); 6348275SEric Cheng 6358453SAnurag.Maskey@Sun.COM status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST); 6368275SEric Cheng 6378275SEric Cheng if (status != DLADM_STATUS_OK) 6388275SEric Cheng return (status); 6398275SEric Cheng 6408275SEric Cheng status = dladm_parselink(dpap.dp_dev, module, &instance); 6418275SEric Cheng 6428275SEric Cheng if (status != DLADM_STATUS_OK) 6438275SEric Cheng return (status); 6448275SEric Cheng 6458275SEric Cheng /* 6468275SEric Cheng * The kstat query could fail if the underlying MAC 6478275SEric Cheng * driver was already detached. 6488275SEric Cheng */ 6498275SEric Cheng if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && 6508275SEric Cheng (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) 6518275SEric Cheng goto bail; 6528275SEric Cheng 6538275SEric Cheng if (kstat_read(kcp, ksp, NULL) == -1) 6548275SEric Cheng goto bail; 6558275SEric Cheng 6568275SEric Cheng if (dladm_kstat_value(ksp, name, type, val) < 0) 6578275SEric Cheng goto bail; 6588275SEric Cheng 6598275SEric Cheng (void) kstat_close(kcp); 6608275SEric Cheng return (DLADM_STATUS_OK); 6618275SEric Cheng 6628275SEric Cheng bail: 6638275SEric Cheng (void) kstat_close(kcp); 6648275SEric Cheng return (dladm_errno2status(errno)); 6658275SEric Cheng } 6668275SEric Cheng 6678275SEric Cheng /* Compute sum of 2 pktsums (s1 = s2 + s3) */ 6688275SEric Cheng void 6698275SEric Cheng dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 6708275SEric Cheng { 6718275SEric Cheng s1->rbytes = s2->rbytes + s3->rbytes; 6728275SEric Cheng s1->ipackets = s2->ipackets + s3->ipackets; 6738275SEric Cheng s1->ierrors = s2->ierrors + s3->ierrors; 6748275SEric Cheng s1->obytes = s2->obytes + s3->obytes; 6758275SEric Cheng s1->opackets = s2->opackets + s3->opackets; 6768275SEric Cheng s1->oerrors = s2->oerrors + s3->oerrors; 6778275SEric Cheng s1->snaptime = s2->snaptime; 6788275SEric Cheng } 6798275SEric Cheng 6808275SEric Cheng /* Compute differences between 2 pktsums (s1 = s2 - s3) */ 6818275SEric Cheng void 6828275SEric Cheng dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 6838275SEric Cheng { 6848275SEric Cheng s1->rbytes = s2->rbytes - s3->rbytes; 6858275SEric Cheng s1->ipackets = s2->ipackets - s3->ipackets; 6868275SEric Cheng s1->ierrors = s2->ierrors - s3->ierrors; 6878275SEric Cheng s1->obytes = s2->obytes - s3->obytes; 6888275SEric Cheng s1->opackets = s2->opackets - s3->opackets; 6898275SEric Cheng s1->oerrors = s2->oerrors - s3->oerrors; 6908275SEric Cheng s1->snaptime = s2->snaptime - s3->snaptime; 6918275SEric Cheng } 692