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