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