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