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