xref: /titanic_44/usr/src/cmd/dladm/dladm.c (revision 648495d6a097a39eaebc7e56d25f2463ff3bba65)
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) dladm_aggr_portstate2str(
2003 			    portp->lp_state, buf);
2004 		} else {
2005 			return ("");
2006 		}
2007 		break;
2008 	case AGGR_X_ADDRESS:
2009 		(void) dladm_aggr_macaddr2str(
2010 		    (is_port ? portp->lp_mac : l->laggr_ginfop->lg_mac),
2011 		    buf);
2012 		break;
2013 	case AGGR_X_PORTSTATE:
2014 		if (is_port)
2015 			(void) dladm_aggr_portstate2str(
2016 			    portp->lp_state, buf);
2017 		else
2018 			return ("");
2019 		break;
2020 	}
2021 	return (buf);
2022 
2023 err:
2024 	*stat = status;
2025 	buf[0] = '\0';
2026 	return (buf);
2027 }
2028 
2029 static dladm_status_t
2030 print_aggr_extended(show_grp_state_t *state, const char *link,
2031     dladm_aggr_grp_attr_t *ginfop)
2032 {
2033 	int			i;
2034 	dladm_status_t		status;
2035 	laggr_args_t		largs;
2036 
2037 	if (!state->gs_parseable && !state->gs_printheader) {
2038 		print_header(&state->gs_print);
2039 		state->gs_printheader = B_TRUE;
2040 	}
2041 
2042 	largs.laggr_lport = -1;
2043 	largs.laggr_link = link;
2044 	largs.laggr_ginfop = ginfop;
2045 	largs.laggr_status = &status;
2046 	largs.laggr_parseable = state->gs_parseable;
2047 
2048 	dladm_print_output(&state->gs_print, state->gs_parseable,
2049 	    print_xaggr_callback, &largs);
2050 
2051 	if (status != DLADM_STATUS_OK)
2052 		goto done;
2053 
2054 	for (i = 0; i < ginfop->lg_nports; i++) {
2055 		largs.laggr_lport = i;
2056 		dladm_print_output(&state->gs_print, state->gs_parseable,
2057 		    print_xaggr_callback, &largs);
2058 		if (status != DLADM_STATUS_OK)
2059 			goto done;
2060 	}
2061 
2062 	status = DLADM_STATUS_OK;
2063 done:
2064 	return (status);
2065 }
2066 
2067 
2068 static char *
2069 print_lacp_callback(print_field_t *pf, void *arg)
2070 {
2071 	const laggr_args_t	*l = arg;
2072 	int			portnum;
2073 	static char		buf[DLADM_STRSIZE];
2074 	boolean_t		is_port = (l->laggr_lport >= 0);
2075 	dladm_aggr_port_attr_t	*portp;
2076 	dladm_status_t		*stat, status;
2077 	aggr_lacp_state_t	*lstate;
2078 
2079 	if (!is_port) {
2080 		return (NULL); /* cannot happen! */
2081 	}
2082 
2083 	stat = l->laggr_status;
2084 
2085 	portnum = l->laggr_lport;
2086 	portp = &(l->laggr_ginfop->lg_ports[portnum]);
2087 	if ((status = dladm_datalink_id2info(portp->lp_linkid,
2088 	    NULL, NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) {
2089 			goto err;
2090 	}
2091 	lstate = &(portp->lp_lacp_state);
2092 
2093 	switch (pf->pf_index) {
2094 	case AGGR_L_LINK:
2095 		(void) snprintf(buf, sizeof (buf), "%s",
2096 		    (portnum > 0 ? "" : l->laggr_link));
2097 		break;
2098 
2099 	case AGGR_L_PORT:
2100 		break;
2101 
2102 	case AGGR_L_AGGREGATABLE:
2103 		(void) snprintf(buf, sizeof (buf), "%s",
2104 		    (lstate->bit.aggregation ? "yes" : "no"));
2105 		break;
2106 
2107 	case AGGR_L_SYNC:
2108 		(void) snprintf(buf, sizeof (buf), "%s",
2109 		    (lstate->bit.sync ? "yes" : "no"));
2110 		break;
2111 
2112 	case AGGR_L_COLL:
2113 		(void) snprintf(buf, sizeof (buf), "%s",
2114 		    (lstate->bit.collecting ? "yes" : "no"));
2115 		break;
2116 
2117 	case AGGR_L_DIST:
2118 		(void) snprintf(buf, sizeof (buf), "%s",
2119 		    (lstate->bit.distributing ? "yes" : "no"));
2120 		break;
2121 
2122 	case AGGR_L_DEFAULTED:
2123 		(void) snprintf(buf, sizeof (buf), "%s",
2124 		    (lstate->bit.defaulted ? "yes" : "no"));
2125 		break;
2126 
2127 	case AGGR_L_EXPIRED:
2128 		(void) snprintf(buf, sizeof (buf), "%s",
2129 		    (lstate->bit.expired ? "yes" : "no"));
2130 		break;
2131 	}
2132 
2133 	*stat = DLADM_STATUS_OK;
2134 	return (buf);
2135 
2136 err:
2137 	*stat = status;
2138 	buf[0] = '\0';
2139 	return (buf);
2140 }
2141 
2142 static dladm_status_t
2143 print_aggr_lacp(show_grp_state_t *state, const char *link,
2144     dladm_aggr_grp_attr_t *ginfop)
2145 {
2146 	int		i;
2147 	dladm_status_t	status;
2148 	laggr_args_t	largs;
2149 
2150 	if (!state->gs_parseable && !state->gs_printheader) {
2151 		print_header(&state->gs_print);
2152 		state->gs_printheader = B_TRUE;
2153 	}
2154 
2155 	largs.laggr_link = link;
2156 	largs.laggr_ginfop = ginfop;
2157 	largs.laggr_status = &status;
2158 
2159 	for (i = 0; i < ginfop->lg_nports; i++) {
2160 		largs.laggr_lport = i;
2161 		dladm_print_output(&state->gs_print, state->gs_parseable,
2162 		    print_lacp_callback, &largs);
2163 		if (status != DLADM_STATUS_OK)
2164 			goto done;
2165 	}
2166 
2167 	status = DLADM_STATUS_OK;
2168 done:
2169 	return (status);
2170 }
2171 
2172 static char *
2173 print_aggr_stats_callback(print_field_t *pf, void *arg)
2174 {
2175 	const laggr_args_t	*l = arg;
2176 	int 			portnum;
2177 	static char		buf[DLADM_STRSIZE];
2178 	boolean_t		is_port = (l->laggr_lport >= 0);
2179 	dladm_aggr_port_attr_t	*portp;
2180 	dladm_phys_attr_t	dpa;
2181 	dladm_status_t		*stat, status;
2182 	pktsum_t		port_stat, diff_stats;
2183 
2184 	stat = l->laggr_status;
2185 	*stat = DLADM_STATUS_OK;
2186 
2187 	if (is_port) {
2188 		portnum = l->laggr_lport;
2189 		portp = &(l->laggr_ginfop->lg_ports[portnum]);
2190 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
2191 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
2192 			goto err;
2193 		}
2194 
2195 		get_mac_stats(dpa.dp_dev, &port_stat);
2196 
2197 		if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL,
2198 		    NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) {
2199 			goto err;
2200 		}
2201 
2202 		stats_diff(&diff_stats, &port_stat, l->laggr_prevstats);
2203 	}
2204 
2205 	switch (pf->pf_index) {
2206 	case AGGR_S_LINK:
2207 		(void) snprintf(buf, sizeof (buf), "%s",
2208 		    (is_port ? "" : l->laggr_link));
2209 		break;
2210 	case AGGR_S_PORT:
2211 		if (is_port)
2212 			break;
2213 		return ("");
2214 		break;
2215 
2216 	case AGGR_S_IPKTS:
2217 		if (is_port) {
2218 			(void) snprintf(buf, sizeof (buf), "%llu",
2219 			    diff_stats.ipackets);
2220 		} else {
2221 			(void) snprintf(buf, sizeof (buf), "%llu",
2222 			    l->laggr_pktsumtot->ipackets);
2223 		}
2224 		break;
2225 
2226 	case AGGR_S_RBYTES:
2227 		if (is_port) {
2228 			(void) snprintf(buf, sizeof (buf), "%llu",
2229 			    diff_stats.rbytes);
2230 		} else {
2231 			(void) snprintf(buf, sizeof (buf), "%llu",
2232 			    l->laggr_pktsumtot->rbytes);
2233 		}
2234 		break;
2235 
2236 	case AGGR_S_OPKTS:
2237 		if (is_port) {
2238 			(void) snprintf(buf, sizeof (buf), "%llu",
2239 			    diff_stats.opackets);
2240 		} else {
2241 			(void) snprintf(buf, sizeof (buf), "%llu",
2242 			    l->laggr_pktsumtot->opackets);
2243 		}
2244 		break;
2245 	case AGGR_S_OBYTES:
2246 		if (is_port) {
2247 			(void) snprintf(buf, sizeof (buf), "%llu",
2248 			    diff_stats.obytes);
2249 		} else {
2250 			(void) snprintf(buf, sizeof (buf), "%llu",
2251 			    l->laggr_pktsumtot->obytes);
2252 
2253 		}
2254 		break;
2255 
2256 	case AGGR_S_IPKTDIST:
2257 		if (is_port) {
2258 			(void) snprintf(buf, sizeof (buf), "%-6.1f",
2259 			    (double)diff_stats.opackets/
2260 			    (double)l->laggr_pktsumtot->ipackets * 100);
2261 		} else {
2262 			return ("");
2263 		}
2264 		break;
2265 	case AGGR_S_OPKTDIST:
2266 		if (is_port) {
2267 			(void) snprintf(buf, sizeof (buf), "%-6.1f",
2268 			    (double)diff_stats.opackets/
2269 			    (double)l->laggr_pktsumtot->opackets * 100);
2270 		} else {
2271 			return ("");
2272 		}
2273 		break;
2274 	}
2275 	return (buf);
2276 
2277 err:
2278 	*stat = status;
2279 	buf[0] = '\0';
2280 	return (buf);
2281 }
2282 
2283 static dladm_status_t
2284 print_aggr_stats(show_grp_state_t *state, const char *link,
2285     dladm_aggr_grp_attr_t *ginfop)
2286 {
2287 	dladm_phys_attr_t	dpa;
2288 	dladm_aggr_port_attr_t	*portp;
2289 	pktsum_t		pktsumtot, port_stat;
2290 	dladm_status_t		status;
2291 	int			i;
2292 	laggr_args_t		largs;
2293 
2294 	/* sum the ports statistics */
2295 	bzero(&pktsumtot, sizeof (pktsumtot));
2296 
2297 	for (i = 0; i < ginfop->lg_nports; i++) {
2298 
2299 		portp = &(ginfop->lg_ports[i]);
2300 		if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
2301 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
2302 			goto done;
2303 		}
2304 
2305 		get_mac_stats(dpa.dp_dev, &port_stat);
2306 		stats_total(&pktsumtot, &port_stat, &state->gs_prevstats[i]);
2307 	}
2308 
2309 	if (!state->gs_parseable && !state->gs_printheader) {
2310 		print_header(&state->gs_print);
2311 		state->gs_printheader = B_TRUE;
2312 	}
2313 
2314 	largs.laggr_lport = -1;
2315 	largs.laggr_link = link;
2316 	largs.laggr_ginfop = ginfop;
2317 	largs.laggr_status = &status;
2318 	largs.laggr_pktsumtot = &pktsumtot;
2319 
2320 	dladm_print_output(&state->gs_print, state->gs_parseable,
2321 	    print_aggr_stats_callback, &largs);
2322 
2323 	if (status != DLADM_STATUS_OK)
2324 		goto done;
2325 
2326 	for (i = 0; i < ginfop->lg_nports; i++) {
2327 		largs.laggr_lport = i;
2328 		largs.laggr_prevstats = &state->gs_prevstats[i];
2329 		dladm_print_output(&state->gs_print, state->gs_parseable,
2330 		    print_aggr_stats_callback, &largs);
2331 		if (status != DLADM_STATUS_OK)
2332 			goto done;
2333 	}
2334 
2335 	status = DLADM_STATUS_OK;
2336 done:
2337 	return (status);
2338 }
2339 
2340 static dladm_status_t
2341 print_aggr(show_grp_state_t *state, datalink_id_t linkid)
2342 {
2343 	char			link[MAXLINKNAMELEN];
2344 	dladm_aggr_grp_attr_t	ginfo;
2345 	uint32_t		flags;
2346 	dladm_status_t		status;
2347 
2348 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link,
2349 	    MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
2350 		return (status);
2351 	}
2352 
2353 	if (!(state->gs_flags & flags))
2354 		return (DLADM_STATUS_NOTFOUND);
2355 
2356 	status = dladm_aggr_info(linkid, &ginfo, state->gs_flags);
2357 	if (status != DLADM_STATUS_OK)
2358 		return (status);
2359 
2360 	if (state->gs_lacp)
2361 		status = print_aggr_lacp(state, link, &ginfo);
2362 	else if (state->gs_extended)
2363 		status = print_aggr_extended(state, link, &ginfo);
2364 	else if (state->gs_stats)
2365 		status = print_aggr_stats(state, link, &ginfo);
2366 	else {
2367 		status = print_aggr_info(state, link, &ginfo);
2368 	}
2369 
2370 done:
2371 	free(ginfo.lg_ports);
2372 	return (status);
2373 }
2374 
2375 static int
2376 show_aggr(datalink_id_t linkid, void *arg)
2377 {
2378 	show_grp_state_t	*state = arg;
2379 	dladm_status_t		status;
2380 
2381 	status = print_aggr(state, linkid);
2382 	if (status != DLADM_STATUS_OK)
2383 		goto done;
2384 
2385 done:
2386 	state->gs_status = status;
2387 	return (DLADM_WALK_CONTINUE);
2388 }
2389 
2390 static char *
2391 print_dev(print_field_t *pf, void *arg)
2392 {
2393 	const char *dev = arg;
2394 	static char buf[DLADM_STRSIZE];
2395 
2396 	switch (pf->pf_index) {
2397 	case DEV_LINK:
2398 		(void) snprintf(buf, sizeof (buf), "%s", dev);
2399 		break;
2400 	case DEV_STATE:
2401 		(void) get_linkstate(dev, B_FALSE, buf);
2402 		break;
2403 	case DEV_SPEED:
2404 		(void) snprintf(buf, sizeof (buf), "%uMb",
2405 		    (unsigned int)(get_ifspeed(dev, B_FALSE) / 1000000ull));
2406 		break;
2407 	case DEV_DUPLEX:
2408 		(void) get_linkduplex(dev, B_FALSE, buf);
2409 		break;
2410 	default:
2411 		die("invalid index '%d'", pf->pf_index);
2412 		break;
2413 	}
2414 	return (buf);
2415 }
2416 
2417 static int
2418 show_dev(const char *dev, void *arg)
2419 {
2420 	show_state_t	*state = arg;
2421 
2422 	if (!state->ls_parseable && !state->ls_printheader) {
2423 		print_header(&state->ls_print);
2424 		state->ls_printheader = B_TRUE;
2425 	}
2426 
2427 	dladm_print_output(&state->ls_print, state->ls_parseable,
2428 	    print_dev, (void *)dev);
2429 
2430 	return (DLADM_WALK_CONTINUE);
2431 }
2432 
2433 static char *
2434 print_dev_stats(print_field_t *pf, void *arg)
2435 {
2436 	dev_args_t *dargs = arg;
2437 	pktsum_t *diff_stats = dargs->devs_psum;
2438 	static char buf[DLADM_STRSIZE];
2439 
2440 	switch (pf->pf_index) {
2441 	case DEVS_LINK:
2442 		(void) snprintf(buf, sizeof (buf), "%s", dargs->devs_link);
2443 		break;
2444 	case DEVS_IPKTS:
2445 		(void) snprintf(buf, sizeof (buf), "%llu",
2446 		    diff_stats->ipackets);
2447 		break;
2448 	case DEVS_RBYTES:
2449 		(void) snprintf(buf, sizeof (buf), "%llu",
2450 		    diff_stats->rbytes);
2451 		break;
2452 	case DEVS_IERRORS:
2453 		(void) snprintf(buf, sizeof (buf), "%u",
2454 		    diff_stats->ierrors);
2455 		break;
2456 	case DEVS_OPKTS:
2457 		(void) snprintf(buf, sizeof (buf), "%llu",
2458 		    diff_stats->opackets);
2459 		break;
2460 	case DEVS_OBYTES:
2461 		(void) snprintf(buf, sizeof (buf), "%llu",
2462 		    diff_stats->obytes);
2463 		break;
2464 	case DEVS_OERRORS:
2465 		(void) snprintf(buf, sizeof (buf), "%u",
2466 		    diff_stats->oerrors);
2467 		break;
2468 	default:
2469 		die("invalid input");
2470 		break;
2471 	}
2472 	return (buf);
2473 }
2474 
2475 static int
2476 show_dev_stats(const char *dev, void *arg)
2477 {
2478 	show_state_t *state = arg;
2479 	pktsum_t stats, diff_stats;
2480 	dev_args_t dargs;
2481 
2482 	if (state->ls_firstonly) {
2483 		if (state->ls_donefirst)
2484 			return (DLADM_WALK_CONTINUE);
2485 		state->ls_donefirst = B_TRUE;
2486 	} else {
2487 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
2488 	}
2489 
2490 	get_mac_stats(dev, &stats);
2491 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
2492 
2493 	dargs.devs_link = (char *)dev;
2494 	dargs.devs_psum = &diff_stats;
2495 	dladm_print_output(&state->ls_print, state->ls_parseable,
2496 	    print_dev_stats, &dargs);
2497 
2498 	state->ls_prevstats = stats;
2499 	return (DLADM_WALK_CONTINUE);
2500 }
2501 
2502 static void
2503 do_show_link(int argc, char *argv[], const char *use)
2504 {
2505 	int		option;
2506 	boolean_t	s_arg = B_FALSE;
2507 	boolean_t	i_arg = B_FALSE;
2508 	uint32_t	flags = DLADM_OPT_ACTIVE;
2509 	boolean_t	p_arg = B_FALSE;
2510 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2511 	int		interval = 0;
2512 	show_state_t	state;
2513 	dladm_status_t	status;
2514 	boolean_t	o_arg = B_FALSE;
2515 	char		*fields_str = NULL;
2516 	print_field_t	**fields;
2517 	uint_t		nfields;
2518 	char		*all_active_fields = "link,class,mtu,state,over";
2519 	char		*all_inactive_fields = "link,class,over";
2520 
2521 	bzero(&state, sizeof (state));
2522 
2523 	opterr = 0;
2524 	while ((option = getopt_long(argc, argv, ":pPsi:o:",
2525 	    show_lopts, NULL)) != -1) {
2526 		switch (option) {
2527 		case 'p':
2528 			if (p_arg)
2529 				die_optdup(option);
2530 
2531 			p_arg = B_TRUE;
2532 			break;
2533 		case 's':
2534 			if (s_arg)
2535 				die_optdup(option);
2536 
2537 			s_arg = B_TRUE;
2538 			break;
2539 		case 'P':
2540 			if (flags != DLADM_OPT_ACTIVE)
2541 				die_optdup(option);
2542 
2543 			flags = DLADM_OPT_PERSIST;
2544 			break;
2545 		case 'o':
2546 			o_arg = B_TRUE;
2547 			fields_str = optarg;
2548 			break;
2549 		case 'i':
2550 			if (i_arg)
2551 				die_optdup(option);
2552 
2553 			i_arg = B_TRUE;
2554 			if (!str2int(optarg, &interval) || interval == 0)
2555 				die("invalid interval value '%s'", optarg);
2556 			break;
2557 		default:
2558 			die_opterr(optopt, option, use);
2559 			break;
2560 		}
2561 	}
2562 
2563 	if (i_arg && !s_arg)
2564 		die("the option -i can be used only with -s");
2565 
2566 	if (s_arg && (p_arg || flags != DLADM_OPT_ACTIVE))
2567 		die("the option -%c cannot be used with -s", p_arg ? 'p' : 'P');
2568 
2569 	/* get link name (optional last argument) */
2570 	if (optind == (argc-1)) {
2571 		uint32_t	f;
2572 
2573 		if ((status = dladm_name2info(argv[optind], &linkid, &f,
2574 		    NULL, NULL)) != DLADM_STATUS_OK) {
2575 			die_dlerr(status, "link %s is not valid", argv[optind]);
2576 		}
2577 
2578 		if (!(f & flags)) {
2579 			die_dlerr(DLADM_STATUS_BADARG, "link %s is %s",
2580 			    argv[optind], flags == DLADM_OPT_PERSIST ?
2581 			    "a temporary link" : "temporarily removed");
2582 		}
2583 	} else if (optind != argc) {
2584 		usage();
2585 	}
2586 
2587 	if (s_arg) {
2588 		link_stats(linkid, interval);
2589 		return;
2590 	}
2591 
2592 	state.ls_parseable = p_arg;
2593 	state.ls_flags = flags;
2594 	state.ls_donefirst = B_FALSE;
2595 
2596 	if (p_arg && !o_arg)
2597 		die("-p requires -o");
2598 
2599 	if (p_arg && strcasecmp(fields_str, "all") == 0)
2600 		die("\"-o all\" is invalid with -p");
2601 
2602 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
2603 		if (state.ls_flags & DLADM_OPT_ACTIVE)
2604 			fields_str = all_active_fields;
2605 		else
2606 			fields_str = all_inactive_fields;
2607 	}
2608 
2609 
2610 	fields = parse_output_fields(fields_str, link_fields, DEV_LINK_FIELDS,
2611 	    CMD_TYPE_ANY, &nfields);
2612 
2613 	if (fields == NULL)
2614 		die("invalid field(s) specified");
2615 
2616 	state.ls_print.ps_fields = fields;
2617 	state.ls_print.ps_nfields = nfields;
2618 
2619 	if (linkid == DATALINK_ALL_LINKID) {
2620 		(void) dladm_walk_datalink_id(show_link, &state,
2621 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
2622 	} else {
2623 		(void) show_link(linkid, &state);
2624 		if (state.ls_status != DLADM_STATUS_OK) {
2625 			die_dlerr(state.ls_status, "failed to show link %s",
2626 			    argv[optind]);
2627 		}
2628 	}
2629 }
2630 
2631 static void
2632 do_show_aggr(int argc, char *argv[], const char *use)
2633 {
2634 	boolean_t		L_arg = B_FALSE;
2635 	boolean_t		s_arg = B_FALSE;
2636 	boolean_t		i_arg = B_FALSE;
2637 	boolean_t		p_arg = B_FALSE;
2638 	boolean_t		x_arg = B_FALSE;
2639 	show_grp_state_t	state;
2640 	uint32_t		flags = DLADM_OPT_ACTIVE;
2641 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
2642 	int			option;
2643 	int			interval = 0;
2644 	int			key;
2645 	dladm_status_t		status;
2646 	boolean_t	o_arg = B_FALSE;
2647 	char		*fields_str = NULL;
2648 	print_field_t   **fields;
2649 	uint_t		nfields;
2650 	char		*all_fields =
2651 	    "link,policy,addrpolicy,lacpactivity,lacptimer,flags";
2652 	char		*all_lacp_fields =
2653 	    "link,port,aggregatable,sync,coll,dist,defaulted,expired";
2654 	char		*all_stats_fields =
2655 	    "link,port,ipackets,rbytes,opackets,obytes,ipktdist,opktdist";
2656 	char		*all_extended_fields =
2657 	    "link,port,speed,duplex,state,address,portstate";
2658 	print_field_t		*pf;
2659 	int			pfmax;
2660 
2661 	bzero(&state, sizeof (state));
2662 
2663 	opterr = 0;
2664 	while ((option = getopt_long(argc, argv, ":LpPxsi:o:",
2665 	    show_lopts, NULL)) != -1) {
2666 		switch (option) {
2667 		case 'L':
2668 			if (L_arg)
2669 				die_optdup(option);
2670 
2671 			L_arg = B_TRUE;
2672 			break;
2673 		case 'p':
2674 			if (p_arg)
2675 				die_optdup(option);
2676 
2677 			p_arg = B_TRUE;
2678 			break;
2679 		case 'x':
2680 			if (x_arg)
2681 				die_optdup(option);
2682 
2683 			x_arg = B_TRUE;
2684 			break;
2685 		case 'P':
2686 			if (flags != DLADM_OPT_ACTIVE)
2687 				die_optdup(option);
2688 
2689 			flags = DLADM_OPT_PERSIST;
2690 			break;
2691 		case 's':
2692 			if (s_arg)
2693 				die_optdup(option);
2694 
2695 			s_arg = B_TRUE;
2696 			break;
2697 		case 'o':
2698 			o_arg = B_TRUE;
2699 			fields_str = optarg;
2700 			break;
2701 		case 'i':
2702 			if (i_arg)
2703 				die_optdup(option);
2704 
2705 			i_arg = B_TRUE;
2706 			if (!str2int(optarg, &interval) || interval == 0)
2707 				die("invalid interval value '%s'", optarg);
2708 			break;
2709 		default:
2710 			die_opterr(optopt, option, use);
2711 			break;
2712 		}
2713 	}
2714 
2715 	if (p_arg && !o_arg)
2716 		die("-p requires -o");
2717 
2718 	if (p_arg && strcasecmp(fields_str, "all") == 0)
2719 		die("\"-o all\" is invalid with -p");
2720 
2721 	if (i_arg && !s_arg)
2722 		die("the option -i can be used only with -s");
2723 
2724 	if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) {
2725 		die("the option -%c cannot be used with -s",
2726 		    L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P')));
2727 	}
2728 
2729 	if (L_arg && flags != DLADM_OPT_ACTIVE)
2730 		die("the option -P cannot be used with -L");
2731 
2732 	if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE))
2733 		die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P');
2734 
2735 	/* get aggregation key or aggrname (optional last argument) */
2736 	if (optind == (argc-1)) {
2737 		if (!str2int(argv[optind], &key)) {
2738 			status = dladm_name2info(argv[optind], &linkid, NULL,
2739 			    NULL, NULL);
2740 		} else {
2741 			status = dladm_key2linkid((uint16_t)key,
2742 			    &linkid, DLADM_OPT_ACTIVE);
2743 		}
2744 
2745 		if (status != DLADM_STATUS_OK)
2746 			die("non-existent aggregation '%s'", argv[optind]);
2747 
2748 	} else if (optind != argc) {
2749 		usage();
2750 	}
2751 
2752 	bzero(&state, sizeof (state));
2753 	state.gs_lacp = L_arg;
2754 	state.gs_stats = s_arg;
2755 	state.gs_flags = flags;
2756 	state.gs_parseable = p_arg;
2757 	state.gs_extended = x_arg;
2758 
2759 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
2760 		if (state.gs_lacp)
2761 			fields_str = all_lacp_fields;
2762 		else if (state.gs_stats)
2763 			fields_str = all_stats_fields;
2764 		else if (state.gs_extended)
2765 			fields_str = all_extended_fields;
2766 		else
2767 			fields_str = all_fields;
2768 	}
2769 
2770 	if (state.gs_lacp) {
2771 		pf = aggr_l_fields;
2772 		pfmax = AGGR_L_MAX_FIELDS;
2773 	} else if (state.gs_stats) {
2774 		pf = aggr_s_fields;
2775 		pfmax = AGGR_S_MAX_FIELDS;
2776 	} else if (state.gs_extended) {
2777 		pf = aggr_x_fields;
2778 		pfmax = AGGR_X_MAX_FIELDS;
2779 	} else {
2780 		pf = laggr_fields;
2781 		pfmax = LAGGR_MAX_FIELDS;
2782 	}
2783 	fields = parse_output_fields(fields_str, pf, pfmax, CMD_TYPE_ANY,
2784 	    &nfields);
2785 
2786 	if (fields == NULL) {
2787 		die("invalid field(s) specified");
2788 		return;
2789 	}
2790 
2791 	state.gs_print.ps_fields = fields;
2792 	state.gs_print.ps_nfields = nfields;
2793 
2794 	if (s_arg) {
2795 		aggr_stats(linkid, &state, interval);
2796 		return;
2797 	}
2798 
2799 	if (linkid == DATALINK_ALL_LINKID) {
2800 		(void) dladm_walk_datalink_id(show_aggr, &state,
2801 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
2802 	} else {
2803 		(void) show_aggr(linkid, &state);
2804 		if (state.gs_status != DLADM_STATUS_OK) {
2805 			die_dlerr(state.gs_status, "failed to show aggr %s",
2806 			    argv[optind]);
2807 		}
2808 	}
2809 }
2810 
2811 static void
2812 do_show_dev(int argc, char *argv[], const char *use)
2813 {
2814 	int		option;
2815 	char		*dev = NULL;
2816 	boolean_t	s_arg = B_FALSE;
2817 	boolean_t	i_arg = B_FALSE;
2818 	boolean_t	o_arg = B_FALSE;
2819 	boolean_t	p_arg = B_FALSE;
2820 	datalink_id_t	linkid;
2821 	int		interval = 0;
2822 	show_state_t	state;
2823 	char		*fields_str = NULL;
2824 	print_field_t	**fields;
2825 	uint_t		nfields;
2826 	char		*all_fields = "link,state,speed,duplex";
2827 	static char	*allstat_fields =
2828 	    "link,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
2829 
2830 	bzero(&state, sizeof (state));
2831 	fields_str = all_fields;
2832 
2833 	opterr = 0;
2834 	while ((option = getopt_long(argc, argv, ":psi:o:",
2835 	    show_lopts, NULL)) != -1) {
2836 		switch (option) {
2837 		case 'p':
2838 			if (p_arg)
2839 				die_optdup(option);
2840 
2841 			p_arg = B_TRUE;
2842 			break;
2843 		case 's':
2844 			if (s_arg)
2845 				die_optdup(option);
2846 
2847 			s_arg = B_TRUE;
2848 			break;
2849 		case 'o':
2850 			o_arg = B_TRUE;
2851 			fields_str = optarg;
2852 			break;
2853 		case 'i':
2854 			if (i_arg)
2855 				die_optdup(option);
2856 
2857 			i_arg = B_TRUE;
2858 			if (!str2int(optarg, &interval) || interval == 0)
2859 				die("invalid interval value '%s'", optarg);
2860 			break;
2861 		default:
2862 			die_opterr(optopt, option, use);
2863 			break;
2864 		}
2865 	}
2866 
2867 	if (p_arg && !o_arg)
2868 		die("-p requires -o");
2869 
2870 	if (p_arg && strcasecmp(fields_str, "all") == 0)
2871 		die("\"-o all\" is invalid with -p");
2872 
2873 	if (i_arg && !s_arg)
2874 		die("the option -i can be used only with -s");
2875 
2876 	if (o_arg && strcasecmp(fields_str, "all") == 0) {
2877 		if (!s_arg)
2878 			fields_str = all_fields;
2879 		else
2880 			fields_str = allstat_fields;
2881 	}
2882 
2883 	if (!o_arg && s_arg)
2884 		fields_str = allstat_fields;
2885 
2886 	if (s_arg && p_arg)
2887 		die("the option -s cannot be used with -p");
2888 
2889 	/* get dev name (optional last argument) */
2890 	if (optind == (argc-1)) {
2891 		uint32_t flags;
2892 
2893 		dev = argv[optind];
2894 
2895 		if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK)
2896 			die("invalid device %s", dev);
2897 
2898 		if ((dladm_datalink_id2info(linkid, &flags, NULL, NULL,
2899 		    NULL, 0) != DLADM_STATUS_OK) ||
2900 		    !(flags & DLADM_OPT_ACTIVE)) {
2901 			die("device %s has been removed", dev);
2902 		}
2903 	} else if (optind != argc) {
2904 		usage();
2905 	}
2906 
2907 	state.ls_parseable = p_arg;
2908 	state.ls_donefirst = B_FALSE;
2909 
2910 	if (s_arg) {
2911 		dev_stats(dev, interval, fields_str, &state);
2912 		return;
2913 	}
2914 
2915 	fields = parse_output_fields(fields_str, dev_fields, DEV_MAX_FIELDS,
2916 	    CMD_TYPE_ANY, &nfields);
2917 
2918 	if (fields == NULL) {
2919 		die("invalid field(s) specified");
2920 		return;
2921 	}
2922 
2923 	state.ls_print.ps_fields = fields;
2924 	state.ls_print.ps_nfields = nfields;
2925 
2926 	if (dev == NULL) {
2927 		(void) dladm_mac_walk(show_dev, &state);
2928 	} else {
2929 		(void) show_dev(dev, &state);
2930 	}
2931 }
2932 
2933 
2934 static dladm_status_t
2935 print_phys(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *pattr)
2936 {
2937 	char			link[MAXLINKNAMELEN];
2938 	dladm_phys_attr_t	dpa;
2939 	uint32_t		flags;
2940 	datalink_class_t	class;
2941 	uint32_t		media;
2942 	dladm_status_t		status;
2943 
2944 	if ((status = dladm_datalink_id2info(linkid, &flags, &class, &media,
2945 	    link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
2946 		goto done;
2947 	}
2948 
2949 	if (class != DATALINK_CLASS_PHYS) {
2950 		status = DLADM_STATUS_BADARG;
2951 		goto done;
2952 	}
2953 
2954 	if (!(state->ls_flags & flags)) {
2955 		status = DLADM_STATUS_NOTFOUND;
2956 		goto done;
2957 	}
2958 
2959 	status = dladm_phys_info(linkid, &dpa, state->ls_flags);
2960 	if (status != DLADM_STATUS_OK)
2961 		goto done;
2962 
2963 	(void) snprintf(pattr->link_phys_device,
2964 	    sizeof (pattr->link_phys_device), "%s", dpa.dp_dev);
2965 	(void) dladm_media2str(media, pattr->link_phys_media);
2966 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2967 		boolean_t	islink;
2968 
2969 		if (!dpa.dp_novanity) {
2970 			(void) strlcpy(pattr->link_name, link,
2971 			    sizeof (pattr->link_name));
2972 			islink = B_TRUE;
2973 		} else {
2974 			/*
2975 			 * This is a physical link that does not have
2976 			 * vanity naming support.
2977 			 */
2978 			(void) strlcpy(pattr->link_name, dpa.dp_dev,
2979 			    sizeof (pattr->link_name));
2980 			islink = B_FALSE;
2981 		}
2982 
2983 		(void) get_linkstate(pattr->link_name, islink,
2984 		    pattr->link_phys_state);
2985 		(void) snprintf(pattr->link_phys_speed,
2986 		    sizeof (pattr->link_phys_speed), "%u",
2987 		    (uint_t)((get_ifspeed(pattr->link_name,
2988 		    islink)) / 1000000ull));
2989 		(void) get_linkduplex(pattr->link_name, islink,
2990 		    pattr->link_phys_duplex);
2991 	} else {
2992 		(void) snprintf(pattr->link_name, sizeof (pattr->link_name),
2993 		    "%s", link);
2994 		(void) snprintf(pattr->link_flags, sizeof (pattr->link_flags),
2995 		    "%c----", flags & DLADM_OPT_ACTIVE ? '-' : 'r');
2996 	}
2997 
2998 done:
2999 	return (status);
3000 }
3001 
3002 static int
3003 show_phys(datalink_id_t linkid, void *arg)
3004 {
3005 	show_state_t	*state = arg;
3006 	dladm_status_t	status;
3007 	link_fields_buf_t	pattr;
3008 
3009 	status = print_phys(state, linkid, &pattr);
3010 	if (status != DLADM_STATUS_OK)
3011 		goto done;
3012 
3013 	if (!state->ls_parseable && !state->ls_printheader) {
3014 		print_header(&state->ls_print);
3015 		state->ls_printheader = B_TRUE;
3016 	}
3017 
3018 	dladm_print_output(&state->ls_print, state->ls_parseable,
3019 	    dladm_print_field, (void *)&pattr);
3020 
3021 done:
3022 	state->ls_status = status;
3023 	return (DLADM_WALK_CONTINUE);
3024 }
3025 
3026 
3027 /*
3028  * Print the active topology information.
3029  */
3030 static dladm_status_t
3031 print_vlan(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *l)
3032 {
3033 	dladm_vlan_attr_t	vinfo;
3034 	uint32_t		flags;
3035 	dladm_status_t		status;
3036 
3037 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL,
3038 	    l->link_name, sizeof (l->link_name))) != DLADM_STATUS_OK) {
3039 		goto done;
3040 	}
3041 
3042 	if (!(state->ls_flags & flags)) {
3043 		status = DLADM_STATUS_NOTFOUND;
3044 		goto done;
3045 	}
3046 
3047 	if ((status = dladm_vlan_info(linkid, &vinfo, state->ls_flags)) !=
3048 	    DLADM_STATUS_OK || (status = dladm_datalink_id2info(
3049 	    vinfo.dv_linkid, NULL, NULL, NULL, l->link_over,
3050 	    sizeof (l->link_over))) != DLADM_STATUS_OK) {
3051 		goto done;
3052 	}
3053 
3054 	(void) snprintf(l->link_vlan_vid, sizeof (l->link_vlan_vid), "%d",
3055 	    vinfo.dv_vid);
3056 	(void) snprintf(l->link_flags, sizeof (l->link_flags), "%c%c---",
3057 	    vinfo.dv_force ? 'f' : '-', vinfo.dv_implicit ? 'i' : '-');
3058 
3059 done:
3060 	return (status);
3061 }
3062 
3063 static int
3064 show_vlan(datalink_id_t linkid, void *arg)
3065 {
3066 	show_state_t	*state = arg;
3067 	dladm_status_t	status;
3068 	link_fields_buf_t	lbuf;
3069 
3070 	status = print_vlan(state, linkid, &lbuf);
3071 	if (status != DLADM_STATUS_OK)
3072 		goto done;
3073 
3074 	if (!state->ls_parseable && !state->ls_printheader) {
3075 		print_header(&state->ls_print);
3076 		state->ls_printheader = B_TRUE;
3077 	}
3078 
3079 	dladm_print_output(&state->ls_print, state->ls_parseable,
3080 	    dladm_print_field, (void *)&lbuf);
3081 
3082 done:
3083 	state->ls_status = status;
3084 	return (DLADM_WALK_CONTINUE);
3085 }
3086 
3087 static void
3088 do_show_phys(int argc, char *argv[], const char *use)
3089 {
3090 	int		option;
3091 	uint32_t	flags = DLADM_OPT_ACTIVE;
3092 	boolean_t	p_arg = B_FALSE;
3093 	boolean_t	o_arg = B_FALSE;
3094 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
3095 	show_state_t	state;
3096 	dladm_status_t	status;
3097 	char			*fields_str = NULL;
3098 	print_field_t		**fields;
3099 	uint_t			nfields;
3100 	char			*all_active_fields =
3101 	    "link,media,state,speed,duplex,device";
3102 	char			*all_inactive_fields =
3103 	    "link,device,media,flags";
3104 
3105 	bzero(&state, sizeof (state));
3106 	opterr = 0;
3107 	while ((option = getopt_long(argc, argv, ":pPo:",
3108 	    show_lopts, NULL)) != -1) {
3109 		switch (option) {
3110 		case 'p':
3111 			if (p_arg)
3112 				die_optdup(option);
3113 
3114 			p_arg = B_TRUE;
3115 			break;
3116 		case 'P':
3117 			if (flags != DLADM_OPT_ACTIVE)
3118 				die_optdup(option);
3119 
3120 			flags = DLADM_OPT_PERSIST;
3121 			break;
3122 		case 'o':
3123 			o_arg = B_TRUE;
3124 			fields_str = optarg;
3125 			break;
3126 		default:
3127 			die_opterr(optopt, option, use);
3128 			break;
3129 		}
3130 	}
3131 
3132 	if (p_arg && !o_arg)
3133 		die("-p requires -o");
3134 
3135 	if (p_arg && strcasecmp(fields_str, "all") == 0)
3136 		die("\"-o all\" is invalid with -p");
3137 
3138 	/* get link name (optional last argument) */
3139 	if (optind == (argc-1)) {
3140 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3141 		    NULL, NULL)) != DLADM_STATUS_OK) {
3142 			die_dlerr(status, "link %s is not valid", argv[optind]);
3143 		}
3144 	} else if (optind != argc) {
3145 		usage();
3146 	}
3147 
3148 	state.ls_parseable = p_arg;
3149 	state.ls_flags = flags;
3150 	state.ls_donefirst = B_FALSE;
3151 
3152 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
3153 		if (state.ls_flags & DLADM_OPT_ACTIVE)
3154 			fields_str = all_active_fields;
3155 		else
3156 			fields_str = all_inactive_fields;
3157 	}
3158 
3159 	fields = parse_output_fields(fields_str, phys_fields,
3160 	    PHYS_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
3161 
3162 	if (fields == NULL) {
3163 		die("invalid field(s) specified");
3164 		return;
3165 	}
3166 
3167 	state.ls_print.ps_fields = fields;
3168 	state.ls_print.ps_nfields = nfields;
3169 
3170 	if (linkid == DATALINK_ALL_LINKID) {
3171 		(void) dladm_walk_datalink_id(show_phys, &state,
3172 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags);
3173 	} else {
3174 		(void) show_phys(linkid, &state);
3175 		if (state.ls_status != DLADM_STATUS_OK) {
3176 			die_dlerr(state.ls_status,
3177 			    "failed to show physical link %s", argv[optind]);
3178 		}
3179 	}
3180 }
3181 
3182 static void
3183 do_show_vlan(int argc, char *argv[], const char *use)
3184 {
3185 	int		option;
3186 	uint32_t	flags = DLADM_OPT_ACTIVE;
3187 	boolean_t	p_arg = B_FALSE;
3188 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
3189 	show_state_t	state;
3190 	dladm_status_t	status;
3191 	boolean_t	o_arg = B_FALSE;
3192 	char		*fields_str = NULL;
3193 	print_field_t	**fields;
3194 	uint_t		nfields;
3195 	char		*all_fields = "link,vid,over,flags";
3196 
3197 	bzero(&state, sizeof (state));
3198 
3199 	opterr = 0;
3200 	while ((option = getopt_long(argc, argv, ":pPo:",
3201 	    show_lopts, NULL)) != -1) {
3202 		switch (option) {
3203 		case 'p':
3204 			if (p_arg)
3205 				die_optdup(option);
3206 
3207 			p_arg = B_TRUE;
3208 			break;
3209 		case 'P':
3210 			if (flags != DLADM_OPT_ACTIVE)
3211 				die_optdup(option);
3212 
3213 			flags = DLADM_OPT_PERSIST;
3214 			break;
3215 		case 'o':
3216 			o_arg = B_TRUE;
3217 			fields_str = optarg;
3218 			break;
3219 		default:
3220 			die_opterr(optopt, option, use);
3221 			break;
3222 		}
3223 	}
3224 
3225 	if (p_arg && !o_arg)
3226 		die("-p requires -o");
3227 
3228 	if (p_arg && strcasecmp(fields_str, "all") == 0)
3229 		die("\"-o all\" is invalid with -p");
3230 
3231 	/* get link name (optional last argument) */
3232 	if (optind == (argc-1)) {
3233 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3234 		    NULL, NULL)) != DLADM_STATUS_OK) {
3235 			die_dlerr(status, "link %s is not valid", argv[optind]);
3236 		}
3237 	} else if (optind != argc) {
3238 		usage();
3239 	}
3240 
3241 	state.ls_parseable = p_arg;
3242 	state.ls_flags = flags;
3243 	state.ls_donefirst = B_FALSE;
3244 
3245 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
3246 		fields_str = all_fields;
3247 
3248 	fields = parse_output_fields(fields_str, vlan_fields, VLAN_MAX_FIELDS,
3249 	    CMD_TYPE_ANY, &nfields);
3250 
3251 	if (fields == NULL) {
3252 		die("invalid field(s) specified");
3253 		return;
3254 	}
3255 	state.ls_print.ps_fields = fields;
3256 	state.ls_print.ps_nfields = nfields;
3257 
3258 	if (linkid == DATALINK_ALL_LINKID) {
3259 		(void) dladm_walk_datalink_id(show_vlan, &state,
3260 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags);
3261 	} else {
3262 		(void) show_vlan(linkid, &state);
3263 		if (state.ls_status != DLADM_STATUS_OK) {
3264 			die_dlerr(state.ls_status, "failed to show vlan %s",
3265 			    argv[optind]);
3266 		}
3267 	}
3268 }
3269 
3270 static void
3271 link_stats(datalink_id_t linkid, uint_t interval)
3272 {
3273 	show_state_t	state;
3274 
3275 	bzero(&state, sizeof (state));
3276 
3277 	/*
3278 	 * If an interval is specified, continuously show the stats
3279 	 * only for the first MAC port.
3280 	 */
3281 	state.ls_firstonly = (interval != 0);
3282 
3283 	for (;;) {
3284 		(void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n",
3285 		    "LINK", "IPACKETS", "RBYTES", "IERRORS", "OPACKETS",
3286 		    "OBYTES", "OERRORS");
3287 
3288 		state.ls_donefirst = B_FALSE;
3289 		if (linkid == DATALINK_ALL_LINKID) {
3290 			(void) dladm_walk_datalink_id(show_link_stats, &state,
3291 			    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
3292 			    DLADM_OPT_ACTIVE);
3293 		} else {
3294 			(void) show_link_stats(linkid, &state);
3295 		}
3296 
3297 		if (interval == 0)
3298 			break;
3299 
3300 		(void) sleep(interval);
3301 	}
3302 }
3303 
3304 static void
3305 aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval)
3306 {
3307 	/*
3308 	 * If an interval is specified, continuously show the stats
3309 	 * only for the first group.
3310 	 */
3311 	state->gs_firstonly = (interval != 0);
3312 
3313 	for (;;) {
3314 		state->gs_donefirst = B_FALSE;
3315 		if (linkid == DATALINK_ALL_LINKID)
3316 			(void) dladm_walk_datalink_id(show_aggr, state,
3317 			    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
3318 			    DLADM_OPT_ACTIVE);
3319 		else
3320 			(void) show_aggr(linkid, state);
3321 
3322 		if (interval == 0)
3323 			break;
3324 
3325 		(void) sleep(interval);
3326 	}
3327 }
3328 
3329 static void
3330 dev_stats(const char *dev, uint32_t interval, char *fields_str,
3331     show_state_t *state)
3332 {
3333 	print_field_t	**fields;
3334 	uint_t		nfields;
3335 
3336 	fields = parse_output_fields(fields_str, devs_fields, DEVS_MAX_FIELDS,
3337 	    CMD_TYPE_ANY, &nfields);
3338 
3339 	if (fields == NULL) {
3340 		die("invalid field(s) specified");
3341 		return;
3342 	}
3343 
3344 	state->ls_print.ps_fields = fields;
3345 	state->ls_print.ps_nfields = nfields;
3346 
3347 
3348 	/*
3349 	 * If an interval is specified, continuously show the stats
3350 	 * only for the first MAC port.
3351 	 */
3352 	state->ls_firstonly = (interval != 0);
3353 
3354 	for (;;) {
3355 
3356 		if (!state->ls_parseable)
3357 			print_header(&state->ls_print);
3358 		state->ls_donefirst = B_FALSE;
3359 
3360 		if (dev == NULL)
3361 			(void) dladm_mac_walk(show_dev_stats, state);
3362 		else
3363 			(void) show_dev_stats(dev, state);
3364 
3365 		if (interval == 0)
3366 			break;
3367 
3368 		(void) sleep(interval);
3369 	}
3370 
3371 	if (dev != NULL && state->ls_status != DLADM_STATUS_OK)
3372 		die_dlerr(state->ls_status, "cannot show device '%s'", dev);
3373 }
3374 
3375 /* accumulate stats (s1 += (s2 - s3)) */
3376 static void
3377 stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
3378 {
3379 	s1->ipackets += (s2->ipackets - s3->ipackets);
3380 	s1->opackets += (s2->opackets - s3->opackets);
3381 	s1->rbytes += (s2->rbytes - s3->rbytes);
3382 	s1->obytes += (s2->obytes - s3->obytes);
3383 	s1->ierrors += (s2->ierrors - s3->ierrors);
3384 	s1->oerrors += (s2->oerrors - s3->oerrors);
3385 }
3386 
3387 /* compute stats differences (s1 = s2 - s3) */
3388 static void
3389 stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
3390 {
3391 	s1->ipackets = s2->ipackets - s3->ipackets;
3392 	s1->opackets = s2->opackets - s3->opackets;
3393 	s1->rbytes = s2->rbytes - s3->rbytes;
3394 	s1->obytes = s2->obytes - s3->obytes;
3395 	s1->ierrors = s2->ierrors - s3->ierrors;
3396 	s1->oerrors = s2->oerrors - s3->oerrors;
3397 }
3398 
3399 static void
3400 get_stats(char *module, int instance, const char *name, pktsum_t *stats)
3401 {
3402 	kstat_ctl_t	*kcp;
3403 	kstat_t		*ksp;
3404 
3405 	if ((kcp = kstat_open()) == NULL) {
3406 		warn("kstat open operation failed");
3407 		return;
3408 	}
3409 
3410 	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
3411 		/*
3412 		 * The kstat query could fail if the underlying MAC
3413 		 * driver was already detached.
3414 		 */
3415 		(void) kstat_close(kcp);
3416 		return;
3417 	}
3418 
3419 	if (kstat_read(kcp, ksp, NULL) == -1)
3420 		goto bail;
3421 
3422 	if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
3423 	    &stats->ipackets) < 0)
3424 		goto bail;
3425 
3426 	if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
3427 	    &stats->opackets) < 0)
3428 		goto bail;
3429 
3430 	if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
3431 	    &stats->rbytes) < 0)
3432 		goto bail;
3433 
3434 	if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
3435 	    &stats->obytes) < 0)
3436 		goto bail;
3437 
3438 	if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
3439 	    &stats->ierrors) < 0)
3440 		goto bail;
3441 
3442 	if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
3443 	    &stats->oerrors) < 0)
3444 		goto bail;
3445 
3446 bail:
3447 	(void) kstat_close(kcp);
3448 	return;
3449 
3450 }
3451 
3452 static void
3453 get_mac_stats(const char *dev, pktsum_t *stats)
3454 {
3455 	char module[DLPI_LINKNAME_MAX];
3456 	uint_t instance;
3457 
3458 	bzero(stats, sizeof (*stats));
3459 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
3460 		return;
3461 
3462 	get_stats(module, instance, "mac", stats);
3463 }
3464 
3465 static void
3466 get_link_stats(const char *link, pktsum_t *stats)
3467 {
3468 	bzero(stats, sizeof (*stats));
3469 	get_stats("link", 0, link, stats);
3470 }
3471 
3472 static int
3473 query_kstat(char *module, int instance, const char *name, const char *stat,
3474     uint8_t type, void *val)
3475 {
3476 	kstat_ctl_t	*kcp;
3477 	kstat_t		*ksp;
3478 
3479 	if ((kcp = kstat_open()) == NULL) {
3480 		warn("kstat open operation failed");
3481 		return (-1);
3482 	}
3483 
3484 	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
3485 		/*
3486 		 * The kstat query could fail if the underlying MAC
3487 		 * driver was already detached.
3488 		 */
3489 		goto bail;
3490 	}
3491 
3492 	if (kstat_read(kcp, ksp, NULL) == -1) {
3493 		warn("kstat read failed");
3494 		goto bail;
3495 	}
3496 
3497 	if (dladm_kstat_value(ksp, stat, type, val) < 0)
3498 		goto bail;
3499 
3500 	(void) kstat_close(kcp);
3501 	return (0);
3502 
3503 bail:
3504 	(void) kstat_close(kcp);
3505 	return (-1);
3506 }
3507 
3508 static int
3509 get_one_kstat(const char *name, const char *stat, uint8_t type,
3510     void *val, boolean_t islink)
3511 {
3512 	char		module[DLPI_LINKNAME_MAX];
3513 	uint_t		instance;
3514 
3515 	if (islink) {
3516 		return (query_kstat("link", 0, name, stat, type, val));
3517 	} else {
3518 		if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
3519 			return (-1);
3520 
3521 		return (query_kstat(module, instance, "mac", stat, type, val));
3522 	}
3523 }
3524 
3525 static uint64_t
3526 get_ifspeed(const char *name, boolean_t islink)
3527 {
3528 	uint64_t ifspeed = 0;
3529 
3530 	(void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64,
3531 	    &ifspeed, islink);
3532 
3533 	return (ifspeed);
3534 }
3535 
3536 static const char *
3537 get_linkstate(const char *name, boolean_t islink, char *buf)
3538 {
3539 	link_state_t	linkstate;
3540 
3541 	if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32,
3542 	    &linkstate, islink) != 0) {
3543 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
3544 		return (buf);
3545 	}
3546 	return (dladm_linkstate2str(linkstate, buf));
3547 }
3548 
3549 static const char *
3550 get_linkduplex(const char *name, boolean_t islink, char *buf)
3551 {
3552 	link_duplex_t	linkduplex;
3553 
3554 	if (get_one_kstat(name, "link_duplex", KSTAT_DATA_UINT32,
3555 	    &linkduplex, islink) != 0) {
3556 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
3557 		return (buf);
3558 	}
3559 
3560 	return (dladm_linkduplex2str(linkduplex, buf));
3561 }
3562 
3563 typedef struct {
3564 	char	*s_buf;
3565 	char	**s_fields;	/* array of pointer to the fields in s_buf */
3566 	uint_t	s_nfields;	/* the number of fields in s_buf */
3567 } split_t;
3568 
3569 /*
3570  * Free the split_t structure pointed to by `sp'.
3571  */
3572 static void
3573 splitfree(split_t *sp)
3574 {
3575 	free(sp->s_buf);
3576 	free(sp->s_fields);
3577 	free(sp);
3578 }
3579 
3580 /*
3581  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
3582  * length.  Return a pointer to a split_t containing the split fields, or NULL
3583  * on failure.
3584  */
3585 static split_t *
3586 split(const char *str, uint_t maxfields, uint_t maxlen)
3587 {
3588 	char	*field, *token, *lasts = NULL;
3589 	split_t	*sp;
3590 
3591 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
3592 		return (NULL);
3593 
3594 	sp = calloc(sizeof (split_t), 1);
3595 	if (sp == NULL)
3596 		return (NULL);
3597 
3598 	sp->s_buf = strdup(str);
3599 	sp->s_fields = malloc(sizeof (char *) * maxfields);
3600 	if (sp->s_buf == NULL || sp->s_fields == NULL)
3601 		goto fail;
3602 
3603 	token = sp->s_buf;
3604 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
3605 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
3606 			goto fail;
3607 		token = NULL;
3608 		sp->s_fields[sp->s_nfields++] = field;
3609 	}
3610 	return (sp);
3611 fail:
3612 	splitfree(sp);
3613 	return (NULL);
3614 }
3615 
3616 static int
3617 parse_wifi_fields(char *str, print_field_t ***fields, uint_t *countp,
3618     uint_t cmdtype)
3619 {
3620 
3621 	if (cmdtype == WIFI_CMD_SCAN) {
3622 		if (str == NULL)
3623 			str = def_scan_wifi_fields;
3624 		if (strcasecmp(str, "all") == 0)
3625 			str = all_scan_wifi_fields;
3626 	} else if (cmdtype == WIFI_CMD_SHOW) {
3627 		if (str == NULL)
3628 			str = def_show_wifi_fields;
3629 		if (strcasecmp(str, "all") == 0)
3630 			str = all_show_wifi_fields;
3631 	} else {
3632 		return (-1);
3633 	}
3634 	*fields = parse_output_fields(str, wifi_fields, WIFI_MAX_FIELDS,
3635 	    cmdtype, countp);
3636 	if (*fields != NULL)
3637 		return (0);
3638 	return (-1);
3639 }
3640 static print_field_t **
3641 parse_output_fields(char *str, print_field_t *template, int max_fields,
3642     uint_t cmdtype, uint_t *countp)
3643 {
3644 	split_t		*sp;
3645 	boolean_t	good_match = B_FALSE;
3646 	uint_t		i, j;
3647 	print_field_t	**pf = NULL;
3648 
3649 	sp = split(str, max_fields, MAX_FIELD_LEN);
3650 
3651 	if (sp == NULL)
3652 		return (NULL);
3653 
3654 	pf = malloc(sp->s_nfields * sizeof (print_field_t *));
3655 	if (pf == NULL)
3656 		goto fail;
3657 
3658 	for (i = 0; i < sp->s_nfields; i++) {
3659 		for (j = 0; j < max_fields; j++) {
3660 			if (strcasecmp(sp->s_fields[i],
3661 			    template[j].pf_name) == 0) {
3662 				good_match = template[j]. pf_cmdtype & cmdtype;
3663 				break;
3664 			}
3665 		}
3666 		if (!good_match)
3667 			goto fail;
3668 
3669 		good_match = B_FALSE;
3670 		pf[i] = &template[j];
3671 	}
3672 	*countp = i;
3673 	splitfree(sp);
3674 	return (pf);
3675 fail:
3676 	free(pf);
3677 	splitfree(sp);
3678 	return (NULL);
3679 }
3680 
3681 typedef struct print_wifi_state {
3682 	char		*ws_link;
3683 	boolean_t	ws_parseable;
3684 	boolean_t	ws_header;
3685 	print_state_t	ws_print_state;
3686 } print_wifi_state_t;
3687 
3688 typedef struct  wlan_scan_args_s {
3689 	print_wifi_state_t	*ws_state;
3690 	void			*ws_attr;
3691 } wlan_scan_args_t;
3692 
3693 static void
3694 print_field(print_state_t *statep, print_field_t *pfp, const char *value,
3695     boolean_t parseable)
3696 {
3697 	uint_t	width = pfp->pf_width;
3698 	uint_t	valwidth = strlen(value);
3699 	uint_t	compress;
3700 
3701 	/*
3702 	 * Parsable fields are separated by ':'. If such a field contains
3703 	 * a ':' or '\', this character is prefixed by a '\'.
3704 	 */
3705 	if (parseable) {
3706 		char	c;
3707 
3708 		if (statep->ps_nfields == 1) {
3709 			(void) printf("%s", value);
3710 			return;
3711 		}
3712 		while ((c = *value++) != '\0') {
3713 			if (c == ':' || c == '\\')
3714 				(void) putchar('\\');
3715 			(void) putchar(c);
3716 		}
3717 		if (!statep->ps_lastfield)
3718 			(void) putchar(':');
3719 		return;
3720 	} else {
3721 		if (value[0] == '\0')
3722 			value = STR_UNDEF_VAL;
3723 		if (statep->ps_lastfield) {
3724 			(void) printf("%s", value);
3725 			return;
3726 		}
3727 
3728 		if (valwidth > width) {
3729 			statep->ps_overflow += valwidth - width;
3730 		} else if (valwidth < width && statep->ps_overflow > 0) {
3731 			compress = min(statep->ps_overflow, width - valwidth);
3732 			statep->ps_overflow -= compress;
3733 			width -= compress;
3734 		}
3735 		(void) printf("%-*s", width, value);
3736 	}
3737 
3738 	if (!statep->ps_lastfield)
3739 		(void) putchar(' ');
3740 }
3741 
3742 static char *
3743 print_wlan_attr(print_field_t *wfp, void *warg)
3744 {
3745 	static char		buf[DLADM_STRSIZE];
3746 	wlan_scan_args_t	*w = warg;
3747 	print_wifi_state_t	*statep = w->ws_state;
3748 	dladm_wlan_attr_t	*attrp = w->ws_attr;
3749 
3750 	if (wfp->pf_index == 0) {
3751 		return ((char *)statep->ws_link);
3752 	}
3753 
3754 	if ((wfp->pf_index & attrp->wa_valid) == 0) {
3755 		return ("");
3756 	}
3757 
3758 	switch (wfp->pf_index) {
3759 	case DLADM_WLAN_ATTR_ESSID:
3760 		(void) dladm_wlan_essid2str(&attrp->wa_essid, buf);
3761 		break;
3762 	case DLADM_WLAN_ATTR_BSSID:
3763 		(void) dladm_wlan_bssid2str(&attrp->wa_bssid, buf);
3764 		break;
3765 	case DLADM_WLAN_ATTR_SECMODE:
3766 		(void) dladm_wlan_secmode2str(&attrp->wa_secmode, buf);
3767 		break;
3768 	case DLADM_WLAN_ATTR_STRENGTH:
3769 		(void) dladm_wlan_strength2str(&attrp->wa_strength, buf);
3770 		break;
3771 	case DLADM_WLAN_ATTR_MODE:
3772 		(void) dladm_wlan_mode2str(&attrp->wa_mode, buf);
3773 		break;
3774 	case DLADM_WLAN_ATTR_SPEED:
3775 		(void) dladm_wlan_speed2str(&attrp->wa_speed, buf);
3776 		(void) strlcat(buf, "Mb", sizeof (buf));
3777 		break;
3778 	case DLADM_WLAN_ATTR_AUTH:
3779 		(void) dladm_wlan_auth2str(&attrp->wa_auth, buf);
3780 		break;
3781 	case DLADM_WLAN_ATTR_BSSTYPE:
3782 		(void) dladm_wlan_bsstype2str(&attrp->wa_bsstype, buf);
3783 		break;
3784 	}
3785 
3786 	return (buf);
3787 }
3788 
3789 static boolean_t
3790 print_scan_results(void *arg, dladm_wlan_attr_t *attrp)
3791 {
3792 	print_wifi_state_t	*statep = arg;
3793 	wlan_scan_args_t	warg;
3794 
3795 	if (statep->ws_header) {
3796 		statep->ws_header = B_FALSE;
3797 		if (!statep->ws_parseable)
3798 			print_header(&statep->ws_print_state);
3799 	}
3800 
3801 	statep->ws_print_state.ps_overflow = 0;
3802 	bzero(&warg, sizeof (warg));
3803 	warg.ws_state = statep;
3804 	warg.ws_attr = attrp;
3805 	dladm_print_output(&statep->ws_print_state, statep->ws_parseable,
3806 	    print_wlan_attr, &warg);
3807 	return (B_TRUE);
3808 }
3809 
3810 static int
3811 scan_wifi(datalink_id_t linkid, void *arg)
3812 {
3813 	print_wifi_state_t	*statep = arg;
3814 	dladm_status_t		status;
3815 	char			link[MAXLINKNAMELEN];
3816 
3817 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
3818 	    sizeof (link))) != DLADM_STATUS_OK) {
3819 		return (DLADM_WALK_CONTINUE);
3820 	}
3821 
3822 	statep->ws_link = link;
3823 	status = dladm_wlan_scan(linkid, statep, print_scan_results);
3824 	if (status != DLADM_STATUS_OK)
3825 		die_dlerr(status, "cannot scan link '%s'", statep->ws_link);
3826 
3827 	return (DLADM_WALK_CONTINUE);
3828 }
3829 
3830 static char *
3831 print_link_attr(print_field_t *wfp, void *warg)
3832 {
3833 	static char		buf[DLADM_STRSIZE];
3834 	char			*ptr;
3835 	wlan_scan_args_t	*w = warg, w1;
3836 	print_wifi_state_t	*statep = w->ws_state;
3837 	dladm_wlan_linkattr_t	*attrp = w->ws_attr;
3838 
3839 	if (strcmp(wfp->pf_name, "status") == 0) {
3840 		if ((wfp->pf_index & attrp->la_valid) != 0)
3841 			(void) dladm_wlan_linkstatus2str(
3842 			    &attrp->la_status, buf);
3843 		return (buf);
3844 	}
3845 	statep->ws_print_state.ps_overflow = 0;
3846 	bzero(&w1, sizeof (w1));
3847 	w1.ws_state = statep;
3848 	w1.ws_attr = &attrp->la_wlan_attr;
3849 	ptr = print_wlan_attr(wfp, &w1);
3850 	return (ptr);
3851 }
3852 
3853 static int
3854 show_wifi(datalink_id_t linkid, void *arg)
3855 {
3856 	print_wifi_state_t	*statep = arg;
3857 	dladm_wlan_linkattr_t	attr;
3858 	dladm_status_t		status;
3859 	char			link[MAXLINKNAMELEN];
3860 	wlan_scan_args_t	warg;
3861 
3862 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
3863 	    sizeof (link))) != DLADM_STATUS_OK) {
3864 		return (DLADM_WALK_CONTINUE);
3865 	}
3866 
3867 	status = dladm_wlan_get_linkattr(linkid, &attr);
3868 	if (status != DLADM_STATUS_OK)
3869 		die_dlerr(status, "cannot get link attributes for %s", link);
3870 
3871 	statep->ws_link = link;
3872 
3873 	if (statep->ws_header) {
3874 		statep->ws_header = B_FALSE;
3875 		if (!statep->ws_parseable)
3876 			print_header(&statep->ws_print_state);
3877 	}
3878 
3879 	statep->ws_print_state.ps_overflow = 0;
3880 	bzero(&warg, sizeof (warg));
3881 	warg.ws_state = statep;
3882 	warg.ws_attr = &attr;
3883 	dladm_print_output(&statep->ws_print_state, statep->ws_parseable,
3884 	    print_link_attr, &warg);
3885 	return (DLADM_WALK_CONTINUE);
3886 }
3887 
3888 static void
3889 do_display_wifi(int argc, char **argv, int cmd, const char *use)
3890 {
3891 	int			option;
3892 	char			*fields_str = NULL;
3893 	print_field_t		**fields;
3894 	int			(*callback)(datalink_id_t, void *);
3895 	uint_t			nfields;
3896 	print_wifi_state_t	state;
3897 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3898 	dladm_status_t		status;
3899 
3900 	if (cmd == WIFI_CMD_SCAN)
3901 		callback = scan_wifi;
3902 	else if (cmd == WIFI_CMD_SHOW)
3903 		callback = show_wifi;
3904 	else
3905 		return;
3906 
3907 	state.ws_parseable = B_FALSE;
3908 	state.ws_header = B_TRUE;
3909 	opterr = 0;
3910 	while ((option = getopt_long(argc, argv, ":o:p",
3911 	    wifi_longopts, NULL)) != -1) {
3912 		switch (option) {
3913 		case 'o':
3914 			fields_str = optarg;
3915 			break;
3916 		case 'p':
3917 			state.ws_parseable = B_TRUE;
3918 			break;
3919 		default:
3920 			die_opterr(optopt, option, use);
3921 		}
3922 	}
3923 
3924 	if (state.ws_parseable && fields_str == NULL)
3925 		die("-p requires -o");
3926 
3927 	if (state.ws_parseable && strcasecmp(fields_str, "all") == 0)
3928 		die("\"-o all\" is invalid with -p");
3929 
3930 	if (optind == (argc - 1)) {
3931 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
3932 		    NULL, NULL)) != DLADM_STATUS_OK) {
3933 			die_dlerr(status, "link %s is not valid", argv[optind]);
3934 		}
3935 	} else if (optind != argc) {
3936 		usage();
3937 	}
3938 
3939 	if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0)
3940 		die("invalid field(s) specified");
3941 
3942 	bzero(&state.ws_print_state, sizeof (state.ws_print_state));
3943 	state.ws_print_state.ps_fields = fields;
3944 	state.ws_print_state.ps_nfields = nfields;
3945 
3946 	if (linkid == DATALINK_ALL_LINKID) {
3947 		(void) dladm_walk_datalink_id(callback, &state,
3948 		    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
3949 	} else {
3950 		(void) (*callback)(linkid, &state);
3951 	}
3952 	free(fields);
3953 }
3954 
3955 static void
3956 do_scan_wifi(int argc, char **argv, const char *use)
3957 {
3958 	do_display_wifi(argc, argv, WIFI_CMD_SCAN, use);
3959 }
3960 
3961 static void
3962 do_show_wifi(int argc, char **argv, const char *use)
3963 {
3964 	do_display_wifi(argc, argv, WIFI_CMD_SHOW, use);
3965 }
3966 
3967 typedef struct wlan_count_attr {
3968 	uint_t		wc_count;
3969 	datalink_id_t	wc_linkid;
3970 } wlan_count_attr_t;
3971 
3972 static int
3973 do_count_wlan(datalink_id_t linkid, void *arg)
3974 {
3975 	wlan_count_attr_t *cp = arg;
3976 
3977 	if (cp->wc_count == 0)
3978 		cp->wc_linkid = linkid;
3979 	cp->wc_count++;
3980 	return (DLADM_WALK_CONTINUE);
3981 }
3982 
3983 static int
3984 parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp)
3985 {
3986 	uint_t			i;
3987 	split_t			*sp;
3988 	dladm_wlan_key_t	*wk;
3989 
3990 	sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_KEYNAME_LEN);
3991 	if (sp == NULL)
3992 		return (-1);
3993 
3994 	wk = malloc(sp->s_nfields * sizeof (dladm_wlan_key_t));
3995 	if (wk == NULL)
3996 		goto fail;
3997 
3998 	for (i = 0; i < sp->s_nfields; i++) {
3999 		char			*s;
4000 		dladm_secobj_class_t	class;
4001 		dladm_status_t		status;
4002 
4003 		(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
4004 		    DLADM_WLAN_MAX_KEYNAME_LEN);
4005 
4006 		wk[i].wk_idx = 1;
4007 		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
4008 			if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
4009 				goto fail;
4010 
4011 			wk[i].wk_idx = (uint_t)(s[1] - '0');
4012 			*s = '\0';
4013 		}
4014 		wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN;
4015 
4016 		status = dladm_get_secobj(wk[i].wk_name, &class,
4017 		    wk[i].wk_val, &wk[i].wk_len, 0);
4018 		if (status != DLADM_STATUS_OK) {
4019 			if (status == DLADM_STATUS_NOTFOUND) {
4020 				status = dladm_get_secobj(wk[i].wk_name,
4021 				    &class, wk[i].wk_val, &wk[i].wk_len,
4022 				    DLADM_OPT_PERSIST);
4023 			}
4024 			if (status != DLADM_STATUS_OK)
4025 				goto fail;
4026 		}
4027 		wk[i].wk_class = class;
4028 	}
4029 	*keys = wk;
4030 	*key_countp = i;
4031 	splitfree(sp);
4032 	return (0);
4033 fail:
4034 	free(wk);
4035 	splitfree(sp);
4036 	return (-1);
4037 }
4038 
4039 static void
4040 do_connect_wifi(int argc, char **argv, const char *use)
4041 {
4042 	int			option;
4043 	dladm_wlan_attr_t	attr, *attrp;
4044 	dladm_status_t		status = DLADM_STATUS_OK;
4045 	int			timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
4046 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
4047 	dladm_wlan_key_t	*keys = NULL;
4048 	uint_t			key_count = 0;
4049 	uint_t			flags = 0;
4050 	dladm_wlan_secmode_t	keysecmode = DLADM_WLAN_SECMODE_NONE;
4051 	char			buf[DLADM_STRSIZE];
4052 
4053 	opterr = 0;
4054 	(void) memset(&attr, 0, sizeof (attr));
4055 	while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
4056 	    wifi_longopts, NULL)) != -1) {
4057 		switch (option) {
4058 		case 'e':
4059 			status = dladm_wlan_str2essid(optarg, &attr.wa_essid);
4060 			if (status != DLADM_STATUS_OK)
4061 				die("invalid ESSID '%s'", optarg);
4062 
4063 			attr.wa_valid |= DLADM_WLAN_ATTR_ESSID;
4064 			/*
4065 			 * Try to connect without doing a scan.
4066 			 */
4067 			flags |= DLADM_WLAN_CONNECT_NOSCAN;
4068 			break;
4069 		case 'i':
4070 			status = dladm_wlan_str2bssid(optarg, &attr.wa_bssid);
4071 			if (status != DLADM_STATUS_OK)
4072 				die("invalid BSSID %s", optarg);
4073 
4074 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
4075 			break;
4076 		case 'a':
4077 			status = dladm_wlan_str2auth(optarg, &attr.wa_auth);
4078 			if (status != DLADM_STATUS_OK)
4079 				die("invalid authentication mode '%s'", optarg);
4080 
4081 			attr.wa_valid |= DLADM_WLAN_ATTR_AUTH;
4082 			break;
4083 		case 'm':
4084 			status = dladm_wlan_str2mode(optarg, &attr.wa_mode);
4085 			if (status != DLADM_STATUS_OK)
4086 				die("invalid mode '%s'", optarg);
4087 
4088 			attr.wa_valid |= DLADM_WLAN_ATTR_MODE;
4089 			break;
4090 		case 'b':
4091 			if ((status = dladm_wlan_str2bsstype(optarg,
4092 			    &attr.wa_bsstype)) != DLADM_STATUS_OK) {
4093 				die("invalid bsstype '%s'", optarg);
4094 			}
4095 
4096 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
4097 			break;
4098 		case 's':
4099 			if ((status = dladm_wlan_str2secmode(optarg,
4100 			    &attr.wa_secmode)) != DLADM_STATUS_OK) {
4101 				die("invalid security mode '%s'", optarg);
4102 			}
4103 
4104 			attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
4105 			break;
4106 		case 'k':
4107 			if (parse_wlan_keys(optarg, &keys, &key_count) < 0)
4108 				die("invalid key(s) '%s'", optarg);
4109 
4110 			if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP)
4111 				keysecmode = DLADM_WLAN_SECMODE_WEP;
4112 			else
4113 				keysecmode = DLADM_WLAN_SECMODE_WPA;
4114 			break;
4115 		case 'T':
4116 			if (strcasecmp(optarg, "forever") == 0) {
4117 				timeout = -1;
4118 				break;
4119 			}
4120 			if (!str2int(optarg, &timeout) || timeout < 0)
4121 				die("invalid timeout value '%s'", optarg);
4122 			break;
4123 		case 'c':
4124 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
4125 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
4126 			break;
4127 		default:
4128 			die_opterr(optopt, option, use);
4129 			break;
4130 		}
4131 	}
4132 
4133 	if (keysecmode == DLADM_WLAN_SECMODE_NONE) {
4134 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) {
4135 			die("key required for security mode '%s'",
4136 			    dladm_wlan_secmode2str(&attr.wa_secmode, buf));
4137 		}
4138 	} else {
4139 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
4140 		    attr.wa_secmode != keysecmode)
4141 			die("incompatible -s and -k options");
4142 		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
4143 		attr.wa_secmode = keysecmode;
4144 	}
4145 
4146 	if (optind == (argc - 1)) {
4147 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
4148 		    NULL, NULL)) != DLADM_STATUS_OK) {
4149 			die_dlerr(status, "link %s is not valid", argv[optind]);
4150 		}
4151 	} else if (optind != argc) {
4152 		usage();
4153 	}
4154 
4155 	if (linkid == DATALINK_ALL_LINKID) {
4156 		wlan_count_attr_t wcattr;
4157 
4158 		wcattr.wc_linkid = DATALINK_INVALID_LINKID;
4159 		wcattr.wc_count = 0;
4160 		(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
4161 		    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
4162 		if (wcattr.wc_count == 0) {
4163 			die("no wifi links are available");
4164 		} else if (wcattr.wc_count > 1) {
4165 			die("link name is required when more than one wifi "
4166 			    "link is available");
4167 		}
4168 		linkid = wcattr.wc_linkid;
4169 	}
4170 	attrp = (attr.wa_valid == 0) ? NULL : &attr;
4171 again:
4172 	if ((status = dladm_wlan_connect(linkid, attrp, timeout, keys,
4173 	    key_count, flags)) != DLADM_STATUS_OK) {
4174 		if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0) {
4175 			/*
4176 			 * Try again with scanning and filtering.
4177 			 */
4178 			flags &= ~DLADM_WLAN_CONNECT_NOSCAN;
4179 			goto again;
4180 		}
4181 
4182 		if (status == DLADM_STATUS_NOTFOUND) {
4183 			if (attr.wa_valid == 0) {
4184 				die("no wifi networks are available");
4185 			} else {
4186 				die("no wifi networks with the specified "
4187 				    "criteria are available");
4188 			}
4189 		}
4190 		die_dlerr(status, "cannot connect");
4191 	}
4192 	free(keys);
4193 }
4194 
4195 /* ARGSUSED */
4196 static int
4197 do_all_disconnect_wifi(datalink_id_t linkid, void *arg)
4198 {
4199 	dladm_status_t	status;
4200 
4201 	status = dladm_wlan_disconnect(linkid);
4202 	if (status != DLADM_STATUS_OK)
4203 		warn_dlerr(status, "cannot disconnect link");
4204 
4205 	return (DLADM_WALK_CONTINUE);
4206 }
4207 
4208 static void
4209 do_disconnect_wifi(int argc, char **argv, const char *use)
4210 {
4211 	int			option;
4212 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
4213 	boolean_t		all_links = B_FALSE;
4214 	dladm_status_t		status;
4215 	wlan_count_attr_t	wcattr;
4216 
4217 	opterr = 0;
4218 	while ((option = getopt_long(argc, argv, ":a",
4219 	    wifi_longopts, NULL)) != -1) {
4220 		switch (option) {
4221 		case 'a':
4222 			all_links = B_TRUE;
4223 			break;
4224 		default:
4225 			die_opterr(optopt, option, use);
4226 			break;
4227 		}
4228 	}
4229 
4230 	if (optind == (argc - 1)) {
4231 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
4232 		    NULL, NULL)) != DLADM_STATUS_OK) {
4233 			die_dlerr(status, "link %s is not valid", argv[optind]);
4234 		}
4235 	} else if (optind != argc) {
4236 		usage();
4237 	}
4238 
4239 	if (linkid == DATALINK_ALL_LINKID) {
4240 		if (!all_links) {
4241 			wcattr.wc_linkid = linkid;
4242 			wcattr.wc_count = 0;
4243 			(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
4244 			    DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
4245 			if (wcattr.wc_count == 0) {
4246 				die("no wifi links are available");
4247 			} else if (wcattr.wc_count > 1) {
4248 				die("link name is required when more than "
4249 				    "one wifi link is available");
4250 			}
4251 			linkid = wcattr.wc_linkid;
4252 		} else {
4253 			(void) dladm_walk_datalink_id(do_all_disconnect_wifi,
4254 			    NULL, DATALINK_CLASS_PHYS, DL_WIFI,
4255 			    DLADM_OPT_ACTIVE);
4256 			return;
4257 		}
4258 	}
4259 	status = dladm_wlan_disconnect(linkid);
4260 	if (status != DLADM_STATUS_OK)
4261 		die_dlerr(status, "cannot disconnect");
4262 }
4263 
4264 
4265 static void
4266 free_props(prop_list_t *list)
4267 {
4268 	if (list != NULL) {
4269 		free(list->pl_buf);
4270 		free(list);
4271 	}
4272 }
4273 
4274 static int
4275 parse_props(char *str, prop_list_t **listp, boolean_t novalues)
4276 {
4277 	prop_list_t	*list;
4278 	prop_info_t	*pip;
4279 	char		*buf, *curr;
4280 	int		len, i;
4281 
4282 	list = malloc(sizeof (prop_list_t));
4283 	if (list == NULL)
4284 		return (-1);
4285 
4286 	list->pl_count = 0;
4287 	list->pl_buf = buf = strdup(str);
4288 	if (buf == NULL)
4289 		goto fail;
4290 
4291 	/*
4292 	 * buf is a string of form [<propname>=<value>][,<propname>=<value>]+
4293 	 * where each <value> string itself could be a comma-separated array.
4294 	 * The loop below will count the number of propname assignments
4295 	 * in pl_count; for each property, there is a pip entry with
4296 	 * pi_name == <propname>, pi_count == # of elements in <value> array.
4297 	 * pi_val[] contains the actual values.
4298 	 *
4299 	 * This could really be a combination of  calls to
4300 	 * strtok (token delimiter is ",") and strchr (chr '=')
4301 	 * with appropriate null/string-bound-checks.
4302 	 */
4303 
4304 	curr = buf;
4305 	len = strlen(buf);
4306 	pip = NULL;
4307 	for (i = 0; i < len; i++) {
4308 		char		c = buf[i];
4309 		boolean_t	match = (c == '=' || c == ',');
4310 
4311 		if (!match && i != len - 1)
4312 			continue;
4313 
4314 		if (match) {
4315 			buf[i] = '\0';
4316 			if (*curr == '\0')
4317 				goto fail;
4318 		}
4319 
4320 		if (pip != NULL && c != '=') {
4321 			if (pip->pi_count > DLADM_MAX_PROP_VALCNT)
4322 				goto fail;
4323 
4324 			if (novalues)
4325 				goto fail;
4326 
4327 			pip->pi_val[pip->pi_count] = curr;
4328 			pip->pi_count++;
4329 		} else {
4330 			if (list->pl_count > MAX_PROPS)
4331 				goto fail;
4332 
4333 			pip = &list->pl_info[list->pl_count];
4334 			pip->pi_name = curr;
4335 			pip->pi_count = 0;
4336 			list->pl_count++;
4337 			if (c == ',')
4338 				pip = NULL;
4339 		}
4340 		curr = buf + i + 1;
4341 	}
4342 	*listp = list;
4343 	return (0);
4344 
4345 fail:
4346 	free_props(list);
4347 	return (-1);
4348 }
4349 
4350 static void
4351 print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep,
4352     const char *propname, dladm_prop_type_t type,
4353     const char *format, char **pptr)
4354 {
4355 	int		i;
4356 	char		*ptr, *lim;
4357 	char		buf[DLADM_STRSIZE];
4358 	char		*unknown = "?", *notsup = "";
4359 	char		**propvals = statep->ls_propvals;
4360 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
4361 	dladm_status_t	status;
4362 
4363 	status = dladm_get_linkprop(linkid, type, propname, propvals, &valcnt);
4364 	if (status != DLADM_STATUS_OK) {
4365 		if (status == DLADM_STATUS_TEMPONLY) {
4366 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
4367 			    statep->ls_persist) {
4368 				valcnt = 1;
4369 				propvals = &unknown;
4370 			} else {
4371 				statep->ls_status = status;
4372 				statep->ls_retstatus = status;
4373 				return;
4374 			}
4375 		} else if (status == DLADM_STATUS_NOTSUP ||
4376 		    statep->ls_persist) {
4377 			valcnt = 1;
4378 			if (type == DLADM_PROP_VAL_CURRENT)
4379 				propvals = &unknown;
4380 			else
4381 				propvals = &notsup;
4382 		} else {
4383 			if (statep->ls_proplist &&
4384 			    statep->ls_status == DLADM_STATUS_OK) {
4385 				warn_dlerr(status,
4386 				    "cannot get link property '%s' for %s",
4387 				    propname, statep->ls_link);
4388 			}
4389 			statep->ls_status = status;
4390 			statep->ls_retstatus = status;
4391 			return;
4392 		}
4393 	}
4394 
4395 	statep->ls_status = DLADM_STATUS_OK;
4396 
4397 	ptr = buf;
4398 	lim = buf + DLADM_STRSIZE;
4399 	for (i = 0; i < valcnt; i++) {
4400 		if (propvals[i][0] == '\0' && !statep->ls_parseable)
4401 			ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL",");
4402 		else
4403 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
4404 		if (ptr >= lim)
4405 			break;
4406 	}
4407 	if (valcnt > 0)
4408 		buf[strlen(buf) - 1] = '\0';
4409 
4410 	lim = statep->ls_line + MAX_PROP_LINE;
4411 	if (statep->ls_parseable) {
4412 		*pptr += snprintf(*pptr, lim - *pptr,
4413 		    "%s", buf);
4414 	} else {
4415 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
4416 	}
4417 }
4418 
4419 static char *
4420 linkprop_callback(print_field_t *pf, void *ls_arg)
4421 {
4422 	linkprop_args_t		*arg = ls_arg;
4423 	char 			*propname = arg->ls_propname;
4424 	show_linkprop_state_t	*statep = arg->ls_state;
4425 	char			*ptr = statep->ls_line;
4426 	char			*lim = ptr + MAX_PROP_LINE;
4427 	datalink_id_t		linkid = arg->ls_linkid;
4428 
4429 	switch (pf->pf_index) {
4430 	case LINKPROP_LINK:
4431 		(void) snprintf(ptr, lim - ptr, "%s", statep->ls_link);
4432 		break;
4433 	case LINKPROP_PROPERTY:
4434 		(void) snprintf(ptr, lim - ptr, "%s", propname);
4435 		break;
4436 	case LINKPROP_VALUE:
4437 		print_linkprop(linkid, statep, propname,
4438 		    statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
4439 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
4440 		/*
4441 		 * If we failed to query the link property, for example, query
4442 		 * the persistent value of a non-persistable link property,
4443 		 * simply skip the output.
4444 		 */
4445 		if (statep->ls_status != DLADM_STATUS_OK)
4446 			goto skip;
4447 		ptr = statep->ls_line;
4448 		break;
4449 	case LINKPROP_DEFAULT:
4450 		print_linkprop(linkid, statep, propname,
4451 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
4452 		if (statep->ls_status != DLADM_STATUS_OK)
4453 			goto skip;
4454 		ptr = statep->ls_line;
4455 		break;
4456 	case LINKPROP_POSSIBLE:
4457 		print_linkprop(linkid, statep, propname,
4458 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
4459 		if (statep->ls_status != DLADM_STATUS_OK)
4460 			goto skip;
4461 		ptr = statep->ls_line;
4462 		break;
4463 	default:
4464 		die("invalid input");
4465 		break;
4466 	}
4467 	return (ptr);
4468 skip:
4469 	if (statep->ls_status != DLADM_STATUS_OK)
4470 		return (NULL);
4471 	else
4472 		return ("");
4473 }
4474 
4475 static int
4476 show_linkprop(datalink_id_t linkid, const char *propname, void *arg)
4477 {
4478 	show_linkprop_state_t	*statep = arg;
4479 	linkprop_args_t		ls_arg;
4480 
4481 	bzero(&ls_arg, sizeof (ls_arg));
4482 	ls_arg.ls_state = statep;
4483 	ls_arg.ls_propname = (char *)propname;
4484 	ls_arg.ls_linkid = linkid;
4485 
4486 	if (statep->ls_header) {
4487 		statep->ls_header = B_FALSE;
4488 		if (!statep->ls_parseable)
4489 			print_header(&statep->ls_print);
4490 	}
4491 	dladm_print_output(&statep->ls_print, statep->ls_parseable,
4492 	    linkprop_callback, (void *)&ls_arg);
4493 
4494 	return (DLADM_WALK_CONTINUE);
4495 }
4496 
4497 static void
4498 do_show_linkprop(int argc, char **argv, const char *use)
4499 {
4500 	int			option;
4501 	prop_list_t		*proplist = NULL;
4502 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
4503 	show_linkprop_state_t	state;
4504 	uint32_t		flags = DLADM_OPT_ACTIVE;
4505 	dladm_status_t		status;
4506 	char			*fields_str = NULL;
4507 	print_field_t		**fields;
4508 	uint_t			nfields;
4509 	boolean_t		o_arg = B_FALSE;
4510 	char			*all_fields =
4511 	    "link,property,value,default,possible";
4512 
4513 	fields_str = all_fields;
4514 
4515 	opterr = 0;
4516 	state.ls_propvals = NULL;
4517 	state.ls_line = NULL;
4518 	state.ls_parseable = B_FALSE;
4519 	state.ls_persist = B_FALSE;
4520 	state.ls_header = B_TRUE;
4521 	state.ls_retstatus = DLADM_STATUS_OK;
4522 	while ((option = getopt_long(argc, argv, ":p:cPo:",
4523 	    prop_longopts, NULL)) != -1) {
4524 		switch (option) {
4525 		case 'p':
4526 			if (parse_props(optarg, &proplist, B_TRUE) < 0)
4527 				die("invalid link properties specified");
4528 			break;
4529 		case 'c':
4530 			state.ls_parseable = B_TRUE;
4531 			break;
4532 		case 'P':
4533 			state.ls_persist = B_TRUE;
4534 			flags = DLADM_OPT_PERSIST;
4535 			break;
4536 		case 'o':
4537 			o_arg = B_TRUE;
4538 			if (strcasecmp(optarg, "all") == 0)
4539 				fields_str = all_fields;
4540 			else
4541 				fields_str = optarg;
4542 			break;
4543 		default:
4544 			die_opterr(optopt, option, use);
4545 			break;
4546 		}
4547 	}
4548 
4549 	if (state.ls_parseable && !o_arg)
4550 		die("-c requires -o");
4551 
4552 	if (state.ls_parseable && fields_str == all_fields)
4553 		die("\"-o all\" is invalid with -c");
4554 
4555 	if (optind == (argc - 1)) {
4556 		if ((status = dladm_name2info(argv[optind], &linkid, NULL,
4557 		    NULL, NULL)) != DLADM_STATUS_OK) {
4558 			die_dlerr(status, "link %s is not valid", argv[optind]);
4559 		}
4560 	} else if (optind != argc) {
4561 		usage();
4562 	}
4563 
4564 	bzero(&state.ls_print, sizeof (print_state_t));
4565 	state.ls_proplist = proplist;
4566 	state.ls_status = DLADM_STATUS_OK;
4567 
4568 	fields = parse_output_fields(fields_str, linkprop_fields,
4569 	    LINKPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
4570 
4571 	if (fields == NULL) {
4572 		die("invalid field(s) specified");
4573 		return;
4574 	}
4575 
4576 	state.ls_print.ps_fields = fields;
4577 	state.ls_print.ps_nfields = nfields;
4578 	if (linkid == DATALINK_ALL_LINKID) {
4579 		(void) dladm_walk_datalink_id(show_linkprop_onelink, &state,
4580 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
4581 	} else {
4582 		(void) show_linkprop_onelink(linkid, &state);
4583 	}
4584 	free_props(proplist);
4585 
4586 	if (state.ls_retstatus != DLADM_STATUS_OK)
4587 		exit(EXIT_FAILURE);
4588 }
4589 
4590 static int
4591 show_linkprop_onelink(datalink_id_t linkid, void *arg)
4592 {
4593 	int			i;
4594 	char			*buf;
4595 	uint32_t		flags;
4596 	prop_list_t		*proplist = NULL;
4597 	show_linkprop_state_t	*statep = arg;
4598 	dlpi_handle_t		dh = NULL;
4599 
4600 	statep->ls_status = DLADM_STATUS_OK;
4601 
4602 	if (dladm_datalink_id2info(linkid, &flags, NULL, NULL, statep->ls_link,
4603 	    MAXLINKNAMELEN) != DLADM_STATUS_OK) {
4604 		statep->ls_status = DLADM_STATUS_NOTFOUND;
4605 		return (DLADM_WALK_CONTINUE);
4606 	}
4607 
4608 	if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) ||
4609 	    (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) {
4610 		statep->ls_status = DLADM_STATUS_BADARG;
4611 		return (DLADM_WALK_CONTINUE);
4612 	}
4613 
4614 	proplist = statep->ls_proplist;
4615 
4616 	/*
4617 	 * When some WiFi links are opened for the first time, their hardware
4618 	 * automatically scans for APs and does other slow operations.	Thus,
4619 	 * if there are no open links, the retrieval of link properties
4620 	 * (below) will proceed slowly unless we hold the link open.
4621 	 *
4622 	 * Note that failure of dlpi_open() does not necessarily mean invalid
4623 	 * link properties, because dlpi_open() may fail because of incorrect
4624 	 * autopush configuration. Therefore, we ingore the return value of
4625 	 * dlpi_open().
4626 	 */
4627 	if (!statep->ls_persist)
4628 		(void) dlpi_open(statep->ls_link, &dh, 0);
4629 
4630 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
4631 	    DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
4632 	if (buf == NULL)
4633 		die("insufficient memory");
4634 
4635 	statep->ls_propvals = (char **)(void *)buf;
4636 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
4637 		statep->ls_propvals[i] = buf +
4638 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
4639 		    i * DLADM_PROP_VAL_MAX;
4640 	}
4641 	statep->ls_line = buf +
4642 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
4643 
4644 	if (proplist != NULL) {
4645 		for (i = 0; i < proplist->pl_count; i++) {
4646 			(void) show_linkprop(linkid,
4647 			    proplist->pl_info[i].pi_name, statep);
4648 		}
4649 	} else {
4650 		(void) dladm_walk_linkprop(linkid, statep, show_linkprop);
4651 	}
4652 	if (dh != NULL)
4653 		dlpi_close(dh);
4654 	free(buf);
4655 	return (DLADM_WALK_CONTINUE);
4656 }
4657 
4658 static dladm_status_t
4659 set_linkprop_persist(datalink_id_t linkid, const char *prop_name,
4660     char **prop_val, uint_t val_cnt, boolean_t reset)
4661 {
4662 	dladm_status_t	status;
4663 
4664 	status = dladm_set_linkprop(linkid, prop_name, prop_val, val_cnt,
4665 	    DLADM_OPT_PERSIST);
4666 
4667 	if (status != DLADM_STATUS_OK) {
4668 		warn_dlerr(status, "cannot persistently %s link property",
4669 		    reset ? "reset" : "set");
4670 	}
4671 	return (status);
4672 }
4673 
4674 static void
4675 set_linkprop(int argc, char **argv, boolean_t reset, const char *use)
4676 {
4677 	int		i, option;
4678 	char		errmsg[DLADM_STRSIZE];
4679 	char		*altroot = NULL;
4680 	datalink_id_t	linkid;
4681 	prop_list_t	*proplist = NULL;
4682 	boolean_t	temp = B_FALSE;
4683 	dladm_status_t	status = DLADM_STATUS_OK;
4684 
4685 	opterr = 0;
4686 	while ((option = getopt_long(argc, argv, ":p:R:t",
4687 	    prop_longopts, NULL)) != -1) {
4688 		switch (option) {
4689 		case 'p':
4690 			if (parse_props(optarg, &proplist, reset) < 0)
4691 				die("invalid link properties specified");
4692 			break;
4693 		case 't':
4694 			temp = B_TRUE;
4695 			break;
4696 		case 'R':
4697 			altroot = optarg;
4698 			break;
4699 		default:
4700 			die_opterr(optopt, option, use);
4701 
4702 		}
4703 	}
4704 
4705 	/* get link name (required last argument) */
4706 	if (optind != (argc - 1))
4707 		usage();
4708 
4709 	if (proplist == NULL && !reset)
4710 		die("link property must be specified");
4711 
4712 	if (altroot != NULL) {
4713 		free_props(proplist);
4714 		altroot_cmd(altroot, argc, argv);
4715 	}
4716 
4717 	status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL);
4718 	if (status != DLADM_STATUS_OK)
4719 		die_dlerr(status, "link %s is not valid", argv[optind]);
4720 
4721 	if (proplist == NULL) {
4722 		status = dladm_set_linkprop(linkid, NULL, NULL, 0,
4723 		    DLADM_OPT_ACTIVE);
4724 		if (status != DLADM_STATUS_OK) {
4725 			warn_dlerr(status, "cannot reset link property "
4726 			    "on '%s'", argv[optind]);
4727 		}
4728 		if (!temp) {
4729 			dladm_status_t	s;
4730 
4731 			s = set_linkprop_persist(linkid, NULL, NULL, 0, reset);
4732 			if (s != DLADM_STATUS_OK)
4733 				status = s;
4734 		}
4735 		goto done;
4736 	}
4737 
4738 	for (i = 0; i < proplist->pl_count; i++) {
4739 		prop_info_t	*pip = &proplist->pl_info[i];
4740 		char		**val;
4741 		uint_t		count;
4742 		dladm_status_t	s;
4743 
4744 		if (reset) {
4745 			val = NULL;
4746 			count = 0;
4747 		} else {
4748 			val = pip->pi_val;
4749 			count = pip->pi_count;
4750 			if (count == 0) {
4751 				warn("no value specified for '%s'",
4752 				    pip->pi_name);
4753 				status = DLADM_STATUS_BADARG;
4754 				continue;
4755 			}
4756 		}
4757 		s = dladm_set_linkprop(linkid, pip->pi_name, val, count,
4758 		    DLADM_OPT_ACTIVE);
4759 		if (s == DLADM_STATUS_OK) {
4760 			if (!temp) {
4761 				s = set_linkprop_persist(linkid,
4762 				    pip->pi_name, val, count, reset);
4763 				if (s != DLADM_STATUS_OK)
4764 					status = s;
4765 			}
4766 			continue;
4767 		}
4768 		status = s;
4769 		switch (s) {
4770 		case DLADM_STATUS_NOTFOUND:
4771 			warn("invalid link property '%s'", pip->pi_name);
4772 			break;
4773 		case DLADM_STATUS_BADVAL: {
4774 			int		j;
4775 			char		*ptr, *lim;
4776 			char		**propvals = NULL;
4777 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
4778 
4779 			ptr = malloc((sizeof (char *) +
4780 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
4781 			    MAX_PROP_LINE);
4782 
4783 			propvals = (char **)(void *)ptr;
4784 			if (propvals == NULL)
4785 				die("insufficient memory");
4786 
4787 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
4788 				propvals[j] = ptr + sizeof (char *) *
4789 				    DLADM_MAX_PROP_VALCNT +
4790 				    j * DLADM_PROP_VAL_MAX;
4791 			}
4792 			s = dladm_get_linkprop(linkid,
4793 			    DLADM_PROP_VAL_MODIFIABLE, pip->pi_name, propvals,
4794 			    &valcnt);
4795 
4796 			if (s != DLADM_STATUS_OK) {
4797 				warn_dlerr(status, "cannot set link property "
4798 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
4799 				free(propvals);
4800 				break;
4801 			}
4802 
4803 			ptr = errmsg;
4804 			lim = ptr + DLADM_STRSIZE;
4805 			*ptr = '\0';
4806 			for (j = 0; j < valcnt; j++) {
4807 				ptr += snprintf(ptr, lim - ptr, "%s,",
4808 				    propvals[j]);
4809 				if (ptr >= lim)
4810 					break;
4811 			}
4812 			if (ptr > errmsg) {
4813 				*(ptr - 1) = '\0';
4814 				warn("link property '%s' must be one of: %s",
4815 				    pip->pi_name, errmsg);
4816 			} else
4817 				warn("invalid link property '%s'", *val);
4818 			free(propvals);
4819 			break;
4820 		}
4821 		default:
4822 			if (reset) {
4823 				warn_dlerr(status, "cannot reset link property "
4824 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
4825 			} else {
4826 				warn_dlerr(status, "cannot set link property "
4827 				    "'%s' on '%s'", pip->pi_name, argv[optind]);
4828 			}
4829 			break;
4830 		}
4831 	}
4832 done:
4833 	free_props(proplist);
4834 	if (status != DLADM_STATUS_OK)
4835 		exit(1);
4836 }
4837 
4838 static void
4839 do_set_linkprop(int argc, char **argv, const char *use)
4840 {
4841 	set_linkprop(argc, argv, B_FALSE, use);
4842 }
4843 
4844 static void
4845 do_reset_linkprop(int argc, char **argv, const char *use)
4846 {
4847 	set_linkprop(argc, argv, B_TRUE, use);
4848 }
4849 
4850 static int
4851 convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
4852     dladm_secobj_class_t class)
4853 {
4854 	int error = 0;
4855 
4856 	if (class == DLADM_SECOBJ_CLASS_WPA) {
4857 		if (len < 8 || len > 63)
4858 			return (EINVAL);
4859 		(void) memcpy(obj_val, buf, len);
4860 		*obj_lenp = len;
4861 		return (error);
4862 	}
4863 
4864 	if (class == DLADM_SECOBJ_CLASS_WEP) {
4865 		switch (len) {
4866 		case 5:			/* ASCII key sizes */
4867 		case 13:
4868 			(void) memcpy(obj_val, buf, len);
4869 			*obj_lenp = len;
4870 			break;
4871 		case 10:		/* Hex key sizes, not preceded by 0x */
4872 		case 26:
4873 			error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
4874 			break;
4875 		case 12:		/* Hex key sizes, preceded by 0x */
4876 		case 28:
4877 			if (strncmp(buf, "0x", 2) != 0)
4878 				return (EINVAL);
4879 			error = hexascii_to_octet(buf + 2, len - 2,
4880 			    obj_val, obj_lenp);
4881 			break;
4882 		default:
4883 			return (EINVAL);
4884 		}
4885 		return (error);
4886 	}
4887 
4888 	return (ENOENT);
4889 }
4890 
4891 /* ARGSUSED */
4892 static void
4893 defersig(int sig)
4894 {
4895 	signalled = sig;
4896 }
4897 
4898 static int
4899 get_secobj_from_tty(uint_t try, const char *objname, char *buf)
4900 {
4901 	uint_t		len = 0;
4902 	int		c;
4903 	struct termios	stored, current;
4904 	void		(*sigfunc)(int);
4905 
4906 	/*
4907 	 * Turn off echo -- but before we do so, defer SIGINT handling
4908 	 * so that a ^C doesn't leave the terminal corrupted.
4909 	 */
4910 	sigfunc = signal(SIGINT, defersig);
4911 	(void) fflush(stdin);
4912 	(void) tcgetattr(0, &stored);
4913 	current = stored;
4914 	current.c_lflag &= ~(ICANON|ECHO);
4915 	current.c_cc[VTIME] = 0;
4916 	current.c_cc[VMIN] = 1;
4917 	(void) tcsetattr(0, TCSANOW, &current);
4918 again:
4919 	if (try == 1)
4920 		(void) printf(gettext("provide value for '%s': "), objname);
4921 	else
4922 		(void) printf(gettext("confirm value for '%s': "), objname);
4923 
4924 	(void) fflush(stdout);
4925 	while (signalled == 0) {
4926 		c = getchar();
4927 		if (c == '\n' || c == '\r') {
4928 			if (len != 0)
4929 				break;
4930 			(void) putchar('\n');
4931 			goto again;
4932 		}
4933 
4934 		buf[len++] = c;
4935 		if (len >= DLADM_SECOBJ_VAL_MAX - 1)
4936 			break;
4937 		(void) putchar('*');
4938 	}
4939 
4940 	(void) putchar('\n');
4941 	(void) fflush(stdin);
4942 
4943 	/*
4944 	 * Restore terminal setting and handle deferred signals.
4945 	 */
4946 	(void) tcsetattr(0, TCSANOW, &stored);
4947 
4948 	(void) signal(SIGINT, sigfunc);
4949 	if (signalled != 0)
4950 		(void) kill(getpid(), signalled);
4951 
4952 	return (len);
4953 }
4954 
4955 static int
4956 get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
4957     dladm_secobj_class_t class, FILE *filep)
4958 {
4959 	int		rval;
4960 	uint_t		len, len2;
4961 	char		buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
4962 
4963 	if (filep == NULL) {
4964 		len = get_secobj_from_tty(1, obj_name, buf);
4965 		rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
4966 		if (rval == 0) {
4967 			len2 = get_secobj_from_tty(2, obj_name, buf2);
4968 			if (len != len2 || memcmp(buf, buf2, len) != 0)
4969 				rval = ENOTSUP;
4970 		}
4971 		return (rval);
4972 	} else {
4973 		for (;;) {
4974 			if (fgets(buf, sizeof (buf), filep) == NULL)
4975 				break;
4976 			if (isspace(buf[0]))
4977 				continue;
4978 
4979 			len = strlen(buf);
4980 			if (buf[len - 1] == '\n') {
4981 				buf[len - 1] = '\0';
4982 				len--;
4983 			}
4984 			break;
4985 		}
4986 		(void) fclose(filep);
4987 	}
4988 	return (convert_secobj(buf, len, obj_val, obj_lenp, class));
4989 }
4990 
4991 static boolean_t
4992 check_auth(const char *auth)
4993 {
4994 	struct passwd	*pw;
4995 
4996 	if ((pw = getpwuid(getuid())) == NULL)
4997 		return (B_FALSE);
4998 
4999 	return (chkauthattr(auth, pw->pw_name) != 0);
5000 }
5001 
5002 static void
5003 audit_secobj(char *auth, char *class, char *obj,
5004     boolean_t success, boolean_t create)
5005 {
5006 	adt_session_data_t	*ah;
5007 	adt_event_data_t	*event;
5008 	au_event_t		flag;
5009 	char			*errstr;
5010 
5011 	if (create) {
5012 		flag = ADT_dladm_create_secobj;
5013 		errstr = "ADT_dladm_create_secobj";
5014 	} else {
5015 		flag = ADT_dladm_delete_secobj;
5016 		errstr = "ADT_dladm_delete_secobj";
5017 	}
5018 
5019 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
5020 		die("adt_start_session: %s", strerror(errno));
5021 
5022 	if ((event = adt_alloc_event(ah, flag)) == NULL)
5023 		die("adt_alloc_event (%s): %s", errstr, strerror(errno));
5024 
5025 	/* fill in audit info */
5026 	if (create) {
5027 		event->adt_dladm_create_secobj.auth_used = auth;
5028 		event->adt_dladm_create_secobj.obj_class = class;
5029 		event->adt_dladm_create_secobj.obj_name = obj;
5030 	} else {
5031 		event->adt_dladm_delete_secobj.auth_used = auth;
5032 		event->adt_dladm_delete_secobj.obj_class = class;
5033 		event->adt_dladm_delete_secobj.obj_name = obj;
5034 	}
5035 
5036 	if (success) {
5037 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
5038 			die("adt_put_event (%s, success): %s", errstr,
5039 			    strerror(errno));
5040 		}
5041 	} else {
5042 		if (adt_put_event(event, ADT_FAILURE,
5043 		    ADT_FAIL_VALUE_AUTH) != 0) {
5044 			die("adt_put_event: (%s, failure): %s", errstr,
5045 			    strerror(errno));
5046 		}
5047 	}
5048 
5049 	adt_free_event(event);
5050 	(void) adt_end_session(ah);
5051 }
5052 
5053 #define	MAX_SECOBJS		32
5054 #define	MAX_SECOBJ_NAMELEN	32
5055 static void
5056 do_create_secobj(int argc, char **argv, const char *use)
5057 {
5058 	int			option, rval;
5059 	FILE			*filep = NULL;
5060 	char			*obj_name = NULL;
5061 	char			*class_name = NULL;
5062 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
5063 	uint_t			obj_len;
5064 	boolean_t		success, temp = B_FALSE;
5065 	dladm_status_t		status;
5066 	dladm_secobj_class_t	class = -1;
5067 	uid_t			euid;
5068 
5069 	opterr = 0;
5070 	(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
5071 	while ((option = getopt_long(argc, argv, ":f:c:R:t",
5072 	    wifi_longopts, NULL)) != -1) {
5073 		switch (option) {
5074 		case 'f':
5075 			euid = geteuid();
5076 			(void) seteuid(getuid());
5077 			filep = fopen(optarg, "r");
5078 			if (filep == NULL) {
5079 				die("cannot open %s: %s", optarg,
5080 				    strerror(errno));
5081 			}
5082 			(void) seteuid(euid);
5083 			break;
5084 		case 'c':
5085 			class_name = optarg;
5086 			status = dladm_str2secobjclass(optarg, &class);
5087 			if (status != DLADM_STATUS_OK) {
5088 				die("invalid secure object class '%s', "
5089 				    "valid values are: wep, wpa", optarg);
5090 			}
5091 			break;
5092 		case 't':
5093 			temp = B_TRUE;
5094 			break;
5095 		case 'R':
5096 			status = dladm_set_rootdir(optarg);
5097 			if (status != DLADM_STATUS_OK) {
5098 				die_dlerr(status, "invalid directory "
5099 				    "specified");
5100 			}
5101 			break;
5102 		default:
5103 			die_opterr(optopt, option, use);
5104 			break;
5105 		}
5106 	}
5107 
5108 	if (optind == (argc - 1))
5109 		obj_name = argv[optind];
5110 	else if (optind != argc)
5111 		usage();
5112 
5113 	if (class == -1)
5114 		die("secure object class required");
5115 
5116 	if (obj_name == NULL)
5117 		die("secure object name required");
5118 
5119 	success = check_auth(LINK_SEC_AUTH);
5120 	audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
5121 	if (!success)
5122 		die("authorization '%s' is required", LINK_SEC_AUTH);
5123 
5124 	rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
5125 	if (rval != 0) {
5126 		switch (rval) {
5127 		case ENOENT:
5128 			die("invalid secure object class");
5129 			break;
5130 		case EINVAL:
5131 			die("invalid secure object value");
5132 			break;
5133 		case ENOTSUP:
5134 			die("verification failed");
5135 			break;
5136 		default:
5137 			die("invalid secure object: %s", strerror(rval));
5138 			break;
5139 		}
5140 	}
5141 
5142 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
5143 	    DLADM_OPT_CREATE | DLADM_OPT_ACTIVE);
5144 	if (status != DLADM_STATUS_OK) {
5145 		die_dlerr(status, "could not create secure object '%s'",
5146 		    obj_name);
5147 	}
5148 	if (temp)
5149 		return;
5150 
5151 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
5152 	    DLADM_OPT_PERSIST);
5153 	if (status != DLADM_STATUS_OK) {
5154 		warn_dlerr(status, "could not persistently create secure "
5155 		    "object '%s'", obj_name);
5156 	}
5157 }
5158 
5159 static void
5160 do_delete_secobj(int argc, char **argv, const char *use)
5161 {
5162 	int		i, option;
5163 	boolean_t	temp = B_FALSE;
5164 	split_t		*sp = NULL;
5165 	boolean_t	success;
5166 	dladm_status_t	status, pstatus;
5167 
5168 	opterr = 0;
5169 	status = pstatus = DLADM_STATUS_OK;
5170 	while ((option = getopt_long(argc, argv, ":R:t",
5171 	    wifi_longopts, NULL)) != -1) {
5172 		switch (option) {
5173 		case 't':
5174 			temp = B_TRUE;
5175 			break;
5176 		case 'R':
5177 			status = dladm_set_rootdir(optarg);
5178 			if (status != DLADM_STATUS_OK) {
5179 				die_dlerr(status, "invalid directory "
5180 				    "specified");
5181 			}
5182 			break;
5183 		default:
5184 			die_opterr(optopt, option, use);
5185 			break;
5186 		}
5187 	}
5188 
5189 	if (optind == (argc - 1)) {
5190 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
5191 		if (sp == NULL) {
5192 			die("invalid secure object name(s): '%s'",
5193 			    argv[optind]);
5194 		}
5195 	} else if (optind != argc)
5196 		usage();
5197 
5198 	if (sp == NULL || sp->s_nfields < 1)
5199 		die("secure object name required");
5200 
5201 	success = check_auth(LINK_SEC_AUTH);
5202 	audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE);
5203 	if (!success)
5204 		die("authorization '%s' is required", LINK_SEC_AUTH);
5205 
5206 	for (i = 0; i < sp->s_nfields; i++) {
5207 		status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_ACTIVE);
5208 		if (!temp) {
5209 			pstatus = dladm_unset_secobj(sp->s_fields[i],
5210 			    DLADM_OPT_PERSIST);
5211 		} else {
5212 			pstatus = DLADM_STATUS_OK;
5213 		}
5214 
5215 		if (status != DLADM_STATUS_OK) {
5216 			warn_dlerr(status, "could not delete secure object "
5217 			    "'%s'", sp->s_fields[i]);
5218 		}
5219 		if (pstatus != DLADM_STATUS_OK) {
5220 			warn_dlerr(pstatus, "could not persistently delete "
5221 			    "secure object '%s'", sp->s_fields[i]);
5222 		}
5223 	}
5224 	if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK)
5225 		exit(1);
5226 }
5227 
5228 typedef struct show_secobj_state {
5229 	boolean_t	ss_persist;
5230 	boolean_t	ss_parseable;
5231 	boolean_t	ss_header;
5232 	print_state_t	ss_print;
5233 } show_secobj_state_t;
5234 
5235 
5236 static boolean_t
5237 show_secobj(void *arg, const char *obj_name)
5238 {
5239 	uint_t			obj_len = DLADM_SECOBJ_VAL_MAX;
5240 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
5241 	char			buf[DLADM_STRSIZE];
5242 	uint_t			flags = 0;
5243 	dladm_secobj_class_t	class;
5244 	show_secobj_state_t	*statep = arg;
5245 	dladm_status_t		status;
5246 	secobj_fields_buf_t	sbuf;
5247 
5248 	if (statep->ss_persist)
5249 		flags |= DLADM_OPT_PERSIST;
5250 
5251 	status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags);
5252 	if (status != DLADM_STATUS_OK)
5253 		die_dlerr(status, "cannot get secure object '%s'", obj_name);
5254 
5255 	if (statep->ss_header) {
5256 		statep->ss_header = B_FALSE;
5257 		if (!statep->ss_parseable)
5258 			print_header(&statep->ss_print);
5259 	}
5260 
5261 	(void) snprintf(sbuf.ss_obj_name, sizeof (sbuf.ss_obj_name),
5262 	    obj_name);
5263 	(void) dladm_secobjclass2str(class, buf);
5264 	(void) snprintf(sbuf.ss_class, sizeof (sbuf.ss_class), "%s", buf);
5265 	if (getuid() == 0) {
5266 		char	val[DLADM_SECOBJ_VAL_MAX * 2];
5267 		uint_t	len = sizeof (val);
5268 
5269 		if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0)
5270 			(void) snprintf(sbuf.ss_val,
5271 			    sizeof (sbuf.ss_val), "%s", val);
5272 	}
5273 	dladm_print_output(&statep->ss_print, statep->ss_parseable,
5274 	    dladm_print_field, (void *)&sbuf);
5275 	return (B_TRUE);
5276 }
5277 
5278 static void
5279 do_show_secobj(int argc, char **argv, const char *use)
5280 {
5281 	int			option;
5282 	show_secobj_state_t	state;
5283 	dladm_status_t		status;
5284 	boolean_t		o_arg = B_FALSE;
5285 	uint_t			i;
5286 	split_t			*sp;
5287 	uint_t			flags;
5288 	char			*fields_str = NULL;
5289 	print_field_t		**fields;
5290 	uint_t			nfields;
5291 	char			*def_fields = "object,class";
5292 	char			*all_fields = "object,class,value";
5293 
5294 	opterr = 0;
5295 	bzero(&state, sizeof (state));
5296 	state.ss_parseable = B_FALSE;
5297 	fields_str = def_fields;
5298 	state.ss_persist = B_FALSE;
5299 	state.ss_parseable = B_FALSE;
5300 	state.ss_header = B_TRUE;
5301 	while ((option = getopt_long(argc, argv, ":pPo:",
5302 	    wifi_longopts, NULL)) != -1) {
5303 		switch (option) {
5304 		case 'p':
5305 			state.ss_parseable = B_TRUE;
5306 			break;
5307 		case 'P':
5308 			state.ss_persist = B_TRUE;
5309 			break;
5310 		case 'o':
5311 			o_arg = B_TRUE;
5312 			if (strcasecmp(optarg, "all") == 0)
5313 				fields_str = all_fields;
5314 			else
5315 				fields_str = optarg;
5316 			break;
5317 		default:
5318 			die_opterr(optopt, option, use);
5319 			break;
5320 		}
5321 	}
5322 
5323 	if (state.ss_parseable && !o_arg)
5324 		die("option -c requires -o");
5325 
5326 	if (state.ss_parseable && fields_str == all_fields)
5327 		die("\"-o all\" is invalid with -p");
5328 
5329 	fields = parse_output_fields(fields_str, secobj_fields,
5330 	    DEV_SOBJ_FIELDS, CMD_TYPE_ANY, &nfields);
5331 
5332 	if (fields == NULL) {
5333 		die("invalid field(s) specified");
5334 		return;
5335 	}
5336 	state.ss_print.ps_fields = fields;
5337 	state.ss_print.ps_nfields = nfields;
5338 
5339 	flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
5340 	if (optind == (argc - 1)) {
5341 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
5342 		if (sp == NULL) {
5343 			die("invalid secure object name(s): '%s'",
5344 			    argv[optind]);
5345 		}
5346 		for (i = 0; i < sp->s_nfields; i++) {
5347 			if (!show_secobj(&state, sp->s_fields[i]))
5348 				break;
5349 		}
5350 		splitfree(sp);
5351 		return;
5352 	} else if (optind != argc)
5353 		usage();
5354 
5355 	status = dladm_walk_secobj(&state, show_secobj, flags);
5356 	if (status != DLADM_STATUS_OK)
5357 		die_dlerr(status, "show-secobj");
5358 }
5359 
5360 /*ARGSUSED*/
5361 static int
5362 i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
5363 {
5364 	(void) dladm_init_linkprop(linkid, B_TRUE);
5365 	return (DLADM_WALK_CONTINUE);
5366 }
5367 
5368 /*ARGSUSED*/
5369 static void
5370 do_init_linkprop(int argc, char **argv, const char *use)
5371 {
5372 	int			option;
5373 	dladm_status_t		status;
5374 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
5375 	datalink_media_t	media = DATALINK_ANY_MEDIATYPE;
5376 	uint_t			any_media = B_TRUE;
5377 
5378 	opterr = 0;
5379 	while ((option = getopt(argc, argv, ":w")) != -1) {
5380 		switch (option) {
5381 		case 'w':
5382 			media = DL_WIFI;
5383 			any_media = B_FALSE;
5384 			break;
5385 		default:
5386 			/*
5387 			 * Because init-linkprop is not a public command,
5388 			 * print the usage instead.
5389 			 */
5390 			usage();
5391 			break;
5392 		}
5393 	}
5394 
5395 	if (optind == (argc - 1)) {
5396 		if ((status = dladm_name2info(argv[optind], &linkid, NULL, NULL,
5397 		    NULL)) != DLADM_STATUS_OK)
5398 			die_dlerr(status, "link %s is not valid", argv[optind]);
5399 	} else if (optind != argc) {
5400 		usage();
5401 	}
5402 
5403 	if (linkid == DATALINK_ALL_LINKID) {
5404 		/*
5405 		 * linkprops of links of other classes have been initialized as
5406 		 * part of the dladm up-xxx operation.
5407 		 */
5408 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
5409 		    DATALINK_CLASS_PHYS, media, DLADM_OPT_PERSIST);
5410 	} else {
5411 		(void) dladm_init_linkprop(linkid, any_media);
5412 	}
5413 }
5414 
5415 /* ARGSUSED */
5416 static void
5417 do_show_ether(int argc, char **argv, const char *use)
5418 {
5419 	int 			option;
5420 	datalink_id_t		linkid;
5421 	print_ether_state_t 	state;
5422 	print_field_t 		**fields;
5423 	boolean_t		o_arg = B_FALSE;
5424 	char			*fields_str;
5425 	uint_t			nfields;
5426 	char *all_fields =
5427 	    "link,ptype,state,auto,speed-duplex,pause,rem_fault";
5428 	char *default_fields =
5429 	    "link,ptype,state,auto,speed-duplex,pause";
5430 
5431 	fields_str = default_fields;
5432 	bzero(&state, sizeof (state));
5433 	state.es_link = NULL;
5434 	state.es_parseable = B_FALSE;
5435 
5436 	while ((option = getopt_long(argc, argv, "o:px",
5437 	    showeth_lopts, NULL)) != -1) {
5438 		switch (option) {
5439 			case 'x':
5440 				state.es_extended = B_TRUE;
5441 				break;
5442 			case 'p':
5443 				state.es_parseable = B_TRUE;
5444 				break;
5445 			case 'o':
5446 				o_arg = B_TRUE;
5447 				if (strcasecmp(optarg, "all") == 0)
5448 					fields_str = all_fields;
5449 				else
5450 					fields_str = optarg;
5451 				break;
5452 			default:
5453 				die_opterr(optopt, option, use);
5454 				break;
5455 		}
5456 	}
5457 
5458 	if (state.es_parseable && !o_arg)
5459 		die("-p requires -o");
5460 
5461 	if (state.es_parseable && fields_str == all_fields)
5462 		die("\"-o all\" is invalid with -p");
5463 
5464 	if (optind == (argc - 1))
5465 		state.es_link = argv[optind];
5466 
5467 	fields = parse_output_fields(fields_str, ether_fields,
5468 	    ETHER_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
5469 
5470 	if (fields == NULL) {
5471 		die("invalid field(s) specified");
5472 		exit(EXIT_FAILURE);
5473 	}
5474 	state.es_print.ps_fields = fields;
5475 	state.es_print.ps_nfields = nfields;
5476 
5477 	if (state.es_link == NULL) {
5478 		(void) dladm_walk_datalink_id(show_etherprop, &state,
5479 		    DATALINK_CLASS_PHYS, DL_ETHER,
5480 		    DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
5481 	} else {
5482 		if (!link_is_ether(state.es_link, &linkid)) {
5483 			die("invalid link specified");
5484 		}
5485 		(void) show_etherprop(linkid, &state);
5486 	}
5487 
5488 	exit(DLADM_STATUS_OK);
5489 
5490 }
5491 
5492 static char *
5493 dladm_print_field(print_field_t *pf, void *arg)
5494 {
5495 	char *value;
5496 
5497 	value = (char *)arg + pf->pf_offset;
5498 	return (value);
5499 }
5500 
5501 static int
5502 show_etherprop(datalink_id_t linkid, void *arg)
5503 {
5504 	print_ether_state_t *statep = arg;
5505 	char buf[DLADM_STRSIZE];
5506 	int speed;
5507 	uint64_t s;
5508 	uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf;
5509 	ether_fields_buf_t ebuf;
5510 	char speed_unit = 'M';
5511 
5512 	if (dladm_datalink_id2info(linkid, NULL, NULL, NULL,
5513 	    ebuf.eth_link, sizeof (ebuf.eth_link)) != DLADM_STATUS_OK) {
5514 		return (DLADM_WALK_CONTINUE);
5515 	}
5516 
5517 	if (!statep->es_header && !statep->es_parseable) {
5518 		print_header(&statep->es_print);
5519 		statep->es_header = B_TRUE;
5520 	}
5521 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5522 	    "%s", "current");
5523 
5524 	(void) dladm_get_single_mac_stat(linkid, "link_autoneg",
5525 	    KSTAT_DATA_UINT32, &autoneg);
5526 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5527 	    "%s", (autoneg ? "yes" : "no"));
5528 
5529 	(void) dladm_get_single_mac_stat(linkid, "link_pause",
5530 	    KSTAT_DATA_UINT32, &pause);
5531 	(void) dladm_get_single_mac_stat(linkid, "link_asmpause",
5532 	    KSTAT_DATA_UINT32, &asmpause);
5533 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5534 	    "%s", pause_str(pause, asmpause));
5535 
5536 	(void) dladm_get_single_mac_stat(linkid, "ifspeed",
5537 	    KSTAT_DATA_UINT64, &s);
5538 	speed = (int)(s/1000000ull);
5539 
5540 	if (speed >= 1000) {
5541 		speed = speed/1000;
5542 		speed_unit = 'G';
5543 	}
5544 	(void) get_linkduplex(ebuf.eth_link, B_TRUE, buf);
5545 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%d%c-%c",
5546 	    speed, speed_unit, buf[0]);
5547 
5548 	(void) get_linkstate(ebuf.eth_link, B_TRUE, buf);
5549 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state),
5550 	    "%s", buf);
5551 
5552 	(void) dladm_get_single_mac_stat(linkid, "adv_rem_fault",
5553 	    KSTAT_DATA_UINT32, &adv_rf);
5554 	(void) dladm_get_single_mac_stat(linkid, "cap_rem_fault",
5555 	    KSTAT_DATA_UINT32, &cap_rf);
5556 	(void) dladm_get_single_mac_stat(linkid, "lp_rem_fault",
5557 	    KSTAT_DATA_UINT32, &lp_rf);
5558 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5559 	    "%s", (adv_rf == 0 && lp_rf == 0 ? "none" : "fault"));
5560 
5561 	dladm_print_output(&statep->es_print, statep->es_parseable,
5562 	    dladm_print_field, &ebuf);
5563 
5564 	if (statep->es_extended)
5565 		show_ether_xprop(linkid, arg);
5566 
5567 	return (DLADM_WALK_CONTINUE);
5568 }
5569 
5570 /* ARGSUSED */
5571 static void
5572 do_init_secobj(int argc, char **argv, const char *use)
5573 {
5574 	dladm_status_t status;
5575 
5576 	status = dladm_init_secobj();
5577 	if (status != DLADM_STATUS_OK)
5578 		die_dlerr(status, "secure object initialization failed");
5579 }
5580 
5581 /*
5582  * "-R" option support. It is used for live upgrading. Append dladm commands
5583  * to a upgrade script which will be run when the alternative root boots up:
5584  *
5585  * - If the /etc/dladm/datalink.conf file exists on the alternative root,
5586  * append dladm commands to the <altroot>/var/svc/profile/upgrade_datalink
5587  * script. This script will be run as part of the network/physical service.
5588  * We cannot defer this to /var/svc/profile/upgrade because then the
5589  * configuration will not be able to take effect before network/physical
5590  * plumbs various interfaces.
5591  *
5592  * - If the /etc/dladm/datalink.conf file does not exist on the alternative
5593  * root, append dladm commands to the <altroot>/var/svc/profile/upgrade script,
5594  * which will be run in the manifest-import service.
5595  *
5596  * Note that the SMF team is considering to move the manifest-import service
5597  * to be run at the very begining of boot. Once that is done, the need for
5598  * the /var/svc/profile/upgrade_datalink script will not exist any more.
5599  */
5600 static void
5601 altroot_cmd(char *altroot, int argc, char *argv[])
5602 {
5603 	char		path[MAXPATHLEN];
5604 	struct stat	stbuf;
5605 	FILE		*fp;
5606 	int		i;
5607 
5608 	/*
5609 	 * Check for the existence of the /etc/dladm/datalink.conf
5610 	 * configuration file, and determine the name of script file.
5611 	 */
5612 	(void) snprintf(path, MAXPATHLEN, "/%s/etc/dladm/datalink.conf",
5613 	    altroot);
5614 	if (stat(path, &stbuf) < 0) {
5615 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
5616 		    SMF_UPGRADE_FILE);
5617 	} else {
5618 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
5619 		    SMF_UPGRADEDATALINK_FILE);
5620 	}
5621 
5622 	if ((fp = fopen(path, "a+")) == NULL)
5623 		die("operation not supported on %s", altroot);
5624 
5625 	(void) fprintf(fp, "/sbin/dladm ");
5626 	for (i = 0; i < argc; i++) {
5627 		/*
5628 		 * Directly write to the file if it is not the "-R <altroot>"
5629 		 * option. In which case, skip it.
5630 		 */
5631 		if (strcmp(argv[i], "-R") != 0)
5632 			(void) fprintf(fp, "%s ", argv[i]);
5633 		else
5634 			i ++;
5635 	}
5636 	(void) fprintf(fp, "%s\n", SMF_DLADM_UPGRADE_MSG);
5637 	(void) fclose(fp);
5638 	exit(0);
5639 }
5640 
5641 /*
5642  * Convert the string to an integer. Note that the string must not have any
5643  * trailing non-integer characters.
5644  */
5645 static boolean_t
5646 str2int(const char *str, int *valp)
5647 {
5648 	int	val;
5649 	char	*endp = NULL;
5650 
5651 	errno = 0;
5652 	val = strtol(str, &endp, 10);
5653 	if (errno != 0 || *endp != '\0')
5654 		return (B_FALSE);
5655 
5656 	*valp = val;
5657 	return (B_TRUE);
5658 }
5659 
5660 /* PRINTFLIKE1 */
5661 static void
5662 warn(const char *format, ...)
5663 {
5664 	va_list alist;
5665 
5666 	format = gettext(format);
5667 	(void) fprintf(stderr, "%s: warning: ", progname);
5668 
5669 	va_start(alist, format);
5670 	(void) vfprintf(stderr, format, alist);
5671 	va_end(alist);
5672 
5673 	(void) putchar('\n');
5674 }
5675 
5676 /* PRINTFLIKE2 */
5677 static void
5678 warn_dlerr(dladm_status_t err, const char *format, ...)
5679 {
5680 	va_list alist;
5681 	char	errmsg[DLADM_STRSIZE];
5682 
5683 	format = gettext(format);
5684 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
5685 
5686 	va_start(alist, format);
5687 	(void) vfprintf(stderr, format, alist);
5688 	va_end(alist);
5689 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
5690 }
5691 
5692 /* PRINTFLIKE2 */
5693 static void
5694 die_dlerr(dladm_status_t err, const char *format, ...)
5695 {
5696 	va_list alist;
5697 	char	errmsg[DLADM_STRSIZE];
5698 
5699 	format = gettext(format);
5700 	(void) fprintf(stderr, "%s: ", progname);
5701 
5702 	va_start(alist, format);
5703 	(void) vfprintf(stderr, format, alist);
5704 	va_end(alist);
5705 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
5706 
5707 	exit(EXIT_FAILURE);
5708 }
5709 
5710 /* PRINTFLIKE1 */
5711 static void
5712 die(const char *format, ...)
5713 {
5714 	va_list alist;
5715 
5716 	format = gettext(format);
5717 	(void) fprintf(stderr, "%s: ", progname);
5718 
5719 	va_start(alist, format);
5720 	(void) vfprintf(stderr, format, alist);
5721 	va_end(alist);
5722 
5723 	(void) putchar('\n');
5724 	exit(EXIT_FAILURE);
5725 }
5726 
5727 static void
5728 die_optdup(int opt)
5729 {
5730 	die("the option -%c cannot be specified more than once", opt);
5731 }
5732 
5733 static void
5734 die_opterr(int opt, int opterr, const char *usage)
5735 {
5736 	switch (opterr) {
5737 	case ':':
5738 		die("option '-%c' requires a value\nusage: %s", opt,
5739 		    gettext(usage));
5740 		break;
5741 	case '?':
5742 	default:
5743 		die("unrecognized option '-%c'\nusage: %s", opt,
5744 		    gettext(usage));
5745 		break;
5746 	}
5747 }
5748 
5749 static void
5750 show_ether_xprop(datalink_id_t linkid, void *arg)
5751 {
5752 	print_ether_state_t *statep = arg;
5753 	char buf[DLADM_STRSIZE];
5754 	uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf;
5755 	boolean_t add_comma, r1;
5756 	ether_fields_buf_t ebuf;
5757 
5758 	/* capable */
5759 	bzero(&ebuf, sizeof (ebuf));
5760 	(void) snprintf(ebuf.eth_link, sizeof (ebuf.eth_link), "");
5761 
5762 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5763 	    "%s", "capable");
5764 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
5765 
5766 	(void) dladm_get_single_mac_stat(linkid, "cap_autoneg",
5767 	    KSTAT_DATA_UINT32, &autoneg);
5768 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5769 	    "%s", (autoneg ? "yes" : "no"));
5770 
5771 	add_comma = B_FALSE;
5772 	bzero(buf, sizeof (buf));
5773 	r1 = get_speed_duplex(linkid, "cap_1000", buf, "1G", B_FALSE);
5774 	if (r1)
5775 		add_comma = B_TRUE;
5776 	r1 = get_speed_duplex(linkid, "cap_100", buf, "100M", add_comma);
5777 	if (r1)
5778 		add_comma = B_TRUE;
5779 	r1 = get_speed_duplex(linkid, "cap_10", buf, "10M", add_comma);
5780 	add_comma = B_FALSE;
5781 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
5782 
5783 	(void) dladm_get_single_mac_stat(linkid, "cap_pause",
5784 	    KSTAT_DATA_UINT32, &pause);
5785 	(void) dladm_get_single_mac_stat(linkid, "cap_asmpause",
5786 	    KSTAT_DATA_UINT32, &asmpause);
5787 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5788 	    "%s", pause_str(pause, asmpause));
5789 
5790 	(void) dladm_get_single_mac_stat(linkid, "adv_rem_fault",
5791 	    KSTAT_DATA_UINT32, &adv_rf);
5792 	(void) dladm_get_single_mac_stat(linkid, "cap_rem_fault",
5793 	    KSTAT_DATA_UINT32, &cap_rf);
5794 	(void) dladm_get_single_mac_stat(linkid, "lp_rem_fault",
5795 	    KSTAT_DATA_UINT32, &lp_rf);
5796 
5797 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5798 	    "%s", (cap_rf ? "yes" : "no"));
5799 
5800 	dladm_print_output(&statep->es_print, statep->es_parseable,
5801 	    dladm_print_field, &ebuf);
5802 
5803 	/* advertised */
5804 	bzero(&ebuf, sizeof (ebuf));
5805 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5806 	    "%s", "adv");
5807 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
5808 
5809 	(void) dladm_get_single_mac_stat(linkid, "adv_cap_autoneg",
5810 	    KSTAT_DATA_UINT32, &autoneg);
5811 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5812 	    "%s", (autoneg ? "yes" : "no"));
5813 
5814 	add_comma = B_FALSE;
5815 	bzero(buf, sizeof (buf));
5816 	r1 = get_speed_duplex(linkid, "adv_cap_1000", buf, "1G", add_comma);
5817 	if (r1)
5818 		add_comma = B_TRUE;
5819 	r1 = get_speed_duplex(linkid, "adv_cap_100", buf, "100M", add_comma);
5820 	if (r1)
5821 		add_comma = B_TRUE;
5822 	r1 = get_speed_duplex(linkid, "adv_cap_10", buf, "10M", add_comma);
5823 	add_comma = B_FALSE;
5824 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
5825 
5826 	(void) dladm_get_single_mac_stat(linkid, "adv_cap_pause",
5827 	    KSTAT_DATA_UINT32, &pause);
5828 	(void) dladm_get_single_mac_stat(linkid, "adv_cap_asmpause",
5829 	    KSTAT_DATA_UINT32, &asmpause);
5830 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5831 	    "%s", pause_str(pause, asmpause));
5832 
5833 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5834 	    "%s", (adv_rf ? "fault" : "none"));
5835 
5836 	dladm_print_output(&statep->es_print, statep->es_parseable,
5837 	    dladm_print_field, &ebuf);
5838 
5839 	/* peeradv */
5840 	bzero(&ebuf, sizeof (ebuf));
5841 	(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
5842 	    "%s", "peeradv");
5843 	(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
5844 
5845 	(void) dladm_get_single_mac_stat(linkid, "lp_cap_autoneg",
5846 	    KSTAT_DATA_UINT32, &autoneg);
5847 	(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
5848 	    "%s", (autoneg ? "yes" : "no"));
5849 
5850 	add_comma = B_FALSE;
5851 	bzero(buf, sizeof (buf));
5852 	r1 = get_speed_duplex(linkid, "lp_cap_1000", buf, "1G", add_comma);
5853 	if (r1)
5854 		add_comma = B_TRUE;
5855 	r1 = get_speed_duplex(linkid, "lp_cap_100", buf, "100M", add_comma);
5856 	if (r1)
5857 		add_comma = B_TRUE;
5858 	r1 = get_speed_duplex(linkid, "lp_cap_10", buf, "10M", add_comma);
5859 	(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
5860 
5861 	(void) dladm_get_single_mac_stat(linkid, "lp_cap_pause",
5862 	    KSTAT_DATA_UINT32, &pause);
5863 	(void) dladm_get_single_mac_stat(linkid, "lp_cap_asmpause",
5864 	    KSTAT_DATA_UINT32, &asmpause);
5865 	(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
5866 	    "%s", pause_str(pause, asmpause));
5867 
5868 	(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
5869 	    "%s", (lp_rf ? "fault" : "none"));
5870 
5871 	dladm_print_output(&statep->es_print, statep->es_parseable,
5872 	    dladm_print_field, &ebuf);
5873 }
5874 
5875 static boolean_t
5876 get_speed_duplex(datalink_id_t linkid, const char *mii_prop_prefix,
5877     char *spbuf, char *sp, boolean_t add_comma)
5878 {
5879 	int speed, duplex = 0;
5880 	boolean_t ret = B_FALSE;
5881 	char mii_prop[DLADM_STRSIZE];
5882 
5883 	(void) snprintf(mii_prop, DLADM_STRSIZE, "%sfdx", mii_prop_prefix);
5884 	(void) dladm_get_single_mac_stat(linkid, mii_prop, KSTAT_DATA_UINT32,
5885 	    &speed);
5886 	if (speed) {
5887 		ret = B_TRUE;
5888 		duplex  |= IS_FDX;
5889 	}
5890 	(void) snprintf(mii_prop, DLADM_STRSIZE, "%shdx", mii_prop_prefix);
5891 	(void) dladm_get_single_mac_stat(linkid, mii_prop,
5892 	    KSTAT_DATA_UINT32, &speed);
5893 	if (speed) {
5894 		ret = B_TRUE;
5895 		duplex |= IS_HDX;
5896 	}
5897 	if (ret) {
5898 		if (add_comma)
5899 			(void) strncat(spbuf, ",", DLADM_STRSIZE);
5900 		(void) strncat(spbuf, sp, DLADM_STRSIZE);
5901 		if ((duplex & (IS_FDX|IS_HDX)) == (IS_FDX|IS_HDX))
5902 			(void) strncat(spbuf, "-fh", DLADM_STRSIZE);
5903 		else if (duplex & IS_FDX)
5904 			(void) strncat(spbuf, "-f", DLADM_STRSIZE);
5905 		else if (duplex & IS_HDX)
5906 			(void) strncat(spbuf, "-h", DLADM_STRSIZE);
5907 	}
5908 	return (ret);
5909 }
5910 
5911 static void
5912 dladm_print_output(print_state_t *statep, boolean_t parseable,
5913     print_callback_t fn, void *arg)
5914 {
5915 	int i;
5916 	char *value;
5917 	print_field_t **pf;
5918 
5919 	pf = statep->ps_fields;
5920 	for (i = 0; i < statep->ps_nfields; i++) {
5921 		statep->ps_lastfield = (i + 1 == statep->ps_nfields);
5922 		value = (*fn)(pf[i], arg);
5923 		if (value != NULL)
5924 			print_field(statep, pf[i], value, parseable);
5925 	}
5926 	(void) putchar('\n');
5927 }
5928 
5929 static void
5930 print_header(print_state_t *ps)
5931 {
5932 	int i;
5933 	print_field_t **pf;
5934 
5935 	pf = ps->ps_fields;
5936 	for (i = 0; i < ps->ps_nfields; i++) {
5937 		ps->ps_lastfield = (i + 1 == ps->ps_nfields);
5938 		print_field(ps, pf[i], pf[i]->pf_header, B_FALSE);
5939 	}
5940 	(void) putchar('\n');
5941 }
5942 
5943 static char *
5944 pause_str(int pause, int asmpause)
5945 {
5946 	if (pause == 1)
5947 		return ("bi");
5948 	if (asmpause == 1)
5949 		return ("tx");
5950 	return ("none");
5951 }
5952 
5953 static boolean_t
5954 link_is_ether(const char *link, datalink_id_t *linkid)
5955 {
5956 	uint32_t media;
5957 	datalink_class_t class;
5958 
5959 	if (dladm_name2info(link, linkid, NULL, &class, &media) ==
5960 	    DLADM_STATUS_OK) {
5961 		if (class == DATALINK_CLASS_PHYS && media == DL_ETHER)
5962 			return (B_TRUE);
5963 	}
5964 	return (B_FALSE);
5965 }
5966