xref: /illumos-gate/usr/src/cmd/dladm/dladm.c (revision 7c478bd9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <locale.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <string.h>
35 #include <stropts.h>
36 #include <errno.h>
37 #include <kstat.h>
38 #include <strings.h>
39 #include <getopt.h>
40 #include <unistd.h>
41 #include <libintl.h>
42 #include <libdlpi.h>
43 #include <libdladm.h>
44 #include <liblaadm.h>
45 #include <libmacadm.h>
46 
47 #define	AGGR_DRIVER	"aggr"
48 #define	AGGR_DEV	"aggr0"
49 #define	MAXPORT		256
50 #define	DUMP_LACP_FORMAT	"    %-9s %-8s %-7s %-12s "	\
51 	"%-5s %-4s %-4s %-9s %-7s\n"
52 
53 typedef struct pktsum_s {
54 	uint64_t	ipackets;
55 	uint64_t	opackets;
56 	uint64_t	rbytes;
57 	uint64_t	obytes;
58 	uint32_t	ierrors;
59 	uint32_t	oerrors;
60 } pktsum_t;
61 
62 typedef struct show_link_state {
63 	boolean_t	ls_firstonly;
64 	boolean_t	ls_donefirst;
65 	boolean_t	ls_stats;
66 	pktsum_t	ls_prevstats;
67 	boolean_t	ls_parseable;
68 } show_link_state_t;
69 
70 typedef struct show_grp_state {
71 	uint32_t	gs_key;
72 	boolean_t	gs_lacp;
73 	boolean_t	gs_found;
74 	boolean_t	gs_stats;
75 	boolean_t	gs_firstonly;
76 	pktsum_t	gs_prevstats[MAXPORT];
77 	boolean_t	gs_parseable;
78 } show_grp_state_t;
79 
80 typedef struct show_mac_state {
81 	boolean_t	ms_firstonly;
82 	boolean_t	ms_donefirst;
83 	pktsum_t	ms_prevstats;
84 	boolean_t	ms_parseable;
85 } show_mac_state_t;
86 
87 typedef struct port_state {
88 	char			*state_name;
89 	aggr_port_state_t	state_num;
90 } port_state_t;
91 
92 static port_state_t port_states[] = {
93 	{"standby", AGGR_PORT_STATE_STANDBY },
94 	{"attached", AGGR_PORT_STATE_ATTACHED }
95 };
96 
97 #define	NPORTSTATES	(sizeof (port_states) / sizeof (port_state_t))
98 
99 static void	do_create_vlan(int, char **);
100 static void	do_delete_vlan(int, char **);
101 static void	do_show_link(int, char **);
102 static void	do_up_link(int, char **);
103 static void	do_init_link(int, char **);
104 static void	do_create_aggr(int, char **);
105 static void	do_delete_aggr(int, char **);
106 static void	do_add_aggr(int, char **);
107 static void	do_remove_aggr(int, char **);
108 static void	do_modify_aggr(int, char **);
109 static void	do_show_aggr(int, char **);
110 static void	do_up_aggr(int, char **);
111 static void	do_down_aggr(int, char **);
112 static void	do_show_dev(int, char **);
113 
114 static void	link_stats(const char *, uint32_t);
115 static void	aggr_stats(uint16_t, uint32_t);
116 static void	dev_stats(const char *dev, uint32_t);
117 
118 static void	get_mac_stats(const char *, uint_t, pktsum_t *);
119 static void	get_link_stats(const char *, pktsum_t *);
120 static uint64_t	mac_ifspeed(const char *, uint_t);
121 static char	*mac_link_state(const char *, uint_t);
122 static char	*mac_link_duplex(const char *, uint_t);
123 static void	stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
124 static void	stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
125 
126 typedef struct	cmd {
127 	char	*c_name;
128 	void	(*c_fn)(int, char **);
129 } cmd_t;
130 
131 static cmd_t	cmds[] = {
132 	{ "create-vlan", do_create_vlan },
133 	{ "delete-vlan", do_delete_vlan },
134 	{ "show-link", do_show_link },
135 	{ "up-link", do_up_link },
136 	{ "init-link", do_init_link },
137 
138 	{ "create-aggr", do_create_aggr },
139 	{ "delete-aggr", do_delete_aggr },
140 	{ "add-aggr", do_add_aggr },
141 	{ "remove-aggr", do_remove_aggr },
142 	{ "modify-aggr", do_modify_aggr },
143 	{ "show-aggr", do_show_aggr },
144 	{ "up-aggr", do_up_aggr },
145 	{ "down-aggr", do_down_aggr },
146 
147 	{ "show-dev", do_show_dev }
148 };
149 
150 static const struct option longopts[] = {
151 	{"vlan-id",	required_argument,	0, 'v'},
152 	{"dev",		required_argument,	0, 'd'},
153 	{"policy",	required_argument,	0, 'P'},
154 	{"lacp-mode",	required_argument,	0, 'l'},
155 	{"lacp-timer",	required_argument,	0, 'T'},
156 	{"unicast",	required_argument,	0, 'u'},
157 	{"statistics",	no_argument,		0, 's'},
158 	{"interval",	required_argument,	0, 'i'},
159 	{"lacp",	no_argument,		0, 'L'},
160 	{"temporary",	no_argument,		0, 't'},
161 	{"root-dir",	required_argument,	0, 'r'},
162 	{"parseable",	no_argument,		0, 'p'},
163 	{ 0, 0, 0, 0 }
164 };
165 
166 static char *progname;
167 
168 #define	PRINT_ERR_DIAG(s, diag, func) {					\
169 	(void) fprintf(stderr, gettext(s), progname, strerror(errno));	\
170 	if (diag != 0)							\
171 		(void) fprintf(stderr, " (%s)", func(diag));		\
172 	(void) fprintf(stderr, "\n");					\
173 }
174 
175 static void
176 usage(void)
177 {
178 	(void) fprintf(stderr, gettext(
179 	    "usage: dladm create-vlan [-t] [-R <root-dir>] -v <vlan> -d <dev>\n"
180 	    "             delete-vlan [-t] [-R <root-dir>] -v <vlan> -d <dev>\n"
181 	    "             create-aggr [-t] [-R <root-dir>] [-P <policy>]\n"
182 	    "                    [-l <mode>] [-T <time>]\n"
183 	    "                    [-u <address>] -d <dev> ... <key>\n"
184 	    "             delete-aggr [-t] [-R <root-dir>] <key>\n"
185 	    "             add-aggr    [-t] [-R <root-dir>] -d <dev> ... <key>\n"
186 	    "             remove-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n"
187 	    "             modify-aggr [-t] [-R <root-dir>] [-P <policy>]\n"
188 	    "                    [-l <mode>] [-T <time>] [-u <address>] <key>\n"
189 	    "             show-aggr [-L] [-s] [-i <interval>] [-p] [<key>]\n"
190 	    "             show-dev [-s] [-i <interval>] [-p] [<dev>]\n"
191 	    "             show-link [-s] [-i <interval>] [-p] [<name>]\n"));
192 	exit(1);
193 }
194 
195 int
196 main(int argc, char *argv[])
197 {
198 	int	i;
199 	cmd_t	*cmdp;
200 
201 	(void) setlocale(LC_ALL, "");
202 #if !defined(TEXT_DOMAIN)
203 #define	TEXT_DOMAIN "SYS_TEST"
204 #endif
205 	(void) textdomain(TEXT_DOMAIN);
206 
207 	progname = argv[0];
208 
209 	if (argc < 2)
210 		usage();
211 
212 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
213 		cmdp = &cmds[i];
214 		if (strcmp(argv[1], cmdp->c_name) == 0) {
215 			cmdp->c_fn(argc - 1, &argv[1]);
216 			exit(0);
217 		}
218 	}
219 
220 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
221 	    progname, argv[1]);
222 	usage();
223 
224 	return (0);
225 }
226 
227 static void
228 do_create_vlan(int argc, char *argv[])
229 {
230 	char		name[MAXNAMELEN];
231 	char		driver[MAXNAMELEN];
232 	int		instance;
233 	dladm_attr_t	dlattr;
234 	int		value;
235 	char		option;
236 	boolean_t	v_arg = B_FALSE;
237 	boolean_t	d_arg = B_FALSE;
238 	boolean_t	t_arg = B_FALSE;
239 	char		*altroot = NULL;
240 	char		*endp = NULL;
241 	dladm_diag_t	diag = 0;
242 
243 	bzero(&dlattr, sizeof (dladm_attr_t));
244 
245 	opterr = 0;
246 	while ((option = getopt_long(argc, argv, ":d:R:tv:", longopts,
247 	    NULL)) != -1) {
248 		switch (option) {
249 		case 'v':
250 			if (v_arg) {
251 				(void) fprintf(stderr, gettext("%s: the option "
252 				    "-v cannot be specified more than once\n"),
253 				    progname);
254 				usage();
255 			}
256 
257 			v_arg = B_TRUE;
258 
259 			errno = 0;
260 			value = (int)strtol(optarg, &endp, 10);
261 			if (errno != 0 || value < 1 || value > 4094 ||
262 			    *endp != '\0') {
263 				(void) fprintf(stderr,
264 				    gettext("%s: illegal VLAN identifier"
265 				    " '%d'\n"), progname, value);
266 				exit(1);
267 			}
268 
269 			dlattr.da_vid = (uint16_t)value;
270 
271 			break;
272 		case 'd':
273 			if (d_arg) {
274 				(void) fprintf(stderr, gettext(
275 				    "%s: the option -d cannot be specified "
276 				    "more than once\n"), progname);
277 				usage();
278 			}
279 
280 			d_arg = B_TRUE;
281 
282 			if (strlcpy(dlattr.da_dev, optarg, MAXNAMELEN) >=
283 			    MAXNAMELEN) {
284 				(void) fprintf(stderr,
285 				    gettext("%s: device name too long\n"),
286 				    progname);
287 				exit(1);
288 			}
289 			break;
290 		case 't':
291 			t_arg = B_TRUE;
292 			break;
293 		case 'R':
294 			altroot = optarg;
295 			break;
296 		case ':':
297 			(void) fprintf(stderr,
298 			    gettext("%s: option requires a value '-%c'\n"),
299 			    progname, optopt);
300 			exit(1);
301 			break;
302 		case '?':
303 		default:
304 			(void) fprintf(stderr,
305 			    gettext("%s: unrecognized option '-%c'\n"),
306 			    progname, optopt);
307 			exit(1);
308 			break;
309 		}
310 	}
311 
312 	if (optind != argc) {
313 		usage();
314 	}
315 
316 	if (dlattr.da_dev[0] == '\0') {
317 		(void) fprintf(stderr,
318 		    gettext("%s: no device specified\n"),
319 		    progname);
320 		exit(1);
321 	}
322 
323 	if (dlattr.da_vid == 0) {
324 		(void) fprintf(stderr,
325 		    gettext("%s: no VLAN ID specified\n"),
326 		    progname);
327 		exit(1);
328 	}
329 
330 	if (dlpi_if_parse(dlattr.da_dev, driver, &instance) < 0 ||
331 	    instance < 0) {
332 		(void) fprintf(stderr,
333 		    gettext("%s: badly formatted device '%s'\n"),
334 		    progname, dlattr.da_dev);
335 		exit(1);
336 	}
337 
338 	(void) snprintf(name, MAXNAMELEN, "%s%d", driver,
339 	    (dlattr.da_vid * 1000) + instance);
340 
341 	/*
342 	 * For aggregations, the da_dev is always AGGR_DEV, and the
343 	 * aggregation key (the instance integer extracted above by
344 	 * dlpi_if_parse) is da_port.
345 	 */
346 	if (strcmp(driver, AGGR_DRIVER) == 0) {
347 		(void) strlcpy(dlattr.da_dev, AGGR_DEV, MAXNAMELEN);
348 		dlattr.da_port = instance;
349 	}
350 
351 	if (dladm_link(name, &dlattr, (t_arg ? DLADM_LINK_TEMP : 0),
352 	    altroot, &diag) < 0) {
353 		PRINT_ERR_DIAG("%s: link operation failed: %s", diag,
354 		    dladm_diag);
355 		exit(1);
356 	}
357 
358 	if (dladm_sync() < 0) {
359 		(void) fprintf(stderr,
360 		    gettext("%s: sync operation failed"),
361 		    progname);
362 		perror(" ");
363 		exit(1);
364 	}
365 }
366 
367 static void
368 do_delete_vlan(int argc, char *argv[])
369 {
370 	char		option;
371 	boolean_t	t_arg = B_FALSE;
372 	boolean_t	v_arg = B_FALSE;
373 	boolean_t	d_arg = B_FALSE;
374 	char		device[MAXNAMELEN];
375 	char		name[MAXNAMELEN];
376 	char		driver[MAXNAMELEN];
377 	int		instance;
378 	int		vid;
379 	char		*altroot = NULL;
380 	char		*endp = NULL;
381 	dladm_diag_t	diag = 0;
382 
383 	opterr = 0;
384 	while ((option = getopt_long(argc, argv, ":R:td:v:", longopts,
385 	    NULL)) != -1) {
386 		switch (option) {
387 		case 'v':
388 			if (v_arg) {
389 				(void) fprintf(stderr, gettext("%s: the option "
390 				    "-v cannot be specified more than once\n"),
391 				    progname);
392 				usage();
393 			}
394 
395 			v_arg = B_TRUE;
396 
397 			errno = 0;
398 			vid = (int)strtol(optarg, &endp, 10);
399 			if (errno != 0 || vid < 1 || vid > 4094 ||
400 			    *endp != '\0') {
401 				(void) fprintf(stderr,
402 				    gettext("%s: illegal VLAN identifier"
403 				    " '%d'\n"), progname, vid);
404 				exit(1);
405 			}
406 
407 			break;
408 		case 'd':
409 			if (d_arg) {
410 				(void) fprintf(stderr, gettext(
411 				    "%s: the option -d cannot be specified "
412 				    "more than once\n"), progname);
413 				usage();
414 			}
415 
416 			d_arg = B_TRUE;
417 
418 			if (strlcpy(device, optarg, MAXNAMELEN) >=
419 			    MAXNAMELEN) {
420 				(void) fprintf(stderr,
421 				    gettext("%s: device name too long\n"),
422 				    progname);
423 				exit(1);
424 			}
425 			break;
426 		case 't':
427 			t_arg = B_TRUE;
428 			break;
429 		case 'R':
430 			altroot = optarg;
431 			break;
432 		case ':':
433 			(void) fprintf(stderr,
434 			    gettext("%s: option requires a value '-%c'\n"),
435 			    progname, optopt);
436 			exit(1);
437 			break;
438 		case '?':
439 		default:
440 			(void) fprintf(stderr,
441 			    gettext("%s: unrecognized option '-%c'\n"),
442 			    progname, optopt);
443 			exit(1);
444 			break;
445 		}
446 	}
447 
448 	if (optind != argc)
449 		usage();
450 
451 	if (d_arg == B_FALSE) {
452 		(void) fprintf(stderr,
453 		    gettext("%s: no device specified\n"),
454 		    progname);
455 		exit(1);
456 	}
457 
458 	if (v_arg == B_FALSE) {
459 		(void) fprintf(stderr,
460 		    gettext("%s: no VLAN ID specified\n"),
461 		    progname);
462 		exit(1);
463 	}
464 
465 	if (dlpi_if_parse(device, driver, &instance) < 0 ||
466 	    instance < 0) {
467 		(void) fprintf(stderr,
468 		    gettext("%s: badly formatted device '%s'\n"),
469 		    progname, device);
470 		exit(1);
471 	}
472 
473 	(void) snprintf(name, MAXNAMELEN, "%s%d", driver,
474 	    (vid * 1000) + instance);
475 
476 	if (dladm_unlink(name, t_arg, altroot, &diag) < 0) {
477 		PRINT_ERR_DIAG("%s: unlink operation failed: %s", diag,
478 		    dladm_diag);
479 		exit(1);
480 	}
481 
482 	if (dladm_sync() < 0) {
483 		(void) fprintf(stderr,
484 		    gettext("%s: sync operation failed"),
485 		    progname);
486 		perror(" ");
487 		exit(1);
488 	}
489 }
490 
491 static void
492 do_create_aggr(int argc, char *argv[])
493 {
494 	char			option;
495 	uint16_t		key;
496 	uint32_t		policy = AGGR_POLICY_L4;
497 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
498 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
499 	laadm_port_attr_db_t	port[MAXPORT];
500 	uint_t			nport = 0;
501 	uint8_t			mac_addr[ETHERADDRL];
502 	boolean_t		mac_addr_fixed = B_FALSE;
503 	boolean_t		P_arg = B_FALSE;
504 	boolean_t		l_arg = B_FALSE;
505 	boolean_t		t_arg = B_FALSE;
506 	boolean_t		u_arg = B_FALSE;
507 	boolean_t		T_arg = B_FALSE;
508 	char			*altroot = NULL;
509 	char			*endp = NULL;
510 	laadm_diag_t		diag = 0;
511 
512 	opterr = 0;
513 	while ((option = getopt_long(argc, argv, ":d:l:P:R:tu:T:",
514 	    longopts, NULL)) != -1) {
515 		switch (option) {
516 		case 'd':
517 			if (nport >= MAXPORT) {
518 				(void) fprintf(stderr,
519 				    gettext("%s: too many <dev> arguments\n"),
520 				    progname);
521 				exit(1);
522 			}
523 
524 			if (strlcpy(port[nport].lp_devname, optarg,
525 			    MAXNAMELEN) >= MAXNAMELEN) {
526 				(void) fprintf(stderr,
527 				    gettext("%s: device name too long\n"),
528 				    progname);
529 				exit(1);
530 			}
531 
532 			port[nport].lp_port = 0;
533 
534 			nport++;
535 			break;
536 		case 'P':
537 			if (P_arg) {
538 				(void) fprintf(stderr, gettext(
539 				    "%s: the option -P cannot be specified "
540 				    "more than once\n"), progname);
541 				usage();
542 			}
543 
544 			P_arg = B_TRUE;
545 
546 			if (!laadm_str_to_policy(optarg, &policy)) {
547 				(void) fprintf(stderr,
548 				    gettext("%s: invalid policy '%s'\n"),
549 				    progname, optarg);
550 				exit(1);
551 			}
552 			break;
553 		case 'u':
554 			if (u_arg) {
555 				(void) fprintf(stderr, gettext(
556 				    "%s: the option -u cannot be specified "
557 				    "more than once\n"), progname);
558 				usage();
559 			}
560 
561 			u_arg = B_TRUE;
562 
563 			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
564 			    mac_addr)) {
565 				(void) fprintf(stderr,
566 				    gettext("%s: invalid MAC address '%s'\n"),
567 				    progname, optarg);
568 				exit(1);
569 			}
570 
571 			break;
572 		case 'l':
573 			if (l_arg) {
574 				(void) fprintf(stderr, gettext(
575 				    "%s: the option -l cannot be specified "
576 				    "more than once\n"), progname);
577 				usage();
578 			}
579 
580 			l_arg = B_TRUE;
581 
582 			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode)) {
583 				(void) fprintf(stderr,
584 				    gettext("%s: invalid LACP mode '%s'\n"),
585 				    progname, optarg);
586 				exit(1);
587 			}
588 
589 			break;
590 		case 'T':
591 			if (T_arg) {
592 				(void) fprintf(stderr, gettext(
593 				    "%s: the option -T cannot be specified "
594 				    "more than once\n"), progname);
595 				usage();
596 			}
597 
598 			T_arg = B_TRUE;
599 
600 			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer)) {
601 				(void) fprintf(stderr,
602 				    gettext("%s: invalid LACP timer value"
603 				    " '%s'\n"),
604 				    progname, optarg);
605 				exit(1);
606 			}
607 
608 			break;
609 		case 't':
610 			t_arg = B_TRUE;
611 			break;
612 		case 'R':
613 			altroot = optarg;
614 			break;
615 		case ':':
616 			(void) fprintf(stderr,
617 			    gettext("%s: option requires a value '-%c'\n"),
618 			    progname, optopt);
619 			exit(1);
620 			/*NOTREACHED*/
621 		case '?':
622 		default:
623 			(void) fprintf(stderr,
624 			    gettext("%s: unrecognized option '-%c'\n"),
625 			    progname, optopt);
626 			exit(1);
627 		}
628 	}
629 
630 	if (nport == 0)
631 		usage();
632 
633 	/* get key value (required last argument) */
634 	if (optind != (argc-1))
635 		usage();
636 
637 	errno = 0;
638 	key = (int)strtol(argv[optind], &endp, 10);
639 	if (errno != 0 || key < 1 || *endp != '\0') {
640 		(void) fprintf(stderr,
641 		    gettext("%s: illegal key value '%d'\n"),
642 		    progname, key);
643 		exit(1);
644 	}
645 
646 	if (laadm_create(key, nport, port, policy, mac_addr_fixed,
647 	    mac_addr, lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0) {
648 		PRINT_ERR_DIAG("%s: create operation failed: %s", diag,
649 		    laadm_diag);
650 		exit(1);
651 	}
652 }
653 
654 static void
655 do_delete_aggr(int argc, char *argv[])
656 {
657 	uint16_t		key;
658 	char			option;
659 	boolean_t		t_arg = B_FALSE;
660 	char			*altroot = NULL;
661 	char			*endp = NULL;
662 	laadm_diag_t		diag = 0;
663 
664 	opterr = 0;
665 	while ((option = getopt_long(argc, argv, ":R:t", longopts,
666 	    NULL)) != -1) {
667 		switch (option) {
668 
669 		case 't':
670 			t_arg = B_TRUE;
671 			break;
672 		case 'R':
673 			altroot = optarg;
674 			break;
675 		case ':':
676 			(void) fprintf(stderr,
677 			    gettext("%s: option requires a value '-%c'\n"),
678 			    progname, optopt);
679 			exit(1);
680 			break;
681 		case '?':
682 		default:
683 			(void) fprintf(stderr,
684 			    gettext("%s: unrecognized option '-%c'\n"),
685 			    progname, optopt);
686 			exit(1);
687 			break;
688 		}
689 	}
690 
691 	/* get key value (required last argument) */
692 	if (optind != (argc-1))
693 		usage();
694 
695 	errno = 0;
696 	key = (int)strtol(argv[optind], &endp, 10);
697 	if (errno != 0 || key < 1 || *endp != '\0') {
698 		(void) fprintf(stderr,
699 		    gettext("%s: illegal key value '%d'\n"),
700 		    progname, key);
701 		exit(1);
702 	}
703 
704 	if (laadm_delete(key, t_arg, altroot, &diag) < 0) {
705 		PRINT_ERR_DIAG("%s: delete operation failed: %s", diag,
706 		    laadm_diag);
707 		exit(1);
708 	}
709 }
710 
711 static void
712 do_add_aggr(int argc, char *argv[])
713 {
714 	char			option;
715 	uint16_t		key;
716 	laadm_port_attr_db_t	port[MAXPORT];
717 	uint_t			nport = 0;
718 	boolean_t		t_arg = B_FALSE;
719 	char			*altroot = NULL;
720 	char			*endp = NULL;
721 	laadm_diag_t		diag = 0;
722 
723 	opterr = 0;
724 	while ((option = getopt_long(argc, argv, ":d:R:t", longopts,
725 	    NULL)) != -1) {
726 		switch (option) {
727 		case 'd':
728 			if (nport >= MAXPORT) {
729 				(void) fprintf(stderr,
730 				    gettext("%s: too many <dev> arguments\n"),
731 				    progname);
732 				exit(1);
733 			}
734 
735 			if (strlcpy(port[nport].lp_devname, optarg,
736 			    MAXNAMELEN) >= MAXNAMELEN) {
737 				(void) fprintf(stderr,
738 				    gettext("%s: device name too long\n"),
739 				    progname);
740 				exit(1);
741 			}
742 			port[nport].lp_port = 0;
743 
744 			nport++;
745 			break;
746 		case 't':
747 			t_arg = B_TRUE;
748 			break;
749 		case 'R':
750 			altroot = optarg;
751 			break;
752 		case ':':
753 			(void) fprintf(stderr,
754 			    gettext("%s: option requires a value '-%c'\n"),
755 			    progname, optopt);
756 			exit(1);
757 			/*NOTREACHED*/
758 		case '?':
759 		default:
760 			(void) fprintf(stderr,
761 			    gettext("%s: unrecognized option '-%c'\n"),
762 			    progname, optopt);
763 			exit(1);
764 		}
765 	}
766 
767 	if (nport == 0)
768 		usage();
769 
770 	/* get key value (required last argument) */
771 	if (optind != (argc-1))
772 		usage();
773 
774 	errno = 0;
775 	key = (int)strtol(argv[optind], &endp, 10);
776 	if (errno != 0 || key < 1 || *endp != '\0') {
777 		(void) fprintf(stderr,
778 		    gettext("%s: illegal key value '%d'\n"),
779 		    progname, key);
780 		exit(1);
781 	}
782 
783 	if (laadm_add(key, nport, port, t_arg, altroot, &diag) < 0) {
784 		PRINT_ERR_DIAG("%s: add operation failed: %s", diag,
785 		    laadm_diag);
786 		exit(1);
787 	}
788 }
789 
790 static void
791 do_remove_aggr(int argc, char *argv[])
792 {
793 	char			option;
794 	uint16_t		key;
795 	laadm_port_attr_db_t	port[MAXPORT];
796 	uint_t			nport = 0;
797 	boolean_t		t_arg = B_FALSE;
798 	char			*altroot = NULL;
799 	char			*endp = NULL;
800 	laadm_diag_t		diag = 0;
801 
802 	opterr = 0;
803 	while ((option = getopt_long(argc, argv, ":d:R:t",
804 	    longopts, NULL)) != -1) {
805 		switch (option) {
806 		case 'd':
807 			if (nport >= MAXPORT) {
808 				(void) fprintf(stderr,
809 				    gettext("%s: too many <dev> arguments\n"),
810 				    progname);
811 				exit(1);
812 			}
813 
814 			if (strlcpy(port[nport].lp_devname, optarg,
815 			    MAXNAMELEN) >= MAXNAMELEN) {
816 				(void) fprintf(stderr,
817 				    gettext("%s: device name too long\n"),
818 				    progname);
819 				exit(1);
820 			}
821 			port[nport].lp_port = 0;
822 
823 			nport++;
824 			break;
825 		case 't':
826 			t_arg = B_TRUE;
827 			break;
828 		case 'R':
829 			altroot = optarg;
830 			break;
831 		case ':':
832 			(void) fprintf(stderr,
833 			    gettext("%s: option requires a value '-%c'\n"),
834 			    progname, optopt);
835 			exit(1);
836 			/*NOTREACHED*/
837 		case '?':
838 		default:
839 			(void) fprintf(stderr,
840 			    gettext("%s: unrecognized option '-%c'\n"),
841 			    progname, optopt);
842 			exit(1);
843 		}
844 	}
845 
846 	if (nport == 0)
847 		usage();
848 
849 	/* get key value (required last argument) */
850 	if (optind != (argc-1))
851 		usage();
852 
853 	errno = 0;
854 	key = (int)strtol(argv[optind], &endp, 10);
855 	if (errno != 0 || key < 1 || *endp != '\0') {
856 		(void) fprintf(stderr,
857 		    gettext("%s: illegal key value '%d'\n"),
858 		    progname, key);
859 		exit(1);
860 	}
861 
862 	if (laadm_remove(key, nport, port, t_arg, altroot, &diag) < 0) {
863 		PRINT_ERR_DIAG("%s: remove operation failed: %s", diag,
864 		    laadm_diag);
865 		exit(1);
866 	}
867 }
868 
869 static void
870 do_modify_aggr(int argc, char *argv[])
871 {
872 	char			option;
873 	uint16_t		key;
874 	uint32_t		policy = AGGR_POLICY_L4;
875 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
876 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
877 	uint8_t			mac_addr[ETHERADDRL];
878 	boolean_t		mac_addr_fixed = B_FALSE;
879 	uint8_t			modify_mask = 0;
880 	boolean_t		t_arg = B_FALSE;
881 	char			*altroot = NULL;
882 	char			*endp = NULL;
883 	laadm_diag_t		diag = 0;
884 
885 	opterr = 0;
886 	while ((option = getopt_long(argc, argv, ":l:P:R:tu:T:", longopts,
887 	    NULL)) != -1) {
888 		switch (option) {
889 		case 'P':
890 			if (modify_mask & LAADM_MODIFY_POLICY) {
891 				(void) fprintf(stderr, gettext(
892 				    "%s: the option -P cannot be specified "
893 				    "more than once\n"), progname);
894 				usage();
895 			}
896 
897 			modify_mask |= LAADM_MODIFY_POLICY;
898 
899 			if (!laadm_str_to_policy(optarg, &policy)) {
900 				(void) fprintf(stderr,
901 				    gettext("%s: invalid policy '%s'\n"),
902 				    progname, optarg);
903 				exit(1);
904 			}
905 			break;
906 		case 'u':
907 			if (modify_mask & LAADM_MODIFY_MAC) {
908 				(void) fprintf(stderr, gettext(
909 				    "%s: the option -u cannot be specified "
910 				    "more than once\n"), progname);
911 				usage();
912 			}
913 
914 			modify_mask |= LAADM_MODIFY_MAC;
915 
916 			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
917 			    mac_addr)) {
918 				(void) fprintf(stderr,
919 				    gettext("%s: invalid MAC address '%s'\n"),
920 				    progname, optarg);
921 				exit(1);
922 			}
923 
924 			break;
925 		case 'l':
926 			if (modify_mask & LAADM_MODIFY_LACP_MODE) {
927 				(void) fprintf(stderr, gettext(
928 				    "%s: the option -l cannot be specified "
929 				    "more than once\n"), progname);
930 				usage();
931 			}
932 
933 			modify_mask |= LAADM_MODIFY_LACP_MODE;
934 
935 			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode)) {
936 				(void) fprintf(stderr,
937 				    gettext("%s: invalid LACP mode '%s'\n"),
938 				    progname, optarg);
939 				exit(1);
940 			}
941 
942 			break;
943 		case 'T':
944 			if (modify_mask & LAADM_MODIFY_LACP_TIMER) {
945 				(void) fprintf(stderr, gettext(
946 				    "%s: the option -T cannot be specified "
947 				    "more than once\n"), progname);
948 				usage();
949 			}
950 
951 			modify_mask |= LAADM_MODIFY_LACP_TIMER;
952 
953 			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer)) {
954 				(void) fprintf(stderr,
955 				    gettext("%s: invalid LACP timer value"
956 				    " '%s'\n"),
957 				    progname, optarg);
958 				exit(1);
959 			}
960 
961 			break;
962 		case 't':
963 			t_arg = B_TRUE;
964 			break;
965 		case 'R':
966 			altroot = optarg;
967 			break;
968 		case ':':
969 			(void) fprintf(stderr,
970 			    gettext("%s: option requires a value '-%c'\n"),
971 			    progname, optopt);
972 			exit(1);
973 			/*NOTREACHED*/
974 		case '?':
975 		default:
976 			(void) fprintf(stderr,
977 			    gettext("%s: unrecognized option '-%c'\n"),
978 			    progname, optopt);
979 			exit(1);
980 		}
981 	}
982 
983 	if (modify_mask == 0) {
984 		(void) fprintf(stderr, gettext("%s: at least one of the "
985 		    "-PulT options must be specified\n"), progname);
986 		usage();
987 	}
988 
989 	/* get key value (required last argument) */
990 	if (optind != (argc-1))
991 		usage();
992 
993 	errno = 0;
994 	key = (int)strtol(argv[optind], &endp, 10);
995 	if (errno != 0 || key < 1 || *endp != '\0') {
996 		(void) fprintf(stderr,
997 		    gettext("%s: illegal key value '%d'\n"),
998 		    progname, key);
999 		exit(1);
1000 	}
1001 
1002 
1003 	if (laadm_modify(key, modify_mask, policy, mac_addr_fixed, mac_addr,
1004 	    lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0) {
1005 		PRINT_ERR_DIAG("%s: modify operation failed: %s", diag,
1006 		    laadm_diag);
1007 		exit(1);
1008 	}
1009 }
1010 
1011 static void
1012 do_up_link(int argc, char *argv[])
1013 {
1014 	char		*name = NULL;
1015 	dladm_diag_t	diag = 0;
1016 
1017 	/* get link name (optional last argument) */
1018 	if (argc == 2)
1019 		name = argv[1];
1020 	else if (argc > 2)
1021 		usage();
1022 
1023 	if (dladm_up(name, &diag) < 0) {
1024 		if (name != NULL) {
1025 			(void) fprintf(stderr,
1026 			    gettext("%s: could not bring up link '%s' : %s"),
1027 			    progname, name, strerror(errno));
1028 			if (diag != 0)
1029 				(void) fprintf(stderr, " (%s)",
1030 				    dladm_diag(diag));
1031 			(void) fprintf(stderr, "\n");
1032 		} else {
1033 			PRINT_ERR_DIAG("%s: could not bring links up: %s",
1034 			    diag, dladm_diag);
1035 		}
1036 		exit(1);
1037 	}
1038 }
1039 
1040 static void
1041 do_up_aggr(int argc, char *argv[])
1042 {
1043 	uint16_t	key = 0;
1044 	char		*endp = NULL;
1045 	laadm_diag_t	diag = 0;
1046 
1047 	/* get aggregation key (optional last argument) */
1048 	if (argc == 2) {
1049 		errno = 0;
1050 		key = (int)strtol(argv[1], &endp, 10);
1051 		if (errno != 0 || key < 1 || *endp != '\0') {
1052 			(void) fprintf(stderr,
1053 			    gettext("%s: illegal key value '%d'\n"),
1054 			    progname, key);
1055 			exit(1);
1056 		}
1057 	} else if (argc > 2) {
1058 		usage();
1059 	}
1060 
1061 	if (laadm_up(key, NULL, &diag) < 0) {
1062 		if (key != 0) {
1063 			(void) fprintf(stderr,
1064 			    gettext("%s: could not bring up aggregation"
1065 			    " '%u' : %s"), progname, key, strerror(errno));
1066 			if (diag != 0)
1067 				(void) fprintf(stderr, " (%s)",
1068 				    laadm_diag(diag));
1069 			(void) fprintf(stderr, "\n");
1070 		} else {
1071 			PRINT_ERR_DIAG(
1072 			    "%s: could not bring aggregations up: %s",
1073 			    diag, laadm_diag);
1074 		}
1075 		exit(1);
1076 	}
1077 }
1078 
1079 static void
1080 do_down_aggr(int argc, char *argv[])
1081 {
1082 	uint16_t	key = 0;
1083 	char		*endp = NULL;
1084 
1085 	/* get aggregation key (optional last argument) */
1086 	if (argc == 2) {
1087 		errno = 0;
1088 		key = (int)strtol(argv[1], &endp, 10);
1089 		if (errno != 0 || key < 1 || *endp != '\0') {
1090 			(void) fprintf(stderr,
1091 			    gettext("%s: illegal key value '%d'\n"),
1092 			    progname, key);
1093 			exit(1);
1094 		}
1095 	} else if (argc > 2) {
1096 		usage();
1097 	}
1098 
1099 	if (laadm_down(key) < 0) {
1100 		if (key != 0) {
1101 			(void) fprintf(stderr,
1102 			    gettext("%s: could not bring aggregation"
1103 			    " down '%u' : %s"),
1104 			    progname, key, strerror(errno));
1105 			(void) fprintf(stderr, "\n");
1106 		} else {
1107 			(void) fprintf(stderr,
1108 			    gettext("%s: could not bring aggregations"
1109 			    " down: %s"), progname, strerror(errno));
1110 		}
1111 		exit(1);
1112 	}
1113 }
1114 
1115 static void
1116 create(void *arg, const char *dev, uint_t port)
1117 {
1118 	dladm_attr_t	dlattr;
1119 	dladm_diag_t	diag;
1120 	int		flags;
1121 
1122 	if (port != 0)
1123 		return;
1124 
1125 	if (strlcpy(dlattr.da_dev, dev, MAXNAMELEN) >= MAXNAMELEN) {
1126 		(void) fprintf(stderr,
1127 		    gettext("%s: device name too long\n"),
1128 		    progname);
1129 		exit(1);
1130 	}
1131 
1132 	dlattr.da_port = 0;
1133 	dlattr.da_vid = 0;
1134 
1135 	flags = DLADM_LINK_FORCED;
1136 	if (arg != NULL)
1137 		flags |= DLADM_LINK_TEMP;
1138 
1139 	(void) dladm_link(dlattr.da_dev, &dlattr, flags, NULL, &diag);
1140 }
1141 
1142 /* ARGSUSED */
1143 static void
1144 do_init_link(int argc, char *argv[])
1145 {
1146 	if (argc != 1 && strcmp(argv[1], "-t") != 0)
1147 		usage();
1148 
1149 	(void) macadm_walk(create, (void *)(argc > 1), B_FALSE);
1150 }
1151 
1152 #define	TYPE_WIDTH	10
1153 #define	MAC_WIDTH	23
1154 #define	MTU_WIDTH	11
1155 #define	STATE_WIDTH	15
1156 
1157 /* ARGSUSED */
1158 static void
1159 show_link(void *arg, const char *name)
1160 {
1161 	dladm_attr_t	dlattr;
1162 	char		type[TYPE_WIDTH];
1163 	int		fd;
1164 	dlpi_if_attr_t	dia;
1165 	dl_info_ack_t	dlia;
1166 	t_uscalar_t	dl_max_sdu;
1167 	show_link_state_t *state = (show_link_state_t *)arg;
1168 
1169 	if ((fd = dlpi_if_open(name, &dia, B_FALSE)) != -1 &&
1170 	    dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL,
1171 	    NULL, NULL) != -1) {
1172 		(void) dlpi_close(fd);
1173 		dl_max_sdu = dlia.dl_max_sdu;
1174 	} else {
1175 		(void) fprintf(stderr,
1176 		    gettext("%s: invalid device '%s'\n"),
1177 		    progname, name);
1178 		exit(1);
1179 	}
1180 
1181 	if (dladm_info(name, &dlattr) < 0) {
1182 		if (!state->ls_parseable) {
1183 			(void) printf(gettext("%-9s\ttype: legacy"
1184 			    "\tmtu: %d\tdevice: %s\n"),
1185 			    name, (int)dl_max_sdu, name);
1186 		} else {
1187 			(void) printf("%s type=legacy mtu=%d device=%s\n",
1188 			    name, (int)dl_max_sdu, name);
1189 		}
1190 	} else {
1191 		if (dlattr.da_vid != 0) {
1192 			(void) snprintf(type, TYPE_WIDTH,
1193 			    state->ls_parseable ? "vlan %u" :
1194 			    gettext("vlan %u"), dlattr.da_vid);
1195 		} else {
1196 			(void) snprintf(type, TYPE_WIDTH,
1197 			    state->ls_parseable ? "non-vlan" :
1198 			    gettext("non-vlan"));
1199 		}
1200 
1201 		if (strcmp(dlattr.da_dev, AGGR_DEV) == 0) {
1202 			if (!state->ls_parseable) {
1203 				(void) printf(gettext("%-9s\ttype: %s"
1204 				    "\tmtu: %d\taggregation: key %u\n"),
1205 				    name, type, (int)dl_max_sdu,
1206 				    dlattr.da_port);
1207 			} else {
1208 				(void) printf("%s type=%s mtu=%d key=%u\n",
1209 				    name, type, (int)dl_max_sdu,
1210 				    dlattr.da_port);
1211 			}
1212 		} else {
1213 			if (!state->ls_parseable) {
1214 				(void) printf(gettext("%-9s\ttype: %s"
1215 				    "\tmtu: %d\tdevice: %s\n"),
1216 				    name, type, (int)dl_max_sdu,
1217 				    dlattr.da_dev);
1218 			} else {
1219 				(void) printf("%s type=%s mtu=%d device=%s\n",
1220 				    name, type, (int)dl_max_sdu, dlattr.da_dev);
1221 			}
1222 		}
1223 	}
1224 }
1225 
1226 static void
1227 show_link_stats(void *arg, const char *name)
1228 {
1229 	show_link_state_t *state = (show_link_state_t *)arg;
1230 	pktsum_t stats, diff_stats;
1231 
1232 	if (state->ls_firstonly) {
1233 		if (state->ls_donefirst)
1234 			return;
1235 		state->ls_donefirst = B_TRUE;
1236 	} else {
1237 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
1238 	}
1239 
1240 	get_link_stats(name, &stats);
1241 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
1242 
1243 	(void) printf("%s", name);
1244 	(void) printf("\t\t%-10llu", diff_stats.ipackets);
1245 	(void) printf("%-12llu", diff_stats.rbytes);
1246 	(void) printf("%-8u", diff_stats.ierrors);
1247 	(void) printf("%-10llu", diff_stats.opackets);
1248 	(void) printf("%-12llu", diff_stats.obytes);
1249 	(void) printf("%-8u\n", diff_stats.oerrors);
1250 
1251 	state->ls_prevstats = stats;
1252 }
1253 
1254 static void
1255 dump_grp(laadm_grp_attr_sys_t	*grp, boolean_t parseable)
1256 {
1257 	char policy_str[LAADM_POLICY_STR_LEN];
1258 	char addr_str[ETHERADDRL * 3];
1259 
1260 	if (!parseable) {
1261 		(void) printf(gettext("key: %d (0x%04x)"),
1262 		    grp->lg_key, grp->lg_key);
1263 
1264 		(void) printf(gettext("\tpolicy: %s"),
1265 		    laadm_policy_to_str(grp->lg_policy, policy_str));
1266 
1267 		(void) printf(gettext("\taddress: %s (%s)\n"),
1268 		    laadm_mac_addr_to_str(grp->lg_mac, addr_str),
1269 		    (grp->lg_mac_fixed) ? gettext("fixed") : gettext("auto"));
1270 	} else {
1271 		(void) printf("aggr key=%d", grp->lg_key);
1272 
1273 		(void) printf(" policy=%s",
1274 		    laadm_policy_to_str(grp->lg_policy, policy_str));
1275 
1276 		(void) printf(" address=%s",
1277 		    laadm_mac_addr_to_str(grp->lg_mac, addr_str));
1278 
1279 		(void) printf(" address-type=%s\n",
1280 		    (grp->lg_mac_fixed) ? "fixed" : "auto");
1281 	}
1282 }
1283 
1284 static void
1285 dump_grp_lacp(laadm_grp_attr_sys_t *grp, boolean_t parseable)
1286 {
1287 	const char *lacp_mode_str = laadm_lacp_mode_to_str(grp->lg_lacp_mode);
1288 	const char *lacp_timer_str =
1289 	    laadm_lacp_timer_to_str(grp->lg_lacp_timer);
1290 
1291 	if (!parseable) {
1292 		(void) printf(gettext("\t\tLACP mode: %s"), lacp_mode_str);
1293 		(void) printf(gettext("\tLACP timer: %s\n"), lacp_timer_str);
1294 	} else {
1295 		(void) printf(" lacp-mode=%s", lacp_mode_str);
1296 		(void) printf(" lacp-timer=%s\n", lacp_timer_str);
1297 	}
1298 }
1299 
1300 static void
1301 dump_grp_stats(laadm_grp_attr_sys_t *grp)
1302 {
1303 	(void) printf("key: %d", grp->lg_key);
1304 	(void) printf("\tipackets  rbytes      opackets	 obytes		 ");
1305 	(void) printf("%%ipkts	%%opkts\n");
1306 }
1307 
1308 static void
1309 dump_ports_lacp_head(void)
1310 {
1311 	(void) printf(DUMP_LACP_FORMAT, gettext("device"), gettext("activity"),
1312 	    gettext("timeout"), gettext("aggregatable"), gettext("sync"),
1313 	    gettext("coll"), gettext("dist"), gettext("defaulted"),
1314 	    gettext("expired"));
1315 }
1316 
1317 static void
1318 dump_ports_head(void)
1319 {
1320 	(void) printf(gettext("	   device\taddress\t\t	speed\t\tduplex\tlink\t"
1321 	    "state\n"));
1322 }
1323 
1324 static char *
1325 port_state_to_str(aggr_port_state_t state_num)
1326 {
1327 	int			i;
1328 	port_state_t		*state;
1329 
1330 	for (i = 0; i < NPORTSTATES; i++) {
1331 		state = &port_states[i];
1332 		if (state->state_num == state_num)
1333 			return (state->state_name);
1334 	}
1335 
1336 	return ("unknown");
1337 }
1338 
1339 static void
1340 dump_port(laadm_port_attr_sys_t *port, boolean_t parseable)
1341 {
1342 	char *dev = port->lp_devname;
1343 	uint_t portnum = port->lp_port;
1344 	char buf[ETHERADDRL * 3];
1345 
1346 	if (!parseable) {
1347 		(void) printf("	   %-9s\t%s", dev, laadm_mac_addr_to_str(
1348 		    port->lp_mac, buf));
1349 		(void) printf("\t  %-5u Mbps", (int)(mac_ifspeed(dev, portnum) /
1350 		    1000000ull));
1351 		(void) printf("\t%s", mac_link_duplex(dev, portnum));
1352 		(void) printf("\t%s", mac_link_state(dev, portnum));
1353 		(void) printf("\t%s\n", port_state_to_str(port->lp_state));
1354 
1355 	} else {
1356 		(void) printf(" device=%s address=%s", dev,
1357 		    laadm_mac_addr_to_str(port->lp_mac, buf));
1358 		(void) printf(" speed=%u", (int)(mac_ifspeed(dev, portnum) /
1359 		    1000000ull));
1360 		(void) printf(" duplex=%s", mac_link_duplex(dev, portnum));
1361 		(void) printf(" link=%s", mac_link_state(dev, portnum));
1362 		(void) printf(" port=%s", port_state_to_str(port->lp_state));
1363 	}
1364 }
1365 
1366 static void
1367 dump_port_lacp(laadm_port_attr_sys_t *port)
1368 {
1369 	aggr_lacp_state_t *state = &port->lp_lacp_state;
1370 
1371 	(void) printf(DUMP_LACP_FORMAT,
1372 	    port->lp_devname, state->bit.activity ? "active" : "passive",
1373 	    state->bit.timeout ? "short" : "long",
1374 	    state->bit.aggregation ? "yes" : "no",
1375 	    state->bit.sync ? "yes" : "no",
1376 	    state->bit.collecting ? "yes" : "no",
1377 	    state->bit.distributing ? "yes" : "no",
1378 	    state->bit.defaulted ? "yes" : "no",
1379 	    state->bit.expired ? "yes" : "no");
1380 }
1381 
1382 static void
1383 dump_port_stat(int index, show_grp_state_t *state, pktsum_t *port_stats,
1384     pktsum_t *tot_stats)
1385 {
1386 	pktsum_t	diff_stats;
1387 	pktsum_t	*old_stats = &state->gs_prevstats[index];
1388 
1389 	stats_diff(&diff_stats, port_stats, old_stats);
1390 
1391 	(void) printf("\t%-10llu", diff_stats.ipackets);
1392 	(void) printf("%-12llu", diff_stats.rbytes);
1393 	(void) printf("%-10llu", diff_stats.opackets);
1394 	(void) printf("%-12llu", diff_stats.obytes);
1395 
1396 	if (tot_stats->ipackets == 0)
1397 		(void) printf("\t-");
1398 	else
1399 		(void) printf("\t%-6.1f", (double)diff_stats.ipackets/
1400 		    (double)tot_stats->ipackets * 100);
1401 
1402 	if (tot_stats->opackets == 0)
1403 		(void) printf("\t-");
1404 	else
1405 		(void) printf("\t%-6.1f", (double)diff_stats.opackets/
1406 		    (double)tot_stats->opackets * 100);
1407 
1408 	(void) printf("\n");
1409 
1410 	*old_stats = *port_stats;
1411 }
1412 
1413 static int
1414 show_key(void *arg, laadm_grp_attr_sys_t *grp)
1415 {
1416 	show_grp_state_t	*state = (show_grp_state_t *)arg;
1417 	int			i;
1418 	pktsum_t		pktsumtot, port_stat;
1419 
1420 	if (state->gs_key != 0 && state->gs_key != grp->lg_key)
1421 		return (0);
1422 	if (state->gs_firstonly) {
1423 		if (state->gs_found)
1424 			return (0);
1425 	} else {
1426 		bzero(&state->gs_prevstats, sizeof (state->gs_prevstats));
1427 	}
1428 
1429 	state->gs_found = B_TRUE;
1430 
1431 	if (state->gs_stats) {
1432 		/* show statistics */
1433 		dump_grp_stats(grp);
1434 
1435 		/* sum the ports statistics */
1436 		bzero(&pktsumtot, sizeof (pktsumtot));
1437 		for (i = 0; i < grp->lg_nports; i++) {
1438 			get_mac_stats(grp->lg_ports[i].lp_devname,
1439 			    grp->lg_ports[i].lp_port, &port_stat);
1440 			stats_total(&pktsumtot, &port_stat,
1441 			    &state->gs_prevstats[i]);
1442 		}
1443 
1444 		(void) printf("	   Total");
1445 		(void) printf("\t%-10llu", pktsumtot.ipackets);
1446 		(void) printf("%-12llu", pktsumtot.rbytes);
1447 		(void) printf("%-10llu", pktsumtot.opackets);
1448 		(void) printf("%-12llu\n", pktsumtot.obytes);
1449 
1450 		for (i = 0; i < grp->lg_nports; i++) {
1451 			get_mac_stats(grp->lg_ports[i].lp_devname,
1452 			    grp->lg_ports[i].lp_port, &port_stat);
1453 			(void) printf("	   %s", grp->lg_ports[i].lp_devname);
1454 			dump_port_stat(i, state, &port_stat, &pktsumtot);
1455 		}
1456 	} else if (state->gs_lacp) {
1457 		/* show LACP info */
1458 		dump_grp(grp, state->gs_parseable);
1459 		dump_grp_lacp(grp, state->gs_parseable);
1460 		dump_ports_lacp_head();
1461 		for (i = 0; i < grp->lg_nports; i++)
1462 			dump_port_lacp(&grp->lg_ports[i]);
1463 	} else {
1464 		dump_grp(grp, state->gs_parseable);
1465 		if (!state->gs_parseable)
1466 			dump_ports_head();
1467 		for (i = 0; i < grp->lg_nports; i++) {
1468 			if (state->gs_parseable)
1469 				(void) printf("dev key=%d", grp->lg_key);
1470 			dump_port(&grp->lg_ports[i], state->gs_parseable);
1471 			if (state->gs_parseable)
1472 				(void) printf("\n");
1473 		}
1474 	}
1475 
1476 	return (0);
1477 }
1478 
1479 static int
1480 kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
1481 {
1482 	kstat_named_t	*knp;
1483 
1484 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
1485 		return (-1);
1486 
1487 	if (knp->data_type != type)
1488 		return (-1);
1489 
1490 	switch (type) {
1491 	case KSTAT_DATA_UINT64:
1492 		*(uint64_t *)buf = knp->value.ui64;
1493 		break;
1494 	case KSTAT_DATA_UINT32:
1495 		*(uint32_t *)buf = knp->value.ui32;
1496 		break;
1497 	default:
1498 		return (-1);
1499 	}
1500 
1501 	return (0);
1502 }
1503 
1504 static void
1505 show_dev(void *arg, const char *dev, uint_t port)
1506 {
1507 	show_mac_state_t	*state = (show_mac_state_t *)arg;
1508 
1509 	/* aggregations are already managed by a set of subcommands */
1510 	if (strcmp(dev, AGGR_DEV) == 0)
1511 		return;
1512 
1513 	(void) printf("%s", dev);
1514 	if (port != 0)
1515 		(void) printf("/%d", port);
1516 
1517 	if (!state->ms_parseable) {
1518 		(void) printf(gettext("\t\tlink: %s"),
1519 		    mac_link_state(dev, port));
1520 		(void) printf(gettext("\tspeed: %-5u Mbps"),
1521 		    (unsigned int)(mac_ifspeed(dev, port) / 1000000ull));
1522 		(void) printf(gettext("\tduplex: %s\n"),
1523 		    mac_link_duplex(dev, port));
1524 	} else {
1525 		(void) printf(" link=%s", mac_link_state(dev, port));
1526 		(void) printf(" speed=%u",
1527 		    (unsigned int)(mac_ifspeed(dev, port) / 1000000ull));
1528 		(void) printf(" duplex=%s\n", mac_link_duplex(dev, port));
1529 	}
1530 }
1531 
1532 /*ARGSUSED*/
1533 static void
1534 show_dev_stats(void *arg, const char *dev, uint_t port)
1535 {
1536 	show_mac_state_t *state = (show_mac_state_t *)arg;
1537 	pktsum_t stats, diff_stats;
1538 
1539 	/* aggregations are already managed by a set of subcommands */
1540 	if (strcmp(dev, AGGR_DEV) == 0)
1541 		return;
1542 
1543 	if (state->ms_firstonly) {
1544 		if (state->ms_donefirst)
1545 			return;
1546 		state->ms_donefirst = B_TRUE;
1547 	} else {
1548 		bzero(&state->ms_prevstats, sizeof (state->ms_prevstats));
1549 	}
1550 
1551 	get_mac_stats(dev, port, &stats);
1552 	stats_diff(&diff_stats, &stats, &state->ms_prevstats);
1553 
1554 	(void) printf("%s", dev);
1555 	if (port != 0)
1556 		(void) printf("/%d", port);
1557 	(void) printf("\t\t%-10llu", diff_stats.ipackets);
1558 	(void) printf("%-12llu", diff_stats.rbytes);
1559 	(void) printf("%-8u", diff_stats.ierrors);
1560 	(void) printf("%-10llu", diff_stats.opackets);
1561 	(void) printf("%-12llu", diff_stats.obytes);
1562 	(void) printf("%-8u\n", diff_stats.oerrors);
1563 
1564 	state->ms_prevstats = stats;
1565 }
1566 
1567 static void
1568 do_show_link(int argc, char *argv[])
1569 {
1570 	char		*name = NULL;
1571 	int		option;
1572 	boolean_t	s_arg = B_FALSE;
1573 	boolean_t	i_arg = B_FALSE;
1574 	uint32_t	interval = 0;
1575 	show_link_state_t state;
1576 	char		*endp = NULL;
1577 
1578 	state.ls_stats = B_FALSE;
1579 	state.ls_parseable = B_FALSE;
1580 
1581 	opterr = 0;
1582 	while ((option = getopt_long(argc, argv, ":psi:",
1583 	    longopts, NULL)) != -1) {
1584 		switch (option) {
1585 		case 'p':
1586 			state.ls_parseable = B_TRUE;
1587 			break;
1588 		case 's':
1589 			if (s_arg) {
1590 				(void) fprintf(stderr, gettext(
1591 				    "%s: the option -s cannot be specified "
1592 				    "more than once\n"), progname);
1593 				usage();
1594 			}
1595 
1596 			s_arg = B_TRUE;
1597 			break;
1598 		case 'i':
1599 			if (i_arg) {
1600 				(void) fprintf(stderr, gettext(
1601 				    "%s: the option -i cannot be specified "
1602 				    "more than once\n"), progname);
1603 				usage();
1604 			}
1605 
1606 			i_arg = B_TRUE;
1607 
1608 			errno = 0;
1609 			interval = (int)strtol(optarg, &endp, 10);
1610 			if (errno != 0 || interval == 0 || *endp != '\0') {
1611 				(void) fprintf(stderr,
1612 				    gettext("%s: invalid interval value"
1613 				    " '%d'\n"),
1614 				    progname, interval);
1615 				exit(1);
1616 			}
1617 			break;
1618 		case ':':
1619 			(void) fprintf(stderr,
1620 			    gettext("%s: option requires a value '-%c'\n"),
1621 			    progname, optopt);
1622 			exit(1);
1623 			/*NOTREACHED*/
1624 		case '?':
1625 		default:
1626 			(void) fprintf(stderr,
1627 			    gettext("%s: unrecognized option '-%c'\n"),
1628 			    progname, optopt);
1629 			exit(1);
1630 		}
1631 	}
1632 
1633 	if (i_arg && !s_arg) {
1634 		(void) fprintf(stderr, gettext("%s: the option -i "
1635 		    "can be used only with -s\n"), progname);
1636 		usage();
1637 	}
1638 
1639 
1640 	/* get link name (optional last argument) */
1641 	if (optind == (argc-1))
1642 		name = argv[optind];
1643 	else if (optind != argc)
1644 		usage();
1645 
1646 	if (s_arg) {
1647 		link_stats(name, interval);
1648 		return;
1649 	}
1650 
1651 	if (name == NULL)
1652 		(void) dladm_walk(show_link, &state);
1653 	else
1654 		show_link(&state, name);
1655 }
1656 
1657 static void
1658 do_show_aggr(int argc, char *argv[])
1659 {
1660 	int			option;
1661 	uint16_t		key = 0;
1662 	boolean_t		L_arg = B_FALSE;
1663 	boolean_t		s_arg = B_FALSE;
1664 	boolean_t		i_arg = B_FALSE;
1665 	show_grp_state_t	state;
1666 	uint32_t		interval = 0;
1667 	char			*endp = NULL;
1668 
1669 	state.gs_stats = B_FALSE;
1670 	state.gs_lacp = B_FALSE;
1671 	state.gs_parseable = B_FALSE;
1672 
1673 	opterr = 0;
1674 	while ((option = getopt_long(argc, argv, ":Lpsi:",
1675 	    longopts, NULL)) != -1) {
1676 		switch (option) {
1677 		case 'L':
1678 			if (L_arg) {
1679 				(void) fprintf(stderr, gettext(
1680 				    "%s: the option -L cannot be specified "
1681 				    "more than once\n"), progname);
1682 				usage();
1683 			}
1684 
1685 			if (s_arg || i_arg) {
1686 				(void) fprintf(stderr, gettext(
1687 				    "%s: the option -L cannot be used with "
1688 				    "any of -is\n"), progname);
1689 				usage();
1690 			}
1691 
1692 			L_arg = B_TRUE;
1693 
1694 			state.gs_lacp = B_TRUE;
1695 			break;
1696 		case 'p':
1697 			state.gs_parseable = B_TRUE;
1698 			break;
1699 		case 's':
1700 			if (s_arg) {
1701 				(void) fprintf(stderr, gettext(
1702 				    "%s: the option -s cannot be specified "
1703 				    "more than once\n"), progname);
1704 				usage();
1705 			}
1706 
1707 			if (L_arg) {
1708 				(void) fprintf(stderr, gettext(
1709 				    "%s: the option -L cannot be used "
1710 				    "with -k\n"), progname);
1711 				usage();
1712 			}
1713 
1714 			s_arg = B_TRUE;
1715 			break;
1716 		case 'i':
1717 			if (i_arg) {
1718 				(void) fprintf(stderr, gettext(
1719 				    "%s: the option -i cannot be specified "
1720 				    "more than once\n"), progname);
1721 				usage();
1722 			}
1723 
1724 			if (L_arg) {
1725 				(void) fprintf(stderr, gettext(
1726 				    "%s: the option -i cannot be used "
1727 				    "with -L\n"), progname);
1728 				usage();
1729 			}
1730 
1731 			i_arg = B_TRUE;
1732 
1733 			errno = 0;
1734 			interval = (int)strtol(optarg, &endp, 10);
1735 			if (errno != 0 || interval == 0 || *endp != '\0') {
1736 				(void) fprintf(stderr,
1737 				    gettext("%s: invalid interval value"
1738 				    " '%d'\n"),
1739 				    progname, interval);
1740 				exit(1);
1741 			}
1742 			break;
1743 		case ':':
1744 			(void) fprintf(stderr,
1745 			    gettext("%s: option requires a value '-%c'\n"),
1746 			    progname, optopt);
1747 			exit(1);
1748 			/*NOTREACHED*/
1749 		case '?':
1750 		default:
1751 			(void) fprintf(stderr,
1752 			    gettext("%s: unrecognized option '-%c'\n"),
1753 			    progname, optopt);
1754 			exit(1);
1755 		}
1756 	}
1757 
1758 	if (i_arg && !s_arg) {
1759 		(void) fprintf(stderr, gettext("%s: the option -i "
1760 		    "can be used only with -s\n"), progname);
1761 		usage();
1762 	}
1763 
1764 	/* get aggregation key (optional last argument) */
1765 	if (optind == (argc-1)) {
1766 		errno = 0;
1767 		key = (int)strtol(argv[optind], &endp, 10);
1768 		if (errno != 0 || key < 1 || *endp != '\0') {
1769 			(void) fprintf(stderr,
1770 			    gettext("%s: illegal key value '%d'\n"),
1771 			    progname, key);
1772 			exit(1);
1773 		}
1774 	} else if (optind != argc) {
1775 		usage();
1776 	}
1777 
1778 	if (s_arg) {
1779 		aggr_stats(key, interval);
1780 		return;
1781 	}
1782 
1783 	state.gs_key = key;
1784 	state.gs_found = B_FALSE;
1785 
1786 	(void) laadm_walk_sys(show_key, &state);
1787 
1788 	if (key != 0 && !state.gs_found) {
1789 		(void) fprintf(stderr,
1790 		    gettext("%s: non-existent aggregation key '%u'\n"),
1791 		    progname, key);
1792 		exit(1);
1793 	}
1794 }
1795 
1796 static void
1797 do_show_dev(int argc, char *argv[])
1798 {
1799 	int		option;
1800 	char		*dev = NULL;
1801 	boolean_t	s_arg = B_FALSE;
1802 	boolean_t	i_arg = B_FALSE;
1803 	uint32_t	interval = 0;
1804 	show_mac_state_t state;
1805 	char		*endp = NULL;
1806 
1807 	state.ms_parseable = B_FALSE;
1808 
1809 	opterr = 0;
1810 	while ((option = getopt_long(argc, argv, ":psi:",
1811 	    longopts, NULL)) != -1) {
1812 		switch (option) {
1813 		case 'p':
1814 			state.ms_parseable = B_TRUE;
1815 			break;
1816 		case 's':
1817 			if (s_arg) {
1818 				(void) fprintf(stderr, gettext(
1819 				    "%s: the option -s cannot be specified "
1820 				    "more than once\n"), progname);
1821 				usage();
1822 			}
1823 
1824 			s_arg = B_TRUE;
1825 			break;
1826 		case 'i':
1827 			if (i_arg) {
1828 				(void) fprintf(stderr, gettext(
1829 				    "%s: the option -i cannot be specified "
1830 				    "more than once\n"), progname);
1831 				usage();
1832 			}
1833 
1834 			i_arg = B_TRUE;
1835 
1836 			errno = 0;
1837 			interval = (int)strtol(optarg, &endp, 10);
1838 			if (errno != 0 || interval == 0 || *endp != '\0') {
1839 				(void) fprintf(stderr,
1840 				    gettext("%s: invalid interval value"
1841 				    " '%d'\n"),
1842 				    progname, interval);
1843 				exit(1);
1844 			}
1845 			break;
1846 		case ':':
1847 			(void) fprintf(stderr,
1848 			    gettext("%s: option requires a value '-%c'\n"),
1849 			    progname, optopt);
1850 			exit(1);
1851 			/*NOTREACHED*/
1852 		case '?':
1853 		default:
1854 			(void) fprintf(stderr,
1855 			    gettext("%s: unrecognized option '-%c'\n"),
1856 			    progname, optopt);
1857 			exit(1);
1858 		}
1859 	}
1860 
1861 	if (i_arg && !s_arg) {
1862 		(void) fprintf(stderr, gettext("%s: the option -i "
1863 		    "can be used only with -s\n"), progname);
1864 		usage();
1865 	}
1866 
1867 	/* get dev name (optional last argument) */
1868 	if (optind == (argc-1))
1869 		dev = argv[optind];
1870 	else if (optind != argc)
1871 		usage();
1872 
1873 	if ((dev != NULL) && (strcmp(dev, AGGR_DEV) == 0)) {
1874 		/* aggregations are already managed by a set of subcommands */
1875 		(void) fprintf(stderr,
1876 		    gettext("%s: non-existant device '%s'\n"),
1877 		    progname, dev);
1878 		exit(1);
1879 	}
1880 
1881 	if (s_arg) {
1882 		dev_stats(dev, interval);
1883 		return;
1884 	}
1885 
1886 	if (dev == NULL)
1887 		(void) macadm_walk(show_dev, &state, B_TRUE);
1888 	else
1889 		show_dev(&state, dev, 0);
1890 }
1891 
1892 /* ARGSUSED */
1893 static void
1894 link_stats(const char *link, uint32_t interval)
1895 {
1896 	show_link_state_t state;
1897 
1898 	if (link != NULL) {
1899 		dlpi_if_attr_t dia;
1900 		int fd;
1901 
1902 		if ((fd = dlpi_if_open(link, &dia, B_FALSE)) != -1) {
1903 			(void) dlpi_close(fd);
1904 		} else {
1905 			(void) fprintf(stderr,
1906 			    gettext("%s: invalid device '%s'\n"),
1907 			    progname, link);
1908 			exit(1);
1909 		}
1910 	}
1911 
1912 	bzero(&state, sizeof (state));
1913 
1914 	/*
1915 	 * If an interval is specified, continuously show the stats
1916 	 * only for the first MAC port.
1917 	 */
1918 	state.ls_firstonly = (interval != 0);
1919 
1920 	for (;;) {
1921 		(void) printf("\t\tipackets  rbytes	 ierrors ");
1922 		(void) printf("opackets	 obytes	     oerrors\n");
1923 
1924 		state.ls_donefirst = B_FALSE;
1925 		if (link == NULL)
1926 			(void) dladm_walk(show_link_stats, &state);
1927 		else
1928 			show_link_stats(&state, link);
1929 
1930 		if (interval == 0)
1931 			break;
1932 
1933 		(void) sleep(interval);
1934 	}
1935 
1936 }
1937 
1938 /* ARGSUSED */
1939 static void
1940 aggr_stats(uint16_t key, uint32_t interval)
1941 {
1942 	show_grp_state_t state;
1943 
1944 	bzero(&state, sizeof (state));
1945 	state.gs_stats = B_TRUE;
1946 	state.gs_key = key;
1947 
1948 	/*
1949 	 * If an interval is specified, continuously show the stats
1950 	 * only for the first group.
1951 	 */
1952 	state.gs_firstonly = (interval != 0);
1953 
1954 	for (;;) {
1955 		state.gs_found = B_FALSE;
1956 		(void) laadm_walk_sys(show_key, &state);
1957 		if (state.gs_key != 0 && !state.gs_found) {
1958 			(void) fprintf(stderr,
1959 			    gettext("%s: non-existent aggregation key '%u'\n"),
1960 			    progname, key);
1961 			exit(1);
1962 		}
1963 
1964 		if (interval == 0)
1965 			break;
1966 
1967 		(void) sleep(interval);
1968 	}
1969 }
1970 
1971 /* ARGSUSED */
1972 static void
1973 dev_stats(const char *dev, uint32_t interval)
1974 {
1975 	show_mac_state_t state;
1976 
1977 	bzero(&state, sizeof (state));
1978 
1979 	/*
1980 	 * If an interval is specified, continuously show the stats
1981 	 * only for the first MAC port.
1982 	 */
1983 	state.ms_firstonly = (interval != 0);
1984 
1985 	for (;;) {
1986 
1987 		(void) printf("\t\tipackets  rbytes	 ierrors ");
1988 		(void) printf("opackets	 obytes	     oerrors\n");
1989 
1990 		state.ms_donefirst = B_FALSE;
1991 		if (dev == NULL) {
1992 			(void) macadm_walk(show_dev_stats, &state, B_TRUE);
1993 		} else {
1994 			show_dev_stats(&state, dev, 0);
1995 		}
1996 
1997 		if (interval == 0)
1998 			break;
1999 
2000 		(void) sleep(interval);
2001 	}
2002 }
2003 
2004 /* accumulate stats (s1 += (s2 - s3)) */
2005 static void
2006 stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
2007 {
2008 	s1->ipackets += (s2->ipackets - s3->ipackets);
2009 	s1->opackets += (s2->opackets - s3->opackets);
2010 	s1->rbytes += (s2->rbytes - s3->rbytes);
2011 	s1->obytes += (s2->obytes - s3->obytes);
2012 	s1->ierrors += (s2->ierrors - s3->ierrors);
2013 	s1->oerrors += (s2->oerrors - s3->oerrors);
2014 }
2015 
2016 /* compute stats differences (s1 = s2 - s3) */
2017 static void
2018 stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
2019 {
2020 	s1->ipackets = s2->ipackets - s3->ipackets;
2021 	s1->opackets = s2->opackets - s3->opackets;
2022 	s1->rbytes = s2->rbytes - s3->rbytes;
2023 	s1->obytes = s2->obytes - s3->obytes;
2024 	s1->ierrors = s2->ierrors - s3->ierrors;
2025 	s1->oerrors = s2->oerrors - s3->oerrors;
2026 }
2027 
2028 static void
2029 get_stats(char *module, int instance, char *name, pktsum_t *stats)
2030 {
2031 	kstat_ctl_t	*kcp;
2032 	kstat_t		*ksp;
2033 
2034 	if ((kcp = kstat_open()) == NULL) {
2035 		(void) fprintf(stderr,
2036 		    gettext("%s: kstat open operation failed\n"),
2037 		    progname);
2038 		return;
2039 	}
2040 
2041 	if ((ksp = kstat_lookup(kcp, module, instance, name)) == NULL) {
2042 		/*
2043 		 * The kstat query could fail if the underlying MAC
2044 		 * driver was already detached.
2045 		 */
2046 		(void) kstat_close(kcp);
2047 		return;
2048 	}
2049 
2050 	if (kstat_read(kcp, ksp, NULL) == -1)
2051 		goto bail;
2052 
2053 	if (kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
2054 	    &stats->ipackets) < 0)
2055 		goto bail;
2056 
2057 	if (kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
2058 	    &stats->opackets) < 0)
2059 		goto bail;
2060 
2061 	if (kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
2062 	    &stats->rbytes) < 0)
2063 		goto bail;
2064 
2065 	if (kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
2066 	    &stats->obytes) < 0)
2067 		goto bail;
2068 
2069 	if (kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
2070 	    &stats->ierrors) < 0)
2071 		goto bail;
2072 
2073 	if (kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
2074 	    &stats->oerrors) < 0)
2075 		goto bail;
2076 
2077 	(void) kstat_close(kcp);
2078 	return;
2079 
2080 bail:
2081 	(void) fprintf(stderr,
2082 	    gettext("%s: kstat operation failed\n"),
2083 	    progname);
2084 	(void) kstat_close(kcp);
2085 }
2086 
2087 static void
2088 get_mac_stats(const char *dev, uint_t port, pktsum_t *stats)
2089 {
2090 	char			name[MAXNAMELEN];
2091 
2092 	bzero(stats, sizeof (*stats));
2093 
2094 	(void) snprintf(name, MAXNAMELEN - 1, "%s/%u", dev, port);
2095 	get_stats((char *)dev, 0, name, stats);
2096 }
2097 
2098 static void
2099 get_link_stats(const char *link, pktsum_t *stats)
2100 {
2101 	bzero(stats, sizeof (*stats));
2102 	get_stats(NULL, -1, (char *)link, stats);
2103 }
2104 
2105 static uint64_t
2106 mac_ifspeed(const char *dev, uint_t port)
2107 {
2108 	char		name[MAXNAMELEN];
2109 	kstat_ctl_t	*kcp;
2110 	kstat_t		*ksp;
2111 	uint64_t	ifspeed = 0;
2112 
2113 	if ((kcp = kstat_open()) == NULL) {
2114 		(void) fprintf(stderr,
2115 		    gettext("%s: kstat open operation failed\n"),
2116 		    progname);
2117 		return (0);
2118 	}
2119 
2120 	(void) snprintf(name, MAXNAMELEN - 1, "%s/%u", dev, port);
2121 	if ((ksp = kstat_lookup(kcp, (char *)dev, 0, name)) == NULL) {
2122 		/*
2123 		 * The kstat query could fail if the underlying MAC
2124 		 * driver was already detached.
2125 		 */
2126 		goto bail;
2127 	}
2128 
2129 	if (kstat_read(kcp, ksp, NULL) == -1) {
2130 		(void) fprintf(stderr,
2131 		    gettext("%s: kstat read failed\n"),
2132 		    progname);
2133 		goto bail;
2134 	}
2135 
2136 	if (kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, &ifspeed) < 0) {
2137 		(void) fprintf(stderr,
2138 		    gettext("%s: kstat value failed\n"),
2139 		    progname);
2140 		goto bail;
2141 	}
2142 
2143 bail:
2144 	(void) kstat_close(kcp);
2145 	return (ifspeed);
2146 }
2147 
2148 static char *
2149 mac_link_state(const char *dev, uint_t port)
2150 {
2151 	char		name[MAXNAMELEN];
2152 	kstat_ctl_t	*kcp;
2153 	kstat_t		*ksp;
2154 	link_state_t	link_state;
2155 	char		*state_str = "unknown";
2156 
2157 	if ((kcp = kstat_open()) == NULL) {
2158 		(void) fprintf(stderr,
2159 		    gettext("%s: kstat open operation failed\n"),
2160 		    progname);
2161 		return (state_str);
2162 	}
2163 
2164 	(void) snprintf(name, MAXNAMELEN - 1, "%s/%u", dev, port);
2165 	if ((ksp = kstat_lookup(kcp, (char *)dev, 0, name)) == NULL) {
2166 		/*
2167 		 * The kstat query could fail if the underlying MAC
2168 		 * driver was already detached.
2169 		 */
2170 		goto bail;
2171 	}
2172 
2173 	if (kstat_read(kcp, ksp, NULL) == -1) {
2174 		(void) fprintf(stderr,
2175 		    gettext("%s: kstat read failed\n"),
2176 		    progname);
2177 		goto bail;
2178 	}
2179 
2180 	if (kstat_value(ksp, "link_state", KSTAT_DATA_UINT32,
2181 	    &link_state) < 0) {
2182 		goto bail;
2183 	}
2184 
2185 	switch (link_state) {
2186 	case LINK_STATE_UP:
2187 		state_str = "up";
2188 		break;
2189 	case LINK_STATE_DOWN:
2190 		state_str = "down";
2191 		break;
2192 	default:
2193 		break;
2194 	}
2195 
2196 bail:
2197 	(void) kstat_close(kcp);
2198 	return (state_str);
2199 }
2200 
2201 
2202 static char *
2203 mac_link_duplex(const char *dev, uint_t port)
2204 {
2205 	char		name[MAXNAMELEN];
2206 	kstat_ctl_t	*kcp;
2207 	kstat_t		*ksp;
2208 	link_duplex_t	link_duplex;
2209 	char		*duplex_str = "unknown";
2210 
2211 	if ((kcp = kstat_open()) == NULL) {
2212 		(void) fprintf(stderr,
2213 		    gettext("%s: kstat open operation failed\n"),
2214 		    progname);
2215 		return (duplex_str);
2216 	}
2217 
2218 	(void) snprintf(name, MAXNAMELEN - 1, "%s/%u", dev, port);
2219 	if ((ksp = kstat_lookup(kcp, (char *)dev, 0, name)) == NULL) {
2220 		/*
2221 		 * The kstat query could fail if the underlying MAC
2222 		 * driver was already detached.
2223 		 */
2224 		goto bail;
2225 	}
2226 
2227 	if (kstat_read(kcp, ksp, NULL) == -1) {
2228 		(void) fprintf(stderr,
2229 		    gettext("%s: kstat read failed\n"),
2230 		    progname);
2231 		goto bail;
2232 	}
2233 
2234 	if (kstat_value(ksp, "link_duplex", KSTAT_DATA_UINT32,
2235 	    &link_duplex) < 0) {
2236 		goto bail;
2237 	}
2238 	switch (link_duplex) {
2239 	case LINK_DUPLEX_FULL:
2240 		duplex_str = "full";
2241 		break;
2242 	case LINK_DUPLEX_HALF:
2243 		duplex_str = "half";
2244 		break;
2245 	default:
2246 		break;
2247 	}
2248 
2249 bail:
2250 	(void) kstat_close(kcp);
2251 	return (duplex_str);
2252 }
2253