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