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