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