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