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