xref: /illumos-gate/usr/src/cmd/dladm/dladm.c (revision 7793aa8b1cb26c7fc1397aa9db2364098fc25543)
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 #include <stdio.h>
27 #include <ctype.h>
28 #include <locale.h>
29 #include <signal.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <stropts.h>
35 #include <sys/stat.h>
36 #include <errno.h>
37 #include <kstat.h>
38 #include <strings.h>
39 #include <getopt.h>
40 #include <unistd.h>
41 #include <priv.h>
42 #include <termios.h>
43 #include <pwd.h>
44 #include <auth_attr.h>
45 #include <auth_list.h>
46 #include <libintl.h>
47 #include <libdevinfo.h>
48 #include <libdlpi.h>
49 #include <libdllink.h>
50 #include <libdlaggr.h>
51 #include <libdlwlan.h>
52 #include <libdlvlan.h>
53 #include <libdlvnic.h>
54 #include <libinetutil.h>
55 #include <bsm/adt.h>
56 #include <bsm/adt_event.h>
57 #include <stddef.h>
58 
59 #define	AGGR_DRV		"aggr"
60 #define	STR_UNDEF_VAL		"--"
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 #define	CMD_TYPE_ANY		0xffffffff
69 #define	WIFI_CMD_SCAN		0x00000001
70 #define	WIFI_CMD_SHOW		0x00000002
71 #define	WIFI_CMD_ALL		(WIFI_CMD_SCAN | WIFI_CMD_SHOW)
72 
73 /*
74  * Data structures and routines for printing output.
75  * All non-parseable output is assumed to be in a columnar format.
76  * Multiple fields in parsable output are separated by ':'; single
77  * field output is printed as-is.
78  *
79  * Each sub-command is associated with a global array of pointers,
80  * print_field_t *fields[], where the print_field_t contains information
81  * about the format in which the output is to be printed.
82  *
83  * Sub-commands may be implemented in one of two ways:
84  * (i)  the implementation could get all field values into a character
85  *      buffer, with pf_offset containing the offset (for pf_name) within
86  *      the buffer. The sub-command would make the needed system calls
87  *      to obtain all possible column values and then invoke the
88  *      dladm_print_field() function to print the specific fields
89  *      requested in the command line. See the comments for dladm_print_field
90  *      for further details.
91  * (ii) Alternatively, each fields[i] entry could store a pf_index value
92  *      that uniquely identifies the column to be printed. The implementation
93  *      of the sub-command would then invoke dladm_print_output() with a
94  *      callback function whose semantics are described below (see comments
95  *      for dladm_print_output())
96  *
97  * Thus, an implementation of a sub-command must provide the following:
98  *
99  * static print_field_t sub_command_fields[] = {
100  *	{<name>, <header>,<field width>,  <offset_or_index>, cmdtype},
101  *	:
102  *	{<name>, <header>,<field width>,  <offset_or_index>, cmdtype}
103  * };
104  *
105  * #define	SUB_COMMAND_MAX_FIELDS sizeof \
106  *		(sub_comand_fields) / sizeof (print_field_t))
107  *
108  * print_state_t sub_command_print_state;
109  *
110  * The function that parses command line arguments (typically
111  * do_sub_command()) should then contain an invocation like:
112  *
113  *	fields = parse_output_fields(fields_str, sub_command_fields,
114  *	    SUB_COMMAND_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
115  *
116  * and store the resulting fields and nfields value in a print_state_t
117  * structure tracked for the command.
118  *
119  *	sub_command_print_state.ps_fields = fields;
120  *	sub_command_print_state.ps_nfields = nfields;
121  *
122  * To print the column header for the output, the print_header()
123  * function must then be invoked by do_sub_command().
124  *
125  * Then if method (i) is used for the sub_command, the do_sub_command()
126  * function should make the necessary system calls to fill up the buffer
127  * and then invoke dladm_print_field(). An example of this method is
128  * the implementation of do_show_link() and show_link();
129  *
130  * If method (ii) is used, do_sub_command should invoke dladm_print_output()
131  * with a callback function that will be called for each field to be printed.
132  * The callback function will be passed a pointer to the print_field_t
133  * for the field, and the pf_index may then be used to identify the
134  * system call required to find the value to be printed. An example of
135  * this implementation may be found in the do_show_dev() and print_dev()
136  * invocation.
137  */
138 
139 typedef struct print_field_s {
140 	const char	*pf_name;	/* name of column to be printed */
141 	const char	*pf_header;	/* header for this column */
142 	uint_t		pf_width;
143 	union {
144 		uint_t	_pf_index;	/* private index for sub-command */
145 		size_t	_pf_offset;
146 	}_pf_un;
147 #define	pf_index	_pf_un._pf_index
148 #define	pf_offset	_pf_un._pf_offset;
149 	uint_t		pf_cmdtype;
150 } print_field_t;
151 
152 /*
153  * The state of the output is tracked in a print_state_t structure.
154  * Each ps_fields[i] entry points at the global print_field_t array for
155  * the sub-command, where ps_nfields is the number of requested fields.
156  */
157 typedef struct print_state_s {
158 	print_field_t	**ps_fields;
159 	uint_t		ps_nfields;
160 	boolean_t	ps_lastfield;
161 	uint_t		ps_overflow;
162 } print_state_t;
163 
164 typedef char *(*print_callback_t)(print_field_t *, void *);
165 static print_field_t **parse_output_fields(char *, print_field_t *, int,
166     uint_t, uint_t *);
167 /*
168  * print the header for the output
169  */
170 static void print_header(print_state_t *);
171 static void print_field(print_state_t *, print_field_t *, const char *,
172     boolean_t);
173 
174 /*
175  * to print output values, call dladm_print_output with a callback
176  * function (*func)() that should parse the args and return an
177  * unformatted character buffer with the value to be printed.
178  *
179  * dladm_print_output() prints the character buffer using the formatting
180  * information provided in the print_field_t for that column.
181  */
182 static void dladm_print_output(print_state_t *, boolean_t,
183     print_callback_t, void *);
184 
185 /*
186  * helper function that, when invoked as dladm_print_field(pf, buf)
187  * prints string which is offset by pf->pf_offset  within buf
188  */
189 static char *dladm_print_field(print_field_t *, void *);
190 
191 
192 #define	MAX_FIELD_LEN	32
193 
194 
195 typedef struct pktsum_s {
196 	uint64_t	ipackets;
197 	uint64_t	opackets;
198 	uint64_t	rbytes;
199 	uint64_t	obytes;
200 	uint32_t	ierrors;
201 	uint32_t	oerrors;
202 } pktsum_t;
203 
204 typedef struct show_state {
205 	boolean_t	ls_firstonly;
206 	boolean_t	ls_donefirst;
207 	pktsum_t	ls_prevstats;
208 	uint32_t	ls_flags;
209 	dladm_status_t	ls_status;
210 	print_state_t	ls_print;
211 	boolean_t	ls_parseable;
212 	boolean_t	ls_printheader;
213 } show_state_t;
214 
215 typedef struct show_grp_state {
216 	pktsum_t	gs_prevstats[MAXPORT];
217 	uint32_t	gs_flags;
218 	dladm_status_t	gs_status;
219 	boolean_t	gs_parseable;
220 	boolean_t	gs_lacp;
221 	boolean_t	gs_extended;
222 	boolean_t	gs_stats;
223 	boolean_t	gs_firstonly;
224 	boolean_t	gs_donefirst;
225 	boolean_t	gs_printheader;
226 	print_state_t	gs_print;
227 } show_grp_state_t;
228 
229 typedef void cmdfunc_t(int, char **, const char *);
230 
231 static cmdfunc_t do_show_link, do_show_dev, do_show_wifi, do_show_phys;
232 static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
233 static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr;
234 static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
235 static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
236 static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
237 static cmdfunc_t do_init_linkprop, do_init_secobj;
238 static cmdfunc_t do_create_vlan, do_delete_vlan, do_up_vlan, do_show_vlan;
239 static cmdfunc_t do_rename_link, do_delete_phys, do_init_phys;
240 static cmdfunc_t do_show_linkmap;
241 static cmdfunc_t do_show_ether;
242 
243 static void	altroot_cmd(char *, int, char **);
244 static int	show_linkprop_onelink(datalink_id_t, void *);
245 
246 static void	link_stats(datalink_id_t, uint_t);
247 static void	aggr_stats(datalink_id_t, show_grp_state_t *, uint_t);
248 static void	dev_stats(const char *dev, uint32_t, char *, show_state_t *);
249 
250 static int	get_one_kstat(const char *, const char *, uint8_t,
251 		    void *, boolean_t);
252 static void	get_mac_stats(const char *, pktsum_t *);
253 static void	get_link_stats(const char *, pktsum_t *);
254 static uint64_t	get_ifspeed(const char *, boolean_t);
255 static void	stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
256 static void	stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
257 static const char	*get_linkstate(const char *, boolean_t, char *);
258 static const char	*get_linkduplex(const char *, boolean_t, char *);
259 
260 static int	show_etherprop(datalink_id_t, void *);
261 static void	show_ether_xprop(datalink_id_t, void *);
262 static boolean_t get_speed_duplex(datalink_id_t, const char *, char *,
263     char *, boolean_t);
264 static char 	*pause_str(int, int);
265 static boolean_t	link_is_ether(const char *, datalink_id_t *);
266 
267 #define	IS_FDX	0x10
268 #define	IS_HDX	0x01
269 
270 static boolean_t str2int(const char *, int *);
271 static void	die(const char *, ...);
272 static void	die_optdup(int);
273 static void	die_opterr(int, int, const char *);
274 static void	die_dlerr(dladm_status_t, const char *, ...);
275 static void	warn(const char *, ...);
276 static void	warn_dlerr(dladm_status_t, const char *, ...);
277 
278 typedef struct	cmd {
279 	char		*c_name;
280 	cmdfunc_t	*c_fn;
281 	const char	*c_usage;
282 } cmd_t;
283 
284 static cmd_t	cmds[] = {
285 	{ "show-link",		do_show_link,
286 	    "\tshow-link\t[-pP] [-o <field>,..] [-s [-i <interval>]] [<link>]"},
287 	{ "rename-link",	do_rename_link,
288 	    "\trename-link\t[-R <root-dir>] <oldlink> <newlink>\n"	},
289 	{ "show-dev",		do_show_dev,
290 	    "\tshow-dev\t[-p] [-o <field>,..] [-s [-i <interval>]] [<dev>]\n" },
291 	{ "create-aggr",	do_create_aggr,
292 	    "\tcreate-aggr\t[-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n"
293 	    "\t\t\t[-T <time>] [-u <address>] [-l <link>] ... <link>"	},
294 	{ "delete-aggr",	do_delete_aggr,
295 	    "\tdelete-aggr\t[-t] [-R <root-dir>] <link>"		},
296 	{ "add-aggr",		do_add_aggr,
297 	    "\tadd-aggr\t[-t] [-R <root-dir>] [-l <link>] ... <link>"	},
298 	{ "remove-aggr",	do_remove_aggr,
299 	    "\tremove-aggr\t[-t] [-R <root-dir>] [-l <link>] ... <link>"},
300 	{ "modify-aggr",	do_modify_aggr,
301 	    "\tmodify-aggr\t[-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n"
302 	    "\t\t\t[-T <time>] [-u <address>] <link>"			},
303 	{ "show-aggr",		do_show_aggr,
304 	    "\tshow-aggr\t[-pPLx] [-o <field>,..] [-s [-i <interval>]] "
305 	    "[<link>]\n"						},
306 	{ "up-aggr",		do_up_aggr,		NULL		},
307 	{ "scan-wifi",		do_scan_wifi,
308 	    "\tscan-wifi\t[-p] [-o <field>,...] [<link>]"		},
309 	{ "connect-wifi",	do_connect_wifi,
310 	    "\tconnect-wifi\t[-e <essid>] [-i <bssid>] [-k <key>,...] "
311 	    "[-s wep|wpa]\n"
312 	    "\t\t\t[-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
313 	    "\t\t\t[-T <time>] [<link>]"				},
314 	{ "disconnect-wifi",	do_disconnect_wifi,
315 	    "\tdisconnect-wifi\t[-a] [<link>]"				},
316 	{ "show-wifi",		do_show_wifi,
317 	    "\tshow-wifi\t[-p] [-o <field>,...] [<link>]\n"		},
318 	{ "show-linkprop",	do_show_linkprop,
319 	    "\tshow-linkprop\t[-cP] [-o <field>,...] [-p <prop>,...] <name>"},
320 	{ "set-linkprop",	do_set_linkprop,
321 	    "\tset-linkprop\t[-t] [-R <root-dir>] -p <prop>=<value>[,...] "
322 	    "<name>"							},
323 	{ "reset-linkprop",	do_reset_linkprop,
324 	    "\treset-linkprop\t[-t] [-R <root-dir>] [-p <prop>,...] <name>\n" },
325 	{ "show-ether",		do_show_ether,
326 	    "\tshow-ether\t[-px][-o <field>,...] <link>\n"		},
327 	{ "create-secobj",	do_create_secobj,
328 	    "\tcreate-secobj\t[-t] [-R <root-dir>] [-f <file>] -c <class> "
329 	    "<secobj>"							},
330 	{ "delete-secobj",	do_delete_secobj,
331 	    "\tdelete-secobj\t[-t] [-R <root-dir>] <secobj>[,...]"	},
332 	{ "show-secobj",	do_show_secobj,
333 	    "\tshow-secobj\t[-pP] [-o <field>,...] [<secobj>,...]\n"	},
334 	{ "init-linkprop",	do_init_linkprop,	NULL		},
335 	{ "init-secobj",	do_init_secobj,		NULL		},
336 	{ "create-vlan", 	do_create_vlan,
337 	    "\tcreate-vlan\t[-ft] [-R <root-dir>] -l <link> -v <vid> [link]" },
338 	{ "delete-vlan", 	do_delete_vlan,
339 	    "\tdelete-vlan\t[-t] [-R <root-dir>] <link>"		},
340 	{ "show-vlan",		do_show_vlan,
341 	    "\tshow-vlan\t[-pP] [-o <field>,..] [<link>]\n"		},
342 	{ "up-vlan",		do_up_vlan,		NULL		},
343 	{ "delete-phys",	do_delete_phys,
344 	    "\tdelete-phys\t<link>"					},
345 	{ "show-phys",		do_show_phys,
346 	    "\tshow-phys\t[-pP] [-o <field>,..] [<link>]"		},
347 	{ "init-phys",		do_init_phys,		NULL		},
348 	{ "show-linkmap",	do_show_linkmap,	NULL		}
349 };
350 
351 static const struct option lopts[] = {
352 	{"vlan-id",	required_argument,	0, 'v'},
353 	{"output",	required_argument,	0, 'o'},
354 	{"dev",		required_argument,	0, 'd'},
355 	{"policy",	required_argument,	0, 'P'},
356 	{"lacp-mode",	required_argument,	0, 'L'},
357 	{"lacp-timer",	required_argument,	0, 'T'},
358 	{"unicast",	required_argument,	0, 'u'},
359 	{"temporary",	no_argument,		0, 't'},
360 	{"root-dir",	required_argument,	0, 'R'},
361 	{"link",	required_argument,	0, 'l'},
362 	{"forcible",	no_argument,		0, 'f'},
363 	{ 0, 0, 0, 0 }
364 };
365 
366 static const struct option show_lopts[] = {
367 	{"statistics",	no_argument,		0, 's'},
368 	{"interval",	required_argument,	0, 'i'},
369 	{"parseable",	no_argument,		0, 'p'},
370 	{"extended",	no_argument,		0, 'x'},
371 	{"output",	required_argument,	0, 'o'},
372 	{"persistent",	no_argument,		0, 'P'},
373 	{"lacp",	no_argument,		0, 'L'},
374 	{ 0, 0, 0, 0 }
375 };
376 
377 static const struct option prop_longopts[] = {
378 	{"temporary",	no_argument,		0, 't'  },
379 	{"output",	required_argument,	0, 'o'  },
380 	{"root-dir",	required_argument,	0, 'R'  },
381 	{"prop",	required_argument,	0, 'p'  },
382 	{"parseable",	no_argument,		0, 'c'  },
383 	{"persistent",	no_argument,		0, 'P'  },
384 	{ 0, 0, 0, 0 }
385 };
386 
387 static const struct option wifi_longopts[] = {
388 	{"parseable",	no_argument,		0, 'p'  },
389 	{"output",	required_argument,	0, 'o'  },
390 	{"essid",	required_argument,	0, 'e'  },
391 	{"bsstype",	required_argument,	0, 'b'  },
392 	{"mode",	required_argument,	0, 'm'  },
393 	{"key",		required_argument,	0, 'k'  },
394 	{"sec",		required_argument,	0, 's'  },
395 	{"auth",	required_argument,	0, 'a'  },
396 	{"create-ibss",	required_argument,	0, 'c'  },
397 	{"timeout",	required_argument,	0, 'T'  },
398 	{"all-links",	no_argument,		0, 'a'  },
399 	{"temporary",	no_argument,		0, 't'  },
400 	{"root-dir",	required_argument,	0, 'R'  },
401 	{"persistent",	no_argument,		0, 'P'  },
402 	{"file",	required_argument,	0, 'f'  },
403 	{ 0, 0, 0, 0 }
404 };
405 static const struct option showeth_lopts[] = {
406 	{"parseable",	no_argument,		0, 'p'	},
407 	{"extended",	no_argument,		0, 'x'	},
408 	{"output",	required_argument,	0, 'o'	},
409 	{ 0, 0, 0, 0 }
410 };
411 
412 /*
413  * structures for 'dladm show-ether'
414  */
415 typedef struct ether_fields_buf_s
416 {
417 	char	eth_link[15];
418 	char	eth_ptype[8];
419 	char	eth_state[8];
420 	char	eth_autoneg[5];
421 	char	eth_spdx[31];
422 	char	eth_pause[6];
423 	char	eth_rem_fault[16];
424 } ether_fields_buf_t;
425 
426 static print_field_t ether_fields[] = {
427 /* name,	header,			field width,  offset,	cmdtype */
428 { "link",	"LINK",			15,
429     offsetof(ether_fields_buf_t, eth_link),	CMD_TYPE_ANY},
430 { "ptype",	"PTYPE",		8,
431     offsetof(ether_fields_buf_t, eth_ptype),	CMD_TYPE_ANY},
432 { "state",	"STATE",		8,
433     offsetof(ether_fields_buf_t, eth_state),	CMD_TYPE_ANY},
434 { "auto",	"AUTO",			5,
435     offsetof(ether_fields_buf_t, eth_autoneg),	CMD_TYPE_ANY},
436 { "speed-duplex", "SPEED-DUPLEX",	31,
437     offsetof(ether_fields_buf_t, eth_spdx),	CMD_TYPE_ANY},
438 { "pause",	"PAUSE",		6,
439     offsetof(ether_fields_buf_t, eth_pause),	CMD_TYPE_ANY},
440 { "rem_fault",	"REM_FAULT",		16,
441     offsetof(ether_fields_buf_t, eth_rem_fault),	CMD_TYPE_ANY}}
442 ;
443 #define	ETHER_MAX_FIELDS	(sizeof (ether_fields) / sizeof (print_field_t))
444 
445 typedef struct print_ether_state {
446 	const char	*es_link;
447 	boolean_t	es_parseable;
448 	boolean_t	es_header;
449 	boolean_t	es_extended;
450 	print_state_t	es_print;
451 } print_ether_state_t;
452 
453 /*
454  * structures for 'dladm show-dev'.
455  */
456 typedef enum {
457 	DEV_LINK,
458 	DEV_STATE,
459 	DEV_SPEED,
460 	DEV_DUPLEX
461 } dev_field_index_t;
462 
463 static print_field_t dev_fields[] = {
464 /* name,	header,		field width,	index,		cmdtype */
465 { "link",	"LINK",			15,	DEV_LINK,	CMD_TYPE_ANY},
466 { "state",	"STATE",		6,	DEV_STATE,	CMD_TYPE_ANY},
467 { "speed",	"SPEED",		8,	DEV_SPEED,	CMD_TYPE_ANY},
468 { "duplex",	"DUPLEX",		8,	DEV_DUPLEX,	CMD_TYPE_ANY}}
469 ;
470 #define	DEV_MAX_FIELDS	(sizeof (dev_fields) / sizeof (print_field_t))
471 
472 /*
473  * structures for 'dladm show-dev -s' (print statistics)
474  */
475 typedef enum {
476 	DEVS_LINK,
477 	DEVS_IPKTS,
478 	DEVS_RBYTES,
479 	DEVS_IERRORS,
480 	DEVS_OPKTS,
481 	DEVS_OBYTES,
482 	DEVS_OERRORS
483 } devs_field_index_t;
484 
485 static print_field_t devs_fields[] = {
486 /* name,	header,		field width,	index,		cmdtype	*/
487 { "link",	"LINK",			15,	DEVS_LINK,	CMD_TYPE_ANY},
488 { "ipackets",	"IPACKETS",		10,	DEVS_IPKTS,	CMD_TYPE_ANY},
489 { "rbytes",	"RBYTES",		8,	DEVS_RBYTES,	CMD_TYPE_ANY},
490 { "ierrors",	"IERRORS",		10,	DEVS_IERRORS,	CMD_TYPE_ANY},
491 { "opackets",	"OPACKETS",		12,	DEVS_OPKTS,	CMD_TYPE_ANY},
492 { "obytes",	"OBYTES",		12,	DEVS_OBYTES,	CMD_TYPE_ANY},
493 { "oerrors",	"OERRORS",		8,	DEVS_OERRORS,	CMD_TYPE_ANY}}
494 ;
495 #define	DEVS_MAX_FIELDS	(sizeof (devs_fields) / sizeof (print_field_t))
496 typedef struct dev_args_s {
497 	char		*devs_link;
498 	pktsum_t 	*devs_psum;
499 } dev_args_t;
500 static char *print_dev_stats(print_field_t *, void *);
501 static char *print_dev(print_field_t *, void *);
502 
503 /*
504  * buffer used by print functions for show-{link,phys,vlan} commands.
505  */
506 typedef struct link_fields_buf_s {
507 	char link_name[MAXLINKNAMELEN];
508 	char link_class[DLADM_STRSIZE];
509 	char link_mtu[11];
510 	char link_state[DLADM_STRSIZE];
511 	char link_over[MAXLINKNAMELEN];
512 	char link_phys_state[DLADM_STRSIZE];
513 	char link_phys_media[DLADM_STRSIZE];
514 	char link_phys_speed[DLADM_STRSIZE];
515 	char link_phys_duplex[DLPI_LINKNAME_MAX];
516 	char link_phys_device[DLPI_LINKNAME_MAX];
517 	char link_flags[6];
518 	char link_vlan_vid[6];
519 } link_fields_buf_t;
520 
521 /*
522  * structures for 'dladm show-link'
523  */
524 static print_field_t link_fields[] = {
525 /* name,	header,		field width,	offset,	cmdtype		*/
526 { "link",	"LINK",		11,
527     offsetof(link_fields_buf_t, link_name),	CMD_TYPE_ANY},
528 { "class",	"CLASS",	 8,
529     offsetof(link_fields_buf_t, link_class),	CMD_TYPE_ANY},
530 { "mtu",	"MTU",		 6,
531     offsetof(link_fields_buf_t, link_mtu),	CMD_TYPE_ANY},
532 { "state",	"STATE",	 8,
533     offsetof(link_fields_buf_t, link_state),	CMD_TYPE_ANY},
534 { "over",	"OVER",		DLPI_LINKNAME_MAX,
535     offsetof(link_fields_buf_t, link_over),	CMD_TYPE_ANY}}
536 ;
537 #define	DEV_LINK_FIELDS	(sizeof (link_fields) / sizeof (print_field_t))
538 
539 /*
540  * structures for 'dladm show-aggr'
541  */
542 typedef struct laggr_fields_buf_s {
543 	char laggr_name[DLPI_LINKNAME_MAX];
544 	char laggr_policy[9];
545 	char laggr_addrpolicy[ETHERADDRL * 3 + 3];
546 	char laggr_lacpactivity[14];
547 	char laggr_lacptimer[DLADM_STRSIZE];
548 	char laggr_flags[7];
549 } laggr_fields_buf_t;
550 
551 typedef struct laggr_args_s {
552 	int			laggr_lport; /* -1 indicates the aggr itself */
553 	const char 		*laggr_link;
554 	dladm_aggr_grp_attr_t	*laggr_ginfop;
555 	dladm_status_t		*laggr_status;
556 	pktsum_t		*laggr_pktsumtot; /* -s only */
557 	pktsum_t		*laggr_prevstats; /* -s only */
558 	boolean_t		laggr_parseable;
559 } laggr_args_t;
560 
561 static print_field_t laggr_fields[] = {
562 /* name,		header,		field width,	offset,	cmdtype	*/
563 { "link",		"LINK",		15,
564     offsetof(laggr_fields_buf_t, laggr_name),		CMD_TYPE_ANY},
565 { "policy",		"POLICY",	 8,
566     offsetof(laggr_fields_buf_t, laggr_policy),	CMD_TYPE_ANY},
567 { "addrpolicy",		"ADDRPOLICY",	 ETHERADDRL * 3 + 2,
568     offsetof(laggr_fields_buf_t, laggr_addrpolicy),	CMD_TYPE_ANY},
569 { "lacpactivity",	"LACPACTIVITY",	 13,
570     offsetof(laggr_fields_buf_t, laggr_lacpactivity),	CMD_TYPE_ANY},
571 { "lacptimer",		"LACPTIMER",	 11,
572     offsetof(laggr_fields_buf_t, laggr_lacptimer),	CMD_TYPE_ANY},
573 { "flags",		"FLAGS",	 7,
574     offsetof(laggr_fields_buf_t, laggr_flags),	CMD_TYPE_ANY}}
575 ;
576 #define	LAGGR_MAX_FIELDS	(sizeof (laggr_fields) / sizeof (print_field_t))
577 
578 /*
579  * structures for 'dladm show-aggr -x'.
580  */
581 typedef enum {
582 	AGGR_X_LINK,
583 	AGGR_X_PORT,
584 	AGGR_X_SPEED,
585 	AGGR_X_DUPLEX,
586 	AGGR_X_STATE,
587 	AGGR_X_ADDRESS,
588 	AGGR_X_PORTSTATE
589 } aggr_x_field_index_t;
590 
591 static print_field_t aggr_x_fields[] = {
592 /* name,	header,		field width,	index,		cmdtype	*/
593 { "link",	"LINK",			11,	AGGR_X_LINK,	CMD_TYPE_ANY},
594 { "port",	"PORT",			14,	AGGR_X_PORT,	CMD_TYPE_ANY},
595 { "speed",	"SPEED",		4,	AGGR_X_SPEED,	CMD_TYPE_ANY},
596 { "duplex",	"DUPLEX",		9,	AGGR_X_DUPLEX,	CMD_TYPE_ANY},
597 { "state",	"STATE",		9,	AGGR_X_STATE,	CMD_TYPE_ANY},
598 { "address",	"ADDRESS",		18,	AGGR_X_ADDRESS,	CMD_TYPE_ANY},
599 { "portstate",	"PORTSTATE",		15,	AGGR_X_PORTSTATE, CMD_TYPE_ANY}}
600 ;
601 #define	AGGR_X_MAX_FIELDS \
602 	(sizeof (aggr_x_fields) / sizeof (print_field_t))
603 
604 /*
605  * structures for 'dladm show-aggr -s'.
606  */
607 typedef enum {
608 	AGGR_S_LINK,
609 	AGGR_S_PORT,
610 	AGGR_S_IPKTS,
611 	AGGR_S_RBYTES,
612 	AGGR_S_OPKTS,
613 	AGGR_S_OBYTES,
614 	AGGR_S_IPKTDIST,
615 	AGGR_S_OPKTDIST
616 } aggr_s_field_index_t;
617 
618 static print_field_t aggr_s_fields[] = {
619 /* name,		header,		field width,	index,	cmdtype	*/
620 { "link",		"LINK",		11,	AGGR_S_LINK,
621     CMD_TYPE_ANY},
622 { "port",		"PORT",		9,	AGGR_S_PORT,
623     CMD_TYPE_ANY},
624 { "ipackets",		"IPACKETS",	7,	AGGR_S_IPKTS,
625     CMD_TYPE_ANY},
626 { "rbytes",		"RBYTES",	7,	AGGR_S_RBYTES,
627     CMD_TYPE_ANY},
628 { "opackets",		"OPACKETS",	7,	AGGR_S_OPKTS,
629     CMD_TYPE_ANY},
630 { "obytes",		"OBYTES",	7,	AGGR_S_OBYTES,
631     CMD_TYPE_ANY},
632 { "ipktdist",		"IPKTDIST",	8,	AGGR_S_IPKTDIST,
633     CMD_TYPE_ANY},
634 { "opktdist",		"OPKTDIST",	14,	AGGR_S_OPKTDIST,
635     CMD_TYPE_ANY}}
636 ;
637 #define	AGGR_S_MAX_FIELDS \
638 	(sizeof (aggr_l_fields) / sizeof (print_field_t))
639 
640 /*
641  * structures for 'dladm show-dev -L'.
642  */
643 typedef enum {
644 	AGGR_L_LINK,
645 	AGGR_L_PORT,
646 	AGGR_L_AGGREGATABLE,
647 	AGGR_L_SYNC,
648 	AGGR_L_COLL,
649 	AGGR_L_DIST,
650 	AGGR_L_DEFAULTED,
651 	AGGR_L_EXPIRED
652 } aggr_l_field_index_t;
653 
654 static print_field_t aggr_l_fields[] = {
655 /* name,		header,		field width,	index,	cmdtype	*/
656 { "link",		"LINK",		11,	AGGR_L_LINK,
657     CMD_TYPE_ANY},
658 { "port",		"PORT",		12,	AGGR_L_PORT,
659     CMD_TYPE_ANY},
660 { "aggregatable",	"AGGREGATABLE",	12,	AGGR_L_AGGREGATABLE,
661     CMD_TYPE_ANY},
662 { "sync",		"SYNC",		4,	AGGR_L_SYNC,
663     CMD_TYPE_ANY},
664 { "coll",		"COLL",		4,	AGGR_L_COLL,
665     CMD_TYPE_ANY},
666 { "dist",		"DIST",		4,	AGGR_L_DIST,
667     CMD_TYPE_ANY},
668 { "defaulted",		"DEFAULTED",	9,	AGGR_L_DEFAULTED,
669     CMD_TYPE_ANY},
670 { "expired",		"EXPIRED",	14,	AGGR_L_EXPIRED,
671     CMD_TYPE_ANY}}
672 ;
673 #define	AGGR_L_MAX_FIELDS \
674 	(sizeof (aggr_l_fields) / sizeof (print_field_t))
675 
676 /*
677  * structures for 'dladm show-phys'
678  */
679 
680 static print_field_t phys_fields[] = {
681 /* name,	header,		field width,	offset,	cmdtype		*/
682 { "link",	"LINK",			12,
683     offsetof(link_fields_buf_t, link_name),		CMD_TYPE_ANY},
684 { "media",	"MEDIA",		20,
685     offsetof(link_fields_buf_t, link_phys_media),	CMD_TYPE_ANY},
686 { "state",	"STATE",		10,
687     offsetof(link_fields_buf_t, link_phys_state),	CMD_TYPE_ANY},
688 { "speed",	"SPEED",		4,
689     offsetof(link_fields_buf_t, link_phys_speed),	CMD_TYPE_ANY},
690 { "duplex",	"DUPLEX",		9,
691     offsetof(link_fields_buf_t, link_phys_duplex),	CMD_TYPE_ANY},
692 { "device",	"DEVICE",		12,
693     offsetof(link_fields_buf_t, link_phys_device),	CMD_TYPE_ANY},
694 { "flags",	"FLAGS",		6,
695     offsetof(link_fields_buf_t, link_flags),		CMD_TYPE_ANY}}
696 ;
697 #define	PHYS_MAX_FIELDS	(sizeof (phys_fields) / sizeof (print_field_t))
698 
699 /*
700  * structures for 'dladm show-vlan'
701  */
702 static print_field_t vlan_fields[] = {
703 /* name,	header,		field width,	offset,	cmdtype		*/
704 { "link",	"LINK",			15,
705     offsetof(link_fields_buf_t, link_name),		CMD_TYPE_ANY},
706 { "vid",	"VID",			8,
707     offsetof(link_fields_buf_t, link_vlan_vid),	CMD_TYPE_ANY},
708 { "over",	"OVER",			12,
709     offsetof(link_fields_buf_t, link_over),		CMD_TYPE_ANY},
710 { "flags",	"FLAGS",		6,
711     offsetof(link_fields_buf_t, link_flags),		CMD_TYPE_ANY}}
712 ;
713 #define	VLAN_MAX_FIELDS	(sizeof (vlan_fields) / sizeof (print_field_t))
714 
715 /*
716  * structures for 'dladm show-wifi'
717  */
718 static print_field_t wifi_fields[] = {
719 { "link",	"LINK",		10, 0,			WIFI_CMD_ALL},
720 { "essid",	"ESSID",	19, DLADM_WLAN_ATTR_ESSID,	WIFI_CMD_ALL},
721 { "bssid",	"BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
722 { "ibssid",	"BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
723 { "mode",	"MODE",		6,  DLADM_WLAN_ATTR_MODE,	WIFI_CMD_ALL},
724 { "speed",	"SPEED",	6,  DLADM_WLAN_ATTR_SPEED,	WIFI_CMD_ALL},
725 { "auth",	"AUTH",		8,  DLADM_WLAN_ATTR_AUTH,	WIFI_CMD_SHOW},
726 { "bsstype",	"BSSTYPE",	8,  DLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL},
727 { "sec",	"SEC",		6,  DLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL},
728 { "status",	"STATUS",	17, DLADM_WLAN_LINKATTR_STATUS, WIFI_CMD_SHOW},
729 { "strength",	"STRENGTH",	10, DLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}}
730 ;
731 
732 static char *all_scan_wifi_fields =
733 	"link,essid,bssid,sec,strength,mode,speed,bsstype";
734 static char *all_show_wifi_fields =
735 	"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
736 static char *def_scan_wifi_fields =
737 	"link,essid,bssid,sec,strength,mode,speed";
738 static char *def_show_wifi_fields =
739 	"link,status,essid,sec,strength,mode,speed";
740 
741 #define	WIFI_MAX_FIELDS		(sizeof (wifi_fields) / sizeof (print_field_t))
742 
743 /*
744  * structures for 'dladm show-linkprop'
745  */
746 typedef enum {
747 	LINKPROP_LINK,
748 	LINKPROP_PROPERTY,
749 	LINKPROP_VALUE,
750 	LINKPROP_DEFAULT,
751 	LINKPROP_POSSIBLE
752 } linkprop_field_index_t;
753 
754 static print_field_t linkprop_fields[] = {
755 /* name,	header,		field width,  index,		cmdtype */
756 { "link",	"LINK",		12,	LINKPROP_LINK,		CMD_TYPE_ANY},
757 { "property",	"PROPERTY",	15,	LINKPROP_PROPERTY,	CMD_TYPE_ANY},
758 { "value",	"VALUE",	14,	LINKPROP_VALUE,		CMD_TYPE_ANY},
759 { "default",	"DEFAULT",	14,	LINKPROP_DEFAULT, 	CMD_TYPE_ANY},
760 { "possible",	"POSSIBLE",	20,	LINKPROP_POSSIBLE,	CMD_TYPE_ANY}}
761 ;
762 #define	LINKPROP_MAX_FIELDS					\
763 	(sizeof (linkprop_fields) / sizeof (print_field_t))
764 
765 #define	MAX_PROPS		32
766 #define	MAX_PROP_LINE		512
767 
768 typedef struct prop_info {
769 	char		*pi_name;
770 	char		*pi_val[DLADM_MAX_PROP_VALCNT];
771 	uint_t		pi_count;
772 } prop_info_t;
773 
774 typedef struct prop_list {
775 	prop_info_t	pl_info[MAX_PROPS];
776 	uint_t		pl_count;
777 	char		*pl_buf;
778 } prop_list_t;
779 
780 typedef struct show_linkprop_state {
781 	char		ls_link[MAXLINKNAMELEN];
782 	char		*ls_line;
783 	char		**ls_propvals;
784 	prop_list_t	*ls_proplist;
785 	boolean_t	ls_parseable;
786 	boolean_t	ls_persist;
787 	boolean_t	ls_header;
788 	dladm_status_t	ls_status;
789 	dladm_status_t	ls_retstatus;
790 	print_state_t	ls_print;
791 } show_linkprop_state_t;
792 
793 typedef struct linkprop_args_s {
794 	show_linkprop_state_t	*ls_state;
795 	char			*ls_propname;
796 	datalink_id_t		ls_linkid;
797 } linkprop_args_t;
798 
799 /*
800  * structures for 'dladm show-secobj'
801  */
802 typedef struct secobj_fields_buf_s {
803 	char			ss_obj_name[DLADM_SECOBJ_VAL_MAX];
804 	char			ss_class[20];
805 	char			ss_val[30];
806 } secobj_fields_buf_t;
807 static print_field_t secobj_fields[] = {
808 /* name,	header,		field width,	offset,	cmdtype		*/
809 { "object",	"OBJECT",		20,
810     offsetof(secobj_fields_buf_t, ss_obj_name),	CMD_TYPE_ANY},
811 { "class",	"CLASS",		20,
812     offsetof(secobj_fields_buf_t, ss_class),	CMD_TYPE_ANY},
813 { "value",	"VALUE",		30,
814     offsetof(secobj_fields_buf_t, ss_val),	CMD_TYPE_ANY}}
815 ;
816 #define	DEV_SOBJ_FIELDS	(sizeof (secobj_fields) / sizeof (print_field_t))
817 
818 static char *progname;
819 static sig_atomic_t signalled;
820 
821 static void
822 usage(void)
823 {
824 	int	i;
825 	cmd_t	*cmdp;
826 	(void) fprintf(stderr, gettext("usage:  dladm <subcommand> <args> ..."
827 	    "\n"));
828 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
829 		cmdp = &cmds[i];
830 		if (cmdp->c_usage != NULL)
831 			(void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
832 	}
833 	exit(1);
834 }
835 
836 int
837 main(int argc, char *argv[])
838 {
839 	int	i;
840 	cmd_t	*cmdp;
841 
842 	(void) setlocale(LC_ALL, "");
843 #if !defined(TEXT_DOMAIN)
844 #define	TEXT_DOMAIN "SYS_TEST"
845 #endif
846 	(void) textdomain(TEXT_DOMAIN);
847 
848 	progname = argv[0];
849 
850 	if (argc < 2)
851 		usage();
852 
853 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
854 		cmdp = &cmds[i];
855 		if (strcmp(argv[1], cmdp->c_name) == 0) {
856 			cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
857 			exit(0);
858 		}
859 	}
860 
861 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
862 	    progname, argv[1]);
863 	usage();
864 
865 	return (0);
866 }
867 
868 static void
869 do_create_aggr(int argc, char *argv[], const char *use)
870 {
871 	char			option;
872 	int			key = 0;
873 	uint32_t		policy = AGGR_POLICY_L4;
874 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
875 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
876 	dladm_aggr_port_attr_db_t	port[MAXPORT];
877 	uint_t			n, ndev, nlink;
878 	uint8_t			mac_addr[ETHERADDRL];
879 	boolean_t		mac_addr_fixed = B_FALSE;
880 	boolean_t		P_arg = B_FALSE;
881 	boolean_t		l_arg = B_FALSE;
882 	boolean_t		u_arg = B_FALSE;
883 	boolean_t		T_arg = B_FALSE;
884 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
885 	char			*altroot = NULL;
886 	char			name[MAXLINKNAMELEN];
887 	char			*devs[MAXPORT];
888 	char			*links[MAXPORT];
889 	dladm_status_t		status;
890 
891 	ndev = nlink = opterr = 0;
892 	while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:",
893 	    lopts, NULL)) != -1) {
894 		switch (option) {
895 		case 'd':
896 			if (ndev + nlink >= MAXPORT)
897 				die("too many ports specified");
898 
899 			devs[ndev++] = optarg;
900 			break;
901 		case 'P':
902 			if (P_arg)
903 				die_optdup(option);
904 
905 			P_arg = B_TRUE;
906 			if (!dladm_aggr_str2policy(optarg, &policy))
907 				die("invalid policy '%s'", optarg);
908 			break;
909 		case 'u':
910 			if (u_arg)
911 				die_optdup(option);
912 
913 			u_arg = B_TRUE;
914 			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
915 			    mac_addr))
916 				die("invalid MAC address '%s'", optarg);
917 			break;
918 		case 'l':
919 			if (isdigit(optarg[strlen(optarg) - 1])) {
920 
921 				/*
922 				 * Ended with digit, possibly a link name.
923 				 */
924 				if (ndev + nlink >= MAXPORT)
925 					die("too many ports specified");
926 
927 				links[nlink++] = optarg;
928 				break;
929 			}
930 			/* FALLTHROUGH */
931 		case 'L':
932 			if (l_arg)
933 				die_optdup(option);
934 
935 			l_arg = B_TRUE;
936 			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
937 				die("invalid LACP mode '%s'", optarg);
938 			break;
939 		case 'T':
940 			if (T_arg)
941 				die_optdup(option);
942 
943 			T_arg = B_TRUE;
944 			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
945 				die("invalid LACP timer value '%s'", optarg);
946 			break;
947 		case 't':
948 			flags &= ~DLADM_OPT_PERSIST;
949 			break;
950 		case 'f':
951 			flags |= DLADM_OPT_FORCE;
952 			break;
953 		case 'R':
954 			altroot = optarg;
955 			break;
956 		default:
957 			die_opterr(optopt, option, use);
958 			break;
959 		}
960 	}
961 
962 	if (ndev + nlink == 0)
963 		usage();
964 
965 	/* get key value or the aggregation name (required last argument) */
966 	if (optind != (argc-1))
967 		usage();
968 
969 	if (!str2int(argv[optind], &key)) {
970 		if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >=
971 		    MAXLINKNAMELEN) {
972 			die("link name too long '%s'", argv[optind]);
973 		}
974 
975 		if (!dladm_valid_linkname(name))
976 			die("invalid link name '%s'", argv[optind]);
977 	} else {
978 		(void) snprintf(name, MAXLINKNAMELEN, "aggr%d", key);
979 	}
980 
981 	if (altroot != NULL)
982 		altroot_cmd(altroot, argc, argv);
983 
984 	for (n = 0; n < ndev; n++) {
985 		if (dladm_dev2linkid(devs[n], &port[n].lp_linkid) !=
986 		    DLADM_STATUS_OK) {
987 			die("invalid dev name '%s'", devs[n]);
988 		}
989 	}
990 
991 	for (n = 0; n < nlink; n++) {
992 		if (dladm_name2info(links[n], &port[ndev + n].lp_linkid,
993 		    NULL, NULL, NULL) != DLADM_STATUS_OK) {
994 			die("invalid link name '%s'", links[n]);
995 		}
996 	}
997 
998 	status = dladm_aggr_create(name, key, ndev + nlink, port, policy,
999 	    mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode,
1000 	    lacp_timer, flags);
1001 done:
1002 	if (status != DLADM_STATUS_OK) {
1003 		if (status == DLADM_STATUS_NONOTIF) {
1004 			die_dlerr(status, "not all links have link up/down "
1005 			    "detection; must use -f (see dladm(1M))\n");
1006 		} else {
1007 			die_dlerr(status, "create operation failed");
1008 		}
1009 	}
1010 }
1011 
1012 /*
1013  * arg is either the key or the aggr name. Validate it and convert it to
1014  * the linkid if altroot is NULL.
1015  */
1016 static dladm_status_t
1017 i_dladm_aggr_get_linkid(const char *altroot, const char *arg,
1018     datalink_id_t *linkidp, uint32_t flags)
1019 {
1020 	int		key = 0;
1021 	char		*aggr = NULL;
1022 	dladm_status_t	status;
1023 
1024 	if (!str2int(arg, &key))
1025 		aggr = (char *)arg;
1026 
1027 	if (aggr == NULL && key == 0)
1028 		return (DLADM_STATUS_LINKINVAL);
1029 
1030 	if (altroot != NULL)
1031 		return (DLADM_STATUS_OK);
1032 
1033 	if (aggr != NULL) {
1034 		status = dladm_name2info(aggr, linkidp, NULL, NULL, NULL);
1035 	} else {
1036 		status = dladm_key2linkid(key, linkidp, flags);
1037 	}
1038 
1039 	return (status);
1040 }
1041 
1042 static void
1043 do_delete_aggr(int argc, char *argv[], const char *use)
1044 {
1045 	char			option;
1046 	char			*altroot = NULL;
1047 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1048 	dladm_status_t		status;
1049 	datalink_id_t		linkid;
1050 
1051 	opterr = 0;
1052 	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
1053 		switch (option) {
1054 		case 't':
1055 			flags &= ~DLADM_OPT_PERSIST;
1056 			break;
1057 		case 'R':
1058 			altroot = optarg;
1059 			break;
1060 		default:
1061 			die_opterr(optopt, option, use);
1062 			break;
1063 		}
1064 	}
1065 
1066 	/* get key value or the aggregation name (required last argument) */
1067 	if (optind != (argc-1))
1068 		usage();
1069 
1070 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
1071 	if (status != DLADM_STATUS_OK)
1072 		goto done;
1073 
1074 	if (altroot != NULL)
1075 		altroot_cmd(altroot, argc, argv);
1076 
1077 	status = dladm_aggr_delete(linkid, flags);
1078 done:
1079 	if (status != DLADM_STATUS_OK)
1080 		die_dlerr(status, "delete operation failed");
1081 }
1082 
1083 static void
1084 do_add_aggr(int argc, char *argv[], const char *use)
1085 {
1086 	char			option;
1087 	uint_t			n, ndev, nlink;
1088 	char			*altroot = NULL;
1089 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1090 	datalink_id_t		linkid;
1091 	dladm_status_t		status;
1092 	dladm_aggr_port_attr_db_t	port[MAXPORT];
1093 	char			*devs[MAXPORT];
1094 	char			*links[MAXPORT];
1095 
1096 	ndev = nlink = opterr = 0;
1097 	while ((option = getopt_long(argc, argv, ":d:l:R:tf", lopts,
1098 	    NULL)) != -1) {
1099 		switch (option) {
1100 		case 'd':
1101 			if (ndev + nlink >= MAXPORT)
1102 				die("too many ports specified");
1103 
1104 			devs[ndev++] = optarg;
1105 			break;
1106 		case 'l':
1107 			if (ndev + nlink >= MAXPORT)
1108 				die("too many ports specified");
1109 
1110 			links[nlink++] = optarg;
1111 			break;
1112 		case 't':
1113 			flags &= ~DLADM_OPT_PERSIST;
1114 			break;
1115 		case 'f':
1116 			flags |= DLADM_OPT_FORCE;
1117 			break;
1118 		case 'R':
1119 			altroot = optarg;
1120 			break;
1121 		default:
1122 			die_opterr(optopt, option, use);
1123 			break;
1124 		}
1125 	}
1126 
1127 	if (ndev + nlink == 0)
1128 		usage();
1129 
1130 	/* get key value or the aggregation name (required last argument) */
1131 	if (optind != (argc-1))
1132 		usage();
1133 
1134 	if ((status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid,
1135 	    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST))) !=
1136 	    DLADM_STATUS_OK) {
1137 		goto done;
1138 	}
1139 
1140 	if (altroot != NULL)
1141 		altroot_cmd(altroot, argc, argv);
1142 
1143 	for (n = 0; n < ndev; n++) {
1144 		if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) !=
1145 		    DLADM_STATUS_OK) {
1146 			die("invalid <dev> '%s'", devs[n]);
1147 		}
1148 	}
1149 
1150 	for (n = 0; n < nlink; n++) {
1151 		if (dladm_name2info(links[n], &port[n + ndev].lp_linkid,
1152 		    NULL, NULL, NULL) != DLADM_STATUS_OK) {
1153 			die("invalid <link> '%s'", links[n]);
1154 		}
1155 	}
1156 
1157 	status = dladm_aggr_add(linkid, ndev + nlink, port, flags);
1158 done:
1159 	if (status != DLADM_STATUS_OK) {
1160 		/*
1161 		 * checking DLADM_STATUS_NOTSUP is a temporary workaround
1162 		 * and should be removed once 6399681 is fixed.
1163 		 */
1164 		if (status == DLADM_STATUS_NOTSUP) {
1165 			(void) fprintf(stderr,
1166 			    gettext("%s: add operation failed: %s\n"),
1167 			    progname,
1168 			    gettext("link capabilities don't match"));
1169 			exit(ENOTSUP);
1170 		} else if (status == DLADM_STATUS_NONOTIF) {
1171 			die_dlerr(status, "not all links have link up/down "
1172 			    "detection; must use -f (see dladm(1M))\n");
1173 		} else {
1174 			die_dlerr(status, "add operation failed");
1175 		}
1176 	}
1177 }
1178 
1179 static void
1180 do_remove_aggr(int argc, char *argv[], const char *use)
1181 {
1182 	char				option;
1183 	dladm_aggr_port_attr_db_t	port[MAXPORT];
1184 	uint_t				n, ndev, nlink;
1185 	char				*devs[MAXPORT];
1186 	char				*links[MAXPORT];
1187 	char				*altroot = NULL;
1188 	uint32_t			flags;
1189 	datalink_id_t			linkid;
1190 	dladm_status_t			status;
1191 
1192 	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1193 	ndev = nlink = opterr = 0;
1194 	while ((option = getopt_long(argc, argv, ":d:l:R:t",
1195 	    lopts, NULL)) != -1) {
1196 		switch (option) {
1197 		case 'd':
1198 			if (ndev + nlink >= MAXPORT)
1199 				die("too many ports specified");
1200 
1201 			devs[ndev++] = optarg;
1202 			break;
1203 		case 'l':
1204 			if (ndev + nlink >= MAXPORT)
1205 				die("too many ports specified");
1206 
1207 			links[nlink++] = optarg;
1208 			break;
1209 		case 't':
1210 			flags &= ~DLADM_OPT_PERSIST;
1211 			break;
1212 		case 'R':
1213 			altroot = optarg;
1214 			break;
1215 		default:
1216 			die_opterr(optopt, option, use);
1217 			break;
1218 		}
1219 	}
1220 
1221 	if (ndev + nlink == 0)
1222 		usage();
1223 
1224 	/* get key value or the aggregation name (required last argument) */
1225 	if (optind != (argc-1))
1226 		usage();
1227 
1228 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
1229 	if (status != DLADM_STATUS_OK)
1230 		goto done;
1231 
1232 	if (altroot != NULL)
1233 		altroot_cmd(altroot, argc, argv);
1234 
1235 	for (n = 0; n < ndev; n++) {
1236 		if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) !=
1237 		    DLADM_STATUS_OK) {
1238 			die("invalid <dev> '%s'", devs[n]);
1239 		}
1240 	}
1241 
1242 	for (n = 0; n < nlink; n++) {
1243 		if (dladm_name2info(links[n], &port[n + ndev].lp_linkid,
1244 		    NULL, NULL, NULL) != DLADM_STATUS_OK) {
1245 			die("invalid <link> '%s'", links[n]);
1246 		}
1247 	}
1248 
1249 	status = dladm_aggr_remove(linkid, ndev + nlink, port, flags);
1250 done:
1251 	if (status != DLADM_STATUS_OK)
1252 		die_dlerr(status, "remove operation failed");
1253 }
1254 
1255 static void
1256 do_modify_aggr(int argc, char *argv[], const char *use)
1257 {
1258 	char			option;
1259 	uint32_t		policy = AGGR_POLICY_L4;
1260 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
1261 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
1262 	uint8_t			mac_addr[ETHERADDRL];
1263 	boolean_t		mac_addr_fixed = B_FALSE;
1264 	uint8_t			modify_mask = 0;
1265 	char			*altroot = NULL;
1266 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1267 	datalink_id_t		linkid;
1268 	dladm_status_t		status;
1269 
1270 	opterr = 0;
1271 	while ((option = getopt_long(argc, argv, ":L:l:P:R:tu:T:", lopts,
1272 	    NULL)) != -1) {
1273 		switch (option) {
1274 		case 'P':
1275 			if (modify_mask & DLADM_AGGR_MODIFY_POLICY)
1276 				die_optdup(option);
1277 
1278 			modify_mask |= DLADM_AGGR_MODIFY_POLICY;
1279 
1280 			if (!dladm_aggr_str2policy(optarg, &policy))
1281 				die("invalid policy '%s'", optarg);
1282 			break;
1283 		case 'u':
1284 			if (modify_mask & DLADM_AGGR_MODIFY_MAC)
1285 				die_optdup(option);
1286 
1287 			modify_mask |= DLADM_AGGR_MODIFY_MAC;
1288 
1289 			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
1290 			    mac_addr))
1291 				die("invalid MAC address '%s'", optarg);
1292 			break;
1293 		case 'l':
1294 		case 'L':
1295 			if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE)
1296 				die_optdup(option);
1297 
1298 			modify_mask |= DLADM_AGGR_MODIFY_LACP_MODE;
1299 
1300 			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
1301 				die("invalid LACP mode '%s'", optarg);
1302 			break;
1303 		case 'T':
1304 			if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER)
1305 				die_optdup(option);
1306 
1307 			modify_mask |= DLADM_AGGR_MODIFY_LACP_TIMER;
1308 
1309 			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
1310 				die("invalid LACP timer value '%s'", optarg);
1311 			break;
1312 		case 't':
1313 			flags &= ~DLADM_OPT_PERSIST;
1314 			break;
1315 		case 'R':
1316 			altroot = optarg;
1317 			break;
1318 		default:
1319 			die_opterr(optopt, option, use);
1320 			break;
1321 		}
1322 	}
1323 
1324 	if (modify_mask == 0)
1325 		die("at least one of the -PulT options must be specified");
1326 
1327 	/* get key value or the aggregation name (required last argument) */
1328 	if (optind != (argc-1))
1329 		usage();
1330 
1331 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
1332 	if (status != DLADM_STATUS_OK)
1333 		goto done;
1334 
1335 	if (altroot != NULL)
1336 		altroot_cmd(altroot, argc, argv);
1337 
1338 	status = dladm_aggr_modify(linkid, modify_mask, policy, mac_addr_fixed,
1339 	    (const uchar_t *)mac_addr, lacp_mode, lacp_timer, flags);
1340 
1341 done:
1342 	if (status != DLADM_STATUS_OK)
1343 		die_dlerr(status, "modify operation failed");
1344 }
1345 
1346 /*ARGSUSED*/
1347 static void
1348 do_up_aggr(int argc, char *argv[], const char *use)
1349 {
1350 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
1351 	dladm_status_t	status;
1352 
1353 	/*
1354 	 * get the key or the name of the aggregation (optional last argument)
1355 	 */
1356 	if (argc == 2) {
1357 		if ((status = i_dladm_aggr_get_linkid(NULL, argv[1], &linkid,
1358 		    DLADM_OPT_PERSIST)) != DLADM_STATUS_OK) {
1359 			goto done;
1360 		}
1361 	} else if (argc > 2) {
1362 		usage();
1363 	}
1364 
1365 	status = dladm_aggr_up(linkid);
1366 done:
1367 	if (status != DLADM_STATUS_OK) {
1368 		if (argc == 2) {
1369 			die_dlerr(status,
1370 			    "could not bring up aggregation '%s'", argv[1]);
1371 		} else {
1372 			die_dlerr(status, "could not bring aggregations up");
1373 		}
1374 	}
1375 }
1376 
1377 static void
1378 do_create_vlan(int argc, char *argv[], const char *use)
1379 {
1380 	char		*link = NULL;
1381 	char		drv[DLPI_LINKNAME_MAX];
1382 	uint_t		ppa;
1383 	datalink_id_t	linkid;
1384 	int		vid = 0;
1385 	char		option;
1386 	uint32_t	flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
1387 	char		*altroot = NULL;
1388 	char		vlan[MAXLINKNAMELEN];
1389 	dladm_status_t	status;
1390 
1391 	opterr = 0;
1392 	while ((option = getopt_long(argc, argv, ":tfl:v:",
1393 	    lopts, NULL)) != -1) {
1394 		switch (option) {
1395 		case 'v':
1396 			if (vid != 0)
1397 				die_optdup(option);
1398 
1399 			if (!str2int(optarg, &vid) || vid < 1 || vid > 4094)
1400 				die("invalid VLAN identifier '%s'", optarg);
1401 
1402 			break;
1403 		case 'l':
1404 			if (link != NULL)
1405 				die_optdup(option);
1406 
1407 			link = optarg;
1408 			break;
1409 		case 'f':
1410 			flags |= DLADM_OPT_FORCE;
1411 			break;
1412 		case 't':
1413 			flags &= ~DLADM_OPT_PERSIST;
1414 			break;
1415 		case 'R':
1416 			altroot = optarg;
1417 			break;
1418 		default:
1419 			die_opterr(optopt, option, use);
1420 			break;
1421 		}
1422 	}
1423 
1424 	/* get vlan name if there is any */
1425 	if ((vid == 0) || (link == NULL) || (argc - optind > 1))
1426 		usage();
1427 
1428 	if (optind == (argc - 1)) {
1429 		if (strlcpy(vlan, argv[optind], MAXLINKNAMELEN) >=
1430 		    MAXLINKNAMELEN) {
1431 			die("vlan name too long '%s'", argv[optind]);
1432 		}
1433 	} else {
1434 		if ((dlpi_parselink(link, drv, &ppa) != DLPI_SUCCESS) ||
1435 		    (ppa >= 1000) ||
1436 		    (dlpi_makelink(vlan, drv, vid * 1000 + ppa) !=
1437 		    DLPI_SUCCESS)) {
1438 			die("invalid link name '%s'", link);
1439 		}
1440 	}
1441 
1442 	if (altroot != NULL)
1443 		altroot_cmd(altroot, argc, argv);
1444 
1445 	if (dladm_name2info(link, &linkid, NULL, NULL, NULL) !=
1446 	    DLADM_STATUS_OK) {
1447 		die("invalid link name '%s'", link);
1448 	}
1449 
1450 	if ((status = dladm_vlan_create(vlan, linkid, vid, flags)) !=
1451 	    DLADM_STATUS_OK) {
1452 		if (status == DLADM_STATUS_NOTSUP) {
1453 			die_dlerr(status, "VLAN over '%s' may require lowered "
1454 			    "MTU; must use -f (see dladm(1M))\n", link);
1455 		} else {
1456 			die_dlerr(status, "create operation failed");
1457 		}
1458 	}
1459 }
1460 
1461 static void
1462 do_delete_vlan(int argc, char *argv[], const char *use)
1463 {
1464 	char		option;
1465 	uint32_t	flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
1466 	char		*altroot = NULL;
1467 	datalink_id_t	linkid;
1468 	dladm_status_t	status;
1469 
1470 	opterr = 0;
1471 	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
1472 		switch (option) {
1473 		case 't':
1474 			flags &= ~DLADM_OPT_PERSIST;
1475 			break;
1476 		case 'R':
1477 			altroot = optarg;
1478 			break;
1479 		default:
1480 			die_opterr(optopt, option, use);
1481 			break;
1482 		}
1483 	}
1484 
1485 	/* get VLAN link name (required last argument) */
1486 	if (optind != (argc - 1))
1487 		usage();
1488 
1489 	if (altroot != NULL)
1490 		altroot_cmd(altroot, argc, argv);
1491 
1492 	status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL);
1493 	if (status != DLADM_STATUS_OK)
1494 		goto done;
1495 
1496 	status = dladm_vlan_delete(linkid, flags);
1497 done:
1498 	if (status != DLADM_STATUS_OK)
1499 		die_dlerr(status, "delete operation failed");
1500 }
1501 
1502 /*ARGSUSED*/
1503 static void
1504 do_up_vlan(int argc, char *argv[], const char *use)
1505 {
1506 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
1507 	dladm_status_t	status;
1508 
1509 	/*
1510 	 * get the name of the VLAN (optional last argument)
1511 	 */
1512 	if (argc > 2)
1513 		usage();
1514 
1515 	if (argc == 2) {
1516 		status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL);
1517 		if (status != DLADM_STATUS_OK)
1518 			goto done;
1519 	}
1520 
1521 	status = dladm_vlan_up(linkid);
1522 done:
1523 	if (status != DLADM_STATUS_OK) {
1524 		if (argc == 2) {
1525 			die_dlerr(status,
1526 			    "could not bring up VLAN '%s'", argv[1]);
1527 		} else {
1528 			die_dlerr(status, "could not bring VLANs up");
1529 		}
1530 	}
1531 }
1532 
1533 static void
1534 do_rename_link(int argc, char *argv[], const char *use)
1535 {
1536 	char		option;
1537 	char		*link1, *link2;
1538 	char		*altroot = NULL;
1539 	dladm_status_t	status;
1540 
1541 	opterr = 0;
1542 	while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) {
1543 		switch (option) {
1544 		case 'R':
1545 			altroot = optarg;
1546 			break;
1547 		default:
1548 			die_opterr(optopt, option, use);
1549 			break;
1550 		}
1551 	}
1552 
1553 	/* get link1 and link2 name (required the last 2 arguments) */
1554 	if (optind != (argc - 2))
1555 		usage();
1556 
1557 	if (altroot != NULL)
1558 		altroot_cmd(altroot, argc, argv);
1559 
1560 	link1 = argv[optind++];
1561 	link2 = argv[optind];
1562 	if ((status = dladm_rename_link(link1, link2)) != DLADM_STATUS_OK)
1563 		die_dlerr(status, "rename operation failed");
1564 }
1565 
1566 /*ARGSUSED*/
1567 static void
1568 do_delete_phys(int argc, char *argv[], const char *use)
1569 {
1570 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
1571 	dladm_status_t	status;
1572 
1573 	/* get link name (required the last argument) */
1574 	if (argc > 2)
1575 		usage();
1576 
1577 	if (argc == 2) {
1578 		status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL);
1579 		if (status != DLADM_STATUS_OK)
1580 			die_dlerr(status, "cannot delete '%s'", argv[1]);
1581 	}
1582 
1583 	if ((status = dladm_phys_delete(linkid)) != DLADM_STATUS_OK) {
1584 		if (argc == 2)
1585 			die_dlerr(status, "cannot delete '%s'", argv[1]);
1586 		else
1587 			die_dlerr(status, "delete operation failed");
1588 	}
1589 }
1590 
1591 /*ARGSUSED*/
1592 static int
1593 i_dladm_walk_linkmap(datalink_id_t linkid, void *arg)
1594 {
1595 	char			name[MAXLINKNAMELEN];
1596 	char			mediabuf[DLADM_STRSIZE];
1597 	char			classbuf[DLADM_STRSIZE];
1598 	datalink_class_t	class;
1599 	uint32_t		media;
1600 	uint32_t		flags;
1601 
1602 	if (dladm_datalink_id2info(linkid, &flags, &class, &media, name,
1603 	    MAXLINKNAMELEN) == DLADM_STATUS_OK) {
1604 		(void) dladm_class2str(class, classbuf);
1605 		(void) dladm_media2str(media, mediabuf);
1606 		(void) printf("%-12s%8d  %-12s%-20s %6d\n", name,
1607 		    linkid, classbuf, mediabuf, flags);
1608 	}
1609 	return (DLADM_WALK_CONTINUE);
1610 }
1611 
1612 /*ARGSUSED*/
1613 static void
1614 do_show_linkmap(int argc, char *argv[], const char *use)
1615 {
1616 	if (argc != 1)
1617 		die("invalid arguments");
1618 
1619 	(void) printf("%-12s%8s  %-12s%-20s %6s\n", "NAME", "LINKID",
1620 	    "CLASS", "MEDIA", "FLAGS");
1621 	(void) dladm_walk_datalink_id(i_dladm_walk_linkmap, NULL,
1622 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1623 	    DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
1624 }
1625 
1626 /*
1627  * Delete inactive physical links.
1628  */
1629 /*ARGSUSED*/
1630 static int
1631 purge_phys(datalink_id_t linkid, void *arg)
1632 {
1633 	datalink_class_t	class;
1634 	uint32_t		flags;
1635 
1636 	if (dladm_datalink_id2info(linkid, &flags, &class, NULL,
1637 	    NULL, 0) != DLADM_STATUS_OK) {
1638 		return (DLADM_WALK_CONTINUE);
1639 	}
1640 
1641 	if (class == DATALINK_CLASS_PHYS && !(flags & DLADM_OPT_ACTIVE))
1642 		(void) dladm_phys_delete(linkid);
1643 
1644 	return (DLADM_WALK_CONTINUE);
1645 }
1646 
1647 /*ARGSUSED*/
1648 static void
1649 do_init_phys(int argc, char *argv[], const char *use)
1650 {
1651 	di_node_t devtree;
1652 
1653 	if (argc > 1)
1654 		usage();
1655 
1656 	/*
1657 	 * Force all the devices to attach, therefore all the network physical
1658 	 * devices can be known to the dlmgmtd daemon.
1659 	 */
1660 	if ((devtree = di_init("/", DINFOFORCE | DINFOSUBTREE)) != DI_NODE_NIL)
1661 		di_fini(devtree);
1662 
1663 	(void) dladm_walk_datalink_id(purge_phys, NULL,
1664 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
1665 }
1666 
1667 
1668 /*
1669  * Print the active topology information.
1670  */
1671 static dladm_status_t
1672 print_link_topology(show_state_t *state, datalink_id_t linkid,
1673     datalink_class_t class, link_fields_buf_t *lbuf)
1674 {
1675 	uint32_t	flags = state->ls_flags;
1676 	dladm_status_t	status = DLADM_STATUS_OK;
1677 	char		tmpbuf[MAXLINKNAMELEN];
1678 
1679 	if (!state->ls_parseable)
1680 		(void) sprintf(lbuf->link_over, STR_UNDEF_VAL);
1681 	else
1682 		(void) sprintf(lbuf->link_over, "");
1683 
1684 	if (class == DATALINK_CLASS_VLAN) {
1685 		dladm_vlan_attr_t	vinfo;
1686 
1687 		status = dladm_vlan_info(linkid, &vinfo, flags);
1688 		if (status != DLADM_STATUS_OK)
1689 			goto done;
1690 		status = dladm_datalink_id2info(vinfo.dv_linkid, NULL, NULL,
1691 		    NULL, lbuf->link_over, sizeof (lbuf->link_over));
1692 		if (status != DLADM_STATUS_OK)
1693 			goto done;
1694 	} else if (class == DATALINK_CLASS_AGGR) {
1695 		dladm_aggr_grp_attr_t	ginfo;
1696 		int			i;
1697 
1698 		(void) sprintf(lbuf->link_over, "");
1699 
1700 		status = dladm_aggr_info(linkid, &ginfo, flags);
1701 		if (status != DLADM_STATUS_OK)
1702 			goto done;
1703 
1704 		if (ginfo.lg_nports == 0) {
1705 			status = DLADM_STATUS_BADVAL;
1706 			goto done;
1707 		}
1708 		for (i = 0; i < ginfo.lg_nports; i++) {
1709 			status = dladm_datalink_id2info(
1710 			    ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL,
1711 			    tmpbuf, sizeof (tmpbuf));
1712 			if (status != DLADM_STATUS_OK) {
1713 				free(ginfo.lg_ports);
1714 				goto done;
1715 			}
1716 			(void) strlcat(lbuf->link_over, tmpbuf,
1717 			    sizeof (lbuf->link_over));
1718 			if (i != (ginfo.lg_nports - 1)) {
1719 				(void) strlcat(lbuf->link_over, " ",
1720 				    sizeof (lbuf->link_over));
1721 			}
1722 		}
1723 		free(ginfo.lg_ports);
1724 	} else if (class == DATALINK_CLASS_VNIC) {
1725 		dladm_vnic_attr_sys_t	vinfo;
1726 
1727 		if ((status = dladm_vnic_info(linkid, &vinfo, flags)) !=
1728 		    DLADM_STATUS_OK || (status = dladm_datalink_id2info(
1729 		    vinfo.va_link_id, NULL, NULL, NULL, lbuf->link_over,
1730 		    sizeof (lbuf->link_over)) != DLADM_STATUS_OK)) {
1731 			goto done;
1732 		}
1733 	}
1734 done:
1735 	return (status);
1736 }
1737 
1738 static dladm_status_t
1739 print_link(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *lbuf)
1740 {
1741 	char			link[MAXLINKNAMELEN];
1742 	datalink_class_t	class;
1743 	uint_t			mtu;
1744 	uint32_t		flags;
1745 	dladm_status_t		status;
1746 
1747 	if ((status = dladm_datalink_id2info(linkid, &flags, &class, NULL,
1748 	    link, sizeof (link))) != DLADM_STATUS_OK) {
1749 		goto done;
1750 	}
1751 
1752 	if (!(state->ls_flags & flags)) {
1753 		status = DLADM_STATUS_NOTFOUND;
1754 		goto done;
1755 	}
1756 
1757 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
1758 		dladm_attr_t	dlattr;
1759 
1760 		if (class == DATALINK_CLASS_PHYS) {
1761 			dladm_phys_attr_t	dpa;
1762 			dlpi_handle_t		dh;
1763 			dlpi_info_t		dlinfo;
1764 
1765 			if ((status = dladm_phys_info(linkid, &dpa,
1766 			    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
1767 				goto done;
1768 			}
1769 
1770 			if (!dpa.dp_novanity)
1771 				goto link_mtu;
1772 
1773 			/*
1774 			 * This is a physical link that does not have
1775 			 * vanity naming support.
1776 			 */
1777 			if (dlpi_open(dpa.dp_dev, &dh, DLPI_DEVONLY) !=
1778 			    DLPI_SUCCESS) {
1779 				status = DLADM_STATUS_NOTFOUND;
1780 				goto done;
1781 			}
1782 
1783 			if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
1784 				dlpi_close(dh);
1785 				status = DLADM_STATUS_BADARG;
1786 				goto done;
1787 			}
1788 
1789 			dlpi_close(dh);
1790 			mtu = dlinfo.di_max_sdu;
1791 		} else {
1792 link_mtu:
1793 			status = dladm_info(linkid, &dlattr);
1794 			if (status != DLADM_STATUS_OK)
1795 				goto done;
1796 			mtu = dlattr.da_max_sdu;
1797 		}
1798 	}
1799 
1800 	(void) snprintf(lbuf->link_name, sizeof (lbuf->link_name),
1801 	    "%s", link);
1802 	(void) dladm_class2str(class, lbuf->link_class);
1803 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
1804 		(void) snprintf(lbuf->link_mtu, sizeof (lbuf->link_mtu),
1805 		    "%u", mtu);
1806 		(void) get_linkstate(link, B_TRUE, lbuf->link_state);
1807 	}
1808 
1809 	status = print_link_topology(state, linkid, class, lbuf);
1810 	if (status != DLADM_STATUS_OK)
1811 		goto done;
1812 
1813 done:
1814 	return (status);
1815 }
1816 
1817 
1818 static int
1819 show_link(datalink_id_t linkid, void *arg)
1820 {
1821 	show_state_t		*state = (show_state_t *)arg;
1822 	dladm_status_t		status;
1823 	link_fields_buf_t	lbuf;
1824 
1825 	/*
1826 	 * first get all the link attributes into lbuf;
1827 	 */
1828 	status = print_link(state, linkid, &lbuf);
1829 
1830 	if (status != DLADM_STATUS_OK)
1831 		goto done;
1832 
1833 	if (!state->ls_parseable && !state->ls_printheader) {
1834 		print_header(&state->ls_print);
1835 		state->ls_printheader = B_TRUE;
1836 	}
1837 
1838 	dladm_print_output(&state->ls_print, state->ls_parseable,
1839 	    dladm_print_field, (void *)&lbuf);
1840 
1841 done:
1842 	state->ls_status = status;
1843 	return (DLADM_WALK_CONTINUE);
1844 }
1845 
1846 static int
1847 show_link_stats(datalink_id_t linkid, void *arg)
1848 {
1849 	char link[DLPI_LINKNAME_MAX];
1850 	datalink_class_t class;
1851 	show_state_t *state = (show_state_t *)arg;
1852 	pktsum_t stats, diff_stats;
1853 	dladm_phys_attr_t dpa;
1854 
1855 	if (state->ls_firstonly) {
1856 		if (state->ls_donefirst)
1857 			return (DLADM_WALK_CONTINUE);
1858 		state->ls_donefirst = B_TRUE;
1859 	} else {
1860 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
1861 	}
1862 
1863 	if (dladm_datalink_id2info(linkid, NULL, &class, NULL, link,
1864 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1865 		return (DLADM_WALK_CONTINUE);
1866 	}
1867 
1868 	if (class == DATALINK_CLASS_PHYS) {
1869 		if (dladm_phys_info(linkid, &dpa, DLADM_OPT_ACTIVE) !=
1870 		    DLADM_STATUS_OK) {
1871 			return (DLADM_WALK_CONTINUE);
1872 		}
1873 		if (dpa.dp_novanity)
1874 			get_mac_stats(dpa.dp_dev, &stats);
1875 		else
1876 			get_link_stats(link, &stats);
1877 	} else {
1878 		get_link_stats(link, &stats);
1879 	}
1880 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
1881 
1882 	(void) printf("%-12s", link);
1883 	(void) printf("%-10llu", diff_stats.ipackets);
1884 	(void) printf("%-12llu", diff_stats.rbytes);
1885 	(void) printf("%-8u", diff_stats.ierrors);
1886 	(void) printf("%-10llu", diff_stats.opackets);
1887 	(void) printf("%-12llu", diff_stats.obytes);
1888 	(void) printf("%-8u\n", diff_stats.oerrors);
1889 
1890 	state->ls_prevstats = stats;
1891 	return (DLADM_WALK_CONTINUE);
1892 }
1893 
1894 
1895 static dladm_status_t
1896 print_aggr_info(show_grp_state_t *state, const char *link,
1897     dladm_aggr_grp_attr_t *ginfop)
1898 {
1899 	char			addr_str[ETHERADDRL * 3];
1900 	laggr_fields_buf_t	lbuf;
1901 
1902 	(void) snprintf(lbuf.laggr_name, sizeof (lbuf.laggr_name),
1903 	    "%s", link);
1904 
1905 	(void) dladm_aggr_policy2str(ginfop->lg_policy,
1906 	    lbuf.laggr_policy);
1907 
1908 	if (ginfop->lg_mac_fixed) {
1909 		(void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str);
1910 		(void) snprintf(lbuf.laggr_addrpolicy,
1911 		    sizeof (lbuf.laggr_addrpolicy), "fixed (%s)", addr_str);
1912 	} else {
1913 		(void) snprintf(lbuf.laggr_addrpolicy,
1914 		    sizeof (lbuf.laggr_addrpolicy), "auto");
1915 	}
1916 
1917 
1918 	(void) dladm_aggr_lacpmode2str(ginfop->lg_lacp_mode,
1919 	    lbuf.laggr_lacpactivity);
1920 	(void) dladm_aggr_lacptimer2str(ginfop->lg_lacp_timer,
1921 	    lbuf.laggr_lacptimer);
1922 	(void) snprintf(lbuf.laggr_flags, sizeof (lbuf.laggr_flags), "%c----",
1923 	    ginfop->lg_force ? 'f' : '-');
1924 
1925 	if (!state->gs_parseable && !state->gs_printheader) {
1926 		print_header(&state->gs_print);
1927 		state->gs_printheader = B_TRUE;
1928 	}
1929 
1930 	dladm_print_output(&state->gs_print, state->gs_parseable,
1931 	    dladm_print_field, (void *)&lbuf);
1932 
1933 	return (DLADM_STATUS_OK);
1934 }
1935 
1936 static char *
1937 print_xaggr_callback(print_field_t *pf, void *arg)
1938 {
1939 	const laggr_args_t 	*l = arg;
1940 	int 			portnum;
1941 	static char 		buf[DLADM_STRSIZE];
1942 	boolean_t		is_port = (l->laggr_lport >= 0);
1943 	dladm_aggr_port_attr_t *portp;
1944 	dladm_phys_attr_t	dpa;
1945 	dladm_status_t		*stat, status;
1946 
1947 	stat = l->laggr_status;
1948 	*stat = DLADM_STATUS_OK;
1949 
1950 	if (is_port) {
1951 		portnum = l->laggr_lport;
1952 		portp = &(l->laggr_ginfop->lg_ports[portnum]);
1953 		if ((status = dladm_datalink_id2info(portp->lp_linkid,
1954 		    NULL, NULL, NULL, buf, sizeof (buf))) !=
1955 		    DLADM_STATUS_OK) {
1956 			goto err;
1957 		}
1958 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
1959 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
1960 			goto err;
1961 		}
1962 	}
1963 
1964 	switch (pf->pf_index) {
1965 	case AGGR_X_LINK:
1966 		(void) snprintf(buf, sizeof (buf), "%s",
1967 		    (is_port && !l->laggr_parseable ? " " : l->laggr_link));
1968 		break;
1969 	case AGGR_X_PORT:
1970 		if (is_port)
1971 			break;
1972 		return ("");
1973 		break;
1974 
1975 	case AGGR_X_SPEED:
1976 		if (is_port) {
1977 			(void) snprintf(buf, sizeof (buf), "%uMb",
1978 			    (uint_t)((get_ifspeed(dpa.dp_dev,
1979 			    B_FALSE)) / 1000000ull));
1980 		} else {
1981 			(void) snprintf(buf, sizeof (buf), "%uMb",
1982 			    (uint_t)((get_ifspeed(l->laggr_link,
1983 			    B_TRUE)) / 1000000ull));
1984 		}
1985 		break;
1986 
1987 	case AGGR_X_DUPLEX:
1988 		if (is_port)
1989 			(void) get_linkduplex(dpa.dp_dev, B_FALSE, buf);
1990 		else
1991 			(void) get_linkduplex(l->laggr_link, B_TRUE, buf);
1992 		break;
1993 
1994 	case AGGR_X_STATE:
1995 		if (is_port)
1996 			(void) get_linkstate(dpa.dp_dev,  B_FALSE, buf);
1997 		else
1998 			(void) get_linkstate(l->laggr_link, B_TRUE, buf);
1999 		break;
2000 	case AGGR_X_ADDRESS:
2001 		(void) dladm_aggr_macaddr2str(
2002 		    (is_port ? portp->lp_mac : l->laggr_ginfop->lg_mac),
2003 		    buf);
2004 		break;
2005 	case AGGR_X_PORTSTATE:
2006 		if (is_port)
2007 			(void) dladm_aggr_portstate2str(
2008 			    portp->lp_state, buf);
2009 		else
2010 			return ("");
2011 		break;
2012 	}
2013 	return (buf);
2014 
2015 err:
2016 	*stat = status;
2017 	buf[0] = '\0';
2018 	return (buf);
2019 }
2020 
2021 static dladm_status_t
2022 print_aggr_extended(show_grp_state_t *state, const char *link,
2023     dladm_aggr_grp_attr_t *ginfop)
2024 {
2025 	int			i;
2026 	dladm_status_t		status;
2027 	laggr_args_t		largs;
2028 
2029 	if (!state->gs_parseable && !state->gs_printheader) {
2030 		print_header(&state->gs_print);
2031 		state->gs_printheader = B_TRUE;
2032 	}
2033 
2034 	largs.laggr_lport = -1;
2035 	largs.laggr_link = link;
2036 	largs.laggr_ginfop = ginfop;
2037 	largs.laggr_status = &status;
2038 	largs.laggr_parseable = state->gs_parseable;
2039 
2040 	dladm_print_output(&state->gs_print, state->gs_parseable,
2041 	    print_xaggr_callback, &largs);
2042 
2043 	if (status != DLADM_STATUS_OK)
2044 		goto done;
2045 
2046 	for (i = 0; i < ginfop->lg_nports; i++) {
2047 		largs.laggr_lport = i;
2048 		dladm_print_output(&state->gs_print, state->gs_parseable,
2049 		    print_xaggr_callback, &largs);
2050 		if (status != DLADM_STATUS_OK)
2051 			goto done;
2052 	}
2053 
2054 	status = DLADM_STATUS_OK;
2055 done:
2056 	return (status);
2057 }
2058 
2059 
2060 static char *
2061 print_lacp_callback(print_field_t *pf, void *arg)
2062 {
2063 	const laggr_args_t	*l = arg;
2064 	int			portnum;
2065 	static char		buf[DLADM_STRSIZE];
2066 	boolean_t		is_port = (l->laggr_lport >= 0);
2067 	dladm_aggr_port_attr_t	*portp;
2068 	dladm_status_t		*stat, status;
2069 	aggr_lacp_state_t	*lstate;
2070 
2071 	if (!is_port) {
2072 		return (NULL); /* cannot happen! */
2073 	}
2074 
2075 	stat = l->laggr_status;
2076 
2077 	portnum = l->laggr_lport;
2078 	portp = &(l->laggr_ginfop->lg_ports[portnum]);
2079 	if ((status = dladm_datalink_id2info(portp->lp_linkid,
2080 	    NULL, NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) {
2081 			goto err;
2082 	}
2083 	lstate = &(portp->lp_lacp_state);
2084 
2085 	switch (pf->pf_index) {
2086 	case AGGR_L_LINK:
2087 		(void) snprintf(buf, sizeof (buf), "%s",
2088 		    (portnum > 0 ? "" : l->laggr_link));
2089 		break;
2090 
2091 	case AGGR_L_PORT:
2092 		break;
2093 
2094 	case AGGR_L_AGGREGATABLE:
2095 		(void) snprintf(buf, sizeof (buf), "%s",
2096 		    (lstate->bit.aggregation ? "yes" : "no"));
2097 		break;
2098 
2099 	case AGGR_L_SYNC:
2100 		(void) snprintf(buf, sizeof (buf), "%s",
2101 		    (lstate->bit.sync ? "yes" : "no"));
2102 		break;
2103 
2104 	case AGGR_L_COLL:
2105 		(void) snprintf(buf, sizeof (buf), "%s",
2106 		    (lstate->bit.collecting ? "yes" : "no"));
2107 		break;
2108 
2109 	case AGGR_L_DIST:
2110 		(void) snprintf(buf, sizeof (buf), "%s",
2111 		    (lstate->bit.distributing ? "yes" : "no"));
2112 		break;
2113 
2114 	case AGGR_L_DEFAULTED:
2115 		(void) snprintf(buf, sizeof (buf), "%s",
2116 		    (lstate->bit.defaulted ? "yes" : "no"));
2117 		break;
2118 
2119 	case AGGR_L_EXPIRED:
2120 		(void) snprintf(buf, sizeof (buf), "%s",
2121 		    (lstate->bit.expired ? "yes" : "no"));
2122 		break;
2123 	}
2124 
2125 	*stat = DLADM_STATUS_OK;
2126 	return (buf);
2127 
2128 err:
2129 	*stat = status;
2130 	buf[0] = '\0';
2131 	return (buf);
2132 }
2133 
2134 static dladm_status_t
2135 print_aggr_lacp(show_grp_state_t *state, const char *link,
2136     dladm_aggr_grp_attr_t *ginfop)
2137 {
2138 	int		i;
2139 	dladm_status_t	status;
2140 	laggr_args_t	largs;
2141 
2142 	if (!state->gs_parseable && !state->gs_printheader) {
2143 		print_header(&state->gs_print);
2144 		state->gs_printheader = B_TRUE;
2145 	}
2146 
2147 	largs.laggr_link = link;
2148 	largs.laggr_ginfop = ginfop;
2149 	largs.laggr_status = &status;
2150 
2151 	for (i = 0; i < ginfop->lg_nports; i++) {
2152 		largs.laggr_lport = i;
2153 		dladm_print_output(&state->gs_print, state->gs_parseable,
2154 		    print_lacp_callback, &largs);
2155 		if (status != DLADM_STATUS_OK)
2156 			goto done;
2157 	}
2158 
2159 	status = DLADM_STATUS_OK;
2160 done:
2161 	return (status);
2162 }
2163 
2164 static char *
2165 print_aggr_stats_callback(print_field_t *pf, void *arg)
2166 {
2167 	const laggr_args_t	*l = arg;
2168 	int 			portnum;
2169 	static char		buf[DLADM_STRSIZE];
2170 	boolean_t		is_port = (l->laggr_lport >= 0);
2171 	dladm_aggr_port_attr_t	*portp;
2172 	dladm_phys_attr_t	dpa;
2173 	dladm_status_t		*stat, status;
2174 	pktsum_t		port_stat, diff_stats;
2175 
2176 	stat = l->laggr_status;
2177 	*stat = DLADM_STATUS_OK;
2178 
2179 	if (is_port) {
2180 		portnum = l->laggr_lport;
2181 		portp = &(l->laggr_ginfop->lg_ports[portnum]);
2182 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
2183 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
2184 			goto err;
2185 		}
2186 
2187 		get_mac_stats(dpa.dp_dev, &port_stat);
2188 
2189 		if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL,
2190 		    NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) {
2191 			goto err;
2192 		}
2193 
2194 		stats_diff(&diff_stats, &port_stat, l->laggr_prevstats);
2195 	}
2196 
2197 	switch (pf->pf_index) {
2198 	case AGGR_S_LINK:
2199 		(void) snprintf(buf, sizeof (buf), "%s",
2200 		    (is_port ? "" : l->laggr_link));
2201 		break;
2202 	case AGGR_S_PORT:
2203 		if (is_port)
2204 			break;
2205 		return ("");
2206 		break;
2207 
2208 	case AGGR_S_IPKTS:
2209 		if (is_port) {
2210 			(void) snprintf(buf, sizeof (buf), "%llu",
2211 			    diff_stats.ipackets);
2212 		} else {
2213 			(void) snprintf(buf, sizeof (buf), "%llu",
2214 			    l->laggr_pktsumtot->ipackets);
2215 		}
2216 		break;
2217 
2218 	case AGGR_S_RBYTES:
2219 		if (is_port) {
2220 			(void) snprintf(buf, sizeof (buf), "%llu",
2221 			    diff_stats.rbytes);
2222 		} else {
2223 			(void) snprintf(buf, sizeof (buf), "%llu",
2224 			    l->laggr_pktsumtot->rbytes);
2225 		}
2226 		break;
2227 
2228 	case AGGR_S_OPKTS:
2229 		if (is_port) {
2230 			(void) snprintf(buf, sizeof (buf), "%llu",
2231 			    diff_stats.opackets);
2232 		} else {
2233 			(void) snprintf(buf, sizeof (buf), "%llu",
2234 			    l->laggr_pktsumtot->opackets);
2235 		}
2236 		break;
2237 	case AGGR_S_OBYTES:
2238 		if (is_port) {
2239 			(void) snprintf(buf, sizeof (buf), "%llu",
2240 			    diff_stats.obytes);
2241 		} else {
2242 			(void) snprintf(buf, sizeof (buf), "%llu",
2243 			    l->laggr_pktsumtot->obytes);
2244 
2245 		}
2246 		break;
2247 
2248 	case AGGR_S_IPKTDIST:
2249 		if (is_port) {
2250 			(void) snprintf(buf, sizeof (buf), "%-6.1f",
2251 			    (double)diff_stats.opackets/
2252 			    (double)l->laggr_pktsumtot->ipackets * 100);
2253 		} else {
2254 			return ("");
2255 		}
2256 		break;
2257 	case AGGR_S_OPKTDIST:
2258 		if (is_port) {
2259 			(void) snprintf(buf, sizeof (buf), "%-6.1f",
2260 			    (double)diff_stats.opackets/
2261 			    (double)l->laggr_pktsumtot->opackets * 100);
2262 		} else {
2263 			return ("");
2264 		}
2265 		break;
2266 	}
2267 	return (buf);
2268 
2269 err:
2270 	*stat = status;
2271 	buf[0] = '\0';
2272 	return (buf);
2273 }
2274 
2275 static dladm_status_t
2276 print_aggr_stats(show_grp_state_t *state, const char *link,
2277     dladm_aggr_grp_attr_t *ginfop)
2278 {
2279 	dladm_phys_attr_t	dpa;
2280 	dladm_aggr_port_attr_t	*portp;
2281 	pktsum_t		pktsumtot, port_stat;
2282 	dladm_status_t		status;
2283 	int			i;
2284 	laggr_args_t		largs;
2285 
2286 	/* sum the ports statistics */
2287 	bzero(&pktsumtot, sizeof (pktsumtot));
2288 
2289 	for (i = 0; i < ginfop->lg_nports; i++) {
2290 
2291 		portp = &(ginfop->lg_ports[i]);
2292 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
2293 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
2294 			goto done;
2295 		}
2296 
2297 		get_mac_stats(dpa.dp_dev, &port_stat);
2298 		stats_total(&pktsumtot, &port_stat, &state->gs_prevstats[i]);
2299 	}
2300 
2301 	if (!state->gs_parseable && !state->gs_printheader) {
2302 		print_header(&state->gs_print);
2303 		state->gs_printheader = B_TRUE;
2304 	}
2305 
2306 	largs.laggr_lport = -1;
2307 	largs.laggr_link = link;
2308 	largs.laggr_ginfop = ginfop;
2309 	largs.laggr_status = &status;
2310 	largs.laggr_pktsumtot = &pktsumtot;
2311 
2312 	dladm_print_output(&state->gs_print, state->gs_parseable,
2313 	    print_aggr_stats_callback, &largs);
2314 
2315 	if (status != DLADM_STATUS_OK)
2316 		goto done;
2317 
2318 	for (i = 0; i < ginfop->lg_nports; i++) {
2319 		largs.laggr_lport = i;
2320 		largs.laggr_prevstats = &state->gs_prevstats[i];
2321 		dladm_print_output(&state->gs_print, state->gs_parseable,
2322 		    print_aggr_stats_callback, &largs);
2323 		if (status != DLADM_STATUS_OK)
2324 			goto done;
2325 	}
2326 
2327 	status = DLADM_STATUS_OK;
2328 done:
2329 	return (status);
2330 }
2331 
2332 static dladm_status_t
2333 print_aggr(show_grp_state_t *state, datalink_id_t linkid)
2334 {
2335 	char			link[MAXLINKNAMELEN];
2336 	dladm_aggr_grp_attr_t	ginfo;
2337 	uint32_t		flags;
2338 	dladm_status_t		status;
2339 
2340 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link,
2341 	    MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
2342 		return (status);
2343 	}
2344 
2345 	if (!(state->gs_flags & flags))
2346 		return (DLADM_STATUS_NOTFOUND);
2347 
2348 	status = dladm_aggr_info(linkid, &ginfo, state->gs_flags);
2349 	if (status != DLADM_STATUS_OK)
2350 		return (status);
2351 
2352 	if (state->gs_lacp)
2353 		status = print_aggr_lacp(state, link, &ginfo);
2354 	else if (state->gs_extended)
2355 		status = print_aggr_extended(state, link, &ginfo);
2356 	else if (state->gs_stats)
2357 		status = print_aggr_stats(state, link, &ginfo);
2358 	else {
2359 		status = print_aggr_info(state, link, &ginfo);
2360 	}
2361 
2362 done:
2363 	free(ginfo.lg_ports);
2364 	return (status);
2365 }
2366 
2367 static int
2368 show_aggr(datalink_id_t linkid, void *arg)
2369 {
2370 	show_grp_state_t	*state = arg;
2371 	dladm_status_t		status;
2372 
2373 	status = print_aggr(state, linkid);
2374 	if (status != DLADM_STATUS_OK)
2375 		goto done;
2376 
2377 done:
2378 	state->gs_status = status;
2379 	return (DLADM_WALK_CONTINUE);
2380 }
2381 
2382 static char *
2383 print_dev(print_field_t *pf, void *arg)
2384 {
2385 	const char *dev = arg;
2386 	static char buf[DLADM_STRSIZE];
2387 
2388 	switch (pf->pf_index) {
2389 	case DEV_LINK:
2390 		(void) snprintf(buf, sizeof (buf), "%s", dev);
2391 		break;
2392 	case DEV_STATE:
2393 		(void) get_linkstate(dev, B_FALSE, buf);
2394 		break;
2395 	case DEV_SPEED:
2396 		(void) snprintf(buf, sizeof (buf), "%uMb",
2397 		    (unsigned int)(get_ifspeed(dev, B_FALSE) / 1000000ull));
2398 		break;
2399 	case DEV_DUPLEX:
2400 		(void) get_linkduplex(dev, B_FALSE, buf);
2401 		break;
2402 	default:
2403 		die("invalid index '%d'", pf->pf_index);
2404 		break;
2405 	}
2406 	return (buf);
2407 }
2408 
2409 static int
2410 show_dev(const char *dev, void *arg)
2411 {
2412 	show_state_t	*state = arg;
2413 
2414 	if (!state->ls_parseable && !state->ls_printheader) {
2415 		print_header(&state->ls_print);
2416 		state->ls_printheader = B_TRUE;
2417 	}
2418 
2419 	dladm_print_output(&state->ls_print, state->ls_parseable,
2420 	    print_dev, (void *)dev);
2421 
2422 	return (DLADM_WALK_CONTINUE);
2423 }
2424 
2425 static char *
2426 print_dev_stats(print_field_t *pf, void *arg)
2427 {
2428 	dev_args_t *dargs = arg;
2429 	pktsum_t *diff_stats = dargs->devs_psum;
2430 	static char buf[DLADM_STRSIZE];
2431 
2432 	switch (pf->pf_index) {
2433 	case DEVS_LINK:
2434 		(void) snprintf(buf, sizeof (buf), "%s", dargs->devs_link);
2435 		break;
2436 	case DEVS_IPKTS:
2437 		(void) snprintf(buf, sizeof (buf), "%llu",
2438 		    diff_stats->ipackets);
2439 		break;
2440 	case DEVS_RBYTES:
2441 		(void) snprintf(buf, sizeof (buf), "%llu",
2442 		    diff_stats->rbytes);
2443 		break;
2444 	case DEVS_IERRORS:
2445 		(void) snprintf(buf, sizeof (buf), "%u",
2446 		    diff_stats->ierrors);
2447 		break;
2448 	case DEVS_OPKTS:
2449 		(void) snprintf(buf, sizeof (buf), "%llu",
2450 		    diff_stats->opackets);
2451 		break;
2452 	case DEVS_OBYTES:
2453 		(void) snprintf(buf, sizeof (buf), "%llu",
2454 		    diff_stats->obytes);
2455 		break;
2456 	case DEVS_OERRORS:
2457 		(void) snprintf(buf, sizeof (buf), "%u",
2458 		    diff_stats->oerrors);
2459 		break;
2460 	default:
2461 		die("invalid input");
2462 		break;
2463 	}
2464 	return (buf);
2465 }
2466 
2467 static int
2468 show_dev_stats(const char *dev, void *arg)
2469 {
2470 	show_state_t *state = arg;
2471 	pktsum_t stats, diff_stats;
2472 	dev_args_t dargs;
2473 
2474 	if (state->ls_firstonly) {
2475 		if (state->ls_donefirst)
2476 			return (DLADM_WALK_CONTINUE);
2477 		state->ls_donefirst = B_TRUE;
2478 	} else {
2479 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
2480 	}
2481 
2482 	get_mac_stats(dev, &stats);
2483 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
2484 
2485 	dargs.devs_link = (char *)dev;
2486 	dargs.devs_psum = &diff_stats;
2487 	dladm_print_output(&state->ls_print, state->ls_parseable,
2488 	    print_dev_stats, &dargs);
2489 
2490 	state->ls_prevstats = stats;
2491 	return (DLADM_WALK_CONTINUE);
2492 }
2493 
2494 static void
2495 do_show_link(int argc, char *argv[], const char *use)
2496 {
2497 	int		option;
2498 	boolean_t	s_arg = B_FALSE;
2499 	boolean_t	i_arg = B_FALSE;
2500 	uint32_t	flags = DLADM_OPT_ACTIVE;
2501 	boolean_t	p_arg = B_FALSE;
2502 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2503 	int		interval = 0;
2504 	show_state_t	state;
2505 	dladm_status_t	status;
2506 	boolean_t	o_arg = B_FALSE;
2507 	char		*fields_str = NULL;
2508 	print_field_t	**fields;
2509 	uint_t		nfields;
2510 	char		*all_active_fields = "link,class,mtu,state,over";
2511 	char		*all_inactive_fields = "link,class,over";
2512 
2513 	bzero(&state, sizeof (state));
2514 
2515 	opterr = 0;
2516 	while ((option = getopt_long(argc, argv, ":pPsi:o:",
2517 	    show_lopts, NULL)) != -1) {
2518 		switch (option) {
2519 		case 'p':
2520 			if (p_arg)
2521 				die_optdup(option);
2522 
2523 			p_arg = B_TRUE;
2524 			break;
2525 		case 's':
2526 			if (s_arg)
2527 				die_optdup(option);
2528 
2529 			s_arg = B_TRUE;
2530 			break;
2531 		case 'P':
2532 			if (flags != DLADM_OPT_ACTIVE)
2533 				die_optdup(option);
2534 
2535 			flags = DLADM_OPT_PERSIST;
2536 			break;
2537 		case 'o':
2538 			o_arg = B_TRUE;
2539 			fields_str = optarg;
2540 			break;
2541 		case 'i':
2542 			if (i_arg)
2543 				die_optdup(option);
2544 
2545 			i_arg = B_TRUE;
2546 			if (!str2int(optarg, &interval) || interval == 0)
2547 				die("invalid interval value '%s'", optarg);
2548 			break;
2549 		default:
2550 			die_opterr(optopt, option, use);
2551 			break;
2552 		}
2553 	}
2554 
2555 	if (i_arg && !s_arg)
2556 		die("the option -i can be used only with -s");
2557 
2558 	if (s_arg && (p_arg || flags != DLADM_OPT_ACTIVE))
2559 		die("the option -%c cannot be used with -s", p_arg ? 'p' : 'P');
2560 
2561 	/* get link name (optional last argument) */
2562 	if (optind == (argc-1)) {
2563 		uint32_t	f;
2564 
2565 		if ((status = dladm_name2info(argv[optind], &linkid, &f,
2566 		    NULL, NULL)) != DLADM_STATUS_OK) {
2567 			die_dlerr(status, "link %s is not valid", argv[optind]);
2568 		}
2569 
2570 		if (!(f & flags)) {
2571 			die_dlerr(DLADM_STATUS_BADARG, "link %s is %s",
2572 			    argv[optind], flags == DLADM_OPT_PERSIST ?
2573 			    "a temporary link" : "temporarily removed");
2574 		}
2575 	} else if (optind != argc) {
2576 		usage();
2577 	}
2578 
2579 	if (s_arg) {
2580 		link_stats(linkid, interval);
2581 		return;
2582 	}
2583 
2584 	state.ls_parseable = p_arg;
2585 	state.ls_flags = flags;
2586 	state.ls_donefirst = B_FALSE;
2587 
2588 	if (p_arg && !o_arg)
2589 		die("-p requires -o");
2590 
2591 	if (p_arg && strcasecmp(fields_str, "all") == 0)
2592 		die("\"-o all\" is invalid with -p");
2593 
2594 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
2595 		if (state.ls_flags & DLADM_OPT_ACTIVE)
2596 			fields_str = all_active_fields;
2597 		else
2598 			fields_str = all_inactive_fields;
2599 	}
2600 
2601 
2602 	fields = parse_output_fields(fields_str, link_fields, DEV_LINK_FIELDS,
2603 	    CMD_TYPE_ANY, &nfields);
2604 
2605 	if (fields == NULL)
2606 		die("invalid field(s) specified");
2607 
2608 	state.ls_print.ps_fields = fields;
2609 	state.ls_print.ps_nfields = nfields;
2610 
2611 	if (linkid == DATALINK_ALL_LINKID) {
2612 		(void) dladm_walk_datalink_id(show_link, &state,
2613 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
2614 	} else {
2615 		(void) show_link(linkid, &state);
2616 		if (state.ls_status != DLADM_STATUS_OK) {
2617 			die_dlerr(state.ls_status, "failed to show link %s",
2618 			    argv[optind]);
2619 		}
2620 	}
2621 }
2622 
2623 static void
2624 do_show_aggr(int argc, char *argv[], const char *use)
2625 {
2626 	boolean_t		L_arg = B_FALSE;
2627 	boolean_t		s_arg = B_FALSE;
2628 	boolean_t		i_arg = B_FALSE;
2629 	boolean_t		p_arg = B_FALSE;
2630 	boolean_t		x_arg = B_FALSE;
2631 	show_grp_state_t	state;
2632 	uint32_t		flags = DLADM_OPT_ACTIVE;
2633 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
2634 	int			option;
2635 	int			interval = 0;
2636 	int			key;
2637 	dladm_status_t		status;
2638 	boolean_t	o_arg = B_FALSE;
2639 	char		*fields_str = NULL;
2640 	print_field_t   **fields;
2641 	uint_t		nfields;
2642 	char		*all_fields =
2643 	    "link,policy,addrpolicy,lacpactivity,lacptimer,flags";
2644 	char		*all_lacp_fields =
2645 	    "link,port,aggregatable,sync,coll,dist,defaulted,expired";
2646 	char		*all_stats_fields =
2647 	    "link,port,ipackets,rbytes,opackets,obytes,ipktdist,opktdist";
2648 	char		*all_extended_fields =
2649 	    "link,port,speed,duplex,state,address,portstate";
2650 	print_field_t		*pf;
2651 	int			pfmax;
2652 
2653 	bzero(&state, sizeof (state));
2654 
2655 	opterr = 0;
2656 	while ((option = getopt_long(argc, argv, ":LpPxsi:o:",
2657 	    show_lopts, NULL)) != -1) {
2658 		switch (option) {
2659 		case 'L':
2660 			if (L_arg)
2661 				die_optdup(option);
2662 
2663 			L_arg = B_TRUE;
2664 			break;
2665 		case 'p':
2666 			if (p_arg)
2667 				die_optdup(option);
2668 
2669 			p_arg = B_TRUE;
2670 			break;
2671 		case 'x':
2672 			if (x_arg)
2673 				die_optdup(option);
2674 
2675 			x_arg = B_TRUE;
2676 			break;
2677 		case 'P':
2678 			if (flags != DLADM_OPT_ACTIVE)
2679 				die_optdup(option);
2680 
2681 			flags = DLADM_OPT_PERSIST;
2682 			break;
2683 		case 's':
2684 			if (s_arg)
2685 				die_optdup(option);
2686 
2687 			s_arg = B_TRUE;
2688 			break;
2689 		case 'o':
2690 			o_arg = B_TRUE;
2691 			fields_str = optarg;
2692 			break;
2693 		case 'i':
2694 			if (i_arg)
2695 				die_optdup(option);
2696 
2697 			i_arg = B_TRUE;
2698 			if (!str2int(optarg, &interval) || interval == 0)
2699 				die("invalid interval value '%s'", optarg);
2700 			break;
2701 		default:
2702 			die_opterr(optopt, option, use);
2703 			break;
2704 		}
2705 	}
2706 
2707 	if (p_arg && !o_arg)
2708 		die("-p requires -o");
2709 
2710 	if (p_arg && strcasecmp(fields_str, "all") == 0)
2711 		die("\"-o all\" is invalid with -p");
2712 
2713 	if (i_arg && !s_arg)
2714 		die("the option -i can be used only with -s");
2715 
2716 	if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) {
2717 		die("the option -%c cannot be used with -s",
2718 		    L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P')));
2719 	}
2720 
2721 	if (L_arg && flags != DLADM_OPT_ACTIVE)
2722 		die("the option -P cannot be used with -L");
2723 
2724 	if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE))
2725 		die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P');
2726 
2727 	/* get aggregation key or aggrname (optional last argument) */
2728 	if (optind == (argc-1)) {
2729 		if (!str2int(argv[optind], &key)) {
2730 			status = dladm_name2info(argv[optind], &linkid, NULL,
2731 			    NULL, NULL);
2732 		} else {
2733 			status = dladm_key2linkid((uint16_t)key,
2734 			    &linkid, DLADM_OPT_ACTIVE);
2735 		}
2736 
2737 		if (status != DLADM_STATUS_OK)
2738 			die("non-existent aggregation '%s'", argv[optind]);
2739 
2740 	} else if (optind != argc) {
2741 		usage();
2742 	}
2743 
2744 	bzero(&state, sizeof (state));
2745 	state.gs_lacp = L_arg;
2746 	state.gs_stats = s_arg;
2747 	state.gs_flags = flags;
2748 	state.gs_parseable = p_arg;
2749 	state.gs_extended = x_arg;
2750 
2751 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
2752 		if (state.gs_lacp)
2753 			fields_str = all_lacp_fields;
2754 		else if (state.gs_stats)
2755 			fields_str = all_stats_fields;
2756 		else if (state.gs_extended)
2757 			fields_str = all_extended_fields;
2758 		else
2759 			fields_str = all_fields;
2760 	}
2761 
2762 	if (state.gs_lacp) {
2763 		pf = aggr_l_fields;
2764 		pfmax = AGGR_L_MAX_FIELDS;
2765 	} else if (state.gs_stats) {
2766 		pf = aggr_s_fields;
2767 		pfmax = AGGR_S_MAX_FIELDS;
2768 	} else if (state.gs_extended) {
2769 		pf = aggr_x_fields;
2770 		pfmax = AGGR_X_MAX_FIELDS;
2771 	} else {
2772 		pf = laggr_fields;
2773 		pfmax = LAGGR_MAX_FIELDS;
2774 	}
2775 	fields = parse_output_fields(fields_str, pf, pfmax, CMD_TYPE_ANY,
2776 	    &nfields);
2777 
2778 	if (fields == NULL) {
2779 		die("invalid field(s) specified");
2780 		return;
2781 	}
2782 
2783 	state.gs_print.ps_fields = fields;
2784 	state.gs_print.ps_nfields = nfields;
2785 
2786 	if (s_arg) {
2787 		aggr_stats(linkid, &state, interval);
2788 		return;
2789 	}
2790 
2791 	if (linkid == DATALINK_ALL_LINKID) {
2792 		(void) dladm_walk_datalink_id(show_aggr, &state,
2793 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
2794 	} else {
2795 		(void) show_aggr(linkid, &state);
2796 		if (state.gs_status != DLADM_STATUS_OK) {
2797 			die_dlerr(state.gs_status, "failed to show aggr %s",
2798 			    argv[optind]);
2799 		}
2800 	}
2801 }
2802 
2803 static void
2804 do_show_dev(int argc, char *argv[], const char *use)
2805 {
2806 	int		option;
2807 	char		*dev = NULL;
2808 	boolean_t	s_arg = B_FALSE;
2809 	boolean_t	i_arg = B_FALSE;
2810 	boolean_t	o_arg = B_FALSE;
2811 	boolean_t	p_arg = B_FALSE;
2812 	datalink_id_t	linkid;
2813 	int		interval = 0;
2814 	show_state_t	state;
2815 	char		*fields_str = NULL;
2816 	print_field_t	**fields;
2817 	uint_t		nfields;
2818 	char		*all_fields = "link,state,speed,duplex";
2819 	static char	*allstat_fields =
2820 	    "link,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
2821 
2822 	bzero(&state, sizeof (state));
2823 	fields_str = all_fields;
2824 
2825 	opterr = 0;
2826 	while ((option = getopt_long(argc, argv, ":psi:o:",
2827 	    show_lopts, NULL)) != -1) {
2828 		switch (option) {
2829 		case 'p':
2830 			if (p_arg)
2831 				die_optdup(option);
2832 
2833 			p_arg = B_TRUE;
2834 			break;
2835 		case 's':
2836 			if (s_arg)
2837 				die_optdup(option);
2838 
2839 			s_arg = B_TRUE;
2840 			break;
2841 		case 'o':
2842 			o_arg = B_TRUE;
2843 			fields_str = optarg;
2844 			break;
2845 		case 'i':
2846 			if (i_arg)
2847 				die_optdup(option);
2848 
2849 			i_arg = B_TRUE;
2850 			if (!str2int(optarg, &interval) || interval == 0)
2851 				die("invalid interval value '%s'", optarg);
2852 			break;
2853 		default:
2854 			die_opterr(optopt, option, use);
2855 			break;
2856 		}
2857 	}
2858 
2859 	if (p_arg && !o_arg)
2860 		die("-p requires -o");
2861 
2862 	if (p_arg && strcasecmp(fields_str, "all") == 0)
2863 		die("\"-o all\" is invalid with -p");
2864 
2865 	if (i_arg && !s_arg)
2866 		die("the option -i can be used only with -s");
2867 
2868 	if (o_arg && strcasecmp(fields_str, "all") == 0) {
2869 		if (!s_arg)
2870 			fields_str = all_fields;
2871 		else
2872 			fields_str = allstat_fields;
2873 	}
2874 
2875 	if (!o_arg && s_arg)
2876 		fields_str = allstat_fields;
2877 
2878 	if (s_arg && p_arg)
2879 		die("the option -s cannot be used with -p");
2880 
2881 	/* get dev name (optional last argument) */
2882 	if (optind == (argc-1)) {
2883 		uint32_t flags;
2884 
2885 		dev = argv[optind];
2886 
2887 		if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK)
2888 			die("invalid device %s", dev);
2889 
2890 		if ((dladm_datalink_id2info(linkid, &flags, NULL, NULL,
2891 		    NULL, 0) != DLADM_STATUS_OK) ||
2892 		    !(flags & DLADM_OPT_ACTIVE)) {
2893 			die("device %s has been removed", dev);
2894 		}
2895 	} else if (optind != argc) {
2896 		usage();
2897 	}
2898 
2899 	state.ls_parseable = p_arg;
2900 	state.ls_donefirst = B_FALSE;
2901 
2902 	if (s_arg) {
2903 		dev_stats(dev, interval, fields_str, &state);
2904 		return;
2905 	}
2906 
2907 	fields = parse_output_fields(fields_str, dev_fields, DEV_MAX_FIELDS,
2908 	    CMD_TYPE_ANY, &nfields);
2909 
2910 	if (fields == NULL) {
2911 		die("invalid field(s) specified");
2912 		return;
2913 	}
2914 
2915 	state.ls_print.ps_fields = fields;
2916 	state.ls_print.ps_nfields = nfields;
2917 
2918 	if (dev == NULL) {
2919 		(void) dladm_mac_walk(show_dev, &state);
2920 	} else {
2921 		(void) show_dev(dev, &state);
2922 	}
2923 }
2924 
2925 
2926 static dladm_status_t
2927 print_phys(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *pattr)
2928 {
2929 	char			link[MAXLINKNAMELEN];
2930 	dladm_phys_attr_t	dpa;
2931 	uint32_t		flags;
2932 	datalink_class_t	class;
2933 	uint32_t		media;
2934 	dladm_status_t		status;
2935 
2936 	if ((status = dladm_datalink_id2info(linkid, &flags, &class, &media,
2937 	    link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
2938 		goto done;
2939 	}
2940 
2941 	if (class != DATALINK_CLASS_PHYS) {
2942 		status = DLADM_STATUS_BADARG;
2943 		goto done;
2944 	}
2945 
2946 	if (!(state->ls_flags & flags)) {
2947 		status = DLADM_STATUS_NOTFOUND;
2948 		goto done;
2949 	}
2950 
2951 	status = dladm_phys_info(linkid, &dpa, state->ls_flags);
2952 	if (status != DLADM_STATUS_OK)
2953 		goto done;
2954 
2955 	(void) snprintf(pattr->link_phys_device,
2956 	    sizeof (pattr->link_phys_device), "%s", dpa.dp_dev);
2957 	(void) dladm_media2str(media, pattr->link_phys_media);
2958 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2959 		boolean_t	islink;
2960 
2961 		if (!dpa.dp_novanity) {
2962 			(void) strlcpy(pattr->link_name, link,
2963 			    sizeof (pattr->link_name));
2964 			islink = B_TRUE;
2965 		} else {
2966 			/*
2967 			 * This is a physical link that does not have
2968 			 * vanity naming support.
2969 			 */
2970 			(void) strlcpy(pattr->link_name, dpa.dp_dev,
2971 			    sizeof (pattr->link_name));
2972 			islink = B_FALSE;
2973 		}
2974 
2975 		(void) get_linkstate(pattr->link_name, islink,
2976 		    pattr->link_phys_state);
2977 		(void) snprintf(pattr->link_phys_speed,
2978 		    sizeof (pattr->link_phys_speed), "%u",
2979 		    (uint_t)((get_ifspeed(pattr->link_name,
2980 		    islink)) / 1000000ull));
2981 		(void) get_linkduplex(pattr->link_name, islink,
2982 		    pattr->link_phys_duplex);
2983 	} else {
2984 		(void) snprintf(pattr->link_name, sizeof (pattr->link_name),
2985 		    "%s", link);
2986 		(void) snprintf(pattr->link_flags, sizeof (pattr->link_flags),
2987 		    "%c----", flags & DLADM_OPT_ACTIVE ? '-' : 'r');
2988 	}
2989 
2990 done:
2991 	return (status);
2992 }
2993 
2994 static int
2995 show_phys(datalink_id_t linkid, void *arg)
2996 {
2997 	show_state_t	*state = arg;
2998 	dladm_status_t	status;
2999 	link_fields_buf_t	pattr;
3000 
3001 	status = print_phys(state, linkid, &pattr);
3002 	if (status != DLADM_STATUS_OK)
3003 		goto done;
3004 
3005 	if (!state->ls_parseable && !state->ls_printheader) {
3006 		print_header(&state->ls_print);
3007 		state->ls_printheader = B_TRUE;
3008 	}
3009 
3010 	dladm_print_output(&state->ls_print, state->ls_parseable,
3011 	    dladm_print_field, (void *)&pattr);
3012 
3013 done:
3014 	state->ls_status = status;
3015 	return (DLADM_WALK_CONTINUE);
3016 }
3017 
3018 
3019 /*
3020  * Print the active topology information.
3021  */
3022 static dladm_status_t
3023 print_vlan(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *l)
3024 {
3025 	dladm_vlan_attr_t	vinfo;
3026 	uint32_t		flags;
3027 	dladm_status_t		status;
3028 
3029 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL,
3030 	    l->link_name, sizeof (l->link_name))) != DLADM_STATUS_OK) {
3031 		goto done;
3032 	}
3033 
3034 	if (!(state->ls_flags & flags)) {
3035 		status = DLADM_STATUS_NOTFOUND;
3036 		goto done;
3037 	}
3038 
3039 	if ((status = dladm_vlan_info(linkid, &vinfo, state->ls_flags)) !=
3040 	    DLADM_STATUS_OK || (status = dladm_datalink_id2info(
3041 	    vinfo.dv_linkid, NULL, NULL, NULL, l->link_over,
3042 	    sizeof (l->link_over))) != DLADM_STATUS_OK) {
3043 		goto done;
3044 	}
3045 
3046 	(void) snprintf(l->link_vlan_vid, sizeof (l->link_vlan_vid), "%d",
3047 	    vinfo.dv_vid);
3048 	(void) snprintf(l->link_flags, sizeof (l->link_flags), "%c%c---",
3049 	    vinfo.dv_force ? 'f' : '-', vinfo.dv_implicit ? 'i' : '-');
3050 
3051 done:
3052 	return (status);
3053 }
3054 
3055 static int
3056 show_vlan(datalink_id_t linkid, void *arg)
3057 {
3058 	show_state_t	*state = arg;
3059 	dladm_status_t	status;
3060 	link_fields_buf_t	lbuf;
3061 
3062 	status = print_vlan(state, linkid, &lbuf);
3063 	if (status != DLADM_STATUS_OK)
3064 		goto done;
3065 
3066 	if (!state->ls_parseable && !state->ls_printheader) {
3067 		print_header(&state->ls_print);
3068 		state->ls_printheader = B_TRUE;
3069 	}
3070 
3071 	dladm_print_output(&state->ls_print, state->ls_parseable,
3072 	    dladm_print_field, (void *)&lbuf);
3073 
3074 done:
3075 	state->ls_status = status;
3076 	return (DLADM_WALK_CONTINUE);
3077 }
3078 
3079 static void
3080 do_show_phys(int argc, char *argv[], const char *use)
3081 {
3082 	int		option;
3083 	uint32_t	flags = DLADM_OPT_ACTIVE;
3084 	boolean_t	p_arg = B_FALSE;
3085 	boolean_t	o_arg = B_FALSE;
3086 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
3087 	show_state_t	state;
3088 	dladm_status_t	status;
3089 	char			*fields_str = NULL;
3090 	print_field_t		**fields;
3091 	uint_t			nfields;
3092 	char			*all_active_fields =
3093 	    "link,media,state,speed,duplex,device";
3094 	char			*all_inactive_fields =
3095 	    "link,device,media,flags";
3096 
3097 	bzero(&state, sizeof (state));
3098 	opterr = 0;
3099 	while ((option = getopt_long(argc, argv, ":pPo:",
3100 	    show_lopts, NULL)) != -1) {
3101 		switch (option) {
3102 		case 'p':
3103 			if (p_arg)
3104 				die_optdup(option);
3105 
3106 			p_arg = B_TRUE;
3107 			break;
3108 		case 'P':
3109 			if (flags != DLADM_OPT_ACTIVE)
3110 				die_optdup(option);
3111 
3112 			flags = DLADM_OPT_PERSIST;
3113 			break;
3114 		case 'o':
3115 			o_arg = B_TRUE;
3116 			fields_str = optarg;
3117 			break;
3118 		default:
3119 			die_opterr(optopt, option, use);
3120 			break;
3121 		}
3122 	}
3123 
3124 	if (p_arg && !o_arg)
3125 		die("-p requires -o");
3126 
3127 	if (p_arg && strcasecmp(fields_str, "all") == 0)
3128 		die("\"-o all\" is invalid with -p");
3129 
3130 	/* get link name (optional last argument) */
3131 	if (optind == (argc-1)) {
3132 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3133 		    NULL, NULL)) != DLADM_STATUS_OK) {
3134 			die_dlerr(status, "link %s is not valid", argv[optind]);
3135 		}
3136 	} else if (optind != argc) {
3137 		usage();
3138 	}
3139 
3140 	state.ls_parseable = p_arg;
3141 	state.ls_flags = flags;
3142 	state.ls_donefirst = B_FALSE;
3143 
3144 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
3145 		if (state.ls_flags & DLADM_OPT_ACTIVE)
3146 			fields_str = all_active_fields;
3147 		else
3148 			fields_str = all_inactive_fields;
3149 	}
3150 
3151 	fields = parse_output_fields(fields_str, phys_fields,
3152 	    PHYS_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
3153 
3154 	if (fields == NULL) {
3155 		die("invalid field(s) specified");
3156 		return;
3157 	}
3158 
3159 	state.ls_print.ps_fields = fields;
3160 	state.ls_print.ps_nfields = nfields;
3161 
3162 	if (linkid == DATALINK_ALL_LINKID) {
3163 		(void) dladm_walk_datalink_id(show_phys, &state,
3164 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags);
3165 	} else {
3166 		(void) show_phys(linkid, &state);
3167 		if (state.ls_status != DLADM_STATUS_OK) {
3168 			die_dlerr(state.ls_status,
3169 			    "failed to show physical link %s", argv[optind]);
3170 		}
3171 	}
3172 }
3173 
3174 static void
3175 do_show_vlan(int argc, char *argv[], const char *use)
3176 {
3177 	int		option;
3178 	uint32_t	flags = DLADM_OPT_ACTIVE;
3179 	boolean_t	p_arg = B_FALSE;
3180 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
3181 	show_state_t	state;
3182 	dladm_status_t	status;
3183 	boolean_t	o_arg = B_FALSE;
3184 	char		*fields_str = NULL;
3185 	print_field_t	**fields;
3186 	uint_t		nfields;
3187 	char		*all_fields = "link,vid,over,flags";
3188 
3189 	bzero(&state, sizeof (state));
3190 
3191 	opterr = 0;
3192 	while ((option = getopt_long(argc, argv, ":pPo:",
3193 	    show_lopts, NULL)) != -1) {
3194 		switch (option) {
3195 		case 'p':
3196 			if (p_arg)
3197 				die_optdup(option);
3198 
3199 			p_arg = B_TRUE;
3200 			break;
3201 		case 'P':
3202 			if (flags != DLADM_OPT_ACTIVE)
3203 				die_optdup(option);
3204 
3205 			flags = DLADM_OPT_PERSIST;
3206 			break;
3207 		case 'o':
3208 			o_arg = B_TRUE;
3209 			fields_str = optarg;
3210 			break;
3211 		default:
3212 			die_opterr(optopt, option, use);
3213 			break;
3214 		}
3215 	}
3216 
3217 	if (p_arg && !o_arg)
3218 		die("-p requires -o");
3219 
3220 	if (p_arg && strcasecmp(fields_str, "all") == 0)
3221 		die("\"-o all\" is invalid with -p");
3222 
3223 	/* get link name (optional last argument) */
3224 	if (optind == (argc-1)) {
3225 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3226 		    NULL, NULL)) != DLADM_STATUS_OK) {
3227 			die_dlerr(status, "link %s is not valid", argv[optind]);
3228 		}
3229 	} else if (optind != argc) {
3230 		usage();
3231 	}
3232 
3233 	state.ls_parseable = p_arg;
3234 	state.ls_flags = flags;
3235 	state.ls_donefirst = B_FALSE;
3236 
3237 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
3238 		fields_str = all_fields;
3239 
3240 	fields = parse_output_fields(fields_str, vlan_fields, VLAN_MAX_FIELDS,
3241 	    CMD_TYPE_ANY, &nfields);
3242 
3243 	if (fields == NULL) {
3244 		die("invalid field(s) specified");
3245 		return;
3246 	}
3247 	state.ls_print.ps_fields = fields;
3248 	state.ls_print.ps_nfields = nfields;
3249 
3250 	if (linkid == DATALINK_ALL_LINKID) {
3251 		(void) dladm_walk_datalink_id(show_vlan, &state,
3252 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags);
3253 	} else {
3254 		(void) show_vlan(linkid, &state);
3255 		if (state.ls_status != DLADM_STATUS_OK) {
3256 			die_dlerr(state.ls_status, "failed to show vlan %s",
3257 			    argv[optind]);
3258 		}
3259 	}
3260 }
3261 
3262 static void
3263 link_stats(datalink_id_t linkid, uint_t interval)
3264 {
3265 	show_state_t	state;
3266 
3267 	bzero(&state, sizeof (state));
3268 
3269 	/*
3270 	 * If an interval is specified, continuously show the stats
3271 	 * only for the first MAC port.
3272 	 */
3273 	state.ls_firstonly = (interval != 0);
3274 
3275 	for (;;) {
3276 		(void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n",
3277 		    "LINK", "IPACKETS", "RBYTES", "IERRORS", "OPACKETS",
3278 		    "OBYTES", "OERRORS");
3279 
3280 		state.ls_donefirst = B_FALSE;
3281 		if (linkid == DATALINK_ALL_LINKID) {
3282 			(void) dladm_walk_datalink_id(show_link_stats, &state,
3283 			    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
3284 			    DLADM_OPT_ACTIVE);
3285 		} else {
3286 			(void) show_link_stats(linkid, &state);
3287 		}
3288 
3289 		if (interval == 0)
3290 			break;
3291 
3292 		(void) sleep(interval);
3293 	}
3294 }
3295 
3296 static void
3297 aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval)
3298 {
3299 	/*
3300 	 * If an interval is specified, continuously show the stats
3301 	 * only for the first group.
3302 	 */
3303 	state->gs_firstonly = (interval != 0);
3304 
3305 	for (;;) {
3306 		state->gs_donefirst = B_FALSE;
3307 		if (linkid == DATALINK_ALL_LINKID)
3308 			(void) dladm_walk_datalink_id(show_aggr, state,
3309 			    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
3310 			    DLADM_OPT_ACTIVE);
3311 		else
3312 			(void) show_aggr(linkid, state);
3313 
3314 		if (interval == 0)
3315 			break;
3316 
3317 		(void) sleep(interval);
3318 	}
3319 }
3320 
3321 static void
3322 dev_stats(const char *dev, uint32_t interval, char *fields_str,
3323     show_state_t *state)
3324 {
3325 	print_field_t	**fields;
3326 	uint_t		nfields;
3327 
3328 	fields = parse_output_fields(fields_str, devs_fields, DEVS_MAX_FIELDS,
3329 	    CMD_TYPE_ANY, &nfields);
3330 
3331 	if (fields == NULL) {
3332 		die("invalid field(s) specified");
3333 		return;
3334 	}
3335 
3336 	state->ls_print.ps_fields = fields;
3337 	state->ls_print.ps_nfields = nfields;
3338 
3339 
3340 	/*
3341 	 * If an interval is specified, continuously show the stats
3342 	 * only for the first MAC port.
3343 	 */
3344 	state->ls_firstonly = (interval != 0);
3345 
3346 	for (;;) {
3347 
3348 		if (!state->ls_parseable)
3349 			print_header(&state->ls_print);
3350 		state->ls_donefirst = B_FALSE;
3351 
3352 		if (dev == NULL)
3353 			(void) dladm_mac_walk(show_dev_stats, state);
3354 		else
3355 			(void) show_dev_stats(dev, state);
3356 
3357 		if (interval == 0)
3358 			break;
3359 
3360 		(void) sleep(interval);
3361 	}
3362 
3363 	if (dev != NULL && state->ls_status != DLADM_STATUS_OK)
3364 		die_dlerr(state->ls_status, "cannot show device '%s'", dev);
3365 }
3366 
3367 /* accumulate stats (s1 += (s2 - s3)) */
3368 static void
3369 stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
3370 {
3371 	s1->ipackets += (s2->ipackets - s3->ipackets);
3372 	s1->opackets += (s2->opackets - s3->opackets);
3373 	s1->rbytes += (s2->rbytes - s3->rbytes);
3374 	s1->obytes += (s2->obytes - s3->obytes);
3375 	s1->ierrors += (s2->ierrors - s3->ierrors);
3376 	s1->oerrors += (s2->oerrors - s3->oerrors);
3377 }
3378 
3379 /* compute stats differences (s1 = s2 - s3) */
3380 static void
3381 stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
3382 {
3383 	s1->ipackets = s2->ipackets - s3->ipackets;
3384 	s1->opackets = s2->opackets - s3->opackets;
3385 	s1->rbytes = s2->rbytes - s3->rbytes;
3386 	s1->obytes = s2->obytes - s3->obytes;
3387 	s1->ierrors = s2->ierrors - s3->ierrors;
3388 	s1->oerrors = s2->oerrors - s3->oerrors;
3389 }
3390 
3391 static void
3392 get_stats(char *module, int instance, const char *name, pktsum_t *stats)
3393 {
3394 	kstat_ctl_t	*kcp;
3395 	kstat_t		*ksp;
3396 
3397 	if ((kcp = kstat_open()) == NULL) {
3398 		warn("kstat open operation failed");
3399 		return;
3400 	}
3401 
3402 	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
3403 		/*
3404 		 * The kstat query could fail if the underlying MAC
3405 		 * driver was already detached.
3406 		 */
3407 		(void) kstat_close(kcp);
3408 		return;
3409 	}
3410 
3411 	if (kstat_read(kcp, ksp, NULL) == -1)
3412 		goto bail;
3413 
3414 	if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
3415 	    &stats->ipackets) < 0)
3416 		goto bail;
3417 
3418 	if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
3419 	    &stats->opackets) < 0)
3420 		goto bail;
3421 
3422 	if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
3423 	    &stats->rbytes) < 0)
3424 		goto bail;
3425 
3426 	if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
3427 	    &stats->obytes) < 0)
3428 		goto bail;
3429 
3430 	if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
3431 	    &stats->ierrors) < 0)
3432 		goto bail;
3433 
3434 	if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
3435 	    &stats->oerrors) < 0)
3436 		goto bail;
3437 
3438 bail:
3439 	(void) kstat_close(kcp);
3440 	return;
3441 
3442 }
3443 
3444 static void
3445 get_mac_stats(const char *dev, pktsum_t *stats)
3446 {
3447 	char module[DLPI_LINKNAME_MAX];
3448 	uint_t instance;
3449 
3450 	bzero(stats, sizeof (*stats));
3451 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
3452 		return;
3453 
3454 	get_stats(module, instance, "mac", stats);
3455 }
3456 
3457 static void
3458 get_link_stats(const char *link, pktsum_t *stats)
3459 {
3460 	bzero(stats, sizeof (*stats));
3461 	get_stats("link", 0, link, stats);
3462 }
3463 
3464 static int
3465 query_kstat(char *module, int instance, const char *name, const char *stat,
3466     uint8_t type, void *val)
3467 {
3468 	kstat_ctl_t	*kcp;
3469 	kstat_t		*ksp;
3470 
3471 	if ((kcp = kstat_open()) == NULL) {
3472 		warn("kstat open operation failed");
3473 		return (-1);
3474 	}
3475 
3476 	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
3477 		/*
3478 		 * The kstat query could fail if the underlying MAC
3479 		 * driver was already detached.
3480 		 */
3481 		goto bail;
3482 	}
3483 
3484 	if (kstat_read(kcp, ksp, NULL) == -1) {
3485 		warn("kstat read failed");
3486 		goto bail;
3487 	}
3488 
3489 	if (dladm_kstat_value(ksp, stat, type, val) < 0)
3490 		goto bail;
3491 
3492 	(void) kstat_close(kcp);
3493 	return (0);
3494 
3495 bail:
3496 	(void) kstat_close(kcp);
3497 	return (-1);
3498 }
3499 
3500 static int
3501 get_one_kstat(const char *name, const char *stat, uint8_t type,
3502     void *val, boolean_t islink)
3503 {
3504 	char		module[DLPI_LINKNAME_MAX];
3505 	uint_t		instance;
3506 
3507 	if (islink) {
3508 		return (query_kstat("link", 0, name, stat, type, val));
3509 	} else {
3510 		if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
3511 			return (-1);
3512 
3513 		return (query_kstat(module, instance, "mac", stat, type, val));
3514 	}
3515 }
3516 
3517 static uint64_t
3518 get_ifspeed(const char *name, boolean_t islink)
3519 {
3520 	uint64_t ifspeed = 0;
3521 
3522 	(void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64,
3523 	    &ifspeed, islink);
3524 
3525 	return (ifspeed);
3526 }
3527 
3528 static const char *
3529 get_linkstate(const char *name, boolean_t islink, char *buf)
3530 {
3531 	link_state_t	linkstate;
3532 
3533 	if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32,
3534 	    &linkstate, islink) != 0) {
3535 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
3536 		return (buf);
3537 	}
3538 	return (dladm_linkstate2str(linkstate, buf));
3539 }
3540 
3541 static const char *
3542 get_linkduplex(const char *name, boolean_t islink, char *buf)
3543 {
3544 	link_duplex_t	linkduplex;
3545 
3546 	if (get_one_kstat(name, "link_duplex", KSTAT_DATA_UINT32,
3547 	    &linkduplex, islink) != 0) {
3548 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
3549 		return (buf);
3550 	}
3551 
3552 	return (dladm_linkduplex2str(linkduplex, buf));
3553 }
3554 
3555 typedef struct {
3556 	char	*s_buf;
3557 	char	**s_fields;	/* array of pointer to the fields in s_buf */
3558 	uint_t	s_nfields;	/* the number of fields in s_buf */
3559 } split_t;
3560 
3561 /*
3562  * Free the split_t structure pointed to by `sp'.
3563  */
3564 static void
3565 splitfree(split_t *sp)
3566 {
3567 	free(sp->s_buf);
3568 	free(sp->s_fields);
3569 	free(sp);
3570 }
3571 
3572 /*
3573  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
3574  * length.  Return a pointer to a split_t containing the split fields, or NULL
3575  * on failure.
3576  */
3577 static split_t *
3578 split(const char *str, uint_t maxfields, uint_t maxlen)
3579 {
3580 	char	*field, *token, *lasts = NULL;
3581 	split_t	*sp;
3582 
3583 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
3584 		return (NULL);
3585 
3586 	sp = calloc(sizeof (split_t), 1);
3587 	if (sp == NULL)
3588 		return (NULL);
3589 
3590 	sp->s_buf = strdup(str);
3591 	sp->s_fields = malloc(sizeof (char *) * maxfields);
3592 	if (sp->s_buf == NULL || sp->s_fields == NULL)
3593 		goto fail;
3594 
3595 	token = sp->s_buf;
3596 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
3597 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
3598 			goto fail;
3599 		token = NULL;
3600 		sp->s_fields[sp->s_nfields++] = field;
3601 	}
3602 	return (sp);
3603 fail:
3604 	splitfree(sp);
3605 	return (NULL);
3606 }
3607 
3608 static int
3609 parse_wifi_fields(char *str, print_field_t ***fields, uint_t *countp,
3610     uint_t cmdtype)
3611 {
3612 
3613 	if (cmdtype == WIFI_CMD_SCAN) {
3614 		if (str == NULL)
3615 			str = def_scan_wifi_fields;
3616 		if (strcasecmp(str, "all") == 0)
3617 			str = all_scan_wifi_fields;
3618 	} else if (cmdtype == WIFI_CMD_SHOW) {
3619 		if (str == NULL)
3620 			str = def_show_wifi_fields;
3621 		if (strcasecmp(str, "all") == 0)
3622 			str = all_show_wifi_fields;
3623 	} else {
3624 		return (-1);
3625 	}
3626 	*fields = parse_output_fields(str, wifi_fields, WIFI_MAX_FIELDS,
3627 	    cmdtype, countp);
3628 	if (*fields != NULL)
3629 		return (0);
3630 	return (-1);
3631 }
3632 static print_field_t **
3633 parse_output_fields(char *str, print_field_t *template, int max_fields,
3634     uint_t cmdtype, uint_t *countp)
3635 {
3636 	split_t		*sp;
3637 	boolean_t	good_match = B_FALSE;
3638 	uint_t		i, j;
3639 	print_field_t	**pf = NULL;
3640 
3641 	sp = split(str, max_fields, MAX_FIELD_LEN);
3642 
3643 	if (sp == NULL)
3644 		return (NULL);
3645 
3646 	pf = malloc(sp->s_nfields * sizeof (print_field_t *));
3647 	if (pf == NULL)
3648 		goto fail;
3649 
3650 	for (i = 0; i < sp->s_nfields; i++) {
3651 		for (j = 0; j < max_fields; j++) {
3652 			if (strcasecmp(sp->s_fields[i],
3653 			    template[j].pf_name) == 0) {
3654 				good_match = template[j]. pf_cmdtype & cmdtype;
3655 				break;
3656 			}
3657 		}
3658 		if (!good_match)
3659 			goto fail;
3660 
3661 		good_match = B_FALSE;
3662 		pf[i] = &template[j];
3663 	}
3664 	*countp = i;
3665 	splitfree(sp);
3666 	return (pf);
3667 fail:
3668 	free(pf);
3669 	splitfree(sp);
3670 	return (NULL);
3671 }
3672 
3673 typedef struct print_wifi_state {
3674 	char		*ws_link;
3675 	boolean_t	ws_parseable;
3676 	boolean_t	ws_header;
3677 	print_state_t	ws_print_state;
3678 } print_wifi_state_t;
3679 
3680 typedef struct  wlan_scan_args_s {
3681 	print_wifi_state_t	*ws_state;
3682 	void			*ws_attr;
3683 } wlan_scan_args_t;
3684 
3685 static void
3686 print_field(print_state_t *statep, print_field_t *pfp, const char *value,
3687     boolean_t parseable)
3688 {
3689 	uint_t	width = pfp->pf_width;
3690 	uint_t	valwidth = strlen(value);
3691 	uint_t	compress;
3692 
3693 	/*
3694 	 * Parsable fields are separated by ':'. If such a field contains
3695 	 * a ':' or '\', this character is prefixed by a '\'.
3696 	 */
3697 	if (parseable) {
3698 		char	c;
3699 
3700 		if (statep->ps_nfields == 1) {
3701 			(void) printf("%s", value);
3702 			return;
3703 		}
3704 		while ((c = *value++) != '\0') {
3705 			if (c == ':' || c == '\\')
3706 				(void) putchar('\\');
3707 			(void) putchar(c);
3708 		}
3709 		if (!statep->ps_lastfield)
3710 			(void) putchar(':');
3711 		return;
3712 	} else {
3713 		if (value[0] == '\0')
3714 			value = STR_UNDEF_VAL;
3715 		if (statep->ps_lastfield) {
3716 			(void) printf("%s", value);
3717 			return;
3718 		}
3719 
3720 		if (valwidth > width) {
3721 			statep->ps_overflow += valwidth - width;
3722 		} else if (valwidth < width && statep->ps_overflow > 0) {
3723 			compress = min(statep->ps_overflow, width - valwidth);
3724 			statep->ps_overflow -= compress;
3725 			width -= compress;
3726 		}
3727 		(void) printf("%-*s", width, value);
3728 	}
3729 
3730 	if (!statep->ps_lastfield)
3731 		(void) putchar(' ');
3732 }
3733 
3734 static char *
3735 print_wlan_attr(print_field_t *wfp, void *warg)
3736 {
3737 	static char		buf[DLADM_STRSIZE];
3738 	wlan_scan_args_t	*w = warg;
3739 	print_wifi_state_t	*statep = w->ws_state;
3740 	dladm_wlan_attr_t	*attrp = w->ws_attr;
3741 
3742 	if (wfp->pf_index == 0) {
3743 		return ((char *)statep->ws_link);
3744 	}
3745 
3746 	if ((wfp->pf_index & attrp->wa_valid) == 0) {
3747 		return ("");
3748 	}
3749 
3750 	switch (wfp->pf_index) {
3751 	case DLADM_WLAN_ATTR_ESSID:
3752 		(void) dladm_wlan_essid2str(&attrp->wa_essid, buf);
3753 		break;
3754 	case DLADM_WLAN_ATTR_BSSID:
3755 		(void) dladm_wlan_bssid2str(&attrp->wa_bssid, buf);
3756 		break;
3757 	case DLADM_WLAN_ATTR_SECMODE:
3758 		(void) dladm_wlan_secmode2str(&attrp->wa_secmode, buf);
3759 		break;
3760 	case DLADM_WLAN_ATTR_STRENGTH:
3761 		(void) dladm_wlan_strength2str(&attrp->wa_strength, buf);
3762 		break;
3763 	case DLADM_WLAN_ATTR_MODE:
3764 		(void) dladm_wlan_mode2str(&attrp->wa_mode, buf);
3765 		break;
3766 	case DLADM_WLAN_ATTR_SPEED:
3767 		(void) dladm_wlan_speed2str(&attrp->wa_speed, buf);
3768 		(void) strlcat(buf, "Mb", sizeof (buf));
3769 		break;
3770 	case DLADM_WLAN_ATTR_AUTH:
3771 		(void) dladm_wlan_auth2str(&attrp->wa_auth, buf);
3772 		break;
3773 	case DLADM_WLAN_ATTR_BSSTYPE:
3774 		(void) dladm_wlan_bsstype2str(&attrp->wa_bsstype, buf);
3775 		break;
3776 	}
3777 
3778 	return (buf);
3779 }
3780 
3781 static boolean_t
3782 print_scan_results(void *arg, dladm_wlan_attr_t *attrp)
3783 {
3784 	print_wifi_state_t	*statep = arg;
3785 	wlan_scan_args_t	warg;
3786 
3787 	if (statep->ws_header) {
3788 		statep->ws_header = B_FALSE;
3789 		if (!statep->ws_parseable)
3790 			print_header(&statep->ws_print_state);
3791 	}
3792 
3793 	statep->ws_print_state.ps_overflow = 0;
3794 	bzero(&warg, sizeof (warg));
3795 	warg.ws_state = statep;
3796 	warg.ws_attr = attrp;
3797 	dladm_print_output(&statep->ws_print_state, statep->ws_parseable,
3798 	    print_wlan_attr, &warg);
3799 	return (B_TRUE);
3800 }
3801 
3802 static int
3803 scan_wifi(datalink_id_t linkid, void *arg)
3804 {
3805 	print_wifi_state_t	*statep = arg;
3806 	dladm_status_t		status;
3807 	char			link[MAXLINKNAMELEN];
3808 
3809 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
3810 	    sizeof (link))) != DLADM_STATUS_OK) {
3811 		return (DLADM_WALK_CONTINUE);
3812 	}
3813 
3814 	statep->ws_link = link;
3815 	status = dladm_wlan_scan(linkid, statep, print_scan_results);
3816 	if (status != DLADM_STATUS_OK)
3817 		die_dlerr(status, "cannot scan link '%s'", statep->ws_link);
3818 
3819 	return (DLADM_WALK_CONTINUE);
3820 }
3821 
3822 static char *
3823 print_link_attr(print_field_t *wfp, void *warg)
3824 {
3825 	static char		buf[DLADM_STRSIZE];
3826 	char			*ptr;
3827 	wlan_scan_args_t	*w = warg, w1;
3828 	print_wifi_state_t	*statep = w->ws_state;
3829 	dladm_wlan_linkattr_t	*attrp = w->ws_attr;
3830 
3831 	if (strcmp(wfp->pf_name, "status") == 0) {
3832 		if ((wfp->pf_index & attrp->la_valid) != 0)
3833 			(void) dladm_wlan_linkstatus2str(
3834 			    &attrp->la_status, buf);
3835 		return (buf);
3836 	}
3837 	statep->ws_print_state.ps_overflow = 0;
3838 	bzero(&w1, sizeof (w1));
3839 	w1.ws_state = statep;
3840 	w1.ws_attr = &attrp->la_wlan_attr;
3841 	ptr = print_wlan_attr(wfp, &w1);
3842 	return (ptr);
3843 }
3844 
3845 static int
3846 show_wifi(datalink_id_t linkid, void *arg)
3847 {
3848 	print_wifi_state_t	*statep = arg;
3849 	dladm_wlan_linkattr_t	attr;
3850 	dladm_status_t		status;
3851 	char			link[MAXLINKNAMELEN];
3852 	wlan_scan_args_t	warg;
3853 
3854 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
3855 	    sizeof (link))) != DLADM_STATUS_OK) {
3856 		return (DLADM_WALK_CONTINUE);
3857 	}
3858 
3859 	status = dladm_wlan_get_linkattr(linkid, &attr);
3860 	if (status != DLADM_STATUS_OK)
3861 		die_dlerr(status, "cannot get link attributes for %s", link);
3862 
3863 	statep->ws_link = link;
3864 
3865 	if (statep->ws_header) {
3866 		statep->ws_header = B_FALSE;
3867 		if (!statep->ws_parseable)
3868 			print_header(&statep->ws_print_state);
3869 	}
3870 
3871 	statep->ws_print_state.ps_overflow = 0;
3872 	bzero(&warg, sizeof (warg));
3873 	warg.ws_state = statep;
3874 	warg.ws_attr = &attr;
3875 	dladm_print_output(&statep->ws_print_state, statep->ws_parseable,
3876 	    print_link_attr, &warg);
3877 	return (DLADM_WALK_CONTINUE);
3878 }
3879 
3880 static void
3881 do_display_wifi(int argc, char **argv, int cmd, const char *use)
3882 {
3883 	int			option;
3884 	char			*fields_str = NULL;
3885 	print_field_t		**fields;
3886 	int			(*callback)(datalink_id_t, void *);
3887 	uint_t			nfields;
3888 	print_wifi_state_t	state;
3889 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3890 	dladm_status_t		status;
3891 
3892 	if (cmd == WIFI_CMD_SCAN)
3893 		callback = scan_wifi;
3894 	else if (cmd == WIFI_CMD_SHOW)
3895 		callback = show_wifi;
3896 	else
3897 		return;
3898 
3899 	state.ws_parseable = B_FALSE;
3900 	state.ws_header = B_TRUE;
3901 	opterr = 0;
3902 	while ((option = getopt_long(argc, argv, ":o:p",
3903 	    wifi_longopts, NULL)) != -1) {
3904 		switch (option) {
3905 		case 'o':
3906 			fields_str = optarg;
3907 			break;
3908 		case 'p':
3909 			state.ws_parseable = B_TRUE;
3910 			break;
3911 		default:
3912 			die_opterr(optopt, option, use);
3913 		}
3914 	}
3915 
3916 	if (state.ws_parseable && fields_str == NULL)
3917 		die("-p requires -o");
3918 
3919 	if (state.ws_parseable && strcasecmp(fields_str, "all") == 0)
3920 		die("\"-o all\" is invalid with -p");
3921 
3922 	if (optind == (argc - 1)) {
3923 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3924 		    NULL, NULL)) != DLADM_STATUS_OK) {
3925 			die_dlerr(status, "link %s is not valid", argv[optind]);
3926 		}
3927 	} else if (optind != argc) {
3928 		usage();
3929 	}
3930 
3931 	if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0)
3932 		die("invalid field(s) specified");
3933 
3934 	bzero(&state.ws_print_state, sizeof (state.ws_print_state));
3935 	state.ws_print_state.ps_fields = fields;
3936 	state.ws_print_state.ps_nfields = nfields;
3937 
3938 	if (linkid == DATALINK_ALL_LINKID) {
3939 		(void) dladm_walk_datalink_id(callback, &state,
3940 		    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
3941 	} else {
3942 		(void) (*callback)(linkid, &state);
3943 	}
3944 	free(fields);
3945 }
3946 
3947 static void
3948 do_scan_wifi(int argc, char **argv, const char *use)
3949 {
3950 	do_display_wifi(argc, argv, WIFI_CMD_SCAN, use);
3951 }
3952 
3953 static void
3954 do_show_wifi(int argc, char **argv, const char *use)
3955 {
3956 	do_display_wifi(argc, argv, WIFI_CMD_SHOW, use);
3957 }
3958 
3959 typedef struct wlan_count_attr {
3960 	uint_t		wc_count;
3961 	datalink_id_t	wc_linkid;
3962 } wlan_count_attr_t;
3963 
3964 static int
3965 do_count_wlan(datalink_id_t linkid, void *arg)
3966 {
3967 	wlan_count_attr_t *cp = arg;
3968 
3969 	if (cp->wc_count == 0)
3970 		cp->wc_linkid = linkid;
3971 	cp->wc_count++;
3972 	return (DLADM_WALK_CONTINUE);
3973 }
3974 
3975 static int
3976 parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp)
3977 {
3978 	uint_t			i;
3979 	split_t			*sp;
3980 	dladm_wlan_key_t	*wk;
3981 
3982 	sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_KEYNAME_LEN);
3983 	if (sp == NULL)
3984 		return (-1);
3985 
3986 	wk = malloc(sp->s_nfields * sizeof (dladm_wlan_key_t));
3987 	if (wk == NULL)
3988 		goto fail;
3989 
3990 	for (i = 0; i < sp->s_nfields; i++) {
3991 		char			*s;
3992 		dladm_secobj_class_t	class;
3993 		dladm_status_t		status;
3994 
3995 		(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
3996 		    DLADM_WLAN_MAX_KEYNAME_LEN);
3997 
3998 		wk[i].wk_idx = 1;
3999 		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
4000 			if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
4001 				goto fail;
4002 
4003 			wk[i].wk_idx = (uint_t)(s[1] - '0');
4004 			*s = '\0';
4005 		}
4006 		wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN;
4007 
4008 		status = dladm_get_secobj(wk[i].wk_name, &class,
4009 		    wk[i].wk_val, &wk[i].wk_len, 0);
4010 		if (status != DLADM_STATUS_OK) {
4011 			if (status == DLADM_STATUS_NOTFOUND) {
4012 				status = dladm_get_secobj(wk[i].wk_name,
4013 				    &class, wk[i].wk_val, &wk[i].wk_len,
4014 				    DLADM_OPT_PERSIST);
4015 			}
4016 			if (status != DLADM_STATUS_OK)
4017 				goto fail;
4018 		}
4019 		wk[i].wk_class = class;
4020 	}
4021 	*keys = wk;
4022 	*key_countp = i;
4023 	splitfree(sp);
4024 	return (0);
4025 fail:
4026 	free(wk);
4027 	splitfree(sp);
4028 	return (-1);
4029 }
4030 
4031 static void
4032 do_connect_wifi(int argc, char **argv, const char *use)
4033 {
4034 	int			option;
4035 	dladm_wlan_attr_t	attr, *attrp;
4036 	dladm_status_t		status = DLADM_STATUS_OK;
4037 	int			timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
4038 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
4039 	dladm_wlan_key_t	*keys = NULL;
4040 	uint_t			key_count = 0;
4041 	uint_t			flags = 0;
4042 	dladm_wlan_secmode_t	keysecmode = DLADM_WLAN_SECMODE_NONE;
4043 	char			buf[DLADM_STRSIZE];
4044 
4045 	opterr = 0;
4046 	(void) memset(&attr, 0, sizeof (attr));
4047 	while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
4048 	    wifi_longopts, NULL)) != -1) {
4049 		switch (option) {
4050 		case 'e':
4051 			status = dladm_wlan_str2essid(optarg, &attr.wa_essid);
4052 			if (status != DLADM_STATUS_OK)
4053 				die("invalid ESSID '%s'", optarg);
4054 
4055 			attr.wa_valid |= DLADM_WLAN_ATTR_ESSID;
4056 			/*
4057 			 * Try to connect without doing a scan.
4058 			 */
4059 			flags |= DLADM_WLAN_CONNECT_NOSCAN;
4060 			break;
4061 		case 'i':
4062 			status = dladm_wlan_str2bssid(optarg, &attr.wa_bssid);
4063 			if (status != DLADM_STATUS_OK)
4064 				die("invalid BSSID %s", optarg);
4065 
4066 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
4067 			break;
4068 		case 'a':
4069 			status = dladm_wlan_str2auth(optarg, &attr.wa_auth);
4070 			if (status != DLADM_STATUS_OK)
4071 				die("invalid authentication mode '%s'", optarg);
4072 
4073 			attr.wa_valid |= DLADM_WLAN_ATTR_AUTH;
4074 			break;
4075 		case 'm':
4076 			status = dladm_wlan_str2mode(optarg, &attr.wa_mode);
4077 			if (status != DLADM_STATUS_OK)
4078 				die("invalid mode '%s'", optarg);
4079 
4080 			attr.wa_valid |= DLADM_WLAN_ATTR_MODE;
4081 			break;
4082 		case 'b':
4083 			if ((status = dladm_wlan_str2bsstype(optarg,
4084 			    &attr.wa_bsstype)) != DLADM_STATUS_OK) {
4085 				die("invalid bsstype '%s'", optarg);
4086 			}
4087 
4088 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
4089 			break;
4090 		case 's':
4091 			if ((status = dladm_wlan_str2secmode(optarg,
4092 			    &attr.wa_secmode)) != DLADM_STATUS_OK) {
4093 				die("invalid security mode '%s'", optarg);
4094 			}
4095 
4096 			attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
4097 			break;
4098 		case 'k':
4099 			if (parse_wlan_keys(optarg, &keys, &key_count) < 0)
4100 				die("invalid key(s) '%s'", optarg);
4101 
4102 			if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP)
4103 				keysecmode = DLADM_WLAN_SECMODE_WEP;
4104 			else
4105 				keysecmode = DLADM_WLAN_SECMODE_WPA;
4106 			break;
4107 		case 'T':
4108 			if (strcasecmp(optarg, "forever") == 0) {
4109 				timeout = -1;
4110 				break;
4111 			}
4112 			if (!str2int(optarg, &timeout) || timeout < 0)
4113 				die("invalid timeout value '%s'", optarg);
4114 			break;
4115 		case 'c':
4116 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
4117 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
4118 			break;
4119 		default:
4120 			die_opterr(optopt, option, use);
4121 			break;
4122 		}
4123 	}
4124 
4125 	if (keysecmode == DLADM_WLAN_SECMODE_NONE) {
4126 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) {
4127 			die("key required for security mode '%s'",
4128 			    dladm_wlan_secmode2str(&attr.wa_secmode, buf));
4129 		}
4130 	} else {
4131 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
4132 		    attr.wa_secmode != keysecmode)
4133 			die("incompatible -s and -k options");
4134 		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
4135 		attr.wa_secmode = keysecmode;
4136 	}
4137 
4138 	if (optind == (argc - 1)) {
4139 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
4140 		    NULL, NULL)) != DLADM_STATUS_OK) {
4141 			die_dlerr(status, "link %s is not valid", argv[optind]);
4142 		}
4143 	} else if (optind != argc) {
4144 		usage();
4145 	}
4146 
4147 	if (linkid == DATALINK_ALL_LINKID) {
4148 		wlan_count_attr_t wcattr;
4149 
4150 		wcattr.wc_linkid = DATALINK_INVALID_LINKID;
4151 		wcattr.wc_count = 0;
4152 		(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
4153 		    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
4154 		if (wcattr.wc_count == 0) {
4155 			die("no wifi links are available");
4156 		} else if (wcattr.wc_count > 1) {
4157 			die("link name is required when more than one wifi "
4158 			    "link is available");
4159 		}
4160 		linkid = wcattr.wc_linkid;
4161 	}
4162 	attrp = (attr.wa_valid == 0) ? NULL : &attr;
4163 again:
4164 	if ((status = dladm_wlan_connect(linkid, attrp, timeout, keys,
4165 	    key_count, flags)) != DLADM_STATUS_OK) {
4166 		if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0) {
4167 			/*
4168 			 * Try again with scanning and filtering.
4169 			 */
4170 			flags &= ~DLADM_WLAN_CONNECT_NOSCAN;
4171 			goto again;
4172 		}
4173 
4174 		if (status == DLADM_STATUS_NOTFOUND) {
4175 			if (attr.wa_valid == 0) {
4176 				die("no wifi networks are available");
4177 			} else {
4178 				die("no wifi networks with the specified "
4179 				    "criteria are available");
4180 			}
4181 		}
4182 		die_dlerr(status, "cannot connect");
4183 	}
4184 	free(keys);
4185 }
4186 
4187 /* ARGSUSED */
4188 static int
4189 do_all_disconnect_wifi(datalink_id_t linkid, void *arg)
4190 {
4191 	dladm_status_t	status;
4192 
4193 	status = dladm_wlan_disconnect(linkid);
4194 	if (status != DLADM_STATUS_OK)
4195 		warn_dlerr(status, "cannot disconnect link");
4196 
4197 	return (DLADM_WALK_CONTINUE);
4198 }
4199 
4200 static void
4201 do_disconnect_wifi(int argc, char **argv, const char *use)
4202 {
4203 	int			option;
4204 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
4205 	boolean_t		all_links = B_FALSE;
4206 	dladm_status_t		status;
4207 	wlan_count_attr_t	wcattr;
4208 
4209 	opterr = 0;
4210 	while ((option = getopt_long(argc, argv, ":a",
4211 	    wifi_longopts, NULL)) != -1) {
4212 		switch (option) {
4213 		case 'a':
4214 			all_links = B_TRUE;
4215 			break;
4216 		default:
4217 			die_opterr(optopt, option, use);
4218 			break;
4219 		}
4220 	}
4221 
4222 	if (optind == (argc - 1)) {
4223 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
4224 		    NULL, NULL)) != DLADM_STATUS_OK) {
4225 			die_dlerr(status, "link %s is not valid", argv[optind]);
4226 		}
4227 	} else if (optind != argc) {
4228 		usage();
4229 	}
4230 
4231 	if (linkid == DATALINK_ALL_LINKID) {
4232 		if (!all_links) {
4233 			wcattr.wc_linkid = linkid;
4234 			wcattr.wc_count = 0;
4235 			(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
4236 			    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
4237 			if (wcattr.wc_count == 0) {
4238 				die("no wifi links are available");
4239 			} else if (wcattr.wc_count > 1) {
4240 				die("link name is required when more than "
4241 				    "one wifi link is available");
4242 			}
4243 			linkid = wcattr.wc_linkid;
4244 		} else {
4245 			(void) dladm_walk_datalink_id(do_all_disconnect_wifi,
4246 			    NULL, DATALINK_CLASS_PHYS, DL_WIFI,
4247 			    DLADM_OPT_ACTIVE);
4248 			return;
4249 		}
4250 	}
4251 	status = dladm_wlan_disconnect(linkid);
4252 	if (status != DLADM_STATUS_OK)
4253 		die_dlerr(status, "cannot disconnect");
4254 }
4255 
4256 
4257 static void
4258 free_props(prop_list_t *list)
4259 {
4260 	if (list != NULL) {
4261 		free(list->pl_buf);
4262 		free(list);
4263 	}
4264 }
4265 
4266 static int
4267 parse_props(char *str, prop_list_t **listp, boolean_t novalues)
4268 {
4269 	prop_list_t	*list;
4270 	prop_info_t	*pip;
4271 	char		*buf, *curr;
4272 	int		len, i;
4273 
4274 	list = malloc(sizeof (prop_list_t));
4275 	if (list == NULL)
4276 		return (-1);
4277 
4278 	list->pl_count = 0;
4279 	list->pl_buf = buf = strdup(str);
4280 	if (buf == NULL)
4281 		goto fail;
4282 
4283 	/*
4284 	 * buf is a string of form [<propname>=<value>][,<propname>=<value>]+
4285 	 * where each <value> string itself could be a comma-separated array.
4286 	 * The loop below will count the number of propname assignments
4287 	 * in pl_count; for each property, there is a pip entry with
4288 	 * pi_name == <propname>, pi_count == # of elements in <value> array.
4289 	 * pi_val[] contains the actual values.
4290 	 *
4291 	 * This could really be a combination of  calls to
4292 	 * strtok (token delimiter is ",") and strchr (chr '=')
4293 	 * with appropriate null/string-bound-checks.
4294 	 */
4295 
4296 	curr = buf;
4297 	len = strlen(buf);
4298 	pip = NULL;
4299 	for (i = 0; i < len; i++) {
4300 		char		c = buf[i];
4301 		boolean_t	match = (c == '=' || c == ',');
4302 
4303 		if (!match && i != len - 1)
4304 			continue;
4305 
4306 		if (match) {
4307 			buf[i] = '\0';
4308 			if (*curr == '\0')
4309 				goto fail;
4310 		}
4311 
4312 		if (pip != NULL && c != '=') {
4313 			if (pip->pi_count > DLADM_MAX_PROP_VALCNT)
4314 				goto fail;
4315 
4316 			if (novalues)
4317 				goto fail;
4318 
4319 			pip->pi_val[pip->pi_count] = curr;
4320 			pip->pi_count++;
4321 		} else {
4322 			if (list->pl_count > MAX_PROPS)
4323 				goto fail;
4324 
4325 			pip = &list->pl_info[list->pl_count];
4326 			pip->pi_name = curr;
4327 			pip->pi_count = 0;
4328 			list->pl_count++;
4329 			if (c == ',')
4330 				pip = NULL;
4331 		}
4332 		curr = buf + i + 1;
4333 	}
4334 	*listp = list;
4335 	return (0);
4336 
4337 fail:
4338 	free_props(list);
4339 	return (-1);
4340 }
4341 
4342 static void
4343 print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep,
4344     const char *propname, dladm_prop_type_t type,
4345     const char *format, char **pptr)
4346 {
4347 	int		i;
4348 	char		*ptr, *lim;
4349 	char		buf[DLADM_STRSIZE];
4350 	char		*unknown = "?", *notsup = "";
4351 	char		**propvals = statep->ls_propvals;
4352 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
4353 	dladm_status_t	status;
4354 
4355 	status = dladm_get_linkprop(linkid, type, propname, propvals, &valcnt);
4356 	if (status != DLADM_STATUS_OK) {
4357 		if (status == DLADM_STATUS_TEMPONLY) {
4358 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
4359 			    statep->ls_persist) {
4360 				valcnt = 1;
4361 				propvals = &unknown;
4362 			} else {
4363 				statep->ls_status = status;
4364 				statep->ls_retstatus = status;
4365 				return;
4366 			}
4367 		} else if (status == DLADM_STATUS_NOTSUP ||
4368 		    statep->ls_persist) {
4369 			valcnt = 1;
4370 			if (type == DLADM_PROP_VAL_CURRENT)
4371 				propvals = &unknown;
4372 			else
4373 				propvals = &notsup;
4374 		} else {
4375 			if (statep->ls_proplist &&
4376 			    statep->ls_status == DLADM_STATUS_OK) {
4377 				warn_dlerr(status,
4378 				    "cannot get link property '%s' for %s",
4379 				    propname, statep->ls_link);
4380 			}
4381 			statep->ls_status = status;
4382 			statep->ls_retstatus = status;
4383 			return;
4384 		}
4385 	}
4386 
4387 	statep->ls_status = DLADM_STATUS_OK;
4388 
4389 	ptr = buf;
4390 	lim = buf + DLADM_STRSIZE;
4391 	for (i = 0; i < valcnt; i++) {
4392 		if (propvals[i][0] == '\0' && !statep->ls_parseable)
4393 			ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL",");
4394 		else
4395 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
4396 		if (ptr >= lim)
4397 			break;
4398 	}
4399 	if (valcnt > 0)
4400 		buf[strlen(buf) - 1] = '\0';
4401 
4402 	lim = statep->ls_line + MAX_PROP_LINE;
4403 	if (statep->ls_parseable) {
4404 		*pptr += snprintf(*pptr, lim - *pptr,
4405 		    "%s", buf);
4406 	} else {
4407 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
4408 	}
4409 }
4410 
4411 static char *
4412 linkprop_callback(print_field_t *pf, void *ls_arg)
4413 {
4414 	linkprop_args_t		*arg = ls_arg;
4415 	char 			*propname = arg->ls_propname;
4416 	show_linkprop_state_t	*statep = arg->ls_state;
4417 	char			*ptr = statep->ls_line;
4418 	char			*lim = ptr + MAX_PROP_LINE;
4419 	datalink_id_t		linkid = arg->ls_linkid;
4420 
4421 	switch (pf->pf_index) {
4422 	case LINKPROP_LINK:
4423 		(void) snprintf(ptr, lim - ptr, "%s", statep->ls_link);
4424 		break;
4425 	case LINKPROP_PROPERTY:
4426 		(void) snprintf(ptr, lim - ptr, "%s", propname);
4427 		break;
4428 	case LINKPROP_VALUE:
4429 		print_linkprop(linkid, statep, propname,
4430 		    statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
4431 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
4432 		/*
4433 		 * If we failed to query the link property, for example, query
4434 		 * the persistent value of a non-persistable link property,
4435 		 * simply skip the output.
4436 		 */
4437 		if (statep->ls_status != DLADM_STATUS_OK)
4438 			goto skip;
4439 		ptr = statep->ls_line;
4440 		break;
4441 	case LINKPROP_DEFAULT:
4442 		print_linkprop(linkid, statep, propname,
4443 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
4444 		if (statep->ls_status != DLADM_STATUS_OK)
4445 			goto skip;
4446 		ptr = statep->ls_line;
4447 		break;
4448 	case LINKPROP_POSSIBLE:
4449 		print_linkprop(linkid, statep, propname,
4450 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
4451 		if (statep->ls_status != DLADM_STATUS_OK)
4452 			goto skip;
4453 		ptr = statep->ls_line;
4454 		break;
4455 	default:
4456 		die("invalid input");
4457 		break;
4458 	}
4459 	return (ptr);
4460 skip:
4461 	if (statep->ls_status != DLADM_STATUS_OK)
4462 		return (NULL);
4463 	else
4464 		return ("");
4465 }
4466 
4467 static int
4468 show_linkprop(datalink_id_t linkid, const char *propname, void *arg)
4469 {
4470 	show_linkprop_state_t	*statep = arg;
4471 	linkprop_args_t		ls_arg;
4472 
4473 	bzero(&ls_arg, sizeof (ls_arg));
4474 	ls_arg.ls_state = statep;
4475 	ls_arg.ls_propname = (char *)propname;
4476 	ls_arg.ls_linkid = linkid;
4477 
4478 	if (statep->ls_header) {
4479 		statep->ls_header = B_FALSE;
4480 		if (!statep->ls_parseable)
4481 			print_header(&statep->ls_print);
4482 	}
4483 	dladm_print_output(&statep->ls_print, statep->ls_parseable,
4484 	    linkprop_callback, (void *)&ls_arg);
4485 
4486 	return (DLADM_WALK_CONTINUE);
4487 }
4488 
4489 static void
4490 do_show_linkprop(int argc, char **argv, const char *use)
4491 {
4492 	int			option;
4493 	prop_list_t		*proplist = NULL;
4494 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
4495 	show_linkprop_state_t	state;
4496 	uint32_t		flags = DLADM_OPT_ACTIVE;
4497 	dladm_status_t		status;
4498 	char			*fields_str = NULL;
4499 	print_field_t		**fields;
4500 	uint_t			nfields;
4501 	boolean_t		o_arg = B_FALSE;
4502 	char			*all_fields =
4503 	    "link,property,value,default,possible";
4504 
4505 	fields_str = all_fields;
4506 
4507 	opterr = 0;
4508 	state.ls_propvals = NULL;
4509 	state.ls_line = NULL;
4510 	state.ls_parseable = B_FALSE;
4511 	state.ls_persist = B_FALSE;
4512 	state.ls_header = B_TRUE;
4513 	state.ls_retstatus = DLADM_STATUS_OK;
4514 	while ((option = getopt_long(argc, argv, ":p:cPo:",
4515 	    prop_longopts, NULL)) != -1) {
4516 		switch (option) {
4517 		case 'p':
4518 			if (parse_props(optarg, &proplist, B_TRUE) < 0)
4519 				die("invalid link properties specified");
4520 			break;
4521 		case 'c':
4522 			state.ls_parseable = B_TRUE;
4523 			break;
4524 		case 'P':
4525 			state.ls_persist = B_TRUE;
4526 			flags = DLADM_OPT_PERSIST;
4527 			break;
4528 		case 'o':
4529 			o_arg = B_TRUE;
4530 			if (strcasecmp(optarg, "all") == 0)
4531 				fields_str = all_fields;
4532 			else
4533 				fields_str = optarg;
4534 			break;
4535 		default:
4536 			die_opterr(optopt, option, use);
4537 			break;
4538 		}
4539 	}
4540 
4541 	if (state.ls_parseable && !o_arg)
4542 		die("-c requires -o");
4543 
4544 	if (state.ls_parseable && fields_str == all_fields)
4545 		die("\"-o all\" is invalid with -c");
4546 
4547 	if (optind == (argc - 1)) {
4548 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
4549 		    NULL, NULL)) != DLADM_STATUS_OK) {
4550 			die_dlerr(status, "link %s is not valid", argv[optind]);
4551 		}
4552 	} else if (optind != argc) {
4553 		usage();
4554 	}
4555 
4556 	bzero(&state.ls_print, sizeof (print_state_t));
4557 	state.ls_proplist = proplist;
4558 	state.ls_status = DLADM_STATUS_OK;
4559 
4560 	fields = parse_output_fields(fields_str, linkprop_fields,
4561 	    LINKPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
4562 
4563 	if (fields == NULL) {
4564 		die("invalid field(s) specified");
4565 		return;
4566 	}
4567 
4568 	state.ls_print.ps_fields = fields;
4569 	state.ls_print.ps_nfields = nfields;
4570 	if (linkid == DATALINK_ALL_LINKID) {
4571 		(void) dladm_walk_datalink_id(show_linkprop_onelink, &state,
4572 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
4573 	} else {
4574 		(void) show_linkprop_onelink(linkid, &state);
4575 	}
4576 	free_props(proplist);
4577 
4578 	if (state.ls_retstatus != DLADM_STATUS_OK)
4579 		exit(EXIT_FAILURE);
4580 }
4581 
4582 static int
4583 show_linkprop_onelink(datalink_id_t linkid, void *arg)
4584 {
4585 	int			i;
4586 	char			*buf;
4587 	uint32_t		flags;
4588 	prop_list_t		*proplist = NULL;
4589 	show_linkprop_state_t	*statep = arg;
4590 	dlpi_handle_t		dh = NULL;
4591 
4592 	statep->ls_status = DLADM_STATUS_OK;
4593 
4594 	if (dladm_datalink_id2info(linkid, &flags, NULL, NULL, statep->ls_link,
4595 	    MAXLINKNAMELEN) != DLADM_STATUS_OK) {
4596 		statep->ls_status = DLADM_STATUS_NOTFOUND;
4597 		return (DLADM_WALK_CONTINUE);
4598 	}
4599 
4600 	if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) ||
4601 	    (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) {
4602 		statep->ls_status = DLADM_STATUS_BADARG;
4603 		return (DLADM_WALK_CONTINUE);
4604 	}
4605 
4606 	proplist = statep->ls_proplist;
4607 
4608 	/*
4609 	 * When some WiFi links are opened for the first time, their hardware
4610 	 * automatically scans for APs and does other slow operations.	Thus,
4611 	 * if there are no open links, the retrieval of link properties
4612 	 * (below) will proceed slowly unless we hold the link open.
4613 	 *
4614 	 * Note that failure of dlpi_open() does not necessarily mean invalid
4615 	 * link properties, because dlpi_open() may fail because of incorrect
4616 	 * autopush configuration. Therefore, we ingore the return value of
4617 	 * dlpi_open().
4618 	 */
4619 	if (!statep->ls_persist)
4620 		(void) dlpi_open(statep->ls_link, &dh, 0);
4621 
4622 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
4623 	    DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
4624 	if (buf == NULL)
4625 		die("insufficient memory");
4626 
4627 	statep->ls_propvals = (char **)(void *)buf;
4628 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
4629 		statep->ls_propvals[i] = buf +
4630 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
4631 		    i * DLADM_PROP_VAL_MAX;
4632 	}
4633 	statep->ls_line = buf +
4634 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
4635 
4636 	if (proplist != NULL) {
4637 		for (i = 0; i < proplist->pl_count; i++) {
4638 			(void) show_linkprop(linkid,
4639 			    proplist->pl_info[i].pi_name, statep);
4640 		}
4641 	} else {
4642 		(void) dladm_walk_linkprop(linkid, statep, show_linkprop);
4643 	}
4644 	if (dh != NULL)
4645 		dlpi_close(dh);
4646 	free(buf);
4647 	return (DLADM_WALK_CONTINUE);
4648 }
4649 
4650 static dladm_status_t
4651 set_linkprop_persist(datalink_id_t linkid, const char *prop_name,
4652     char **prop_val, uint_t val_cnt, boolean_t reset)
4653 {
4654 	dladm_status_t	status;
4655 
4656 	status = dladm_set_linkprop(linkid, prop_name, prop_val, val_cnt,
4657 	    DLADM_OPT_PERSIST);
4658 
4659 	if (status != DLADM_STATUS_OK) {
4660 		warn_dlerr(status, "cannot persistently %s link property",
4661 		    reset ? "reset" : "set");
4662 	}
4663 	return (status);
4664 }
4665 
4666 static void
4667 set_linkprop(int argc, char **argv, boolean_t reset, const char *use)
4668 {
4669 	int		i, option;
4670 	char		errmsg[DLADM_STRSIZE];
4671 	char		*altroot = NULL;
4672 	datalink_id_t	linkid;
4673 	prop_list_t	*proplist = NULL;
4674 	boolean_t	temp = B_FALSE;
4675 	dladm_status_t	status = DLADM_STATUS_OK;
4676 
4677 	opterr = 0;
4678 	while ((option = getopt_long(argc, argv, ":p:R:t",
4679 	    prop_longopts, NULL)) != -1) {
4680 		switch (option) {
4681 		case 'p':
4682 			if (parse_props(optarg, &proplist, reset) < 0)
4683 				die("invalid link properties specified");
4684 			break;
4685 		case 't':
4686 			temp = B_TRUE;
4687 			break;
4688 		case 'R':
4689 			altroot = optarg;
4690 			break;
4691 		default:
4692 			die_opterr(optopt, option, use);
4693 
4694 		}
4695 	}
4696 
4697 	/* get link name (required last argument) */
4698 	if (optind != (argc - 1))
4699 		usage();
4700 
4701 	if (proplist == NULL && !reset)
4702 		die("link property must be specified");
4703 
4704 	if (altroot != NULL) {
4705 		free_props(proplist);
4706 		altroot_cmd(altroot, argc, argv);
4707 	}
4708 
4709 	status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL);
4710 	if (status != DLADM_STATUS_OK)
4711 		die_dlerr(status, "link %s is not valid", argv[optind]);
4712 
4713 	if (proplist == NULL) {
4714 		status = dladm_set_linkprop(linkid, NULL, NULL, 0,
4715 		    DLADM_OPT_ACTIVE);
4716 		if (status != DLADM_STATUS_OK) {
4717 			warn_dlerr(status, "cannot reset link property "
4718 			    "on '%s'", argv[optind]);
4719 		}
4720 		if (!temp) {
4721 			dladm_status_t	s;
4722 
4723 			s = set_linkprop_persist(linkid, NULL, NULL, 0, reset);
4724 			if (s != DLADM_STATUS_OK)
4725 				status = s;
4726 		}
4727 		goto done;
4728 	}
4729 
4730 	for (i = 0; i < proplist->pl_count; i++) {
4731 		prop_info_t	*pip = &proplist->pl_info[i];
4732 		char		**val;
4733 		uint_t		count;
4734 		dladm_status_t	s;
4735 
4736 		if (reset) {
4737 			val = NULL;
4738 			count = 0;
4739 		} else {
4740 			val = pip->pi_val;
4741 			count = pip->pi_count;
4742 			if (count == 0) {
4743 				warn("no value specified for '%s'",
4744 				    pip->pi_name);
4745 				status = DLADM_STATUS_BADARG;
4746 				continue;
4747 			}
4748 		}
4749 		s = dladm_set_linkprop(linkid, pip->pi_name, val, count,
4750 		    DLADM_OPT_ACTIVE);
4751 		if (s == DLADM_STATUS_OK) {
4752 			if (!temp) {
4753 				s = set_linkprop_persist(linkid,
4754 				    pip->pi_name, val, count, reset);
4755 				if (s != DLADM_STATUS_OK)
4756 					status = s;
4757 			}
4758 			continue;
4759 		}
4760 		status = s;
4761 		switch (s) {
4762 		case DLADM_STATUS_NOTFOUND:
4763 			warn("invalid link property '%s'", pip->pi_name);
4764 			break;
4765 		case DLADM_STATUS_BADVAL: {
4766 			int		j;
4767 			char		*ptr, *lim;
4768 			char		**propvals = NULL;
4769 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
4770 
4771 			ptr = malloc((sizeof (char *) +
4772 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
4773 			    MAX_PROP_LINE);
4774 
4775 			propvals = (char **)(void *)ptr;
4776 			if (propvals == NULL)
4777 				die("insufficient memory");
4778 
4779 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
4780 				propvals[j] = ptr + sizeof (char *) *
4781 				    DLADM_MAX_PROP_VALCNT +
4782 				    j * DLADM_PROP_VAL_MAX;
4783 			}
4784 			s = dladm_get_linkprop(linkid,
4785 			    DLADM_PROP_VAL_MODIFIABLE, pip->pi_name, propvals,
4786 			    &valcnt);
4787 
4788 			if (s != DLADM_STATUS_OK) {
4789 				warn_dlerr(status, "cannot set link property "
4790 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
4791 				free(propvals);
4792 				break;
4793 			}
4794 
4795 			ptr = errmsg;
4796 			lim = ptr + DLADM_STRSIZE;
4797 			*ptr = '\0';
4798 			for (j = 0; j < valcnt; j++) {
4799 				ptr += snprintf(ptr, lim - ptr, "%s,",
4800 				    propvals[j]);
4801 				if (ptr >= lim)
4802 					break;
4803 			}
4804 			if (ptr > errmsg) {
4805 				*(ptr - 1) = '\0';
4806 				warn("link property '%s' must be one of: %s",
4807 				    pip->pi_name, errmsg);
4808 			} else
4809 				warn("invalid link property '%s'", *val);
4810 			free(propvals);
4811 			break;
4812 		}
4813 		default:
4814 			if (reset) {
4815 				warn_dlerr(status, "cannot reset link property "
4816 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
4817 			} else {
4818 				warn_dlerr(status, "cannot set link property "
4819 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
4820 			}
4821 			break;
4822 		}
4823 	}
4824 done:
4825 	free_props(proplist);
4826 	if (status != DLADM_STATUS_OK)
4827 		exit(1);
4828 }
4829 
4830 static void
4831 do_set_linkprop(int argc, char **argv, const char *use)
4832 {
4833 	set_linkprop(argc, argv, B_FALSE, use);
4834 }
4835 
4836 static void
4837 do_reset_linkprop(int argc, char **argv, const char *use)
4838 {
4839 	set_linkprop(argc, argv, B_TRUE, use);
4840 }
4841 
4842 static int
4843 convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
4844     dladm_secobj_class_t class)
4845 {
4846 	int error = 0;
4847 
4848 	if (class == DLADM_SECOBJ_CLASS_WPA) {
4849 		if (len < 8 || len > 63)
4850 			return (EINVAL);
4851 		(void) memcpy(obj_val, buf, len);
4852 		*obj_lenp = len;
4853 		return (error);
4854 	}
4855 
4856 	if (class == DLADM_SECOBJ_CLASS_WEP) {
4857 		switch (len) {
4858 		case 5:			/* ASCII key sizes */
4859 		case 13:
4860 			(void) memcpy(obj_val, buf, len);
4861 			*obj_lenp = len;
4862 			break;
4863 		case 10:		/* Hex key sizes, not preceded by 0x */
4864 		case 26:
4865 			error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
4866 			break;
4867 		case 12:		/* Hex key sizes, preceded by 0x */
4868 		case 28:
4869 			if (strncmp(buf, "0x", 2) != 0)
4870 				return (EINVAL);
4871 			error = hexascii_to_octet(buf + 2, len - 2,
4872 			    obj_val, obj_lenp);
4873 			break;
4874 		default:
4875 			return (EINVAL);
4876 		}
4877 		return (error);
4878 	}
4879 
4880 	return (ENOENT);
4881 }
4882 
4883 /* ARGSUSED */
4884 static void
4885 defersig(int sig)
4886 {
4887 	signalled = sig;
4888 }
4889 
4890 static int
4891 get_secobj_from_tty(uint_t try, const char *objname, char *buf)
4892 {
4893 	uint_t		len = 0;
4894 	int		c;
4895 	struct termios	stored, current;
4896 	void		(*sigfunc)(int);
4897 
4898 	/*
4899 	 * Turn off echo -- but before we do so, defer SIGINT handling
4900 	 * so that a ^C doesn't leave the terminal corrupted.
4901 	 */
4902 	sigfunc = signal(SIGINT, defersig);
4903 	(void) fflush(stdin);
4904 	(void) tcgetattr(0, &stored);
4905 	current = stored;
4906 	current.c_lflag &= ~(ICANON|ECHO);
4907 	current.c_cc[VTIME] = 0;
4908 	current.c_cc[VMIN] = 1;
4909 	(void) tcsetattr(0, TCSANOW, &current);
4910 again:
4911 	if (try == 1)
4912 		(void) printf(gettext("provide value for '%s': "), objname);
4913 	else
4914 		(void) printf(gettext("confirm value for '%s': "), objname);
4915 
4916 	(void) fflush(stdout);
4917 	while (signalled == 0) {
4918 		c = getchar();
4919 		if (c == '\n' || c == '\r') {
4920 			if (len != 0)
4921 				break;
4922 			(void) putchar('\n');
4923 			goto again;
4924 		}
4925 
4926 		buf[len++] = c;
4927 		if (len >= DLADM_SECOBJ_VAL_MAX - 1)
4928 			break;
4929 		(void) putchar('*');
4930 	}
4931 
4932 	(void) putchar('\n');
4933 	(void) fflush(stdin);
4934 
4935 	/*
4936 	 * Restore terminal setting and handle deferred signals.
4937 	 */
4938 	(void) tcsetattr(0, TCSANOW, &stored);
4939 
4940 	(void) signal(SIGINT, sigfunc);
4941 	if (signalled != 0)
4942 		(void) kill(getpid(), signalled);
4943 
4944 	return (len);
4945 }
4946 
4947 static int
4948 get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
4949     dladm_secobj_class_t class, FILE *filep)
4950 {
4951 	int		rval;
4952 	uint_t		len, len2;
4953 	char		buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
4954 
4955 	if (filep == NULL) {
4956 		len = get_secobj_from_tty(1, obj_name, buf);
4957 		rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
4958 		if (rval == 0) {
4959 			len2 = get_secobj_from_tty(2, obj_name, buf2);
4960 			if (len != len2 || memcmp(buf, buf2, len) != 0)
4961 				rval = ENOTSUP;
4962 		}
4963 		return (rval);
4964 	} else {
4965 		for (;;) {
4966 			if (fgets(buf, sizeof (buf), filep) == NULL)
4967 				break;
4968 			if (isspace(buf[0]))
4969 				continue;
4970 
4971 			len = strlen(buf);
4972 			if (buf[len - 1] == '\n') {
4973 				buf[len - 1] = '\0';
4974 				len--;
4975 			}
4976 			break;
4977 		}
4978 		(void) fclose(filep);
4979 	}
4980 	return (convert_secobj(buf, len, obj_val, obj_lenp, class));
4981 }
4982 
4983 static boolean_t
4984 check_auth(const char *auth)
4985 {
4986 	struct passwd	*pw;
4987 
4988 	if ((pw = getpwuid(getuid())) == NULL)
4989 		return (B_FALSE);
4990 
4991 	return (chkauthattr(auth, pw->pw_name) != 0);
4992 }
4993 
4994 static void
4995 audit_secobj(char *auth, char *class, char *obj,
4996     boolean_t success, boolean_t create)
4997 {
4998 	adt_session_data_t	*ah;
4999 	adt_event_data_t	*event;
5000 	au_event_t		flag;
5001 	char			*errstr;
5002 
5003 	if (create) {
5004 		flag = ADT_dladm_create_secobj;
5005 		errstr = "ADT_dladm_create_secobj";
5006 	} else {
5007 		flag = ADT_dladm_delete_secobj;
5008 		errstr = "ADT_dladm_delete_secobj";
5009 	}
5010 
5011 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
5012 		die("adt_start_session: %s", strerror(errno));
5013 
5014 	if ((event = adt_alloc_event(ah, flag)) == NULL)
5015 		die("adt_alloc_event (%s): %s", errstr, strerror(errno));
5016 
5017 	/* fill in audit info */
5018 	if (create) {
5019 		event->adt_dladm_create_secobj.auth_used = auth;
5020 		event->adt_dladm_create_secobj.obj_class = class;
5021 		event->adt_dladm_create_secobj.obj_name = obj;
5022 	} else {
5023 		event->adt_dladm_delete_secobj.auth_used = auth;
5024 		event->adt_dladm_delete_secobj.obj_class = class;
5025 		event->adt_dladm_delete_secobj.obj_name = obj;
5026 	}
5027 
5028 	if (success) {
5029 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
5030 			die("adt_put_event (%s, success): %s", errstr,
5031 			    strerror(errno));
5032 		}
5033 	} else {
5034 		if (adt_put_event(event, ADT_FAILURE,
5035 		    ADT_FAIL_VALUE_AUTH) != 0) {
5036 			die("adt_put_event: (%s, failure): %s", errstr,
5037 			    strerror(errno));
5038 		}
5039 	}
5040 
5041 	adt_free_event(event);
5042 	(void) adt_end_session(ah);
5043 }
5044 
5045 #define	MAX_SECOBJS		32
5046 #define	MAX_SECOBJ_NAMELEN	32
5047 static void
5048 do_create_secobj(int argc, char **argv, const char *use)
5049 {
5050 	int			option, rval;
5051 	FILE			*filep = NULL;
5052 	char			*obj_name = NULL;
5053 	char			*class_name = NULL;
5054 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
5055 	uint_t			obj_len;
5056 	boolean_t		success, temp = B_FALSE;
5057 	dladm_status_t		status;
5058 	dladm_secobj_class_t	class = -1;
5059 	uid_t			euid;
5060 
5061 	opterr = 0;
5062 	(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
5063 	while ((option = getopt_long(argc, argv, ":f:c:R:t",
5064 	    wifi_longopts, NULL)) != -1) {
5065 		switch (option) {
5066 		case 'f':
5067 			euid = geteuid();
5068 			(void) seteuid(getuid());
5069 			filep = fopen(optarg, "r");
5070 			if (filep == NULL) {
5071 				die("cannot open %s: %s", optarg,
5072 				    strerror(errno));
5073 			}
5074 			(void) seteuid(euid);
5075 			break;
5076 		case 'c':
5077 			class_name = optarg;
5078 			status = dladm_str2secobjclass(optarg, &class);
5079 			if (status != DLADM_STATUS_OK) {
5080 				die("invalid secure object class '%s', "
5081 				    "valid values are: wep, wpa", optarg);
5082 			}
5083 			break;
5084 		case 't':
5085 			temp = B_TRUE;
5086 			break;
5087 		case 'R':
5088 			status = dladm_set_rootdir(optarg);
5089 			if (status != DLADM_STATUS_OK) {
5090 				die_dlerr(status, "invalid directory "
5091 				    "specified");
5092 			}
5093 			break;
5094 		default:
5095 			die_opterr(optopt, option, use);
5096 			break;
5097 		}
5098 	}
5099 
5100 	if (optind == (argc - 1))
5101 		obj_name = argv[optind];
5102 	else if (optind != argc)
5103 		usage();
5104 
5105 	if (class == -1)
5106 		die("secure object class required");
5107 
5108 	if (obj_name == NULL)
5109 		die("secure object name required");
5110 
5111 	success = check_auth(LINK_SEC_AUTH);
5112 	audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
5113 	if (!success)
5114 		die("authorization '%s' is required", LINK_SEC_AUTH);
5115 
5116 	rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
5117 	if (rval != 0) {
5118 		switch (rval) {
5119 		case ENOENT:
5120 			die("invalid secure object class");
5121 			break;
5122 		case EINVAL:
5123 			die("invalid secure object value");
5124 			break;
5125 		case ENOTSUP:
5126 			die("verification failed");
5127 			break;
5128 		default:
5129 			die("invalid secure object: %s", strerror(rval));
5130 			break;
5131 		}
5132 	}
5133 
5134 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
5135 	    DLADM_OPT_CREATE | DLADM_OPT_ACTIVE);
5136 	if (status != DLADM_STATUS_OK) {
5137 		die_dlerr(status, "could not create secure object '%s'",
5138 		    obj_name);
5139 	}
5140 	if (temp)
5141 		return;
5142 
5143 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
5144 	    DLADM_OPT_PERSIST);
5145 	if (status != DLADM_STATUS_OK) {
5146 		warn_dlerr(status, "could not persistently create secure "
5147 		    "object '%s'", obj_name);
5148 	}
5149 }
5150 
5151 static void
5152 do_delete_secobj(int argc, char **argv, const char *use)
5153 {
5154 	int		i, option;
5155 	boolean_t	temp = B_FALSE;
5156 	split_t		*sp = NULL;
5157 	boolean_t	success;
5158 	dladm_status_t	status, pstatus;
5159 
5160 	opterr = 0;
5161 	status = pstatus = DLADM_STATUS_OK;
5162 	while ((option = getopt_long(argc, argv, ":R:t",
5163 	    wifi_longopts, NULL)) != -1) {
5164 		switch (option) {
5165 		case 't':
5166 			temp = B_TRUE;
5167 			break;
5168 		case 'R':
5169 			status = dladm_set_rootdir(optarg);
5170 			if (status != DLADM_STATUS_OK) {
5171 				die_dlerr(status, "invalid directory "
5172 				    "specified");
5173 			}
5174 			break;
5175 		default:
5176 			die_opterr(optopt, option, use);
5177 			break;
5178 		}
5179 	}
5180 
5181 	if (optind == (argc - 1)) {
5182 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
5183 		if (sp == NULL) {
5184 			die("invalid secure object name(s): '%s'",
5185 			    argv[optind]);
5186 		}
5187 	} else if (optind != argc)
5188 		usage();
5189 
5190 	if (sp == NULL || sp->s_nfields < 1)
5191 		die("secure object name required");
5192 
5193 	success = check_auth(LINK_SEC_AUTH);
5194 	audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE);
5195 	if (!success)
5196 		die("authorization '%s' is required", LINK_SEC_AUTH);
5197 
5198 	for (i = 0; i < sp->s_nfields; i++) {
5199 		status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_ACTIVE);
5200 		if (!temp) {
5201 			pstatus = dladm_unset_secobj(sp->s_fields[i],
5202 			    DLADM_OPT_PERSIST);
5203 		} else {
5204 			pstatus = DLADM_STATUS_OK;
5205 		}
5206 
5207 		if (status != DLADM_STATUS_OK) {
5208 			warn_dlerr(status, "could not delete secure object "
5209 			    "'%s'", sp->s_fields[i]);
5210 		}
5211 		if (pstatus != DLADM_STATUS_OK) {
5212 			warn_dlerr(pstatus, "could not persistently delete "
5213 			    "secure object '%s'", sp->s_fields[i]);
5214 		}
5215 	}
5216 	if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK)
5217 		exit(1);
5218 }
5219 
5220 typedef struct show_secobj_state {
5221 	boolean_t	ss_persist;
5222 	boolean_t	ss_parseable;
5223 	boolean_t	ss_header;
5224 	print_state_t	ss_print;
5225 } show_secobj_state_t;
5226 
5227 
5228 static boolean_t
5229 show_secobj(void *arg, const char *obj_name)
5230 {
5231 	uint_t			obj_len = DLADM_SECOBJ_VAL_MAX;
5232 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
5233 	char			buf[DLADM_STRSIZE];
5234 	uint_t			flags = 0;
5235 	dladm_secobj_class_t	class;
5236 	show_secobj_state_t	*statep = arg;
5237 	dladm_status_t		status;
5238 	secobj_fields_buf_t	sbuf;
5239 
5240 	if (statep->ss_persist)
5241 		flags |= DLADM_OPT_PERSIST;
5242 
5243 	status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags);
5244 	if (status != DLADM_STATUS_OK)
5245 		die_dlerr(status, "cannot get secure object '%s'", obj_name);
5246 
5247 	if (statep->ss_header) {
5248 		statep->ss_header = B_FALSE;
5249 		if (!statep->ss_parseable)
5250 			print_header(&statep->ss_print);
5251 	}
5252 
5253 	(void) snprintf(sbuf.ss_obj_name, sizeof (sbuf.ss_obj_name),
5254 	    obj_name);
5255 	(void) dladm_secobjclass2str(class, buf);
5256 	(void) snprintf(sbuf.ss_class, sizeof (sbuf.ss_class), "%s", buf);
5257 	if (getuid() == 0) {
5258 		char	val[DLADM_SECOBJ_VAL_MAX * 2];
5259 		uint_t	len = sizeof (val);
5260 
5261 		if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0)
5262 			(void) snprintf(sbuf.ss_val,
5263 			    sizeof (sbuf.ss_val), "%s", val);
5264 	}
5265 	dladm_print_output(&statep->ss_print, statep->ss_parseable,
5266 	    dladm_print_field, (void *)&sbuf);
5267 	return (B_TRUE);
5268 }
5269 
5270 static void
5271 do_show_secobj(int argc, char **argv, const char *use)
5272 {
5273 	int			option;
5274 	show_secobj_state_t	state;
5275 	dladm_status_t		status;
5276 	boolean_t		o_arg = B_FALSE;
5277 	uint_t			i;
5278 	split_t			*sp;
5279 	uint_t			flags;
5280 	char			*fields_str = NULL;
5281 	print_field_t		**fields;
5282 	uint_t			nfields;
5283 	char			*def_fields = "object,class";
5284 	char			*all_fields = "object,class,value";
5285 
5286 	opterr = 0;
5287 	bzero(&state, sizeof (state));
5288 	state.ss_parseable = B_FALSE;
5289 	fields_str = def_fields;
5290 	state.ss_persist = B_FALSE;
5291 	state.ss_parseable = B_FALSE;
5292 	state.ss_header = B_TRUE;
5293 	while ((option = getopt_long(argc, argv, ":pPo:",
5294 	    wifi_longopts, NULL)) != -1) {
5295 		switch (option) {
5296 		case 'p':
5297 			state.ss_parseable = B_TRUE;
5298 			break;
5299 		case 'P':
5300 			state.ss_persist = B_TRUE;
5301 			break;
5302 		case 'o':
5303 			o_arg = B_TRUE;
5304 			if (strcasecmp(optarg, "all") == 0)
5305 				fields_str = all_fields;
5306 			else
5307 				fields_str = optarg;
5308 			break;
5309 		default:
5310 			die_opterr(optopt, option, use);
5311 			break;
5312 		}
5313 	}
5314 
5315 	if (state.ss_parseable && !o_arg)
5316 		die("option -c requires -o");
5317 
5318 	if (state.ss_parseable && fields_str == all_fields)
5319 		die("\"-o all\" is invalid with -p");
5320 
5321 	fields = parse_output_fields(fields_str, secobj_fields,
5322 	    DEV_SOBJ_FIELDS, CMD_TYPE_ANY, &nfields);
5323 
5324 	if (fields == NULL) {
5325 		die("invalid field(s) specified");
5326 		return;
5327 	}
5328 	state.ss_print.ps_fields = fields;
5329 	state.ss_print.ps_nfields = nfields;
5330 
5331 	flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
5332 	if (optind == (argc - 1)) {
5333 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
5334 		if (sp == NULL) {
5335 			die("invalid secure object name(s): '%s'",
5336 			    argv[optind]);
5337 		}
5338 		for (i = 0; i < sp->s_nfields; i++) {
5339 			if (!show_secobj(&state, sp->s_fields[i]))
5340 				break;
5341 		}
5342 		splitfree(sp);
5343 		return;
5344 	} else if (optind != argc)
5345 		usage();
5346 
5347 	status = dladm_walk_secobj(&state, show_secobj, flags);
5348 	if (status != DLADM_STATUS_OK)
5349 		die_dlerr(status, "show-secobj");
5350 }
5351 
5352 /*ARGSUSED*/
5353 static int
5354 i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
5355 {
5356 	(void) dladm_init_linkprop(linkid, B_TRUE);
5357 	return (DLADM_WALK_CONTINUE);
5358 }
5359 
5360 /*ARGSUSED*/
5361 static void
5362 do_init_linkprop(int argc, char **argv, const char *use)
5363 {
5364 	int			option;
5365 	dladm_status_t		status;
5366 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
5367 	datalink_media_t	media = DATALINK_ANY_MEDIATYPE;
5368 	uint_t			any_media = B_TRUE;
5369 
5370 	opterr = 0;
5371 	while ((option = getopt(argc, argv, ":w")) != -1) {
5372 		switch (option) {
5373 		case 'w':
5374 			media = DL_WIFI;
5375 			any_media = B_FALSE;
5376 			break;
5377 		default:
5378 			/*
5379 			 * Because init-linkprop is not a public command,
5380 			 * print the usage instead.
5381 			 */
5382 			usage();
5383 			break;
5384 		}
5385 	}
5386 
5387 	if (optind == (argc - 1)) {
5388 		if ((status = dladm_name2info(argv[optind], &linkid, NULL, NULL,
5389 		    NULL)) != DLADM_STATUS_OK)
5390 			die_dlerr(status, "link %s is not valid", argv[optind]);
5391 	} else if (optind != argc) {
5392 		usage();
5393 	}
5394 
5395 	if (linkid == DATALINK_ALL_LINKID) {
5396 		/*
5397 		 * linkprops of links of other classes have been initialized as
5398 		 * part of the dladm up-xxx operation.
5399 		 */
5400 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
5401 		    DATALINK_CLASS_PHYS, media, DLADM_OPT_PERSIST);
5402 	} else {
5403 		(void) dladm_init_linkprop(linkid, any_media);
5404 	}
5405 }
5406 
5407 /* ARGSUSED */
5408 static void
5409 do_show_ether(int argc, char **argv, const char *use)
5410 {
5411 	int 			option;
5412 	datalink_id_t		linkid;
5413 	print_ether_state_t 	state;
5414 	print_field_t 		**fields;
5415 	boolean_t		o_arg = B_FALSE;
5416 	char			*fields_str;
5417 	uint_t			nfields;
5418 	char *all_fields =
5419 	    "link,ptype,state,auto,speed-duplex,pause,rem_fault";
5420 	char *default_fields =
5421 	    "link,ptype,state,auto,speed-duplex,pause";
5422 
5423 	fields_str = default_fields;
5424 	bzero(&state, sizeof (state));
5425 	state.es_link = NULL;
5426 	state.es_parseable = B_FALSE;
5427 
5428 	while ((option = getopt_long(argc, argv, "o:px",
5429 	    showeth_lopts, NULL)) != -1) {
5430 		switch (option) {
5431 			case 'x':
5432 				state.es_extended = B_TRUE;
5433 				break;
5434 			case 'p':
5435 				state.es_parseable = B_TRUE;
5436 				break;
5437 			case 'o':
5438 				o_arg = B_TRUE;
5439 				if (strcasecmp(optarg, "all") == 0)
5440 					fields_str = all_fields;
5441 				else
5442 					fields_str = optarg;
5443 				break;
5444 			default:
5445 				die_opterr(optopt, option, use);
5446 				break;
5447 		}
5448 	}
5449 
5450 	if (state.es_parseable && !o_arg)
5451 		die("-p requires -o");
5452 
5453 	if (state.es_parseable && fields_str == all_fields)
5454 		die("\"-o all\" is invalid with -p");
5455 
5456 	if (optind == (argc - 1))
5457 		state.es_link = argv[optind];
5458 
5459 	fields = parse_output_fields(fields_str, ether_fields,
5460 	    ETHER_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
5461 
5462 	if (fields == NULL) {
5463 		die("invalid field(s) specified");
5464 		exit(EXIT_FAILURE);
5465 	}
5466 	state.es_print.ps_fields = fields;
5467 	state.es_print.ps_nfields = nfields;
5468 
5469 	if (state.es_link == NULL) {
5470 		(void) dladm_walk_datalink_id(show_etherprop, &state,
5471 		    DATALINK_CLASS_PHYS, DL_ETHER,
5472 		    DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
5473 	} else {
5474 		if (!link_is_ether(state.es_link, &linkid)) {
5475 			die("invalid link specified");
5476 		}
5477 		(void) show_etherprop(linkid, &state);
5478 	}
5479 
5480 	exit(DLADM_STATUS_OK);
5481 
5482 }
5483 
5484 static char *
5485 dladm_print_field(print_field_t *pf, void *arg)
5486 {
5487 	char *value;
5488 
5489 	value = (char *)arg + pf->pf_offset;
5490 	return (value);
5491 }
5492 
5493 static int
5494 show_etherprop(datalink_id_t linkid, void *arg)
5495 {
5496 	print_ether_state_t *statep = arg;
5497 	char buf[DLADM_STRSIZE];
5498 	int speed;
5499 	uint64_t s;
5500 	uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf;
5501 	ether_fields_buf_t ebuf;
5502 	char speed_unit = 'M';
5503 
5504 	if (dladm_datalink_id2info(linkid, NULL, NULL, NULL,
5505 	    ebuf.eth_link, sizeof (ebuf.eth_link)) != DLADM_STATUS_OK) {
5506 		return (DLADM_WALK_CONTINUE);
5507 	}
5508 
5509 	if (!statep->es_header && !statep->es_parseable) {
5510 		print_header(&statep->es_print);
5511 		statep->es_header = B_TRUE;
5512 	}
5513 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5514 	    "%s", "current");
5515 
5516 	(void) dladm_get_single_mac_stat(linkid, "link_autoneg",
5517 	    KSTAT_DATA_UINT32, &autoneg);
5518 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5519 	    "%s", (autoneg ? "yes" : "no"));
5520 
5521 	(void) dladm_get_single_mac_stat(linkid, "link_pause",
5522 	    KSTAT_DATA_UINT32, &pause);
5523 	(void) dladm_get_single_mac_stat(linkid, "link_asmpause",
5524 	    KSTAT_DATA_UINT32, &asmpause);
5525 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5526 	    "%s", pause_str(pause, asmpause));
5527 
5528 	(void) dladm_get_single_mac_stat(linkid, "ifspeed",
5529 	    KSTAT_DATA_UINT64, &s);
5530 	speed = (int)(s/1000000ull);
5531 
5532 	if (speed >= 1000) {
5533 		speed = speed/1000;
5534 		speed_unit = 'G';
5535 	}
5536 	(void) get_linkduplex(ebuf.eth_link, B_TRUE, buf);
5537 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%d%c-%c",
5538 	    speed, speed_unit, buf[0]);
5539 
5540 	(void) get_linkstate(ebuf.eth_link, B_TRUE, buf);
5541 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state),
5542 	    "%s", buf);
5543 
5544 	(void) dladm_get_single_mac_stat(linkid, "adv_rem_fault",
5545 	    KSTAT_DATA_UINT32, &adv_rf);
5546 	(void) dladm_get_single_mac_stat(linkid, "cap_rem_fault",
5547 	    KSTAT_DATA_UINT32, &cap_rf);
5548 	(void) dladm_get_single_mac_stat(linkid, "lp_rem_fault",
5549 	    KSTAT_DATA_UINT32, &lp_rf);
5550 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5551 	    "%s", (adv_rf == 0 && lp_rf == 0 ? "none" : "fault"));
5552 
5553 	dladm_print_output(&statep->es_print, statep->es_parseable,
5554 	    dladm_print_field, &ebuf);
5555 
5556 	if (statep->es_extended)
5557 		show_ether_xprop(linkid, arg);
5558 
5559 	return (DLADM_WALK_CONTINUE);
5560 }
5561 
5562 /* ARGSUSED */
5563 static void
5564 do_init_secobj(int argc, char **argv, const char *use)
5565 {
5566 	dladm_status_t status;
5567 
5568 	status = dladm_init_secobj();
5569 	if (status != DLADM_STATUS_OK)
5570 		die_dlerr(status, "secure object initialization failed");
5571 }
5572 
5573 /*
5574  * "-R" option support. It is used for live upgrading. Append dladm commands
5575  * to a upgrade script which will be run when the alternative root boots up:
5576  *
5577  * - If the /etc/dladm/datalink.conf file exists on the alternative root,
5578  * append dladm commands to the <altroot>/var/svc/profile/upgrade_datalink
5579  * script. This script will be run as part of the network/physical service.
5580  * We cannot defer this to /var/svc/profile/upgrade because then the
5581  * configuration will not be able to take effect before network/physical
5582  * plumbs various interfaces.
5583  *
5584  * - If the /etc/dladm/datalink.conf file does not exist on the alternative
5585  * root, append dladm commands to the <altroot>/var/svc/profile/upgrade script,
5586  * which will be run in the manifest-import service.
5587  *
5588  * Note that the SMF team is considering to move the manifest-import service
5589  * to be run at the very begining of boot. Once that is done, the need for
5590  * the /var/svc/profile/upgrade_datalink script will not exist any more.
5591  */
5592 static void
5593 altroot_cmd(char *altroot, int argc, char *argv[])
5594 {
5595 	char		path[MAXPATHLEN];
5596 	struct stat	stbuf;
5597 	FILE		*fp;
5598 	int		i;
5599 
5600 	/*
5601 	 * Check for the existence of the /etc/dladm/datalink.conf
5602 	 * configuration file, and determine the name of script file.
5603 	 */
5604 	(void) snprintf(path, MAXPATHLEN, "/%s/etc/dladm/datalink.conf",
5605 	    altroot);
5606 	if (stat(path, &stbuf) < 0) {
5607 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
5608 		    SMF_UPGRADE_FILE);
5609 	} else {
5610 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
5611 		    SMF_UPGRADEDATALINK_FILE);
5612 	}
5613 
5614 	if ((fp = fopen(path, "a+")) == NULL)
5615 		die("operation not supported on %s", altroot);
5616 
5617 	(void) fprintf(fp, "/sbin/dladm ");
5618 	for (i = 0; i < argc; i++) {
5619 		/*
5620 		 * Directly write to the file if it is not the "-R <altroot>"
5621 		 * option. In which case, skip it.
5622 		 */
5623 		if (strcmp(argv[i], "-R") != 0)
5624 			(void) fprintf(fp, "%s ", argv[i]);
5625 		else
5626 			i ++;
5627 	}
5628 	(void) fprintf(fp, "%s\n", SMF_DLADM_UPGRADE_MSG);
5629 	(void) fclose(fp);
5630 	exit(0);
5631 }
5632 
5633 /*
5634  * Convert the string to an integer. Note that the string must not have any
5635  * trailing non-integer characters.
5636  */
5637 static boolean_t
5638 str2int(const char *str, int *valp)
5639 {
5640 	int	val;
5641 	char	*endp = NULL;
5642 
5643 	errno = 0;
5644 	val = strtol(str, &endp, 10);
5645 	if (errno != 0 || *endp != '\0')
5646 		return (B_FALSE);
5647 
5648 	*valp = val;
5649 	return (B_TRUE);
5650 }
5651 
5652 /* PRINTFLIKE1 */
5653 static void
5654 warn(const char *format, ...)
5655 {
5656 	va_list alist;
5657 
5658 	format = gettext(format);
5659 	(void) fprintf(stderr, "%s: warning: ", progname);
5660 
5661 	va_start(alist, format);
5662 	(void) vfprintf(stderr, format, alist);
5663 	va_end(alist);
5664 
5665 	(void) putchar('\n');
5666 }
5667 
5668 /* PRINTFLIKE2 */
5669 static void
5670 warn_dlerr(dladm_status_t err, const char *format, ...)
5671 {
5672 	va_list alist;
5673 	char	errmsg[DLADM_STRSIZE];
5674 
5675 	format = gettext(format);
5676 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
5677 
5678 	va_start(alist, format);
5679 	(void) vfprintf(stderr, format, alist);
5680 	va_end(alist);
5681 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
5682 }
5683 
5684 /* PRINTFLIKE2 */
5685 static void
5686 die_dlerr(dladm_status_t err, const char *format, ...)
5687 {
5688 	va_list alist;
5689 	char	errmsg[DLADM_STRSIZE];
5690 
5691 	format = gettext(format);
5692 	(void) fprintf(stderr, "%s: ", progname);
5693 
5694 	va_start(alist, format);
5695 	(void) vfprintf(stderr, format, alist);
5696 	va_end(alist);
5697 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
5698 
5699 	exit(EXIT_FAILURE);
5700 }
5701 
5702 /* PRINTFLIKE1 */
5703 static void
5704 die(const char *format, ...)
5705 {
5706 	va_list alist;
5707 
5708 	format = gettext(format);
5709 	(void) fprintf(stderr, "%s: ", progname);
5710 
5711 	va_start(alist, format);
5712 	(void) vfprintf(stderr, format, alist);
5713 	va_end(alist);
5714 
5715 	(void) putchar('\n');
5716 	exit(EXIT_FAILURE);
5717 }
5718 
5719 static void
5720 die_optdup(int opt)
5721 {
5722 	die("the option -%c cannot be specified more than once", opt);
5723 }
5724 
5725 static void
5726 die_opterr(int opt, int opterr, const char *usage)
5727 {
5728 	switch (opterr) {
5729 	case ':':
5730 		die("option '-%c' requires a value\nusage: %s", opt,
5731 		    gettext(usage));
5732 		break;
5733 	case '?':
5734 	default:
5735 		die("unrecognized option '-%c'\nusage: %s", opt,
5736 		    gettext(usage));
5737 		break;
5738 	}
5739 }
5740 
5741 static void
5742 show_ether_xprop(datalink_id_t linkid, void *arg)
5743 {
5744 	print_ether_state_t *statep = arg;
5745 	char buf[DLADM_STRSIZE];
5746 	uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf;
5747 	boolean_t add_comma, r1;
5748 	ether_fields_buf_t ebuf;
5749 
5750 	/* capable */
5751 	bzero(&ebuf, sizeof (ebuf));
5752 	(void) snprintf(ebuf.eth_link, sizeof (ebuf.eth_link), "");
5753 
5754 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5755 	    "%s", "capable");
5756 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
5757 
5758 	(void) dladm_get_single_mac_stat(linkid, "cap_autoneg",
5759 	    KSTAT_DATA_UINT32, &autoneg);
5760 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5761 	    "%s", (autoneg ? "yes" : "no"));
5762 
5763 	add_comma = B_FALSE;
5764 	bzero(buf, sizeof (buf));
5765 	r1 = get_speed_duplex(linkid, "cap_1000", buf, "1G", B_FALSE);
5766 	if (r1)
5767 		add_comma = B_TRUE;
5768 	r1 = get_speed_duplex(linkid, "cap_100", buf, "100M", add_comma);
5769 	if (r1)
5770 		add_comma = B_TRUE;
5771 	r1 = get_speed_duplex(linkid, "cap_10", buf, "10M", add_comma);
5772 	add_comma = B_FALSE;
5773 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
5774 
5775 	(void) dladm_get_single_mac_stat(linkid, "cap_pause",
5776 	    KSTAT_DATA_UINT32, &pause);
5777 	(void) dladm_get_single_mac_stat(linkid, "cap_asmpause",
5778 	    KSTAT_DATA_UINT32, &asmpause);
5779 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5780 	    "%s", pause_str(pause, asmpause));
5781 
5782 	(void) dladm_get_single_mac_stat(linkid, "adv_rem_fault",
5783 	    KSTAT_DATA_UINT32, &adv_rf);
5784 	(void) dladm_get_single_mac_stat(linkid, "cap_rem_fault",
5785 	    KSTAT_DATA_UINT32, &cap_rf);
5786 	(void) dladm_get_single_mac_stat(linkid, "lp_rem_fault",
5787 	    KSTAT_DATA_UINT32, &lp_rf);
5788 
5789 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5790 	    "%s", (cap_rf ? "yes" : "no"));
5791 
5792 	dladm_print_output(&statep->es_print, statep->es_parseable,
5793 	    dladm_print_field, &ebuf);
5794 
5795 	/* advertised */
5796 	bzero(&ebuf, sizeof (ebuf));
5797 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5798 	    "%s", "adv");
5799 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
5800 
5801 	(void) dladm_get_single_mac_stat(linkid, "adv_cap_autoneg",
5802 	    KSTAT_DATA_UINT32, &autoneg);
5803 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5804 	    "%s", (autoneg ? "yes" : "no"));
5805 
5806 	add_comma = B_FALSE;
5807 	bzero(buf, sizeof (buf));
5808 	r1 = get_speed_duplex(linkid, "adv_cap_1000", buf, "1G", add_comma);
5809 	if (r1)
5810 		add_comma = B_TRUE;
5811 	r1 = get_speed_duplex(linkid, "adv_cap_100", buf, "100M", add_comma);
5812 	if (r1)
5813 		add_comma = B_TRUE;
5814 	r1 = get_speed_duplex(linkid, "adv_cap_10", buf, "10M", add_comma);
5815 	add_comma = B_FALSE;
5816 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
5817 
5818 	(void) dladm_get_single_mac_stat(linkid, "adv_cap_pause",
5819 	    KSTAT_DATA_UINT32, &pause);
5820 	(void) dladm_get_single_mac_stat(linkid, "adv_cap_asmpause",
5821 	    KSTAT_DATA_UINT32, &asmpause);
5822 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5823 	    "%s", pause_str(pause, asmpause));
5824 
5825 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5826 	    "%s", (adv_rf ? "fault" : "none"));
5827 
5828 	dladm_print_output(&statep->es_print, statep->es_parseable,
5829 	    dladm_print_field, &ebuf);
5830 
5831 	/* peeradv */
5832 	bzero(&ebuf, sizeof (ebuf));
5833 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5834 	    "%s", "peeradv");
5835 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
5836 
5837 	(void) dladm_get_single_mac_stat(linkid, "lp_cap_autoneg",
5838 	    KSTAT_DATA_UINT32, &autoneg);
5839 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5840 	    "%s", (autoneg ? "yes" : "no"));
5841 
5842 	add_comma = B_FALSE;
5843 	bzero(buf, sizeof (buf));
5844 	r1 = get_speed_duplex(linkid, "lp_cap_1000", buf, "1G", add_comma);
5845 	if (r1)
5846 		add_comma = B_TRUE;
5847 	r1 = get_speed_duplex(linkid, "lp_cap_100", buf, "100M", add_comma);
5848 	if (r1)
5849 		add_comma = B_TRUE;
5850 	r1 = get_speed_duplex(linkid, "lp_cap_10", buf, "10M", add_comma);
5851 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
5852 
5853 	(void) dladm_get_single_mac_stat(linkid, "lp_cap_pause",
5854 	    KSTAT_DATA_UINT32, &pause);
5855 	(void) dladm_get_single_mac_stat(linkid, "lp_cap_asmpause",
5856 	    KSTAT_DATA_UINT32, &asmpause);
5857 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5858 	    "%s", pause_str(pause, asmpause));
5859 
5860 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5861 	    "%s", (lp_rf ? "fault" : "none"));
5862 
5863 	dladm_print_output(&statep->es_print, statep->es_parseable,
5864 	    dladm_print_field, &ebuf);
5865 }
5866 
5867 static boolean_t
5868 get_speed_duplex(datalink_id_t linkid, const char *mii_prop_prefix,
5869     char *spbuf, char *sp, boolean_t add_comma)
5870 {
5871 	int speed, duplex = 0;
5872 	boolean_t ret = B_FALSE;
5873 	char mii_prop[DLADM_STRSIZE];
5874 
5875 	(void) snprintf(mii_prop, DLADM_STRSIZE, "%sfdx", mii_prop_prefix);
5876 	(void) dladm_get_single_mac_stat(linkid, mii_prop, KSTAT_DATA_UINT32,
5877 	    &speed);
5878 	if (speed) {
5879 		ret = B_TRUE;
5880 		duplex  |= IS_FDX;
5881 	}
5882 	(void) snprintf(mii_prop, DLADM_STRSIZE, "%shdx", mii_prop_prefix);
5883 	(void) dladm_get_single_mac_stat(linkid, mii_prop,
5884 	    KSTAT_DATA_UINT32, &speed);
5885 	if (speed) {
5886 		ret = B_TRUE;
5887 		duplex |= IS_HDX;
5888 	}
5889 	if (ret) {
5890 		if (add_comma)
5891 			(void) strncat(spbuf, ",", DLADM_STRSIZE);
5892 		(void) strncat(spbuf, sp, DLADM_STRSIZE);
5893 		if ((duplex & (IS_FDX|IS_HDX)) == (IS_FDX|IS_HDX))
5894 			(void) strncat(spbuf, "-fh", DLADM_STRSIZE);
5895 		else if (duplex & IS_FDX)
5896 			(void) strncat(spbuf, "-f", DLADM_STRSIZE);
5897 		else if (duplex & IS_HDX)
5898 			(void) strncat(spbuf, "-h", DLADM_STRSIZE);
5899 	}
5900 	return (ret);
5901 }
5902 
5903 static void
5904 dladm_print_output(print_state_t *statep, boolean_t parseable,
5905     print_callback_t fn, void *arg)
5906 {
5907 	int i;
5908 	char *value;
5909 	print_field_t **pf;
5910 
5911 	pf = statep->ps_fields;
5912 	for (i = 0; i < statep->ps_nfields; i++) {
5913 		statep->ps_lastfield = (i + 1 == statep->ps_nfields);
5914 		value = (*fn)(pf[i], arg);
5915 		if (value != NULL)
5916 			print_field(statep, pf[i], value, parseable);
5917 	}
5918 	(void) putchar('\n');
5919 }
5920 
5921 static void
5922 print_header(print_state_t *ps)
5923 {
5924 	int i;
5925 	print_field_t **pf;
5926 
5927 	pf = ps->ps_fields;
5928 	for (i = 0; i < ps->ps_nfields; i++) {
5929 		ps->ps_lastfield = (i + 1 == ps->ps_nfields);
5930 		print_field(ps, pf[i], pf[i]->pf_header, B_FALSE);
5931 	}
5932 	(void) putchar('\n');
5933 }
5934 
5935 static char *
5936 pause_str(int pause, int asmpause)
5937 {
5938 	if (pause == 1)
5939 		return ("bi");
5940 	if (asmpause == 1)
5941 		return ("tx");
5942 	return ("none");
5943 }
5944 
5945 static boolean_t
5946 link_is_ether(const char *link, datalink_id_t *linkid)
5947 {
5948 	uint32_t media;
5949 	datalink_class_t class;
5950 
5951 	if (dladm_name2info(link, linkid, NULL, &class, &media) ==
5952 	    DLADM_STATUS_OK) {
5953 		if (class == DATALINK_CLASS_PHYS && media == DL_ETHER)
5954 			return (B_TRUE);
5955 	}
5956 	return (B_FALSE);
5957 }
5958