xref: /titanic_50/usr/src/cmd/dladm/dladm.c (revision 575bd8a230f2c20b1878ebb473c30a12f0eb1b2e)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <locale.h>
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <stropts.h>
37 #include <sys/stat.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 <libdevinfo.h>
50 #include <libdlpi.h>
51 #include <libdllink.h>
52 #include <libdlaggr.h>
53 #include <libdlwlan.h>
54 #include <libdlvlan.h>
55 #include <libdlvnic.h>
56 #include <libinetutil.h>
57 #include <bsm/adt.h>
58 #include <bsm/adt_event.h>
59 
60 #define	AGGR_DRV		"aggr"
61 #define	MAXPORT			256
62 #define	BUFLEN(lim, ptr)	(((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
63 #define	MAXLINELEN		1024
64 #define	SMF_UPGRADE_FILE		"/var/svc/profile/upgrade"
65 #define	SMF_UPGRADEDATALINK_FILE	"/var/svc/profile/upgrade_datalink"
66 #define	SMF_DLADM_UPGRADE_MSG		" # added by dladm(1M)"
67 
68 typedef struct pktsum_s {
69 	uint64_t	ipackets;
70 	uint64_t	opackets;
71 	uint64_t	rbytes;
72 	uint64_t	obytes;
73 	uint32_t	ierrors;
74 	uint32_t	oerrors;
75 } pktsum_t;
76 
77 typedef struct show_state {
78 	boolean_t	ls_firstonly;
79 	boolean_t	ls_donefirst;
80 	pktsum_t	ls_prevstats;
81 	boolean_t	ls_parseable;
82 	uint32_t	ls_flags;
83 	dladm_status_t	ls_status;
84 } show_state_t;
85 
86 typedef struct show_grp_state {
87 	boolean_t	gs_lacp;
88 	boolean_t	gs_extended;
89 	boolean_t	gs_stats;
90 	boolean_t	gs_firstonly;
91 	boolean_t	gs_donefirst;
92 	pktsum_t	gs_prevstats[MAXPORT];
93 	boolean_t	gs_parseable;
94 	uint32_t	gs_flags;
95 	dladm_status_t	gs_status;
96 } show_grp_state_t;
97 
98 typedef void cmdfunc_t(int, char **);
99 
100 static cmdfunc_t do_show_link, do_show_dev, do_show_wifi, do_show_phys;
101 static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
102 static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr;
103 static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
104 static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
105 static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
106 static cmdfunc_t do_init_linkprop, do_init_secobj;
107 static cmdfunc_t do_create_vlan, do_delete_vlan, do_up_vlan, do_show_vlan;
108 static cmdfunc_t do_rename_link, do_delete_phys, do_init_phys;
109 static cmdfunc_t do_show_linkmap;
110 
111 static void	altroot_cmd(char *, int, char **);
112 static int	show_linkprop_onelink(datalink_id_t, void *);
113 
114 static void	link_stats(datalink_id_t, uint_t);
115 static void	aggr_stats(datalink_id_t, show_grp_state_t *, uint_t);
116 static void	dev_stats(const char *dev, uint32_t);
117 
118 static int	get_one_kstat(const char *, const char *, uint8_t,
119 		    void *, boolean_t);
120 static void	get_mac_stats(const char *, pktsum_t *);
121 static void	get_link_stats(const char *, pktsum_t *);
122 static uint64_t	get_ifspeed(const char *, boolean_t);
123 static void	stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
124 static void	stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
125 static const char	*get_linkstate(const char *, boolean_t, char *);
126 static const char	*get_linkduplex(const char *, boolean_t, char *);
127 
128 static boolean_t str2int(const char *, int *);
129 static void	die(const char *, ...);
130 static void	die_optdup(int);
131 static void	die_opterr(int, int);
132 static void	die_dlerr(dladm_status_t, const char *, ...);
133 static void	warn(const char *, ...);
134 static void	warn_dlerr(dladm_status_t, const char *, ...);
135 
136 typedef struct	cmd {
137 	char		*c_name;
138 	cmdfunc_t	*c_fn;
139 } cmd_t;
140 
141 static cmd_t	cmds[] = {
142 	{ "show-link",		do_show_link		},
143 	{ "show-dev",		do_show_dev		},
144 	{ "create-aggr",	do_create_aggr		},
145 	{ "delete-aggr",	do_delete_aggr		},
146 	{ "add-aggr",		do_add_aggr		},
147 	{ "remove-aggr",	do_remove_aggr		},
148 	{ "modify-aggr",	do_modify_aggr		},
149 	{ "show-aggr",		do_show_aggr		},
150 	{ "up-aggr",		do_up_aggr		},
151 	{ "scan-wifi",		do_scan_wifi		},
152 	{ "connect-wifi",	do_connect_wifi		},
153 	{ "disconnect-wifi",	do_disconnect_wifi	},
154 	{ "show-wifi",		do_show_wifi		},
155 	{ "show-linkprop",	do_show_linkprop	},
156 	{ "set-linkprop",	do_set_linkprop		},
157 	{ "reset-linkprop",	do_reset_linkprop	},
158 	{ "create-secobj",	do_create_secobj	},
159 	{ "delete-secobj",	do_delete_secobj	},
160 	{ "show-secobj",	do_show_secobj		},
161 	{ "init-linkprop",	do_init_linkprop	},
162 	{ "init-secobj",	do_init_secobj		},
163 	{ "create-vlan", 	do_create_vlan 		},
164 	{ "delete-vlan", 	do_delete_vlan 		},
165 	{ "show-vlan",		do_show_vlan		},
166 	{ "up-vlan",		do_up_vlan		},
167 	{ "rename-link",	do_rename_link 		},
168 	{ "delete-phys",	do_delete_phys 		},
169 	{ "show-phys",		do_show_phys		},
170 	{ "init-phys",		do_init_phys		},
171 	{ "show-linkmap",	do_show_linkmap		}
172 };
173 
174 static const struct option lopts[] = {
175 	{"vlan-id",	required_argument,	0, 'v'},
176 	{"dev",		required_argument,	0, 'd'},
177 	{"policy",	required_argument,	0, 'P'},
178 	{"lacp-mode",	required_argument,	0, 'L'},
179 	{"lacp-timer",	required_argument,	0, 'T'},
180 	{"unicast",	required_argument,	0, 'u'},
181 	{"temporary",	no_argument,		0, 't'},
182 	{"root-dir",	required_argument,	0, 'R'},
183 	{"link",	required_argument,	0, 'l'},
184 	{"forcible",	no_argument,		0, 'f'},
185 	{ 0, 0, 0, 0 }
186 };
187 
188 static const struct option show_lopts[] = {
189 	{"statistics",	no_argument,		0, 's'},
190 	{"interval",	required_argument,	0, 'i'},
191 	{"parseable",	no_argument,		0, 'p'},
192 	{"extended",	no_argument,		0, 'x'},
193 	{"persistent",	no_argument,		0, 'P'},
194 	{"lacp",	no_argument,		0, 'L'},
195 	{ 0, 0, 0, 0 }
196 };
197 
198 static const struct option prop_longopts[] = {
199 	{"temporary",	no_argument,		0, 't'  },
200 	{"root-dir",	required_argument,	0, 'R'  },
201 	{"prop",	required_argument,	0, 'p'  },
202 	{"parseable",	no_argument,		0, 'c'  },
203 	{"persistent",	no_argument,		0, 'P'  },
204 	{ 0, 0, 0, 0 }
205 };
206 
207 static const struct option wifi_longopts[] = {
208 	{"parseable",	no_argument,		0, 'p'  },
209 	{"output",	required_argument,	0, 'o'  },
210 	{"essid",	required_argument,	0, 'e'  },
211 	{"bsstype",	required_argument,	0, 'b'  },
212 	{"mode",	required_argument,	0, 'm'  },
213 	{"key",		required_argument,	0, 'k'  },
214 	{"sec",		required_argument,	0, 's'  },
215 	{"auth",	required_argument,	0, 'a'  },
216 	{"create-ibss",	required_argument,	0, 'c'  },
217 	{"timeout",	required_argument,	0, 'T'  },
218 	{"all-links",	no_argument,		0, 'a'  },
219 	{"temporary",	no_argument,		0, 't'  },
220 	{"root-dir",	required_argument,	0, 'R'  },
221 	{"persistent",	no_argument,		0, 'P'  },
222 	{"file",	required_argument,	0, 'f'  },
223 	{ 0, 0, 0, 0 }
224 };
225 
226 static char *progname;
227 static sig_atomic_t signalled;
228 
229 static void
230 usage(void)
231 {
232 	(void) fprintf(stderr, gettext("usage:	dladm <subcommand> <args> ...\n"
233 	    "\tshow-link       [-pP] [-s [-i <interval>]] [<link>]\n"
234 	    "\trename-link     [-R <root-dir>] <oldlink> <newlink>\n"
235 	    "\n"
236 	    "\tdelete-phys     <link>\n"
237 	    "\tshow-phys       [-pP] [<link>]\n"
238 	    "\tshow-dev        [-p]  [-s [-i <interval>]] [<dev>]\n"
239 	    "\n"
240 	    "\tcreate-aggr     [-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n"
241 	    "\t		[-T <time>] [-u <address>] [-l <link>] ... <link>\n"
242 	    "\tmodify-aggr     [-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n"
243 	    "\t		[-T <time>] [-u <address>] <link>\n"
244 	    "\tdelete-aggr     [-t] [-R <root-dir>] <link>\n"
245 	    "\tadd-aggr	[-t] [-R <root-dir>] [-l <link>] ... <link>\n"
246 	    "\tremove-aggr     [-t] [-R <root-dir>] [-l <link>] ... <link>"
247 	    "\n\tshow-aggr       [-pPLx][-s [-i <interval>]] [<link>]\n"
248 	    "\n"
249 	    "\tcreate-vlan     [-ft] [-R <root-dir>] -l <link> -v <vid> [link]"
250 	    "\n\tdelete-vlan     [-t]  [-R <root-dir>] <link>\n"
251 	    "\tshow-vlan       [-pP] [<link>]\n"
252 	    "\n"
253 	    "\tscan-wifi       [-p] [-o <field>,...] [<link>]\n"
254 	    "\tconnect-wifi    [-e <essid>] [-i <bssid>] [-k <key>,...]"
255 	    " [-s wep|wpa]\n"
256 	    "\t                [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
257 	    "\t                [-T <time>] [<link>]\n"
258 	    "\tdisconnect-wifi [-a] [<link>]\n"
259 	    "\tshow-wifi       [-p] [-o <field>,...] [<link>]\n"
260 	    "\n"
261 	    "\tset-linkprop    [-t] [-R <root-dir>]  -p <prop>=<value>[,...]"
262 	    " <name>\n"
263 	    "\treset-linkprop  [-t] [-R <root-dir>] [-p <prop>,...] <name>\n"
264 	    "\tshow-linkprop   [-cP][-p <prop>,...] <name>\n"
265 	    "\n"
266 	    "\tcreate-secobj   [-t] [-R <root-dir>] [-f <file>] -c <class>"
267 	    " <secobj>\n"
268 	    "\tdelete-secobj   [-t] [-R <root-dir>] <secobj>[,...]\n"
269 	    "\tshow-secobj     [-pP][<secobj>,...]\n"));
270 	exit(1);
271 }
272 
273 int
274 main(int argc, char *argv[])
275 {
276 	int	i;
277 	cmd_t	*cmdp;
278 
279 	(void) setlocale(LC_ALL, "");
280 #if !defined(TEXT_DOMAIN)
281 #define	TEXT_DOMAIN "SYS_TEST"
282 #endif
283 	(void) textdomain(TEXT_DOMAIN);
284 
285 	progname = argv[0];
286 
287 	if (argc < 2)
288 		usage();
289 
290 	if (!priv_ineffect(PRIV_SYS_NET_CONFIG) ||
291 	    !priv_ineffect(PRIV_NET_RAWACCESS))
292 		die("insufficient privileges");
293 
294 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
295 		cmdp = &cmds[i];
296 		if (strcmp(argv[1], cmdp->c_name) == 0) {
297 			cmdp->c_fn(argc - 1, &argv[1]);
298 			exit(0);
299 		}
300 	}
301 
302 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
303 	    progname, argv[1]);
304 	usage();
305 
306 	return (0);
307 }
308 
309 static void
310 do_create_aggr(int argc, char *argv[])
311 {
312 	char			option;
313 	int			key = 0;
314 	uint32_t		policy = AGGR_POLICY_L4;
315 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
316 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
317 	dladm_aggr_port_attr_db_t	port[MAXPORT];
318 	uint_t			n, ndev, nlink;
319 	uint8_t			mac_addr[ETHERADDRL];
320 	boolean_t		mac_addr_fixed = B_FALSE;
321 	boolean_t		P_arg = B_FALSE;
322 	boolean_t		l_arg = B_FALSE;
323 	boolean_t		u_arg = B_FALSE;
324 	boolean_t		T_arg = B_FALSE;
325 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
326 	char			*altroot = NULL;
327 	char			name[MAXLINKNAMELEN];
328 	char			*devs[MAXPORT];
329 	char			*links[MAXPORT];
330 	dladm_status_t		status;
331 
332 	ndev = nlink = opterr = 0;
333 	while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:",
334 	    lopts, NULL)) != -1) {
335 		switch (option) {
336 		case 'd':
337 			if (ndev + nlink >= MAXPORT)
338 				die("too many ports specified");
339 
340 			devs[ndev++] = optarg;
341 			break;
342 		case 'P':
343 			if (P_arg)
344 				die_optdup(option);
345 
346 			P_arg = B_TRUE;
347 			if (!dladm_aggr_str2policy(optarg, &policy))
348 				die("invalid policy '%s'", optarg);
349 			break;
350 		case 'u':
351 			if (u_arg)
352 				die_optdup(option);
353 
354 			u_arg = B_TRUE;
355 			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
356 			    mac_addr))
357 				die("invalid MAC address '%s'", optarg);
358 			break;
359 		case 'l':
360 			if (isdigit(optarg[strlen(optarg) - 1])) {
361 
362 				/*
363 				 * Ended with digit, possibly a link name.
364 				 */
365 				if (ndev + nlink >= MAXPORT)
366 					die("too many ports specified");
367 
368 				links[nlink++] = optarg;
369 				break;
370 			}
371 			/* FALLTHROUGH */
372 		case 'L':
373 			if (l_arg)
374 				die_optdup(option);
375 
376 			l_arg = B_TRUE;
377 			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
378 				die("invalid LACP mode '%s'", optarg);
379 			break;
380 		case 'T':
381 			if (T_arg)
382 				die_optdup(option);
383 
384 			T_arg = B_TRUE;
385 			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
386 				die("invalid LACP timer value '%s'", optarg);
387 			break;
388 		case 't':
389 			flags &= ~DLADM_OPT_PERSIST;
390 			break;
391 		case 'f':
392 			flags |= DLADM_OPT_FORCE;
393 			break;
394 		case 'R':
395 			altroot = optarg;
396 			break;
397 		default:
398 			die_opterr(optopt, option);
399 			break;
400 		}
401 	}
402 
403 	if (ndev + nlink == 0)
404 		usage();
405 
406 	/* get key value or the aggregation name (required last argument) */
407 	if (optind != (argc-1))
408 		usage();
409 
410 	if (!str2int(argv[optind], &key)) {
411 		if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >=
412 		    MAXLINKNAMELEN) {
413 			die("link name too long '%s'", argv[optind]);
414 		}
415 
416 		if (!dladm_valid_linkname(name))
417 			die("invalid link name '%s'", argv[optind]);
418 	} else {
419 		(void) snprintf(name, MAXLINKNAMELEN, "aggr%d", key);
420 	}
421 
422 	if (altroot != NULL)
423 		altroot_cmd(altroot, argc, argv);
424 
425 	for (n = 0; n < ndev; n++) {
426 		if (dladm_dev2linkid(devs[n], &port[n].lp_linkid) !=
427 		    DLADM_STATUS_OK) {
428 			die("invalid dev name '%s'", devs[n]);
429 		}
430 	}
431 
432 	for (n = 0; n < nlink; n++) {
433 		if (dladm_name2info(links[n], &port[ndev + n].lp_linkid,
434 		    NULL, NULL, NULL) != DLADM_STATUS_OK) {
435 			die("invalid link name '%s'", links[n]);
436 		}
437 	}
438 
439 	status = dladm_aggr_create(name, key, ndev + nlink, port, policy,
440 	    mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode,
441 	    lacp_timer, flags);
442 done:
443 	if (status != DLADM_STATUS_OK) {
444 		if (status == DLADM_STATUS_NONOTIF) {
445 			die_dlerr(status, "not all links have link up/down "
446 			    "detection; must use -f (see dladm(1M))\n");
447 		} else {
448 			die_dlerr(status, "create operation failed");
449 		}
450 	}
451 }
452 
453 /*
454  * arg is either the key or the aggr name. Validate it and convert it to
455  * the linkid if altroot is NULL.
456  */
457 static dladm_status_t
458 i_dladm_aggr_get_linkid(const char *altroot, const char *arg,
459     datalink_id_t *linkidp, uint32_t flags)
460 {
461 	int		key = 0;
462 	char		*aggr = NULL;
463 	dladm_status_t	status;
464 
465 	if (!str2int(arg, &key))
466 		aggr = (char *)arg;
467 
468 	if (aggr == NULL && key == 0)
469 		return (DLADM_STATUS_LINKINVAL);
470 
471 	if (altroot != NULL)
472 		return (DLADM_STATUS_OK);
473 
474 	if (aggr != NULL) {
475 		status = dladm_name2info(aggr, linkidp, NULL, NULL, NULL);
476 	} else {
477 		status = dladm_key2linkid(key, linkidp, flags);
478 	}
479 
480 	return (status);
481 }
482 
483 static void
484 do_delete_aggr(int argc, char *argv[])
485 {
486 	char			option;
487 	char			*altroot = NULL;
488 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
489 	dladm_status_t		status;
490 	datalink_id_t		linkid;
491 
492 	opterr = 0;
493 	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
494 		switch (option) {
495 		case 't':
496 			flags &= ~DLADM_OPT_PERSIST;
497 			break;
498 		case 'R':
499 			altroot = optarg;
500 			break;
501 		default:
502 			die_opterr(optopt, option);
503 			break;
504 		}
505 	}
506 
507 	/* get key value or the aggregation name (required last argument) */
508 	if (optind != (argc-1))
509 		usage();
510 
511 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
512 	if (status != DLADM_STATUS_OK)
513 		goto done;
514 
515 	if (altroot != NULL)
516 		altroot_cmd(altroot, argc, argv);
517 
518 	status = dladm_aggr_delete(linkid, flags);
519 done:
520 	if (status != DLADM_STATUS_OK)
521 		die_dlerr(status, "delete operation failed");
522 }
523 
524 static void
525 do_add_aggr(int argc, char *argv[])
526 {
527 	char			option;
528 	uint_t			n, ndev, nlink;
529 	char			*altroot = NULL;
530 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
531 	datalink_id_t		linkid;
532 	dladm_status_t		status;
533 	dladm_aggr_port_attr_db_t	port[MAXPORT];
534 	char			*devs[MAXPORT];
535 	char			*links[MAXPORT];
536 
537 	ndev = nlink = opterr = 0;
538 	while ((option = getopt_long(argc, argv, ":d:l:R:tf", lopts,
539 	    NULL)) != -1) {
540 		switch (option) {
541 		case 'd':
542 			if (ndev + nlink >= MAXPORT)
543 				die("too many ports specified");
544 
545 			devs[ndev++] = optarg;
546 			break;
547 		case 'l':
548 			if (ndev + nlink >= MAXPORT)
549 				die("too many ports specified");
550 
551 			links[nlink++] = optarg;
552 			break;
553 		case 't':
554 			flags &= ~DLADM_OPT_PERSIST;
555 			break;
556 		case 'f':
557 			flags |= DLADM_OPT_FORCE;
558 			break;
559 		case 'R':
560 			altroot = optarg;
561 			break;
562 		default:
563 			die_opterr(optopt, option);
564 			break;
565 		}
566 	}
567 
568 	if (ndev + nlink == 0)
569 		usage();
570 
571 	/* get key value or the aggregation name (required last argument) */
572 	if (optind != (argc-1))
573 		usage();
574 
575 	if ((status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid,
576 	    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST))) !=
577 	    DLADM_STATUS_OK) {
578 		goto done;
579 	}
580 
581 	if (altroot != NULL)
582 		altroot_cmd(altroot, argc, argv);
583 
584 	for (n = 0; n < ndev; n++) {
585 		if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) !=
586 		    DLADM_STATUS_OK) {
587 			die("invalid <dev> '%s'", devs[n]);
588 		}
589 	}
590 
591 	for (n = 0; n < nlink; n++) {
592 		if (dladm_name2info(links[n], &port[n + ndev].lp_linkid,
593 		    NULL, NULL, NULL) != DLADM_STATUS_OK) {
594 			die("invalid <link> '%s'", links[n]);
595 		}
596 	}
597 
598 	status = dladm_aggr_add(linkid, ndev + nlink, port, flags);
599 done:
600 	if (status != DLADM_STATUS_OK) {
601 		/*
602 		 * checking DLADM_STATUS_NOTSUP is a temporary workaround
603 		 * and should be removed once 6399681 is fixed.
604 		 */
605 		if (status == DLADM_STATUS_NOTSUP) {
606 			(void) fprintf(stderr,
607 			    gettext("%s: add operation failed: %s\n"),
608 			    progname,
609 			    gettext("link capabilities don't match"));
610 			exit(ENOTSUP);
611 		} else if (status == DLADM_STATUS_NONOTIF) {
612 			die_dlerr(status, "not all links have link up/down "
613 			    "detection; must use -f (see dladm(1M))\n");
614 		} else {
615 			die_dlerr(status, "add operation failed");
616 		}
617 	}
618 }
619 
620 static void
621 do_remove_aggr(int argc, char *argv[])
622 {
623 	char				option;
624 	dladm_aggr_port_attr_db_t	port[MAXPORT];
625 	uint_t				n, ndev, nlink;
626 	char				*devs[MAXPORT];
627 	char				*links[MAXPORT];
628 	char				*altroot = NULL;
629 	uint32_t			flags;
630 	datalink_id_t			linkid;
631 	dladm_status_t			status;
632 
633 	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
634 	ndev = nlink = opterr = 0;
635 	while ((option = getopt_long(argc, argv, ":d:l:R:t",
636 	    lopts, NULL)) != -1) {
637 		switch (option) {
638 		case 'd':
639 			if (ndev + nlink >= MAXPORT)
640 				die("too many ports specified");
641 
642 			devs[ndev++] = optarg;
643 			break;
644 		case 'l':
645 			if (ndev + nlink >= MAXPORT)
646 				die("too many ports specified");
647 
648 			links[nlink++] = optarg;
649 			break;
650 		case 't':
651 			flags &= ~DLADM_OPT_PERSIST;
652 			break;
653 		case 'R':
654 			altroot = optarg;
655 			break;
656 		default:
657 			die_opterr(optopt, option);
658 			break;
659 		}
660 	}
661 
662 	if (ndev + nlink == 0)
663 		usage();
664 
665 	/* get key value or the aggregation name (required last argument) */
666 	if (optind != (argc-1))
667 		usage();
668 
669 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
670 	if (status != DLADM_STATUS_OK)
671 		goto done;
672 
673 	if (altroot != NULL)
674 		altroot_cmd(altroot, argc, argv);
675 
676 	for (n = 0; n < ndev; n++) {
677 		if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) !=
678 		    DLADM_STATUS_OK) {
679 			die("invalid <dev> '%s'", devs[n]);
680 		}
681 	}
682 
683 	for (n = 0; n < nlink; n++) {
684 		if (dladm_name2info(links[n], &port[n + ndev].lp_linkid,
685 		    NULL, NULL, NULL) != DLADM_STATUS_OK) {
686 			die("invalid <link> '%s'", links[n]);
687 		}
688 	}
689 
690 	status = dladm_aggr_remove(linkid, ndev + nlink, port, flags);
691 done:
692 	if (status != DLADM_STATUS_OK)
693 		die_dlerr(status, "remove operation failed");
694 }
695 
696 static void
697 do_modify_aggr(int argc, char *argv[])
698 {
699 	char			option;
700 	uint32_t		policy = AGGR_POLICY_L4;
701 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
702 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
703 	uint8_t			mac_addr[ETHERADDRL];
704 	boolean_t		mac_addr_fixed = B_FALSE;
705 	uint8_t			modify_mask = 0;
706 	char			*altroot = NULL;
707 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
708 	datalink_id_t		linkid;
709 	dladm_status_t		status;
710 
711 	opterr = 0;
712 	while ((option = getopt_long(argc, argv, ":L:l:P:R:tu:T:", lopts,
713 	    NULL)) != -1) {
714 		switch (option) {
715 		case 'P':
716 			if (modify_mask & DLADM_AGGR_MODIFY_POLICY)
717 				die_optdup(option);
718 
719 			modify_mask |= DLADM_AGGR_MODIFY_POLICY;
720 
721 			if (!dladm_aggr_str2policy(optarg, &policy))
722 				die("invalid policy '%s'", optarg);
723 			break;
724 		case 'u':
725 			if (modify_mask & DLADM_AGGR_MODIFY_MAC)
726 				die_optdup(option);
727 
728 			modify_mask |= DLADM_AGGR_MODIFY_MAC;
729 
730 			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
731 			    mac_addr))
732 				die("invalid MAC address '%s'", optarg);
733 			break;
734 		case 'l':
735 		case 'L':
736 			if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE)
737 				die_optdup(option);
738 
739 			modify_mask |= DLADM_AGGR_MODIFY_LACP_MODE;
740 
741 			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
742 				die("invalid LACP mode '%s'", optarg);
743 			break;
744 		case 'T':
745 			if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER)
746 				die_optdup(option);
747 
748 			modify_mask |= DLADM_AGGR_MODIFY_LACP_TIMER;
749 
750 			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
751 				die("invalid LACP timer value '%s'", optarg);
752 			break;
753 		case 't':
754 			flags &= ~DLADM_OPT_PERSIST;
755 			break;
756 		case 'R':
757 			altroot = optarg;
758 			break;
759 		default:
760 			die_opterr(optopt, option);
761 			break;
762 		}
763 	}
764 
765 	if (modify_mask == 0)
766 		die("at least one of the -PulT options must be specified");
767 
768 	/* get key value or the aggregation name (required last argument) */
769 	if (optind != (argc-1))
770 		usage();
771 
772 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
773 	if (status != DLADM_STATUS_OK)
774 		goto done;
775 
776 	if (altroot != NULL)
777 		altroot_cmd(altroot, argc, argv);
778 
779 	status = dladm_aggr_modify(linkid, modify_mask, policy, mac_addr_fixed,
780 	    (const uchar_t *)mac_addr, lacp_mode, lacp_timer, flags);
781 
782 done:
783 	if (status != DLADM_STATUS_OK)
784 		die_dlerr(status, "modify operation failed");
785 }
786 
787 static void
788 do_up_aggr(int argc, char *argv[])
789 {
790 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
791 	dladm_status_t	status;
792 
793 	/*
794 	 * get the key or the name of the aggregation (optional last argument)
795 	 */
796 	if (argc == 2) {
797 		if ((status = i_dladm_aggr_get_linkid(NULL, argv[1], &linkid,
798 		    DLADM_OPT_PERSIST)) != DLADM_STATUS_OK) {
799 			goto done;
800 		}
801 	} else if (argc > 2) {
802 		usage();
803 	}
804 
805 	status = dladm_aggr_up(linkid);
806 done:
807 	if (status != DLADM_STATUS_OK) {
808 		if (argc == 2) {
809 			die_dlerr(status,
810 			    "could not bring up aggregation '%s'", argv[1]);
811 		} else {
812 			die_dlerr(status, "could not bring aggregations up");
813 		}
814 	}
815 }
816 
817 static void
818 do_create_vlan(int argc, char *argv[])
819 {
820 	char		*link = NULL;
821 	char		drv[DLPI_LINKNAME_MAX];
822 	uint_t		ppa;
823 	datalink_id_t	linkid;
824 	int		vid = 0;
825 	char		option;
826 	uint32_t	flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
827 	char		*altroot = NULL;
828 	char		vlan[MAXLINKNAMELEN];
829 	dladm_status_t	status;
830 
831 	opterr = 0;
832 	while ((option = getopt_long(argc, argv, ":tfl:v:",
833 	    lopts, NULL)) != -1) {
834 		switch (option) {
835 		case 'v':
836 			if (vid != 0)
837 				die_optdup(option);
838 
839 			if (!str2int(optarg, &vid) || vid < 1 || vid > 4094)
840 				die("invalid VLAN identifier '%s'", optarg);
841 
842 			break;
843 		case 'l':
844 			if (link != NULL)
845 				die_optdup(option);
846 
847 			link = optarg;
848 			break;
849 		case 'f':
850 			flags |= DLADM_OPT_FORCE;
851 			break;
852 		case 't':
853 			flags &= ~DLADM_OPT_PERSIST;
854 			break;
855 		case 'R':
856 			altroot = optarg;
857 			break;
858 		default:
859 			die_opterr(optopt, option);
860 			break;
861 		}
862 	}
863 
864 	/* get vlan name if there is any */
865 	if ((vid == 0) || (link == NULL) || (argc - optind > 1))
866 		usage();
867 
868 	if (optind == (argc - 1)) {
869 		if (strlcpy(vlan, argv[optind], MAXLINKNAMELEN) >=
870 		    MAXLINKNAMELEN) {
871 			die("vlan name too long '%s'", argv[optind]);
872 		}
873 	} else {
874 		if ((dlpi_parselink(link, drv, &ppa) != DLPI_SUCCESS) ||
875 		    (ppa >= 1000) ||
876 		    (dlpi_makelink(vlan, drv, vid * 1000 + ppa) !=
877 		    DLPI_SUCCESS)) {
878 			die("invalid link name '%s'", link);
879 		}
880 	}
881 
882 	if (altroot != NULL)
883 		altroot_cmd(altroot, argc, argv);
884 
885 	if (dladm_name2info(link, &linkid, NULL, NULL, NULL) !=
886 	    DLADM_STATUS_OK) {
887 		die("invalid link name '%s'", link);
888 	}
889 
890 	if ((status = dladm_vlan_create(vlan, linkid, vid, flags)) !=
891 	    DLADM_STATUS_OK) {
892 		if (status == DLADM_STATUS_NOTSUP) {
893 			die_dlerr(status, "VLAN over '%s' may require lowered "
894 			    "MTU; must use -f (see dladm(1M))\n", link);
895 		} else {
896 			die_dlerr(status, "create operation failed");
897 		}
898 	}
899 }
900 
901 static void
902 do_delete_vlan(int argc, char *argv[])
903 {
904 	char		option;
905 	uint32_t	flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
906 	char		*altroot = NULL;
907 	datalink_id_t	linkid;
908 	dladm_status_t	status;
909 
910 	opterr = 0;
911 	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
912 		switch (option) {
913 		case 't':
914 			flags &= ~DLADM_OPT_PERSIST;
915 			break;
916 		case 'R':
917 			altroot = optarg;
918 			break;
919 		default:
920 			die_opterr(optopt, option);
921 			break;
922 		}
923 	}
924 
925 	/* get VLAN link name (required last argument) */
926 	if (optind != (argc - 1))
927 		usage();
928 
929 	if (altroot != NULL)
930 		altroot_cmd(altroot, argc, argv);
931 
932 	status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL);
933 	if (status != DLADM_STATUS_OK)
934 		goto done;
935 
936 	status = dladm_vlan_delete(linkid, flags);
937 done:
938 	if (status != DLADM_STATUS_OK)
939 		die_dlerr(status, "delete operation failed");
940 }
941 
942 static void
943 do_up_vlan(int argc, char *argv[])
944 {
945 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
946 	dladm_status_t	status;
947 
948 	/*
949 	 * get the name of the VLAN (optional last argument)
950 	 */
951 	if (argc > 2)
952 		usage();
953 
954 	if (argc == 2) {
955 		status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL);
956 		if (status != DLADM_STATUS_OK)
957 			goto done;
958 	}
959 
960 	status = dladm_vlan_up(linkid);
961 done:
962 	if (status != DLADM_STATUS_OK) {
963 		if (argc == 2) {
964 			die_dlerr(status,
965 			    "could not bring up VLAN '%s'", argv[1]);
966 		} else {
967 			die_dlerr(status, "could not bring VLANs up");
968 		}
969 	}
970 }
971 
972 static void
973 do_rename_link(int argc, char *argv[])
974 {
975 	char		option;
976 	char		*link1, *link2;
977 	char		*altroot = NULL;
978 	dladm_status_t	status;
979 
980 	opterr = 0;
981 	while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) {
982 		switch (option) {
983 		case 'R':
984 			altroot = optarg;
985 			break;
986 		default:
987 			die_opterr(optopt, option);
988 			break;
989 		}
990 	}
991 
992 	/* get link1 and link2 name (required the last 2 arguments) */
993 	if (optind != (argc - 2))
994 		usage();
995 
996 	if (altroot != NULL)
997 		altroot_cmd(altroot, argc, argv);
998 
999 	link1 = argv[optind++];
1000 	link2 = argv[optind];
1001 	if ((status = dladm_rename_link(link1, link2)) != DLADM_STATUS_OK)
1002 		die_dlerr(status, "rename operation failed");
1003 }
1004 
1005 static void
1006 do_delete_phys(int argc, char *argv[])
1007 {
1008 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
1009 	dladm_status_t	status;
1010 
1011 	/* get link name (required the last argument) */
1012 	if (argc > 2)
1013 		usage();
1014 
1015 	if (argc == 2) {
1016 		status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL);
1017 		if (status != DLADM_STATUS_OK)
1018 			die_dlerr(status, "cannot delete '%s'", argv[1]);
1019 	}
1020 
1021 	if ((status = dladm_phys_delete(linkid)) != DLADM_STATUS_OK) {
1022 		if (argc == 2)
1023 			die_dlerr(status, "cannot delete '%s'", argv[1]);
1024 		else
1025 			die_dlerr(status, "delete operation failed");
1026 	}
1027 }
1028 
1029 /*ARGSUSED*/
1030 static int
1031 i_dladm_walk_linkmap(datalink_id_t linkid, void *arg)
1032 {
1033 	char			name[MAXLINKNAMELEN];
1034 	char			mediabuf[DLADM_STRSIZE];
1035 	char			classbuf[DLADM_STRSIZE];
1036 	datalink_class_t	class;
1037 	uint32_t		media;
1038 	uint32_t		flags;
1039 
1040 	if (dladm_datalink_id2info(linkid, &flags, &class, &media, name,
1041 	    MAXLINKNAMELEN) == DLADM_STATUS_OK) {
1042 		(void) dladm_class2str(class, classbuf);
1043 		(void) dladm_media2str(media, mediabuf);
1044 		(void) printf("%-12s%8d  %-12s%-20s %6d\n", name,
1045 		    linkid, classbuf, mediabuf, flags);
1046 	}
1047 	return (DLADM_WALK_CONTINUE);
1048 }
1049 
1050 /*ARGSUSED*/
1051 static void
1052 do_show_linkmap(int argc, char *argv[])
1053 {
1054 	if (argc != 1)
1055 		die("invalid arguments");
1056 
1057 	(void) printf("%-12s%8s  %-12s%-20s %6s\n", "NAME", "LINKID",
1058 	    "CLASS", "MEDIA", "FLAGS");
1059 	(void) dladm_walk_datalink_id(i_dladm_walk_linkmap, NULL,
1060 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1061 	    DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
1062 }
1063 
1064 /*
1065  * Delete inactive physical links.
1066  */
1067 /*ARGSUSED*/
1068 static int
1069 purge_phys(datalink_id_t linkid, void *arg)
1070 {
1071 	datalink_class_t	class;
1072 	uint32_t		flags;
1073 
1074 	if (dladm_datalink_id2info(linkid, &flags, &class, NULL,
1075 	    NULL, 0) != DLADM_STATUS_OK) {
1076 		return (DLADM_WALK_CONTINUE);
1077 	}
1078 
1079 	if (class == DATALINK_CLASS_PHYS && !(flags & DLADM_OPT_ACTIVE))
1080 		(void) dladm_phys_delete(linkid);
1081 
1082 	return (DLADM_WALK_CONTINUE);
1083 }
1084 
1085 /*ARGSUSED*/
1086 static void
1087 do_init_phys(int argc, char *argv[])
1088 {
1089 	di_node_t devtree;
1090 
1091 	if (argc > 1)
1092 		usage();
1093 
1094 	/*
1095 	 * Force all the devices to attach, therefore all the network physical
1096 	 * devices can be known to the dlmgmtd daemon.
1097 	 */
1098 	if ((devtree = di_init("/", DINFOFORCE | DINFOSUBTREE)) != DI_NODE_NIL)
1099 		di_fini(devtree);
1100 
1101 	(void) dladm_walk_datalink_id(purge_phys, NULL,
1102 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1103 }
1104 
1105 static void
1106 print_link_head(show_state_t *state)
1107 {
1108 	if (state->ls_donefirst)
1109 		return;
1110 	state->ls_donefirst = B_TRUE;
1111 
1112 	if (state->ls_parseable)
1113 		return;
1114 
1115 	if (state->ls_flags & DLADM_OPT_ACTIVE) {
1116 		(void) printf("%-12s%-8s%6s  %-9s%s\n", "LINK", "CLASS", "MTU",
1117 		    "STATE", "OVER");
1118 	} else {
1119 		(void) printf("%-12s%-8s%s\n", "LINK", "CLASS", "OVER");
1120 	}
1121 }
1122 
1123 /*
1124  * Print the active topology information.
1125  */
1126 static dladm_status_t
1127 print_link_topology(show_state_t *state, datalink_id_t linkid,
1128     datalink_class_t class, char **pptr, char *lim)
1129 {
1130 	char		*fmt;
1131 	char		over[MAXLINKNAMELEN];
1132 	uint32_t	flags = state->ls_flags;
1133 	dladm_status_t	status = DLADM_STATUS_OK;
1134 
1135 	if (state->ls_parseable)
1136 		fmt = "OVER=\"%s";
1137 	else
1138 		fmt = "%s";
1139 
1140 	if (class == DATALINK_CLASS_VLAN) {
1141 		dladm_vlan_attr_t	vinfo;
1142 
1143 		status = dladm_vlan_info(linkid, &vinfo, flags);
1144 		if (status != DLADM_STATUS_OK)
1145 			goto done;
1146 		status = dladm_datalink_id2info(vinfo.dv_linkid, NULL, NULL,
1147 		    NULL, over, sizeof (over));
1148 		if (status != DLADM_STATUS_OK)
1149 			goto done;
1150 
1151 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1152 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, over);
1153 	} else if (class == DATALINK_CLASS_AGGR) {
1154 		dladm_aggr_grp_attr_t	ginfo;
1155 		int			i;
1156 
1157 		status = dladm_aggr_info(linkid, &ginfo, flags);
1158 		if (status != DLADM_STATUS_OK)
1159 			goto done;
1160 
1161 		if (ginfo.lg_nports == 0) {
1162 			status = DLADM_STATUS_BADVAL;
1163 			goto done;
1164 		}
1165 		for (i = 0; i < ginfo.lg_nports; i++) {
1166 			status = dladm_datalink_id2info(
1167 			    ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL, over,
1168 			    sizeof (over));
1169 			if (status != DLADM_STATUS_OK) {
1170 				free(ginfo.lg_ports);
1171 				goto done;
1172 			}
1173 			/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1174 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, over);
1175 			fmt = " %s";
1176 		}
1177 		free(ginfo.lg_ports);
1178 	} else if (class == DATALINK_CLASS_VNIC) {
1179 		dladm_vnic_attr_sys_t	vinfo;
1180 
1181 		if ((status = dladm_vnic_info(linkid, &vinfo, flags)) !=
1182 		    DLADM_STATUS_OK || (status = dladm_datalink_id2info(
1183 		    vinfo.va_link_id, NULL, NULL, NULL, over,
1184 		    sizeof (over))) != DLADM_STATUS_OK) {
1185 			goto done;
1186 		}
1187 
1188 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1189 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, over);
1190 	} else {
1191 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1192 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt,
1193 		    state->ls_parseable ? "" : "--");
1194 	}
1195 	if (state->ls_parseable)
1196 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "\"\n");
1197 	else
1198 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "\n");
1199 
1200 done:
1201 	return (status);
1202 }
1203 
1204 static dladm_status_t
1205 print_link(show_state_t *state, datalink_id_t linkid, char **pptr, char *lim)
1206 {
1207 	char			link[MAXLINKNAMELEN];
1208 	char			buf[DLADM_STRSIZE];
1209 	datalink_class_t	class;
1210 	uint_t			mtu;
1211 	char			*fmt;
1212 	uint32_t		flags;
1213 	dladm_status_t		status;
1214 
1215 	if ((status = dladm_datalink_id2info(linkid, &flags, &class, NULL,
1216 	    link, sizeof (link))) != DLADM_STATUS_OK) {
1217 		goto done;
1218 	}
1219 
1220 	if (!(state->ls_flags & flags)) {
1221 		status = DLADM_STATUS_NOTFOUND;
1222 		goto done;
1223 	}
1224 
1225 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
1226 		dladm_attr_t	dlattr;
1227 
1228 		if (class == DATALINK_CLASS_PHYS) {
1229 			dladm_phys_attr_t	dpa;
1230 			dlpi_handle_t		dh;
1231 			dlpi_info_t		dlinfo;
1232 
1233 			if ((status = dladm_phys_info(linkid, &dpa,
1234 			    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
1235 				goto done;
1236 			}
1237 
1238 			if (!dpa.dp_novanity)
1239 				goto link_mtu;
1240 
1241 			/*
1242 			 * This is a physical link that does not have
1243 			 * vanity naming support.
1244 			 */
1245 			if (dlpi_open(dpa.dp_dev, &dh, DLPI_DEVONLY) !=
1246 			    DLPI_SUCCESS) {
1247 				status = DLADM_STATUS_NOTFOUND;
1248 				goto done;
1249 			}
1250 
1251 			if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
1252 				dlpi_close(dh);
1253 				status = DLADM_STATUS_BADARG;
1254 				goto done;
1255 			}
1256 
1257 			dlpi_close(dh);
1258 			mtu = dlinfo.di_max_sdu;
1259 		} else {
1260 link_mtu:
1261 			status = dladm_info(linkid, &dlattr);
1262 			if (status != DLADM_STATUS_OK)
1263 				goto done;
1264 			mtu = dlattr.da_max_sdu;
1265 		}
1266 	}
1267 
1268 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
1269 		if (state->ls_parseable)
1270 			fmt = "LINK=\"%s\" CLASS=\"%s\" MTU=\"%d\" ";
1271 		else
1272 			fmt = "%-12s%-8s%6d  ";
1273 	} else {
1274 		if (state->ls_parseable)
1275 			fmt = "LINK=\"%s\" CLASS=\"%s\" ";
1276 		else
1277 			fmt = "%-12s%-8s";
1278 	}
1279 
1280 	(void) dladm_class2str(class, buf);
1281 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
1282 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1283 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link,
1284 		    buf, mtu);
1285 	} else {
1286 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1287 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link, buf);
1288 	}
1289 
1290 	(void) get_linkstate(link, B_TRUE, buf);
1291 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
1292 		if (state->ls_parseable) {
1293 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1294 			    "STATE=\"%s\" ", buf);
1295 		} else {
1296 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1297 			    "%-9s", buf);
1298 		}
1299 	}
1300 
1301 	status = print_link_topology(state, linkid, class, pptr, lim);
1302 	if (status != DLADM_STATUS_OK)
1303 		goto done;
1304 
1305 done:
1306 	return (status);
1307 }
1308 
1309 static int
1310 show_link(datalink_id_t linkid, void *arg)
1311 {
1312 	show_state_t	*state = arg;
1313 	dladm_status_t	status;
1314 	char		buf[MAXLINELEN];
1315 	char		*ptr = buf, *lim = buf + MAXLINELEN;
1316 
1317 	status = print_link(state, linkid, &ptr, lim);
1318 	if (status != DLADM_STATUS_OK)
1319 		goto done;
1320 	print_link_head(state);
1321 	(void) printf("%s", buf);
1322 
1323 done:
1324 	state->ls_status = status;
1325 	return (DLADM_WALK_CONTINUE);
1326 }
1327 
1328 static int
1329 show_link_stats(datalink_id_t linkid, void *arg)
1330 {
1331 	char link[MAXLINKNAMELEN];
1332 	datalink_class_t class;
1333 	show_state_t *state = arg;
1334 	pktsum_t stats, diff_stats;
1335 	dladm_phys_attr_t dpa;
1336 
1337 	if (state->ls_firstonly) {
1338 		if (state->ls_donefirst)
1339 			return (DLADM_WALK_CONTINUE);
1340 		state->ls_donefirst = B_TRUE;
1341 	} else {
1342 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
1343 	}
1344 
1345 	if (dladm_datalink_id2info(linkid, NULL, &class, NULL, link,
1346 	    sizeof (link)) != DLADM_STATUS_OK) {
1347 		return (DLADM_WALK_CONTINUE);
1348 	}
1349 
1350 	if (class == DATALINK_CLASS_PHYS) {
1351 		if (dladm_phys_info(linkid, &dpa, DLADM_OPT_ACTIVE) !=
1352 		    DLADM_STATUS_OK) {
1353 			return (DLADM_WALK_CONTINUE);
1354 		}
1355 		if (dpa.dp_novanity)
1356 			get_mac_stats(dpa.dp_dev, &stats);
1357 		else
1358 			get_link_stats(link, &stats);
1359 	} else {
1360 		get_link_stats(link, &stats);
1361 	}
1362 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
1363 
1364 	(void) printf("%-12s", link);
1365 	(void) printf("%-10llu", diff_stats.ipackets);
1366 	(void) printf("%-12llu", diff_stats.rbytes);
1367 	(void) printf("%-8u", diff_stats.ierrors);
1368 	(void) printf("%-10llu", diff_stats.opackets);
1369 	(void) printf("%-12llu", diff_stats.obytes);
1370 	(void) printf("%-8u\n", diff_stats.oerrors);
1371 
1372 	state->ls_prevstats = stats;
1373 	return (DLADM_WALK_CONTINUE);
1374 }
1375 
1376 static void
1377 print_port_stat(const char *port, pktsum_t *old_stats, pktsum_t *port_stats,
1378     pktsum_t *tot_stats, char **pptr, char *lim)
1379 {
1380 	pktsum_t	diff_stats;
1381 
1382 	stats_diff(&diff_stats, port_stats, old_stats);
1383 	*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1384 	    "%-12s%-10s%8llu  %8llu  %8llu  %8llu  ", "", port,
1385 	    diff_stats.ipackets, diff_stats.rbytes, diff_stats.opackets,
1386 	    diff_stats.obytes);
1387 
1388 	if (tot_stats->ipackets == 0) {
1389 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%8s ", "--");
1390 	} else {
1391 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%7.1f%% ",
1392 		    (double)diff_stats.ipackets/
1393 		    (double)tot_stats->ipackets * 100);
1394 	}
1395 
1396 	if (tot_stats->opackets == 0) {
1397 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%8s\n", "--");
1398 	} else {
1399 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%7.1f%%\n",
1400 		    (double)diff_stats.opackets/
1401 		    (double)tot_stats->opackets * 100);
1402 	}
1403 
1404 	*old_stats = *port_stats;
1405 }
1406 
1407 static void
1408 print_aggr_head(show_grp_state_t *state)
1409 {
1410 	if (state->gs_donefirst)
1411 		return;
1412 	state->gs_donefirst = B_TRUE;
1413 
1414 	if (state->gs_parseable)
1415 		return;
1416 
1417 	if (state->gs_lacp) {
1418 		(void) printf("%-12s%-12s%-13s%-5s%-5s%-5s%-10s%s\n", "LINK",
1419 		    "PORT", "AGGREGATABLE", "SYNC", "COLL", "DIST",
1420 		    "DEFAULTED", "EXPIRED");
1421 	} else if (state->gs_extended) {
1422 		(void) printf("%-12s%-14s%6s  %-9s%-9s%-18s%s\n", "LINK",
1423 		    "PORT", "SPEED", "DUPLEX", "STATE", "ADDRESS", "PORTSTATE");
1424 	} else if (!state->gs_stats) {
1425 		(void) printf("%-12s%-8s%-24s%-13s%-11s%s\n", "LINK", "POLICY",
1426 		    "ADDRPOLICY", "LACPACTIVITY", "LACPTIMER", "FLAGS");
1427 	}
1428 }
1429 
1430 static dladm_status_t
1431 print_aggr_info(show_grp_state_t *state, const char *link,
1432     dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim)
1433 {
1434 	char			buf[DLADM_STRSIZE];
1435 	char			*fmt;
1436 	char			addr_str[ETHERADDRL * 3];
1437 	char			str[ETHERADDRL * 3 + 2];
1438 
1439 	if (state->gs_parseable)
1440 		fmt = "LINK=\"%s\" POLICY=\"%s\" ADDRPOLICY=\"%s%s\" ";
1441 	else
1442 		fmt = "%-12s%-8s%-6s%-18s";
1443 
1444 	if (ginfop->lg_mac_fixed) {
1445 		(void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str);
1446 		(void) snprintf(str, ETHERADDRL * 3 + 3, " (%s)", addr_str);
1447 	} else {
1448 		str[0] = '\0';
1449 	}
1450 
1451 	/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1452 	*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link,
1453 	    dladm_aggr_policy2str(ginfop->lg_policy, buf),
1454 	    ginfop->lg_mac_fixed ? "fixed" : "auto", str);
1455 
1456 	(void) dladm_aggr_lacpmode2str(ginfop->lg_lacp_mode, buf);
1457 	if (state->gs_parseable) {
1458 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1459 		    "LACPACTIVITY=\"%s\" ", buf);
1460 	} else {
1461 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%-13s", buf);
1462 	}
1463 
1464 	(void) dladm_aggr_lacptimer2str(ginfop->lg_lacp_timer, buf);
1465 	if (state->gs_parseable) {
1466 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1467 		    "LACPTIMER=\"%s\" FLAGS=\"%c----\"\n", buf,
1468 		    ginfop->lg_force ? 'f' : '-');
1469 	} else {
1470 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1471 		    "%-11s%c----\n", buf, ginfop->lg_force ? 'f' : '-');
1472 	}
1473 
1474 	return (DLADM_STATUS_OK);
1475 }
1476 
1477 static dladm_status_t
1478 print_aggr_extended(show_grp_state_t *state, const char *link,
1479     dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim)
1480 {
1481 	char			addr_str[ETHERADDRL * 3];
1482 	char			port[MAXLINKNAMELEN];
1483 	dladm_phys_attr_t	dpa;
1484 	char			buf[DLADM_STRSIZE];
1485 	char			*fmt;
1486 	int			i;
1487 	dladm_status_t		status;
1488 
1489 	if (state->gs_parseable)
1490 		fmt = "LINK=\"%s\" PORT=\"%s\" SPEED=\"%uMb\" DUPLEX=\"%s\" ";
1491 	else
1492 		fmt = "%-12s%-14s%4uMb  %-9s";
1493 
1494 	(void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str);
1495 
1496 	/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1497 	*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link,
1498 	    state->gs_parseable ? "" : "--",
1499 	    (uint_t)((get_ifspeed(link, B_TRUE)) / 1000000ull),
1500 	    get_linkduplex(link, B_TRUE, buf));
1501 
1502 	(void) get_linkstate(link, B_TRUE, buf);
1503 	if (state->gs_parseable) {
1504 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1505 		    "STATE=\"%s\" ADDRESS=\"%s\" PORTSTATE=\"%s\"\n", buf,
1506 		    addr_str, "");
1507 	} else {
1508 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%-9s%-18s%s\n",
1509 		    buf, addr_str, "--");
1510 	}
1511 
1512 	for (i = 0; i < ginfop->lg_nports; i++) {
1513 		dladm_aggr_port_attr_t	*portp = &(ginfop->lg_ports[i]);
1514 		const char		*tmp;
1515 
1516 		if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL,
1517 		    NULL, NULL, port, sizeof (port))) != DLADM_STATUS_OK) {
1518 			goto done;
1519 		}
1520 
1521 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
1522 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
1523 			goto done;
1524 		}
1525 
1526 		(void) dladm_aggr_macaddr2str(portp->lp_mac, addr_str);
1527 
1528 		if (state->gs_parseable)
1529 			tmp = link;
1530 		else
1531 			tmp = "";
1532 
1533 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1534 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, tmp, port,
1535 		    (uint_t)((get_ifspeed(dpa.dp_dev, B_FALSE)) / 1000000ull),
1536 		    get_linkduplex(dpa.dp_dev, B_FALSE, buf));
1537 
1538 		(void) get_linkstate(dpa.dp_dev, B_FALSE, buf);
1539 		if (state->gs_parseable) {
1540 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1541 			    "STATE=\"%s\" ADDRESS=\"%s\" ", buf, addr_str);
1542 		} else {
1543 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1544 			    "%-9s%-18s", buf, addr_str);
1545 		}
1546 
1547 		(void) dladm_aggr_portstate2str(
1548 		    ginfop->lg_ports[i].lp_state, buf);
1549 		if (state->gs_parseable) {
1550 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1551 			    "PORTSTATE=\"%s\"\n", buf);
1552 		} else {
1553 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1554 			    "%s\n", buf);
1555 		}
1556 	}
1557 
1558 	status = DLADM_STATUS_OK;
1559 done:
1560 	return (status);
1561 }
1562 
1563 static dladm_status_t
1564 print_aggr_lacp(show_grp_state_t *state, const char *link,
1565     dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim)
1566 {
1567 	char		port[MAXLINKNAMELEN];
1568 	char		*fmt;
1569 	const char	*dlink = link;
1570 	int		i;
1571 	dladm_status_t	status;
1572 
1573 	if (state->gs_parseable) {
1574 		fmt = "LINK=\"%s\" PORT=\"%s\" AGGREGATABLE=\"%s\" SYNC=\"%s\" "
1575 		    "COLL=\"%s\" DIST=\"%s\" DEFAULTED=\"%s\" EXPITED=\"%s\"\n";
1576 	} else {
1577 		fmt = "%-12s%-12s%-13s%-5s%-5s%-5s%-10s%s\n";
1578 	}
1579 
1580 	for (i = 0; i < ginfop->lg_nports; i++) {
1581 		aggr_lacp_state_t *lstate;
1582 
1583 		status = dladm_datalink_id2info(ginfop->lg_ports[i].lp_linkid,
1584 		    NULL, NULL, NULL, port, sizeof (port));
1585 		if (status != DLADM_STATUS_OK)
1586 			goto done;
1587 
1588 		/*
1589 		 * Only display link for the first port.
1590 		 */
1591 		if ((i > 0) && !(state->gs_parseable))
1592 			dlink = "";
1593 		lstate = &(ginfop->lg_ports[i].lp_lacp_state);
1594 
1595 		/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1596 		*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, dlink, port,
1597 		    lstate->bit.aggregation ? "yes" : "no",
1598 		    lstate->bit.sync ? "yes" : "no",
1599 		    lstate->bit.collecting ? "yes" : "no",
1600 		    lstate->bit.distributing ? "yes" : "no",
1601 		    lstate->bit.defaulted ? "yes" : "no",
1602 		    lstate->bit.expired ? "yes" : "no");
1603 	}
1604 
1605 	status = DLADM_STATUS_OK;
1606 done:
1607 	return (status);
1608 }
1609 
1610 static dladm_status_t
1611 print_aggr_stats(show_grp_state_t *state, const char *link,
1612     dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim)
1613 {
1614 	char			port[MAXLINKNAMELEN];
1615 	dladm_phys_attr_t	dpa;
1616 	dladm_aggr_port_attr_t	*portp;
1617 	pktsum_t		pktsumtot, port_stat;
1618 	dladm_status_t		status;
1619 	int			i;
1620 
1621 	if (state->gs_firstonly) {
1622 		if (state->gs_donefirst)
1623 			return (DLADM_WALK_CONTINUE);
1624 		state->gs_donefirst = B_TRUE;
1625 	} else {
1626 		bzero(&state->gs_prevstats, sizeof (state->gs_prevstats));
1627 	}
1628 
1629 	/* sum the ports statistics */
1630 	bzero(&pktsumtot, sizeof (pktsumtot));
1631 
1632 	for (i = 0; i < ginfop->lg_nports; i++) {
1633 
1634 		portp = &(ginfop->lg_ports[i]);
1635 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
1636 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
1637 			goto done;
1638 		}
1639 
1640 		get_mac_stats(dpa.dp_dev, &port_stat);
1641 		stats_total(&pktsumtot, &port_stat, &state->gs_prevstats[i]);
1642 	}
1643 
1644 	*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
1645 	    "%-12s%-10s%8llu  %8llu  %8llu  %8llu  %8s %8s\n", link, "--",
1646 	    pktsumtot.ipackets, pktsumtot.rbytes, pktsumtot.opackets,
1647 	    pktsumtot.obytes, "--", "--");
1648 
1649 	for (i = 0; i < ginfop->lg_nports; i++) {
1650 		portp = &(ginfop->lg_ports[i]);
1651 
1652 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
1653 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
1654 			goto done;
1655 		}
1656 
1657 		get_mac_stats(dpa.dp_dev, &port_stat);
1658 
1659 		if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL,
1660 		    NULL, NULL, port, sizeof (port))) != DLADM_STATUS_OK) {
1661 			goto done;
1662 		}
1663 
1664 		print_port_stat(port, &state->gs_prevstats[i], &port_stat,
1665 		    &pktsumtot, pptr, lim);
1666 	}
1667 
1668 	status = DLADM_STATUS_OK;
1669 done:
1670 	return (status);
1671 }
1672 
1673 static dladm_status_t
1674 print_aggr(show_grp_state_t *state, datalink_id_t linkid, char **pptr,
1675     char *lim)
1676 {
1677 	char			link[MAXLINKNAMELEN];
1678 	dladm_aggr_grp_attr_t	ginfo;
1679 	uint32_t		flags;
1680 	dladm_status_t		status;
1681 
1682 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link,
1683 	    sizeof (link))) != DLADM_STATUS_OK) {
1684 		return (status);
1685 	}
1686 
1687 	if (!(state->gs_flags & flags))
1688 		return (DLADM_STATUS_NOTFOUND);
1689 
1690 	status = dladm_aggr_info(linkid, &ginfo, state->gs_flags);
1691 	if (status != DLADM_STATUS_OK)
1692 		return (status);
1693 
1694 	if (state->gs_lacp)
1695 		status = print_aggr_lacp(state, link, &ginfo, pptr, lim);
1696 	else if (state->gs_extended)
1697 		status = print_aggr_extended(state, link, &ginfo, pptr, lim);
1698 	else if (state->gs_stats)
1699 		status = print_aggr_stats(state, link, &ginfo, pptr, lim);
1700 	else
1701 		status = print_aggr_info(state, link, &ginfo, pptr, lim);
1702 
1703 done:
1704 	free(ginfo.lg_ports);
1705 	return (status);
1706 }
1707 
1708 static int
1709 show_aggr(datalink_id_t linkid, void *arg)
1710 {
1711 	show_grp_state_t	*state = arg;
1712 	dladm_status_t		status;
1713 	char			buf[MAXLINELEN];
1714 	char			*ptr = buf, *lim = buf + MAXLINELEN;
1715 
1716 	status = print_aggr(state, linkid, &ptr, lim);
1717 	if (status != DLADM_STATUS_OK)
1718 		goto done;
1719 	print_aggr_head(state);
1720 	(void) printf("%s", buf);
1721 
1722 done:
1723 	state->gs_status = status;
1724 	return (DLADM_WALK_CONTINUE);
1725 }
1726 
1727 static int
1728 kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
1729 {
1730 	kstat_named_t	*knp;
1731 
1732 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
1733 		return (-1);
1734 
1735 	if (knp->data_type != type)
1736 		return (-1);
1737 
1738 	switch (type) {
1739 	case KSTAT_DATA_UINT64:
1740 		*(uint64_t *)buf = knp->value.ui64;
1741 		break;
1742 	case KSTAT_DATA_UINT32:
1743 		*(uint32_t *)buf = knp->value.ui32;
1744 		break;
1745 	default:
1746 		return (-1);
1747 	}
1748 
1749 	return (0);
1750 }
1751 
1752 static int
1753 show_dev(const char *dev, void *arg)
1754 {
1755 	show_state_t	*state = arg;
1756 	char		buf[DLADM_STRSIZE];
1757 	char		*fmt;
1758 
1759 	if (state->ls_parseable)
1760 		fmt = "DEV=\"%s\" STATE=\"%s\" SPEED=\"%u\" ";
1761 	else
1762 		fmt = "%-12s%-10s%4uMb  ";
1763 
1764 	if (!state->ls_donefirst) {
1765 		if (!state->ls_parseable) {
1766 			(void) printf("%-12s%-10s%6s  %s\n", "DEV", "STATE",
1767 			    "SPEED", "DUPLEX");
1768 		}
1769 		state->ls_donefirst = B_TRUE;
1770 	}
1771 
1772 	/*LINTED: E_SEC_PRINTF_VAR_FMT*/
1773 	(void) printf(fmt, dev, get_linkstate(dev, B_FALSE, buf),
1774 	    (uint_t)(get_ifspeed(dev, B_FALSE) / 1000000ull));
1775 
1776 	(void) get_linkduplex(dev, B_FALSE, buf);
1777 	if (state->ls_parseable)
1778 		(void) printf("DUPLEX=\"%s\"\n", buf);
1779 	else
1780 		(void) printf("%s\n", buf);
1781 
1782 	return (DLADM_WALK_CONTINUE);
1783 }
1784 
1785 static int
1786 show_dev_stats(const char *dev, void *arg)
1787 {
1788 	show_state_t *state = arg;
1789 	pktsum_t stats, diff_stats;
1790 
1791 	if (state->ls_firstonly) {
1792 		if (state->ls_donefirst)
1793 			return (DLADM_WALK_CONTINUE);
1794 		state->ls_donefirst = B_TRUE;
1795 	} else {
1796 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
1797 	}
1798 
1799 	get_mac_stats(dev, &stats);
1800 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
1801 
1802 	(void) printf("%-12s", dev);
1803 	(void) printf("%-10llu", diff_stats.ipackets);
1804 	(void) printf("%-12llu", diff_stats.rbytes);
1805 	(void) printf("%-8u", diff_stats.ierrors);
1806 	(void) printf("%-10llu", diff_stats.opackets);
1807 	(void) printf("%-12llu", diff_stats.obytes);
1808 	(void) printf("%-8u\n", diff_stats.oerrors);
1809 
1810 	state->ls_prevstats = stats;
1811 	return (DLADM_WALK_CONTINUE);
1812 }
1813 
1814 static void
1815 do_show_link(int argc, char *argv[])
1816 {
1817 	int		option;
1818 	boolean_t	s_arg = B_FALSE;
1819 	boolean_t	i_arg = B_FALSE;
1820 	uint32_t	flags = DLADM_OPT_ACTIVE;
1821 	boolean_t	p_arg = B_FALSE;
1822 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
1823 	int		interval = 0;
1824 	show_state_t	state;
1825 	dladm_status_t	status;
1826 
1827 	opterr = 0;
1828 	while ((option = getopt_long(argc, argv, ":pPsi:",
1829 	    show_lopts, NULL)) != -1) {
1830 		switch (option) {
1831 		case 'p':
1832 			if (p_arg)
1833 				die_optdup(option);
1834 
1835 			p_arg = B_TRUE;
1836 			break;
1837 		case 's':
1838 			if (s_arg)
1839 				die_optdup(option);
1840 
1841 			s_arg = B_TRUE;
1842 			break;
1843 		case 'P':
1844 			if (flags != DLADM_OPT_ACTIVE)
1845 				die_optdup(option);
1846 
1847 			flags = DLADM_OPT_PERSIST;
1848 			break;
1849 		case 'i':
1850 			if (i_arg)
1851 				die_optdup(option);
1852 
1853 			i_arg = B_TRUE;
1854 			if (!str2int(optarg, &interval) || interval == 0)
1855 				die("invalid interval value '%s'", optarg);
1856 			break;
1857 		default:
1858 			die_opterr(optopt, option);
1859 			break;
1860 		}
1861 	}
1862 
1863 	if (i_arg && !s_arg)
1864 		die("the option -i can be used only with -s");
1865 
1866 	if (s_arg && (p_arg || flags != DLADM_OPT_ACTIVE))
1867 		die("the option -%c cannot be used with -s", p_arg ? 'p' : 'P');
1868 
1869 	/* get link name (optional last argument) */
1870 	if (optind == (argc-1)) {
1871 		uint32_t	f;
1872 
1873 		if ((status = dladm_name2info(argv[optind], &linkid, &f,
1874 		    NULL, NULL)) != DLADM_STATUS_OK) {
1875 			die_dlerr(status, "link %s is not valid", argv[optind]);
1876 		}
1877 
1878 		if (!(f & flags)) {
1879 			die_dlerr(DLADM_STATUS_BADARG, "link %s is %s",
1880 			    argv[optind], flags == DLADM_OPT_PERSIST ?
1881 			    "a temporary link" : "temporarily removed");
1882 		}
1883 	} else if (optind != argc) {
1884 		usage();
1885 	}
1886 
1887 	if (s_arg) {
1888 		link_stats(linkid, interval);
1889 		return;
1890 	}
1891 
1892 	state.ls_parseable = p_arg;
1893 	state.ls_flags = flags;
1894 	state.ls_donefirst = B_FALSE;
1895 	if (linkid == DATALINK_ALL_LINKID) {
1896 		(void) dladm_walk_datalink_id(show_link, &state,
1897 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
1898 	} else {
1899 		(void) show_link(linkid, &state);
1900 		if (state.ls_status != DLADM_STATUS_OK) {
1901 			die_dlerr(state.ls_status, "failed to show link %s",
1902 			    argv[optind]);
1903 		}
1904 	}
1905 }
1906 
1907 static void
1908 do_show_aggr(int argc, char *argv[])
1909 {
1910 	boolean_t		L_arg = B_FALSE;
1911 	boolean_t		s_arg = B_FALSE;
1912 	boolean_t		i_arg = B_FALSE;
1913 	boolean_t		p_arg = B_FALSE;
1914 	boolean_t		x_arg = B_FALSE;
1915 	show_grp_state_t	state;
1916 	uint32_t		flags = DLADM_OPT_ACTIVE;
1917 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
1918 	int			option;
1919 	int			interval = 0;
1920 	int			key;
1921 	dladm_status_t		status;
1922 
1923 	opterr = 0;
1924 	while ((option = getopt_long(argc, argv, ":LpPxsi:",
1925 	    show_lopts, NULL)) != -1) {
1926 		switch (option) {
1927 		case 'L':
1928 			if (L_arg)
1929 				die_optdup(option);
1930 
1931 			L_arg = B_TRUE;
1932 			break;
1933 		case 'p':
1934 			if (p_arg)
1935 				die_optdup(option);
1936 
1937 			p_arg = B_TRUE;
1938 			break;
1939 		case 'x':
1940 			if (x_arg)
1941 				die_optdup(option);
1942 
1943 			x_arg = B_TRUE;
1944 			break;
1945 		case 'P':
1946 			if (flags != DLADM_OPT_ACTIVE)
1947 				die_optdup(option);
1948 
1949 			flags = DLADM_OPT_PERSIST;
1950 			break;
1951 		case 's':
1952 			if (s_arg)
1953 				die_optdup(option);
1954 
1955 			s_arg = B_TRUE;
1956 			break;
1957 		case 'i':
1958 			if (i_arg)
1959 				die_optdup(option);
1960 
1961 			i_arg = B_TRUE;
1962 			if (!str2int(optarg, &interval) || interval == 0)
1963 				die("invalid interval value '%s'", optarg);
1964 			break;
1965 		default:
1966 			die_opterr(optopt, option);
1967 			break;
1968 		}
1969 	}
1970 
1971 	if (i_arg && !s_arg)
1972 		die("the option -i can be used only with -s");
1973 
1974 	if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) {
1975 		die("the option -%c cannot be used with -s",
1976 		    L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P')));
1977 	}
1978 
1979 	if (L_arg && flags != DLADM_OPT_ACTIVE)
1980 		die("the option -P cannot be used with -L");
1981 
1982 	if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE))
1983 		die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P');
1984 
1985 	/* get aggregation key or aggrname (optional last argument) */
1986 	if (optind == (argc-1)) {
1987 		if (!str2int(argv[optind], &key)) {
1988 			status = dladm_name2info(argv[optind], &linkid, NULL,
1989 			    NULL, NULL);
1990 		} else {
1991 			status = dladm_key2linkid((uint16_t)key,
1992 			    &linkid, DLADM_OPT_ACTIVE);
1993 		}
1994 
1995 		if (status != DLADM_STATUS_OK)
1996 			die("non-existent aggregation '%s'", argv[optind]);
1997 
1998 	} else if (optind != argc) {
1999 		usage();
2000 	}
2001 
2002 	bzero(&state, sizeof (state));
2003 	state.gs_lacp = L_arg;
2004 	state.gs_stats = s_arg;
2005 	state.gs_flags = flags;
2006 	state.gs_parseable = p_arg;
2007 	state.gs_extended = x_arg;
2008 
2009 	if (s_arg) {
2010 		aggr_stats(linkid, &state, interval);
2011 		return;
2012 	}
2013 
2014 	if (linkid == DATALINK_ALL_LINKID) {
2015 		(void) dladm_walk_datalink_id(show_aggr, &state,
2016 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
2017 	} else {
2018 		(void) show_aggr(linkid, &state);
2019 		if (state.gs_status != DLADM_STATUS_OK) {
2020 			die_dlerr(state.gs_status, "failed to show aggr %s",
2021 			    argv[optind]);
2022 		}
2023 	}
2024 }
2025 
2026 static void
2027 do_show_dev(int argc, char *argv[])
2028 {
2029 	int		option;
2030 	char		*dev = NULL;
2031 	boolean_t	s_arg = B_FALSE;
2032 	boolean_t	i_arg = B_FALSE;
2033 	boolean_t	p_arg = B_FALSE;
2034 	datalink_id_t	linkid;
2035 	int		interval = 0;
2036 	show_state_t	state;
2037 
2038 	opterr = 0;
2039 	while ((option = getopt_long(argc, argv, ":psi:",
2040 	    show_lopts, NULL)) != -1) {
2041 		switch (option) {
2042 		case 'p':
2043 			if (p_arg)
2044 				die_optdup(option);
2045 
2046 			p_arg = B_TRUE;
2047 			break;
2048 		case 's':
2049 			if (s_arg)
2050 				die_optdup(option);
2051 
2052 			s_arg = B_TRUE;
2053 			break;
2054 		case 'i':
2055 			if (i_arg)
2056 				die_optdup(option);
2057 
2058 			i_arg = B_TRUE;
2059 			if (!str2int(optarg, &interval) || interval == 0)
2060 				die("invalid interval value '%s'", optarg);
2061 			break;
2062 		default:
2063 			die_opterr(optopt, option);
2064 			break;
2065 		}
2066 	}
2067 
2068 	if (i_arg && !s_arg)
2069 		die("the option -i can be used only with -s");
2070 
2071 	if (s_arg && p_arg)
2072 		die("the option -s cannot be used with -p");
2073 
2074 	/* get dev name (optional last argument) */
2075 	if (optind == (argc-1)) {
2076 		uint32_t flags;
2077 
2078 		dev = argv[optind];
2079 
2080 		if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK)
2081 			die("invalid device %s", dev);
2082 
2083 		if ((dladm_datalink_id2info(linkid, &flags, NULL, NULL,
2084 		    NULL, 0) != DLADM_STATUS_OK) ||
2085 		    !(flags & DLADM_OPT_ACTIVE)) {
2086 			die("device %s has been removed", dev);
2087 		}
2088 	} else if (optind != argc) {
2089 		usage();
2090 	}
2091 
2092 	if (s_arg) {
2093 		dev_stats(dev, interval);
2094 		return;
2095 	}
2096 
2097 	state.ls_donefirst = B_FALSE;
2098 	state.ls_parseable = p_arg;
2099 	if (dev == NULL) {
2100 		(void) dladm_mac_walk(show_dev, &state);
2101 	} else {
2102 		(void) show_dev(dev, &state);
2103 	}
2104 }
2105 
2106 static void
2107 print_phys_head(show_state_t *state)
2108 {
2109 	if (state->ls_donefirst)
2110 		return;
2111 	state->ls_donefirst = B_TRUE;
2112 
2113 	if (state->ls_parseable)
2114 		return;
2115 
2116 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2117 		(void) printf("%-12s%-20s%-10s%6s  %-9s%s\n", "LINK",
2118 		    "MEDIA", "STATE", "SPEED", "DUPLEX", "DEVICE");
2119 	} else {
2120 		(void) printf("%-12s%-12s%-20s%s\n", "LINK", "DEVICE",
2121 		    "MEDIA", "FLAGS");
2122 	}
2123 }
2124 
2125 static dladm_status_t
2126 print_phys(show_state_t *state, datalink_id_t linkid, char **pptr, char *lim)
2127 {
2128 	char			link[MAXLINKNAMELEN];
2129 	dladm_phys_attr_t	dpa;
2130 	char			buf[DLADM_STRSIZE];
2131 	uint32_t		flags;
2132 	datalink_class_t	class;
2133 	uint32_t		media;
2134 	dladm_status_t		status;
2135 
2136 	if ((status = dladm_datalink_id2info(linkid, &flags, &class, &media,
2137 	    link, sizeof (link))) != DLADM_STATUS_OK) {
2138 		goto done;
2139 	}
2140 
2141 	if (class != DATALINK_CLASS_PHYS) {
2142 		status = DLADM_STATUS_BADARG;
2143 		goto done;
2144 	}
2145 
2146 	if (!(state->ls_flags & flags)) {
2147 		status = DLADM_STATUS_NOTFOUND;
2148 		goto done;
2149 	}
2150 
2151 	status = dladm_phys_info(linkid, &dpa, state->ls_flags);
2152 	if (status != DLADM_STATUS_OK)
2153 		goto done;
2154 
2155 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2156 		char		name[MAXLINKNAMELEN];
2157 		boolean_t	islink;
2158 
2159 		if (!dpa.dp_novanity) {
2160 			(void) strlcpy(name, link, sizeof (name));
2161 			islink = B_TRUE;
2162 		} else {
2163 			/*
2164 			 * This is a physical link that does not have
2165 			 * vanity naming support.
2166 			 */
2167 			(void) strlcpy(name, dpa.dp_dev, sizeof (name));
2168 			islink = B_FALSE;
2169 		}
2170 
2171 		if (state->ls_parseable) {
2172 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2173 			    "LINK=\"%s\" MEDIA=\"%s\" ", link,
2174 			    dladm_media2str(media, buf));
2175 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2176 			    "STATE=\"%s\" SPEED=\"%uMb\" ",
2177 			    get_linkstate(name, islink, buf),
2178 			    (uint_t)((get_ifspeed(name, islink)) / 1000000ull));
2179 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2180 			    "DUPLEX=\"%s\" DEVICE=\"%s\"\n",
2181 			    get_linkduplex(name, islink, buf), dpa.dp_dev);
2182 		} else {
2183 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2184 			    "%-12s%-20s", link,
2185 			    dladm_media2str(media, buf));
2186 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2187 			    "%-10s%4uMb  ",
2188 			    get_linkstate(name, islink, buf),
2189 			    (uint_t)((get_ifspeed(name, islink)) / 1000000ull));
2190 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2191 			    "%-9s%s\n", get_linkduplex(name, islink, buf),
2192 			    dpa.dp_dev);
2193 		}
2194 	} else {
2195 		if (state->ls_parseable) {
2196 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2197 			    "LINK=\"%s\" DEVICE=\"%s\" MEDIA=\"%s\" "
2198 			    "FLAGS=\"%c----\"\n", link, dpa.dp_dev,
2199 			    dladm_media2str(media, buf),
2200 			    flags & DLADM_OPT_ACTIVE ? '-' : 'r');
2201 		} else {
2202 			*pptr += snprintf(*pptr, BUFLEN(lim, *pptr),
2203 			    "%-12s%-12s%-20s%c----\n", link,
2204 			    dpa.dp_dev, dladm_media2str(media, buf),
2205 			    flags & DLADM_OPT_ACTIVE ? '-' : 'r');
2206 		}
2207 	}
2208 
2209 done:
2210 	return (status);
2211 }
2212 
2213 static int
2214 show_phys(datalink_id_t linkid, void *arg)
2215 {
2216 	show_state_t	*state = arg;
2217 	dladm_status_t	status;
2218 	char		buf[MAXLINELEN];
2219 	char		*ptr = buf, *lim = buf + MAXLINELEN;
2220 
2221 	status = print_phys(state, linkid, &ptr, lim);
2222 	if (status != DLADM_STATUS_OK)
2223 		goto done;
2224 	print_phys_head(state);
2225 	(void) printf("%s", buf);
2226 
2227 done:
2228 	state->ls_status = status;
2229 	return (DLADM_WALK_CONTINUE);
2230 }
2231 
2232 static void
2233 print_vlan_head(show_state_t *state)
2234 {
2235 	if (state->ls_donefirst)
2236 		return;
2237 	state->ls_donefirst = B_TRUE;
2238 
2239 	if (state->ls_parseable)
2240 		return;
2241 
2242 	(void) printf("%-12s%5s   %-12s%s\n", "LINK", "VID", "OVER", "FLAGS");
2243 }
2244 
2245 /*
2246  * Print the active topology information.
2247  */
2248 static dladm_status_t
2249 print_vlan(show_state_t *state, datalink_id_t linkid, char **pptr, char *lim)
2250 {
2251 	char			link[MAXLINKNAMELEN];
2252 	char			over[MAXLINKNAMELEN];
2253 	char			*fmt;
2254 	dladm_vlan_attr_t	vinfo;
2255 	uint32_t		flags;
2256 	dladm_status_t		status;
2257 
2258 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link,
2259 	    sizeof (link))) != DLADM_STATUS_OK) {
2260 		goto done;
2261 	}
2262 
2263 	if (!(state->ls_flags & flags)) {
2264 		status = DLADM_STATUS_NOTFOUND;
2265 		goto done;
2266 	}
2267 
2268 	if ((status = dladm_vlan_info(linkid, &vinfo, state->ls_flags)) !=
2269 	    DLADM_STATUS_OK || (status = dladm_datalink_id2info(
2270 	    vinfo.dv_linkid, NULL, NULL, NULL, over, sizeof (over))) !=
2271 	    DLADM_STATUS_OK) {
2272 		goto done;
2273 	}
2274 
2275 	if (state->ls_parseable)
2276 		fmt = "LINK=\"%s\" VID=\"%d\" OVER=\"%s\" FLAGS=\"%c%c---\"\n";
2277 	else
2278 		fmt = "%-12s%5d   %-12s%c%c---\n";
2279 	/*LINTED: E_SEC_PRINTF_VAR_FMT*/
2280 	*pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link,
2281 	    vinfo.dv_vid, over, vinfo.dv_force ? 'f' : '-',
2282 	    vinfo.dv_implicit ? 'i' : '-');
2283 
2284 done:
2285 	return (status);
2286 }
2287 
2288 static int
2289 show_vlan(datalink_id_t linkid, void *arg)
2290 {
2291 	show_state_t	*state = arg;
2292 	dladm_status_t	status;
2293 	char		buf[MAXLINELEN];
2294 	char		*ptr = buf, *lim = buf + MAXLINELEN;
2295 
2296 	status = print_vlan(state, linkid, &ptr, lim);
2297 	if (status != DLADM_STATUS_OK)
2298 		goto done;
2299 	print_vlan_head(state);
2300 	(void) printf("%s", buf);
2301 
2302 done:
2303 	state->ls_status = status;
2304 	return (DLADM_WALK_CONTINUE);
2305 }
2306 
2307 static void
2308 do_show_phys(int argc, char *argv[])
2309 {
2310 	int		option;
2311 	uint32_t	flags = DLADM_OPT_ACTIVE;
2312 	boolean_t	p_arg = B_FALSE;
2313 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2314 	show_state_t	state;
2315 	dladm_status_t	status;
2316 
2317 	opterr = 0;
2318 	while ((option = getopt_long(argc, argv, ":pP",
2319 	    show_lopts, NULL)) != -1) {
2320 		switch (option) {
2321 		case 'p':
2322 			if (p_arg)
2323 				die_optdup(option);
2324 
2325 			p_arg = B_TRUE;
2326 			break;
2327 		case 'P':
2328 			if (flags != DLADM_OPT_ACTIVE)
2329 				die_optdup(option);
2330 
2331 			flags = DLADM_OPT_PERSIST;
2332 			break;
2333 		default:
2334 			die_opterr(optopt, option);
2335 			break;
2336 		}
2337 	}
2338 
2339 	/* get link name (optional last argument) */
2340 	if (optind == (argc-1)) {
2341 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
2342 		    NULL, NULL)) != DLADM_STATUS_OK) {
2343 			die_dlerr(status, "link %s is not valid", argv[optind]);
2344 		}
2345 	} else if (optind != argc) {
2346 		usage();
2347 	}
2348 
2349 	state.ls_parseable = p_arg;
2350 	state.ls_flags = flags;
2351 	state.ls_donefirst = B_FALSE;
2352 
2353 	if (linkid == DATALINK_ALL_LINKID) {
2354 		(void) dladm_walk_datalink_id(show_phys, &state,
2355 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags);
2356 	} else {
2357 		(void) show_phys(linkid, &state);
2358 		if (state.ls_status != DLADM_STATUS_OK) {
2359 			die_dlerr(state.ls_status,
2360 			    "failed to show physical link %s", argv[optind]);
2361 		}
2362 	}
2363 }
2364 
2365 static void
2366 do_show_vlan(int argc, char *argv[])
2367 {
2368 	int		option;
2369 	uint32_t	flags = DLADM_OPT_ACTIVE;
2370 	boolean_t	p_arg = B_FALSE;
2371 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2372 	show_state_t	state;
2373 	dladm_status_t	status;
2374 
2375 	opterr = 0;
2376 	while ((option = getopt_long(argc, argv, ":pP",
2377 	    show_lopts, NULL)) != -1) {
2378 		switch (option) {
2379 		case 'p':
2380 			if (p_arg)
2381 				die_optdup(option);
2382 
2383 			p_arg = B_TRUE;
2384 			break;
2385 		case 'P':
2386 			if (flags != DLADM_OPT_ACTIVE)
2387 				die_optdup(option);
2388 
2389 			flags = DLADM_OPT_PERSIST;
2390 			break;
2391 		default:
2392 			die_opterr(optopt, option);
2393 			break;
2394 		}
2395 	}
2396 
2397 	/* get link name (optional last argument) */
2398 	if (optind == (argc-1)) {
2399 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
2400 		    NULL, NULL)) != DLADM_STATUS_OK) {
2401 			die_dlerr(status, "link %s is not valid", argv[optind]);
2402 		}
2403 	} else if (optind != argc) {
2404 		usage();
2405 	}
2406 
2407 	state.ls_parseable = p_arg;
2408 	state.ls_flags = flags;
2409 	state.ls_donefirst = B_FALSE;
2410 
2411 	if (linkid == DATALINK_ALL_LINKID) {
2412 		(void) dladm_walk_datalink_id(show_vlan, &state,
2413 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags);
2414 	} else {
2415 		(void) show_vlan(linkid, &state);
2416 		if (state.ls_status != DLADM_STATUS_OK) {
2417 			die_dlerr(state.ls_status, "failed to show vlan %s",
2418 			    argv[optind]);
2419 		}
2420 	}
2421 }
2422 
2423 static void
2424 link_stats(datalink_id_t linkid, uint_t interval)
2425 {
2426 	show_state_t	state;
2427 
2428 	bzero(&state, sizeof (state));
2429 
2430 	/*
2431 	 * If an interval is specified, continuously show the stats
2432 	 * only for the first MAC port.
2433 	 */
2434 	state.ls_firstonly = (interval != 0);
2435 
2436 	for (;;) {
2437 		(void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n",
2438 		    "LINK", "IPACKETS", "RBYTES", "IERRORS", "OPACKETS",
2439 		    "OBYTES", "OERRORS");
2440 
2441 		state.ls_donefirst = B_FALSE;
2442 		if (linkid == DATALINK_ALL_LINKID) {
2443 			(void) dladm_walk_datalink_id(show_link_stats, &state,
2444 			    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
2445 			    DLADM_OPT_ACTIVE);
2446 		} else {
2447 			(void) show_link_stats(linkid, &state);
2448 		}
2449 
2450 		if (interval == 0)
2451 			break;
2452 
2453 		(void) sleep(interval);
2454 	}
2455 }
2456 
2457 static void
2458 aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval)
2459 {
2460 	/*
2461 	 * If an interval is specified, continuously show the stats
2462 	 * only for the first group.
2463 	 */
2464 	state->gs_firstonly = (interval != 0);
2465 
2466 	for (;;) {
2467 
2468 		(void) printf("%-12s%-10s%8s  %8s  %8s  %8s  %-9s%s\n",
2469 		    "LINK", "PORT", "IPACKETS", "RBYTES", "OPACKETS",
2470 		    "OBYTES", "IPKTDIST", "OPKTDIST");
2471 
2472 		state->gs_donefirst = B_FALSE;
2473 		if (linkid == DATALINK_ALL_LINKID)
2474 			(void) dladm_walk_datalink_id(show_aggr, state,
2475 			    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
2476 			    DLADM_OPT_ACTIVE);
2477 		else
2478 			(void) show_aggr(linkid, state);
2479 
2480 		if (interval == 0)
2481 			break;
2482 
2483 		(void) sleep(interval);
2484 	}
2485 }
2486 
2487 static void
2488 dev_stats(const char *dev, uint32_t interval)
2489 {
2490 	show_state_t state;
2491 
2492 	bzero(&state, sizeof (state));
2493 
2494 	/*
2495 	 * If an interval is specified, continuously show the stats
2496 	 * only for the first MAC port.
2497 	 */
2498 	state.ls_firstonly = (interval != 0);
2499 
2500 	for (;;) {
2501 
2502 		(void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n",
2503 		    "DEV", "IPACKETS", "RBYTES", "IERRORS", "OPACKETS",
2504 		    "OBYTES", "OERRORS");
2505 
2506 		state.ls_donefirst = B_FALSE;
2507 		if (dev == NULL)
2508 			(void) dladm_mac_walk(show_dev_stats, &state);
2509 		else
2510 			(void) show_dev_stats(dev, &state);
2511 
2512 		if (interval == 0)
2513 			break;
2514 
2515 		(void) sleep(interval);
2516 	}
2517 
2518 	if (dev != NULL && state.ls_status != DLADM_STATUS_OK)
2519 		die_dlerr(state.ls_status, "cannot show device '%s'", dev);
2520 }
2521 
2522 /* accumulate stats (s1 += (s2 - s3)) */
2523 static void
2524 stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
2525 {
2526 	s1->ipackets += (s2->ipackets - s3->ipackets);
2527 	s1->opackets += (s2->opackets - s3->opackets);
2528 	s1->rbytes += (s2->rbytes - s3->rbytes);
2529 	s1->obytes += (s2->obytes - s3->obytes);
2530 	s1->ierrors += (s2->ierrors - s3->ierrors);
2531 	s1->oerrors += (s2->oerrors - s3->oerrors);
2532 }
2533 
2534 /* compute stats differences (s1 = s2 - s3) */
2535 static void
2536 stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
2537 {
2538 	s1->ipackets = s2->ipackets - s3->ipackets;
2539 	s1->opackets = s2->opackets - s3->opackets;
2540 	s1->rbytes = s2->rbytes - s3->rbytes;
2541 	s1->obytes = s2->obytes - s3->obytes;
2542 	s1->ierrors = s2->ierrors - s3->ierrors;
2543 	s1->oerrors = s2->oerrors - s3->oerrors;
2544 }
2545 
2546 static void
2547 get_stats(char *module, int instance, const char *name, pktsum_t *stats)
2548 {
2549 	kstat_ctl_t	*kcp;
2550 	kstat_t		*ksp;
2551 
2552 	if ((kcp = kstat_open()) == NULL) {
2553 		warn("kstat open operation failed");
2554 		return;
2555 	}
2556 
2557 	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
2558 		/*
2559 		 * The kstat query could fail if the underlying MAC
2560 		 * driver was already detached.
2561 		 */
2562 		(void) kstat_close(kcp);
2563 		return;
2564 	}
2565 
2566 	if (kstat_read(kcp, ksp, NULL) == -1)
2567 		goto bail;
2568 
2569 	if (kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
2570 	    &stats->ipackets) < 0)
2571 		goto bail;
2572 
2573 	if (kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
2574 	    &stats->opackets) < 0)
2575 		goto bail;
2576 
2577 	if (kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
2578 	    &stats->rbytes) < 0)
2579 		goto bail;
2580 
2581 	if (kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
2582 	    &stats->obytes) < 0)
2583 		goto bail;
2584 
2585 	if (kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
2586 	    &stats->ierrors) < 0)
2587 		goto bail;
2588 
2589 	if (kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
2590 	    &stats->oerrors) < 0)
2591 		goto bail;
2592 
2593 bail:
2594 	(void) kstat_close(kcp);
2595 	return;
2596 
2597 }
2598 
2599 static void
2600 get_mac_stats(const char *dev, pktsum_t *stats)
2601 {
2602 	char module[DLPI_LINKNAME_MAX];
2603 	uint_t instance;
2604 
2605 	bzero(stats, sizeof (*stats));
2606 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
2607 		return;
2608 
2609 	get_stats(module, instance, "mac", stats);
2610 }
2611 
2612 static void
2613 get_link_stats(const char *link, pktsum_t *stats)
2614 {
2615 	bzero(stats, sizeof (*stats));
2616 	get_stats("link", 0, link, stats);
2617 }
2618 
2619 static int
2620 query_kstat(char *module, int instance, const char *name, const char *stat,
2621     uint8_t type, void *val)
2622 {
2623 	kstat_ctl_t	*kcp;
2624 	kstat_t		*ksp;
2625 
2626 	if ((kcp = kstat_open()) == NULL) {
2627 		warn("kstat open operation failed");
2628 		return (-1);
2629 	}
2630 
2631 	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
2632 		/*
2633 		 * The kstat query could fail if the underlying MAC
2634 		 * driver was already detached.
2635 		 */
2636 		goto bail;
2637 	}
2638 
2639 	if (kstat_read(kcp, ksp, NULL) == -1) {
2640 		warn("kstat read failed");
2641 		goto bail;
2642 	}
2643 
2644 	if (kstat_value(ksp, stat, type, val) < 0)
2645 		goto bail;
2646 
2647 	(void) kstat_close(kcp);
2648 	return (0);
2649 
2650 bail:
2651 	(void) kstat_close(kcp);
2652 	return (-1);
2653 }
2654 
2655 static int
2656 get_one_kstat(const char *name, const char *stat, uint8_t type,
2657     void *val, boolean_t islink)
2658 {
2659 	char		module[DLPI_LINKNAME_MAX];
2660 	uint_t		instance;
2661 
2662 	if (islink) {
2663 		return (query_kstat("link", 0, name, stat, type, val));
2664 	} else {
2665 		if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
2666 			return (-1);
2667 
2668 		return (query_kstat(module, instance, "mac", stat, type, val));
2669 	}
2670 }
2671 
2672 static uint64_t
2673 get_ifspeed(const char *name, boolean_t islink)
2674 {
2675 	uint64_t ifspeed = 0;
2676 
2677 	(void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64,
2678 	    &ifspeed, islink);
2679 
2680 	return (ifspeed);
2681 }
2682 
2683 static const char *
2684 get_linkstate(const char *name, boolean_t islink, char *buf)
2685 {
2686 	link_state_t	linkstate;
2687 
2688 	if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32,
2689 	    &linkstate, islink) != 0) {
2690 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
2691 		return (buf);
2692 	}
2693 	return (dladm_linkstate2str(linkstate, buf));
2694 }
2695 
2696 static const char *
2697 get_linkduplex(const char *name, boolean_t islink, char *buf)
2698 {
2699 	link_duplex_t	linkduplex;
2700 
2701 	if (get_one_kstat(name, "link_duplex", KSTAT_DATA_UINT32,
2702 	    &linkduplex, islink) != 0) {
2703 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
2704 		return (buf);
2705 	}
2706 
2707 	return (dladm_linkduplex2str(linkduplex, buf));
2708 }
2709 
2710 #define	WIFI_CMD_SCAN	0x00000001
2711 #define	WIFI_CMD_SHOW	0x00000002
2712 #define	WIFI_CMD_ALL	(WIFI_CMD_SCAN | WIFI_CMD_SHOW)
2713 typedef struct wifi_field {
2714 	const char	*wf_name;
2715 	const char	*wf_header;
2716 	uint_t		wf_width;
2717 	uint_t		wf_mask;
2718 	uint_t		wf_cmdtype;
2719 } wifi_field_t;
2720 
2721 static wifi_field_t wifi_fields[] = {
2722 { "link",	"LINK",		10, 0,			WIFI_CMD_ALL},
2723 { "essid",	"ESSID",	19, DLADM_WLAN_ATTR_ESSID,	WIFI_CMD_ALL},
2724 { "bssid",	"BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
2725 { "ibssid",	"BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
2726 { "mode",	"MODE",		6,  DLADM_WLAN_ATTR_MODE,	WIFI_CMD_ALL},
2727 { "speed",	"SPEED",	6,  DLADM_WLAN_ATTR_SPEED,	WIFI_CMD_ALL},
2728 { "auth",	"AUTH",		8,  DLADM_WLAN_ATTR_AUTH,	WIFI_CMD_SHOW},
2729 { "bsstype",	"BSSTYPE",	8,  DLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL},
2730 { "sec",	"SEC",		6,  DLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL},
2731 { "status",	"STATUS",	17, DLADM_WLAN_LINKATTR_STATUS, WIFI_CMD_SHOW},
2732 { "strength",	"STRENGTH",	10, DLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}}
2733 ;
2734 
2735 static char *all_scan_wifi_fields =
2736 	"link,essid,bssid,sec,strength,mode,speed,bsstype";
2737 static char *all_show_wifi_fields =
2738 	"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
2739 static char *def_scan_wifi_fields =
2740 	"link,essid,bssid,sec,strength,mode,speed";
2741 static char *def_show_wifi_fields =
2742 	"link,status,essid,sec,strength,mode,speed";
2743 
2744 #define	WIFI_MAX_FIELDS		(sizeof (wifi_fields) / sizeof (wifi_field_t))
2745 #define	WIFI_MAX_FIELD_LEN	32
2746 
2747 typedef struct {
2748 	char	*s_buf;
2749 	char	**s_fields;	/* array of pointer to the fields in s_buf */
2750 	uint_t	s_nfields;	/* the number of fields in s_buf */
2751 } split_t;
2752 
2753 /*
2754  * Free the split_t structure pointed to by `sp'.
2755  */
2756 static void
2757 splitfree(split_t *sp)
2758 {
2759 	free(sp->s_buf);
2760 	free(sp->s_fields);
2761 	free(sp);
2762 }
2763 
2764 /*
2765  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
2766  * length.  Return a pointer to a split_t containing the split fields, or NULL
2767  * on failure.
2768  */
2769 static split_t *
2770 split(const char *str, uint_t maxfields, uint_t maxlen)
2771 {
2772 	char	*field, *token, *lasts = NULL;
2773 	split_t	*sp;
2774 
2775 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
2776 		return (NULL);
2777 
2778 	sp = calloc(sizeof (split_t), 1);
2779 	if (sp == NULL)
2780 		return (NULL);
2781 
2782 	sp->s_buf = strdup(str);
2783 	sp->s_fields = malloc(sizeof (char *) * maxfields);
2784 	if (sp->s_buf == NULL || sp->s_fields == NULL)
2785 		goto fail;
2786 
2787 	token = sp->s_buf;
2788 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
2789 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
2790 			goto fail;
2791 		token = NULL;
2792 		sp->s_fields[sp->s_nfields++] = field;
2793 	}
2794 	return (sp);
2795 fail:
2796 	splitfree(sp);
2797 	return (NULL);
2798 }
2799 
2800 static int
2801 parse_wifi_fields(char *str, wifi_field_t ***fields, uint_t *countp,
2802     uint_t cmdtype)
2803 {
2804 	uint_t		i, j;
2805 	wifi_field_t	**wf = NULL;
2806 	split_t		*sp;
2807 	boolean_t	good_match = B_FALSE;
2808 
2809 	if (cmdtype == WIFI_CMD_SCAN) {
2810 		if (str == NULL)
2811 			str = def_scan_wifi_fields;
2812 		if (strcasecmp(str, "all") == 0)
2813 			str = all_scan_wifi_fields;
2814 	} else if (cmdtype == WIFI_CMD_SHOW) {
2815 		if (str == NULL)
2816 			str = def_show_wifi_fields;
2817 		if (strcasecmp(str, "all") == 0)
2818 			str = all_show_wifi_fields;
2819 	} else {
2820 		return (-1);
2821 	}
2822 
2823 	sp = split(str, WIFI_MAX_FIELDS, WIFI_MAX_FIELD_LEN);
2824 	if (sp == NULL)
2825 		return (-1);
2826 
2827 	wf = malloc(sp->s_nfields * sizeof (wifi_field_t *));
2828 	if (wf == NULL)
2829 		goto fail;
2830 
2831 	for (i = 0; i < sp->s_nfields; i++) {
2832 		for (j = 0; j < WIFI_MAX_FIELDS; j++) {
2833 			if (strcasecmp(sp->s_fields[i],
2834 			    wifi_fields[j].wf_name) == 0) {
2835 				good_match = wifi_fields[j].
2836 				    wf_cmdtype & cmdtype;
2837 				break;
2838 			}
2839 		}
2840 		if (!good_match)
2841 			goto fail;
2842 
2843 		good_match = B_FALSE;
2844 		wf[i] = &wifi_fields[j];
2845 	}
2846 	*countp = i;
2847 	*fields = wf;
2848 	splitfree(sp);
2849 	return (0);
2850 fail:
2851 	free(wf);
2852 	splitfree(sp);
2853 	return (-1);
2854 }
2855 
2856 typedef struct print_wifi_state {
2857 	char		*ws_link;
2858 	boolean_t	ws_parseable;
2859 	boolean_t	ws_header;
2860 	wifi_field_t	**ws_fields;
2861 	uint_t		ws_nfields;
2862 	boolean_t	ws_lastfield;
2863 	uint_t		ws_overflow;
2864 } print_wifi_state_t;
2865 
2866 static void
2867 print_wifi_head(print_wifi_state_t *statep)
2868 {
2869 	int		i;
2870 	wifi_field_t	*wfp;
2871 
2872 	for (i = 0; i < statep->ws_nfields; i++) {
2873 		wfp = statep->ws_fields[i];
2874 		if (i + 1 < statep->ws_nfields)
2875 			(void) printf("%-*s ", wfp->wf_width, wfp->wf_header);
2876 		else
2877 			(void) printf("%s", wfp->wf_header);
2878 	}
2879 	(void) printf("\n");
2880 }
2881 
2882 static void
2883 print_wifi_field(print_wifi_state_t *statep, wifi_field_t *wfp,
2884     const char *value)
2885 {
2886 	uint_t	width = wfp->wf_width;
2887 	uint_t	valwidth = strlen(value);
2888 	uint_t	compress;
2889 
2890 	if (statep->ws_parseable) {
2891 		(void) printf("%s=\"%s\"", wfp->wf_header, value);
2892 	} else {
2893 		if (value[0] == '\0')
2894 			value = "--";
2895 		if (statep->ws_lastfield) {
2896 			(void) printf("%s", value);
2897 			return;
2898 		}
2899 
2900 		if (valwidth > width) {
2901 			statep->ws_overflow += valwidth - width;
2902 		} else if (valwidth < width && statep->ws_overflow > 0) {
2903 			compress = min(statep->ws_overflow, width - valwidth);
2904 			statep->ws_overflow -= compress;
2905 			width -= compress;
2906 		}
2907 		(void) printf("%-*s", width, value);
2908 	}
2909 
2910 	if (!statep->ws_lastfield)
2911 		(void) putchar(' ');
2912 }
2913 
2914 static void
2915 print_wlan_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
2916     dladm_wlan_attr_t *attrp)
2917 {
2918 	char		buf[DLADM_STRSIZE];
2919 	const char	*str = "";
2920 
2921 	if (wfp->wf_mask == 0) {
2922 		print_wifi_field(statep, wfp, statep->ws_link);
2923 		return;
2924 	}
2925 
2926 	if ((wfp->wf_mask & attrp->wa_valid) == 0) {
2927 		print_wifi_field(statep, wfp, "");
2928 		return;
2929 	}
2930 
2931 	switch (wfp->wf_mask) {
2932 	case DLADM_WLAN_ATTR_ESSID:
2933 		str = dladm_wlan_essid2str(&attrp->wa_essid, buf);
2934 		break;
2935 	case DLADM_WLAN_ATTR_BSSID:
2936 		str = dladm_wlan_bssid2str(&attrp->wa_bssid, buf);
2937 		break;
2938 	case DLADM_WLAN_ATTR_SECMODE:
2939 		str = dladm_wlan_secmode2str(&attrp->wa_secmode, buf);
2940 		break;
2941 	case DLADM_WLAN_ATTR_STRENGTH:
2942 		str = dladm_wlan_strength2str(&attrp->wa_strength, buf);
2943 		break;
2944 	case DLADM_WLAN_ATTR_MODE:
2945 		str = dladm_wlan_mode2str(&attrp->wa_mode, buf);
2946 		break;
2947 	case DLADM_WLAN_ATTR_SPEED:
2948 		str = dladm_wlan_speed2str(&attrp->wa_speed, buf);
2949 		(void) strlcat(buf, "Mb", sizeof (buf));
2950 		break;
2951 	case DLADM_WLAN_ATTR_AUTH:
2952 		str = dladm_wlan_auth2str(&attrp->wa_auth, buf);
2953 		break;
2954 	case DLADM_WLAN_ATTR_BSSTYPE:
2955 		str = dladm_wlan_bsstype2str(&attrp->wa_bsstype, buf);
2956 		break;
2957 	}
2958 
2959 	print_wifi_field(statep, wfp, str);
2960 }
2961 
2962 static boolean_t
2963 print_scan_results(void *arg, dladm_wlan_attr_t *attrp)
2964 {
2965 	print_wifi_state_t	*statep = arg;
2966 	int			i;
2967 
2968 	if (statep->ws_header) {
2969 		statep->ws_header = B_FALSE;
2970 		if (!statep->ws_parseable)
2971 			print_wifi_head(statep);
2972 	}
2973 
2974 	statep->ws_overflow = 0;
2975 	for (i = 0; i < statep->ws_nfields; i++) {
2976 		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
2977 		print_wlan_attr(statep, statep->ws_fields[i], attrp);
2978 	}
2979 	(void) putchar('\n');
2980 	return (B_TRUE);
2981 }
2982 
2983 static int
2984 scan_wifi(datalink_id_t linkid, void *arg)
2985 {
2986 	print_wifi_state_t	*statep = arg;
2987 	dladm_status_t		status;
2988 	char			link[MAXLINKNAMELEN];
2989 
2990 	if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
2991 	    sizeof (link)) != DLADM_STATUS_OK) {
2992 		return (DLADM_WALK_CONTINUE);
2993 	}
2994 
2995 	statep->ws_link = link;
2996 	status = dladm_wlan_scan(linkid, statep, print_scan_results);
2997 	if (status != DLADM_STATUS_OK)
2998 		die_dlerr(status, "cannot scan link '%s'", statep->ws_link);
2999 
3000 	return (DLADM_WALK_CONTINUE);
3001 }
3002 
3003 static void
3004 print_link_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
3005     dladm_wlan_linkattr_t *attrp)
3006 {
3007 	char		buf[DLADM_STRSIZE];
3008 	const char	*str = "";
3009 
3010 	if (strcmp(wfp->wf_name, "status") == 0) {
3011 		if ((wfp->wf_mask & attrp->la_valid) != 0)
3012 			str = dladm_wlan_linkstatus2str(&attrp->la_status, buf);
3013 		print_wifi_field(statep, wfp, str);
3014 		return;
3015 	}
3016 	print_wlan_attr(statep, wfp, &attrp->la_wlan_attr);
3017 }
3018 
3019 static int
3020 show_wifi(datalink_id_t linkid, void *arg)
3021 {
3022 	int			i;
3023 	print_wifi_state_t	*statep = arg;
3024 	dladm_wlan_linkattr_t	attr;
3025 	dladm_status_t		status;
3026 	char			link[MAXLINKNAMELEN];
3027 
3028 	if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
3029 	    sizeof (link)) != DLADM_STATUS_OK) {
3030 		return (DLADM_WALK_CONTINUE);
3031 	}
3032 
3033 	status = dladm_wlan_get_linkattr(linkid, &attr);
3034 	if (status != DLADM_STATUS_OK)
3035 		die_dlerr(status, "cannot get link attributes for %s", link);
3036 
3037 	statep->ws_link = link;
3038 
3039 	if (statep->ws_header) {
3040 		statep->ws_header = B_FALSE;
3041 		if (!statep->ws_parseable)
3042 			print_wifi_head(statep);
3043 	}
3044 
3045 	statep->ws_overflow = 0;
3046 	for (i = 0; i < statep->ws_nfields; i++) {
3047 		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
3048 		print_link_attr(statep, statep->ws_fields[i], &attr);
3049 	}
3050 	(void) putchar('\n');
3051 	return (DLADM_WALK_CONTINUE);
3052 }
3053 
3054 static void
3055 do_display_wifi(int argc, char **argv, int cmd)
3056 {
3057 	int			option;
3058 	char			*fields_str = NULL;
3059 	wifi_field_t		**fields;
3060 	int			(*callback)(datalink_id_t, void *);
3061 	uint_t			nfields;
3062 	print_wifi_state_t	state;
3063 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3064 	dladm_status_t		status;
3065 
3066 	if (cmd == WIFI_CMD_SCAN)
3067 		callback = scan_wifi;
3068 	else if (cmd == WIFI_CMD_SHOW)
3069 		callback = show_wifi;
3070 	else
3071 		return;
3072 
3073 	state.ws_parseable = B_FALSE;
3074 	state.ws_header = B_TRUE;
3075 	opterr = 0;
3076 	while ((option = getopt_long(argc, argv, ":o:p",
3077 	    wifi_longopts, NULL)) != -1) {
3078 		switch (option) {
3079 		case 'o':
3080 			fields_str = optarg;
3081 			break;
3082 		case 'p':
3083 			state.ws_parseable = B_TRUE;
3084 			if (fields_str == NULL)
3085 				fields_str = "all";
3086 			break;
3087 		default:
3088 			die_opterr(optopt, option);
3089 			break;
3090 		}
3091 	}
3092 
3093 	if (optind == (argc - 1)) {
3094 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3095 		    NULL, NULL)) != DLADM_STATUS_OK) {
3096 			die_dlerr(status, "link %s is not valid", argv[optind]);
3097 		}
3098 	} else if (optind != argc) {
3099 		usage();
3100 	}
3101 
3102 	if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0)
3103 		die("invalid field(s) specified");
3104 
3105 	state.ws_fields = fields;
3106 	state.ws_nfields = nfields;
3107 
3108 	if (linkid == DATALINK_ALL_LINKID) {
3109 		(void) dladm_walk_datalink_id(callback, &state,
3110 		    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
3111 	} else {
3112 		(void) (*callback)(linkid, &state);
3113 	}
3114 	free(fields);
3115 }
3116 
3117 static void
3118 do_scan_wifi(int argc, char **argv)
3119 {
3120 	do_display_wifi(argc, argv, WIFI_CMD_SCAN);
3121 }
3122 
3123 static void
3124 do_show_wifi(int argc, char **argv)
3125 {
3126 	do_display_wifi(argc, argv, WIFI_CMD_SHOW);
3127 }
3128 
3129 typedef struct wlan_count_attr {
3130 	uint_t		wc_count;
3131 	datalink_id_t	wc_linkid;
3132 } wlan_count_attr_t;
3133 
3134 static int
3135 do_count_wlan(datalink_id_t linkid, void *arg)
3136 {
3137 	wlan_count_attr_t *cp = arg;
3138 
3139 	if (cp->wc_count == 0)
3140 		cp->wc_linkid = linkid;
3141 	cp->wc_count++;
3142 	return (DLADM_WALK_CONTINUE);
3143 }
3144 
3145 static int
3146 parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp)
3147 {
3148 	uint_t			i;
3149 	split_t			*sp;
3150 	dladm_wlan_key_t	*wk;
3151 
3152 	sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_KEYNAME_LEN);
3153 	if (sp == NULL)
3154 		return (-1);
3155 
3156 	wk = malloc(sp->s_nfields * sizeof (dladm_wlan_key_t));
3157 	if (wk == NULL)
3158 		goto fail;
3159 
3160 	for (i = 0; i < sp->s_nfields; i++) {
3161 		char			*s;
3162 		dladm_secobj_class_t	class;
3163 		dladm_status_t		status;
3164 
3165 		(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
3166 		    DLADM_WLAN_MAX_KEYNAME_LEN);
3167 
3168 		wk[i].wk_idx = 1;
3169 		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
3170 			if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
3171 				goto fail;
3172 
3173 			wk[i].wk_idx = (uint_t)(s[1] - '0');
3174 			*s = '\0';
3175 		}
3176 		wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN;
3177 
3178 		status = dladm_get_secobj(wk[i].wk_name, &class,
3179 		    wk[i].wk_val, &wk[i].wk_len, 0);
3180 		if (status != DLADM_STATUS_OK) {
3181 			if (status == DLADM_STATUS_NOTFOUND) {
3182 				status = dladm_get_secobj(wk[i].wk_name,
3183 				    &class, wk[i].wk_val, &wk[i].wk_len,
3184 				    DLADM_OPT_PERSIST);
3185 			}
3186 			if (status != DLADM_STATUS_OK)
3187 				goto fail;
3188 		}
3189 		wk[i].wk_class = class;
3190 	}
3191 	*keys = wk;
3192 	*key_countp = i;
3193 	splitfree(sp);
3194 	return (0);
3195 fail:
3196 	free(wk);
3197 	splitfree(sp);
3198 	return (-1);
3199 }
3200 
3201 static void
3202 do_connect_wifi(int argc, char **argv)
3203 {
3204 	int			option;
3205 	dladm_wlan_attr_t	attr, *attrp;
3206 	dladm_status_t		status = DLADM_STATUS_OK;
3207 	int			timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
3208 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3209 	dladm_wlan_key_t	*keys = NULL;
3210 	uint_t			key_count = 0;
3211 	uint_t			flags = 0;
3212 	dladm_wlan_secmode_t	keysecmode = DLADM_WLAN_SECMODE_NONE;
3213 	char			buf[DLADM_STRSIZE];
3214 
3215 	opterr = 0;
3216 	(void) memset(&attr, 0, sizeof (attr));
3217 	while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
3218 	    wifi_longopts, NULL)) != -1) {
3219 		switch (option) {
3220 		case 'e':
3221 			status = dladm_wlan_str2essid(optarg, &attr.wa_essid);
3222 			if (status != DLADM_STATUS_OK)
3223 				die("invalid ESSID '%s'", optarg);
3224 
3225 			attr.wa_valid |= DLADM_WLAN_ATTR_ESSID;
3226 			/*
3227 			 * Try to connect without doing a scan.
3228 			 */
3229 			flags |= DLADM_WLAN_CONNECT_NOSCAN;
3230 			break;
3231 		case 'i':
3232 			status = dladm_wlan_str2bssid(optarg, &attr.wa_bssid);
3233 			if (status != DLADM_STATUS_OK)
3234 				die("invalid BSSID %s", optarg);
3235 
3236 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
3237 			break;
3238 		case 'a':
3239 			status = dladm_wlan_str2auth(optarg, &attr.wa_auth);
3240 			if (status != DLADM_STATUS_OK)
3241 				die("invalid authentication mode '%s'", optarg);
3242 
3243 			attr.wa_valid |= DLADM_WLAN_ATTR_AUTH;
3244 			break;
3245 		case 'm':
3246 			status = dladm_wlan_str2mode(optarg, &attr.wa_mode);
3247 			if (status != DLADM_STATUS_OK)
3248 				die("invalid mode '%s'", optarg);
3249 
3250 			attr.wa_valid |= DLADM_WLAN_ATTR_MODE;
3251 			break;
3252 		case 'b':
3253 			if ((status = dladm_wlan_str2bsstype(optarg,
3254 			    &attr.wa_bsstype)) != DLADM_STATUS_OK) {
3255 				die("invalid bsstype '%s'", optarg);
3256 			}
3257 
3258 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
3259 			break;
3260 		case 's':
3261 			if ((status = dladm_wlan_str2secmode(optarg,
3262 			    &attr.wa_secmode)) != DLADM_STATUS_OK) {
3263 				die("invalid security mode '%s'", optarg);
3264 			}
3265 
3266 			attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
3267 			break;
3268 		case 'k':
3269 			if (parse_wlan_keys(optarg, &keys, &key_count) < 0)
3270 				die("invalid key(s) '%s'", optarg);
3271 
3272 			if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP)
3273 				keysecmode = DLADM_WLAN_SECMODE_WEP;
3274 			else
3275 				keysecmode = DLADM_WLAN_SECMODE_WPA;
3276 			break;
3277 		case 'T':
3278 			if (strcasecmp(optarg, "forever") == 0) {
3279 				timeout = -1;
3280 				break;
3281 			}
3282 			if (!str2int(optarg, &timeout) || timeout < 0)
3283 				die("invalid timeout value '%s'", optarg);
3284 			break;
3285 		case 'c':
3286 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
3287 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
3288 			break;
3289 		default:
3290 			die_opterr(optopt, option);
3291 			break;
3292 		}
3293 	}
3294 
3295 	if (keysecmode == DLADM_WLAN_SECMODE_NONE) {
3296 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) {
3297 			die("key required for security mode '%s'",
3298 			    dladm_wlan_secmode2str(&attr.wa_secmode, buf));
3299 		}
3300 	} else {
3301 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
3302 		    attr.wa_secmode != keysecmode)
3303 			die("incompatible -s and -k options");
3304 		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
3305 		attr.wa_secmode = keysecmode;
3306 	}
3307 
3308 	if (optind == (argc - 1)) {
3309 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3310 		    NULL, NULL)) != DLADM_STATUS_OK) {
3311 			die_dlerr(status, "link %s is not valid", argv[optind]);
3312 		}
3313 	} else if (optind != argc) {
3314 		usage();
3315 	}
3316 
3317 	if (linkid == DATALINK_ALL_LINKID) {
3318 		wlan_count_attr_t wcattr;
3319 
3320 		wcattr.wc_linkid = DATALINK_INVALID_LINKID;
3321 		wcattr.wc_count = 0;
3322 		(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
3323 		    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
3324 		if (wcattr.wc_count == 0) {
3325 			die("no wifi links are available");
3326 		} else if (wcattr.wc_count > 1) {
3327 			die("link name is required when more than one wifi "
3328 			    "link is available");
3329 		}
3330 		linkid = wcattr.wc_linkid;
3331 	}
3332 	attrp = (attr.wa_valid == 0) ? NULL : &attr;
3333 again:
3334 	if ((status = dladm_wlan_connect(linkid, attrp, timeout, keys,
3335 	    key_count, flags)) != DLADM_STATUS_OK) {
3336 		if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0) {
3337 			/*
3338 			 * Try again with scanning and filtering.
3339 			 */
3340 			flags &= ~DLADM_WLAN_CONNECT_NOSCAN;
3341 			goto again;
3342 		}
3343 
3344 		if (status == DLADM_STATUS_NOTFOUND) {
3345 			if (attr.wa_valid == 0) {
3346 				die("no wifi networks are available");
3347 			} else {
3348 				die("no wifi networks with the specified "
3349 				    "criteria are available");
3350 			}
3351 		}
3352 		die_dlerr(status, "cannot connect");
3353 	}
3354 	free(keys);
3355 }
3356 
3357 /* ARGSUSED */
3358 static int
3359 do_all_disconnect_wifi(datalink_id_t linkid, void *arg)
3360 {
3361 	dladm_status_t	status;
3362 
3363 	status = dladm_wlan_disconnect(linkid);
3364 	if (status != DLADM_STATUS_OK)
3365 		warn_dlerr(status, "cannot disconnect link");
3366 
3367 	return (DLADM_WALK_CONTINUE);
3368 }
3369 
3370 static void
3371 do_disconnect_wifi(int argc, char **argv)
3372 {
3373 	int			option;
3374 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3375 	boolean_t		all_links = B_FALSE;
3376 	dladm_status_t		status;
3377 	wlan_count_attr_t	wcattr;
3378 
3379 	opterr = 0;
3380 	while ((option = getopt_long(argc, argv, ":a",
3381 	    wifi_longopts, NULL)) != -1) {
3382 		switch (option) {
3383 		case 'a':
3384 			all_links = B_TRUE;
3385 			break;
3386 		default:
3387 			die_opterr(optopt, option);
3388 			break;
3389 		}
3390 	}
3391 
3392 	if (optind == (argc - 1)) {
3393 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3394 		    NULL, NULL)) != DLADM_STATUS_OK) {
3395 			die_dlerr(status, "link %s is not valid", argv[optind]);
3396 		}
3397 	} else if (optind != argc) {
3398 		usage();
3399 	}
3400 
3401 	if (linkid == DATALINK_ALL_LINKID) {
3402 		if (!all_links) {
3403 			wcattr.wc_linkid = linkid;
3404 			wcattr.wc_count = 0;
3405 			(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
3406 			    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
3407 			if (wcattr.wc_count == 0) {
3408 				die("no wifi links are available");
3409 			} else if (wcattr.wc_count > 1) {
3410 				die("link name is required when more than "
3411 				    "one wifi link is available");
3412 			}
3413 			linkid = wcattr.wc_linkid;
3414 		} else {
3415 			(void) dladm_walk_datalink_id(do_all_disconnect_wifi,
3416 			    NULL, DATALINK_CLASS_PHYS, DL_WIFI,
3417 			    DLADM_OPT_ACTIVE);
3418 			return;
3419 		}
3420 	}
3421 	status = dladm_wlan_disconnect(linkid);
3422 	if (status != DLADM_STATUS_OK)
3423 		die_dlerr(status, "cannot disconnect");
3424 }
3425 
3426 #define	MAX_PROPS		32
3427 #define	MAX_PROP_LINE		512
3428 
3429 typedef struct prop_info {
3430 	char		*pi_name;
3431 	char		*pi_val[DLADM_MAX_PROP_VALCNT];
3432 	uint_t		pi_count;
3433 } prop_info_t;
3434 
3435 typedef struct prop_list {
3436 	prop_info_t	pl_info[MAX_PROPS];
3437 	uint_t		pl_count;
3438 	char		*pl_buf;
3439 } prop_list_t;
3440 
3441 typedef struct show_linkprop_state {
3442 	char		ls_link[MAXLINKNAMELEN];
3443 	char		*ls_line;
3444 	char		**ls_propvals;
3445 	prop_list_t	*ls_proplist;
3446 	uint32_t	ls_parseable : 1,
3447 			ls_persist : 1,
3448 			ls_header : 1,
3449 			ls_pad_bits : 29;
3450 	dladm_status_t	ls_status;
3451 } show_linkprop_state_t;
3452 
3453 static void
3454 free_props(prop_list_t *list)
3455 {
3456 	if (list != NULL) {
3457 		free(list->pl_buf);
3458 		free(list);
3459 	}
3460 }
3461 
3462 static int
3463 parse_props(char *str, prop_list_t **listp, boolean_t novalues)
3464 {
3465 	prop_list_t	*list;
3466 	prop_info_t	*pip;
3467 	char		*buf, *curr;
3468 	int		len, i;
3469 
3470 	list = malloc(sizeof (prop_list_t));
3471 	if (list == NULL)
3472 		return (-1);
3473 
3474 	list->pl_count = 0;
3475 	list->pl_buf = buf = strdup(str);
3476 	if (buf == NULL)
3477 		goto fail;
3478 
3479 	curr = buf;
3480 	len = strlen(buf);
3481 	pip = NULL;
3482 	for (i = 0; i < len; i++) {
3483 		char		c = buf[i];
3484 		boolean_t	match = (c == '=' || c == ',');
3485 
3486 		if (!match && i != len - 1)
3487 			continue;
3488 
3489 		if (match) {
3490 			buf[i] = '\0';
3491 			if (*curr == '\0')
3492 				goto fail;
3493 		}
3494 
3495 		if (pip != NULL && c != '=') {
3496 			if (pip->pi_count > DLADM_MAX_PROP_VALCNT)
3497 				goto fail;
3498 
3499 			if (novalues)
3500 				goto fail;
3501 
3502 			pip->pi_val[pip->pi_count] = curr;
3503 			pip->pi_count++;
3504 		} else {
3505 			if (list->pl_count > MAX_PROPS)
3506 				goto fail;
3507 
3508 			pip = &list->pl_info[list->pl_count];
3509 			pip->pi_name = curr;
3510 			pip->pi_count = 0;
3511 			list->pl_count++;
3512 			if (c == ',')
3513 				pip = NULL;
3514 		}
3515 		curr = buf + i + 1;
3516 	}
3517 	*listp = list;
3518 	return (0);
3519 
3520 fail:
3521 	free_props(list);
3522 	return (-1);
3523 }
3524 
3525 static void
3526 print_linkprop_head(void)
3527 {
3528 	(void) printf("%-12s %-15s %-14s %-14s %-20s \n",
3529 	    "LINK", "PROPERTY", "VALUE", "DEFAULT", "POSSIBLE");
3530 }
3531 
3532 static void
3533 print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep,
3534     const char *propname, dladm_prop_type_t type, const char *typename,
3535     const char *format, char **pptr)
3536 {
3537 	int		i;
3538 	char		*ptr, *lim;
3539 	char		buf[DLADM_STRSIZE];
3540 	char		*unknown = "?", *notsup = "";
3541 	char		**propvals = statep->ls_propvals;
3542 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
3543 	dladm_status_t	status;
3544 
3545 	status = dladm_get_linkprop(linkid, type, propname, propvals, &valcnt);
3546 	if (status != DLADM_STATUS_OK) {
3547 		if (status == DLADM_STATUS_TEMPONLY) {
3548 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
3549 			    statep->ls_persist) {
3550 				valcnt = 1;
3551 				propvals = &unknown;
3552 			} else {
3553 				statep->ls_status = status;
3554 				return;
3555 			}
3556 		} else if (status == DLADM_STATUS_NOTSUP ||
3557 		    statep->ls_persist) {
3558 			valcnt = 1;
3559 			if (type == DLADM_PROP_VAL_CURRENT)
3560 				propvals = &unknown;
3561 			else
3562 				propvals = &notsup;
3563 		} else {
3564 			statep->ls_status = status;
3565 			if (statep->ls_proplist) {
3566 				warn_dlerr(status,
3567 				    "cannot get link property '%s' for %s",
3568 				    propname, statep->ls_link);
3569 			}
3570 			return;
3571 		}
3572 	}
3573 
3574 	ptr = buf;
3575 	lim = buf + DLADM_STRSIZE;
3576 	for (i = 0; i < valcnt; i++) {
3577 		if (propvals[i][0] == '\0' && !statep->ls_parseable)
3578 			ptr += snprintf(ptr, lim - ptr, "--,");
3579 		else
3580 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
3581 		if (ptr >= lim)
3582 			break;
3583 	}
3584 	if (valcnt > 0)
3585 		buf[strlen(buf) - 1] = '\0';
3586 
3587 	lim = statep->ls_line + MAX_PROP_LINE;
3588 	if (statep->ls_parseable) {
3589 		*pptr += snprintf(*pptr, lim - *pptr,
3590 		    "%s=\"%s\" ", typename, buf);
3591 	} else {
3592 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
3593 	}
3594 }
3595 
3596 static int
3597 show_linkprop(datalink_id_t linkid, const char *propname, void *arg)
3598 {
3599 	show_linkprop_state_t	*statep = arg;
3600 	char			*ptr = statep->ls_line;
3601 	char			*lim = ptr + MAX_PROP_LINE;
3602 
3603 	if (statep->ls_parseable)
3604 		ptr += snprintf(ptr, lim - ptr, "LINK=\"%s\" ",
3605 		    statep->ls_link);
3606 	else
3607 		ptr += snprintf(ptr, lim - ptr, "%-12s ", statep->ls_link);
3608 
3609 	if (statep->ls_parseable)
3610 		ptr += snprintf(ptr, lim - ptr, "PROPERTY=\"%s\" ", propname);
3611 	else
3612 		ptr += snprintf(ptr, lim - ptr, "%-15s ", propname);
3613 
3614 	print_linkprop(linkid, statep, propname,
3615 	    statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
3616 	    DLADM_PROP_VAL_CURRENT, "VALUE", "%-14s ", &ptr);
3617 
3618 	/*
3619 	 * If we failed to query the link property, for example, query
3620 	 * the persistent value of a non-persistable link property, simply
3621 	 * skip the output.
3622 	 */
3623 	if (statep->ls_status != DLADM_STATUS_OK)
3624 		return (DLADM_WALK_CONTINUE);
3625 
3626 	print_linkprop(linkid, statep, propname, DLADM_PROP_VAL_DEFAULT,
3627 	    "DEFAULT", "%-14s ", &ptr);
3628 	if (statep->ls_status != DLADM_STATUS_OK)
3629 		return (DLADM_WALK_CONTINUE);
3630 
3631 	print_linkprop(linkid, statep, propname, DLADM_PROP_VAL_MODIFIABLE,
3632 	    "POSSIBLE", "%-20s ", &ptr);
3633 	if (statep->ls_status != DLADM_STATUS_OK)
3634 		return (DLADM_WALK_CONTINUE);
3635 
3636 	if (statep->ls_header) {
3637 		statep->ls_header = B_FALSE;
3638 		if (!statep->ls_parseable)
3639 			print_linkprop_head();
3640 	}
3641 	(void) printf("%s\n", statep->ls_line);
3642 	return (DLADM_WALK_CONTINUE);
3643 }
3644 
3645 static void
3646 do_show_linkprop(int argc, char **argv)
3647 {
3648 	int			option;
3649 	prop_list_t		*proplist = NULL;
3650 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3651 	show_linkprop_state_t	state;
3652 	uint32_t		flags = DLADM_OPT_ACTIVE;
3653 	dladm_status_t		status;
3654 
3655 	opterr = 0;
3656 	state.ls_propvals = NULL;
3657 	state.ls_line = NULL;
3658 	state.ls_parseable = B_FALSE;
3659 	state.ls_persist = B_FALSE;
3660 	state.ls_header = B_TRUE;
3661 	while ((option = getopt_long(argc, argv, ":p:cP",
3662 	    prop_longopts, NULL)) != -1) {
3663 		switch (option) {
3664 		case 'p':
3665 			if (parse_props(optarg, &proplist, B_TRUE) < 0)
3666 				die("invalid link properties specified");
3667 			break;
3668 		case 'c':
3669 			state.ls_parseable = B_TRUE;
3670 			break;
3671 		case 'P':
3672 			state.ls_persist = B_TRUE;
3673 			flags = DLADM_OPT_PERSIST;
3674 			break;
3675 		default:
3676 			die_opterr(optopt, option);
3677 			break;
3678 		}
3679 	}
3680 
3681 	if (optind == (argc - 1)) {
3682 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3683 		    NULL, NULL)) != DLADM_STATUS_OK) {
3684 			die_dlerr(status, "link %s is not valid", argv[optind]);
3685 		}
3686 	} else if (optind != argc) {
3687 		usage();
3688 	}
3689 
3690 	state.ls_proplist = proplist;
3691 	state.ls_status = DLADM_STATUS_OK;
3692 
3693 	if (linkid == DATALINK_ALL_LINKID) {
3694 		(void) dladm_walk_datalink_id(show_linkprop_onelink, &state,
3695 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
3696 	} else {
3697 		(void) show_linkprop_onelink(linkid, &state);
3698 	}
3699 	free_props(proplist);
3700 
3701 	if (state.ls_status != DLADM_STATUS_OK) {
3702 		if (optind == (argc - 1)) {
3703 			warn_dlerr(state.ls_status,
3704 			    "show-linkprop failed for %s", argv[optind]);
3705 		}
3706 		exit(EXIT_FAILURE);
3707 	}
3708 }
3709 
3710 static int
3711 show_linkprop_onelink(datalink_id_t linkid, void *arg)
3712 {
3713 	int			i;
3714 	char			*buf;
3715 	uint32_t		flags;
3716 	prop_list_t		*proplist = NULL;
3717 	show_linkprop_state_t	*statep = arg;
3718 	dlpi_handle_t		dh = NULL;
3719 
3720 	statep->ls_status = DLADM_STATUS_OK;
3721 
3722 	if (dladm_datalink_id2info(linkid, &flags, NULL, NULL, statep->ls_link,
3723 	    MAXLINKNAMELEN) != DLADM_STATUS_OK) {
3724 		statep->ls_status = DLADM_STATUS_NOTFOUND;
3725 		return (DLADM_WALK_CONTINUE);
3726 	}
3727 
3728 	if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) ||
3729 	    (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) {
3730 		statep->ls_status = DLADM_STATUS_BADARG;
3731 		return (DLADM_WALK_CONTINUE);
3732 	}
3733 
3734 	proplist = statep->ls_proplist;
3735 
3736 	/*
3737 	 * When some WiFi links are opened for the first time, their hardware
3738 	 * automatically scans for APs and does other slow operations.	Thus,
3739 	 * if there are no open links, the retrieval of link properties
3740 	 * (below) will proceed slowly unless we hold the link open.
3741 	 *
3742 	 * Note that failure of dlpi_open() does not necessarily mean invalid
3743 	 * link properties, because dlpi_open() may fail because of incorrect
3744 	 * autopush configuration. Therefore, we ingore the return value of
3745 	 * dlpi_open().
3746 	 */
3747 	if (!statep->ls_persist)
3748 		(void) dlpi_open(statep->ls_link, &dh, 0);
3749 
3750 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
3751 	    DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
3752 	if (buf == NULL)
3753 		die("insufficient memory");
3754 
3755 	statep->ls_propvals = (char **)(void *)buf;
3756 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
3757 		statep->ls_propvals[i] = buf +
3758 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
3759 		    i * DLADM_PROP_VAL_MAX;
3760 	}
3761 	statep->ls_line = buf +
3762 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
3763 
3764 	if (proplist != NULL) {
3765 		for (i = 0; i < proplist->pl_count; i++) {
3766 			(void) show_linkprop(linkid,
3767 			    proplist->pl_info[i].pi_name, statep);
3768 		}
3769 	} else {
3770 		(void) dladm_walk_linkprop(linkid, statep, show_linkprop);
3771 	}
3772 	if (dh != NULL)
3773 		dlpi_close(dh);
3774 	free(buf);
3775 	return (DLADM_WALK_CONTINUE);
3776 }
3777 
3778 static dladm_status_t
3779 set_linkprop_persist(datalink_id_t linkid, const char *prop_name,
3780     char **prop_val, uint_t val_cnt, boolean_t reset)
3781 {
3782 	dladm_status_t	status;
3783 
3784 	status = dladm_set_linkprop(linkid, prop_name, prop_val, val_cnt,
3785 	    DLADM_OPT_PERSIST);
3786 
3787 	if (status != DLADM_STATUS_OK) {
3788 		warn_dlerr(status, "cannot persistently %s link property",
3789 		    reset ? "reset" : "set");
3790 	}
3791 	return (status);
3792 }
3793 
3794 static void
3795 set_linkprop(int argc, char **argv, boolean_t reset)
3796 {
3797 	int		i, option;
3798 	char		errmsg[DLADM_STRSIZE];
3799 	char		*altroot = NULL;
3800 	datalink_id_t	linkid;
3801 	prop_list_t	*proplist = NULL;
3802 	boolean_t	temp = B_FALSE;
3803 	dladm_status_t	status = DLADM_STATUS_OK;
3804 
3805 	opterr = 0;
3806 	while ((option = getopt_long(argc, argv, ":p:R:t",
3807 	    prop_longopts, NULL)) != -1) {
3808 		switch (option) {
3809 		case 'p':
3810 			if (parse_props(optarg, &proplist, reset) < 0)
3811 				die("invalid link properties specified");
3812 			break;
3813 		case 't':
3814 			temp = B_TRUE;
3815 			break;
3816 		case 'R':
3817 			altroot = optarg;
3818 			break;
3819 		default:
3820 			die_opterr(optopt, option);
3821 			break;
3822 		}
3823 	}
3824 
3825 	/* get link name (required last argument) */
3826 	if (optind != (argc - 1))
3827 		usage();
3828 
3829 	if (proplist == NULL && !reset)
3830 		die("link property must be specified");
3831 
3832 	if (altroot != NULL) {
3833 		free_props(proplist);
3834 		altroot_cmd(altroot, argc, argv);
3835 	}
3836 
3837 	status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL);
3838 	if (status != DLADM_STATUS_OK)
3839 		die_dlerr(status, "link %s is not valid", argv[optind]);
3840 
3841 	if (proplist == NULL) {
3842 		if ((status = dladm_set_linkprop(linkid, NULL, NULL, 0,
3843 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
3844 			warn_dlerr(status, "cannot reset link property "
3845 			    "on '%s'", argv[optind]);
3846 		}
3847 		if (!temp) {
3848 			dladm_status_t	s;
3849 
3850 			s = set_linkprop_persist(linkid, NULL, NULL, 0, reset);
3851 			if (s != DLADM_STATUS_OK)
3852 				status = s;
3853 		}
3854 		goto done;
3855 	}
3856 
3857 	for (i = 0; i < proplist->pl_count; i++) {
3858 		prop_info_t	*pip = &proplist->pl_info[i];
3859 		char		**val;
3860 		uint_t		count;
3861 		dladm_status_t	s;
3862 
3863 		if (reset) {
3864 			val = NULL;
3865 			count = 0;
3866 		} else {
3867 			val = pip->pi_val;
3868 			count = pip->pi_count;
3869 			if (count == 0) {
3870 				warn("no value specified for '%s'",
3871 				    pip->pi_name);
3872 				status = DLADM_STATUS_BADARG;
3873 				continue;
3874 			}
3875 		}
3876 		s = dladm_set_linkprop(linkid, pip->pi_name, val, count,
3877 		    DLADM_OPT_ACTIVE);
3878 		if (s == DLADM_STATUS_OK) {
3879 			if (!temp) {
3880 				s = set_linkprop_persist(linkid,
3881 				    pip->pi_name, val, count, reset);
3882 				if (s != DLADM_STATUS_OK)
3883 					status = s;
3884 			}
3885 			continue;
3886 		}
3887 		status = s;
3888 		switch (s) {
3889 		case DLADM_STATUS_NOTFOUND:
3890 			warn("invalid link property '%s'", pip->pi_name);
3891 			break;
3892 		case DLADM_STATUS_BADVAL: {
3893 			int		j;
3894 			char		*ptr, *lim;
3895 			char		**propvals = NULL;
3896 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
3897 
3898 			ptr = malloc((sizeof (char *) +
3899 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
3900 			    MAX_PROP_LINE);
3901 
3902 			propvals = (char **)(void *)ptr;
3903 			if (propvals == NULL)
3904 				die("insufficient memory");
3905 
3906 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
3907 				propvals[j] = ptr + sizeof (char *) *
3908 				    DLADM_MAX_PROP_VALCNT +
3909 				    j * DLADM_PROP_VAL_MAX;
3910 			}
3911 			s = dladm_get_linkprop(linkid,
3912 			    DLADM_PROP_VAL_MODIFIABLE, pip->pi_name, propvals,
3913 			    &valcnt);
3914 
3915 			if (s != DLADM_STATUS_OK) {
3916 				warn_dlerr(status, "cannot set link property "
3917 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
3918 				free(propvals);
3919 				break;
3920 			}
3921 
3922 			ptr = errmsg;
3923 			lim = ptr + DLADM_STRSIZE;
3924 			*ptr = '\0';
3925 			for (j = 0; j < valcnt; j++) {
3926 				ptr += snprintf(ptr, lim - ptr, "%s,",
3927 				    propvals[j]);
3928 				if (ptr >= lim)
3929 					break;
3930 			}
3931 			if (ptr > errmsg) {
3932 				*(ptr - 1) = '\0';
3933 				warn("link property '%s' must be one of: %s",
3934 				    pip->pi_name, errmsg);
3935 			} else
3936 				warn("invalid link property '%s'", *val);
3937 			free(propvals);
3938 			break;
3939 		}
3940 		default:
3941 			if (reset) {
3942 				warn_dlerr(status, "cannot reset link property "
3943 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
3944 			} else {
3945 				warn_dlerr(status, "cannot set link property "
3946 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
3947 			}
3948 			break;
3949 		}
3950 	}
3951 done:
3952 	free_props(proplist);
3953 	if (status != DLADM_STATUS_OK)
3954 		exit(1);
3955 }
3956 
3957 static void
3958 do_set_linkprop(int argc, char **argv)
3959 {
3960 	set_linkprop(argc, argv, B_FALSE);
3961 }
3962 
3963 static void
3964 do_reset_linkprop(int argc, char **argv)
3965 {
3966 	set_linkprop(argc, argv, B_TRUE);
3967 }
3968 
3969 static int
3970 convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
3971     dladm_secobj_class_t class)
3972 {
3973 	int error = 0;
3974 
3975 	if (class == DLADM_SECOBJ_CLASS_WPA) {
3976 		if (len < 8 || len > 63)
3977 			return (EINVAL);
3978 		(void) memcpy(obj_val, buf, len);
3979 		*obj_lenp = len;
3980 		return (error);
3981 	}
3982 
3983 	if (class == DLADM_SECOBJ_CLASS_WEP) {
3984 		switch (len) {
3985 		case 5:			/* ASCII key sizes */
3986 		case 13:
3987 			(void) memcpy(obj_val, buf, len);
3988 			*obj_lenp = len;
3989 			break;
3990 		case 10:		/* Hex key sizes, not preceded by 0x */
3991 		case 26:
3992 			error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
3993 			break;
3994 		case 12:		/* Hex key sizes, preceded by 0x */
3995 		case 28:
3996 			if (strncmp(buf, "0x", 2) != 0)
3997 				return (EINVAL);
3998 			error = hexascii_to_octet(buf + 2, len - 2,
3999 			    obj_val, obj_lenp);
4000 			break;
4001 		default:
4002 			return (EINVAL);
4003 		}
4004 		return (error);
4005 	}
4006 
4007 	return (ENOENT);
4008 }
4009 
4010 /* ARGSUSED */
4011 static void
4012 defersig(int sig)
4013 {
4014 	signalled = sig;
4015 }
4016 
4017 static int
4018 get_secobj_from_tty(uint_t try, const char *objname, char *buf)
4019 {
4020 	uint_t		len = 0;
4021 	int		c;
4022 	struct termios	stored, current;
4023 	void		(*sigfunc)(int);
4024 
4025 	/*
4026 	 * Turn off echo -- but before we do so, defer SIGINT handling
4027 	 * so that a ^C doesn't leave the terminal corrupted.
4028 	 */
4029 	sigfunc = signal(SIGINT, defersig);
4030 	(void) fflush(stdin);
4031 	(void) tcgetattr(0, &stored);
4032 	current = stored;
4033 	current.c_lflag &= ~(ICANON|ECHO);
4034 	current.c_cc[VTIME] = 0;
4035 	current.c_cc[VMIN] = 1;
4036 	(void) tcsetattr(0, TCSANOW, &current);
4037 again:
4038 	if (try == 1)
4039 		(void) printf(gettext("provide value for '%s': "), objname);
4040 	else
4041 		(void) printf(gettext("confirm value for '%s': "), objname);
4042 
4043 	(void) fflush(stdout);
4044 	while (signalled == 0) {
4045 		c = getchar();
4046 		if (c == '\n' || c == '\r') {
4047 			if (len != 0)
4048 				break;
4049 			(void) putchar('\n');
4050 			goto again;
4051 		}
4052 
4053 		buf[len++] = c;
4054 		if (len >= DLADM_SECOBJ_VAL_MAX - 1)
4055 			break;
4056 		(void) putchar('*');
4057 	}
4058 
4059 	(void) putchar('\n');
4060 	(void) fflush(stdin);
4061 
4062 	/*
4063 	 * Restore terminal setting and handle deferred signals.
4064 	 */
4065 	(void) tcsetattr(0, TCSANOW, &stored);
4066 
4067 	(void) signal(SIGINT, sigfunc);
4068 	if (signalled != 0)
4069 		(void) kill(getpid(), signalled);
4070 
4071 	return (len);
4072 }
4073 
4074 static int
4075 get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
4076     dladm_secobj_class_t class, FILE *filep)
4077 {
4078 	int		rval;
4079 	uint_t		len, len2;
4080 	char		buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
4081 
4082 	if (filep == NULL) {
4083 		len = get_secobj_from_tty(1, obj_name, buf);
4084 		rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
4085 		if (rval == 0) {
4086 			len2 = get_secobj_from_tty(2, obj_name, buf2);
4087 			if (len != len2 || memcmp(buf, buf2, len) != 0)
4088 				rval = ENOTSUP;
4089 		}
4090 		return (rval);
4091 	} else {
4092 		for (;;) {
4093 			if (fgets(buf, sizeof (buf), filep) == NULL)
4094 				break;
4095 			if (isspace(buf[0]))
4096 				continue;
4097 
4098 			len = strlen(buf);
4099 			if (buf[len - 1] == '\n') {
4100 				buf[len - 1] = '\0';
4101 				len--;
4102 			}
4103 			break;
4104 		}
4105 		(void) fclose(filep);
4106 	}
4107 	return (convert_secobj(buf, len, obj_val, obj_lenp, class));
4108 }
4109 
4110 static boolean_t
4111 check_auth(const char *auth)
4112 {
4113 	struct passwd	*pw;
4114 
4115 	if ((pw = getpwuid(getuid())) == NULL)
4116 		return (B_FALSE);
4117 
4118 	return (chkauthattr(auth, pw->pw_name) != 0);
4119 }
4120 
4121 static void
4122 audit_secobj(char *auth, char *class, char *obj,
4123     boolean_t success, boolean_t create)
4124 {
4125 	adt_session_data_t	*ah;
4126 	adt_event_data_t	*event;
4127 	au_event_t		flag;
4128 	char			*errstr;
4129 
4130 	if (create) {
4131 		flag = ADT_dladm_create_secobj;
4132 		errstr = "ADT_dladm_create_secobj";
4133 	} else {
4134 		flag = ADT_dladm_delete_secobj;
4135 		errstr = "ADT_dladm_delete_secobj";
4136 	}
4137 
4138 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
4139 		die("adt_start_session: %s", strerror(errno));
4140 
4141 	if ((event = adt_alloc_event(ah, flag)) == NULL)
4142 		die("adt_alloc_event (%s): %s", errstr, strerror(errno));
4143 
4144 	/* fill in audit info */
4145 	if (create) {
4146 		event->adt_dladm_create_secobj.auth_used = auth;
4147 		event->adt_dladm_create_secobj.obj_class = class;
4148 		event->adt_dladm_create_secobj.obj_name = obj;
4149 	} else {
4150 		event->adt_dladm_delete_secobj.auth_used = auth;
4151 		event->adt_dladm_delete_secobj.obj_class = class;
4152 		event->adt_dladm_delete_secobj.obj_name = obj;
4153 	}
4154 
4155 	if (success) {
4156 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
4157 			die("adt_put_event (%s, success): %s", errstr,
4158 			    strerror(errno));
4159 		}
4160 	} else {
4161 		if (adt_put_event(event, ADT_FAILURE,
4162 		    ADT_FAIL_VALUE_AUTH) != 0) {
4163 			die("adt_put_event: (%s, failure): %s", errstr,
4164 			    strerror(errno));
4165 		}
4166 	}
4167 
4168 	adt_free_event(event);
4169 	(void) adt_end_session(ah);
4170 }
4171 
4172 #define	MAX_SECOBJS		32
4173 #define	MAX_SECOBJ_NAMELEN	32
4174 static void
4175 do_create_secobj(int argc, char **argv)
4176 {
4177 	int			option, rval;
4178 	FILE			*filep = NULL;
4179 	char			*obj_name = NULL;
4180 	char			*class_name = NULL;
4181 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
4182 	uint_t			obj_len;
4183 	boolean_t		success, temp = B_FALSE;
4184 	dladm_status_t		status;
4185 	dladm_secobj_class_t	class = -1;
4186 	uid_t			euid;
4187 
4188 	opterr = 0;
4189 	(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
4190 	while ((option = getopt_long(argc, argv, ":f:c:R:t",
4191 	    wifi_longopts, NULL)) != -1) {
4192 		switch (option) {
4193 		case 'f':
4194 			euid = geteuid();
4195 			(void) seteuid(getuid());
4196 			filep = fopen(optarg, "r");
4197 			if (filep == NULL) {
4198 				die("cannot open %s: %s", optarg,
4199 				    strerror(errno));
4200 			}
4201 			(void) seteuid(euid);
4202 			break;
4203 		case 'c':
4204 			class_name = optarg;
4205 			status = dladm_str2secobjclass(optarg, &class);
4206 			if (status != DLADM_STATUS_OK) {
4207 				die("invalid secure object class '%s', "
4208 				    "valid values are: wep, wpa", optarg);
4209 			}
4210 			break;
4211 		case 't':
4212 			temp = B_TRUE;
4213 			break;
4214 		case 'R':
4215 			status = dladm_set_rootdir(optarg);
4216 			if (status != DLADM_STATUS_OK) {
4217 				die_dlerr(status, "invalid directory "
4218 				    "specified");
4219 			}
4220 			break;
4221 		default:
4222 			die_opterr(optopt, option);
4223 			break;
4224 		}
4225 	}
4226 
4227 	if (optind == (argc - 1))
4228 		obj_name = argv[optind];
4229 	else if (optind != argc)
4230 		usage();
4231 
4232 	if (class == -1)
4233 		die("secure object class required");
4234 
4235 	if (obj_name == NULL)
4236 		die("secure object name required");
4237 
4238 	success = check_auth(LINK_SEC_AUTH);
4239 	audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
4240 	if (!success)
4241 		die("authorization '%s' is required", LINK_SEC_AUTH);
4242 
4243 	rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
4244 	if (rval != 0) {
4245 		switch (rval) {
4246 		case ENOENT:
4247 			die("invalid secure object class");
4248 			break;
4249 		case EINVAL:
4250 			die("invalid secure object value");
4251 			break;
4252 		case ENOTSUP:
4253 			die("verification failed");
4254 			break;
4255 		default:
4256 			die("invalid secure object: %s", strerror(rval));
4257 			break;
4258 		}
4259 	}
4260 
4261 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
4262 	    DLADM_OPT_CREATE | DLADM_OPT_ACTIVE);
4263 	if (status != DLADM_STATUS_OK) {
4264 		die_dlerr(status, "could not create secure object '%s'",
4265 		    obj_name);
4266 	}
4267 	if (temp)
4268 		return;
4269 
4270 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
4271 	    DLADM_OPT_PERSIST);
4272 	if (status != DLADM_STATUS_OK) {
4273 		warn_dlerr(status, "could not persistently create secure "
4274 		    "object '%s'", obj_name);
4275 	}
4276 }
4277 
4278 static void
4279 do_delete_secobj(int argc, char **argv)
4280 {
4281 	int		i, option;
4282 	boolean_t	temp = B_FALSE;
4283 	split_t		*sp = NULL;
4284 	boolean_t	success;
4285 	dladm_status_t	status, pstatus;
4286 
4287 	opterr = 0;
4288 	status = pstatus = DLADM_STATUS_OK;
4289 	while ((option = getopt_long(argc, argv, ":R:t",
4290 	    wifi_longopts, NULL)) != -1) {
4291 		switch (option) {
4292 		case 't':
4293 			temp = B_TRUE;
4294 			break;
4295 		case 'R':
4296 			status = dladm_set_rootdir(optarg);
4297 			if (status != DLADM_STATUS_OK) {
4298 				die_dlerr(status, "invalid directory "
4299 				    "specified");
4300 			}
4301 			break;
4302 		default:
4303 			die_opterr(optopt, option);
4304 			break;
4305 		}
4306 	}
4307 
4308 	if (optind == (argc - 1)) {
4309 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
4310 		if (sp == NULL) {
4311 			die("invalid secure object name(s): '%s'",
4312 			    argv[optind]);
4313 		}
4314 	} else if (optind != argc)
4315 		usage();
4316 
4317 	if (sp == NULL || sp->s_nfields < 1)
4318 		die("secure object name required");
4319 
4320 	success = check_auth(LINK_SEC_AUTH);
4321 	audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE);
4322 	if (!success)
4323 		die("authorization '%s' is required", LINK_SEC_AUTH);
4324 
4325 	for (i = 0; i < sp->s_nfields; i++) {
4326 		status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_ACTIVE);
4327 		if (!temp) {
4328 			pstatus = dladm_unset_secobj(sp->s_fields[i],
4329 			    DLADM_OPT_PERSIST);
4330 		} else {
4331 			pstatus = DLADM_STATUS_OK;
4332 		}
4333 
4334 		if (status != DLADM_STATUS_OK) {
4335 			warn_dlerr(status, "could not delete secure object "
4336 			    "'%s'", sp->s_fields[i]);
4337 		}
4338 		if (pstatus != DLADM_STATUS_OK) {
4339 			warn_dlerr(pstatus, "could not persistently delete "
4340 			    "secure object '%s'", sp->s_fields[i]);
4341 		}
4342 	}
4343 	if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK)
4344 		exit(1);
4345 }
4346 
4347 typedef struct show_secobj_state {
4348 	boolean_t	ss_persist;
4349 	boolean_t	ss_parseable;
4350 	boolean_t	ss_debug;
4351 	boolean_t	ss_header;
4352 } show_secobj_state_t;
4353 
4354 static void
4355 print_secobj_head(show_secobj_state_t *statep)
4356 {
4357 	(void) printf("%-20s %-20s ", "OBJECT", "CLASS");
4358 	if (statep->ss_debug)
4359 		(void) printf("%-30s", "VALUE");
4360 	(void) putchar('\n');
4361 }
4362 
4363 static boolean_t
4364 show_secobj(void *arg, const char *obj_name)
4365 {
4366 	uint_t			obj_len = DLADM_SECOBJ_VAL_MAX;
4367 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
4368 	char			buf[DLADM_STRSIZE];
4369 	uint_t			flags = 0;
4370 	dladm_secobj_class_t	class;
4371 	show_secobj_state_t	*statep = arg;
4372 	dladm_status_t		status;
4373 
4374 	if (statep->ss_persist)
4375 		flags |= DLADM_OPT_PERSIST;
4376 
4377 	status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags);
4378 	if (status != DLADM_STATUS_OK)
4379 		die_dlerr(status, "cannot get secure object '%s'", obj_name);
4380 
4381 	if (statep->ss_header) {
4382 		statep->ss_header = B_FALSE;
4383 		if (!statep->ss_parseable)
4384 			print_secobj_head(statep);
4385 	}
4386 
4387 	if (statep->ss_parseable) {
4388 		(void) printf("OBJECT=\"%s\" CLASS=\"%s\" ", obj_name,
4389 		    dladm_secobjclass2str(class, buf));
4390 	} else {
4391 		(void) printf("%-20s %-20s ", obj_name,
4392 		    dladm_secobjclass2str(class, buf));
4393 	}
4394 
4395 	if (statep->ss_debug) {
4396 		char	val[DLADM_SECOBJ_VAL_MAX * 2];
4397 		uint_t	len = sizeof (val);
4398 
4399 		if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0) {
4400 			if (statep->ss_parseable)
4401 				(void) printf("VALUE=\"0x%s\"", val);
4402 			else
4403 				(void) printf("0x%-30s", val);
4404 		}
4405 	}
4406 	(void) putchar('\n');
4407 	return (B_TRUE);
4408 }
4409 
4410 static void
4411 do_show_secobj(int argc, char **argv)
4412 {
4413 	int			option;
4414 	show_secobj_state_t	state;
4415 	dladm_status_t		status;
4416 	uint_t			i;
4417 	split_t			*sp;
4418 	uint_t			flags;
4419 
4420 	opterr = 0;
4421 	state.ss_persist = B_FALSE;
4422 	state.ss_parseable = B_FALSE;
4423 	state.ss_debug = B_FALSE;
4424 	state.ss_header = B_TRUE;
4425 	while ((option = getopt_long(argc, argv, ":pPd",
4426 	    wifi_longopts, NULL)) != -1) {
4427 		switch (option) {
4428 		case 'p':
4429 			state.ss_parseable = B_TRUE;
4430 			break;
4431 		case 'P':
4432 			state.ss_persist = B_TRUE;
4433 			break;
4434 		case 'd':
4435 			if (getuid() != 0)
4436 				die("insufficient privileges");
4437 			state.ss_debug = B_TRUE;
4438 			break;
4439 		default:
4440 			die_opterr(optopt, option);
4441 			break;
4442 		}
4443 	}
4444 
4445 	if (optind == (argc - 1)) {
4446 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
4447 		if (sp == NULL) {
4448 			die("invalid secure object name(s): '%s'",
4449 			    argv[optind]);
4450 		}
4451 		for (i = 0; i < sp->s_nfields; i++) {
4452 			if (!show_secobj(&state, sp->s_fields[i]))
4453 				break;
4454 		}
4455 		splitfree(sp);
4456 		return;
4457 	} else if (optind != argc)
4458 		usage();
4459 
4460 	flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
4461 	status = dladm_walk_secobj(&state, show_secobj, flags);
4462 	if (status != DLADM_STATUS_OK)
4463 		die_dlerr(status, "show-secobj");
4464 }
4465 
4466 /*ARGSUSED*/
4467 static int
4468 i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
4469 {
4470 	(void) dladm_init_linkprop(linkid);
4471 	return (DLADM_WALK_CONTINUE);
4472 }
4473 
4474 /* ARGSUSED */
4475 static void
4476 do_init_linkprop(int argc, char **argv)
4477 {
4478 	/*
4479 	 * linkprops of links of other classes have been initialized as a
4480 	 * part of the dladm up-xxx operation.
4481 	 */
4482 	(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
4483 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
4484 }
4485 
4486 /* ARGSUSED */
4487 static void
4488 do_init_secobj(int argc, char **argv)
4489 {
4490 	dladm_status_t status;
4491 
4492 	status = dladm_init_secobj();
4493 	if (status != DLADM_STATUS_OK)
4494 		die_dlerr(status, "secure object initialization failed");
4495 }
4496 
4497 /*
4498  * "-R" option support. It is used for live upgrading. Append dladm commands
4499  * to a upgrade script which will be run when the alternative root boots up:
4500  *
4501  * - If the dlmgmtd door file exists on the alternative root, append dladm
4502  * commands to the <altroot>/var/svc/profile/upgrade_datalink script. This
4503  * script will be run as part of the network/physical service. We cannot defer
4504  * this to /var/svc/profile/upgrade because then the configuration will not
4505  * be able to take effect before network/physical plumbs various interfaces.
4506  *
4507  * - If the dlmgmtd door file does not exist on the alternative root, append
4508  * dladm commands to the <altroot>/var/svc/profile/upgrade script, which will
4509  * be run in the manifest-import service.
4510  *
4511  * Note that the SMF team is considering to move the manifest-import service
4512  * to be run at the very begining of boot. Once that is done, the need for
4513  * the /var/svc/profile/upgrade_datalink script will not exist any more.
4514  */
4515 static void
4516 altroot_cmd(char *altroot, int argc, char *argv[])
4517 {
4518 	char		path[MAXPATHLEN];
4519 	struct stat	stbuf;
4520 	FILE		*fp;
4521 	int		i;
4522 
4523 	/*
4524 	 * Check for the existence of the dlmgmtd door file, and determine
4525 	 * the name of script file.
4526 	 */
4527 	(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot, DLMGMT_DOOR);
4528 	if (stat(path, &stbuf) < 0) {
4529 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
4530 		    SMF_UPGRADE_FILE);
4531 	} else {
4532 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
4533 		    SMF_UPGRADEDATALINK_FILE);
4534 	}
4535 
4536 	if ((fp = fopen(path, "a+")) == NULL)
4537 		die("operation not supported on %s", altroot);
4538 
4539 	(void) fprintf(fp, "/sbin/dladm ");
4540 	for (i = 0; i < argc; i++) {
4541 		/*
4542 		 * Directly write to the file if it is not the "-R <altroot>"
4543 		 * option. In which case, skip it.
4544 		 */
4545 		if (strcmp(argv[i], "-R") != 0)
4546 			(void) fprintf(fp, "%s ", argv[i]);
4547 		else
4548 			i ++;
4549 	}
4550 	(void) fprintf(fp, "%s\n", SMF_DLADM_UPGRADE_MSG);
4551 	(void) fclose(fp);
4552 	exit(0);
4553 }
4554 
4555 /*
4556  * Convert the string to an integer. Note that the string must not have any
4557  * trailing non-integer characters.
4558  */
4559 static boolean_t
4560 str2int(const char *str, int *valp)
4561 {
4562 	int	val;
4563 	char	*endp = NULL;
4564 
4565 	errno = 0;
4566 	val = strtol(str, &endp, 10);
4567 	if (errno != 0 || *endp != '\0')
4568 		return (B_FALSE);
4569 
4570 	*valp = val;
4571 	return (B_TRUE);
4572 }
4573 
4574 /* PRINTFLIKE1 */
4575 static void
4576 warn(const char *format, ...)
4577 {
4578 	va_list alist;
4579 
4580 	format = gettext(format);
4581 	(void) fprintf(stderr, "%s: warning: ", progname);
4582 
4583 	va_start(alist, format);
4584 	(void) vfprintf(stderr, format, alist);
4585 	va_end(alist);
4586 
4587 	(void) putchar('\n');
4588 }
4589 
4590 /* PRINTFLIKE2 */
4591 static void
4592 warn_dlerr(dladm_status_t err, const char *format, ...)
4593 {
4594 	va_list alist;
4595 	char	errmsg[DLADM_STRSIZE];
4596 
4597 	format = gettext(format);
4598 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
4599 
4600 	va_start(alist, format);
4601 	(void) vfprintf(stderr, format, alist);
4602 	va_end(alist);
4603 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
4604 }
4605 
4606 /* PRINTFLIKE2 */
4607 static void
4608 die_dlerr(dladm_status_t err, const char *format, ...)
4609 {
4610 	va_list alist;
4611 	char	errmsg[DLADM_STRSIZE];
4612 
4613 	format = gettext(format);
4614 	(void) fprintf(stderr, "%s: ", progname);
4615 
4616 	va_start(alist, format);
4617 	(void) vfprintf(stderr, format, alist);
4618 	va_end(alist);
4619 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
4620 
4621 	exit(EXIT_FAILURE);
4622 }
4623 
4624 /* PRINTFLIKE1 */
4625 static void
4626 die(const char *format, ...)
4627 {
4628 	va_list alist;
4629 
4630 	format = gettext(format);
4631 	(void) fprintf(stderr, "%s: ", progname);
4632 
4633 	va_start(alist, format);
4634 	(void) vfprintf(stderr, format, alist);
4635 	va_end(alist);
4636 
4637 	(void) putchar('\n');
4638 	exit(EXIT_FAILURE);
4639 }
4640 
4641 static void
4642 die_optdup(int opt)
4643 {
4644 	die("the option -%c cannot be specified more than once", opt);
4645 }
4646 
4647 static void
4648 die_opterr(int opt, int opterr)
4649 {
4650 	switch (opterr) {
4651 	case ':':
4652 		die("option '-%c' requires a value", opt);
4653 		break;
4654 	case '?':
4655 	default:
4656 		die("unrecognized option '-%c'", opt);
4657 		break;
4658 	}
4659 }
4660