xref: /titanic_50/usr/src/cmd/dladm/dladm.c (revision b323a6159ab002be737b0f5f6a56ade96637e5bd)
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 <dlfcn.h>
29 #include <locale.h>
30 #include <signal.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <string.h>
35 #include <stropts.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38 #include <kstat.h>
39 #include <strings.h>
40 #include <getopt.h>
41 #include <unistd.h>
42 #include <priv.h>
43 #include <limits.h>
44 #include <termios.h>
45 #include <pwd.h>
46 #include <auth_attr.h>
47 #include <auth_list.h>
48 #include <libintl.h>
49 #include <libdevinfo.h>
50 #include <libdlpi.h>
51 #include <libdladm.h>
52 #include <libdllink.h>
53 #include <libdlstat.h>
54 #include <libdlaggr.h>
55 #include <libdlwlan.h>
56 #include <libdlvlan.h>
57 #include <libdlvnic.h>
58 #include <libdlether.h>
59 #include <libdliptun.h>
60 #include <libdlsim.h>
61 #include <libdlbridge.h>
62 #include <libinetutil.h>
63 #include <bsm/adt.h>
64 #include <bsm/adt_event.h>
65 #include <libdlvnic.h>
66 #include <sys/types.h>
67 #include <sys/socket.h>
68 #include <sys/processor.h>
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
71 #include <net/if_types.h>
72 #include <stddef.h>
73 #include <stp_in.h>
74 #include <ofmt.h>
75 
76 #define	MAXPORT			256
77 #define	MAXVNIC			256
78 #define	BUFLEN(lim, ptr)	(((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
79 #define	MAXLINELEN		1024
80 #define	SMF_UPGRADE_FILE		"/var/svc/profile/upgrade"
81 #define	SMF_UPGRADEDATALINK_FILE	"/var/svc/profile/upgrade_datalink"
82 #define	SMF_DLADM_UPGRADE_MSG		" # added by dladm(1M)"
83 #define	DLADM_DEFAULT_COL	80
84 
85 /*
86  * used by the wifi show-* commands to set up ofmt_field_t structures.
87  */
88 #define	WIFI_CMD_SCAN		0x00000001
89 #define	WIFI_CMD_SHOW		0x00000002
90 #define	WIFI_CMD_ALL		(WIFI_CMD_SCAN | WIFI_CMD_SHOW)
91 
92 /* No larger than pktsum_t */
93 typedef struct brsum_s {
94 	uint64_t	drops;
95 	uint64_t	forward_dir;
96 	uint64_t	forward_mb;
97 	uint64_t	forward_unk;
98 	uint64_t	recv;
99 	uint64_t	sent;
100 } brsum_t;
101 
102 /* No larger than pktsum_t */
103 typedef struct brlsum_s {
104 	uint32_t	cfgbpdu;
105 	uint32_t	tcnbpdu;
106 	uint32_t	rstpbpdu;
107 	uint32_t	txbpdu;
108 	uint64_t	drops;
109 	uint64_t	recv;
110 	uint64_t	xmit;
111 } brlsum_t;
112 
113 typedef struct show_state {
114 	boolean_t	ls_firstonly;
115 	boolean_t	ls_donefirst;
116 	pktsum_t	ls_prevstats;
117 	uint32_t	ls_flags;
118 	dladm_status_t	ls_status;
119 	ofmt_handle_t	ls_ofmt;
120 	boolean_t	ls_parsable;
121 	boolean_t	ls_mac;
122 	boolean_t	ls_hwgrp;
123 } show_state_t;
124 
125 typedef struct show_grp_state {
126 	pktsum_t	gs_prevstats[MAXPORT];
127 	uint32_t	gs_flags;
128 	dladm_status_t	gs_status;
129 	boolean_t	gs_parsable;
130 	boolean_t	gs_lacp;
131 	boolean_t	gs_extended;
132 	boolean_t	gs_stats;
133 	boolean_t	gs_firstonly;
134 	boolean_t	gs_donefirst;
135 	ofmt_handle_t	gs_ofmt;
136 } show_grp_state_t;
137 
138 typedef struct show_vnic_state {
139 	datalink_id_t	vs_vnic_id;
140 	datalink_id_t	vs_link_id;
141 	char		vs_vnic[MAXLINKNAMELEN];
142 	char		vs_link[MAXLINKNAMELEN];
143 	boolean_t	vs_parsable;
144 	boolean_t	vs_found;
145 	boolean_t	vs_firstonly;
146 	boolean_t	vs_donefirst;
147 	boolean_t	vs_stats;
148 	boolean_t	vs_printstats;
149 	pktsum_t	vs_totalstats;
150 	pktsum_t	vs_prevstats[MAXVNIC];
151 	boolean_t	vs_etherstub;
152 	dladm_status_t	vs_status;
153 	uint32_t	vs_flags;
154 	ofmt_handle_t	vs_ofmt;
155 } show_vnic_state_t;
156 
157 typedef struct show_usage_state_s {
158 	boolean_t	us_plot;
159 	boolean_t	us_parsable;
160 	boolean_t	us_printheader;
161 	boolean_t	us_first;
162 	boolean_t	us_showall;
163 	ofmt_handle_t	us_ofmt;
164 } show_usage_state_t;
165 
166 /*
167  * callback functions for printing output and error diagnostics.
168  */
169 static ofmt_cb_t print_default_cb, print_link_stats_cb, print_linkprop_cb;
170 static ofmt_cb_t print_lacp_cb, print_phys_one_mac_cb;
171 static ofmt_cb_t print_xaggr_cb, print_aggr_stats_cb;
172 static ofmt_cb_t print_phys_one_hwgrp_cb, print_wlan_attr_cb;
173 static ofmt_cb_t print_wifi_status_cb, print_link_attr_cb;
174 static void dladm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
175 
176 typedef void cmdfunc_t(int, char **, const char *);
177 
178 static cmdfunc_t do_show_link, do_show_wifi, do_show_phys;
179 static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
180 static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr;
181 static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
182 static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
183 static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
184 static cmdfunc_t do_init_linkprop, do_init_secobj;
185 static cmdfunc_t do_create_vlan, do_delete_vlan, do_up_vlan, do_show_vlan;
186 static cmdfunc_t do_rename_link, do_delete_phys, do_init_phys;
187 static cmdfunc_t do_show_linkmap;
188 static cmdfunc_t do_show_ether;
189 static cmdfunc_t do_create_vnic, do_delete_vnic, do_show_vnic;
190 static cmdfunc_t do_up_vnic;
191 static cmdfunc_t do_create_etherstub, do_delete_etherstub, do_show_etherstub;
192 static cmdfunc_t do_create_simnet, do_modify_simnet;
193 static cmdfunc_t do_delete_simnet, do_show_simnet, do_up_simnet;
194 static cmdfunc_t do_show_usage;
195 static cmdfunc_t do_create_bridge, do_modify_bridge, do_delete_bridge;
196 static cmdfunc_t do_add_bridge, do_remove_bridge, do_show_bridge;
197 static cmdfunc_t do_create_iptun, do_modify_iptun, do_delete_iptun;
198 static cmdfunc_t do_show_iptun, do_up_iptun, do_down_iptun;
199 
200 static void 	do_up_vnic_common(int, char **, const char *, boolean_t);
201 
202 static void	altroot_cmd(char *, int, char **);
203 static int	show_linkprop_onelink(dladm_handle_t, datalink_id_t, void *);
204 
205 static void	link_stats(datalink_id_t, uint_t, char *, show_state_t *);
206 static void	aggr_stats(datalink_id_t, show_grp_state_t *, uint_t);
207 static void	vnic_stats(show_vnic_state_t *, uint32_t);
208 
209 static int	get_one_kstat(const char *, const char *, uint8_t,
210 		    void *, boolean_t);
211 static void	get_mac_stats(const char *, pktsum_t *);
212 static void	get_link_stats(const char *, pktsum_t *);
213 static uint64_t	get_ifspeed(const char *, boolean_t);
214 static const char	*get_linkstate(const char *, boolean_t, char *);
215 static const char	*get_linkduplex(const char *, boolean_t, char *);
216 
217 static iptun_type_t	iptun_gettypebyname(char *);
218 static const char	*iptun_gettypebyvalue(iptun_type_t);
219 static dladm_status_t	print_iptun(dladm_handle_t, datalink_id_t,
220 			    show_state_t *);
221 static int	print_iptun_walker(dladm_handle_t, datalink_id_t, void *);
222 
223 static int	show_etherprop(dladm_handle_t, datalink_id_t, void *);
224 static void	show_ether_xprop(void *, dladm_ether_info_t *);
225 static boolean_t	link_is_ether(const char *, datalink_id_t *);
226 
227 static boolean_t str2int(const char *, int *);
228 static void	die(const char *, ...);
229 static void	die_optdup(int);
230 static void	die_opterr(int, int, const char *);
231 static void	die_dlerr(dladm_status_t, const char *, ...);
232 static void	warn(const char *, ...);
233 static void	warn_dlerr(dladm_status_t, const char *, ...);
234 
235 typedef struct	cmd {
236 	char		*c_name;
237 	cmdfunc_t	*c_fn;
238 	const char	*c_usage;
239 } cmd_t;
240 
241 static cmd_t	cmds[] = {
242 	{ "rename-link",	do_rename_link,
243 	    "    rename-link      <oldlink> <newlink>"			},
244 	{ "show-link",		do_show_link,
245 	    "    show-link        [-pP] [-o <field>,..] [-s [-i <interval>]] "
246 	    "[<link>]\n"						},
247 	{ "create-aggr",	do_create_aggr,
248 	    "    create-aggr      [-t] [-P <policy>] [-L <mode>] [-T <time>] "
249 	    "[-u <address>]\n"
250 	    "\t\t     -l <link> [-l <link>...] <link>"			},
251 	{ "delete-aggr",	do_delete_aggr,
252 	    "    delete-aggr      [-t] <link>"				},
253 	{ "add-aggr",		do_add_aggr,
254 	    "    add-aggr         [-t] -l <link> [-l <link>...] <link>" },
255 	{ "remove-aggr",	do_remove_aggr,
256 	    "    remove-aggr      [-t] -l <link> [-l <link>...] <link>" },
257 	{ "modify-aggr",	do_modify_aggr,
258 	    "    modify-aggr      [-t] [-P <policy>] [-L <mode>] [-T <time>] "
259 	    "[-u <address>]\n"
260 	    "\t\t     <link>"						},
261 	{ "show-aggr",		do_show_aggr,
262 	    "    show-aggr        [-pPLx] [-o <field>,..] [-s [-i <interval>]] "
263 	    "[<link>]\n"						},
264 	{ "up-aggr",		do_up_aggr,	NULL			},
265 	{ "scan-wifi",		do_scan_wifi,
266 	    "    scan-wifi        [-p] [-o <field>,...] [<link>]"	},
267 	{ "connect-wifi",	do_connect_wifi,
268 	    "    connect-wifi     [-e <essid>] [-i <bssid>] [-k <key>,...] "
269 	    "[-s wep|wpa]\n"
270 	    "\t\t     [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g] "
271 	    "[-T <time>]\n"
272 	    "\t\t     [<link>]"						},
273 	{ "disconnect-wifi",	do_disconnect_wifi,
274 	    "    disconnect-wifi  [-a] [<link>]"			},
275 	{ "show-wifi",		do_show_wifi,
276 	    "    show-wifi        [-p] [-o <field>,...] [<link>]\n"	},
277 	{ "set-linkprop",	do_set_linkprop,
278 	    "    set-linkprop     [-t] -p <prop>=<value>[,...] <name>"	},
279 	{ "reset-linkprop",	do_reset_linkprop,
280 	    "    reset-linkprop   [-t] [-p <prop>,...] <name>"		},
281 	{ "show-linkprop",	do_show_linkprop,
282 	    "    show-linkprop    [-cP] [-o <field>,...] [-p <prop>,...] "
283 	    "<name>\n"							},
284 	{ "show-ether",		do_show_ether,
285 	    "    show-ether       [-px][-o <field>,...] <link>\n"	},
286 	{ "create-secobj",	do_create_secobj,
287 	    "    create-secobj    [-t] [-f <file>] -c <class> <secobj>"	},
288 	{ "delete-secobj",	do_delete_secobj,
289 	    "    delete-secobj    [-t] <secobj>[,...]"			},
290 	{ "show-secobj",	do_show_secobj,
291 	    "    show-secobj      [-pP] [-o <field>,...] [<secobj>,...]\n" },
292 	{ "init-linkprop",	do_init_linkprop,	NULL		},
293 	{ "init-secobj",	do_init_secobj,		NULL		},
294 	{ "create-vlan", 	do_create_vlan,
295 	    "    create-vlan      [-ft] -l <link> -v <vid> [link]"	},
296 	{ "delete-vlan", 	do_delete_vlan,
297 	    "    delete-vlan      [-t] <link>"				},
298 	{ "show-vlan",		do_show_vlan,
299 	    "    show-vlan        [-pP] [-o <field>,..] [<link>]\n"	},
300 	{ "up-vlan",		do_up_vlan,		NULL		},
301 	{ "create-iptun",	do_create_iptun,
302 	    "    create-iptun     [-t] -T <type> "
303 	    "[-a {local|remote}=<addr>,...] <link>]" },
304 	{ "delete-iptun",	do_delete_iptun,
305 	    "    delete-iptun     [-t] <link>"				},
306 	{ "modify-iptun",	do_modify_iptun,
307 	    "    modify-iptun     [-t] -a {local|remote}=<addr>,... <link>" },
308 	{ "show-iptun",		do_show_iptun,
309 	    "    show-iptun       [-pP] [-o <field>,..] [<link>]\n"	},
310 	{ "up-iptun",		do_up_iptun,		NULL		},
311 	{ "down-iptun",		do_down_iptun,		NULL		},
312 	{ "delete-phys",	do_delete_phys,
313 	    "    delete-phys      <link>"				},
314 	{ "show-phys",		do_show_phys,
315 	    "    show-phys        [-pP] [-o <field>,..] [-H] [<link>]\n"},
316 	{ "init-phys",		do_init_phys,		NULL		},
317 	{ "show-linkmap",	do_show_linkmap,	NULL		},
318 	{ "create-vnic",	do_create_vnic,
319 	    "    create-vnic      [-t] -l <link> [-m <value> | auto |\n"
320 	    "\t\t     {factory [-n <slot-id>]} | {random [-r <prefix>]}]\n"
321 	    "\t\t     [-v <vid> [-f]] [-p <prop>=<value>[,...]] [-H] "
322 	    "<vnic-link>"						},
323 	{ "delete-vnic",	do_delete_vnic,
324 	    "    delete-vnic      [-t] <vnic-link>"			},
325 	{ "show-vnic",		do_show_vnic,
326 	    "    show-vnic        [-pP] [-l <link>] [-s [-i <interval>]] "
327 	    "[<link>]\n"						},
328 	{ "up-vnic",		do_up_vnic,		NULL		},
329 	{ "create-etherstub",	do_create_etherstub,
330 	    "    create-etherstub [-t] <link>"				},
331 	{ "delete-etherstub",	do_delete_etherstub,
332 	    "    delete-etherstub [-t] <link>"				},
333 	{ "show-etherstub",	do_show_etherstub,
334 	    "    show-etherstub   [-t] [<link>]\n"			},
335 	{ "create-simnet",	do_create_simnet,	NULL		},
336 	{ "modify-simnet",	do_modify_simnet,	NULL		},
337 	{ "delete-simnet",	do_delete_simnet,	NULL		},
338 	{ "show-simnet",	do_show_simnet,		NULL		},
339 	{ "up-simnet",		do_up_simnet,		NULL		},
340 	{ "create-bridge",	do_create_bridge,
341 	    "    create-bridge    [-R <root-dir>] [-P <protect>] "
342 	    "[-p <priority>]\n"
343 	    "\t\t     [-m <max-age>] [-h <hello-time>] [-d <forward-delay>]\n"
344 	    "\t\t     [-f <force-protocol>] [-l <link>]... <bridge>"	},
345 	{ "modify-bridge",	do_modify_bridge,
346 	    "    modify-bridge    [-R <root-dir>] [-P <protect>] "
347 	    "[-p <priority>]\n"
348 	    "\t\t     [-m <max-age>] [-h <hello-time>] [-d <forward-delay>]\n"
349 	    "\t\t     [-f <force-protocol>] <bridge>"			},
350 	{ "delete-bridge",	do_delete_bridge,
351 	    "    delete-bridge    [-R <root-dir>] <bridge>"		},
352 	{ "add-bridge",		do_add_bridge,
353 	    "    add-bridge       [-R <root-dir>] -l <link> [-l <link>]... "
354 	    "<bridge>"							},
355 	{ "remove-bridge",	do_remove_bridge,
356 	    "    remove-bridge    [-R <root-dir>] -l <link> [-l <link>]... "
357 	    "<bridge>"							},
358 	{ "show-bridge",	do_show_bridge,
359 	    "    show-bridge      [-p] [-o <field>,...] [-s [-i <interval>]] "
360 	    "[<bridge>]\n"
361 	    "    show-bridge      -l [-p] [-o <field>,...] [-s [-i <interval>]]"
362 	    " <bridge>\n"
363 	    "    show-bridge      -f [-p] [-o <field>,...] [-s [-i <interval>]]"
364 	    " <bridge>\n"
365 	    "    show-bridge      -t [-p] [-o <field>,...] [-s [-i <interval>]]"
366 	    " <bridge>\n"						},
367 	{ "show-usage",		do_show_usage,
368 	    "    show-usage       [-a] [-d | -F <format>] "
369 	    "[-s <DD/MM/YYYY,HH:MM:SS>]\n"
370 	    "\t\t     [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<link>]"	}
371 };
372 
373 static const struct option lopts[] = {
374 	{"vlan-id",	required_argument,	0, 'v'},
375 	{"output",	required_argument,	0, 'o'},
376 	{"dev",		required_argument,	0, 'd'},
377 	{"policy",	required_argument,	0, 'P'},
378 	{"lacp-mode",	required_argument,	0, 'L'},
379 	{"lacp-timer",	required_argument,	0, 'T'},
380 	{"unicast",	required_argument,	0, 'u'},
381 	{"temporary",	no_argument,		0, 't'},
382 	{"root-dir",	required_argument,	0, 'R'},
383 	{"link",	required_argument,	0, 'l'},
384 	{"forcible",	no_argument,		0, 'f'},
385 	{"bw-limit",	required_argument,	0, 'b'},
386 	{"mac-address",	required_argument,	0, 'm'},
387 	{"slot",	required_argument,	0, 'n'},
388 	{ 0, 0, 0, 0 }
389 };
390 
391 static const struct option show_lopts[] = {
392 	{"statistics",	no_argument,		0, 's'},
393 	{"continuous",	no_argument,		0, 'S'},
394 	{"interval",	required_argument,	0, 'i'},
395 	{"parsable",	no_argument,		0, 'p'},
396 	{"parseable",	no_argument,		0, 'p'},
397 	{"extended",	no_argument,		0, 'x'},
398 	{"output",	required_argument,	0, 'o'},
399 	{"persistent",	no_argument,		0, 'P'},
400 	{"lacp",	no_argument,		0, 'L'},
401 	{ 0, 0, 0, 0 }
402 };
403 
404 static const struct option iptun_lopts[] = {
405 	{"output",	required_argument,	0, 'o'},
406 	{"tunnel-type",	required_argument,	0, 'T'},
407 	{"address",	required_argument,	0, 'a'},
408 	{"root-dir",	required_argument,	0, 'R'},
409 	{"parsable",	no_argument,		0, 'p'},
410 	{"parseable",	no_argument,		0, 'p'},
411 	{"persistent",	no_argument,		0, 'P'},
412 	{ 0, 0, 0, 0 }
413 };
414 
415 static char * const iptun_addropts[] = {
416 #define	IPTUN_LOCAL	0
417 	"local",
418 #define	IPTUN_REMOTE	1
419 	"remote",
420 	NULL};
421 
422 static const struct {
423 	const char	*type_name;
424 	iptun_type_t	type_value;
425 } iptun_types[] = {
426 	{"ipv4",	IPTUN_TYPE_IPV4},
427 	{"ipv6",	IPTUN_TYPE_IPV6},
428 	{"6to4",	IPTUN_TYPE_6TO4},
429 	{NULL,		0}
430 };
431 
432 static const struct option prop_longopts[] = {
433 	{"temporary",	no_argument,		0, 't'  },
434 	{"output",	required_argument,	0, 'o'  },
435 	{"root-dir",	required_argument,	0, 'R'  },
436 	{"prop",	required_argument,	0, 'p'  },
437 	{"parsable",	no_argument,		0, 'c'  },
438 	{"parseable",	no_argument,		0, 'c'  },
439 	{"persistent",	no_argument,		0, 'P'  },
440 	{ 0, 0, 0, 0 }
441 };
442 
443 static const struct option wifi_longopts[] = {
444 	{"parsable",	no_argument,		0, 'p'  },
445 	{"parseable",	no_argument,		0, 'p'  },
446 	{"output",	required_argument,	0, 'o'  },
447 	{"essid",	required_argument,	0, 'e'  },
448 	{"bsstype",	required_argument,	0, 'b'  },
449 	{"mode",	required_argument,	0, 'm'  },
450 	{"key",		required_argument,	0, 'k'  },
451 	{"sec",		required_argument,	0, 's'  },
452 	{"auth",	required_argument,	0, 'a'  },
453 	{"create-ibss",	required_argument,	0, 'c'  },
454 	{"timeout",	required_argument,	0, 'T'  },
455 	{"all-links",	no_argument,		0, 'a'  },
456 	{"temporary",	no_argument,		0, 't'  },
457 	{"root-dir",	required_argument,	0, 'R'  },
458 	{"persistent",	no_argument,		0, 'P'  },
459 	{"file",	required_argument,	0, 'f'  },
460 	{ 0, 0, 0, 0 }
461 };
462 
463 static const struct option showeth_lopts[] = {
464 	{"parsable",	no_argument,		0, 'p'	},
465 	{"parseable",	no_argument,		0, 'p'	},
466 	{"extended",	no_argument,		0, 'x'	},
467 	{"output",	required_argument,	0, 'o'	},
468 	{ 0, 0, 0, 0 }
469 };
470 
471 static const struct option vnic_lopts[] = {
472 	{"temporary",	no_argument,		0, 't'	},
473 	{"root-dir",	required_argument,	0, 'R'	},
474 	{"dev",		required_argument,	0, 'd'	},
475 	{"mac-address",	required_argument,	0, 'm'	},
476 	{"cpus",	required_argument,	0, 'c'	},
477 	{"bw-limit",	required_argument,	0, 'b'	},
478 	{"slot",	required_argument,	0, 'n'	},
479 	{"mac-prefix",	required_argument,	0, 'r'	},
480 	{ 0, 0, 0, 0 }
481 };
482 
483 static const struct option etherstub_lopts[] = {
484 	{"temporary",	no_argument,		0, 't'	},
485 	{"root-dir",	required_argument,	0, 'R'	},
486 	{ 0, 0, 0, 0 }
487 };
488 
489 static const struct option usage_opts[] = {
490 	{"file",	required_argument,	0, 'f'	},
491 	{"format",	required_argument,	0, 'F'	},
492 	{"start",	required_argument,	0, 's'	},
493 	{"stop",	required_argument,	0, 'e'	},
494 	{ 0, 0, 0, 0 }
495 };
496 
497 static const struct option simnet_lopts[] = {
498 	{"temporary",	no_argument,		0, 't'	},
499 	{"root-dir",	required_argument,	0, 'R'	},
500 	{"media",	required_argument,	0, 'm'	},
501 	{"peer",	required_argument,	0, 'p'	},
502 	{ 0, 0, 0, 0 }
503 };
504 
505 static const struct option bridge_lopts[] = {
506 	{ "protect",		required_argument,	0, 'P' },
507 	{ "root-dir",		required_argument,	0, 'R'	},
508 	{ "forward-delay",	required_argument,	0, 'd'	},
509 	{ "force-protocol",	required_argument,	0, 'f'	},
510 	{ "hello-time",		required_argument,	0, 'h'	},
511 	{ "link",		required_argument,	0, 'l'	},
512 	{ "max-age",		required_argument,	0, 'm'	},
513 	{ "priority",		required_argument,	0, 'p'	},
514 	{ NULL, NULL, 0, 0 }
515 };
516 
517 static const struct option bridge_show_lopts[] = {
518 	{ "forwarding", no_argument,		0, 'f' },
519 	{ "interval",	required_argument,	0, 'i' },
520 	{ "link",	no_argument,		0, 'l' },
521 	{ "output",	required_argument,	0, 'o' },
522 	{ "parsable",	no_argument,		0, 'p' },
523 	{ "parseable",	no_argument,		0, 'p' },
524 	{ "statistics",	no_argument,		0, 's' },
525 	{ "trill",	no_argument,		0, 't' },
526 	{ 0, 0, 0, 0 }
527 };
528 
529 /*
530  * structures for 'dladm show-ether'
531  */
532 static const char *ptype[] = {LEI_ATTR_NAMES};
533 
534 typedef struct ether_fields_buf_s
535 {
536 	char	eth_link[15];
537 	char	eth_ptype[8];
538 	char	eth_state[8];
539 	char	eth_autoneg[5];
540 	char	eth_spdx[31];
541 	char	eth_pause[6];
542 	char	eth_rem_fault[16];
543 } ether_fields_buf_t;
544 
545 static const ofmt_field_t ether_fields[] = {
546 /* name,	field width,	offset	    callback */
547 { "LINK",	16,
548 	offsetof(ether_fields_buf_t, eth_link), print_default_cb},
549 { "PTYPE",	9,
550 	offsetof(ether_fields_buf_t, eth_ptype), print_default_cb},
551 { "STATE",	9,
552 	offsetof(ether_fields_buf_t, eth_state),
553 	print_default_cb},
554 { "AUTO",	6,
555 	offsetof(ether_fields_buf_t, eth_autoneg), print_default_cb},
556 { "SPEED-DUPLEX", 32,
557 	offsetof(ether_fields_buf_t, eth_spdx), print_default_cb},
558 { "PAUSE",	7,
559 	offsetof(ether_fields_buf_t, eth_pause), print_default_cb},
560 { "REM_FAULT",	17,
561 	offsetof(ether_fields_buf_t, eth_rem_fault), print_default_cb},
562 {NULL,		0,
563 	0, 	NULL}}
564 ;
565 
566 typedef struct print_ether_state {
567 	const char	*es_link;
568 	boolean_t	es_parsable;
569 	boolean_t	es_header;
570 	boolean_t	es_extended;
571 	ofmt_handle_t	es_ofmt;
572 } print_ether_state_t;
573 
574 /*
575  * structures for 'dladm show-link -s' (print statistics)
576  */
577 typedef enum {
578 	LINK_S_LINK,
579 	LINK_S_IPKTS,
580 	LINK_S_RBYTES,
581 	LINK_S_IERRORS,
582 	LINK_S_OPKTS,
583 	LINK_S_OBYTES,
584 	LINK_S_OERRORS
585 } link_s_field_index_t;
586 
587 static const ofmt_field_t link_s_fields[] = {
588 /* name,	field width,	index,		callback	*/
589 { "LINK",	15,		LINK_S_LINK,	print_link_stats_cb},
590 { "IPACKETS",	10,		LINK_S_IPKTS,	print_link_stats_cb},
591 { "RBYTES",	8,		LINK_S_RBYTES,	print_link_stats_cb},
592 { "IERRORS",	10,		LINK_S_IERRORS,	print_link_stats_cb},
593 { "OPACKETS",	12,		LINK_S_OPKTS,	print_link_stats_cb},
594 { "OBYTES",	12,		LINK_S_OBYTES,	print_link_stats_cb},
595 { "OERRORS",	8,		LINK_S_OERRORS,	print_link_stats_cb}}
596 ;
597 
598 typedef struct link_args_s {
599 	char		*link_s_link;
600 	pktsum_t	*link_s_psum;
601 } link_args_t;
602 
603 /*
604  * buffer used by print functions for show-{link,phys,vlan} commands.
605  */
606 typedef struct link_fields_buf_s {
607 	char link_name[MAXLINKNAMELEN];
608 	char link_class[DLADM_STRSIZE];
609 	char link_mtu[11];
610 	char link_state[DLADM_STRSIZE];
611 	char link_bridge[MAXLINKNAMELEN];
612 	char link_over[MAXLINKNAMELEN];
613 	char link_phys_state[DLADM_STRSIZE];
614 	char link_phys_media[DLADM_STRSIZE];
615 	char link_phys_speed[DLADM_STRSIZE];
616 	char link_phys_duplex[DLPI_LINKNAME_MAX];
617 	char link_phys_device[DLPI_LINKNAME_MAX];
618 	char link_flags[6];
619 	char link_vlan_vid[6];
620 } link_fields_buf_t;
621 
622 /*
623  * structures for 'dladm show-link'
624  */
625 static const ofmt_field_t link_fields[] = {
626 /* name,	field width,	index,	callback */
627 { "LINK",	12,
628 	offsetof(link_fields_buf_t, link_name), print_default_cb},
629 { "CLASS",	10,
630 	offsetof(link_fields_buf_t, link_class), print_default_cb},
631 { "MTU",	7,
632 	offsetof(link_fields_buf_t, link_mtu), print_default_cb},
633 { "STATE",	9,
634 	offsetof(link_fields_buf_t, link_state), print_default_cb},
635 { "BRIDGE",	11,
636     offsetof(link_fields_buf_t, link_bridge), print_default_cb},
637 { "OVER",	DLPI_LINKNAME_MAX,
638 	offsetof(link_fields_buf_t, link_over), print_default_cb},
639 { NULL,		0, 0, NULL}}
640 ;
641 
642 /*
643  * structures for 'dladm show-aggr'
644  */
645 typedef struct laggr_fields_buf_s {
646 	char laggr_name[DLPI_LINKNAME_MAX];
647 	char laggr_policy[9];
648 	char laggr_addrpolicy[ETHERADDRL * 3 + 3];
649 	char laggr_lacpactivity[14];
650 	char laggr_lacptimer[DLADM_STRSIZE];
651 	char laggr_flags[7];
652 } laggr_fields_buf_t;
653 
654 typedef struct laggr_args_s {
655 	int			laggr_lport; /* -1 indicates the aggr itself */
656 	const char 		*laggr_link;
657 	dladm_aggr_grp_attr_t	*laggr_ginfop;
658 	dladm_status_t		*laggr_status;
659 	pktsum_t		*laggr_pktsumtot; /* -s only */
660 	pktsum_t		*laggr_diffstats; /* -s only */
661 	boolean_t		laggr_parsable;
662 } laggr_args_t;
663 
664 static const ofmt_field_t laggr_fields[] = {
665 /* name,	field width,	offset,	callback */
666 { "LINK",	16,
667 	offsetof(laggr_fields_buf_t, laggr_name), print_default_cb},
668 { "POLICY",	9,
669 	offsetof(laggr_fields_buf_t, laggr_policy), print_default_cb},
670 { "ADDRPOLICY",	ETHERADDRL * 3 + 3,
671 	offsetof(laggr_fields_buf_t, laggr_addrpolicy), print_default_cb},
672 { "LACPACTIVITY", 14,
673 	offsetof(laggr_fields_buf_t, laggr_lacpactivity), print_default_cb},
674 { "LACPTIMER",	12,
675 	offsetof(laggr_fields_buf_t, laggr_lacptimer), print_default_cb},
676 { "FLAGS",	8,
677 	offsetof(laggr_fields_buf_t, laggr_flags), print_default_cb},
678 { NULL,		0, 0, NULL}}
679 ;
680 
681 /*
682  * structures for 'dladm show-aggr -x'.
683  */
684 typedef enum {
685 	AGGR_X_LINK,
686 	AGGR_X_PORT,
687 	AGGR_X_SPEED,
688 	AGGR_X_DUPLEX,
689 	AGGR_X_STATE,
690 	AGGR_X_ADDRESS,
691 	AGGR_X_PORTSTATE
692 } aggr_x_field_index_t;
693 
694 static const ofmt_field_t aggr_x_fields[] = {
695 /* name,	field width,	index		callback */
696 { "LINK",	12,	AGGR_X_LINK,		print_xaggr_cb},
697 { "PORT",	15,	AGGR_X_PORT,		print_xaggr_cb},
698 { "SPEED",	5,	AGGR_X_SPEED,		print_xaggr_cb},
699 { "DUPLEX",	10,	AGGR_X_DUPLEX,		print_xaggr_cb},
700 { "STATE",	10,	AGGR_X_STATE,		print_xaggr_cb},
701 { "ADDRESS",	19,	AGGR_X_ADDRESS,		print_xaggr_cb},
702 { "PORTSTATE",	16,	AGGR_X_PORTSTATE,	print_xaggr_cb},
703 { NULL,		0,	0,			NULL}}
704 ;
705 
706 /*
707  * structures for 'dladm show-aggr -s'.
708  */
709 typedef enum {
710 	AGGR_S_LINK,
711 	AGGR_S_PORT,
712 	AGGR_S_IPKTS,
713 	AGGR_S_RBYTES,
714 	AGGR_S_OPKTS,
715 	AGGR_S_OBYTES,
716 	AGGR_S_IPKTDIST,
717 	AGGR_S_OPKTDIST
718 } aggr_s_field_index_t;
719 
720 static const ofmt_field_t aggr_s_fields[] = {
721 { "LINK",		12,	AGGR_S_LINK, print_aggr_stats_cb},
722 { "PORT",		10,	AGGR_S_PORT, print_aggr_stats_cb},
723 { "IPACKETS",		8,	AGGR_S_IPKTS, print_aggr_stats_cb},
724 { "RBYTES",		8,	AGGR_S_RBYTES, print_aggr_stats_cb},
725 { "OPACKETS",		8,	AGGR_S_OPKTS, print_aggr_stats_cb},
726 { "OBYTES",		8,	AGGR_S_OBYTES, print_aggr_stats_cb},
727 { "IPKTDIST",		9,	AGGR_S_IPKTDIST, print_aggr_stats_cb},
728 { "OPKTDIST",		15,	AGGR_S_OPKTDIST, print_aggr_stats_cb},
729 { NULL,			0,	0,		NULL}}
730 ;
731 
732 /*
733  * structures for 'dladm show-aggr -L'.
734  */
735 typedef enum {
736 	AGGR_L_LINK,
737 	AGGR_L_PORT,
738 	AGGR_L_AGGREGATABLE,
739 	AGGR_L_SYNC,
740 	AGGR_L_COLL,
741 	AGGR_L_DIST,
742 	AGGR_L_DEFAULTED,
743 	AGGR_L_EXPIRED
744 } aggr_l_field_index_t;
745 
746 static const ofmt_field_t aggr_l_fields[] = {
747 /* name,		field width,	index */
748 { "LINK",		12,	AGGR_L_LINK,		print_lacp_cb},
749 { "PORT",		13,	AGGR_L_PORT,		print_lacp_cb},
750 { "AGGREGATABLE",	13,	AGGR_L_AGGREGATABLE,	print_lacp_cb},
751 { "SYNC",		5,	AGGR_L_SYNC,		print_lacp_cb},
752 { "COLL",		5,	AGGR_L_COLL,		print_lacp_cb},
753 { "DIST",		5,	AGGR_L_DIST,		print_lacp_cb},
754 { "DEFAULTED",		10,	AGGR_L_DEFAULTED,	print_lacp_cb},
755 { "EXPIRED",		15,	AGGR_L_EXPIRED,		print_lacp_cb},
756 { NULL,			0,	0,			NULL}}
757 ;
758 
759 /*
760  * structures for 'dladm show-phys'
761  */
762 
763 static const ofmt_field_t phys_fields[] = {
764 /* name,	field width,	offset */
765 { "LINK",	13,
766 	offsetof(link_fields_buf_t, link_name), print_default_cb},
767 { "MEDIA",	21,
768 	offsetof(link_fields_buf_t, link_phys_media), print_default_cb},
769 { "STATE",	11,
770 	offsetof(link_fields_buf_t, link_phys_state), print_default_cb},
771 { "SPEED",	7,
772 	offsetof(link_fields_buf_t, link_phys_speed), print_default_cb},
773 { "DUPLEX",	10,
774 	offsetof(link_fields_buf_t, link_phys_duplex), print_default_cb},
775 { "DEVICE",	13,
776 	offsetof(link_fields_buf_t, link_phys_device), print_default_cb},
777 { "FLAGS",	7,
778 	offsetof(link_fields_buf_t, link_flags), print_default_cb},
779 { NULL,		0, NULL, 0}}
780 ;
781 
782 /*
783  * structures for 'dladm show-phys -m'
784  */
785 
786 typedef enum {
787 	PHYS_M_LINK,
788 	PHYS_M_SLOT,
789 	PHYS_M_ADDRESS,
790 	PHYS_M_INUSE,
791 	PHYS_M_CLIENT
792 } phys_m_field_index_t;
793 
794 static const ofmt_field_t phys_m_fields[] = {
795 /* name,	field width,	offset */
796 { "LINK",	13,	PHYS_M_LINK,	print_phys_one_mac_cb},
797 { "SLOT",	9,	PHYS_M_SLOT,	print_phys_one_mac_cb},
798 { "ADDRESS",	19,	PHYS_M_ADDRESS,	print_phys_one_mac_cb},
799 { "INUSE",	5,	PHYS_M_INUSE,	print_phys_one_mac_cb},
800 { "CLIENT",	13,	PHYS_M_CLIENT,	print_phys_one_mac_cb},
801 { NULL,		0,	0,		NULL}}
802 ;
803 
804 /*
805  * structures for 'dladm show-phys -H'
806  */
807 
808 typedef enum {
809 	PHYS_H_LINK,
810 	PHYS_H_GROUP,
811 	PHYS_H_GRPTYPE,
812 	PHYS_H_RINGS,
813 	PHYS_H_CLIENTS
814 } phys_h_field_index_t;
815 
816 static const ofmt_field_t phys_h_fields[] = {
817 { "LINK",	13,	PHYS_H_LINK,	print_phys_one_hwgrp_cb},
818 { "GROUP",	9,	PHYS_H_GROUP,	print_phys_one_hwgrp_cb},
819 { "GROUPTYPE",	7,	PHYS_H_GRPTYPE,	print_phys_one_hwgrp_cb},
820 { "RINGS",	17,	PHYS_H_RINGS,	print_phys_one_hwgrp_cb},
821 { "CLIENTS",	21,	PHYS_H_CLIENTS,	print_phys_one_hwgrp_cb},
822 { NULL,		0,	0,		NULL}}
823 ;
824 
825 /*
826  * structures for 'dladm show-vlan'
827  */
828 static const ofmt_field_t vlan_fields[] = {
829 { "LINK",	16,
830 	offsetof(link_fields_buf_t, link_name), print_default_cb},
831 { "VID",	9,
832 	offsetof(link_fields_buf_t, link_vlan_vid), print_default_cb},
833 { "OVER",	13,
834 	offsetof(link_fields_buf_t, link_over), print_default_cb},
835 { "FLAGS",	7,
836 	offsetof(link_fields_buf_t, link_flags), print_default_cb},
837 { NULL,		0, 0, NULL}}
838 ;
839 
840 /*
841  * structures common to 'dladm scan-wifi' and 'dladm show-wifi'
842  * callback will be determined in parse_wifi_fields.
843  */
844 static ofmt_field_t wifi_common_fields[] = {
845 { "LINK",	11, 0,				NULL},
846 { "ESSID",	20, DLADM_WLAN_ATTR_ESSID,	NULL},
847 { "BSSID",	18, DLADM_WLAN_ATTR_BSSID,	NULL},
848 { "IBSSID",	18, DLADM_WLAN_ATTR_BSSID,	NULL},
849 { "MODE",	7,  DLADM_WLAN_ATTR_MODE,	NULL},
850 { "SPEED",	7,  DLADM_WLAN_ATTR_SPEED,	NULL},
851 { "BSSTYPE",	9,  DLADM_WLAN_ATTR_BSSTYPE,	NULL},
852 { "SEC",	7,  DLADM_WLAN_ATTR_SECMODE,	NULL},
853 { "STRENGTH",	11, DLADM_WLAN_ATTR_STRENGTH,	NULL},
854 { NULL,		0,  0,				NULL}};
855 
856 /*
857  * the 'show-wifi' command supports all the fields in wifi_common_fields
858  * plus the AUTH and STATUS fields.
859  */
860 static ofmt_field_t wifi_show_fields[A_CNT(wifi_common_fields) + 2] = {
861 { "AUTH",	9,  DLADM_WLAN_ATTR_AUTH,	NULL},
862 { "STATUS",	18, DLADM_WLAN_LINKATTR_STATUS,	print_wifi_status_cb},
863 /* copy wifi_common_fields here */
864 };
865 
866 static char *all_scan_wifi_fields =
867 	"link,essid,bssid,sec,strength,mode,speed,bsstype";
868 static char *all_show_wifi_fields =
869 	"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
870 static char *def_scan_wifi_fields =
871 	"link,essid,bssid,sec,strength,mode,speed";
872 static char *def_show_wifi_fields =
873 	"link,status,essid,sec,strength,mode,speed";
874 
875 /*
876  * structures for 'dladm show-linkprop'
877  */
878 typedef enum {
879 	LINKPROP_LINK,
880 	LINKPROP_PROPERTY,
881 	LINKPROP_PERM,
882 	LINKPROP_VALUE,
883 	LINKPROP_DEFAULT,
884 	LINKPROP_POSSIBLE
885 } linkprop_field_index_t;
886 
887 static const ofmt_field_t linkprop_fields[] = {
888 /* name,	field width,  index */
889 { "LINK",	13,	LINKPROP_LINK,		print_linkprop_cb},
890 { "PROPERTY",	16,	LINKPROP_PROPERTY,	print_linkprop_cb},
891 { "PERM",	5,	LINKPROP_PERM,		print_linkprop_cb},
892 { "VALUE",	15,	LINKPROP_VALUE,		print_linkprop_cb},
893 { "DEFAULT",	15,	LINKPROP_DEFAULT,	print_linkprop_cb},
894 { "POSSIBLE",	20,	LINKPROP_POSSIBLE,	print_linkprop_cb},
895 { NULL,		0,	0,			NULL}}
896 ;
897 
898 #define	MAX_PROP_LINE		512
899 
900 typedef struct show_linkprop_state {
901 	char			ls_link[MAXLINKNAMELEN];
902 	char			*ls_line;
903 	char			**ls_propvals;
904 	dladm_arg_list_t	*ls_proplist;
905 	boolean_t		ls_parsable;
906 	boolean_t		ls_persist;
907 	boolean_t		ls_header;
908 	dladm_status_t		ls_status;
909 	dladm_status_t		ls_retstatus;
910 	ofmt_handle_t		ls_ofmt;
911 } show_linkprop_state_t;
912 
913 typedef struct set_linkprop_state {
914 	const char		*ls_name;
915 	boolean_t		ls_reset;
916 	boolean_t		ls_temp;
917 	dladm_status_t		ls_status;
918 } set_linkprop_state_t;
919 
920 typedef struct linkprop_args_s {
921 	show_linkprop_state_t	*ls_state;
922 	char			*ls_propname;
923 	datalink_id_t		ls_linkid;
924 } linkprop_args_t;
925 
926 /*
927  * structures for 'dladm show-secobj'
928  */
929 typedef struct secobj_fields_buf_s {
930 	char			ss_obj_name[DLADM_SECOBJ_VAL_MAX];
931 	char			ss_class[20];
932 	char			ss_val[30];
933 } secobj_fields_buf_t;
934 
935 static const ofmt_field_t secobj_fields[] = {
936 { "OBJECT",	21,
937 	offsetof(secobj_fields_buf_t, ss_obj_name), print_default_cb},
938 { "CLASS",	21,
939 	offsetof(secobj_fields_buf_t, ss_class), print_default_cb},
940 { "VALUE",	31,
941 	offsetof(secobj_fields_buf_t, ss_val), print_default_cb},
942 { NULL,		0, 0, NULL}}
943 ;
944 
945 /*
946  * structures for 'dladm show-vnic'
947  */
948 typedef struct vnic_fields_buf_s
949 {
950 	char vnic_link[DLPI_LINKNAME_MAX];
951 	char vnic_over[DLPI_LINKNAME_MAX];
952 	char vnic_speed[6];
953 	char vnic_macaddr[18];
954 	char vnic_macaddrtype[19];
955 	char vnic_vid[6];
956 } vnic_fields_buf_t;
957 
958 static const ofmt_field_t vnic_fields[] = {
959 { "LINK",		13,
960 	offsetof(vnic_fields_buf_t, vnic_link),	print_default_cb},
961 { "OVER",		13,
962 	offsetof(vnic_fields_buf_t, vnic_over),	print_default_cb},
963 { "SPEED",		7,
964 	offsetof(vnic_fields_buf_t, vnic_speed), print_default_cb},
965 { "MACADDRESS",		18,
966 	offsetof(vnic_fields_buf_t, vnic_macaddr), print_default_cb},
967 { "MACADDRTYPE",	20,
968 	offsetof(vnic_fields_buf_t, vnic_macaddrtype), print_default_cb},
969 { "VID",		7,
970 	offsetof(vnic_fields_buf_t, vnic_vid), print_default_cb},
971 { NULL,			0, 0, NULL}}
972 ;
973 
974 /*
975  * structures for 'dladm show-simnet'
976  */
977 typedef struct simnet_fields_buf_s
978 {
979 	char simnet_name[DLPI_LINKNAME_MAX];
980 	char simnet_media[DLADM_STRSIZE];
981 	char simnet_macaddr[18];
982 	char simnet_otherlink[DLPI_LINKNAME_MAX];
983 } simnet_fields_buf_t;
984 
985 static const ofmt_field_t simnet_fields[] = {
986 { "LINK",		12,
987 	offsetof(simnet_fields_buf_t, simnet_name), print_default_cb},
988 { "MEDIA",		20,
989 	offsetof(simnet_fields_buf_t, simnet_media), print_default_cb},
990 { "MACADDRESS",		18,
991 	offsetof(simnet_fields_buf_t, simnet_macaddr), print_default_cb},
992 { "OTHERLINK",		12,
993 	offsetof(simnet_fields_buf_t, simnet_otherlink), print_default_cb},
994 { NULL,			0, 0, NULL}}
995 ;
996 
997 /*
998  * structures for 'dladm show-usage'
999  */
1000 
1001 typedef struct  usage_fields_buf_s {
1002 	char	usage_link[12];
1003 	char	usage_duration[10];
1004 	char	usage_ipackets[9];
1005 	char	usage_rbytes[10];
1006 	char	usage_opackets[9];
1007 	char	usage_obytes[10];
1008 	char	usage_bandwidth[14];
1009 } usage_fields_buf_t;
1010 
1011 static const ofmt_field_t usage_fields[] = {
1012 { "LINK",	13,
1013 	offsetof(usage_fields_buf_t, usage_link), print_default_cb},
1014 { "DURATION",	11,
1015 	offsetof(usage_fields_buf_t, usage_duration), print_default_cb},
1016 { "IPACKETS",	10,
1017 	offsetof(usage_fields_buf_t, usage_ipackets), print_default_cb},
1018 { "RBYTES",	11,
1019 	offsetof(usage_fields_buf_t, usage_rbytes), print_default_cb},
1020 { "OPACKETS",	10,
1021 	offsetof(usage_fields_buf_t, usage_opackets), print_default_cb},
1022 { "OBYTES",	11,
1023 	offsetof(usage_fields_buf_t, usage_obytes), print_default_cb},
1024 { "BANDWIDTH",	15,
1025 	offsetof(usage_fields_buf_t, usage_bandwidth), print_default_cb},
1026 { NULL,		0, 0, NULL}}
1027 ;
1028 
1029 
1030 /*
1031  * structures for 'dladm show-usage link'
1032  */
1033 
1034 typedef struct  usage_l_fields_buf_s {
1035 	char	usage_l_link[12];
1036 	char	usage_l_stime[13];
1037 	char	usage_l_etime[13];
1038 	char	usage_l_rbytes[8];
1039 	char	usage_l_obytes[8];
1040 	char	usage_l_bandwidth[14];
1041 } usage_l_fields_buf_t;
1042 
1043 static const ofmt_field_t usage_l_fields[] = {
1044 /* name,	field width,	offset */
1045 { "LINK",	13,
1046 	offsetof(usage_l_fields_buf_t, usage_l_link), print_default_cb},
1047 { "START",	14,
1048 	offsetof(usage_l_fields_buf_t, usage_l_stime), print_default_cb},
1049 { "END",	14,
1050 	offsetof(usage_l_fields_buf_t, usage_l_etime), print_default_cb},
1051 { "RBYTES",	9,
1052 	offsetof(usage_l_fields_buf_t, usage_l_rbytes), print_default_cb},
1053 { "OBYTES",	9,
1054 	offsetof(usage_l_fields_buf_t, usage_l_obytes), print_default_cb},
1055 { "BANDWIDTH",	15,
1056 	offsetof(usage_l_fields_buf_t, usage_l_bandwidth), print_default_cb},
1057 { NULL,		0, 0, NULL}}
1058 ;
1059 
1060 /* IPTUN_*FLAG_INDEX values are indices into iptun_flags below. */
1061 enum { IPTUN_SFLAG_INDEX, IPTUN_IFLAG_INDEX, IPTUN_NUM_FLAGS };
1062 
1063 /*
1064  * structures for 'dladm show-iptun'
1065  */
1066 typedef struct iptun_fields_buf_s {
1067 	char	iptun_name[MAXLINKNAMELEN];
1068 	char	iptun_type[5];
1069 	char	iptun_laddr[NI_MAXHOST];
1070 	char	iptun_raddr[NI_MAXHOST];
1071 	char	iptun_flags[IPTUN_NUM_FLAGS + 1];
1072 } iptun_fields_buf_t;
1073 
1074 static const ofmt_field_t iptun_fields[] = {
1075 { "LINK",	16,
1076 	offsetof(iptun_fields_buf_t, iptun_name), print_default_cb },
1077 { "TYPE",	6,
1078 	offsetof(iptun_fields_buf_t, iptun_type), print_default_cb },
1079 { "FLAGS",	7,
1080 	offsetof(iptun_fields_buf_t, iptun_flags), print_default_cb },
1081 { "LOCAL",	20,
1082 	offsetof(iptun_fields_buf_t, iptun_laddr), print_default_cb },
1083 { "REMOTE",	20,
1084 	offsetof(iptun_fields_buf_t, iptun_raddr), print_default_cb },
1085 { NULL, 0, 0, NULL}
1086 };
1087 
1088 /*
1089  * structures for 'dladm show-bridge'.  These are based on sections 14.8.1.1.3
1090  * and 14.8.1.2.2 of IEEE 802.1D-2004.
1091  */
1092 typedef struct bridge_fields_buf_s {
1093 	char bridge_name[MAXLINKNAMELEN]; /* 14.4.1.2.3(b) */
1094 	char bridge_protect[7];		/* stp or trill */
1095 	char bridge_address[24];	/* 17.18.3, 7.12.5, 14.4.1.2.3(a) */
1096 	char bridge_priority[7];	/* 17.18.3 9.2.5 - only upper 4 bits */
1097 	char bridge_bmaxage[7];		/* 17.18.4 configured */
1098 	char bridge_bhellotime[7];	/* 17.18.4 configured */
1099 	char bridge_bfwddelay[7];	/* 17.18.4 configured */
1100 	char bridge_forceproto[3];	/* 17.13.4 configured */
1101 	char bridge_tctime[12];		/* 14.8.1.1.3(b) */
1102 	char bridge_tccount[12];	/* 17.17.8 */
1103 	char bridge_tchange[12];	/* 17.17.8 */
1104 	char bridge_desroot[24];	/* 17.18.6 priority "/" MAC */
1105 	char bridge_rootcost[12];	/* 17.18.6 */
1106 	char bridge_rootport[12];	/* 17.18.6 */
1107 	char bridge_maxage[7];		/* 17.18.7 for root */
1108 	char bridge_hellotime[7];	/* 17.13.6 for root */
1109 	char bridge_fwddelay[7];	/* 17.13.5 for root */
1110 	char bridge_holdtime[12];	/* 17.13.12 for root */
1111 } bridge_fields_buf_t;
1112 
1113 static ofmt_field_t bridge_fields[] = {
1114 /* name,	field width,	offset,	callback	*/
1115 { "BRIDGE",	12,
1116     offsetof(bridge_fields_buf_t, bridge_name), print_default_cb },
1117 { "PROTECT",	8,
1118     offsetof(bridge_fields_buf_t, bridge_protect), print_default_cb },
1119 { "ADDRESS",	19,
1120     offsetof(bridge_fields_buf_t, bridge_address), print_default_cb },
1121 { "PRIORITY",	9,
1122     offsetof(bridge_fields_buf_t, bridge_priority), print_default_cb },
1123 { "BMAXAGE",	8,
1124     offsetof(bridge_fields_buf_t, bridge_bmaxage), print_default_cb },
1125 { "BHELLOTIME",	11,
1126     offsetof(bridge_fields_buf_t, bridge_bhellotime), print_default_cb },
1127 { "BFWDDELAY",	10,
1128     offsetof(bridge_fields_buf_t, bridge_bfwddelay), print_default_cb },
1129 { "FORCEPROTO",	11,
1130     offsetof(bridge_fields_buf_t, bridge_forceproto), print_default_cb },
1131 { "TCTIME",	10,
1132     offsetof(bridge_fields_buf_t, bridge_tctime), print_default_cb },
1133 { "TCCOUNT",	10,
1134     offsetof(bridge_fields_buf_t, bridge_tccount), print_default_cb },
1135 { "TCHANGE",	10,
1136     offsetof(bridge_fields_buf_t, bridge_tchange), print_default_cb },
1137 { "DESROOT",	23,
1138     offsetof(bridge_fields_buf_t, bridge_desroot), print_default_cb },
1139 { "ROOTCOST",	11,
1140     offsetof(bridge_fields_buf_t, bridge_rootcost), print_default_cb },
1141 { "ROOTPORT",	11,
1142     offsetof(bridge_fields_buf_t, bridge_rootport), print_default_cb },
1143 { "MAXAGE",	8,
1144     offsetof(bridge_fields_buf_t, bridge_maxage), print_default_cb },
1145 { "HELLOTIME",	10,
1146     offsetof(bridge_fields_buf_t, bridge_hellotime), print_default_cb },
1147 { "FWDDELAY",	9,
1148     offsetof(bridge_fields_buf_t, bridge_fwddelay), print_default_cb },
1149 { "HOLDTIME",	9,
1150     offsetof(bridge_fields_buf_t, bridge_holdtime), print_default_cb },
1151 { NULL,		0, 0, NULL}};
1152 
1153 /*
1154  * structures for 'dladm show-bridge -l'.  These are based on 14.4.1.2.3 and
1155  * 14.8.2.1.3 of IEEE 802.1D-2004.
1156  */
1157 typedef struct bridge_link_fields_buf_s {
1158 	char bridgel_link[MAXLINKNAMELEN];
1159 	char bridgel_index[7];			/* 14.4.1.2.3(d1) */
1160 	char bridgel_state[11];			/* 14.8.2.1.3(b) */
1161 	char bridgel_uptime[7];			/* 14.8.2.1.3(a) */
1162 	char bridgel_opercost[7]		/* 14.8.2.1.3(d) */;
1163 	char bridgel_operp2p[4];		/* 14.8.2.1.3(p) */
1164 	char bridgel_operedge[4];		/* 14.8.2.1.3(k) */
1165 	char bridgel_desroot[23];		/* 14.8.2.1.3(e) */
1166 	char bridgel_descost[12];		/* 14.8.2.1.3(f) */
1167 	char bridgel_desbridge[23];		/* 14.8.2.1.3(g) */
1168 	char bridgel_desport[7];		/* 14.8.2.1.3(h) */
1169 	char bridgel_tcack[4];			/* 14.8.2.1.3(i) */
1170 } bridge_link_fields_buf_t;
1171 
1172 static ofmt_field_t bridge_link_fields[] = {
1173 /* name,	field width,	offset,	callback	*/
1174 { "LINK",		12,
1175     offsetof(bridge_link_fields_buf_t, bridgel_link), print_default_cb },
1176 { "INDEX",	8,
1177     offsetof(bridge_link_fields_buf_t, bridgel_index), print_default_cb },
1178 { "STATE",	12,
1179     offsetof(bridge_link_fields_buf_t, bridgel_state), print_default_cb },
1180 { "UPTIME",	8,
1181     offsetof(bridge_link_fields_buf_t, bridgel_uptime), print_default_cb },
1182 { "OPERCOST",	9,
1183     offsetof(bridge_link_fields_buf_t, bridgel_opercost), print_default_cb },
1184 { "OPERP2P",	8,
1185     offsetof(bridge_link_fields_buf_t, bridgel_operp2p), print_default_cb },
1186 { "OPEREDGE",	9,
1187     offsetof(bridge_link_fields_buf_t, bridgel_operedge), print_default_cb },
1188 { "DESROOT",	22,
1189     offsetof(bridge_link_fields_buf_t, bridgel_desroot), print_default_cb },
1190 { "DESCOST",	11,
1191     offsetof(bridge_link_fields_buf_t, bridgel_descost), print_default_cb },
1192 { "DESBRIDGE",	22,
1193     offsetof(bridge_link_fields_buf_t, bridgel_desbridge), print_default_cb },
1194 { "DESPORT",	8,
1195     offsetof(bridge_link_fields_buf_t, bridgel_desport), print_default_cb },
1196 { "TCACK",	6,
1197     offsetof(bridge_link_fields_buf_t, bridgel_tcack), print_default_cb },
1198 { NULL,		0, 0, NULL}};
1199 
1200 /*
1201  * structures for 'dladm show-bridge -s'.  These are not based on IEEE
1202  * 802.1D-2004.
1203  */
1204 #define	ULONG_DIG	(((sizeof (ulong_t) * NBBY) * 3 / 10) + 1)
1205 #define	UINT64_DIG	(((sizeof (uint64_t) * NBBY) * 3 / 10) + 1)
1206 typedef struct bridge_statfields_buf_s {
1207 	char bridges_name[MAXLINKNAMELEN];
1208 	char bridges_drops[UINT64_DIG];
1209 	char bridges_forwards[UINT64_DIG];
1210 	char bridges_mbcast[UINT64_DIG];
1211 	char bridges_unknown[UINT64_DIG];
1212 	char bridges_recv[UINT64_DIG];
1213 	char bridges_sent[UINT64_DIG];
1214 } bridge_statfields_buf_t;
1215 
1216 static ofmt_field_t bridge_statfields[] = {
1217 /* name,	field width,	offset,	callback	*/
1218 { "BRIDGE",	12,
1219     offsetof(bridge_statfields_buf_t, bridges_name), print_default_cb },
1220 { "DROPS",	12,
1221     offsetof(bridge_statfields_buf_t, bridges_drops), print_default_cb },
1222 { "FORWARDS",	12,
1223     offsetof(bridge_statfields_buf_t, bridges_forwards), print_default_cb },
1224 { "MBCAST",	12,
1225     offsetof(bridge_statfields_buf_t, bridges_mbcast), print_default_cb },
1226 { "UNKNOWN",	12,
1227     offsetof(bridge_statfields_buf_t, bridges_unknown), print_default_cb },
1228 { "RECV",	12,
1229     offsetof(bridge_statfields_buf_t, bridges_recv), print_default_cb },
1230 { "SENT",	12,
1231     offsetof(bridge_statfields_buf_t, bridges_sent), print_default_cb },
1232 { NULL,		0, 0, NULL}};
1233 
1234 /*
1235  * structures for 'dladm show-bridge -s -l'.  These are based in part on
1236  * section 14.6.1.1.3 of IEEE 802.1D-2004.
1237  */
1238 typedef struct bridge_link_statfields_buf_s {
1239 	char bridgels_link[MAXLINKNAMELEN];
1240 	char bridgels_cfgbpdu[ULONG_DIG];
1241 	char bridgels_tcnbpdu[ULONG_DIG];
1242 	char bridgels_rstpbpdu[ULONG_DIG];
1243 	char bridgels_txbpdu[ULONG_DIG];
1244 	char bridgels_drops[UINT64_DIG];	/* 14.6.1.1.3(d) */
1245 	char bridgels_recv[UINT64_DIG];		/* 14.6.1.1.3(a) */
1246 	char bridgels_xmit[UINT64_DIG];		/* 14.6.1.1.3(c) */
1247 } bridge_link_statfields_buf_t;
1248 
1249 static ofmt_field_t bridge_link_statfields[] = {
1250 /* name,	field width,	offset,	callback	*/
1251 { "LINK",	12,
1252     offsetof(bridge_link_statfields_buf_t, bridgels_link), print_default_cb },
1253 { "CFGBPDU",	9,
1254     offsetof(bridge_link_statfields_buf_t, bridgels_cfgbpdu),
1255     print_default_cb },
1256 { "TCNBPDU",	9,
1257     offsetof(bridge_link_statfields_buf_t, bridgels_tcnbpdu),
1258     print_default_cb },
1259 { "RSTPBPDU",	9,
1260     offsetof(bridge_link_statfields_buf_t, bridgels_rstpbpdu),
1261     print_default_cb },
1262 { "TXBPDU",	9,
1263     offsetof(bridge_link_statfields_buf_t, bridgels_txbpdu), print_default_cb },
1264 { "DROPS",	9,
1265     offsetof(bridge_link_statfields_buf_t, bridgels_drops), print_default_cb },
1266 { "RECV",	9,
1267     offsetof(bridge_link_statfields_buf_t, bridgels_recv), print_default_cb },
1268 { "XMIT",	9,
1269     offsetof(bridge_link_statfields_buf_t, bridgels_xmit), print_default_cb },
1270 { NULL,		0, 0, NULL}};
1271 
1272 /*
1273  * structures for 'dladm show-bridge -f'.  These are based in part on
1274  * section  14.7.6.3.3 of IEEE 802.1D-2004.
1275  */
1276 typedef struct bridge_fwd_fields_buf_s {
1277 	char bridgef_dest[18];			/* 14.7.6.3.3(a) */
1278 	char bridgef_age[8];
1279 	char bridgef_flags[6];
1280 	char bridgef_output[MAXLINKNAMELEN];	/* 14.7.6.3.3(c) */
1281 } bridge_fwd_fields_buf_t;
1282 
1283 static ofmt_field_t bridge_fwd_fields[] = {
1284 /* name,	field width,	offset,	callback	*/
1285 { "DEST",	17,
1286     offsetof(bridge_fwd_fields_buf_t, bridgef_dest), print_default_cb },
1287 { "AGE",	7,
1288     offsetof(bridge_fwd_fields_buf_t, bridgef_age), print_default_cb },
1289 { "FLAGS",	6,
1290     offsetof(bridge_fwd_fields_buf_t, bridgef_flags), print_default_cb },
1291 { "OUTPUT",	12,
1292     offsetof(bridge_fwd_fields_buf_t, bridgef_output), print_default_cb },
1293 { NULL,		0, 0, NULL}};
1294 
1295 /*
1296  * structures for 'dladm show-bridge -t'.
1297  */
1298 typedef struct bridge_trill_fields_buf_s {
1299 	char bridget_nick[6];
1300 	char bridget_flags[6];
1301 	char bridget_link[MAXLINKNAMELEN];
1302 	char bridget_nexthop[18];
1303 } bridge_trill_fields_buf_t;
1304 
1305 static ofmt_field_t bridge_trill_fields[] = {
1306 /* name,	field width,	offset,	callback	*/
1307 { "NICK",	5,
1308     offsetof(bridge_trill_fields_buf_t, bridget_nick), print_default_cb },
1309 { "FLAGS",	6,
1310     offsetof(bridge_trill_fields_buf_t, bridget_flags), print_default_cb },
1311 { "LINK",	12,
1312     offsetof(bridge_trill_fields_buf_t, bridget_link), print_default_cb },
1313 { "NEXTHOP",	17,
1314     offsetof(bridge_trill_fields_buf_t, bridget_nexthop), print_default_cb },
1315 { NULL,		0, 0, NULL}};
1316 
1317 static char *progname;
1318 static sig_atomic_t signalled;
1319 
1320 /*
1321  * Handle to libdladm.  Opened in main() before the sub-command
1322  * specific function is called.
1323  */
1324 static dladm_handle_t handle = NULL;
1325 
1326 #define	DLADM_ETHERSTUB_NAME	"etherstub"
1327 #define	DLADM_IS_ETHERSTUB(id)	(id == DATALINK_INVALID_LINKID)
1328 
1329 static void
1330 usage(void)
1331 {
1332 	int	i;
1333 	cmd_t	*cmdp;
1334 	(void) fprintf(stderr, gettext("usage:  dladm <subcommand> <args> ..."
1335 	    "\n"));
1336 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
1337 		cmdp = &cmds[i];
1338 		if (cmdp->c_usage != NULL)
1339 			(void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
1340 	}
1341 
1342 	/* close dladm handle if it was opened */
1343 	if (handle != NULL)
1344 		dladm_close(handle);
1345 
1346 	exit(EXIT_FAILURE);
1347 }
1348 
1349 int
1350 main(int argc, char *argv[])
1351 {
1352 	int	i;
1353 	cmd_t	*cmdp;
1354 	dladm_status_t status;
1355 
1356 	(void) setlocale(LC_ALL, "");
1357 #if !defined(TEXT_DOMAIN)
1358 #define	TEXT_DOMAIN "SYS_TEST"
1359 #endif
1360 	(void) textdomain(TEXT_DOMAIN);
1361 
1362 	progname = argv[0];
1363 
1364 	if (argc < 2)
1365 		usage();
1366 
1367 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
1368 		cmdp = &cmds[i];
1369 		if (strcmp(argv[1], cmdp->c_name) == 0) {
1370 			/* Open the libdladm handle */
1371 			if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
1372 				die_dlerr(status,
1373 				    "could not open /dev/dld");
1374 			}
1375 
1376 			cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
1377 
1378 			dladm_close(handle);
1379 			return (EXIT_SUCCESS);
1380 		}
1381 	}
1382 
1383 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
1384 	    progname, argv[1]);
1385 	usage();
1386 	return (EXIT_FAILURE);
1387 }
1388 
1389 /*ARGSUSED*/
1390 static int
1391 show_usage_date(dladm_usage_t *usage, void *arg)
1392 {
1393 	show_usage_state_t	*state = (show_usage_state_t *)arg;
1394 	time_t			stime;
1395 	char			timebuf[20];
1396 	dladm_status_t		status;
1397 	uint32_t		flags;
1398 
1399 	/*
1400 	 * Only show usage information for existing links unless '-a'
1401 	 * is specified.
1402 	 */
1403 	if (!state->us_showall) {
1404 		if ((status = dladm_name2info(handle, usage->du_name,
1405 		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
1406 			return (status);
1407 		}
1408 		if ((flags & DLADM_OPT_ACTIVE) == 0)
1409 			return (DLADM_STATUS_LINKINVAL);
1410 	}
1411 
1412 	stime = usage->du_stime;
1413 	(void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
1414 	    localtime(&stime));
1415 	(void) printf("%s\n", timebuf);
1416 
1417 	return (DLADM_STATUS_OK);
1418 }
1419 
1420 static int
1421 show_usage_time(dladm_usage_t *usage, void *arg)
1422 {
1423 	show_usage_state_t	*state = (show_usage_state_t *)arg;
1424 	char			buf[DLADM_STRSIZE];
1425 	usage_l_fields_buf_t 	ubuf;
1426 	time_t			time;
1427 	double			bw;
1428 	dladm_status_t		status;
1429 	uint32_t		flags;
1430 
1431 	/*
1432 	 * Only show usage information for existing links unless '-a'
1433 	 * is specified.
1434 	 */
1435 	if (!state->us_showall) {
1436 		if ((status = dladm_name2info(handle, usage->du_name,
1437 		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
1438 			return (status);
1439 		}
1440 		if ((flags & DLADM_OPT_ACTIVE) == 0)
1441 			return (DLADM_STATUS_LINKINVAL);
1442 	}
1443 
1444 	if (state->us_plot) {
1445 		if (!state->us_printheader) {
1446 			if (state->us_first) {
1447 				(void) printf("# Time");
1448 				state->us_first = B_FALSE;
1449 			}
1450 			(void) printf(" %s", usage->du_name);
1451 			if (usage->du_last) {
1452 				(void) printf("\n");
1453 				state->us_first = B_TRUE;
1454 				state->us_printheader = B_TRUE;
1455 			}
1456 		} else {
1457 			if (state->us_first) {
1458 				time = usage->du_etime;
1459 				(void) strftime(buf, sizeof (buf), "%T",
1460 				    localtime(&time));
1461 				state->us_first = B_FALSE;
1462 				(void) printf("%s", buf);
1463 			}
1464 			bw = (double)usage->du_bandwidth/1000;
1465 			(void) printf(" %.2f", bw);
1466 			if (usage->du_last) {
1467 				(void) printf("\n");
1468 				state->us_first = B_TRUE;
1469 			}
1470 		}
1471 		return (DLADM_STATUS_OK);
1472 	}
1473 
1474 	bzero(&ubuf, sizeof (ubuf));
1475 
1476 	(void) snprintf(ubuf.usage_l_link, sizeof (ubuf.usage_l_link), "%s",
1477 	    usage->du_name);
1478 	time = usage->du_stime;
1479 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
1480 	(void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s",
1481 	    buf);
1482 	time = usage->du_etime;
1483 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
1484 	(void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s",
1485 	    buf);
1486 	(void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes),
1487 	    "%llu", usage->du_rbytes);
1488 	(void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes),
1489 	    "%llu", usage->du_obytes);
1490 	(void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth),
1491 	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
1492 
1493 	ofmt_print(state->us_ofmt, &ubuf);
1494 	return (DLADM_STATUS_OK);
1495 }
1496 
1497 static int
1498 show_usage_res(dladm_usage_t *usage, void *arg)
1499 {
1500 	show_usage_state_t	*state = (show_usage_state_t *)arg;
1501 	char			buf[DLADM_STRSIZE];
1502 	usage_fields_buf_t	ubuf;
1503 	dladm_status_t		status;
1504 	uint32_t		flags;
1505 
1506 	/*
1507 	 * Only show usage information for existing links unless '-a'
1508 	 * is specified.
1509 	 */
1510 	if (!state->us_showall) {
1511 		if ((status = dladm_name2info(handle, usage->du_name,
1512 		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
1513 			return (status);
1514 		}
1515 		if ((flags & DLADM_OPT_ACTIVE) == 0)
1516 			return (DLADM_STATUS_LINKINVAL);
1517 	}
1518 
1519 	bzero(&ubuf, sizeof (ubuf));
1520 
1521 	(void) snprintf(ubuf.usage_link, sizeof (ubuf.usage_link), "%s",
1522 	    usage->du_name);
1523 	(void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration),
1524 	    "%llu", usage->du_duration);
1525 	(void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets),
1526 	    "%llu", usage->du_ipackets);
1527 	(void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes),
1528 	    "%llu", usage->du_rbytes);
1529 	(void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets),
1530 	    "%llu", usage->du_opackets);
1531 	(void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes),
1532 	    "%llu", usage->du_obytes);
1533 	(void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth),
1534 	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
1535 
1536 	ofmt_print(state->us_ofmt, &ubuf);
1537 
1538 	return (DLADM_STATUS_OK);
1539 }
1540 
1541 static boolean_t
1542 valid_formatspec(char *formatspec_str)
1543 {
1544 	if (strcmp(formatspec_str, "gnuplot") == 0)
1545 		return (B_TRUE);
1546 	return (B_FALSE);
1547 
1548 }
1549 
1550 /*ARGSUSED*/
1551 static void
1552 do_show_usage(int argc, char *argv[], const char *use)
1553 {
1554 	char			*file = NULL;
1555 	int			opt;
1556 	dladm_status_t		status;
1557 	boolean_t		d_arg = B_FALSE;
1558 	char			*stime = NULL;
1559 	char			*etime = NULL;
1560 	char			*resource = NULL;
1561 	show_usage_state_t	state;
1562 	boolean_t		o_arg = B_FALSE;
1563 	boolean_t		F_arg = B_FALSE;
1564 	char			*fields_str = NULL;
1565 	char			*formatspec_str = NULL;
1566 	char			*all_l_fields =
1567 	    "link,start,end,rbytes,obytes,bandwidth";
1568 	ofmt_handle_t		ofmt;
1569 	ofmt_status_t		oferr;
1570 	uint_t			ofmtflags = 0;
1571 
1572 	bzero(&state, sizeof (show_usage_state_t));
1573 	state.us_parsable = B_FALSE;
1574 	state.us_printheader = B_FALSE;
1575 	state.us_plot = B_FALSE;
1576 	state.us_first = B_TRUE;
1577 
1578 	while ((opt = getopt_long(argc, argv, "das:e:o:f:F:",
1579 	    usage_opts, NULL)) != -1) {
1580 		switch (opt) {
1581 		case 'd':
1582 			d_arg = B_TRUE;
1583 			break;
1584 		case 'a':
1585 			state.us_showall = B_TRUE;
1586 			break;
1587 		case 'f':
1588 			file = optarg;
1589 			break;
1590 		case 's':
1591 			stime = optarg;
1592 			break;
1593 		case 'e':
1594 			etime = optarg;
1595 			break;
1596 		case 'o':
1597 			o_arg = B_TRUE;
1598 			fields_str = optarg;
1599 			break;
1600 		case 'F':
1601 			state.us_plot = F_arg = B_TRUE;
1602 			formatspec_str = optarg;
1603 			break;
1604 		default:
1605 			die_opterr(optopt, opt, use);
1606 			break;
1607 		}
1608 	}
1609 
1610 	if (file == NULL)
1611 		die("show-usage requires a file");
1612 
1613 	if (optind == (argc-1)) {
1614 		uint32_t 	flags;
1615 
1616 		resource = argv[optind];
1617 		if (!state.us_showall &&
1618 		    (((status = dladm_name2info(handle, resource, NULL, &flags,
1619 		    NULL, NULL)) != DLADM_STATUS_OK) ||
1620 		    ((flags & DLADM_OPT_ACTIVE) == 0))) {
1621 			die("invalid link: '%s'", resource);
1622 		}
1623 	}
1624 
1625 	if (F_arg && d_arg)
1626 		die("incompatible -d and -F options");
1627 
1628 	if (F_arg && valid_formatspec(formatspec_str) == B_FALSE)
1629 		die("Format specifier %s not supported", formatspec_str);
1630 
1631 	if (state.us_parsable)
1632 		ofmtflags |= OFMT_PARSABLE;
1633 
1634 	if (resource == NULL && stime == NULL && etime == NULL) {
1635 		oferr = ofmt_open(fields_str, usage_fields, ofmtflags, 0,
1636 		    &ofmt);
1637 	} else {
1638 		if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
1639 			fields_str = all_l_fields;
1640 		oferr = ofmt_open(fields_str, usage_l_fields, ofmtflags, 0,
1641 		    &ofmt);
1642 
1643 	}
1644 	dladm_ofmt_check(oferr, state.us_parsable, ofmt);
1645 	state.us_ofmt = ofmt;
1646 
1647 	if (d_arg) {
1648 		/* Print log dates */
1649 		status = dladm_usage_dates(show_usage_date,
1650 		    DLADM_LOGTYPE_LINK, file, resource, &state);
1651 	} else if (resource == NULL && stime == NULL && etime == NULL &&
1652 	    !F_arg) {
1653 		/* Print summary */
1654 		status = dladm_usage_summary(show_usage_res,
1655 		    DLADM_LOGTYPE_LINK, file, &state);
1656 	} else if (resource != NULL) {
1657 		/* Print log entries for named resource */
1658 		status = dladm_walk_usage_res(show_usage_time,
1659 		    DLADM_LOGTYPE_LINK, file, resource, stime, etime, &state);
1660 	} else {
1661 		/* Print time and information for each link */
1662 		status = dladm_walk_usage_time(show_usage_time,
1663 		    DLADM_LOGTYPE_LINK, file, stime, etime, &state);
1664 	}
1665 
1666 	if (status != DLADM_STATUS_OK)
1667 		die_dlerr(status, "show-usage");
1668 	ofmt_close(ofmt);
1669 }
1670 
1671 static void
1672 do_create_aggr(int argc, char *argv[], const char *use)
1673 {
1674 	int			option;
1675 	int			key = 0;
1676 	uint32_t		policy = AGGR_POLICY_L4;
1677 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
1678 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
1679 	dladm_aggr_port_attr_db_t	port[MAXPORT];
1680 	uint_t			n, ndev, nlink;
1681 	uint8_t			mac_addr[ETHERADDRL];
1682 	boolean_t		mac_addr_fixed = B_FALSE;
1683 	boolean_t		P_arg = B_FALSE;
1684 	boolean_t		l_arg = B_FALSE;
1685 	boolean_t		u_arg = B_FALSE;
1686 	boolean_t		T_arg = B_FALSE;
1687 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1688 	char			*altroot = NULL;
1689 	char			name[MAXLINKNAMELEN];
1690 	char			*devs[MAXPORT];
1691 	char			*links[MAXPORT];
1692 	dladm_status_t		status;
1693 	dladm_status_t		pstatus;
1694 	char			propstr[DLADM_STRSIZE];
1695 	dladm_arg_list_t	*proplist = NULL;
1696 	int			i;
1697 	datalink_id_t		linkid;
1698 
1699 	ndev = nlink = opterr = 0;
1700 	bzero(propstr, DLADM_STRSIZE);
1701 
1702 	while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:p:",
1703 	    lopts, NULL)) != -1) {
1704 		switch (option) {
1705 		case 'd':
1706 			if (ndev + nlink >= MAXPORT)
1707 				die("too many ports specified");
1708 
1709 			devs[ndev++] = optarg;
1710 			break;
1711 		case 'P':
1712 			if (P_arg)
1713 				die_optdup(option);
1714 
1715 			P_arg = B_TRUE;
1716 			if (!dladm_aggr_str2policy(optarg, &policy))
1717 				die("invalid policy '%s'", optarg);
1718 			break;
1719 		case 'u':
1720 			if (u_arg)
1721 				die_optdup(option);
1722 
1723 			u_arg = B_TRUE;
1724 			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
1725 			    mac_addr))
1726 				die("invalid MAC address '%s'", optarg);
1727 			break;
1728 		case 'l':
1729 			if (isdigit(optarg[strlen(optarg) - 1])) {
1730 
1731 				/*
1732 				 * Ended with digit, possibly a link name.
1733 				 */
1734 				if (ndev + nlink >= MAXPORT)
1735 					die("too many ports specified");
1736 
1737 				links[nlink++] = optarg;
1738 				break;
1739 			}
1740 			/* FALLTHROUGH */
1741 		case 'L':
1742 			if (l_arg)
1743 				die_optdup(option);
1744 
1745 			l_arg = B_TRUE;
1746 			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
1747 				die("invalid LACP mode '%s'", optarg);
1748 			break;
1749 		case 'T':
1750 			if (T_arg)
1751 				die_optdup(option);
1752 
1753 			T_arg = B_TRUE;
1754 			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
1755 				die("invalid LACP timer value '%s'", optarg);
1756 			break;
1757 		case 't':
1758 			flags &= ~DLADM_OPT_PERSIST;
1759 			break;
1760 		case 'f':
1761 			flags |= DLADM_OPT_FORCE;
1762 			break;
1763 		case 'R':
1764 			altroot = optarg;
1765 			break;
1766 		case 'p':
1767 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
1768 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
1769 			    DLADM_STRSIZE)
1770 				die("property list too long '%s'", propstr);
1771 			break;
1772 
1773 		default:
1774 			die_opterr(optopt, option, use);
1775 			break;
1776 		}
1777 	}
1778 
1779 	if (ndev + nlink == 0)
1780 		usage();
1781 
1782 	/* get key value or the aggregation name (required last argument) */
1783 	if (optind != (argc-1))
1784 		usage();
1785 
1786 	if (!str2int(argv[optind], &key)) {
1787 		if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >=
1788 		    MAXLINKNAMELEN) {
1789 			die("link name too long '%s'", argv[optind]);
1790 		}
1791 
1792 		if (!dladm_valid_linkname(name))
1793 			die("invalid link name '%s'", argv[optind]);
1794 	} else {
1795 		(void) snprintf(name, MAXLINKNAMELEN, "aggr%d", key);
1796 	}
1797 
1798 	if (altroot != NULL)
1799 		altroot_cmd(altroot, argc, argv);
1800 
1801 	for (n = 0; n < ndev; n++) {
1802 		if ((status = dladm_dev2linkid(handle, devs[n],
1803 		    &port[n].lp_linkid)) != DLADM_STATUS_OK) {
1804 			die_dlerr(status, "invalid dev name '%s'", devs[n]);
1805 		}
1806 	}
1807 
1808 	for (n = 0; n < nlink; n++) {
1809 		if ((status = dladm_name2info(handle, links[n],
1810 		    &port[ndev + n].lp_linkid, NULL, NULL, NULL)) !=
1811 		    DLADM_STATUS_OK) {
1812 			die_dlerr(status, "invalid link name '%s'", links[n]);
1813 		}
1814 	}
1815 
1816 	status = dladm_aggr_create(handle, name, key, ndev + nlink, port,
1817 	    policy, mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode,
1818 	    lacp_timer, flags);
1819 	if (status != DLADM_STATUS_OK)
1820 		goto done;
1821 
1822 	if (dladm_parse_link_props(propstr, &proplist, B_FALSE)
1823 	    != DLADM_STATUS_OK)
1824 		die("invalid aggregation property");
1825 
1826 	if (proplist == NULL)
1827 		return;
1828 
1829 	status = dladm_name2info(handle, name, &linkid, NULL, NULL, NULL);
1830 	if (status != DLADM_STATUS_OK)
1831 		goto done;
1832 
1833 	for (i = 0; i < proplist->al_count; i++) {
1834 		dladm_arg_info_t	*aip = &proplist->al_info[i];
1835 
1836 		pstatus = dladm_set_linkprop(handle, linkid, aip->ai_name,
1837 		    aip->ai_val, aip->ai_count, flags);
1838 
1839 		if (pstatus != DLADM_STATUS_OK) {
1840 			die_dlerr(pstatus,
1841 			    "aggr creation succeeded but "
1842 			    "could not set property '%s'", aip->ai_name);
1843 		}
1844 	}
1845 done:
1846 	dladm_free_props(proplist);
1847 	if (status != DLADM_STATUS_OK) {
1848 		if (status == DLADM_STATUS_NONOTIF) {
1849 			die("not all links have link up/down detection; must "
1850 			    "use -f (see dladm(1M))");
1851 		} else {
1852 			die_dlerr(status, "create operation failed");
1853 		}
1854 	}
1855 }
1856 
1857 /*
1858  * arg is either the key or the aggr name. Validate it and convert it to
1859  * the linkid if altroot is NULL.
1860  */
1861 static dladm_status_t
1862 i_dladm_aggr_get_linkid(const char *altroot, const char *arg,
1863     datalink_id_t *linkidp, uint32_t flags)
1864 {
1865 	int		key = 0;
1866 	char		*aggr = NULL;
1867 	dladm_status_t	status;
1868 
1869 	if (!str2int(arg, &key))
1870 		aggr = (char *)arg;
1871 
1872 	if (aggr == NULL && key == 0)
1873 		return (DLADM_STATUS_LINKINVAL);
1874 
1875 	if (altroot != NULL)
1876 		return (DLADM_STATUS_OK);
1877 
1878 	if (aggr != NULL) {
1879 		status = dladm_name2info(handle, aggr, linkidp, NULL, NULL,
1880 		    NULL);
1881 	} else {
1882 		status = dladm_key2linkid(handle, key, linkidp, flags);
1883 	}
1884 
1885 	return (status);
1886 }
1887 
1888 static void
1889 do_delete_aggr(int argc, char *argv[], const char *use)
1890 {
1891 	int			option;
1892 	char			*altroot = NULL;
1893 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1894 	dladm_status_t		status;
1895 	datalink_id_t		linkid;
1896 
1897 	opterr = 0;
1898 	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
1899 		switch (option) {
1900 		case 't':
1901 			flags &= ~DLADM_OPT_PERSIST;
1902 			break;
1903 		case 'R':
1904 			altroot = optarg;
1905 			break;
1906 		default:
1907 			die_opterr(optopt, option, use);
1908 			break;
1909 		}
1910 	}
1911 
1912 	/* get key value or the aggregation name (required last argument) */
1913 	if (optind != (argc-1))
1914 		usage();
1915 
1916 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
1917 	if (status != DLADM_STATUS_OK)
1918 		goto done;
1919 
1920 	if (altroot != NULL)
1921 		altroot_cmd(altroot, argc, argv);
1922 
1923 	status = dladm_aggr_delete(handle, linkid, flags);
1924 done:
1925 	if (status != DLADM_STATUS_OK)
1926 		die_dlerr(status, "delete operation failed");
1927 }
1928 
1929 static void
1930 do_add_aggr(int argc, char *argv[], const char *use)
1931 {
1932 	int			option;
1933 	uint_t			n, ndev, nlink;
1934 	char			*altroot = NULL;
1935 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1936 	datalink_id_t		linkid;
1937 	dladm_status_t		status;
1938 	dladm_aggr_port_attr_db_t	port[MAXPORT];
1939 	char			*devs[MAXPORT];
1940 	char			*links[MAXPORT];
1941 
1942 	ndev = nlink = opterr = 0;
1943 	while ((option = getopt_long(argc, argv, ":d:l:R:tf", lopts,
1944 	    NULL)) != -1) {
1945 		switch (option) {
1946 		case 'd':
1947 			if (ndev + nlink >= MAXPORT)
1948 				die("too many ports specified");
1949 
1950 			devs[ndev++] = optarg;
1951 			break;
1952 		case 'l':
1953 			if (ndev + nlink >= MAXPORT)
1954 				die("too many ports specified");
1955 
1956 			links[nlink++] = optarg;
1957 			break;
1958 		case 't':
1959 			flags &= ~DLADM_OPT_PERSIST;
1960 			break;
1961 		case 'f':
1962 			flags |= DLADM_OPT_FORCE;
1963 			break;
1964 		case 'R':
1965 			altroot = optarg;
1966 			break;
1967 		default:
1968 			die_opterr(optopt, option, use);
1969 			break;
1970 		}
1971 	}
1972 
1973 	if (ndev + nlink == 0)
1974 		usage();
1975 
1976 	/* get key value or the aggregation name (required last argument) */
1977 	if (optind != (argc-1))
1978 		usage();
1979 
1980 	if ((status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid,
1981 	    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST))) !=
1982 	    DLADM_STATUS_OK) {
1983 		goto done;
1984 	}
1985 
1986 	if (altroot != NULL)
1987 		altroot_cmd(altroot, argc, argv);
1988 
1989 	for (n = 0; n < ndev; n++) {
1990 		if ((status = dladm_dev2linkid(handle, devs[n],
1991 		    &(port[n].lp_linkid))) != DLADM_STATUS_OK) {
1992 			die_dlerr(status, "invalid <dev> '%s'", devs[n]);
1993 		}
1994 	}
1995 
1996 	for (n = 0; n < nlink; n++) {
1997 		if ((status = dladm_name2info(handle, links[n],
1998 		    &port[n + ndev].lp_linkid, NULL, NULL, NULL)) !=
1999 		    DLADM_STATUS_OK) {
2000 			die_dlerr(status, "invalid <link> '%s'", links[n]);
2001 		}
2002 	}
2003 
2004 	status = dladm_aggr_add(handle, linkid, ndev + nlink, port, flags);
2005 done:
2006 	if (status != DLADM_STATUS_OK) {
2007 		/*
2008 		 * checking DLADM_STATUS_NOTSUP is a temporary workaround
2009 		 * and should be removed once 6399681 is fixed.
2010 		 */
2011 		if (status == DLADM_STATUS_NOTSUP) {
2012 			die("add operation failed: link capabilities don't "
2013 			    "match");
2014 		} else if (status == DLADM_STATUS_NONOTIF) {
2015 			die("not all links have link up/down detection; must "
2016 			    "use -f (see dladm(1M))");
2017 		} else {
2018 			die_dlerr(status, "add operation failed");
2019 		}
2020 	}
2021 }
2022 
2023 static void
2024 do_remove_aggr(int argc, char *argv[], const char *use)
2025 {
2026 	int				option;
2027 	dladm_aggr_port_attr_db_t	port[MAXPORT];
2028 	uint_t				n, ndev, nlink;
2029 	char				*devs[MAXPORT];
2030 	char				*links[MAXPORT];
2031 	char				*altroot = NULL;
2032 	uint32_t			flags;
2033 	datalink_id_t			linkid;
2034 	dladm_status_t			status;
2035 
2036 	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
2037 	ndev = nlink = opterr = 0;
2038 	while ((option = getopt_long(argc, argv, ":d:l:R:t",
2039 	    lopts, NULL)) != -1) {
2040 		switch (option) {
2041 		case 'd':
2042 			if (ndev + nlink >= MAXPORT)
2043 				die("too many ports specified");
2044 
2045 			devs[ndev++] = optarg;
2046 			break;
2047 		case 'l':
2048 			if (ndev + nlink >= MAXPORT)
2049 				die("too many ports specified");
2050 
2051 			links[nlink++] = optarg;
2052 			break;
2053 		case 't':
2054 			flags &= ~DLADM_OPT_PERSIST;
2055 			break;
2056 		case 'R':
2057 			altroot = optarg;
2058 			break;
2059 		default:
2060 			die_opterr(optopt, option, use);
2061 			break;
2062 		}
2063 	}
2064 
2065 	if (ndev + nlink == 0)
2066 		usage();
2067 
2068 	/* get key value or the aggregation name (required last argument) */
2069 	if (optind != (argc-1))
2070 		usage();
2071 
2072 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
2073 	if (status != DLADM_STATUS_OK)
2074 		goto done;
2075 
2076 	if (altroot != NULL)
2077 		altroot_cmd(altroot, argc, argv);
2078 
2079 	for (n = 0; n < ndev; n++) {
2080 		if ((status = dladm_dev2linkid(handle, devs[n],
2081 		    &(port[n].lp_linkid))) != DLADM_STATUS_OK) {
2082 			die_dlerr(status, "invalid <dev> '%s'", devs[n]);
2083 		}
2084 	}
2085 
2086 	for (n = 0; n < nlink; n++) {
2087 		if ((status = dladm_name2info(handle, links[n],
2088 		    &port[n + ndev].lp_linkid, NULL, NULL, NULL)) !=
2089 		    DLADM_STATUS_OK) {
2090 			die_dlerr(status, "invalid <link> '%s'", links[n]);
2091 		}
2092 	}
2093 
2094 	status = dladm_aggr_remove(handle, linkid, ndev + nlink, port, flags);
2095 done:
2096 	if (status != DLADM_STATUS_OK)
2097 		die_dlerr(status, "remove operation failed");
2098 }
2099 
2100 static void
2101 do_modify_aggr(int argc, char *argv[], const char *use)
2102 {
2103 	int			option;
2104 	uint32_t		policy = AGGR_POLICY_L4;
2105 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
2106 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
2107 	uint8_t			mac_addr[ETHERADDRL];
2108 	boolean_t		mac_addr_fixed = B_FALSE;
2109 	uint8_t			modify_mask = 0;
2110 	char			*altroot = NULL;
2111 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
2112 	datalink_id_t		linkid;
2113 	dladm_status_t		status;
2114 
2115 	opterr = 0;
2116 	while ((option = getopt_long(argc, argv, ":L:l:P:R:tu:T:", lopts,
2117 	    NULL)) != -1) {
2118 		switch (option) {
2119 		case 'P':
2120 			if (modify_mask & DLADM_AGGR_MODIFY_POLICY)
2121 				die_optdup(option);
2122 
2123 			modify_mask |= DLADM_AGGR_MODIFY_POLICY;
2124 
2125 			if (!dladm_aggr_str2policy(optarg, &policy))
2126 				die("invalid policy '%s'", optarg);
2127 			break;
2128 		case 'u':
2129 			if (modify_mask & DLADM_AGGR_MODIFY_MAC)
2130 				die_optdup(option);
2131 
2132 			modify_mask |= DLADM_AGGR_MODIFY_MAC;
2133 
2134 			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
2135 			    mac_addr))
2136 				die("invalid MAC address '%s'", optarg);
2137 			break;
2138 		case 'l':
2139 		case 'L':
2140 			if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE)
2141 				die_optdup(option);
2142 
2143 			modify_mask |= DLADM_AGGR_MODIFY_LACP_MODE;
2144 
2145 			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
2146 				die("invalid LACP mode '%s'", optarg);
2147 			break;
2148 		case 'T':
2149 			if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER)
2150 				die_optdup(option);
2151 
2152 			modify_mask |= DLADM_AGGR_MODIFY_LACP_TIMER;
2153 
2154 			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
2155 				die("invalid LACP timer value '%s'", optarg);
2156 			break;
2157 		case 't':
2158 			flags &= ~DLADM_OPT_PERSIST;
2159 			break;
2160 		case 'R':
2161 			altroot = optarg;
2162 			break;
2163 		default:
2164 			die_opterr(optopt, option, use);
2165 			break;
2166 		}
2167 	}
2168 
2169 	if (modify_mask == 0)
2170 		die("at least one of the -PulT options must be specified");
2171 
2172 	/* get key value or the aggregation name (required last argument) */
2173 	if (optind != (argc-1))
2174 		usage();
2175 
2176 	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
2177 	if (status != DLADM_STATUS_OK)
2178 		goto done;
2179 
2180 	if (altroot != NULL)
2181 		altroot_cmd(altroot, argc, argv);
2182 
2183 	status = dladm_aggr_modify(handle, linkid, modify_mask, policy,
2184 	    mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode, lacp_timer,
2185 	    flags);
2186 
2187 done:
2188 	if (status != DLADM_STATUS_OK)
2189 		die_dlerr(status, "modify operation failed");
2190 }
2191 
2192 /*ARGSUSED*/
2193 static void
2194 do_up_aggr(int argc, char *argv[], const char *use)
2195 {
2196 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2197 	dladm_status_t	status;
2198 
2199 	/*
2200 	 * get the key or the name of the aggregation (optional last argument)
2201 	 */
2202 	if (argc == 2) {
2203 		if ((status = i_dladm_aggr_get_linkid(NULL, argv[1], &linkid,
2204 		    DLADM_OPT_PERSIST)) != DLADM_STATUS_OK)
2205 			goto done;
2206 	} else if (argc > 2) {
2207 		usage();
2208 	}
2209 
2210 	status = dladm_aggr_up(handle, linkid);
2211 done:
2212 	if (status != DLADM_STATUS_OK) {
2213 		if (argc == 2) {
2214 			die_dlerr(status,
2215 			    "could not bring up aggregation '%s'", argv[1]);
2216 		} else {
2217 			die_dlerr(status, "could not bring aggregations up");
2218 		}
2219 	}
2220 }
2221 
2222 static void
2223 do_create_vlan(int argc, char *argv[], const char *use)
2224 {
2225 	char			*link = NULL;
2226 	char			drv[DLPI_LINKNAME_MAX];
2227 	uint_t			ppa;
2228 	datalink_id_t		linkid;
2229 	datalink_id_t		dev_linkid;
2230 	int			vid = 0;
2231 	int			option;
2232 	uint32_t		flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
2233 	char			*altroot = NULL;
2234 	char			vlan[MAXLINKNAMELEN];
2235 	char			propstr[DLADM_STRSIZE];
2236 	dladm_arg_list_t	*proplist = NULL;
2237 	dladm_status_t		status;
2238 
2239 	opterr = 0;
2240 	bzero(propstr, DLADM_STRSIZE);
2241 
2242 	while ((option = getopt_long(argc, argv, ":tfR:l:v:p:",
2243 	    lopts, NULL)) != -1) {
2244 		switch (option) {
2245 		case 'v':
2246 			if (vid != 0)
2247 				die_optdup(option);
2248 
2249 			if (!str2int(optarg, &vid) || vid < 1 || vid > 4094)
2250 				die("invalid VLAN identifier '%s'", optarg);
2251 
2252 			break;
2253 		case 'l':
2254 			if (link != NULL)
2255 				die_optdup(option);
2256 
2257 			link = optarg;
2258 			break;
2259 		case 't':
2260 			flags &= ~DLADM_OPT_PERSIST;
2261 			break;
2262 		case 'R':
2263 			altroot = optarg;
2264 			break;
2265 		case 'p':
2266 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
2267 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
2268 			    DLADM_STRSIZE)
2269 				die("property list too long '%s'", propstr);
2270 			break;
2271 		case 'f':
2272 			flags |= DLADM_OPT_FORCE;
2273 			break;
2274 		default:
2275 			die_opterr(optopt, option, use);
2276 			break;
2277 		}
2278 	}
2279 
2280 	/* get vlan name if there is any */
2281 	if ((vid == 0) || (link == NULL) || (argc - optind > 1))
2282 		usage();
2283 
2284 	if (optind == (argc - 1)) {
2285 		if (strlcpy(vlan, argv[optind], MAXLINKNAMELEN) >=
2286 		    MAXLINKNAMELEN) {
2287 			die("vlan name too long '%s'", argv[optind]);
2288 		}
2289 	} else {
2290 		if ((dlpi_parselink(link, drv, &ppa) != DLPI_SUCCESS) ||
2291 		    (ppa >= 1000) ||
2292 		    (dlpi_makelink(vlan, drv, vid * 1000 + ppa) !=
2293 		    DLPI_SUCCESS)) {
2294 			die("invalid link name '%s'", link);
2295 		}
2296 	}
2297 
2298 	if (altroot != NULL)
2299 		altroot_cmd(altroot, argc, argv);
2300 
2301 	if (dladm_name2info(handle, link, &dev_linkid, NULL, NULL, NULL) !=
2302 	    DLADM_STATUS_OK) {
2303 		die("invalid link name '%s'", link);
2304 	}
2305 
2306 	if (dladm_parse_link_props(propstr, &proplist, B_FALSE)
2307 	    != DLADM_STATUS_OK)
2308 		die("invalid vlan property");
2309 
2310 	status = dladm_vlan_create(handle, vlan, dev_linkid, vid, proplist,
2311 	    flags, &linkid);
2312 	switch (status) {
2313 	case DLADM_STATUS_OK:
2314 		break;
2315 
2316 	case DLADM_STATUS_NOTSUP:
2317 		die("VLAN over '%s' may require lowered MTU; must use -f (see "
2318 		    "dladm(1M))", link);
2319 		break;
2320 
2321 	case DLADM_STATUS_LINKBUSY:
2322 		die("VLAN over '%s' may not use default_tag ID", link);
2323 		break;
2324 
2325 	default:
2326 		die_dlerr(status, "create operation failed");
2327 	}
2328 }
2329 
2330 static void
2331 do_delete_vlan(int argc, char *argv[], const char *use)
2332 {
2333 	int		option;
2334 	uint32_t	flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
2335 	char		*altroot = NULL;
2336 	datalink_id_t	linkid;
2337 	dladm_status_t	status;
2338 
2339 	opterr = 0;
2340 	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
2341 		switch (option) {
2342 		case 't':
2343 			flags &= ~DLADM_OPT_PERSIST;
2344 			break;
2345 		case 'R':
2346 			altroot = optarg;
2347 			break;
2348 		default:
2349 			die_opterr(optopt, option, use);
2350 			break;
2351 		}
2352 	}
2353 
2354 	/* get VLAN link name (required last argument) */
2355 	if (optind != (argc - 1))
2356 		usage();
2357 
2358 	if (altroot != NULL)
2359 		altroot_cmd(altroot, argc, argv);
2360 
2361 	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
2362 	    NULL);
2363 	if (status != DLADM_STATUS_OK)
2364 		goto done;
2365 
2366 	status = dladm_vlan_delete(handle, linkid, flags);
2367 done:
2368 	if (status != DLADM_STATUS_OK)
2369 		die_dlerr(status, "delete operation failed");
2370 }
2371 
2372 /*ARGSUSED*/
2373 static void
2374 do_up_vlan(int argc, char *argv[], const char *use)
2375 {
2376 	do_up_vnic_common(argc, argv, use, B_TRUE);
2377 }
2378 
2379 static void
2380 do_rename_link(int argc, char *argv[], const char *use)
2381 {
2382 	int		option;
2383 	char		*link1, *link2;
2384 	char		*altroot = NULL;
2385 	dladm_status_t	status;
2386 
2387 	opterr = 0;
2388 	while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) {
2389 		switch (option) {
2390 		case 'R':
2391 			altroot = optarg;
2392 			break;
2393 		default:
2394 			die_opterr(optopt, option, use);
2395 			break;
2396 		}
2397 	}
2398 
2399 	/* get link1 and link2 name (required the last 2 arguments) */
2400 	if (optind != (argc - 2))
2401 		usage();
2402 
2403 	if (altroot != NULL)
2404 		altroot_cmd(altroot, argc, argv);
2405 
2406 	link1 = argv[optind++];
2407 	link2 = argv[optind];
2408 	if ((status = dladm_rename_link(handle, link1, link2)) !=
2409 	    DLADM_STATUS_OK)
2410 		die_dlerr(status, "rename operation failed");
2411 }
2412 
2413 /*ARGSUSED*/
2414 static void
2415 do_delete_phys(int argc, char *argv[], const char *use)
2416 {
2417 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2418 	dladm_status_t	status;
2419 
2420 	/* get link name (required the last argument) */
2421 	if (argc > 2)
2422 		usage();
2423 
2424 	if (argc == 2) {
2425 		if ((status = dladm_name2info(handle, argv[1], &linkid, NULL,
2426 		    NULL, NULL)) != DLADM_STATUS_OK)
2427 			die_dlerr(status, "cannot delete '%s'", argv[1]);
2428 	}
2429 
2430 	if ((status = dladm_phys_delete(handle, linkid)) != DLADM_STATUS_OK) {
2431 		if (argc == 2)
2432 			die_dlerr(status, "cannot delete '%s'", argv[1]);
2433 		else
2434 			die_dlerr(status, "delete operation failed");
2435 	}
2436 }
2437 
2438 /*ARGSUSED*/
2439 static int
2440 i_dladm_walk_linkmap(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2441 {
2442 	char			name[MAXLINKNAMELEN];
2443 	char			mediabuf[DLADM_STRSIZE];
2444 	char			classbuf[DLADM_STRSIZE];
2445 	datalink_class_t	class;
2446 	uint32_t		media;
2447 	uint32_t		flags;
2448 
2449 	if (dladm_datalink_id2info(dh, linkid, &flags, &class, &media, name,
2450 	    MAXLINKNAMELEN) == DLADM_STATUS_OK) {
2451 		(void) dladm_class2str(class, classbuf);
2452 		(void) dladm_media2str(media, mediabuf);
2453 		(void) printf("%-12s%8d  %-12s%-20s %6d\n", name,
2454 		    linkid, classbuf, mediabuf, flags);
2455 	}
2456 	return (DLADM_WALK_CONTINUE);
2457 }
2458 
2459 /*ARGSUSED*/
2460 static void
2461 do_show_linkmap(int argc, char *argv[], const char *use)
2462 {
2463 	if (argc != 1)
2464 		die("invalid arguments");
2465 
2466 	(void) printf("%-12s%8s  %-12s%-20s %6s\n", "NAME", "LINKID",
2467 	    "CLASS", "MEDIA", "FLAGS");
2468 
2469 	(void) dladm_walk_datalink_id(i_dladm_walk_linkmap, handle, NULL,
2470 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
2471 	    DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
2472 }
2473 
2474 /*
2475  * Delete inactive physical links.
2476  */
2477 /*ARGSUSED*/
2478 static int
2479 purge_phys(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2480 {
2481 	datalink_class_t	class;
2482 	uint32_t		flags;
2483 
2484 	if (dladm_datalink_id2info(dh, linkid, &flags, &class, NULL, NULL, 0)
2485 	    != DLADM_STATUS_OK) {
2486 		return (DLADM_WALK_CONTINUE);
2487 	}
2488 
2489 	if (class == DATALINK_CLASS_PHYS && !(flags & DLADM_OPT_ACTIVE))
2490 		(void) dladm_phys_delete(dh, linkid);
2491 
2492 	return (DLADM_WALK_CONTINUE);
2493 }
2494 
2495 /*ARGSUSED*/
2496 static void
2497 do_init_phys(int argc, char *argv[], const char *use)
2498 {
2499 	di_node_t	devtree;
2500 
2501 	if (argc > 1)
2502 		usage();
2503 
2504 	/*
2505 	 * Force all the devices to attach, therefore all the network physical
2506 	 * devices can be known to the dlmgmtd daemon.
2507 	 */
2508 	if ((devtree = di_init("/", DINFOFORCE | DINFOSUBTREE)) != DI_NODE_NIL)
2509 		di_fini(devtree);
2510 
2511 	(void) dladm_walk_datalink_id(purge_phys, handle, NULL,
2512 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
2513 }
2514 
2515 /*
2516  * Print the active topology information.
2517  */
2518 void
2519 print_link_topology(show_state_t *state, datalink_id_t linkid,
2520     datalink_class_t class, link_fields_buf_t *lbuf)
2521 {
2522 	uint32_t	flags = state->ls_flags;
2523 	dladm_status_t	status;
2524 	char		tmpbuf[MAXLINKNAMELEN];
2525 
2526 	lbuf->link_over[0] = '\0';
2527 	lbuf->link_bridge[0] = '\0';
2528 
2529 	switch (class) {
2530 	case DATALINK_CLASS_AGGR:
2531 	case DATALINK_CLASS_PHYS:
2532 	case DATALINK_CLASS_ETHERSTUB:
2533 		status = dladm_bridge_getlink(handle, linkid, lbuf->link_bridge,
2534 		    sizeof (lbuf->link_bridge));
2535 		if (status != DLADM_STATUS_OK &&
2536 		    status != DLADM_STATUS_NOTFOUND)
2537 			(void) strcpy(lbuf->link_bridge, "?");
2538 		break;
2539 	}
2540 
2541 	switch (class) {
2542 	case DATALINK_CLASS_VLAN: {
2543 		dladm_vlan_attr_t	vinfo;
2544 
2545 		if (dladm_vlan_info(handle, linkid, &vinfo, flags) !=
2546 		    DLADM_STATUS_OK) {
2547 			(void) strcpy(lbuf->link_over, "?");
2548 			break;
2549 		}
2550 		if (dladm_datalink_id2info(handle, vinfo.dv_linkid, NULL, NULL,
2551 		    NULL, lbuf->link_over, sizeof (lbuf->link_over)) !=
2552 		    DLADM_STATUS_OK)
2553 			(void) strcpy(lbuf->link_over, "?");
2554 		break;
2555 	}
2556 	case DATALINK_CLASS_AGGR: {
2557 		dladm_aggr_grp_attr_t	ginfo;
2558 		int			i;
2559 
2560 		if (dladm_aggr_info(handle, linkid, &ginfo, flags) !=
2561 		    DLADM_STATUS_OK || ginfo.lg_nports == 0) {
2562 			(void) strcpy(lbuf->link_over, "?");
2563 			break;
2564 		}
2565 		for (i = 0; i < ginfo.lg_nports; i++) {
2566 			if (dladm_datalink_id2info(handle,
2567 			    ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL,
2568 			    tmpbuf, sizeof (tmpbuf)) != DLADM_STATUS_OK) {
2569 				(void) strcpy(lbuf->link_over, "?");
2570 				break;
2571 			}
2572 			(void) strlcat(lbuf->link_over, tmpbuf,
2573 			    sizeof (lbuf->link_over));
2574 			if (i != (ginfo.lg_nports - 1)) {
2575 				(void) strlcat(lbuf->link_over, " ",
2576 				    sizeof (lbuf->link_over));
2577 			}
2578 		}
2579 		free(ginfo.lg_ports);
2580 		break;
2581 	}
2582 	case DATALINK_CLASS_VNIC: {
2583 		dladm_vnic_attr_t	vinfo;
2584 
2585 		if (dladm_vnic_info(handle, linkid, &vinfo, flags) !=
2586 		    DLADM_STATUS_OK) {
2587 			(void) strcpy(lbuf->link_over, "?");
2588 			break;
2589 		}
2590 		if (dladm_datalink_id2info(handle, vinfo.va_link_id, NULL, NULL,
2591 		    NULL, lbuf->link_over, sizeof (lbuf->link_over)) !=
2592 		    DLADM_STATUS_OK)
2593 			(void) strcpy(lbuf->link_over, "?");
2594 		break;
2595 	}
2596 	case DATALINK_CLASS_BRIDGE: {
2597 		datalink_id_t *dlp;
2598 		uint_t i, nports;
2599 
2600 		if (dladm_datalink_id2info(handle, linkid, NULL, NULL,
2601 		    NULL, tmpbuf, sizeof (tmpbuf)) != DLADM_STATUS_OK) {
2602 			(void) strcpy(lbuf->link_over, "?");
2603 			break;
2604 		}
2605 		if (tmpbuf[0] != '\0')
2606 			tmpbuf[strlen(tmpbuf) - 1] = '\0';
2607 		dlp = dladm_bridge_get_portlist(tmpbuf, &nports);
2608 		if (dlp == NULL) {
2609 			(void) strcpy(lbuf->link_over, "?");
2610 			break;
2611 		}
2612 		for (i = 0; i < nports; i++) {
2613 			if (dladm_datalink_id2info(handle, dlp[i], NULL,
2614 			    NULL, NULL, tmpbuf, sizeof (tmpbuf)) !=
2615 			    DLADM_STATUS_OK) {
2616 				(void) strcpy(lbuf->link_over, "?");
2617 				break;
2618 			}
2619 			(void) strlcat(lbuf->link_over, tmpbuf,
2620 			    sizeof (lbuf->link_over));
2621 			if (i != nports - 1) {
2622 				(void) strlcat(lbuf->link_over, " ",
2623 				    sizeof (lbuf->link_over));
2624 			}
2625 		}
2626 		dladm_bridge_free_portlist(dlp);
2627 		break;
2628 	}
2629 
2630 	case DATALINK_CLASS_SIMNET: {
2631 		dladm_simnet_attr_t	slinfo;
2632 
2633 		if (dladm_simnet_info(handle, linkid, &slinfo, flags) !=
2634 		    DLADM_STATUS_OK) {
2635 			(void) strcpy(lbuf->link_over, "?");
2636 			break;
2637 		}
2638 		if (slinfo.sna_peer_link_id != DATALINK_INVALID_LINKID) {
2639 			if (dladm_datalink_id2info(handle,
2640 			    slinfo.sna_peer_link_id, NULL, NULL, NULL,
2641 			    lbuf->link_over, sizeof (lbuf->link_over)) !=
2642 			    DLADM_STATUS_OK)
2643 				(void) strcpy(lbuf->link_over, "?");
2644 		}
2645 		break;
2646 	}
2647 	}
2648 }
2649 
2650 static dladm_status_t
2651 print_link(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *lbuf)
2652 {
2653 	char			link[MAXLINKNAMELEN];
2654 	datalink_class_t	class;
2655 	uint_t			mtu;
2656 	uint32_t		flags;
2657 	dladm_status_t		status;
2658 
2659 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
2660 	    NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
2661 		goto done;
2662 	}
2663 
2664 	if (!(state->ls_flags & flags)) {
2665 		status = DLADM_STATUS_NOTFOUND;
2666 		goto done;
2667 	}
2668 
2669 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2670 		dladm_attr_t	dlattr;
2671 
2672 		if (class == DATALINK_CLASS_PHYS) {
2673 			dladm_phys_attr_t	dpa;
2674 			dlpi_handle_t		dh;
2675 			dlpi_info_t		dlinfo;
2676 
2677 			if ((status = dladm_phys_info(handle, linkid, &dpa,
2678 			    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
2679 				goto done;
2680 			}
2681 
2682 			if (!dpa.dp_novanity)
2683 				goto link_mtu;
2684 
2685 			/*
2686 			 * This is a physical link that does not have
2687 			 * vanity naming support.
2688 			 */
2689 			if (dlpi_open(dpa.dp_dev, &dh, DLPI_DEVONLY) !=
2690 			    DLPI_SUCCESS) {
2691 				status = DLADM_STATUS_NOTFOUND;
2692 				goto done;
2693 			}
2694 
2695 			if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
2696 				dlpi_close(dh);
2697 				status = DLADM_STATUS_BADARG;
2698 				goto done;
2699 			}
2700 
2701 			dlpi_close(dh);
2702 			mtu = dlinfo.di_max_sdu;
2703 		} else {
2704 link_mtu:
2705 			status = dladm_info(handle, linkid, &dlattr);
2706 			if (status != DLADM_STATUS_OK)
2707 				goto done;
2708 			mtu = dlattr.da_max_sdu;
2709 		}
2710 	}
2711 
2712 	(void) snprintf(lbuf->link_name, sizeof (lbuf->link_name),
2713 	    "%s", link);
2714 	(void) dladm_class2str(class, lbuf->link_class);
2715 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2716 		(void) snprintf(lbuf->link_mtu, sizeof (lbuf->link_mtu),
2717 		    "%u", mtu);
2718 		(void) get_linkstate(link, B_TRUE, lbuf->link_state);
2719 	}
2720 
2721 	print_link_topology(state, linkid, class, lbuf);
2722 done:
2723 	return (status);
2724 }
2725 
2726 /* ARGSUSED */
2727 static int
2728 show_link(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2729 {
2730 	show_state_t		*state = (show_state_t *)arg;
2731 	dladm_status_t		status;
2732 	link_fields_buf_t	lbuf;
2733 
2734 	/*
2735 	 * first get all the link attributes into lbuf;
2736 	 */
2737 	bzero(&lbuf, sizeof (link_fields_buf_t));
2738 	if ((status = print_link(state, linkid, &lbuf)) == DLADM_STATUS_OK)
2739 		ofmt_print(state->ls_ofmt, &lbuf);
2740 	state->ls_status = status;
2741 	return (DLADM_WALK_CONTINUE);
2742 }
2743 
2744 static boolean_t
2745 print_link_stats_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
2746 {
2747 	link_args_t *largs = ofarg->ofmt_cbarg;
2748 	pktsum_t *diff_stats = largs->link_s_psum;
2749 
2750 	switch (ofarg->ofmt_id) {
2751 	case LINK_S_LINK:
2752 		(void) snprintf(buf, bufsize, "%s", largs->link_s_link);
2753 		break;
2754 	case LINK_S_IPKTS:
2755 		(void) snprintf(buf, bufsize, "%llu", diff_stats->ipackets);
2756 		break;
2757 	case LINK_S_RBYTES:
2758 		(void) snprintf(buf, bufsize, "%llu", diff_stats->rbytes);
2759 		break;
2760 	case LINK_S_IERRORS:
2761 		(void) snprintf(buf, bufsize, "%u", diff_stats->ierrors);
2762 		break;
2763 	case LINK_S_OPKTS:
2764 		(void) snprintf(buf, bufsize, "%llu", diff_stats->opackets);
2765 		break;
2766 	case LINK_S_OBYTES:
2767 		(void) snprintf(buf, bufsize, "%llu", diff_stats->obytes);
2768 		break;
2769 	case LINK_S_OERRORS:
2770 		(void) snprintf(buf, bufsize, "%u", diff_stats->oerrors);
2771 		break;
2772 	default:
2773 		die("invalid input");
2774 		break;
2775 	}
2776 	return (B_TRUE);
2777 }
2778 
2779 static int
2780 show_link_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2781 {
2782 	char			link[DLPI_LINKNAME_MAX];
2783 	datalink_class_t	class;
2784 	show_state_t		*state = arg;
2785 	pktsum_t		stats, diff_stats;
2786 	dladm_phys_attr_t	dpa;
2787 	link_args_t		largs;
2788 
2789 	if (state->ls_firstonly) {
2790 		if (state->ls_donefirst)
2791 			return (DLADM_WALK_CONTINUE);
2792 		state->ls_donefirst = B_TRUE;
2793 	} else {
2794 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
2795 	}
2796 
2797 	if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, link,
2798 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2799 		return (DLADM_WALK_CONTINUE);
2800 	}
2801 
2802 	if (class == DATALINK_CLASS_PHYS) {
2803 		if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
2804 		    DLADM_STATUS_OK) {
2805 			return (DLADM_WALK_CONTINUE);
2806 		}
2807 		if (dpa.dp_novanity)
2808 			get_mac_stats(dpa.dp_dev, &stats);
2809 		else
2810 			get_link_stats(link, &stats);
2811 	} else {
2812 		get_link_stats(link, &stats);
2813 	}
2814 	dladm_stats_diff(&diff_stats, &stats, &state->ls_prevstats);
2815 
2816 	largs.link_s_link = link;
2817 	largs.link_s_psum = &diff_stats;
2818 	ofmt_print(state->ls_ofmt, &largs);
2819 
2820 	state->ls_prevstats = stats;
2821 	return (DLADM_WALK_CONTINUE);
2822 }
2823 
2824 
2825 static dladm_status_t
2826 print_aggr_info(show_grp_state_t *state, const char *link,
2827     dladm_aggr_grp_attr_t *ginfop)
2828 {
2829 	char			addr_str[ETHERADDRL * 3];
2830 	laggr_fields_buf_t	lbuf;
2831 
2832 	(void) snprintf(lbuf.laggr_name, sizeof (lbuf.laggr_name),
2833 	    "%s", link);
2834 
2835 	(void) dladm_aggr_policy2str(ginfop->lg_policy,
2836 	    lbuf.laggr_policy);
2837 
2838 	if (ginfop->lg_mac_fixed) {
2839 		(void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str);
2840 		(void) snprintf(lbuf.laggr_addrpolicy,
2841 		    sizeof (lbuf.laggr_addrpolicy), "fixed (%s)", addr_str);
2842 	} else {
2843 		(void) snprintf(lbuf.laggr_addrpolicy,
2844 		    sizeof (lbuf.laggr_addrpolicy), "auto");
2845 	}
2846 
2847 	(void) dladm_aggr_lacpmode2str(ginfop->lg_lacp_mode,
2848 	    lbuf.laggr_lacpactivity);
2849 	(void) dladm_aggr_lacptimer2str(ginfop->lg_lacp_timer,
2850 	    lbuf.laggr_lacptimer);
2851 	(void) snprintf(lbuf.laggr_flags, sizeof (lbuf.laggr_flags), "%c----",
2852 	    ginfop->lg_force ? 'f' : '-');
2853 
2854 	ofmt_print(state->gs_ofmt, &lbuf);
2855 
2856 	return (DLADM_STATUS_OK);
2857 }
2858 
2859 static boolean_t
2860 print_xaggr_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
2861 {
2862 	const laggr_args_t 	*l = ofarg->ofmt_cbarg;
2863 	boolean_t		is_port = (l->laggr_lport >= 0);
2864 	char			tmpbuf[DLADM_STRSIZE];
2865 	const char		*objname;
2866 	dladm_aggr_port_attr_t	*portp;
2867 	dladm_phys_attr_t	dpa;
2868 
2869 	if (is_port) {
2870 		portp = &(l->laggr_ginfop->lg_ports[l->laggr_lport]);
2871 		if (dladm_phys_info(handle, portp->lp_linkid, &dpa,
2872 		    DLADM_OPT_ACTIVE) != DLADM_STATUS_OK)
2873 			objname = "?";
2874 		else
2875 			objname = dpa.dp_dev;
2876 	} else {
2877 		objname = l->laggr_link;
2878 	}
2879 
2880 	switch (ofarg->ofmt_id) {
2881 	case AGGR_X_LINK:
2882 		(void) snprintf(buf, bufsize, "%s",
2883 		    (is_port && !l->laggr_parsable ? " " : l->laggr_link));
2884 		break;
2885 	case AGGR_X_PORT:
2886 		if (is_port) {
2887 			if (dladm_datalink_id2info(handle, portp->lp_linkid,
2888 			    NULL, NULL, NULL, buf, bufsize) != DLADM_STATUS_OK)
2889 				(void) sprintf(buf, "?");
2890 		}
2891 		break;
2892 
2893 	case AGGR_X_SPEED:
2894 		(void) snprintf(buf, bufsize, "%uMb",
2895 		    (uint_t)((get_ifspeed(objname, !is_port)) / 1000000ull));
2896 		break;
2897 
2898 	case AGGR_X_DUPLEX:
2899 		(void) get_linkduplex(objname, !is_port, tmpbuf);
2900 		(void) strlcpy(buf, tmpbuf, bufsize);
2901 		break;
2902 
2903 	case AGGR_X_STATE:
2904 		(void) get_linkstate(objname, !is_port, tmpbuf);
2905 		(void) strlcpy(buf, tmpbuf, bufsize);
2906 		break;
2907 	case AGGR_X_ADDRESS:
2908 		(void) dladm_aggr_macaddr2str(
2909 		    (is_port ? portp->lp_mac : l->laggr_ginfop->lg_mac),
2910 		    tmpbuf);
2911 		(void) strlcpy(buf, tmpbuf, bufsize);
2912 		break;
2913 	case AGGR_X_PORTSTATE:
2914 		if (is_port) {
2915 			(void) dladm_aggr_portstate2str(portp->lp_state,
2916 			    tmpbuf);
2917 			(void) strlcpy(buf, tmpbuf, bufsize);
2918 		}
2919 		break;
2920 	}
2921 err:
2922 	*(l->laggr_status) = DLADM_STATUS_OK;
2923 	return (B_TRUE);
2924 }
2925 
2926 static dladm_status_t
2927 print_aggr_extended(show_grp_state_t *state, const char *link,
2928     dladm_aggr_grp_attr_t *ginfop)
2929 {
2930 	int			i;
2931 	dladm_status_t		status;
2932 	laggr_args_t		largs;
2933 
2934 	largs.laggr_lport = -1;
2935 	largs.laggr_link = link;
2936 	largs.laggr_ginfop = ginfop;
2937 	largs.laggr_status = &status;
2938 	largs.laggr_parsable = state->gs_parsable;
2939 
2940 	ofmt_print(state->gs_ofmt, &largs);
2941 
2942 	if (status != DLADM_STATUS_OK)
2943 		goto done;
2944 
2945 	for (i = 0; i < ginfop->lg_nports; i++) {
2946 		largs.laggr_lport = i;
2947 		ofmt_print(state->gs_ofmt, &largs);
2948 		if (status != DLADM_STATUS_OK)
2949 			goto done;
2950 	}
2951 
2952 	status = DLADM_STATUS_OK;
2953 done:
2954 	return (status);
2955 }
2956 
2957 static boolean_t
2958 print_lacp_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
2959 {
2960 	const laggr_args_t	*l = ofarg->ofmt_cbarg;
2961 	int			portnum;
2962 	boolean_t		is_port = (l->laggr_lport >= 0);
2963 	dladm_aggr_port_attr_t	*portp;
2964 	aggr_lacp_state_t	*lstate;
2965 
2966 	if (!is_port)
2967 		return (B_FALSE); /* cannot happen! */
2968 
2969 	portnum = l->laggr_lport;
2970 	portp = &(l->laggr_ginfop->lg_ports[portnum]);
2971 	lstate = &(portp->lp_lacp_state);
2972 
2973 	switch (ofarg->ofmt_id) {
2974 	case AGGR_L_LINK:
2975 		(void) snprintf(buf, bufsize, "%s",
2976 		    (portnum > 0 ? "" : l->laggr_link));
2977 		break;
2978 
2979 	case AGGR_L_PORT:
2980 		if (dladm_datalink_id2info(handle, portp->lp_linkid, NULL, NULL,
2981 		    NULL, buf, bufsize) != DLADM_STATUS_OK)
2982 			(void) sprintf(buf, "?");
2983 		break;
2984 
2985 	case AGGR_L_AGGREGATABLE:
2986 		(void) snprintf(buf, bufsize, "%s",
2987 		    (lstate->bit.aggregation ? "yes" : "no"));
2988 		break;
2989 
2990 	case AGGR_L_SYNC:
2991 		(void) snprintf(buf, bufsize, "%s",
2992 		    (lstate->bit.sync ? "yes" : "no"));
2993 		break;
2994 
2995 	case AGGR_L_COLL:
2996 		(void) snprintf(buf, bufsize, "%s",
2997 		    (lstate->bit.collecting ? "yes" : "no"));
2998 		break;
2999 
3000 	case AGGR_L_DIST:
3001 		(void) snprintf(buf, bufsize, "%s",
3002 		    (lstate->bit.distributing ? "yes" : "no"));
3003 		break;
3004 
3005 	case AGGR_L_DEFAULTED:
3006 		(void) snprintf(buf, bufsize, "%s",
3007 		    (lstate->bit.defaulted ? "yes" : "no"));
3008 		break;
3009 
3010 	case AGGR_L_EXPIRED:
3011 		(void) snprintf(buf, bufsize, "%s",
3012 		    (lstate->bit.expired ? "yes" : "no"));
3013 		break;
3014 	}
3015 
3016 	*(l->laggr_status) = DLADM_STATUS_OK;
3017 	return (B_TRUE);
3018 }
3019 
3020 static dladm_status_t
3021 print_aggr_lacp(show_grp_state_t *state, const char *link,
3022     dladm_aggr_grp_attr_t *ginfop)
3023 {
3024 	int		i;
3025 	dladm_status_t	status;
3026 	laggr_args_t	largs;
3027 
3028 	largs.laggr_link = link;
3029 	largs.laggr_ginfop = ginfop;
3030 	largs.laggr_status = &status;
3031 
3032 	for (i = 0; i < ginfop->lg_nports; i++) {
3033 		largs.laggr_lport = i;
3034 		ofmt_print(state->gs_ofmt, &largs);
3035 		if (status != DLADM_STATUS_OK)
3036 			goto done;
3037 	}
3038 
3039 	status = DLADM_STATUS_OK;
3040 done:
3041 	return (status);
3042 }
3043 
3044 static boolean_t
3045 print_aggr_stats_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
3046 {
3047 	const laggr_args_t	*l = ofarg->ofmt_cbarg;
3048 	int 			portnum;
3049 	boolean_t		is_port = (l->laggr_lport >= 0);
3050 	dladm_aggr_port_attr_t	*portp;
3051 	dladm_status_t		*stat, status;
3052 	pktsum_t		*diff_stats;
3053 
3054 	stat = l->laggr_status;
3055 	*stat = DLADM_STATUS_OK;
3056 
3057 	if (is_port) {
3058 		portnum = l->laggr_lport;
3059 		portp = &(l->laggr_ginfop->lg_ports[portnum]);
3060 
3061 		if ((status = dladm_datalink_id2info(handle,
3062 		    portp->lp_linkid, NULL, NULL, NULL, buf, bufsize)) !=
3063 		    DLADM_STATUS_OK) {
3064 			goto err;
3065 		}
3066 		diff_stats = l->laggr_diffstats;
3067 	}
3068 
3069 	switch (ofarg->ofmt_id) {
3070 	case AGGR_S_LINK:
3071 		(void) snprintf(buf, bufsize, "%s",
3072 		    (is_port ? "" : l->laggr_link));
3073 		break;
3074 	case AGGR_S_PORT:
3075 		/*
3076 		 * if (is_port), buf has port name. Otherwise we print
3077 		 * STR_UNDEF_VAL
3078 		 */
3079 		break;
3080 
3081 	case AGGR_S_IPKTS:
3082 		if (is_port) {
3083 			(void) snprintf(buf, bufsize, "%llu",
3084 			    diff_stats->ipackets);
3085 		} else {
3086 			(void) snprintf(buf, bufsize, "%llu",
3087 			    l->laggr_pktsumtot->ipackets);
3088 		}
3089 		break;
3090 
3091 	case AGGR_S_RBYTES:
3092 		if (is_port) {
3093 			(void) snprintf(buf, bufsize, "%llu",
3094 			    diff_stats->rbytes);
3095 		} else {
3096 			(void) snprintf(buf, bufsize, "%llu",
3097 			    l->laggr_pktsumtot->rbytes);
3098 		}
3099 		break;
3100 
3101 	case AGGR_S_OPKTS:
3102 		if (is_port) {
3103 			(void) snprintf(buf, bufsize, "%llu",
3104 			    diff_stats->opackets);
3105 		} else {
3106 			(void) snprintf(buf, bufsize, "%llu",
3107 			    l->laggr_pktsumtot->opackets);
3108 		}
3109 		break;
3110 	case AGGR_S_OBYTES:
3111 		if (is_port) {
3112 			(void) snprintf(buf, bufsize, "%llu",
3113 			    diff_stats->obytes);
3114 		} else {
3115 			(void) snprintf(buf, bufsize, "%llu",
3116 			    l->laggr_pktsumtot->obytes);
3117 		}
3118 		break;
3119 
3120 	case AGGR_S_IPKTDIST:
3121 		if (is_port) {
3122 			(void) snprintf(buf, bufsize, "%-6.1f",
3123 			    (double)diff_stats->ipackets/
3124 			    (double)l->laggr_pktsumtot->ipackets * 100);
3125 		}
3126 		break;
3127 	case AGGR_S_OPKTDIST:
3128 		if (is_port) {
3129 			(void) snprintf(buf, bufsize, "%-6.1f",
3130 			    (double)diff_stats->opackets/
3131 			    (double)l->laggr_pktsumtot->opackets * 100);
3132 		}
3133 		break;
3134 	}
3135 	return (B_TRUE);
3136 
3137 err:
3138 	*stat = status;
3139 	return (B_TRUE);
3140 }
3141 
3142 static dladm_status_t
3143 print_aggr_stats(show_grp_state_t *state, const char *link,
3144     dladm_aggr_grp_attr_t *ginfop)
3145 {
3146 	dladm_phys_attr_t	dpa;
3147 	dladm_aggr_port_attr_t	*portp;
3148 	pktsum_t		pktsumtot, *port_stat;
3149 	dladm_status_t		status;
3150 	int			i;
3151 	laggr_args_t		largs;
3152 
3153 	/* sum the ports statistics */
3154 	bzero(&pktsumtot, sizeof (pktsumtot));
3155 
3156 	/* Allocate memory to keep stats of each port */
3157 	port_stat = malloc(ginfop->lg_nports * sizeof (pktsum_t));
3158 	if (port_stat == NULL) {
3159 		/* Bail out; no memory */
3160 		return (DLADM_STATUS_NOMEM);
3161 	}
3162 
3163 
3164 	for (i = 0; i < ginfop->lg_nports; i++) {
3165 
3166 		portp = &(ginfop->lg_ports[i]);
3167 		if ((status = dladm_phys_info(handle, portp->lp_linkid, &dpa,
3168 		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
3169 			goto done;
3170 		}
3171 
3172 		get_mac_stats(dpa.dp_dev, &port_stat[i]);
3173 
3174 		/*
3175 		 * Let's re-use gs_prevstats[] to store the difference of the
3176 		 * counters since last use. We will store the new stats from
3177 		 * port_stat[] once we have the stats displayed.
3178 		 */
3179 
3180 		dladm_stats_diff(&state->gs_prevstats[i], &port_stat[i],
3181 		    &state->gs_prevstats[i]);
3182 		dladm_stats_total(&pktsumtot, &pktsumtot,
3183 		    &state->gs_prevstats[i]);
3184 	}
3185 
3186 	largs.laggr_lport = -1;
3187 	largs.laggr_link = link;
3188 	largs.laggr_ginfop = ginfop;
3189 	largs.laggr_status = &status;
3190 	largs.laggr_pktsumtot = &pktsumtot;
3191 
3192 	ofmt_print(state->gs_ofmt, &largs);
3193 
3194 	if (status != DLADM_STATUS_OK)
3195 		goto done;
3196 
3197 	for (i = 0; i < ginfop->lg_nports; i++) {
3198 		largs.laggr_lport = i;
3199 		largs.laggr_diffstats = &state->gs_prevstats[i];
3200 		ofmt_print(state->gs_ofmt, &largs);
3201 		if (status != DLADM_STATUS_OK)
3202 			goto done;
3203 	}
3204 
3205 	status = DLADM_STATUS_OK;
3206 	for (i = 0; i < ginfop->lg_nports; i++)
3207 		state->gs_prevstats[i] = port_stat[i];
3208 
3209 done:
3210 	free(port_stat);
3211 	return (status);
3212 }
3213 
3214 static dladm_status_t
3215 print_aggr(show_grp_state_t *state, datalink_id_t linkid)
3216 {
3217 	char			link[MAXLINKNAMELEN];
3218 	dladm_aggr_grp_attr_t	ginfo;
3219 	uint32_t		flags;
3220 	dladm_status_t		status;
3221 
3222 	bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
3223 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
3224 	    NULL, link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
3225 		return (status);
3226 	}
3227 
3228 	if (!(state->gs_flags & flags))
3229 		return (DLADM_STATUS_NOTFOUND);
3230 
3231 	status = dladm_aggr_info(handle, linkid, &ginfo, state->gs_flags);
3232 	if (status != DLADM_STATUS_OK)
3233 		return (status);
3234 
3235 	if (state->gs_lacp)
3236 		status = print_aggr_lacp(state, link, &ginfo);
3237 	else if (state->gs_extended)
3238 		status = print_aggr_extended(state, link, &ginfo);
3239 	else if (state->gs_stats)
3240 		status = print_aggr_stats(state, link, &ginfo);
3241 	else
3242 		status = print_aggr_info(state, link, &ginfo);
3243 
3244 done:
3245 	free(ginfo.lg_ports);
3246 	return (status);
3247 }
3248 
3249 /* ARGSUSED */
3250 static int
3251 show_aggr(dladm_handle_t dh, datalink_id_t linkid, void *arg)
3252 {
3253 	show_grp_state_t	*state = arg;
3254 
3255 	state->gs_status = print_aggr(state, linkid);
3256 	return (DLADM_WALK_CONTINUE);
3257 }
3258 
3259 static void
3260 do_show_link(int argc, char *argv[], const char *use)
3261 {
3262 	int		option;
3263 	boolean_t	s_arg = B_FALSE;
3264 	boolean_t	S_arg = B_FALSE;
3265 	boolean_t	i_arg = B_FALSE;
3266 	uint32_t	flags = DLADM_OPT_ACTIVE;
3267 	boolean_t	p_arg = B_FALSE;
3268 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
3269 	char		linkname[MAXLINKNAMELEN];
3270 	uint32_t	interval = 0;
3271 	show_state_t	state;
3272 	dladm_status_t	status;
3273 	boolean_t	o_arg = B_FALSE;
3274 	char		*fields_str = NULL;
3275 	char		*all_active_fields = "link,class,mtu,state,bridge,over";
3276 	char		*all_inactive_fields = "link,class,bridge,over";
3277 	char		*allstat_fields =
3278 	    "link,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
3279 	ofmt_handle_t	ofmt;
3280 	ofmt_status_t	oferr;
3281 	uint_t		ofmtflags = 0;
3282 
3283 	bzero(&state, sizeof (state));
3284 
3285 	opterr = 0;
3286 	while ((option = getopt_long(argc, argv, ":pPsSi:o:",
3287 	    show_lopts, NULL)) != -1) {
3288 		switch (option) {
3289 		case 'p':
3290 			if (p_arg)
3291 				die_optdup(option);
3292 
3293 			p_arg = B_TRUE;
3294 			break;
3295 		case 's':
3296 			if (s_arg)
3297 				die_optdup(option);
3298 
3299 			s_arg = B_TRUE;
3300 			break;
3301 		case 'P':
3302 			if (flags != DLADM_OPT_ACTIVE)
3303 				die_optdup(option);
3304 
3305 			flags = DLADM_OPT_PERSIST;
3306 			break;
3307 		case 'S':
3308 			if (S_arg)
3309 				die_optdup(option);
3310 
3311 			S_arg = B_TRUE;
3312 			break;
3313 		case 'o':
3314 			o_arg = B_TRUE;
3315 			fields_str = optarg;
3316 			break;
3317 		case 'i':
3318 			if (i_arg)
3319 				die_optdup(option);
3320 
3321 			i_arg = B_TRUE;
3322 			if (!dladm_str2interval(optarg, &interval))
3323 				die("invalid interval value '%s'", optarg);
3324 			break;
3325 		default:
3326 			die_opterr(optopt, option, use);
3327 			break;
3328 		}
3329 	}
3330 
3331 	if (i_arg && !(s_arg || S_arg))
3332 		die("the option -i can be used only with -s or -S");
3333 
3334 	if (s_arg && S_arg)
3335 		die("the -s option cannot be used with -S");
3336 
3337 	if (s_arg && flags != DLADM_OPT_ACTIVE)
3338 		die("the option -P cannot be used with -s");
3339 
3340 	if (S_arg && (p_arg || flags != DLADM_OPT_ACTIVE))
3341 		die("the option -%c cannot be used with -S", p_arg ? 'p' : 'P');
3342 
3343 	/* get link name (optional last argument) */
3344 	if (optind == (argc-1)) {
3345 		uint32_t	f;
3346 
3347 		if (strlcpy(linkname, argv[optind], MAXLINKNAMELEN) >=
3348 		    MAXLINKNAMELEN)
3349 			die("link name too long");
3350 		if ((status = dladm_name2info(handle, linkname, &linkid, &f,
3351 		    NULL, NULL)) != DLADM_STATUS_OK) {
3352 			die_dlerr(status, "link %s is not valid", linkname);
3353 		}
3354 
3355 		if (!(f & flags)) {
3356 			die_dlerr(DLADM_STATUS_BADARG, "link %s is %s",
3357 			    argv[optind], flags == DLADM_OPT_PERSIST ?
3358 			    "a temporary link" : "temporarily removed");
3359 		}
3360 	} else if (optind != argc) {
3361 		usage();
3362 	}
3363 
3364 	if (p_arg && !o_arg)
3365 		die("-p requires -o");
3366 
3367 	if (S_arg) {
3368 		dladm_continuous(handle, linkid, NULL, interval, LINK_REPORT);
3369 		return;
3370 	}
3371 
3372 	if (p_arg && strcasecmp(fields_str, "all") == 0)
3373 		die("\"-o all\" is invalid with -p");
3374 
3375 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
3376 		if (s_arg)
3377 			fields_str = allstat_fields;
3378 		else if (flags & DLADM_OPT_ACTIVE)
3379 			fields_str = all_active_fields;
3380 		else
3381 			fields_str = all_inactive_fields;
3382 	}
3383 
3384 	state.ls_parsable = p_arg;
3385 	state.ls_flags = flags;
3386 	state.ls_donefirst = B_FALSE;
3387 
3388 	if (s_arg) {
3389 		link_stats(linkid, interval, fields_str, &state);
3390 		return;
3391 	}
3392 	if (state.ls_parsable)
3393 		ofmtflags |= OFMT_PARSABLE;
3394 	oferr = ofmt_open(fields_str, link_fields, ofmtflags, 0, &ofmt);
3395 	dladm_ofmt_check(oferr, state.ls_parsable, ofmt);
3396 	state.ls_ofmt = ofmt;
3397 
3398 	if (linkid == DATALINK_ALL_LINKID) {
3399 		(void) dladm_walk_datalink_id(show_link, handle, &state,
3400 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
3401 	} else {
3402 		(void) show_link(handle, linkid, &state);
3403 		if (state.ls_status != DLADM_STATUS_OK) {
3404 			die_dlerr(state.ls_status, "failed to show link %s",
3405 			    argv[optind]);
3406 		}
3407 	}
3408 	ofmt_close(ofmt);
3409 }
3410 
3411 static void
3412 do_show_aggr(int argc, char *argv[], const char *use)
3413 {
3414 	boolean_t		L_arg = B_FALSE;
3415 	boolean_t		s_arg = B_FALSE;
3416 	boolean_t		i_arg = B_FALSE;
3417 	boolean_t		p_arg = B_FALSE;
3418 	boolean_t		x_arg = B_FALSE;
3419 	show_grp_state_t	state;
3420 	uint32_t		flags = DLADM_OPT_ACTIVE;
3421 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3422 	int			option;
3423 	uint32_t		interval = 0;
3424 	int			key;
3425 	dladm_status_t		status;
3426 	boolean_t		o_arg = B_FALSE;
3427 	char			*fields_str = NULL;
3428 	char			*all_fields =
3429 	    "link,policy,addrpolicy,lacpactivity,lacptimer,flags";
3430 	char			*all_lacp_fields =
3431 	    "link,port,aggregatable,sync,coll,dist,defaulted,expired";
3432 	char			*all_stats_fields =
3433 	    "link,port,ipackets,rbytes,opackets,obytes,ipktdist,opktdist";
3434 	char			*all_extended_fields =
3435 	    "link,port,speed,duplex,state,address,portstate";
3436 	const ofmt_field_t	*pf;
3437 	ofmt_handle_t		ofmt;
3438 	ofmt_status_t		oferr;
3439 	uint_t			ofmtflags = 0;
3440 
3441 	opterr = 0;
3442 	while ((option = getopt_long(argc, argv, ":LpPxsi:o:",
3443 	    show_lopts, NULL)) != -1) {
3444 		switch (option) {
3445 		case 'L':
3446 			if (L_arg)
3447 				die_optdup(option);
3448 
3449 			L_arg = B_TRUE;
3450 			break;
3451 		case 'p':
3452 			if (p_arg)
3453 				die_optdup(option);
3454 
3455 			p_arg = B_TRUE;
3456 			break;
3457 		case 'x':
3458 			if (x_arg)
3459 				die_optdup(option);
3460 
3461 			x_arg = B_TRUE;
3462 			break;
3463 		case 'P':
3464 			if (flags != DLADM_OPT_ACTIVE)
3465 				die_optdup(option);
3466 
3467 			flags = DLADM_OPT_PERSIST;
3468 			break;
3469 		case 's':
3470 			if (s_arg)
3471 				die_optdup(option);
3472 
3473 			s_arg = B_TRUE;
3474 			break;
3475 		case 'o':
3476 			o_arg = B_TRUE;
3477 			fields_str = optarg;
3478 			break;
3479 		case 'i':
3480 			if (i_arg)
3481 				die_optdup(option);
3482 
3483 			i_arg = B_TRUE;
3484 			if (!dladm_str2interval(optarg, &interval))
3485 				die("invalid interval value '%s'", optarg);
3486 			break;
3487 		default:
3488 			die_opterr(optopt, option, use);
3489 			break;
3490 		}
3491 	}
3492 
3493 	if (p_arg && !o_arg)
3494 		die("-p requires -o");
3495 
3496 	if (p_arg && strcasecmp(fields_str, "all") == 0)
3497 		die("\"-o all\" is invalid with -p");
3498 
3499 	if (i_arg && !s_arg)
3500 		die("the option -i can be used only with -s");
3501 
3502 	if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) {
3503 		die("the option -%c cannot be used with -s",
3504 		    L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P')));
3505 	}
3506 
3507 	if (L_arg && flags != DLADM_OPT_ACTIVE)
3508 		die("the option -P cannot be used with -L");
3509 
3510 	if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE))
3511 		die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P');
3512 
3513 	/* get aggregation key or aggrname (optional last argument) */
3514 	if (optind == (argc-1)) {
3515 		if (!str2int(argv[optind], &key)) {
3516 			status = dladm_name2info(handle, argv[optind],
3517 			    &linkid, NULL, NULL, NULL);
3518 		} else {
3519 			status = dladm_key2linkid(handle, (uint16_t)key,
3520 			    &linkid, DLADM_OPT_ACTIVE);
3521 		}
3522 
3523 		if (status != DLADM_STATUS_OK)
3524 			die("non-existent aggregation '%s'", argv[optind]);
3525 
3526 	} else if (optind != argc) {
3527 		usage();
3528 	}
3529 
3530 	bzero(&state, sizeof (state));
3531 	state.gs_lacp = L_arg;
3532 	state.gs_stats = s_arg;
3533 	state.gs_flags = flags;
3534 	state.gs_parsable = p_arg;
3535 	state.gs_extended = x_arg;
3536 
3537 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
3538 		if (state.gs_lacp)
3539 			fields_str = all_lacp_fields;
3540 		else if (state.gs_stats)
3541 			fields_str = all_stats_fields;
3542 		else if (state.gs_extended)
3543 			fields_str = all_extended_fields;
3544 		else
3545 			fields_str = all_fields;
3546 	}
3547 
3548 	if (state.gs_lacp) {
3549 		pf = aggr_l_fields;
3550 	} else if (state.gs_stats) {
3551 		pf = aggr_s_fields;
3552 	} else if (state.gs_extended) {
3553 		pf = aggr_x_fields;
3554 	} else {
3555 		pf = laggr_fields;
3556 	}
3557 
3558 	if (state.gs_parsable)
3559 		ofmtflags |= OFMT_PARSABLE;
3560 	oferr = ofmt_open(fields_str, pf, ofmtflags, 0, &ofmt);
3561 	dladm_ofmt_check(oferr, state.gs_parsable, ofmt);
3562 	state.gs_ofmt = ofmt;
3563 
3564 	if (s_arg) {
3565 		aggr_stats(linkid, &state, interval);
3566 		ofmt_close(ofmt);
3567 		return;
3568 	}
3569 
3570 	if (linkid == DATALINK_ALL_LINKID) {
3571 		(void) dladm_walk_datalink_id(show_aggr, handle, &state,
3572 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
3573 	} else {
3574 		(void) show_aggr(handle, linkid, &state);
3575 		if (state.gs_status != DLADM_STATUS_OK) {
3576 			die_dlerr(state.gs_status, "failed to show aggr %s",
3577 			    argv[optind]);
3578 		}
3579 	}
3580 	ofmt_close(ofmt);
3581 }
3582 
3583 static dladm_status_t
3584 print_phys_default(show_state_t *state, datalink_id_t linkid,
3585     const char *link, uint32_t flags, uint32_t media)
3586 {
3587 	dladm_phys_attr_t dpa;
3588 	dladm_status_t status;
3589 	link_fields_buf_t pattr;
3590 
3591 	status = dladm_phys_info(handle, linkid, &dpa, state->ls_flags);
3592 	if (status != DLADM_STATUS_OK)
3593 		goto done;
3594 
3595 	(void) snprintf(pattr.link_phys_device,
3596 	    sizeof (pattr.link_phys_device), "%s", dpa.dp_dev);
3597 	(void) dladm_media2str(media, pattr.link_phys_media);
3598 	if (state->ls_flags == DLADM_OPT_ACTIVE) {
3599 		boolean_t	islink;
3600 
3601 		if (!dpa.dp_novanity) {
3602 			(void) strlcpy(pattr.link_name, link,
3603 			    sizeof (pattr.link_name));
3604 			islink = B_TRUE;
3605 		} else {
3606 			/*
3607 			 * This is a physical link that does not have
3608 			 * vanity naming support.
3609 			 */
3610 			(void) strlcpy(pattr.link_name, dpa.dp_dev,
3611 			    sizeof (pattr.link_name));
3612 			islink = B_FALSE;
3613 		}
3614 
3615 		(void) get_linkstate(pattr.link_name, islink,
3616 		    pattr.link_phys_state);
3617 		(void) snprintf(pattr.link_phys_speed,
3618 		    sizeof (pattr.link_phys_speed), "%u",
3619 		    (uint_t)((get_ifspeed(pattr.link_name,
3620 		    islink)) / 1000000ull));
3621 		(void) get_linkduplex(pattr.link_name, islink,
3622 		    pattr.link_phys_duplex);
3623 	} else {
3624 		(void) snprintf(pattr.link_name, sizeof (pattr.link_name),
3625 		    "%s", link);
3626 		(void) snprintf(pattr.link_flags, sizeof (pattr.link_flags),
3627 		    "%c----", flags & DLADM_OPT_ACTIVE ? '-' : 'r');
3628 	}
3629 
3630 	ofmt_print(state->ls_ofmt, &pattr);
3631 
3632 done:
3633 	return (status);
3634 }
3635 
3636 typedef struct {
3637 	show_state_t	*ms_state;
3638 	char		*ms_link;
3639 	dladm_macaddr_attr_t *ms_mac_attr;
3640 } print_phys_mac_state_t;
3641 
3642 /*
3643  *  callback for ofmt_print()
3644  */
3645 static boolean_t
3646 print_phys_one_mac_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
3647 {
3648 	print_phys_mac_state_t *mac_state = ofarg->ofmt_cbarg;
3649 	dladm_macaddr_attr_t *attr = mac_state->ms_mac_attr;
3650 	boolean_t is_primary = (attr->ma_slot == 0);
3651 	boolean_t is_parsable = mac_state->ms_state->ls_parsable;
3652 
3653 	switch (ofarg->ofmt_id) {
3654 	case PHYS_M_LINK:
3655 		(void) snprintf(buf, bufsize, "%s",
3656 		    (is_primary || is_parsable) ? mac_state->ms_link : " ");
3657 		break;
3658 	case PHYS_M_SLOT:
3659 		if (is_primary)
3660 			(void) snprintf(buf, bufsize, gettext("primary"));
3661 		else
3662 			(void) snprintf(buf, bufsize, "%d", attr->ma_slot);
3663 		break;
3664 	case PHYS_M_ADDRESS:
3665 		(void) dladm_aggr_macaddr2str(attr->ma_addr, buf);
3666 		break;
3667 	case PHYS_M_INUSE:
3668 		(void) snprintf(buf, bufsize, "%s",
3669 		    attr->ma_flags & DLADM_MACADDR_USED ? gettext("yes") :
3670 		    gettext("no"));
3671 		break;
3672 	case PHYS_M_CLIENT:
3673 		/*
3674 		 * CR 6678526: resolve link id to actual link name if
3675 		 * it is valid.
3676 		 */
3677 		(void) snprintf(buf, bufsize, "%s", attr->ma_client_name);
3678 		break;
3679 	}
3680 
3681 	return (B_TRUE);
3682 }
3683 
3684 typedef struct {
3685 	show_state_t	*hs_state;
3686 	char		*hs_link;
3687 	dladm_hwgrp_attr_t *hs_grp_attr;
3688 } print_phys_hwgrp_state_t;
3689 
3690 static boolean_t
3691 print_phys_one_hwgrp_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
3692 {
3693 	print_phys_hwgrp_state_t *hg_state = ofarg->ofmt_cbarg;
3694 	dladm_hwgrp_attr_t *attr = hg_state->hs_grp_attr;
3695 
3696 	switch (ofarg->ofmt_id) {
3697 	case PHYS_H_LINK:
3698 		(void) snprintf(buf, bufsize, "%s", attr->hg_link_name);
3699 		break;
3700 	case PHYS_H_GROUP:
3701 		(void) snprintf(buf, bufsize, "%d", attr->hg_grp_num);
3702 		break;
3703 	case PHYS_H_GRPTYPE:
3704 		(void) snprintf(buf, bufsize, "%s",
3705 		    attr->hg_grp_type == DLADM_HWGRP_TYPE_RX ? "RX" : "TX");
3706 		break;
3707 	case PHYS_H_RINGS:
3708 		(void) snprintf(buf, bufsize, "%d", attr->hg_n_rings);
3709 		break;
3710 	case PHYS_H_CLIENTS:
3711 		if (attr->hg_client_names[0] == '\0') {
3712 			(void) snprintf(buf, bufsize, "--");
3713 		} else {
3714 			(void) snprintf(buf, bufsize, "%s ",
3715 			    attr->hg_client_names);
3716 		}
3717 		break;
3718 	}
3719 
3720 	return (B_TRUE);
3721 }
3722 
3723 /*
3724  * callback for dladm_walk_macaddr, invoked for each MAC address slot
3725  */
3726 static boolean_t
3727 print_phys_mac_callback(void *arg, dladm_macaddr_attr_t *attr)
3728 {
3729 	print_phys_mac_state_t *mac_state = arg;
3730 	show_state_t *state = mac_state->ms_state;
3731 
3732 	mac_state->ms_mac_attr = attr;
3733 	ofmt_print(state->ls_ofmt, mac_state);
3734 
3735 	return (B_TRUE);
3736 }
3737 
3738 /*
3739  * invoked by show-phys -m for each physical data-link
3740  */
3741 static dladm_status_t
3742 print_phys_mac(show_state_t *state, datalink_id_t linkid, char *link)
3743 {
3744 	print_phys_mac_state_t mac_state;
3745 
3746 	mac_state.ms_state = state;
3747 	mac_state.ms_link = link;
3748 
3749 	return (dladm_walk_macaddr(handle, linkid, &mac_state,
3750 	    print_phys_mac_callback));
3751 }
3752 
3753 /*
3754  * callback for dladm_walk_hwgrp, invoked for each MAC hwgrp
3755  */
3756 static boolean_t
3757 print_phys_hwgrp_callback(void *arg, dladm_hwgrp_attr_t *attr)
3758 {
3759 	print_phys_hwgrp_state_t *hwgrp_state = arg;
3760 	show_state_t *state = hwgrp_state->hs_state;
3761 
3762 	hwgrp_state->hs_grp_attr = attr;
3763 	ofmt_print(state->ls_ofmt, hwgrp_state);
3764 
3765 	return (B_TRUE);
3766 }
3767 
3768 /* invoked by show-phys -H for each physical data-link */
3769 static dladm_status_t
3770 print_phys_hwgrp(show_state_t *state, datalink_id_t linkid, char *link)
3771 {
3772 	print_phys_hwgrp_state_t hwgrp_state;
3773 
3774 	hwgrp_state.hs_state = state;
3775 	hwgrp_state.hs_link = link;
3776 	return (dladm_walk_hwgrp(handle, linkid, &hwgrp_state,
3777 	    print_phys_hwgrp_callback));
3778 }
3779 
3780 /*
3781  * Parse the "local=<laddr>,remote=<raddr>" sub-options for the -a option of
3782  * *-iptun subcommands.
3783  */
3784 static void
3785 iptun_process_addrarg(char *addrarg, iptun_params_t *params)
3786 {
3787 	char *addrval;
3788 
3789 	while (*addrarg != '\0') {
3790 		switch (getsubopt(&addrarg, iptun_addropts, &addrval)) {
3791 		case IPTUN_LOCAL:
3792 			params->iptun_param_flags |= IPTUN_PARAM_LADDR;
3793 			if (strlcpy(params->iptun_param_laddr, addrval,
3794 			    sizeof (params->iptun_param_laddr)) >=
3795 			    sizeof (params->iptun_param_laddr))
3796 				die("tunnel source address is too long");
3797 			break;
3798 		case IPTUN_REMOTE:
3799 			params->iptun_param_flags |= IPTUN_PARAM_RADDR;
3800 			if (strlcpy(params->iptun_param_raddr, addrval,
3801 			    sizeof (params->iptun_param_raddr)) >=
3802 			    sizeof (params->iptun_param_raddr))
3803 				die("tunnel destination address is too long");
3804 			break;
3805 		default:
3806 			die("invalid address type: %s", addrval);
3807 			break;
3808 		}
3809 	}
3810 }
3811 
3812 /*
3813  * Convenience routine to process iptun-create/modify/delete subcommand
3814  * arguments.
3815  */
3816 static void
3817 iptun_process_args(int argc, char *argv[], const char *opts,
3818     iptun_params_t *params, uint32_t *flags, char *name, const char *use)
3819 {
3820 	int	option;
3821 	char	*altroot = NULL;
3822 
3823 	if (params != NULL)
3824 		bzero(params, sizeof (*params));
3825 	*flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
3826 
3827 	opterr = 0;
3828 	while ((option = getopt_long(argc, argv, opts, iptun_lopts, NULL)) !=
3829 	    -1) {
3830 		switch (option) {
3831 		case 'a':
3832 			iptun_process_addrarg(optarg, params);
3833 			break;
3834 		case 'R':
3835 			altroot = optarg;
3836 			break;
3837 		case 't':
3838 			*flags &= ~DLADM_OPT_PERSIST;
3839 			break;
3840 		case 'T':
3841 			params->iptun_param_type = iptun_gettypebyname(optarg);
3842 			if (params->iptun_param_type == IPTUN_TYPE_UNKNOWN)
3843 				die("unknown tunnel type: %s", optarg);
3844 			params->iptun_param_flags |= IPTUN_PARAM_TYPE;
3845 			break;
3846 		default:
3847 			die_opterr(optopt, option, use);
3848 			break;
3849 		}
3850 	}
3851 
3852 	/* Get the required tunnel name argument. */
3853 	if (argc - optind != 1)
3854 		usage();
3855 
3856 	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
3857 		die("tunnel name is too long");
3858 
3859 	if (altroot != NULL)
3860 		altroot_cmd(altroot, argc, argv);
3861 }
3862 
3863 static void
3864 do_create_iptun(int argc, char *argv[], const char *use)
3865 {
3866 	iptun_params_t	params;
3867 	dladm_status_t	status;
3868 	uint32_t	flags;
3869 	char		name[MAXLINKNAMELEN];
3870 
3871 	iptun_process_args(argc, argv, ":a:R:tT:", &params, &flags, name,
3872 	    use);
3873 
3874 	status = dladm_iptun_create(handle, name, &params, flags);
3875 	if (status != DLADM_STATUS_OK)
3876 		die_dlerr(status, "could not create tunnel");
3877 }
3878 
3879 static void
3880 do_delete_iptun(int argc, char *argv[], const char *use)
3881 {
3882 	uint32_t	flags;
3883 	datalink_id_t	linkid;
3884 	dladm_status_t	status;
3885 	char		name[MAXLINKNAMELEN];
3886 
3887 	iptun_process_args(argc, argv, ":R:t", NULL, &flags, name, use);
3888 
3889 	status = dladm_name2info(handle, name, &linkid, NULL, NULL, NULL);
3890 	if (status != DLADM_STATUS_OK)
3891 		die_dlerr(status, "could not delete tunnel");
3892 	status = dladm_iptun_delete(handle, linkid, flags);
3893 	if (status != DLADM_STATUS_OK)
3894 		die_dlerr(status, "could not delete tunnel");
3895 }
3896 
3897 static void
3898 do_modify_iptun(int argc, char *argv[], const char *use)
3899 {
3900 	iptun_params_t	params;
3901 	uint32_t	flags;
3902 	dladm_status_t	status;
3903 	char		name[MAXLINKNAMELEN];
3904 
3905 	iptun_process_args(argc, argv, ":a:R:t", &params, &flags, name, use);
3906 
3907 	if ((status = dladm_name2info(handle, name, &params.iptun_param_linkid,
3908 	    NULL, NULL, NULL)) != DLADM_STATUS_OK)
3909 		die_dlerr(status, "could not modify tunnel");
3910 	status = dladm_iptun_modify(handle, &params, flags);
3911 	if (status != DLADM_STATUS_OK)
3912 		die_dlerr(status, "could not modify tunnel");
3913 }
3914 
3915 static void
3916 do_show_iptun(int argc, char *argv[], const char *use)
3917 {
3918 	char		option;
3919 	datalink_id_t	linkid;
3920 	uint32_t	flags = DLADM_OPT_ACTIVE;
3921 	char		*name = NULL;
3922 	dladm_status_t	status;
3923 	const char	*fields_str = NULL;
3924 	show_state_t	state;
3925 	ofmt_handle_t	ofmt;
3926 	ofmt_status_t	oferr;
3927 	uint_t		ofmtflags = 0;
3928 
3929 	bzero(&state, sizeof (state));
3930 	opterr = 0;
3931 	while ((option = getopt_long(argc, argv, ":pPo:",
3932 	    iptun_lopts, NULL)) != -1) {
3933 		switch (option) {
3934 		case 'o':
3935 			fields_str = optarg;
3936 			break;
3937 		case 'p':
3938 			state.ls_parsable = B_TRUE;
3939 			ofmtflags = OFMT_PARSABLE;
3940 			break;
3941 		case 'P':
3942 			flags = DLADM_OPT_PERSIST;
3943 			break;
3944 		default:
3945 			die_opterr(optopt, option, use);
3946 			break;
3947 		}
3948 	}
3949 
3950 	/*
3951 	 * Get the optional tunnel name argument.  If there is one, it must
3952 	 * be the last thing remaining on the command-line.
3953 	 */
3954 	if (argc - optind > 1)
3955 		die(gettext(use));
3956 	if (argc - optind == 1)
3957 		name = argv[optind];
3958 
3959 	oferr = ofmt_open(fields_str, iptun_fields, ofmtflags,
3960 	    DLADM_DEFAULT_COL, &ofmt);
3961 	dladm_ofmt_check(oferr, state.ls_parsable, ofmt);
3962 
3963 	state.ls_ofmt = ofmt;
3964 	state.ls_flags = flags;
3965 
3966 	if (name == NULL) {
3967 		(void) dladm_walk_datalink_id(print_iptun_walker, handle,
3968 		    &state, DATALINK_CLASS_IPTUN, DATALINK_ANY_MEDIATYPE,
3969 		    flags);
3970 		status = state.ls_status;
3971 	} else {
3972 		if ((status = dladm_name2info(handle, name, &linkid, NULL, NULL,
3973 		    NULL)) == DLADM_STATUS_OK)
3974 			status = print_iptun(handle, linkid, &state);
3975 	}
3976 
3977 	if (status != DLADM_STATUS_OK)
3978 		die_dlerr(status, "unable to obtain tunnel status");
3979 }
3980 
3981 /* ARGSUSED */
3982 static void
3983 do_up_iptun(int argc, char *argv[], const char *use)
3984 {
3985 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
3986 	dladm_status_t	status = DLADM_STATUS_OK;
3987 
3988 	/*
3989 	 * Get the optional tunnel name argument.  If there is one, it must
3990 	 * be the last thing remaining on the command-line.
3991 	 */
3992 	if (argc - optind > 1)
3993 		usage();
3994 	if (argc - optind == 1) {
3995 		status = dladm_name2info(handle, argv[optind], &linkid, NULL,
3996 		    NULL, NULL);
3997 	}
3998 	if (status == DLADM_STATUS_OK)
3999 		status = dladm_iptun_up(handle, linkid);
4000 	if (status != DLADM_STATUS_OK)
4001 		die_dlerr(status, "unable to configure IP tunnel links");
4002 }
4003 
4004 /* ARGSUSED */
4005 static void
4006 do_down_iptun(int argc, char *argv[], const char *use)
4007 {
4008 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4009 	dladm_status_t	status = DLADM_STATUS_OK;
4010 
4011 	/*
4012 	 * Get the optional tunnel name argument.  If there is one, it must
4013 	 * be the last thing remaining on the command-line.
4014 	 */
4015 	if (argc - optind > 1)
4016 		usage();
4017 	if (argc - optind == 1) {
4018 		status = dladm_name2info(handle, argv[optind], &linkid, NULL,
4019 		    NULL, NULL);
4020 	}
4021 	if (status == DLADM_STATUS_OK)
4022 		status = dladm_iptun_down(handle, linkid);
4023 	if (status != DLADM_STATUS_OK)
4024 		die_dlerr(status, "unable to bring down IP tunnel links");
4025 }
4026 
4027 static iptun_type_t
4028 iptun_gettypebyname(char *typestr)
4029 {
4030 	int i;
4031 
4032 	for (i = 0; iptun_types[i].type_name != NULL; i++) {
4033 		if (strncmp(iptun_types[i].type_name, typestr,
4034 		    strlen(iptun_types[i].type_name)) == 0) {
4035 			return (iptun_types[i].type_value);
4036 		}
4037 	}
4038 	return (IPTUN_TYPE_UNKNOWN);
4039 }
4040 
4041 static const char *
4042 iptun_gettypebyvalue(iptun_type_t type)
4043 {
4044 	int i;
4045 
4046 	for (i = 0; iptun_types[i].type_name != NULL; i++) {
4047 		if (iptun_types[i].type_value == type)
4048 			return (iptun_types[i].type_name);
4049 	}
4050 	return (NULL);
4051 }
4052 
4053 static dladm_status_t
4054 print_iptun(dladm_handle_t dh, datalink_id_t linkid, show_state_t *state)
4055 {
4056 	dladm_status_t		status;
4057 	iptun_params_t		params;
4058 	iptun_fields_buf_t	lbuf;
4059 	const char		*laddr;
4060 	const char		*raddr;
4061 
4062 	params.iptun_param_linkid = linkid;
4063 	status = dladm_iptun_getparams(dh, &params, state->ls_flags);
4064 	if (status != DLADM_STATUS_OK)
4065 		return (status);
4066 
4067 	/* LINK */
4068 	status = dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL,
4069 	    lbuf.iptun_name, sizeof (lbuf.iptun_name));
4070 	if (status != DLADM_STATUS_OK)
4071 		return (status);
4072 
4073 	/* TYPE */
4074 	(void) strlcpy(lbuf.iptun_type,
4075 	    iptun_gettypebyvalue(params.iptun_param_type),
4076 	    sizeof (lbuf.iptun_type));
4077 
4078 	/* FLAGS */
4079 	(void) memset(lbuf.iptun_flags, '-', IPTUN_NUM_FLAGS);
4080 	lbuf.iptun_flags[IPTUN_NUM_FLAGS] = '\0';
4081 	if (params.iptun_param_flags & IPTUN_PARAM_IPSECPOL)
4082 		lbuf.iptun_flags[IPTUN_SFLAG_INDEX] = 's';
4083 	if (params.iptun_param_flags & IPTUN_PARAM_IMPLICIT)
4084 		lbuf.iptun_flags[IPTUN_IFLAG_INDEX] = 'i';
4085 
4086 	/* LOCAL */
4087 	if (params.iptun_param_flags & IPTUN_PARAM_LADDR)
4088 		laddr = params.iptun_param_laddr;
4089 	else
4090 		laddr = (state->ls_parsable) ? "" : "--";
4091 	(void) strlcpy(lbuf.iptun_laddr, laddr, sizeof (lbuf.iptun_laddr));
4092 
4093 	/* REMOTE */
4094 	if (params.iptun_param_flags & IPTUN_PARAM_RADDR)
4095 		raddr = params.iptun_param_raddr;
4096 	else
4097 		raddr = (state->ls_parsable) ? "" : "--";
4098 	(void) strlcpy(lbuf.iptun_raddr, raddr, sizeof (lbuf.iptun_raddr));
4099 
4100 	ofmt_print(state->ls_ofmt, &lbuf);
4101 
4102 	return (DLADM_STATUS_OK);
4103 }
4104 
4105 static int
4106 print_iptun_walker(dladm_handle_t dh, datalink_id_t linkid, void *arg)
4107 {
4108 	((show_state_t *)arg)->ls_status = print_iptun(dh, linkid, arg);
4109 	return (DLADM_WALK_CONTINUE);
4110 }
4111 
4112 static dladm_status_t
4113 print_phys(show_state_t *state, datalink_id_t linkid)
4114 {
4115 	char			link[MAXLINKNAMELEN];
4116 	uint32_t		flags;
4117 	dladm_status_t		status;
4118 	datalink_class_t	class;
4119 	uint32_t		media;
4120 
4121 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
4122 	    &media, link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
4123 		goto done;
4124 	}
4125 
4126 	if (class != DATALINK_CLASS_PHYS) {
4127 		status = DLADM_STATUS_BADARG;
4128 		goto done;
4129 	}
4130 
4131 	if (!(state->ls_flags & flags)) {
4132 		status = DLADM_STATUS_NOTFOUND;
4133 		goto done;
4134 	}
4135 
4136 	if (state->ls_mac)
4137 		status = print_phys_mac(state, linkid, link);
4138 	else if (state->ls_hwgrp)
4139 		status = print_phys_hwgrp(state, linkid, link);
4140 	else
4141 		status = print_phys_default(state, linkid, link, flags, media);
4142 
4143 done:
4144 	return (status);
4145 }
4146 
4147 /* ARGSUSED */
4148 static int
4149 show_phys(dladm_handle_t dh, datalink_id_t linkid, void *arg)
4150 {
4151 	show_state_t	*state = arg;
4152 
4153 	state->ls_status = print_phys(state, linkid);
4154 	return (DLADM_WALK_CONTINUE);
4155 }
4156 
4157 /*
4158  * Print the active topology information.
4159  */
4160 static dladm_status_t
4161 print_vlan(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *l)
4162 {
4163 	dladm_vlan_attr_t	vinfo;
4164 	uint32_t		flags;
4165 	dladm_status_t		status;
4166 
4167 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, NULL,
4168 	    l->link_name, sizeof (l->link_name))) != DLADM_STATUS_OK) {
4169 		goto done;
4170 	}
4171 
4172 	if (!(state->ls_flags & flags)) {
4173 		status = DLADM_STATUS_NOTFOUND;
4174 		goto done;
4175 	}
4176 
4177 	if ((status = dladm_vlan_info(handle, linkid, &vinfo,
4178 	    state->ls_flags)) != DLADM_STATUS_OK ||
4179 	    (status = dladm_datalink_id2info(handle, vinfo.dv_linkid, NULL,
4180 	    NULL, NULL, l->link_over, sizeof (l->link_over))) !=
4181 	    DLADM_STATUS_OK) {
4182 		goto done;
4183 	}
4184 
4185 	(void) snprintf(l->link_vlan_vid, sizeof (l->link_vlan_vid), "%d",
4186 	    vinfo.dv_vid);
4187 	(void) snprintf(l->link_flags, sizeof (l->link_flags), "%c----",
4188 	    vinfo.dv_force ? 'f' : '-');
4189 
4190 done:
4191 	return (status);
4192 }
4193 
4194 /* ARGSUSED */
4195 static int
4196 show_vlan(dladm_handle_t dh, datalink_id_t linkid, void *arg)
4197 {
4198 	show_state_t		*state = arg;
4199 	dladm_status_t		status;
4200 	link_fields_buf_t	lbuf;
4201 
4202 	bzero(&lbuf, sizeof (link_fields_buf_t));
4203 	status = print_vlan(state, linkid, &lbuf);
4204 	if (status != DLADM_STATUS_OK)
4205 		goto done;
4206 
4207 	ofmt_print(state->ls_ofmt, &lbuf);
4208 
4209 done:
4210 	state->ls_status = status;
4211 	return (DLADM_WALK_CONTINUE);
4212 }
4213 
4214 static void
4215 do_show_phys(int argc, char *argv[], const char *use)
4216 {
4217 	int		option;
4218 	uint32_t	flags = DLADM_OPT_ACTIVE;
4219 	boolean_t	p_arg = B_FALSE;
4220 	boolean_t	o_arg = B_FALSE;
4221 	boolean_t	m_arg = B_FALSE;
4222 	boolean_t	H_arg = B_FALSE;
4223 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4224 	show_state_t	state;
4225 	dladm_status_t	status;
4226 	char		*fields_str = NULL;
4227 	char		*all_active_fields =
4228 	    "link,media,state,speed,duplex,device";
4229 	char		*all_inactive_fields = "link,device,media,flags";
4230 	char		*all_mac_fields = "link,slot,address,inuse,client";
4231 	char		*all_hwgrp_fields =
4232 	    "link,group,grouptype,rings,clients";
4233 	const ofmt_field_t *pf;
4234 	ofmt_handle_t	ofmt;
4235 	ofmt_status_t	oferr;
4236 	uint_t		ofmtflags = 0;
4237 
4238 	bzero(&state, sizeof (state));
4239 	opterr = 0;
4240 	while ((option = getopt_long(argc, argv, ":pPo:mH",
4241 	    show_lopts, NULL)) != -1) {
4242 		switch (option) {
4243 		case 'p':
4244 			if (p_arg)
4245 				die_optdup(option);
4246 
4247 			p_arg = B_TRUE;
4248 			break;
4249 		case 'P':
4250 			if (flags != DLADM_OPT_ACTIVE)
4251 				die_optdup(option);
4252 
4253 			flags = DLADM_OPT_PERSIST;
4254 			break;
4255 		case 'o':
4256 			o_arg = B_TRUE;
4257 			fields_str = optarg;
4258 			break;
4259 		case 'm':
4260 			m_arg = B_TRUE;
4261 			break;
4262 		case 'H':
4263 			H_arg = B_TRUE;
4264 			break;
4265 		default:
4266 			die_opterr(optopt, option, use);
4267 			break;
4268 		}
4269 	}
4270 
4271 	if (p_arg && !o_arg)
4272 		die("-p requires -o");
4273 
4274 	if (m_arg && H_arg)
4275 		die("-m cannot combine with -H");
4276 
4277 	if (p_arg && strcasecmp(fields_str, "all") == 0)
4278 		die("\"-o all\" is invalid with -p");
4279 
4280 	/* get link name (optional last argument) */
4281 	if (optind == (argc-1)) {
4282 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
4283 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
4284 			die_dlerr(status, "link %s is not valid", argv[optind]);
4285 		}
4286 	} else if (optind != argc) {
4287 		usage();
4288 	}
4289 
4290 	state.ls_parsable = p_arg;
4291 	state.ls_flags = flags;
4292 	state.ls_donefirst = B_FALSE;
4293 	state.ls_mac = m_arg;
4294 	state.ls_hwgrp = H_arg;
4295 
4296 	if (m_arg && !(flags & DLADM_OPT_ACTIVE)) {
4297 		/*
4298 		 * We can only display the factory MAC addresses of
4299 		 * active data-links.
4300 		 */
4301 		die("-m not compatible with -P");
4302 	}
4303 
4304 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
4305 		if (state.ls_mac)
4306 			fields_str = all_mac_fields;
4307 		else if (state.ls_hwgrp)
4308 			fields_str = all_hwgrp_fields;
4309 		else if (state.ls_flags & DLADM_OPT_ACTIVE) {
4310 			fields_str = all_active_fields;
4311 		} else {
4312 			fields_str = all_inactive_fields;
4313 		}
4314 	}
4315 
4316 	if (state.ls_mac) {
4317 		pf = phys_m_fields;
4318 	} else if (state.ls_hwgrp) {
4319 		pf = phys_h_fields;
4320 	} else {
4321 		pf = phys_fields;
4322 	}
4323 
4324 	if (state.ls_parsable)
4325 		ofmtflags |= OFMT_PARSABLE;
4326 	oferr = ofmt_open(fields_str, pf, ofmtflags, 0, &ofmt);
4327 	dladm_ofmt_check(oferr, state.ls_parsable, ofmt);
4328 	state.ls_ofmt = ofmt;
4329 
4330 	if (linkid == DATALINK_ALL_LINKID) {
4331 		(void) dladm_walk_datalink_id(show_phys, handle, &state,
4332 		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags);
4333 	} else {
4334 		(void) show_phys(handle, linkid, &state);
4335 		if (state.ls_status != DLADM_STATUS_OK) {
4336 			die_dlerr(state.ls_status,
4337 			    "failed to show physical link %s", argv[optind]);
4338 		}
4339 	}
4340 	ofmt_close(ofmt);
4341 }
4342 
4343 static void
4344 do_show_vlan(int argc, char *argv[], const char *use)
4345 {
4346 	int		option;
4347 	uint32_t	flags = DLADM_OPT_ACTIVE;
4348 	boolean_t	p_arg = B_FALSE;
4349 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4350 	show_state_t	state;
4351 	dladm_status_t	status;
4352 	boolean_t	o_arg = B_FALSE;
4353 	char		*fields_str = NULL;
4354 	ofmt_handle_t	ofmt;
4355 	ofmt_status_t	oferr;
4356 	uint_t		ofmtflags = 0;
4357 
4358 	bzero(&state, sizeof (state));
4359 
4360 	opterr = 0;
4361 	while ((option = getopt_long(argc, argv, ":pPo:",
4362 	    show_lopts, NULL)) != -1) {
4363 		switch (option) {
4364 		case 'p':
4365 			if (p_arg)
4366 				die_optdup(option);
4367 
4368 			p_arg = B_TRUE;
4369 			break;
4370 		case 'P':
4371 			if (flags != DLADM_OPT_ACTIVE)
4372 				die_optdup(option);
4373 
4374 			flags = DLADM_OPT_PERSIST;
4375 			break;
4376 		case 'o':
4377 			o_arg = B_TRUE;
4378 			fields_str = optarg;
4379 			break;
4380 		default:
4381 			die_opterr(optopt, option, use);
4382 			break;
4383 		}
4384 	}
4385 
4386 	/* get link name (optional last argument) */
4387 	if (optind == (argc-1)) {
4388 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
4389 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
4390 			die_dlerr(status, "link %s is not valid", argv[optind]);
4391 		}
4392 	} else if (optind != argc) {
4393 		usage();
4394 	}
4395 
4396 	state.ls_parsable = p_arg;
4397 	state.ls_flags = flags;
4398 	state.ls_donefirst = B_FALSE;
4399 
4400 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
4401 		fields_str = NULL;
4402 
4403 	if (state.ls_parsable)
4404 		ofmtflags |= OFMT_PARSABLE;
4405 	oferr = ofmt_open(fields_str, vlan_fields, ofmtflags, 0, &ofmt);
4406 	dladm_ofmt_check(oferr, state.ls_parsable, ofmt);
4407 	state.ls_ofmt = ofmt;
4408 
4409 	if (linkid == DATALINK_ALL_LINKID) {
4410 		(void) dladm_walk_datalink_id(show_vlan, handle, &state,
4411 		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags);
4412 	} else {
4413 		(void) show_vlan(handle, linkid, &state);
4414 		if (state.ls_status != DLADM_STATUS_OK) {
4415 			die_dlerr(state.ls_status, "failed to show vlan %s",
4416 			    argv[optind]);
4417 		}
4418 	}
4419 	ofmt_close(ofmt);
4420 }
4421 
4422 static void
4423 do_create_vnic(int argc, char *argv[], const char *use)
4424 {
4425 	datalink_id_t		linkid, dev_linkid;
4426 	char			devname[MAXLINKNAMELEN];
4427 	char			name[MAXLINKNAMELEN];
4428 	boolean_t		l_arg = B_FALSE;
4429 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
4430 	char			*altroot = NULL;
4431 	int			option;
4432 	char			*endp = NULL;
4433 	dladm_status_t		status;
4434 	vnic_mac_addr_type_t	mac_addr_type = VNIC_MAC_ADDR_TYPE_AUTO;
4435 	uchar_t			*mac_addr;
4436 	int			mac_slot = -1, maclen = 0, mac_prefix_len = 0;
4437 	char			propstr[DLADM_STRSIZE];
4438 	dladm_arg_list_t	*proplist = NULL;
4439 	int			vid = 0;
4440 
4441 	opterr = 0;
4442 	bzero(propstr, DLADM_STRSIZE);
4443 
4444 	while ((option = getopt_long(argc, argv, ":tfR:l:m:n:p:r:v:H",
4445 	    vnic_lopts, NULL)) != -1) {
4446 		switch (option) {
4447 		case 't':
4448 			flags &= ~DLADM_OPT_PERSIST;
4449 			break;
4450 		case 'R':
4451 			altroot = optarg;
4452 			break;
4453 		case 'l':
4454 			if (strlcpy(devname, optarg, MAXLINKNAMELEN) >=
4455 			    MAXLINKNAMELEN)
4456 				die("link name too long");
4457 			l_arg = B_TRUE;
4458 			break;
4459 		case 'm':
4460 			if (strcmp(optarg, "fixed") == 0) {
4461 				/*
4462 				 * A fixed MAC address must be specified
4463 				 * by its value, not by the keyword 'fixed'.
4464 				 */
4465 				die("'fixed' is not a valid MAC address");
4466 			}
4467 			if (dladm_vnic_str2macaddrtype(optarg,
4468 			    &mac_addr_type) != DLADM_STATUS_OK) {
4469 				mac_addr_type = VNIC_MAC_ADDR_TYPE_FIXED;
4470 				/* MAC address specified by value */
4471 				mac_addr = _link_aton(optarg, &maclen);
4472 				if (mac_addr == NULL) {
4473 					if (maclen == -1)
4474 						die("invalid MAC address");
4475 					else
4476 						die("out of memory");
4477 				}
4478 			}
4479 			break;
4480 		case 'n':
4481 			errno = 0;
4482 			mac_slot = (int)strtol(optarg, &endp, 10);
4483 			if (errno != 0 || *endp != '\0')
4484 				die("invalid slot number");
4485 			break;
4486 		case 'p':
4487 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
4488 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
4489 			    DLADM_STRSIZE)
4490 				die("property list too long '%s'", propstr);
4491 			break;
4492 		case 'r':
4493 			mac_addr = _link_aton(optarg, &mac_prefix_len);
4494 			if (mac_addr == NULL) {
4495 				if (mac_prefix_len == -1)
4496 					die("invalid MAC address");
4497 				else
4498 					die("out of memory");
4499 			}
4500 			break;
4501 		case 'v':
4502 			if (vid != 0)
4503 				die_optdup(option);
4504 
4505 			if (!str2int(optarg, &vid) || vid < 1 || vid > 4094)
4506 				die("invalid VLAN identifier '%s'", optarg);
4507 
4508 			break;
4509 		case 'f':
4510 			flags |= DLADM_OPT_FORCE;
4511 			break;
4512 		case 'H':
4513 			flags |= DLADM_OPT_HWRINGS;
4514 			break;
4515 		default:
4516 			die_opterr(optopt, option, use);
4517 		}
4518 	}
4519 
4520 	/*
4521 	 * 'f' - force, flag can be specified only with 'v' - vlan.
4522 	 */
4523 	if ((flags & DLADM_OPT_FORCE) != 0 && vid == 0)
4524 		die("-f option can only be used with -v");
4525 
4526 	if (mac_prefix_len != 0 && mac_addr_type != VNIC_MAC_ADDR_TYPE_RANDOM &&
4527 	    mac_addr_type != VNIC_MAC_ADDR_TYPE_FIXED)
4528 		usage();
4529 
4530 	/* check required options */
4531 	if (!l_arg)
4532 		usage();
4533 
4534 	if (mac_slot != -1 && mac_addr_type != VNIC_MAC_ADDR_TYPE_FACTORY)
4535 		usage();
4536 
4537 	/* the VNIC id is the required operand */
4538 	if (optind != (argc - 1))
4539 		usage();
4540 
4541 	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
4542 		die("link name too long '%s'", argv[optind]);
4543 
4544 	if (!dladm_valid_linkname(name))
4545 		die("invalid link name '%s'", argv[optind]);
4546 
4547 	if (altroot != NULL)
4548 		altroot_cmd(altroot, argc, argv);
4549 
4550 	if (dladm_name2info(handle, devname, &dev_linkid, NULL, NULL, NULL) !=
4551 	    DLADM_STATUS_OK)
4552 		die("invalid link name '%s'", devname);
4553 
4554 	if (dladm_parse_link_props(propstr, &proplist, B_FALSE)
4555 	    != DLADM_STATUS_OK)
4556 		die("invalid vnic property");
4557 
4558 	status = dladm_vnic_create(handle, name, dev_linkid, mac_addr_type,
4559 	    mac_addr, maclen, &mac_slot, mac_prefix_len, vid, &linkid, proplist,
4560 	    flags);
4561 	if (status != DLADM_STATUS_OK)
4562 		die_dlerr(status, "vnic creation over %s failed", devname);
4563 
4564 	dladm_free_props(proplist);
4565 }
4566 
4567 static void
4568 do_etherstub_check(const char *name, datalink_id_t linkid, boolean_t etherstub,
4569     uint32_t flags)
4570 {
4571 	boolean_t is_etherstub;
4572 	dladm_vnic_attr_t attr;
4573 
4574 	if (dladm_vnic_info(handle, linkid, &attr, flags) != DLADM_STATUS_OK) {
4575 		/*
4576 		 * Let the delete continue anyway.
4577 		 */
4578 		return;
4579 	}
4580 	is_etherstub = (attr.va_link_id == DATALINK_INVALID_LINKID);
4581 	if (is_etherstub != etherstub) {
4582 		die("'%s' is not %s", name,
4583 		    (is_etherstub ? "a vnic" : "an etherstub"));
4584 	}
4585 }
4586 
4587 static void
4588 do_delete_vnic_common(int argc, char *argv[], const char *use,
4589     boolean_t etherstub)
4590 {
4591 	int option;
4592 	uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
4593 	datalink_id_t linkid;
4594 	char *altroot = NULL;
4595 	dladm_status_t status;
4596 
4597 	opterr = 0;
4598 	while ((option = getopt_long(argc, argv, ":R:t", lopts,
4599 	    NULL)) != -1) {
4600 		switch (option) {
4601 		case 't':
4602 			flags &= ~DLADM_OPT_PERSIST;
4603 			break;
4604 		case 'R':
4605 			altroot = optarg;
4606 			break;
4607 		default:
4608 			die_opterr(optopt, option, use);
4609 		}
4610 	}
4611 
4612 	/* get vnic name (required last argument) */
4613 	if (optind != (argc - 1))
4614 		usage();
4615 
4616 	if (altroot != NULL)
4617 		altroot_cmd(altroot, argc, argv);
4618 
4619 	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
4620 	    NULL);
4621 	if (status != DLADM_STATUS_OK)
4622 		die("invalid link name '%s'", argv[optind]);
4623 
4624 	if ((flags & DLADM_OPT_ACTIVE) != 0) {
4625 		do_etherstub_check(argv[optind], linkid, etherstub,
4626 		    DLADM_OPT_ACTIVE);
4627 	}
4628 	if ((flags & DLADM_OPT_PERSIST) != 0) {
4629 		do_etherstub_check(argv[optind], linkid, etherstub,
4630 		    DLADM_OPT_PERSIST);
4631 	}
4632 
4633 	status = dladm_vnic_delete(handle, linkid, flags);
4634 	if (status != DLADM_STATUS_OK)
4635 		die_dlerr(status, "vnic deletion failed");
4636 }
4637 
4638 static void
4639 do_delete_vnic(int argc, char *argv[], const char *use)
4640 {
4641 	do_delete_vnic_common(argc, argv, use, B_FALSE);
4642 }
4643 
4644 /* ARGSUSED */
4645 static void
4646 do_up_vnic_common(int argc, char *argv[], const char *use, boolean_t vlan)
4647 {
4648 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4649 	dladm_status_t	status;
4650 	char 		*type;
4651 
4652 	type = vlan ? "vlan" : "vnic";
4653 
4654 	/*
4655 	 * get the id or the name of the vnic/vlan (optional last argument)
4656 	 */
4657 	if (argc == 2) {
4658 		status = dladm_name2info(handle, argv[1], &linkid, NULL, NULL,
4659 		    NULL);
4660 		if (status != DLADM_STATUS_OK)
4661 			goto done;
4662 
4663 	} else if (argc > 2) {
4664 		usage();
4665 	}
4666 
4667 	if (vlan)
4668 		status = dladm_vlan_up(handle, linkid);
4669 	else
4670 		status = dladm_vnic_up(handle, linkid, 0);
4671 
4672 done:
4673 	if (status != DLADM_STATUS_OK) {
4674 		if (argc == 2) {
4675 			die_dlerr(status,
4676 			    "could not bring up %s '%s'", type, argv[1]);
4677 		} else {
4678 			die_dlerr(status, "could not bring %ss up", type);
4679 		}
4680 	}
4681 }
4682 
4683 static void
4684 do_up_vnic(int argc, char *argv[], const char *use)
4685 {
4686 	do_up_vnic_common(argc, argv, use, B_FALSE);
4687 }
4688 
4689 static void
4690 dump_vnics_head(const char *dev)
4691 {
4692 	if (strlen(dev))
4693 		(void) printf("%s", dev);
4694 
4695 	(void) printf("\tipackets  rbytes      opackets  obytes          ");
4696 
4697 	if (strlen(dev))
4698 		(void) printf("%%ipkts  %%opkts\n");
4699 	else
4700 		(void) printf("\n");
4701 }
4702 
4703 static void
4704 dump_vnic_stat(const char *name, datalink_id_t vnic_id,
4705     show_vnic_state_t *state, pktsum_t *vnic_stats, pktsum_t *tot_stats)
4706 {
4707 	pktsum_t	diff_stats;
4708 	pktsum_t	*old_stats = &state->vs_prevstats[vnic_id];
4709 
4710 	dladm_stats_diff(&diff_stats, vnic_stats, old_stats);
4711 
4712 	(void) printf("%s", name);
4713 
4714 	(void) printf("\t%-10llu", diff_stats.ipackets);
4715 	(void) printf("%-12llu", diff_stats.rbytes);
4716 	(void) printf("%-10llu", diff_stats.opackets);
4717 	(void) printf("%-12llu", diff_stats.obytes);
4718 
4719 	if (tot_stats) {
4720 		if (tot_stats->ipackets == 0) {
4721 			(void) printf("\t-");
4722 		} else {
4723 			(void) printf("\t%-6.1f", (double)diff_stats.ipackets/
4724 			    (double)tot_stats->ipackets * 100);
4725 		}
4726 		if (tot_stats->opackets == 0) {
4727 			(void) printf("\t-");
4728 		} else {
4729 			(void) printf("\t%-6.1f", (double)diff_stats.opackets/
4730 			    (double)tot_stats->opackets * 100);
4731 		}
4732 	}
4733 	(void) printf("\n");
4734 
4735 	*old_stats = *vnic_stats;
4736 }
4737 
4738 /*
4739  * Called from the walker dladm_vnic_walk_sys() for each vnic to display
4740  * vnic information or statistics.
4741  */
4742 static dladm_status_t
4743 print_vnic(show_vnic_state_t *state, datalink_id_t linkid)
4744 {
4745 	dladm_vnic_attr_t	attr, *vnic = &attr;
4746 	dladm_status_t		status;
4747 	boolean_t		is_etherstub;
4748 	char			devname[MAXLINKNAMELEN];
4749 	char			vnic_name[MAXLINKNAMELEN];
4750 	char			mstr[MAXMACADDRLEN * 3];
4751 	vnic_fields_buf_t	vbuf;
4752 
4753 	if ((status = dladm_vnic_info(handle, linkid, vnic, state->vs_flags)) !=
4754 	    DLADM_STATUS_OK)
4755 		return (status);
4756 
4757 	is_etherstub = (vnic->va_link_id == DATALINK_INVALID_LINKID);
4758 	if (state->vs_etherstub != is_etherstub) {
4759 		/*
4760 		 * Want all etherstub but it's not one, or want
4761 		 * non-etherstub and it's one.
4762 		 */
4763 		return (DLADM_STATUS_OK);
4764 	}
4765 
4766 	if (state->vs_link_id != DATALINK_ALL_LINKID) {
4767 		if (state->vs_link_id != vnic->va_link_id)
4768 			return (DLADM_STATUS_OK);
4769 	}
4770 
4771 	if (dladm_datalink_id2info(handle, linkid, NULL, NULL,
4772 	    NULL, vnic_name, sizeof (vnic_name)) != DLADM_STATUS_OK)
4773 		return (DLADM_STATUS_BADARG);
4774 
4775 	bzero(devname, sizeof (devname));
4776 	if (!is_etherstub &&
4777 	    dladm_datalink_id2info(handle, vnic->va_link_id, NULL, NULL,
4778 	    NULL, devname, sizeof (devname)) != DLADM_STATUS_OK)
4779 		(void) sprintf(devname, "?");
4780 
4781 	state->vs_found = B_TRUE;
4782 	if (state->vs_stats) {
4783 		/* print vnic statistics */
4784 		pktsum_t vnic_stats;
4785 
4786 		if (state->vs_firstonly) {
4787 			if (state->vs_donefirst)
4788 				return (0);
4789 			state->vs_donefirst = B_TRUE;
4790 		}
4791 
4792 		if (!state->vs_printstats) {
4793 			/*
4794 			 * get vnic statistics and add to the sum for the
4795 			 * named device.
4796 			 */
4797 			get_link_stats(vnic_name, &vnic_stats);
4798 			dladm_stats_total(&state->vs_totalstats, &vnic_stats,
4799 			    &state->vs_prevstats[vnic->va_vnic_id]);
4800 		} else {
4801 			/* get and print vnic statistics */
4802 			get_link_stats(vnic_name, &vnic_stats);
4803 			dump_vnic_stat(vnic_name, linkid, state, &vnic_stats,
4804 			    &state->vs_totalstats);
4805 		}
4806 		return (DLADM_STATUS_OK);
4807 	} else {
4808 		(void) snprintf(vbuf.vnic_link, sizeof (vbuf.vnic_link),
4809 		    "%s", vnic_name);
4810 
4811 		if (!is_etherstub) {
4812 
4813 			(void) snprintf(vbuf.vnic_over, sizeof (vbuf.vnic_over),
4814 			    "%s", devname);
4815 			(void) snprintf(vbuf.vnic_speed,
4816 			    sizeof (vbuf.vnic_speed), "%u",
4817 			    (uint_t)((get_ifspeed(vnic_name, B_TRUE))
4818 			    / 1000000ull));
4819 
4820 			switch (vnic->va_mac_addr_type) {
4821 			case VNIC_MAC_ADDR_TYPE_FIXED:
4822 			case VNIC_MAC_ADDR_TYPE_PRIMARY:
4823 				(void) snprintf(vbuf.vnic_macaddrtype,
4824 				    sizeof (vbuf.vnic_macaddrtype),
4825 				    gettext("fixed"));
4826 				break;
4827 			case VNIC_MAC_ADDR_TYPE_RANDOM:
4828 				(void) snprintf(vbuf.vnic_macaddrtype,
4829 				    sizeof (vbuf.vnic_macaddrtype),
4830 				    gettext("random"));
4831 				break;
4832 			case VNIC_MAC_ADDR_TYPE_FACTORY:
4833 				(void) snprintf(vbuf.vnic_macaddrtype,
4834 				    sizeof (vbuf.vnic_macaddrtype),
4835 				    gettext("factory, slot %d"),
4836 				    vnic->va_mac_slot);
4837 				break;
4838 			}
4839 
4840 			if (strlen(vbuf.vnic_macaddrtype) > 0) {
4841 				(void) snprintf(vbuf.vnic_macaddr,
4842 				    sizeof (vbuf.vnic_macaddr), "%s",
4843 				    dladm_aggr_macaddr2str(vnic->va_mac_addr,
4844 				    mstr));
4845 			}
4846 
4847 			(void) snprintf(vbuf.vnic_vid, sizeof (vbuf.vnic_vid),
4848 			    "%d", vnic->va_vid);
4849 		}
4850 
4851 		ofmt_print(state->vs_ofmt, &vbuf);
4852 
4853 		return (DLADM_STATUS_OK);
4854 	}
4855 }
4856 
4857 /* ARGSUSED */
4858 static int
4859 show_vnic(dladm_handle_t dh, datalink_id_t linkid, void *arg)
4860 {
4861 	show_vnic_state_t	*state = arg;
4862 
4863 	state->vs_status = print_vnic(state, linkid);
4864 	return (DLADM_WALK_CONTINUE);
4865 }
4866 
4867 static void
4868 do_show_vnic_common(int argc, char *argv[], const char *use,
4869     boolean_t etherstub)
4870 {
4871 	int			option;
4872 	boolean_t		s_arg = B_FALSE;
4873 	boolean_t		i_arg = B_FALSE;
4874 	boolean_t		l_arg = B_FALSE;
4875 	uint32_t		interval = 0, flags = DLADM_OPT_ACTIVE;
4876 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
4877 	datalink_id_t		dev_linkid = DATALINK_ALL_LINKID;
4878 	show_vnic_state_t	state;
4879 	dladm_status_t		status;
4880 	boolean_t		o_arg = B_FALSE;
4881 	char			*fields_str = NULL;
4882 	const ofmt_field_t	*pf;
4883 	char			*all_e_fields = "link";
4884 	ofmt_handle_t		ofmt;
4885 	ofmt_status_t		oferr;
4886 	uint_t			ofmtflags = 0;
4887 
4888 	bzero(&state, sizeof (state));
4889 	opterr = 0;
4890 	while ((option = getopt_long(argc, argv, ":pPl:si:o:", lopts,
4891 	    NULL)) != -1) {
4892 		switch (option) {
4893 		case 'p':
4894 			state.vs_parsable = B_TRUE;
4895 			break;
4896 		case 'P':
4897 			flags = DLADM_OPT_PERSIST;
4898 			break;
4899 		case 'l':
4900 			if (etherstub)
4901 				die("option not supported for this command");
4902 
4903 			if (strlcpy(state.vs_link, optarg, MAXLINKNAMELEN) >=
4904 			    MAXLINKNAMELEN)
4905 				die("link name too long");
4906 
4907 			l_arg = B_TRUE;
4908 			break;
4909 		case 's':
4910 			if (s_arg) {
4911 				die("the option -s cannot be specified "
4912 				    "more than once");
4913 			}
4914 			s_arg = B_TRUE;
4915 			break;
4916 		case 'i':
4917 			if (i_arg) {
4918 				die("the option -i cannot be specified "
4919 				    "more than once");
4920 			}
4921 			i_arg = B_TRUE;
4922 			if (!dladm_str2interval(optarg, &interval))
4923 				die("invalid interval value '%s'", optarg);
4924 			break;
4925 		case 'o':
4926 			o_arg = B_TRUE;
4927 			fields_str = optarg;
4928 			break;
4929 		default:
4930 			die_opterr(optopt, option, use);
4931 		}
4932 	}
4933 
4934 	if (i_arg && !s_arg)
4935 		die("the option -i can be used only with -s");
4936 
4937 	/* get vnic ID (optional last argument) */
4938 	if (optind == (argc - 1)) {
4939 		status = dladm_name2info(handle, argv[optind], &linkid, NULL,
4940 		    NULL, NULL);
4941 		if (status != DLADM_STATUS_OK) {
4942 			die_dlerr(status, "invalid vnic name '%s'",
4943 			    argv[optind]);
4944 		}
4945 		(void) strlcpy(state.vs_vnic, argv[optind], MAXLINKNAMELEN);
4946 	} else if (optind != argc) {
4947 		usage();
4948 	}
4949 
4950 	if (l_arg) {
4951 		status = dladm_name2info(handle, state.vs_link, &dev_linkid,
4952 		    NULL, NULL, NULL);
4953 		if (status != DLADM_STATUS_OK) {
4954 			die_dlerr(status, "invalid link name '%s'",
4955 			    state.vs_link);
4956 		}
4957 	}
4958 
4959 	state.vs_vnic_id = linkid;
4960 	state.vs_link_id = dev_linkid;
4961 	state.vs_etherstub = etherstub;
4962 	state.vs_found = B_FALSE;
4963 	state.vs_flags = flags;
4964 
4965 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
4966 		if (etherstub)
4967 			fields_str = all_e_fields;
4968 	}
4969 	pf = vnic_fields;
4970 
4971 	if (state.vs_parsable)
4972 		ofmtflags |= OFMT_PARSABLE;
4973 	oferr = ofmt_open(fields_str, pf, ofmtflags, 0, &ofmt);
4974 	dladm_ofmt_check(oferr, state.vs_parsable, ofmt);
4975 	state.vs_ofmt = ofmt;
4976 
4977 	if (s_arg) {
4978 		/* Display vnic statistics */
4979 		vnic_stats(&state, interval);
4980 		ofmt_close(ofmt);
4981 		return;
4982 	}
4983 
4984 	/* Display vnic information */
4985 	state.vs_donefirst = B_FALSE;
4986 
4987 	if (linkid == DATALINK_ALL_LINKID) {
4988 		(void) dladm_walk_datalink_id(show_vnic, handle, &state,
4989 		    DATALINK_CLASS_VNIC | DATALINK_CLASS_ETHERSTUB,
4990 		    DATALINK_ANY_MEDIATYPE, flags);
4991 	} else {
4992 		(void) show_vnic(handle, linkid, &state);
4993 		if (state.vs_status != DLADM_STATUS_OK) {
4994 			ofmt_close(ofmt);
4995 			die_dlerr(state.vs_status, "failed to show vnic '%s'",
4996 			    state.vs_vnic);
4997 		}
4998 	}
4999 	ofmt_close(ofmt);
5000 }
5001 
5002 static void
5003 do_show_vnic(int argc, char *argv[], const char *use)
5004 {
5005 	do_show_vnic_common(argc, argv, use, B_FALSE);
5006 }
5007 
5008 static void
5009 do_create_etherstub(int argc, char *argv[], const char *use)
5010 {
5011 	uint32_t flags;
5012 	char *altroot = NULL;
5013 	int option;
5014 	dladm_status_t status;
5015 	char name[MAXLINKNAMELEN];
5016 	uchar_t mac_addr[ETHERADDRL];
5017 
5018 	name[0] = '\0';
5019 	bzero(mac_addr, sizeof (mac_addr));
5020 	flags = DLADM_OPT_ANCHOR | DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5021 
5022 	opterr = 0;
5023 	while ((option = getopt_long(argc, argv, "tR:",
5024 	    etherstub_lopts, NULL)) != -1) {
5025 		switch (option) {
5026 		case 't':
5027 			flags &= ~DLADM_OPT_PERSIST;
5028 			break;
5029 		case 'R':
5030 			altroot = optarg;
5031 			break;
5032 		default:
5033 			die_opterr(optopt, option, use);
5034 		}
5035 	}
5036 
5037 	/* the etherstub id is the required operand */
5038 	if (optind != (argc - 1))
5039 		usage();
5040 
5041 	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
5042 		die("link name too long '%s'", argv[optind]);
5043 
5044 	if (!dladm_valid_linkname(name))
5045 		die("invalid link name '%s'", argv[optind]);
5046 
5047 	if (altroot != NULL)
5048 		altroot_cmd(altroot, argc, argv);
5049 
5050 	status = dladm_vnic_create(handle, name, DATALINK_INVALID_LINKID,
5051 	    VNIC_MAC_ADDR_TYPE_AUTO, mac_addr, ETHERADDRL, NULL, 0, 0, NULL,
5052 	    NULL, flags);
5053 	if (status != DLADM_STATUS_OK)
5054 		die_dlerr(status, "etherstub creation failed");
5055 }
5056 
5057 static void
5058 do_delete_etherstub(int argc, char *argv[], const char *use)
5059 {
5060 	do_delete_vnic_common(argc, argv, use, B_TRUE);
5061 }
5062 
5063 /* ARGSUSED */
5064 static void
5065 do_show_etherstub(int argc, char *argv[], const char *use)
5066 {
5067 	do_show_vnic_common(argc, argv, use, B_TRUE);
5068 }
5069 
5070 /* ARGSUSED */
5071 static void
5072 do_up_simnet(int argc, char *argv[], const char *use)
5073 {
5074 	(void) dladm_simnet_up(handle, DATALINK_ALL_LINKID, 0);
5075 }
5076 
5077 static void
5078 do_create_simnet(int argc, char *argv[], const char *use)
5079 {
5080 	uint32_t flags;
5081 	char *altroot = NULL;
5082 	char *media = NULL;
5083 	uint32_t mtype = DL_ETHER;
5084 	int option;
5085 	dladm_status_t status;
5086 	char name[MAXLINKNAMELEN];
5087 
5088 	name[0] = '\0';
5089 	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5090 
5091 	opterr = 0;
5092 	while ((option = getopt_long(argc, argv, ":tR:m:",
5093 	    simnet_lopts, NULL)) != -1) {
5094 		switch (option) {
5095 		case 't':
5096 			flags &= ~DLADM_OPT_PERSIST;
5097 			break;
5098 		case 'R':
5099 			altroot = optarg;
5100 			break;
5101 		case 'm':
5102 			media = optarg;
5103 			break;
5104 		default:
5105 			die_opterr(optopt, option, use);
5106 		}
5107 	}
5108 
5109 	/* the simnet id is the required operand */
5110 	if (optind != (argc - 1))
5111 		usage();
5112 
5113 	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
5114 		die("link name too long '%s'", argv[optind]);
5115 
5116 	if (!dladm_valid_linkname(name))
5117 		die("invalid link name '%s'", name);
5118 
5119 	if (media != NULL) {
5120 		mtype = dladm_str2media(media);
5121 		if (mtype != DL_ETHER && mtype != DL_WIFI)
5122 			die("media type '%s' is not supported", media);
5123 	}
5124 
5125 	if (altroot != NULL)
5126 		altroot_cmd(altroot, argc, argv);
5127 
5128 	status = dladm_simnet_create(handle, name, mtype, flags);
5129 	if (status != DLADM_STATUS_OK)
5130 		die_dlerr(status, "simnet creation failed");
5131 }
5132 
5133 static void
5134 do_delete_simnet(int argc, char *argv[], const char *use)
5135 {
5136 	int option;
5137 	uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5138 	datalink_id_t linkid;
5139 	char *altroot = NULL;
5140 	dladm_status_t status;
5141 	dladm_simnet_attr_t slinfo;
5142 
5143 	opterr = 0;
5144 	while ((option = getopt_long(argc, argv, ":tR:", simnet_lopts,
5145 	    NULL)) != -1) {
5146 		switch (option) {
5147 		case 't':
5148 			flags &= ~DLADM_OPT_PERSIST;
5149 			break;
5150 		case 'R':
5151 			altroot = optarg;
5152 			break;
5153 		default:
5154 			die_opterr(optopt, option, use);
5155 		}
5156 	}
5157 
5158 	/* get simnet name (required last argument) */
5159 	if (optind != (argc - 1))
5160 		usage();
5161 
5162 	if (!dladm_valid_linkname(argv[optind]))
5163 		die("invalid link name '%s'", argv[optind]);
5164 
5165 	if (altroot != NULL)
5166 		altroot_cmd(altroot, argc, argv);
5167 
5168 	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
5169 	    NULL);
5170 	if (status != DLADM_STATUS_OK)
5171 		die("simnet '%s' not found", argv[optind]);
5172 
5173 	if ((status = dladm_simnet_info(handle, linkid, &slinfo,
5174 	    flags)) != DLADM_STATUS_OK)
5175 		die_dlerr(status, "failed to retrieve simnet information");
5176 
5177 	status = dladm_simnet_delete(handle, linkid, flags);
5178 	if (status != DLADM_STATUS_OK)
5179 		die_dlerr(status, "simnet deletion failed");
5180 }
5181 
5182 static void
5183 do_modify_simnet(int argc, char *argv[], const char *use)
5184 {
5185 	int option;
5186 	uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5187 	datalink_id_t linkid;
5188 	datalink_id_t peer_linkid;
5189 	char *altroot = NULL;
5190 	dladm_status_t status;
5191 	boolean_t p_arg = B_FALSE;
5192 
5193 	opterr = 0;
5194 	while ((option = getopt_long(argc, argv, ":tR:p:", simnet_lopts,
5195 	    NULL)) != -1) {
5196 		switch (option) {
5197 		case 't':
5198 			flags &= ~DLADM_OPT_PERSIST;
5199 			break;
5200 		case 'R':
5201 			altroot = optarg;
5202 			break;
5203 		case 'p':
5204 			if (p_arg)
5205 				die_optdup(option);
5206 			p_arg = B_TRUE;
5207 			if (strcasecmp(optarg, "none") == 0)
5208 				peer_linkid = DATALINK_INVALID_LINKID;
5209 			else if (dladm_name2info(handle, optarg, &peer_linkid,
5210 			    NULL, NULL, NULL) != DLADM_STATUS_OK)
5211 				die("invalid peer link name '%s'", optarg);
5212 			break;
5213 		default:
5214 			die_opterr(optopt, option, use);
5215 		}
5216 	}
5217 
5218 	/* get simnet name (required last argument) */
5219 	if (optind != (argc - 1))
5220 		usage();
5221 
5222 	/* Nothing to do if no peer link argument */
5223 	if (!p_arg)
5224 		return;
5225 
5226 	if (altroot != NULL)
5227 		altroot_cmd(altroot, argc, argv);
5228 
5229 	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
5230 	    NULL);
5231 	if (status != DLADM_STATUS_OK)
5232 		die("invalid link name '%s'", argv[optind]);
5233 
5234 	status = dladm_simnet_modify(handle, linkid, peer_linkid, flags);
5235 	if (status != DLADM_STATUS_OK)
5236 		die_dlerr(status, "simnet modification failed");
5237 }
5238 
5239 static dladm_status_t
5240 print_simnet(show_state_t *state, datalink_id_t linkid)
5241 {
5242 	dladm_simnet_attr_t	slinfo;
5243 	uint32_t		flags;
5244 	dladm_status_t		status;
5245 	simnet_fields_buf_t	slbuf;
5246 	char			mstr[ETHERADDRL * 3];
5247 
5248 	bzero(&slbuf, sizeof (slbuf));
5249 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, NULL,
5250 	    slbuf.simnet_name, sizeof (slbuf.simnet_name)))
5251 	    != DLADM_STATUS_OK)
5252 		return (status);
5253 
5254 	if (!(state->ls_flags & flags))
5255 		return (DLADM_STATUS_NOTFOUND);
5256 
5257 	if ((status = dladm_simnet_info(handle, linkid, &slinfo,
5258 	    state->ls_flags)) != DLADM_STATUS_OK)
5259 		return (status);
5260 
5261 	if (slinfo.sna_peer_link_id != DATALINK_INVALID_LINKID &&
5262 	    (status = dladm_datalink_id2info(handle, slinfo.sna_peer_link_id,
5263 	    NULL, NULL, NULL, slbuf.simnet_otherlink,
5264 	    sizeof (slbuf.simnet_otherlink))) !=
5265 	    DLADM_STATUS_OK)
5266 		return (status);
5267 
5268 	if (slinfo.sna_mac_len > sizeof (slbuf.simnet_macaddr))
5269 		return (DLADM_STATUS_BADVAL);
5270 
5271 	(void) strlcpy(slbuf.simnet_macaddr,
5272 	    dladm_aggr_macaddr2str(slinfo.sna_mac_addr, mstr),
5273 	    sizeof (slbuf.simnet_macaddr));
5274 	(void) dladm_media2str(slinfo.sna_type, slbuf.simnet_media);
5275 
5276 	ofmt_print(state->ls_ofmt, &slbuf);
5277 	return (status);
5278 }
5279 
5280 /* ARGSUSED */
5281 static int
5282 show_simnet(dladm_handle_t dh, datalink_id_t linkid, void *arg)
5283 {
5284 	show_state_t		*state = arg;
5285 
5286 	state->ls_status = print_simnet(state, linkid);
5287 	return (DLADM_WALK_CONTINUE);
5288 }
5289 
5290 static void
5291 do_show_simnet(int argc, char *argv[], const char *use)
5292 {
5293 	int		option;
5294 	uint32_t	flags = DLADM_OPT_ACTIVE;
5295 	boolean_t	p_arg = B_FALSE;
5296 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
5297 	show_state_t	state;
5298 	dladm_status_t	status;
5299 	boolean_t	o_arg = B_FALSE;
5300 	ofmt_handle_t	ofmt;
5301 	ofmt_status_t	oferr;
5302 	char		*all_fields = "link,media,macaddress,otherlink";
5303 	char		*fields_str = all_fields;
5304 	uint_t		ofmtflags = 0;
5305 
5306 	bzero(&state, sizeof (state));
5307 
5308 	opterr = 0;
5309 	while ((option = getopt_long(argc, argv, ":pPo:",
5310 	    show_lopts, NULL)) != -1) {
5311 		switch (option) {
5312 		case 'p':
5313 			if (p_arg)
5314 				die_optdup(option);
5315 
5316 			p_arg = B_TRUE;
5317 			state.ls_parsable = p_arg;
5318 			break;
5319 		case 'P':
5320 			if (flags != DLADM_OPT_ACTIVE)
5321 				die_optdup(option);
5322 
5323 			flags = DLADM_OPT_PERSIST;
5324 			break;
5325 		case 'o':
5326 			o_arg = B_TRUE;
5327 			fields_str = optarg;
5328 			break;
5329 		default:
5330 			die_opterr(optopt, option, use);
5331 			break;
5332 		}
5333 	}
5334 
5335 	if (p_arg && !o_arg)
5336 		die("-p requires -o");
5337 
5338 	if (strcasecmp(fields_str, "all") == 0) {
5339 		if (p_arg)
5340 			die("\"-o all\" is invalid with -p");
5341 		fields_str = all_fields;
5342 	}
5343 
5344 	/* get link name (optional last argument) */
5345 	if (optind == (argc-1)) {
5346 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
5347 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
5348 			die_dlerr(status, "link %s is not valid", argv[optind]);
5349 		}
5350 	} else if (optind != argc) {
5351 		usage();
5352 	}
5353 
5354 	state.ls_flags = flags;
5355 	state.ls_donefirst = B_FALSE;
5356 	if (state.ls_parsable)
5357 		ofmtflags |= OFMT_PARSABLE;
5358 	oferr = ofmt_open(fields_str, simnet_fields, ofmtflags, 0, &ofmt);
5359 	dladm_ofmt_check(oferr, state.ls_parsable, ofmt);
5360 	state.ls_ofmt = ofmt;
5361 
5362 	if (linkid == DATALINK_ALL_LINKID) {
5363 		(void) dladm_walk_datalink_id(show_simnet, handle, &state,
5364 		    DATALINK_CLASS_SIMNET, DATALINK_ANY_MEDIATYPE, flags);
5365 	} else {
5366 		(void) show_simnet(handle, linkid, &state);
5367 		if (state.ls_status != DLADM_STATUS_OK) {
5368 			ofmt_close(ofmt);
5369 			die_dlerr(state.ls_status, "failed to show simnet %s",
5370 			    argv[optind]);
5371 		}
5372 	}
5373 	ofmt_close(ofmt);
5374 }
5375 
5376 static void
5377 link_stats(datalink_id_t linkid, uint_t interval, char *fields_str,
5378     show_state_t *state)
5379 {
5380 	ofmt_handle_t	ofmt;
5381 	ofmt_status_t	oferr;
5382 	uint_t		ofmtflags = 0;
5383 
5384 	if (state->ls_parsable)
5385 		ofmtflags |= OFMT_PARSABLE;
5386 	oferr = ofmt_open(fields_str, link_s_fields, ofmtflags, 0, &ofmt);
5387 	dladm_ofmt_check(oferr, state->ls_parsable, ofmt);
5388 	state->ls_ofmt = ofmt;
5389 
5390 	/*
5391 	 * If an interval is specified, continuously show the stats
5392 	 * only for the first MAC port.
5393 	 */
5394 	state->ls_firstonly = (interval != 0);
5395 
5396 	for (;;) {
5397 		state->ls_donefirst = B_FALSE;
5398 		if (linkid == DATALINK_ALL_LINKID) {
5399 			(void) dladm_walk_datalink_id(show_link_stats, handle,
5400 			    state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
5401 			    DLADM_OPT_ACTIVE);
5402 		} else {
5403 			(void) show_link_stats(handle, linkid, state);
5404 		}
5405 
5406 		if (interval == 0)
5407 			break;
5408 
5409 		(void) fflush(stdout);
5410 		(void) sleep(interval);
5411 	}
5412 	ofmt_close(ofmt);
5413 }
5414 
5415 static void
5416 aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval)
5417 {
5418 	/*
5419 	 * If an interval is specified, continuously show the stats
5420 	 * only for the first group.
5421 	 */
5422 	state->gs_firstonly = (interval != 0);
5423 
5424 	for (;;) {
5425 		state->gs_donefirst = B_FALSE;
5426 		if (linkid == DATALINK_ALL_LINKID)
5427 			(void) dladm_walk_datalink_id(show_aggr, handle, state,
5428 			    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
5429 			    DLADM_OPT_ACTIVE);
5430 		else
5431 			(void) show_aggr(handle, linkid, state);
5432 
5433 		if (interval == 0)
5434 			break;
5435 
5436 		(void) fflush(stdout);
5437 		(void) sleep(interval);
5438 	}
5439 }
5440 
5441 /* ARGSUSED */
5442 static void
5443 vnic_stats(show_vnic_state_t *sp, uint32_t interval)
5444 {
5445 	show_vnic_state_t	state;
5446 	boolean_t		specific_link, specific_dev;
5447 
5448 	/* Display vnic statistics */
5449 	dump_vnics_head(sp->vs_link);
5450 
5451 	bzero(&state, sizeof (state));
5452 	state.vs_stats = B_TRUE;
5453 	state.vs_vnic_id = sp->vs_vnic_id;
5454 	state.vs_link_id = sp->vs_link_id;
5455 
5456 	/*
5457 	 * If an interval is specified, and a vnic ID is not specified,
5458 	 * continuously show the stats only for the first vnic.
5459 	 */
5460 	specific_link = (sp->vs_vnic_id != DATALINK_ALL_LINKID);
5461 	specific_dev = (sp->vs_link_id != DATALINK_ALL_LINKID);
5462 
5463 	for (;;) {
5464 		/* Get stats for each vnic */
5465 		state.vs_found = B_FALSE;
5466 		state.vs_donefirst = B_FALSE;
5467 		state.vs_printstats = B_FALSE;
5468 		state.vs_flags = DLADM_OPT_ACTIVE;
5469 
5470 		if (!specific_link) {
5471 			(void) dladm_walk_datalink_id(show_vnic, handle, &state,
5472 			    DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE,
5473 			    DLADM_OPT_ACTIVE);
5474 		} else {
5475 			(void) show_vnic(handle, sp->vs_vnic_id, &state);
5476 			if (state.vs_status != DLADM_STATUS_OK) {
5477 				die_dlerr(state.vs_status,
5478 				    "failed to show vnic '%s'", sp->vs_vnic);
5479 			}
5480 		}
5481 
5482 		if (specific_link && !state.vs_found)
5483 			die("non-existent vnic '%s'", sp->vs_vnic);
5484 		if (specific_dev && !state.vs_found)
5485 			die("device %s has no vnics", sp->vs_link);
5486 
5487 		/* Show totals */
5488 		if ((specific_link | specific_dev) && !interval) {
5489 			(void) printf("Total");
5490 			(void) printf("\t%-10llu",
5491 			    state.vs_totalstats.ipackets);
5492 			(void) printf("%-12llu",
5493 			    state.vs_totalstats.rbytes);
5494 			(void) printf("%-10llu",
5495 			    state.vs_totalstats.opackets);
5496 			(void) printf("%-12llu\n",
5497 			    state.vs_totalstats.obytes);
5498 		}
5499 
5500 		/* Show stats for each vnic */
5501 		state.vs_donefirst = B_FALSE;
5502 		state.vs_printstats = B_TRUE;
5503 
5504 		if (!specific_link) {
5505 			(void) dladm_walk_datalink_id(show_vnic, handle, &state,
5506 			    DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE,
5507 			    DLADM_OPT_ACTIVE);
5508 		} else {
5509 			(void) show_vnic(handle, sp->vs_vnic_id, &state);
5510 			if (state.vs_status != DLADM_STATUS_OK) {
5511 				die_dlerr(state.vs_status,
5512 				    "failed to show vnic '%s'", sp->vs_vnic);
5513 			}
5514 		}
5515 
5516 		if (interval == 0)
5517 			break;
5518 
5519 		(void) fflush(stdout);
5520 		(void) sleep(interval);
5521 	}
5522 }
5523 
5524 static void
5525 get_mac_stats(const char *dev, pktsum_t *stats)
5526 {
5527 	kstat_ctl_t	*kcp;
5528 	kstat_t		*ksp;
5529 	char module[DLPI_LINKNAME_MAX];
5530 	uint_t instance;
5531 
5532 
5533 	bzero(stats, sizeof (*stats));
5534 
5535 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
5536 		return;
5537 
5538 	if ((kcp = kstat_open()) == NULL) {
5539 		warn("kstat open operation failed");
5540 		return;
5541 	}
5542 
5543 	ksp = dladm_kstat_lookup(kcp, module, instance, "mac", NULL);
5544 	if (ksp != NULL)
5545 		dladm_get_stats(kcp, ksp, stats);
5546 
5547 	(void) kstat_close(kcp);
5548 
5549 }
5550 
5551 static void
5552 get_link_stats(const char *link, pktsum_t *stats)
5553 {
5554 	kstat_ctl_t	*kcp;
5555 	kstat_t		*ksp;
5556 
5557 	bzero(stats, sizeof (*stats));
5558 
5559 	if ((kcp = kstat_open()) == NULL) {
5560 		warn("kstat_open operation failed");
5561 		return;
5562 	}
5563 
5564 	ksp = dladm_kstat_lookup(kcp, "link", 0, link, NULL);
5565 
5566 	if (ksp != NULL)
5567 		dladm_get_stats(kcp, ksp, stats);
5568 
5569 	(void) kstat_close(kcp);
5570 }
5571 
5572 static int
5573 query_kstat(char *module, int instance, const char *name, const char *stat,
5574     uint8_t type, void *val)
5575 {
5576 	kstat_ctl_t	*kcp;
5577 	kstat_t		*ksp;
5578 
5579 	if ((kcp = kstat_open()) == NULL) {
5580 		warn("kstat open operation failed");
5581 		return (-1);
5582 	}
5583 
5584 	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
5585 		/*
5586 		 * The kstat query could fail if the underlying MAC
5587 		 * driver was already detached.
5588 		 */
5589 		goto bail;
5590 	}
5591 
5592 	if (kstat_read(kcp, ksp, NULL) == -1) {
5593 		warn("kstat read failed");
5594 		goto bail;
5595 	}
5596 
5597 	if (dladm_kstat_value(ksp, stat, type, val) < 0)
5598 		goto bail;
5599 
5600 	(void) kstat_close(kcp);
5601 	return (0);
5602 
5603 bail:
5604 	(void) kstat_close(kcp);
5605 	return (-1);
5606 }
5607 
5608 static int
5609 get_one_kstat(const char *name, const char *stat, uint8_t type,
5610     void *val, boolean_t islink)
5611 {
5612 	char		module[DLPI_LINKNAME_MAX];
5613 	uint_t		instance;
5614 
5615 	if (islink) {
5616 		return (query_kstat("link", 0, name, stat, type, val));
5617 	} else {
5618 		if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
5619 			return (-1);
5620 
5621 		return (query_kstat(module, instance, "mac", stat, type, val));
5622 	}
5623 }
5624 
5625 static uint64_t
5626 get_ifspeed(const char *name, boolean_t islink)
5627 {
5628 	uint64_t ifspeed = 0;
5629 
5630 	(void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64,
5631 	    &ifspeed, islink);
5632 
5633 	return (ifspeed);
5634 }
5635 
5636 static const char *
5637 get_linkstate(const char *name, boolean_t islink, char *buf)
5638 {
5639 	link_state_t	linkstate;
5640 
5641 	if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32,
5642 	    &linkstate, islink) != 0) {
5643 		(void) strlcpy(buf, "?", DLADM_STRSIZE);
5644 		return (buf);
5645 	}
5646 	return (dladm_linkstate2str(linkstate, buf));
5647 }
5648 
5649 static const char *
5650 get_linkduplex(const char *name, boolean_t islink, char *buf)
5651 {
5652 	link_duplex_t	linkduplex;
5653 
5654 	if (get_one_kstat(name, "link_duplex", KSTAT_DATA_UINT32,
5655 	    &linkduplex, islink) != 0) {
5656 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
5657 		return (buf);
5658 	}
5659 
5660 	return (dladm_linkduplex2str(linkduplex, buf));
5661 }
5662 
5663 static int
5664 parse_wifi_fields(char *str, ofmt_handle_t *ofmt, uint_t cmdtype,
5665     boolean_t parsable)
5666 {
5667 	ofmt_field_t	*template, *of;
5668 	ofmt_cb_t	*fn;
5669 	ofmt_status_t	oferr;
5670 
5671 	if (cmdtype == WIFI_CMD_SCAN) {
5672 		template = wifi_common_fields;
5673 		if (str == NULL)
5674 			str = def_scan_wifi_fields;
5675 		if (strcasecmp(str, "all") == 0)
5676 			str = all_scan_wifi_fields;
5677 		fn = print_wlan_attr_cb;
5678 	} else if (cmdtype == WIFI_CMD_SHOW) {
5679 		bcopy(wifi_common_fields, &wifi_show_fields[2],
5680 		    sizeof (wifi_common_fields));
5681 		template = wifi_show_fields;
5682 		if (str == NULL)
5683 			str = def_show_wifi_fields;
5684 		if (strcasecmp(str, "all") == 0)
5685 			str = all_show_wifi_fields;
5686 		fn = print_link_attr_cb;
5687 	} else {
5688 		return (-1);
5689 	}
5690 
5691 	for (of = template; of->of_name != NULL; of++) {
5692 		if (of->of_cb == NULL)
5693 			of->of_cb = fn;
5694 	}
5695 
5696 	oferr = ofmt_open(str, template, (parsable ? OFMT_PARSABLE : 0),
5697 	    0, ofmt);
5698 	dladm_ofmt_check(oferr, parsable, *ofmt);
5699 	return (0);
5700 }
5701 
5702 typedef struct print_wifi_state {
5703 	char		*ws_link;
5704 	boolean_t	ws_parsable;
5705 	boolean_t	ws_header;
5706 	ofmt_handle_t	ws_ofmt;
5707 } print_wifi_state_t;
5708 
5709 typedef struct  wlan_scan_args_s {
5710 	print_wifi_state_t	*ws_state;
5711 	void			*ws_attr;
5712 } wlan_scan_args_t;
5713 
5714 static boolean_t
5715 print_wlan_attr_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
5716 {
5717 	wlan_scan_args_t	*w = ofarg->ofmt_cbarg;
5718 	print_wifi_state_t	*statep = w->ws_state;
5719 	dladm_wlan_attr_t	*attrp = w->ws_attr;
5720 	char			tmpbuf[DLADM_STRSIZE];
5721 
5722 	if (ofarg->ofmt_id == 0) {
5723 		(void) strlcpy(buf, (char *)statep->ws_link, bufsize);
5724 		return (B_TRUE);
5725 	}
5726 
5727 	if ((ofarg->ofmt_id & attrp->wa_valid) == 0)
5728 		return (B_TRUE);
5729 
5730 	switch (ofarg->ofmt_id) {
5731 	case DLADM_WLAN_ATTR_ESSID:
5732 		(void) dladm_wlan_essid2str(&attrp->wa_essid, tmpbuf);
5733 		break;
5734 	case DLADM_WLAN_ATTR_BSSID:
5735 		(void) dladm_wlan_bssid2str(&attrp->wa_bssid, tmpbuf);
5736 		break;
5737 	case DLADM_WLAN_ATTR_SECMODE:
5738 		(void) dladm_wlan_secmode2str(&attrp->wa_secmode, tmpbuf);
5739 		break;
5740 	case DLADM_WLAN_ATTR_STRENGTH:
5741 		(void) dladm_wlan_strength2str(&attrp->wa_strength, tmpbuf);
5742 		break;
5743 	case DLADM_WLAN_ATTR_MODE:
5744 		(void) dladm_wlan_mode2str(&attrp->wa_mode, tmpbuf);
5745 		break;
5746 	case DLADM_WLAN_ATTR_SPEED:
5747 		(void) dladm_wlan_speed2str(&attrp->wa_speed, tmpbuf);
5748 		(void) strlcat(tmpbuf, "Mb", sizeof (tmpbuf));
5749 		break;
5750 	case DLADM_WLAN_ATTR_AUTH:
5751 		(void) dladm_wlan_auth2str(&attrp->wa_auth, tmpbuf);
5752 		break;
5753 	case DLADM_WLAN_ATTR_BSSTYPE:
5754 		(void) dladm_wlan_bsstype2str(&attrp->wa_bsstype, tmpbuf);
5755 		break;
5756 	}
5757 	(void) strlcpy(buf, tmpbuf, bufsize);
5758 
5759 	return (B_TRUE);
5760 }
5761 
5762 static boolean_t
5763 print_scan_results(void *arg, dladm_wlan_attr_t *attrp)
5764 {
5765 	print_wifi_state_t	*statep = arg;
5766 	wlan_scan_args_t	warg;
5767 
5768 	bzero(&warg, sizeof (warg));
5769 	warg.ws_state = statep;
5770 	warg.ws_attr = attrp;
5771 	ofmt_print(statep->ws_ofmt, &warg);
5772 	return (B_TRUE);
5773 }
5774 
5775 static int
5776 scan_wifi(dladm_handle_t dh, datalink_id_t linkid, void *arg)
5777 {
5778 	print_wifi_state_t	*statep = arg;
5779 	dladm_status_t		status;
5780 	char			link[MAXLINKNAMELEN];
5781 
5782 	if ((status = dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, link,
5783 	    sizeof (link))) != DLADM_STATUS_OK) {
5784 		return (DLADM_WALK_CONTINUE);
5785 	}
5786 
5787 	statep->ws_link = link;
5788 	status = dladm_wlan_scan(dh, linkid, statep, print_scan_results);
5789 	if (status != DLADM_STATUS_OK)
5790 		die_dlerr(status, "cannot scan link '%s'", statep->ws_link);
5791 
5792 	return (DLADM_WALK_CONTINUE);
5793 }
5794 
5795 static boolean_t
5796 print_wifi_status_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
5797 {
5798 	static char		tmpbuf[DLADM_STRSIZE];
5799 	wlan_scan_args_t	*w = ofarg->ofmt_cbarg;
5800 	dladm_wlan_linkattr_t	*attrp = w->ws_attr;
5801 
5802 	if ((ofarg->ofmt_id & attrp->la_valid) != 0) {
5803 		(void) dladm_wlan_linkstatus2str(&attrp->la_status, tmpbuf);
5804 		(void) strlcpy(buf, tmpbuf, bufsize);
5805 	}
5806 	return (B_TRUE);
5807 }
5808 
5809 static boolean_t
5810 print_link_attr_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
5811 {
5812 	wlan_scan_args_t	*w = ofarg->ofmt_cbarg, w1;
5813 	print_wifi_state_t	*statep = w->ws_state;
5814 	dladm_wlan_linkattr_t	*attrp = w->ws_attr;
5815 
5816 	bzero(&w1, sizeof (w1));
5817 	w1.ws_state = statep;
5818 	w1.ws_attr = &attrp->la_wlan_attr;
5819 	ofarg->ofmt_cbarg = &w1;
5820 	return (print_wlan_attr_cb(ofarg, buf, bufsize));
5821 }
5822 
5823 static int
5824 show_wifi(dladm_handle_t dh, datalink_id_t linkid, void *arg)
5825 {
5826 	print_wifi_state_t	*statep = arg;
5827 	dladm_wlan_linkattr_t	attr;
5828 	dladm_status_t		status;
5829 	char			link[MAXLINKNAMELEN];
5830 	wlan_scan_args_t	warg;
5831 
5832 	if ((status = dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, link,
5833 	    sizeof (link))) != DLADM_STATUS_OK) {
5834 		return (DLADM_WALK_CONTINUE);
5835 	}
5836 
5837 	/* dladm_wlan_get_linkattr() memsets attr with 0 */
5838 	status = dladm_wlan_get_linkattr(dh, linkid, &attr);
5839 	if (status != DLADM_STATUS_OK)
5840 		die_dlerr(status, "cannot get link attributes for %s", link);
5841 
5842 	statep->ws_link = link;
5843 
5844 	bzero(&warg, sizeof (warg));
5845 	warg.ws_state = statep;
5846 	warg.ws_attr = &attr;
5847 	ofmt_print(statep->ws_ofmt, &warg);
5848 	return (DLADM_WALK_CONTINUE);
5849 }
5850 
5851 static void
5852 do_display_wifi(int argc, char **argv, int cmd, const char *use)
5853 {
5854 	int			option;
5855 	char			*fields_str = NULL;
5856 	int		(*callback)(dladm_handle_t, datalink_id_t, void *);
5857 	print_wifi_state_t	state;
5858 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
5859 	dladm_status_t		status;
5860 
5861 	if (cmd == WIFI_CMD_SCAN)
5862 		callback = scan_wifi;
5863 	else if (cmd == WIFI_CMD_SHOW)
5864 		callback = show_wifi;
5865 	else
5866 		return;
5867 
5868 	state.ws_parsable = B_FALSE;
5869 	state.ws_header = B_TRUE;
5870 	opterr = 0;
5871 	while ((option = getopt_long(argc, argv, ":o:p",
5872 	    wifi_longopts, NULL)) != -1) {
5873 		switch (option) {
5874 		case 'o':
5875 			fields_str = optarg;
5876 			break;
5877 		case 'p':
5878 			state.ws_parsable = B_TRUE;
5879 			break;
5880 		default:
5881 			die_opterr(optopt, option, use);
5882 		}
5883 	}
5884 
5885 	if (state.ws_parsable && fields_str == NULL)
5886 		die("-p requires -o");
5887 
5888 	if (state.ws_parsable && strcasecmp(fields_str, "all") == 0)
5889 		die("\"-o all\" is invalid with -p");
5890 
5891 	if (optind == (argc - 1)) {
5892 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
5893 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
5894 			die_dlerr(status, "link %s is not valid", argv[optind]);
5895 		}
5896 	} else if (optind != argc) {
5897 		usage();
5898 	}
5899 
5900 	if (parse_wifi_fields(fields_str, &state.ws_ofmt, cmd,
5901 	    state.ws_parsable) < 0)
5902 		die("invalid field(s) specified");
5903 
5904 	if (linkid == DATALINK_ALL_LINKID) {
5905 		(void) dladm_walk_datalink_id(callback, handle, &state,
5906 		    DATALINK_CLASS_PHYS | DATALINK_CLASS_SIMNET,
5907 		    DL_WIFI, DLADM_OPT_ACTIVE);
5908 	} else {
5909 		(void) (*callback)(handle, linkid, &state);
5910 	}
5911 	ofmt_close(state.ws_ofmt);
5912 }
5913 
5914 static void
5915 do_scan_wifi(int argc, char **argv, const char *use)
5916 {
5917 	do_display_wifi(argc, argv, WIFI_CMD_SCAN, use);
5918 }
5919 
5920 static void
5921 do_show_wifi(int argc, char **argv, const char *use)
5922 {
5923 	do_display_wifi(argc, argv, WIFI_CMD_SHOW, use);
5924 }
5925 
5926 typedef struct wlan_count_attr {
5927 	uint_t		wc_count;
5928 	datalink_id_t	wc_linkid;
5929 } wlan_count_attr_t;
5930 
5931 /* ARGSUSED */
5932 static int
5933 do_count_wlan(dladm_handle_t dh, datalink_id_t linkid, void *arg)
5934 {
5935 	wlan_count_attr_t *cp = arg;
5936 
5937 	if (cp->wc_count == 0)
5938 		cp->wc_linkid = linkid;
5939 	cp->wc_count++;
5940 	return (DLADM_WALK_CONTINUE);
5941 }
5942 
5943 static int
5944 parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp)
5945 {
5946 	uint_t			i;
5947 	dladm_wlan_key_t	*wk;
5948 	int			nfields = 1;
5949 	char			*field, *token, *lasts = NULL, c;
5950 
5951 	token = str;
5952 	while ((c = *token++) != NULL) {
5953 		if (c == ',')
5954 			nfields++;
5955 	}
5956 	token = strdup(str);
5957 	if (token == NULL)
5958 		return (-1);
5959 
5960 	wk = malloc(nfields * sizeof (dladm_wlan_key_t));
5961 	if (wk == NULL)
5962 		goto fail;
5963 
5964 	token = str;
5965 	for (i = 0; i < nfields; i++) {
5966 		char			*s;
5967 		dladm_secobj_class_t	class;
5968 		dladm_status_t		status;
5969 
5970 		field = strtok_r(token, ",", &lasts);
5971 		token = NULL;
5972 
5973 		(void) strlcpy(wk[i].wk_name, field,
5974 		    DLADM_WLAN_MAX_KEYNAME_LEN);
5975 
5976 		wk[i].wk_idx = 1;
5977 		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
5978 			if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
5979 				goto fail;
5980 
5981 			wk[i].wk_idx = (uint_t)(s[1] - '0');
5982 			*s = '\0';
5983 		}
5984 		wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN;
5985 
5986 		status = dladm_get_secobj(handle, wk[i].wk_name, &class,
5987 		    wk[i].wk_val, &wk[i].wk_len, 0);
5988 		if (status != DLADM_STATUS_OK) {
5989 			if (status == DLADM_STATUS_NOTFOUND) {
5990 				status = dladm_get_secobj(handle, wk[i].wk_name,
5991 				    &class, wk[i].wk_val, &wk[i].wk_len,
5992 				    DLADM_OPT_PERSIST);
5993 			}
5994 			if (status != DLADM_STATUS_OK)
5995 				goto fail;
5996 		}
5997 		wk[i].wk_class = class;
5998 	}
5999 	*keys = wk;
6000 	*key_countp = i;
6001 	free(token);
6002 	return (0);
6003 fail:
6004 	free(wk);
6005 	free(token);
6006 	return (-1);
6007 }
6008 
6009 static void
6010 do_connect_wifi(int argc, char **argv, const char *use)
6011 {
6012 	int			option;
6013 	dladm_wlan_attr_t	attr, *attrp;
6014 	dladm_status_t		status = DLADM_STATUS_OK;
6015 	int			timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
6016 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
6017 	dladm_wlan_key_t	*keys = NULL;
6018 	uint_t			key_count = 0;
6019 	uint_t			flags = 0;
6020 	dladm_wlan_secmode_t	keysecmode = DLADM_WLAN_SECMODE_NONE;
6021 	char			buf[DLADM_STRSIZE];
6022 
6023 	opterr = 0;
6024 	(void) memset(&attr, 0, sizeof (attr));
6025 	while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
6026 	    wifi_longopts, NULL)) != -1) {
6027 		switch (option) {
6028 		case 'e':
6029 			status = dladm_wlan_str2essid(optarg, &attr.wa_essid);
6030 			if (status != DLADM_STATUS_OK)
6031 				die("invalid ESSID '%s'", optarg);
6032 
6033 			attr.wa_valid |= DLADM_WLAN_ATTR_ESSID;
6034 			/*
6035 			 * Try to connect without doing a scan.
6036 			 */
6037 			flags |= DLADM_WLAN_CONNECT_NOSCAN;
6038 			break;
6039 		case 'i':
6040 			status = dladm_wlan_str2bssid(optarg, &attr.wa_bssid);
6041 			if (status != DLADM_STATUS_OK)
6042 				die("invalid BSSID %s", optarg);
6043 
6044 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
6045 			break;
6046 		case 'a':
6047 			status = dladm_wlan_str2auth(optarg, &attr.wa_auth);
6048 			if (status != DLADM_STATUS_OK)
6049 				die("invalid authentication mode '%s'", optarg);
6050 
6051 			attr.wa_valid |= DLADM_WLAN_ATTR_AUTH;
6052 			break;
6053 		case 'm':
6054 			status = dladm_wlan_str2mode(optarg, &attr.wa_mode);
6055 			if (status != DLADM_STATUS_OK)
6056 				die("invalid mode '%s'", optarg);
6057 
6058 			attr.wa_valid |= DLADM_WLAN_ATTR_MODE;
6059 			break;
6060 		case 'b':
6061 			if ((status = dladm_wlan_str2bsstype(optarg,
6062 			    &attr.wa_bsstype)) != DLADM_STATUS_OK) {
6063 				die("invalid bsstype '%s'", optarg);
6064 			}
6065 
6066 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
6067 			break;
6068 		case 's':
6069 			if ((status = dladm_wlan_str2secmode(optarg,
6070 			    &attr.wa_secmode)) != DLADM_STATUS_OK) {
6071 				die("invalid security mode '%s'", optarg);
6072 			}
6073 
6074 			attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
6075 			break;
6076 		case 'k':
6077 			if (parse_wlan_keys(optarg, &keys, &key_count) < 0)
6078 				die("invalid key(s) '%s'", optarg);
6079 
6080 			if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP)
6081 				keysecmode = DLADM_WLAN_SECMODE_WEP;
6082 			else
6083 				keysecmode = DLADM_WLAN_SECMODE_WPA;
6084 			break;
6085 		case 'T':
6086 			if (strcasecmp(optarg, "forever") == 0) {
6087 				timeout = -1;
6088 				break;
6089 			}
6090 			if (!str2int(optarg, &timeout) || timeout < 0)
6091 				die("invalid timeout value '%s'", optarg);
6092 			break;
6093 		case 'c':
6094 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
6095 			flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
6096 			break;
6097 		default:
6098 			die_opterr(optopt, option, use);
6099 			break;
6100 		}
6101 	}
6102 
6103 	if (keysecmode == DLADM_WLAN_SECMODE_NONE) {
6104 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) {
6105 			die("key required for security mode '%s'",
6106 			    dladm_wlan_secmode2str(&attr.wa_secmode, buf));
6107 		}
6108 	} else {
6109 		if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
6110 		    attr.wa_secmode != keysecmode)
6111 			die("incompatible -s and -k options");
6112 		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
6113 		attr.wa_secmode = keysecmode;
6114 	}
6115 
6116 	if (optind == (argc - 1)) {
6117 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
6118 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
6119 			die_dlerr(status, "link %s is not valid", argv[optind]);
6120 		}
6121 	} else if (optind != argc) {
6122 		usage();
6123 	}
6124 
6125 	if (linkid == DATALINK_ALL_LINKID) {
6126 		wlan_count_attr_t wcattr;
6127 
6128 		wcattr.wc_linkid = DATALINK_INVALID_LINKID;
6129 		wcattr.wc_count = 0;
6130 		(void) dladm_walk_datalink_id(do_count_wlan, handle, &wcattr,
6131 		    DATALINK_CLASS_PHYS | DATALINK_CLASS_SIMNET,
6132 		    DL_WIFI, DLADM_OPT_ACTIVE);
6133 		if (wcattr.wc_count == 0) {
6134 			die("no wifi links are available");
6135 		} else if (wcattr.wc_count > 1) {
6136 			die("link name is required when more than one wifi "
6137 			    "link is available");
6138 		}
6139 		linkid = wcattr.wc_linkid;
6140 	}
6141 	attrp = (attr.wa_valid == 0) ? NULL : &attr;
6142 again:
6143 	if ((status = dladm_wlan_connect(handle, linkid, attrp, timeout, keys,
6144 	    key_count, flags)) != DLADM_STATUS_OK) {
6145 		if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0) {
6146 			/*
6147 			 * Try again with scanning and filtering.
6148 			 */
6149 			flags &= ~DLADM_WLAN_CONNECT_NOSCAN;
6150 			goto again;
6151 		}
6152 
6153 		if (status == DLADM_STATUS_NOTFOUND) {
6154 			if (attr.wa_valid == 0) {
6155 				die("no wifi networks are available");
6156 			} else {
6157 				die("no wifi networks with the specified "
6158 				    "criteria are available");
6159 			}
6160 		}
6161 		die_dlerr(status, "cannot connect");
6162 	}
6163 	free(keys);
6164 }
6165 
6166 /* ARGSUSED */
6167 static int
6168 do_all_disconnect_wifi(dladm_handle_t dh, datalink_id_t linkid, void *arg)
6169 {
6170 	dladm_status_t	status;
6171 
6172 	status = dladm_wlan_disconnect(dh, linkid);
6173 	if (status != DLADM_STATUS_OK)
6174 		warn_dlerr(status, "cannot disconnect link");
6175 
6176 	return (DLADM_WALK_CONTINUE);
6177 }
6178 
6179 static void
6180 do_disconnect_wifi(int argc, char **argv, const char *use)
6181 {
6182 	int			option;
6183 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
6184 	boolean_t		all_links = B_FALSE;
6185 	dladm_status_t		status;
6186 	wlan_count_attr_t	wcattr;
6187 
6188 	opterr = 0;
6189 	while ((option = getopt_long(argc, argv, ":a",
6190 	    wifi_longopts, NULL)) != -1) {
6191 		switch (option) {
6192 		case 'a':
6193 			all_links = B_TRUE;
6194 			break;
6195 		default:
6196 			die_opterr(optopt, option, use);
6197 			break;
6198 		}
6199 	}
6200 
6201 	if (optind == (argc - 1)) {
6202 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
6203 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
6204 			die_dlerr(status, "link %s is not valid", argv[optind]);
6205 		}
6206 	} else if (optind != argc) {
6207 		usage();
6208 	}
6209 
6210 	if (linkid == DATALINK_ALL_LINKID) {
6211 		if (!all_links) {
6212 			wcattr.wc_linkid = linkid;
6213 			wcattr.wc_count = 0;
6214 			(void) dladm_walk_datalink_id(do_count_wlan, handle,
6215 			    &wcattr,
6216 			    DATALINK_CLASS_PHYS | DATALINK_CLASS_SIMNET,
6217 			    DL_WIFI, DLADM_OPT_ACTIVE);
6218 			if (wcattr.wc_count == 0) {
6219 				die("no wifi links are available");
6220 			} else if (wcattr.wc_count > 1) {
6221 				die("link name is required when more than "
6222 				    "one wifi link is available");
6223 			}
6224 			linkid = wcattr.wc_linkid;
6225 		} else {
6226 			(void) dladm_walk_datalink_id(do_all_disconnect_wifi,
6227 			    handle, NULL,
6228 			    DATALINK_CLASS_PHYS | DATALINK_CLASS_SIMNET,
6229 			    DL_WIFI, DLADM_OPT_ACTIVE);
6230 			return;
6231 		}
6232 	}
6233 	status = dladm_wlan_disconnect(handle, linkid);
6234 	if (status != DLADM_STATUS_OK)
6235 		die_dlerr(status, "cannot disconnect");
6236 }
6237 
6238 static void
6239 print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep,
6240     const char *propname, dladm_prop_type_t type, const char *format,
6241     char **pptr)
6242 {
6243 	int		i;
6244 	char		*ptr, *lim;
6245 	char		buf[DLADM_STRSIZE];
6246 	char		*unknown = "--", *notsup = "";
6247 	char		**propvals = statep->ls_propvals;
6248 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
6249 	dladm_status_t	status;
6250 
6251 	status = dladm_get_linkprop(handle, linkid, type, propname, propvals,
6252 	    &valcnt);
6253 	if (status != DLADM_STATUS_OK) {
6254 		if (status == DLADM_STATUS_TEMPONLY) {
6255 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
6256 			    statep->ls_persist) {
6257 				valcnt = 1;
6258 				propvals = &unknown;
6259 			} else {
6260 				statep->ls_status = status;
6261 				statep->ls_retstatus = status;
6262 				return;
6263 			}
6264 		} else if (status == DLADM_STATUS_NOTSUP ||
6265 		    statep->ls_persist) {
6266 			valcnt = 1;
6267 			if (type == DLADM_PROP_VAL_CURRENT ||
6268 			    type == DLADM_PROP_VAL_PERM)
6269 				propvals = &unknown;
6270 			else
6271 				propvals = &notsup;
6272 		} else if (status == DLADM_STATUS_NOTDEFINED) {
6273 			propvals = &notsup; /* STR_UNDEF_VAL */
6274 		} else {
6275 			if (statep->ls_proplist &&
6276 			    statep->ls_status == DLADM_STATUS_OK) {
6277 				warn_dlerr(status,
6278 				    "cannot get link property '%s' for %s",
6279 				    propname, statep->ls_link);
6280 			}
6281 			statep->ls_status = status;
6282 			statep->ls_retstatus = status;
6283 			return;
6284 		}
6285 	}
6286 
6287 	statep->ls_status = DLADM_STATUS_OK;
6288 
6289 	buf[0] = '\0';
6290 	ptr = buf;
6291 	lim = buf + DLADM_STRSIZE;
6292 	for (i = 0; i < valcnt; i++) {
6293 		if (propvals[i][0] == '\0' && !statep->ls_parsable)
6294 			ptr += snprintf(ptr, lim - ptr, "--,");
6295 		else
6296 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
6297 		if (ptr >= lim)
6298 			break;
6299 	}
6300 	if (valcnt > 0)
6301 		buf[strlen(buf) - 1] = '\0';
6302 
6303 	lim = statep->ls_line + MAX_PROP_LINE;
6304 	if (statep->ls_parsable) {
6305 		*pptr += snprintf(*pptr, lim - *pptr,
6306 		    "%s", buf);
6307 	} else {
6308 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
6309 	}
6310 }
6311 
6312 static boolean_t
6313 print_linkprop_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
6314 {
6315 	linkprop_args_t		*arg = ofarg->ofmt_cbarg;
6316 	char 			*propname = arg->ls_propname;
6317 	show_linkprop_state_t	*statep = arg->ls_state;
6318 	char			*ptr = statep->ls_line;
6319 	char			*lim = ptr + MAX_PROP_LINE;
6320 	datalink_id_t		linkid = arg->ls_linkid;
6321 
6322 	switch (ofarg->ofmt_id) {
6323 	case LINKPROP_LINK:
6324 		(void) snprintf(ptr, lim - ptr, "%s", statep->ls_link);
6325 		break;
6326 	case LINKPROP_PROPERTY:
6327 		(void) snprintf(ptr, lim - ptr, "%s", propname);
6328 		break;
6329 	case LINKPROP_VALUE:
6330 		print_linkprop(linkid, statep, propname,
6331 		    statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
6332 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
6333 		/*
6334 		 * If we failed to query the link property, for example, query
6335 		 * the persistent value of a non-persistable link property,
6336 		 * simply skip the output.
6337 		 */
6338 		if (statep->ls_status != DLADM_STATUS_OK)
6339 			goto skip;
6340 		ptr = statep->ls_line;
6341 		break;
6342 	case LINKPROP_PERM:
6343 		print_linkprop(linkid, statep, propname,
6344 		    DLADM_PROP_VAL_PERM, "%s", &ptr);
6345 		if (statep->ls_status != DLADM_STATUS_OK)
6346 			goto skip;
6347 		ptr = statep->ls_line;
6348 		break;
6349 	case LINKPROP_DEFAULT:
6350 		print_linkprop(linkid, statep, propname,
6351 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
6352 		if (statep->ls_status != DLADM_STATUS_OK)
6353 			goto skip;
6354 		ptr = statep->ls_line;
6355 		break;
6356 	case LINKPROP_POSSIBLE:
6357 		print_linkprop(linkid, statep, propname,
6358 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
6359 		if (statep->ls_status != DLADM_STATUS_OK)
6360 			goto skip;
6361 		ptr = statep->ls_line;
6362 		break;
6363 	default:
6364 		die("invalid input");
6365 		break;
6366 	}
6367 	(void) strlcpy(buf, ptr, bufsize);
6368 	return (B_TRUE);
6369 skip:
6370 	return ((statep->ls_status == DLADM_STATUS_OK) ?
6371 	    B_TRUE : B_FALSE);
6372 }
6373 
6374 static boolean_t
6375 linkprop_is_supported(datalink_id_t  linkid, const char *propname,
6376     show_linkprop_state_t *statep)
6377 {
6378 	dladm_status_t	status;
6379 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
6380 
6381 	/* if used with -p flag, always print output */
6382 	if (statep->ls_proplist != NULL)
6383 		return (B_TRUE);
6384 
6385 	status = dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_DEFAULT,
6386 	    propname, statep->ls_propvals, &valcnt);
6387 
6388 	if (status == DLADM_STATUS_OK)
6389 		return (B_TRUE);
6390 
6391 	/*
6392 	 * A system wide default value is not available for the
6393 	 * property. Check if current value can be retrieved.
6394 	 */
6395 	status = dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_CURRENT,
6396 	    propname, statep->ls_propvals, &valcnt);
6397 
6398 	return (status == DLADM_STATUS_OK);
6399 }
6400 
6401 /* ARGSUSED */
6402 static int
6403 show_linkprop(dladm_handle_t dh, datalink_id_t linkid, const char *propname,
6404     void *arg)
6405 {
6406 	show_linkprop_state_t	*statep = arg;
6407 	linkprop_args_t		ls_arg;
6408 
6409 	bzero(&ls_arg, sizeof (ls_arg));
6410 	ls_arg.ls_state = statep;
6411 	ls_arg.ls_propname = (char *)propname;
6412 	ls_arg.ls_linkid = linkid;
6413 
6414 	/*
6415 	 * This will need to be fixed when kernel interfaces are added
6416 	 * to enable walking of all known private properties. For now,
6417 	 * we are limited to walking persistent private properties only.
6418 	 */
6419 	if ((propname[0] == '_') && !statep->ls_persist &&
6420 	    (statep->ls_proplist == NULL))
6421 		return (DLADM_WALK_CONTINUE);
6422 	if (!statep->ls_parsable &&
6423 	    !linkprop_is_supported(linkid, propname, statep))
6424 		return (DLADM_WALK_CONTINUE);
6425 
6426 	ofmt_print(statep->ls_ofmt, &ls_arg);
6427 
6428 	return (DLADM_WALK_CONTINUE);
6429 }
6430 
6431 static void
6432 do_show_linkprop(int argc, char **argv, const char *use)
6433 {
6434 	int			option;
6435 	char			propstr[DLADM_STRSIZE];
6436 	dladm_arg_list_t	*proplist = NULL;
6437 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
6438 	show_linkprop_state_t	state;
6439 	uint32_t		flags = DLADM_OPT_ACTIVE;
6440 	dladm_status_t		status;
6441 	char			*fields_str = NULL;
6442 	ofmt_handle_t		ofmt;
6443 	ofmt_status_t		oferr;
6444 	uint_t			ofmtflags = 0;
6445 
6446 	bzero(propstr, DLADM_STRSIZE);
6447 	opterr = 0;
6448 	state.ls_propvals = NULL;
6449 	state.ls_line = NULL;
6450 	state.ls_parsable = B_FALSE;
6451 	state.ls_persist = B_FALSE;
6452 	state.ls_header = B_TRUE;
6453 	state.ls_retstatus = DLADM_STATUS_OK;
6454 
6455 	while ((option = getopt_long(argc, argv, ":p:cPo:",
6456 	    prop_longopts, NULL)) != -1) {
6457 		switch (option) {
6458 		case 'p':
6459 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
6460 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
6461 			    DLADM_STRSIZE)
6462 				die("property list too long '%s'", propstr);
6463 			break;
6464 		case 'c':
6465 			state.ls_parsable = B_TRUE;
6466 			break;
6467 		case 'P':
6468 			state.ls_persist = B_TRUE;
6469 			flags = DLADM_OPT_PERSIST;
6470 			break;
6471 		case 'o':
6472 			fields_str = optarg;
6473 			break;
6474 		default:
6475 			die_opterr(optopt, option, use);
6476 			break;
6477 		}
6478 	}
6479 
6480 	if (optind == (argc - 1)) {
6481 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
6482 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
6483 			die_dlerr(status, "link %s is not valid", argv[optind]);
6484 		}
6485 	} else if (optind != argc) {
6486 		usage();
6487 	}
6488 
6489 	if (dladm_parse_link_props(propstr, &proplist, B_TRUE)
6490 	    != DLADM_STATUS_OK)
6491 		die("invalid link properties specified");
6492 	state.ls_proplist = proplist;
6493 	state.ls_status = DLADM_STATUS_OK;
6494 
6495 	if (state.ls_parsable)
6496 		ofmtflags |= OFMT_PARSABLE;
6497 	else
6498 		ofmtflags |= OFMT_WRAP;
6499 
6500 	oferr = ofmt_open(fields_str, linkprop_fields, ofmtflags, 0, &ofmt);
6501 	dladm_ofmt_check(oferr, state.ls_parsable, ofmt);
6502 	state.ls_ofmt = ofmt;
6503 
6504 	if (linkid == DATALINK_ALL_LINKID) {
6505 		(void) dladm_walk_datalink_id(show_linkprop_onelink, handle,
6506 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
6507 	} else {
6508 		(void) show_linkprop_onelink(handle, linkid, &state);
6509 	}
6510 	ofmt_close(ofmt);
6511 	dladm_free_props(proplist);
6512 
6513 	if (state.ls_retstatus != DLADM_STATUS_OK) {
6514 		dladm_close(handle);
6515 		exit(EXIT_FAILURE);
6516 	}
6517 }
6518 
6519 static int
6520 show_linkprop_onelink(dladm_handle_t hdl, datalink_id_t linkid, void *arg)
6521 {
6522 	int			i;
6523 	char			*buf;
6524 	uint32_t		flags;
6525 	dladm_arg_list_t	*proplist = NULL;
6526 	show_linkprop_state_t	*statep = arg;
6527 	dlpi_handle_t		dh = NULL;
6528 
6529 	statep->ls_status = DLADM_STATUS_OK;
6530 
6531 	if (dladm_datalink_id2info(hdl, linkid, &flags, NULL, NULL,
6532 	    statep->ls_link, MAXLINKNAMELEN) != DLADM_STATUS_OK) {
6533 		statep->ls_status = DLADM_STATUS_NOTFOUND;
6534 		return (DLADM_WALK_CONTINUE);
6535 	}
6536 
6537 	if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) ||
6538 	    (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) {
6539 		statep->ls_status = DLADM_STATUS_BADARG;
6540 		return (DLADM_WALK_CONTINUE);
6541 	}
6542 
6543 	proplist = statep->ls_proplist;
6544 
6545 	/*
6546 	 * When some WiFi links are opened for the first time, their hardware
6547 	 * automatically scans for APs and does other slow operations.	Thus,
6548 	 * if there are no open links, the retrieval of link properties
6549 	 * (below) will proceed slowly unless we hold the link open.
6550 	 *
6551 	 * Note that failure of dlpi_open() does not necessarily mean invalid
6552 	 * link properties, because dlpi_open() may fail because of incorrect
6553 	 * autopush configuration. Therefore, we ingore the return value of
6554 	 * dlpi_open().
6555 	 */
6556 	if (!statep->ls_persist)
6557 		(void) dlpi_open(statep->ls_link, &dh, 0);
6558 
6559 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
6560 	    DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
6561 	if (buf == NULL)
6562 		die("insufficient memory");
6563 
6564 	statep->ls_propvals = (char **)(void *)buf;
6565 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
6566 		statep->ls_propvals[i] = buf +
6567 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
6568 		    i * DLADM_PROP_VAL_MAX;
6569 	}
6570 	statep->ls_line = buf +
6571 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
6572 
6573 	if (proplist != NULL) {
6574 		for (i = 0; i < proplist->al_count; i++) {
6575 			(void) show_linkprop(hdl, linkid,
6576 			    proplist->al_info[i].ai_name, statep);
6577 		}
6578 	} else {
6579 		(void) dladm_walk_linkprop(hdl, linkid, statep,
6580 		    show_linkprop);
6581 	}
6582 	if (dh != NULL)
6583 		dlpi_close(dh);
6584 	free(buf);
6585 	return (DLADM_WALK_CONTINUE);
6586 }
6587 
6588 static int
6589 reset_one_linkprop(dladm_handle_t dh, datalink_id_t linkid,
6590     const char *propname, void *arg)
6591 {
6592 	set_linkprop_state_t	*statep = arg;
6593 	dladm_status_t		status;
6594 
6595 	status = dladm_set_linkprop(dh, linkid, propname, NULL, 0,
6596 	    DLADM_OPT_ACTIVE | (statep->ls_temp ? 0 : DLADM_OPT_PERSIST));
6597 	if (status != DLADM_STATUS_OK &&
6598 	    status != DLADM_STATUS_PROPRDONLY &&
6599 	    status != DLADM_STATUS_NOTSUP) {
6600 		warn_dlerr(status, "cannot reset link property '%s' on '%s'",
6601 		    propname, statep->ls_name);
6602 		statep->ls_status = status;
6603 	}
6604 
6605 	return (DLADM_WALK_CONTINUE);
6606 }
6607 
6608 static void
6609 set_linkprop(int argc, char **argv, boolean_t reset, const char *use)
6610 {
6611 	int			i, option;
6612 	char			errmsg[DLADM_STRSIZE];
6613 	char			*altroot = NULL;
6614 	datalink_id_t		linkid;
6615 	boolean_t		temp = B_FALSE;
6616 	dladm_status_t		status = DLADM_STATUS_OK;
6617 	char			propstr[DLADM_STRSIZE];
6618 	dladm_arg_list_t	*proplist = NULL;
6619 
6620 	opterr = 0;
6621 	bzero(propstr, DLADM_STRSIZE);
6622 
6623 	while ((option = getopt_long(argc, argv, ":p:R:t",
6624 	    prop_longopts, NULL)) != -1) {
6625 		switch (option) {
6626 		case 'p':
6627 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
6628 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
6629 			    DLADM_STRSIZE)
6630 				die("property list too long '%s'", propstr);
6631 			break;
6632 		case 't':
6633 			temp = B_TRUE;
6634 			break;
6635 		case 'R':
6636 			altroot = optarg;
6637 			break;
6638 		default:
6639 			die_opterr(optopt, option, use);
6640 
6641 		}
6642 	}
6643 
6644 	/* get link name (required last argument) */
6645 	if (optind != (argc - 1))
6646 		usage();
6647 
6648 	if (dladm_parse_link_props(propstr, &proplist, reset) !=
6649 	    DLADM_STATUS_OK)
6650 		die("invalid link properties specified");
6651 
6652 	if (proplist == NULL && !reset)
6653 		die("link property must be specified");
6654 
6655 	if (altroot != NULL) {
6656 		dladm_free_props(proplist);
6657 		altroot_cmd(altroot, argc, argv);
6658 	}
6659 
6660 	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
6661 	    NULL);
6662 	if (status != DLADM_STATUS_OK)
6663 		die_dlerr(status, "link %s is not valid", argv[optind]);
6664 
6665 	if (proplist == NULL) {
6666 		set_linkprop_state_t	state;
6667 
6668 		state.ls_name = argv[optind];
6669 		state.ls_reset = reset;
6670 		state.ls_temp = temp;
6671 		state.ls_status = DLADM_STATUS_OK;
6672 
6673 		(void) dladm_walk_linkprop(handle, linkid, &state,
6674 		    reset_one_linkprop);
6675 
6676 		status = state.ls_status;
6677 		goto done;
6678 	}
6679 
6680 	for (i = 0; i < proplist->al_count; i++) {
6681 		dladm_arg_info_t	*aip = &proplist->al_info[i];
6682 		char		**val;
6683 		uint_t		count;
6684 
6685 		if (reset) {
6686 			val = NULL;
6687 			count = 0;
6688 		} else {
6689 			val = aip->ai_val;
6690 			count = aip->ai_count;
6691 			if (count == 0) {
6692 				warn("no value specified for '%s'",
6693 				    aip->ai_name);
6694 				status = DLADM_STATUS_BADARG;
6695 				continue;
6696 			}
6697 		}
6698 		status = dladm_set_linkprop(handle, linkid, aip->ai_name, val,
6699 		    count, DLADM_OPT_ACTIVE | (temp ? 0 : DLADM_OPT_PERSIST));
6700 		switch (status) {
6701 		case DLADM_STATUS_OK:
6702 			break;
6703 		case DLADM_STATUS_NOTFOUND:
6704 			warn("invalid link property '%s'", aip->ai_name);
6705 			break;
6706 		case DLADM_STATUS_BADVAL: {
6707 			int		j;
6708 			char		*ptr, *lim;
6709 			char		**propvals = NULL;
6710 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
6711 			dladm_status_t	s;
6712 
6713 			ptr = malloc((sizeof (char *) +
6714 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
6715 			    MAX_PROP_LINE);
6716 
6717 			propvals = (char **)(void *)ptr;
6718 			if (propvals == NULL)
6719 				die("insufficient memory");
6720 
6721 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
6722 				propvals[j] = ptr + sizeof (char *) *
6723 				    DLADM_MAX_PROP_VALCNT +
6724 				    j * DLADM_PROP_VAL_MAX;
6725 			}
6726 			s = dladm_get_linkprop(handle, linkid,
6727 			    DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals,
6728 			    &valcnt);
6729 
6730 			if (s != DLADM_STATUS_OK) {
6731 				warn_dlerr(status, "cannot set link property "
6732 				    "'%s' on '%s'", aip->ai_name, argv[optind]);
6733 				free(propvals);
6734 				break;
6735 			}
6736 
6737 			ptr = errmsg;
6738 			lim = ptr + DLADM_STRSIZE;
6739 			*ptr = '\0';
6740 			for (j = 0; j < valcnt; j++) {
6741 				ptr += snprintf(ptr, lim - ptr, "%s,",
6742 				    propvals[j]);
6743 				if (ptr >= lim)
6744 					break;
6745 			}
6746 			if (ptr > errmsg) {
6747 				*(ptr - 1) = '\0';
6748 				warn("link property '%s' must be one of: %s",
6749 				    aip->ai_name, errmsg);
6750 			} else
6751 				warn("invalid link property '%s'", *val);
6752 			free(propvals);
6753 			break;
6754 		}
6755 		default:
6756 			if (reset) {
6757 				warn_dlerr(status, "cannot reset link property "
6758 				    "'%s' on '%s'", aip->ai_name, argv[optind]);
6759 			} else {
6760 				warn_dlerr(status, "cannot set link property "
6761 				    "'%s' on '%s'", aip->ai_name, argv[optind]);
6762 			}
6763 			break;
6764 		}
6765 	}
6766 done:
6767 	dladm_free_props(proplist);
6768 	if (status != DLADM_STATUS_OK) {
6769 		dladm_close(handle);
6770 		exit(EXIT_FAILURE);
6771 	}
6772 }
6773 
6774 static void
6775 do_set_linkprop(int argc, char **argv, const char *use)
6776 {
6777 	set_linkprop(argc, argv, B_FALSE, use);
6778 }
6779 
6780 static void
6781 do_reset_linkprop(int argc, char **argv, const char *use)
6782 {
6783 	set_linkprop(argc, argv, B_TRUE, use);
6784 }
6785 
6786 static int
6787 convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
6788     dladm_secobj_class_t class)
6789 {
6790 	int error = 0;
6791 
6792 	if (class == DLADM_SECOBJ_CLASS_WPA) {
6793 		if (len < 8 || len > 63)
6794 			return (EINVAL);
6795 		(void) memcpy(obj_val, buf, len);
6796 		*obj_lenp = len;
6797 		return (error);
6798 	}
6799 
6800 	if (class == DLADM_SECOBJ_CLASS_WEP) {
6801 		switch (len) {
6802 		case 5:			/* ASCII key sizes */
6803 		case 13:
6804 			(void) memcpy(obj_val, buf, len);
6805 			*obj_lenp = len;
6806 			break;
6807 		case 10:		/* Hex key sizes, not preceded by 0x */
6808 		case 26:
6809 			error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
6810 			break;
6811 		case 12:		/* Hex key sizes, preceded by 0x */
6812 		case 28:
6813 			if (strncmp(buf, "0x", 2) != 0)
6814 				return (EINVAL);
6815 			error = hexascii_to_octet(buf + 2, len - 2,
6816 			    obj_val, obj_lenp);
6817 			break;
6818 		default:
6819 			return (EINVAL);
6820 		}
6821 		return (error);
6822 	}
6823 
6824 	return (ENOENT);
6825 }
6826 
6827 static void
6828 defersig(int sig)
6829 {
6830 	signalled = sig;
6831 }
6832 
6833 static int
6834 get_secobj_from_tty(uint_t try, const char *objname, char *buf)
6835 {
6836 	uint_t		len = 0;
6837 	int		c;
6838 	struct termios	stored, current;
6839 	void		(*sigfunc)(int);
6840 
6841 	/*
6842 	 * Turn off echo -- but before we do so, defer SIGINT handling
6843 	 * so that a ^C doesn't leave the terminal corrupted.
6844 	 */
6845 	sigfunc = signal(SIGINT, defersig);
6846 	(void) fflush(stdin);
6847 	(void) tcgetattr(0, &stored);
6848 	current = stored;
6849 	current.c_lflag &= ~(ICANON|ECHO);
6850 	current.c_cc[VTIME] = 0;
6851 	current.c_cc[VMIN] = 1;
6852 	(void) tcsetattr(0, TCSANOW, &current);
6853 again:
6854 	if (try == 1)
6855 		(void) printf(gettext("provide value for '%s': "), objname);
6856 	else
6857 		(void) printf(gettext("confirm value for '%s': "), objname);
6858 
6859 	(void) fflush(stdout);
6860 	while (signalled == 0) {
6861 		c = getchar();
6862 		if (c == '\n' || c == '\r') {
6863 			if (len != 0)
6864 				break;
6865 			(void) putchar('\n');
6866 			goto again;
6867 		}
6868 
6869 		buf[len++] = c;
6870 		if (len >= DLADM_SECOBJ_VAL_MAX - 1)
6871 			break;
6872 		(void) putchar('*');
6873 	}
6874 
6875 	(void) putchar('\n');
6876 	(void) fflush(stdin);
6877 
6878 	/*
6879 	 * Restore terminal setting and handle deferred signals.
6880 	 */
6881 	(void) tcsetattr(0, TCSANOW, &stored);
6882 
6883 	(void) signal(SIGINT, sigfunc);
6884 	if (signalled != 0)
6885 		(void) kill(getpid(), signalled);
6886 
6887 	return (len);
6888 }
6889 
6890 static int
6891 get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
6892     dladm_secobj_class_t class, FILE *filep)
6893 {
6894 	int		rval;
6895 	uint_t		len, len2;
6896 	char		buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
6897 
6898 	if (filep == NULL) {
6899 		len = get_secobj_from_tty(1, obj_name, buf);
6900 		rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
6901 		if (rval == 0) {
6902 			len2 = get_secobj_from_tty(2, obj_name, buf2);
6903 			if (len != len2 || memcmp(buf, buf2, len) != 0)
6904 				rval = ENOTSUP;
6905 		}
6906 		return (rval);
6907 	} else {
6908 		for (;;) {
6909 			if (fgets(buf, sizeof (buf), filep) == NULL)
6910 				break;
6911 			if (isspace(buf[0]))
6912 				continue;
6913 
6914 			len = strlen(buf);
6915 			if (buf[len - 1] == '\n') {
6916 				buf[len - 1] = '\0';
6917 				len--;
6918 			}
6919 			break;
6920 		}
6921 		(void) fclose(filep);
6922 	}
6923 	return (convert_secobj(buf, len, obj_val, obj_lenp, class));
6924 }
6925 
6926 static boolean_t
6927 check_auth(const char *auth)
6928 {
6929 	struct passwd	*pw;
6930 
6931 	if ((pw = getpwuid(getuid())) == NULL)
6932 		return (B_FALSE);
6933 
6934 	return (chkauthattr(auth, pw->pw_name) != 0);
6935 }
6936 
6937 static void
6938 audit_secobj(char *auth, char *class, char *obj,
6939     boolean_t success, boolean_t create)
6940 {
6941 	adt_session_data_t	*ah;
6942 	adt_event_data_t	*event;
6943 	au_event_t		flag;
6944 	char			*errstr;
6945 
6946 	if (create) {
6947 		flag = ADT_dladm_create_secobj;
6948 		errstr = "ADT_dladm_create_secobj";
6949 	} else {
6950 		flag = ADT_dladm_delete_secobj;
6951 		errstr = "ADT_dladm_delete_secobj";
6952 	}
6953 
6954 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
6955 		die("adt_start_session: %s", strerror(errno));
6956 
6957 	if ((event = adt_alloc_event(ah, flag)) == NULL)
6958 		die("adt_alloc_event (%s): %s", errstr, strerror(errno));
6959 
6960 	/* fill in audit info */
6961 	if (create) {
6962 		event->adt_dladm_create_secobj.auth_used = auth;
6963 		event->adt_dladm_create_secobj.obj_class = class;
6964 		event->adt_dladm_create_secobj.obj_name = obj;
6965 	} else {
6966 		event->adt_dladm_delete_secobj.auth_used = auth;
6967 		event->adt_dladm_delete_secobj.obj_class = class;
6968 		event->adt_dladm_delete_secobj.obj_name = obj;
6969 	}
6970 
6971 	if (success) {
6972 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
6973 			die("adt_put_event (%s, success): %s", errstr,
6974 			    strerror(errno));
6975 		}
6976 	} else {
6977 		if (adt_put_event(event, ADT_FAILURE,
6978 		    ADT_FAIL_VALUE_AUTH) != 0) {
6979 			die("adt_put_event: (%s, failure): %s", errstr,
6980 			    strerror(errno));
6981 		}
6982 	}
6983 
6984 	adt_free_event(event);
6985 	(void) adt_end_session(ah);
6986 }
6987 
6988 #define	MAX_SECOBJS		32
6989 #define	MAX_SECOBJ_NAMELEN	32
6990 static void
6991 do_create_secobj(int argc, char **argv, const char *use)
6992 {
6993 	int			option, rval;
6994 	FILE			*filep = NULL;
6995 	char			*obj_name = NULL;
6996 	char			*class_name = NULL;
6997 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
6998 	uint_t			obj_len;
6999 	boolean_t		success, temp = B_FALSE;
7000 	dladm_status_t		status;
7001 	dladm_secobj_class_t	class = -1;
7002 	uid_t			euid;
7003 
7004 	opterr = 0;
7005 	(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
7006 	while ((option = getopt_long(argc, argv, ":f:c:R:t",
7007 	    wifi_longopts, NULL)) != -1) {
7008 		switch (option) {
7009 		case 'f':
7010 			euid = geteuid();
7011 			(void) seteuid(getuid());
7012 			filep = fopen(optarg, "r");
7013 			if (filep == NULL) {
7014 				die("cannot open %s: %s", optarg,
7015 				    strerror(errno));
7016 			}
7017 			(void) seteuid(euid);
7018 			break;
7019 		case 'c':
7020 			class_name = optarg;
7021 			status = dladm_str2secobjclass(optarg, &class);
7022 			if (status != DLADM_STATUS_OK) {
7023 				die("invalid secure object class '%s', "
7024 				    "valid values are: wep, wpa", optarg);
7025 			}
7026 			break;
7027 		case 't':
7028 			temp = B_TRUE;
7029 			break;
7030 		case 'R':
7031 			status = dladm_set_rootdir(optarg);
7032 			if (status != DLADM_STATUS_OK) {
7033 				die_dlerr(status, "invalid directory "
7034 				    "specified");
7035 			}
7036 			break;
7037 		default:
7038 			die_opterr(optopt, option, use);
7039 			break;
7040 		}
7041 	}
7042 
7043 	if (optind == (argc - 1))
7044 		obj_name = argv[optind];
7045 	else if (optind != argc)
7046 		usage();
7047 
7048 	if (class == -1)
7049 		die("secure object class required");
7050 
7051 	if (obj_name == NULL)
7052 		die("secure object name required");
7053 
7054 	if (!dladm_valid_secobj_name(obj_name))
7055 		die("invalid secure object name '%s'", obj_name);
7056 
7057 	success = check_auth(LINK_SEC_AUTH);
7058 	audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
7059 	if (!success)
7060 		die("authorization '%s' is required", LINK_SEC_AUTH);
7061 
7062 	rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
7063 	if (rval != 0) {
7064 		switch (rval) {
7065 		case ENOENT:
7066 			die("invalid secure object class");
7067 			break;
7068 		case EINVAL:
7069 			die("invalid secure object value");
7070 			break;
7071 		case ENOTSUP:
7072 			die("verification failed");
7073 			break;
7074 		default:
7075 			die("invalid secure object: %s", strerror(rval));
7076 			break;
7077 		}
7078 	}
7079 
7080 	status = dladm_set_secobj(handle, obj_name, class, obj_val, obj_len,
7081 	    DLADM_OPT_CREATE | DLADM_OPT_ACTIVE);
7082 	if (status != DLADM_STATUS_OK) {
7083 		die_dlerr(status, "could not create secure object '%s'",
7084 		    obj_name);
7085 	}
7086 	if (temp)
7087 		return;
7088 
7089 	status = dladm_set_secobj(handle, obj_name, class, obj_val, obj_len,
7090 	    DLADM_OPT_PERSIST);
7091 	if (status != DLADM_STATUS_OK) {
7092 		warn_dlerr(status, "could not persistently create secure "
7093 		    "object '%s'", obj_name);
7094 	}
7095 }
7096 
7097 static void
7098 do_delete_secobj(int argc, char **argv, const char *use)
7099 {
7100 	int		i, option;
7101 	boolean_t	temp = B_FALSE;
7102 	boolean_t	success;
7103 	dladm_status_t	status, pstatus;
7104 	int		nfields = 1;
7105 	char		*field, *token, *lasts = NULL, c;
7106 
7107 	opterr = 0;
7108 	status = pstatus = DLADM_STATUS_OK;
7109 	while ((option = getopt_long(argc, argv, ":R:t",
7110 	    wifi_longopts, NULL)) != -1) {
7111 		switch (option) {
7112 		case 't':
7113 			temp = B_TRUE;
7114 			break;
7115 		case 'R':
7116 			status = dladm_set_rootdir(optarg);
7117 			if (status != DLADM_STATUS_OK) {
7118 				die_dlerr(status, "invalid directory "
7119 				    "specified");
7120 			}
7121 			break;
7122 		default:
7123 			die_opterr(optopt, option, use);
7124 			break;
7125 		}
7126 	}
7127 
7128 	if (optind == (argc - 1)) {
7129 		token = argv[optind];
7130 		if (token == NULL)
7131 			die("secure object name required");
7132 		while ((c = *token++) != NULL) {
7133 			if (c == ',')
7134 				nfields++;
7135 		}
7136 		token = strdup(argv[optind]);
7137 		if (token == NULL)
7138 			die("no memory");
7139 	} else if (optind != argc)
7140 		usage();
7141 
7142 	success = check_auth(LINK_SEC_AUTH);
7143 	audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE);
7144 	if (!success)
7145 		die("authorization '%s' is required", LINK_SEC_AUTH);
7146 
7147 	for (i = 0; i < nfields; i++) {
7148 
7149 		field = strtok_r(token, ",", &lasts);
7150 		token = NULL;
7151 		status = dladm_unset_secobj(handle, field, DLADM_OPT_ACTIVE);
7152 		if (!temp) {
7153 			pstatus = dladm_unset_secobj(handle, field,
7154 			    DLADM_OPT_PERSIST);
7155 		} else {
7156 			pstatus = DLADM_STATUS_OK;
7157 		}
7158 
7159 		if (status != DLADM_STATUS_OK) {
7160 			warn_dlerr(status, "could not delete secure object "
7161 			    "'%s'", field);
7162 		}
7163 		if (pstatus != DLADM_STATUS_OK) {
7164 			warn_dlerr(pstatus, "could not persistently delete "
7165 			    "secure object '%s'", field);
7166 		}
7167 	}
7168 	free(token);
7169 
7170 	if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK) {
7171 		dladm_close(handle);
7172 		exit(EXIT_FAILURE);
7173 	}
7174 }
7175 
7176 typedef struct show_secobj_state {
7177 	boolean_t	ss_persist;
7178 	boolean_t	ss_parsable;
7179 	boolean_t	ss_header;
7180 	ofmt_handle_t	ss_ofmt;
7181 } show_secobj_state_t;
7182 
7183 
7184 static boolean_t
7185 show_secobj(dladm_handle_t dh, void *arg, const char *obj_name)
7186 {
7187 	uint_t			obj_len = DLADM_SECOBJ_VAL_MAX;
7188 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
7189 	char			buf[DLADM_STRSIZE];
7190 	uint_t			flags = 0;
7191 	dladm_secobj_class_t	class;
7192 	show_secobj_state_t	*statep = arg;
7193 	dladm_status_t		status;
7194 	secobj_fields_buf_t	sbuf;
7195 
7196 	bzero(&sbuf, sizeof (secobj_fields_buf_t));
7197 	if (statep->ss_persist)
7198 		flags |= DLADM_OPT_PERSIST;
7199 
7200 	status = dladm_get_secobj(dh, obj_name, &class, obj_val, &obj_len,
7201 	    flags);
7202 	if (status != DLADM_STATUS_OK)
7203 		die_dlerr(status, "cannot get secure object '%s'", obj_name);
7204 
7205 	(void) snprintf(sbuf.ss_obj_name, sizeof (sbuf.ss_obj_name),
7206 	    obj_name);
7207 	(void) dladm_secobjclass2str(class, buf);
7208 	(void) snprintf(sbuf.ss_class, sizeof (sbuf.ss_class), "%s", buf);
7209 	if (getuid() == 0) {
7210 		char	val[DLADM_SECOBJ_VAL_MAX * 2];
7211 		uint_t	len = sizeof (val);
7212 
7213 		if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0)
7214 			(void) snprintf(sbuf.ss_val,
7215 			    sizeof (sbuf.ss_val), "%s", val);
7216 	}
7217 	ofmt_print(statep->ss_ofmt, &sbuf);
7218 	return (B_TRUE);
7219 }
7220 
7221 static void
7222 do_show_secobj(int argc, char **argv, const char *use)
7223 {
7224 	int			option;
7225 	show_secobj_state_t	state;
7226 	dladm_status_t		status;
7227 	boolean_t		o_arg = B_FALSE;
7228 	uint_t			i;
7229 	uint_t			flags;
7230 	char			*fields_str = NULL;
7231 	char			*def_fields = "object,class";
7232 	char			*all_fields = "object,class,value";
7233 	char			*field, *token, *lasts = NULL, c;
7234 	ofmt_handle_t		ofmt;
7235 	ofmt_status_t		oferr;
7236 	uint_t			ofmtflags = 0;
7237 
7238 	opterr = 0;
7239 	bzero(&state, sizeof (state));
7240 	state.ss_parsable = B_FALSE;
7241 	fields_str = def_fields;
7242 	state.ss_persist = B_FALSE;
7243 	state.ss_parsable = B_FALSE;
7244 	state.ss_header = B_TRUE;
7245 	while ((option = getopt_long(argc, argv, ":pPo:",
7246 	    wifi_longopts, NULL)) != -1) {
7247 		switch (option) {
7248 		case 'p':
7249 			state.ss_parsable = B_TRUE;
7250 			break;
7251 		case 'P':
7252 			state.ss_persist = B_TRUE;
7253 			break;
7254 		case 'o':
7255 			o_arg = B_TRUE;
7256 			if (strcasecmp(optarg, "all") == 0)
7257 				fields_str = all_fields;
7258 			else
7259 				fields_str = optarg;
7260 			break;
7261 		default:
7262 			die_opterr(optopt, option, use);
7263 			break;
7264 		}
7265 	}
7266 
7267 	if (state.ss_parsable && !o_arg)
7268 		die("option -c requires -o");
7269 
7270 	if (state.ss_parsable && fields_str == all_fields)
7271 		die("\"-o all\" is invalid with -p");
7272 
7273 	if (state.ss_parsable)
7274 		ofmtflags |= OFMT_PARSABLE;
7275 	oferr = ofmt_open(fields_str, secobj_fields, ofmtflags, 0, &ofmt);
7276 	dladm_ofmt_check(oferr, state.ss_parsable, ofmt);
7277 	state.ss_ofmt = ofmt;
7278 
7279 	flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
7280 
7281 	if (optind == (argc - 1)) {
7282 		uint_t obj_fields = 1;
7283 
7284 		token = argv[optind];
7285 		if (token == NULL)
7286 			die("secure object name required");
7287 		while ((c = *token++) != NULL) {
7288 			if (c == ',')
7289 				obj_fields++;
7290 		}
7291 		token = strdup(argv[optind]);
7292 		if (token == NULL)
7293 			die("no memory");
7294 		for (i = 0; i < obj_fields; i++) {
7295 			field = strtok_r(token, ",", &lasts);
7296 			token = NULL;
7297 			if (!show_secobj(handle, &state, field))
7298 				break;
7299 		}
7300 		free(token);
7301 		ofmt_close(ofmt);
7302 		return;
7303 	} else if (optind != argc)
7304 		usage();
7305 
7306 	status = dladm_walk_secobj(handle, &state, show_secobj, flags);
7307 
7308 	if (status != DLADM_STATUS_OK)
7309 		die_dlerr(status, "show-secobj");
7310 	ofmt_close(ofmt);
7311 }
7312 
7313 /*ARGSUSED*/
7314 static int
7315 i_dladm_init_linkprop(dladm_handle_t dh, datalink_id_t linkid, void *arg)
7316 {
7317 	(void) dladm_init_linkprop(dh, linkid, B_TRUE);
7318 	return (DLADM_WALK_CONTINUE);
7319 }
7320 
7321 /*ARGSUSED*/
7322 void
7323 do_init_linkprop(int argc, char **argv, const char *use)
7324 {
7325 	int			option;
7326 	dladm_status_t		status;
7327 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
7328 	datalink_media_t	media = DATALINK_ANY_MEDIATYPE;
7329 	uint_t			any_media = B_TRUE;
7330 
7331 	opterr = 0;
7332 	while ((option = getopt(argc, argv, ":w")) != -1) {
7333 		switch (option) {
7334 		case 'w':
7335 			media = DL_WIFI;
7336 			any_media = B_FALSE;
7337 			break;
7338 		default:
7339 			/*
7340 			 * Because init-linkprop is not a public command,
7341 			 * print the usage instead.
7342 			 */
7343 			usage();
7344 			break;
7345 		}
7346 	}
7347 
7348 	if (optind == (argc - 1)) {
7349 		if ((status = dladm_name2info(handle, argv[optind], &linkid,
7350 		    NULL, NULL, NULL)) != DLADM_STATUS_OK)
7351 			die_dlerr(status, "link %s is not valid", argv[optind]);
7352 	} else if (optind != argc) {
7353 		usage();
7354 	}
7355 
7356 	if (linkid == DATALINK_ALL_LINKID) {
7357 		/*
7358 		 * linkprops of links of other classes have been initialized as
7359 		 * part of the dladm up-xxx operation.
7360 		 */
7361 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, handle,
7362 		    NULL, DATALINK_CLASS_PHYS, media, DLADM_OPT_PERSIST);
7363 	} else {
7364 		(void) dladm_init_linkprop(handle, linkid, any_media);
7365 	}
7366 }
7367 
7368 static void
7369 do_show_ether(int argc, char **argv, const char *use)
7370 {
7371 	int 			option;
7372 	datalink_id_t		linkid;
7373 	print_ether_state_t 	state;
7374 	char			*fields_str = NULL;
7375 	ofmt_handle_t		ofmt;
7376 	ofmt_status_t		oferr;
7377 	uint_t			ofmtflags = 0;
7378 
7379 	bzero(&state, sizeof (state));
7380 	state.es_link = NULL;
7381 	state.es_parsable = B_FALSE;
7382 
7383 	while ((option = getopt_long(argc, argv, "o:px",
7384 	    showeth_lopts, NULL)) != -1) {
7385 		switch (option) {
7386 			case 'x':
7387 				state.es_extended = B_TRUE;
7388 				break;
7389 			case 'p':
7390 				state.es_parsable = B_TRUE;
7391 				break;
7392 			case 'o':
7393 				fields_str = optarg;
7394 				break;
7395 			default:
7396 				die_opterr(optopt, option, use);
7397 				break;
7398 		}
7399 	}
7400 
7401 	if (optind == (argc - 1))
7402 		state.es_link = argv[optind];
7403 
7404 	if (state.es_parsable)
7405 		ofmtflags |= OFMT_PARSABLE;
7406 	oferr = ofmt_open(fields_str, ether_fields, ofmtflags,
7407 	    DLADM_DEFAULT_COL, &ofmt);
7408 	dladm_ofmt_check(oferr, state.es_parsable, ofmt);
7409 	state.es_ofmt = ofmt;
7410 
7411 	if (state.es_link == NULL) {
7412 		(void) dladm_walk_datalink_id(show_etherprop, handle, &state,
7413 		    DATALINK_CLASS_PHYS, DL_ETHER, DLADM_OPT_ACTIVE);
7414 	} else {
7415 		if (!link_is_ether(state.es_link, &linkid))
7416 			die("invalid link specified");
7417 		(void) show_etherprop(handle, linkid, &state);
7418 	}
7419 	ofmt_close(ofmt);
7420 }
7421 
7422 static int
7423 show_etherprop(dladm_handle_t dh, datalink_id_t linkid, void *arg)
7424 {
7425 	print_ether_state_t	*statep = arg;
7426 	ether_fields_buf_t	ebuf;
7427 	dladm_ether_info_t	eattr;
7428 	dladm_status_t		status;
7429 
7430 	bzero(&ebuf, sizeof (ether_fields_buf_t));
7431 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL,
7432 	    ebuf.eth_link, sizeof (ebuf.eth_link)) != DLADM_STATUS_OK) {
7433 		return (DLADM_WALK_CONTINUE);
7434 	}
7435 
7436 	status = dladm_ether_info(dh, linkid, &eattr);
7437 	if (status != DLADM_STATUS_OK)
7438 		goto cleanup;
7439 
7440 	(void) strlcpy(ebuf.eth_ptype, "current", sizeof (ebuf.eth_ptype));
7441 
7442 	(void) dladm_ether_autoneg2str(ebuf.eth_autoneg,
7443 	    sizeof (ebuf.eth_autoneg), &eattr, CURRENT);
7444 	(void) dladm_ether_pause2str(ebuf.eth_pause,
7445 	    sizeof (ebuf.eth_pause), &eattr, CURRENT);
7446 	(void) dladm_ether_spdx2str(ebuf.eth_spdx,
7447 	    sizeof (ebuf.eth_spdx), &eattr, CURRENT);
7448 	(void) strlcpy(ebuf.eth_state,
7449 	    dladm_linkstate2str(eattr.lei_state, ebuf.eth_state),
7450 	    sizeof (ebuf.eth_state));
7451 	(void) strlcpy(ebuf.eth_rem_fault,
7452 	    (eattr.lei_attr[CURRENT].le_fault ? "fault" : "none"),
7453 	    sizeof (ebuf.eth_rem_fault));
7454 
7455 	ofmt_print(statep->es_ofmt, &ebuf);
7456 
7457 	if (statep->es_extended)
7458 		show_ether_xprop(arg, &eattr);
7459 
7460 cleanup:
7461 	dladm_ether_info_done(&eattr);
7462 	return (DLADM_WALK_CONTINUE);
7463 }
7464 
7465 /* ARGSUSED */
7466 static void
7467 do_init_secobj(int argc, char **argv, const char *use)
7468 {
7469 	dladm_status_t	status;
7470 
7471 	status = dladm_init_secobj(handle);
7472 	if (status != DLADM_STATUS_OK)
7473 		die_dlerr(status, "secure object initialization failed");
7474 }
7475 
7476 enum bridge_func {
7477 	brCreate, brAdd, brModify
7478 };
7479 
7480 static void
7481 create_modify_add_bridge(int argc, char **argv, const char *use,
7482     enum bridge_func func)
7483 {
7484 	int			option;
7485 	uint_t			n, i, nlink;
7486 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
7487 	char			*altroot = NULL;
7488 	char			*links[MAXPORT];
7489 	datalink_id_t		linkids[MAXPORT];
7490 	dladm_status_t		status;
7491 	const char		*bridge;
7492 	UID_STP_CFG_T		cfg, cfg_old;
7493 	dladm_bridge_prot_t	brprot = DLADM_BRIDGE_PROT_UNKNOWN;
7494 	dladm_bridge_prot_t	brprot_old;
7495 
7496 	/* Set up the default configuration values */
7497 	cfg.field_mask = 0;
7498 	cfg.bridge_priority = DEF_BR_PRIO;
7499 	cfg.max_age = DEF_BR_MAXAGE;
7500 	cfg.hello_time = DEF_BR_HELLOT;
7501 	cfg.forward_delay = DEF_BR_FWDELAY;
7502 	cfg.force_version = DEF_FORCE_VERS;
7503 
7504 	nlink = opterr = 0;
7505 	while ((option = getopt_long(argc, argv, ":P:R:d:f:h:l:m:p:",
7506 	    bridge_lopts, NULL)) != -1) {
7507 		switch (option) {
7508 		case 'P':
7509 			if (func == brAdd)
7510 				die_opterr(optopt, option, use);
7511 			status = dladm_bridge_str2prot(optarg, &brprot);
7512 			if (status != DLADM_STATUS_OK)
7513 				die_dlerr(status, "protection %s", optarg);
7514 			break;
7515 		case 'R':
7516 			altroot = optarg;
7517 			break;
7518 		case 'd':
7519 			if (func == brAdd)
7520 				die_opterr(optopt, option, use);
7521 			if (cfg.field_mask & BR_CFG_DELAY)
7522 				die("forwarding delay set more than once");
7523 			if (!str2int(optarg, &cfg.forward_delay) ||
7524 			    cfg.forward_delay < MIN_BR_FWDELAY ||
7525 			    cfg.forward_delay > MAX_BR_FWDELAY)
7526 				die("incorrect forwarding delay");
7527 			cfg.field_mask |= BR_CFG_DELAY;
7528 			break;
7529 		case 'f':
7530 			if (func == brAdd)
7531 				die_opterr(optopt, option, use);
7532 			if (cfg.field_mask & BR_CFG_FORCE_VER)
7533 				die("force protocol set more than once");
7534 			if (!str2int(optarg, &cfg.force_version) ||
7535 			    cfg.force_version < 0)
7536 				die("incorrect force protocol");
7537 			cfg.field_mask |= BR_CFG_FORCE_VER;
7538 			break;
7539 		case 'h':
7540 			if (func == brAdd)
7541 				die_opterr(optopt, option, use);
7542 			if (cfg.field_mask & BR_CFG_HELLO)
7543 				die("hello time set more than once");
7544 			if (!str2int(optarg, &cfg.hello_time) ||
7545 			    cfg.hello_time < MIN_BR_HELLOT ||
7546 			    cfg.hello_time > MAX_BR_HELLOT)
7547 				die("incorrect hello time");
7548 			cfg.field_mask |= BR_CFG_HELLO;
7549 			break;
7550 		case 'l':
7551 			if (func == brModify)
7552 				die_opterr(optopt, option, use);
7553 			if (nlink >= MAXPORT)
7554 				die("too many links specified");
7555 			links[nlink++] = optarg;
7556 			break;
7557 		case 'm':
7558 			if (func == brAdd)
7559 				die_opterr(optopt, option, use);
7560 			if (cfg.field_mask & BR_CFG_AGE)
7561 				die("max age set more than once");
7562 			if (!str2int(optarg, &cfg.max_age) ||
7563 			    cfg.max_age < MIN_BR_MAXAGE ||
7564 			    cfg.max_age > MAX_BR_MAXAGE)
7565 				die("incorrect max age");
7566 			cfg.field_mask |= BR_CFG_AGE;
7567 			break;
7568 		case 'p':
7569 			if (func == brAdd)
7570 				die_opterr(optopt, option, use);
7571 			if (cfg.field_mask & BR_CFG_PRIO)
7572 				die("priority set more than once");
7573 			if (!str2int(optarg, &cfg.bridge_priority) ||
7574 			    cfg.bridge_priority < MIN_BR_PRIO ||
7575 			    cfg.bridge_priority > MAX_BR_PRIO)
7576 				die("incorrect priority");
7577 			cfg.bridge_priority &= 0xF000;
7578 			cfg.field_mask |= BR_CFG_PRIO;
7579 			break;
7580 		default:
7581 			die_opterr(optopt, option, use);
7582 			break;
7583 		}
7584 	}
7585 
7586 	/* get the bridge name (required last argument) */
7587 	if (optind != (argc-1))
7588 		usage();
7589 
7590 	bridge = argv[optind];
7591 	if (!dladm_valid_bridgename(bridge))
7592 		die("invalid bridge name '%s'", bridge);
7593 
7594 	/*
7595 	 * Get the current properties, if any, and merge in with changes.  This
7596 	 * is necessary (even with the field_mask feature) so that the
7597 	 * value-checking macros will produce the right results with proposed
7598 	 * changes to existing configuration.  We only need it for those
7599 	 * parameters, though.
7600 	 */
7601 	(void) dladm_bridge_get_properties(bridge, &cfg_old, &brprot_old);
7602 	if (brprot == DLADM_BRIDGE_PROT_UNKNOWN)
7603 		brprot = brprot_old;
7604 	if (!(cfg.field_mask & BR_CFG_AGE))
7605 		cfg.max_age = cfg_old.max_age;
7606 	if (!(cfg.field_mask & BR_CFG_HELLO))
7607 		cfg.hello_time = cfg_old.hello_time;
7608 	if (!(cfg.field_mask & BR_CFG_DELAY))
7609 		cfg.forward_delay = cfg_old.forward_delay;
7610 
7611 	if (!CHECK_BRIDGE_CONFIG(cfg)) {
7612 		warn("illegal forward delay / max age / hello time "
7613 		    "combination");
7614 		if (NO_MAXAGE(cfg)) {
7615 			die("no max age possible: need forward delay >= %d or "
7616 			    "hello time <= %d", MIN_FWDELAY_NOM(cfg),
7617 			    MAX_HELLOTIME_NOM(cfg));
7618 		} else if (SMALL_MAXAGE(cfg)) {
7619 			if (CAPPED_MAXAGE(cfg))
7620 				die("max age too small: need age >= %d and "
7621 				    "<= %d or hello time <= %d",
7622 				    MIN_MAXAGE(cfg), MAX_MAXAGE(cfg),
7623 				    MAX_HELLOTIME(cfg));
7624 			else
7625 				die("max age too small: need age >= %d or "
7626 				    "hello time <= %d",
7627 				    MIN_MAXAGE(cfg), MAX_HELLOTIME(cfg));
7628 		} else if (FLOORED_MAXAGE(cfg)) {
7629 			die("max age too large: need age >= %d and <= %d or "
7630 			    "forward delay >= %d",
7631 			    MIN_MAXAGE(cfg), MAX_MAXAGE(cfg),
7632 			    MIN_FWDELAY(cfg));
7633 		} else {
7634 			die("max age too large: need age <= %d or forward "
7635 			    "delay >= %d",
7636 			    MAX_MAXAGE(cfg), MIN_FWDELAY(cfg));
7637 		}
7638 	}
7639 
7640 	if (altroot != NULL)
7641 		altroot_cmd(altroot, argc, argv);
7642 
7643 	for (n = 0; n < nlink; n++) {
7644 		datalink_class_t class;
7645 		uint32_t media;
7646 		char pointless[DLADM_STRSIZE];
7647 
7648 		if (dladm_name2info(handle, links[n], &linkids[n], NULL, &class,
7649 		    &media) != DLADM_STATUS_OK)
7650 			die("invalid link name '%s'", links[n]);
7651 		if (class & ~(DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR |
7652 		    DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET))
7653 			die("%s %s cannot be bridged",
7654 			    dladm_class2str(class, pointless), links[n]);
7655 		if (media != DL_ETHER && media != DL_100VG &&
7656 		    media != DL_ETH_CSMA && media != DL_100BT)
7657 			die("%s interface %s cannot be bridged",
7658 			    dladm_media2str(media, pointless), links[n]);
7659 	}
7660 
7661 	if (func == brCreate)
7662 		flags |= DLADM_OPT_CREATE;
7663 
7664 	if (func != brAdd) {
7665 		status = dladm_bridge_configure(handle, bridge, &cfg, brprot,
7666 		    flags);
7667 		if (status != DLADM_STATUS_OK)
7668 			die_dlerr(status, "create operation failed");
7669 	}
7670 
7671 	status = DLADM_STATUS_OK;
7672 	for (n = 0; n < nlink; n++) {
7673 		status = dladm_bridge_setlink(handle, linkids[n], bridge);
7674 		if (status != DLADM_STATUS_OK)
7675 			break;
7676 	}
7677 
7678 	if (n >= nlink) {
7679 		/*
7680 		 * We were successful.  If we're creating a new bridge, then
7681 		 * there's just one more step: enabling.  If we're modifying or
7682 		 * just adding links, then we're done.
7683 		 */
7684 		if (func != brCreate ||
7685 		    (status = dladm_bridge_enable(bridge)) == DLADM_STATUS_OK)
7686 			return;
7687 	}
7688 
7689 	/* clean up the partial configuration */
7690 	for (i = 0; i < n; i++)
7691 		(void) dladm_bridge_setlink(handle, linkids[i], "");
7692 
7693 	/* if failure for brCreate, then delete the bridge */
7694 	if (func == brCreate)
7695 		(void) dladm_bridge_delete(handle, bridge, flags);
7696 
7697 	if (n < nlink)
7698 		die_dlerr(status, "unable to add link %s to bridge %s",
7699 		    links[n], bridge);
7700 	else
7701 		die_dlerr(status, "unable to enable bridge %s", bridge);
7702 }
7703 
7704 static void
7705 do_create_bridge(int argc, char **argv, const char *use)
7706 {
7707 	create_modify_add_bridge(argc, argv, use, brCreate);
7708 }
7709 
7710 static void
7711 do_modify_bridge(int argc, char **argv, const char *use)
7712 {
7713 	create_modify_add_bridge(argc, argv, use, brModify);
7714 }
7715 
7716 static void
7717 do_add_bridge(int argc, char **argv, const char *use)
7718 {
7719 	create_modify_add_bridge(argc, argv, use, brAdd);
7720 }
7721 
7722 static void
7723 do_delete_bridge(int argc, char **argv, const char *use)
7724 {
7725 	char			option;
7726 	char			*altroot = NULL;
7727 	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
7728 	dladm_status_t		status;
7729 
7730 	opterr = 0;
7731 	while ((option = getopt_long(argc, argv, ":R:", bridge_lopts, NULL)) !=
7732 	    -1) {
7733 		switch (option) {
7734 		case 'R':
7735 			altroot = optarg;
7736 			break;
7737 		default:
7738 			die_opterr(optopt, option, use);
7739 			break;
7740 		}
7741 	}
7742 
7743 	/* get the bridge name (required last argument) */
7744 	if (optind != (argc-1))
7745 		usage();
7746 
7747 	if (altroot != NULL)
7748 		altroot_cmd(altroot, argc, argv);
7749 
7750 	status = dladm_bridge_delete(handle, argv[optind], flags);
7751 	if (status != DLADM_STATUS_OK)
7752 		die_dlerr(status, "delete operation failed");
7753 }
7754 
7755 static void
7756 do_remove_bridge(int argc, char **argv, const char *use)
7757 {
7758 	char		option;
7759 	uint_t		n, nlink;
7760 	char		*links[MAXPORT];
7761 	datalink_id_t	linkids[MAXPORT];
7762 	char		*altroot = NULL;
7763 	dladm_status_t	status;
7764 	boolean_t	removed_one;
7765 
7766 	nlink = opterr = 0;
7767 	while ((option = getopt_long(argc, argv, ":R:l:", bridge_lopts,
7768 	    NULL)) != -1) {
7769 		switch (option) {
7770 		case 'R':
7771 			altroot = optarg;
7772 			break;
7773 		case 'l':
7774 			if (nlink >= MAXPORT)
7775 				die("too many links specified");
7776 			links[nlink++] = optarg;
7777 			break;
7778 		default:
7779 			die_opterr(optopt, option, use);
7780 			break;
7781 		}
7782 	}
7783 
7784 	if (nlink == 0)
7785 		usage();
7786 
7787 	/* get the bridge name (required last argument) */
7788 	if (optind != (argc-1))
7789 		usage();
7790 
7791 	if (altroot != NULL)
7792 		altroot_cmd(altroot, argc, argv);
7793 
7794 	for (n = 0; n < nlink; n++) {
7795 		char bridge[MAXLINKNAMELEN];
7796 
7797 		if (dladm_name2info(handle, links[n], &linkids[n], NULL, NULL,
7798 		    NULL) != DLADM_STATUS_OK)
7799 			die("invalid link name '%s'", links[n]);
7800 		status = dladm_bridge_getlink(handle, linkids[n], bridge,
7801 		    sizeof (bridge));
7802 		if (status != DLADM_STATUS_OK &&
7803 		    status != DLADM_STATUS_NOTFOUND) {
7804 			die_dlerr(status, "cannot get bridge status on %s",
7805 			    links[n]);
7806 		}
7807 		if (status == DLADM_STATUS_NOTFOUND ||
7808 		    strcmp(bridge, argv[optind]) != 0)
7809 			die("link %s is not on bridge %s", links[n],
7810 			    argv[optind]);
7811 	}
7812 
7813 	removed_one = B_FALSE;
7814 	for (n = 0; n < nlink; n++) {
7815 		status = dladm_bridge_setlink(handle, linkids[n], "");
7816 		if (status == DLADM_STATUS_OK) {
7817 			removed_one = B_TRUE;
7818 		} else {
7819 			warn_dlerr(status,
7820 			    "cannot remove link %s from bridge %s",
7821 			    links[n], argv[optind]);
7822 		}
7823 	}
7824 	if (!removed_one)
7825 		die("unable to remove any links from bridge %s", argv[optind]);
7826 }
7827 
7828 static void
7829 fmt_int(char *buf, size_t buflen, int value, int runvalue,
7830     boolean_t printstar)
7831 {
7832 	(void) snprintf(buf, buflen, "%d", value);
7833 	if (value != runvalue && printstar)
7834 		(void) strlcat(buf, "*", buflen);
7835 }
7836 
7837 static void
7838 fmt_bridge_id(char *buf, size_t buflen, UID_BRIDGE_ID_T *bid)
7839 {
7840 	(void) snprintf(buf, buflen, "%u/%x:%x:%x:%x:%x:%x", bid->prio,
7841 	    bid->addr[0], bid->addr[1], bid->addr[2], bid->addr[3],
7842 	    bid->addr[4], bid->addr[5]);
7843 }
7844 
7845 static dladm_status_t
7846 print_bridge(show_state_t *state, datalink_id_t linkid,
7847     bridge_fields_buf_t *bbuf)
7848 {
7849 	char			link[MAXLINKNAMELEN];
7850 	datalink_class_t	class;
7851 	uint32_t		flags;
7852 	dladm_status_t		status;
7853 	UID_STP_CFG_T		smfcfg, runcfg;
7854 	UID_STP_STATE_T		stpstate;
7855 	dladm_bridge_prot_t	smfprot, runprot;
7856 
7857 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
7858 	    NULL, link, sizeof (link))) != DLADM_STATUS_OK)
7859 		return (status);
7860 
7861 	if (!(state->ls_flags & flags))
7862 		return (DLADM_STATUS_NOTFOUND);
7863 
7864 	/* Convert observability node name back to bridge name */
7865 	if (!dladm_observe_to_bridge(link))
7866 		return (DLADM_STATUS_NOTFOUND);
7867 	(void) strlcpy(bbuf->bridge_name, link, sizeof (bbuf->bridge_name));
7868 
7869 	/*
7870 	 * If the running value differs from the one in SMF, and parsable
7871 	 * output is not requested, then we show the running value with an
7872 	 * asterisk.
7873 	 */
7874 	(void) dladm_bridge_get_properties(bbuf->bridge_name, &smfcfg,
7875 	    &smfprot);
7876 	(void) dladm_bridge_run_properties(bbuf->bridge_name, &runcfg,
7877 	    &runprot);
7878 	(void) snprintf(bbuf->bridge_protect, sizeof (bbuf->bridge_protect),
7879 	    "%s%s", state->ls_parsable || smfprot == runprot ? "" : "*",
7880 	    dladm_bridge_prot2str(runprot));
7881 	fmt_int(bbuf->bridge_priority, sizeof (bbuf->bridge_priority),
7882 	    smfcfg.bridge_priority, runcfg.bridge_priority,
7883 	    !state->ls_parsable && (runcfg.field_mask & BR_CFG_AGE));
7884 	fmt_int(bbuf->bridge_bmaxage, sizeof (bbuf->bridge_bmaxage),
7885 	    smfcfg.max_age, runcfg.max_age,
7886 	    !state->ls_parsable && (runcfg.field_mask & BR_CFG_AGE));
7887 	fmt_int(bbuf->bridge_bhellotime,
7888 	    sizeof (bbuf->bridge_bhellotime), smfcfg.hello_time,
7889 	    runcfg.hello_time,
7890 	    !state->ls_parsable && (runcfg.field_mask & BR_CFG_HELLO));
7891 	fmt_int(bbuf->bridge_bfwddelay, sizeof (bbuf->bridge_bfwddelay),
7892 	    smfcfg.forward_delay, runcfg.forward_delay,
7893 	    !state->ls_parsable && (runcfg.field_mask & BR_CFG_DELAY));
7894 	fmt_int(bbuf->bridge_forceproto, sizeof (bbuf->bridge_forceproto),
7895 	    smfcfg.force_version, runcfg.force_version,
7896 	    !state->ls_parsable && (runcfg.field_mask & BR_CFG_FORCE_VER));
7897 	fmt_int(bbuf->bridge_holdtime, sizeof (bbuf->bridge_holdtime),
7898 	    smfcfg.hold_time, runcfg.hold_time,
7899 	    !state->ls_parsable && (runcfg.field_mask & BR_CFG_HOLD_TIME));
7900 
7901 	if (dladm_bridge_state(bbuf->bridge_name, &stpstate) ==
7902 	    DLADM_STATUS_OK) {
7903 		fmt_bridge_id(bbuf->bridge_address,
7904 		    sizeof (bbuf->bridge_address), &stpstate.bridge_id);
7905 		(void) snprintf(bbuf->bridge_tctime,
7906 		    sizeof (bbuf->bridge_tctime), "%lu",
7907 		    stpstate.timeSince_Topo_Change);
7908 		(void) snprintf(bbuf->bridge_tccount,
7909 		    sizeof (bbuf->bridge_tccount), "%lu",
7910 		    stpstate.Topo_Change_Count);
7911 		(void) snprintf(bbuf->bridge_tchange,
7912 		    sizeof (bbuf->bridge_tchange), "%u", stpstate.Topo_Change);
7913 		fmt_bridge_id(bbuf->bridge_desroot,
7914 		    sizeof (bbuf->bridge_desroot), &stpstate.designated_root);
7915 		(void) snprintf(bbuf->bridge_rootcost,
7916 		    sizeof (bbuf->bridge_rootcost), "%lu",
7917 		    stpstate.root_path_cost);
7918 		(void) snprintf(bbuf->bridge_rootport,
7919 		    sizeof (bbuf->bridge_rootport), "%u", stpstate.root_port);
7920 		(void) snprintf(bbuf->bridge_maxage,
7921 		    sizeof (bbuf->bridge_maxage), "%d", stpstate.max_age);
7922 		(void) snprintf(bbuf->bridge_hellotime,
7923 		    sizeof (bbuf->bridge_hellotime), "%d", stpstate.hello_time);
7924 		(void) snprintf(bbuf->bridge_fwddelay,
7925 		    sizeof (bbuf->bridge_fwddelay), "%d",
7926 		    stpstate.forward_delay);
7927 	}
7928 	return (DLADM_STATUS_OK);
7929 }
7930 
7931 static dladm_status_t
7932 print_bridge_stats(show_state_t *state, datalink_id_t linkid,
7933     bridge_statfields_buf_t *bbuf)
7934 {
7935 	char			link[MAXLINKNAMELEN];
7936 	datalink_class_t	class;
7937 	uint32_t		flags;
7938 	dladm_status_t		status;
7939 	kstat_ctl_t		*kcp;
7940 	kstat_t			*ksp;
7941 	brsum_t			*brsum = (brsum_t *)&state->ls_prevstats;
7942 	brsum_t			newval;
7943 
7944 #ifndef lint
7945 	/* This is a compile-time assertion; optimizer normally fixes this */
7946 	extern void brsum_t_is_too_large(void);
7947 
7948 	if (sizeof (*brsum) > sizeof (state->ls_prevstats))
7949 		brsum_t_is_too_large();
7950 #endif
7951 
7952 	if (state->ls_firstonly) {
7953 		if (state->ls_donefirst)
7954 			return (DLADM_WALK_CONTINUE);
7955 		state->ls_donefirst = B_TRUE;
7956 	} else {
7957 		bzero(brsum, sizeof (*brsum));
7958 	}
7959 	bzero(&newval, sizeof (newval));
7960 
7961 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
7962 	    NULL, link, sizeof (link))) != DLADM_STATUS_OK)
7963 		return (status);
7964 
7965 	if (!(state->ls_flags & flags))
7966 		return (DLADM_STATUS_NOTFOUND);
7967 
7968 	if ((kcp = kstat_open()) == NULL) {
7969 		warn("kstat open operation failed");
7970 		return (DLADM_STATUS_OK);
7971 	}
7972 	if ((ksp = kstat_lookup(kcp, "bridge", 0, link)) != NULL &&
7973 	    kstat_read(kcp, ksp, NULL) != -1) {
7974 		if (dladm_kstat_value(ksp, "drops", KSTAT_DATA_UINT64,
7975 		    &newval.drops) == DLADM_STATUS_OK) {
7976 			(void) snprintf(bbuf->bridges_drops,
7977 			    sizeof (bbuf->bridges_drops), "%llu",
7978 			    newval.drops - brsum->drops);
7979 		}
7980 		if (dladm_kstat_value(ksp, "forward_direct", KSTAT_DATA_UINT64,
7981 		    &newval.forward_dir) == DLADM_STATUS_OK) {
7982 			(void) snprintf(bbuf->bridges_forwards,
7983 			    sizeof (bbuf->bridges_forwards), "%llu",
7984 			    newval.forward_dir - brsum->forward_dir);
7985 		}
7986 		if (dladm_kstat_value(ksp, "forward_mbcast", KSTAT_DATA_UINT64,
7987 		    &newval.forward_mb) == DLADM_STATUS_OK) {
7988 			(void) snprintf(bbuf->bridges_mbcast,
7989 			    sizeof (bbuf->bridges_mbcast), "%llu",
7990 			    newval.forward_mb - brsum->forward_mb);
7991 		}
7992 		if (dladm_kstat_value(ksp, "forward_unknown", KSTAT_DATA_UINT64,
7993 		    &newval.forward_unk) == DLADM_STATUS_OK) {
7994 			(void) snprintf(bbuf->bridges_unknown,
7995 			    sizeof (bbuf->bridges_unknown), "%llu",
7996 			    newval.forward_unk - brsum->forward_unk);
7997 		}
7998 		if (dladm_kstat_value(ksp, "recv", KSTAT_DATA_UINT64,
7999 		    &newval.recv) == DLADM_STATUS_OK) {
8000 			(void) snprintf(bbuf->bridges_recv,
8001 			    sizeof (bbuf->bridges_recv), "%llu",
8002 			    newval.recv - brsum->recv);
8003 		}
8004 		if (dladm_kstat_value(ksp, "sent", KSTAT_DATA_UINT64,
8005 		    &newval.sent) == DLADM_STATUS_OK) {
8006 			(void) snprintf(bbuf->bridges_sent,
8007 			    sizeof (bbuf->bridges_sent), "%llu",
8008 			    newval.sent - brsum->sent);
8009 		}
8010 	}
8011 	(void) kstat_close(kcp);
8012 
8013 	/* Convert observability node name back to bridge name */
8014 	if (!dladm_observe_to_bridge(link))
8015 		return (DLADM_STATUS_NOTFOUND);
8016 	(void) strlcpy(bbuf->bridges_name, link, sizeof (bbuf->bridges_name));
8017 
8018 	*brsum = newval;
8019 
8020 	return (DLADM_STATUS_OK);
8021 }
8022 
8023 /*
8024  * This structure carries around extra state information for the show-bridge
8025  * command and allows us to use common support functions.
8026  */
8027 typedef struct {
8028 	show_state_t	state;
8029 	boolean_t	show_stats;
8030 	const char	*bridge;
8031 } show_brstate_t;
8032 
8033 /* ARGSUSED */
8034 static int
8035 show_bridge(dladm_handle_t handle, datalink_id_t linkid, void *arg)
8036 {
8037 	show_brstate_t	*brstate = arg;
8038 	void *buf;
8039 
8040 	if (brstate->show_stats) {
8041 		bridge_statfields_buf_t bbuf;
8042 
8043 		bzero(&bbuf, sizeof (bbuf));
8044 		brstate->state.ls_status = print_bridge_stats(&brstate->state,
8045 		    linkid, &bbuf);
8046 		buf = &bbuf;
8047 	} else {
8048 		bridge_fields_buf_t bbuf;
8049 
8050 		bzero(&bbuf, sizeof (bbuf));
8051 		brstate->state.ls_status = print_bridge(&brstate->state, linkid,
8052 		    &bbuf);
8053 		buf = &bbuf;
8054 	}
8055 	if (brstate->state.ls_status == DLADM_STATUS_OK)
8056 		ofmt_print(brstate->state.ls_ofmt, buf);
8057 	return (DLADM_WALK_CONTINUE);
8058 }
8059 
8060 static void
8061 fmt_bool(char *buf, size_t buflen, int val)
8062 {
8063 	(void) strlcpy(buf, val ? "yes" : "no", buflen);
8064 }
8065 
8066 static dladm_status_t
8067 print_bridge_link(show_state_t *state, datalink_id_t linkid,
8068     bridge_link_fields_buf_t *bbuf)
8069 {
8070 	datalink_class_t	class;
8071 	uint32_t		flags;
8072 	dladm_status_t		status;
8073 	UID_STP_PORT_STATE_T	stpstate;
8074 
8075 	status = dladm_datalink_id2info(handle, linkid, &flags, &class, NULL,
8076 	    bbuf->bridgel_link, sizeof (bbuf->bridgel_link));
8077 	if (status != DLADM_STATUS_OK)
8078 		return (status);
8079 
8080 	if (!(state->ls_flags & flags))
8081 		return (DLADM_STATUS_NOTFOUND);
8082 
8083 	if (dladm_bridge_link_state(handle, linkid, &stpstate) ==
8084 	    DLADM_STATUS_OK) {
8085 		(void) snprintf(bbuf->bridgel_index,
8086 		    sizeof (bbuf->bridgel_index), "%u", stpstate.port_no);
8087 		if (dlsym(RTLD_PROBE, "STP_IN_state2str")) {
8088 			(void) strlcpy(bbuf->bridgel_state,
8089 			    STP_IN_state2str(stpstate.state),
8090 			    sizeof (bbuf->bridgel_state));
8091 		} else {
8092 			(void) snprintf(bbuf->bridgel_state,
8093 			    sizeof (bbuf->bridgel_state), "%u",
8094 			    stpstate.state);
8095 		}
8096 		(void) snprintf(bbuf->bridgel_uptime,
8097 		    sizeof (bbuf->bridgel_uptime), "%lu", stpstate.uptime);
8098 		(void) snprintf(bbuf->bridgel_opercost,
8099 		    sizeof (bbuf->bridgel_opercost), "%lu",
8100 		    stpstate.oper_port_path_cost);
8101 		fmt_bool(bbuf->bridgel_operp2p, sizeof (bbuf->bridgel_operp2p),
8102 		    stpstate.oper_point2point);
8103 		fmt_bool(bbuf->bridgel_operedge,
8104 		    sizeof (bbuf->bridgel_operedge), stpstate.oper_edge);
8105 		fmt_bridge_id(bbuf->bridgel_desroot,
8106 		    sizeof (bbuf->bridgel_desroot), &stpstate.designated_root);
8107 		(void) snprintf(bbuf->bridgel_descost,
8108 		    sizeof (bbuf->bridgel_descost), "%lu",
8109 		    stpstate.designated_cost);
8110 		fmt_bridge_id(bbuf->bridgel_desbridge,
8111 		    sizeof (bbuf->bridgel_desbridge),
8112 		    &stpstate.designated_bridge);
8113 		(void) snprintf(bbuf->bridgel_desport,
8114 		    sizeof (bbuf->bridgel_desport), "%u",
8115 		    stpstate.designated_port);
8116 		fmt_bool(bbuf->bridgel_tcack, sizeof (bbuf->bridgel_tcack),
8117 		    stpstate.top_change_ack);
8118 	}
8119 	return (DLADM_STATUS_OK);
8120 }
8121 
8122 static dladm_status_t
8123 print_bridge_link_stats(show_state_t *state, datalink_id_t linkid,
8124     bridge_link_statfields_buf_t *bbuf)
8125 {
8126 	datalink_class_t	class;
8127 	uint32_t		flags;
8128 	dladm_status_t		status;
8129 	UID_STP_PORT_STATE_T	stpstate;
8130 	kstat_ctl_t		*kcp;
8131 	kstat_t			*ksp;
8132 	char			bridge[MAXLINKNAMELEN];
8133 	char			kstatname[MAXLINKNAMELEN*2 + 1];
8134 	brlsum_t		*brlsum = (brlsum_t *)&state->ls_prevstats;
8135 	brlsum_t		newval;
8136 
8137 #ifndef lint
8138 	/* This is a compile-time assertion; optimizer normally fixes this */
8139 	extern void brlsum_t_is_too_large(void);
8140 
8141 	if (sizeof (*brlsum) > sizeof (state->ls_prevstats))
8142 		brlsum_t_is_too_large();
8143 #endif
8144 
8145 	if (state->ls_firstonly) {
8146 		if (state->ls_donefirst)
8147 			return (DLADM_WALK_CONTINUE);
8148 		state->ls_donefirst = B_TRUE;
8149 	} else {
8150 		bzero(brlsum, sizeof (*brlsum));
8151 	}
8152 	bzero(&newval, sizeof (newval));
8153 
8154 	status = dladm_datalink_id2info(handle, linkid, &flags, &class, NULL,
8155 	    bbuf->bridgels_link, sizeof (bbuf->bridgels_link));
8156 	if (status != DLADM_STATUS_OK)
8157 		return (status);
8158 
8159 	if (!(state->ls_flags & flags))
8160 		return (DLADM_STATUS_NOTFOUND);
8161 
8162 	if (dladm_bridge_link_state(handle, linkid, &stpstate) ==
8163 	    DLADM_STATUS_OK) {
8164 		newval.cfgbpdu = stpstate.rx_cfg_bpdu_cnt;
8165 		newval.tcnbpdu = stpstate.rx_tcn_bpdu_cnt;
8166 		newval.rstpbpdu = stpstate.rx_rstp_bpdu_cnt;
8167 		newval.txbpdu = stpstate.txCount;
8168 
8169 		(void) snprintf(bbuf->bridgels_cfgbpdu,
8170 		    sizeof (bbuf->bridgels_cfgbpdu), "%lu",
8171 		    newval.cfgbpdu - brlsum->cfgbpdu);
8172 		(void) snprintf(bbuf->bridgels_tcnbpdu,
8173 		    sizeof (bbuf->bridgels_tcnbpdu), "%lu",
8174 		    newval.tcnbpdu - brlsum->tcnbpdu);
8175 		(void) snprintf(bbuf->bridgels_rstpbpdu,
8176 		    sizeof (bbuf->bridgels_rstpbpdu), "%lu",
8177 		    newval.rstpbpdu - brlsum->rstpbpdu);
8178 		(void) snprintf(bbuf->bridgels_txbpdu,
8179 		    sizeof (bbuf->bridgels_txbpdu), "%lu",
8180 		    newval.txbpdu - brlsum->txbpdu);
8181 	}
8182 
8183 	if ((status = dladm_bridge_getlink(handle, linkid, bridge,
8184 	    sizeof (bridge))) != DLADM_STATUS_OK)
8185 		goto bls_out;
8186 	(void) snprintf(kstatname, sizeof (kstatname), "%s0-%s", bridge,
8187 	    bbuf->bridgels_link);
8188 	if ((kcp = kstat_open()) == NULL) {
8189 		warn("kstat open operation failed");
8190 		goto bls_out;
8191 	}
8192 	if ((ksp = kstat_lookup(kcp, "bridge", 0, kstatname)) != NULL &&
8193 	    kstat_read(kcp, ksp, NULL) != -1) {
8194 		if (dladm_kstat_value(ksp, "drops", KSTAT_DATA_UINT64,
8195 		    &newval.drops) != -1) {
8196 			(void) snprintf(bbuf->bridgels_drops,
8197 			    sizeof (bbuf->bridgels_drops), "%llu",
8198 			    newval.drops - brlsum->drops);
8199 		}
8200 		if (dladm_kstat_value(ksp, "recv", KSTAT_DATA_UINT64,
8201 		    &newval.recv) != -1) {
8202 			(void) snprintf(bbuf->bridgels_recv,
8203 			    sizeof (bbuf->bridgels_recv), "%llu",
8204 			    newval.recv - brlsum->recv);
8205 		}
8206 		if (dladm_kstat_value(ksp, "xmit", KSTAT_DATA_UINT64,
8207 		    &newval.xmit) != -1) {
8208 			(void) snprintf(bbuf->bridgels_xmit,
8209 			    sizeof (bbuf->bridgels_xmit), "%llu",
8210 			    newval.xmit - brlsum->xmit);
8211 		}
8212 	}
8213 	(void) kstat_close(kcp);
8214 bls_out:
8215 	*brlsum = newval;
8216 
8217 	return (status);
8218 }
8219 
8220 static void
8221 show_bridge_link(datalink_id_t linkid, show_brstate_t *brstate)
8222 {
8223 	void *buf;
8224 
8225 	if (brstate->show_stats) {
8226 		bridge_link_statfields_buf_t bbuf;
8227 
8228 		bzero(&bbuf, sizeof (bbuf));
8229 		brstate->state.ls_status = print_bridge_link_stats(
8230 		    &brstate->state, linkid, &bbuf);
8231 		buf = &bbuf;
8232 	} else {
8233 		bridge_link_fields_buf_t bbuf;
8234 
8235 		bzero(&bbuf, sizeof (bbuf));
8236 		brstate->state.ls_status = print_bridge_link(&brstate->state,
8237 		    linkid, &bbuf);
8238 		buf = &bbuf;
8239 	}
8240 	if (brstate->state.ls_status == DLADM_STATUS_OK)
8241 		ofmt_print(brstate->state.ls_ofmt, buf);
8242 }
8243 
8244 /* ARGSUSED */
8245 static int
8246 show_bridge_link_walk(dladm_handle_t handle, datalink_id_t linkid, void *arg)
8247 {
8248 	show_brstate_t	*brstate = arg;
8249 	char bridge[MAXLINKNAMELEN];
8250 
8251 	if (dladm_bridge_getlink(handle, linkid, bridge, sizeof (bridge)) ==
8252 	    DLADM_STATUS_OK && strcmp(bridge, brstate->bridge) == 0) {
8253 		show_bridge_link(linkid, brstate);
8254 	}
8255 	return (DLADM_WALK_CONTINUE);
8256 }
8257 
8258 static void
8259 show_bridge_fwd(dladm_handle_t handle, bridge_listfwd_t *blf,
8260     show_state_t *state)
8261 {
8262 	bridge_fwd_fields_buf_t bbuf;
8263 
8264 	bzero(&bbuf, sizeof (bbuf));
8265 	(void) snprintf(bbuf.bridgef_dest, sizeof (bbuf.bridgef_dest),
8266 	    "%s", ether_ntoa((struct ether_addr *)blf->blf_dest));
8267 	if (blf->blf_is_local) {
8268 		(void) strlcpy(bbuf.bridgef_flags, "L",
8269 		    sizeof (bbuf.bridgef_flags));
8270 	} else {
8271 		(void) snprintf(bbuf.bridgef_age, sizeof (bbuf.bridgef_age),
8272 		    "%2d.%03d", blf->blf_ms_age / 1000, blf->blf_ms_age % 1000);
8273 		if (blf->blf_trill_nick != 0) {
8274 			(void) snprintf(bbuf.bridgef_output,
8275 			    sizeof (bbuf.bridgef_output), "%u",
8276 			    blf->blf_trill_nick);
8277 		}
8278 	}
8279 	if (blf->blf_linkid != DATALINK_INVALID_LINKID &&
8280 	    blf->blf_trill_nick == 0) {
8281 		state->ls_status = dladm_datalink_id2info(handle,
8282 		    blf->blf_linkid, NULL, NULL, NULL, bbuf.bridgef_output,
8283 		    sizeof (bbuf.bridgef_output));
8284 	}
8285 	if (state->ls_status == DLADM_STATUS_OK)
8286 		ofmt_print(state->ls_ofmt, &bbuf);
8287 }
8288 
8289 static void
8290 show_bridge_trillnick(trill_listnick_t *tln, show_state_t *state)
8291 {
8292 	bridge_trill_fields_buf_t bbuf;
8293 
8294 	bzero(&bbuf, sizeof (bbuf));
8295 	(void) snprintf(bbuf.bridget_nick, sizeof (bbuf.bridget_nick),
8296 	    "%u", tln->tln_nick);
8297 	if (tln->tln_ours) {
8298 		(void) strlcpy(bbuf.bridget_flags, "L",
8299 		    sizeof (bbuf.bridget_flags));
8300 	} else {
8301 		state->ls_status = dladm_datalink_id2info(handle,
8302 		    tln->tln_linkid, NULL, NULL, NULL, bbuf.bridget_link,
8303 		    sizeof (bbuf.bridget_link));
8304 		(void) snprintf(bbuf.bridget_nexthop,
8305 		    sizeof (bbuf.bridget_nexthop), "%s",
8306 		    ether_ntoa((struct ether_addr *)tln->tln_nexthop));
8307 	}
8308 	if (state->ls_status == DLADM_STATUS_OK)
8309 		ofmt_print(state->ls_ofmt, &bbuf);
8310 }
8311 
8312 static void
8313 do_show_bridge(int argc, char **argv, const char *use)
8314 {
8315 	int		option;
8316 	enum {
8317 		bridgeMode, linkMode, fwdMode, trillMode
8318 	}		op_mode = bridgeMode;
8319 	uint32_t	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
8320 	boolean_t	parsable = B_FALSE;
8321 	datalink_id_t	linkid = DATALINK_ALL_LINKID;
8322 	int		interval = 0;
8323 	show_brstate_t	brstate;
8324 	dladm_status_t	status;
8325 	char		*fields_str = NULL;
8326 	/* default: bridge-related data */
8327 	char		*all_fields = "bridge,protect,address,priority,bmaxage,"
8328 	    "bhellotime,bfwddelay,forceproto,tctime,tccount,tchange,"
8329 	    "desroot,rootcost,rootport,maxage,hellotime,fwddelay,holdtime";
8330 	char		*default_fields = "bridge,protect,address,priority,"
8331 	    "desroot";
8332 	char		*all_statfields = "bridge,drops,forwards,mbcast,"
8333 	    "unknown,recv,sent";
8334 	char		*default_statfields = "bridge,drops,forwards,mbcast,"
8335 	    "unknown";
8336 	/* -l: link-related data */
8337 	char		*all_link_fields = "link,index,state,uptime,opercost,"
8338 	    "operp2p,operedge,desroot,descost,desbridge,desport,tcack";
8339 	char		*default_link_fields = "link,state,uptime,desroot";
8340 	char		*all_link_statfields = "link,cfgbpdu,tcnbpdu,rstpbpdu,"
8341 	    "txbpdu,drops,recv,xmit";
8342 	char		*default_link_statfields = "link,drops,recv,xmit";
8343 	/* -f: bridge forwarding table related data */
8344 	char		*default_fwd_fields = "dest,age,flags,output";
8345 	/* -t: TRILL nickname table related data */
8346 	char		*default_trill_fields = "nick,flags,link,nexthop";
8347 	char		*default_str;
8348 	char		*all_str;
8349 	ofmt_field_t	*field_arr;
8350 	ofmt_handle_t	ofmt;
8351 	ofmt_status_t	oferr;
8352 	uint_t		ofmtflags = 0;
8353 
8354 	bzero(&brstate, sizeof (brstate));
8355 
8356 	opterr = 0;
8357 	while ((option = getopt_long(argc, argv, ":fi:lo:pst",
8358 	    bridge_show_lopts, NULL)) != -1) {
8359 		switch (option) {
8360 		case 'f':
8361 			if (op_mode != bridgeMode && op_mode != fwdMode)
8362 				die("-f is incompatible with -l or -t");
8363 			op_mode = fwdMode;
8364 			break;
8365 		case 'i':
8366 			if (interval != 0)
8367 				die_optdup(option);
8368 			if (!str2int(optarg, &interval) || interval == 0)
8369 				die("invalid interval value '%s'", optarg);
8370 			break;
8371 		case 'l':
8372 			if (op_mode != bridgeMode && op_mode != linkMode)
8373 				die("-l is incompatible with -f or -t");
8374 			op_mode = linkMode;
8375 			break;
8376 		case 'o':
8377 			fields_str = optarg;
8378 			break;
8379 		case 'p':
8380 			if (parsable)
8381 				die_optdup(option);
8382 			parsable = B_TRUE;
8383 			break;
8384 		case 's':
8385 			if (brstate.show_stats)
8386 				die_optdup(option);
8387 			brstate.show_stats = B_TRUE;
8388 			break;
8389 		case 't':
8390 			if (op_mode != bridgeMode && op_mode != trillMode)
8391 				die("-t is incompatible with -f or -l");
8392 			op_mode = trillMode;
8393 			break;
8394 		default:
8395 			die_opterr(optopt, option, use);
8396 			break;
8397 		}
8398 	}
8399 
8400 	if (interval != 0 && !brstate.show_stats)
8401 		die("the -i option can be used only with -s");
8402 
8403 	if ((op_mode == fwdMode || op_mode == trillMode) && brstate.show_stats)
8404 		die("the -f/-t and -s options cannot be used together");
8405 
8406 	/* get the bridge name (optional last argument) */
8407 	if (optind == (argc-1)) {
8408 		char lname[MAXLINKNAMELEN];
8409 		uint32_t lnkflg;
8410 		datalink_class_t class;
8411 
8412 		brstate.bridge = argv[optind];
8413 		(void) snprintf(lname, sizeof (lname), "%s0", brstate.bridge);
8414 		if ((status = dladm_name2info(handle, lname, &linkid, &lnkflg,
8415 		    &class, NULL)) != DLADM_STATUS_OK) {
8416 			die_dlerr(status, "bridge %s is not valid",
8417 			    brstate.bridge);
8418 		}
8419 
8420 		if (class != DATALINK_CLASS_BRIDGE)
8421 			die("%s is not a bridge", brstate.bridge);
8422 
8423 		if (!(lnkflg & flags)) {
8424 			die_dlerr(DLADM_STATUS_BADARG,
8425 			    "bridge %s is temporarily removed", brstate.bridge);
8426 		}
8427 	} else if (optind != argc) {
8428 		usage();
8429 	} else if (op_mode != bridgeMode) {
8430 		die("bridge name required for -l, -f, or -t");
8431 		return;
8432 	}
8433 
8434 	brstate.state.ls_parsable = parsable;
8435 	brstate.state.ls_flags = flags;
8436 	brstate.state.ls_firstonly = (interval != 0);
8437 
8438 	switch (op_mode) {
8439 	case bridgeMode:
8440 		if (brstate.show_stats) {
8441 			default_str = default_statfields;
8442 			all_str = all_statfields;
8443 			field_arr = bridge_statfields;
8444 		} else {
8445 			default_str = default_fields;
8446 			all_str = all_fields;
8447 			field_arr = bridge_fields;
8448 		}
8449 		break;
8450 
8451 	case linkMode:
8452 		if (brstate.show_stats) {
8453 			default_str = default_link_statfields;
8454 			all_str = all_link_statfields;
8455 			field_arr = bridge_link_statfields;
8456 		} else {
8457 			default_str = default_link_fields;
8458 			all_str = all_link_fields;
8459 			field_arr = bridge_link_fields;
8460 		}
8461 		break;
8462 
8463 	case fwdMode:
8464 		default_str = all_str = default_fwd_fields;
8465 		field_arr = bridge_fwd_fields;
8466 		break;
8467 
8468 	case trillMode:
8469 		default_str = all_str = default_trill_fields;
8470 		field_arr = bridge_trill_fields;
8471 		break;
8472 	}
8473 
8474 	if (fields_str == NULL)
8475 		fields_str = default_str;
8476 	else if (strcasecmp(fields_str, "all") == 0)
8477 		fields_str = all_str;
8478 
8479 	if (parsable)
8480 		ofmtflags |= OFMT_PARSABLE;
8481 	oferr = ofmt_open(fields_str, field_arr, ofmtflags, 0, &ofmt);
8482 	dladm_ofmt_check(oferr, brstate.state.ls_parsable, ofmt);
8483 	brstate.state.ls_ofmt = ofmt;
8484 
8485 	for (;;) {
8486 		brstate.state.ls_donefirst = B_FALSE;
8487 		switch (op_mode) {
8488 		case bridgeMode:
8489 			if (linkid == DATALINK_ALL_LINKID) {
8490 				(void) dladm_walk_datalink_id(show_bridge,
8491 				    handle, &brstate, DATALINK_CLASS_BRIDGE,
8492 				    DATALINK_ANY_MEDIATYPE, flags);
8493 			} else {
8494 				(void) show_bridge(handle, linkid, &brstate);
8495 				if (brstate.state.ls_status !=
8496 				    DLADM_STATUS_OK) {
8497 					die_dlerr(brstate.state.ls_status,
8498 					    "failed to show bridge %s",
8499 					    brstate.bridge);
8500 				}
8501 			}
8502 			break;
8503 
8504 		case linkMode: {
8505 			datalink_id_t *dlp;
8506 			uint_t i, nlinks;
8507 
8508 			dlp = dladm_bridge_get_portlist(brstate.bridge,
8509 			    &nlinks);
8510 			if (dlp != NULL) {
8511 				for (i = 0; i < nlinks; i++)
8512 					show_bridge_link(dlp[i], &brstate);
8513 				dladm_bridge_free_portlist(dlp);
8514 			} else if (errno == ENOENT) {
8515 				/* bridge not running; iterate on libdladm */
8516 				(void) dladm_walk_datalink_id(
8517 				    show_bridge_link_walk, handle,
8518 				    &brstate, DATALINK_CLASS_PHYS |
8519 				    DATALINK_CLASS_AGGR |
8520 				    DATALINK_CLASS_ETHERSTUB,
8521 				    DATALINK_ANY_MEDIATYPE, flags);
8522 			} else {
8523 				die("unable to get port list for bridge %s: %s",
8524 				    brstate.bridge, strerror(errno));
8525 			}
8526 			break;
8527 		}
8528 
8529 		case fwdMode: {
8530 			bridge_listfwd_t *blf;
8531 			uint_t i, nfwd;
8532 
8533 			blf = dladm_bridge_get_fwdtable(handle, brstate.bridge,
8534 			    &nfwd);
8535 			if (blf == NULL) {
8536 				die("unable to get forwarding entries for "
8537 				    "bridge %s", brstate.bridge);
8538 			} else {
8539 				for (i = 0; i < nfwd; i++)
8540 					show_bridge_fwd(handle, blf + i,
8541 					    &brstate.state);
8542 				dladm_bridge_free_fwdtable(blf);
8543 			}
8544 			break;
8545 		}
8546 
8547 		case trillMode: {
8548 			trill_listnick_t *tln;
8549 			uint_t i, nnick;
8550 
8551 			tln = dladm_bridge_get_trillnick(brstate.bridge,
8552 			    &nnick);
8553 			if (tln == NULL) {
8554 				if (errno == ENOENT)
8555 					die("bridge %s is not running TRILL",
8556 					    brstate.bridge);
8557 				else
8558 					die("unable to get TRILL nickname "
8559 					    "entries for bridge %s",
8560 					    brstate.bridge);
8561 			} else {
8562 				for (i = 0; i < nnick; i++)
8563 					show_bridge_trillnick(tln + i,
8564 					    &brstate.state);
8565 				dladm_bridge_free_trillnick(tln);
8566 			}
8567 			break;
8568 		}
8569 		}
8570 		if (interval == 0)
8571 			break;
8572 		(void) sleep(interval);
8573 	}
8574 }
8575 
8576 /*
8577  * "-R" option support. It is used for live upgrading. Append dladm commands
8578  * to a upgrade script which will be run when the alternative root boots up:
8579  *
8580  * - If the /etc/dladm/datalink.conf file exists on the alternative root,
8581  * append dladm commands to the <altroot>/var/svc/profile/upgrade_datalink
8582  * script. This script will be run as part of the network/physical service.
8583  * We cannot defer this to /var/svc/profile/upgrade because then the
8584  * configuration will not be able to take effect before network/physical
8585  * plumbs various interfaces.
8586  *
8587  * - If the /etc/dladm/datalink.conf file does not exist on the alternative
8588  * root, append dladm commands to the <altroot>/var/svc/profile/upgrade script,
8589  * which will be run in the manifest-import service.
8590  *
8591  * Note that the SMF team is considering to move the manifest-import service
8592  * to be run at the very begining of boot. Once that is done, the need for
8593  * the /var/svc/profile/upgrade_datalink script will not exist any more.
8594  */
8595 static void
8596 altroot_cmd(char *altroot, int argc, char *argv[])
8597 {
8598 	char		path[MAXPATHLEN];
8599 	struct stat	stbuf;
8600 	FILE		*fp;
8601 	int		i;
8602 
8603 	/*
8604 	 * Check for the existence of the /etc/dladm/datalink.conf
8605 	 * configuration file, and determine the name of script file.
8606 	 */
8607 	(void) snprintf(path, MAXPATHLEN, "/%s/etc/dladm/datalink.conf",
8608 	    altroot);
8609 	if (stat(path, &stbuf) < 0) {
8610 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
8611 		    SMF_UPGRADE_FILE);
8612 	} else {
8613 		(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
8614 		    SMF_UPGRADEDATALINK_FILE);
8615 	}
8616 
8617 	if ((fp = fopen(path, "a+")) == NULL)
8618 		die("operation not supported on %s", altroot);
8619 
8620 	(void) fprintf(fp, "/sbin/dladm ");
8621 	for (i = 0; i < argc; i++) {
8622 		/*
8623 		 * Directly write to the file if it is not the "-R <altroot>"
8624 		 * option. In which case, skip it.
8625 		 */
8626 		if (strcmp(argv[i], "-R") != 0)
8627 			(void) fprintf(fp, "%s ", argv[i]);
8628 		else
8629 			i ++;
8630 	}
8631 	(void) fprintf(fp, "%s\n", SMF_DLADM_UPGRADE_MSG);
8632 	(void) fclose(fp);
8633 	dladm_close(handle);
8634 	exit(EXIT_SUCCESS);
8635 }
8636 
8637 /*
8638  * Convert the string to an integer. Note that the string must not have any
8639  * trailing non-integer characters.
8640  */
8641 static boolean_t
8642 str2int(const char *str, int *valp)
8643 {
8644 	int	val;
8645 	char	*endp = NULL;
8646 
8647 	errno = 0;
8648 	val = strtol(str, &endp, 10);
8649 	if (errno != 0 || *endp != '\0')
8650 		return (B_FALSE);
8651 
8652 	*valp = val;
8653 	return (B_TRUE);
8654 }
8655 
8656 /* PRINTFLIKE1 */
8657 static void
8658 warn(const char *format, ...)
8659 {
8660 	va_list alist;
8661 
8662 	format = gettext(format);
8663 	(void) fprintf(stderr, "%s: warning: ", progname);
8664 
8665 	va_start(alist, format);
8666 	(void) vfprintf(stderr, format, alist);
8667 	va_end(alist);
8668 
8669 	(void) putchar('\n');
8670 }
8671 
8672 /* PRINTFLIKE2 */
8673 static void
8674 warn_dlerr(dladm_status_t err, const char *format, ...)
8675 {
8676 	va_list alist;
8677 	char	errmsg[DLADM_STRSIZE];
8678 
8679 	format = gettext(format);
8680 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
8681 
8682 	va_start(alist, format);
8683 	(void) vfprintf(stderr, format, alist);
8684 	va_end(alist);
8685 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
8686 }
8687 
8688 /*
8689  * Also closes the dladm handle if it is not NULL.
8690  */
8691 /* PRINTFLIKE2 */
8692 static void
8693 die_dlerr(dladm_status_t err, const char *format, ...)
8694 {
8695 	va_list alist;
8696 	char	errmsg[DLADM_STRSIZE];
8697 
8698 	format = gettext(format);
8699 	(void) fprintf(stderr, "%s: ", progname);
8700 
8701 	va_start(alist, format);
8702 	(void) vfprintf(stderr, format, alist);
8703 	va_end(alist);
8704 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
8705 
8706 	/* close dladm handle if it was opened */
8707 	if (handle != NULL)
8708 		dladm_close(handle);
8709 
8710 	exit(EXIT_FAILURE);
8711 }
8712 
8713 /* PRINTFLIKE1 */
8714 static void
8715 die(const char *format, ...)
8716 {
8717 	va_list alist;
8718 
8719 	format = gettext(format);
8720 	(void) fprintf(stderr, "%s: ", progname);
8721 
8722 	va_start(alist, format);
8723 	(void) vfprintf(stderr, format, alist);
8724 	va_end(alist);
8725 
8726 	(void) putchar('\n');
8727 
8728 	/* close dladm handle if it was opened */
8729 	if (handle != NULL)
8730 		dladm_close(handle);
8731 
8732 	exit(EXIT_FAILURE);
8733 }
8734 
8735 static void
8736 die_optdup(int opt)
8737 {
8738 	die("the option -%c cannot be specified more than once", opt);
8739 }
8740 
8741 static void
8742 die_opterr(int opt, int opterr, const char *usage)
8743 {
8744 	switch (opterr) {
8745 	case ':':
8746 		die("option '-%c' requires a value\nusage: %s", opt,
8747 		    gettext(usage));
8748 		break;
8749 	case '?':
8750 	default:
8751 		die("unrecognized option '-%c'\nusage: %s", opt,
8752 		    gettext(usage));
8753 		break;
8754 	}
8755 }
8756 
8757 static void
8758 show_ether_xprop(void *arg, dladm_ether_info_t *eattr)
8759 {
8760 	print_ether_state_t	*statep = arg;
8761 	ether_fields_buf_t	ebuf;
8762 	int			i;
8763 
8764 	for (i = CAPABLE; i <= PEERADV; i++)  {
8765 		bzero(&ebuf, sizeof (ebuf));
8766 		(void) strlcpy(ebuf.eth_ptype, ptype[i],
8767 		    sizeof (ebuf.eth_ptype));
8768 		(void) dladm_ether_autoneg2str(ebuf.eth_autoneg,
8769 		    sizeof (ebuf.eth_autoneg), eattr, i);
8770 		(void) dladm_ether_spdx2str(ebuf.eth_spdx,
8771 		    sizeof (ebuf.eth_spdx), eattr, i);
8772 		(void) dladm_ether_pause2str(ebuf.eth_pause,
8773 		    sizeof (ebuf.eth_pause), eattr, i);
8774 		(void) strlcpy(ebuf.eth_rem_fault,
8775 		    (eattr->lei_attr[i].le_fault ? "fault" : "none"),
8776 		    sizeof (ebuf.eth_rem_fault));
8777 		ofmt_print(statep->es_ofmt, &ebuf);
8778 	}
8779 
8780 }
8781 
8782 static boolean_t
8783 link_is_ether(const char *link, datalink_id_t *linkid)
8784 {
8785 	uint32_t media;
8786 	datalink_class_t class;
8787 
8788 	if (dladm_name2info(handle, link, linkid, NULL, &class, &media) ==
8789 	    DLADM_STATUS_OK) {
8790 		if (class == DATALINK_CLASS_PHYS && media == DL_ETHER)
8791 			return (B_TRUE);
8792 	}
8793 	return (B_FALSE);
8794 }
8795 
8796 /*
8797  * default output callback function that, when invoked,
8798  * prints string which is offset by ofmt_arg->ofmt_id within buf.
8799  */
8800 static boolean_t
8801 print_default_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
8802 {
8803 	char *value;
8804 
8805 	value = (char *)ofarg->ofmt_cbarg + ofarg->ofmt_id;
8806 	(void) strlcpy(buf, value, bufsize);
8807 	return (B_TRUE);
8808 }
8809 
8810 static void
8811 dladm_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
8812     ofmt_handle_t ofmt)
8813 {
8814 	char buf[OFMT_BUFSIZE];
8815 
8816 	if (oferr == OFMT_SUCCESS)
8817 		return;
8818 	(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
8819 	/*
8820 	 * All errors are considered fatal in parsable mode.
8821 	 * NOMEM errors are always fatal, regardless of mode.
8822 	 * For other errors, we print diagnostics in human-readable
8823 	 * mode and processs what we can.
8824 	 */
8825 	if (parsable || oferr == OFMT_ENOFIELDS) {
8826 		ofmt_close(ofmt);
8827 		die(buf);
8828 	} else {
8829 		warn(buf);
8830 	}
8831 }
8832