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