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