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