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