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