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