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/*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2016 Nexenta Systems, Inc.
25 * Copyright 2020 Peter Tribble.
26 */
27
28#include <stdio.h>
29#include <ctype.h>
30#include <dlfcn.h>
31#include <locale.h>
32#include <signal.h>
33#include <stdarg.h>
34#include <stdlib.h>
35#include <fcntl.h>
36#include <string.h>
37#include <stropts.h>
38#include <sys/stat.h>
39#include <errno.h>
40#include <kstat.h>
41#include <strings.h>
42#include <getopt.h>
43#include <unistd.h>
44#include <priv.h>
45#include <limits.h>
46#include <termios.h>
47#include <pwd.h>
48#include <auth_attr.h>
49#include <auth_list.h>
50#include <libintl.h>
51#include <libdevinfo.h>
52#include <libdlpi.h>
53#include <libdladm.h>
54#include <libdllink.h>
55#include <libdlstat.h>
56#include <libdlaggr.h>
57#include <libdlwlan.h>
58#include <libdlvlan.h>
59#include <libdlvnic.h>
60#include <libdlib.h>
61#include <libdlether.h>
62#include <libdliptun.h>
63#include <libdlsim.h>
64#include <libdlbridge.h>
65#include <libinetutil.h>
66#include <libvrrpadm.h>
67#include <bsm/adt.h>
68#include <bsm/adt_event.h>
69#include <libdlvnic.h>
70#include <sys/types.h>
71#include <sys/socket.h>
72#include <sys/ib/ib_types.h>
73#include <sys/processor.h>
74#include <netinet/in.h>
75#include <arpa/inet.h>
76#include <net/if_types.h>
77#include <stddef.h>
78#include <stp_in.h>
79#include <ofmt.h>
80
81#define	MAXPORT			256
82#define	MAXVNIC			256
83#define	BUFLEN(lim, ptr)	(((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
84#define	MAXLINELEN		1024
85#define	SMF_UPGRADE_FILE		"/var/svc/profile/upgrade"
86#define	SMF_UPGRADEDATALINK_FILE	"/var/svc/profile/upgrade_datalink"
87#define	SMF_DLADM_UPGRADE_MSG		" # added by dladm(1M)"
88#define	DLADM_DEFAULT_COL	80
89
90/*
91 * used by the wifi show-* commands to set up ofmt_field_t structures.
92 */
93#define	WIFI_CMD_SCAN		0x00000001
94#define	WIFI_CMD_SHOW		0x00000002
95#define	WIFI_CMD_ALL		(WIFI_CMD_SCAN | WIFI_CMD_SHOW)
96
97/* No larger than pktsum_t */
98typedef struct brsum_s {
99	uint64_t	drops;
100	uint64_t	forward_dir;
101	uint64_t	forward_mb;
102	uint64_t	forward_unk;
103	uint64_t	recv;
104	uint64_t	sent;
105} brsum_t;
106
107/* No larger than pktsum_t */
108typedef struct brlsum_s {
109	uint32_t	cfgbpdu;
110	uint32_t	tcnbpdu;
111	uint32_t	rstpbpdu;
112	uint32_t	txbpdu;
113	uint64_t	drops;
114	uint64_t	recv;
115	uint64_t	xmit;
116} brlsum_t;
117
118typedef struct show_state {
119	boolean_t	ls_firstonly;
120	boolean_t	ls_donefirst;
121	pktsum_t	ls_prevstats;
122	uint32_t	ls_flags;
123	dladm_status_t	ls_status;
124	ofmt_handle_t	ls_ofmt;
125	boolean_t	ls_parsable;
126	boolean_t	ls_mac;
127	boolean_t	ls_hwgrp;
128} show_state_t;
129
130typedef struct show_grp_state {
131	pktsum_t	gs_prevstats[MAXPORT];
132	uint32_t	gs_flags;
133	dladm_status_t	gs_status;
134	boolean_t	gs_parsable;
135	boolean_t	gs_lacp;
136	boolean_t	gs_extended;
137	boolean_t	gs_stats;
138	boolean_t	gs_firstonly;
139	boolean_t	gs_donefirst;
140	ofmt_handle_t	gs_ofmt;
141} show_grp_state_t;
142
143typedef struct show_vnic_state {
144	datalink_id_t	vs_vnic_id;
145	datalink_id_t	vs_link_id;
146	char		vs_vnic[MAXLINKNAMELEN];
147	char		vs_link[MAXLINKNAMELEN];
148	boolean_t	vs_parsable;
149	boolean_t	vs_found;
150	boolean_t	vs_firstonly;
151	boolean_t	vs_donefirst;
152	boolean_t	vs_stats;
153	boolean_t	vs_printstats;
154	pktsum_t	vs_totalstats;
155	pktsum_t	vs_prevstats[MAXVNIC];
156	boolean_t	vs_etherstub;
157	dladm_status_t	vs_status;
158	uint32_t	vs_flags;
159	ofmt_handle_t	vs_ofmt;
160} show_vnic_state_t;
161
162typedef struct show_part_state {
163	datalink_id_t	ps_over_id;
164	char		ps_part[MAXLINKNAMELEN];
165	boolean_t	ps_parsable;
166	boolean_t	ps_found;
167	dladm_status_t	ps_status;
168	uint32_t	ps_flags;
169	ofmt_handle_t	ps_ofmt;
170} show_part_state_t;
171
172typedef struct show_ib_state {
173	datalink_id_t	is_link_id;
174	char		is_link[MAXLINKNAMELEN];
175	boolean_t	is_parsable;
176	dladm_status_t	is_status;
177	uint32_t	is_flags;
178	ofmt_handle_t	is_ofmt;
179} show_ib_state_t;
180
181typedef struct show_usage_state_s {
182	boolean_t	us_plot;
183	boolean_t	us_parsable;
184	boolean_t	us_printheader;
185	boolean_t	us_first;
186	boolean_t	us_showall;
187	ofmt_handle_t	us_ofmt;
188} show_usage_state_t;
189
190/*
191 * callback functions for printing output and error diagnostics.
192 */
193static ofmt_cb_t print_default_cb, print_link_stats_cb, print_linkprop_cb;
194static ofmt_cb_t print_lacp_cb, print_phys_one_mac_cb;
195static ofmt_cb_t print_xaggr_cb, print_aggr_stats_cb;
196static ofmt_cb_t print_phys_one_hwgrp_cb, print_wlan_attr_cb;
197static ofmt_cb_t print_wifi_status_cb, print_link_attr_cb;
198
199typedef void cmdfunc_t(int, char **, const char *);
200
201static cmdfunc_t do_show_link, do_show_wifi, do_show_phys;
202static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
203static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr;
204static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
205static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
206static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
207static cmdfunc_t do_init_linkprop, do_init_secobj;
208static cmdfunc_t do_create_vlan, do_delete_vlan, do_up_vlan, do_show_vlan;
209static cmdfunc_t do_rename_link, do_delete_phys, do_init_phys;
210static cmdfunc_t do_show_linkmap;
211static cmdfunc_t do_show_ether;
212static cmdfunc_t do_create_vnic, do_delete_vnic, do_show_vnic;
213static cmdfunc_t do_up_vnic;
214static cmdfunc_t do_create_part, do_delete_part, do_show_part, do_show_ib;
215static cmdfunc_t do_up_part;
216static cmdfunc_t do_create_etherstub, do_delete_etherstub, do_show_etherstub;
217static cmdfunc_t do_create_simnet, do_modify_simnet;
218static cmdfunc_t do_delete_simnet, do_show_simnet, do_up_simnet;
219static cmdfunc_t do_show_usage;
220static cmdfunc_t do_create_bridge, do_modify_bridge, do_delete_bridge;
221static cmdfunc_t do_add_bridge, do_remove_bridge, do_show_bridge;
222static cmdfunc_t do_create_iptun, do_modify_iptun, do_delete_iptun;
223static cmdfunc_t do_show_iptun, do_up_iptun, do_down_iptun;
224
225static void	do_up_vnic_common(int, char **, const char *, boolean_t);
226
227static int show_part(dladm_handle_t, datalink_id_t, void *);
228
229static void	altroot_cmd(char *, int, char **);
230static int	show_linkprop_onelink(dladm_handle_t, datalink_id_t, void *);
231
232static void	link_stats(datalink_id_t, uint_t, char *, show_state_t *);
233static void	aggr_stats(datalink_id_t, show_grp_state_t *, uint_t);
234static void	vnic_stats(show_vnic_state_t *, uint32_t);
235
236static int	get_one_kstat(const char *, const char *, uint8_t,
237		    void *, boolean_t);
238static void	get_mac_stats(const char *, pktsum_t *);
239static void	get_link_stats(const char *, pktsum_t *);
240static uint64_t	get_ifspeed(const char *, boolean_t);
241static const char	*get_linkstate(const char *, boolean_t, char *);
242static const char	*get_linkduplex(const char *, boolean_t, char *);
243
244static iptun_type_t	iptun_gettypebyname(char *);
245static const char	*iptun_gettypebyvalue(iptun_type_t);
246static dladm_status_t	print_iptun(dladm_handle_t, datalink_id_t,
247			    show_state_t *);
248static int	print_iptun_walker(dladm_handle_t, datalink_id_t, void *);
249
250static int	show_etherprop(dladm_handle_t, datalink_id_t, void *);
251static void	show_ether_xprop(void *, dladm_ether_info_t *);
252static boolean_t	link_is_ether(const char *, datalink_id_t *);
253
254static boolean_t str2int(const char *, int *);
255static void	die(const char *, ...);
256static void	die_optdup(int);
257static void	die_opterr(int, int, const char *);
258static void	die_dlerr(dladm_status_t, const char *, ...);
259static void	warn(const char *, ...);
260static void	warn_dlerr(dladm_status_t, const char *, ...);
261
262typedef struct	cmd {
263	char		*c_name;
264	cmdfunc_t	*c_fn;
265	const char	*c_usage;
266} cmd_t;
267
268static cmd_t	cmds[] = {
269	{ "rename-link",	do_rename_link,
270	    "    rename-link      <oldlink> <newlink>"			},
271	{ "show-link",		do_show_link,
272	    "    show-link        [-pP] [-o <field>,..] [-s [-i <interval>]] "
273	    "[<link>]\n"						},
274	{ "create-aggr",	do_create_aggr,
275	    "    create-aggr      [-t] [-P <policy>] [-L <mode>] [-T <time>] "
276	    "[-u <address>]\n"
277	    "\t\t     -l <link> [-l <link>...] <link>"			},
278	{ "delete-aggr",	do_delete_aggr,
279	    "    delete-aggr      [-t] <link>"				},
280	{ "add-aggr",		do_add_aggr,
281	    "    add-aggr         [-t] -l <link> [-l <link>...] <link>" },
282	{ "remove-aggr",	do_remove_aggr,
283	    "    remove-aggr      [-t] -l <link> [-l <link>...] <link>" },
284	{ "modify-aggr",	do_modify_aggr,
285	    "    modify-aggr      [-t] [-P <policy>] [-L <mode>] [-T <time>] "
286	    "[-u <address>]\n"
287	    "\t\t     <link>"						},
288	{ "show-aggr",		do_show_aggr,
289	    "    show-aggr        [-pPLx] [-o <field>,..] [-s [-i <interval>]] "
290	    "[<link>]\n"						},
291	{ "up-aggr",		do_up_aggr,	NULL			},
292	{ "scan-wifi",		do_scan_wifi,
293	    "    scan-wifi        [-p] [-o <field>,...] [<link>]"	},
294	{ "connect-wifi",	do_connect_wifi,
295	    "    connect-wifi     [-e <essid>] [-i <bssid>] [-k <key>,...] "
296	    "[-s wep|wpa]\n"
297	    "\t\t     [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g] "
298	    "[-T <time>]\n"
299	    "\t\t     [<link>]"						},
300	{ "disconnect-wifi",	do_disconnect_wifi,
301	    "    disconnect-wifi  [-a] [<link>]"			},
302	{ "show-wifi",		do_show_wifi,
303	    "    show-wifi        [-p] [-o <field>,...] [<link>]\n"	},
304	{ "set-linkprop",	do_set_linkprop,
305	    "    set-linkprop     [-t] -p <prop>=<value>[,...] <name>"	},
306	{ "reset-linkprop",	do_reset_linkprop,
307	    "    reset-linkprop   [-t] [-p <prop>,...] <name>"		},
308	{ "show-linkprop",	do_show_linkprop,
309	    "    show-linkprop    [-cP] [-o <field>,...] [-p <prop>,...] "
310	    "<name>\n"							},
311	{ "show-ether",		do_show_ether,
312	    "    show-ether       [-px][-o <field>,...] <link>\n"	},
313	{ "create-secobj",	do_create_secobj,
314	    "    create-secobj    [-t] [-f <file>] -c <class> <secobj>"	},
315	{ "delete-secobj",	do_delete_secobj,
316	    "    delete-secobj    [-t] <secobj>[,...]"			},
317	{ "show-secobj",	do_show_secobj,
318	    "    show-secobj      [-pP] [-o <field>,...] [<secobj>,...]\n" },
319	{ "init-linkprop",	do_init_linkprop,	NULL		},
320	{ "init-secobj",	do_init_secobj,		NULL		},
321	{ "create-vlan",	do_create_vlan,
322	    "    create-vlan      [-ft] -l <link> -v <vid> [link]"	},
323	{ "delete-vlan",	do_delete_vlan,
324	    "    delete-vlan      [-t] <link>"				},
325	{ "show-vlan",		do_show_vlan,
326	    "    show-vlan        [-pP] [-o <field>,..] [<link>]\n"	},
327	{ "up-vlan",		do_up_vlan,		NULL		},
328	{ "create-iptun",	do_create_iptun,
329	    "    create-iptun     [-t] -T <type> "
330	    "[-a {local|remote}=<addr>,...] <link>]" },
331	{ "delete-iptun",	do_delete_iptun,
332	    "    delete-iptun     [-t] <link>"				},
333	{ "modify-iptun",	do_modify_iptun,
334	    "    modify-iptun     [-t] -a {local|remote}=<addr>,... <link>" },
335	{ "show-iptun",		do_show_iptun,
336	    "    show-iptun       [-pP] [-o <field>,..] [<link>]\n"	},
337	{ "up-iptun",		do_up_iptun,		NULL		},
338	{ "down-iptun",		do_down_iptun,		NULL		},
339	{ "delete-phys",	do_delete_phys,
340	    "    delete-phys      <link>"				},
341	{ "show-phys",		do_show_phys,
342	    "    show-phys        [-m | -H | -P] [[-p] [-o <field>[,...]] "
343	    "[<link>]\n"						},
344	{ "init-phys",		do_init_phys,		NULL		},
345	{ "show-linkmap",	do_show_linkmap,	NULL		},
346	{ "create-vnic",	do_create_vnic,
347	    "    create-vnic      [-t] -l <link> [-m <value> | auto |\n"
348	    "\t\t     {factory [-n <slot-id>]} | {random [-r <prefix>]} |\n"
349	    "\t\t     {vrrp -V <vrid> -A {inet | inet6}} [-v <vid> [-f]]\n"
350	    "\t\t     [-p <prop>=<value>[,...]] <vnic-link>"	},
351	{ "delete-vnic",	do_delete_vnic,
352	    "    delete-vnic      [-t] <vnic-link>"			},
353	{ "show-vnic",		do_show_vnic,
354	    "    show-vnic        [-pP] [-l <link>] [-s [-i <interval>]] "
355	    "[<link>]\n"						},
356	{ "up-vnic",		do_up_vnic,		NULL		},
357	{ "create-part",	do_create_part,
358	    "    create-part      [-t] [-f] -l <link> [-P <pkey>]\n"
359	    "\t\t     [-R <root-dir>] <part-link>"			},
360	{ "delete-part",	do_delete_part,
361	    "    delete-part      [-t] [-R <root-dir>] <part-link>"},
362	{ "show-part",		do_show_part,
363	    "    show-part        [-pP] [-o <field>,...][-l <linkover>]\n"
364	    "\t\t     [<part-link>]"		},
365	{ "show-ib",		do_show_ib,
366	    "    show-ib          [-p] [-o <field>,...] [<link>]\n"	},
367	{ "up-part",		do_up_part,		NULL		},
368	{ "create-etherstub",	do_create_etherstub,
369	    "    create-etherstub [-t] <link>"				},
370	{ "delete-etherstub",	do_delete_etherstub,
371	    "    delete-etherstub [-t] <link>"				},
372	{ "show-etherstub",	do_show_etherstub,
373	    "    show-etherstub   [-t] [<link>]\n"			},
374	{ "create-simnet",	do_create_simnet,	NULL		},
375	{ "modify-simnet",	do_modify_simnet,	NULL		},
376	{ "delete-simnet",	do_delete_simnet,	NULL		},
377	{ "show-simnet",	do_show_simnet,		NULL		},
378	{ "up-simnet",		do_up_simnet,		NULL		},
379	{ "create-bridge",	do_create_bridge,
380	    "    create-bridge    [-R <root-dir>] [-P <protect>] "
381	    "[-p <priority>]\n"
382	    "\t\t     [-m <max-age>] [-h <hello-time>] [-d <forward-delay>]\n"
383	    "\t\t     [-f <force-protocol>] [-l <link>]... <bridge>"	},
384	{ "modify-bridge",	do_modify_bridge,
385	    "    modify-bridge    [-R <root-dir>] [-P <protect>] "
386	    "[-p <priority>]\n"
387	    "\t\t     [-m <max-age>] [-h <hello-time>] [-d <forward-delay>]\n"
388	    "\t\t     [-f <force-protocol>] <bridge>"			},
389	{ "delete-bridge",	do_delete_bridge,
390	    "    delete-bridge    [-R <root-dir>] <bridge>"		},
391	{ "add-bridge",		do_add_bridge,
392	    "    add-bridge       [-R <root-dir>] -l <link> [-l <link>]... "
393	    "<bridge>"							},
394	{ "remove-bridge",	do_remove_bridge,
395	    "    remove-bridge    [-R <root-dir>] -l <link> [-l <link>]... "
396	    "<bridge>"							},
397	{ "show-bridge",	do_show_bridge,
398	    "    show-bridge      [-p] [-o <field>,...] [-s [-i <interval>]] "
399	    "[<bridge>]\n"
400	    "    show-bridge      -l [-p] [-o <field>,...] [-s [-i <interval>]]"
401	    " <bridge>\n"
402	    "    show-bridge      -f [-p] [-o <field>,...] [-s [-i <interval>]]"
403	    " <bridge>\n"
404	    "    show-bridge      -t [-p] [-o <field>,...] [-s [-i <interval>]]"
405	    " <bridge>\n"						},
406	{ "show-usage",		do_show_usage,
407	    "    show-usage       [-a] [-d | -F <format>] "
408	    "[-s <DD/MM/YYYY,HH:MM:SS>]\n"
409	    "\t\t     [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<link>]"	}
410};
411
412static const struct option lopts[] = {
413	{"vlan-id",	required_argument,	0, 'v'},
414	{"output",	required_argument,	0, 'o'},
415	{"dev",		required_argument,	0, 'd'},
416	{"policy",	required_argument,	0, 'P'},
417	{"lacp-mode",	required_argument,	0, 'L'},
418	{"lacp-timer",	required_argument,	0, 'T'},
419	{"unicast",	required_argument,	0, 'u'},
420	{"temporary",	no_argument,		0, 't'},
421	{"root-dir",	required_argument,	0, 'R'},
422	{"link",	required_argument,	0, 'l'},
423	{"forcible",	no_argument,		0, 'f'},
424	{"bw-limit",	required_argument,	0, 'b'},
425	{"mac-address",	required_argument,	0, 'm'},
426	{"slot",	required_argument,	0, 'n'},
427	{ NULL, 0, NULL, 0 }
428};
429
430static const struct option show_lopts[] = {
431	{"statistics",	no_argument,		0, 's'},
432	{"continuous",	no_argument,		0, 'S'},
433	{"interval",	required_argument,	0, 'i'},
434	{"parsable",	no_argument,		0, 'p'},
435	{"parseable",	no_argument,		0, 'p'},
436	{"extended",	no_argument,		0, 'x'},
437	{"output",	required_argument,	0, 'o'},
438	{"persistent",	no_argument,		0, 'P'},
439	{"lacp",	no_argument,		0, 'L'},
440	{ NULL, 0, NULL, 0 }
441};
442
443static const struct option iptun_lopts[] = {
444	{"output",	required_argument,	0, 'o'},
445	{"tunnel-type",	required_argument,	0, 'T'},
446	{"address",	required_argument,	0, 'a'},
447	{"root-dir",	required_argument,	0, 'R'},
448	{"parsable",	no_argument,		0, 'p'},
449	{"parseable",	no_argument,		0, 'p'},
450	{"persistent",	no_argument,		0, 'P'},
451	{ NULL, 0, NULL, 0 }
452};
453
454static char * const iptun_addropts[] = {
455#define	IPTUN_LOCAL	0
456	"local",
457#define	IPTUN_REMOTE	1
458	"remote",
459	NULL};
460
461static const struct {
462	const char	*type_name;
463	iptun_type_t	type_value;
464} iptun_types[] = {
465	{"ipv4",	IPTUN_TYPE_IPV4},
466	{"ipv6",	IPTUN_TYPE_IPV6},
467	{"6to4",	IPTUN_TYPE_6TO4},
468	{NULL,		0}
469};
470
471static const struct option prop_longopts[] = {
472	{"temporary",	no_argument,		0, 't'  },
473	{"output",	required_argument,	0, 'o'  },
474	{"root-dir",	required_argument,	0, 'R'  },
475	{"prop",	required_argument,	0, 'p'  },
476	{"parsable",	no_argument,		0, 'c'  },
477	{"parseable",	no_argument,		0, 'c'  },
478	{"persistent",	no_argument,		0, 'P'  },
479	{ NULL, 0, NULL, 0 }
480};
481
482static const struct option wifi_longopts[] = {
483	{"parsable",	no_argument,		0, 'p'  },
484	{"parseable",	no_argument,		0, 'p'  },
485	{"output",	required_argument,	0, 'o'  },
486	{"essid",	required_argument,	0, 'e'  },
487	{"bsstype",	required_argument,	0, 'b'  },
488	{"mode",	required_argument,	0, 'm'  },
489	{"key",		required_argument,	0, 'k'  },
490	{"sec",		required_argument,	0, 's'  },
491	{"auth",	required_argument,	0, 'a'  },
492	{"create-ibss",	required_argument,	0, 'c'  },
493	{"timeout",	required_argument,	0, 'T'  },
494	{"all-links",	no_argument,		0, 'a'  },
495	{"temporary",	no_argument,		0, 't'  },
496	{"root-dir",	required_argument,	0, 'R'  },
497	{"persistent",	no_argument,		0, 'P'  },
498	{"file",	required_argument,	0, 'f'  },
499	{ NULL, 0, NULL, 0 }
500};
501
502static const struct option showeth_lopts[] = {
503	{"parsable",	no_argument,		0, 'p'	},
504	{"parseable",	no_argument,		0, 'p'	},
505	{"extended",	no_argument,		0, 'x'	},
506	{"output",	required_argument,	0, 'o'	},
507	{ NULL, 0, NULL, 0 }
508};
509
510static const struct option vnic_lopts[] = {
511	{"temporary",	no_argument,		0, 't'	},
512	{"root-dir",	required_argument,	0, 'R'	},
513	{"dev",		required_argument,	0, 'd'	},
514	{"mac-address",	required_argument,	0, 'm'	},
515	{"cpus",	required_argument,	0, 'c'	},
516	{"bw-limit",	required_argument,	0, 'b'	},
517	{"slot",	required_argument,	0, 'n'	},
518	{"mac-prefix",	required_argument,	0, 'r'	},
519	{"vrid",	required_argument,	0, 'V'	},
520	{"address-family",	required_argument,	0, 'A'	},
521	{ NULL, 0, NULL, 0 }
522};
523
524static const struct option part_lopts[] = {
525	{"temporary",	no_argument,		0, 't'  },
526	{"pkey",	required_argument,	0, 'P'  },
527	{"link",	required_argument,	0, 'l'  },
528	{"force",	no_argument,		0, 'f'  },
529	{"root-dir",	required_argument,	0, 'R'  },
530	{"prop",	required_argument,	0, 'p'  },
531	{ NULL, 0, NULL, 0 }
532};
533
534static const struct option show_part_lopts[] = {
535	{"parsable",	no_argument,		0, 'p'  },
536	{"parseable",	no_argument,		0, 'p'  },
537	{"link",	required_argument,	0, 'l'  },
538	{"persistent",	no_argument,		0, 'P'  },
539	{"output",	required_argument,	0, 'o'  },
540	{ NULL, 0, NULL, 0 }
541};
542
543static const struct option etherstub_lopts[] = {
544	{"temporary",	no_argument,		0, 't'	},
545	{"root-dir",	required_argument,	0, 'R'	},
546	{ NULL, 0, NULL, 0 }
547};
548
549static const struct option usage_opts[] = {
550	{"file",	required_argument,	0, 'f'	},
551	{"format",	required_argument,	0, 'F'	},
552	{"start",	required_argument,	0, 's'	},
553	{"stop",	required_argument,	0, 'e'	},
554	{ NULL, 0, NULL, 0 }
555};
556
557static const struct option simnet_lopts[] = {
558	{"temporary",	no_argument,		0, 't'	},
559	{"root-dir",	required_argument,	0, 'R'	},
560	{"media",	required_argument,	0, 'm'	},
561	{"peer",	required_argument,	0, 'p'	},
562	{ NULL, 0, NULL, 0 }
563};
564
565static const struct option bridge_lopts[] = {
566	{ "protect",		required_argument,	0, 'P' },
567	{ "root-dir",		required_argument,	0, 'R'	},
568	{ "forward-delay",	required_argument,	0, 'd'	},
569	{ "force-protocol",	required_argument,	0, 'f'	},
570	{ "hello-time",		required_argument,	0, 'h'	},
571	{ "link",		required_argument,	0, 'l'	},
572	{ "max-age",		required_argument,	0, 'm'	},
573	{ "priority",		required_argument,	0, 'p'	},
574	{ NULL, 0, NULL, 0 }
575};
576
577static const struct option bridge_show_lopts[] = {
578	{ "forwarding", no_argument,		0, 'f' },
579	{ "interval",	required_argument,	0, 'i' },
580	{ "link",	no_argument,		0, 'l' },
581	{ "output",	required_argument,	0, 'o' },
582	{ "parsable",	no_argument,		0, 'p' },
583	{ "parseable",	no_argument,		0, 'p' },
584	{ "statistics",	no_argument,		0, 's' },
585	{ "trill",	no_argument,		0, 't' },
586	{ NULL, 0, NULL, 0 }
587};
588
589/*
590 * structures for 'dladm show-ether'
591 */
592static const char *ptype[] = {LEI_ATTR_NAMES};
593
594typedef struct ether_fields_buf_s
595{
596	char	eth_link[15];
597	char	eth_ptype[8];
598	char	eth_state[8];
599	char	eth_autoneg[5];
600	char	eth_spdx[31];
601	char	eth_pause[6];
602	char	eth_rem_fault[16];
603} ether_fields_buf_t;
604
605static const ofmt_field_t ether_fields[] = {
606/* name,	field width,	offset	    callback */
607{ "LINK",	16,
608	offsetof(ether_fields_buf_t, eth_link), print_default_cb},
609{ "PTYPE",	9,
610	offsetof(ether_fields_buf_t, eth_ptype), print_default_cb},
611{ "STATE",	9,
612	offsetof(ether_fields_buf_t, eth_state),
613	print_default_cb},
614{ "AUTO",	6,
615	offsetof(ether_fields_buf_t, eth_autoneg), print_default_cb},
616{ "SPEED-DUPLEX", 32,
617	offsetof(ether_fields_buf_t, eth_spdx), print_default_cb},
618{ "PAUSE",	7,
619	offsetof(ether_fields_buf_t, eth_pause), print_default_cb},
620{ "REM_FAULT",	17,
621	offsetof(ether_fields_buf_t, eth_rem_fault), print_default_cb},
622{NULL,		0,
623	0,	NULL}}
624;
625
626typedef struct print_ether_state {
627	const char	*es_link;
628	boolean_t	es_parsable;
629	boolean_t	es_header;
630	boolean_t	es_extended;
631	ofmt_handle_t	es_ofmt;
632} print_ether_state_t;
633
634/*
635 * structures for 'dladm show-link -s' (print statistics)
636 */
637typedef enum {
638	LINK_S_LINK,
639	LINK_S_IPKTS,
640	LINK_S_RBYTES,
641	LINK_S_IERRORS,
642	LINK_S_OPKTS,
643	LINK_S_OBYTES,
644	LINK_S_OERRORS
645} link_s_field_index_t;
646
647static const ofmt_field_t link_s_fields[] = {
648/* name,	field width,	index,		callback	*/
649{ "LINK",	15,		LINK_S_LINK,	print_link_stats_cb},
650{ "IPACKETS",	10,		LINK_S_IPKTS,	print_link_stats_cb},
651{ "RBYTES",	8,		LINK_S_RBYTES,	print_link_stats_cb},
652{ "IERRORS",	10,		LINK_S_IERRORS,	print_link_stats_cb},
653{ "OPACKETS",	12,		LINK_S_OPKTS,	print_link_stats_cb},
654{ "OBYTES",	12,		LINK_S_OBYTES,	print_link_stats_cb},
655{ "OERRORS",	8,		LINK_S_OERRORS,	print_link_stats_cb},
656{ NULL,		0,		0,		NULL}};
657
658typedef struct link_args_s {
659	char		*link_s_link;
660	pktsum_t	*link_s_psum;
661} link_args_t;
662
663/*
664 * buffer used by print functions for show-{link,phys,vlan} commands.
665 */
666typedef struct link_fields_buf_s {
667	char link_name[MAXLINKNAMELEN];
668	char link_class[DLADM_STRSIZE];
669	char link_mtu[11];
670	char link_state[DLADM_STRSIZE];
671	char link_bridge[MAXLINKNAMELEN * MAXPORT];
672	char link_over[MAXLINKNAMELEN * MAXPORT];
673	char link_phys_state[DLADM_STRSIZE];
674	char link_phys_media[DLADM_STRSIZE];
675	char link_phys_speed[DLADM_STRSIZE];
676	char link_phys_duplex[DLPI_LINKNAME_MAX];
677	char link_phys_device[DLPI_LINKNAME_MAX];
678	char link_flags[6];
679	char link_vlan_vid[6];
680} link_fields_buf_t;
681
682/*
683 * structures for 'dladm show-link'
684 */
685static const ofmt_field_t link_fields[] = {
686/* name,	field width,	index,	callback */
687{ "LINK",	12,
688	offsetof(link_fields_buf_t, link_name), print_default_cb},
689{ "CLASS",	10,
690	offsetof(link_fields_buf_t, link_class), print_default_cb},
691{ "MTU",	7,
692	offsetof(link_fields_buf_t, link_mtu), print_default_cb},
693{ "STATE",	9,
694	offsetof(link_fields_buf_t, link_state), print_default_cb},
695{ "BRIDGE",	11,
696    offsetof(link_fields_buf_t, link_bridge), print_default_cb},
697{ "OVER",	30,
698	offsetof(link_fields_buf_t, link_over), print_default_cb},
699{ NULL,		0, 0, NULL}}
700;
701
702/*
703 * structures for 'dladm show-aggr'
704 */
705typedef struct laggr_fields_buf_s {
706	char laggr_name[DLPI_LINKNAME_MAX];
707	char laggr_policy[9];
708	char laggr_addrpolicy[ETHERADDRL * 3 + 3];
709	char laggr_lacpactivity[14];
710	char laggr_lacptimer[DLADM_STRSIZE];
711	char laggr_flags[7];
712} laggr_fields_buf_t;
713
714typedef struct laggr_args_s {
715	int			laggr_lport; /* -1 indicates the aggr itself */
716	const char		*laggr_link;
717	dladm_aggr_grp_attr_t	*laggr_ginfop;
718	dladm_status_t		*laggr_status;
719	pktsum_t		*laggr_pktsumtot; /* -s only */
720	pktsum_t		*laggr_diffstats; /* -s only */
721	boolean_t		laggr_parsable;
722} laggr_args_t;
723
724static const ofmt_field_t laggr_fields[] = {
725/* name,	field width,	offset,	callback */
726{ "LINK",	16,
727	offsetof(laggr_fields_buf_t, laggr_name), print_default_cb},
728{ "POLICY",	9,
729	offsetof(laggr_fields_buf_t, laggr_policy), print_default_cb},
730{ "ADDRPOLICY",	ETHERADDRL * 3 + 3,
731	offsetof(laggr_fields_buf_t, laggr_addrpolicy), print_default_cb},
732{ "LACPACTIVITY", 14,
733	offsetof(laggr_fields_buf_t, laggr_lacpactivity), print_default_cb},
734{ "LACPTIMER",	12,
735	offsetof(laggr_fields_buf_t, laggr_lacptimer), print_default_cb},
736{ "FLAGS",	8,
737	offsetof(laggr_fields_buf_t, laggr_flags), print_default_cb},
738{ NULL,		0, 0, NULL}}
739;
740
741/*
742 * structures for 'dladm show-aggr -x'.
743 */
744typedef enum {
745	AGGR_X_LINK,
746	AGGR_X_PORT,
747	AGGR_X_SPEED,
748	AGGR_X_DUPLEX,
749	AGGR_X_STATE,
750	AGGR_X_ADDRESS,
751	AGGR_X_PORTSTATE
752} aggr_x_field_index_t;
753
754static const ofmt_field_t aggr_x_fields[] = {
755/* name,	field width,	index		callback */
756{ "LINK",	12,	AGGR_X_LINK,		print_xaggr_cb},
757{ "PORT",	15,	AGGR_X_PORT,		print_xaggr_cb},
758{ "SPEED",	5,	AGGR_X_SPEED,		print_xaggr_cb},
759{ "DUPLEX",	10,	AGGR_X_DUPLEX,		print_xaggr_cb},
760{ "STATE",	10,	AGGR_X_STATE,		print_xaggr_cb},
761{ "ADDRESS",	19,	AGGR_X_ADDRESS,		print_xaggr_cb},
762{ "PORTSTATE",	16,	AGGR_X_PORTSTATE,	print_xaggr_cb},
763{ NULL,		0,	0,			NULL}}
764;
765
766/*
767 * structures for 'dladm show-aggr -s'.
768 */
769typedef enum {
770	AGGR_S_LINK,
771	AGGR_S_PORT,
772	AGGR_S_IPKTS,
773	AGGR_S_RBYTES,
774	AGGR_S_OPKTS,
775	AGGR_S_OBYTES,
776	AGGR_S_IPKTDIST,
777	AGGR_S_OPKTDIST
778} aggr_s_field_index_t;
779
780static const ofmt_field_t aggr_s_fields[] = {
781{ "LINK",		12,	AGGR_S_LINK, print_aggr_stats_cb},
782{ "PORT",		10,	AGGR_S_PORT, print_aggr_stats_cb},
783{ "IPACKETS",		8,	AGGR_S_IPKTS, print_aggr_stats_cb},
784{ "RBYTES",		8,	AGGR_S_RBYTES, print_aggr_stats_cb},
785{ "OPACKETS",		8,	AGGR_S_OPKTS, print_aggr_stats_cb},
786{ "OBYTES",		8,	AGGR_S_OBYTES, print_aggr_stats_cb},
787{ "IPKTDIST",		9,	AGGR_S_IPKTDIST, print_aggr_stats_cb},
788{ "OPKTDIST",		15,	AGGR_S_OPKTDIST, print_aggr_stats_cb},
789{ NULL,			0,	0,		NULL}}
790;
791
792/*
793 * structures for 'dladm show-aggr -L'.
794 */
795typedef enum {
796	AGGR_L_LINK,
797	AGGR_L_PORT,
798	AGGR_L_AGGREGATABLE,
799	AGGR_L_SYNC,
800	AGGR_L_COLL,
801	AGGR_L_DIST,
802	AGGR_L_DEFAULTED,
803	AGGR_L_EXPIRED
804} aggr_l_field_index_t;
805
806static const ofmt_field_t aggr_l_fields[] = {
807/* name,		field width,	index */
808{ "LINK",		12,	AGGR_L_LINK,		print_lacp_cb},
809{ "PORT",		13,	AGGR_L_PORT,		print_lacp_cb},
810{ "AGGREGATABLE",	13,	AGGR_L_AGGREGATABLE,	print_lacp_cb},
811{ "SYNC",		5,	AGGR_L_SYNC,		print_lacp_cb},
812{ "COLL",		5,	AGGR_L_COLL,		print_lacp_cb},
813{ "DIST",		5,	AGGR_L_DIST,		print_lacp_cb},
814{ "DEFAULTED",		10,	AGGR_L_DEFAULTED,	print_lacp_cb},
815{ "EXPIRED",		15,	AGGR_L_EXPIRED,		print_lacp_cb},
816{ NULL,			0,	0,			NULL}}
817;
818
819/*
820 * structures for 'dladm show-phys'
821 */
822
823static const ofmt_field_t phys_fields[] = {
824/* name,	field width,	offset */
825{ "LINK",	13,
826	offsetof(link_fields_buf_t, link_name), print_default_cb},
827{ "MEDIA",	21,
828	offsetof(link_fields_buf_t, link_phys_media), print_default_cb},
829{ "STATE",	11,
830	offsetof(link_fields_buf_t, link_phys_state), print_default_cb},
831{ "SPEED",	7,
832	offsetof(link_fields_buf_t, link_phys_speed), print_default_cb},
833{ "DUPLEX",	10,
834	offsetof(link_fields_buf_t, link_phys_duplex), print_default_cb},
835{ "DEVICE",	13,
836	offsetof(link_fields_buf_t, link_phys_device), print_default_cb},
837{ "FLAGS",	7,
838	offsetof(link_fields_buf_t, link_flags), print_default_cb},
839{ NULL,		0, 0, NULL}}
840;
841
842/*
843 * structures for 'dladm show-phys -m'
844 */
845
846typedef enum {
847	PHYS_M_LINK,
848	PHYS_M_SLOT,
849	PHYS_M_ADDRESS,
850	PHYS_M_INUSE,
851	PHYS_M_CLIENT
852} phys_m_field_index_t;
853
854static const ofmt_field_t phys_m_fields[] = {
855/* name,	field width,	offset */
856{ "LINK",	13,	PHYS_M_LINK,	print_phys_one_mac_cb},
857{ "SLOT",	9,	PHYS_M_SLOT,	print_phys_one_mac_cb},
858{ "ADDRESS",	19,	PHYS_M_ADDRESS,	print_phys_one_mac_cb},
859{ "INUSE",	5,	PHYS_M_INUSE,	print_phys_one_mac_cb},
860{ "CLIENT",	13,	PHYS_M_CLIENT,	print_phys_one_mac_cb},
861{ NULL,		0,	0,		NULL}}
862;
863
864/*
865 * structures for 'dladm show-phys -H'
866 */
867
868typedef enum {
869	PHYS_H_LINK,
870	PHYS_H_RINGTYPE,
871	PHYS_H_RINGS,
872	PHYS_H_CLIENTS
873} phys_h_field_index_t;
874
875#define	RINGSTRLEN	21
876
877static const ofmt_field_t phys_h_fields[] = {
878{ "LINK",	13,	PHYS_H_LINK,	print_phys_one_hwgrp_cb},
879{ "RINGTYPE",	9,	PHYS_H_RINGTYPE,	print_phys_one_hwgrp_cb},
880{ "RINGS",	RINGSTRLEN,	PHYS_H_RINGS,	print_phys_one_hwgrp_cb},
881{ "CLIENTS",	24,	PHYS_H_CLIENTS,	print_phys_one_hwgrp_cb},
882{ NULL,		0,	0,		NULL}}
883;
884
885/*
886 * structures for 'dladm show-vlan'
887 */
888static const ofmt_field_t vlan_fields[] = {
889{ "LINK",	16,
890	offsetof(link_fields_buf_t, link_name), print_default_cb},
891{ "VID",	9,
892	offsetof(link_fields_buf_t, link_vlan_vid), print_default_cb},
893{ "OVER",	13,
894	offsetof(link_fields_buf_t, link_over), print_default_cb},
895{ "FLAGS",	7,
896	offsetof(link_fields_buf_t, link_flags), print_default_cb},
897{ NULL,		0, 0, NULL}}
898;
899
900/*
901 * structures common to 'dladm scan-wifi' and 'dladm show-wifi'
902 * callback will be determined in parse_wifi_fields.
903 */
904static ofmt_field_t wifi_common_fields[] = {
905{ "LINK",	11, 0,				NULL},
906{ "ESSID",	20, DLADM_WLAN_ATTR_ESSID,	NULL},
907{ "BSSID",	18, DLADM_WLAN_ATTR_BSSID,	NULL},
908{ "IBSSID",	18, DLADM_WLAN_ATTR_BSSID,	NULL},
909{ "MODE",	7,  DLADM_WLAN_ATTR_MODE,	NULL},
910{ "SPEED",	7,  DLADM_WLAN_ATTR_SPEED,	NULL},
911{ "BSSTYPE",	9,  DLADM_WLAN_ATTR_BSSTYPE,	NULL},
912{ "SEC",	7,  DLADM_WLAN_ATTR_SECMODE,	NULL},
913{ "STRENGTH",	11, DLADM_WLAN_ATTR_STRENGTH,	NULL},
914{ NULL,		0,  0,				NULL}};
915
916/*
917 * the 'show-wifi' command supports all the fields in wifi_common_fields
918 * plus the AUTH and STATUS fields.
919 */
920static ofmt_field_t wifi_show_fields[A_CNT(wifi_common_fields) + 2] = {
921{ "AUTH",	9,  DLADM_WLAN_ATTR_AUTH,	NULL},
922{ "STATUS",	18, DLADM_WLAN_LINKATTR_STATUS,	print_wifi_status_cb},
923/* copy wifi_common_fields here */
924};
925
926static char *all_scan_wifi_fields =
927	"link,essid,bssid,sec,strength,mode,speed,bsstype";
928static char *all_show_wifi_fields =
929	"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
930static char *def_scan_wifi_fields =
931	"link,essid,bssid,sec,strength,mode,speed";
932static char *def_show_wifi_fields =
933	"link,status,essid,sec,strength,mode,speed";
934
935/*
936 * structures for 'dladm show-linkprop'
937 */
938typedef enum {
939	LINKPROP_LINK,
940	LINKPROP_PROPERTY,
941	LINKPROP_PERM,
942	LINKPROP_VALUE,
943	LINKPROP_DEFAULT,
944	LINKPROP_POSSIBLE
945} linkprop_field_index_t;
946
947static const ofmt_field_t linkprop_fields[] = {
948/* name,	field width,  index */
949{ "LINK",	13,	LINKPROP_LINK,		print_linkprop_cb},
950{ "PROPERTY",	16,	LINKPROP_PROPERTY,	print_linkprop_cb},
951{ "PERM",	5,	LINKPROP_PERM,		print_linkprop_cb},
952{ "VALUE",	15,	LINKPROP_VALUE,		print_linkprop_cb},
953{ "DEFAULT",	15,	LINKPROP_DEFAULT,	print_linkprop_cb},
954{ "POSSIBLE",	20,	LINKPROP_POSSIBLE,	print_linkprop_cb},
955{ NULL,		0,	0,			NULL}}
956;
957
958#define	MAX_PROP_LINE		512
959
960typedef struct show_linkprop_state {
961	char			ls_link[MAXLINKNAMELEN];
962	char			*ls_line;
963	char			**ls_propvals;
964	dladm_arg_list_t	*ls_proplist;
965	boolean_t		ls_parsable;
966	boolean_t		ls_persist;
967	boolean_t		ls_header;
968	dladm_status_t		ls_status;
969	dladm_status_t		ls_retstatus;
970	ofmt_handle_t		ls_ofmt;
971} show_linkprop_state_t;
972
973typedef struct set_linkprop_state {
974	const char		*ls_name;
975	boolean_t		ls_reset;
976	boolean_t		ls_temp;
977	dladm_status_t		ls_status;
978} set_linkprop_state_t;
979
980typedef struct linkprop_args_s {
981	show_linkprop_state_t	*ls_state;
982	char			*ls_propname;
983	datalink_id_t		ls_linkid;
984} linkprop_args_t;
985
986/*
987 * structures for 'dladm show-secobj'
988 */
989typedef struct secobj_fields_buf_s {
990	char			ss_obj_name[DLADM_SECOBJ_VAL_MAX];
991	char			ss_class[20];
992	char			ss_val[30];
993} secobj_fields_buf_t;
994
995static const ofmt_field_t secobj_fields[] = {
996{ "OBJECT",	21,
997	offsetof(secobj_fields_buf_t, ss_obj_name), print_default_cb},
998{ "CLASS",	21,
999	offsetof(secobj_fields_buf_t, ss_class), print_default_cb},
1000{ "VALUE",	31,
1001	offsetof(secobj_fields_buf_t, ss_val), print_default_cb},
1002{ NULL,		0, 0, NULL}}
1003;
1004
1005/*
1006 * structures for 'dladm show-vnic'
1007 */
1008typedef struct vnic_fields_buf_s
1009{
1010	char vnic_link[DLPI_LINKNAME_MAX];
1011	char vnic_over[DLPI_LINKNAME_MAX];
1012	char vnic_speed[6];
1013	char vnic_macaddr[18];
1014	char vnic_macaddrtype[19];
1015	char vnic_vid[6];
1016} vnic_fields_buf_t;
1017
1018static const ofmt_field_t vnic_fields[] = {
1019{ "LINK",		13,
1020	offsetof(vnic_fields_buf_t, vnic_link),	print_default_cb},
1021{ "OVER",		13,
1022	offsetof(vnic_fields_buf_t, vnic_over),	print_default_cb},
1023{ "SPEED",		7,
1024	offsetof(vnic_fields_buf_t, vnic_speed), print_default_cb},
1025{ "MACADDRESS",		18,
1026	offsetof(vnic_fields_buf_t, vnic_macaddr), print_default_cb},
1027{ "MACADDRTYPE",	20,
1028	offsetof(vnic_fields_buf_t, vnic_macaddrtype), print_default_cb},
1029{ "VID",		7,
1030	offsetof(vnic_fields_buf_t, vnic_vid), print_default_cb},
1031{ NULL,			0, 0, NULL}}
1032;
1033
1034/*
1035 * structures for 'dladm show-ib'
1036 */
1037typedef struct ib_fields_buf_s
1038{
1039	char ib_link[DLPI_LINKNAME_MAX];
1040	char ib_hcaguid[17];
1041	char ib_portguid[17];
1042	char ib_portnum[4];
1043	char ib_state[6];
1044	char ib_pkeys[MAXPKEYSTRSZ];
1045} ib_fields_buf_t;
1046
1047static const ofmt_field_t ib_fields[] = {
1048{ "LINK",		13,
1049	offsetof(ib_fields_buf_t, ib_link),	print_default_cb},
1050{ "HCAGUID",		IBGUIDSTRLEN,
1051	offsetof(ib_fields_buf_t, ib_hcaguid),	print_default_cb},
1052{ "PORTGUID",		IBGUIDSTRLEN,
1053	offsetof(ib_fields_buf_t, ib_portguid),	print_default_cb},
1054{ "PORT",		IBPORTSTRLEN,
1055	offsetof(ib_fields_buf_t, ib_portnum), print_default_cb},
1056{ "STATE",		7,
1057	offsetof(ib_fields_buf_t, ib_state), print_default_cb},
1058{ "PKEYS",	18,
1059	offsetof(ib_fields_buf_t, ib_pkeys), print_default_cb},
1060{ NULL,			0, 0, NULL}};
1061
1062/*
1063 * structures for 'dladm show-part'
1064 */
1065typedef struct part_fields_buf_s
1066{
1067	char part_link[DLPI_LINKNAME_MAX];
1068	char part_pkey[5];
1069	char part_over[DLPI_LINKNAME_MAX];
1070	char part_state[8];
1071	char part_flags[5];
1072} part_fields_buf_t;
1073
1074static const ofmt_field_t part_fields[] = {
1075{ "LINK",		13,
1076	offsetof(part_fields_buf_t, part_link),	print_default_cb},
1077{ "PKEY",		MAXPKEYLEN,
1078	offsetof(part_fields_buf_t, part_pkey),	print_default_cb},
1079{ "OVER",		13,
1080	offsetof(part_fields_buf_t, part_over), print_default_cb},
1081{ "STATE",		9,
1082	offsetof(part_fields_buf_t, part_state), print_default_cb},
1083{ "FLAGS",	5,
1084	offsetof(part_fields_buf_t, part_flags), print_default_cb},
1085{ NULL,			0, 0, NULL}};
1086
1087/*
1088 * structures for 'dladm show-simnet'
1089 */
1090typedef struct simnet_fields_buf_s
1091{
1092	char simnet_name[DLPI_LINKNAME_MAX];
1093	char simnet_media[DLADM_STRSIZE];
1094	char simnet_macaddr[18];
1095	char simnet_otherlink[DLPI_LINKNAME_MAX];
1096} simnet_fields_buf_t;
1097
1098static const ofmt_field_t simnet_fields[] = {
1099{ "LINK",		12,
1100	offsetof(simnet_fields_buf_t, simnet_name), print_default_cb},
1101{ "MEDIA",		20,
1102	offsetof(simnet_fields_buf_t, simnet_media), print_default_cb},
1103{ "MACADDRESS",		18,
1104	offsetof(simnet_fields_buf_t, simnet_macaddr), print_default_cb},
1105{ "OTHERLINK",		12,
1106	offsetof(simnet_fields_buf_t, simnet_otherlink), print_default_cb},
1107{ NULL,			0, 0, NULL}}
1108;
1109
1110/*
1111 * structures for 'dladm show-usage'
1112 */
1113
1114typedef struct  usage_fields_buf_s {
1115	char	usage_link[12];
1116	char	usage_duration[10];
1117	char	usage_ipackets[9];
1118	char	usage_rbytes[10];
1119	char	usage_opackets[9];
1120	char	usage_obytes[10];
1121	char	usage_bandwidth[15];
1122} usage_fields_buf_t;
1123
1124static const ofmt_field_t usage_fields[] = {
1125{ "LINK",	13,
1126	offsetof(usage_fields_buf_t, usage_link), print_default_cb},
1127{ "DURATION",	11,
1128	offsetof(usage_fields_buf_t, usage_duration), print_default_cb},
1129{ "IPACKETS",	10,
1130	offsetof(usage_fields_buf_t, usage_ipackets), print_default_cb},
1131{ "RBYTES",	11,
1132	offsetof(usage_fields_buf_t, usage_rbytes), print_default_cb},
1133{ "OPACKETS",	10,
1134	offsetof(usage_fields_buf_t, usage_opackets), print_default_cb},
1135{ "OBYTES",	11,
1136	offsetof(usage_fields_buf_t, usage_obytes), print_default_cb},
1137{ "BANDWIDTH",	16,
1138	offsetof(usage_fields_buf_t, usage_bandwidth), print_default_cb},
1139{ NULL,		0, 0, NULL}}
1140;
1141
1142
1143/*
1144 * structures for 'dladm show-usage link'
1145 */
1146
1147typedef struct  usage_l_fields_buf_s {
1148	char	usage_l_link[12];
1149	char	usage_l_stime[13];
1150	char	usage_l_etime[13];
1151	char	usage_l_rbytes[8];
1152	char	usage_l_obytes[8];
1153	char	usage_l_bandwidth[15];
1154} usage_l_fields_buf_t;
1155
1156static const ofmt_field_t usage_l_fields[] = {
1157/* name,	field width,	offset */
1158{ "LINK",	13,
1159	offsetof(usage_l_fields_buf_t, usage_l_link), print_default_cb},
1160{ "START",	14,
1161	offsetof(usage_l_fields_buf_t, usage_l_stime), print_default_cb},
1162{ "END",	14,
1163	offsetof(usage_l_fields_buf_t, usage_l_etime), print_default_cb},
1164{ "RBYTES",	9,
1165	offsetof(usage_l_fields_buf_t, usage_l_rbytes), print_default_cb},
1166{ "OBYTES",	9,
1167	offsetof(usage_l_fields_buf_t, usage_l_obytes), print_default_cb},
1168{ "BANDWIDTH",	16,
1169	offsetof(usage_l_fields_buf_t, usage_l_bandwidth), print_default_cb},
1170{ NULL,		0, 0, NULL}}
1171;
1172
1173/* IPTUN_*FLAG_INDEX values are indices into iptun_flags below. */
1174enum { IPTUN_SFLAG_INDEX, IPTUN_IFLAG_INDEX, IPTUN_NUM_FLAGS };
1175
1176/*
1177 * structures for 'dladm show-iptun'
1178 */
1179typedef struct iptun_fields_buf_s {
1180	char	iptun_name[MAXLINKNAMELEN];
1181	char	iptun_type[5];
1182	char	iptun_laddr[NI_MAXHOST];
1183	char	iptun_raddr[NI_MAXHOST];
1184	char	iptun_flags[IPTUN_NUM_FLAGS + 1];
1185} iptun_fields_buf_t;
1186
1187static const ofmt_field_t iptun_fields[] = {
1188{ "LINK",	16,
1189	offsetof(iptun_fields_buf_t, iptun_name), print_default_cb },
1190{ "TYPE",	6,
1191	offsetof(iptun_fields_buf_t, iptun_type), print_default_cb },
1192{ "FLAGS",	7,
1193	offsetof(iptun_fields_buf_t, iptun_flags), print_default_cb },
1194{ "LOCAL",	20,
1195	offsetof(iptun_fields_buf_t, iptun_laddr), print_default_cb },
1196{ "REMOTE",	20,
1197	offsetof(iptun_fields_buf_t, iptun_raddr), print_default_cb },
1198{ NULL, 0, 0, NULL}
1199};
1200
1201/*
1202 * structures for 'dladm show-bridge'.  These are based on sections 14.8.1.1.3
1203 * and 14.8.1.2.2 of IEEE 802.1D-2004.
1204 */
1205typedef struct bridge_fields_buf_s {
1206	char bridge_name[MAXLINKNAMELEN]; /* 14.4.1.2.3(b) */
1207	char bridge_protect[7];		/* stp or trill */
1208	char bridge_address[24];	/* 17.18.3, 7.12.5, 14.4.1.2.3(a) */
1209	char bridge_priority[7];	/* 17.18.3 9.2.5 - only upper 4 bits */
1210	char bridge_bmaxage[7];		/* 17.18.4 configured */
1211	char bridge_bhellotime[7];	/* 17.18.4 configured */
1212	char bridge_bfwddelay[7];	/* 17.18.4 configured */
1213	char bridge_forceproto[3];	/* 17.13.4 configured */
1214	char bridge_tctime[12];		/* 14.8.1.1.3(b) */
1215	char bridge_tccount[12];	/* 17.17.8 */
1216	char bridge_tchange[12];	/* 17.17.8 */
1217	char bridge_desroot[24];	/* 17.18.6 priority "/" MAC */
1218	char bridge_rootcost[12];	/* 17.18.6 */
1219	char bridge_rootport[12];	/* 17.18.6 */
1220	char bridge_maxage[7];		/* 17.18.7 for root */
1221	char bridge_hellotime[7];	/* 17.13.6 for root */
1222	char bridge_fwddelay[7];	/* 17.13.5 for root */
1223	char bridge_holdtime[12];	/* 17.13.12 for root */
1224} bridge_fields_buf_t;
1225
1226static ofmt_field_t bridge_fields[] = {
1227/* name,	field width,	offset,	callback	*/
1228{ "BRIDGE",	12,
1229    offsetof(bridge_fields_buf_t, bridge_name), print_default_cb },
1230{ "PROTECT",	8,
1231    offsetof(bridge_fields_buf_t, bridge_protect), print_default_cb },
1232{ "ADDRESS",	19,
1233    offsetof(bridge_fields_buf_t, bridge_address), print_default_cb },
1234{ "PRIORITY",	9,
1235    offsetof(bridge_fields_buf_t, bridge_priority), print_default_cb },
1236{ "BMAXAGE",	8,
1237    offsetof(bridge_fields_buf_t, bridge_bmaxage), print_default_cb },
1238{ "BHELLOTIME",	11,
1239    offsetof(bridge_fields_buf_t, bridge_bhellotime), print_default_cb },
1240{ "BFWDDELAY",	10,
1241    offsetof(bridge_fields_buf_t, bridge_bfwddelay), print_default_cb },
1242{ "FORCEPROTO",	11,
1243    offsetof(bridge_fields_buf_t, bridge_forceproto), print_default_cb },
1244{ "TCTIME",	10,
1245    offsetof(bridge_fields_buf_t, bridge_tctime), print_default_cb },
1246{ "TCCOUNT",	10,
1247    offsetof(bridge_fields_buf_t, bridge_tccount), print_default_cb },
1248{ "TCHANGE",	10,
1249    offsetof(bridge_fields_buf_t, bridge_tchange), print_default_cb },
1250{ "DESROOT",	23,
1251    offsetof(bridge_fields_buf_t, bridge_desroot), print_default_cb },
1252{ "ROOTCOST",	11,
1253    offsetof(bridge_fields_buf_t, bridge_rootcost), print_default_cb },
1254{ "ROOTPORT",	11,
1255    offsetof(bridge_fields_buf_t, bridge_rootport), print_default_cb },
1256{ "MAXAGE",	8,
1257    offsetof(bridge_fields_buf_t, bridge_maxage), print_default_cb },
1258{ "HELLOTIME",	10,
1259    offsetof(bridge_fields_buf_t, bridge_hellotime), print_default_cb },
1260{ "FWDDELAY",	9,
1261    offsetof(bridge_fields_buf_t, bridge_fwddelay), print_default_cb },
1262{ "HOLDTIME",	9,
1263    offsetof(bridge_fields_buf_t, bridge_holdtime), print_default_cb },
1264{ NULL,		0, 0, NULL}};
1265
1266/*
1267 * structures for 'dladm show-bridge -l'.  These are based on 14.4.1.2.3 and
1268 * 14.8.2.1.3 of IEEE 802.1D-2004.
1269 */
1270typedef struct bridge_link_fields_buf_s {
1271	char bridgel_link[MAXLINKNAMELEN];
1272	char bridgel_index[7];			/* 14.4.1.2.3(d1) */
1273	char bridgel_state[11];			/* 14.8.2.1.3(b) */
1274	char bridgel_uptime[7];			/* 14.8.2.1.3(a) */
1275	char bridgel_opercost[7]		/* 14.8.2.1.3(d) */;
1276	char bridgel_operp2p[4];		/* 14.8.2.1.3(p) */
1277	char bridgel_operedge[4];		/* 14.8.2.1.3(k) */
1278	char bridgel_desroot[23];		/* 14.8.2.1.3(e) */
1279	char bridgel_descost[12];		/* 14.8.2.1.3(f) */
1280	char bridgel_desbridge[23];		/* 14.8.2.1.3(g) */
1281	char bridgel_desport[7];		/* 14.8.2.1.3(h) */
1282	char bridgel_tcack[4];			/* 14.8.2.1.3(i) */
1283} bridge_link_fields_buf_t;
1284
1285static ofmt_field_t bridge_link_fields[] = {
1286/* name,	field width,	offset,	callback	*/
1287{ "LINK",		12,
1288    offsetof(bridge_link_fields_buf_t, bridgel_link), print_default_cb },
1289{ "INDEX",	8,
1290    offsetof(bridge_link_fields_buf_t, bridgel_index), print_default_cb },
1291{ "STATE",	12,
1292    offsetof(bridge_link_fields_buf_t, bridgel_state), print_default_cb },
1293{ "UPTIME",	8,
1294    offsetof(bridge_link_fields_buf_t, bridgel_uptime), print_default_cb },
1295{ "OPERCOST",	9,
1296    offsetof(bridge_link_fields_buf_t, bridgel_opercost), print_default_cb },
1297{ "OPERP2P",	8,
1298    offsetof(bridge_link_fields_buf_t, bridgel_operp2p), print_default_cb },
1299{ "OPEREDGE",	9,
1300    offsetof(bridge_link_fields_buf_t, bridgel_operedge), print_default_cb },
1301{ "DESROOT",	22,
1302    offsetof(bridge_link_fields_buf_t, bridgel_desroot), print_default_cb },
1303{ "DESCOST",	11,
1304    offsetof(bridge_link_fields_buf_t, bridgel_descost), print_default_cb },
1305{ "DESBRIDGE",	22,
1306    offsetof(bridge_link_fields_buf_t, bridgel_desbridge), print_default_cb },
1307{ "DESPORT",	8,
1308    offsetof(bridge_link_fields_buf_t, bridgel_desport), print_default_cb },
1309{ "TCACK",	6,
1310    offsetof(bridge_link_fields_buf_t, bridgel_tcack), print_default_cb },
1311{ NULL,		0, 0, NULL}};
1312
1313/*
1314 * structures for 'dladm show-bridge -s'.  These are not based on IEEE
1315 * 802.1D-2004.
1316 */
1317#define	ULONG_DIG	(((sizeof (ulong_t) * NBBY) * 3 / 10) + 1)
1318#define	UINT64_DIG	(((sizeof (uint64_t) * NBBY) * 3 / 10) + 1)
1319typedef struct bridge_statfields_buf_s {
1320	char bridges_name[MAXLINKNAMELEN];
1321	char bridges_drops[UINT64_DIG];
1322	char bridges_forwards[UINT64_DIG];
1323	char bridges_mbcast[UINT64_DIG];
1324	char bridges_unknown[UINT64_DIG];
1325	char bridges_recv[UINT64_DIG];
1326	char bridges_sent[UINT64_DIG];
1327} bridge_statfields_buf_t;
1328
1329static ofmt_field_t bridge_statfields[] = {
1330/* name,	field width,	offset,	callback	*/
1331{ "BRIDGE",	12,
1332    offsetof(bridge_statfields_buf_t, bridges_name), print_default_cb },
1333{ "DROPS",	12,
1334    offsetof(bridge_statfields_buf_t, bridges_drops), print_default_cb },
1335{ "FORWARDS",	12,
1336    offsetof(bridge_statfields_buf_t, bridges_forwards), print_default_cb },
1337{ "MBCAST",	12,
1338    offsetof(bridge_statfields_buf_t, bridges_mbcast), print_default_cb },
1339{ "UNKNOWN",	12,
1340    offsetof(bridge_statfields_buf_t, bridges_unknown), print_default_cb },
1341{ "RECV",	12,
1342    offsetof(bridge_statfields_buf_t, bridges_recv), print_default_cb },
1343{ "SENT",	12,
1344    offsetof(bridge_statfields_buf_t, bridges_sent), print_default_cb },
1345{ NULL,		0, 0, NULL}};
1346
1347/*
1348 * structures for 'dladm show-bridge -s -l'.  These are based in part on
1349 * section 14.6.1.1.3 of IEEE 802.1D-2004.
1350 */
1351typedef struct bridge_link_statfields_buf_s {
1352	char bridgels_link[MAXLINKNAMELEN];
1353	char bridgels_cfgbpdu[ULONG_DIG];
1354	char bridgels_tcnbpdu[ULONG_DIG];
1355	char bridgels_rstpbpdu[ULONG_DIG];
1356	char bridgels_txbpdu[ULONG_DIG];
1357	char bridgels_drops[UINT64_DIG];	/* 14.6.1.1.3(d) */
1358	char bridgels_recv[UINT64_DIG];		/* 14.6.1.1.3(a) */
1359	char bridgels_xmit[UINT64_DIG];		/* 14.6.1.1.3(c) */
1360} bridge_link_statfields_buf_t;
1361
1362static ofmt_field_t bridge_link_statfields[] = {
1363/* name,	field width,	offset,	callback	*/
1364{ "LINK",	12,
1365    offsetof(bridge_link_statfields_buf_t, bridgels_link), print_default_cb },
1366{ "CFGBPDU",	9,
1367    offsetof(bridge_link_statfields_buf_t, bridgels_cfgbpdu),
1368    print_default_cb },
1369{ "TCNBPDU",	9,
1370    offsetof(bridge_link_statfields_buf_t, bridgels_tcnbpdu),
1371    print_default_cb },
1372{ "RSTPBPDU",	9,
1373    offsetof(bridge_link_statfields_buf_t, bridgels_rstpbpdu),
1374    print_default_cb },
1375{ "TXBPDU",	9,
1376    offsetof(bridge_link_statfields_buf_t, bridgels_txbpdu), print_default_cb },
1377{ "DROPS",	9,
1378    offsetof(bridge_link_statfields_buf_t, bridgels_drops), print_default_cb },
1379{ "RECV",	9,
1380    offsetof(bridge_link_statfields_buf_t, bridgels_recv), print_default_cb },
1381{ "XMIT",	9,
1382    offsetof(bridge_link_statfields_buf_t, bridgels_xmit), print_default_cb },
1383{ NULL,		0, 0, NULL}};
1384
1385/*
1386 * structures for 'dladm show-bridge -f'.  These are based in part on
1387 * section  14.7.6.3.3 of IEEE 802.1D-2004.
1388 */
1389typedef struct bridge_fwd_fields_buf_s {
1390	char bridgef_dest[18];			/* 14.7.6.3.3(a) */
1391	char bridgef_age[8];
1392	char bridgef_flags[6];
1393	char bridgef_output[MAXLINKNAMELEN];	/* 14.7.6.3.3(c) */
1394} bridge_fwd_fields_buf_t;
1395
1396static ofmt_field_t bridge_fwd_fields[] = {
1397/* name,	field width,	offset,	callback	*/
1398{ "DEST",	17,
1399    offsetof(bridge_fwd_fields_buf_t, bridgef_dest), print_default_cb },
1400{ "AGE",	7,
1401    offsetof(bridge_fwd_fields_buf_t, bridgef_age), print_default_cb },
1402{ "FLAGS",	6,
1403    offsetof(bridge_fwd_fields_buf_t, bridgef_flags), print_default_cb },
1404{ "OUTPUT",	12,
1405    offsetof(bridge_fwd_fields_buf_t, bridgef_output), print_default_cb },
1406{ NULL,		0, 0, NULL}};
1407
1408/*
1409 * structures for 'dladm show-bridge -t'.
1410 */
1411typedef struct bridge_trill_fields_buf_s {
1412	char bridget_nick[6];
1413	char bridget_flags[6];
1414	char bridget_link[MAXLINKNAMELEN];
1415	char bridget_nexthop[18];
1416} bridge_trill_fields_buf_t;
1417
1418static ofmt_field_t bridge_trill_fields[] = {
1419/* name,	field width,	offset,	callback	*/
1420{ "NICK",	5,
1421    offsetof(bridge_trill_fields_buf_t, bridget_nick), print_default_cb },
1422{ "FLAGS",	6,
1423    offsetof(bridge_trill_fields_buf_t, bridget_flags), print_default_cb },
1424{ "LINK",	12,
1425    offsetof(bridge_trill_fields_buf_t, bridget_link), print_default_cb },
1426{ "NEXTHOP",	17,
1427    offsetof(bridge_trill_fields_buf_t, bridget_nexthop), print_default_cb },
1428{ NULL,		0, 0, NULL}};
1429
1430static char *progname;
1431static sig_atomic_t signalled;
1432
1433/*
1434 * Handle to libdladm.  Opened in main() before the sub-command
1435 * specific function is called.
1436 */
1437static dladm_handle_t handle = NULL;
1438
1439#define	DLADM_ETHERSTUB_NAME	"etherstub"
1440#define	DLADM_IS_ETHERSTUB(id)	(id == DATALINK_INVALID_LINKID)
1441
1442static void
1443usage(void)
1444{
1445	int	i;
1446	cmd_t	*cmdp;
1447	(void) fprintf(stderr, gettext("usage:  dladm <subcommand> <args> ..."
1448	    "\n"));
1449	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
1450		cmdp = &cmds[i];
1451		if (cmdp->c_usage != NULL)
1452			(void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
1453	}
1454
1455	/* close dladm handle if it was opened */
1456	if (handle != NULL)
1457		dladm_close(handle);
1458
1459	exit(EXIT_FAILURE);
1460}
1461
1462int
1463main(int argc, char *argv[])
1464{
1465	int	i;
1466	cmd_t	*cmdp;
1467	dladm_status_t status;
1468
1469	(void) setlocale(LC_ALL, "");
1470#if !defined(TEXT_DOMAIN)
1471#define	TEXT_DOMAIN "SYS_TEST"
1472#endif
1473	(void) textdomain(TEXT_DOMAIN);
1474
1475	progname = argv[0];
1476
1477	if (argc < 2)
1478		usage();
1479
1480	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
1481		cmdp = &cmds[i];
1482		if (strcmp(argv[1], cmdp->c_name) == 0) {
1483			/* Open the libdladm handle */
1484			if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
1485				die_dlerr(status,
1486				    "could not open /dev/dld");
1487			}
1488
1489			cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
1490
1491			dladm_close(handle);
1492			return (EXIT_SUCCESS);
1493		}
1494	}
1495
1496	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
1497	    progname, argv[1]);
1498	usage();
1499	return (EXIT_FAILURE);
1500}
1501
1502/*ARGSUSED*/
1503static int
1504show_usage_date(dladm_usage_t *usage, void *arg)
1505{
1506	show_usage_state_t	*state = (show_usage_state_t *)arg;
1507	time_t			stime;
1508	char			timebuf[20];
1509	dladm_status_t		status;
1510	uint32_t		flags;
1511
1512	/*
1513	 * Only show usage information for existing links unless '-a'
1514	 * is specified.
1515	 */
1516	if (!state->us_showall) {
1517		if ((status = dladm_name2info(handle, usage->du_name,
1518		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
1519			return (status);
1520		}
1521		if ((flags & DLADM_OPT_ACTIVE) == 0)
1522			return (DLADM_STATUS_LINKINVAL);
1523	}
1524
1525	stime = usage->du_stime;
1526	(void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
1527	    localtime(&stime));
1528	(void) printf("%s\n", timebuf);
1529
1530	return (DLADM_STATUS_OK);
1531}
1532
1533static int
1534show_usage_time(dladm_usage_t *usage, void *arg)
1535{
1536	show_usage_state_t	*state = (show_usage_state_t *)arg;
1537	char			buf[DLADM_STRSIZE];
1538	usage_l_fields_buf_t	ubuf;
1539	time_t			time;
1540	double			bw;
1541	dladm_status_t		status;
1542	uint32_t		flags;
1543
1544	/*
1545	 * Only show usage information for existing links unless '-a'
1546	 * is specified.
1547	 */
1548	if (!state->us_showall) {
1549		if ((status = dladm_name2info(handle, usage->du_name,
1550		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
1551			return (status);
1552		}
1553		if ((flags & DLADM_OPT_ACTIVE) == 0)
1554			return (DLADM_STATUS_LINKINVAL);
1555	}
1556
1557	if (state->us_plot) {
1558		if (!state->us_printheader) {
1559			if (state->us_first) {
1560				(void) printf("# Time");
1561				state->us_first = B_FALSE;
1562			}
1563			(void) printf(" %s", usage->du_name);
1564			if (usage->du_last) {
1565				(void) printf("\n");
1566				state->us_first = B_TRUE;
1567				state->us_printheader = B_TRUE;
1568			}
1569		} else {
1570			if (state->us_first) {
1571				time = usage->du_etime;
1572				(void) strftime(buf, sizeof (buf), "%T",
1573				    localtime(&time));
1574				state->us_first = B_FALSE;
1575				(void) printf("%s", buf);
1576			}
1577			bw = (double)usage->du_bandwidth/1000;
1578			(void) printf(" %.2f", bw);
1579			if (usage->du_last) {
1580				(void) printf("\n");
1581				state->us_first = B_TRUE;
1582			}
1583		}
1584		return (DLADM_STATUS_OK);
1585	}
1586
1587	bzero(&ubuf, sizeof (ubuf));
1588
1589	(void) snprintf(ubuf.usage_l_link, sizeof (ubuf.usage_l_link), "%s",
1590	    usage->du_name);
1591	time = usage->du_stime;
1592	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
1593	(void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s",
1594	    buf);
1595	time = usage->du_etime;
1596	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
1597	(void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s",
1598	    buf);
1599	(void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes),
1600	    "%llu", usage->du_rbytes);
1601	(void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes),
1602	    "%llu", usage->du_obytes);
1603	(void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth),
1604	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
1605
1606	ofmt_print(state->us_ofmt, &ubuf);
1607	return (DLADM_STATUS_OK);
1608}
1609
1610static int
1611show_usage_res(dladm_usage_t *usage, void *arg)
1612{
1613	show_usage_state_t	*state = (show_usage_state_t *)arg;
1614	char			buf[DLADM_STRSIZE];
1615	usage_fields_buf_t	ubuf;
1616	dladm_status_t		status;
1617	uint32_t		flags;
1618
1619	/*
1620	 * Only show usage information for existing links unless '-a'
1621	 * is specified.
1622	 */
1623	if (!state->us_showall) {
1624		if ((status = dladm_name2info(handle, usage->du_name,
1625		    NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
1626			return (status);
1627		}
1628		if ((flags & DLADM_OPT_ACTIVE) == 0)
1629			return (DLADM_STATUS_LINKINVAL);
1630	}
1631
1632	bzero(&ubuf, sizeof (ubuf));
1633
1634	(void) snprintf(ubuf.usage_link, sizeof (ubuf.usage_link), "%s",
1635	    usage->du_name);
1636	(void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration),
1637	    "%llu", usage->du_duration);
1638	(void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets),
1639	    "%llu", usage->du_ipackets);
1640	(void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes),
1641	    "%llu", usage->du_rbytes);
1642	(void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets),
1643	    "%llu", usage->du_opackets);
1644	(void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes),
1645	    "%llu", usage->du_obytes);
1646	(void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth),
1647	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
1648
1649	ofmt_print(state->us_ofmt, &ubuf);
1650
1651	return (DLADM_STATUS_OK);
1652}
1653
1654static boolean_t
1655valid_formatspec(char *formatspec_str)
1656{
1657	if (strcmp(formatspec_str, "gnuplot") == 0)
1658		return (B_TRUE);
1659	return (B_FALSE);
1660
1661}
1662
1663/*ARGSUSED*/
1664static void
1665do_show_usage(int argc, char *argv[], const char *use)
1666{
1667	char			*file = NULL;
1668	int			opt;
1669	dladm_status_t		status;
1670	boolean_t		d_arg = B_FALSE;
1671	char			*stime = NULL;
1672	char			*etime = NULL;
1673	char			*resource = NULL;
1674	show_usage_state_t	state;
1675	boolean_t		o_arg = B_FALSE;
1676	boolean_t		F_arg = B_FALSE;
1677	char			*fields_str = NULL;
1678	char			*formatspec_str = NULL;
1679	char			*all_l_fields =
1680	    "link,start,end,rbytes,obytes,bandwidth";
1681	ofmt_handle_t		ofmt;
1682	ofmt_status_t		oferr;
1683	uint_t			ofmtflags = 0;
1684
1685	bzero(&state, sizeof (show_usage_state_t));
1686	state.us_parsable = B_FALSE;
1687	state.us_printheader = B_FALSE;
1688	state.us_plot = B_FALSE;
1689	state.us_first = B_TRUE;
1690
1691	while ((opt = getopt_long(argc, argv, "das:e:o:f:F:",
1692	    usage_opts, NULL)) != -1) {
1693		switch (opt) {
1694		case 'd':
1695			d_arg = B_TRUE;
1696			break;
1697		case 'a':
1698			state.us_showall = B_TRUE;
1699			break;
1700		case 'f':
1701			file = optarg;
1702			break;
1703		case 's':
1704			stime = optarg;
1705			break;
1706		case 'e':
1707			etime = optarg;
1708			break;
1709		case 'o':
1710			o_arg = B_TRUE;
1711			fields_str = optarg;
1712			break;
1713		case 'F':
1714			state.us_plot = F_arg = B_TRUE;
1715			formatspec_str = optarg;
1716			break;
1717		default:
1718			die_opterr(optopt, opt, use);
1719			break;
1720		}
1721	}
1722
1723	if (file == NULL)
1724		die("show-usage requires a file");
1725
1726	if (optind == (argc-1)) {
1727		uint32_t	flags;
1728
1729		resource = argv[optind];
1730		if (!state.us_showall &&
1731		    (((status = dladm_name2info(handle, resource, NULL, &flags,
1732		    NULL, NULL)) != DLADM_STATUS_OK) ||
1733		    ((flags & DLADM_OPT_ACTIVE) == 0))) {
1734			die("invalid link: '%s'", resource);
1735		}
1736	}
1737
1738	if (F_arg && d_arg)
1739		die("incompatible -d and -F options");
1740
1741	if (F_arg && valid_formatspec(formatspec_str) == B_FALSE)
1742		die("Format specifier %s not supported", formatspec_str);
1743
1744	if (state.us_parsable)
1745		ofmtflags |= OFMT_PARSABLE;
1746
1747	if (resource == NULL && stime == NULL && etime == NULL) {
1748		oferr = ofmt_open(fields_str, usage_fields, ofmtflags, 0,
1749		    &ofmt);
1750	} else {
1751		if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
1752			fields_str = all_l_fields;
1753		oferr = ofmt_open(fields_str, usage_l_fields, ofmtflags, 0,
1754		    &ofmt);
1755
1756	}
1757	ofmt_check(oferr, state.us_parsable, ofmt, die, warn);
1758	state.us_ofmt = ofmt;
1759
1760	if (d_arg) {
1761		/* Print log dates */
1762		status = dladm_usage_dates(show_usage_date,
1763		    DLADM_LOGTYPE_LINK, file, resource, &state);
1764	} else if (resource == NULL && stime == NULL && etime == NULL &&
1765	    !F_arg) {
1766		/* Print summary */
1767		status = dladm_usage_summary(show_usage_res,
1768		    DLADM_LOGTYPE_LINK, file, &state);
1769	} else if (resource != NULL) {
1770		/* Print log entries for named resource */
1771		status = dladm_walk_usage_res(show_usage_time,
1772		    DLADM_LOGTYPE_LINK, file, resource, stime, etime, &state);
1773	} else {
1774		/* Print time and information for each link */
1775		status = dladm_walk_usage_time(show_usage_time,
1776		    DLADM_LOGTYPE_LINK, file, stime, etime, &state);
1777	}
1778
1779	if (status != DLADM_STATUS_OK)
1780		die_dlerr(status, "show-usage");
1781	ofmt_close(ofmt);
1782}
1783
1784static void
1785do_create_aggr(int argc, char *argv[], const char *use)
1786{
1787	int			option;
1788	int			key = 0;
1789	uint32_t		policy = AGGR_POLICY_L4;
1790	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
1791	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
1792	dladm_aggr_port_attr_db_t	port[MAXPORT];
1793	uint_t			n, ndev, nlink;
1794	uint8_t			mac_addr[ETHERADDRL];
1795	boolean_t		mac_addr_fixed = B_FALSE;
1796	boolean_t		P_arg = B_FALSE;
1797	boolean_t		l_arg = B_FALSE;
1798	boolean_t		u_arg = B_FALSE;
1799	boolean_t		T_arg = B_FALSE;
1800	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
1801	char			*altroot = NULL;
1802	char			name[MAXLINKNAMELEN];
1803	char			*devs[MAXPORT];
1804	char			*links[MAXPORT];
1805	dladm_status_t		status;
1806	dladm_status_t		pstatus;
1807	char			propstr[DLADM_STRSIZE];
1808	dladm_arg_list_t	*proplist = NULL;
1809	int			i;
1810	datalink_id_t		linkid;
1811
1812	ndev = nlink = opterr = 0;
1813	bzero(propstr, DLADM_STRSIZE);
1814
1815	while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:p:",
1816	    lopts, NULL)) != -1) {
1817		switch (option) {
1818		case 'd':
1819			if (ndev + nlink >= MAXPORT)
1820				die("too many ports specified");
1821
1822			devs[ndev++] = optarg;
1823			break;
1824		case 'P':
1825			if (P_arg)
1826				die_optdup(option);
1827
1828			P_arg = B_TRUE;
1829			if (!dladm_aggr_str2policy(optarg, &policy))
1830				die("invalid policy '%s'", optarg);
1831			break;
1832		case 'u':
1833			if (u_arg)
1834				die_optdup(option);
1835
1836			u_arg = B_TRUE;
1837			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
1838			    mac_addr))
1839				die("invalid MAC address '%s'", optarg);
1840			break;
1841		case 'l':
1842			if (isdigit(optarg[strlen(optarg) - 1])) {
1843
1844				/*
1845				 * Ended with digit, possibly a link name.
1846				 */
1847				if (ndev + nlink >= MAXPORT)
1848					die("too many ports specified");
1849
1850				links[nlink++] = optarg;
1851				break;
1852			}
1853			/* FALLTHROUGH */
1854		case 'L':
1855			if (l_arg)
1856				die_optdup(option);
1857
1858			l_arg = B_TRUE;
1859			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
1860				die("invalid LACP mode '%s'", optarg);
1861			break;
1862		case 'T':
1863			if (T_arg)
1864				die_optdup(option);
1865
1866			T_arg = B_TRUE;
1867			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
1868				die("invalid LACP timer value '%s'", optarg);
1869			break;
1870		case 't':
1871			flags &= ~DLADM_OPT_PERSIST;
1872			break;
1873		case 'f':
1874			flags |= DLADM_OPT_FORCE;
1875			break;
1876		case 'R':
1877			altroot = optarg;
1878			break;
1879		case 'p':
1880			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
1881			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
1882			    DLADM_STRSIZE)
1883				die("property list too long '%s'", propstr);
1884			break;
1885
1886		default:
1887			die_opterr(optopt, option, use);
1888			break;
1889		}
1890	}
1891
1892	if (ndev + nlink == 0)
1893		usage();
1894
1895	/* get key value or the aggregation name (required last argument) */
1896	if (optind != (argc-1))
1897		usage();
1898
1899	if (!str2int(argv[optind], &key)) {
1900		if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >=
1901		    MAXLINKNAMELEN) {
1902			die("link name too long '%s'", argv[optind]);
1903		}
1904
1905		if (!dladm_valid_linkname(name))
1906			die("invalid link name '%s'", argv[optind]);
1907	} else {
1908		(void) snprintf(name, MAXLINKNAMELEN, "aggr%d", key);
1909	}
1910
1911	if (altroot != NULL)
1912		altroot_cmd(altroot, argc, argv);
1913
1914	for (n = 0; n < ndev; n++) {
1915		if ((status = dladm_dev2linkid(handle, devs[n],
1916		    &port[n].lp_linkid)) != DLADM_STATUS_OK) {
1917			die_dlerr(status, "invalid dev name '%s'", devs[n]);
1918		}
1919	}
1920
1921	for (n = 0; n < nlink; n++) {
1922		if ((status = dladm_name2info(handle, links[n],
1923		    &port[ndev + n].lp_linkid, NULL, NULL, NULL)) !=
1924		    DLADM_STATUS_OK) {
1925			die_dlerr(status, "invalid link name '%s'", links[n]);
1926		}
1927	}
1928
1929	status = dladm_aggr_create(handle, name, key, ndev + nlink, port,
1930	    policy, mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode,
1931	    lacp_timer, flags);
1932	if (status != DLADM_STATUS_OK)
1933		goto done;
1934
1935	if (dladm_parse_link_props(propstr, &proplist, B_FALSE)
1936	    != DLADM_STATUS_OK)
1937		die("invalid aggregation property");
1938
1939	if (proplist == NULL)
1940		return;
1941
1942	status = dladm_name2info(handle, name, &linkid, NULL, NULL, NULL);
1943	if (status != DLADM_STATUS_OK)
1944		goto done;
1945
1946	for (i = 0; i < proplist->al_count; i++) {
1947		dladm_arg_info_t	*aip = &proplist->al_info[i];
1948
1949		pstatus = dladm_set_linkprop(handle, linkid, aip->ai_name,
1950		    aip->ai_val, aip->ai_count, flags);
1951
1952		if (pstatus != DLADM_STATUS_OK) {
1953			die_dlerr(pstatus,
1954			    "aggr creation succeeded but "
1955			    "could not set property '%s'", aip->ai_name);
1956		}
1957	}
1958done:
1959	dladm_free_props(proplist);
1960	if (status != DLADM_STATUS_OK) {
1961		if (status == DLADM_STATUS_NONOTIF) {
1962			die("not all links have link up/down detection; must "
1963			    "use -f (see dladm(1M))");
1964		} else {
1965			die_dlerr(status, "create operation failed");
1966		}
1967	}
1968}
1969
1970/*
1971 * arg is either the key or the aggr name. Validate it and convert it to
1972 * the linkid if altroot is NULL.
1973 */
1974static dladm_status_t
1975i_dladm_aggr_get_linkid(const char *altroot, const char *arg,
1976    datalink_id_t *linkidp, uint32_t flags)
1977{
1978	int		key = 0;
1979	char		*aggr = NULL;
1980	dladm_status_t	status;
1981
1982	if (!str2int(arg, &key))
1983		aggr = (char *)arg;
1984
1985	if (aggr == NULL && key == 0)
1986		return (DLADM_STATUS_LINKINVAL);
1987
1988	if (altroot != NULL)
1989		return (DLADM_STATUS_OK);
1990
1991	if (aggr != NULL) {
1992		status = dladm_name2info(handle, aggr, linkidp, NULL, NULL,
1993		    NULL);
1994	} else {
1995		status = dladm_key2linkid(handle, key, linkidp, flags);
1996	}
1997
1998	return (status);
1999}
2000
2001static void
2002do_delete_aggr(int argc, char *argv[], const char *use)
2003{
2004	int			option;
2005	char			*altroot = NULL;
2006	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
2007	dladm_status_t		status;
2008	datalink_id_t		linkid;
2009
2010	opterr = 0;
2011	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
2012		switch (option) {
2013		case 't':
2014			flags &= ~DLADM_OPT_PERSIST;
2015			break;
2016		case 'R':
2017			altroot = optarg;
2018			break;
2019		default:
2020			die_opterr(optopt, option, use);
2021			break;
2022		}
2023	}
2024
2025	/* get key value or the aggregation name (required last argument) */
2026	if (optind != (argc-1))
2027		usage();
2028
2029	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
2030	if (status != DLADM_STATUS_OK)
2031		goto done;
2032
2033	if (altroot != NULL)
2034		altroot_cmd(altroot, argc, argv);
2035
2036	status = dladm_aggr_delete(handle, linkid, flags);
2037done:
2038	if (status != DLADM_STATUS_OK)
2039		die_dlerr(status, "delete operation failed");
2040}
2041
2042static void
2043do_add_aggr(int argc, char *argv[], const char *use)
2044{
2045	int			option;
2046	uint_t			n, ndev, nlink;
2047	char			*altroot = NULL;
2048	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
2049	datalink_id_t		linkid;
2050	dladm_status_t		status;
2051	dladm_aggr_port_attr_db_t	port[MAXPORT];
2052	char			*devs[MAXPORT];
2053	char			*links[MAXPORT];
2054
2055	ndev = nlink = opterr = 0;
2056	while ((option = getopt_long(argc, argv, ":d:l:R:tf", lopts,
2057	    NULL)) != -1) {
2058		switch (option) {
2059		case 'd':
2060			if (ndev + nlink >= MAXPORT)
2061				die("too many ports specified");
2062
2063			devs[ndev++] = optarg;
2064			break;
2065		case 'l':
2066			if (ndev + nlink >= MAXPORT)
2067				die("too many ports specified");
2068
2069			links[nlink++] = optarg;
2070			break;
2071		case 't':
2072			flags &= ~DLADM_OPT_PERSIST;
2073			break;
2074		case 'f':
2075			flags |= DLADM_OPT_FORCE;
2076			break;
2077		case 'R':
2078			altroot = optarg;
2079			break;
2080		default:
2081			die_opterr(optopt, option, use);
2082			break;
2083		}
2084	}
2085
2086	if (ndev + nlink == 0)
2087		usage();
2088
2089	/* get key value or the aggregation name (required last argument) */
2090	if (optind != (argc-1))
2091		usage();
2092
2093	if ((status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid,
2094	    flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST))) !=
2095	    DLADM_STATUS_OK) {
2096		goto done;
2097	}
2098
2099	if (altroot != NULL)
2100		altroot_cmd(altroot, argc, argv);
2101
2102	for (n = 0; n < ndev; n++) {
2103		if ((status = dladm_dev2linkid(handle, devs[n],
2104		    &(port[n].lp_linkid))) != DLADM_STATUS_OK) {
2105			die_dlerr(status, "invalid <dev> '%s'", devs[n]);
2106		}
2107	}
2108
2109	for (n = 0; n < nlink; n++) {
2110		if ((status = dladm_name2info(handle, links[n],
2111		    &port[n + ndev].lp_linkid, NULL, NULL, NULL)) !=
2112		    DLADM_STATUS_OK) {
2113			die_dlerr(status, "invalid <link> '%s'", links[n]);
2114		}
2115	}
2116
2117	status = dladm_aggr_add(handle, linkid, ndev + nlink, port, flags);
2118done:
2119	if (status != DLADM_STATUS_OK) {
2120		/*
2121		 * checking DLADM_STATUS_NOTSUP is a temporary workaround
2122		 * and should be removed once 6399681 is fixed.
2123		 */
2124		if (status == DLADM_STATUS_NOTSUP) {
2125			die("add operation failed: link capabilities don't "
2126			    "match");
2127		} else if (status == DLADM_STATUS_NONOTIF) {
2128			die("not all links have link up/down detection; must "
2129			    "use -f (see dladm(1M))");
2130		} else {
2131			die_dlerr(status, "add operation failed");
2132		}
2133	}
2134}
2135
2136static void
2137do_remove_aggr(int argc, char *argv[], const char *use)
2138{
2139	int				option;
2140	dladm_aggr_port_attr_db_t	port[MAXPORT];
2141	uint_t				n, ndev, nlink;
2142	char				*devs[MAXPORT];
2143	char				*links[MAXPORT];
2144	char				*altroot = NULL;
2145	uint32_t			flags;
2146	datalink_id_t			linkid;
2147	dladm_status_t			status;
2148
2149	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
2150	ndev = nlink = opterr = 0;
2151	while ((option = getopt_long(argc, argv, ":d:l:R:t",
2152	    lopts, NULL)) != -1) {
2153		switch (option) {
2154		case 'd':
2155			if (ndev + nlink >= MAXPORT)
2156				die("too many ports specified");
2157
2158			devs[ndev++] = optarg;
2159			break;
2160		case 'l':
2161			if (ndev + nlink >= MAXPORT)
2162				die("too many ports specified");
2163
2164			links[nlink++] = optarg;
2165			break;
2166		case 't':
2167			flags &= ~DLADM_OPT_PERSIST;
2168			break;
2169		case 'R':
2170			altroot = optarg;
2171			break;
2172		default:
2173			die_opterr(optopt, option, use);
2174			break;
2175		}
2176	}
2177
2178	if (ndev + nlink == 0)
2179		usage();
2180
2181	/* get key value or the aggregation name (required last argument) */
2182	if (optind != (argc-1))
2183		usage();
2184
2185	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
2186	if (status != DLADM_STATUS_OK)
2187		goto done;
2188
2189	if (altroot != NULL)
2190		altroot_cmd(altroot, argc, argv);
2191
2192	for (n = 0; n < ndev; n++) {
2193		if ((status = dladm_dev2linkid(handle, devs[n],
2194		    &(port[n].lp_linkid))) != DLADM_STATUS_OK) {
2195			die_dlerr(status, "invalid <dev> '%s'", devs[n]);
2196		}
2197	}
2198
2199	for (n = 0; n < nlink; n++) {
2200		if ((status = dladm_name2info(handle, links[n],
2201		    &port[n + ndev].lp_linkid, NULL, NULL, NULL)) !=
2202		    DLADM_STATUS_OK) {
2203			die_dlerr(status, "invalid <link> '%s'", links[n]);
2204		}
2205	}
2206
2207	status = dladm_aggr_remove(handle, linkid, ndev + nlink, port, flags);
2208done:
2209	if (status != DLADM_STATUS_OK)
2210		die_dlerr(status, "remove operation failed");
2211}
2212
2213static void
2214do_modify_aggr(int argc, char *argv[], const char *use)
2215{
2216	int			option;
2217	uint32_t		policy = AGGR_POLICY_L4;
2218	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
2219	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
2220	uint8_t			mac_addr[ETHERADDRL];
2221	boolean_t		mac_addr_fixed = B_FALSE;
2222	uint8_t			modify_mask = 0;
2223	char			*altroot = NULL;
2224	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
2225	datalink_id_t		linkid;
2226	dladm_status_t		status;
2227
2228	opterr = 0;
2229	while ((option = getopt_long(argc, argv, ":L:l:P:R:tu:T:", lopts,
2230	    NULL)) != -1) {
2231		switch (option) {
2232		case 'P':
2233			if (modify_mask & DLADM_AGGR_MODIFY_POLICY)
2234				die_optdup(option);
2235
2236			modify_mask |= DLADM_AGGR_MODIFY_POLICY;
2237
2238			if (!dladm_aggr_str2policy(optarg, &policy))
2239				die("invalid policy '%s'", optarg);
2240			break;
2241		case 'u':
2242			if (modify_mask & DLADM_AGGR_MODIFY_MAC)
2243				die_optdup(option);
2244
2245			modify_mask |= DLADM_AGGR_MODIFY_MAC;
2246
2247			if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
2248			    mac_addr))
2249				die("invalid MAC address '%s'", optarg);
2250			break;
2251		case 'l':
2252		case 'L':
2253			if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE)
2254				die_optdup(option);
2255
2256			modify_mask |= DLADM_AGGR_MODIFY_LACP_MODE;
2257
2258			if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
2259				die("invalid LACP mode '%s'", optarg);
2260			break;
2261		case 'T':
2262			if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER)
2263				die_optdup(option);
2264
2265			modify_mask |= DLADM_AGGR_MODIFY_LACP_TIMER;
2266
2267			if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
2268				die("invalid LACP timer value '%s'", optarg);
2269			break;
2270		case 't':
2271			flags &= ~DLADM_OPT_PERSIST;
2272			break;
2273		case 'R':
2274			altroot = optarg;
2275			break;
2276		default:
2277			die_opterr(optopt, option, use);
2278			break;
2279		}
2280	}
2281
2282	if (modify_mask == 0)
2283		die("at least one of the -PulT options must be specified");
2284
2285	/* get key value or the aggregation name (required last argument) */
2286	if (optind != (argc-1))
2287		usage();
2288
2289	status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
2290	if (status != DLADM_STATUS_OK)
2291		goto done;
2292
2293	if (altroot != NULL)
2294		altroot_cmd(altroot, argc, argv);
2295
2296	status = dladm_aggr_modify(handle, linkid, modify_mask, policy,
2297	    mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode, lacp_timer,
2298	    flags);
2299
2300done:
2301	if (status != DLADM_STATUS_OK)
2302		die_dlerr(status, "modify operation failed");
2303}
2304
2305/*ARGSUSED*/
2306static void
2307do_up_aggr(int argc, char *argv[], const char *use)
2308{
2309	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2310	dladm_status_t	status;
2311
2312	/*
2313	 * get the key or the name of the aggregation (optional last argument)
2314	 */
2315	if (argc == 2) {
2316		if ((status = i_dladm_aggr_get_linkid(NULL, argv[1], &linkid,
2317		    DLADM_OPT_PERSIST)) != DLADM_STATUS_OK)
2318			goto done;
2319	} else if (argc > 2) {
2320		usage();
2321	}
2322
2323	status = dladm_aggr_up(handle, linkid);
2324done:
2325	if (status != DLADM_STATUS_OK) {
2326		if (argc == 2) {
2327			die_dlerr(status,
2328			    "could not bring up aggregation '%s'", argv[1]);
2329		} else {
2330			die_dlerr(status, "could not bring aggregations up");
2331		}
2332	}
2333}
2334
2335static void
2336do_create_vlan(int argc, char *argv[], const char *use)
2337{
2338	char			*link = NULL;
2339	char			drv[DLPI_LINKNAME_MAX];
2340	uint_t			ppa;
2341	datalink_id_t		linkid;
2342	datalink_id_t		dev_linkid;
2343	int			vid = 0;
2344	int			option;
2345	uint32_t		flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
2346	char			*altroot = NULL;
2347	char			vlan[MAXLINKNAMELEN];
2348	char			propstr[DLADM_STRSIZE];
2349	dladm_arg_list_t	*proplist = NULL;
2350	dladm_status_t		status;
2351
2352	opterr = 0;
2353	bzero(propstr, DLADM_STRSIZE);
2354
2355	while ((option = getopt_long(argc, argv, ":tfR:l:v:p:",
2356	    lopts, NULL)) != -1) {
2357		switch (option) {
2358		case 'v':
2359			if (vid != 0)
2360				die_optdup(option);
2361
2362			if (!str2int(optarg, &vid) || vid < 1 || vid > 4094)
2363				die("invalid VLAN identifier '%s'", optarg);
2364
2365			break;
2366		case 'l':
2367			if (link != NULL)
2368				die_optdup(option);
2369
2370			link = optarg;
2371			break;
2372		case 't':
2373			flags &= ~DLADM_OPT_PERSIST;
2374			break;
2375		case 'R':
2376			altroot = optarg;
2377			break;
2378		case 'p':
2379			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
2380			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
2381			    DLADM_STRSIZE)
2382				die("property list too long '%s'", propstr);
2383			break;
2384		case 'f':
2385			flags |= DLADM_OPT_FORCE;
2386			break;
2387		default:
2388			die_opterr(optopt, option, use);
2389			break;
2390		}
2391	}
2392
2393	/* get vlan name if there is any */
2394	if ((vid == 0) || (link == NULL) || (argc - optind > 1))
2395		usage();
2396
2397	if (optind == (argc - 1)) {
2398		if (strlcpy(vlan, argv[optind], MAXLINKNAMELEN) >=
2399		    MAXLINKNAMELEN) {
2400			die("vlan name too long '%s'", argv[optind]);
2401		}
2402	} else {
2403		if ((dlpi_parselink(link, drv, &ppa) != DLPI_SUCCESS) ||
2404		    (ppa >= 1000) ||
2405		    (dlpi_makelink(vlan, drv, vid * 1000 + ppa) !=
2406		    DLPI_SUCCESS)) {
2407			die("invalid link name '%s'", link);
2408		}
2409	}
2410
2411	if (altroot != NULL)
2412		altroot_cmd(altroot, argc, argv);
2413
2414	if (dladm_name2info(handle, link, &dev_linkid, NULL, NULL, NULL) !=
2415	    DLADM_STATUS_OK) {
2416		die("invalid link name '%s'", link);
2417	}
2418
2419	if (dladm_parse_link_props(propstr, &proplist, B_FALSE)
2420	    != DLADM_STATUS_OK)
2421		die("invalid vlan property");
2422
2423	status = dladm_vlan_create(handle, vlan, dev_linkid, vid, proplist,
2424	    flags, &linkid);
2425	switch (status) {
2426	case DLADM_STATUS_OK:
2427		break;
2428
2429	case DLADM_STATUS_NOTSUP:
2430		die("VLAN over '%s' may require lowered MTU; must use -f (see "
2431		    "dladm(1M))", link);
2432		break;
2433
2434	case DLADM_STATUS_LINKBUSY:
2435		die("VLAN over '%s' may not use default_tag ID "
2436		    "(see dladm(1M))", link);
2437		break;
2438
2439	default:
2440		die_dlerr(status, "create operation failed");
2441	}
2442}
2443
2444static void
2445do_delete_vlan(int argc, char *argv[], const char *use)
2446{
2447	int		option;
2448	uint32_t	flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
2449	char		*altroot = NULL;
2450	datalink_id_t	linkid;
2451	dladm_status_t	status;
2452
2453	opterr = 0;
2454	while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
2455		switch (option) {
2456		case 't':
2457			flags &= ~DLADM_OPT_PERSIST;
2458			break;
2459		case 'R':
2460			altroot = optarg;
2461			break;
2462		default:
2463			die_opterr(optopt, option, use);
2464			break;
2465		}
2466	}
2467
2468	/* get VLAN link name (required last argument) */
2469	if (optind != (argc - 1))
2470		usage();
2471
2472	if (altroot != NULL)
2473		altroot_cmd(altroot, argc, argv);
2474
2475	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
2476	    NULL);
2477	if (status != DLADM_STATUS_OK)
2478		goto done;
2479
2480	status = dladm_vlan_delete(handle, linkid, flags);
2481done:
2482	if (status != DLADM_STATUS_OK)
2483		die_dlerr(status, "delete operation failed");
2484}
2485
2486/*ARGSUSED*/
2487static void
2488do_up_vlan(int argc, char *argv[], const char *use)
2489{
2490	do_up_vnic_common(argc, argv, use, B_TRUE);
2491}
2492
2493static void
2494do_rename_link(int argc, char *argv[], const char *use)
2495{
2496	int		option;
2497	char		*link1, *link2;
2498	char		*altroot = NULL;
2499	dladm_status_t	status;
2500
2501	opterr = 0;
2502	while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) {
2503		switch (option) {
2504		case 'R':
2505			altroot = optarg;
2506			break;
2507		default:
2508			die_opterr(optopt, option, use);
2509			break;
2510		}
2511	}
2512
2513	/* get link1 and link2 name (required the last 2 arguments) */
2514	if (optind != (argc - 2))
2515		usage();
2516
2517	if (altroot != NULL)
2518		altroot_cmd(altroot, argc, argv);
2519
2520	link1 = argv[optind++];
2521	link2 = argv[optind];
2522	if ((status = dladm_rename_link(handle, link1, link2)) !=
2523	    DLADM_STATUS_OK)
2524		die_dlerr(status, "rename operation failed");
2525}
2526
2527/*ARGSUSED*/
2528static void
2529do_delete_phys(int argc, char *argv[], const char *use)
2530{
2531	datalink_id_t	linkid = DATALINK_ALL_LINKID;
2532	dladm_status_t	status;
2533
2534	/* get link name (required the last argument) */
2535	if (argc > 2)
2536		usage();
2537
2538	if (argc == 2) {
2539		if ((status = dladm_name2info(handle, argv[1], &linkid, NULL,
2540		    NULL, NULL)) != DLADM_STATUS_OK)
2541			die_dlerr(status, "cannot delete '%s'", argv[1]);
2542	}
2543
2544	if ((status = dladm_phys_delete(handle, linkid)) != DLADM_STATUS_OK) {
2545		if (argc == 2)
2546			die_dlerr(status, "cannot delete '%s'", argv[1]);
2547		else
2548			die_dlerr(status, "delete operation failed");
2549	}
2550}
2551
2552/*ARGSUSED*/
2553static int
2554i_dladm_walk_linkmap(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2555{
2556	char			name[MAXLINKNAMELEN];
2557	char			mediabuf[DLADM_STRSIZE];
2558	char			classbuf[DLADM_STRSIZE];
2559	datalink_class_t	class;
2560	uint32_t		media;
2561	uint32_t		flags;
2562
2563	if (dladm_datalink_id2info(dh, linkid, &flags, &class, &media, name,
2564	    MAXLINKNAMELEN) == DLADM_STATUS_OK) {
2565		(void) dladm_class2str(class, classbuf);
2566		(void) dladm_media2str(media, mediabuf);
2567		(void) printf("%-12s%8d  %-12s%-20s %6d\n", name,
2568		    linkid, classbuf, mediabuf, flags);
2569	}
2570	return (DLADM_WALK_CONTINUE);
2571}
2572
2573/*ARGSUSED*/
2574static void
2575do_show_linkmap(int argc, char *argv[], const char *use)
2576{
2577	if (argc != 1)
2578		die("invalid arguments");
2579
2580	(void) printf("%-12s%8s  %-12s%-20s %6s\n", "NAME", "LINKID",
2581	    "CLASS", "MEDIA", "FLAGS");
2582
2583	(void) dladm_walk_datalink_id(i_dladm_walk_linkmap, handle, NULL,
2584	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
2585	    DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
2586}
2587
2588/*
2589 * Delete inactive physical links.
2590 */
2591/*ARGSUSED*/
2592static int
2593purge_phys(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2594{
2595	datalink_class_t	class;
2596	uint32_t		flags;
2597
2598	if (dladm_datalink_id2info(dh, linkid, &flags, &class, NULL, NULL, 0)
2599	    != DLADM_STATUS_OK) {
2600		return (DLADM_WALK_CONTINUE);
2601	}
2602
2603	if (class == DATALINK_CLASS_PHYS && !(flags & DLADM_OPT_ACTIVE))
2604		(void) dladm_phys_delete(dh, linkid);
2605
2606	return (DLADM_WALK_CONTINUE);
2607}
2608
2609/*ARGSUSED*/
2610static void
2611do_init_phys(int argc, char *argv[], const char *use)
2612{
2613	di_node_t	devtree;
2614
2615	if (argc > 1)
2616		usage();
2617
2618	/*
2619	 * Force all the devices to attach, therefore all the network physical
2620	 * devices can be known to the dlmgmtd daemon.
2621	 */
2622	if ((devtree = di_init("/", DINFOFORCE | DINFOSUBTREE)) != DI_NODE_NIL)
2623		di_fini(devtree);
2624
2625	(void) dladm_walk_datalink_id(purge_phys, handle, NULL,
2626	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
2627}
2628
2629/*
2630 * Print the active topology information.
2631 */
2632void
2633print_link_topology(show_state_t *state, datalink_id_t linkid,
2634    datalink_class_t class, link_fields_buf_t *lbuf)
2635{
2636	uint32_t	flags = state->ls_flags;
2637	dladm_status_t	status;
2638	char		tmpbuf[MAXLINKNAMELEN];
2639
2640	lbuf->link_over[0] = '\0';
2641	lbuf->link_bridge[0] = '\0';
2642
2643	switch (class) {
2644	case DATALINK_CLASS_AGGR:
2645	case DATALINK_CLASS_PHYS:
2646	case DATALINK_CLASS_ETHERSTUB:
2647		status = dladm_bridge_getlink(handle, linkid, lbuf->link_bridge,
2648		    sizeof (lbuf->link_bridge));
2649		if (status != DLADM_STATUS_OK &&
2650		    status != DLADM_STATUS_NOTFOUND)
2651			(void) strcpy(lbuf->link_bridge, "?");
2652		break;
2653	}
2654
2655	switch (class) {
2656	case DATALINK_CLASS_VLAN: {
2657		dladm_vlan_attr_t	vinfo;
2658
2659		if (dladm_vlan_info(handle, linkid, &vinfo, flags) !=
2660		    DLADM_STATUS_OK) {
2661			(void) strcpy(lbuf->link_over, "?");
2662			break;
2663		}
2664		if (dladm_datalink_id2info(handle, vinfo.dv_linkid, NULL, NULL,
2665		    NULL, lbuf->link_over, sizeof (lbuf->link_over)) !=
2666		    DLADM_STATUS_OK)
2667			(void) strcpy(lbuf->link_over, "?");
2668		break;
2669	}
2670	case DATALINK_CLASS_AGGR: {
2671		dladm_aggr_grp_attr_t	ginfo;
2672		int			i;
2673
2674		if (dladm_aggr_info(handle, linkid, &ginfo, flags) !=
2675		    DLADM_STATUS_OK || ginfo.lg_nports == 0) {
2676			(void) strcpy(lbuf->link_over, "?");
2677			break;
2678		}
2679		for (i = 0; i < ginfo.lg_nports; i++) {
2680			if (dladm_datalink_id2info(handle,
2681			    ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL,
2682			    tmpbuf, sizeof (tmpbuf)) != DLADM_STATUS_OK) {
2683				(void) strcpy(lbuf->link_over, "?");
2684				break;
2685			}
2686			(void) strlcat(lbuf->link_over, tmpbuf,
2687			    sizeof (lbuf->link_over));
2688			if (i != (ginfo.lg_nports - 1)) {
2689				(void) strlcat(lbuf->link_over, ",",
2690				    sizeof (lbuf->link_over));
2691			}
2692		}
2693		free(ginfo.lg_ports);
2694		break;
2695	}
2696	case DATALINK_CLASS_VNIC: {
2697		dladm_vnic_attr_t	vinfo;
2698
2699		if (dladm_vnic_info(handle, linkid, &vinfo, flags) !=
2700		    DLADM_STATUS_OK) {
2701			(void) strcpy(lbuf->link_over, "?");
2702			break;
2703		}
2704		if (dladm_datalink_id2info(handle, vinfo.va_link_id, NULL, NULL,
2705		    NULL, lbuf->link_over, sizeof (lbuf->link_over)) !=
2706		    DLADM_STATUS_OK)
2707			(void) strcpy(lbuf->link_over, "?");
2708		break;
2709	}
2710
2711	case DATALINK_CLASS_PART: {
2712		dladm_part_attr_t	pinfo;
2713
2714		if (dladm_part_info(handle, linkid, &pinfo, flags) !=
2715		    DLADM_STATUS_OK) {
2716			(void) strcpy(lbuf->link_over, "?");
2717			break;
2718		}
2719		if (dladm_datalink_id2info(handle, pinfo.dia_physlinkid, NULL,
2720		    NULL, NULL, lbuf->link_over, sizeof (lbuf->link_over)) !=
2721		    DLADM_STATUS_OK)
2722			(void) strcpy(lbuf->link_over, "?");
2723		break;
2724	}
2725
2726	case DATALINK_CLASS_BRIDGE: {
2727		datalink_id_t *dlp;
2728		uint_t i, nports;
2729
2730		if (dladm_datalink_id2info(handle, linkid, NULL, NULL,
2731		    NULL, tmpbuf, sizeof (tmpbuf)) != DLADM_STATUS_OK) {
2732			(void) strcpy(lbuf->link_over, "?");
2733			break;
2734		}
2735		if (tmpbuf[0] != '\0')
2736			tmpbuf[strlen(tmpbuf) - 1] = '\0';
2737		dlp = dladm_bridge_get_portlist(tmpbuf, &nports);
2738		if (dlp == NULL) {
2739			(void) strcpy(lbuf->link_over, "?");
2740			break;
2741		}
2742		for (i = 0; i < nports; i++) {
2743			if (dladm_datalink_id2info(handle, dlp[i], NULL,
2744			    NULL, NULL, tmpbuf, sizeof (tmpbuf)) !=
2745			    DLADM_STATUS_OK) {
2746				(void) strcpy(lbuf->link_over, "?");
2747				break;
2748			}
2749			(void) strlcat(lbuf->link_over, tmpbuf,
2750			    sizeof (lbuf->link_over));
2751			if (i != nports - 1) {
2752				(void) strlcat(lbuf->link_over, ",",
2753				    sizeof (lbuf->link_over));
2754			}
2755		}
2756		dladm_bridge_free_portlist(dlp);
2757		break;
2758	}
2759
2760	case DATALINK_CLASS_SIMNET: {
2761		dladm_simnet_attr_t	slinfo;
2762
2763		if (dladm_simnet_info(handle, linkid, &slinfo, flags) !=
2764		    DLADM_STATUS_OK) {
2765			(void) strcpy(lbuf->link_over, "?");
2766			break;
2767		}
2768		if (slinfo.sna_peer_link_id != DATALINK_INVALID_LINKID) {
2769			if (dladm_datalink_id2info(handle,
2770			    slinfo.sna_peer_link_id, NULL, NULL, NULL,
2771			    lbuf->link_over, sizeof (lbuf->link_over)) !=
2772			    DLADM_STATUS_OK)
2773				(void) strcpy(lbuf->link_over, "?");
2774		}
2775		break;
2776	}
2777	}
2778}
2779
2780static dladm_status_t
2781print_link(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *lbuf)
2782{
2783	char			link[MAXLINKNAMELEN];
2784	datalink_class_t	class;
2785	uint_t			mtu;
2786	uint32_t		flags;
2787	dladm_status_t		status;
2788
2789	if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
2790	    NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
2791		goto done;
2792	}
2793
2794	if (!(state->ls_flags & flags)) {
2795		status = DLADM_STATUS_NOTFOUND;
2796		goto done;
2797	}
2798
2799	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2800		dladm_attr_t	dlattr;
2801
2802		if (class == DATALINK_CLASS_PHYS) {
2803			dladm_phys_attr_t	dpa;
2804			dlpi_handle_t		dh;
2805			dlpi_info_t		dlinfo;
2806
2807			if ((status = dladm_phys_info(handle, linkid, &dpa,
2808			    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
2809				goto done;
2810			}
2811
2812			if (!dpa.dp_novanity)
2813				goto link_mtu;
2814
2815			/*
2816			 * This is a physical link that does not have
2817			 * vanity naming support.
2818			 */
2819			if (dlpi_open(dpa.dp_dev, &dh, DLPI_DEVONLY) !=
2820			    DLPI_SUCCESS) {
2821				status = DLADM_STATUS_NOTFOUND;
2822				goto done;
2823			}
2824
2825			if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
2826				dlpi_close(dh);
2827				status = DLADM_STATUS_BADARG;
2828				goto done;
2829			}
2830
2831			dlpi_close(dh);
2832			mtu = dlinfo.di_max_sdu;
2833		} else {
2834link_mtu:
2835			status = dladm_info(handle, linkid, &dlattr);
2836			if (status != DLADM_STATUS_OK)
2837				goto done;
2838			mtu = dlattr.da_max_sdu;
2839		}
2840	}
2841
2842	(void) snprintf(lbuf->link_name, sizeof (lbuf->link_name),
2843	    "%s", link);
2844	(void) dladm_class2str(class, lbuf->link_class);
2845	if (state->ls_flags == DLADM_OPT_ACTIVE) {
2846		(void) snprintf(lbuf->link_mtu, sizeof (lbuf->link_mtu),
2847		    "%u", mtu);
2848		(void) get_linkstate(link, B_TRUE, lbuf->link_state);
2849	}
2850
2851	print_link_topology(state, linkid, class, lbuf);
2852done:
2853	return (status);
2854}
2855
2856/* ARGSUSED */
2857static int
2858show_link(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2859{
2860	show_state_t		*state = (show_state_t *)arg;
2861	dladm_status_t		status;
2862	link_fields_buf_t	lbuf;
2863
2864	/*
2865	 * first get all the link attributes into lbuf;
2866	 */
2867	bzero(&lbuf, sizeof (link_fields_buf_t));
2868	if ((status = print_link(state, linkid, &lbuf)) == DLADM_STATUS_OK)
2869		ofmt_print(state->ls_ofmt, &lbuf);
2870	state->ls_status = status;
2871	return (DLADM_WALK_CONTINUE);
2872}
2873
2874static boolean_t
2875print_link_stats_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
2876{
2877	link_args_t *largs = ofarg->ofmt_cbarg;
2878	pktsum_t *diff_stats = largs->link_s_psum;
2879
2880	switch (ofarg->ofmt_id) {
2881	case LINK_S_LINK:
2882		(void) snprintf(buf, bufsize, "%s", largs->link_s_link);
2883		break;
2884	case LINK_S_IPKTS:
2885		(void) snprintf(buf, bufsize, "%llu", diff_stats->ipackets);
2886		break;
2887	case LINK_S_RBYTES:
2888		(void) snprintf(buf, bufsize, "%llu", diff_stats->rbytes);
2889		break;
2890	case LINK_S_IERRORS:
2891		(void) snprintf(buf, bufsize, "%u", diff_stats->ierrors);
2892		break;
2893	case LINK_S_OPKTS:
2894		(void) snprintf(buf, bufsize, "%llu", diff_stats->opackets);
2895		break;
2896	case LINK_S_OBYTES:
2897		(void) snprintf(buf, bufsize, "%llu", diff_stats->obytes);
2898		break;
2899	case LINK_S_OERRORS:
2900		(void) snprintf(buf, bufsize, "%u", diff_stats->oerrors);
2901		break;
2902	default:
2903		die("invalid input");
2904		break;
2905	}
2906	return (B_TRUE);
2907}
2908
2909static int
2910show_link_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
2911{
2912	char			link[DLPI_LINKNAME_MAX];
2913	datalink_class_t	class;
2914	show_state_t		*state = arg;
2915	pktsum_t		stats, diff_stats;
2916	dladm_phys_attr_t	dpa;
2917	link_args_t		largs;
2918
2919	if (state->ls_firstonly) {
2920		if (state->ls_donefirst)
2921			return (DLADM_WALK_CONTINUE);
2922		state->ls_donefirst = B_TRUE;
2923	} else {
2924		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
2925	}
2926
2927	if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, link,
2928	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2929		return (DLADM_WALK_CONTINUE);
2930	}
2931
2932	if (class == DATALINK_CLASS_PHYS) {
2933		if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
2934		    DLADM_STATUS_OK) {
2935			return (DLADM_WALK_CONTINUE);
2936		}
2937		if (dpa.dp_novanity)
2938			get_mac_stats(dpa.dp_dev, &stats);
2939		else
2940			get_link_stats(link, &stats);
2941	} else {
2942		get_link_stats(link, &stats);
2943	}
2944	dladm_stats_diff(&diff_stats, &stats, &state->ls_prevstats);
2945
2946	largs.link_s_link = link;
2947	largs.link_s_psum = &diff_stats;
2948	ofmt_print(state->ls_ofmt, &largs);
2949
2950	state->ls_prevstats = stats;
2951	return (DLADM_WALK_CONTINUE);
2952}
2953
2954
2955static dladm_status_t
2956print_aggr_info(show_grp_state_t *state, const char *link,
2957    dladm_aggr_grp_attr_t *ginfop)
2958{
2959	char			addr_str[ETHERADDRL * 3];
2960	laggr_fields_buf_t	lbuf;
2961
2962	(void) snprintf(lbuf.laggr_name, sizeof (lbuf.laggr_name),
2963	    "%s", link);
2964
2965	(void) dladm_aggr_policy2str(ginfop->lg_policy,
2966	    lbuf.laggr_policy);
2967
2968	if (ginfop->lg_mac_fixed) {
2969		(void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str);
2970		(void) snprintf(lbuf.laggr_addrpolicy,
2971		    sizeof (lbuf.laggr_addrpolicy), "fixed (%s)", addr_str);
2972	} else {
2973		(void) snprintf(lbuf.laggr_addrpolicy,
2974		    sizeof (lbuf.laggr_addrpolicy), "auto");
2975	}
2976
2977	(void) dladm_aggr_lacpmode2str(ginfop->lg_lacp_mode,
2978	    lbuf.laggr_lacpactivity);
2979	(void) dladm_aggr_lacptimer2str(ginfop->lg_lacp_timer,
2980	    lbuf.laggr_lacptimer);
2981	(void) snprintf(lbuf.laggr_flags, sizeof (lbuf.laggr_flags), "%c----",
2982	    ginfop->lg_force ? 'f' : '-');
2983
2984	ofmt_print(state->gs_ofmt, &lbuf);
2985
2986	return (DLADM_STATUS_OK);
2987}
2988
2989static boolean_t
2990print_xaggr_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
2991{
2992	const laggr_args_t	*l = ofarg->ofmt_cbarg;
2993	boolean_t		is_port = (l->laggr_lport >= 0);
2994	char			tmpbuf[DLADM_STRSIZE];
2995	const char		*objname;
2996	dladm_aggr_port_attr_t	*portp;
2997	dladm_phys_attr_t	dpa;
2998
2999	if (is_port) {
3000		portp = &(l->laggr_ginfop->lg_ports[l->laggr_lport]);
3001		if (dladm_phys_info(handle, portp->lp_linkid, &dpa,
3002		    DLADM_OPT_ACTIVE) != DLADM_STATUS_OK)
3003			objname = "?";
3004		else
3005			objname = dpa.dp_dev;
3006	} else {
3007		objname = l->laggr_link;
3008	}
3009
3010	switch (ofarg->ofmt_id) {
3011	case AGGR_X_LINK:
3012		(void) snprintf(buf, bufsize, "%s",
3013		    (is_port && !l->laggr_parsable ? " " : l->laggr_link));
3014		break;
3015	case AGGR_X_PORT:
3016		if (is_port) {
3017			if (dladm_datalink_id2info(handle, portp->lp_linkid,
3018			    NULL, NULL, NULL, buf, bufsize) != DLADM_STATUS_OK)
3019				(void) sprintf(buf, "?");
3020		}
3021		break;
3022
3023	case AGGR_X_SPEED:
3024		(void) snprintf(buf, bufsize, "%uMb",
3025		    (uint_t)((get_ifspeed(objname, !is_port)) / 1000000ull));
3026		break;
3027
3028	case AGGR_X_DUPLEX:
3029		(void) get_linkduplex(objname, !is_port, tmpbuf);
3030		(void) strlcpy(buf, tmpbuf, bufsize);
3031		break;
3032
3033	case AGGR_X_STATE:
3034		(void) get_linkstate(objname, !is_port, tmpbuf);
3035		(void) strlcpy(buf, tmpbuf, bufsize);
3036		break;
3037	case AGGR_X_ADDRESS:
3038		(void) dladm_aggr_macaddr2str(
3039		    (is_port ? portp->lp_mac : l->laggr_ginfop->lg_mac),
3040		    tmpbuf);
3041		(void) strlcpy(buf, tmpbuf, bufsize);
3042		break;
3043	case AGGR_X_PORTSTATE:
3044		if (is_port) {
3045			(void) dladm_aggr_portstate2str(portp->lp_state,
3046			    tmpbuf);
3047			(void) strlcpy(buf, tmpbuf, bufsize);
3048		}
3049		break;
3050	}
3051err:
3052	*(l->laggr_status) = DLADM_STATUS_OK;
3053	return (B_TRUE);
3054}
3055
3056static dladm_status_t
3057print_aggr_extended(show_grp_state_t *state, const char *link,
3058    dladm_aggr_grp_attr_t *ginfop)
3059{
3060	int			i;
3061	dladm_status_t		status;
3062	laggr_args_t		largs;
3063
3064	largs.laggr_lport = -1;
3065	largs.laggr_link = link;
3066	largs.laggr_ginfop = ginfop;
3067	largs.laggr_status = &status;
3068	largs.laggr_parsable = state->gs_parsable;
3069
3070	ofmt_print(state->gs_ofmt, &largs);
3071
3072	if (status != DLADM_STATUS_OK)
3073		goto done;
3074
3075	for (i = 0; i < ginfop->lg_nports; i++) {
3076		largs.laggr_lport = i;
3077		ofmt_print(state->gs_ofmt, &largs);
3078		if (status != DLADM_STATUS_OK)
3079			goto done;
3080	}
3081
3082	status = DLADM_STATUS_OK;
3083done:
3084	return (status);
3085}
3086
3087static boolean_t
3088print_lacp_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
3089{
3090	const laggr_args_t	*l = ofarg->ofmt_cbarg;
3091	int			portnum;
3092	boolean_t		is_port = (l->laggr_lport >= 0);
3093	dladm_aggr_port_attr_t	*portp;
3094	aggr_lacp_state_t	*lstate;
3095
3096	if (!is_port)
3097		return (B_FALSE); /* cannot happen! */
3098
3099	portnum = l->laggr_lport;
3100	portp = &(l->laggr_ginfop->lg_ports[portnum]);
3101	lstate = &(portp->lp_lacp_state);
3102
3103	switch (ofarg->ofmt_id) {
3104	case AGGR_L_LINK:
3105		(void) snprintf(buf, bufsize, "%s",
3106		    (portnum > 0 ? "" : l->laggr_link));
3107		break;
3108
3109	case AGGR_L_PORT:
3110		if (dladm_datalink_id2info(handle, portp->lp_linkid, NULL, NULL,
3111		    NULL, buf, bufsize) != DLADM_STATUS_OK)
3112			(void) sprintf(buf, "?");
3113		break;
3114
3115	case AGGR_L_AGGREGATABLE:
3116		(void) snprintf(buf, bufsize, "%s",
3117		    (lstate->bit.aggregation ? "yes" : "no"));
3118		break;
3119
3120	case AGGR_L_SYNC:
3121		(void) snprintf(buf, bufsize, "%s",
3122		    (lstate->bit.sync ? "yes" : "no"));
3123		break;
3124
3125	case AGGR_L_COLL:
3126		(void) snprintf(buf, bufsize, "%s",
3127		    (lstate->bit.collecting ? "yes" : "no"));
3128		break;
3129
3130	case AGGR_L_DIST:
3131		(void) snprintf(buf, bufsize, "%s",
3132		    (lstate->bit.distributing ? "yes" : "no"));
3133		break;
3134
3135	case AGGR_L_DEFAULTED:
3136		(void) snprintf(buf, bufsize, "%s",
3137		    (lstate->bit.defaulted ? "yes" : "no"));
3138		break;
3139
3140	case AGGR_L_EXPIRED:
3141		(void) snprintf(buf, bufsize, "%s",
3142		    (lstate->bit.expired ? "yes" : "no"));
3143		break;
3144	}
3145
3146	*(l->laggr_status) = DLADM_STATUS_OK;
3147	return (B_TRUE);
3148}
3149
3150static dladm_status_t
3151print_aggr_lacp(show_grp_state_t *state, const char *link,
3152    dladm_aggr_grp_attr_t *ginfop)
3153{
3154	int		i;
3155	dladm_status_t	status;
3156	laggr_args_t	largs;
3157
3158	largs.laggr_link = link;
3159	largs.laggr_ginfop = ginfop;
3160	largs.laggr_status = &status;
3161
3162	for (i = 0; i < ginfop->lg_nports; i++) {
3163		largs.laggr_lport = i;
3164		ofmt_print(state->gs_ofmt, &largs);
3165		if (status != DLADM_STATUS_OK)
3166			goto done;
3167	}
3168
3169	status = DLADM_STATUS_OK;
3170done:
3171	return (status);
3172}
3173
3174static boolean_t
3175print_aggr_stats_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
3176{
3177	const laggr_args_t	*l = ofarg->ofmt_cbarg;
3178	int			portnum;
3179	boolean_t		is_port = (l->laggr_lport >= 0);
3180	dladm_aggr_port_attr_t	*portp;
3181	dladm_status_t		*stat, status;
3182	pktsum_t		*diff_stats;
3183
3184	stat = l->laggr_status;
3185	*stat = DLADM_STATUS_OK;
3186
3187	if (is_port) {
3188		portnum = l->laggr_lport;
3189		portp = &(l->laggr_ginfop->lg_ports[portnum]);
3190
3191		if ((status = dladm_datalink_id2info(handle,
3192		    portp->lp_linkid, NULL, NULL, NULL, buf, bufsize)) !=
3193		    DLADM_STATUS_OK) {
3194			goto err;
3195		}
3196		diff_stats = l->laggr_diffstats;
3197	}
3198
3199	switch (ofarg->ofmt_id) {
3200	case AGGR_S_LINK:
3201		(void) snprintf(buf, bufsize, "%s",
3202		    (is_port ? "" : l->laggr_link));
3203		break;
3204	case AGGR_S_PORT:
3205		/*
3206		 * if (is_port), buf has port name. Otherwise we print
3207		 * STR_UNDEF_VAL
3208		 */
3209		break;
3210
3211	case AGGR_S_IPKTS:
3212		if (is_port) {
3213			(void) snprintf(buf, bufsize, "%llu",
3214			    diff_stats->ipackets);
3215		} else {
3216			(void) snprintf(buf, bufsize, "%llu",
3217			    l->laggr_pktsumtot->ipackets);
3218		}
3219		break;
3220
3221	case AGGR_S_RBYTES:
3222		if (is_port) {
3223			(void) snprintf(buf, bufsize, "%llu",
3224			    diff_stats->rbytes);
3225		} else {
3226			(void) snprintf(buf, bufsize, "%llu",
3227			    l->laggr_pktsumtot->rbytes);
3228		}
3229		break;
3230
3231	case AGGR_S_OPKTS:
3232		if (is_port) {
3233			(void) snprintf(buf, bufsize, "%llu",
3234			    diff_stats->opackets);
3235		} else {
3236			(void) snprintf(buf, bufsize, "%llu",
3237			    l->laggr_pktsumtot->opackets);
3238		}
3239		break;
3240	case AGGR_S_OBYTES:
3241		if (is_port) {
3242			(void) snprintf(buf, bufsize, "%llu",
3243			    diff_stats->obytes);
3244		} else {
3245			(void) snprintf(buf, bufsize, "%llu",
3246			    l->laggr_pktsumtot->obytes);
3247		}
3248		break;
3249
3250	case AGGR_S_IPKTDIST:
3251		if (is_port) {
3252			(void) snprintf(buf, bufsize, "%-6.1f",
3253			    (double)diff_stats->ipackets/
3254			    (double)l->laggr_pktsumtot->ipackets * 100);
3255		}
3256		break;
3257	case AGGR_S_OPKTDIST:
3258		if (is_port) {
3259			(void) snprintf(buf, bufsize, "%-6.1f",
3260			    (double)diff_stats->opackets/
3261			    (double)l->laggr_pktsumtot->opackets * 100);
3262		}
3263		break;
3264	}
3265	return (B_TRUE);
3266
3267err:
3268	*stat = status;
3269	return (B_TRUE);
3270}
3271
3272static dladm_status_t
3273print_aggr_stats(show_grp_state_t *state, const char *link,
3274    dladm_aggr_grp_attr_t *ginfop)
3275{
3276	dladm_phys_attr_t	dpa;
3277	dladm_aggr_port_attr_t	*portp;
3278	pktsum_t		pktsumtot, *port_stat;
3279	dladm_status_t		status;
3280	int			i;
3281	laggr_args_t		largs;
3282
3283	/* sum the ports statistics */
3284	bzero(&pktsumtot, sizeof (pktsumtot));
3285
3286	/* Allocate memory to keep stats of each port */
3287	port_stat = malloc(ginfop->lg_nports * sizeof (pktsum_t));
3288	if (port_stat == NULL) {
3289		/* Bail out; no memory */
3290		return (DLADM_STATUS_NOMEM);
3291	}
3292
3293
3294	for (i = 0; i < ginfop->lg_nports; i++) {
3295
3296		portp = &(ginfop->lg_ports[i]);
3297		if ((status = dladm_phys_info(handle, portp->lp_linkid, &dpa,
3298		    DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
3299			goto done;
3300		}
3301
3302		get_mac_stats(dpa.dp_dev, &port_stat[i]);
3303
3304		/*
3305		 * Let's re-use gs_prevstats[] to store the difference of the
3306		 * counters since last use. We will store the new stats from
3307		 * port_stat[] once we have the stats displayed.
3308		 */
3309
3310		dladm_stats_diff(&state->gs_prevstats[i], &port_stat[i],
3311		    &state->gs_prevstats[i]);
3312		dladm_stats_total(&pktsumtot, &pktsumtot,
3313		    &state->gs_prevstats[i]);
3314	}
3315
3316	largs.laggr_lport = -1;
3317	largs.laggr_link = link;
3318	largs.laggr_ginfop = ginfop;
3319	largs.laggr_status = &status;
3320	largs.laggr_pktsumtot = &pktsumtot;
3321
3322	ofmt_print(state->gs_ofmt, &largs);
3323
3324	if (status != DLADM_STATUS_OK)
3325		goto done;
3326
3327	for (i = 0; i < ginfop->lg_nports; i++) {
3328		largs.laggr_lport = i;
3329		largs.laggr_diffstats = &state->gs_prevstats[i];
3330		ofmt_print(state->gs_ofmt, &largs);
3331		if (status != DLADM_STATUS_OK)
3332			goto done;
3333	}
3334
3335	status = DLADM_STATUS_OK;
3336	for (i = 0; i < ginfop->lg_nports; i++)
3337		state->gs_prevstats[i] = port_stat[i];
3338
3339done:
3340	free(port_stat);
3341	return (status);
3342}
3343
3344static dladm_status_t
3345print_aggr(show_grp_state_t *state, datalink_id_t linkid)
3346{
3347	char			link[MAXLINKNAMELEN];
3348	dladm_aggr_grp_attr_t	ginfo;
3349	uint32_t		flags;
3350	dladm_status_t		status;
3351
3352	bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
3353	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
3354	    NULL, link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
3355		return (status);
3356	}
3357
3358	if (!(state->gs_flags & flags))
3359		return (DLADM_STATUS_NOTFOUND);
3360
3361	status = dladm_aggr_info(handle, linkid, &ginfo, state->gs_flags);
3362	if (status != DLADM_STATUS_OK)
3363		return (status);
3364
3365	if (state->gs_lacp)
3366		status = print_aggr_lacp(state, link, &ginfo);
3367	else if (state->gs_extended)
3368		status = print_aggr_extended(state, link, &ginfo);
3369	else if (state->gs_stats)
3370		status = print_aggr_stats(state, link, &ginfo);
3371	else
3372		status = print_aggr_info(state, link, &ginfo);
3373
3374done:
3375	free(ginfo.lg_ports);
3376	return (status);
3377}
3378
3379/* ARGSUSED */
3380static int
3381show_aggr(dladm_handle_t dh, datalink_id_t linkid, void *arg)
3382{
3383	show_grp_state_t	*state = arg;
3384
3385	state->gs_status = print_aggr(state, linkid);
3386	return (DLADM_WALK_CONTINUE);
3387}
3388
3389static void
3390do_show_link(int argc, char *argv[], const char *use)
3391{
3392	int		option;
3393	boolean_t	s_arg = B_FALSE;
3394	boolean_t	i_arg = B_FALSE;
3395	uint32_t	flags = DLADM_OPT_ACTIVE;
3396	boolean_t	p_arg = B_FALSE;
3397	datalink_id_t	linkid = DATALINK_ALL_LINKID;
3398	char		linkname[MAXLINKNAMELEN];
3399	uint32_t	interval = 0;
3400	show_state_t	state;
3401	dladm_status_t	status;
3402	boolean_t	o_arg = B_FALSE;
3403	char		*fields_str = NULL;
3404	char		*all_active_fields = "link,class,mtu,state,bridge,over";
3405	char		*all_inactive_fields = "link,class,bridge,over";
3406	char		*allstat_fields =
3407	    "link,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
3408	ofmt_handle_t	ofmt;
3409	ofmt_status_t	oferr;
3410	uint_t		ofmtflags = 0;
3411
3412	bzero(&state, sizeof (state));
3413
3414	opterr = 0;
3415	while ((option = getopt_long(argc, argv, ":pPsi:o:",
3416	    show_lopts, NULL)) != -1) {
3417		switch (option) {
3418		case 'p':
3419			if (p_arg)
3420				die_optdup(option);
3421
3422			p_arg = B_TRUE;
3423			break;
3424		case 's':
3425			if (s_arg)
3426				die_optdup(option);
3427
3428			s_arg = B_TRUE;
3429			break;
3430		case 'P':
3431			if (flags != DLADM_OPT_ACTIVE)
3432				die_optdup(option);
3433
3434			flags = DLADM_OPT_PERSIST;
3435			break;
3436		case 'o':
3437			o_arg = B_TRUE;
3438			fields_str = optarg;
3439			break;
3440		case 'i':
3441			if (i_arg)
3442				die_optdup(option);
3443
3444			i_arg = B_TRUE;
3445			if (!dladm_str2interval(optarg, &interval))
3446				die("invalid interval value '%s'", optarg);
3447			break;
3448		default:
3449			die_opterr(optopt, option, use);
3450			break;
3451		}
3452	}
3453
3454	if (i_arg && !s_arg)
3455		die("the option -i can be used only with -s");
3456
3457	if (s_arg && flags != DLADM_OPT_ACTIVE)
3458		die("the option -P cannot be used with -s");
3459
3460	/* get link name (optional last argument) */
3461	if (optind == (argc-1)) {
3462		uint32_t	f;
3463
3464		if (strlcpy(linkname, argv[optind], MAXLINKNAMELEN) >=
3465		    MAXLINKNAMELEN)
3466			die("link name too long");
3467		if ((status = dladm_name2info(handle, linkname, &linkid, &f,
3468		    NULL, NULL)) != DLADM_STATUS_OK) {
3469			die_dlerr(status, "link %s is not valid", linkname);
3470		}
3471
3472		if (!(f & flags)) {
3473			die_dlerr(DLADM_STATUS_BADARG, "link %s is %s",
3474			    argv[optind], flags == DLADM_OPT_PERSIST ?
3475			    "a temporary link" : "temporarily removed");
3476		}
3477	} else if (optind != argc) {
3478		usage();
3479	}
3480
3481	if (p_arg && !o_arg)
3482		die("-p requires -o");
3483
3484	if (p_arg && strcasecmp(fields_str, "all") == 0)
3485		die("\"-o all\" is invalid with -p");
3486
3487	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
3488		if (s_arg)
3489			fields_str = allstat_fields;
3490		else if (flags & DLADM_OPT_ACTIVE)
3491			fields_str = all_active_fields;
3492		else
3493			fields_str = all_inactive_fields;
3494	}
3495
3496	state.ls_parsable = p_arg;
3497	state.ls_flags = flags;
3498	state.ls_donefirst = B_FALSE;
3499
3500	if (s_arg) {
3501		link_stats(linkid, interval, fields_str, &state);
3502		return;
3503	}
3504	if (state.ls_parsable)
3505		ofmtflags |= OFMT_PARSABLE;
3506	else
3507		ofmtflags |= OFMT_WRAP;
3508
3509	oferr = ofmt_open(fields_str, link_fields, ofmtflags, 0, &ofmt);
3510	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
3511	state.ls_ofmt = ofmt;
3512
3513	if (linkid == DATALINK_ALL_LINKID) {
3514		(void) dladm_walk_datalink_id(show_link, handle, &state,
3515		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
3516	} else {
3517		(void) show_link(handle, linkid, &state);
3518		if (state.ls_status != DLADM_STATUS_OK) {
3519			die_dlerr(state.ls_status, "failed to show link %s",
3520			    argv[optind]);
3521		}
3522	}
3523	ofmt_close(ofmt);
3524}
3525
3526static void
3527do_show_aggr(int argc, char *argv[], const char *use)
3528{
3529	boolean_t		L_arg = B_FALSE;
3530	boolean_t		s_arg = B_FALSE;
3531	boolean_t		i_arg = B_FALSE;
3532	boolean_t		p_arg = B_FALSE;
3533	boolean_t		x_arg = B_FALSE;
3534	show_grp_state_t	state;
3535	uint32_t		flags = DLADM_OPT_ACTIVE;
3536	datalink_id_t		linkid = DATALINK_ALL_LINKID;
3537	int			option;
3538	uint32_t		interval = 0;
3539	int			key;
3540	dladm_status_t		status;
3541	boolean_t		o_arg = B_FALSE;
3542	char			*fields_str = NULL;
3543	char			*all_fields =
3544	    "link,policy,addrpolicy,lacpactivity,lacptimer,flags";
3545	char			*all_lacp_fields =
3546	    "link,port,aggregatable,sync,coll,dist,defaulted,expired";
3547	char			*all_stats_fields =
3548	    "link,port,ipackets,rbytes,opackets,obytes,ipktdist,opktdist";
3549	char			*all_extended_fields =
3550	    "link,port,speed,duplex,state,address,portstate";
3551	const ofmt_field_t	*pf;
3552	ofmt_handle_t		ofmt;
3553	ofmt_status_t		oferr;
3554	uint_t			ofmtflags = 0;
3555
3556	opterr = 0;
3557	while ((option = getopt_long(argc, argv, ":LpPxsi:o:",
3558	    show_lopts, NULL)) != -1) {
3559		switch (option) {
3560		case 'L':
3561			if (L_arg)
3562				die_optdup(option);
3563
3564			L_arg = B_TRUE;
3565			break;
3566		case 'p':
3567			if (p_arg)
3568				die_optdup(option);
3569
3570			p_arg = B_TRUE;
3571			break;
3572		case 'x':
3573			if (x_arg)
3574				die_optdup(option);
3575
3576			x_arg = B_TRUE;
3577			break;
3578		case 'P':
3579			if (flags != DLADM_OPT_ACTIVE)
3580				die_optdup(option);
3581
3582			flags = DLADM_OPT_PERSIST;
3583			break;
3584		case 's':
3585			if (s_arg)
3586				die_optdup(option);
3587
3588			s_arg = B_TRUE;
3589			break;
3590		case 'o':
3591			o_arg = B_TRUE;
3592			fields_str = optarg;
3593			break;
3594		case 'i':
3595			if (i_arg)
3596				die_optdup(option);
3597
3598			i_arg = B_TRUE;
3599			if (!dladm_str2interval(optarg, &interval))
3600				die("invalid interval value '%s'", optarg);
3601			break;
3602		default:
3603			die_opterr(optopt, option, use);
3604			break;
3605		}
3606	}
3607
3608	if (p_arg && !o_arg)
3609		die("-p requires -o");
3610
3611	if (p_arg && strcasecmp(fields_str, "all") == 0)
3612		die("\"-o all\" is invalid with -p");
3613
3614	if (i_arg && !s_arg)
3615		die("the option -i can be used only with -s");
3616
3617	if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) {
3618		die("the option -%c cannot be used with -s",
3619		    L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P')));
3620	}
3621
3622	if (L_arg && flags != DLADM_OPT_ACTIVE)
3623		die("the option -P cannot be used with -L");
3624
3625	if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE))
3626		die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P');
3627
3628	/* get aggregation key or aggrname (optional last argument) */
3629	if (optind == (argc-1)) {
3630		if (!str2int(argv[optind], &key)) {
3631			status = dladm_name2info(handle, argv[optind],
3632			    &linkid, NULL, NULL, NULL);
3633		} else {
3634			status = dladm_key2linkid(handle, (uint16_t)key,
3635			    &linkid, DLADM_OPT_ACTIVE);
3636		}
3637
3638		if (status != DLADM_STATUS_OK)
3639			die("non-existent aggregation '%s'", argv[optind]);
3640
3641	} else if (optind != argc) {
3642		usage();
3643	}
3644
3645	bzero(&state, sizeof (state));
3646	state.gs_lacp = L_arg;
3647	state.gs_stats = s_arg;
3648	state.gs_flags = flags;
3649	state.gs_parsable = p_arg;
3650	state.gs_extended = x_arg;
3651
3652	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
3653		if (state.gs_lacp)
3654			fields_str = all_lacp_fields;
3655		else if (state.gs_stats)
3656			fields_str = all_stats_fields;
3657		else if (state.gs_extended)
3658			fields_str = all_extended_fields;
3659		else
3660			fields_str = all_fields;
3661	}
3662
3663	if (state.gs_lacp) {
3664		pf = aggr_l_fields;
3665	} else if (state.gs_stats) {
3666		pf = aggr_s_fields;
3667	} else if (state.gs_extended) {
3668		pf = aggr_x_fields;
3669	} else {
3670		pf = laggr_fields;
3671	}
3672
3673	if (state.gs_parsable)
3674		ofmtflags |= OFMT_PARSABLE;
3675	oferr = ofmt_open(fields_str, pf, ofmtflags, 0, &ofmt);
3676	ofmt_check(oferr, state.gs_parsable, ofmt, die, warn);
3677	state.gs_ofmt = ofmt;
3678
3679	if (s_arg) {
3680		aggr_stats(linkid, &state, interval);
3681		ofmt_close(ofmt);
3682		return;
3683	}
3684
3685	if (linkid == DATALINK_ALL_LINKID) {
3686		(void) dladm_walk_datalink_id(show_aggr, handle, &state,
3687		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
3688	} else {
3689		(void) show_aggr(handle, linkid, &state);
3690		if (state.gs_status != DLADM_STATUS_OK) {
3691			die_dlerr(state.gs_status, "failed to show aggr %s",
3692			    argv[optind]);
3693		}
3694	}
3695	ofmt_close(ofmt);
3696}
3697
3698static dladm_status_t
3699print_phys_default(show_state_t *state, datalink_id_t linkid,
3700    const char *link, uint32_t flags, uint32_t media)
3701{
3702	dladm_phys_attr_t dpa;
3703	dladm_status_t status;
3704	link_fields_buf_t pattr;
3705
3706	status = dladm_phys_info(handle, linkid, &dpa, state->ls_flags);
3707	if (status != DLADM_STATUS_OK)
3708		goto done;
3709
3710	bzero(&pattr, sizeof (pattr));
3711	(void) snprintf(pattr.link_phys_device,
3712	    sizeof (pattr.link_phys_device), "%s", dpa.dp_dev);
3713	(void) dladm_media2str(media, pattr.link_phys_media);
3714	if (state->ls_flags == DLADM_OPT_ACTIVE) {
3715		boolean_t	islink;
3716
3717		if (!dpa.dp_novanity) {
3718			(void) strlcpy(pattr.link_name, link,
3719			    sizeof (pattr.link_name));
3720			islink = B_TRUE;
3721		} else {
3722			/*
3723			 * This is a physical link that does not have
3724			 * vanity naming support.
3725			 */
3726			(void) strlcpy(pattr.link_name, dpa.dp_dev,
3727			    sizeof (pattr.link_name));
3728			islink = B_FALSE;
3729		}
3730
3731		(void) get_linkstate(pattr.link_name, islink,
3732		    pattr.link_phys_state);
3733		(void) snprintf(pattr.link_phys_speed,
3734		    sizeof (pattr.link_phys_speed), "%u",
3735		    (uint_t)((get_ifspeed(pattr.link_name,
3736		    islink)) / 1000000ull));
3737		(void) get_linkduplex(pattr.link_name, islink,
3738		    pattr.link_phys_duplex);
3739	} else {
3740		(void) snprintf(pattr.link_name, sizeof (pattr.link_name),
3741		    "%s", link);
3742		(void) snprintf(pattr.link_flags, sizeof (pattr.link_flags),
3743		    "%c----", flags & DLADM_OPT_ACTIVE ? '-' : 'r');
3744	}
3745
3746	ofmt_print(state->ls_ofmt, &pattr);
3747
3748done:
3749	return (status);
3750}
3751
3752typedef struct {
3753	show_state_t	*ms_state;
3754	char		*ms_link;
3755	dladm_macaddr_attr_t *ms_mac_attr;
3756} print_phys_mac_state_t;
3757
3758/*
3759 *  callback for ofmt_print()
3760 */
3761static boolean_t
3762print_phys_one_mac_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
3763{
3764	print_phys_mac_state_t *mac_state = ofarg->ofmt_cbarg;
3765	dladm_macaddr_attr_t *attr = mac_state->ms_mac_attr;
3766	boolean_t is_primary = (attr->ma_slot == 0);
3767	boolean_t is_parsable = mac_state->ms_state->ls_parsable;
3768
3769	switch (ofarg->ofmt_id) {
3770	case PHYS_M_LINK:
3771		(void) snprintf(buf, bufsize, "%s",
3772		    (is_primary || is_parsable) ? mac_state->ms_link : " ");
3773		break;
3774	case PHYS_M_SLOT:
3775		if (is_primary)
3776			(void) snprintf(buf, bufsize, gettext("primary"));
3777		else
3778			(void) snprintf(buf, bufsize, "%d", attr->ma_slot);
3779		break;
3780	case PHYS_M_ADDRESS:
3781		(void) dladm_aggr_macaddr2str(attr->ma_addr, buf);
3782		break;
3783	case PHYS_M_INUSE:
3784		(void) snprintf(buf, bufsize, "%s",
3785		    attr->ma_flags & DLADM_MACADDR_USED ? gettext("yes") :
3786		    gettext("no"));
3787		break;
3788	case PHYS_M_CLIENT:
3789		/*
3790		 * CR 6678526: resolve link id to actual link name if
3791		 * it is valid.
3792		 */
3793		(void) snprintf(buf, bufsize, "%s", attr->ma_client_name);
3794		break;
3795	}
3796
3797	return (B_TRUE);
3798}
3799
3800typedef struct {
3801	show_state_t	*hs_state;
3802	char		*hs_link;
3803	dladm_hwgrp_attr_t *hs_grp_attr;
3804} print_phys_hwgrp_state_t;
3805
3806static boolean_t
3807print_phys_one_hwgrp_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
3808{
3809	int		i;
3810	boolean_t	first = B_TRUE;
3811	int		start = -1;
3812	int		end = -1;
3813	char		ringstr[RINGSTRLEN];
3814	char		ringsubstr[RINGSTRLEN];
3815
3816	print_phys_hwgrp_state_t *hg_state = ofarg->ofmt_cbarg;
3817	dladm_hwgrp_attr_t *attr = hg_state->hs_grp_attr;
3818
3819	switch (ofarg->ofmt_id) {
3820	case PHYS_H_LINK:
3821		(void) snprintf(buf, bufsize, "%s", attr->hg_link_name);
3822		break;
3823	case PHYS_H_RINGTYPE:
3824		(void) snprintf(buf, bufsize, "%s",
3825		    attr->hg_grp_type == DLADM_HWGRP_TYPE_RX ? "RX" : "TX");
3826		break;
3827	case PHYS_H_RINGS:
3828		ringstr[0] = '\0';
3829		for (i = 0; i < attr->hg_n_rings; i++) {
3830			uint_t	index = attr->hg_rings[i];
3831
3832			if (start == -1) {
3833				start = index;
3834				end = index;
3835			} else if (index == end + 1) {
3836				end = index;
3837			} else {
3838				if (start == end) {
3839					if (first) {
3840						(void) snprintf(
3841						    ringsubstr,
3842						    RINGSTRLEN, "%d",
3843						    start);
3844						first = B_FALSE;
3845					} else {
3846						(void) snprintf(
3847						    ringsubstr,
3848						    RINGSTRLEN, ",%d",
3849						    start);
3850					}
3851				} else {
3852					if (first) {
3853						(void) snprintf(
3854						    ringsubstr,
3855						    RINGSTRLEN,
3856						    "%d-%d",
3857						    start, end);
3858						first = B_FALSE;
3859					} else {
3860						(void) snprintf(
3861						    ringsubstr,
3862						    RINGSTRLEN,
3863						    ",%d-%d",
3864						    start, end);
3865					}
3866				}
3867				(void) strlcat(ringstr, ringsubstr,
3868				    RINGSTRLEN);
3869				start = index;
3870				end = index;
3871			}
3872		}
3873		/* The last one */
3874		if (start != -1) {
3875			if (first) {
3876				if (start == end) {
3877					(void) snprintf(buf, bufsize, "%d",
3878					    start);
3879				} else {
3880					(void) snprintf(buf, bufsize, "%d-%d",
3881					    start, end);
3882				}
3883			} else {
3884				if (start == end) {
3885					(void) snprintf(ringsubstr, RINGSTRLEN,
3886					    ",%d", start);
3887				} else {
3888					(void) snprintf(ringsubstr, RINGSTRLEN,
3889					    ",%d-%d", start, end);
3890				}
3891				(void) strlcat(ringstr, ringsubstr, RINGSTRLEN);
3892				(void) snprintf(buf, bufsize, "%s", ringstr);
3893			}
3894		}
3895		break;
3896	case PHYS_H_CLIENTS:
3897		if (attr->hg_client_names[0] == '\0') {
3898			(void) snprintf(buf, bufsize, "--");
3899		} else {
3900			(void) snprintf(buf, bufsize, "%s ",
3901			    attr->hg_client_names);
3902		}
3903		break;
3904	}
3905
3906	return (B_TRUE);
3907}
3908
3909/*
3910 * callback for dladm_walk_macaddr, invoked for each MAC address slot
3911 */
3912static boolean_t
3913print_phys_mac_callback(void *arg, dladm_macaddr_attr_t *attr)
3914{
3915	print_phys_mac_state_t *mac_state = arg;
3916	show_state_t *state = mac_state->ms_state;
3917
3918	mac_state->ms_mac_attr = attr;
3919	ofmt_print(state->ls_ofmt, mac_state);
3920
3921	return (B_TRUE);
3922}
3923
3924/*
3925 * invoked by show-phys -m for each physical data-link
3926 */
3927static dladm_status_t
3928print_phys_mac(show_state_t *state, datalink_id_t linkid, char *link)
3929{
3930	print_phys_mac_state_t mac_state;
3931
3932	mac_state.ms_state = state;
3933	mac_state.ms_link = link;
3934
3935	return (dladm_walk_macaddr(handle, linkid, &mac_state,
3936	    print_phys_mac_callback));
3937}
3938
3939/*
3940 * callback for dladm_walk_hwgrp, invoked for each MAC hwgrp
3941 */
3942static boolean_t
3943print_phys_hwgrp_callback(void *arg, dladm_hwgrp_attr_t *attr)
3944{
3945	print_phys_hwgrp_state_t *hwgrp_state = arg;
3946	show_state_t *state = hwgrp_state->hs_state;
3947
3948	hwgrp_state->hs_grp_attr = attr;
3949	ofmt_print(state->ls_ofmt, hwgrp_state);
3950
3951	return (B_TRUE);
3952}
3953
3954/* invoked by show-phys -H for each physical data-link */
3955static dladm_status_t
3956print_phys_hwgrp(show_state_t *state, datalink_id_t linkid, char *link)
3957{
3958	print_phys_hwgrp_state_t hwgrp_state;
3959
3960	hwgrp_state.hs_state = state;
3961	hwgrp_state.hs_link = link;
3962	return (dladm_walk_hwgrp(handle, linkid, &hwgrp_state,
3963	    print_phys_hwgrp_callback));
3964}
3965
3966/*
3967 * Parse the "local=<laddr>,remote=<raddr>" sub-options for the -a option of
3968 * *-iptun subcommands.
3969 */
3970static void
3971iptun_process_addrarg(char *addrarg, iptun_params_t *params)
3972{
3973	char *addrval;
3974
3975	while (*addrarg != '\0') {
3976		switch (getsubopt(&addrarg, iptun_addropts, &addrval)) {
3977		case IPTUN_LOCAL:
3978			if (addrval == NULL)
3979				die("tunnel source address value is missing");
3980			params->iptun_param_flags |= IPTUN_PARAM_LADDR;
3981			if (strlcpy(params->iptun_param_laddr, addrval,
3982			    sizeof (params->iptun_param_laddr)) >=
3983			    sizeof (params->iptun_param_laddr))
3984				die("tunnel source address is too long");
3985			break;
3986		case IPTUN_REMOTE:
3987			if (addrval == NULL)
3988				die("tunnel destination address value "
3989				    "is missing");
3990			params->iptun_param_flags |= IPTUN_PARAM_RADDR;
3991			if (strlcpy(params->iptun_param_raddr, addrval,
3992			    sizeof (params->iptun_param_raddr)) >=
3993			    sizeof (params->iptun_param_raddr))
3994				die("tunnel destination address is too long");
3995			break;
3996		default:
3997			die("invalid address type: %s", addrval);
3998			break;
3999		}
4000	}
4001}
4002
4003/*
4004 * Convenience routine to process iptun-create/modify/delete subcommand
4005 * arguments.
4006 */
4007static void
4008iptun_process_args(int argc, char *argv[], const char *opts,
4009    iptun_params_t *params, uint32_t *flags, char *name, const char *use)
4010{
4011	int	option;
4012	char	*altroot = NULL;
4013
4014	if (params != NULL)
4015		bzero(params, sizeof (*params));
4016	*flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
4017
4018	opterr = 0;
4019	while ((option = getopt_long(argc, argv, opts, iptun_lopts, NULL)) !=
4020	    -1) {
4021		switch (option) {
4022		case 'a':
4023			iptun_process_addrarg(optarg, params);
4024			break;
4025		case 'R':
4026			altroot = optarg;
4027			break;
4028		case 't':
4029			*flags &= ~DLADM_OPT_PERSIST;
4030			break;
4031		case 'T':
4032			params->iptun_param_type = iptun_gettypebyname(optarg);
4033			if (params->iptun_param_type == IPTUN_TYPE_UNKNOWN)
4034				die("unknown tunnel type: %s", optarg);
4035			params->iptun_param_flags |= IPTUN_PARAM_TYPE;
4036			break;
4037		default:
4038			die_opterr(optopt, option, use);
4039			break;
4040		}
4041	}
4042
4043	/* Get the required tunnel name argument. */
4044	if (argc - optind != 1)
4045		usage();
4046
4047	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
4048		die("tunnel name is too long");
4049
4050	if (altroot != NULL)
4051		altroot_cmd(altroot, argc, argv);
4052}
4053
4054static void
4055do_create_iptun(int argc, char *argv[], const char *use)
4056{
4057	iptun_params_t	params;
4058	dladm_status_t	status;
4059	uint32_t	flags;
4060	char		name[MAXLINKNAMELEN];
4061
4062	iptun_process_args(argc, argv, ":a:R:tT:", &params, &flags, name,
4063	    use);
4064
4065	status = dladm_iptun_create(handle, name, &params, flags);
4066	if (status != DLADM_STATUS_OK)
4067		die_dlerr(status, "could not create tunnel");
4068}
4069
4070static void
4071do_delete_iptun(int argc, char *argv[], const char *use)
4072{
4073	uint32_t	flags;
4074	datalink_id_t	linkid;
4075	dladm_status_t	status;
4076	char		name[MAXLINKNAMELEN];
4077
4078	iptun_process_args(argc, argv, ":R:t", NULL, &flags, name, use);
4079
4080	status = dladm_name2info(handle, name, &linkid, NULL, NULL, NULL);
4081	if (status != DLADM_STATUS_OK)
4082		die_dlerr(status, "could not delete tunnel");
4083	status = dladm_iptun_delete(handle, linkid, flags);
4084	if (status != DLADM_STATUS_OK)
4085		die_dlerr(status, "could not delete tunnel");
4086}
4087
4088static void
4089do_modify_iptun(int argc, char *argv[], const char *use)
4090{
4091	iptun_params_t	params;
4092	uint32_t	flags;
4093	dladm_status_t	status;
4094	char		name[MAXLINKNAMELEN];
4095
4096	iptun_process_args(argc, argv, ":a:R:t", &params, &flags, name, use);
4097
4098	if ((status = dladm_name2info(handle, name, &params.iptun_param_linkid,
4099	    NULL, NULL, NULL)) != DLADM_STATUS_OK)
4100		die_dlerr(status, "could not modify tunnel");
4101	status = dladm_iptun_modify(handle, &params, flags);
4102	if (status != DLADM_STATUS_OK)
4103		die_dlerr(status, "could not modify tunnel");
4104}
4105
4106static void
4107do_show_iptun(int argc, char *argv[], const char *use)
4108{
4109	char		option;
4110	datalink_id_t	linkid;
4111	uint32_t	flags = DLADM_OPT_ACTIVE;
4112	char		*name = NULL;
4113	dladm_status_t	status;
4114	const char	*fields_str = NULL;
4115	show_state_t	state;
4116	ofmt_handle_t	ofmt;
4117	ofmt_status_t	oferr;
4118	uint_t		ofmtflags = 0;
4119
4120	bzero(&state, sizeof (state));
4121	opterr = 0;
4122	while ((option = getopt_long(argc, argv, ":pPo:",
4123	    iptun_lopts, NULL)) != -1) {
4124		switch (option) {
4125		case 'o':
4126			fields_str = optarg;
4127			break;
4128		case 'p':
4129			state.ls_parsable = B_TRUE;
4130			ofmtflags = OFMT_PARSABLE;
4131			break;
4132		case 'P':
4133			flags = DLADM_OPT_PERSIST;
4134			break;
4135		default:
4136			die_opterr(optopt, option, use);
4137			break;
4138		}
4139	}
4140
4141	/*
4142	 * Get the optional tunnel name argument.  If there is one, it must
4143	 * be the last thing remaining on the command-line.
4144	 */
4145	if (argc - optind > 1)
4146		die(gettext(use));
4147	if (argc - optind == 1)
4148		name = argv[optind];
4149
4150	oferr = ofmt_open(fields_str, iptun_fields, ofmtflags,
4151	    DLADM_DEFAULT_COL, &ofmt);
4152	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
4153
4154	state.ls_ofmt = ofmt;
4155	state.ls_flags = flags;
4156
4157	if (name == NULL) {
4158		(void) dladm_walk_datalink_id(print_iptun_walker, handle,
4159		    &state, DATALINK_CLASS_IPTUN, DATALINK_ANY_MEDIATYPE,
4160		    flags);
4161		status = state.ls_status;
4162	} else {
4163		if ((status = dladm_name2info(handle, name, &linkid, NULL, NULL,
4164		    NULL)) == DLADM_STATUS_OK)
4165			status = print_iptun(handle, linkid, &state);
4166	}
4167
4168	if (status != DLADM_STATUS_OK)
4169		die_dlerr(status, "unable to obtain tunnel status");
4170}
4171
4172/* ARGSUSED */
4173static void
4174do_up_iptun(int argc, char *argv[], const char *use)
4175{
4176	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4177	dladm_status_t	status = DLADM_STATUS_OK;
4178
4179	/*
4180	 * Get the optional tunnel name argument.  If there is one, it must
4181	 * be the last thing remaining on the command-line.
4182	 */
4183	if (argc - optind > 1)
4184		usage();
4185	if (argc - optind == 1) {
4186		status = dladm_name2info(handle, argv[optind], &linkid, NULL,
4187		    NULL, NULL);
4188	}
4189	if (status == DLADM_STATUS_OK)
4190		status = dladm_iptun_up(handle, linkid);
4191	if (status != DLADM_STATUS_OK)
4192		die_dlerr(status, "unable to configure IP tunnel links");
4193}
4194
4195/* ARGSUSED */
4196static void
4197do_down_iptun(int argc, char *argv[], const char *use)
4198{
4199	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4200	dladm_status_t	status = DLADM_STATUS_OK;
4201
4202	/*
4203	 * Get the optional tunnel name argument.  If there is one, it must
4204	 * be the last thing remaining on the command-line.
4205	 */
4206	if (argc - optind > 1)
4207		usage();
4208	if (argc - optind == 1) {
4209		status = dladm_name2info(handle, argv[optind], &linkid, NULL,
4210		    NULL, NULL);
4211	}
4212	if (status == DLADM_STATUS_OK)
4213		status = dladm_iptun_down(handle, linkid);
4214	if (status != DLADM_STATUS_OK)
4215		die_dlerr(status, "unable to bring down IP tunnel links");
4216}
4217
4218static iptun_type_t
4219iptun_gettypebyname(char *typestr)
4220{
4221	int i;
4222
4223	for (i = 0; iptun_types[i].type_name != NULL; i++) {
4224		if (strncmp(iptun_types[i].type_name, typestr,
4225		    strlen(iptun_types[i].type_name)) == 0) {
4226			return (iptun_types[i].type_value);
4227		}
4228	}
4229	return (IPTUN_TYPE_UNKNOWN);
4230}
4231
4232static const char *
4233iptun_gettypebyvalue(iptun_type_t type)
4234{
4235	int i;
4236
4237	for (i = 0; iptun_types[i].type_name != NULL; i++) {
4238		if (iptun_types[i].type_value == type)
4239			return (iptun_types[i].type_name);
4240	}
4241	return (NULL);
4242}
4243
4244static dladm_status_t
4245print_iptun(dladm_handle_t dh, datalink_id_t linkid, show_state_t *state)
4246{
4247	dladm_status_t		status;
4248	iptun_params_t		params;
4249	iptun_fields_buf_t	lbuf;
4250	const char		*laddr;
4251	const char		*raddr;
4252
4253	params.iptun_param_linkid = linkid;
4254	status = dladm_iptun_getparams(dh, &params, state->ls_flags);
4255	if (status != DLADM_STATUS_OK)
4256		return (status);
4257
4258	/* LINK */
4259	status = dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL,
4260	    lbuf.iptun_name, sizeof (lbuf.iptun_name));
4261	if (status != DLADM_STATUS_OK)
4262		return (status);
4263
4264	/* TYPE */
4265	(void) strlcpy(lbuf.iptun_type,
4266	    iptun_gettypebyvalue(params.iptun_param_type),
4267	    sizeof (lbuf.iptun_type));
4268
4269	/* FLAGS */
4270	(void) memset(lbuf.iptun_flags, '-', IPTUN_NUM_FLAGS);
4271	lbuf.iptun_flags[IPTUN_NUM_FLAGS] = '\0';
4272	if (params.iptun_param_flags & IPTUN_PARAM_IPSECPOL)
4273		lbuf.iptun_flags[IPTUN_SFLAG_INDEX] = 's';
4274	if (params.iptun_param_flags & IPTUN_PARAM_IMPLICIT)
4275		lbuf.iptun_flags[IPTUN_IFLAG_INDEX] = 'i';
4276
4277	/* LOCAL */
4278	if (params.iptun_param_flags & IPTUN_PARAM_LADDR)
4279		laddr = params.iptun_param_laddr;
4280	else
4281		laddr = (state->ls_parsable) ? "" : "--";
4282	(void) strlcpy(lbuf.iptun_laddr, laddr, sizeof (lbuf.iptun_laddr));
4283
4284	/* REMOTE */
4285	if (params.iptun_param_flags & IPTUN_PARAM_RADDR)
4286		raddr = params.iptun_param_raddr;
4287	else
4288		raddr = (state->ls_parsable) ? "" : "--";
4289	(void) strlcpy(lbuf.iptun_raddr, raddr, sizeof (lbuf.iptun_raddr));
4290
4291	ofmt_print(state->ls_ofmt, &lbuf);
4292
4293	return (DLADM_STATUS_OK);
4294}
4295
4296static int
4297print_iptun_walker(dladm_handle_t dh, datalink_id_t linkid, void *arg)
4298{
4299	((show_state_t *)arg)->ls_status = print_iptun(dh, linkid, arg);
4300	return (DLADM_WALK_CONTINUE);
4301}
4302
4303static dladm_status_t
4304print_phys(show_state_t *state, datalink_id_t linkid)
4305{
4306	char			link[MAXLINKNAMELEN];
4307	uint32_t		flags;
4308	dladm_status_t		status;
4309	datalink_class_t	class;
4310	uint32_t		media;
4311
4312	if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
4313	    &media, link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
4314		goto done;
4315	}
4316
4317	if (class != DATALINK_CLASS_PHYS) {
4318		status = DLADM_STATUS_BADARG;
4319		goto done;
4320	}
4321
4322	if (!(state->ls_flags & flags)) {
4323		status = DLADM_STATUS_NOTFOUND;
4324		goto done;
4325	}
4326
4327	if (state->ls_mac)
4328		status = print_phys_mac(state, linkid, link);
4329	else if (state->ls_hwgrp)
4330		status = print_phys_hwgrp(state, linkid, link);
4331	else
4332		status = print_phys_default(state, linkid, link, flags, media);
4333
4334done:
4335	return (status);
4336}
4337
4338/* ARGSUSED */
4339static int
4340show_phys(dladm_handle_t dh, datalink_id_t linkid, void *arg)
4341{
4342	show_state_t	*state = arg;
4343
4344	state->ls_status = print_phys(state, linkid);
4345	return (DLADM_WALK_CONTINUE);
4346}
4347
4348/*
4349 * Print the active topology information.
4350 */
4351static dladm_status_t
4352print_vlan(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *l)
4353{
4354	dladm_vlan_attr_t	vinfo;
4355	uint32_t		flags;
4356	dladm_status_t		status;
4357
4358	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, NULL,
4359	    l->link_name, sizeof (l->link_name))) != DLADM_STATUS_OK) {
4360		goto done;
4361	}
4362
4363	if (!(state->ls_flags & flags)) {
4364		status = DLADM_STATUS_NOTFOUND;
4365		goto done;
4366	}
4367
4368	if ((status = dladm_vlan_info(handle, linkid, &vinfo,
4369	    state->ls_flags)) != DLADM_STATUS_OK ||
4370	    (status = dladm_datalink_id2info(handle, vinfo.dv_linkid, NULL,
4371	    NULL, NULL, l->link_over, sizeof (l->link_over))) !=
4372	    DLADM_STATUS_OK) {
4373		goto done;
4374	}
4375
4376	(void) snprintf(l->link_vlan_vid, sizeof (l->link_vlan_vid), "%d",
4377	    vinfo.dv_vid);
4378	(void) snprintf(l->link_flags, sizeof (l->link_flags), "%c----",
4379	    vinfo.dv_force ? 'f' : '-');
4380
4381done:
4382	return (status);
4383}
4384
4385/* ARGSUSED */
4386static int
4387show_vlan(dladm_handle_t dh, datalink_id_t linkid, void *arg)
4388{
4389	show_state_t		*state = arg;
4390	dladm_status_t		status;
4391	link_fields_buf_t	lbuf;
4392
4393	bzero(&lbuf, sizeof (link_fields_buf_t));
4394	status = print_vlan(state, linkid, &lbuf);
4395	if (status != DLADM_STATUS_OK)
4396		goto done;
4397
4398	ofmt_print(state->ls_ofmt, &lbuf);
4399
4400done:
4401	state->ls_status = status;
4402	return (DLADM_WALK_CONTINUE);
4403}
4404
4405static void
4406do_show_phys(int argc, char *argv[], const char *use)
4407{
4408	int		option;
4409	uint32_t	flags = DLADM_OPT_ACTIVE;
4410	boolean_t	p_arg = B_FALSE;
4411	boolean_t	o_arg = B_FALSE;
4412	boolean_t	m_arg = B_FALSE;
4413	boolean_t	H_arg = B_FALSE;
4414	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4415	show_state_t	state;
4416	dladm_status_t	status;
4417	char		*fields_str = NULL;
4418	char		*all_active_fields =
4419	    "link,media,state,speed,duplex,device";
4420	char		*all_inactive_fields = "link,device,media,flags";
4421	char		*all_mac_fields = "link,slot,address,inuse,client";
4422	char		*all_hwgrp_fields = "link,ringtype,rings,clients";
4423	const ofmt_field_t *pf;
4424	ofmt_handle_t	ofmt;
4425	ofmt_status_t	oferr;
4426	uint_t		ofmtflags = 0;
4427
4428	bzero(&state, sizeof (state));
4429	opterr = 0;
4430	while ((option = getopt_long(argc, argv, ":pPo:mH",
4431	    show_lopts, NULL)) != -1) {
4432		switch (option) {
4433		case 'p':
4434			if (p_arg)
4435				die_optdup(option);
4436
4437			p_arg = B_TRUE;
4438			break;
4439		case 'P':
4440			if (flags != DLADM_OPT_ACTIVE)
4441				die_optdup(option);
4442
4443			flags = DLADM_OPT_PERSIST;
4444			break;
4445		case 'o':
4446			o_arg = B_TRUE;
4447			fields_str = optarg;
4448			break;
4449		case 'm':
4450			m_arg = B_TRUE;
4451			break;
4452		case 'H':
4453			H_arg = B_TRUE;
4454			break;
4455		default:
4456			die_opterr(optopt, option, use);
4457			break;
4458		}
4459	}
4460
4461	if (p_arg && !o_arg)
4462		die("-p requires -o");
4463
4464	if (m_arg && H_arg)
4465		die("-m cannot combine with -H");
4466
4467	if (p_arg && strcasecmp(fields_str, "all") == 0)
4468		die("\"-o all\" is invalid with -p");
4469
4470	/* get link name (optional last argument) */
4471	if (optind == (argc-1)) {
4472		if ((status = dladm_name2info(handle, argv[optind], &linkid,
4473		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
4474			die_dlerr(status, "link %s is not valid", argv[optind]);
4475		}
4476	} else if (optind != argc) {
4477		usage();
4478	}
4479
4480	state.ls_parsable = p_arg;
4481	state.ls_flags = flags;
4482	state.ls_donefirst = B_FALSE;
4483	state.ls_mac = m_arg;
4484	state.ls_hwgrp = H_arg;
4485
4486	if (m_arg && !(flags & DLADM_OPT_ACTIVE)) {
4487		/*
4488		 * We can only display the factory MAC addresses of
4489		 * active data-links.
4490		 */
4491		die("-m not compatible with -P");
4492	}
4493
4494	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
4495		if (state.ls_mac)
4496			fields_str = all_mac_fields;
4497		else if (state.ls_hwgrp)
4498			fields_str = all_hwgrp_fields;
4499		else if (state.ls_flags & DLADM_OPT_ACTIVE) {
4500			fields_str = all_active_fields;
4501		} else {
4502			fields_str = all_inactive_fields;
4503		}
4504	}
4505
4506	if (state.ls_mac) {
4507		pf = phys_m_fields;
4508	} else if (state.ls_hwgrp) {
4509		pf = phys_h_fields;
4510	} else {
4511		pf = phys_fields;
4512	}
4513
4514	if (state.ls_parsable)
4515		ofmtflags |= OFMT_PARSABLE;
4516	oferr = ofmt_open(fields_str, pf, ofmtflags, 0, &ofmt);
4517	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
4518	state.ls_ofmt = ofmt;
4519
4520	if (linkid == DATALINK_ALL_LINKID) {
4521		(void) dladm_walk_datalink_id(show_phys, handle, &state,
4522		    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags);
4523	} else {
4524		(void) show_phys(handle, linkid, &state);
4525		if (state.ls_status != DLADM_STATUS_OK) {
4526			die_dlerr(state.ls_status,
4527			    "failed to show physical link %s", argv[optind]);
4528		}
4529	}
4530	ofmt_close(ofmt);
4531}
4532
4533static void
4534do_show_vlan(int argc, char *argv[], const char *use)
4535{
4536	int		option;
4537	uint32_t	flags = DLADM_OPT_ACTIVE;
4538	boolean_t	p_arg = B_FALSE;
4539	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4540	show_state_t	state;
4541	dladm_status_t	status;
4542	boolean_t	o_arg = B_FALSE;
4543	char		*fields_str = NULL;
4544	ofmt_handle_t	ofmt;
4545	ofmt_status_t	oferr;
4546	uint_t		ofmtflags = 0;
4547
4548	bzero(&state, sizeof (state));
4549
4550	opterr = 0;
4551	while ((option = getopt_long(argc, argv, ":pPo:",
4552	    show_lopts, NULL)) != -1) {
4553		switch (option) {
4554		case 'p':
4555			if (p_arg)
4556				die_optdup(option);
4557
4558			p_arg = B_TRUE;
4559			break;
4560		case 'P':
4561			if (flags != DLADM_OPT_ACTIVE)
4562				die_optdup(option);
4563
4564			flags = DLADM_OPT_PERSIST;
4565			break;
4566		case 'o':
4567			o_arg = B_TRUE;
4568			fields_str = optarg;
4569			break;
4570		default:
4571			die_opterr(optopt, option, use);
4572			break;
4573		}
4574	}
4575
4576	/* get link name (optional last argument) */
4577	if (optind == (argc-1)) {
4578		if ((status = dladm_name2info(handle, argv[optind], &linkid,
4579		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
4580			die_dlerr(status, "link %s is not valid", argv[optind]);
4581		}
4582	} else if (optind != argc) {
4583		usage();
4584	}
4585
4586	state.ls_parsable = p_arg;
4587	state.ls_flags = flags;
4588	state.ls_donefirst = B_FALSE;
4589
4590	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
4591		fields_str = NULL;
4592
4593	if (state.ls_parsable)
4594		ofmtflags |= OFMT_PARSABLE;
4595	oferr = ofmt_open(fields_str, vlan_fields, ofmtflags, 0, &ofmt);
4596	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
4597	state.ls_ofmt = ofmt;
4598
4599	if (linkid == DATALINK_ALL_LINKID) {
4600		(void) dladm_walk_datalink_id(show_vlan, handle, &state,
4601		    DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags);
4602	} else {
4603		(void) show_vlan(handle, linkid, &state);
4604		if (state.ls_status != DLADM_STATUS_OK) {
4605			die_dlerr(state.ls_status, "failed to show vlan %s",
4606			    argv[optind]);
4607		}
4608	}
4609	ofmt_close(ofmt);
4610}
4611
4612static void
4613do_create_vnic(int argc, char *argv[], const char *use)
4614{
4615	datalink_id_t		linkid, dev_linkid;
4616	char			devname[MAXLINKNAMELEN];
4617	char			name[MAXLINKNAMELEN];
4618	boolean_t		l_arg = B_FALSE;
4619	uint32_t		flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
4620	char			*altroot = NULL;
4621	int			option;
4622	char			*endp = NULL;
4623	dladm_status_t		status;
4624	vnic_mac_addr_type_t	mac_addr_type = VNIC_MAC_ADDR_TYPE_UNKNOWN;
4625	uchar_t			*mac_addr = NULL;
4626	int			mac_slot = -1;
4627	uint_t			maclen = 0, mac_prefix_len = 0;
4628	char			propstr[DLADM_STRSIZE];
4629	dladm_arg_list_t	*proplist = NULL;
4630	int			vid = 0;
4631	int			af = AF_UNSPEC;
4632	vrid_t			vrid = VRRP_VRID_NONE;
4633
4634	opterr = 0;
4635	bzero(propstr, DLADM_STRSIZE);
4636
4637	while ((option = getopt_long(argc, argv, ":tfR:l:m:n:p:r:v:V:A:H",
4638	    vnic_lopts, NULL)) != -1) {
4639		switch (option) {
4640		case 't':
4641			flags &= ~DLADM_OPT_PERSIST;
4642			break;
4643		case 'R':
4644			altroot = optarg;
4645			break;
4646		case 'l':
4647			if (strlcpy(devname, optarg, MAXLINKNAMELEN) >=
4648			    MAXLINKNAMELEN)
4649				die("link name too long");
4650			l_arg = B_TRUE;
4651			break;
4652		case 'm':
4653			if (mac_addr_type != VNIC_MAC_ADDR_TYPE_UNKNOWN)
4654				die("cannot specify -m option twice");
4655
4656			if (strcmp(optarg, "fixed") == 0) {
4657				/*
4658				 * A fixed MAC address must be specified
4659				 * by its value, not by the keyword 'fixed'.
4660				 */
4661				die("'fixed' is not a valid MAC address");
4662			}
4663			if (dladm_vnic_str2macaddrtype(optarg,
4664			    &mac_addr_type) != DLADM_STATUS_OK) {
4665				mac_addr_type = VNIC_MAC_ADDR_TYPE_FIXED;
4666				/* MAC address specified by value */
4667				mac_addr = _link_aton(optarg, (int *)&maclen);
4668				if (mac_addr == NULL) {
4669					if (maclen == (uint_t)-1)
4670						die("invalid MAC address");
4671					else
4672						die("out of memory");
4673				}
4674			}
4675			break;
4676		case 'n':
4677			errno = 0;
4678			mac_slot = (int)strtol(optarg, &endp, 10);
4679			if (errno != 0 || *endp != '\0')
4680				die("invalid slot number");
4681			break;
4682		case 'p':
4683			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
4684			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
4685			    DLADM_STRSIZE)
4686				die("property list too long '%s'", propstr);
4687			break;
4688		case 'r':
4689			mac_addr = _link_aton(optarg, (int *)&mac_prefix_len);
4690			if (mac_addr == NULL) {
4691				if (mac_prefix_len == (uint_t)-1)
4692					die("invalid MAC address");
4693				else
4694					die("out of memory");
4695			}
4696			break;
4697		case 'V':
4698			if (!str2int(optarg, (int *)&vrid) ||
4699			    vrid < VRRP_VRID_MIN || vrid > VRRP_VRID_MAX) {
4700				die("invalid VRRP identifier '%s'", optarg);
4701			}
4702
4703			break;
4704		case 'A':
4705			if (strcmp(optarg, "inet") == 0)
4706				af = AF_INET;
4707			else if (strcmp(optarg, "inet6") == 0)
4708				af = AF_INET6;
4709			else
4710				die("invalid address family '%s'", optarg);
4711			break;
4712		case 'v':
4713			if (vid != 0)
4714				die_optdup(option);
4715
4716			if (!str2int(optarg, &vid) || vid < 1 || vid > 4094)
4717				die("invalid VLAN identifier '%s'", optarg);
4718
4719			break;
4720		case 'f':
4721			flags |= DLADM_OPT_FORCE;
4722			break;
4723		default:
4724			die_opterr(optopt, option, use);
4725		}
4726	}
4727
4728	if (mac_addr_type == VNIC_MAC_ADDR_TYPE_UNKNOWN)
4729		mac_addr_type = VNIC_MAC_ADDR_TYPE_AUTO;
4730
4731	/*
4732	 * 'f' - force, flag can be specified only with 'v' - vlan.
4733	 */
4734	if ((flags & DLADM_OPT_FORCE) != 0 && vid == 0)
4735		die("-f option can only be used with -v");
4736
4737	if (mac_prefix_len != 0 && mac_addr_type != VNIC_MAC_ADDR_TYPE_RANDOM &&
4738	    mac_addr_type != VNIC_MAC_ADDR_TYPE_FIXED)
4739		usage();
4740
4741	if (mac_addr_type == VNIC_MAC_ADDR_TYPE_VRID) {
4742		if (vrid == VRRP_VRID_NONE || af == AF_UNSPEC ||
4743		    mac_addr != NULL || maclen != 0 || mac_slot != -1 ||
4744		    mac_prefix_len != 0) {
4745			usage();
4746		}
4747	} else if ((af != AF_UNSPEC || vrid != VRRP_VRID_NONE)) {
4748		usage();
4749	}
4750
4751	/* check required options */
4752	if (!l_arg)
4753		usage();
4754
4755	if (mac_slot != -1 && mac_addr_type != VNIC_MAC_ADDR_TYPE_FACTORY)
4756		usage();
4757
4758	/* the VNIC id is the required operand */
4759	if (optind != (argc - 1))
4760		usage();
4761
4762	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
4763		die("link name too long '%s'", argv[optind]);
4764
4765	if (!dladm_valid_linkname(name))
4766		die("invalid link name '%s'", argv[optind]);
4767
4768	if (altroot != NULL)
4769		altroot_cmd(altroot, argc, argv);
4770
4771	if (dladm_name2info(handle, devname, &dev_linkid, NULL, NULL, NULL) !=
4772	    DLADM_STATUS_OK)
4773		die("invalid link name '%s'", devname);
4774
4775	if (dladm_parse_link_props(propstr, &proplist, B_FALSE)
4776	    != DLADM_STATUS_OK)
4777		die("invalid vnic property");
4778
4779	status = dladm_vnic_create(handle, name, dev_linkid, mac_addr_type,
4780	    mac_addr, maclen, &mac_slot, mac_prefix_len, vid, vrid, af,
4781	    &linkid, proplist, flags);
4782	switch (status) {
4783	case DLADM_STATUS_OK:
4784		break;
4785
4786	case DLADM_STATUS_LINKBUSY:
4787		die("VLAN over '%s' may not use default_tag ID "
4788		    "(see dladm(1M))", devname);
4789		break;
4790
4791	default:
4792		die_dlerr(status, "vnic creation over %s failed", devname);
4793	}
4794
4795	dladm_free_props(proplist);
4796	free(mac_addr);
4797}
4798
4799static void
4800do_etherstub_check(const char *name, datalink_id_t linkid, boolean_t etherstub,
4801    uint32_t flags)
4802{
4803	boolean_t is_etherstub;
4804	dladm_vnic_attr_t attr;
4805
4806	if (dladm_vnic_info(handle, linkid, &attr, flags) != DLADM_STATUS_OK) {
4807		/*
4808		 * Let the delete continue anyway.
4809		 */
4810		return;
4811	}
4812	is_etherstub = (attr.va_link_id == DATALINK_INVALID_LINKID);
4813	if (is_etherstub != etherstub) {
4814		die("'%s' is not %s", name,
4815		    (is_etherstub ? "a vnic" : "an etherstub"));
4816	}
4817}
4818
4819static void
4820do_delete_vnic_common(int argc, char *argv[], const char *use,
4821    boolean_t etherstub)
4822{
4823	int option;
4824	uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
4825	datalink_id_t linkid;
4826	char *altroot = NULL;
4827	dladm_status_t status;
4828
4829	opterr = 0;
4830	while ((option = getopt_long(argc, argv, ":R:t", lopts,
4831	    NULL)) != -1) {
4832		switch (option) {
4833		case 't':
4834			flags &= ~DLADM_OPT_PERSIST;
4835			break;
4836		case 'R':
4837			altroot = optarg;
4838			break;
4839		default:
4840			die_opterr(optopt, option, use);
4841		}
4842	}
4843
4844	/* get vnic name (required last argument) */
4845	if (optind != (argc - 1))
4846		usage();
4847
4848	if (altroot != NULL)
4849		altroot_cmd(altroot, argc, argv);
4850
4851	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
4852	    NULL);
4853	if (status != DLADM_STATUS_OK)
4854		die("invalid link name '%s'", argv[optind]);
4855
4856	if ((flags & DLADM_OPT_ACTIVE) != 0) {
4857		do_etherstub_check(argv[optind], linkid, etherstub,
4858		    DLADM_OPT_ACTIVE);
4859	}
4860	if ((flags & DLADM_OPT_PERSIST) != 0) {
4861		do_etherstub_check(argv[optind], linkid, etherstub,
4862		    DLADM_OPT_PERSIST);
4863	}
4864
4865	status = dladm_vnic_delete(handle, linkid, flags);
4866	if (status != DLADM_STATUS_OK)
4867		die_dlerr(status, "vnic deletion failed");
4868}
4869
4870static void
4871do_delete_vnic(int argc, char *argv[], const char *use)
4872{
4873	do_delete_vnic_common(argc, argv, use, B_FALSE);
4874}
4875
4876/* ARGSUSED */
4877static void
4878do_up_vnic_common(int argc, char *argv[], const char *use, boolean_t vlan)
4879{
4880	datalink_id_t	linkid = DATALINK_ALL_LINKID;
4881	dladm_status_t	status;
4882	char		*type;
4883
4884	type = vlan ? "vlan" : "vnic";
4885
4886	/*
4887	 * get the id or the name of the vnic/vlan (optional last argument)
4888	 */
4889	if (argc == 2) {
4890		status = dladm_name2info(handle, argv[1], &linkid, NULL, NULL,
4891		    NULL);
4892		if (status != DLADM_STATUS_OK)
4893			goto done;
4894
4895	} else if (argc > 2) {
4896		usage();
4897	}
4898
4899	if (vlan)
4900		status = dladm_vlan_up(handle, linkid);
4901	else
4902		status = dladm_vnic_up(handle, linkid, 0);
4903
4904done:
4905	if (status != DLADM_STATUS_OK) {
4906		if (argc == 2) {
4907			die_dlerr(status,
4908			    "could not bring up %s '%s'", type, argv[1]);
4909		} else {
4910			die_dlerr(status, "could not bring %ss up", type);
4911		}
4912	}
4913}
4914
4915static void
4916do_up_vnic(int argc, char *argv[], const char *use)
4917{
4918	do_up_vnic_common(argc, argv, use, B_FALSE);
4919}
4920
4921static void
4922dump_vnics_head(const char *dev)
4923{
4924	if (strlen(dev))
4925		(void) printf("%s", dev);
4926
4927	(void) printf("\tipackets  rbytes      opackets  obytes          ");
4928
4929	if (strlen(dev))
4930		(void) printf("%%ipkts  %%opkts\n");
4931	else
4932		(void) printf("\n");
4933}
4934
4935static void
4936dump_vnic_stat(const char *name, datalink_id_t vnic_id,
4937    show_vnic_state_t *state, pktsum_t *vnic_stats, pktsum_t *tot_stats)
4938{
4939	pktsum_t	diff_stats;
4940	pktsum_t	*old_stats = &state->vs_prevstats[vnic_id];
4941
4942	dladm_stats_diff(&diff_stats, vnic_stats, old_stats);
4943
4944	(void) printf("%s", name);
4945
4946	(void) printf("\t%-10llu", diff_stats.ipackets);
4947	(void) printf("%-12llu", diff_stats.rbytes);
4948	(void) printf("%-10llu", diff_stats.opackets);
4949	(void) printf("%-12llu", diff_stats.obytes);
4950
4951	if (tot_stats) {
4952		if (tot_stats->ipackets == 0) {
4953			(void) printf("\t-");
4954		} else {
4955			(void) printf("\t%-6.1f", (double)diff_stats.ipackets/
4956			    (double)tot_stats->ipackets * 100);
4957		}
4958		if (tot_stats->opackets == 0) {
4959			(void) printf("\t-");
4960		} else {
4961			(void) printf("\t%-6.1f", (double)diff_stats.opackets/
4962			    (double)tot_stats->opackets * 100);
4963		}
4964	}
4965	(void) printf("\n");
4966
4967	*old_stats = *vnic_stats;
4968}
4969
4970/*
4971 * Called from the walker dladm_vnic_walk_sys() for each vnic to display
4972 * vnic information or statistics.
4973 */
4974static dladm_status_t
4975print_vnic(show_vnic_state_t *state, datalink_id_t linkid)
4976{
4977	dladm_vnic_attr_t	attr, *vnic = &attr;
4978	dladm_status_t		status;
4979	boolean_t		is_etherstub;
4980	char			devname[MAXLINKNAMELEN];
4981	char			vnic_name[MAXLINKNAMELEN];
4982	char			mstr[MAXMACADDRLEN * 3];
4983	vnic_fields_buf_t	vbuf;
4984
4985	if ((status = dladm_vnic_info(handle, linkid, vnic, state->vs_flags)) !=
4986	    DLADM_STATUS_OK)
4987		return (status);
4988
4989	is_etherstub = (vnic->va_link_id == DATALINK_INVALID_LINKID);
4990	if (state->vs_etherstub != is_etherstub) {
4991		/*
4992		 * Want all etherstub but it's not one, or want
4993		 * non-etherstub and it's one.
4994		 */
4995		return (DLADM_STATUS_OK);
4996	}
4997
4998	if (state->vs_link_id != DATALINK_ALL_LINKID) {
4999		if (state->vs_link_id != vnic->va_link_id)
5000			return (DLADM_STATUS_OK);
5001	}
5002
5003	if (dladm_datalink_id2info(handle, linkid, NULL, NULL,
5004	    NULL, vnic_name, sizeof (vnic_name)) != DLADM_STATUS_OK)
5005		return (DLADM_STATUS_BADARG);
5006
5007	bzero(devname, sizeof (devname));
5008	if (!is_etherstub &&
5009	    dladm_datalink_id2info(handle, vnic->va_link_id, NULL, NULL,
5010	    NULL, devname, sizeof (devname)) != DLADM_STATUS_OK)
5011		(void) sprintf(devname, "?");
5012
5013	state->vs_found = B_TRUE;
5014	if (state->vs_stats) {
5015		/* print vnic statistics */
5016		pktsum_t vnic_stats;
5017
5018		if (state->vs_firstonly) {
5019			if (state->vs_donefirst)
5020				return (0);
5021			state->vs_donefirst = B_TRUE;
5022		}
5023
5024		if (!state->vs_printstats) {
5025			/*
5026			 * get vnic statistics and add to the sum for the
5027			 * named device.
5028			 */
5029			get_link_stats(vnic_name, &vnic_stats);
5030			dladm_stats_total(&state->vs_totalstats, &vnic_stats,
5031			    &state->vs_prevstats[vnic->va_vnic_id]);
5032		} else {
5033			/* get and print vnic statistics */
5034			get_link_stats(vnic_name, &vnic_stats);
5035			dump_vnic_stat(vnic_name, linkid, state, &vnic_stats,
5036			    &state->vs_totalstats);
5037		}
5038		return (DLADM_STATUS_OK);
5039	} else {
5040		(void) snprintf(vbuf.vnic_link, sizeof (vbuf.vnic_link),
5041		    "%s", vnic_name);
5042
5043		if (!is_etherstub) {
5044
5045			(void) snprintf(vbuf.vnic_over, sizeof (vbuf.vnic_over),
5046			    "%s", devname);
5047			(void) snprintf(vbuf.vnic_speed,
5048			    sizeof (vbuf.vnic_speed), "%u",
5049			    (uint_t)((get_ifspeed(vnic_name, B_TRUE))
5050			    / 1000000ull));
5051
5052			switch (vnic->va_mac_addr_type) {
5053			case VNIC_MAC_ADDR_TYPE_FIXED:
5054			case VNIC_MAC_ADDR_TYPE_PRIMARY:
5055				(void) snprintf(vbuf.vnic_macaddrtype,
5056				    sizeof (vbuf.vnic_macaddrtype),
5057				    gettext("fixed"));
5058				break;
5059			case VNIC_MAC_ADDR_TYPE_RANDOM:
5060				(void) snprintf(vbuf.vnic_macaddrtype,
5061				    sizeof (vbuf.vnic_macaddrtype),
5062				    gettext("random"));
5063				break;
5064			case VNIC_MAC_ADDR_TYPE_FACTORY:
5065				(void) snprintf(vbuf.vnic_macaddrtype,
5066				    sizeof (vbuf.vnic_macaddrtype),
5067				    gettext("factory, slot %d"),
5068				    vnic->va_mac_slot);
5069				break;
5070			case VNIC_MAC_ADDR_TYPE_VRID:
5071				(void) snprintf(vbuf.vnic_macaddrtype,
5072				    sizeof (vbuf.vnic_macaddrtype),
5073				    gettext("vrrp, %d/%s"),
5074				    vnic->va_vrid, vnic->va_af == AF_INET ?
5075				    "inet" : "inet6");
5076				break;
5077			}
5078
5079			if (strlen(vbuf.vnic_macaddrtype) > 0) {
5080				(void) snprintf(vbuf.vnic_macaddr,
5081				    sizeof (vbuf.vnic_macaddr), "%s",
5082				    dladm_aggr_macaddr2str(vnic->va_mac_addr,
5083				    mstr));
5084			}
5085
5086			(void) snprintf(vbuf.vnic_vid, sizeof (vbuf.vnic_vid),
5087			    "%d", vnic->va_vid);
5088		}
5089
5090		ofmt_print(state->vs_ofmt, &vbuf);
5091
5092		return (DLADM_STATUS_OK);
5093	}
5094}
5095
5096/* ARGSUSED */
5097static int
5098show_vnic(dladm_handle_t dh, datalink_id_t linkid, void *arg)
5099{
5100	show_vnic_state_t	*state = arg;
5101
5102	state->vs_status = print_vnic(state, linkid);
5103	return (DLADM_WALK_CONTINUE);
5104}
5105
5106static void
5107do_show_vnic_common(int argc, char *argv[], const char *use,
5108    boolean_t etherstub)
5109{
5110	int			option;
5111	boolean_t		s_arg = B_FALSE;
5112	boolean_t		i_arg = B_FALSE;
5113	boolean_t		l_arg = B_FALSE;
5114	uint32_t		interval = 0, flags = DLADM_OPT_ACTIVE;
5115	datalink_id_t		linkid = DATALINK_ALL_LINKID;
5116	datalink_id_t		dev_linkid = DATALINK_ALL_LINKID;
5117	show_vnic_state_t	state;
5118	dladm_status_t		status;
5119	boolean_t		o_arg = B_FALSE;
5120	char			*fields_str = NULL;
5121	const ofmt_field_t	*pf;
5122	char			*all_e_fields = "link";
5123	ofmt_handle_t		ofmt;
5124	ofmt_status_t		oferr;
5125	uint_t			ofmtflags = 0;
5126
5127	bzero(&state, sizeof (state));
5128	opterr = 0;
5129	while ((option = getopt_long(argc, argv, ":pPl:si:o:", lopts,
5130	    NULL)) != -1) {
5131		switch (option) {
5132		case 'p':
5133			state.vs_parsable = B_TRUE;
5134			break;
5135		case 'P':
5136			flags = DLADM_OPT_PERSIST;
5137			break;
5138		case 'l':
5139			if (etherstub)
5140				die("option not supported for this command");
5141
5142			if (strlcpy(state.vs_link, optarg, MAXLINKNAMELEN) >=
5143			    MAXLINKNAMELEN)
5144				die("link name too long");
5145
5146			l_arg = B_TRUE;
5147			break;
5148		case 's':
5149			if (s_arg) {
5150				die("the option -s cannot be specified "
5151				    "more than once");
5152			}
5153			s_arg = B_TRUE;
5154			break;
5155		case 'i':
5156			if (i_arg) {
5157				die("the option -i cannot be specified "
5158				    "more than once");
5159			}
5160			i_arg = B_TRUE;
5161			if (!dladm_str2interval(optarg, &interval))
5162				die("invalid interval value '%s'", optarg);
5163			break;
5164		case 'o':
5165			o_arg = B_TRUE;
5166			fields_str = optarg;
5167			break;
5168		default:
5169			die_opterr(optopt, option, use);
5170		}
5171	}
5172
5173	if (i_arg && !s_arg)
5174		die("the option -i can be used only with -s");
5175
5176	/* get vnic ID (optional last argument) */
5177	if (optind == (argc - 1)) {
5178		status = dladm_name2info(handle, argv[optind], &linkid, NULL,
5179		    NULL, NULL);
5180		if (status != DLADM_STATUS_OK) {
5181			die_dlerr(status, "invalid vnic name '%s'",
5182			    argv[optind]);
5183		}
5184		(void) strlcpy(state.vs_vnic, argv[optind], MAXLINKNAMELEN);
5185	} else if (optind != argc) {
5186		usage();
5187	}
5188
5189	if (l_arg) {
5190		status = dladm_name2info(handle, state.vs_link, &dev_linkid,
5191		    NULL, NULL, NULL);
5192		if (status != DLADM_STATUS_OK) {
5193			die_dlerr(status, "invalid link name '%s'",
5194			    state.vs_link);
5195		}
5196	}
5197
5198	state.vs_vnic_id = linkid;
5199	state.vs_link_id = dev_linkid;
5200	state.vs_etherstub = etherstub;
5201	state.vs_found = B_FALSE;
5202	state.vs_flags = flags;
5203
5204	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
5205		if (etherstub)
5206			fields_str = all_e_fields;
5207	}
5208	pf = vnic_fields;
5209
5210	if (state.vs_parsable)
5211		ofmtflags |= OFMT_PARSABLE;
5212	oferr = ofmt_open(fields_str, pf, ofmtflags, 0, &ofmt);
5213	ofmt_check(oferr, state.vs_parsable, ofmt, die, warn);
5214	state.vs_ofmt = ofmt;
5215
5216	if (s_arg) {
5217		/* Display vnic statistics */
5218		vnic_stats(&state, interval);
5219		ofmt_close(ofmt);
5220		return;
5221	}
5222
5223	/* Display vnic information */
5224	state.vs_donefirst = B_FALSE;
5225
5226	if (linkid == DATALINK_ALL_LINKID) {
5227		(void) dladm_walk_datalink_id(show_vnic, handle, &state,
5228		    DATALINK_CLASS_VNIC | DATALINK_CLASS_ETHERSTUB,
5229		    DATALINK_ANY_MEDIATYPE, flags);
5230	} else {
5231		(void) show_vnic(handle, linkid, &state);
5232		if (state.vs_status != DLADM_STATUS_OK) {
5233			ofmt_close(ofmt);
5234			die_dlerr(state.vs_status, "failed to show vnic '%s'",
5235			    state.vs_vnic);
5236		}
5237	}
5238	ofmt_close(ofmt);
5239}
5240
5241static void
5242do_show_vnic(int argc, char *argv[], const char *use)
5243{
5244	do_show_vnic_common(argc, argv, use, B_FALSE);
5245}
5246
5247static void
5248do_create_etherstub(int argc, char *argv[], const char *use)
5249{
5250	uint32_t flags;
5251	char *altroot = NULL;
5252	int option;
5253	dladm_status_t status;
5254	char name[MAXLINKNAMELEN];
5255	uchar_t mac_addr[ETHERADDRL];
5256
5257	name[0] = '\0';
5258	bzero(mac_addr, sizeof (mac_addr));
5259	flags = DLADM_OPT_ANCHOR | DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5260
5261	opterr = 0;
5262	while ((option = getopt_long(argc, argv, "tR:",
5263	    etherstub_lopts, NULL)) != -1) {
5264		switch (option) {
5265		case 't':
5266			flags &= ~DLADM_OPT_PERSIST;
5267			break;
5268		case 'R':
5269			altroot = optarg;
5270			break;
5271		default:
5272			die_opterr(optopt, option, use);
5273		}
5274	}
5275
5276	/* the etherstub id is the required operand */
5277	if (optind != (argc - 1))
5278		usage();
5279
5280	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
5281		die("link name too long '%s'", argv[optind]);
5282
5283	if (!dladm_valid_linkname(name))
5284		die("invalid link name '%s'", argv[optind]);
5285
5286	if (altroot != NULL)
5287		altroot_cmd(altroot, argc, argv);
5288
5289	status = dladm_vnic_create(handle, name, DATALINK_INVALID_LINKID,
5290	    VNIC_MAC_ADDR_TYPE_AUTO, mac_addr, ETHERADDRL, NULL, 0, 0,
5291	    VRRP_VRID_NONE, AF_UNSPEC, NULL, NULL, flags);
5292	if (status != DLADM_STATUS_OK)
5293		die_dlerr(status, "etherstub creation failed");
5294}
5295
5296static void
5297do_delete_etherstub(int argc, char *argv[], const char *use)
5298{
5299	do_delete_vnic_common(argc, argv, use, B_TRUE);
5300}
5301
5302/* ARGSUSED */
5303static void
5304do_show_etherstub(int argc, char *argv[], const char *use)
5305{
5306	do_show_vnic_common(argc, argv, use, B_TRUE);
5307}
5308
5309/* ARGSUSED */
5310static void
5311do_up_simnet(int argc, char *argv[], const char *use)
5312{
5313	(void) dladm_simnet_up(handle, DATALINK_ALL_LINKID, 0);
5314}
5315
5316static void
5317do_create_simnet(int argc, char *argv[], const char *use)
5318{
5319	uint32_t flags;
5320	char *altroot = NULL;
5321	char *media = NULL;
5322	uint32_t mtype = DL_ETHER;
5323	int option;
5324	dladm_status_t status;
5325	char name[MAXLINKNAMELEN];
5326
5327	name[0] = '\0';
5328	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5329
5330	opterr = 0;
5331	while ((option = getopt_long(argc, argv, ":tR:m:",
5332	    simnet_lopts, NULL)) != -1) {
5333		switch (option) {
5334		case 't':
5335			flags &= ~DLADM_OPT_PERSIST;
5336			break;
5337		case 'R':
5338			altroot = optarg;
5339			break;
5340		case 'm':
5341			media = optarg;
5342			break;
5343		default:
5344			die_opterr(optopt, option, use);
5345		}
5346	}
5347
5348	/* the simnet id is the required operand */
5349	if (optind != (argc - 1))
5350		usage();
5351
5352	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
5353		die("link name too long '%s'", argv[optind]);
5354
5355	if (!dladm_valid_linkname(name))
5356		die("invalid link name '%s'", name);
5357
5358	if (media != NULL) {
5359		mtype = dladm_str2media(media);
5360		if (mtype != DL_ETHER && mtype != DL_WIFI)
5361			die("media type '%s' is not supported", media);
5362	}
5363
5364	if (altroot != NULL)
5365		altroot_cmd(altroot, argc, argv);
5366
5367	status = dladm_simnet_create(handle, name, mtype, flags);
5368	if (status != DLADM_STATUS_OK)
5369		die_dlerr(status, "simnet creation failed");
5370}
5371
5372static void
5373do_delete_simnet(int argc, char *argv[], const char *use)
5374{
5375	int option;
5376	uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5377	datalink_id_t linkid;
5378	char *altroot = NULL;
5379	dladm_status_t status;
5380	dladm_simnet_attr_t slinfo;
5381
5382	opterr = 0;
5383	while ((option = getopt_long(argc, argv, ":tR:", simnet_lopts,
5384	    NULL)) != -1) {
5385		switch (option) {
5386		case 't':
5387			flags &= ~DLADM_OPT_PERSIST;
5388			break;
5389		case 'R':
5390			altroot = optarg;
5391			break;
5392		default:
5393			die_opterr(optopt, option, use);
5394		}
5395	}
5396
5397	/* get simnet name (required last argument) */
5398	if (optind != (argc - 1))
5399		usage();
5400
5401	if (!dladm_valid_linkname(argv[optind]))
5402		die("invalid link name '%s'", argv[optind]);
5403
5404	if (altroot != NULL)
5405		altroot_cmd(altroot, argc, argv);
5406
5407	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
5408	    NULL);
5409	if (status != DLADM_STATUS_OK)
5410		die("simnet '%s' not found", argv[optind]);
5411
5412	if ((status = dladm_simnet_info(handle, linkid, &slinfo,
5413	    flags)) != DLADM_STATUS_OK)
5414		die_dlerr(status, "failed to retrieve simnet information");
5415
5416	status = dladm_simnet_delete(handle, linkid, flags);
5417	if (status != DLADM_STATUS_OK)
5418		die_dlerr(status, "simnet deletion failed");
5419}
5420
5421static void
5422do_modify_simnet(int argc, char *argv[], const char *use)
5423{
5424	int option;
5425	uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
5426	datalink_id_t linkid;
5427	datalink_id_t peer_linkid;
5428	char *altroot = NULL;
5429	dladm_status_t status;
5430	boolean_t p_arg = B_FALSE;
5431
5432	opterr = 0;
5433	while ((option = getopt_long(argc, argv, ":tR:p:", simnet_lopts,
5434	    NULL)) != -1) {
5435		switch (option) {
5436		case 't':
5437			flags &= ~DLADM_OPT_PERSIST;
5438			break;
5439		case 'R':
5440			altroot = optarg;
5441			break;
5442		case 'p':
5443			if (p_arg)
5444				die_optdup(option);
5445			p_arg = B_TRUE;
5446			if (strcasecmp(optarg, "none") == 0)
5447				peer_linkid = DATALINK_INVALID_LINKID;
5448			else if (dladm_name2info(handle, optarg, &peer_linkid,
5449			    NULL, NULL, NULL) != DLADM_STATUS_OK)
5450				die("invalid peer link name '%s'", optarg);
5451			break;
5452		default:
5453			die_opterr(optopt, option, use);
5454		}
5455	}
5456
5457	/* get simnet name (required last argument) */
5458	if (optind != (argc - 1))
5459		usage();
5460
5461	/* Nothing to do if no peer link argument */
5462	if (!p_arg)
5463		return;
5464
5465	if (altroot != NULL)
5466		altroot_cmd(altroot, argc, argv);
5467
5468	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
5469	    NULL);
5470	if (status != DLADM_STATUS_OK)
5471		die("invalid link name '%s'", argv[optind]);
5472
5473	status = dladm_simnet_modify(handle, linkid, peer_linkid, flags);
5474	if (status != DLADM_STATUS_OK)
5475		die_dlerr(status, "simnet modification failed");
5476}
5477
5478static dladm_status_t
5479print_simnet(show_state_t *state, datalink_id_t linkid)
5480{
5481	dladm_simnet_attr_t	slinfo;
5482	uint32_t		flags;
5483	dladm_status_t		status;
5484	simnet_fields_buf_t	slbuf;
5485	char			mstr[ETHERADDRL * 3];
5486
5487	bzero(&slbuf, sizeof (slbuf));
5488	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, NULL,
5489	    slbuf.simnet_name, sizeof (slbuf.simnet_name)))
5490	    != DLADM_STATUS_OK)
5491		return (status);
5492
5493	if (!(state->ls_flags & flags))
5494		return (DLADM_STATUS_NOTFOUND);
5495
5496	if ((status = dladm_simnet_info(handle, linkid, &slinfo,
5497	    state->ls_flags)) != DLADM_STATUS_OK)
5498		return (status);
5499
5500	if (slinfo.sna_peer_link_id != DATALINK_INVALID_LINKID &&
5501	    (status = dladm_datalink_id2info(handle, slinfo.sna_peer_link_id,
5502	    NULL, NULL, NULL, slbuf.simnet_otherlink,
5503	    sizeof (slbuf.simnet_otherlink))) !=
5504	    DLADM_STATUS_OK)
5505		return (status);
5506
5507	if (slinfo.sna_mac_len > sizeof (slbuf.simnet_macaddr))
5508		return (DLADM_STATUS_BADVAL);
5509
5510	(void) strlcpy(slbuf.simnet_macaddr,
5511	    dladm_aggr_macaddr2str(slinfo.sna_mac_addr, mstr),
5512	    sizeof (slbuf.simnet_macaddr));
5513	(void) dladm_media2str(slinfo.sna_type, slbuf.simnet_media);
5514
5515	ofmt_print(state->ls_ofmt, &slbuf);
5516	return (status);
5517}
5518
5519/* ARGSUSED */
5520static int
5521show_simnet(dladm_handle_t dh, datalink_id_t linkid, void *arg)
5522{
5523	show_state_t		*state = arg;
5524
5525	state->ls_status = print_simnet(state, linkid);
5526	return (DLADM_WALK_CONTINUE);
5527}
5528
5529static void
5530do_show_simnet(int argc, char *argv[], const char *use)
5531{
5532	int		option;
5533	uint32_t	flags = DLADM_OPT_ACTIVE;
5534	boolean_t	p_arg = B_FALSE;
5535	datalink_id_t	linkid = DATALINK_ALL_LINKID;
5536	show_state_t	state;
5537	dladm_status_t	status;
5538	boolean_t	o_arg = B_FALSE;
5539	ofmt_handle_t	ofmt;
5540	ofmt_status_t	oferr;
5541	char		*all_fields = "link,media,macaddress,otherlink";
5542	char		*fields_str = all_fields;
5543	uint_t		ofmtflags = 0;
5544
5545	bzero(&state, sizeof (state));
5546
5547	opterr = 0;
5548	while ((option = getopt_long(argc, argv, ":pPo:",
5549	    show_lopts, NULL)) != -1) {
5550		switch (option) {
5551		case 'p':
5552			if (p_arg)
5553				die_optdup(option);
5554
5555			p_arg = B_TRUE;
5556			state.ls_parsable = p_arg;
5557			break;
5558		case 'P':
5559			if (flags != DLADM_OPT_ACTIVE)
5560				die_optdup(option);
5561
5562			flags = DLADM_OPT_PERSIST;
5563			break;
5564		case 'o':
5565			o_arg = B_TRUE;
5566			fields_str = optarg;
5567			break;
5568		default:
5569			die_opterr(optopt, option, use);
5570			break;
5571		}
5572	}
5573
5574	if (p_arg && !o_arg)
5575		die("-p requires -o");
5576
5577	if (strcasecmp(fields_str, "all") == 0) {
5578		if (p_arg)
5579			die("\"-o all\" is invalid with -p");
5580		fields_str = all_fields;
5581	}
5582
5583	/* get link name (optional last argument) */
5584	if (optind == (argc-1)) {
5585		if ((status = dladm_name2info(handle, argv[optind], &linkid,
5586		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
5587			die_dlerr(status, "link %s is not valid", argv[optind]);
5588		}
5589	} else if (optind != argc) {
5590		usage();
5591	}
5592
5593	state.ls_flags = flags;
5594	state.ls_donefirst = B_FALSE;
5595	if (state.ls_parsable)
5596		ofmtflags |= OFMT_PARSABLE;
5597	oferr = ofmt_open(fields_str, simnet_fields, ofmtflags, 0, &ofmt);
5598	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
5599	state.ls_ofmt = ofmt;
5600
5601	if (linkid == DATALINK_ALL_LINKID) {
5602		(void) dladm_walk_datalink_id(show_simnet, handle, &state,
5603		    DATALINK_CLASS_SIMNET, DATALINK_ANY_MEDIATYPE, flags);
5604	} else {
5605		(void) show_simnet(handle, linkid, &state);
5606		if (state.ls_status != DLADM_STATUS_OK) {
5607			ofmt_close(ofmt);
5608			die_dlerr(state.ls_status, "failed to show simnet %s",
5609			    argv[optind]);
5610		}
5611	}
5612	ofmt_close(ofmt);
5613}
5614
5615static void
5616link_stats(datalink_id_t linkid, uint_t interval, char *fields_str,
5617    show_state_t *state)
5618{
5619	ofmt_handle_t	ofmt;
5620	ofmt_status_t	oferr;
5621	uint_t		ofmtflags = 0;
5622
5623	if (state->ls_parsable)
5624		ofmtflags |= OFMT_PARSABLE;
5625	oferr = ofmt_open(fields_str, link_s_fields, ofmtflags, 0, &ofmt);
5626	ofmt_check(oferr, state->ls_parsable, ofmt, die, warn);
5627	state->ls_ofmt = ofmt;
5628
5629	/*
5630	 * If an interval is specified, continuously show the stats
5631	 * only for the first MAC port.
5632	 */
5633	state->ls_firstonly = (interval != 0);
5634
5635	for (;;) {
5636		state->ls_donefirst = B_FALSE;
5637		if (linkid == DATALINK_ALL_LINKID) {
5638			(void) dladm_walk_datalink_id(show_link_stats, handle,
5639			    state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
5640			    DLADM_OPT_ACTIVE);
5641		} else {
5642			(void) show_link_stats(handle, linkid, state);
5643		}
5644
5645		if (interval == 0)
5646			break;
5647
5648		(void) fflush(stdout);
5649		(void) sleep(interval);
5650	}
5651	ofmt_close(ofmt);
5652}
5653
5654static void
5655aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval)
5656{
5657	/*
5658	 * If an interval is specified, continuously show the stats
5659	 * only for the first group.
5660	 */
5661	state->gs_firstonly = (interval != 0);
5662
5663	for (;;) {
5664		state->gs_donefirst = B_FALSE;
5665		if (linkid == DATALINK_ALL_LINKID)
5666			(void) dladm_walk_datalink_id(show_aggr, handle, state,
5667			    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
5668			    DLADM_OPT_ACTIVE);
5669		else
5670			(void) show_aggr(handle, linkid, state);
5671
5672		if (interval == 0)
5673			break;
5674
5675		(void) fflush(stdout);
5676		(void) sleep(interval);
5677	}
5678}
5679
5680/* ARGSUSED */
5681static void
5682vnic_stats(show_vnic_state_t *sp, uint32_t interval)
5683{
5684	show_vnic_state_t	state;
5685	boolean_t		specific_link, specific_dev;
5686
5687	/* Display vnic statistics */
5688	dump_vnics_head(sp->vs_link);
5689
5690	bzero(&state, sizeof (state));
5691	state.vs_stats = B_TRUE;
5692	state.vs_vnic_id = sp->vs_vnic_id;
5693	state.vs_link_id = sp->vs_link_id;
5694
5695	/*
5696	 * If an interval is specified, and a vnic ID is not specified,
5697	 * continuously show the stats only for the first vnic.
5698	 */
5699	specific_link = (sp->vs_vnic_id != DATALINK_ALL_LINKID);
5700	specific_dev = (sp->vs_link_id != DATALINK_ALL_LINKID);
5701
5702	for (;;) {
5703		/* Get stats for each vnic */
5704		state.vs_found = B_FALSE;
5705		state.vs_donefirst = B_FALSE;
5706		state.vs_printstats = B_FALSE;
5707		state.vs_flags = DLADM_OPT_ACTIVE;
5708
5709		if (!specific_link) {
5710			(void) dladm_walk_datalink_id(show_vnic, handle, &state,
5711			    DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE,
5712			    DLADM_OPT_ACTIVE);
5713		} else {
5714			(void) show_vnic(handle, sp->vs_vnic_id, &state);
5715			if (state.vs_status != DLADM_STATUS_OK) {
5716				die_dlerr(state.vs_status,
5717				    "failed to show vnic '%s'", sp->vs_vnic);
5718			}
5719		}
5720
5721		if (specific_link && !state.vs_found)
5722			die("non-existent vnic '%s'", sp->vs_vnic);
5723		if (specific_dev && !state.vs_found)
5724			die("device %s has no vnics", sp->vs_link);
5725
5726		/* Show totals */
5727		if ((specific_link | specific_dev) && !interval) {
5728			(void) printf("Total");
5729			(void) printf("\t%-10llu",
5730			    state.vs_totalstats.ipackets);
5731			(void) printf("%-12llu",
5732			    state.vs_totalstats.rbytes);
5733			(void) printf("%-10llu",
5734			    state.vs_totalstats.opackets);
5735			(void) printf("%-12llu\n",
5736			    state.vs_totalstats.obytes);
5737		}
5738
5739		/* Show stats for each vnic */
5740		state.vs_donefirst = B_FALSE;
5741		state.vs_printstats = B_TRUE;
5742
5743		if (!specific_link) {
5744			(void) dladm_walk_datalink_id(show_vnic, handle, &state,
5745			    DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE,
5746			    DLADM_OPT_ACTIVE);
5747		} else {
5748			(void) show_vnic(handle, sp->vs_vnic_id, &state);
5749			if (state.vs_status != DLADM_STATUS_OK) {
5750				die_dlerr(state.vs_status,
5751				    "failed to show vnic '%s'", sp->vs_vnic);
5752			}
5753		}
5754
5755		if (interval == 0)
5756			break;
5757
5758		(void) fflush(stdout);
5759		(void) sleep(interval);
5760	}
5761}
5762
5763static void
5764get_mac_stats(const char *dev, pktsum_t *stats)
5765{
5766	kstat_ctl_t	*kcp;
5767	kstat_t		*ksp;
5768	char module[DLPI_LINKNAME_MAX];
5769	uint_t instance;
5770
5771
5772	bzero(stats, sizeof (*stats));
5773
5774	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
5775		return;
5776
5777	if ((kcp = kstat_open()) == NULL) {
5778		warn("kstat open operation failed");
5779		return;
5780	}
5781
5782	ksp = dladm_kstat_lookup(kcp, module, instance, "mac", NULL);
5783	if (ksp != NULL)
5784		dladm_get_stats(kcp, ksp, stats);
5785
5786	(void) kstat_close(kcp);
5787
5788}
5789
5790static void
5791get_link_stats(const char *link, pktsum_t *stats)
5792{
5793	kstat_ctl_t	*kcp;
5794	kstat_t		*ksp;
5795
5796	bzero(stats, sizeof (*stats));
5797
5798	if ((kcp = kstat_open()) == NULL) {
5799		warn("kstat_open operation failed");
5800		return;
5801	}
5802
5803	ksp = dladm_kstat_lookup(kcp, "link", 0, link, NULL);
5804
5805	if (ksp != NULL)
5806		dladm_get_stats(kcp, ksp, stats);
5807
5808	(void) kstat_close(kcp);
5809}
5810
5811static int
5812query_kstat(char *module, int instance, const char *name, const char *stat,
5813    uint8_t type, void *val)
5814{
5815	kstat_ctl_t	*kcp;
5816	kstat_t		*ksp;
5817
5818	if ((kcp = kstat_open()) == NULL) {
5819		warn("kstat open operation failed");
5820		return (-1);
5821	}
5822
5823	if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
5824		/*
5825		 * The kstat query could fail if the underlying MAC
5826		 * driver was already detached.
5827		 */
5828		goto bail;
5829	}
5830
5831	if (kstat_read(kcp, ksp, NULL) == -1) {
5832		warn("kstat read failed");
5833		goto bail;
5834	}
5835
5836	if (dladm_kstat_value(ksp, stat, type, val) < 0)
5837		goto bail;
5838
5839	(void) kstat_close(kcp);
5840	return (0);
5841
5842bail:
5843	(void) kstat_close(kcp);
5844	return (-1);
5845}
5846
5847static int
5848get_one_kstat(const char *name, const char *stat, uint8_t type,
5849    void *val, boolean_t islink)
5850{
5851	char		module[DLPI_LINKNAME_MAX];
5852	uint_t		instance;
5853
5854	if (islink) {
5855		return (query_kstat("link", 0, name, stat, type, val));
5856	} else {
5857		if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
5858			return (-1);
5859
5860		return (query_kstat(module, instance, "mac", stat, type, val));
5861	}
5862}
5863
5864static uint64_t
5865get_ifspeed(const char *name, boolean_t islink)
5866{
5867	uint64_t ifspeed = 0;
5868
5869	(void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64,
5870	    &ifspeed, islink);
5871
5872	return (ifspeed);