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