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