xref: /illumos-gate/usr/src/cmd/dlstat/dlstat.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright 2017 Joyent, Inc.
28  */
29 
30 /*
31  * Copyright 2020 Peter Tribble.
32  */
33 
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <locale.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 #include <string.h>
42 #include <stropts.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 #include <strings.h>
46 #include <getopt.h>
47 #include <unistd.h>
48 #include <priv.h>
49 #include <termios.h>
50 #include <pwd.h>
51 #include <auth_attr.h>
52 #include <auth_list.h>
53 #include <libintl.h>
54 #include <libdevinfo.h>
55 #include <libdlpi.h>
56 #include <libdladm.h>
57 #include <libdllink.h>
58 #include <libdlstat.h>
59 #include <libdlaggr.h>
60 #include <libinetutil.h>
61 #include <bsm/adt.h>
62 #include <bsm/adt_event.h>
63 #include <stddef.h>
64 #include <ofmt.h>
65 
66 typedef struct link_chain_s {
67 	datalink_id_t		lc_linkid;
68 	boolean_t		lc_visited;
69 	dladm_stat_chain_t	*lc_statchain[DLADM_STAT_NUM_STATS];
70 	struct link_chain_s	*lc_next;
71 } link_chain_t;
72 
73 typedef void *	(*stats2str_t)(const char *, void *,
74 		    char, boolean_t);
75 
76 typedef struct show_state {
77 	link_chain_t	*ls_linkchain;
78 	boolean_t	ls_stattype[DLADM_STAT_NUM_STATS];
79 	stats2str_t	ls_stats2str[DLADM_STAT_NUM_STATS];
80 	ofmt_handle_t	ls_ofmt;
81 	char		ls_unit;
82 	boolean_t	ls_parsable;
83 } show_state_t;
84 
85 typedef struct show_history_state_s {
86 	boolean_t	hs_plot;
87 	boolean_t	hs_parsable;
88 	boolean_t	hs_printheader;
89 	boolean_t	hs_first;
90 	boolean_t	hs_showall;
91 	ofmt_handle_t	hs_ofmt;
92 } show_history_state_t;
93 
94 /*
95  * callback functions for printing output and error diagnostics.
96  */
97 static ofmt_cb_t print_default_cb;
98 
99 typedef void cmdfunc_t(int, char **, const char *);
100 
101 static cmdfunc_t do_show, do_show_history, do_show_phys, do_show_link;
102 static cmdfunc_t do_show_aggr;
103 
104 static void	die(const char *, ...);
105 static void	die_optdup(int);
106 static void	die_opterr(int, int, const char *);
107 static void	die_dlerr(dladm_status_t, const char *, ...);
108 static void	warn(const char *, ...);
109 
110 typedef struct	cmd {
111 	char		*c_name;
112 	cmdfunc_t	*c_fn;
113 	const char	*c_usage;
114 } cmd_t;
115 
116 static cmd_t	cmds[] = {
117 	{ "",		do_show,
118 	    "dlstat [-r | -t] [-i <interval>] [link]\n"
119 	    "       dlstat [-a | -A] [-i <interval>] [-p] [ -o field[,...]]\n"
120 	    "              [-u R|K|M|G|T|P] [link]"},
121 	{ "show-phys", do_show_phys,
122 	    "dlstat show-phys [-r | -t] [-i interval] [-a]\n"
123 	    "                 [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
124 	    "[link]"},
125 	{ "show-link", do_show_link,
126 	    "dlstat show-link [-r [-F] | -t] [-i interval] [-a]\n"
127 	    "                 [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
128 	    "[link]\n"
129 	    "       dlstat show-link -h [-a] [-d] [-F <format>]\n"
130 	    "                 [-s <DD/MM/YYYY,HH:MM:SS>] "
131 	    "[-e <DD/MM/YYYY,HH:MM:SS>]\n"
132 	    "                 -f <logfile> [<link>]" },
133 	{ "show-aggr", do_show_aggr,
134 	    "dlstat show-aggr [-r | -t] [-i interval] [-p]\n"
135 	    "                 [ -o field[,...]] [-u R|K|M|G|T|P] "
136 	    " [link]" }
137 };
138 
139 #define	MAXSTATLEN 15
140 
141 /*
142  * dlstat : total stat fields
143  */
144 typedef struct total_fields_buf_s {
145 	char t_linkname[MAXLINKNAMELEN];
146 	char t_ipackets[MAXSTATLEN];
147 	char t_rbytes[MAXSTATLEN];
148 	char t_opackets[MAXSTATLEN];
149 	char t_obytes[MAXSTATLEN];
150 } total_fields_buf_t;
151 
152 static ofmt_field_t total_s_fields[] = {
153 { "LINK",	15,
154     offsetof(total_fields_buf_t, t_linkname),	print_default_cb},
155 { "IPKTS",	8,
156     offsetof(total_fields_buf_t, t_ipackets),	print_default_cb},
157 { "RBYTES",	8,
158     offsetof(total_fields_buf_t, t_rbytes),	print_default_cb},
159 { "OPKTS",	8,
160     offsetof(total_fields_buf_t, t_opackets),	print_default_cb},
161 { "OBYTES",	8,
162     offsetof(total_fields_buf_t, t_obytes),	print_default_cb},
163 { NULL,		0,	0,		NULL}};
164 
165 /*
166  * dlstat show-phys: both Rx and Tx stat fields
167  */
168 typedef struct ring_fields_buf_s {
169 	char r_linkname[MAXLINKNAMELEN];
170 	char r_type[MAXSTATLEN];
171 	char r_id[MAXSTATLEN];
172 	char r_index[MAXSTATLEN];
173 	char r_packets[MAXSTATLEN];
174 	char r_bytes[MAXSTATLEN];
175 } ring_fields_buf_t;
176 
177 static ofmt_field_t ring_s_fields[] = {
178 { "LINK",	15,
179     offsetof(ring_fields_buf_t, r_linkname),	print_default_cb},
180 { "TYPE",	5,
181     offsetof(ring_fields_buf_t, r_type),	print_default_cb},
182 { "ID",		7,
183     offsetof(ring_fields_buf_t, r_id),		print_default_cb},
184 { "INDEX",	6,
185     offsetof(ring_fields_buf_t, r_index),	print_default_cb},
186 { "PKTS",	8,
187     offsetof(ring_fields_buf_t, r_packets),	print_default_cb},
188 { "BYTES",	8,
189     offsetof(ring_fields_buf_t, r_bytes),	print_default_cb},
190 { NULL,		0,		0,		NULL}};
191 
192 /*
193  * dlstat show-phys -r: Rx Ring stat fields
194  */
195 typedef struct rx_ring_fields_buf_s {
196 	char rr_linkname[MAXLINKNAMELEN];
197 	char rr_type[MAXSTATLEN];
198 	char rr_id[MAXSTATLEN];
199 	char rr_index[MAXSTATLEN];
200 	char rr_ipackets[MAXSTATLEN];
201 	char rr_rbytes[MAXSTATLEN];
202 } rx_ring_fields_buf_t;
203 
204 static ofmt_field_t rx_ring_s_fields[] = {
205 { "LINK",	15,
206     offsetof(rx_ring_fields_buf_t, rr_linkname),	print_default_cb},
207 { "TYPE",	5,
208     offsetof(rx_ring_fields_buf_t, rr_type),		print_default_cb},
209 { "ID",		7,
210     offsetof(rx_ring_fields_buf_t, rr_id),		print_default_cb},
211 { "INDEX",	6,
212     offsetof(rx_ring_fields_buf_t, rr_index),		print_default_cb},
213 { "IPKTS",	8,
214     offsetof(rx_ring_fields_buf_t, rr_ipackets),	print_default_cb},
215 { "RBYTES",	8,
216     offsetof(rx_ring_fields_buf_t, rr_rbytes),		print_default_cb},
217 { NULL,		0,		0,		NULL}};
218 
219 /*
220  * dlstat show-phys -t: Tx Ring stat fields
221  */
222 typedef struct tx_ring_fields_buf_s {
223 	char tr_linkname[MAXLINKNAMELEN];
224 	char tr_type[MAXSTATLEN];
225 	char tr_id[MAXSTATLEN];
226 	char tr_index[MAXSTATLEN];
227 	char tr_opackets[MAXSTATLEN];
228 	char tr_obytes[MAXSTATLEN];
229 } tx_ring_fields_buf_t;
230 
231 static ofmt_field_t tx_ring_s_fields[] = {
232 { "LINK",	15,
233     offsetof(tx_ring_fields_buf_t, tr_linkname),	print_default_cb},
234 { "TYPE",	5,
235     offsetof(tx_ring_fields_buf_t, tr_type),		print_default_cb},
236 { "ID",		7,
237     offsetof(tx_ring_fields_buf_t, tr_id),		print_default_cb},
238 { "INDEX",	6,
239     offsetof(tx_ring_fields_buf_t, tr_index),		print_default_cb},
240 { "OPKTS",	8,
241     offsetof(tx_ring_fields_buf_t, tr_opackets),	print_default_cb},
242 { "OBYTES",	8,
243     offsetof(tx_ring_fields_buf_t, tr_obytes),		print_default_cb},
244 { NULL,		0,		0,		NULL}};
245 
246 /*
247  * dlstat show-link: both Rx and Tx lane fields
248  */
249 typedef struct lane_fields_buf_s {
250 	char l_linkname[MAXLINKNAMELEN];
251 	char l_type[MAXSTATLEN];
252 	char l_id[MAXSTATLEN];
253 	char l_index[MAXSTATLEN];
254 	char l_packets[MAXSTATLEN];
255 	char l_bytes[MAXSTATLEN];
256 } lane_fields_buf_t;
257 
258 static ofmt_field_t lane_s_fields[] = {
259 { "LINK",	15,
260     offsetof(lane_fields_buf_t, l_linkname),	print_default_cb},
261 { "TYPE",	5,
262     offsetof(lane_fields_buf_t, l_type),	print_default_cb},
263 { "ID",		7,
264     offsetof(lane_fields_buf_t, l_id),		print_default_cb},
265 { "INDEX",	6,
266     offsetof(lane_fields_buf_t, l_index),	print_default_cb},
267 { "PKTS",	8,
268     offsetof(lane_fields_buf_t, l_packets),	print_default_cb},
269 { "BYTES",	8,
270     offsetof(lane_fields_buf_t, l_bytes),	print_default_cb},
271 { NULL,		0,		0,		NULL}};
272 
273 /*
274  * dlstat show-link -r, dlstat -r: Rx Lane stat fields
275  */
276 typedef struct rx_lane_fields_buf_s {
277 	char rl_linkname[MAXLINKNAMELEN];
278 	char rl_type[MAXSTATLEN];
279 	char rl_id[MAXSTATLEN];
280 	char rl_index[MAXSTATLEN];
281 	char rl_ipackets[MAXSTATLEN];
282 	char rl_rbytes[MAXSTATLEN];
283 	char rl_intrs[MAXSTATLEN];
284 	char rl_polls[MAXSTATLEN];
285 	char rl_sdrops[MAXSTATLEN];
286 	char rl_chl10[MAXSTATLEN];
287 	char rl_ch10_50[MAXSTATLEN];
288 	char rl_chg50[MAXSTATLEN];
289 } rx_lane_fields_buf_t;
290 
291 static ofmt_field_t rx_lane_s_fields[] = {
292 { "LINK",	10,
293     offsetof(rx_lane_fields_buf_t, rl_linkname),	print_default_cb},
294 { "TYPE",	5,
295     offsetof(rx_lane_fields_buf_t, rl_type),		print_default_cb},
296 { "ID",		7,
297     offsetof(rx_lane_fields_buf_t, rl_id),		print_default_cb},
298 { "INDEX",	6,
299     offsetof(rx_lane_fields_buf_t, rl_index),		print_default_cb},
300 { "IPKTS",	8,
301     offsetof(rx_lane_fields_buf_t, rl_ipackets),	print_default_cb},
302 { "RBYTES",	8,
303     offsetof(rx_lane_fields_buf_t, rl_rbytes),		print_default_cb},
304 { "INTRS",	8,
305     offsetof(rx_lane_fields_buf_t, rl_intrs),		print_default_cb},
306 { "POLLS",	8,
307     offsetof(rx_lane_fields_buf_t, rl_polls),		print_default_cb},
308 { "SDROPS",	8,
309     offsetof(rx_lane_fields_buf_t, rl_sdrops),		print_default_cb},
310 { "CH<10",	8,
311     offsetof(rx_lane_fields_buf_t, rl_chl10),		print_default_cb},
312 { "CH10-50",	8,
313     offsetof(rx_lane_fields_buf_t, rl_ch10_50),		print_default_cb},
314 { "CH>50",	8,
315     offsetof(rx_lane_fields_buf_t, rl_chg50),		print_default_cb},
316 { NULL,		0,		0,		NULL}};
317 
318 /*
319  * dlstat show-link -r -F: Rx fanout stat fields
320  */
321 typedef struct rx_fanout_lane_fields_buf_s {
322 	char rfl_linkname[MAXLINKNAMELEN];
323 	char rfl_type[MAXSTATLEN];
324 	char rfl_id[MAXSTATLEN];
325 	char rfl_index[MAXSTATLEN];
326 	char rfl_fout[MAXSTATLEN];
327 	char rfl_ipackets[MAXSTATLEN];
328 	char rfl_rbytes[MAXSTATLEN];
329 } rx_fanout_lane_fields_buf_t;
330 
331 static ofmt_field_t rx_fanout_lane_s_fields[] = {
332 { "LINK",	15,
333     offsetof(rx_fanout_lane_fields_buf_t, rfl_linkname), print_default_cb},
334 { "TYPE",	5,
335     offsetof(rx_fanout_lane_fields_buf_t, rfl_type),	print_default_cb},
336 { "ID",		7,
337     offsetof(rx_fanout_lane_fields_buf_t, rfl_id),	print_default_cb},
338 { "INDEX",	6,
339     offsetof(rx_fanout_lane_fields_buf_t, rfl_index),	print_default_cb},
340 { "FOUT",	6,
341     offsetof(rx_fanout_lane_fields_buf_t, rfl_fout),	print_default_cb},
342 { "IPKTS",	8,
343     offsetof(rx_fanout_lane_fields_buf_t, rfl_ipackets), print_default_cb},
344 { "RBYTES",	8,
345     offsetof(rx_fanout_lane_fields_buf_t, rfl_rbytes),	print_default_cb},
346 { NULL,		0,		0,		NULL}};
347 
348 /*
349  * dlstat show-link -t: Tx Lane stat fields
350  */
351 typedef struct tx_lane_fields_buf_s {
352 	char tl_linkname[MAXLINKNAMELEN];
353 	char tl_index[MAXSTATLEN];
354 	char tl_type[MAXSTATLEN];
355 	char tl_id[MAXSTATLEN];
356 	char tl_opackets[MAXSTATLEN];
357 	char tl_obytes[MAXSTATLEN];
358 	char tl_blockcnt[MAXSTATLEN];
359 	char tl_unblockcnt[MAXSTATLEN];
360 	char tl_sdrops[MAXSTATLEN];
361 } tx_lane_fields_buf_t;
362 
363 static ofmt_field_t tx_lane_s_fields[] = {
364 { "LINK",	15,
365     offsetof(tx_lane_fields_buf_t, tl_linkname),	print_default_cb},
366 { "TYPE",	5,
367     offsetof(tx_lane_fields_buf_t, tl_type),		print_default_cb},
368 { "ID",		7,
369     offsetof(tx_lane_fields_buf_t, tl_id),		print_default_cb},
370 { "INDEX",	6,
371     offsetof(tx_lane_fields_buf_t, tl_index),		print_default_cb},
372 { "OPKTS",	8,
373     offsetof(tx_lane_fields_buf_t, tl_opackets),	print_default_cb},
374 { "OBYTES",	8,
375     offsetof(tx_lane_fields_buf_t, tl_obytes),		print_default_cb},
376 { "BLKCNT",	8,
377     offsetof(tx_lane_fields_buf_t, tl_blockcnt),	print_default_cb},
378 { "UBLKCNT",	8,
379     offsetof(tx_lane_fields_buf_t, tl_unblockcnt),	print_default_cb},
380 { "SDROPS",	8,
381     offsetof(tx_lane_fields_buf_t, tl_sdrops),		print_default_cb},
382 { NULL,		0,		0,		NULL}};
383 
384 /*
385  * dlstat show-aggr: aggr port stat fields
386  */
387 typedef struct aggr_port_fields_buf_s {
388 	char ap_linkname[MAXLINKNAMELEN];
389 	char ap_portname[MAXLINKNAMELEN];
390 	char ap_ipackets[MAXSTATLEN];
391 	char ap_rbytes[MAXSTATLEN];
392 	char ap_opackets[MAXSTATLEN];
393 	char ap_obytes[MAXSTATLEN];
394 } aggr_port_fields_buf_t;
395 
396 static ofmt_field_t aggr_port_s_fields[] = {
397 { "LINK",	15,
398     offsetof(aggr_port_fields_buf_t, ap_linkname),	print_default_cb},
399 { "PORT",	15,
400     offsetof(aggr_port_fields_buf_t, ap_portname),	print_default_cb},
401 { "IPKTS",	8,
402     offsetof(aggr_port_fields_buf_t, ap_ipackets),	print_default_cb},
403 { "RBYTES",	8,
404     offsetof(aggr_port_fields_buf_t, ap_rbytes),	print_default_cb},
405 { "OPKTS",	8,
406     offsetof(aggr_port_fields_buf_t, ap_opackets),	print_default_cb},
407 { "OBYTES",	8,
408     offsetof(aggr_port_fields_buf_t, ap_obytes),	print_default_cb},
409 { NULL,		0,		0,		NULL}};
410 
411 /*
412  * structures for 'dlstat show-link -h'
413  */
414 typedef struct  history_fields_buf_s {
415 	char	h_link[12];
416 	char	h_duration[10];
417 	char	h_ipackets[9];
418 	char	h_rbytes[10];
419 	char	h_opackets[9];
420 	char	h_obytes[10];
421 	char	h_bandwidth[15];
422 } history_fields_buf_t;
423 
424 static ofmt_field_t history_fields[] = {
425 { "LINK",	13,
426 	offsetof(history_fields_buf_t, h_link), print_default_cb},
427 { "DURATION",	11,
428 	offsetof(history_fields_buf_t, h_duration), print_default_cb},
429 { "IPKTS",	10,
430 	offsetof(history_fields_buf_t, h_ipackets), print_default_cb},
431 { "RBYTES",	11,
432 	offsetof(history_fields_buf_t, h_rbytes), print_default_cb},
433 { "OPKTS",	10,
434 	offsetof(history_fields_buf_t, h_opackets), print_default_cb},
435 { "OBYTES",	11,
436 	offsetof(history_fields_buf_t, h_obytes), print_default_cb},
437 { "BANDWIDTH",	16,
438 	offsetof(history_fields_buf_t, h_bandwidth), print_default_cb},
439 { NULL,		0, 0, NULL}};
440 
441 /*
442  * structures for 'dlstat show-link -h link'
443  */
444 typedef struct  history_l_fields_buf_s {
445 	char	hl_link[12];
446 	char	hl_stime[13];
447 	char	hl_etime[13];
448 	char	hl_rbytes[8];
449 	char	hl_obytes[8];
450 	char	hl_bandwidth[15];
451 } history_l_fields_buf_t;
452 
453 static ofmt_field_t history_l_fields[] = {
454 /* name,	field width,	offset */
455 { "LINK",	13,
456 	offsetof(history_l_fields_buf_t, hl_link), print_default_cb},
457 { "START",	14,
458 	offsetof(history_l_fields_buf_t, hl_stime), print_default_cb},
459 { "END",	14,
460 	offsetof(history_l_fields_buf_t, hl_etime), print_default_cb},
461 { "RBYTES",	9,
462 	offsetof(history_l_fields_buf_t, hl_rbytes), print_default_cb},
463 { "OBYTES",	9,
464 	offsetof(history_l_fields_buf_t, hl_obytes), print_default_cb},
465 { "BANDWIDTH",	16,
466 	offsetof(history_l_fields_buf_t, hl_bandwidth), print_default_cb},
467 { NULL,		0, 0, NULL}}
468 ;
469 
470 static char *progname;
471 
472 /*
473  * Handle to libdladm.  Opened in main() before the sub-command
474  * specific function is called.
475  */
476 static dladm_handle_t handle = NULL;
477 
478 static void
479 usage(void)
480 {
481 	int	i;
482 	cmd_t	*cmdp;
483 
484 	(void) fprintf(stderr, gettext("usage: "));
485 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
486 		cmdp = &cmds[i];
487 		if (cmdp->c_usage != NULL)
488 			(void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
489 	}
490 
491 	/* close dladm handle if it was opened */
492 	if (handle != NULL)
493 		dladm_close(handle);
494 
495 	exit(1);
496 }
497 
498 int
499 main(int argc, char *argv[])
500 {
501 	int		i;
502 	cmd_t		*cmdp;
503 	dladm_status_t	status;
504 
505 	(void) setlocale(LC_ALL, "");
506 #if !defined(TEXT_DOMAIN)
507 #define	TEXT_DOMAIN "SYS_TEST"
508 #endif
509 	(void) textdomain(TEXT_DOMAIN);
510 
511 	progname = argv[0];
512 
513 	/* Open the libdladm handle */
514 	if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
515 		die_dlerr(status, "could not open /dev/dld");
516 
517 	if (argc == 1) {
518 		do_show(argc - 1, NULL, cmds[0].c_usage);
519 		goto done;
520 	}
521 
522 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
523 		cmdp = &cmds[i];
524 		if (strcmp(argv[1], cmdp->c_name) == 0) {
525 			cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
526 			goto done;
527 		}
528 	}
529 
530 	do_show(argc, &argv[0], cmds[0].c_usage);
531 
532 done:
533 	dladm_close(handle);
534 	return (0);
535 }
536 
537 /*ARGSUSED*/
538 static int
539 show_history_date(dladm_usage_t *history, void *arg)
540 {
541 	show_history_state_t	*state = arg;
542 	time_t			stime;
543 	char			timebuf[20];
544 	dladm_status_t		status;
545 	uint32_t		flags;
546 
547 	/*
548 	 * Only show history information for existing links unless '-a'
549 	 * is specified.
550 	 */
551 	if (!state->hs_showall) {
552 		if ((status = dladm_name2info(handle, history->du_name,
553 		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
554 			return (status);
555 		}
556 		if ((flags & DLADM_OPT_ACTIVE) == 0)
557 			return (DLADM_STATUS_LINKINVAL);
558 	}
559 
560 	stime = history->du_stime;
561 	(void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
562 	    localtime(&stime));
563 	(void) printf("%s\n", timebuf);
564 
565 	return (DLADM_STATUS_OK);
566 }
567 
568 static int
569 show_history_time(dladm_usage_t *history, void *arg)
570 {
571 	show_history_state_t	*state = arg;
572 	char			buf[DLADM_STRSIZE];
573 	history_l_fields_buf_t	ubuf;
574 	time_t			time;
575 	double			bw;
576 	dladm_status_t		status;
577 	uint32_t		flags;
578 
579 	/*
580 	 * Only show history information for existing links unless '-a'
581 	 * is specified.
582 	 */
583 	if (!state->hs_showall) {
584 		if ((status = dladm_name2info(handle, history->du_name,
585 		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
586 			return (status);
587 		}
588 		if ((flags & DLADM_OPT_ACTIVE) == 0)
589 			return (DLADM_STATUS_LINKINVAL);
590 	}
591 
592 	if (state->hs_plot) {
593 		if (!state->hs_printheader) {
594 			if (state->hs_first) {
595 				(void) printf("# Time");
596 				state->hs_first = B_FALSE;
597 			}
598 			(void) printf(" %s", history->du_name);
599 			if (history->du_last) {
600 				(void) printf("\n");
601 				state->hs_first = B_TRUE;
602 				state->hs_printheader = B_TRUE;
603 			}
604 		} else {
605 			if (state->hs_first) {
606 				time = history->du_etime;
607 				(void) strftime(buf, sizeof (buf), "%T",
608 				    localtime(&time));
609 				state->hs_first = B_FALSE;
610 				(void) printf("%s", buf);
611 			}
612 			bw = (double)history->du_bandwidth/1000;
613 			(void) printf(" %.2f", bw);
614 			if (history->du_last) {
615 				(void) printf("\n");
616 				state->hs_first = B_TRUE;
617 			}
618 		}
619 		return (DLADM_STATUS_OK);
620 	}
621 
622 	bzero(&ubuf, sizeof (ubuf));
623 
624 	(void) snprintf(ubuf.hl_link, sizeof (ubuf.hl_link), "%s",
625 	    history->du_name);
626 	time = history->du_stime;
627 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
628 	(void) snprintf(ubuf.hl_stime, sizeof (ubuf.hl_stime), "%s",
629 	    buf);
630 	time = history->du_etime;
631 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
632 	(void) snprintf(ubuf.hl_etime, sizeof (ubuf.hl_etime), "%s",
633 	    buf);
634 	(void) snprintf(ubuf.hl_rbytes, sizeof (ubuf.hl_rbytes),
635 	    "%llu", history->du_rbytes);
636 	(void) snprintf(ubuf.hl_obytes, sizeof (ubuf.hl_obytes),
637 	    "%llu", history->du_obytes);
638 	(void) snprintf(ubuf.hl_bandwidth, sizeof (ubuf.hl_bandwidth),
639 	    "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
640 
641 	ofmt_print(state->hs_ofmt, &ubuf);
642 	return (DLADM_STATUS_OK);
643 }
644 
645 static int
646 show_history_res(dladm_usage_t *history, void *arg)
647 {
648 	show_history_state_t	*state = arg;
649 	char			buf[DLADM_STRSIZE];
650 	history_fields_buf_t	ubuf;
651 	dladm_status_t		status;
652 	uint32_t		flags;
653 
654 	/*
655 	 * Only show history information for existing links unless '-a'
656 	 * is specified.
657 	 */
658 	if (!state->hs_showall) {
659 		if ((status = dladm_name2info(handle, history->du_name,
660 		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
661 			return (status);
662 		}
663 		if ((flags & DLADM_OPT_ACTIVE) == 0)
664 			return (DLADM_STATUS_LINKINVAL);
665 	}
666 
667 	bzero(&ubuf, sizeof (ubuf));
668 
669 	(void) snprintf(ubuf.h_link, sizeof (ubuf.h_link), "%s",
670 	    history->du_name);
671 	(void) snprintf(ubuf.h_duration, sizeof (ubuf.h_duration),
672 	    "%llu", history->du_duration);
673 	(void) snprintf(ubuf.h_ipackets, sizeof (ubuf.h_ipackets),
674 	    "%llu", history->du_ipackets);
675 	(void) snprintf(ubuf.h_rbytes, sizeof (ubuf.h_rbytes),
676 	    "%llu", history->du_rbytes);
677 	(void) snprintf(ubuf.h_opackets, sizeof (ubuf.h_opackets),
678 	    "%llu", history->du_opackets);
679 	(void) snprintf(ubuf.h_obytes, sizeof (ubuf.h_obytes),
680 	    "%llu", history->du_obytes);
681 	(void) snprintf(ubuf.h_bandwidth, sizeof (ubuf.h_bandwidth),
682 	    "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
683 
684 	ofmt_print(state->hs_ofmt, &ubuf);
685 
686 	return (DLADM_STATUS_OK);
687 }
688 
689 static boolean_t
690 valid_formatspec(char *formatspec_str)
691 {
692 	return (strcmp(formatspec_str, "gnuplot") == 0);
693 }
694 
695 /*ARGSUSED*/
696 static void
697 do_show_history(int argc, char *argv[], const char *use)
698 {
699 	char			*file = NULL;
700 	int			opt;
701 	dladm_status_t		status;
702 	boolean_t		d_arg = B_FALSE;
703 	char			*stime = NULL;
704 	char			*etime = NULL;
705 	char			*resource = NULL;
706 	show_history_state_t	state;
707 	boolean_t		o_arg = B_FALSE;
708 	boolean_t		F_arg = B_FALSE;
709 	char			*fields_str = NULL;
710 	char			*formatspec_str = NULL;
711 	char			*all_l_fields =
712 	    "link,start,end,rbytes,obytes,bandwidth";
713 	ofmt_handle_t		ofmt;
714 	ofmt_status_t		oferr;
715 	uint_t			ofmtflags = 0;
716 
717 	bzero(&state, sizeof (show_history_state_t));
718 	state.hs_parsable = B_FALSE;
719 	state.hs_printheader = B_FALSE;
720 	state.hs_plot = B_FALSE;
721 	state.hs_first = B_TRUE;
722 
723 	while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
724 		switch (opt) {
725 		case 'd':
726 			d_arg = B_TRUE;
727 			break;
728 		case 'a':
729 			state.hs_showall = B_TRUE;
730 			break;
731 		case 'f':
732 			file = optarg;
733 			break;
734 		case 's':
735 			stime = optarg;
736 			break;
737 		case 'e':
738 			etime = optarg;
739 			break;
740 		case 'o':
741 			o_arg = B_TRUE;
742 			fields_str = optarg;
743 			break;
744 		case 'F':
745 			state.hs_plot = F_arg = B_TRUE;
746 			formatspec_str = optarg;
747 			break;
748 		default:
749 			die_opterr(optopt, opt, use);
750 			break;
751 		}
752 	}
753 
754 	if (file == NULL)
755 		die("show-link -h requires a file");
756 
757 	if (optind == (argc-1)) {
758 		uint32_t	flags;
759 
760 		resource = argv[optind];
761 		if (!state.hs_showall &&
762 		    (((status = dladm_name2info(handle, resource, NULL, &flags,
763 		    NULL, NULL)) != DLADM_STATUS_OK) ||
764 		    ((flags & DLADM_OPT_ACTIVE) == 0))) {
765 			die("invalid link: '%s'", resource);
766 		}
767 	}
768 
769 	if (F_arg && d_arg)
770 		die("incompatible -d and -F options");
771 
772 	if (F_arg && !valid_formatspec(formatspec_str))
773 		die("Format specifier %s not supported", formatspec_str);
774 
775 	if (state.hs_parsable)
776 		ofmtflags |= OFMT_PARSABLE;
777 
778 	if (resource == NULL && stime == NULL && etime == NULL) {
779 		oferr = ofmt_open(fields_str, history_fields, ofmtflags, 0,
780 		    &ofmt);
781 	} else {
782 		if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
783 			fields_str = all_l_fields;
784 		oferr = ofmt_open(fields_str, history_l_fields, ofmtflags, 0,
785 		    &ofmt);
786 
787 	}
788 	ofmt_check(oferr, state.hs_parsable, ofmt, die, warn);
789 	state.hs_ofmt = ofmt;
790 
791 	if (d_arg) {
792 		/* Print log dates */
793 		status = dladm_usage_dates(show_history_date,
794 		    DLADM_LOGTYPE_LINK, file, resource, &state);
795 	} else if (resource == NULL && stime == NULL && etime == NULL &&
796 	    !F_arg) {
797 		/* Print summary */
798 		status = dladm_usage_summary(show_history_res,
799 		    DLADM_LOGTYPE_LINK, file, &state);
800 	} else if (resource != NULL) {
801 		/* Print log entries for named resource */
802 		status = dladm_walk_usage_res(show_history_time,
803 		    DLADM_LOGTYPE_LINK, file, resource, stime, etime, &state);
804 	} else {
805 		/* Print time and information for each link */
806 		status = dladm_walk_usage_time(show_history_time,
807 		    DLADM_LOGTYPE_LINK, file, stime, etime, &state);
808 	}
809 
810 	if (status != DLADM_STATUS_OK)
811 		die_dlerr(status, "show-link -h");
812 	ofmt_close(ofmt);
813 }
814 
815 boolean_t
816 dlstat_unit(char *oarg, char *unit)
817 {
818 	if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
819 	    (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
820 	    (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
821 		*unit = oarg[0];
822 		return (B_TRUE);
823 	}
824 
825 	return (B_FALSE);
826 }
827 
828 void
829 map_to_units(char *buf, uint_t bufsize, double num, char unit,
830     boolean_t parsable)
831 {
832 	if (parsable) {
833 		(void) snprintf(buf, bufsize, "%.0lf", num);
834 		return;
835 	}
836 
837 	if (unit == '\0') {
838 		int index;
839 
840 		for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
841 			;
842 
843 		switch (index) {
844 			case 0:
845 				unit = '\0';
846 				break;
847 			case 1:
848 				unit = 'K';
849 				break;
850 			case 2:
851 				unit = 'M';
852 				break;
853 			case 3:
854 				unit = 'G';
855 				break;
856 			case 4:
857 				unit = 'T';
858 				break;
859 			case 5:
860 				/* Largest unit supported */
861 			default:
862 				unit = 'P';
863 				break;
864 		}
865 	} else  {
866 		switch (unit) {
867 			case 'R':
868 				/* Already raw numbers */
869 				unit = '\0';
870 				break;
871 			case 'K':
872 				num /= 1000;
873 				break;
874 			case 'M':
875 				num /= (1000*1000);
876 				break;
877 			case 'G':
878 				num /= (1000*1000*1000);
879 				break;
880 			case 'T':
881 				num /= (1000.0*1000.0*1000.0*1000.0);
882 				break;
883 			case 'P':
884 				/* Largest unit supported */
885 			default:
886 				num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
887 				break;
888 		}
889 	}
890 
891 	if (unit == '\0')
892 		(void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
893 	else
894 		(void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
895 }
896 
897 link_chain_t *
898 get_link_prev_stat(datalink_id_t linkid, void *arg)
899 {
900 	show_state_t	*state = (show_state_t *)arg;
901 	link_chain_t	*link_curr = NULL;
902 
903 	/* Scan prev linkid list and look for entry matching this entry */
904 	for (link_curr = state->ls_linkchain; link_curr;
905 	    link_curr = link_curr->lc_next) {
906 		if (link_curr->lc_linkid == linkid)
907 			break;
908 	}
909 				/* New link, add it */
910 	if (link_curr == NULL) {
911 		link_curr = (link_chain_t *)malloc(sizeof (link_chain_t));
912 		if (link_curr == NULL)
913 			goto done;
914 		link_curr->lc_linkid = linkid;
915 		bzero(&link_curr->lc_statchain,
916 		    sizeof (link_curr->lc_statchain));
917 		link_curr->lc_next = state->ls_linkchain;
918 		state->ls_linkchain = link_curr;
919 	}
920 done:
921 	return (link_curr);
922 }
923 
924 /*
925  * Number of links may change while dlstat with -i is executing.
926  * Free memory allocated for links that are no longer there.
927  * Prepare for next iteration by marking visited = false for existing stat
928  * entries.
929  */
930 static void
931 cleanup_removed_links(show_state_t *state)
932 {
933 	link_chain_t	*lcurr;
934 	link_chain_t	*lprev;
935 	link_chain_t	*tofree;
936 	int		i;
937 
938 	/* Delete all nodes from the list that have lc_visited marked false */
939 	lcurr = state->ls_linkchain;
940 	while (lcurr != NULL) {
941 		if (lcurr->lc_visited) {
942 			lcurr->lc_visited = B_FALSE;
943 			lprev = lcurr;
944 			lcurr = lcurr->lc_next;
945 			continue;
946 		}
947 				/* Is it head of the list? */
948 		if (lcurr == state->ls_linkchain)
949 			state->ls_linkchain = lcurr->lc_next;
950 		else
951 			lprev->lc_next = lcurr->lc_next;
952 				/* lprev remains the same */
953 		tofree = lcurr;
954 		lcurr = lcurr->lc_next;
955 
956 				/* Free stats memory for the removed link */
957 		for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
958 			if (state->ls_stattype[i])
959 				dladm_link_stat_free(tofree->lc_statchain[i]);
960 		}
961 		free(tofree);
962 	}
963 }
964 
965 void *
966 print_total_stats(const char *linkname, void *statentry, char unit,
967     boolean_t parsable)
968 {
969 	total_stat_entry_t	*sentry = statentry;
970 	total_stat_t		*link_stats = &sentry->tse_stats;
971 	total_fields_buf_t	*buf;
972 
973 	buf = malloc(sizeof (total_fields_buf_t));
974 	if (buf == NULL)
975 		goto done;
976 
977 	(void) snprintf(buf->t_linkname, sizeof (buf->t_linkname), "%s",
978 	    linkname);
979 
980 	map_to_units(buf->t_ipackets, sizeof (buf->t_ipackets),
981 	    link_stats->ts_ipackets, unit, parsable);
982 
983 	map_to_units(buf->t_rbytes, sizeof (buf->t_rbytes),
984 	    link_stats->ts_rbytes, unit, parsable);
985 
986 	map_to_units(buf->t_opackets, sizeof (buf->t_opackets),
987 	    link_stats->ts_opackets, unit, parsable);
988 
989 	map_to_units(buf->t_obytes, sizeof (buf->t_obytes),
990 	    link_stats->ts_obytes, unit, parsable);
991 
992 done:
993 	return (buf);
994 }
995 
996 void *
997 print_rx_generic_ring_stats(const char *linkname, void *statentry, char unit,
998     boolean_t parsable)
999 {
1000 	ring_stat_entry_t	*sentry = statentry;
1001 	ring_stat_t		*link_stats = &sentry->re_stats;
1002 	ring_fields_buf_t	*buf;
1003 
1004 	buf = malloc(sizeof (ring_fields_buf_t));
1005 	if (buf == NULL)
1006 		goto done;
1007 
1008 	(void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
1009 	    linkname);
1010 
1011 	(void) snprintf(buf->r_type, sizeof (buf->r_type), "rx");
1012 
1013 	if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1014 		(void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
1015 	} else {
1016 		(void) snprintf(buf->r_index, sizeof (buf->r_index),
1017 		    "%llu", sentry->re_index);
1018 	}
1019 
1020 	map_to_units(buf->r_packets, sizeof (buf->r_packets),
1021 	    link_stats->r_packets, unit, parsable);
1022 
1023 	map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
1024 	    link_stats->r_bytes, unit, parsable);
1025 
1026 done:
1027 	return (buf);
1028 }
1029 
1030 void *
1031 print_tx_generic_ring_stats(const char *linkname, void *statentry, char unit,
1032     boolean_t parsable)
1033 {
1034 	ring_stat_entry_t	*sentry = statentry;
1035 	ring_stat_t		*link_stats = &sentry->re_stats;
1036 	ring_fields_buf_t	*buf;
1037 
1038 	buf = malloc(sizeof (ring_fields_buf_t));
1039 	if (buf == NULL)
1040 		goto done;
1041 
1042 	(void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
1043 	    linkname);
1044 
1045 	(void) snprintf(buf->r_type, sizeof (buf->r_type), "tx");
1046 
1047 	if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1048 		(void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
1049 	} else {
1050 		(void) snprintf(buf->r_index, sizeof (buf->r_index),
1051 		    "%llu", sentry->re_index);
1052 	}
1053 
1054 	map_to_units(buf->r_packets, sizeof (buf->r_packets),
1055 	    link_stats->r_packets, unit, parsable);
1056 
1057 	map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
1058 	    link_stats->r_bytes, unit, parsable);
1059 
1060 done:
1061 	return (buf);
1062 }
1063 
1064 void *
1065 print_rx_ring_stats(const char *linkname, void *statentry, char unit,
1066     boolean_t parsable)
1067 {
1068 	ring_stat_entry_t	*sentry = statentry;
1069 	ring_stat_t		*link_stats = &sentry->re_stats;
1070 	rx_ring_fields_buf_t	*buf;
1071 
1072 	buf = malloc(sizeof (rx_ring_fields_buf_t));
1073 	if (buf == NULL)
1074 		goto done;
1075 
1076 	(void) snprintf(buf->rr_linkname, sizeof (buf->rr_linkname), "%s",
1077 	    linkname);
1078 
1079 	(void) snprintf(buf->rr_type, sizeof (buf->rr_type), "rx");
1080 
1081 	if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1082 		(void) snprintf(buf->rr_index, sizeof (buf->rr_index), "--");
1083 	} else {
1084 		(void) snprintf(buf->rr_index, sizeof (buf->rr_index),
1085 		    "%llu", sentry->re_index);
1086 	}
1087 
1088 	map_to_units(buf->rr_ipackets, sizeof (buf->rr_ipackets),
1089 	    link_stats->r_packets, unit, parsable);
1090 
1091 	map_to_units(buf->rr_rbytes, sizeof (buf->rr_rbytes),
1092 	    link_stats->r_bytes, unit, parsable);
1093 
1094 done:
1095 	return (buf);
1096 }
1097 
1098 void *
1099 print_tx_ring_stats(const char *linkname, void *statentry, char unit,
1100     boolean_t parsable)
1101 {
1102 	ring_stat_entry_t	*sentry = statentry;
1103 	ring_stat_t		*link_stats = &sentry->re_stats;
1104 	tx_ring_fields_buf_t	*buf;
1105 
1106 	buf = malloc(sizeof (tx_ring_fields_buf_t));
1107 	if (buf == NULL)
1108 		goto done;
1109 
1110 	(void) snprintf(buf->tr_linkname, sizeof (buf->tr_linkname), "%s",
1111 	    linkname);
1112 
1113 	(void) snprintf(buf->tr_type, sizeof (buf->tr_type), "tx");
1114 
1115 	if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1116 		(void) snprintf(buf->tr_index, sizeof (buf->tr_index), "--");
1117 	} else {
1118 		(void) snprintf(buf->tr_index, sizeof (buf->tr_index),
1119 		    "%llu", sentry->re_index);
1120 	}
1121 
1122 	map_to_units(buf->tr_opackets, sizeof (buf->tr_opackets),
1123 	    link_stats->r_packets, unit, parsable);
1124 
1125 	map_to_units(buf->tr_obytes, sizeof (buf->tr_obytes),
1126 	    link_stats->r_bytes, unit, parsable);
1127 
1128 done:
1129 	return (buf);
1130 }
1131 
1132 void *
1133 print_rx_generic_lane_stats(const char *linkname, void *statentry, char unit,
1134     boolean_t parsable)
1135 {
1136 	rx_lane_stat_entry_t	*sentry = statentry;
1137 	rx_lane_stat_t		*link_stats = &sentry->rle_stats;
1138 	lane_fields_buf_t	*buf;
1139 
1140 	if (sentry->rle_id == L_DFNCT)
1141 		return (NULL);
1142 
1143 	buf = malloc(sizeof (lane_fields_buf_t));
1144 	if (buf == NULL)
1145 		goto done;
1146 
1147 	(void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
1148 	    linkname);
1149 
1150 	(void) snprintf(buf->l_type, sizeof (buf->l_type), "rx");
1151 
1152 	if (sentry->rle_id == L_HWLANE)
1153 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
1154 	else if (sentry->rle_id == L_SWLANE)
1155 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
1156 	else if (sentry->rle_id == L_LOCAL)
1157 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "local");
1158 	else if (sentry->rle_id == L_BCAST)
1159 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
1160 	else
1161 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
1162 
1163 	if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
1164 		(void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
1165 	} else {
1166 		(void) snprintf(buf->l_index, sizeof (buf->l_index),
1167 		    "%llu", sentry->rle_index);
1168 	}
1169 
1170 	map_to_units(buf->l_packets, sizeof (buf->l_packets),
1171 	    link_stats->rl_ipackets, unit, parsable);
1172 
1173 	map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
1174 	    link_stats->rl_rbytes, unit, parsable);
1175 
1176 done:
1177 	return (buf);
1178 }
1179 
1180 void *
1181 print_tx_generic_lane_stats(const char *linkname, void *statentry, char unit,
1182     boolean_t parsable)
1183 {
1184 	tx_lane_stat_entry_t	*sentry = statentry;
1185 	tx_lane_stat_t		*link_stats = &sentry->tle_stats;
1186 	lane_fields_buf_t	*buf;
1187 
1188 	if (sentry->tle_id == L_DFNCT)
1189 		return (NULL);
1190 
1191 	buf = malloc(sizeof (lane_fields_buf_t));
1192 	if (buf == NULL)
1193 		goto done;
1194 
1195 	(void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
1196 	    linkname);
1197 
1198 	(void) snprintf(buf->l_type, sizeof (buf->l_type), "tx");
1199 
1200 	if (sentry->tle_id == L_HWLANE)
1201 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
1202 	else if (sentry->tle_id == L_SWLANE)
1203 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
1204 	else if (sentry->tle_id == L_BCAST)
1205 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
1206 	else
1207 		(void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
1208 
1209 	if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
1210 		(void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
1211 	} else {
1212 		(void) snprintf(buf->l_index, sizeof (buf->l_index),
1213 		    "%llu", sentry->tle_index);
1214 	}
1215 	map_to_units(buf->l_packets, sizeof (buf->l_packets),
1216 	    link_stats->tl_opackets, unit, parsable);
1217 
1218 	map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
1219 	    link_stats->tl_obytes, unit, parsable);
1220 
1221 done:
1222 	return (buf);
1223 }
1224 
1225 void *
1226 print_rx_lane_stats(const char *linkname, void *statentry, char unit,
1227     boolean_t parsable)
1228 {
1229 	rx_lane_stat_entry_t	*sentry = statentry;
1230 	rx_lane_stat_t		*link_stats = &sentry->rle_stats;
1231 	rx_lane_fields_buf_t	*buf;
1232 
1233 	if (sentry->rle_id == L_DFNCT)
1234 		return (NULL);
1235 
1236 	buf = malloc(sizeof (rx_lane_fields_buf_t));
1237 	if (buf == NULL)
1238 		goto done;
1239 
1240 	(void) snprintf(buf->rl_linkname, sizeof (buf->rl_linkname), "%s",
1241 	    linkname);
1242 
1243 	(void) snprintf(buf->rl_type, sizeof (buf->rl_type), "rx");
1244 
1245 	if (sentry->rle_id == L_HWLANE)
1246 		(void) snprintf(buf->rl_id, sizeof (buf->rl_id), "hw");
1247 	else if (sentry->rle_id == L_SWLANE)
1248 		(void) snprintf(buf->rl_id, sizeof (buf->rl_id), "sw");
1249 	else if (sentry->rle_id == L_LOCAL)
1250 		(void) snprintf(buf->rl_id, sizeof (buf->rl_id), "local");
1251 	else if (sentry->rle_id == L_BCAST)
1252 		(void) snprintf(buf->rl_id, sizeof (buf->rl_id), "bcast");
1253 	else
1254 		(void) snprintf(buf->rl_id, sizeof (buf->rl_id), "--");
1255 
1256 	if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
1257 		(void) snprintf(buf->rl_index, sizeof (buf->rl_index), "--");
1258 	} else {
1259 		(void) snprintf(buf->rl_index, sizeof (buf->rl_index),
1260 		    "%llu", sentry->rle_index);
1261 	}
1262 
1263 	map_to_units(buf->rl_ipackets, sizeof (buf->rl_ipackets),
1264 	    link_stats->rl_ipackets, unit, parsable);
1265 
1266 	map_to_units(buf->rl_rbytes, sizeof (buf->rl_rbytes),
1267 	    link_stats->rl_rbytes, unit, parsable);
1268 
1269 	map_to_units(buf->rl_intrs, sizeof (buf->rl_intrs),
1270 	    link_stats->rl_intrs, unit, parsable);
1271 
1272 	map_to_units(buf->rl_polls, sizeof (buf->rl_polls),
1273 	    link_stats->rl_polls, unit, parsable);
1274 
1275 	map_to_units(buf->rl_sdrops, sizeof (buf->rl_sdrops),
1276 	    link_stats->rl_sdrops, unit, parsable);
1277 
1278 	map_to_units(buf->rl_chl10, sizeof (buf->rl_chl10),
1279 	    link_stats->rl_chl10, unit, parsable);
1280 
1281 	map_to_units(buf->rl_ch10_50, sizeof (buf->rl_ch10_50),
1282 	    link_stats->rl_ch10_50, unit, parsable);
1283 
1284 	map_to_units(buf->rl_chg50, sizeof (buf->rl_chg50),
1285 	    link_stats->rl_chg50, unit, parsable);
1286 
1287 done:
1288 	return (buf);
1289 }
1290 
1291 void *
1292 print_tx_lane_stats(const char *linkname, void *statentry, char unit,
1293     boolean_t parsable)
1294 {
1295 	tx_lane_stat_entry_t	*sentry = statentry;
1296 	tx_lane_stat_t		*link_stats = &sentry->tle_stats;
1297 	tx_lane_fields_buf_t	*buf = NULL;
1298 
1299 	if (sentry->tle_id == L_DFNCT)
1300 		return (NULL);
1301 
1302 	buf = malloc(sizeof (tx_lane_fields_buf_t));
1303 	if (buf == NULL)
1304 		goto done;
1305 
1306 	(void) snprintf(buf->tl_linkname, sizeof (buf->tl_linkname), "%s",
1307 	    linkname);
1308 
1309 	(void) snprintf(buf->tl_type, sizeof (buf->tl_type), "tx");
1310 
1311 	if (sentry->tle_id == L_HWLANE)
1312 		(void) snprintf(buf->tl_id, sizeof (buf->tl_id), "hw");
1313 	else if (sentry->tle_id == L_SWLANE)
1314 		(void) snprintf(buf->tl_id, sizeof (buf->tl_id), "sw");
1315 	else if (sentry->tle_id == L_BCAST)
1316 		(void) snprintf(buf->tl_id, sizeof (buf->tl_id), "bcast");
1317 	else
1318 		(void) snprintf(buf->tl_id, sizeof (buf->tl_id), "--");
1319 
1320 	if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
1321 		(void) snprintf(buf->tl_index, sizeof (buf->tl_index), "--");
1322 	} else {
1323 		(void) snprintf(buf->tl_index, sizeof (buf->tl_index),
1324 		    "%llu", sentry->tle_index);
1325 	}
1326 
1327 	map_to_units(buf->tl_opackets, sizeof (buf->tl_opackets),
1328 	    link_stats->tl_opackets, unit, parsable);
1329 
1330 	map_to_units(buf->tl_obytes, sizeof (buf->tl_obytes),
1331 	    link_stats->tl_obytes, unit, parsable);
1332 
1333 	map_to_units(buf->tl_blockcnt, sizeof (buf->tl_blockcnt),
1334 	    link_stats->tl_blockcnt, unit, parsable);
1335 
1336 	map_to_units(buf->tl_unblockcnt, sizeof (buf->tl_unblockcnt),
1337 	    link_stats->tl_unblockcnt, unit, parsable);
1338 
1339 	map_to_units(buf->tl_sdrops, sizeof (buf->tl_sdrops),
1340 	    link_stats->tl_sdrops, unit, parsable);
1341 
1342 done:
1343 	return (buf);
1344 }
1345 
1346 void *
1347 print_fanout_stats(const char *linkname, void *statentry, char unit,
1348     boolean_t parsable)
1349 {
1350 	fanout_stat_entry_t		*sentry = statentry;
1351 	fanout_stat_t			*link_stats = &sentry->fe_stats;
1352 	rx_fanout_lane_fields_buf_t	*buf;
1353 
1354 	buf = malloc(sizeof (rx_fanout_lane_fields_buf_t));
1355 	if (buf == NULL)
1356 		goto done;
1357 
1358 	(void) snprintf(buf->rfl_linkname, sizeof (buf->rfl_linkname), "%s",
1359 	    linkname);
1360 
1361 	(void) snprintf(buf->rfl_type, sizeof (buf->rfl_type), "rx");
1362 
1363 	if (sentry->fe_id == L_HWLANE)
1364 		(void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "hw");
1365 	else if (sentry->fe_id == L_SWLANE)
1366 		(void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "sw");
1367 	else if (sentry->fe_id == L_LCLSWLANE)
1368 		(void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "lcl/sw");
1369 	else if (sentry->fe_id == L_LOCAL)
1370 		(void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "local");
1371 	else if (sentry->fe_id == L_BCAST)
1372 		(void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "bcast");
1373 	else
1374 		(void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "--");
1375 
1376 	if (sentry->fe_index == DLSTAT_INVALID_ENTRY) {
1377 		(void) snprintf(buf->rfl_index, sizeof (buf->rfl_index), "--");
1378 	} else {
1379 		(void) snprintf(buf->rfl_index, sizeof (buf->rfl_index),
1380 		    "%llu", sentry->fe_index);
1381 	}
1382 
1383 	if (sentry->fe_foutindex == DLSTAT_INVALID_ENTRY)
1384 		(void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "--");
1385 	else {
1386 		(void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "%llu",
1387 		    sentry->fe_foutindex);
1388 	}
1389 
1390 	map_to_units(buf->rfl_ipackets, sizeof (buf->rfl_ipackets),
1391 	    link_stats->f_ipackets, unit, parsable);
1392 
1393 	map_to_units(buf->rfl_rbytes, sizeof (buf->rfl_rbytes),
1394 	    link_stats->f_rbytes, unit, parsable);
1395 
1396 done:
1397 	return (buf);
1398 }
1399 
1400 void *
1401 print_aggr_port_stats(const char *linkname, void *statentry, char unit,
1402     boolean_t parsable)
1403 {
1404 	aggr_port_stat_entry_t	*sentry = statentry;
1405 	aggr_port_stat_t	*link_stats = &sentry->ape_stats;
1406 	aggr_port_fields_buf_t	*buf;
1407 	char			portname[MAXLINKNAMELEN];
1408 
1409 	buf = malloc(sizeof (aggr_port_fields_buf_t));
1410 	if (buf == NULL)
1411 		goto done;
1412 
1413 	(void) snprintf(buf->ap_linkname, sizeof (buf->ap_linkname), "%s",
1414 	    linkname);
1415 
1416 	if (dladm_datalink_id2info(handle, sentry->ape_portlinkid, NULL,
1417 	    NULL, NULL, portname, DLPI_LINKNAME_MAX)
1418 	    != DLADM_STATUS_OK) {
1419 		(void) snprintf(buf->ap_portname,
1420 		    sizeof (buf->ap_portname), "--");
1421 	} else {
1422 		(void) snprintf(buf->ap_portname,
1423 		    sizeof (buf->ap_portname), "%s", portname);
1424 	}
1425 
1426 	map_to_units(buf->ap_ipackets, sizeof (buf->ap_ipackets),
1427 	    link_stats->ap_ipackets, unit, parsable);
1428 
1429 	map_to_units(buf->ap_rbytes, sizeof (buf->ap_rbytes),
1430 	    link_stats->ap_rbytes, unit, parsable);
1431 
1432 	map_to_units(buf->ap_opackets, sizeof (buf->ap_opackets),
1433 	    link_stats->ap_opackets, unit, parsable);
1434 
1435 	map_to_units(buf->ap_obytes, sizeof (buf->ap_obytes),
1436 	    link_stats->ap_obytes, unit, parsable);
1437 
1438 done:
1439 	return (buf);
1440 }
1441 
1442 dladm_stat_chain_t *
1443 query_link_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg,
1444     dladm_stat_type_t stattype)
1445 {
1446 	link_chain_t		*link_node;
1447 	dladm_stat_chain_t	*curr_stat;
1448 	dladm_stat_chain_t	*prev_stat = NULL;
1449 	dladm_stat_chain_t	*diff_stat = NULL;
1450 
1451 	/*  Get prev iteration stat for this link */
1452 	link_node = get_link_prev_stat(linkid, arg);
1453 	if (link_node == NULL)
1454 		goto done;
1455 
1456 	link_node->lc_visited = B_TRUE;
1457 	prev_stat = link_node->lc_statchain[stattype];
1458 
1459 	/* Query library for current stats */
1460 	curr_stat = dladm_link_stat_query(dh, linkid, stattype);
1461 	if (curr_stat == NULL)
1462 		goto done;
1463 
1464 	/* current stats - prev iteration stats */
1465 	diff_stat = dladm_link_stat_diffchain(curr_stat, prev_stat, stattype);
1466 
1467 	/* Free prev stats */
1468 	dladm_link_stat_free(prev_stat);
1469 
1470 	/* Prev <- curr stats */
1471 	link_node->lc_statchain[stattype] = curr_stat;
1472 
1473 done:
1474 	return (diff_stat);
1475 }
1476 
1477 void
1478 walk_dlstat_stats(show_state_t *state, const char *linkname,
1479     dladm_stat_type_t stattype, dladm_stat_chain_t *diff_stat)
1480 {
1481 	dladm_stat_chain_t  *curr;
1482 
1483 	/* Unpack invidual stat entry and call library consumer's callback */
1484 	for (curr = diff_stat; curr != NULL; curr = curr->dc_next) {
1485 		void	*fields_buf;
1486 
1487 		/* Format the raw numbers for printing */
1488 		fields_buf = state->ls_stats2str[stattype](linkname,
1489 		    curr->dc_statentry, state->ls_unit, state->ls_parsable);
1490 		/* Print the stats */
1491 		if (fields_buf != NULL)
1492 			ofmt_print(state->ls_ofmt, fields_buf);
1493 		free(fields_buf);
1494 	}
1495 }
1496 
1497 static int
1498 show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1499 {
1500 	show_state_t		*state = arg;
1501 	int			i;
1502 	dladm_stat_chain_t	*diff_stat;
1503 	char			linkname[DLPI_LINKNAME_MAX];
1504 
1505 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1506 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1507 		goto done;
1508 	}
1509 
1510 	for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
1511 		if (state->ls_stattype[i]) {
1512 			/*
1513 			 * Query library for stats
1514 			 * Stats are returned as chain of raw numbers
1515 			 */
1516 			diff_stat = query_link_stats(handle, linkid, arg, i);
1517 			walk_dlstat_stats(state, linkname, i, diff_stat);
1518 			dladm_link_stat_free(diff_stat);
1519 		}
1520 	}
1521 done:
1522 	return (DLADM_WALK_CONTINUE);
1523 }
1524 
1525 void
1526 show_link_stats(datalink_id_t linkid, show_state_t state, uint32_t interval)
1527 {
1528 	for (;;) {
1529 		if (linkid == DATALINK_ALL_LINKID) {
1530 			(void) dladm_walk_datalink_id(show_queried_stats,
1531 			    handle, &state, DATALINK_CLASS_ALL,
1532 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1533 		} else {
1534 			(void) show_queried_stats(handle, linkid, &state);
1535 		}
1536 
1537 		if (interval == 0)
1538 			break;
1539 
1540 		cleanup_removed_links(&state);
1541 		(void) sleep(interval);
1542 	}
1543 }
1544 
1545 void
1546 print_all_stats(dladm_handle_t dh, datalink_id_t linkid,
1547     dladm_stat_chain_t *stat_chain)
1548 {
1549 	dladm_stat_chain_t	*curr;
1550 	name_value_stat_entry_t	*stat_entry;
1551 	name_value_stat_t	*curr_stat;
1552 	boolean_t		stat_printed = B_FALSE;
1553 	char			linkname[MAXLINKNAMELEN];
1554 	char			prev_linkname[MAXLINKNAMELEN];
1555 
1556 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1557 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK)
1558 		return;
1559 
1560 	for (curr = stat_chain; curr != NULL; curr = curr->dc_next) {
1561 		stat_entry = curr->dc_statentry;
1562 		/*
1563 		 * Print header
1564 		 * If link name is already printed in previous iteration,
1565 		 * don't print again
1566 		 */
1567 		if (strcmp(prev_linkname, linkname) != 0)
1568 			printf("%s \n", linkname);
1569 		printf("  %s \n", stat_entry->nve_header);
1570 
1571 		/* Print stat fields */
1572 		for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
1573 		    curr_stat = curr_stat->nv_nextstat) {
1574 			printf("\t%15s", curr_stat->nv_statname);
1575 			printf("\t\t%15llu\n", curr_stat->nv_statval);
1576 		}
1577 
1578 		strncpy(prev_linkname, linkname, MAXLINKNAMELEN);
1579 		stat_printed = B_TRUE;
1580 	}
1581 	if (stat_printed)
1582 		printf("---------------------------------------------------\n");
1583 }
1584 
1585 static int
1586 dump_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1587 {
1588 	boolean_t		*stattype = arg;
1589 	int			i;
1590 	dladm_stat_chain_t	*stat_chain;
1591 
1592 	for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
1593 		if (stattype[i]) {
1594 			stat_chain = dladm_link_stat_query_all(dh, linkid, i);
1595 			print_all_stats(dh, linkid, stat_chain);
1596 			dladm_link_stat_query_all_free(stat_chain);
1597 		}
1598 	}
1599 done:
1600 	return (DLADM_WALK_CONTINUE);
1601 }
1602 
1603 void
1604 dump_all_link_stats(datalink_id_t linkid, boolean_t *stattype)
1605 {
1606 	if (linkid == DATALINK_ALL_LINKID) {
1607 		(void) dladm_walk_datalink_id(dump_queried_stats,
1608 		    handle, stattype, DATALINK_CLASS_ALL,
1609 		    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1610 	} else {
1611 		(void) dump_queried_stats(handle, linkid, stattype);
1612 	}
1613 }
1614 
1615 static void
1616 do_show(int argc, char *argv[], const char *use)
1617 {
1618 	int			option;
1619 	boolean_t		r_arg = B_FALSE;
1620 	boolean_t		t_arg = B_FALSE;
1621 	boolean_t		i_arg = B_FALSE;
1622 	boolean_t		p_arg = B_FALSE;
1623 	boolean_t		o_arg = B_FALSE;
1624 	boolean_t		u_arg = B_FALSE;
1625 	boolean_t		a_arg = B_FALSE;
1626 	boolean_t		A_arg = B_FALSE;
1627 	uint32_t		flags = DLADM_OPT_ACTIVE;
1628 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
1629 	uint32_t		interval = 0;
1630 	char			unit = '\0';
1631 	show_state_t		state;
1632 	dladm_status_t		status;
1633 	char			*fields_str = NULL;
1634 	char			*o_fields_str = NULL;
1635 
1636 	char			*total_stat_fields =
1637 	    "link,ipkts,rbytes,opkts,obytes";
1638 	char			*rx_total_stat_fields =
1639 	    "link,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
1640 	char			*tx_total_stat_fields =
1641 	    "link,opkts,obytes,blkcnt,ublkcnt";
1642 
1643 	ofmt_handle_t		ofmt;
1644 	ofmt_status_t		oferr;
1645 	uint_t			ofmtflags = OFMT_RIGHTJUST;
1646 	ofmt_field_t		*oftemplate;
1647 
1648 	bzero(&state, sizeof (state));
1649 	opterr = 0;
1650 	while ((option = getopt_long(argc, argv, ":rtaApi:o:u:",
1651 	    NULL, NULL)) != -1) {
1652 		switch (option) {
1653 		case 'r':
1654 			if (r_arg)
1655 				die_optdup(option);
1656 
1657 			r_arg = B_TRUE;
1658 			break;
1659 		case 't':
1660 			if (t_arg)
1661 				die_optdup(option);
1662 
1663 			t_arg = B_TRUE;
1664 			break;
1665 		case 'a':
1666 			if (a_arg)
1667 				die_optdup(option);
1668 
1669 			a_arg = B_TRUE;
1670 			break;
1671 		case 'A':
1672 			if (A_arg)
1673 				die_optdup(option);
1674 
1675 			A_arg = B_TRUE;
1676 			break;
1677 		case 'i':
1678 			if (i_arg)
1679 				die_optdup(option);
1680 
1681 			i_arg = B_TRUE;
1682 			if (!dladm_str2interval(optarg, &interval))
1683 				die("invalid interval value '%s'", optarg);
1684 			break;
1685 		case 'p':
1686 			if (p_arg)
1687 				die_optdup(option);
1688 
1689 			p_arg = B_TRUE;
1690 			break;
1691 		case 'o':
1692 			o_arg = B_TRUE;
1693 			o_fields_str = optarg;
1694 			break;
1695 		case 'u':
1696 			if (u_arg)
1697 				die_optdup(option);
1698 
1699 			u_arg = B_TRUE;
1700 			if (!dlstat_unit(optarg, &unit))
1701 				die("invalid unit value '%s',"
1702 				    "unit must be R|K|M|G|T|P", optarg);
1703 			break;
1704 		default:
1705 			die_opterr(optopt, option, use);
1706 			break;
1707 		}
1708 	}
1709 
1710 	if (r_arg && t_arg)
1711 		die("the options -t and -r are not compatible");
1712 
1713 	if (u_arg && p_arg)
1714 		die("the options -u and -p are not compatible");
1715 
1716 	if (p_arg && !o_arg)
1717 		die("-p requires -o");
1718 
1719 	if (p_arg && strcasecmp(o_fields_str, "all") == 0)
1720 		die("\"-o all\" is invalid with -p");
1721 
1722 	if (a_arg && A_arg)
1723 		die("the options -a and -A are not compatible");
1724 
1725 	if (a_arg &&
1726 	    (p_arg || o_arg || u_arg || i_arg)) {
1727 		die("the option -a is not compatible with "
1728 		    "-p, -o, -u, -i");
1729 	}
1730 
1731 	if (A_arg &&
1732 	    (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg)) {
1733 		die("the option -A is not compatible with "
1734 		    "-r, -t, -p, -o, -u, -i");
1735 	}
1736 
1737 	/* get link name (optional last argument) */
1738 	if (optind == (argc-1)) {
1739 		if (strlen(argv[optind]) >= MAXLINKNAMELEN)
1740 			die("link name too long");
1741 
1742 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
1743 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
1744 			die_dlerr(status, "link %s is not valid", argv[optind]);
1745 		}
1746 	} else if (optind != argc) {
1747 		if (argc != 0)
1748 			usage();
1749 	}
1750 
1751 	if (a_arg) {
1752 		boolean_t	stattype[DLADM_STAT_NUM_STATS];
1753 
1754 		bzero(&stattype, sizeof (stattype));
1755 		if (r_arg) {
1756 			stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
1757 		} else if (t_arg) {
1758 			stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
1759 		} else {		/* Display both Rx and Tx lanes */
1760 			stattype[DLADM_STAT_TOTAL] = B_TRUE;
1761 		}
1762 
1763 		dump_all_link_stats(linkid, stattype);
1764 		return;
1765 	}
1766 
1767 	if (A_arg) {
1768 		boolean_t	stattype[DLADM_STAT_NUM_STATS];
1769 		int		i;
1770 
1771 		for (i = 0; i < DLADM_STAT_NUM_STATS; i++)
1772 			stattype[i] = B_TRUE;
1773 
1774 		dump_all_link_stats(linkid, stattype);
1775 		return;
1776 	}
1777 
1778 	state.ls_unit = unit;
1779 	state.ls_parsable = p_arg;
1780 
1781 	if (state.ls_parsable)
1782 		ofmtflags |= OFMT_PARSABLE;
1783 
1784 	if (r_arg) {
1785 		fields_str = rx_total_stat_fields;
1786 		oftemplate = rx_lane_s_fields;
1787 		state.ls_stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
1788 		state.ls_stats2str[DLADM_STAT_RX_LANE_TOTAL] =
1789 		    print_rx_lane_stats;
1790 	} else if (t_arg) {
1791 		fields_str = tx_total_stat_fields;
1792 		oftemplate = tx_lane_s_fields;
1793 		state.ls_stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
1794 		state.ls_stats2str[DLADM_STAT_TX_LANE_TOTAL] =
1795 		    print_tx_lane_stats;
1796 	} else {		/* Display both Rx and Tx lanes total */
1797 		fields_str = total_stat_fields;
1798 		oftemplate = total_s_fields;
1799 		state.ls_stattype[DLADM_STAT_TOTAL] = B_TRUE;
1800 		state.ls_stats2str[DLADM_STAT_TOTAL] = print_total_stats;
1801 	}
1802 
1803 	if (o_arg) {
1804 		fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
1805 		    fields_str : o_fields_str;
1806 	}
1807 
1808 	oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
1809 	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
1810 	state.ls_ofmt = ofmt;
1811 
1812 	show_link_stats(linkid, state, interval);
1813 
1814 	ofmt_close(ofmt);
1815 }
1816 
1817 static void
1818 do_show_phys(int argc, char *argv[], const char *use)
1819 {
1820 	int			option;
1821 	boolean_t		r_arg = B_FALSE;
1822 	boolean_t		t_arg = B_FALSE;
1823 	boolean_t		i_arg = B_FALSE;
1824 	boolean_t		p_arg = B_FALSE;
1825 	boolean_t		o_arg = B_FALSE;
1826 	boolean_t		u_arg = B_FALSE;
1827 	boolean_t		a_arg = B_FALSE;
1828 	uint32_t		flags = DLADM_OPT_ACTIVE;
1829 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
1830 	char			linkname[MAXLINKNAMELEN];
1831 	uint32_t		interval = 0;
1832 	char			unit = '\0';
1833 	show_state_t		state;
1834 	dladm_status_t		status;
1835 	char			*fields_str = NULL;
1836 	char			*o_fields_str = NULL;
1837 	char			*ring_stat_fields =
1838 	    "link,type,index,pkts,bytes";
1839 	char			*rx_ring_stat_fields =
1840 	    "link,type,index,ipkts,rbytes";
1841 	char			*tx_ring_stat_fields =
1842 	    "link,type,index,opkts,obytes";
1843 
1844 	ofmt_handle_t		ofmt;
1845 	ofmt_status_t		oferr;
1846 	uint_t			ofmtflags = OFMT_RIGHTJUST;
1847 	ofmt_field_t		*oftemplate;
1848 
1849 	bzero(&state, sizeof (state));
1850 	opterr = 0;
1851 	while ((option = getopt_long(argc, argv, ":rtapi:o:u:",
1852 	    NULL, NULL)) != -1) {
1853 		switch (option) {
1854 		case 'r':
1855 			if (r_arg)
1856 				die_optdup(option);
1857 
1858 			r_arg = B_TRUE;
1859 			break;
1860 		case 't':
1861 			if (t_arg)
1862 				die_optdup(option);
1863 
1864 			t_arg = B_TRUE;
1865 			break;
1866 		case 'a':
1867 			if (a_arg)
1868 				die_optdup(option);
1869 
1870 			a_arg = B_TRUE;
1871 			break;
1872 		case 'i':
1873 			if (i_arg)
1874 				die_optdup(option);
1875 
1876 			i_arg = B_TRUE;
1877 			if (!dladm_str2interval(optarg, &interval))
1878 				die("invalid interval value '%s'", optarg);
1879 			break;
1880 		case 'p':
1881 			if (p_arg)
1882 				die_optdup(option);
1883 
1884 			p_arg = B_TRUE;
1885 			break;
1886 		case 'o':
1887 			o_arg = B_TRUE;
1888 			o_fields_str = optarg;
1889 			break;
1890 		case 'u':
1891 			if (u_arg)
1892 				die_optdup(option);
1893 
1894 			u_arg = B_TRUE;
1895 			if (!dlstat_unit(optarg, &unit))
1896 				die("invalid unit value '%s',"
1897 				    "unit must be R|K|M|G|T|P", optarg);
1898 			break;
1899 		default:
1900 			die_opterr(optopt, option, use);
1901 			break;
1902 		}
1903 	}
1904 
1905 	if (r_arg && t_arg)
1906 		die("the options -t and -r are not compatible");
1907 
1908 	if (u_arg && p_arg)
1909 		die("the options -u and -p are not compatible");
1910 
1911 	if (p_arg && !o_arg)
1912 		die("-p requires -o");
1913 
1914 	if (p_arg && strcasecmp(o_fields_str, "all") == 0)
1915 		die("\"-o all\" is invalid with -p");
1916 
1917 	if (a_arg &&
1918 	    (p_arg || o_arg || u_arg || i_arg)) {
1919 		die("the option -a is not compatible with "
1920 		    "-p, -o, -u, -i");
1921 	}
1922 
1923 
1924 	/* get link name (optional last argument) */
1925 	if (optind == (argc-1)) {
1926 		if (strlen(argv[optind]) >= MAXLINKNAMELEN)
1927 			die("link name too long");
1928 
1929 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
1930 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
1931 			die_dlerr(status, "link %s is not valid", argv[optind]);
1932 		}
1933 	} else if (optind != argc) {
1934 		usage();
1935 	}
1936 
1937 	if (a_arg) {
1938 		boolean_t	stattype[DLADM_STAT_NUM_STATS];
1939 
1940 		bzero(&stattype, sizeof (stattype));
1941 
1942 		if (r_arg) {
1943 			stattype[DLADM_STAT_RX_RING] = B_TRUE;
1944 		} else if (t_arg) {
1945 			stattype[DLADM_STAT_TX_RING] = B_TRUE;
1946 		} else {		/* Display both Rx and Tx lanes */
1947 			stattype[DLADM_STAT_RX_RING] = B_TRUE;
1948 			stattype[DLADM_STAT_TX_RING] = B_TRUE;
1949 		}
1950 
1951 		dump_all_link_stats(linkid, stattype);
1952 		return;
1953 	}
1954 
1955 	state.ls_unit = unit;
1956 	state.ls_parsable = p_arg;
1957 
1958 	if (state.ls_parsable)
1959 		ofmtflags |= OFMT_PARSABLE;
1960 
1961 	if (r_arg) {
1962 		fields_str = rx_ring_stat_fields;
1963 		oftemplate = rx_ring_s_fields;
1964 		state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
1965 		state.ls_stats2str[DLADM_STAT_RX_RING] = print_rx_ring_stats;
1966 	} else if (t_arg) {
1967 		fields_str = tx_ring_stat_fields;
1968 		oftemplate = tx_ring_s_fields;
1969 		state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
1970 		state.ls_stats2str[DLADM_STAT_TX_RING] = print_tx_ring_stats;
1971 	} else {		/* Display both Rx and Tx lanes */
1972 		fields_str = ring_stat_fields;
1973 		oftemplate = ring_s_fields;
1974 		state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
1975 		state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
1976 		state.ls_stats2str[DLADM_STAT_RX_RING] =
1977 		    print_rx_generic_ring_stats;
1978 		state.ls_stats2str[DLADM_STAT_TX_RING] =
1979 		    print_tx_generic_ring_stats;
1980 	}
1981 
1982 	if (o_arg) {
1983 		fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
1984 		    fields_str : o_fields_str;
1985 	}
1986 
1987 	oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
1988 	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
1989 	state.ls_ofmt = ofmt;
1990 
1991 	show_link_stats(linkid, state, interval);
1992 
1993 	ofmt_close(ofmt);
1994 }
1995 
1996 static void
1997 do_show_link(int argc, char *argv[], const char *use)
1998 {
1999 	int			option;
2000 	boolean_t		r_arg = B_FALSE;
2001 	boolean_t		F_arg = B_FALSE;
2002 	boolean_t		t_arg = B_FALSE;
2003 	boolean_t		i_arg = B_FALSE;
2004 	boolean_t		p_arg = B_FALSE;
2005 	boolean_t		o_arg = B_FALSE;
2006 	boolean_t		u_arg = B_FALSE;
2007 	boolean_t		a_arg = B_FALSE;
2008 	uint32_t		flags = DLADM_OPT_ACTIVE;
2009 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
2010 	uint32_t		interval = 0;
2011 	char			unit = '\0';
2012 	show_state_t		state;
2013 	dladm_status_t		status;
2014 	char			*fields_str = NULL;
2015 	char			*o_fields_str = NULL;
2016 
2017 	char			*lane_stat_fields =
2018 	    "link,type,id,index,pkts,bytes";
2019 	char			*rx_lane_stat_fields =
2020 	    "link,type,id,index,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
2021 	char			*tx_lane_stat_fields =
2022 	    "link,type,id,index,opkts,obytes,blkcnt,ublkcnt";
2023 	char			*rx_fanout_stat_fields =
2024 	    "link,id,index,fout,ipkts,rbytes";
2025 
2026 	ofmt_handle_t		ofmt;
2027 	ofmt_status_t		oferr;
2028 	uint_t			ofmtflags = OFMT_RIGHTJUST;
2029 	ofmt_field_t		*oftemplate;
2030 
2031 	bzero(&state, sizeof (state));
2032 	opterr = 0;
2033 	while ((option = getopt_long(argc, argv, ":hrtFapi:o:u:",
2034 	    NULL, NULL)) != -1) {
2035 		switch (option) {
2036 		case 'h':
2037 			if (r_arg || F_arg || t_arg || i_arg || p_arg ||
2038 			    o_arg || u_arg || a_arg) {
2039 				die("the option -h is not compatible with "
2040 				    "-r, -F, -t, -i, -p, -o, -u, -a");
2041 			}
2042 			do_show_history(argc, &argv[0], use);
2043 			return;
2044 		case 'r':
2045 			if (r_arg)
2046 				die_optdup(option);
2047 
2048 			r_arg = B_TRUE;
2049 			break;
2050 		case 'F':
2051 			if (F_arg)
2052 				die_optdup(option);
2053 
2054 			F_arg = B_TRUE;
2055 			break;
2056 		case 't':
2057 			if (t_arg)
2058 				die_optdup(option);
2059 
2060 			t_arg = B_TRUE;
2061 			break;
2062 		case 'a':
2063 			if (a_arg)
2064 				die_optdup(option);
2065 
2066 			a_arg = B_TRUE;
2067 			break;
2068 		case 'i':
2069 			if (i_arg)
2070 				die_optdup(option);
2071 
2072 			i_arg = B_TRUE;
2073 			if (!dladm_str2interval(optarg, &interval))
2074 				die("invalid interval value '%s'", optarg);
2075 			break;
2076 		case 'p':
2077 			if (p_arg)
2078 				die_optdup(option);
2079 
2080 			p_arg = B_TRUE;
2081 			break;
2082 		case 'o':
2083 			o_arg = B_TRUE;
2084 			o_fields_str = optarg;
2085 			break;
2086 		case 'u':
2087 			if (u_arg)
2088 				die_optdup(option);
2089 
2090 			u_arg = B_TRUE;
2091 			if (!dlstat_unit(optarg, &unit))
2092 				die("invalid unit value '%s',"
2093 				    "unit must be R|K|M|G|T|P", optarg);
2094 			break;
2095 		default:
2096 			die_opterr(optopt, option, use);
2097 			break;
2098 		}
2099 	}
2100 
2101 	if (r_arg && t_arg)
2102 		die("the options -t and -r are not compatible");
2103 
2104 	if (u_arg && p_arg)
2105 		die("the options -u and -p are not compatible");
2106 
2107 	if (F_arg && !r_arg)
2108 		die("-F must be used with -r");
2109 
2110 	if (p_arg && !o_arg)
2111 		die("-p requires -o");
2112 
2113 	if (p_arg && strcasecmp(o_fields_str, "all") == 0)
2114 		die("\"-o all\" is invalid with -p");
2115 
2116 	if (a_arg &&
2117 	    (p_arg || o_arg || u_arg || i_arg)) {
2118 		die("the option -a is not compatible with "
2119 		    "-p, -o, -u, -i");
2120 	}
2121 
2122 	/* get link name (optional last argument) */
2123 	if (optind == (argc-1)) {
2124 		if (strlen(argv[optind]) >= MAXLINKNAMELEN)
2125 			die("link name too long");
2126 
2127 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
2128 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
2129 			die_dlerr(status, "link %s is not valid", argv[optind]);
2130 		}
2131 	} else if (optind != argc) {
2132 		usage();
2133 	}
2134 
2135 	if (a_arg) {
2136 		boolean_t	stattype[DLADM_STAT_NUM_STATS];
2137 
2138 		bzero(&stattype, sizeof (stattype));
2139 
2140 		if (r_arg) {
2141 			if (F_arg) {
2142 				stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
2143 			} else {
2144 				stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2145 			}
2146 		} else if (t_arg) {
2147 			stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2148 		} else {		/* Display both Rx and Tx lanes */
2149 			stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2150 			stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2151 		}
2152 
2153 		dump_all_link_stats(linkid, stattype);
2154 		return;
2155 	}
2156 
2157 	state.ls_unit = unit;
2158 	state.ls_parsable = p_arg;
2159 
2160 	if (state.ls_parsable)
2161 		ofmtflags |= OFMT_PARSABLE;
2162 
2163 	if (r_arg) {
2164 		if (F_arg) {
2165 			fields_str = rx_fanout_stat_fields;
2166 			oftemplate = rx_fanout_lane_s_fields;
2167 			state.ls_stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
2168 			state.ls_stats2str[DLADM_STAT_RX_LANE_FOUT] =
2169 			    print_fanout_stats;
2170 		} else {
2171 			fields_str = rx_lane_stat_fields;
2172 			oftemplate = rx_lane_s_fields;
2173 			state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2174 			state.ls_stats2str[DLADM_STAT_RX_LANE] =
2175 			    print_rx_lane_stats;
2176 		}
2177 	} else if (t_arg) {
2178 		fields_str = tx_lane_stat_fields;
2179 		oftemplate = tx_lane_s_fields;
2180 		state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2181 		state.ls_stats2str[DLADM_STAT_TX_LANE] = print_tx_lane_stats;
2182 	} else {		/* Display both Rx and Tx lanes */
2183 		fields_str = lane_stat_fields;
2184 		oftemplate = lane_s_fields;
2185 		state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2186 		state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2187 		state.ls_stats2str[DLADM_STAT_RX_LANE] =
2188 		    print_rx_generic_lane_stats;
2189 		state.ls_stats2str[DLADM_STAT_TX_LANE] =
2190 		    print_tx_generic_lane_stats;
2191 	}
2192 	if (o_arg) {
2193 		fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
2194 		    fields_str : o_fields_str;
2195 	}
2196 
2197 	oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
2198 	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
2199 
2200 	state.ls_ofmt = ofmt;
2201 
2202 	show_link_stats(linkid, state, interval);
2203 
2204 	ofmt_close(ofmt);
2205 }
2206 
2207 static void
2208 do_show_aggr(int argc, char *argv[], const char *use)
2209 {
2210 	int			option;
2211 	boolean_t		r_arg = B_FALSE;
2212 	boolean_t		t_arg = B_FALSE;
2213 	boolean_t		i_arg = B_FALSE;
2214 	boolean_t		p_arg = B_FALSE;
2215 	boolean_t		o_arg = B_FALSE;
2216 	boolean_t		u_arg = B_FALSE;
2217 	uint32_t		flags = DLADM_OPT_ACTIVE;
2218 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
2219 	uint32_t		interval = 0;
2220 	char			unit = '\0';
2221 	show_state_t		state;
2222 	dladm_status_t		status;
2223 	char			*fields_str = NULL;
2224 	char			*o_fields_str = NULL;
2225 
2226 	char			*aggr_stat_fields =
2227 	    "link,port,ipkts,rbytes,opkts,obytes";
2228 	char			*rx_aggr_stat_fields = "link,port,ipkts,rbytes";
2229 	char			*tx_aggr_stat_fields = "link,port,opkts,obytes";
2230 
2231 	ofmt_handle_t		ofmt;
2232 	ofmt_status_t		oferr;
2233 	uint_t			ofmtflags = OFMT_RIGHTJUST;
2234 	ofmt_field_t		*oftemplate;
2235 
2236 	bzero(&state, sizeof (state));
2237 	opterr = 0;
2238 	while ((option = getopt_long(argc, argv, ":rtpi:o:u:",
2239 	    NULL, NULL)) != -1) {
2240 		switch (option) {
2241 		case 'r':
2242 			if (r_arg)
2243 				die_optdup(option);
2244 
2245 			r_arg = B_TRUE;
2246 			break;
2247 		case 't':
2248 			if (t_arg)
2249 				die_optdup(option);
2250 
2251 			t_arg = B_TRUE;
2252 			break;
2253 		case 'i':
2254 			if (i_arg)
2255 				die_optdup(option);
2256 
2257 			i_arg = B_TRUE;
2258 			if (!dladm_str2interval(optarg, &interval))
2259 				die("invalid interval value '%s'", optarg);
2260 			break;
2261 		case 'p':
2262 			if (p_arg)
2263 				die_optdup(option);
2264 
2265 			p_arg = B_TRUE;
2266 			break;
2267 		case 'o':
2268 			o_arg = B_TRUE;
2269 			o_fields_str = optarg;
2270 			break;
2271 		case 'u':
2272 			if (u_arg)
2273 				die_optdup(option);
2274 
2275 			u_arg = B_TRUE;
2276 			if (!dlstat_unit(optarg, &unit))
2277 				die("invalid unit value '%s',"
2278 				    "unit must be R|K|M|G|T|P", optarg);
2279 			break;
2280 		default:
2281 			die_opterr(optopt, option, use);
2282 			break;
2283 		}
2284 	}
2285 
2286 	if (r_arg && t_arg)
2287 		die("the options -t and -r are not compatible");
2288 
2289 	if (u_arg && p_arg)
2290 		die("the options -u and -p are not compatible");
2291 
2292 	if (p_arg && !o_arg)
2293 		die("-p requires -o");
2294 
2295 	if (p_arg && strcasecmp(o_fields_str, "all") == 0)
2296 		die("\"-o all\" is invalid with -p");
2297 
2298 
2299 	/* get link name (optional last argument) */
2300 	if (optind == (argc-1)) {
2301 		if (strlen(argv[optind]) >= MAXLINKNAMELEN)
2302 			die("link name too long");
2303 
2304 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
2305 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
2306 			die_dlerr(status, "link %s is not valid", argv[optind]);
2307 		}
2308 	} else if (optind != argc) {
2309 		usage();
2310 	}
2311 
2312 	state.ls_unit = unit;
2313 	state.ls_parsable = p_arg;
2314 
2315 	if (state.ls_parsable)
2316 		ofmtflags |= OFMT_PARSABLE;
2317 
2318 	oftemplate = aggr_port_s_fields;
2319 	state.ls_stattype[DLADM_STAT_AGGR_PORT] = B_TRUE;
2320 	state.ls_stats2str[DLADM_STAT_AGGR_PORT] = print_aggr_port_stats;
2321 
2322 	if (r_arg)
2323 		fields_str = rx_aggr_stat_fields;
2324 	else if (t_arg)
2325 		fields_str = tx_aggr_stat_fields;
2326 	else
2327 		fields_str = aggr_stat_fields;
2328 
2329 	if (o_arg) {
2330 		fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
2331 		    fields_str : o_fields_str;
2332 	}
2333 
2334 	oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
2335 	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
2336 	state.ls_ofmt = ofmt;
2337 
2338 	show_link_stats(linkid, state, interval);
2339 
2340 	ofmt_close(ofmt);
2341 }
2342 
2343 /* PRINTFLIKE1 */
2344 static void
2345 warn(const char *format, ...)
2346 {
2347 	va_list alist;
2348 
2349 	format = gettext(format);
2350 	(void) fprintf(stderr, "%s: warning: ", progname);
2351 
2352 	va_start(alist, format);
2353 	(void) vfprintf(stderr, format, alist);
2354 	va_end(alist);
2355 
2356 	(void) putc('\n', stderr);
2357 }
2358 
2359 /*
2360  * Also closes the dladm handle if it is not NULL.
2361  */
2362 /* PRINTFLIKE2 */
2363 static void
2364 die_dlerr(dladm_status_t err, const char *format, ...)
2365 {
2366 	va_list alist;
2367 	char	errmsg[DLADM_STRSIZE];
2368 
2369 	format = gettext(format);
2370 	(void) fprintf(stderr, "%s: ", progname);
2371 
2372 	va_start(alist, format);
2373 	(void) vfprintf(stderr, format, alist);
2374 	va_end(alist);
2375 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
2376 
2377 	/* close dladm handle if it was opened */
2378 	if (handle != NULL)
2379 		dladm_close(handle);
2380 
2381 	exit(EXIT_FAILURE);
2382 }
2383 
2384 /* PRINTFLIKE1 */
2385 static void
2386 die(const char *format, ...)
2387 {
2388 	va_list alist;
2389 
2390 	format = gettext(format);
2391 	(void) fprintf(stderr, "%s: ", progname);
2392 
2393 	va_start(alist, format);
2394 	(void) vfprintf(stderr, format, alist);
2395 	va_end(alist);
2396 
2397 	(void) putc('\n', stderr);
2398 
2399 	/* close dladm handle if it was opened */
2400 	if (handle != NULL)
2401 		dladm_close(handle);
2402 
2403 	exit(EXIT_FAILURE);
2404 }
2405 
2406 static void
2407 die_optdup(int opt)
2408 {
2409 	die("the option -%c cannot be specified more than once", opt);
2410 }
2411 
2412 static void
2413 die_opterr(int opt, int opterr, const char *usage)
2414 {
2415 	switch (opterr) {
2416 	case ':':
2417 		die("option '-%c' requires a value\nusage: %s", opt,
2418 		    gettext(usage));
2419 		break;
2420 	case '?':
2421 	default:
2422 		die("unrecognized option '-%c'\nusage: %s", opt,
2423 		    gettext(usage));
2424 		break;
2425 	}
2426 }
2427 
2428 /*
2429  * default output callback function that, when invoked,
2430  * prints string which is offset by ofmt_arg->ofmt_id within buf.
2431  */
2432 static boolean_t
2433 print_default_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
2434 {
2435 	char *value;
2436 
2437 	value = (char *)ofarg->ofmt_cbarg + ofarg->ofmt_id;
2438 	(void) strlcpy(buf, value, bufsize);
2439 	return (B_TRUE);
2440 }
2441