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