xref: /illumos-gate/usr/src/cmd/dladm/dladm.c (revision bb5e3b2f129cc39517b925419c22f69a378ec023)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <locale.h>
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <stropts.h>
37 #include <errno.h>
38 #include <kstat.h>
39 #include <strings.h>
40 #include <getopt.h>
41 #include <unistd.h>
42 #include <priv.h>
43 #include <termios.h>
44 #include <pwd.h>
45 #include <auth_attr.h>
46 #include <auth_list.h>
47 #include <libintl.h>
48 #include <libdlpi.h>
49 #include <libdladm.h>
50 #include <liblaadm.h>
51 #include <libmacadm.h>
52 #include <libwladm.h>
53 #include <libinetutil.h>
54 #include <bsm/adt.h>
55 #include <bsm/adt_event.h>
56 
57 #define	AGGR_DRV	"aggr"
58 #define	MAXPORT		256
59 #define	DUMP_LACP_FORMAT	"    %-9s %-8s %-7s %-12s "	\
60 	"%-5s %-4s %-4s %-9s %-7s\n"
61 
62 typedef struct pktsum_s {
63 	uint64_t	ipackets;
64 	uint64_t	opackets;
65 	uint64_t	rbytes;
66 	uint64_t	obytes;
67 	uint32_t	ierrors;
68 	uint32_t	oerrors;
69 } pktsum_t;
70 
71 typedef struct show_link_state {
72 	boolean_t	ls_firstonly;
73 	boolean_t	ls_donefirst;
74 	boolean_t	ls_stats;
75 	pktsum_t	ls_prevstats;
76 	boolean_t	ls_parseable;
77 } show_link_state_t;
78 
79 typedef struct show_grp_state {
80 	uint32_t	gs_key;
81 	boolean_t	gs_lacp;
82 	boolean_t	gs_found;
83 	boolean_t	gs_stats;
84 	boolean_t	gs_firstonly;
85 	pktsum_t	gs_prevstats[MAXPORT];
86 	boolean_t	gs_parseable;
87 } show_grp_state_t;
88 
89 typedef struct show_mac_state {
90 	boolean_t	ms_firstonly;
91 	boolean_t	ms_donefirst;
92 	pktsum_t	ms_prevstats;
93 	boolean_t	ms_parseable;
94 } show_mac_state_t;
95 
96 typedef struct port_state {
97 	char			*state_name;
98 	aggr_port_state_t	state_num;
99 } port_state_t;
100 
101 static port_state_t port_states[] = {
102 	{"standby", AGGR_PORT_STATE_STANDBY },
103 	{"attached", AGGR_PORT_STATE_ATTACHED }
104 };
105 
106 #define	NPORTSTATES	(sizeof (port_states) / sizeof (port_state_t))
107 
108 typedef	void cmdfunc_t(int, char **);
109 
110 static cmdfunc_t do_show_link, do_show_dev, do_show_wifi;
111 static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
112 static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr, do_down_aggr;
113 static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
114 static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
115 static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
116 static cmdfunc_t do_init_linkprop, do_init_secobj;
117 
118 static void	show_linkprop_onelink(void *, const char *);
119 
120 static void	link_stats(const char *, uint_t);
121 static void	aggr_stats(uint32_t, uint_t);
122 static void	dev_stats(const char *dev, uint32_t);
123 
124 static void	get_mac_stats(const char *, pktsum_t *);
125 static void	get_link_stats(const char *, pktsum_t *);
126 static uint64_t	mac_ifspeed(const char *);
127 static char	*mac_link_state(const char *);
128 static char	*mac_link_duplex(const char *);
129 static void	stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
130 static void	stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
131 
132 static boolean_t str2int(const char *, int *);
133 static void	die(const char *, ...);
134 static void	die_optdup(int);
135 static void	die_opterr(int, int);
136 static void	die_laerr(laadm_diag_t, const char *, ...);
137 static void	die_wlerr(wladm_status_t, const char *, ...);
138 static void	die_dlerr(dladm_status_t, const char *, ...);
139 static void	warn(const char *, ...);
140 static void	warn_wlerr(wladm_status_t, const char *, ...);
141 static void	warn_dlerr(dladm_status_t, const char *, ...);
142 
143 typedef struct	cmd {
144 	char		*c_name;
145 	cmdfunc_t	*c_fn;
146 } cmd_t;
147 
148 static cmd_t	cmds[] = {
149 	{ "show-link",		do_show_link		},
150 	{ "show-dev",		do_show_dev		},
151 	{ "create-aggr",	do_create_aggr		},
152 	{ "delete-aggr",	do_delete_aggr		},
153 	{ "add-aggr",		do_add_aggr		},
154 	{ "remove-aggr",	do_remove_aggr		},
155 	{ "modify-aggr",	do_modify_aggr		},
156 	{ "show-aggr",		do_show_aggr		},
157 	{ "up-aggr",		do_up_aggr		},
158 	{ "down-aggr",		do_down_aggr		},
159 	{ "scan-wifi",		do_scan_wifi		},
160 	{ "connect-wifi",	do_connect_wifi		},
161 	{ "disconnect-wifi",	do_disconnect_wifi	},
162 	{ "show-wifi",		do_show_wifi		},
163 	{ "show-linkprop",	do_show_linkprop	},
164 	{ "set-linkprop",	do_set_linkprop		},
165 	{ "reset-linkprop",	do_reset_linkprop	},
166 	{ "create-secobj",	do_create_secobj	},
167 	{ "delete-secobj",	do_delete_secobj	},
168 	{ "show-secobj",	do_show_secobj		},
169 	{ "init-linkprop",	do_init_linkprop	},
170 	{ "init-secobj",	do_init_secobj		}
171 };
172 
173 static const struct option longopts[] = {
174 	{"vlan-id",	required_argument,	0, 'v'	},
175 	{"dev",		required_argument,	0, 'd'	},
176 	{"policy",	required_argument,	0, 'P'	},
177 	{"lacp-mode",	required_argument,	0, 'l'	},
178 	{"lacp-timer",	required_argument,	0, 'T'	},
179 	{"unicast",	required_argument,	0, 'u'	},
180 	{"statistics",	no_argument,		0, 's'	},
181 	{"interval",	required_argument,	0, 'i'	},
182 	{"lacp",	no_argument,		0, 'L'	},
183 	{"temporary",	no_argument,		0, 't'	},
184 	{"root-dir",	required_argument,	0, 'r'	},
185 	{"parseable",	no_argument,		0, 'p'	},
186 	{ 0, 0, 0, 0 }
187 };
188 
189 static const struct option prop_longopts[] = {
190 	{"temporary",	no_argument,		0, 't'	},
191 	{"root-dir",	required_argument,	0, 'R'	},
192 	{"prop",	required_argument,	0, 'p'	},
193 	{"parseable",	no_argument,		0, 'c'	},
194 	{"persistent",	no_argument,		0, 'P'	},
195 	{ 0, 0, 0, 0 }
196 };
197 
198 static const struct option wifi_longopts[] = {
199 	{"parseable",	no_argument,		0, 'p'	},
200 	{"output",	required_argument,	0, 'o'	},
201 	{"essid",	required_argument,	0, 'e'	},
202 	{"bsstype",	required_argument,	0, 'b'	},
203 	{"mode",	required_argument,	0, 'm'	},
204 	{"key",		required_argument,	0, 'k'	},
205 	{"sec",		required_argument,	0, 's'	},
206 	{"auth",	required_argument,	0, 'a'	},
207 	{"create-ibss",	required_argument,	0, 'c'	},
208 	{"timeout",	required_argument,	0, 'T'	},
209 	{"all-links",	no_argument,		0, 'a'	},
210 	{"temporary",	no_argument,		0, 't'	},
211 	{"root-dir",	required_argument,	0, 'R'	},
212 	{"persistent",	no_argument,		0, 'P'	},
213 	{"file",	required_argument,	0, 'f'	},
214 	{ 0, 0, 0, 0 }
215 };
216 
217 static char *progname;
218 static sig_atomic_t signalled;
219 
220 static void
221 usage(void)
222 {
223 	(void) fprintf(stderr, gettext("usage:	dladm <subcommand> <args> ...\n"
224 	    "\tshow-link       [-p] [-s [-i <interval>]] [<name>]\n"
225 	    "\tshow-dev        [-p] [-s [-i <interval>]] [<dev>]\n"
226 	    "\n"
227 	    "\tcreate-aggr     [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n"
228 	    "\t                [-T <time>] [-u <address>] -d <dev> ... <key>\n"
229 	    "\tmodify-aggr     [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n"
230 	    "\t                [-T <time>] [-u <address>] <key>\n"
231 	    "\tdelete-aggr     [-t] [-R <root-dir>] <key>\n"
232 	    "\tadd-aggr        [-t] [-R <root-dir>] -d <dev> ... <key>\n"
233 	    "\tremove-aggr     [-t] [-R <root-dir>] -d <dev> ... <key>\n"
234 	    "\tshow-aggr       [-pL][-s [-i <interval>]] [<key>]\n"
235 	    "\n"
236 	    "\tscan-wifi       [-p] [-o <field>,...] [<name>]\n"
237 	    "\tconnect-wifi    [-e <essid>] [-i <bssid>] [-k <key>,...]"
238 	    " [-s wep]\n"
239 	    "\t                [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
240 	    "\t                [-T <time>] [<name>]\n"
241 	    "\tdisconnect-wifi [-a] [<name>]\n"
242 	    "\tshow-wifi       [-p] [-o <field>,...] [<name>]\n"
243 	    "\n"
244 	    "\tset-linkprop    [-t] [-R <root-dir>]  -p <prop>=<value>[,...]"
245 	    " <name>\n"
246 	    "\treset-linkprop  [-t] [-R <root-dir>] [-p <prop>,...] <name>\n"
247 	    "\tshow-linkprop   [-cP][-p <prop>,...] <name>\n"
248 	    "\n"
249 	    "\tcreate-secobj   [-t] [-R <root-dir>] [-f <file>] -c <class>"
250 	    " <secobj>\n"
251 	    "\tdelete-secobj   [-t] [-R <root-dir>] <secobj>[,...]\n"
252 	    "\tshow-secobj     [-pP][<secobj>,...]\n"));
253 	exit(1);
254 }
255 
256 int
257 main(int argc, char *argv[])
258 {
259 	int	i;
260 	cmd_t	*cmdp;
261 
262 	(void) setlocale(LC_ALL, "");
263 #if !defined(TEXT_DOMAIN)
264 #define	TEXT_DOMAIN "SYS_TEST"
265 #endif
266 	(void) textdomain(TEXT_DOMAIN);
267 
268 	progname = argv[0];
269 
270 	if (argc < 2)
271 		usage();
272 
273 	if (!priv_ineffect(PRIV_SYS_NET_CONFIG) ||
274 	    !priv_ineffect(PRIV_NET_RAWACCESS))
275 		die("insufficient privileges");
276 
277 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
278 		cmdp = &cmds[i];
279 		if (strcmp(argv[1], cmdp->c_name) == 0) {
280 			cmdp->c_fn(argc - 1, &argv[1]);
281 			exit(0);
282 		}
283 	}
284 
285 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
286 	    progname, argv[1]);
287 	usage();
288 
289 	return (0);
290 }
291 
292 static void
293 do_create_aggr(int argc, char *argv[])
294 {
295 	char			option;
296 	int			key;
297 	uint32_t		policy = AGGR_POLICY_L4;
298 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
299 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
300 	laadm_port_attr_db_t	port[MAXPORT];
301 	uint_t			nport = 0;
302 	uint8_t			mac_addr[ETHERADDRL];
303 	boolean_t		mac_addr_fixed = B_FALSE;
304 	boolean_t		P_arg = B_FALSE;
305 	boolean_t		l_arg = B_FALSE;
306 	boolean_t		t_arg = B_FALSE;
307 	boolean_t		u_arg = B_FALSE;
308 	boolean_t		T_arg = B_FALSE;
309 	char			*altroot = NULL;
310 	laadm_diag_t		diag = 0;
311 
312 	opterr = 0;
313 	while ((option = getopt_long(argc, argv, ":d:l:P:R:tu:T:",
314 	    longopts, NULL)) != -1) {
315 		switch (option) {
316 		case 'd':
317 			if (nport >= MAXPORT)
318 				die("too many <dev> arguments");
319 
320 			if (strlcpy(port[nport].lp_devname, optarg,
321 			    MAXNAMELEN) >= MAXNAMELEN)
322 				die("device name too long");
323 
324 			nport++;
325 			break;
326 		case 'P':
327 			if (P_arg)
328 				die_optdup(option);
329 
330 			P_arg = B_TRUE;
331 			if (!laadm_str_to_policy(optarg, &policy))
332 				die("invalid policy '%s'", optarg);
333 			break;
334 		case 'u':
335 			if (u_arg)
336 				die_optdup(option);
337 
338 			u_arg = B_TRUE;
339 			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
340 			    mac_addr))
341 				die("invalid MAC address '%s'", optarg);
342 			break;
343 		case 'l':
344 			if (l_arg)
345 				die_optdup(option);
346 
347 			l_arg = B_TRUE;
348 			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode))
349 				die("invalid LACP mode '%s'", optarg);
350 			break;
351 		case 'T':
352 			if (T_arg)
353 				die_optdup(option);
354 
355 			T_arg = B_TRUE;
356 			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer))
357 				die("invalid LACP timer value '%s'", optarg);
358 			break;
359 		case 't':
360 			t_arg = B_TRUE;
361 			break;
362 		case 'R':
363 			altroot = optarg;
364 			break;
365 		default:
366 			die_opterr(optopt, option);
367 			break;
368 		}
369 	}
370 
371 	if (nport == 0)
372 		usage();
373 
374 	/* get key value (required last argument) */
375 	if (optind != (argc-1))
376 		usage();
377 
378 	if (!str2int(argv[optind], &key) || key < 1)
379 		die("invalid key value '%s'", argv[optind]);
380 
381 	if (laadm_create(key, nport, port, policy, mac_addr_fixed,
382 	    mac_addr, lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0)
383 		die_laerr(diag, "create operation failed");
384 }
385 
386 static void
387 do_delete_aggr(int argc, char *argv[])
388 {
389 	int			key;
390 	char			option;
391 	boolean_t		t_arg = B_FALSE;
392 	char			*altroot = NULL;
393 	laadm_diag_t		diag = 0;
394 
395 	opterr = 0;
396 	while ((option = getopt_long(argc, argv, ":R:t", longopts,
397 	    NULL)) != -1) {
398 		switch (option) {
399 		case 't':
400 			t_arg = B_TRUE;
401 			break;
402 		case 'R':
403 			altroot = optarg;
404 			break;
405 		default:
406 			die_opterr(optopt, option);
407 			break;
408 		}
409 	}
410 
411 	/* get key value (required last argument) */
412 	if (optind != (argc-1))
413 		usage();
414 
415 	if (!str2int(argv[optind], &key) || key < 1)
416 		die("invalid key value '%s'", argv[optind]);
417 
418 	if (laadm_delete(key, t_arg, altroot, &diag) < 0)
419 		die_laerr(diag, "delete operation failed");
420 }
421 
422 static void
423 do_add_aggr(int argc, char *argv[])
424 {
425 	char			option;
426 	int			key;
427 	laadm_port_attr_db_t	port[MAXPORT];
428 	uint_t			nport = 0;
429 	boolean_t		t_arg = B_FALSE;
430 	char			*altroot = NULL;
431 	laadm_diag_t		diag = 0;
432 
433 	opterr = 0;
434 	while ((option = getopt_long(argc, argv, ":d:R:t", longopts,
435 	    NULL)) != -1) {
436 		switch (option) {
437 		case 'd':
438 			if (nport >= MAXPORT)
439 				die("too many <dev> arguments");
440 
441 			if (strlcpy(port[nport].lp_devname, optarg,
442 			    MAXNAMELEN) >= MAXNAMELEN)
443 				die("device name too long");
444 
445 			nport++;
446 			break;
447 		case 't':
448 			t_arg = B_TRUE;
449 			break;
450 		case 'R':
451 			altroot = optarg;
452 			break;
453 		default:
454 			die_opterr(optopt, option);
455 			break;
456 		}
457 	}
458 
459 	if (nport == 0)
460 		usage();
461 
462 	/* get key value (required last argument) */
463 	if (optind != (argc-1))
464 		usage();
465 
466 	if (!str2int(argv[optind], &key) || key < 1)
467 		die("invalid key value '%s'", argv[optind]);
468 
469 	if (laadm_add(key, nport, port, t_arg, altroot, &diag) < 0) {
470 		/*
471 		 * checking ENOTSUP is a temporary workaround
472 		 * and should be removed once 6399681 is fixed.
473 		 */
474 		if (errno == ENOTSUP) {
475 			(void) fprintf(stderr,
476 			    gettext("%s: add operation failed: %s\n"),
477 			    progname,
478 			    gettext("device capabilities don't match"));
479 			exit(ENOTSUP);
480 		}
481 		die_laerr(diag, "add operation failed");
482 	}
483 }
484 
485 static void
486 do_remove_aggr(int argc, char *argv[])
487 {
488 	char			option;
489 	int			key;
490 	laadm_port_attr_db_t	port[MAXPORT];
491 	uint_t			nport = 0;
492 	boolean_t		t_arg = B_FALSE;
493 	char			*altroot = NULL;
494 	laadm_diag_t		diag = 0;
495 
496 	opterr = 0;
497 	while ((option = getopt_long(argc, argv, ":d:R:t",
498 	    longopts, NULL)) != -1) {
499 		switch (option) {
500 		case 'd':
501 			if (nport >= MAXPORT)
502 				die("too many <dev> arguments");
503 
504 			if (strlcpy(port[nport].lp_devname, optarg,
505 			    MAXNAMELEN) >= MAXNAMELEN)
506 				die("device name too long");
507 
508 			nport++;
509 			break;
510 		case 't':
511 			t_arg = B_TRUE;
512 			break;
513 		case 'R':
514 			altroot = optarg;
515 			break;
516 		default:
517 			die_opterr(optopt, option);
518 			break;
519 		}
520 	}
521 
522 	if (nport == 0)
523 		usage();
524 
525 	/* get key value (required last argument) */
526 	if (optind != (argc-1))
527 		usage();
528 
529 	if (!str2int(argv[optind], &key) || key < 1)
530 		die("invalid key value '%s'", argv[optind]);
531 
532 	if (laadm_remove(key, nport, port, t_arg, altroot, &diag) < 0)
533 		die_laerr(diag, "remove operation failed");
534 }
535 
536 static void
537 do_modify_aggr(int argc, char *argv[])
538 {
539 	char			option;
540 	int			key;
541 	uint32_t		policy = AGGR_POLICY_L4;
542 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
543 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
544 	uint8_t			mac_addr[ETHERADDRL];
545 	boolean_t		mac_addr_fixed = B_FALSE;
546 	uint8_t			modify_mask = 0;
547 	boolean_t		t_arg = B_FALSE;
548 	char			*altroot = NULL;
549 	laadm_diag_t		diag = 0;
550 
551 	opterr = 0;
552 	while ((option = getopt_long(argc, argv, ":l:P:R:tu:T:", longopts,
553 	    NULL)) != -1) {
554 		switch (option) {
555 		case 'P':
556 			if (modify_mask & LAADM_MODIFY_POLICY)
557 				die_optdup(option);
558 
559 			modify_mask |= LAADM_MODIFY_POLICY;
560 
561 			if (!laadm_str_to_policy(optarg, &policy))
562 				die("invalid policy '%s'", optarg);
563 			break;
564 		case 'u':
565 			if (modify_mask & LAADM_MODIFY_MAC)
566 				die_optdup(option);
567 
568 			modify_mask |= LAADM_MODIFY_MAC;
569 
570 			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
571 			    mac_addr))
572 				die("invalid MAC address '%s'", optarg);
573 			break;
574 		case 'l':
575 			if (modify_mask & LAADM_MODIFY_LACP_MODE)
576 				die_optdup(option);
577 
578 			modify_mask |= LAADM_MODIFY_LACP_MODE;
579 
580 			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode))
581 				die("invalid LACP mode '%s'", optarg);
582 			break;
583 		case 'T':
584 			if (modify_mask & LAADM_MODIFY_LACP_TIMER)
585 				die_optdup(option);
586 
587 			modify_mask |= LAADM_MODIFY_LACP_TIMER;
588 
589 			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer))
590 				die("invalid LACP timer value '%s'", optarg);
591 			break;
592 		case 't':
593 			t_arg = B_TRUE;
594 			break;
595 		case 'R':
596 			altroot = optarg;
597 			break;
598 		default:
599 			die_opterr(optopt, option);
600 			break;
601 		}
602 	}
603 
604 	if (modify_mask == 0)
605 		die("at least one of the -PulT options must be specified");
606 
607 	/* get key value (required last argument) */
608 	if (optind != (argc-1))
609 		usage();
610 
611 	if (!str2int(argv[optind], &key) || key < 1)
612 		die("invalid key value '%s'", argv[optind]);
613 
614 	if (laadm_modify(key, modify_mask, policy, mac_addr_fixed, mac_addr,
615 	    lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0)
616 		die_laerr(diag, "modify operation failed");
617 }
618 
619 static void
620 do_up_aggr(int argc, char *argv[])
621 {
622 	int		key = 0;
623 	laadm_diag_t	diag = 0;
624 
625 	/* get aggregation key (optional last argument) */
626 	if (argc == 2) {
627 		if (!str2int(argv[1], &key) || key < 1)
628 			die("invalid key value '%s'", argv[1]);
629 	} else if (argc > 2) {
630 		usage();
631 	}
632 
633 	if (laadm_up(key, NULL, &diag) < 0) {
634 		if (key != 0) {
635 			die_laerr(diag, "could not bring up aggregation '%u'",
636 			    key);
637 		} else {
638 			die_laerr(diag, "could not bring aggregations up");
639 		}
640 	}
641 }
642 
643 static void
644 do_down_aggr(int argc, char *argv[])
645 {
646 	int	key = 0;
647 
648 	/* get aggregation key (optional last argument) */
649 	if (argc == 2) {
650 		if (!str2int(argv[1], &key) || key < 1)
651 			die("invalid key value '%s'", argv[1]);
652 	} else if (argc > 2) {
653 		usage();
654 	}
655 
656 	if (laadm_down(key) < 0) {
657 		if (key != 0) {
658 			die("could not bring down aggregation '%u': %s",
659 			    key, strerror(errno));
660 		} else {
661 			die("could not bring down aggregations: %s",
662 			    strerror(errno));
663 		}
664 	}
665 }
666 
667 #define	TYPE_WIDTH	10
668 
669 static void
670 print_link_parseable(const char *name, dladm_attr_t *dap, boolean_t legacy)
671 {
672 	char	type[TYPE_WIDTH];
673 
674 	if (!legacy) {
675 		char	drv[DLPI_LINKNAME_MAX];
676 		uint_t	instance;
677 
678 		if (dap->da_vid != 0) {
679 			(void) snprintf(type, TYPE_WIDTH, "vlan %u",
680 			    dap->da_vid);
681 		} else {
682 			(void) snprintf(type, TYPE_WIDTH, "non-vlan");
683 		}
684 
685 		if (dlpi_parselink(dap->da_dev, drv, &instance) != DLPI_SUCCESS)
686 			return;
687 
688 		if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) {
689 			(void) printf("%s type=%s mtu=%d key=%u\n",
690 			    name, type, dap->da_max_sdu, instance);
691 		} else {
692 			(void) printf("%s type=%s mtu=%d device=%s\n",
693 			    name, type, dap->da_max_sdu, dap->da_dev);
694 		}
695 	} else {
696 		(void) printf("%s type=legacy mtu=%d device=%s\n",
697 		    name, dap->da_max_sdu, name);
698 	}
699 }
700 
701 static void
702 print_link(const char *name, dladm_attr_t *dap, boolean_t legacy)
703 {
704 	char	type[TYPE_WIDTH];
705 
706 	if (!legacy) {
707 		char 	drv[DLPI_LINKNAME_MAX];
708 		uint_t	instance;
709 
710 		if (dap->da_vid != 0) {
711 			(void) snprintf(type, TYPE_WIDTH, gettext("vlan %u"),
712 			    dap->da_vid);
713 		} else {
714 			(void) snprintf(type, TYPE_WIDTH, gettext("non-vlan"));
715 		}
716 
717 		if (dlpi_parselink(dap->da_dev, drv, &instance) != DLPI_SUCCESS)
718 			return;
719 		if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) {
720 			(void) printf(gettext("%-9s\ttype: %s\tmtu: %d"
721 			    "\taggregation: key %u\n"), name, type,
722 			    dap->da_max_sdu, instance);
723 		} else {
724 			(void) printf(gettext("%-9s\ttype: %s\tmtu: "
725 			    "%d\tdevice: %s\n"), name, type, dap->da_max_sdu,
726 			    dap->da_dev);
727 		}
728 	} else {
729 		(void) printf(gettext("%-9s\ttype: legacy\tmtu: "
730 		    "%d\tdevice: %s\n"), name, dap->da_max_sdu, name);
731 	}
732 }
733 
734 static int
735 get_if_info(const char *name, dladm_attr_t *dlattrp, boolean_t *legacy)
736 {
737 	int	err;
738 
739 	if ((err = dladm_info(name, dlattrp)) == 0) {
740 		*legacy = B_FALSE;
741 	} else if (err < 0 && errno == ENODEV) {
742 		dlpi_handle_t   dh;
743 		dlpi_info_t	dlinfo;
744 
745 		/*
746 		 * A return value of ENODEV means that the specified
747 		 * device is not gldv3.
748 		 */
749 		if (dlpi_open(name, &dh, 0) != DLPI_SUCCESS) {
750 			errno = ENOENT;
751 			return (-1);
752 		}
753 		if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
754 			dlpi_close(dh);
755 			errno = EINVAL;
756 			return (-1);
757 		}
758 		dlpi_close(dh);
759 		*legacy = B_TRUE;
760 		bzero(dlattrp, sizeof (*dlattrp));
761 		dlattrp->da_max_sdu = dlinfo.di_max_sdu;
762 
763 	} else {
764 		/*
765 		 * If the return value is not ENODEV, this means that
766 		 * user is either passing in a bogus interface name
767 		 * or a vlan interface name that doesn't exist yet.
768 		 */
769 		errno = ENOENT;
770 		return (-1);
771 	}
772 	return (0);
773 }
774 
775 /* ARGSUSED */
776 static void
777 show_link(void *arg, const char *name)
778 {
779 	dladm_attr_t	dlattr;
780 	boolean_t	legacy = B_TRUE;
781 	show_link_state_t *state = (show_link_state_t *)arg;
782 
783 	if (get_if_info(name, &dlattr, &legacy) < 0)
784 		die("invalid link '%s'", name);
785 
786 	if (state->ls_parseable) {
787 		print_link_parseable(name, &dlattr, legacy);
788 	} else {
789 		print_link(name, &dlattr, legacy);
790 	}
791 }
792 
793 static void
794 show_link_stats(void *arg, const char *name)
795 {
796 	show_link_state_t *state = (show_link_state_t *)arg;
797 	pktsum_t stats, diff_stats;
798 
799 	if (state->ls_firstonly) {
800 		if (state->ls_donefirst)
801 			return;
802 		state->ls_donefirst = B_TRUE;
803 	} else {
804 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
805 	}
806 
807 	get_link_stats(name, &stats);
808 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
809 
810 	(void) printf("%s", name);
811 	(void) printf("\t\t%-10llu", diff_stats.ipackets);
812 	(void) printf("%-12llu", diff_stats.rbytes);
813 	(void) printf("%-8u", diff_stats.ierrors);
814 	(void) printf("%-10llu", diff_stats.opackets);
815 	(void) printf("%-12llu", diff_stats.obytes);
816 	(void) printf("%-8u\n", diff_stats.oerrors);
817 
818 	state->ls_prevstats = stats;
819 }
820 
821 static void
822 dump_grp(laadm_grp_attr_sys_t	*grp, boolean_t parseable)
823 {
824 	char policy_str[LAADM_POLICY_STR_LEN];
825 	char addr_str[ETHERADDRL * 3];
826 
827 	if (!parseable) {
828 		(void) printf(gettext("key: %d (0x%04x)"),
829 		    grp->lg_key, grp->lg_key);
830 
831 		(void) printf(gettext("\tpolicy: %s"),
832 		    laadm_policy_to_str(grp->lg_policy, policy_str));
833 
834 		(void) printf(gettext("\taddress: %s (%s)\n"),
835 		    laadm_mac_addr_to_str(grp->lg_mac, addr_str),
836 		    (grp->lg_mac_fixed) ? gettext("fixed") : gettext("auto"));
837 	} else {
838 		(void) printf("aggr key=%d", grp->lg_key);
839 
840 		(void) printf(" policy=%s",
841 		    laadm_policy_to_str(grp->lg_policy, policy_str));
842 
843 		(void) printf(" address=%s",
844 		    laadm_mac_addr_to_str(grp->lg_mac, addr_str));
845 
846 		(void) printf(" address-type=%s\n",
847 		    (grp->lg_mac_fixed) ? "fixed" : "auto");
848 	}
849 }
850 
851 static void
852 dump_grp_lacp(laadm_grp_attr_sys_t *grp, boolean_t parseable)
853 {
854 	const char *lacp_mode_str = laadm_lacp_mode_to_str(grp->lg_lacp_mode);
855 	const char *lacp_timer_str =
856 	    laadm_lacp_timer_to_str(grp->lg_lacp_timer);
857 
858 	if (!parseable) {
859 		(void) printf(gettext("\t\tLACP mode: %s"), lacp_mode_str);
860 		(void) printf(gettext("\tLACP timer: %s\n"), lacp_timer_str);
861 	} else {
862 		(void) printf(" lacp-mode=%s", lacp_mode_str);
863 		(void) printf(" lacp-timer=%s\n", lacp_timer_str);
864 	}
865 }
866 
867 static void
868 dump_grp_stats(laadm_grp_attr_sys_t *grp)
869 {
870 	(void) printf("key: %d", grp->lg_key);
871 	(void) printf("\tipackets  rbytes      opackets	 obytes		 ");
872 	(void) printf("%%ipkts	%%opkts\n");
873 }
874 
875 static void
876 dump_ports_lacp_head(void)
877 {
878 	(void) printf(DUMP_LACP_FORMAT, gettext("device"), gettext("activity"),
879 	    gettext("timeout"), gettext("aggregatable"), gettext("sync"),
880 	    gettext("coll"), gettext("dist"), gettext("defaulted"),
881 	    gettext("expired"));
882 }
883 
884 static void
885 dump_ports_head(void)
886 {
887 	(void) printf(gettext("	   device\taddress\t\t	speed\t\tduplex\tlink\t"
888 	    "state\n"));
889 }
890 
891 static char *
892 port_state_to_str(aggr_port_state_t state_num)
893 {
894 	int			i;
895 	port_state_t		*state;
896 
897 	for (i = 0; i < NPORTSTATES; i++) {
898 		state = &port_states[i];
899 		if (state->state_num == state_num)
900 			return (state->state_name);
901 	}
902 
903 	return ("unknown");
904 }
905 
906 static void
907 dump_port(laadm_port_attr_sys_t *port, boolean_t parseable)
908 {
909 	char *dev = port->lp_devname;
910 	char buf[ETHERADDRL * 3];
911 
912 	if (!parseable) {
913 		(void) printf("	   %-9s\t%s", dev, laadm_mac_addr_to_str(
914 		    port->lp_mac, buf));
915 		(void) printf("\t %5uMb", (int)(mac_ifspeed(dev) /
916 		    1000000ull));
917 		(void) printf("\t%s", mac_link_duplex(dev));
918 		(void) printf("\t%s", mac_link_state(dev));
919 		(void) printf("\t%s\n", port_state_to_str(port->lp_state));
920 
921 	} else {
922 		(void) printf(" device=%s address=%s", dev,
923 		    laadm_mac_addr_to_str(port->lp_mac, buf));
924 		(void) printf(" speed=%u", (int)(mac_ifspeed(dev) /
925 		    1000000ull));
926 		(void) printf(" duplex=%s", mac_link_duplex(dev));
927 		(void) printf(" link=%s", mac_link_state(dev));
928 		(void) printf(" port=%s", port_state_to_str(port->lp_state));
929 	}
930 }
931 
932 static void
933 dump_port_lacp(laadm_port_attr_sys_t *port)
934 {
935 	aggr_lacp_state_t *state = &port->lp_lacp_state;
936 
937 	(void) printf(DUMP_LACP_FORMAT,
938 	    port->lp_devname, state->bit.activity ? "active" : "passive",
939 	    state->bit.timeout ? "short" : "long",
940 	    state->bit.aggregation ? "yes" : "no",
941 	    state->bit.sync ? "yes" : "no",
942 	    state->bit.collecting ? "yes" : "no",
943 	    state->bit.distributing ? "yes" : "no",
944 	    state->bit.defaulted ? "yes" : "no",
945 	    state->bit.expired ? "yes" : "no");
946 }
947 
948 static void
949 dump_port_stat(int index, show_grp_state_t *state, pktsum_t *port_stats,
950     pktsum_t *tot_stats)
951 {
952 	pktsum_t	diff_stats;
953 	pktsum_t	*old_stats = &state->gs_prevstats[index];
954 
955 	stats_diff(&diff_stats, port_stats, old_stats);
956 
957 	(void) printf("\t%-10llu", diff_stats.ipackets);
958 	(void) printf("%-12llu", diff_stats.rbytes);
959 	(void) printf("%-10llu", diff_stats.opackets);
960 	(void) printf("%-12llu", diff_stats.obytes);
961 
962 	if (tot_stats->ipackets == 0)
963 		(void) printf("\t-");
964 	else
965 		(void) printf("\t%-6.1f", (double)diff_stats.ipackets/
966 		    (double)tot_stats->ipackets * 100);
967 
968 	if (tot_stats->opackets == 0)
969 		(void) printf("\t-");
970 	else
971 		(void) printf("\t%-6.1f", (double)diff_stats.opackets/
972 		    (double)tot_stats->opackets * 100);
973 
974 	(void) printf("\n");
975 
976 	*old_stats = *port_stats;
977 }
978 
979 static int
980 show_key(void *arg, laadm_grp_attr_sys_t *grp)
981 {
982 	show_grp_state_t	*state = (show_grp_state_t *)arg;
983 	int			i;
984 	pktsum_t		pktsumtot, port_stat;
985 
986 	if (state->gs_key != 0 && state->gs_key != grp->lg_key)
987 		return (0);
988 	if (state->gs_firstonly) {
989 		if (state->gs_found)
990 			return (0);
991 	} else {
992 		bzero(&state->gs_prevstats, sizeof (state->gs_prevstats));
993 	}
994 
995 	state->gs_found = B_TRUE;
996 
997 	if (state->gs_stats) {
998 		/* show statistics */
999 		dump_grp_stats(grp);
1000 
1001 		/* sum the ports statistics */
1002 		bzero(&pktsumtot, sizeof (pktsumtot));
1003 		for (i = 0; i < grp->lg_nports; i++) {
1004 			get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat);
1005 			stats_total(&pktsumtot, &port_stat,
1006 			    &state->gs_prevstats[i]);
1007 		}
1008 
1009 		(void) printf("	   Total");
1010 		(void) printf("\t%-10llu", pktsumtot.ipackets);
1011 		(void) printf("%-12llu", pktsumtot.rbytes);
1012 		(void) printf("%-10llu", pktsumtot.opackets);
1013 		(void) printf("%-12llu\n", pktsumtot.obytes);
1014 
1015 		for (i = 0; i < grp->lg_nports; i++) {
1016 			get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat);
1017 			(void) printf("	   %s", grp->lg_ports[i].lp_devname);
1018 			dump_port_stat(i, state, &port_stat, &pktsumtot);
1019 		}
1020 	} else if (state->gs_lacp) {
1021 		/* show LACP info */
1022 		dump_grp(grp, state->gs_parseable);
1023 		dump_grp_lacp(grp, state->gs_parseable);
1024 		dump_ports_lacp_head();
1025 		for (i = 0; i < grp->lg_nports; i++)
1026 			dump_port_lacp(&grp->lg_ports[i]);
1027 	} else {
1028 		dump_grp(grp, state->gs_parseable);
1029 		if (!state->gs_parseable)
1030 			dump_ports_head();
1031 		for (i = 0; i < grp->lg_nports; i++) {
1032 			if (state->gs_parseable)
1033 				(void) printf("dev key=%d", grp->lg_key);
1034 			dump_port(&grp->lg_ports[i], state->gs_parseable);
1035 			if (state->gs_parseable)
1036 				(void) printf("\n");
1037 		}
1038 	}
1039 
1040 	return (0);
1041 }
1042 
1043 static int
1044 kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
1045 {
1046 	kstat_named_t	*knp;
1047 
1048 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
1049 		return (-1);
1050 
1051 	if (knp->data_type != type)
1052 		return (-1);
1053 
1054 	switch (type) {
1055 	case KSTAT_DATA_UINT64:
1056 		*(uint64_t *)buf = knp->value.ui64;
1057 		break;
1058 	case KSTAT_DATA_UINT32:
1059 		*(uint32_t *)buf = knp->value.ui32;
1060 		break;
1061 	default:
1062 		return (-1);
1063 	}
1064 
1065 	return (0);
1066 }
1067 
1068 static void
1069 show_dev(void *arg, const char *dev)
1070 {
1071 	show_mac_state_t *state = (show_mac_state_t *)arg;
1072 
1073 	(void) printf("%s", dev);
1074 
1075 	if (!state->ms_parseable) {
1076 		(void) printf(gettext("\t\tlink: %s"),
1077 		    mac_link_state(dev));
1078 		(void) printf(gettext("\tspeed: %5uMb"),
1079 		    (unsigned int)(mac_ifspeed(dev) / 1000000ull));
1080 		(void) printf(gettext("\tduplex: %s\n"),
1081 		    mac_link_duplex(dev));
1082 	} else {
1083 		(void) printf(" link=%s", mac_link_state(dev));
1084 		(void) printf(" speed=%u",
1085 		    (unsigned int)(mac_ifspeed(dev) / 1000000ull));
1086 		(void) printf(" duplex=%s\n", mac_link_duplex(dev));
1087 	}
1088 }
1089 
1090 /*ARGSUSED*/
1091 static void
1092 show_dev_stats(void *arg, const char *dev)
1093 {
1094 	show_mac_state_t *state = (show_mac_state_t *)arg;
1095 	pktsum_t stats, diff_stats;
1096 
1097 	if (state->ms_firstonly) {
1098 		if (state->ms_donefirst)
1099 			return;
1100 		state->ms_donefirst = B_TRUE;
1101 	} else {
1102 		bzero(&state->ms_prevstats, sizeof (state->ms_prevstats));
1103 	}
1104 
1105 	get_mac_stats(dev, &stats);
1106 	stats_diff(&diff_stats, &stats, &state->ms_prevstats);
1107 
1108 	(void) printf("%s", dev);
1109 	(void) printf("\t\t%-10llu", diff_stats.ipackets);
1110 	(void) printf("%-12llu", diff_stats.rbytes);
1111 	(void) printf("%-8u", diff_stats.ierrors);
1112 	(void) printf("%-10llu", diff_stats.opackets);
1113 	(void) printf("%-12llu", diff_stats.obytes);
1114 	(void) printf("%-8u\n", diff_stats.oerrors);
1115 
1116 	state->ms_prevstats = stats;
1117 }
1118 
1119 static void
1120 do_show_link(int argc, char *argv[])
1121 {
1122 	char		*name = NULL;
1123 	int		option;
1124 	boolean_t	s_arg = B_FALSE;
1125 	boolean_t	i_arg = B_FALSE;
1126 	int		interval = 0;
1127 	show_link_state_t state;
1128 
1129 	state.ls_stats = B_FALSE;
1130 	state.ls_parseable = B_FALSE;
1131 
1132 	opterr = 0;
1133 	while ((option = getopt_long(argc, argv, ":psi:",
1134 	    longopts, NULL)) != -1) {
1135 		switch (option) {
1136 		case 'p':
1137 			state.ls_parseable = B_TRUE;
1138 			break;
1139 		case 's':
1140 			if (s_arg)
1141 				die_optdup(option);
1142 
1143 			s_arg = B_TRUE;
1144 			break;
1145 		case 'i':
1146 			if (i_arg)
1147 				die_optdup(option);
1148 
1149 			i_arg = B_TRUE;
1150 			if (!str2int(optarg, &interval) || interval == 0)
1151 				die("invalid interval value '%s'", optarg);
1152 			break;
1153 		default:
1154 			die_opterr(optopt, option);
1155 			break;
1156 		}
1157 	}
1158 
1159 	if (i_arg && !s_arg)
1160 		die("the option -i can be used only with -s");
1161 
1162 	/* get link name (optional last argument) */
1163 	if (optind == (argc-1))
1164 		name = argv[optind];
1165 	else if (optind != argc)
1166 		usage();
1167 
1168 	if (s_arg) {
1169 		link_stats(name, interval);
1170 		return;
1171 	}
1172 
1173 	if (name == NULL) {
1174 		(void) dladm_walk(show_link, &state);
1175 	} else {
1176 		show_link(&state, name);
1177 	}
1178 }
1179 
1180 static void
1181 do_show_aggr(int argc, char *argv[])
1182 {
1183 	int			option;
1184 	int			key = 0;
1185 	boolean_t		L_arg = B_FALSE;
1186 	boolean_t		s_arg = B_FALSE;
1187 	boolean_t		i_arg = B_FALSE;
1188 	show_grp_state_t	state;
1189 	int			interval = 0;
1190 
1191 	state.gs_stats = B_FALSE;
1192 	state.gs_lacp = B_FALSE;
1193 	state.gs_parseable = B_FALSE;
1194 
1195 	opterr = 0;
1196 	while ((option = getopt_long(argc, argv, ":Lpsi:",
1197 	    longopts, NULL)) != -1) {
1198 		switch (option) {
1199 		case 'L':
1200 			if (L_arg)
1201 				die_optdup(option);
1202 
1203 			if (s_arg || i_arg) {
1204 				die("the option -L cannot be used with -i "
1205 				    "or -s");
1206 			}
1207 
1208 			L_arg = B_TRUE;
1209 			state.gs_lacp = B_TRUE;
1210 			break;
1211 		case 'p':
1212 			state.gs_parseable = B_TRUE;
1213 			break;
1214 		case 's':
1215 			if (s_arg)
1216 				die_optdup(option);
1217 
1218 			if (L_arg)
1219 				die("the option -s cannot be used with -L");
1220 
1221 			s_arg = B_TRUE;
1222 			break;
1223 		case 'i':
1224 			if (i_arg)
1225 				die_optdup(option);
1226 
1227 			if (L_arg)
1228 				die("the option -i cannot be used with -L");
1229 
1230 			i_arg = B_TRUE;
1231 			if (!str2int(optarg, &interval) || interval == 0)
1232 				die("invalid interval value '%s'", optarg);
1233 			break;
1234 		default:
1235 			die_opterr(optopt, option);
1236 			break;
1237 		}
1238 	}
1239 
1240 	if (i_arg && !s_arg)
1241 		die("the option -i can be used only with -s");
1242 
1243 	/* get aggregation key (optional last argument) */
1244 	if (optind == (argc-1)) {
1245 		if (!str2int(argv[optind], &key) || key < 1)
1246 			die("invalid key value '%s'", argv[optind]);
1247 	} else if (optind != argc) {
1248 		usage();
1249 	}
1250 
1251 	if (s_arg) {
1252 		aggr_stats(key, interval);
1253 		return;
1254 	}
1255 
1256 	state.gs_key = key;
1257 	state.gs_found = B_FALSE;
1258 
1259 	(void) laadm_walk_sys(show_key, &state);
1260 
1261 	if (key != 0 && !state.gs_found)
1262 		die("non-existent aggregation key '%u'", key);
1263 }
1264 
1265 static void
1266 do_show_dev(int argc, char *argv[])
1267 {
1268 	int		option;
1269 	char		*dev = NULL;
1270 	boolean_t	s_arg = B_FALSE;
1271 	boolean_t	i_arg = B_FALSE;
1272 	int		interval = 0;
1273 	show_mac_state_t state;
1274 
1275 	state.ms_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.ms_parseable = B_TRUE;
1283 			break;
1284 		case 's':
1285 			if (s_arg)
1286 				die_optdup(option);
1287 
1288 			s_arg = B_TRUE;
1289 			break;
1290 		case 'i':
1291 			if (i_arg)
1292 				die_optdup(option);
1293 
1294 			i_arg = B_TRUE;
1295 			if (!str2int(optarg, &interval) || interval == 0)
1296 				die("invalid interval value '%s'", optarg);
1297 			break;
1298 		default:
1299 			die_opterr(optopt, option);
1300 			break;
1301 		}
1302 	}
1303 
1304 	if (i_arg && !s_arg)
1305 		die("the option -i can be used only with -s");
1306 
1307 	/* get dev name (optional last argument) */
1308 	if (optind == (argc-1))
1309 		dev = argv[optind];
1310 	else if (optind != argc)
1311 		usage();
1312 
1313 	if (dev != NULL) {
1314 		uint_t		ppa;
1315 		char		drv[DLPI_LINKNAME_MAX];
1316 		dladm_attr_t	dlattr;
1317 		boolean_t	legacy;
1318 
1319 		/*
1320 		 * Check for invalid devices.
1321 		 * aggregations and vlans are not considered devices.
1322 		 */
1323 		if (dlpi_parselink(dev, drv, &ppa) != DLPI_SUCCESS ||
1324 		    strcmp(drv, "aggr") == 0 || ppa >= 1000 ||
1325 		    get_if_info(dev, &dlattr, &legacy) < 0)
1326 			die("invalid device '%s'", dev);
1327 	}
1328 
1329 	if (s_arg) {
1330 		dev_stats(dev, interval);
1331 		return;
1332 	}
1333 
1334 	if (dev == NULL)
1335 		(void) macadm_walk(show_dev, &state, B_TRUE);
1336 	else
1337 		show_dev(&state, dev);
1338 }
1339 
1340 /* ARGSUSED */
1341 static void
1342 link_stats(const char *link, uint_t interval)
1343 {
1344 	dladm_attr_t		dlattr;
1345 	boolean_t		legacy;
1346 	show_link_state_t	state;
1347 
1348 	if (link != NULL && get_if_info(link, &dlattr, &legacy) < 0)
1349 		die("invalid link '%s'", link);
1350 
1351 	bzero(&state, sizeof (state));
1352 
1353 	/*
1354 	 * If an interval is specified, continuously show the stats
1355 	 * only for the first MAC port.
1356 	 */
1357 	state.ls_firstonly = (interval != 0);
1358 
1359 	for (;;) {
1360 		(void) printf("\t\tipackets  rbytes	 ierrors ");
1361 		(void) printf("opackets	 obytes	     oerrors\n");
1362 
1363 		state.ls_donefirst = B_FALSE;
1364 		if (link == NULL)
1365 			(void) dladm_walk(show_link_stats, &state);
1366 		else
1367 			show_link_stats(&state, link);
1368 
1369 		if (interval == 0)
1370 			break;
1371 
1372 		(void) sleep(interval);
1373 	}
1374 }
1375 
1376 /* ARGSUSED */
1377 static void
1378 aggr_stats(uint32_t key, uint_t interval)
1379 {
1380 	show_grp_state_t state;
1381 
1382 	bzero(&state, sizeof (state));
1383 	state.gs_stats = B_TRUE;
1384 	state.gs_key = key;
1385 
1386 	/*
1387 	 * If an interval is specified, continuously show the stats
1388 	 * only for the first group.
1389 	 */
1390 	state.gs_firstonly = (interval != 0);
1391 
1392 	for (;;) {
1393 		state.gs_found = B_FALSE;
1394 		(void) laadm_walk_sys(show_key, &state);
1395 		if (state.gs_key != 0 && !state.gs_found)
1396 			die("non-existent aggregation key '%u'", key);
1397 
1398 		if (interval == 0)
1399 			break;
1400 
1401 		(void) sleep(interval);
1402 	}
1403 }
1404 
1405 /* ARGSUSED */
1406 static void
1407 dev_stats(const char *dev, uint32_t interval)
1408 {
1409 	show_mac_state_t state;
1410 
1411 	bzero(&state, sizeof (state));
1412 
1413 	/*
1414 	 * If an interval is specified, continuously show the stats
1415 	 * only for the first MAC port.
1416 	 */
1417 	state.ms_firstonly = (interval != 0);
1418 
1419 	for (;;) {
1420 
1421 		(void) printf("\t\tipackets  rbytes	 ierrors ");
1422 		(void) printf("opackets	 obytes	     oerrors\n");
1423 
1424 		state.ms_donefirst = B_FALSE;
1425 		if (dev == NULL)
1426 			(void) macadm_walk(show_dev_stats, &state, B_TRUE);
1427 		else
1428 			show_dev_stats(&state, dev);
1429 
1430 		if (interval == 0)
1431 			break;
1432 
1433 		(void) sleep(interval);
1434 	}
1435 }
1436 
1437 /* accumulate stats (s1 += (s2 - s3)) */
1438 static void
1439 stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
1440 {
1441 	s1->ipackets += (s2->ipackets - s3->ipackets);
1442 	s1->opackets += (s2->opackets - s3->opackets);
1443 	s1->rbytes += (s2->rbytes - s3->rbytes);
1444 	s1->obytes += (s2->obytes - s3->obytes);
1445 	s1->ierrors += (s2->ierrors - s3->ierrors);
1446 	s1->oerrors += (s2->oerrors - s3->oerrors);
1447 }
1448 
1449 /* compute stats differences (s1 = s2 - s3) */
1450 static void
1451 stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
1452 {
1453 	s1->ipackets = s2->ipackets - s3->ipackets;
1454 	s1->opackets = s2->opackets - s3->opackets;
1455 	s1->rbytes = s2->rbytes - s3->rbytes;
1456 	s1->obytes = s2->obytes - s3->obytes;
1457 	s1->ierrors = s2->ierrors - s3->ierrors;
1458 	s1->oerrors = s2->oerrors - s3->oerrors;
1459 }
1460 
1461 /*
1462  * In the following routines, we do the first kstat_lookup() assuming that
1463  * the device is gldv3-based and that the kstat name is the one passed in
1464  * as the "name" argument. If the lookup fails, we redo the kstat_lookup()
1465  * omitting the kstat name. This second lookup is needed for getting kstats
1466  * from legacy devices. This can fail too if the device is not attached or
1467  * the device is legacy and doesn't export the kstats we need.
1468  */
1469 static void
1470 get_stats(char *module, int instance, char *name, pktsum_t *stats)
1471 {
1472 	kstat_ctl_t	*kcp;
1473 	kstat_t		*ksp;
1474 
1475 	if ((kcp = kstat_open()) == NULL) {
1476 		warn("kstat open operation failed");
1477 		return;
1478 	}
1479 
1480 	if ((ksp = kstat_lookup(kcp, module, instance, name)) == NULL &&
1481 	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) {
1482 		/*
1483 		 * The kstat query could fail if the underlying MAC
1484 		 * driver was already detached.
1485 		 */
1486 		(void) kstat_close(kcp);
1487 		return;
1488 	}
1489 
1490 	if (kstat_read(kcp, ksp, NULL) == -1)
1491 		goto bail;
1492 
1493 	if (kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
1494 	    &stats->ipackets) < 0)
1495 		goto bail;
1496 
1497 	if (kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
1498 	    &stats->opackets) < 0)
1499 		goto bail;
1500 
1501 	if (kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
1502 	    &stats->rbytes) < 0)
1503 		goto bail;
1504 
1505 	if (kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
1506 	    &stats->obytes) < 0)
1507 		goto bail;
1508 
1509 	if (kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
1510 	    &stats->ierrors) < 0)
1511 		goto bail;
1512 
1513 	if (kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
1514 	    &stats->oerrors) < 0)
1515 		goto bail;
1516 
1517 	(void) kstat_close(kcp);
1518 	return;
1519 
1520 bail:
1521 	(void) kstat_close(kcp);
1522 }
1523 
1524 static void
1525 get_mac_stats(const char *dev, pktsum_t *stats)
1526 {
1527 	char	module[DLPI_LINKNAME_MAX];
1528 	uint_t	instance;
1529 
1530 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
1531 		return;
1532 	bzero(stats, sizeof (*stats));
1533 	get_stats(module, instance, "mac", stats);
1534 }
1535 
1536 static void
1537 get_link_stats(const char *link, pktsum_t *stats)
1538 {
1539 	char	module[DLPI_LINKNAME_MAX];
1540 	uint_t	instance;
1541 
1542 	if (dlpi_parselink(link, module, &instance) != DLPI_SUCCESS)
1543 		return;
1544 	bzero(stats, sizeof (*stats));
1545 	get_stats(module, instance, (char *)link, stats);
1546 }
1547 
1548 static int
1549 get_single_mac_stat(const char *dev, const char *name, uint8_t type,
1550     void *val)
1551 {
1552 	char		module[DLPI_LINKNAME_MAX];
1553 	uint_t		instance;
1554 	kstat_ctl_t	*kcp;
1555 	kstat_t		*ksp;
1556 
1557 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
1558 		return (-1);
1559 
1560 	if ((kcp = kstat_open()) == NULL) {
1561 		warn("kstat open operation failed");
1562 		return (-1);
1563 	}
1564 
1565 	if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
1566 	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) {
1567 		/*
1568 		 * The kstat query could fail if the underlying MAC
1569 		 * driver was already detached.
1570 		 */
1571 		goto bail;
1572 	}
1573 
1574 	if (kstat_read(kcp, ksp, NULL) == -1) {
1575 		warn("kstat read failed");
1576 		goto bail;
1577 	}
1578 
1579 	if (kstat_value(ksp, name, type, val) < 0)
1580 		goto bail;
1581 
1582 	(void) kstat_close(kcp);
1583 	return (0);
1584 
1585 bail:
1586 	(void) kstat_close(kcp);
1587 	return (-1);
1588 }
1589 
1590 static uint64_t
1591 mac_ifspeed(const char *dev)
1592 {
1593 	uint64_t ifspeed = 0;
1594 
1595 	(void) get_single_mac_stat(dev, "ifspeed", KSTAT_DATA_UINT64, &ifspeed);
1596 	return (ifspeed);
1597 }
1598 
1599 static char *
1600 mac_link_state(const char *dev)
1601 {
1602 	link_state_t	link_state;
1603 	char		*state_str = "unknown";
1604 
1605 	if (get_single_mac_stat(dev, "link_state", KSTAT_DATA_UINT32,
1606 	    &link_state) != 0) {
1607 		return (state_str);
1608 	}
1609 
1610 	switch (link_state) {
1611 	case LINK_STATE_UP:
1612 		state_str = "up";
1613 		break;
1614 	case LINK_STATE_DOWN:
1615 		state_str = "down";
1616 		break;
1617 	default:
1618 		break;
1619 	}
1620 
1621 	return (state_str);
1622 }
1623 
1624 
1625 static char *
1626 mac_link_duplex(const char *dev)
1627 {
1628 	link_duplex_t	link_duplex;
1629 	char		*duplex_str = "unknown";
1630 
1631 	if (get_single_mac_stat(dev, "link_duplex", KSTAT_DATA_UINT32,
1632 	    &link_duplex) != 0) {
1633 		return (duplex_str);
1634 	}
1635 
1636 	switch (link_duplex) {
1637 	case LINK_DUPLEX_FULL:
1638 		duplex_str = "full";
1639 		break;
1640 	case LINK_DUPLEX_HALF:
1641 		duplex_str = "half";
1642 		break;
1643 	default:
1644 		break;
1645 	}
1646 
1647 	return (duplex_str);
1648 }
1649 
1650 #define	WIFI_CMD_SCAN	0x00000001
1651 #define	WIFI_CMD_SHOW	0x00000002
1652 #define	WIFI_CMD_ALL	(WIFI_CMD_SCAN | WIFI_CMD_SHOW)
1653 typedef struct wifi_field {
1654 	const char	*wf_name;
1655 	const char	*wf_header;
1656 	uint_t		wf_width;
1657 	uint_t		wf_mask;
1658 	uint_t		wf_cmdtype;
1659 } wifi_field_t;
1660 
1661 static wifi_field_t wifi_fields[] = {
1662 { "link",	"LINK",		10,	0,			WIFI_CMD_ALL},
1663 { "essid",	"ESSID",	19,	WLADM_WLAN_ATTR_ESSID,	WIFI_CMD_ALL},
1664 { "bssid",	"BSSID/IBSSID", 17,	WLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
1665 { "ibssid",	"BSSID/IBSSID", 17,	WLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
1666 { "mode",	"MODE",		6,	WLADM_WLAN_ATTR_MODE,	WIFI_CMD_ALL},
1667 { "speed",	"SPEED",	6,	WLADM_WLAN_ATTR_SPEED,	WIFI_CMD_ALL},
1668 { "auth",	"AUTH",		8,	WLADM_WLAN_ATTR_AUTH,	WIFI_CMD_SHOW},
1669 { "bsstype",	"BSSTYPE",	8,	WLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL},
1670 { "sec",	"SEC",		6,	WLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL},
1671 { "status",	"STATUS",	17,	WLADM_LINK_ATTR_STATUS, WIFI_CMD_SHOW},
1672 { "strength",	"STRENGTH",	10,	WLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}}
1673 ;
1674 
1675 static char *all_scan_wifi_fields =
1676 	"link,essid,bssid,sec,strength,mode,speed,bsstype";
1677 static char *all_show_wifi_fields =
1678 	"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
1679 static char *def_scan_wifi_fields =
1680 	"link,essid,bssid,sec,strength,mode,speed";
1681 static char *def_show_wifi_fields =
1682 	"link,status,essid,sec,strength,mode,speed";
1683 
1684 #define	WIFI_MAX_FIELDS		(sizeof (wifi_fields) / sizeof (wifi_field_t))
1685 #define	WIFI_MAX_FIELD_LEN	32
1686 
1687 typedef struct {
1688 	char	*s_buf;
1689 	char	**s_fields;	/* array of pointer to the fields in s_buf */
1690 	uint_t	s_nfields;	/* the number of fields in s_buf */
1691 } split_t;
1692 
1693 /*
1694  * Free the split_t structure pointed to by `sp'.
1695  */
1696 static void
1697 splitfree(split_t *sp)
1698 {
1699 	free(sp->s_buf);
1700 	free(sp->s_fields);
1701 	free(sp);
1702 }
1703 
1704 /*
1705  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
1706  * length.  Return a pointer to a split_t containing the split fields, or NULL
1707  * on failure.
1708  */
1709 static split_t *
1710 split(const char *str, uint_t maxfields, uint_t maxlen)
1711 {
1712 	char	*field, *token, *lasts = NULL;
1713 	split_t	*sp;
1714 
1715 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
1716 		return (NULL);
1717 
1718 	sp = calloc(sizeof (split_t), 1);
1719 	if (sp == NULL)
1720 		return (NULL);
1721 
1722 	sp->s_buf = strdup(str);
1723 	sp->s_fields = malloc(sizeof (char *) * maxfields);
1724 	if (sp->s_buf == NULL || sp->s_fields == NULL)
1725 		goto fail;
1726 
1727 	token = sp->s_buf;
1728 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
1729 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
1730 			goto fail;
1731 		token = NULL;
1732 		sp->s_fields[sp->s_nfields++] = field;
1733 	}
1734 	return (sp);
1735 fail:
1736 	splitfree(sp);
1737 	return (NULL);
1738 }
1739 
1740 static int
1741 parse_wifi_fields(char *str, wifi_field_t ***fields, uint_t *countp,
1742     uint_t cmdtype)
1743 {
1744 	uint_t		i, j;
1745 	wifi_field_t	**wf = NULL;
1746 	split_t		*sp;
1747 	boolean_t	good_match = B_FALSE;
1748 
1749 	if (cmdtype == WIFI_CMD_SCAN) {
1750 		if (str == NULL)
1751 			str = def_scan_wifi_fields;
1752 		if (strcasecmp(str, "all") == 0)
1753 			str = all_scan_wifi_fields;
1754 	} else if (cmdtype == WIFI_CMD_SHOW) {
1755 		if (str == NULL)
1756 			str = def_show_wifi_fields;
1757 		if (strcasecmp(str, "all") == 0)
1758 			str = all_show_wifi_fields;
1759 	} else {
1760 		return (-1);
1761 	}
1762 
1763 	sp = split(str, WIFI_MAX_FIELDS, WIFI_MAX_FIELD_LEN);
1764 	if (sp == NULL)
1765 		return (-1);
1766 
1767 	wf = malloc(sp->s_nfields * sizeof (wifi_field_t *));
1768 	if (wf == NULL)
1769 		goto fail;
1770 
1771 	for (i = 0; i < sp->s_nfields; i++) {
1772 		for (j = 0; j < WIFI_MAX_FIELDS; j++) {
1773 			if (strcasecmp(sp->s_fields[i],
1774 			    wifi_fields[j].wf_name) == 0) {
1775 				good_match = wifi_fields[j].
1776 				    wf_cmdtype & cmdtype;
1777 				break;
1778 			}
1779 		}
1780 		if (!good_match)
1781 			goto fail;
1782 
1783 		good_match = B_FALSE;
1784 		wf[i] = &wifi_fields[j];
1785 	}
1786 	*countp = i;
1787 	*fields = wf;
1788 	splitfree(sp);
1789 	return (0);
1790 fail:
1791 	free(wf);
1792 	splitfree(sp);
1793 	return (-1);
1794 }
1795 
1796 typedef struct print_wifi_state {
1797 	const char	*ws_link;
1798 	boolean_t	ws_parseable;
1799 	boolean_t	ws_header;
1800 	wifi_field_t	**ws_fields;
1801 	uint_t		ws_nfields;
1802 	boolean_t	ws_lastfield;
1803 	uint_t		ws_overflow;
1804 } print_wifi_state_t;
1805 
1806 static void
1807 print_wifi_head(print_wifi_state_t *statep)
1808 {
1809 	int		i;
1810 	wifi_field_t	*wfp;
1811 
1812 	for (i = 0; i < statep->ws_nfields; i++) {
1813 		wfp = statep->ws_fields[i];
1814 		if (i + 1 < statep->ws_nfields)
1815 			(void) printf("%-*s ", wfp->wf_width, wfp->wf_header);
1816 		else
1817 			(void) printf("%s", wfp->wf_header);
1818 	}
1819 	(void) printf("\n");
1820 }
1821 
1822 static void
1823 print_wifi_field(print_wifi_state_t *statep, wifi_field_t *wfp,
1824     const char *value)
1825 {
1826 	uint_t	width = wfp->wf_width;
1827 	uint_t	valwidth = strlen(value);
1828 	uint_t	compress;
1829 
1830 	if (statep->ws_parseable) {
1831 		(void) printf("%s=\"%s\"", wfp->wf_header, value);
1832 	} else {
1833 		if (value[0] == '\0')
1834 			value = "--";
1835 		if (statep->ws_lastfield) {
1836 			(void) printf("%s", value);
1837 			return;
1838 		}
1839 
1840 		if (valwidth > width) {
1841 			statep->ws_overflow += valwidth - width;
1842 		} else if (valwidth < width && statep->ws_overflow > 0) {
1843 			compress = min(statep->ws_overflow, width - valwidth);
1844 			statep->ws_overflow -= compress;
1845 			width -= compress;
1846 		}
1847 		(void) printf("%-*s", width, value);
1848 	}
1849 
1850 	if (!statep->ws_lastfield)
1851 		(void) putchar(' ');
1852 }
1853 
1854 static void
1855 print_wlan_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
1856     wladm_wlan_attr_t *attrp)
1857 {
1858 	char		buf[WLADM_STRSIZE];
1859 	const char	*str = "";
1860 
1861 	if (wfp->wf_mask == 0) {
1862 		print_wifi_field(statep, wfp, statep->ws_link);
1863 		return;
1864 	}
1865 
1866 	if ((wfp->wf_mask & attrp->wa_valid) == 0) {
1867 		print_wifi_field(statep, wfp, "");
1868 		return;
1869 	}
1870 
1871 	switch (wfp->wf_mask) {
1872 	case WLADM_WLAN_ATTR_ESSID:
1873 		str = wladm_essid2str(&attrp->wa_essid, buf);
1874 		break;
1875 	case WLADM_WLAN_ATTR_BSSID:
1876 		str = wladm_bssid2str(&attrp->wa_bssid, buf);
1877 		break;
1878 	case WLADM_WLAN_ATTR_SECMODE:
1879 		str = wladm_secmode2str(&attrp->wa_secmode, buf);
1880 		break;
1881 	case WLADM_WLAN_ATTR_STRENGTH:
1882 		str = wladm_strength2str(&attrp->wa_strength, buf);
1883 		break;
1884 	case WLADM_WLAN_ATTR_MODE:
1885 		str = wladm_mode2str(&attrp->wa_mode, buf);
1886 		break;
1887 	case WLADM_WLAN_ATTR_SPEED:
1888 		str = wladm_speed2str(&attrp->wa_speed, buf);
1889 		(void) strlcat(buf, "Mb", sizeof (buf));
1890 		break;
1891 	case WLADM_WLAN_ATTR_AUTH:
1892 		str = wladm_auth2str(&attrp->wa_auth, buf);
1893 		break;
1894 	case WLADM_WLAN_ATTR_BSSTYPE:
1895 		str = wladm_bsstype2str(&attrp->wa_bsstype, buf);
1896 		break;
1897 	}
1898 
1899 	print_wifi_field(statep, wfp, str);
1900 }
1901 
1902 static boolean_t
1903 print_scan_results(void *arg, wladm_wlan_attr_t *attrp)
1904 {
1905 	print_wifi_state_t	*statep = arg;
1906 	int			i;
1907 
1908 	if (statep->ws_header) {
1909 		statep->ws_header = B_FALSE;
1910 		if (!statep->ws_parseable)
1911 			print_wifi_head(statep);
1912 	}
1913 
1914 	statep->ws_overflow = 0;
1915 	for (i = 0; i < statep->ws_nfields; i++) {
1916 		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
1917 		print_wlan_attr(statep, statep->ws_fields[i], attrp);
1918 	}
1919 	(void) putchar('\n');
1920 	return (B_TRUE);
1921 }
1922 
1923 static boolean_t
1924 scan_wifi(void *arg, const char *link)
1925 {
1926 	print_wifi_state_t	*statep = arg;
1927 	wladm_status_t		status;
1928 
1929 	statep->ws_link = link;
1930 	status = wladm_scan(link, statep, print_scan_results);
1931 	if (status != WLADM_STATUS_OK)
1932 		die_wlerr(status, "cannot scan link '%s'", link);
1933 
1934 	return (B_TRUE);
1935 }
1936 
1937 static void
1938 print_link_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
1939     wladm_link_attr_t *attrp)
1940 {
1941 	char		buf[WLADM_STRSIZE];
1942 	const char	*str = "";
1943 
1944 	if (strcmp(wfp->wf_name, "status") == 0) {
1945 		if ((wfp->wf_mask & attrp->la_valid) != 0)
1946 			str = wladm_linkstatus2str(&attrp->la_status, buf);
1947 		print_wifi_field(statep, wfp, str);
1948 		return;
1949 	}
1950 	print_wlan_attr(statep, wfp, &attrp->la_wlan_attr);
1951 }
1952 
1953 static boolean_t
1954 show_wifi(void *arg, const char *link)
1955 {
1956 	int			i;
1957 	print_wifi_state_t	*statep = arg;
1958 	wladm_link_attr_t	attr;
1959 	wladm_status_t		status;
1960 
1961 	status = wladm_get_link_attr(link, &attr);
1962 	if (status != WLADM_STATUS_OK)
1963 		die_wlerr(status, "cannot get link attributes for '%s'", link);
1964 
1965 	if (statep->ws_header) {
1966 		statep->ws_header = B_FALSE;
1967 		if (!statep->ws_parseable)
1968 			print_wifi_head(statep);
1969 	}
1970 
1971 	statep->ws_link = link;
1972 	statep->ws_overflow = 0;
1973 	for (i = 0; i < statep->ws_nfields; i++) {
1974 		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
1975 		print_link_attr(statep, statep->ws_fields[i], &attr);
1976 	}
1977 	(void) putchar('\n');
1978 	return (B_TRUE);
1979 }
1980 
1981 static void
1982 do_display_wifi(int argc, char **argv, int cmd)
1983 {
1984 	int			option;
1985 	char			*fields_str = NULL;
1986 	wifi_field_t		**fields;
1987 	boolean_t		(*callback)(void *, const char *);
1988 	uint_t			nfields;
1989 	print_wifi_state_t	state;
1990 	wladm_status_t		status;
1991 
1992 	if (cmd == WIFI_CMD_SCAN)
1993 		callback = scan_wifi;
1994 	else if (cmd == WIFI_CMD_SHOW)
1995 		callback = show_wifi;
1996 	else
1997 		return;
1998 
1999 	state.ws_link = NULL;
2000 	state.ws_parseable = B_FALSE;
2001 	state.ws_header = B_TRUE;
2002 	opterr = 0;
2003 	while ((option = getopt_long(argc, argv, ":o:p",
2004 	    wifi_longopts, NULL)) != -1) {
2005 		switch (option) {
2006 		case 'o':
2007 			fields_str = optarg;
2008 			break;
2009 		case 'p':
2010 			state.ws_parseable = B_TRUE;
2011 			if (fields_str == NULL)
2012 				fields_str = "all";
2013 			break;
2014 		default:
2015 			die_opterr(optopt, option);
2016 			break;
2017 		}
2018 	}
2019 
2020 	if (optind == (argc - 1))
2021 		state.ws_link = argv[optind];
2022 	else if (optind != argc)
2023 		usage();
2024 
2025 	if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0)
2026 		die("invalid field(s) specified");
2027 
2028 	state.ws_fields = fields;
2029 	state.ws_nfields = nfields;
2030 
2031 	if (state.ws_link == NULL) {
2032 		status = wladm_walk(&state, callback);
2033 		if (status != WLADM_STATUS_OK)
2034 			die_wlerr(status, "cannot walk wifi links");
2035 	} else {
2036 		(void) (*callback)(&state, state.ws_link);
2037 	}
2038 	free(fields);
2039 }
2040 
2041 static void
2042 do_scan_wifi(int argc, char **argv)
2043 {
2044 	do_display_wifi(argc, argv, WIFI_CMD_SCAN);
2045 }
2046 
2047 static void
2048 do_show_wifi(int argc, char **argv)
2049 {
2050 	do_display_wifi(argc, argv, WIFI_CMD_SHOW);
2051 }
2052 
2053 typedef struct wlan_count_attr {
2054 	uint_t		wc_count;
2055 	const char	*wc_link;
2056 } wlan_count_attr_t;
2057 
2058 static boolean_t
2059 do_count_wlan(void *arg, const char *link)
2060 {
2061 	wlan_count_attr_t *cp = arg;
2062 
2063 	if (cp->wc_count == 0)
2064 		cp->wc_link = strdup(link);
2065 	cp->wc_count++;
2066 	return (B_TRUE);
2067 }
2068 
2069 static int
2070 parse_wep_keys(char *str, wladm_wep_key_t **keys, uint_t *key_countp)
2071 {
2072 	uint_t		i;
2073 	split_t		*sp;
2074 	wladm_wep_key_t	*wk;
2075 
2076 	sp = split(str, WLADM_MAX_WEPKEYS, WLADM_MAX_WEPKEYNAME_LEN);
2077 	if (sp == NULL)
2078 		return (-1);
2079 
2080 	wk = malloc(sp->s_nfields * sizeof (wladm_wep_key_t));
2081 	if (wk == NULL)
2082 		goto fail;
2083 
2084 	for (i = 0; i < sp->s_nfields; i++) {
2085 		char			*s;
2086 		dladm_secobj_class_t	class;
2087 		dladm_status_t		status;
2088 
2089 		(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
2090 		    WLADM_MAX_WEPKEYNAME_LEN);
2091 
2092 		wk[i].wk_idx = 1;
2093 		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
2094 			if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
2095 				goto fail;
2096 
2097 			wk[i].wk_idx = (uint_t)(s[1] - '0');
2098 			*s = '\0';
2099 		}
2100 		wk[i].wk_len = WLADM_MAX_WEPKEY_LEN;
2101 
2102 		status = dladm_get_secobj(wk[i].wk_name, &class,
2103 		    wk[i].wk_val, &wk[i].wk_len, 0);
2104 		if (status != DLADM_STATUS_OK) {
2105 			if (status == DLADM_STATUS_NOTFOUND) {
2106 				status = dladm_get_secobj(wk[i].wk_name,
2107 				    &class, wk[i].wk_val, &wk[i].wk_len,
2108 				    DLADM_OPT_PERSIST);
2109 			}
2110 			if (status != DLADM_STATUS_OK)
2111 				goto fail;
2112 		}
2113 	}
2114 	*keys = wk;
2115 	*key_countp = i;
2116 	splitfree(sp);
2117 	return (0);
2118 fail:
2119 	free(wk);
2120 	splitfree(sp);
2121 	return (-1);
2122 }
2123 
2124 static void
2125 do_connect_wifi(int argc, char **argv)
2126 {
2127 	int			option;
2128 	wladm_wlan_attr_t	attr, *attrp;
2129 	wladm_status_t		status = WLADM_STATUS_OK;
2130 	int			timeout = WLADM_CONNECT_TIMEOUT_DEFAULT;
2131 	const char		*link = NULL;
2132 	wladm_wep_key_t		*keys = NULL;
2133 	uint_t			key_count = 0;
2134 	uint_t			flags = 0;
2135 	wladm_secmode_t		keysecmode = WLADM_SECMODE_NONE;
2136 
2137 	opterr = 0;
2138 	(void) memset(&attr, 0, sizeof (attr));
2139 	while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
2140 	    wifi_longopts, NULL)) != -1) {
2141 		switch (option) {
2142 		case 'e':
2143 			status = wladm_str2essid(optarg, &attr.wa_essid);
2144 			if (status != WLADM_STATUS_OK)
2145 				die("invalid ESSID '%s'", optarg);
2146 
2147 			attr.wa_valid |= WLADM_WLAN_ATTR_ESSID;
2148 			/*
2149 			 * Try to connect without doing a scan.
2150 			 */
2151 			flags |= WLADM_OPT_NOSCAN;
2152 			break;
2153 		case 'i':
2154 			status = wladm_str2bssid(optarg, &attr.wa_bssid);
2155 			if (status != WLADM_STATUS_OK)
2156 				die("invalid BSSID %s", optarg);
2157 
2158 			attr.wa_valid |= WLADM_WLAN_ATTR_BSSID;
2159 			break;
2160 		case 'a':
2161 			status = wladm_str2auth(optarg, &attr.wa_auth);
2162 			if (status != WLADM_STATUS_OK)
2163 				die("invalid authentication mode '%s'", optarg);
2164 
2165 			attr.wa_valid |= WLADM_WLAN_ATTR_AUTH;
2166 			break;
2167 		case 'm':
2168 			status = wladm_str2mode(optarg, &attr.wa_mode);
2169 			if (status != WLADM_STATUS_OK)
2170 				die("invalid mode '%s'", optarg);
2171 
2172 			attr.wa_valid |= WLADM_WLAN_ATTR_MODE;
2173 			break;
2174 		case 'b':
2175 			status = wladm_str2bsstype(optarg, &attr.wa_bsstype);
2176 			if (status != WLADM_STATUS_OK)
2177 				die("invalid bsstype '%s'", optarg);
2178 
2179 			attr.wa_valid |= WLADM_WLAN_ATTR_BSSTYPE;
2180 			break;
2181 		case 's':
2182 			status = wladm_str2secmode(optarg, &attr.wa_secmode);
2183 			if (status != WLADM_STATUS_OK)
2184 				die("invalid security mode '%s'", optarg);
2185 
2186 			attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE;
2187 			break;
2188 		case 'k':
2189 			if (parse_wep_keys(optarg, &keys, &key_count) < 0)
2190 				die("invalid key(s) '%s'", optarg);
2191 
2192 			keysecmode = WLADM_SECMODE_WEP;
2193 			break;
2194 		case 'T':
2195 			if (strcasecmp(optarg, "forever") == 0) {
2196 				timeout = -1;
2197 				break;
2198 			}
2199 			if (!str2int(optarg, &timeout) || timeout < 0)
2200 				die("invalid timeout value '%s'", optarg);
2201 			break;
2202 		case 'c':
2203 			flags |= WLADM_OPT_CREATEIBSS;
2204 			break;
2205 		default:
2206 			die_opterr(optopt, option);
2207 			break;
2208 		}
2209 	}
2210 
2211 	if (keysecmode == WLADM_SECMODE_NONE) {
2212 		if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 &&
2213 		    attr.wa_secmode == WLADM_SECMODE_WEP)
2214 			die("key required for security mode 'wep'");
2215 	} else {
2216 		if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 &&
2217 		    attr.wa_secmode != keysecmode)
2218 			die("incompatible -s and -k options");
2219 	}
2220 	attr.wa_secmode = keysecmode;
2221 	attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE;
2222 
2223 	if (optind == (argc - 1))
2224 		link = argv[optind];
2225 	else if (optind != argc)
2226 		usage();
2227 
2228 	if (link == NULL) {
2229 		wlan_count_attr_t wcattr;
2230 
2231 		wcattr.wc_link = NULL;
2232 		wcattr.wc_count = 0;
2233 		(void) wladm_walk(&wcattr, do_count_wlan);
2234 		if (wcattr.wc_count == 0) {
2235 			die("no wifi links are available");
2236 		} else if (wcattr.wc_count > 1) {
2237 			die("link name is required when more than one wifi "
2238 			    "link is available");
2239 		}
2240 		link = wcattr.wc_link;
2241 	}
2242 	attrp = (attr.wa_valid == 0) ? NULL : &attr;
2243 again:
2244 	status = wladm_connect(link, attrp, timeout, keys, key_count, flags);
2245 	if (status != WLADM_STATUS_OK) {
2246 		if ((flags & WLADM_OPT_NOSCAN) != 0) {
2247 			/*
2248 			 * Try again with scanning and filtering.
2249 			 */
2250 			flags &= ~WLADM_OPT_NOSCAN;
2251 			goto again;
2252 		}
2253 
2254 		if (status == WLADM_STATUS_NOTFOUND) {
2255 			if (attr.wa_valid == 0) {
2256 				die("no wifi networks are available");
2257 			} else {
2258 				die("no wifi networks with the specified "
2259 				    "criteria are available");
2260 			}
2261 		}
2262 		die_wlerr(status, "cannot connect link '%s'", link);
2263 	}
2264 	free(keys);
2265 }
2266 
2267 /* ARGSUSED */
2268 static boolean_t
2269 do_all_disconnect_wifi(void *arg, const char *link)
2270 {
2271 	wladm_status_t	status;
2272 
2273 	status = wladm_disconnect(link);
2274 	if (status != WLADM_STATUS_OK)
2275 		warn_wlerr(status, "cannot disconnect link '%s'", link);
2276 
2277 	return (B_TRUE);
2278 }
2279 
2280 static void
2281 do_disconnect_wifi(int argc, char **argv)
2282 {
2283 	int			option;
2284 	const char		*link = NULL;
2285 	boolean_t		all_links = B_FALSE;
2286 	wladm_status_t		status;
2287 	wlan_count_attr_t	wcattr;
2288 
2289 	opterr = 0;
2290 	while ((option = getopt_long(argc, argv, ":a",
2291 	    wifi_longopts, NULL)) != -1) {
2292 		switch (option) {
2293 		case 'a':
2294 			all_links = B_TRUE;
2295 			break;
2296 		default:
2297 			die_opterr(optopt, option);
2298 			break;
2299 		}
2300 	}
2301 
2302 	if (optind == (argc - 1))
2303 		link = argv[optind];
2304 	else if (optind != argc)
2305 		usage();
2306 
2307 	if (link == NULL) {
2308 		if (!all_links) {
2309 			wcattr.wc_link = NULL;
2310 			wcattr.wc_count = 0;
2311 			(void) wladm_walk(&wcattr, do_count_wlan);
2312 			if (wcattr.wc_count == 0) {
2313 				die("no wifi links are available");
2314 			} else if (wcattr.wc_count > 1) {
2315 				die("link name is required when more than "
2316 				    "one wifi link is available");
2317 			}
2318 			link = wcattr.wc_link;
2319 		} else {
2320 			(void) wladm_walk(&all_links, do_all_disconnect_wifi);
2321 			return;
2322 		}
2323 	}
2324 	status = wladm_disconnect(link);
2325 	if (status != WLADM_STATUS_OK)
2326 		die_wlerr(status, "cannot disconnect link '%s'", link);
2327 }
2328 
2329 #define	MAX_PROPS		32
2330 #define	MAX_PROP_VALS		32
2331 #define	MAX_PROP_LINE		512
2332 
2333 typedef struct prop_info {
2334 	char		*pi_name;
2335 	char		*pi_val[MAX_PROP_VALS];
2336 	uint_t		pi_count;
2337 } prop_info_t;
2338 
2339 typedef struct prop_list {
2340 	prop_info_t	pl_info[MAX_PROPS];
2341 	uint_t		pl_count;
2342 	char		*pl_buf;
2343 } prop_list_t;
2344 
2345 typedef struct show_linkprop_state {
2346 	const char	*ls_link;
2347 	char		*ls_line;
2348 	char		**ls_propvals;
2349 	prop_list_t	*ls_proplist;
2350 	boolean_t	ls_parseable;
2351 	boolean_t	ls_persist;
2352 	boolean_t	ls_header;
2353 } show_linkprop_state_t;
2354 
2355 static void
2356 free_props(prop_list_t *list)
2357 {
2358 	if (list != NULL) {
2359 		free(list->pl_buf);
2360 		free(list);
2361 	}
2362 }
2363 
2364 static int
2365 parse_props(char *str, prop_list_t **listp, boolean_t novalues)
2366 {
2367 	prop_list_t	*list;
2368 	prop_info_t	*pip;
2369 	char		*buf, *curr;
2370 	int		len, i;
2371 
2372 	list = malloc(sizeof (prop_list_t));
2373 	if (list == NULL)
2374 		return (-1);
2375 
2376 	list->pl_count = 0;
2377 	list->pl_buf = buf = strdup(str);
2378 	if (buf == NULL)
2379 		goto fail;
2380 
2381 	curr = buf;
2382 	len = strlen(buf);
2383 	pip = NULL;
2384 	for (i = 0; i < len; i++) {
2385 		char		c = buf[i];
2386 		boolean_t	match = (c == '=' || c == ',');
2387 
2388 		if (!match && i != len - 1)
2389 			continue;
2390 
2391 		if (match) {
2392 			buf[i] = '\0';
2393 			if (*curr == '\0')
2394 				goto fail;
2395 		}
2396 
2397 		if (pip != NULL && c != '=') {
2398 			if (pip->pi_count > MAX_PROP_VALS)
2399 				goto fail;
2400 
2401 			if (novalues)
2402 				goto fail;
2403 
2404 			pip->pi_val[pip->pi_count] = curr;
2405 			pip->pi_count++;
2406 		} else {
2407 			if (list->pl_count > MAX_PROPS)
2408 				goto fail;
2409 
2410 			pip = &list->pl_info[list->pl_count];
2411 			pip->pi_name = curr;
2412 			pip->pi_count = 0;
2413 			list->pl_count++;
2414 			if (c == ',')
2415 				pip = NULL;
2416 		}
2417 		curr = buf + i + 1;
2418 	}
2419 	*listp = list;
2420 	return (0);
2421 
2422 fail:
2423 	free_props(list);
2424 	return (-1);
2425 }
2426 
2427 static void
2428 print_linkprop_head(void)
2429 {
2430 	(void) printf("%-12s %-15s %-14s %-14s %-20s \n",
2431 	    "LINK", "PROPERTY", "VALUE", "DEFAULT", "POSSIBLE");
2432 }
2433 
2434 static void
2435 print_linkprop(show_linkprop_state_t *statep, const char *propname,
2436     dladm_prop_type_t type, const char *typename, const char *format,
2437     char **pptr)
2438 {
2439 	int		i;
2440 	char		*ptr, *lim;
2441 	char		buf[DLADM_STRSIZE];
2442 	char		*unknown = "?", *notsup = "";
2443 	char		**propvals = statep->ls_propvals;
2444 	uint_t		valcnt = MAX_PROP_VALS;
2445 	dladm_status_t	status;
2446 
2447 	status = dladm_get_prop(statep->ls_link, type, propname,
2448 	    propvals, &valcnt);
2449 	if (status != DLADM_STATUS_OK) {
2450 		if (status == DLADM_STATUS_NOTSUP || statep->ls_persist) {
2451 			valcnt = 1;
2452 			if (type == DLADM_PROP_VAL_CURRENT)
2453 				propvals = &unknown;
2454 			else
2455 				propvals = &notsup;
2456 		} else {
2457 			die_dlerr(status, "cannot get link property '%s'",
2458 			    propname);
2459 		}
2460 	}
2461 
2462 	ptr = buf;
2463 	lim = buf + DLADM_STRSIZE;
2464 	for (i = 0; i < valcnt; i++) {
2465 		if (propvals[i][0] == '\0' && !statep->ls_parseable)
2466 			ptr += snprintf(ptr, lim - ptr, "--,");
2467 		else
2468 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
2469 		if (ptr >= lim)
2470 			break;
2471 	}
2472 	if (valcnt > 0)
2473 		buf[strlen(buf) - 1] = '\0';
2474 
2475 	lim = statep->ls_line + MAX_PROP_LINE;
2476 	if (statep->ls_parseable) {
2477 		*pptr += snprintf(*pptr, lim - *pptr,
2478 		    "%s=\"%s\" ", typename, buf);
2479 	} else {
2480 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
2481 	}
2482 }
2483 
2484 static boolean_t
2485 show_linkprop(void *arg, const char *propname)
2486 {
2487 	show_linkprop_state_t	*statep = arg;
2488 	char			*ptr = statep->ls_line;
2489 	char			*lim = ptr + MAX_PROP_LINE;
2490 
2491 	if (statep->ls_persist && dladm_is_prop_temponly(propname, NULL))
2492 		return (B_TRUE);
2493 
2494 	if (statep->ls_parseable)
2495 		ptr += snprintf(ptr, lim - ptr, "LINK=\"%s\" ",
2496 		    statep->ls_link);
2497 	else
2498 		ptr += snprintf(ptr, lim - ptr, "%-12s ", statep->ls_link);
2499 
2500 	if (statep->ls_parseable)
2501 		ptr += snprintf(ptr, lim - ptr, "PROPERTY=\"%s\" ", propname);
2502 	else
2503 		ptr += snprintf(ptr, lim - ptr, "%-15s ", propname);
2504 
2505 	print_linkprop(statep, propname,
2506 	    statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
2507 	    DLADM_PROP_VAL_CURRENT, "VALUE", "%-14s ", &ptr);
2508 	print_linkprop(statep, propname, DLADM_PROP_VAL_DEFAULT,
2509 	    "DEFAULT", "%-14s ", &ptr);
2510 	print_linkprop(statep, propname, DLADM_PROP_VAL_MODIFIABLE,
2511 	    "POSSIBLE", "%-20s ", &ptr);
2512 
2513 	if (statep->ls_header) {
2514 		statep->ls_header = B_FALSE;
2515 		if (!statep->ls_parseable)
2516 			print_linkprop_head();
2517 	}
2518 	(void) printf("%s\n", statep->ls_line);
2519 	return (B_TRUE);
2520 }
2521 
2522 static void
2523 do_show_linkprop(int argc, char **argv)
2524 {
2525 	int			option;
2526 	prop_list_t		*proplist = NULL;
2527 	show_linkprop_state_t	state;
2528 
2529 	opterr = 0;
2530 	state.ls_link = NULL;
2531 	state.ls_propvals = NULL;
2532 	state.ls_line = NULL;
2533 	state.ls_parseable = B_FALSE;
2534 	state.ls_persist = B_FALSE;
2535 	state.ls_header = B_TRUE;
2536 	while ((option = getopt_long(argc, argv, ":p:cP",
2537 	    prop_longopts, NULL)) != -1) {
2538 		switch (option) {
2539 		case 'p':
2540 			if (parse_props(optarg, &proplist, B_TRUE) < 0)
2541 				die("invalid link properties specified");
2542 			break;
2543 		case 'c':
2544 			state.ls_parseable = B_TRUE;
2545 			break;
2546 		case 'P':
2547 			state.ls_persist = B_TRUE;
2548 			break;
2549 		default:
2550 			die_opterr(optopt, option);
2551 			break;
2552 		}
2553 	}
2554 
2555 	if (optind == (argc - 1))
2556 		state.ls_link = argv[optind];
2557 	else if (optind != argc)
2558 		usage();
2559 
2560 	state.ls_proplist = proplist;
2561 
2562 	if (state.ls_link == NULL) {
2563 		(void) dladm_walk(show_linkprop_onelink, &state);
2564 	} else {
2565 		show_linkprop_onelink(&state, state.ls_link);
2566 	}
2567 	free_props(proplist);
2568 }
2569 
2570 static void
2571 show_linkprop_onelink(void *arg, const char *link)
2572 {
2573 	int			i, fd;
2574 	char			linkname[MAXPATHLEN];
2575 	char			*buf;
2576 	dladm_status_t		status;
2577 	prop_list_t		*proplist = NULL;
2578 	show_linkprop_state_t	*statep;
2579 	const char		*savep;
2580 
2581 	statep = (show_linkprop_state_t *)arg;
2582 	savep = statep->ls_link;
2583 	statep->ls_link = link;
2584 	proplist = statep->ls_proplist;
2585 
2586 	/*
2587 	 * When some WiFi links are opened for the first time, their hardware
2588 	 * automatically scans for APs and does other slow operations.	Thus,
2589 	 * if there are no open links, the retrieval of link properties
2590 	 * (below) will proceed slowly unless we hold the link open.
2591 	 */
2592 	(void) snprintf(linkname, MAXPATHLEN, "/dev/%s", link);
2593 	if ((fd = open(linkname, O_RDWR)) < 0)
2594 		die("cannot open %s: %s", link, strerror(errno));
2595 
2596 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS +
2597 	    MAX_PROP_LINE);
2598 	if (buf == NULL)
2599 		die("insufficient memory");
2600 
2601 	statep->ls_propvals = (char **)(void *)buf;
2602 	for (i = 0; i < MAX_PROP_VALS; i++) {
2603 		statep->ls_propvals[i] = buf + sizeof (char *) * MAX_PROP_VALS +
2604 		    i * DLADM_PROP_VAL_MAX;
2605 	}
2606 	statep->ls_line = buf +
2607 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS;
2608 
2609 	if (proplist != NULL) {
2610 		for (i = 0; i < proplist->pl_count; i++) {
2611 			if (!show_linkprop(statep,
2612 			    proplist->pl_info[i].pi_name))
2613 				break;
2614 		}
2615 	} else {
2616 		status = dladm_walk_prop(link, statep, show_linkprop);
2617 		if (status != DLADM_STATUS_OK)
2618 			die_dlerr(status, "show-linkprop");
2619 	}
2620 	(void) close(fd);
2621 	free(buf);
2622 	statep->ls_link = savep;
2623 }
2624 
2625 static dladm_status_t
2626 set_linkprop_persist(const char *link, const char *prop_name, char **prop_val,
2627     uint_t val_cnt, boolean_t reset)
2628 {
2629 	dladm_status_t	status;
2630 	char		*errprop;
2631 
2632 	status = dladm_set_prop(link, prop_name, prop_val, val_cnt,
2633 	    DLADM_OPT_PERSIST, &errprop);
2634 
2635 	if (status != DLADM_STATUS_OK) {
2636 		if (reset) {
2637 			warn_dlerr(status, "cannot persistently reset link "
2638 			    "property '%s' on '%s'", errprop, link);
2639 		} else {
2640 			warn_dlerr(status, "cannot persistently set link "
2641 			    "property '%s' on '%s'", errprop, link);
2642 		}
2643 	}
2644 	return (status);
2645 }
2646 
2647 static void
2648 set_linkprop(int argc, char **argv, boolean_t reset)
2649 {
2650 	int		i, option;
2651 	char		errmsg[DLADM_STRSIZE];
2652 	const char	*link = NULL;
2653 	prop_list_t	*proplist = NULL;
2654 	boolean_t	temp = B_FALSE;
2655 	dladm_status_t	status = DLADM_STATUS_OK;
2656 
2657 	opterr = 0;
2658 	while ((option = getopt_long(argc, argv, ":p:R:t",
2659 	    prop_longopts, NULL)) != -1) {
2660 		switch (option) {
2661 		case 'p':
2662 			if (parse_props(optarg, &proplist, reset) < 0)
2663 				die("invalid link properties specified");
2664 			break;
2665 		case 't':
2666 			temp = B_TRUE;
2667 			break;
2668 		case 'R':
2669 			status = dladm_set_rootdir(optarg);
2670 			if (status != DLADM_STATUS_OK) {
2671 				die_dlerr(status, "invalid directory "
2672 				    "specified");
2673 			}
2674 			break;
2675 		default:
2676 			die_opterr(optopt, option);
2677 			break;
2678 		}
2679 	}
2680 
2681 	if (optind == (argc - 1))
2682 		link = argv[optind];
2683 	else if (optind != argc)
2684 		usage();
2685 
2686 	if (link == NULL)
2687 		die("link name must be specified");
2688 
2689 	if (proplist == NULL) {
2690 		char *errprop;
2691 
2692 		if (!reset)
2693 			die("link property must be specified");
2694 
2695 		status = dladm_set_prop(link, NULL, NULL, 0, DLADM_OPT_TEMP,
2696 		    &errprop);
2697 		if (status != DLADM_STATUS_OK) {
2698 			warn_dlerr(status, "cannot reset link property '%s' "
2699 			    "on '%s'", errprop, link);
2700 		}
2701 		if (!temp) {
2702 			dladm_status_t	s;
2703 
2704 			s = set_linkprop_persist(link, NULL, NULL, 0, reset);
2705 			if (s != DLADM_STATUS_OK)
2706 				status = s;
2707 		}
2708 		goto done;
2709 	}
2710 
2711 	for (i = 0; i < proplist->pl_count; i++) {
2712 		prop_info_t	*pip = &proplist->pl_info[i];
2713 		char		**val;
2714 		uint_t		count;
2715 		dladm_status_t	s;
2716 
2717 		if (reset) {
2718 			val = NULL;
2719 			count = 0;
2720 		} else {
2721 			val = pip->pi_val;
2722 			count = pip->pi_count;
2723 			if (count == 0) {
2724 				warn("no value specified for '%s'",
2725 				    pip->pi_name);
2726 				status = DLADM_STATUS_BADARG;
2727 				continue;
2728 			}
2729 		}
2730 		s = dladm_set_prop(link, pip->pi_name, val, count,
2731 		    DLADM_OPT_TEMP, NULL);
2732 		if (s == DLADM_STATUS_OK) {
2733 			if (!temp) {
2734 				s = set_linkprop_persist(link,
2735 				    pip->pi_name, val, count, reset);
2736 				if (s != DLADM_STATUS_OK)
2737 					status = s;
2738 			}
2739 			continue;
2740 		}
2741 		status = s;
2742 		switch (s) {
2743 		case DLADM_STATUS_NOTFOUND:
2744 			warn("invalid link property '%s'", pip->pi_name);
2745 			break;
2746 		case DLADM_STATUS_BADVAL: {
2747 			int		j;
2748 			char		*ptr, *lim;
2749 			char		**propvals = NULL;
2750 			uint_t		valcnt = MAX_PROP_VALS;
2751 
2752 			ptr = malloc((sizeof (char *) +
2753 			    DLADM_PROP_VAL_MAX) * MAX_PROP_VALS +
2754 			    MAX_PROP_LINE);
2755 
2756 			propvals = (char **)(void *)ptr;
2757 			if (propvals == NULL)
2758 				die("insufficient memory");
2759 
2760 			for (j = 0; j < MAX_PROP_VALS; j++) {
2761 				propvals[j] = ptr + sizeof (char *) *
2762 				    MAX_PROP_VALS +
2763 				    j * DLADM_PROP_VAL_MAX;
2764 			}
2765 			s = dladm_get_prop(link, DLADM_PROP_VAL_MODIFIABLE,
2766 			    pip->pi_name, propvals, &valcnt);
2767 
2768 			ptr = errmsg;
2769 			lim = ptr + DLADM_STRSIZE;
2770 			*ptr = '\0';
2771 			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
2772 				ptr += snprintf(ptr, lim - ptr, "%s,",
2773 				    propvals[j]);
2774 				if (ptr >= lim)
2775 					break;
2776 			}
2777 			if (ptr > errmsg) {
2778 				*(ptr - 1) = '\0';
2779 				warn("link property '%s' must be one of: %s",
2780 				    pip->pi_name, errmsg);
2781 			} else
2782 				warn("invalid link property '%s'", *val);
2783 			free(propvals);
2784 			break;
2785 		}
2786 		default:
2787 			if (reset) {
2788 				warn_dlerr(status, "cannot reset link property "
2789 				    "'%s' on '%s'", pip->pi_name, link);
2790 			} else {
2791 				warn_dlerr(status, "cannot set link property "
2792 				    "'%s' on '%s'", pip->pi_name, link);
2793 			}
2794 			break;
2795 		}
2796 	}
2797 done:
2798 	free_props(proplist);
2799 	if (status != DLADM_STATUS_OK)
2800 		exit(1);
2801 }
2802 
2803 static void
2804 do_set_linkprop(int argc, char **argv)
2805 {
2806 	set_linkprop(argc, argv, B_FALSE);
2807 }
2808 
2809 static void
2810 do_reset_linkprop(int argc, char **argv)
2811 {
2812 	set_linkprop(argc, argv, B_TRUE);
2813 }
2814 
2815 static int
2816 convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
2817     dladm_secobj_class_t class)
2818 {
2819 	int error = 0;
2820 
2821 	if (class != DLADM_SECOBJ_CLASS_WEP)
2822 		return (ENOENT);
2823 
2824 	switch (len) {
2825 	case 5:			/* ASCII key sizes */
2826 	case 13:
2827 		(void) memcpy(obj_val, buf, len);
2828 		*obj_lenp = len;
2829 		break;
2830 	case 10:		/* Hex key sizes, not preceded by 0x */
2831 	case 26:
2832 		error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
2833 		break;
2834 	case 12:		/* Hex key sizes, preceded by 0x */
2835 	case 28:
2836 		if (strncmp(buf, "0x", 2) != 0)
2837 			return (EINVAL);
2838 		error = hexascii_to_octet(buf + 2, len - 2, obj_val, obj_lenp);
2839 		break;
2840 	default:
2841 		return (EINVAL);
2842 	}
2843 	return (error);
2844 }
2845 
2846 /* ARGSUSED */
2847 static void
2848 defersig(int sig)
2849 {
2850 	signalled = sig;
2851 }
2852 
2853 static int
2854 get_secobj_from_tty(uint_t try, const char *objname, char *buf)
2855 {
2856 	uint_t		len = 0;
2857 	int		c;
2858 	struct termios	stored, current;
2859 	void		(*sigfunc)(int);
2860 
2861 	/*
2862 	 * Turn off echo -- but before we do so, defer SIGINT handling
2863 	 * so that a ^C doesn't leave the terminal corrupted.
2864 	 */
2865 	sigfunc = signal(SIGINT, defersig);
2866 	(void) fflush(stdin);
2867 	(void) tcgetattr(0, &stored);
2868 	current = stored;
2869 	current.c_lflag &= ~(ICANON|ECHO);
2870 	current.c_cc[VTIME] = 0;
2871 	current.c_cc[VMIN] = 1;
2872 	(void) tcsetattr(0, TCSANOW, &current);
2873 again:
2874 	if (try == 1)
2875 		(void) printf(gettext("provide value for '%s': "), objname);
2876 	else
2877 		(void) printf(gettext("confirm value for '%s': "), objname);
2878 
2879 	(void) fflush(stdout);
2880 	while (signalled == 0) {
2881 		c = getchar();
2882 		if (c == '\n' || c == '\r') {
2883 			if (len != 0)
2884 				break;
2885 			(void) putchar('\n');
2886 			goto again;
2887 		}
2888 
2889 		buf[len++] = c;
2890 		if (len >= DLADM_SECOBJ_VAL_MAX - 1)
2891 			break;
2892 		(void) putchar('*');
2893 	}
2894 
2895 	(void) putchar('\n');
2896 	(void) fflush(stdin);
2897 
2898 	/*
2899 	 * Restore terminal setting and handle deferred signals.
2900 	 */
2901 	(void) tcsetattr(0, TCSANOW, &stored);
2902 
2903 	(void) signal(SIGINT, sigfunc);
2904 	if (signalled != 0)
2905 		(void) kill(getpid(), signalled);
2906 
2907 	return (len);
2908 }
2909 
2910 static int
2911 get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
2912     dladm_secobj_class_t class, FILE *filep)
2913 {
2914 	int		rval;
2915 	uint_t		len, len2;
2916 	char		buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
2917 
2918 	if (filep == NULL) {
2919 		len = get_secobj_from_tty(1, obj_name, buf);
2920 		rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
2921 		if (rval == 0) {
2922 			len2 = get_secobj_from_tty(2, obj_name, buf2);
2923 			if (len != len2 || memcmp(buf, buf2, len) != 0)
2924 				rval = ENOTSUP;
2925 		}
2926 		return (rval);
2927 	} else {
2928 		for (;;) {
2929 			if (fgets(buf, sizeof (buf), filep) == NULL)
2930 				break;
2931 			if (isspace(buf[0]))
2932 				continue;
2933 
2934 			len = strlen(buf);
2935 			if (buf[len - 1] == '\n') {
2936 				buf[len - 1] = '\0';
2937 				len--;
2938 			}
2939 			break;
2940 		}
2941 		(void) fclose(filep);
2942 	}
2943 	return (convert_secobj(buf, len, obj_val, obj_lenp, class));
2944 }
2945 
2946 static boolean_t
2947 check_auth(const char *auth)
2948 {
2949 	struct passwd	*pw;
2950 
2951 	if ((pw = getpwuid(getuid())) == NULL)
2952 		return (B_FALSE);
2953 
2954 	return (chkauthattr(auth, pw->pw_name) != 0);
2955 }
2956 
2957 static void
2958 audit_secobj(char *auth, char *class, char *obj,
2959     boolean_t success, boolean_t create)
2960 {
2961 	adt_session_data_t	*ah;
2962 	adt_event_data_t	*event;
2963 	au_event_t		flag;
2964 	char			*errstr;
2965 
2966 	if (create) {
2967 		flag = ADT_dladm_create_secobj;
2968 		errstr = "ADT_dladm_create_secobj";
2969 	} else {
2970 		flag = ADT_dladm_delete_secobj;
2971 		errstr = "ADT_dladm_delete_secobj";
2972 	}
2973 
2974 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
2975 		die("adt_start_session: %s", strerror(errno));
2976 
2977 	if ((event = adt_alloc_event(ah, flag)) == NULL)
2978 		die("adt_alloc_event (%s): %s", errstr, strerror(errno));
2979 
2980 	/* fill in audit info */
2981 	if (create) {
2982 		event->adt_dladm_create_secobj.auth_used = auth;
2983 		event->adt_dladm_create_secobj.obj_class = class;
2984 		event->adt_dladm_create_secobj.obj_name = obj;
2985 	} else {
2986 		event->adt_dladm_delete_secobj.auth_used = auth;
2987 		event->adt_dladm_delete_secobj.obj_class = class;
2988 		event->adt_dladm_delete_secobj.obj_name = obj;
2989 	}
2990 
2991 	if (success) {
2992 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
2993 			die("adt_put_event (%s, success): %s", errstr,
2994 			    strerror(errno));
2995 		}
2996 	} else {
2997 		if (adt_put_event(event, ADT_FAILURE,
2998 		    ADT_FAIL_VALUE_AUTH) != 0) {
2999 			die("adt_put_event: (%s, failure): %s", errstr,
3000 			    strerror(errno));
3001 		}
3002 	}
3003 
3004 	adt_free_event(event);
3005 	(void) adt_end_session(ah);
3006 }
3007 
3008 #define	MAX_SECOBJS		32
3009 #define	MAX_SECOBJ_NAMELEN	32
3010 static void
3011 do_create_secobj(int argc, char **argv)
3012 {
3013 	int			option, rval;
3014 	FILE			*filep = NULL;
3015 	char			*obj_name = NULL;
3016 	char			*class_name = NULL;
3017 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
3018 	uint_t			obj_len;
3019 	boolean_t		success, temp = B_FALSE;
3020 	dladm_status_t		status;
3021 	dladm_secobj_class_t	class = -1;
3022 	uid_t			euid;
3023 
3024 	opterr = 0;
3025 	(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
3026 	while ((option = getopt_long(argc, argv, ":f:c:R:t",
3027 	    wifi_longopts, NULL)) != -1) {
3028 		switch (option) {
3029 		case 'f':
3030 			euid = geteuid();
3031 			(void) seteuid(getuid());
3032 			filep = fopen(optarg, "r");
3033 			if (filep == NULL) {
3034 				die("cannot open %s: %s", optarg,
3035 				    strerror(errno));
3036 			}
3037 			(void) seteuid(euid);
3038 			break;
3039 		case 'c':
3040 			class_name = optarg;
3041 			status = dladm_str2secobjclass(optarg, &class);
3042 			if (status != DLADM_STATUS_OK) {
3043 				die("invalid secure object class '%s', "
3044 				    "valid values are: wep", optarg);
3045 			}
3046 			break;
3047 		case 't':
3048 			temp = B_TRUE;
3049 			break;
3050 		case 'R':
3051 			status = dladm_set_rootdir(optarg);
3052 			if (status != DLADM_STATUS_OK) {
3053 				die_dlerr(status, "invalid directory "
3054 				    "specified");
3055 			}
3056 			break;
3057 		default:
3058 			die_opterr(optopt, option);
3059 			break;
3060 		}
3061 	}
3062 
3063 	if (optind == (argc - 1))
3064 		obj_name = argv[optind];
3065 	else if (optind != argc)
3066 		usage();
3067 
3068 	if (class == -1)
3069 		die("secure object class required");
3070 
3071 	if (obj_name == NULL)
3072 		die("secure object name required");
3073 
3074 	success = check_auth(LINK_SEC_AUTH);
3075 	audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
3076 	if (!success)
3077 		die("authorization '%s' is required", LINK_SEC_AUTH);
3078 
3079 	rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
3080 	if (rval != 0) {
3081 		switch (rval) {
3082 		case ENOENT:
3083 			die("invalid secure object class");
3084 			break;
3085 		case EINVAL:
3086 			die("invalid secure object value");
3087 			break;
3088 		case ENOTSUP:
3089 			die("verification failed");
3090 			break;
3091 		default:
3092 			die("invalid secure object: %s", strerror(rval));
3093 			break;
3094 		}
3095 	}
3096 
3097 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
3098 	    DLADM_OPT_CREATE | DLADM_OPT_TEMP);
3099 	if (status != DLADM_STATUS_OK) {
3100 		die_dlerr(status, "could not create secure object '%s'",
3101 		    obj_name);
3102 	}
3103 	if (temp)
3104 		return;
3105 
3106 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
3107 	    DLADM_OPT_PERSIST);
3108 	if (status != DLADM_STATUS_OK) {
3109 		warn_dlerr(status, "could not persistently create secure "
3110 		    "object '%s'", obj_name);
3111 	}
3112 }
3113 
3114 static void
3115 do_delete_secobj(int argc, char **argv)
3116 {
3117 	int		i, option;
3118 	boolean_t	temp = B_FALSE;
3119 	split_t		*sp = NULL;
3120 	boolean_t	success;
3121 	dladm_status_t	status, pstatus;
3122 
3123 	opterr = 0;
3124 	status = pstatus = DLADM_STATUS_OK;
3125 	while ((option = getopt_long(argc, argv, ":R:t",
3126 	    wifi_longopts, NULL)) != -1) {
3127 		switch (option) {
3128 		case 't':
3129 			temp = B_TRUE;
3130 			break;
3131 		case 'R':
3132 			status = dladm_set_rootdir(optarg);
3133 			if (status != DLADM_STATUS_OK) {
3134 				die_dlerr(status, "invalid directory "
3135 				    "specified");
3136 			}
3137 			break;
3138 		default:
3139 			die_opterr(optopt, option);
3140 			break;
3141 		}
3142 	}
3143 
3144 	if (optind == (argc - 1)) {
3145 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
3146 		if (sp == NULL) {
3147 			die("invalid secure object name(s): '%s'",
3148 			    argv[optind]);
3149 		}
3150 	} else if (optind != argc)
3151 		usage();
3152 
3153 	if (sp == NULL || sp->s_nfields < 1)
3154 		die("secure object name required");
3155 
3156 	success = check_auth(LINK_SEC_AUTH);
3157 	audit_secobj(LINK_SEC_AUTH, "wep", argv[optind], success, B_FALSE);
3158 	if (!success)
3159 		die("authorization '%s' is required", LINK_SEC_AUTH);
3160 
3161 	for (i = 0; i < sp->s_nfields; i++) {
3162 		status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_TEMP);
3163 		if (!temp) {
3164 			pstatus = dladm_unset_secobj(sp->s_fields[i],
3165 			    DLADM_OPT_PERSIST);
3166 		} else {
3167 			pstatus = DLADM_STATUS_OK;
3168 		}
3169 
3170 		if (status != DLADM_STATUS_OK) {
3171 			warn_dlerr(status, "could not delete secure object "
3172 			    "'%s'", sp->s_fields[i]);
3173 		}
3174 		if (pstatus != DLADM_STATUS_OK) {
3175 			warn_dlerr(pstatus, "could not persistently delete "
3176 			    "secure object '%s'", sp->s_fields[i]);
3177 		}
3178 	}
3179 	if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK)
3180 		exit(1);
3181 }
3182 
3183 typedef struct show_secobj_state {
3184 	boolean_t	ss_persist;
3185 	boolean_t	ss_parseable;
3186 	boolean_t	ss_debug;
3187 	boolean_t	ss_header;
3188 } show_secobj_state_t;
3189 
3190 static void
3191 print_secobj_head(show_secobj_state_t *statep)
3192 {
3193 	(void) printf("%-20s %-20s ", "OBJECT", "CLASS");
3194 	if (statep->ss_debug)
3195 		(void) printf("%-30s", "VALUE");
3196 	(void) putchar('\n');
3197 }
3198 
3199 static boolean_t
3200 show_secobj(void *arg, const char *obj_name)
3201 {
3202 	uint_t			obj_len = DLADM_SECOBJ_VAL_MAX;
3203 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
3204 	char			buf[DLADM_STRSIZE];
3205 	uint_t			flags = 0;
3206 	dladm_secobj_class_t	class;
3207 	show_secobj_state_t	*statep = arg;
3208 	dladm_status_t		status;
3209 
3210 	if (statep->ss_persist)
3211 		flags |= DLADM_OPT_PERSIST;
3212 
3213 	status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags);
3214 	if (status != DLADM_STATUS_OK)
3215 		die_dlerr(status, "cannot get secure object '%s'", obj_name);
3216 
3217 	if (statep->ss_header) {
3218 		statep->ss_header = B_FALSE;
3219 		if (!statep->ss_parseable)
3220 			print_secobj_head(statep);
3221 	}
3222 
3223 	if (statep->ss_parseable) {
3224 		(void) printf("OBJECT=\"%s\" CLASS=\"%s\" ", obj_name,
3225 		    dladm_secobjclass2str(class, buf));
3226 	} else {
3227 		(void) printf("%-20s %-20s ", obj_name,
3228 		    dladm_secobjclass2str(class, buf));
3229 	}
3230 
3231 	if (statep->ss_debug) {
3232 		char	val[DLADM_SECOBJ_VAL_MAX * 2];
3233 		uint_t	len = sizeof (val);
3234 
3235 		if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0) {
3236 			if (statep->ss_parseable)
3237 				(void) printf("VALUE=\"0x%s\"", val);
3238 			else
3239 				(void) printf("0x%-30s", val);
3240 		}
3241 	}
3242 	(void) putchar('\n');
3243 	return (B_TRUE);
3244 }
3245 
3246 static void
3247 do_show_secobj(int argc, char **argv)
3248 {
3249 	int			option;
3250 	show_secobj_state_t	state;
3251 	dladm_status_t		status;
3252 	uint_t			i;
3253 	split_t			*sp;
3254 	uint_t			flags;
3255 
3256 	opterr = 0;
3257 	state.ss_persist = B_FALSE;
3258 	state.ss_parseable = B_FALSE;
3259 	state.ss_debug = B_FALSE;
3260 	state.ss_header = B_TRUE;
3261 	while ((option = getopt_long(argc, argv, ":pPd",
3262 	    wifi_longopts, NULL)) != -1) {
3263 		switch (option) {
3264 		case 'p':
3265 			state.ss_parseable = B_TRUE;
3266 			break;
3267 		case 'P':
3268 			state.ss_persist = B_TRUE;
3269 			break;
3270 		case 'd':
3271 			if (getuid() != 0)
3272 				die("insufficient privileges");
3273 			state.ss_debug = B_TRUE;
3274 			break;
3275 		default:
3276 			die_opterr(optopt, option);
3277 			break;
3278 		}
3279 	}
3280 
3281 	if (optind == (argc - 1)) {
3282 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
3283 		if (sp == NULL) {
3284 			die("invalid secure object name(s): '%s'",
3285 			    argv[optind]);
3286 		}
3287 		for (i = 0; i < sp->s_nfields; i++) {
3288 			if (!show_secobj(&state, sp->s_fields[i]))
3289 				break;
3290 		}
3291 		splitfree(sp);
3292 		return;
3293 	} else if (optind != argc)
3294 		usage();
3295 
3296 	flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
3297 	status = dladm_walk_secobj(&state, show_secobj, flags);
3298 	if (status != DLADM_STATUS_OK)
3299 		die_dlerr(status, "show-secobj");
3300 }
3301 
3302 /* ARGSUSED */
3303 static void
3304 do_init_linkprop(int argc, char **argv)
3305 {
3306 	dladm_status_t status;
3307 
3308 	status = dladm_init_linkprop();
3309 	if (status != DLADM_STATUS_OK)
3310 		die_dlerr(status, "link property initialization failed");
3311 }
3312 
3313 /* ARGSUSED */
3314 static void
3315 do_init_secobj(int argc, char **argv)
3316 {
3317 	dladm_status_t status;
3318 
3319 	status = dladm_init_secobj();
3320 	if (status != DLADM_STATUS_OK)
3321 		die_dlerr(status, "secure object initialization failed");
3322 }
3323 
3324 static boolean_t
3325 str2int(const char *str, int *valp)
3326 {
3327 	int	val;
3328 	char	*endp = NULL;
3329 
3330 	errno = 0;
3331 	val = strtol(str, &endp, 10);
3332 	if (errno != 0 || *endp != '\0')
3333 		return (B_FALSE);
3334 
3335 	*valp = val;
3336 	return (B_TRUE);
3337 }
3338 
3339 /* PRINTFLIKE1 */
3340 static void
3341 warn(const char *format, ...)
3342 {
3343 	va_list alist;
3344 
3345 	format = gettext(format);
3346 	(void) fprintf(stderr, "%s: warning: ", progname);
3347 
3348 	va_start(alist, format);
3349 	(void) vfprintf(stderr, format, alist);
3350 	va_end(alist);
3351 
3352 	(void) putchar('\n');
3353 }
3354 
3355 /* PRINTFLIKE2 */
3356 static void
3357 warn_wlerr(wladm_status_t err, const char *format, ...)
3358 {
3359 	va_list alist;
3360 	char	errmsg[WLADM_STRSIZE];
3361 
3362 	format = gettext(format);
3363 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
3364 
3365 	va_start(alist, format);
3366 	(void) vfprintf(stderr, format, alist);
3367 	va_end(alist);
3368 	(void) fprintf(stderr, ": %s\n", wladm_status2str(err, errmsg));
3369 }
3370 
3371 /* PRINTFLIKE2 */
3372 static void
3373 warn_dlerr(dladm_status_t err, const char *format, ...)
3374 {
3375 	va_list alist;
3376 	char	errmsg[DLADM_STRSIZE];
3377 
3378 	format = gettext(format);
3379 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
3380 
3381 	va_start(alist, format);
3382 	(void) vfprintf(stderr, format, alist);
3383 	va_end(alist);
3384 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
3385 }
3386 
3387 /* PRINTFLIKE2 */
3388 static void
3389 die_laerr(laadm_diag_t diag, const char *format, ...)
3390 {
3391 	va_list alist;
3392 	char	*errstr = strerror(errno);
3393 
3394 	format = gettext(format);
3395 	(void) fprintf(stderr, "%s: ", progname);
3396 
3397 	va_start(alist, format);
3398 	(void) vfprintf(stderr, format, alist);
3399 	va_end(alist);
3400 
3401 	if (diag == 0)
3402 		(void) fprintf(stderr, ": %s\n", errstr);
3403 	else
3404 		(void) fprintf(stderr, ": %s (%s)\n", errstr, laadm_diag(diag));
3405 
3406 	exit(EXIT_FAILURE);
3407 }
3408 
3409 /* PRINTFLIKE2 */
3410 static void
3411 die_wlerr(wladm_status_t err, const char *format, ...)
3412 {
3413 	va_list alist;
3414 	char	errmsg[WLADM_STRSIZE];
3415 
3416 	format = gettext(format);
3417 	(void) fprintf(stderr, "%s: ", progname);
3418 
3419 	va_start(alist, format);
3420 	(void) vfprintf(stderr, format, alist);
3421 	va_end(alist);
3422 	(void) fprintf(stderr, ": %s\n", wladm_status2str(err, errmsg));
3423 
3424 	exit(EXIT_FAILURE);
3425 }
3426 
3427 /* PRINTFLIKE2 */
3428 static void
3429 die_dlerr(dladm_status_t err, const char *format, ...)
3430 {
3431 	va_list alist;
3432 	char	errmsg[DLADM_STRSIZE];
3433 
3434 	format = gettext(format);
3435 	(void) fprintf(stderr, "%s: ", progname);
3436 
3437 	va_start(alist, format);
3438 	(void) vfprintf(stderr, format, alist);
3439 	va_end(alist);
3440 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
3441 
3442 	exit(EXIT_FAILURE);
3443 }
3444 
3445 /* PRINTFLIKE1 */
3446 static void
3447 die(const char *format, ...)
3448 {
3449 	va_list alist;
3450 
3451 	format = gettext(format);
3452 	(void) fprintf(stderr, "%s: ", progname);
3453 
3454 	va_start(alist, format);
3455 	(void) vfprintf(stderr, format, alist);
3456 	va_end(alist);
3457 
3458 	(void) putchar('\n');
3459 	exit(EXIT_FAILURE);
3460 }
3461 
3462 static void
3463 die_optdup(int opt)
3464 {
3465 	die("the option -%c cannot be specified more than once", opt);
3466 }
3467 
3468 static void
3469 die_opterr(int opt, int opterr)
3470 {
3471 	switch (opterr) {
3472 	case ':':
3473 		die("option '-%c' requires a value", opt);
3474 		break;
3475 	case '?':
3476 	default:
3477 		die("unrecognized option '-%c'", opt);
3478 		break;
3479 	}
3480 }
3481