xref: /titanic_53/usr/src/cmd/dladm/dladm.c (revision c7e4935f5b755b4bbeaec416f1ad24337aeac7a4)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5219a2a31Shl157128  * Common Development and Distribution License (the "License").
6219a2a31Shl157128  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22f4b3ec61Sdh155122  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <stdio.h>
290ba2cbe9Sxc151355 #include <ctype.h>
307c478bd9Sstevel@tonic-gate #include <locale.h>
310ba2cbe9Sxc151355 #include <signal.h>
327c478bd9Sstevel@tonic-gate #include <stdarg.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <fcntl.h>
357c478bd9Sstevel@tonic-gate #include <string.h>
367c478bd9Sstevel@tonic-gate #include <stropts.h>
377c478bd9Sstevel@tonic-gate #include <errno.h>
387c478bd9Sstevel@tonic-gate #include <kstat.h>
397c478bd9Sstevel@tonic-gate #include <strings.h>
407c478bd9Sstevel@tonic-gate #include <getopt.h>
417c478bd9Sstevel@tonic-gate #include <unistd.h>
42cd93090eSericheng #include <priv.h>
430ba2cbe9Sxc151355 #include <termios.h>
440ba2cbe9Sxc151355 #include <pwd.h>
450ba2cbe9Sxc151355 #include <auth_attr.h>
460ba2cbe9Sxc151355 #include <auth_list.h>
477c478bd9Sstevel@tonic-gate #include <libintl.h>
487c478bd9Sstevel@tonic-gate #include <libdlpi.h>
497c478bd9Sstevel@tonic-gate #include <libdladm.h>
507c478bd9Sstevel@tonic-gate #include <liblaadm.h>
517c478bd9Sstevel@tonic-gate #include <libmacadm.h>
520ba2cbe9Sxc151355 #include <libwladm.h>
530ba2cbe9Sxc151355 #include <libinetutil.h>
540ba2cbe9Sxc151355 #include <bsm/adt.h>
550ba2cbe9Sxc151355 #include <bsm/adt_event.h>
567c478bd9Sstevel@tonic-gate 
57ba2e4443Sseb #define	AGGR_DRV	"aggr"
587c478bd9Sstevel@tonic-gate #define	MAXPORT		256
597c478bd9Sstevel@tonic-gate #define	DUMP_LACP_FORMAT	"    %-9s %-8s %-7s %-12s "	\
607c478bd9Sstevel@tonic-gate 	"%-5s %-4s %-4s %-9s %-7s\n"
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate typedef struct pktsum_s {
637c478bd9Sstevel@tonic-gate 	uint64_t	ipackets;
647c478bd9Sstevel@tonic-gate 	uint64_t	opackets;
657c478bd9Sstevel@tonic-gate 	uint64_t	rbytes;
667c478bd9Sstevel@tonic-gate 	uint64_t	obytes;
677c478bd9Sstevel@tonic-gate 	uint32_t	ierrors;
687c478bd9Sstevel@tonic-gate 	uint32_t	oerrors;
697c478bd9Sstevel@tonic-gate } pktsum_t;
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate typedef struct show_link_state {
727c478bd9Sstevel@tonic-gate 	boolean_t	ls_firstonly;
737c478bd9Sstevel@tonic-gate 	boolean_t	ls_donefirst;
747c478bd9Sstevel@tonic-gate 	boolean_t	ls_stats;
757c478bd9Sstevel@tonic-gate 	pktsum_t	ls_prevstats;
767c478bd9Sstevel@tonic-gate 	boolean_t	ls_parseable;
777c478bd9Sstevel@tonic-gate } show_link_state_t;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate typedef struct show_grp_state {
807c478bd9Sstevel@tonic-gate 	uint32_t	gs_key;
817c478bd9Sstevel@tonic-gate 	boolean_t	gs_lacp;
827c478bd9Sstevel@tonic-gate 	boolean_t	gs_found;
837c478bd9Sstevel@tonic-gate 	boolean_t	gs_stats;
847c478bd9Sstevel@tonic-gate 	boolean_t	gs_firstonly;
857c478bd9Sstevel@tonic-gate 	pktsum_t	gs_prevstats[MAXPORT];
867c478bd9Sstevel@tonic-gate 	boolean_t	gs_parseable;
877c478bd9Sstevel@tonic-gate } show_grp_state_t;
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate typedef struct show_mac_state {
907c478bd9Sstevel@tonic-gate 	boolean_t	ms_firstonly;
917c478bd9Sstevel@tonic-gate 	boolean_t	ms_donefirst;
927c478bd9Sstevel@tonic-gate 	pktsum_t	ms_prevstats;
937c478bd9Sstevel@tonic-gate 	boolean_t	ms_parseable;
947c478bd9Sstevel@tonic-gate } show_mac_state_t;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate typedef struct port_state {
977c478bd9Sstevel@tonic-gate 	char			*state_name;
987c478bd9Sstevel@tonic-gate 	aggr_port_state_t	state_num;
997c478bd9Sstevel@tonic-gate } port_state_t;
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate static port_state_t port_states[] = {
1027c478bd9Sstevel@tonic-gate 	{"standby", AGGR_PORT_STATE_STANDBY },
1037c478bd9Sstevel@tonic-gate 	{"attached", AGGR_PORT_STATE_ATTACHED }
1047c478bd9Sstevel@tonic-gate };
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate #define	NPORTSTATES	(sizeof (port_states) / sizeof (port_state_t))
1077c478bd9Sstevel@tonic-gate 
1080ba2cbe9Sxc151355 typedef	void cmdfunc_t(int, char **);
1090ba2cbe9Sxc151355 
1100ba2cbe9Sxc151355 static cmdfunc_t do_show_link, do_show_dev, do_show_wifi;
1110ba2cbe9Sxc151355 static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
1120ba2cbe9Sxc151355 static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr, do_down_aggr;
1130ba2cbe9Sxc151355 static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
1140ba2cbe9Sxc151355 static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
1150ba2cbe9Sxc151355 static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
1160ba2cbe9Sxc151355 static cmdfunc_t do_init_linkprop, do_init_secobj;
1177c478bd9Sstevel@tonic-gate 
118f4b3ec61Sdh155122 static void	show_linkprop_onelink(void *, const char *);
119f4b3ec61Sdh155122 
12033343a97Smeem static void	link_stats(const char *, uint_t);
12133343a97Smeem static void	aggr_stats(uint32_t, uint_t);
1227c478bd9Sstevel@tonic-gate static void	dev_stats(const char *dev, uint32_t);
1237c478bd9Sstevel@tonic-gate 
124ba2e4443Sseb static void	get_mac_stats(const char *, pktsum_t *);
1257c478bd9Sstevel@tonic-gate static void	get_link_stats(const char *, pktsum_t *);
126ba2e4443Sseb static uint64_t	mac_ifspeed(const char *);
127ba2e4443Sseb static char	*mac_link_state(const char *);
128ba2e4443Sseb static char	*mac_link_duplex(const char *);
1297c478bd9Sstevel@tonic-gate static void	stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
1307c478bd9Sstevel@tonic-gate static void	stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
1317c478bd9Sstevel@tonic-gate 
13233343a97Smeem static boolean_t str2int(const char *, int *);
13333343a97Smeem static void	die(const char *, ...);
13433343a97Smeem static void	die_optdup(int);
13533343a97Smeem static void	die_opterr(int, int);
13633343a97Smeem static void	die_laerr(laadm_diag_t, const char *, ...);
13733343a97Smeem static void	die_wlerr(wladm_status_t, const char *, ...);
13833343a97Smeem static void	die_dlerr(dladm_status_t, const char *, ...);
13933343a97Smeem static void	warn(const char *, ...);
14033343a97Smeem static void	warn_wlerr(wladm_status_t, const char *, ...);
14133343a97Smeem static void	warn_dlerr(dladm_status_t, const char *, ...);
14233343a97Smeem 
1437c478bd9Sstevel@tonic-gate typedef struct	cmd {
1447c478bd9Sstevel@tonic-gate 	char		*c_name;
1450ba2cbe9Sxc151355 	cmdfunc_t	*c_fn;
1467c478bd9Sstevel@tonic-gate } cmd_t;
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate static cmd_t	cmds[] = {
1497c478bd9Sstevel@tonic-gate 	{ "show-link",		do_show_link		},
150210db224Sericheng 	{ "show-dev",		do_show_dev		},
1517c478bd9Sstevel@tonic-gate 	{ "create-aggr",	do_create_aggr		},
1527c478bd9Sstevel@tonic-gate 	{ "delete-aggr",	do_delete_aggr		},
1537c478bd9Sstevel@tonic-gate 	{ "add-aggr",		do_add_aggr		},
1547c478bd9Sstevel@tonic-gate 	{ "remove-aggr",	do_remove_aggr		},
1557c478bd9Sstevel@tonic-gate 	{ "modify-aggr",	do_modify_aggr		},
1567c478bd9Sstevel@tonic-gate 	{ "show-aggr",		do_show_aggr		},
1577c478bd9Sstevel@tonic-gate 	{ "up-aggr",		do_up_aggr		},
1580ba2cbe9Sxc151355 	{ "down-aggr",		do_down_aggr		},
1590ba2cbe9Sxc151355 	{ "scan-wifi",		do_scan_wifi		},
1600ba2cbe9Sxc151355 	{ "connect-wifi",	do_connect_wifi		},
1610ba2cbe9Sxc151355 	{ "disconnect-wifi",	do_disconnect_wifi	},
1620ba2cbe9Sxc151355 	{ "show-wifi",		do_show_wifi		},
1630ba2cbe9Sxc151355 	{ "show-linkprop",	do_show_linkprop	},
1640ba2cbe9Sxc151355 	{ "set-linkprop",	do_set_linkprop		},
1650ba2cbe9Sxc151355 	{ "reset-linkprop",	do_reset_linkprop	},
1660ba2cbe9Sxc151355 	{ "create-secobj",	do_create_secobj	},
1670ba2cbe9Sxc151355 	{ "delete-secobj",	do_delete_secobj	},
1680ba2cbe9Sxc151355 	{ "show-secobj",	do_show_secobj		},
1690ba2cbe9Sxc151355 	{ "init-linkprop",	do_init_linkprop	},
1700ba2cbe9Sxc151355 	{ "init-secobj",	do_init_secobj		}
1717c478bd9Sstevel@tonic-gate };
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate static const struct option longopts[] = {
1747c478bd9Sstevel@tonic-gate 	{"vlan-id",	required_argument,	0, 'v'	},
1757c478bd9Sstevel@tonic-gate 	{"dev",		required_argument,	0, 'd'	},
1767c478bd9Sstevel@tonic-gate 	{"policy",	required_argument,	0, 'P'	},
1777c478bd9Sstevel@tonic-gate 	{"lacp-mode",	required_argument,	0, 'l'	},
1787c478bd9Sstevel@tonic-gate 	{"lacp-timer",	required_argument,	0, 'T'	},
1797c478bd9Sstevel@tonic-gate 	{"unicast",	required_argument,	0, 'u'	},
1807c478bd9Sstevel@tonic-gate 	{"statistics",	no_argument,		0, 's'	},
1817c478bd9Sstevel@tonic-gate 	{"interval",	required_argument,	0, 'i'	},
1827c478bd9Sstevel@tonic-gate 	{"lacp",	no_argument,		0, 'L'	},
1837c478bd9Sstevel@tonic-gate 	{"temporary",	no_argument,		0, 't'	},
1847c478bd9Sstevel@tonic-gate 	{"root-dir",	required_argument,	0, 'r'	},
1857c478bd9Sstevel@tonic-gate 	{"parseable",	no_argument,		0, 'p'	},
1867c478bd9Sstevel@tonic-gate 	{ 0, 0, 0, 0 }
1877c478bd9Sstevel@tonic-gate };
1887c478bd9Sstevel@tonic-gate 
1890ba2cbe9Sxc151355 static const struct option prop_longopts[] = {
1900ba2cbe9Sxc151355 	{"temporary",	no_argument,		0, 't'	},
1910ba2cbe9Sxc151355 	{"root-dir",	required_argument,	0, 'R'	},
1920ba2cbe9Sxc151355 	{"prop",	required_argument,	0, 'p'	},
1930ba2cbe9Sxc151355 	{"parseable",	no_argument,		0, 'c'	},
1940ba2cbe9Sxc151355 	{"persistent",	no_argument,		0, 'P'	},
1950ba2cbe9Sxc151355 	{ 0, 0, 0, 0 }
1960ba2cbe9Sxc151355 };
1970ba2cbe9Sxc151355 
1980ba2cbe9Sxc151355 static const struct option wifi_longopts[] = {
1990ba2cbe9Sxc151355 	{"parseable",	no_argument,		0, 'p'	},
2000ba2cbe9Sxc151355 	{"output",	required_argument,	0, 'o'	},
2010ba2cbe9Sxc151355 	{"essid",	required_argument,	0, 'e'	},
2020ba2cbe9Sxc151355 	{"bsstype",	required_argument,	0, 'b'	},
2030ba2cbe9Sxc151355 	{"mode",	required_argument,	0, 'm'	},
2040ba2cbe9Sxc151355 	{"key",		required_argument,	0, 'k'	},
2050ba2cbe9Sxc151355 	{"sec",		required_argument,	0, 's'	},
2060ba2cbe9Sxc151355 	{"auth",	required_argument,	0, 'a'	},
2070ba2cbe9Sxc151355 	{"create-ibss",	required_argument,	0, 'c'	},
2080ba2cbe9Sxc151355 	{"timeout",	required_argument,	0, 'T'	},
2090ba2cbe9Sxc151355 	{"all-links",	no_argument,		0, 'a'	},
2100ba2cbe9Sxc151355 	{"temporary",	no_argument,		0, 't'	},
2110ba2cbe9Sxc151355 	{"root-dir",	required_argument,	0, 'R'	},
2120ba2cbe9Sxc151355 	{"persistent",	no_argument,		0, 'P'	},
2130ba2cbe9Sxc151355 	{"file",	required_argument,	0, 'f'	},
2140ba2cbe9Sxc151355 	{ 0, 0, 0, 0 }
2150ba2cbe9Sxc151355 };
2160ba2cbe9Sxc151355 
2177c478bd9Sstevel@tonic-gate static char *progname;
2180ba2cbe9Sxc151355 static sig_atomic_t signalled;
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate static void
2217c478bd9Sstevel@tonic-gate usage(void)
2227c478bd9Sstevel@tonic-gate {
2230ba2cbe9Sxc151355 	(void) fprintf(stderr, gettext("usage:	dladm <subcommand> <args> ...\n"
2240ba2cbe9Sxc151355 	    "\tshow-link       [-p] [-s [-i <interval>]] [<name>]\n"
2250ba2cbe9Sxc151355 	    "\tshow-dev        [-p] [-s [-i <interval>]] [<dev>]\n"
2260ba2cbe9Sxc151355 	    "\n"
2270ba2cbe9Sxc151355 	    "\tcreate-aggr     [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n"
2280ba2cbe9Sxc151355 	    "\t                [-T <time>] [-u <address>] -d <dev> ... <key>\n"
2290ba2cbe9Sxc151355 	    "\tmodify-aggr     [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n"
2300ba2cbe9Sxc151355 	    "\t                [-T <time>] [-u <address>] <key>\n"
2310ba2cbe9Sxc151355 	    "\tdelete-aggr     [-t] [-R <root-dir>] <key>\n"
2320ba2cbe9Sxc151355 	    "\tadd-aggr        [-t] [-R <root-dir>] -d <dev> ... <key>\n"
2330ba2cbe9Sxc151355 	    "\tremove-aggr     [-t] [-R <root-dir>] -d <dev> ... <key>\n"
2340ba2cbe9Sxc151355 	    "\tshow-aggr       [-pL][-s [-i <interval>]] [<key>]\n"
2350ba2cbe9Sxc151355 	    "\n"
2360ba2cbe9Sxc151355 	    "\tscan-wifi       [-p] [-o <field>,...] [<name>]\n"
2370ba2cbe9Sxc151355 	    "\tconnect-wifi    [-e <essid>] [-i <bssid>] [-k <key>,...]"
2380ba2cbe9Sxc151355 	    " [-s wep]\n"
2390ba2cbe9Sxc151355 	    "\t                [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
2400ba2cbe9Sxc151355 	    "\t                [-T <time>] [<name>]\n"
2410ba2cbe9Sxc151355 	    "\tdisconnect-wifi [-a] [<name>]\n"
2420ba2cbe9Sxc151355 	    "\tshow-wifi       [-p] [-o <field>,...] [<name>]\n"
2430ba2cbe9Sxc151355 	    "\n"
2440ba2cbe9Sxc151355 	    "\tset-linkprop    [-t] [-R <root-dir>]  -p <prop>=<value>[,...]"
2450ba2cbe9Sxc151355 	    " <name>\n"
2460ba2cbe9Sxc151355 	    "\treset-linkprop  [-t] [-R <root-dir>] [-p <prop>,...] <name>\n"
2470ba2cbe9Sxc151355 	    "\tshow-linkprop   [-cP][-p <prop>,...] <name>\n"
2480ba2cbe9Sxc151355 	    "\n"
2490ba2cbe9Sxc151355 	    "\tcreate-secobj   [-t] [-R <root-dir>] [-f <file>] -c <class>"
2500ba2cbe9Sxc151355 	    " <secobj>\n"
2510ba2cbe9Sxc151355 	    "\tdelete-secobj   [-t] [-R <root-dir>] <secobj>[,...]\n"
2520ba2cbe9Sxc151355 	    "\tshow-secobj     [-pP][<secobj>,...]\n"));
2537c478bd9Sstevel@tonic-gate 	exit(1);
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate int
2577c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate 	int	i;
2607c478bd9Sstevel@tonic-gate 	cmd_t	*cmdp;
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
2637c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
2647c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
2657c478bd9Sstevel@tonic-gate #endif
2667c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	progname = argv[0];
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 	if (argc < 2)
2717c478bd9Sstevel@tonic-gate 		usage();
2727c478bd9Sstevel@tonic-gate 
273cd93090eSericheng 	if (!priv_ineffect(PRIV_SYS_NET_CONFIG) ||
27433343a97Smeem 	    !priv_ineffect(PRIV_NET_RAWACCESS))
27533343a97Smeem 		die("insufficient privileges");
276cd93090eSericheng 
2777c478bd9Sstevel@tonic-gate 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
2787c478bd9Sstevel@tonic-gate 		cmdp = &cmds[i];
2797c478bd9Sstevel@tonic-gate 		if (strcmp(argv[1], cmdp->c_name) == 0) {
2807c478bd9Sstevel@tonic-gate 			cmdp->c_fn(argc - 1, &argv[1]);
2817c478bd9Sstevel@tonic-gate 			exit(0);
2827c478bd9Sstevel@tonic-gate 		}
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
2867c478bd9Sstevel@tonic-gate 	    progname, argv[1]);
2877c478bd9Sstevel@tonic-gate 	usage();
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	return (0);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate static void
2937c478bd9Sstevel@tonic-gate do_create_aggr(int argc, char *argv[])
2947c478bd9Sstevel@tonic-gate {
2957c478bd9Sstevel@tonic-gate 	char			option;
29633343a97Smeem 	int			key;
2977c478bd9Sstevel@tonic-gate 	uint32_t		policy = AGGR_POLICY_L4;
2987c478bd9Sstevel@tonic-gate 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
2997c478bd9Sstevel@tonic-gate 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
3007c478bd9Sstevel@tonic-gate 	laadm_port_attr_db_t	port[MAXPORT];
3017c478bd9Sstevel@tonic-gate 	uint_t			nport = 0;
3027c478bd9Sstevel@tonic-gate 	uint8_t			mac_addr[ETHERADDRL];
3037c478bd9Sstevel@tonic-gate 	boolean_t		mac_addr_fixed = B_FALSE;
3047c478bd9Sstevel@tonic-gate 	boolean_t		P_arg = B_FALSE;
3057c478bd9Sstevel@tonic-gate 	boolean_t		l_arg = B_FALSE;
3067c478bd9Sstevel@tonic-gate 	boolean_t		t_arg = B_FALSE;
3077c478bd9Sstevel@tonic-gate 	boolean_t		u_arg = B_FALSE;
3087c478bd9Sstevel@tonic-gate 	boolean_t		T_arg = B_FALSE;
3097c478bd9Sstevel@tonic-gate 	char			*altroot = NULL;
3107c478bd9Sstevel@tonic-gate 	laadm_diag_t		diag = 0;
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	opterr = 0;
3137c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":d:l:P:R:tu:T:",
3147c478bd9Sstevel@tonic-gate 	    longopts, NULL)) != -1) {
3157c478bd9Sstevel@tonic-gate 		switch (option) {
3167c478bd9Sstevel@tonic-gate 		case 'd':
31733343a97Smeem 			if (nport >= MAXPORT)
31833343a97Smeem 				die("too many <dev> arguments");
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 			if (strlcpy(port[nport].lp_devname, optarg,
32133343a97Smeem 			    MAXNAMELEN) >= MAXNAMELEN)
32233343a97Smeem 				die("device name too long");
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 			nport++;
3257c478bd9Sstevel@tonic-gate 			break;
3267c478bd9Sstevel@tonic-gate 		case 'P':
32733343a97Smeem 			if (P_arg)
32833343a97Smeem 				die_optdup(option);
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 			P_arg = B_TRUE;
33133343a97Smeem 			if (!laadm_str_to_policy(optarg, &policy))
33233343a97Smeem 				die("invalid policy '%s'", optarg);
3337c478bd9Sstevel@tonic-gate 			break;
3347c478bd9Sstevel@tonic-gate 		case 'u':
33533343a97Smeem 			if (u_arg)
33633343a97Smeem 				die_optdup(option);
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 			u_arg = B_TRUE;
3397c478bd9Sstevel@tonic-gate 			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
34033343a97Smeem 			    mac_addr))
34133343a97Smeem 				die("invalid MAC address '%s'", optarg);
3427c478bd9Sstevel@tonic-gate 			break;
3437c478bd9Sstevel@tonic-gate 		case 'l':
34433343a97Smeem 			if (l_arg)
34533343a97Smeem 				die_optdup(option);
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate 			l_arg = B_TRUE;
34833343a97Smeem 			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode))
34933343a97Smeem 				die("invalid LACP mode '%s'", optarg);
3507c478bd9Sstevel@tonic-gate 			break;
3517c478bd9Sstevel@tonic-gate 		case 'T':
35233343a97Smeem 			if (T_arg)
35333343a97Smeem 				die_optdup(option);
3547c478bd9Sstevel@tonic-gate 
3557c478bd9Sstevel@tonic-gate 			T_arg = B_TRUE;
35633343a97Smeem 			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer))
35733343a97Smeem 				die("invalid LACP timer value '%s'", optarg);
3587c478bd9Sstevel@tonic-gate 			break;
3597c478bd9Sstevel@tonic-gate 		case 't':
3607c478bd9Sstevel@tonic-gate 			t_arg = B_TRUE;
3617c478bd9Sstevel@tonic-gate 			break;
3627c478bd9Sstevel@tonic-gate 		case 'R':
3637c478bd9Sstevel@tonic-gate 			altroot = optarg;
3647c478bd9Sstevel@tonic-gate 			break;
3657c478bd9Sstevel@tonic-gate 		default:
36633343a97Smeem 			die_opterr(optopt, option);
36733343a97Smeem 			break;
3687c478bd9Sstevel@tonic-gate 		}
3697c478bd9Sstevel@tonic-gate 	}
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	if (nport == 0)
3727c478bd9Sstevel@tonic-gate 		usage();
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	/* get key value (required last argument) */
3757c478bd9Sstevel@tonic-gate 	if (optind != (argc-1))
3767c478bd9Sstevel@tonic-gate 		usage();
3777c478bd9Sstevel@tonic-gate 
37833343a97Smeem 	if (!str2int(argv[optind], &key) || key < 1)
37933343a97Smeem 		die("invalid key value '%s'", argv[optind]);
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	if (laadm_create(key, nport, port, policy, mac_addr_fixed,
38233343a97Smeem 	    mac_addr, lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0)
38333343a97Smeem 		die_laerr(diag, "create operation failed");
3847c478bd9Sstevel@tonic-gate }
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate static void
3877c478bd9Sstevel@tonic-gate do_delete_aggr(int argc, char *argv[])
3887c478bd9Sstevel@tonic-gate {
38933343a97Smeem 	int			key;
3907c478bd9Sstevel@tonic-gate 	char			option;
3917c478bd9Sstevel@tonic-gate 	boolean_t		t_arg = B_FALSE;
3927c478bd9Sstevel@tonic-gate 	char			*altroot = NULL;
3937c478bd9Sstevel@tonic-gate 	laadm_diag_t		diag = 0;
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	opterr = 0;
3967c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":R:t", longopts,
3977c478bd9Sstevel@tonic-gate 	    NULL)) != -1) {
3987c478bd9Sstevel@tonic-gate 		switch (option) {
3997c478bd9Sstevel@tonic-gate 		case 't':
4007c478bd9Sstevel@tonic-gate 			t_arg = B_TRUE;
4017c478bd9Sstevel@tonic-gate 			break;
4027c478bd9Sstevel@tonic-gate 		case 'R':
4037c478bd9Sstevel@tonic-gate 			altroot = optarg;
4047c478bd9Sstevel@tonic-gate 			break;
4057c478bd9Sstevel@tonic-gate 		default:
40633343a97Smeem 			die_opterr(optopt, option);
4077c478bd9Sstevel@tonic-gate 			break;
4087c478bd9Sstevel@tonic-gate 		}
4097c478bd9Sstevel@tonic-gate 	}
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	/* get key value (required last argument) */
4127c478bd9Sstevel@tonic-gate 	if (optind != (argc-1))
4137c478bd9Sstevel@tonic-gate 		usage();
4147c478bd9Sstevel@tonic-gate 
41533343a97Smeem 	if (!str2int(argv[optind], &key) || key < 1)
41633343a97Smeem 		die("invalid key value '%s'", argv[optind]);
4177c478bd9Sstevel@tonic-gate 
41833343a97Smeem 	if (laadm_delete(key, t_arg, altroot, &diag) < 0)
41933343a97Smeem 		die_laerr(diag, "delete operation failed");
4207c478bd9Sstevel@tonic-gate }
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate static void
4237c478bd9Sstevel@tonic-gate do_add_aggr(int argc, char *argv[])
4247c478bd9Sstevel@tonic-gate {
4257c478bd9Sstevel@tonic-gate 	char			option;
42633343a97Smeem 	int			key;
4277c478bd9Sstevel@tonic-gate 	laadm_port_attr_db_t	port[MAXPORT];
4287c478bd9Sstevel@tonic-gate 	uint_t			nport = 0;
4297c478bd9Sstevel@tonic-gate 	boolean_t		t_arg = B_FALSE;
4307c478bd9Sstevel@tonic-gate 	char			*altroot = NULL;
4317c478bd9Sstevel@tonic-gate 	laadm_diag_t		diag = 0;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	opterr = 0;
4347c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":d:R:t", longopts,
4357c478bd9Sstevel@tonic-gate 	    NULL)) != -1) {
4367c478bd9Sstevel@tonic-gate 		switch (option) {
4377c478bd9Sstevel@tonic-gate 		case 'd':
43833343a97Smeem 			if (nport >= MAXPORT)
43933343a97Smeem 				die("too many <dev> arguments");
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 			if (strlcpy(port[nport].lp_devname, optarg,
44233343a97Smeem 			    MAXNAMELEN) >= MAXNAMELEN)
44333343a97Smeem 				die("device name too long");
44433343a97Smeem 
4457c478bd9Sstevel@tonic-gate 			nport++;
4467c478bd9Sstevel@tonic-gate 			break;
4477c478bd9Sstevel@tonic-gate 		case 't':
4487c478bd9Sstevel@tonic-gate 			t_arg = B_TRUE;
4497c478bd9Sstevel@tonic-gate 			break;
4507c478bd9Sstevel@tonic-gate 		case 'R':
4517c478bd9Sstevel@tonic-gate 			altroot = optarg;
4527c478bd9Sstevel@tonic-gate 			break;
4537c478bd9Sstevel@tonic-gate 		default:
45433343a97Smeem 			die_opterr(optopt, option);
45533343a97Smeem 			break;
4567c478bd9Sstevel@tonic-gate 		}
4577c478bd9Sstevel@tonic-gate 	}
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	if (nport == 0)
4607c478bd9Sstevel@tonic-gate 		usage();
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 	/* get key value (required last argument) */
4637c478bd9Sstevel@tonic-gate 	if (optind != (argc-1))
4647c478bd9Sstevel@tonic-gate 		usage();
4657c478bd9Sstevel@tonic-gate 
46633343a97Smeem 	if (!str2int(argv[optind], &key) || key < 1)
46733343a97Smeem 		die("invalid key value '%s'", argv[optind]);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	if (laadm_add(key, nport, port, t_arg, altroot, &diag) < 0) {
470219a2a31Shl157128 		/*
471219a2a31Shl157128 		 * checking ENOTSUP is a temporary workaround
472219a2a31Shl157128 		 * and should be removed once 6399681 is fixed.
473219a2a31Shl157128 		 */
474219a2a31Shl157128 		if (errno == ENOTSUP) {
475219a2a31Shl157128 			(void) fprintf(stderr,
476219a2a31Shl157128 			    gettext("%s: add operation failed: %s\n"),
477219a2a31Shl157128 			    progname,
478219a2a31Shl157128 			    gettext("device capabilities don't match"));
479219a2a31Shl157128 			exit(ENOTSUP);
480219a2a31Shl157128 		}
48133343a97Smeem 		die_laerr(diag, "add operation failed");
4827c478bd9Sstevel@tonic-gate 	}
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate static void
4867c478bd9Sstevel@tonic-gate do_remove_aggr(int argc, char *argv[])
4877c478bd9Sstevel@tonic-gate {
4887c478bd9Sstevel@tonic-gate 	char			option;
48933343a97Smeem 	int			key;
4907c478bd9Sstevel@tonic-gate 	laadm_port_attr_db_t	port[MAXPORT];
4917c478bd9Sstevel@tonic-gate 	uint_t			nport = 0;
4927c478bd9Sstevel@tonic-gate 	boolean_t		t_arg = B_FALSE;
4937c478bd9Sstevel@tonic-gate 	char			*altroot = NULL;
4947c478bd9Sstevel@tonic-gate 	laadm_diag_t		diag = 0;
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	opterr = 0;
4977c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":d:R:t",
4987c478bd9Sstevel@tonic-gate 	    longopts, NULL)) != -1) {
4997c478bd9Sstevel@tonic-gate 		switch (option) {
5007c478bd9Sstevel@tonic-gate 		case 'd':
50133343a97Smeem 			if (nport >= MAXPORT)
50233343a97Smeem 				die("too many <dev> arguments");
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 			if (strlcpy(port[nport].lp_devname, optarg,
50533343a97Smeem 			    MAXNAMELEN) >= MAXNAMELEN)
50633343a97Smeem 				die("device name too long");
50733343a97Smeem 
5087c478bd9Sstevel@tonic-gate 			nport++;
5097c478bd9Sstevel@tonic-gate 			break;
5107c478bd9Sstevel@tonic-gate 		case 't':
5117c478bd9Sstevel@tonic-gate 			t_arg = B_TRUE;
5127c478bd9Sstevel@tonic-gate 			break;
5137c478bd9Sstevel@tonic-gate 		case 'R':
5147c478bd9Sstevel@tonic-gate 			altroot = optarg;
5157c478bd9Sstevel@tonic-gate 			break;
5167c478bd9Sstevel@tonic-gate 		default:
51733343a97Smeem 			die_opterr(optopt, option);
51833343a97Smeem 			break;
5197c478bd9Sstevel@tonic-gate 		}
5207c478bd9Sstevel@tonic-gate 	}
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	if (nport == 0)
5237c478bd9Sstevel@tonic-gate 		usage();
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	/* get key value (required last argument) */
5267c478bd9Sstevel@tonic-gate 	if (optind != (argc-1))
5277c478bd9Sstevel@tonic-gate 		usage();
5287c478bd9Sstevel@tonic-gate 
52933343a97Smeem 	if (!str2int(argv[optind], &key) || key < 1)
53033343a97Smeem 		die("invalid key value '%s'", argv[optind]);
5317c478bd9Sstevel@tonic-gate 
53233343a97Smeem 	if (laadm_remove(key, nport, port, t_arg, altroot, &diag) < 0)
53333343a97Smeem 		die_laerr(diag, "remove operation failed");
5347c478bd9Sstevel@tonic-gate }
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate static void
5377c478bd9Sstevel@tonic-gate do_modify_aggr(int argc, char *argv[])
5387c478bd9Sstevel@tonic-gate {
5397c478bd9Sstevel@tonic-gate 	char			option;
54033343a97Smeem 	int			key;
5417c478bd9Sstevel@tonic-gate 	uint32_t		policy = AGGR_POLICY_L4;
5427c478bd9Sstevel@tonic-gate 	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
5437c478bd9Sstevel@tonic-gate 	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
5447c478bd9Sstevel@tonic-gate 	uint8_t			mac_addr[ETHERADDRL];
5457c478bd9Sstevel@tonic-gate 	boolean_t		mac_addr_fixed = B_FALSE;
5467c478bd9Sstevel@tonic-gate 	uint8_t			modify_mask = 0;
5477c478bd9Sstevel@tonic-gate 	boolean_t		t_arg = B_FALSE;
5487c478bd9Sstevel@tonic-gate 	char			*altroot = NULL;
5497c478bd9Sstevel@tonic-gate 	laadm_diag_t		diag = 0;
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	opterr = 0;
5527c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":l:P:R:tu:T:", longopts,
5537c478bd9Sstevel@tonic-gate 	    NULL)) != -1) {
5547c478bd9Sstevel@tonic-gate 		switch (option) {
5557c478bd9Sstevel@tonic-gate 		case 'P':
55633343a97Smeem 			if (modify_mask & LAADM_MODIFY_POLICY)
55733343a97Smeem 				die_optdup(option);
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 			modify_mask |= LAADM_MODIFY_POLICY;
5607c478bd9Sstevel@tonic-gate 
56133343a97Smeem 			if (!laadm_str_to_policy(optarg, &policy))
56233343a97Smeem 				die("invalid policy '%s'", optarg);
5637c478bd9Sstevel@tonic-gate 			break;
5647c478bd9Sstevel@tonic-gate 		case 'u':
56533343a97Smeem 			if (modify_mask & LAADM_MODIFY_MAC)
56633343a97Smeem 				die_optdup(option);
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 			modify_mask |= LAADM_MODIFY_MAC;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
57133343a97Smeem 			    mac_addr))
57233343a97Smeem 				die("invalid MAC address '%s'", optarg);
5737c478bd9Sstevel@tonic-gate 			break;
5747c478bd9Sstevel@tonic-gate 		case 'l':
57533343a97Smeem 			if (modify_mask & LAADM_MODIFY_LACP_MODE)
57633343a97Smeem 				die_optdup(option);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 			modify_mask |= LAADM_MODIFY_LACP_MODE;
5797c478bd9Sstevel@tonic-gate 
58033343a97Smeem 			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode))
58133343a97Smeem 				die("invalid LACP mode '%s'", optarg);
5827c478bd9Sstevel@tonic-gate 			break;
5837c478bd9Sstevel@tonic-gate 		case 'T':
58433343a97Smeem 			if (modify_mask & LAADM_MODIFY_LACP_TIMER)
58533343a97Smeem 				die_optdup(option);
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 			modify_mask |= LAADM_MODIFY_LACP_TIMER;
5887c478bd9Sstevel@tonic-gate 
58933343a97Smeem 			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer))
59033343a97Smeem 				die("invalid LACP timer value '%s'", optarg);
5917c478bd9Sstevel@tonic-gate 			break;
5927c478bd9Sstevel@tonic-gate 		case 't':
5937c478bd9Sstevel@tonic-gate 			t_arg = B_TRUE;
5947c478bd9Sstevel@tonic-gate 			break;
5957c478bd9Sstevel@tonic-gate 		case 'R':
5967c478bd9Sstevel@tonic-gate 			altroot = optarg;
5977c478bd9Sstevel@tonic-gate 			break;
5987c478bd9Sstevel@tonic-gate 		default:
59933343a97Smeem 			die_opterr(optopt, option);
60033343a97Smeem 			break;
6017c478bd9Sstevel@tonic-gate 		}
6027c478bd9Sstevel@tonic-gate 	}
6037c478bd9Sstevel@tonic-gate 
60433343a97Smeem 	if (modify_mask == 0)
60533343a97Smeem 		die("at least one of the -PulT options must be specified");
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	/* get key value (required last argument) */
6087c478bd9Sstevel@tonic-gate 	if (optind != (argc-1))
6097c478bd9Sstevel@tonic-gate 		usage();
6107c478bd9Sstevel@tonic-gate 
61133343a97Smeem 	if (!str2int(argv[optind], &key) || key < 1)
61233343a97Smeem 		die("invalid key value '%s'", argv[optind]);
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	if (laadm_modify(key, modify_mask, policy, mac_addr_fixed, mac_addr,
61533343a97Smeem 	    lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0)
61633343a97Smeem 		die_laerr(diag, "modify operation failed");
6177c478bd9Sstevel@tonic-gate }
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate static void
6207c478bd9Sstevel@tonic-gate do_up_aggr(int argc, char *argv[])
6217c478bd9Sstevel@tonic-gate {
62233343a97Smeem 	int		key = 0;
6237c478bd9Sstevel@tonic-gate 	laadm_diag_t	diag = 0;
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	/* get aggregation key (optional last argument) */
6267c478bd9Sstevel@tonic-gate 	if (argc == 2) {
62733343a97Smeem 		if (!str2int(argv[1], &key) || key < 1)
62833343a97Smeem 			die("invalid key value '%s'", argv[1]);
6297c478bd9Sstevel@tonic-gate 	} else if (argc > 2) {
6307c478bd9Sstevel@tonic-gate 		usage();
6317c478bd9Sstevel@tonic-gate 	}
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	if (laadm_up(key, NULL, &diag) < 0) {
6347c478bd9Sstevel@tonic-gate 		if (key != 0) {
63533343a97Smeem 			die_laerr(diag, "could not bring up aggregation '%u'",
63633343a97Smeem 			    key);
6377c478bd9Sstevel@tonic-gate 		} else {
63833343a97Smeem 			die_laerr(diag, "could not bring aggregations up");
6397c478bd9Sstevel@tonic-gate 		}
6407c478bd9Sstevel@tonic-gate 	}
6417c478bd9Sstevel@tonic-gate }
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate static void
6447c478bd9Sstevel@tonic-gate do_down_aggr(int argc, char *argv[])
6457c478bd9Sstevel@tonic-gate {
64633343a97Smeem 	int	key = 0;
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	/* get aggregation key (optional last argument) */
6497c478bd9Sstevel@tonic-gate 	if (argc == 2) {
65033343a97Smeem 		if (!str2int(argv[1], &key) || key < 1)
65133343a97Smeem 			die("invalid key value '%s'", argv[1]);
6527c478bd9Sstevel@tonic-gate 	} else if (argc > 2) {
6537c478bd9Sstevel@tonic-gate 		usage();
6547c478bd9Sstevel@tonic-gate 	}
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	if (laadm_down(key) < 0) {
6577c478bd9Sstevel@tonic-gate 		if (key != 0) {
65833343a97Smeem 			die("could not bring down aggregation '%u': %s",
65933343a97Smeem 			    key, strerror(errno));
6607c478bd9Sstevel@tonic-gate 		} else {
66133343a97Smeem 			die("could not bring down aggregations: %s",
66233343a97Smeem 			    strerror(errno));
6637c478bd9Sstevel@tonic-gate 		}
6647c478bd9Sstevel@tonic-gate 	}
6657c478bd9Sstevel@tonic-gate }
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate #define	TYPE_WIDTH	10
668210db224Sericheng 
669210db224Sericheng static void
670210db224Sericheng print_link_parseable(const char *name, dladm_attr_t *dap, boolean_t legacy)
671210db224Sericheng {
672210db224Sericheng 	char	type[TYPE_WIDTH];
673210db224Sericheng 
674210db224Sericheng 	if (!legacy) {
675*c7e4935fSss150715 		char	drv[DLPI_LINKNAME_MAX];
676*c7e4935fSss150715 		uint_t	instance;
677ba2e4443Sseb 
678210db224Sericheng 		if (dap->da_vid != 0) {
679210db224Sericheng 			(void) snprintf(type, TYPE_WIDTH, "vlan %u",
680210db224Sericheng 			    dap->da_vid);
681210db224Sericheng 		} else {
682210db224Sericheng 			(void) snprintf(type, TYPE_WIDTH, "non-vlan");
683210db224Sericheng 		}
684*c7e4935fSss150715 
685*c7e4935fSss150715 		if (dlpi_parselink(dap->da_dev, drv, &instance) != DLPI_SUCCESS)
686ba2e4443Sseb 			return;
687*c7e4935fSss150715 
688ba2e4443Sseb 		if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) {
689210db224Sericheng 			(void) printf("%s type=%s mtu=%d key=%u\n",
690ba2e4443Sseb 			    name, type, dap->da_max_sdu, instance);
691210db224Sericheng 		} else {
692210db224Sericheng 			(void) printf("%s type=%s mtu=%d device=%s\n",
693210db224Sericheng 			    name, type, dap->da_max_sdu, dap->da_dev);
694210db224Sericheng 		}
695210db224Sericheng 	} else {
696210db224Sericheng 		(void) printf("%s type=legacy mtu=%d device=%s\n",
697210db224Sericheng 		    name, dap->da_max_sdu, name);
698210db224Sericheng 	}
699210db224Sericheng }
700210db224Sericheng 
701210db224Sericheng static void
702210db224Sericheng print_link(const char *name, dladm_attr_t *dap, boolean_t legacy)
703210db224Sericheng {
704210db224Sericheng 	char	type[TYPE_WIDTH];
705210db224Sericheng 
706210db224Sericheng 	if (!legacy) {
707*c7e4935fSss150715 		char 	drv[DLPI_LINKNAME_MAX];
708*c7e4935fSss150715 		uint_t	instance;
709ba2e4443Sseb 
710210db224Sericheng 		if (dap->da_vid != 0) {
711210db224Sericheng 			(void) snprintf(type, TYPE_WIDTH, gettext("vlan %u"),
712210db224Sericheng 			    dap->da_vid);
713210db224Sericheng 		} else {
714210db224Sericheng 			(void) snprintf(type, TYPE_WIDTH, gettext("non-vlan"));
715210db224Sericheng 		}
716*c7e4935fSss150715 
717*c7e4935fSss150715 		if (dlpi_parselink(dap->da_dev, drv, &instance) != DLPI_SUCCESS)
718ba2e4443Sseb 			return;
719ba2e4443Sseb 		if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) {
720210db224Sericheng 			(void) printf(gettext("%-9s\ttype: %s\tmtu: %d"
721210db224Sericheng 			    "\taggregation: key %u\n"), name, type,
722ba2e4443Sseb 			    dap->da_max_sdu, instance);
723210db224Sericheng 		} else {
724210db224Sericheng 			(void) printf(gettext("%-9s\ttype: %s\tmtu: "
725210db224Sericheng 			    "%d\tdevice: %s\n"), name, type, dap->da_max_sdu,
726210db224Sericheng 			    dap->da_dev);
727210db224Sericheng 		}
728210db224Sericheng 	} else {
729210db224Sericheng 		(void) printf(gettext("%-9s\ttype: legacy\tmtu: "
730210db224Sericheng 		    "%d\tdevice: %s\n"), name, dap->da_max_sdu, name);
731210db224Sericheng 	}
732210db224Sericheng }
733210db224Sericheng 
734210db224Sericheng static int
735210db224Sericheng get_if_info(const char *name, dladm_attr_t *dlattrp, boolean_t *legacy)
736210db224Sericheng {
737210db224Sericheng 	int	err;
738210db224Sericheng 
739210db224Sericheng 	if ((err = dladm_info(name, dlattrp)) == 0) {
740210db224Sericheng 		*legacy = B_FALSE;
741210db224Sericheng 	} else if (err < 0 && errno == ENODEV) {
742*c7e4935fSss150715 		dlpi_handle_t   dh;
743*c7e4935fSss150715 		dlpi_info_t	dlinfo;
744210db224Sericheng 
745210db224Sericheng 		/*
746210db224Sericheng 		 * A return value of ENODEV means that the specified
747210db224Sericheng 		 * device is not gldv3.
748210db224Sericheng 		 */
749*c7e4935fSss150715 		if (dlpi_open(name, &dh, 0) != DLPI_SUCCESS) {
750210db224Sericheng 			errno = ENOENT;
751210db224Sericheng 			return (-1);
752210db224Sericheng 		}
753*c7e4935fSss150715 		if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
754*c7e4935fSss150715 			dlpi_close(dh);
755*c7e4935fSss150715 			errno = EINVAL;
756*c7e4935fSss150715 			return (-1);
757*c7e4935fSss150715 		}
758*c7e4935fSss150715 		dlpi_close(dh);
759*c7e4935fSss150715 		*legacy = B_TRUE;
760*c7e4935fSss150715 		bzero(dlattrp, sizeof (*dlattrp));
761*c7e4935fSss150715 		dlattrp->da_max_sdu = dlinfo.di_max_sdu;
762*c7e4935fSss150715 
763210db224Sericheng 	} else {
764210db224Sericheng 		/*
765210db224Sericheng 		 * If the return value is not ENODEV, this means that
766210db224Sericheng 		 * user is either passing in a bogus interface name
767210db224Sericheng 		 * or a vlan interface name that doesn't exist yet.
768210db224Sericheng 		 */
769210db224Sericheng 		errno = ENOENT;
770210db224Sericheng 		return (-1);
771210db224Sericheng 	}
772210db224Sericheng 	return (0);
773210db224Sericheng }
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate /* ARGSUSED */
7767c478bd9Sstevel@tonic-gate static void
7777c478bd9Sstevel@tonic-gate show_link(void *arg, const char *name)
7787c478bd9Sstevel@tonic-gate {
7797c478bd9Sstevel@tonic-gate 	dladm_attr_t	dlattr;
780210db224Sericheng 	boolean_t	legacy = B_TRUE;
7817c478bd9Sstevel@tonic-gate 	show_link_state_t *state = (show_link_state_t *)arg;
7827c478bd9Sstevel@tonic-gate 
78333343a97Smeem 	if (get_if_info(name, &dlattr, &legacy) < 0)
78433343a97Smeem 		die("invalid link '%s'", name);
7857c478bd9Sstevel@tonic-gate 
786210db224Sericheng 	if (state->ls_parseable) {
787210db224Sericheng 		print_link_parseable(name, &dlattr, legacy);
7887c478bd9Sstevel@tonic-gate 	} else {
789210db224Sericheng 		print_link(name, &dlattr, legacy);
7907c478bd9Sstevel@tonic-gate 	}
7917c478bd9Sstevel@tonic-gate }
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate static void
7947c478bd9Sstevel@tonic-gate show_link_stats(void *arg, const char *name)
7957c478bd9Sstevel@tonic-gate {
7967c478bd9Sstevel@tonic-gate 	show_link_state_t *state = (show_link_state_t *)arg;
7977c478bd9Sstevel@tonic-gate 	pktsum_t stats, diff_stats;
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	if (state->ls_firstonly) {
8007c478bd9Sstevel@tonic-gate 		if (state->ls_donefirst)
8017c478bd9Sstevel@tonic-gate 			return;
8027c478bd9Sstevel@tonic-gate 		state->ls_donefirst = B_TRUE;
8037c478bd9Sstevel@tonic-gate 	} else {
8047c478bd9Sstevel@tonic-gate 		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
8057c478bd9Sstevel@tonic-gate 	}
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	get_link_stats(name, &stats);
8087c478bd9Sstevel@tonic-gate 	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 	(void) printf("%s", name);
8117c478bd9Sstevel@tonic-gate 	(void) printf("\t\t%-10llu", diff_stats.ipackets);
8127c478bd9Sstevel@tonic-gate 	(void) printf("%-12llu", diff_stats.rbytes);
8137c478bd9Sstevel@tonic-gate 	(void) printf("%-8u", diff_stats.ierrors);
8147c478bd9Sstevel@tonic-gate 	(void) printf("%-10llu", diff_stats.opackets);
8157c478bd9Sstevel@tonic-gate 	(void) printf("%-12llu", diff_stats.obytes);
8167c478bd9Sstevel@tonic-gate 	(void) printf("%-8u\n", diff_stats.oerrors);
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	state->ls_prevstats = stats;
8197c478bd9Sstevel@tonic-gate }
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate static void
8227c478bd9Sstevel@tonic-gate dump_grp(laadm_grp_attr_sys_t	*grp, boolean_t parseable)
8237c478bd9Sstevel@tonic-gate {
8247c478bd9Sstevel@tonic-gate 	char policy_str[LAADM_POLICY_STR_LEN];
8257c478bd9Sstevel@tonic-gate 	char addr_str[ETHERADDRL * 3];
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 	if (!parseable) {
8287c478bd9Sstevel@tonic-gate 		(void) printf(gettext("key: %d (0x%04x)"),
8297c478bd9Sstevel@tonic-gate 		    grp->lg_key, grp->lg_key);
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\tpolicy: %s"),
8327c478bd9Sstevel@tonic-gate 		    laadm_policy_to_str(grp->lg_policy, policy_str));
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\taddress: %s (%s)\n"),
8357c478bd9Sstevel@tonic-gate 		    laadm_mac_addr_to_str(grp->lg_mac, addr_str),
8367c478bd9Sstevel@tonic-gate 		    (grp->lg_mac_fixed) ? gettext("fixed") : gettext("auto"));
8377c478bd9Sstevel@tonic-gate 	} else {
8387c478bd9Sstevel@tonic-gate 		(void) printf("aggr key=%d", grp->lg_key);
8397c478bd9Sstevel@tonic-gate 
8407c478bd9Sstevel@tonic-gate 		(void) printf(" policy=%s",
8417c478bd9Sstevel@tonic-gate 		    laadm_policy_to_str(grp->lg_policy, policy_str));
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 		(void) printf(" address=%s",
8447c478bd9Sstevel@tonic-gate 		    laadm_mac_addr_to_str(grp->lg_mac, addr_str));
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 		(void) printf(" address-type=%s\n",
8477c478bd9Sstevel@tonic-gate 		    (grp->lg_mac_fixed) ? "fixed" : "auto");
8487c478bd9Sstevel@tonic-gate 	}
8497c478bd9Sstevel@tonic-gate }
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate static void
8527c478bd9Sstevel@tonic-gate dump_grp_lacp(laadm_grp_attr_sys_t *grp, boolean_t parseable)
8537c478bd9Sstevel@tonic-gate {
8547c478bd9Sstevel@tonic-gate 	const char *lacp_mode_str = laadm_lacp_mode_to_str(grp->lg_lacp_mode);
8557c478bd9Sstevel@tonic-gate 	const char *lacp_timer_str =
8567c478bd9Sstevel@tonic-gate 	    laadm_lacp_timer_to_str(grp->lg_lacp_timer);
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	if (!parseable) {
8597c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\t\tLACP mode: %s"), lacp_mode_str);
8607c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\tLACP timer: %s\n"), lacp_timer_str);
8617c478bd9Sstevel@tonic-gate 	} else {
8627c478bd9Sstevel@tonic-gate 		(void) printf(" lacp-mode=%s", lacp_mode_str);
8637c478bd9Sstevel@tonic-gate 		(void) printf(" lacp-timer=%s\n", lacp_timer_str);
8647c478bd9Sstevel@tonic-gate 	}
8657c478bd9Sstevel@tonic-gate }
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate static void
8687c478bd9Sstevel@tonic-gate dump_grp_stats(laadm_grp_attr_sys_t *grp)
8697c478bd9Sstevel@tonic-gate {
8707c478bd9Sstevel@tonic-gate 	(void) printf("key: %d", grp->lg_key);
8717c478bd9Sstevel@tonic-gate 	(void) printf("\tipackets  rbytes      opackets	 obytes		 ");
8727c478bd9Sstevel@tonic-gate 	(void) printf("%%ipkts	%%opkts\n");
8737c478bd9Sstevel@tonic-gate }
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate static void
8767c478bd9Sstevel@tonic-gate dump_ports_lacp_head(void)
8777c478bd9Sstevel@tonic-gate {
8787c478bd9Sstevel@tonic-gate 	(void) printf(DUMP_LACP_FORMAT, gettext("device"), gettext("activity"),
8797c478bd9Sstevel@tonic-gate 	    gettext("timeout"), gettext("aggregatable"), gettext("sync"),
8807c478bd9Sstevel@tonic-gate 	    gettext("coll"), gettext("dist"), gettext("defaulted"),
8817c478bd9Sstevel@tonic-gate 	    gettext("expired"));
8827c478bd9Sstevel@tonic-gate }
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate static void
8857c478bd9Sstevel@tonic-gate dump_ports_head(void)
8867c478bd9Sstevel@tonic-gate {
8877c478bd9Sstevel@tonic-gate 	(void) printf(gettext("	   device\taddress\t\t	speed\t\tduplex\tlink\t"
8887c478bd9Sstevel@tonic-gate 	    "state\n"));
8897c478bd9Sstevel@tonic-gate }
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate static char *
8927c478bd9Sstevel@tonic-gate port_state_to_str(aggr_port_state_t state_num)
8937c478bd9Sstevel@tonic-gate {
8947c478bd9Sstevel@tonic-gate 	int			i;
8957c478bd9Sstevel@tonic-gate 	port_state_t		*state;
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	for (i = 0; i < NPORTSTATES; i++) {
8987c478bd9Sstevel@tonic-gate 		state = &port_states[i];
8997c478bd9Sstevel@tonic-gate 		if (state->state_num == state_num)
9007c478bd9Sstevel@tonic-gate 			return (state->state_name);
9017c478bd9Sstevel@tonic-gate 	}
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	return ("unknown");
9047c478bd9Sstevel@tonic-gate }
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate static void
9077c478bd9Sstevel@tonic-gate dump_port(laadm_port_attr_sys_t *port, boolean_t parseable)
9087c478bd9Sstevel@tonic-gate {
9097c478bd9Sstevel@tonic-gate 	char *dev = port->lp_devname;
9107c478bd9Sstevel@tonic-gate 	char buf[ETHERADDRL * 3];
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	if (!parseable) {
9137c478bd9Sstevel@tonic-gate 		(void) printf("	   %-9s\t%s", dev, laadm_mac_addr_to_str(
9147c478bd9Sstevel@tonic-gate 		    port->lp_mac, buf));
91533343a97Smeem 		(void) printf("\t %5uMb", (int)(mac_ifspeed(dev) /
9167c478bd9Sstevel@tonic-gate 		    1000000ull));
917ba2e4443Sseb 		(void) printf("\t%s", mac_link_duplex(dev));
918ba2e4443Sseb 		(void) printf("\t%s", mac_link_state(dev));
9197c478bd9Sstevel@tonic-gate 		(void) printf("\t%s\n", port_state_to_str(port->lp_state));
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	} else {
9227c478bd9Sstevel@tonic-gate 		(void) printf(" device=%s address=%s", dev,
9237c478bd9Sstevel@tonic-gate 		    laadm_mac_addr_to_str(port->lp_mac, buf));
924ba2e4443Sseb 		(void) printf(" speed=%u", (int)(mac_ifspeed(dev) /
9257c478bd9Sstevel@tonic-gate 		    1000000ull));
926ba2e4443Sseb 		(void) printf(" duplex=%s", mac_link_duplex(dev));
927ba2e4443Sseb 		(void) printf(" link=%s", mac_link_state(dev));
9287c478bd9Sstevel@tonic-gate 		(void) printf(" port=%s", port_state_to_str(port->lp_state));
9297c478bd9Sstevel@tonic-gate 	}
9307c478bd9Sstevel@tonic-gate }
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate static void
9337c478bd9Sstevel@tonic-gate dump_port_lacp(laadm_port_attr_sys_t *port)
9347c478bd9Sstevel@tonic-gate {
9357c478bd9Sstevel@tonic-gate 	aggr_lacp_state_t *state = &port->lp_lacp_state;
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	(void) printf(DUMP_LACP_FORMAT,
9387c478bd9Sstevel@tonic-gate 	    port->lp_devname, state->bit.activity ? "active" : "passive",
9397c478bd9Sstevel@tonic-gate 	    state->bit.timeout ? "short" : "long",
9407c478bd9Sstevel@tonic-gate 	    state->bit.aggregation ? "yes" : "no",
9417c478bd9Sstevel@tonic-gate 	    state->bit.sync ? "yes" : "no",
9427c478bd9Sstevel@tonic-gate 	    state->bit.collecting ? "yes" : "no",
9437c478bd9Sstevel@tonic-gate 	    state->bit.distributing ? "yes" : "no",
9447c478bd9Sstevel@tonic-gate 	    state->bit.defaulted ? "yes" : "no",
9457c478bd9Sstevel@tonic-gate 	    state->bit.expired ? "yes" : "no");
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate static void
9497c478bd9Sstevel@tonic-gate dump_port_stat(int index, show_grp_state_t *state, pktsum_t *port_stats,
9507c478bd9Sstevel@tonic-gate     pktsum_t *tot_stats)
9517c478bd9Sstevel@tonic-gate {
9527c478bd9Sstevel@tonic-gate 	pktsum_t	diff_stats;
9537c478bd9Sstevel@tonic-gate 	pktsum_t	*old_stats = &state->gs_prevstats[index];
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	stats_diff(&diff_stats, port_stats, old_stats);
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	(void) printf("\t%-10llu", diff_stats.ipackets);
9587c478bd9Sstevel@tonic-gate 	(void) printf("%-12llu", diff_stats.rbytes);
9597c478bd9Sstevel@tonic-gate 	(void) printf("%-10llu", diff_stats.opackets);
9607c478bd9Sstevel@tonic-gate 	(void) printf("%-12llu", diff_stats.obytes);
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	if (tot_stats->ipackets == 0)
9637c478bd9Sstevel@tonic-gate 		(void) printf("\t-");
9647c478bd9Sstevel@tonic-gate 	else
9657c478bd9Sstevel@tonic-gate 		(void) printf("\t%-6.1f", (double)diff_stats.ipackets/
9667c478bd9Sstevel@tonic-gate 		    (double)tot_stats->ipackets * 100);
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	if (tot_stats->opackets == 0)
9697c478bd9Sstevel@tonic-gate 		(void) printf("\t-");
9707c478bd9Sstevel@tonic-gate 	else
9717c478bd9Sstevel@tonic-gate 		(void) printf("\t%-6.1f", (double)diff_stats.opackets/
9727c478bd9Sstevel@tonic-gate 		    (double)tot_stats->opackets * 100);
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	(void) printf("\n");
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 	*old_stats = *port_stats;
9777c478bd9Sstevel@tonic-gate }
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate static int
9807c478bd9Sstevel@tonic-gate show_key(void *arg, laadm_grp_attr_sys_t *grp)
9817c478bd9Sstevel@tonic-gate {
9827c478bd9Sstevel@tonic-gate 	show_grp_state_t	*state = (show_grp_state_t *)arg;
9837c478bd9Sstevel@tonic-gate 	int			i;
9847c478bd9Sstevel@tonic-gate 	pktsum_t		pktsumtot, port_stat;
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 	if (state->gs_key != 0 && state->gs_key != grp->lg_key)
9877c478bd9Sstevel@tonic-gate 		return (0);
9887c478bd9Sstevel@tonic-gate 	if (state->gs_firstonly) {
9897c478bd9Sstevel@tonic-gate 		if (state->gs_found)
9907c478bd9Sstevel@tonic-gate 			return (0);
9917c478bd9Sstevel@tonic-gate 	} else {
9927c478bd9Sstevel@tonic-gate 		bzero(&state->gs_prevstats, sizeof (state->gs_prevstats));
9937c478bd9Sstevel@tonic-gate 	}
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	state->gs_found = B_TRUE;
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	if (state->gs_stats) {
9987c478bd9Sstevel@tonic-gate 		/* show statistics */
9997c478bd9Sstevel@tonic-gate 		dump_grp_stats(grp);
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 		/* sum the ports statistics */
10027c478bd9Sstevel@tonic-gate 		bzero(&pktsumtot, sizeof (pktsumtot));
10037c478bd9Sstevel@tonic-gate 		for (i = 0; i < grp->lg_nports; i++) {
1004ba2e4443Sseb 			get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat);
10057c478bd9Sstevel@tonic-gate 			stats_total(&pktsumtot, &port_stat,
10067c478bd9Sstevel@tonic-gate 			    &state->gs_prevstats[i]);
10077c478bd9Sstevel@tonic-gate 		}
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 		(void) printf("	   Total");
10107c478bd9Sstevel@tonic-gate 		(void) printf("\t%-10llu", pktsumtot.ipackets);
10117c478bd9Sstevel@tonic-gate 		(void) printf("%-12llu", pktsumtot.rbytes);
10127c478bd9Sstevel@tonic-gate 		(void) printf("%-10llu", pktsumtot.opackets);
10137c478bd9Sstevel@tonic-gate 		(void) printf("%-12llu\n", pktsumtot.obytes);
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 		for (i = 0; i < grp->lg_nports; i++) {
1016ba2e4443Sseb 			get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat);
10177c478bd9Sstevel@tonic-gate 			(void) printf("	   %s", grp->lg_ports[i].lp_devname);
10187c478bd9Sstevel@tonic-gate 			dump_port_stat(i, state, &port_stat, &pktsumtot);
10197c478bd9Sstevel@tonic-gate 		}
10207c478bd9Sstevel@tonic-gate 	} else if (state->gs_lacp) {
10217c478bd9Sstevel@tonic-gate 		/* show LACP info */
10227c478bd9Sstevel@tonic-gate 		dump_grp(grp, state->gs_parseable);
10237c478bd9Sstevel@tonic-gate 		dump_grp_lacp(grp, state->gs_parseable);
10247c478bd9Sstevel@tonic-gate 		dump_ports_lacp_head();
10257c478bd9Sstevel@tonic-gate 		for (i = 0; i < grp->lg_nports; i++)
10267c478bd9Sstevel@tonic-gate 			dump_port_lacp(&grp->lg_ports[i]);
10277c478bd9Sstevel@tonic-gate 	} else {
10287c478bd9Sstevel@tonic-gate 		dump_grp(grp, state->gs_parseable);
10297c478bd9Sstevel@tonic-gate 		if (!state->gs_parseable)
10307c478bd9Sstevel@tonic-gate 			dump_ports_head();
10317c478bd9Sstevel@tonic-gate 		for (i = 0; i < grp->lg_nports; i++) {
10327c478bd9Sstevel@tonic-gate 			if (state->gs_parseable)
10337c478bd9Sstevel@tonic-gate 				(void) printf("dev key=%d", grp->lg_key);
10347c478bd9Sstevel@tonic-gate 			dump_port(&grp->lg_ports[i], state->gs_parseable);
10357c478bd9Sstevel@tonic-gate 			if (state->gs_parseable)
10367c478bd9Sstevel@tonic-gate 				(void) printf("\n");
10377c478bd9Sstevel@tonic-gate 		}
10387c478bd9Sstevel@tonic-gate 	}
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	return (0);
10417c478bd9Sstevel@tonic-gate }
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate static int
10447c478bd9Sstevel@tonic-gate kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
10457c478bd9Sstevel@tonic-gate {
10467c478bd9Sstevel@tonic-gate 	kstat_named_t	*knp;
10477c478bd9Sstevel@tonic-gate 
10487c478bd9Sstevel@tonic-gate 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
10497c478bd9Sstevel@tonic-gate 		return (-1);
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 	if (knp->data_type != type)
10527c478bd9Sstevel@tonic-gate 		return (-1);
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	switch (type) {
10557c478bd9Sstevel@tonic-gate 	case KSTAT_DATA_UINT64:
10567c478bd9Sstevel@tonic-gate 		*(uint64_t *)buf = knp->value.ui64;
10577c478bd9Sstevel@tonic-gate 		break;
10587c478bd9Sstevel@tonic-gate 	case KSTAT_DATA_UINT32:
10597c478bd9Sstevel@tonic-gate 		*(uint32_t *)buf = knp->value.ui32;
10607c478bd9Sstevel@tonic-gate 		break;
10617c478bd9Sstevel@tonic-gate 	default:
10627c478bd9Sstevel@tonic-gate 		return (-1);
10637c478bd9Sstevel@tonic-gate 	}
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate 	return (0);
10667c478bd9Sstevel@tonic-gate }
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate static void
1069210db224Sericheng show_dev(void *arg, const char *dev)
10707c478bd9Sstevel@tonic-gate {
10717c478bd9Sstevel@tonic-gate 	show_mac_state_t *state = (show_mac_state_t *)arg;
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	(void) printf("%s", dev);
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 	if (!state->ms_parseable) {
10767c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\t\tlink: %s"),
1077ba2e4443Sseb 		    mac_link_state(dev));
107833343a97Smeem 		(void) printf(gettext("\tspeed: %5uMb"),
1079ba2e4443Sseb 		    (unsigned int)(mac_ifspeed(dev) / 1000000ull));
10807c478bd9Sstevel@tonic-gate 		(void) printf(gettext("\tduplex: %s\n"),
1081ba2e4443Sseb 		    mac_link_duplex(dev));
10827c478bd9Sstevel@tonic-gate 	} else {
1083ba2e4443Sseb 		(void) printf(" link=%s", mac_link_state(dev));
10847c478bd9Sstevel@tonic-gate 		(void) printf(" speed=%u",
1085ba2e4443Sseb 		    (unsigned int)(mac_ifspeed(dev) / 1000000ull));
1086ba2e4443Sseb 		(void) printf(" duplex=%s\n", mac_link_duplex(dev));
10877c478bd9Sstevel@tonic-gate 	}
10887c478bd9Sstevel@tonic-gate }
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10917c478bd9Sstevel@tonic-gate static void
1092210db224Sericheng show_dev_stats(void *arg, const char *dev)
10937c478bd9Sstevel@tonic-gate {
10947c478bd9Sstevel@tonic-gate 	show_mac_state_t *state = (show_mac_state_t *)arg;
10957c478bd9Sstevel@tonic-gate 	pktsum_t stats, diff_stats;
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	if (state->ms_firstonly) {
10987c478bd9Sstevel@tonic-gate 		if (state->ms_donefirst)
10997c478bd9Sstevel@tonic-gate 			return;
11007c478bd9Sstevel@tonic-gate 		state->ms_donefirst = B_TRUE;
11017c478bd9Sstevel@tonic-gate 	} else {
11027c478bd9Sstevel@tonic-gate 		bzero(&state->ms_prevstats, sizeof (state->ms_prevstats));
11037c478bd9Sstevel@tonic-gate 	}
11047c478bd9Sstevel@tonic-gate 
1105ba2e4443Sseb 	get_mac_stats(dev, &stats);
11067c478bd9Sstevel@tonic-gate 	stats_diff(&diff_stats, &stats, &state->ms_prevstats);
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate 	(void) printf("%s", dev);
11097c478bd9Sstevel@tonic-gate 	(void) printf("\t\t%-10llu", diff_stats.ipackets);
11107c478bd9Sstevel@tonic-gate 	(void) printf("%-12llu", diff_stats.rbytes);
11117c478bd9Sstevel@tonic-gate 	(void) printf("%-8u", diff_stats.ierrors);
11127c478bd9Sstevel@tonic-gate 	(void) printf("%-10llu", diff_stats.opackets);
11137c478bd9Sstevel@tonic-gate 	(void) printf("%-12llu", diff_stats.obytes);
11147c478bd9Sstevel@tonic-gate 	(void) printf("%-8u\n", diff_stats.oerrors);
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	state->ms_prevstats = stats;
11177c478bd9Sstevel@tonic-gate }
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate static void
11207c478bd9Sstevel@tonic-gate do_show_link(int argc, char *argv[])
11217c478bd9Sstevel@tonic-gate {
11227c478bd9Sstevel@tonic-gate 	char		*name = NULL;
11237c478bd9Sstevel@tonic-gate 	int		option;
11247c478bd9Sstevel@tonic-gate 	boolean_t	s_arg = B_FALSE;
11257c478bd9Sstevel@tonic-gate 	boolean_t	i_arg = B_FALSE;
112633343a97Smeem 	int		interval = 0;
11277c478bd9Sstevel@tonic-gate 	show_link_state_t state;
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	state.ls_stats = B_FALSE;
11307c478bd9Sstevel@tonic-gate 	state.ls_parseable = B_FALSE;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	opterr = 0;
11337c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":psi:",
11347c478bd9Sstevel@tonic-gate 	    longopts, NULL)) != -1) {
11357c478bd9Sstevel@tonic-gate 		switch (option) {
11367c478bd9Sstevel@tonic-gate 		case 'p':
11377c478bd9Sstevel@tonic-gate 			state.ls_parseable = B_TRUE;
11387c478bd9Sstevel@tonic-gate 			break;
11397c478bd9Sstevel@tonic-gate 		case 's':
114033343a97Smeem 			if (s_arg)
114133343a97Smeem 				die_optdup(option);
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 			s_arg = B_TRUE;
11447c478bd9Sstevel@tonic-gate 			break;
11457c478bd9Sstevel@tonic-gate 		case 'i':
114633343a97Smeem 			if (i_arg)
114733343a97Smeem 				die_optdup(option);
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 			i_arg = B_TRUE;
115033343a97Smeem 			if (!str2int(optarg, &interval) || interval == 0)
115133343a97Smeem 				die("invalid interval value '%s'", optarg);
11527c478bd9Sstevel@tonic-gate 			break;
11537c478bd9Sstevel@tonic-gate 		default:
115433343a97Smeem 			die_opterr(optopt, option);
115533343a97Smeem 			break;
11567c478bd9Sstevel@tonic-gate 		}
11577c478bd9Sstevel@tonic-gate 	}
11587c478bd9Sstevel@tonic-gate 
115933343a97Smeem 	if (i_arg && !s_arg)
116033343a97Smeem 		die("the option -i can be used only with -s");
11617c478bd9Sstevel@tonic-gate 
11627c478bd9Sstevel@tonic-gate 	/* get link name (optional last argument) */
11637c478bd9Sstevel@tonic-gate 	if (optind == (argc-1))
11647c478bd9Sstevel@tonic-gate 		name = argv[optind];
11657c478bd9Sstevel@tonic-gate 	else if (optind != argc)
11667c478bd9Sstevel@tonic-gate 		usage();
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate 	if (s_arg) {
11697c478bd9Sstevel@tonic-gate 		link_stats(name, interval);
11707c478bd9Sstevel@tonic-gate 		return;
11717c478bd9Sstevel@tonic-gate 	}
11727c478bd9Sstevel@tonic-gate 
1173210db224Sericheng 	if (name == NULL) {
11747c478bd9Sstevel@tonic-gate 		(void) dladm_walk(show_link, &state);
1175210db224Sericheng 	} else {
11767c478bd9Sstevel@tonic-gate 		show_link(&state, name);
11777c478bd9Sstevel@tonic-gate 	}
1178210db224Sericheng }
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate static void
11817c478bd9Sstevel@tonic-gate do_show_aggr(int argc, char *argv[])
11827c478bd9Sstevel@tonic-gate {
11837c478bd9Sstevel@tonic-gate 	int			option;
118433343a97Smeem 	int			key = 0;
11857c478bd9Sstevel@tonic-gate 	boolean_t		L_arg = B_FALSE;
11867c478bd9Sstevel@tonic-gate 	boolean_t		s_arg = B_FALSE;
11877c478bd9Sstevel@tonic-gate 	boolean_t		i_arg = B_FALSE;
11887c478bd9Sstevel@tonic-gate 	show_grp_state_t	state;
118933343a97Smeem 	int			interval = 0;
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 	state.gs_stats = B_FALSE;
11927c478bd9Sstevel@tonic-gate 	state.gs_lacp = B_FALSE;
11937c478bd9Sstevel@tonic-gate 	state.gs_parseable = B_FALSE;
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate 	opterr = 0;
11967c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":Lpsi:",
11977c478bd9Sstevel@tonic-gate 	    longopts, NULL)) != -1) {
11987c478bd9Sstevel@tonic-gate 		switch (option) {
11997c478bd9Sstevel@tonic-gate 		case 'L':
120033343a97Smeem 			if (L_arg)
120133343a97Smeem 				die_optdup(option);
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 			if (s_arg || i_arg) {
120433343a97Smeem 				die("the option -L cannot be used with -i "
120533343a97Smeem 				    "or -s");
12067c478bd9Sstevel@tonic-gate 			}
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 			L_arg = B_TRUE;
12097c478bd9Sstevel@tonic-gate 			state.gs_lacp = B_TRUE;
12107c478bd9Sstevel@tonic-gate 			break;
12117c478bd9Sstevel@tonic-gate 		case 'p':
12127c478bd9Sstevel@tonic-gate 			state.gs_parseable = B_TRUE;
12137c478bd9Sstevel@tonic-gate 			break;
12147c478bd9Sstevel@tonic-gate 		case 's':
121533343a97Smeem 			if (s_arg)
121633343a97Smeem 				die_optdup(option);
12177c478bd9Sstevel@tonic-gate 
121833343a97Smeem 			if (L_arg)
121913994ee8Sxz162242 				die("the option -s cannot be used with -L");
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate 			s_arg = B_TRUE;
12227c478bd9Sstevel@tonic-gate 			break;
12237c478bd9Sstevel@tonic-gate 		case 'i':
122433343a97Smeem 			if (i_arg)
122533343a97Smeem 				die_optdup(option);
12267c478bd9Sstevel@tonic-gate 
122733343a97Smeem 			if (L_arg)
122833343a97Smeem 				die("the option -i cannot be used with -L");
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate 			i_arg = B_TRUE;
123133343a97Smeem 			if (!str2int(optarg, &interval) || interval == 0)
123233343a97Smeem 				die("invalid interval value '%s'", optarg);
12337c478bd9Sstevel@tonic-gate 			break;
12347c478bd9Sstevel@tonic-gate 		default:
123533343a97Smeem 			die_opterr(optopt, option);
123633343a97Smeem 			break;
12377c478bd9Sstevel@tonic-gate 		}
12387c478bd9Sstevel@tonic-gate 	}
12397c478bd9Sstevel@tonic-gate 
124033343a97Smeem 	if (i_arg && !s_arg)
124133343a97Smeem 		die("the option -i can be used only with -s");
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 	/* get aggregation key (optional last argument) */
12447c478bd9Sstevel@tonic-gate 	if (optind == (argc-1)) {
124533343a97Smeem 		if (!str2int(argv[optind], &key) || key < 1)
124633343a97Smeem 			die("invalid key value '%s'", argv[optind]);
12477c478bd9Sstevel@tonic-gate 	} else if (optind != argc) {
12487c478bd9Sstevel@tonic-gate 		usage();
12497c478bd9Sstevel@tonic-gate 	}
12507c478bd9Sstevel@tonic-gate 
12517c478bd9Sstevel@tonic-gate 	if (s_arg) {
12527c478bd9Sstevel@tonic-gate 		aggr_stats(key, interval);
12537c478bd9Sstevel@tonic-gate 		return;
12547c478bd9Sstevel@tonic-gate 	}
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	state.gs_key = key;
12577c478bd9Sstevel@tonic-gate 	state.gs_found = B_FALSE;
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	(void) laadm_walk_sys(show_key, &state);
12607c478bd9Sstevel@tonic-gate 
126133343a97Smeem 	if (key != 0 && !state.gs_found)
126233343a97Smeem 		die("non-existent aggregation key '%u'", key);
12637c478bd9Sstevel@tonic-gate }
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate static void
12667c478bd9Sstevel@tonic-gate do_show_dev(int argc, char *argv[])
12677c478bd9Sstevel@tonic-gate {
12687c478bd9Sstevel@tonic-gate 	int		option;
12697c478bd9Sstevel@tonic-gate 	char		*dev = NULL;
12707c478bd9Sstevel@tonic-gate 	boolean_t	s_arg = B_FALSE;
12717c478bd9Sstevel@tonic-gate 	boolean_t	i_arg = B_FALSE;
127233343a97Smeem 	int		interval = 0;
12737c478bd9Sstevel@tonic-gate 	show_mac_state_t state;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	state.ms_parseable = B_FALSE;
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	opterr = 0;
12787c478bd9Sstevel@tonic-gate 	while ((option = getopt_long(argc, argv, ":psi:",
12797c478bd9Sstevel@tonic-gate 	    longopts, NULL)) != -1) {
12807c478bd9Sstevel@tonic-gate 		switch (option) {
12817c478bd9Sstevel@tonic-gate 		case 'p':
12827c478bd9Sstevel@tonic-gate 			state.ms_parseable = B_TRUE;
12837c478bd9Sstevel@tonic-gate 			break;
12847c478bd9Sstevel@tonic-gate 		case 's':
128533343a97Smeem 			if (s_arg)
128633343a97Smeem 				die_optdup(option);
12877c478bd9Sstevel@tonic-gate 
12887c478bd9Sstevel@tonic-gate 			s_arg = B_TRUE;
12897c478bd9Sstevel@tonic-gate 			break;
12907c478bd9Sstevel@tonic-gate 		case 'i':
129133343a97Smeem 			if (i_arg)
129233343a97Smeem 				die_optdup(option);
12937c478bd9Sstevel@tonic-gate 
12947c478bd9Sstevel@tonic-gate 			i_arg = B_TRUE;
129533343a97Smeem 			if (!str2int(optarg, &interval) || interval == 0)
129633343a97Smeem 				die("invalid interval value '%s'", optarg);
12977c478bd9Sstevel@tonic-gate 			break;
12987c478bd9Sstevel@tonic-gate 		default:
129933343a97Smeem 			die_opterr(optopt, option);
130033343a97Smeem 			break;
13017c478bd9Sstevel@tonic-gate 		}
13027c478bd9Sstevel@tonic-gate 	}
13037c478bd9Sstevel@tonic-gate 
130433343a97Smeem 	if (i_arg && !s_arg)
130533343a97Smeem 		die("the option -i can be used only with -s");
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 	/* get dev name (optional last argument) */
13087c478bd9Sstevel@tonic-gate 	if (optind == (argc-1))
13097c478bd9Sstevel@tonic-gate 		dev = argv[optind];
13107c478bd9Sstevel@tonic-gate 	else if (optind != argc)
13117c478bd9Sstevel@tonic-gate 		usage();
13127c478bd9Sstevel@tonic-gate 
1313cd93090eSericheng 	if (dev != NULL) {
1314*c7e4935fSss150715 		uint_t		ppa;
1315*c7e4935fSss150715 		char		drv[DLPI_LINKNAME_MAX];
1316cd93090eSericheng 		dladm_attr_t	dlattr;
1317cd93090eSericheng 		boolean_t	legacy;
1318cd93090eSericheng 
1319cd93090eSericheng 		/*
1320cd93090eSericheng 		 * Check for invalid devices.
1321cd93090eSericheng 		 * aggregations and vlans are not considered devices.
1322cd93090eSericheng 		 */
1323*c7e4935fSss150715 		if (dlpi_parselink(dev, drv, &ppa) != DLPI_SUCCESS ||
1324*c7e4935fSss150715 		    strcmp(drv, "aggr") == 0 || ppa >= 1000 ||
1325*c7e4935fSss150715 		    get_if_info(dev, &dlattr, &legacy) < 0)
132633343a97Smeem 			die("invalid device '%s'", dev);
1327cd93090eSericheng 	}
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	if (s_arg) {
13307c478bd9Sstevel@tonic-gate 		dev_stats(dev, interval);
13317c478bd9Sstevel@tonic-gate 		return;
13327c478bd9Sstevel@tonic-gate 	}
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 	if (dev == NULL)
13357c478bd9Sstevel@tonic-gate 		(void) macadm_walk(show_dev, &state, B_TRUE);
13367c478bd9Sstevel@tonic-gate 	else
1337210db224Sericheng 		show_dev(&state, dev);
13387c478bd9Sstevel@tonic-gate }
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate /* ARGSUSED */
13417c478bd9Sstevel@tonic-gate static void
134233343a97Smeem link_stats(const char *link, uint_t interval)
13437c478bd9Sstevel@tonic-gate {
1344210db224Sericheng 	dladm_attr_t		dlattr;
1345210db224Sericheng 	boolean_t		legacy;
13467c478bd9Sstevel@tonic-gate 	show_link_state_t	state;
13477c478bd9Sstevel@tonic-gate 
134833343a97Smeem 	if (link != NULL && get_if_info(link, &dlattr, &legacy) < 0)
134933343a97Smeem 		die("invalid link '%s'", link);
135033343a97Smeem 
13517c478bd9Sstevel@tonic-gate 	bzero(&state, sizeof (state));
13527c478bd9Sstevel@tonic-gate 
13537c478bd9Sstevel@tonic-gate 	/*
13547c478bd9Sstevel@tonic-gate 	 * If an interval is specified, continuously show the stats
13557c478bd9Sstevel@tonic-gate 	 * only for the first MAC port.
13567c478bd9Sstevel@tonic-gate 	 */
13577c478bd9Sstevel@tonic-gate 	state.ls_firstonly = (interval != 0);
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	for (;;) {
13607c478bd9Sstevel@tonic-gate 		(void) printf("\t\tipackets  rbytes	 ierrors ");
13617c478bd9Sstevel@tonic-gate 		(void) printf("opackets	 obytes	     oerrors\n");
13627c478bd9Sstevel@tonic-gate 
13637c478bd9Sstevel@tonic-gate 		state.ls_donefirst = B_FALSE;
13647c478bd9Sstevel@tonic-gate 		if (link == NULL)
13657c478bd9Sstevel@tonic-gate 			(void) dladm_walk(show_link_stats, &state);
13667c478bd9Sstevel@tonic-gate 		else
13677c478bd9Sstevel@tonic-gate 			show_link_stats(&state, link);
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 		if (interval == 0)
13707c478bd9Sstevel@tonic-gate 			break;
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate 		(void) sleep(interval);
13737c478bd9Sstevel@tonic-gate 	}
13747c478bd9Sstevel@tonic-gate }
13757c478bd9Sstevel@tonic-gate 
13767c478bd9Sstevel@tonic-gate /* ARGSUSED */
13777c478bd9Sstevel@tonic-gate static void
137833343a97Smeem aggr_stats(uint32_t key, uint_t interval)
13797c478bd9Sstevel@tonic-gate {
13807c478bd9Sstevel@tonic-gate 	show_grp_state_t state;
13817c478bd9Sstevel@tonic-gate 
13827c478bd9Sstevel@tonic-gate 	bzero(&state, sizeof (state));
13837c478bd9Sstevel@tonic-gate 	state.gs_stats = B_TRUE;
13847c478bd9Sstevel@tonic-gate 	state.gs_key = key;
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 	/*
13877c478bd9Sstevel@tonic-gate 	 * If an interval is specified, continuously show the stats
13887c478bd9Sstevel@tonic-gate 	 * only for the first group.
13897c478bd9Sstevel@tonic-gate 	 */
13907c478bd9Sstevel@tonic-gate 	state.gs_firstonly = (interval != 0);
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	for (;;) {
13937c478bd9Sstevel@tonic-gate 		state.gs_found = B_FALSE;
13947c478bd9Sstevel@tonic-gate 		(void) laadm_walk_sys(show_key, &state);
139533343a97Smeem 		if (state.gs_key != 0 && !state.gs_found)
139633343a97Smeem 			die("non-existent aggregation key '%u'", key);
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate 		if (interval == 0)
13997c478bd9Sstevel@tonic-gate 			break;
14007c478bd9Sstevel@tonic-gate 
14017c478bd9Sstevel@tonic-gate 		(void) sleep(interval);
14027c478bd9Sstevel@tonic-gate 	}
14037c478bd9Sstevel@tonic-gate }
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate /* ARGSUSED */
14067c478bd9Sstevel@tonic-gate static void
14077c478bd9Sstevel@tonic-gate dev_stats(const char *dev, uint32_t interval)
14087c478bd9Sstevel@tonic-gate {
14097c478bd9Sstevel@tonic-gate 	show_mac_state_t state;
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate 	bzero(&state, sizeof (state));
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	/*
14147c478bd9Sstevel@tonic-gate 	 * If an interval is specified, continuously show the stats
14157c478bd9Sstevel@tonic-gate 	 * only for the first MAC port.
14167c478bd9Sstevel@tonic-gate 	 */
14177c478bd9Sstevel@tonic-gate 	state.ms_firstonly = (interval != 0);
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	for (;;) {
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate 		(void) printf("\t\tipackets  rbytes	 ierrors ");
14227c478bd9Sstevel@tonic-gate 		(void) printf("opackets	 obytes	     oerrors\n");
14237c478bd9Sstevel@tonic-gate 
14247c478bd9Sstevel@tonic-gate 		state.ms_donefirst = B_FALSE;
1425210db224Sericheng 		if (dev == NULL)
14267c478bd9Sstevel@tonic-gate 			(void) macadm_walk(show_dev_stats, &state, B_TRUE);
1427210db224Sericheng 		else
1428210db224Sericheng 			show_dev_stats(&state, dev);
14297c478bd9Sstevel@tonic-gate 
14307c478bd9Sstevel@tonic-gate 		if (interval == 0)
14317c478bd9Sstevel@tonic-gate 			break;
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate 		(void) sleep(interval);
14347c478bd9Sstevel@tonic-gate 	}
14357c478bd9Sstevel@tonic-gate }
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate /* accumulate stats (s1 += (s2 - s3)) */
14387c478bd9Sstevel@tonic-gate static void
14397c478bd9Sstevel@tonic-gate stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
14407c478bd9Sstevel@tonic-gate {
14417c478bd9Sstevel@tonic-gate 	s1->ipackets += (s2->ipackets - s3->ipackets);
14427c478bd9Sstevel@tonic-gate 	s1->opackets += (s2->opackets - s3->opackets);
14437c478bd9Sstevel@tonic-gate 	s1->rbytes += (s2->rbytes - s3->rbytes);
14447c478bd9Sstevel@tonic-gate 	s1->obytes += (s2->obytes - s3->obytes);
14457c478bd9Sstevel@tonic-gate 	s1->ierrors += (s2->ierrors - s3->ierrors);
14467c478bd9Sstevel@tonic-gate 	s1->oerrors += (s2->oerrors - s3->oerrors);
14477c478bd9Sstevel@tonic-gate }
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate /* compute stats differences (s1 = s2 - s3) */
14507c478bd9Sstevel@tonic-gate static void
14517c478bd9Sstevel@tonic-gate stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
14527c478bd9Sstevel@tonic-gate {
14537c478bd9Sstevel@tonic-gate 	s1->ipackets = s2->ipackets - s3->ipackets;
14547c478bd9Sstevel@tonic-gate 	s1->opackets = s2->opackets - s3->opackets;
14557c478bd9Sstevel@tonic-gate 	s1->rbytes = s2->rbytes - s3->rbytes;
14567c478bd9Sstevel@tonic-gate 	s1->obytes = s2->obytes - s3->obytes;
14577c478bd9Sstevel@tonic-gate 	s1->ierrors = s2->ierrors - s3->ierrors;
14587c478bd9Sstevel@tonic-gate 	s1->oerrors = s2->oerrors - s3->oerrors;
14597c478bd9Sstevel@tonic-gate }
14607c478bd9Sstevel@tonic-gate 
1461cd93090eSericheng /*
1462ba2e4443Sseb  * In the following routines, we do the first kstat_lookup() assuming that
1463ba2e4443Sseb  * the device is gldv3-based and that the kstat name is the one passed in
1464ba2e4443Sseb  * as the "name" argument. If the lookup fails, we redo the kstat_lookup()
1465ba2e4443Sseb  * omitting the kstat name. This second lookup is needed for getting kstats
1466ba2e4443Sseb  * from legacy devices. This can fail too if the device is not attached or
1467ba2e4443Sseb  * the device is legacy and doesn't export the kstats we need.
1468cd93090eSericheng  */
14697c478bd9Sstevel@tonic-gate static void
14707c478bd9Sstevel@tonic-gate get_stats(char *module, int instance, char *name, pktsum_t *stats)
14717c478bd9Sstevel@tonic-gate {
14727c478bd9Sstevel@tonic-gate 	kstat_ctl_t	*kcp;
14737c478bd9Sstevel@tonic-gate 	kstat_t		*ksp;
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	if ((kcp = kstat_open()) == NULL) {
147633343a97Smeem 		warn("kstat open operation failed");
14777c478bd9Sstevel@tonic-gate 		return;
14787c478bd9Sstevel@tonic-gate 	}
14797c478bd9Sstevel@tonic-gate 
1480cd93090eSericheng 	if ((ksp = kstat_lookup(kcp, module, instance, name)) == NULL &&
1481ba2e4443Sseb 	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) {
14827c478bd9Sstevel@tonic-gate 		/*
14837c478bd9Sstevel@tonic-gate 		 * The kstat query could fail if the underlying MAC
14847c478bd9Sstevel@tonic-gate 		 * driver was already detached.
14857c478bd9Sstevel@tonic-gate 		 */
14867c478bd9Sstevel@tonic-gate 		(void) kstat_close(kcp);
14877c478bd9Sstevel@tonic-gate 		return;
14887c478bd9Sstevel@tonic-gate 	}
14897c478bd9Sstevel@tonic-gate 
14907c478bd9Sstevel@tonic-gate 	if (kstat_read(kcp, ksp, NULL) == -1)
14917c478bd9Sstevel@tonic-gate 		goto bail;
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 	if (kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
14947c478bd9Sstevel@tonic-gate 	    &stats->ipackets) < 0)
14957c478bd9Sstevel@tonic-gate 		goto bail;
14967c478bd9Sstevel@tonic-gate 
14977c478bd9Sstevel@tonic-gate 	if (kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
14987c478bd9Sstevel@tonic-gate 	    &stats->opackets) < 0)
14997c478bd9Sstevel@tonic-gate 		goto bail;
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 	if (kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
15027c478bd9Sstevel@tonic-gate 	    &stats->rbytes) < 0)
15037c478bd9Sstevel@tonic-gate 		goto bail;
15047c478bd9Sstevel@tonic-gate 
15057c478bd9Sstevel@tonic-gate 	if (kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
15067c478bd9Sstevel@tonic-gate 	    &stats->obytes) < 0)
15077c478bd9Sstevel@tonic-gate 		goto bail;
15087c478bd9Sstevel@tonic-gate 
15097c478bd9Sstevel@tonic-gate 	if (kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
15107c478bd9Sstevel@tonic-gate 	    &stats->ierrors) < 0)
15117c478bd9Sstevel@tonic-gate 		goto bail;
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate 	if (kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
15147c478bd9Sstevel@tonic-gate 	    &stats->oerrors) < 0)
15157c478bd9Sstevel@tonic-gate 		goto bail;
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 	(void) kstat_close(kcp);
15187c478bd9Sstevel@tonic-gate 	return;
15197c478bd9Sstevel@tonic-gate 
15207c478bd9Sstevel@tonic-gate bail:
15217c478bd9Sstevel@tonic-gate 	(void) kstat_close(kcp);
15227c478bd9Sstevel@tonic-gate }
15237c478bd9Sstevel@tonic-gate 
15247c478bd9Sstevel@tonic-gate static void
1525ba2e4443Sseb get_mac_stats(const char *dev, pktsum_t *stats)
15267c478bd9Sstevel@tonic-gate {
1527*c7e4935fSss150715 	char	module[DLPI_LINKNAME_MAX];
1528*c7e4935fSss150715 	uint_t	instance;
15297c478bd9Sstevel@tonic-gate 
1530*c7e4935fSss150715 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
1531ba2e4443Sseb 		return;
15327c478bd9Sstevel@tonic-gate 	bzero(stats, sizeof (*stats));
1533ba2e4443Sseb 	get_stats(module, instance, "mac", stats);
15347c478bd9Sstevel@tonic-gate }
15357c478bd9Sstevel@tonic-gate 
15367c478bd9Sstevel@tonic-gate static void
15377c478bd9Sstevel@tonic-gate get_link_stats(const char *link, pktsum_t *stats)
15387c478bd9Sstevel@tonic-gate {
1539*c7e4935fSss150715 	char	module[DLPI_LINKNAME_MAX];
1540*c7e4935fSss150715 	uint_t	instance;
1541ba2e4443Sseb 
1542*c7e4935fSss150715 	if (dlpi_parselink(link, module, &instance) != DLPI_SUCCESS)
1543ba2e4443Sseb 		return;
15447c478bd9Sstevel@tonic-gate 	bzero(stats, sizeof (*stats));
1545ba2e4443Sseb 	get_stats(module, instance, (char *)link, stats);
15467c478bd9Sstevel@tonic-gate }
15477c478bd9Sstevel@tonic-gate 
1548ba2e4443Sseb static int
1549ba2e4443Sseb get_single_mac_stat(const char *dev, const char *name, uint8_t type,
1550ba2e4443Sseb     void *val)
15517c478bd9Sstevel@tonic-gate {
1552*c7e4935fSss150715 	char		module[DLPI_LINKNAME_MAX];
1553*c7e4935fSss150715 	uint_t		instance;
15547c478bd9Sstevel@tonic-gate 	kstat_ctl_t	*kcp;
15557c478bd9Sstevel@tonic-gate 	kstat_t		*ksp;
15567c478bd9Sstevel@tonic-gate 
1557*c7e4935fSss150715 	if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
1558*c7e4935fSss150715 		return (-1);
1559*c7e4935fSss150715 
15607c478bd9Sstevel@tonic-gate 	if ((kcp = kstat_open()) == NULL) {
156133343a97Smeem 		warn("kstat open operation failed");
1562ba2e4443Sseb 		return (-1);
15637c478bd9Sstevel@tonic-gate 	}
15647c478bd9Sstevel@tonic-gate 
1565ba2e4443Sseb 	if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
1566ba2e4443Sseb 	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) {
15677c478bd9Sstevel@tonic-gate 		/*
15687c478bd9Sstevel@tonic-gate 		 * The kstat query could fail if the underlying MAC
15697c478bd9Sstevel@tonic-gate 		 * driver was already detached.
15707c478bd9Sstevel@tonic-gate 		 */
15717c478bd9Sstevel@tonic-gate 		goto bail;
15727c478bd9Sstevel@tonic-gate 	}
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 	if (kstat_read(kcp, ksp, NULL) == -1) {
157533343a97Smeem 		warn("kstat read failed");
15767c478bd9Sstevel@tonic-gate 		goto bail;
15777c478bd9Sstevel@tonic-gate 	}
15787c478bd9Sstevel@tonic-gate 
1579ba2e4443Sseb 	if (kstat_value(ksp, name, type, val) < 0)
15807c478bd9Sstevel@tonic-gate 		goto bail;
1581ba2e4443Sseb 
1582ba2e4443Sseb 	(void) kstat_close(kcp);
1583ba2e4443Sseb 	return (0);
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate bail:
15867c478bd9Sstevel@tonic-gate 	(void) kstat_close(kcp);
1587ba2e4443Sseb 	return (-1);
1588ba2e4443Sseb }
1589ba2e4443Sseb 
1590ba2e4443Sseb static uint64_t
1591ba2e4443Sseb mac_ifspeed(const char *dev)
1592ba2e4443Sseb {
1593ba2e4443Sseb 	uint64_t ifspeed = 0;
1594ba2e4443Sseb 
1595ba2e4443Sseb 	(void) get_single_mac_stat(dev, "ifspeed", KSTAT_DATA_UINT64, &ifspeed);
15967c478bd9Sstevel@tonic-gate 	return (ifspeed);
15977c478bd9Sstevel@tonic-gate }
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate static char *
1600ba2e4443Sseb mac_link_state(const char *dev)
16017c478bd9Sstevel@tonic-gate {
16027c478bd9Sstevel@tonic-gate 	link_state_t	link_state;
16037c478bd9Sstevel@tonic-gate 	char		*state_str = "unknown";
16047c478bd9Sstevel@tonic-gate 
1605ba2e4443Sseb 	if (get_single_mac_stat(dev, "link_state", KSTAT_DATA_UINT32,
1606ba2e4443Sseb 	    &link_state) != 0) {
16077c478bd9Sstevel@tonic-gate 		return (state_str);
16087c478bd9Sstevel@tonic-gate 	}
16097c478bd9Sstevel@tonic-gate 
16107c478bd9Sstevel@tonic-gate 	switch (link_state) {
16117c478bd9Sstevel@tonic-gate 	case LINK_STATE_UP:
16127c478bd9Sstevel@tonic-gate 		state_str = "up";
16137c478bd9Sstevel@tonic-gate 		break;
16147c478bd9Sstevel@tonic-gate 	case LINK_STATE_DOWN:
16157c478bd9Sstevel@tonic-gate 		state_str = "down";
16167c478bd9Sstevel@tonic-gate 		break;
16177c478bd9Sstevel@tonic-gate 	default:
16187c478bd9Sstevel@tonic-gate 		break;
16197c478bd9Sstevel@tonic-gate 	}
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 	return (state_str);
16227c478bd9Sstevel@tonic-gate }
16237c478bd9Sstevel@tonic-gate 
16247c478bd9Sstevel@tonic-gate 
16257c478bd9Sstevel@tonic-gate static char *
1626ba2e4443Sseb mac_link_duplex(const char *dev)
16277c478bd9Sstevel@tonic-gate {
16287c478bd9Sstevel@tonic-gate 	link_duplex_t	link_duplex;
16297c478bd9Sstevel@tonic-gate 	char		*duplex_str = "unknown";
16307c478bd9Sstevel@tonic-gate 
1631ba2e4443Sseb 	if (get_single_mac_stat(dev, "link_duplex", KSTAT_DATA_UINT32,
1632ba2e4443Sseb 	    &link_duplex) != 0) {
16337c478bd9Sstevel@tonic-gate 		return (duplex_str);
16347c478bd9Sstevel@tonic-gate 	}
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 	switch (link_duplex) {
16377c478bd9Sstevel@tonic-gate 	case LINK_DUPLEX_FULL:
16387c478bd9Sstevel@tonic-gate 		duplex_str = "full";
16397c478bd9Sstevel@tonic-gate 		break;
16407c478bd9Sstevel@tonic-gate 	case LINK_DUPLEX_HALF:
16417c478bd9Sstevel@tonic-gate 		duplex_str = "half";
16427c478bd9Sstevel@tonic-gate 		break;
16437c478bd9Sstevel@tonic-gate 	default:
16447c478bd9Sstevel@tonic-gate 		break;
16457c478bd9Sstevel@tonic-gate 	}
16467c478bd9Sstevel@tonic-gate 
16477c478bd9Sstevel@tonic-gate 	return (duplex_str);
16487c478bd9Sstevel@tonic-gate }
16490ba2cbe9Sxc151355 
16500ba2cbe9Sxc151355 #define	WIFI_CMD_SCAN	0x00000001
16510ba2cbe9Sxc151355 #define	WIFI_CMD_SHOW	0x00000002
16520ba2cbe9Sxc151355 #define	WIFI_CMD_ALL	(WIFI_CMD_SCAN | WIFI_CMD_SHOW)
16530ba2cbe9Sxc151355 typedef struct wifi_field {
16540ba2cbe9Sxc151355 	const char	*wf_name;
16550ba2cbe9Sxc151355 	const char	*wf_header;
16560ba2cbe9Sxc151355 	uint_t		wf_width;
16570ba2cbe9Sxc151355 	uint_t		wf_mask;
16580ba2cbe9Sxc151355 	uint_t		wf_cmdtype;
16590ba2cbe9Sxc151355 } wifi_field_t;
16600ba2cbe9Sxc151355 
16610ba2cbe9Sxc151355 static wifi_field_t wifi_fields[] = {
16620ba2cbe9Sxc151355 { "link",	"LINK",		10,	0,			WIFI_CMD_ALL},
16630ba2cbe9Sxc151355 { "essid",	"ESSID",	19,	WLADM_WLAN_ATTR_ESSID,	WIFI_CMD_ALL},
16640ba2cbe9Sxc151355 { "bssid",	"BSSID/IBSSID", 17,	WLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
16650ba2cbe9Sxc151355 { "ibssid",	"BSSID/IBSSID", 17,	WLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
16660ba2cbe9Sxc151355 { "mode",	"MODE",		6,	WLADM_WLAN_ATTR_MODE,	WIFI_CMD_ALL},
16670ba2cbe9Sxc151355 { "speed",	"SPEED",	6,	WLADM_WLAN_ATTR_SPEED,	WIFI_CMD_ALL},
166813994ee8Sxz162242 { "auth",	"AUTH",		8,	WLADM_WLAN_ATTR_AUTH,	WIFI_CMD_SHOW},
16690ba2cbe9Sxc151355 { "bsstype",	"BSSTYPE",	8,	WLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL},
16700ba2cbe9Sxc151355 { "sec",	"SEC",		6,	WLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL},
16710ba2cbe9Sxc151355 { "status",	"STATUS",	17,	WLADM_LINK_ATTR_STATUS, WIFI_CMD_SHOW},
16720ba2cbe9Sxc151355 { "strength",	"STRENGTH",	10,	WLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}}
16730ba2cbe9Sxc151355 ;
16740ba2cbe9Sxc151355 
16750ba2cbe9Sxc151355 static char *all_scan_wifi_fields =
167613994ee8Sxz162242 	"link,essid,bssid,sec,strength,mode,speed,bsstype";
16770ba2cbe9Sxc151355 static char *all_show_wifi_fields =
16780ba2cbe9Sxc151355 	"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
16790ba2cbe9Sxc151355 static char *def_scan_wifi_fields =
16800ba2cbe9Sxc151355 	"link,essid,bssid,sec,strength,mode,speed";
16810ba2cbe9Sxc151355 static char *def_show_wifi_fields =
16820ba2cbe9Sxc151355 	"link,status,essid,sec,strength,mode,speed";
16830ba2cbe9Sxc151355 
16840ba2cbe9Sxc151355 #define	WIFI_MAX_FIELDS		(sizeof (wifi_fields) / sizeof (wifi_field_t))
16850ba2cbe9Sxc151355 #define	WIFI_MAX_FIELD_LEN	32
16860ba2cbe9Sxc151355 
16870ba2cbe9Sxc151355 typedef struct {
16880ba2cbe9Sxc151355 	char	*s_buf;
16890ba2cbe9Sxc151355 	char	**s_fields;	/* array of pointer to the fields in s_buf */
16900ba2cbe9Sxc151355 	uint_t	s_nfields;	/* the number of fields in s_buf */
16910ba2cbe9Sxc151355 } split_t;
16920ba2cbe9Sxc151355 
16930ba2cbe9Sxc151355 /*
16940ba2cbe9Sxc151355  * Free the split_t structure pointed to by `sp'.
16950ba2cbe9Sxc151355  */
16960ba2cbe9Sxc151355 static void
16970ba2cbe9Sxc151355 splitfree(split_t *sp)
16980ba2cbe9Sxc151355 {
16990ba2cbe9Sxc151355 	free(sp->s_buf);
17000ba2cbe9Sxc151355 	free(sp->s_fields);
17010ba2cbe9Sxc151355 	free(sp);
17020ba2cbe9Sxc151355 }
17030ba2cbe9Sxc151355 
17040ba2cbe9Sxc151355 /*
17050ba2cbe9Sxc151355  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
17060ba2cbe9Sxc151355  * length.  Return a pointer to a split_t containing the split fields, or NULL
17070ba2cbe9Sxc151355  * on failure.
17080ba2cbe9Sxc151355  */
17090ba2cbe9Sxc151355 static split_t *
17100ba2cbe9Sxc151355 split(const char *str, uint_t maxfields, uint_t maxlen)
17110ba2cbe9Sxc151355 {
17120ba2cbe9Sxc151355 	char	*field, *token, *lasts = NULL;
17130ba2cbe9Sxc151355 	split_t	*sp;
17140ba2cbe9Sxc151355 
17150ba2cbe9Sxc151355 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
17160ba2cbe9Sxc151355 		return (NULL);
17170ba2cbe9Sxc151355 
17180ba2cbe9Sxc151355 	sp = calloc(sizeof (split_t), 1);
17190ba2cbe9Sxc151355 	if (sp == NULL)
17200ba2cbe9Sxc151355 		return (NULL);
17210ba2cbe9Sxc151355 
17220ba2cbe9Sxc151355 	sp->s_buf = strdup(str);
17230ba2cbe9Sxc151355 	sp->s_fields = malloc(sizeof (char *) * maxfields);
17240ba2cbe9Sxc151355 	if (sp->s_buf == NULL || sp->s_fields == NULL)
17250ba2cbe9Sxc151355 		goto fail;
17260ba2cbe9Sxc151355 
17270ba2cbe9Sxc151355 	token = sp->s_buf;
17280ba2cbe9Sxc151355 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
17290ba2cbe9Sxc151355 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
17300ba2cbe9Sxc151355 			goto fail;
17310ba2cbe9Sxc151355 		token = NULL;
17320ba2cbe9Sxc151355 		sp->s_fields[sp->s_nfields++] = field;
17330ba2cbe9Sxc151355 	}
17340ba2cbe9Sxc151355 	return (sp);
17350ba2cbe9Sxc151355 fail:
17360ba2cbe9Sxc151355 	splitfree(sp);
17370ba2cbe9Sxc151355 	return (NULL);
17380ba2cbe9Sxc151355 }
17390ba2cbe9Sxc151355 
17400ba2cbe9Sxc151355 static int
17410ba2cbe9Sxc151355 parse_wifi_fields(char *str, wifi_field_t ***fields, uint_t *countp,
17420ba2cbe9Sxc151355     uint_t cmdtype)
17430ba2cbe9Sxc151355 {
17440ba2cbe9Sxc151355 	uint_t		i, j;
17450ba2cbe9Sxc151355 	wifi_field_t	**wf = NULL;
17460ba2cbe9Sxc151355 	split_t		*sp;
17470ba2cbe9Sxc151355 	boolean_t	good_match = B_FALSE;
17480ba2cbe9Sxc151355 
17490ba2cbe9Sxc151355 	if (cmdtype == WIFI_CMD_SCAN) {
17500ba2cbe9Sxc151355 		if (str == NULL)
17510ba2cbe9Sxc151355 			str = def_scan_wifi_fields;
17520ba2cbe9Sxc151355 		if (strcasecmp(str, "all") == 0)
17530ba2cbe9Sxc151355 			str = all_scan_wifi_fields;
17540ba2cbe9Sxc151355 	} else if (cmdtype == WIFI_CMD_SHOW) {
17550ba2cbe9Sxc151355 		if (str == NULL)
17560ba2cbe9Sxc151355 			str = def_show_wifi_fields;
17570ba2cbe9Sxc151355 		if (strcasecmp(str, "all") == 0)
17580ba2cbe9Sxc151355 			str = all_show_wifi_fields;
17590ba2cbe9Sxc151355 	} else {
17600ba2cbe9Sxc151355 		return (-1);
17610ba2cbe9Sxc151355 	}
17620ba2cbe9Sxc151355 
17630ba2cbe9Sxc151355 	sp = split(str, WIFI_MAX_FIELDS, WIFI_MAX_FIELD_LEN);
17640ba2cbe9Sxc151355 	if (sp == NULL)
17650ba2cbe9Sxc151355 		return (-1);
17660ba2cbe9Sxc151355 
17670ba2cbe9Sxc151355 	wf = malloc(sp->s_nfields * sizeof (wifi_field_t *));
17680ba2cbe9Sxc151355 	if (wf == NULL)
17690ba2cbe9Sxc151355 		goto fail;
17700ba2cbe9Sxc151355 
17710ba2cbe9Sxc151355 	for (i = 0; i < sp->s_nfields; i++) {
17720ba2cbe9Sxc151355 		for (j = 0; j < WIFI_MAX_FIELDS; j++) {
17730ba2cbe9Sxc151355 			if (strcasecmp(sp->s_fields[i],
17740ba2cbe9Sxc151355 			    wifi_fields[j].wf_name) == 0) {
177513994ee8Sxz162242 				good_match = wifi_fields[j].
17760ba2cbe9Sxc151355 				    wf_cmdtype & cmdtype;
17770ba2cbe9Sxc151355 				break;
17780ba2cbe9Sxc151355 			}
17790ba2cbe9Sxc151355 		}
17800ba2cbe9Sxc151355 		if (!good_match)
17810ba2cbe9Sxc151355 			goto fail;
17820ba2cbe9Sxc151355 
17830ba2cbe9Sxc151355 		good_match = B_FALSE;
17840ba2cbe9Sxc151355 		wf[i] = &wifi_fields[j];
17850ba2cbe9Sxc151355 	}
17860ba2cbe9Sxc151355 	*countp = i;
17870ba2cbe9Sxc151355 	*fields = wf;
17880ba2cbe9Sxc151355 	splitfree(sp);
17890ba2cbe9Sxc151355 	return (0);
17900ba2cbe9Sxc151355 fail:
17910ba2cbe9Sxc151355 	free(wf);
17920ba2cbe9Sxc151355 	splitfree(sp);
17930ba2cbe9Sxc151355 	return (-1);
17940ba2cbe9Sxc151355 }
17950ba2cbe9Sxc151355 
17960ba2cbe9Sxc151355 typedef struct print_wifi_state {
17970ba2cbe9Sxc151355 	const char	*ws_link;
17980ba2cbe9Sxc151355 	boolean_t	ws_parseable;
17990ba2cbe9Sxc151355 	boolean_t	ws_header;
18000ba2cbe9Sxc151355 	wifi_field_t	**ws_fields;
18010ba2cbe9Sxc151355 	uint_t		ws_nfields;
18020ba2cbe9Sxc151355 	boolean_t	ws_lastfield;
18030ba2cbe9Sxc151355 	uint_t		ws_overflow;
18040ba2cbe9Sxc151355 } print_wifi_state_t;
18050ba2cbe9Sxc151355 
18060ba2cbe9Sxc151355 static void
18070ba2cbe9Sxc151355 print_wifi_head(print_wifi_state_t *statep)
18080ba2cbe9Sxc151355 {
18090ba2cbe9Sxc151355 	int		i;
18100ba2cbe9Sxc151355 	wifi_field_t	*wfp;
18110ba2cbe9Sxc151355 
18120ba2cbe9Sxc151355 	for (i = 0; i < statep->ws_nfields; i++) {
18130ba2cbe9Sxc151355 		wfp = statep->ws_fields[i];
18140ba2cbe9Sxc151355 		if (i + 1 < statep->ws_nfields)
18150ba2cbe9Sxc151355 			(void) printf("%-*s ", wfp->wf_width, wfp->wf_header);
18160ba2cbe9Sxc151355 		else
18170ba2cbe9Sxc151355 			(void) printf("%s", wfp->wf_header);
18180ba2cbe9Sxc151355 	}
18190ba2cbe9Sxc151355 	(void) printf("\n");
18200ba2cbe9Sxc151355 }
18210ba2cbe9Sxc151355 
18220ba2cbe9Sxc151355 static void
18230ba2cbe9Sxc151355 print_wifi_field(print_wifi_state_t *statep, wifi_field_t *wfp,
18240ba2cbe9Sxc151355     const char *value)
18250ba2cbe9Sxc151355 {
18260ba2cbe9Sxc151355 	uint_t	width = wfp->wf_width;
18270ba2cbe9Sxc151355 	uint_t	valwidth = strlen(value);
18280ba2cbe9Sxc151355 	uint_t	compress;
18290ba2cbe9Sxc151355 
18300ba2cbe9Sxc151355 	if (statep->ws_parseable) {
18310ba2cbe9Sxc151355 		(void) printf("%s=\"%s\"", wfp->wf_header, value);
18320ba2cbe9Sxc151355 	} else {
18330ba2cbe9Sxc151355 		if (value[0] == '\0')
18340ba2cbe9Sxc151355 			value = "--";
18350ba2cbe9Sxc151355 		if (statep->ws_lastfield) {
18360ba2cbe9Sxc151355 			(void) printf("%s", value);
18370ba2cbe9Sxc151355 			return;
18380ba2cbe9Sxc151355 		}
18390ba2cbe9Sxc151355 
18400ba2cbe9Sxc151355 		if (valwidth > width) {
18410ba2cbe9Sxc151355 			statep->ws_overflow += valwidth - width;
18420ba2cbe9Sxc151355 		} else if (valwidth < width && statep->ws_overflow > 0) {
18430ba2cbe9Sxc151355 			compress = min(statep->ws_overflow, width - valwidth);
18440ba2cbe9Sxc151355 			statep->ws_overflow -= compress;
18450ba2cbe9Sxc151355 			width -= compress;
18460ba2cbe9Sxc151355 		}
18470ba2cbe9Sxc151355 		(void) printf("%-*s", width, value);
18480ba2cbe9Sxc151355 	}
18490ba2cbe9Sxc151355 
18500ba2cbe9Sxc151355 	if (!statep->ws_lastfield)
18510ba2cbe9Sxc151355 		(void) putchar(' ');
18520ba2cbe9Sxc151355 }
18530ba2cbe9Sxc151355 
18540ba2cbe9Sxc151355 static void
18550ba2cbe9Sxc151355 print_wlan_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
18560ba2cbe9Sxc151355     wladm_wlan_attr_t *attrp)
18570ba2cbe9Sxc151355 {
18580ba2cbe9Sxc151355 	char		buf[WLADM_STRSIZE];
18590ba2cbe9Sxc151355 	const char	*str = "";
18600ba2cbe9Sxc151355 
18610ba2cbe9Sxc151355 	if (wfp->wf_mask == 0) {
18620ba2cbe9Sxc151355 		print_wifi_field(statep, wfp, statep->ws_link);
18630ba2cbe9Sxc151355 		return;
18640ba2cbe9Sxc151355 	}
18650ba2cbe9Sxc151355 
18660ba2cbe9Sxc151355 	if ((wfp->wf_mask & attrp->wa_valid) == 0) {
18670ba2cbe9Sxc151355 		print_wifi_field(statep, wfp, "");
18680ba2cbe9Sxc151355 		return;
18690ba2cbe9Sxc151355 	}
18700ba2cbe9Sxc151355 
18710ba2cbe9Sxc151355 	switch (wfp->wf_mask) {
18720ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_ESSID:
18730ba2cbe9Sxc151355 		str = wladm_essid2str(&attrp->wa_essid, buf);
18740ba2cbe9Sxc151355 		break;
18750ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_BSSID:
18760ba2cbe9Sxc151355 		str = wladm_bssid2str(&attrp->wa_bssid, buf);
18770ba2cbe9Sxc151355 		break;
18780ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_SECMODE:
18790ba2cbe9Sxc151355 		str = wladm_secmode2str(&attrp->wa_secmode, buf);
18800ba2cbe9Sxc151355 		break;
18810ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_STRENGTH:
18820ba2cbe9Sxc151355 		str = wladm_strength2str(&attrp->wa_strength, buf);
18830ba2cbe9Sxc151355 		break;
18840ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_MODE:
18850ba2cbe9Sxc151355 		str = wladm_mode2str(&attrp->wa_mode, buf);
18860ba2cbe9Sxc151355 		break;
18870ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_SPEED:
18880ba2cbe9Sxc151355 		str = wladm_speed2str(&attrp->wa_speed, buf);
18890ba2cbe9Sxc151355 		(void) strlcat(buf, "Mb", sizeof (buf));
18900ba2cbe9Sxc151355 		break;
18910ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_AUTH:
18920ba2cbe9Sxc151355 		str = wladm_auth2str(&attrp->wa_auth, buf);
18930ba2cbe9Sxc151355 		break;
18940ba2cbe9Sxc151355 	case WLADM_WLAN_ATTR_BSSTYPE:
18950ba2cbe9Sxc151355 		str = wladm_bsstype2str(&attrp->wa_bsstype, buf);
18960ba2cbe9Sxc151355 		break;
18970ba2cbe9Sxc151355 	}
18980ba2cbe9Sxc151355 
18990ba2cbe9Sxc151355 	print_wifi_field(statep, wfp, str);
19000ba2cbe9Sxc151355 }
19010ba2cbe9Sxc151355 
19020ba2cbe9Sxc151355 static boolean_t
19030ba2cbe9Sxc151355 print_scan_results(void *arg, wladm_wlan_attr_t *attrp)
19040ba2cbe9Sxc151355 {
19050ba2cbe9Sxc151355 	print_wifi_state_t	*statep = arg;
19060ba2cbe9Sxc151355 	int			i;
19070ba2cbe9Sxc151355 
19080ba2cbe9Sxc151355 	if (statep->ws_header) {
19090ba2cbe9Sxc151355 		statep->ws_header = B_FALSE;
19100ba2cbe9Sxc151355 		if (!statep->ws_parseable)
19110ba2cbe9Sxc151355 			print_wifi_head(statep);
19120ba2cbe9Sxc151355 	}
19130ba2cbe9Sxc151355 
19140ba2cbe9Sxc151355 	statep->ws_overflow = 0;
19150ba2cbe9Sxc151355 	for (i = 0; i < statep->ws_nfields; i++) {
19160ba2cbe9Sxc151355 		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
19170ba2cbe9Sxc151355 		print_wlan_attr(statep, statep->ws_fields[i], attrp);
19180ba2cbe9Sxc151355 	}
19190ba2cbe9Sxc151355 	(void) putchar('\n');
19200ba2cbe9Sxc151355 	return (B_TRUE);
19210ba2cbe9Sxc151355 }
19220ba2cbe9Sxc151355 
19230ba2cbe9Sxc151355 static boolean_t
19240ba2cbe9Sxc151355 scan_wifi(void *arg, const char *link)
19250ba2cbe9Sxc151355 {
19260ba2cbe9Sxc151355 	print_wifi_state_t	*statep = arg;
19270ba2cbe9Sxc151355 	wladm_status_t		status;
19280ba2cbe9Sxc151355 
19290ba2cbe9Sxc151355 	statep->ws_link = link;
19300ba2cbe9Sxc151355 	status = wladm_scan(link, statep, print_scan_results);
193133343a97Smeem 	if (status != WLADM_STATUS_OK)
193233343a97Smeem 		die_wlerr(status, "cannot scan link '%s'", link);
193333343a97Smeem 
19340ba2cbe9Sxc151355 	return (B_TRUE);
19350ba2cbe9Sxc151355 }
19360ba2cbe9Sxc151355 
19370ba2cbe9Sxc151355 static void
19380ba2cbe9Sxc151355 print_link_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
19390ba2cbe9Sxc151355     wladm_link_attr_t *attrp)
19400ba2cbe9Sxc151355 {
19410ba2cbe9Sxc151355 	char		buf[WLADM_STRSIZE];
19420ba2cbe9Sxc151355 	const char	*str = "";
19430ba2cbe9Sxc151355 
19440ba2cbe9Sxc151355 	if (strcmp(wfp->wf_name, "status") == 0) {
19450ba2cbe9Sxc151355 		if ((wfp->wf_mask & attrp->la_valid) != 0)
19460ba2cbe9Sxc151355 			str = wladm_linkstatus2str(&attrp->la_status, buf);
19470ba2cbe9Sxc151355 		print_wifi_field(statep, wfp, str);
19480ba2cbe9Sxc151355 		return;
19490ba2cbe9Sxc151355 	}
19500ba2cbe9Sxc151355 	print_wlan_attr(statep, wfp, &attrp->la_wlan_attr);
19510ba2cbe9Sxc151355 }
19520ba2cbe9Sxc151355 
19530ba2cbe9Sxc151355 static boolean_t
19540ba2cbe9Sxc151355 show_wifi(void *arg, const char *link)
19550ba2cbe9Sxc151355 {
19560ba2cbe9Sxc151355 	int			i;
19570ba2cbe9Sxc151355 	print_wifi_state_t	*statep = arg;
19580ba2cbe9Sxc151355 	wladm_link_attr_t	attr;
19590ba2cbe9Sxc151355 	wladm_status_t		status;
19600ba2cbe9Sxc151355 
19610ba2cbe9Sxc151355 	status = wladm_get_link_attr(link, &attr);
196233343a97Smeem 	if (status != WLADM_STATUS_OK)
196333343a97Smeem 		die_wlerr(status, "cannot get link attributes for '%s'", link);
19640ba2cbe9Sxc151355 
19650ba2cbe9Sxc151355 	if (statep->ws_header) {
19660ba2cbe9Sxc151355 		statep->ws_header = B_FALSE;
19670ba2cbe9Sxc151355 		if (!statep->ws_parseable)
19680ba2cbe9Sxc151355 			print_wifi_head(statep);
19690ba2cbe9Sxc151355 	}
19700ba2cbe9Sxc151355 
19710ba2cbe9Sxc151355 	statep->ws_link = link;
19720ba2cbe9Sxc151355 	statep->ws_overflow = 0;
19730ba2cbe9Sxc151355 	for (i = 0; i < statep->ws_nfields; i++) {
19740ba2cbe9Sxc151355 		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
19750ba2cbe9Sxc151355 		print_link_attr(statep, statep->ws_fields[i], &attr);
19760ba2cbe9Sxc151355 	}
19770ba2cbe9Sxc151355 	(void) putchar('\n');
19780ba2cbe9Sxc151355 	return (B_TRUE);
19790ba2cbe9Sxc151355 }
19800ba2cbe9Sxc151355 
19810ba2cbe9Sxc151355 static void
19820ba2cbe9Sxc151355 do_display_wifi(int argc, char **argv, int cmd)
19830ba2cbe9Sxc151355 {
19840ba2cbe9Sxc151355 	int			option;
19850ba2cbe9Sxc151355 	char			*fields_str = NULL;
19860ba2cbe9Sxc151355 	wifi_field_t		**fields;
19870ba2cbe9Sxc151355 	boolean_t		(*callback)(void *, const char *);
19880ba2cbe9Sxc151355 	uint_t			nfields;
19890ba2cbe9Sxc151355 	print_wifi_state_t	state;
19900ba2cbe9Sxc151355 	wladm_status_t		status;
19910ba2cbe9Sxc151355 
19920ba2cbe9Sxc151355 	if (cmd == WIFI_CMD_SCAN)
19930ba2cbe9Sxc151355 		callback = scan_wifi;
19940ba2cbe9Sxc151355 	else if (cmd == WIFI_CMD_SHOW)
19950ba2cbe9Sxc151355 		callback = show_wifi;
19960ba2cbe9Sxc151355 	else
19970ba2cbe9Sxc151355 		return;
19980ba2cbe9Sxc151355 
19990ba2cbe9Sxc151355 	state.ws_link = NULL;
20000ba2cbe9Sxc151355 	state.ws_parseable = B_FALSE;
20010ba2cbe9Sxc151355 	state.ws_header = B_TRUE;
20020ba2cbe9Sxc151355 	opterr = 0;
20030ba2cbe9Sxc151355 	while ((option = getopt_long(argc, argv, ":o:p",
20040ba2cbe9Sxc151355 	    wifi_longopts, NULL)) != -1) {
20050ba2cbe9Sxc151355 		switch (option) {
20060ba2cbe9Sxc151355 		case 'o':
20070ba2cbe9Sxc151355 			fields_str = optarg;
20080ba2cbe9Sxc151355 			break;
20090ba2cbe9Sxc151355 		case 'p':
20100ba2cbe9Sxc151355 			state.ws_parseable = B_TRUE;
20110ba2cbe9Sxc151355 			if (fields_str == NULL)
20120ba2cbe9Sxc151355 				fields_str = "all";
20130ba2cbe9Sxc151355 			break;
20140ba2cbe9Sxc151355 		default:
201533343a97Smeem 			die_opterr(optopt, option);
20160ba2cbe9Sxc151355 			break;
20170ba2cbe9Sxc151355 		}
20180ba2cbe9Sxc151355 	}
20190ba2cbe9Sxc151355 
20200ba2cbe9Sxc151355 	if (optind == (argc - 1))
20210ba2cbe9Sxc151355 		state.ws_link = argv[optind];
20220ba2cbe9Sxc151355 	else if (optind != argc)
20230ba2cbe9Sxc151355 		usage();
20240ba2cbe9Sxc151355 
202533343a97Smeem 	if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0)
202633343a97Smeem 		die("invalid field(s) specified");
202733343a97Smeem 
20280ba2cbe9Sxc151355 	state.ws_fields = fields;
20290ba2cbe9Sxc151355 	state.ws_nfields = nfields;
20300ba2cbe9Sxc151355 
20310ba2cbe9Sxc151355 	if (state.ws_link == NULL) {
20320ba2cbe9Sxc151355 		status = wladm_walk(&state, callback);
203333343a97Smeem 		if (status != WLADM_STATUS_OK)
203433343a97Smeem 			die_wlerr(status, "cannot walk wifi links");
20350ba2cbe9Sxc151355 	} else {
20360ba2cbe9Sxc151355 		(void) (*callback)(&state, state.ws_link);
20370ba2cbe9Sxc151355 	}
20380ba2cbe9Sxc151355 	free(fields);
20390ba2cbe9Sxc151355 }
20400ba2cbe9Sxc151355 
20410ba2cbe9Sxc151355 static void
20420ba2cbe9Sxc151355 do_scan_wifi(int argc, char **argv)
20430ba2cbe9Sxc151355 {
20440ba2cbe9Sxc151355 	do_display_wifi(argc, argv, WIFI_CMD_SCAN);
20450ba2cbe9Sxc151355 }
20460ba2cbe9Sxc151355 
20470ba2cbe9Sxc151355 static void
20480ba2cbe9Sxc151355 do_show_wifi(int argc, char **argv)
20490ba2cbe9Sxc151355 {
20500ba2cbe9Sxc151355 	do_display_wifi(argc, argv, WIFI_CMD_SHOW);
20510ba2cbe9Sxc151355 }
20520ba2cbe9Sxc151355 
20530ba2cbe9Sxc151355 typedef struct wlan_count_attr {
20540ba2cbe9Sxc151355 	uint_t		wc_count;
20550ba2cbe9Sxc151355 	const char	*wc_link;
20560ba2cbe9Sxc151355 } wlan_count_attr_t;
20570ba2cbe9Sxc151355 
20580ba2cbe9Sxc151355 static boolean_t
20590ba2cbe9Sxc151355 do_count_wlan(void *arg, const char *link)
20600ba2cbe9Sxc151355 {
206133343a97Smeem 	wlan_count_attr_t *cp = arg;
20620ba2cbe9Sxc151355 
20630ba2cbe9Sxc151355 	if (cp->wc_count == 0)
20640ba2cbe9Sxc151355 		cp->wc_link = strdup(link);
20650ba2cbe9Sxc151355 	cp->wc_count++;
20660ba2cbe9Sxc151355 	return (B_TRUE);
20670ba2cbe9Sxc151355 }
20680ba2cbe9Sxc151355 
20690ba2cbe9Sxc151355 static int
20700ba2cbe9Sxc151355 parse_wep_keys(char *str, wladm_wep_key_t **keys, uint_t *key_countp)
20710ba2cbe9Sxc151355 {
20720ba2cbe9Sxc151355 	uint_t		i;
20730ba2cbe9Sxc151355 	split_t		*sp;
20740ba2cbe9Sxc151355 	wladm_wep_key_t	*wk;
20750ba2cbe9Sxc151355 
20760ba2cbe9Sxc151355 	sp = split(str, WLADM_MAX_WEPKEYS, WLADM_MAX_WEPKEYNAME_LEN);
20770ba2cbe9Sxc151355 	if (sp == NULL)
20780ba2cbe9Sxc151355 		return (-1);
20790ba2cbe9Sxc151355 
20800ba2cbe9Sxc151355 	wk = malloc(sp->s_nfields * sizeof (wladm_wep_key_t));
20810ba2cbe9Sxc151355 	if (wk == NULL)
20820ba2cbe9Sxc151355 		goto fail;
20830ba2cbe9Sxc151355 
20840ba2cbe9Sxc151355 	for (i = 0; i < sp->s_nfields; i++) {
20850ba2cbe9Sxc151355 		char			*s;
20860ba2cbe9Sxc151355 		dladm_secobj_class_t	class;
20870ba2cbe9Sxc151355 		dladm_status_t		status;
20880ba2cbe9Sxc151355 
20890ba2cbe9Sxc151355 		(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
20900ba2cbe9Sxc151355 		    WLADM_MAX_WEPKEYNAME_LEN);
20910ba2cbe9Sxc151355 
20920ba2cbe9Sxc151355 		wk[i].wk_idx = 1;
20930ba2cbe9Sxc151355 		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
20940ba2cbe9Sxc151355 			if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
20950ba2cbe9Sxc151355 				goto fail;
20960ba2cbe9Sxc151355 
20970ba2cbe9Sxc151355 			wk[i].wk_idx = (uint_t)(s[1] - '0');
20980ba2cbe9Sxc151355 			*s = '\0';
20990ba2cbe9Sxc151355 		}
21000ba2cbe9Sxc151355 		wk[i].wk_len = WLADM_MAX_WEPKEY_LEN;
21010ba2cbe9Sxc151355 
21020ba2cbe9Sxc151355 		status = dladm_get_secobj(wk[i].wk_name, &class,
21030ba2cbe9Sxc151355 		    wk[i].wk_val, &wk[i].wk_len, 0);
21040ba2cbe9Sxc151355 		if (status != DLADM_STATUS_OK) {
21050ba2cbe9Sxc151355 			if (status == DLADM_STATUS_NOTFOUND) {
21060ba2cbe9Sxc151355 				status = dladm_get_secobj(wk[i].wk_name,
21070ba2cbe9Sxc151355 				    &class, wk[i].wk_val, &wk[i].wk_len,
21080ba2cbe9Sxc151355 				    DLADM_OPT_PERSIST);
21090ba2cbe9Sxc151355 			}
21100ba2cbe9Sxc151355 			if (status != DLADM_STATUS_OK)
21110ba2cbe9Sxc151355 				goto fail;
21120ba2cbe9Sxc151355 		}
21130ba2cbe9Sxc151355 	}
21140ba2cbe9Sxc151355 	*keys = wk;
21150ba2cbe9Sxc151355 	*key_countp = i;
21160ba2cbe9Sxc151355 	splitfree(sp);
21170ba2cbe9Sxc151355 	return (0);
21180ba2cbe9Sxc151355 fail:
21190ba2cbe9Sxc151355 	free(wk);
21200ba2cbe9Sxc151355 	splitfree(sp);
21210ba2cbe9Sxc151355 	return (-1);
21220ba2cbe9Sxc151355 }
21230ba2cbe9Sxc151355 
21240ba2cbe9Sxc151355 static void
21250ba2cbe9Sxc151355 do_connect_wifi(int argc, char **argv)
21260ba2cbe9Sxc151355 {
21270ba2cbe9Sxc151355 	int			option;
21280ba2cbe9Sxc151355 	wladm_wlan_attr_t	attr, *attrp;
21290ba2cbe9Sxc151355 	wladm_status_t		status = WLADM_STATUS_OK;
21300ba2cbe9Sxc151355 	int			timeout = WLADM_CONNECT_TIMEOUT_DEFAULT;
21310ba2cbe9Sxc151355 	const char		*link = NULL;
21320ba2cbe9Sxc151355 	wladm_wep_key_t		*keys = NULL;
21330ba2cbe9Sxc151355 	uint_t			key_count = 0;
21340ba2cbe9Sxc151355 	uint_t			flags = 0;
21350ba2cbe9Sxc151355 	wladm_secmode_t		keysecmode = WLADM_SECMODE_NONE;
21360ba2cbe9Sxc151355 
21370ba2cbe9Sxc151355 	opterr = 0;
21380ba2cbe9Sxc151355 	(void) memset(&attr, 0, sizeof (attr));
21390ba2cbe9Sxc151355 	while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
21400ba2cbe9Sxc151355 	    wifi_longopts, NULL)) != -1) {
21410ba2cbe9Sxc151355 		switch (option) {
21420ba2cbe9Sxc151355 		case 'e':
21430ba2cbe9Sxc151355 			status = wladm_str2essid(optarg, &attr.wa_essid);
214433343a97Smeem 			if (status != WLADM_STATUS_OK)
214533343a97Smeem 				die("invalid ESSID '%s'", optarg);
214633343a97Smeem 
21470ba2cbe9Sxc151355 			attr.wa_valid |= WLADM_WLAN_ATTR_ESSID;
21480ba2cbe9Sxc151355 			/*
21490ba2cbe9Sxc151355 			 * Try to connect without doing a scan.
21500ba2cbe9Sxc151355 			 */
21510ba2cbe9Sxc151355 			flags |= WLADM_OPT_NOSCAN;
21520ba2cbe9Sxc151355 			break;
21530ba2cbe9Sxc151355 		case 'i':
21540ba2cbe9Sxc151355 			status = wladm_str2bssid(optarg, &attr.wa_bssid);
215533343a97Smeem 			if (status != WLADM_STATUS_OK)
215633343a97Smeem 				die("invalid BSSID %s", optarg);
215733343a97Smeem 
21580ba2cbe9Sxc151355 			attr.wa_valid |= WLADM_WLAN_ATTR_BSSID;
21590ba2cbe9Sxc151355 			break;
21600ba2cbe9Sxc151355 		case 'a':
21610ba2cbe9Sxc151355 			status = wladm_str2auth(optarg, &attr.wa_auth);
216233343a97Smeem 			if (status != WLADM_STATUS_OK)
216333343a97Smeem 				die("invalid authentication mode '%s'", optarg);
216433343a97Smeem 
21650ba2cbe9Sxc151355 			attr.wa_valid |= WLADM_WLAN_ATTR_AUTH;
21660ba2cbe9Sxc151355 			break;
21670ba2cbe9Sxc151355 		case 'm':
21680ba2cbe9Sxc151355 			status = wladm_str2mode(optarg, &attr.wa_mode);
216933343a97Smeem 			if (status != WLADM_STATUS_OK)
217033343a97Smeem 				die("invalid mode '%s'", optarg);
217133343a97Smeem 
21720ba2cbe9Sxc151355 			attr.wa_valid |= WLADM_WLAN_ATTR_MODE;
21730ba2cbe9Sxc151355 			break;
21740ba2cbe9Sxc151355 		case 'b':
21750ba2cbe9Sxc151355 			status = wladm_str2bsstype(optarg, &attr.wa_bsstype);
217633343a97Smeem 			if (status != WLADM_STATUS_OK)
217733343a97Smeem 				die("invalid bsstype '%s'", optarg);
217833343a97Smeem 
21790ba2cbe9Sxc151355 			attr.wa_valid |= WLADM_WLAN_ATTR_BSSTYPE;
21800ba2cbe9Sxc151355 			break;
21810ba2cbe9Sxc151355 		case 's':
21820ba2cbe9Sxc151355 			status = wladm_str2secmode(optarg, &attr.wa_secmode);
218333343a97Smeem 			if (status != WLADM_STATUS_OK)
218433343a97Smeem 				die("invalid security mode '%s'", optarg);
218533343a97Smeem 
21860ba2cbe9Sxc151355 			attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE;
21870ba2cbe9Sxc151355 			break;
21880ba2cbe9Sxc151355 		case 'k':
218933343a97Smeem 			if (parse_wep_keys(optarg, &keys, &key_count) < 0)
219033343a97Smeem 				die("invalid key(s) '%s'", optarg);
219133343a97Smeem 
21920ba2cbe9Sxc151355 			keysecmode = WLADM_SECMODE_WEP;
21930ba2cbe9Sxc151355 			break;
21940ba2cbe9Sxc151355 		case 'T':
21950ba2cbe9Sxc151355 			if (strcasecmp(optarg, "forever") == 0) {
21960ba2cbe9Sxc151355 				timeout = -1;
21970ba2cbe9Sxc151355 				break;
21980ba2cbe9Sxc151355 			}
219933343a97Smeem 			if (!str2int(optarg, &timeout) || timeout < 0)
220033343a97Smeem 				die("invalid timeout value '%s'", optarg);
22010ba2cbe9Sxc151355 			break;
22020ba2cbe9Sxc151355 		case 'c':
22030ba2cbe9Sxc151355 			flags |= WLADM_OPT_CREATEIBSS;
22040ba2cbe9Sxc151355 			break;
22050ba2cbe9Sxc151355 		default:
220633343a97Smeem 			die_opterr(optopt, option);
22070ba2cbe9Sxc151355 			break;
22080ba2cbe9Sxc151355 		}
22090ba2cbe9Sxc151355 	}
22100ba2cbe9Sxc151355 
22110ba2cbe9Sxc151355 	if (keysecmode == WLADM_SECMODE_NONE) {
22120ba2cbe9Sxc151355 		if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 &&
221333343a97Smeem 		    attr.wa_secmode == WLADM_SECMODE_WEP)
221433343a97Smeem 			die("key required for security mode 'wep'");
22150ba2cbe9Sxc151355 	} else {
22160ba2cbe9Sxc151355 		if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 &&
221733343a97Smeem 		    attr.wa_secmode != keysecmode)
221833343a97Smeem 			die("incompatible -s and -k options");
22190ba2cbe9Sxc151355 	}
22200ba2cbe9Sxc151355 	attr.wa_secmode = keysecmode;
22210ba2cbe9Sxc151355 	attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE;
22220ba2cbe9Sxc151355 
22230ba2cbe9Sxc151355 	if (optind == (argc - 1))
22240ba2cbe9Sxc151355 		link = argv[optind];
22250ba2cbe9Sxc151355 	else if (optind != argc)
22260ba2cbe9Sxc151355 		usage();
22270ba2cbe9Sxc151355 
22280ba2cbe9Sxc151355 	if (link == NULL) {
22290ba2cbe9Sxc151355 		wlan_count_attr_t wcattr;
22300ba2cbe9Sxc151355 
22310ba2cbe9Sxc151355 		wcattr.wc_link = NULL;
22320ba2cbe9Sxc151355 		wcattr.wc_count = 0;
22330ba2cbe9Sxc151355 		(void) wladm_walk(&wcattr, do_count_wlan);
22340ba2cbe9Sxc151355 		if (wcattr.wc_count == 0) {
223533343a97Smeem 			die("no wifi links are available");
22360ba2cbe9Sxc151355 		} else if (wcattr.wc_count > 1) {
223733343a97Smeem 			die("link name is required when more than one wifi "
223833343a97Smeem 			    "link is available");
22390ba2cbe9Sxc151355 		}
22400ba2cbe9Sxc151355 		link = wcattr.wc_link;
22410ba2cbe9Sxc151355 	}
22420ba2cbe9Sxc151355 	attrp = (attr.wa_valid == 0) ? NULL : &attr;
224333343a97Smeem again:
22440ba2cbe9Sxc151355 	status = wladm_connect(link, attrp, timeout, keys, key_count, flags);
22450ba2cbe9Sxc151355 	if (status != WLADM_STATUS_OK) {
22460ba2cbe9Sxc151355 		if ((flags & WLADM_OPT_NOSCAN) != 0) {
22470ba2cbe9Sxc151355 			/*
224833343a97Smeem 			 * Try again with scanning and filtering.
22490ba2cbe9Sxc151355 			 */
22500ba2cbe9Sxc151355 			flags &= ~WLADM_OPT_NOSCAN;
225133343a97Smeem 			goto again;
22520ba2cbe9Sxc151355 		}
225333343a97Smeem 
22540ba2cbe9Sxc151355 		if (status == WLADM_STATUS_NOTFOUND) {
22550ba2cbe9Sxc151355 			if (attr.wa_valid == 0) {
225633343a97Smeem 				die("no wifi networks are available");
22570ba2cbe9Sxc151355 			} else {
225833343a97Smeem 				die("no wifi networks with the specified "
225933343a97Smeem 				    "criteria are available");
22600ba2cbe9Sxc151355 			}
22610ba2cbe9Sxc151355 		}
226213994ee8Sxz162242 		die_wlerr(status, "cannot connect link '%s'", link);
22630ba2cbe9Sxc151355 	}
22640ba2cbe9Sxc151355 	free(keys);
22650ba2cbe9Sxc151355 }
22660ba2cbe9Sxc151355 
22670ba2cbe9Sxc151355 /* ARGSUSED */
22680ba2cbe9Sxc151355 static boolean_t
22690ba2cbe9Sxc151355 do_all_disconnect_wifi(void *arg, const char *link)
22700ba2cbe9Sxc151355 {
22710ba2cbe9Sxc151355 	wladm_status_t	status;
22720ba2cbe9Sxc151355 
22730ba2cbe9Sxc151355 	status = wladm_disconnect(link);
227433343a97Smeem 	if (status != WLADM_STATUS_OK)
227533343a97Smeem 		warn_wlerr(status, "cannot disconnect link '%s'", link);
227633343a97Smeem 
22770ba2cbe9Sxc151355 	return (B_TRUE);
22780ba2cbe9Sxc151355 }
22790ba2cbe9Sxc151355 
22800ba2cbe9Sxc151355 static void
22810ba2cbe9Sxc151355 do_disconnect_wifi(int argc, char **argv)
22820ba2cbe9Sxc151355 {
22830ba2cbe9Sxc151355 	int			option;
22840ba2cbe9Sxc151355 	const char		*link = NULL;
22850ba2cbe9Sxc151355 	boolean_t		all_links = B_FALSE;
22860ba2cbe9Sxc151355 	wladm_status_t		status;
22870ba2cbe9Sxc151355 	wlan_count_attr_t	wcattr;
22880ba2cbe9Sxc151355 
22890ba2cbe9Sxc151355 	opterr = 0;
22900ba2cbe9Sxc151355 	while ((option = getopt_long(argc, argv, ":a",
22910ba2cbe9Sxc151355 	    wifi_longopts, NULL)) != -1) {
22920ba2cbe9Sxc151355 		switch (option) {
22930ba2cbe9Sxc151355 		case 'a':
22940ba2cbe9Sxc151355 			all_links = B_TRUE;
22950ba2cbe9Sxc151355 			break;
22960ba2cbe9Sxc151355 		default:
229733343a97Smeem 			die_opterr(optopt, option);
22980ba2cbe9Sxc151355 			break;
22990ba2cbe9Sxc151355 		}
23000ba2cbe9Sxc151355 	}
23010ba2cbe9Sxc151355 
23020ba2cbe9Sxc151355 	if (optind == (argc - 1))
23030ba2cbe9Sxc151355 		link = argv[optind];
23040ba2cbe9Sxc151355 	else if (optind != argc)
23050ba2cbe9Sxc151355 		usage();
23060ba2cbe9Sxc151355 
23070ba2cbe9Sxc151355 	if (link == NULL) {
23080ba2cbe9Sxc151355 		if (!all_links) {
23090ba2cbe9Sxc151355 			wcattr.wc_link = NULL;
23100ba2cbe9Sxc151355 			wcattr.wc_count = 0;
23110ba2cbe9Sxc151355 			(void) wladm_walk(&wcattr, do_count_wlan);
23120ba2cbe9Sxc151355 			if (wcattr.wc_count == 0) {
231333343a97Smeem 				die("no wifi links are available");
23140ba2cbe9Sxc151355 			} else if (wcattr.wc_count > 1) {
231533343a97Smeem 				die("link name is required when more than "
231633343a97Smeem 				    "one wifi link is available");
23170ba2cbe9Sxc151355 			}
23180ba2cbe9Sxc151355 			link = wcattr.wc_link;
23190ba2cbe9Sxc151355 		} else {
23200ba2cbe9Sxc151355 			(void) wladm_walk(&all_links, do_all_disconnect_wifi);
23210ba2cbe9Sxc151355 			return;
23220ba2cbe9Sxc151355 		}
23230ba2cbe9Sxc151355 	}
23240ba2cbe9Sxc151355 	status = wladm_disconnect(link);
232533343a97Smeem 	if (status != WLADM_STATUS_OK)
232633343a97Smeem 		die_wlerr(status, "cannot disconnect link '%s'", link);
23270ba2cbe9Sxc151355 }
23280ba2cbe9Sxc151355 
23290ba2cbe9Sxc151355 #define	MAX_PROPS		32
23300ba2cbe9Sxc151355 #define	MAX_PROP_VALS		32
23310ba2cbe9Sxc151355 #define	MAX_PROP_LINE		512
23320ba2cbe9Sxc151355 
23330ba2cbe9Sxc151355 typedef struct prop_info {
23340ba2cbe9Sxc151355 	char		*pi_name;
23350ba2cbe9Sxc151355 	char		*pi_val[MAX_PROP_VALS];
23360ba2cbe9Sxc151355 	uint_t		pi_count;
23370ba2cbe9Sxc151355 } prop_info_t;
23380ba2cbe9Sxc151355 
23390ba2cbe9Sxc151355 typedef struct prop_list {
23400ba2cbe9Sxc151355 	prop_info_t	pl_info[MAX_PROPS];
23410ba2cbe9Sxc151355 	uint_t		pl_count;
23420ba2cbe9Sxc151355 	char		*pl_buf;
23430ba2cbe9Sxc151355 } prop_list_t;
23440ba2cbe9Sxc151355 
23450ba2cbe9Sxc151355 typedef struct show_linkprop_state {
23460ba2cbe9Sxc151355 	const char	*ls_link;
23470ba2cbe9Sxc151355 	char		*ls_line;
23480ba2cbe9Sxc151355 	char		**ls_propvals;
2349f4b3ec61Sdh155122 	prop_list_t	*ls_proplist;
23500ba2cbe9Sxc151355 	boolean_t	ls_parseable;
23510ba2cbe9Sxc151355 	boolean_t	ls_persist;
23520ba2cbe9Sxc151355 	boolean_t	ls_header;
23530ba2cbe9Sxc151355 } show_linkprop_state_t;
23540ba2cbe9Sxc151355 
23550ba2cbe9Sxc151355 static void
23560ba2cbe9Sxc151355 free_props(prop_list_t *list)
23570ba2cbe9Sxc151355 {
23580ba2cbe9Sxc151355 	if (list != NULL) {
23590ba2cbe9Sxc151355 		free(list->pl_buf);
23600ba2cbe9Sxc151355 		free(list);
23610ba2cbe9Sxc151355 	}
23620ba2cbe9Sxc151355 }
23630ba2cbe9Sxc151355 
23640ba2cbe9Sxc151355 static int
23650ba2cbe9Sxc151355 parse_props(char *str, prop_list_t **listp, boolean_t novalues)
23660ba2cbe9Sxc151355 {
23670ba2cbe9Sxc151355 	prop_list_t	*list;
23680ba2cbe9Sxc151355 	prop_info_t	*pip;
23690ba2cbe9Sxc151355 	char		*buf, *curr;
23700ba2cbe9Sxc151355 	int		len, i;
23710ba2cbe9Sxc151355 
23720ba2cbe9Sxc151355 	list = malloc(sizeof (prop_list_t));
23730ba2cbe9Sxc151355 	if (list == NULL)
23740ba2cbe9Sxc151355 		return (-1);
23750ba2cbe9Sxc151355 
23760ba2cbe9Sxc151355 	list->pl_count = 0;
23770ba2cbe9Sxc151355 	list->pl_buf = buf = strdup(str);
23780ba2cbe9Sxc151355 	if (buf == NULL)
23790ba2cbe9Sxc151355 		goto fail;
23800ba2cbe9Sxc151355 
23810ba2cbe9Sxc151355 	curr = buf;
23820ba2cbe9Sxc151355 	len = strlen(buf);
23830ba2cbe9Sxc151355 	pip = NULL;
23840ba2cbe9Sxc151355 	for (i = 0; i < len; i++) {
23850ba2cbe9Sxc151355 		char		c = buf[i];
23860ba2cbe9Sxc151355 		boolean_t	match = (c == '=' || c == ',');
23870ba2cbe9Sxc151355 
23880ba2cbe9Sxc151355 		if (!match && i != len - 1)
23890ba2cbe9Sxc151355 			continue;
23900ba2cbe9Sxc151355 
23910ba2cbe9Sxc151355 		if (match) {
23920ba2cbe9Sxc151355 			buf[i] = '\0';
23930ba2cbe9Sxc151355 			if (*curr == '\0')
23940ba2cbe9Sxc151355 				goto fail;
23950ba2cbe9Sxc151355 		}
23960ba2cbe9Sxc151355 
23970ba2cbe9Sxc151355 		if (pip != NULL && c != '=') {
23980ba2cbe9Sxc151355 			if (pip->pi_count > MAX_PROP_VALS)
23990ba2cbe9Sxc151355 				goto fail;
24000ba2cbe9Sxc151355 
24010ba2cbe9Sxc151355 			if (novalues)
24020ba2cbe9Sxc151355 				goto fail;
24030ba2cbe9Sxc151355 
24040ba2cbe9Sxc151355 			pip->pi_val[pip->pi_count] = curr;
24050ba2cbe9Sxc151355 			pip->pi_count++;
24060ba2cbe9Sxc151355 		} else {
24070ba2cbe9Sxc151355 			if (list->pl_count > MAX_PROPS)
24080ba2cbe9Sxc151355 				goto fail;
24090ba2cbe9Sxc151355 
24100ba2cbe9Sxc151355 			pip = &list->pl_info[list->pl_count];
24110ba2cbe9Sxc151355 			pip->pi_name = curr;
24120ba2cbe9Sxc151355 			pip->pi_count = 0;
24130ba2cbe9Sxc151355 			list->pl_count++;
24140ba2cbe9Sxc151355 			if (c == ',')
24150ba2cbe9Sxc151355 				pip = NULL;
24160ba2cbe9Sxc151355 		}
24170ba2cbe9Sxc151355 		curr = buf + i + 1;
24180ba2cbe9Sxc151355 	}
24190ba2cbe9Sxc151355 	*listp = list;
24200ba2cbe9Sxc151355 	return (0);
24210ba2cbe9Sxc151355 
24220ba2cbe9Sxc151355 fail:
24230ba2cbe9Sxc151355 	free_props(list);
24240ba2cbe9Sxc151355 	return (-1);
24250ba2cbe9Sxc151355 }
24260ba2cbe9Sxc151355 
24270ba2cbe9Sxc151355 static void
24280ba2cbe9Sxc151355 print_linkprop_head(void)
24290ba2cbe9Sxc151355 {
2430f4b3ec61Sdh155122 	(void) printf("%-12s %-15s %-14s %-14s %-20s \n",
2431f4b3ec61Sdh155122 	    "LINK", "PROPERTY", "VALUE", "DEFAULT", "POSSIBLE");
24320ba2cbe9Sxc151355 }
24330ba2cbe9Sxc151355 
24340ba2cbe9Sxc151355 static void
24350ba2cbe9Sxc151355 print_linkprop(show_linkprop_state_t *statep, const char *propname,
24360ba2cbe9Sxc151355     dladm_prop_type_t type, const char *typename, const char *format,
24370ba2cbe9Sxc151355     char **pptr)
24380ba2cbe9Sxc151355 {
24390ba2cbe9Sxc151355 	int		i;
24400ba2cbe9Sxc151355 	char		*ptr, *lim;
24410ba2cbe9Sxc151355 	char		buf[DLADM_STRSIZE];
24420ba2cbe9Sxc151355 	char		*unknown = "?", *notsup = "";
24430ba2cbe9Sxc151355 	char		**propvals = statep->ls_propvals;
24440ba2cbe9Sxc151355 	uint_t		valcnt = MAX_PROP_VALS;
24450ba2cbe9Sxc151355 	dladm_status_t	status;
24460ba2cbe9Sxc151355 
24470ba2cbe9Sxc151355 	status = dladm_get_prop(statep->ls_link, type, propname,
24480ba2cbe9Sxc151355 	    propvals, &valcnt);
24490ba2cbe9Sxc151355 	if (status != DLADM_STATUS_OK) {
24500ba2cbe9Sxc151355 		if (status == DLADM_STATUS_NOTSUP || statep->ls_persist) {
24510ba2cbe9Sxc151355 			valcnt = 1;
24520ba2cbe9Sxc151355 			if (type == DLADM_PROP_VAL_CURRENT)
24530ba2cbe9Sxc151355 				propvals = &unknown;
24540ba2cbe9Sxc151355 			else
24550ba2cbe9Sxc151355 				propvals = &notsup;
24560ba2cbe9Sxc151355 		} else {
245733343a97Smeem 			die_dlerr(status, "cannot get link property '%s'",
245833343a97Smeem 			    propname);
24590ba2cbe9Sxc151355 		}
24600ba2cbe9Sxc151355 	}
24610ba2cbe9Sxc151355 
24620ba2cbe9Sxc151355 	ptr = buf;
24630ba2cbe9Sxc151355 	lim = buf + DLADM_STRSIZE;
24640ba2cbe9Sxc151355 	for (i = 0; i < valcnt; i++) {
24650ba2cbe9Sxc151355 		if (propvals[i][0] == '\0' && !statep->ls_parseable)
24660ba2cbe9Sxc151355 			ptr += snprintf(ptr, lim - ptr, "--,");
24670ba2cbe9Sxc151355 		else
24680ba2cbe9Sxc151355 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
24690ba2cbe9Sxc151355 		if (ptr >= lim)
24700ba2cbe9Sxc151355 			break;
24710ba2cbe9Sxc151355 	}
24720ba2cbe9Sxc151355 	if (valcnt > 0)
24730ba2cbe9Sxc151355 		buf[strlen(buf) - 1] = '\0';
24740ba2cbe9Sxc151355 
24750ba2cbe9Sxc151355 	lim = statep->ls_line + MAX_PROP_LINE;
24760ba2cbe9Sxc151355 	if (statep->ls_parseable) {
24770ba2cbe9Sxc151355 		*pptr += snprintf(*pptr, lim - *pptr,
24780ba2cbe9Sxc151355 		    "%s=\"%s\" ", typename, buf);
24790ba2cbe9Sxc151355 	} else {
24800ba2cbe9Sxc151355 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
24810ba2cbe9Sxc151355 	}
24820ba2cbe9Sxc151355 }
24830ba2cbe9Sxc151355 
24840ba2cbe9Sxc151355 static boolean_t
24850ba2cbe9Sxc151355 show_linkprop(void *arg, const char *propname)
24860ba2cbe9Sxc151355 {
24870ba2cbe9Sxc151355 	show_linkprop_state_t	*statep = arg;
24880ba2cbe9Sxc151355 	char			*ptr = statep->ls_line;
24890ba2cbe9Sxc151355 	char			*lim = ptr + MAX_PROP_LINE;
24900ba2cbe9Sxc151355 
2491f4b3ec61Sdh155122 	if (statep->ls_persist && dladm_is_prop_temponly(propname, NULL))
2492f4b3ec61Sdh155122 		return (B_TRUE);
2493f4b3ec61Sdh155122 
2494f4b3ec61Sdh155122 	if (statep->ls_parseable)
2495f4b3ec61Sdh155122 		ptr += snprintf(ptr, lim - ptr, "LINK=\"%s\" ",
2496f4b3ec61Sdh155122 		    statep->ls_link);
2497f4b3ec61Sdh155122 	else
2498f4b3ec61Sdh155122 		ptr += snprintf(ptr, lim - ptr, "%-12s ", statep->ls_link);
2499f4b3ec61Sdh155122 
25000ba2cbe9Sxc151355 	if (statep->ls_parseable)
25010ba2cbe9Sxc151355 		ptr += snprintf(ptr, lim - ptr, "PROPERTY=\"%s\" ", propname);
25020ba2cbe9Sxc151355 	else
25030ba2cbe9Sxc151355 		ptr += snprintf(ptr, lim - ptr, "%-15s ", propname);
25040ba2cbe9Sxc151355 
25050ba2cbe9Sxc151355 	print_linkprop(statep, propname,
25060ba2cbe9Sxc151355 	    statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
25070ba2cbe9Sxc151355 	    DLADM_PROP_VAL_CURRENT, "VALUE", "%-14s ", &ptr);
25080ba2cbe9Sxc151355 	print_linkprop(statep, propname, DLADM_PROP_VAL_DEFAULT,
25090ba2cbe9Sxc151355 	    "DEFAULT", "%-14s ", &ptr);
25100ba2cbe9Sxc151355 	print_linkprop(statep, propname, DLADM_PROP_VAL_MODIFIABLE,
2511f4b3ec61Sdh155122 	    "POSSIBLE", "%-20s ", &ptr);
25120ba2cbe9Sxc151355 
25130ba2cbe9Sxc151355 	if (statep->ls_header) {
25140ba2cbe9Sxc151355 		statep->ls_header = B_FALSE;
25150ba2cbe9Sxc151355 		if (!statep->ls_parseable)
25160ba2cbe9Sxc151355 			print_linkprop_head();
25170ba2cbe9Sxc151355 	}
25180ba2cbe9Sxc151355 	(void) printf("%s\n", statep->ls_line);
25190ba2cbe9Sxc151355 	return (B_TRUE);
25200ba2cbe9Sxc151355 }
25210ba2cbe9Sxc151355 
25220ba2cbe9Sxc151355 static void
25230ba2cbe9Sxc151355 do_show_linkprop(int argc, char **argv)
25240ba2cbe9Sxc151355 {
2525f4b3ec61Sdh155122 	int			option;
25260ba2cbe9Sxc151355 	prop_list_t		*proplist = NULL;
25270ba2cbe9Sxc151355 	show_linkprop_state_t	state;
25280ba2cbe9Sxc151355 
25290ba2cbe9Sxc151355 	opterr = 0;
25300ba2cbe9Sxc151355 	state.ls_link = NULL;
25310ba2cbe9Sxc151355 	state.ls_propvals = NULL;
25320ba2cbe9Sxc151355 	state.ls_line = NULL;
25330ba2cbe9Sxc151355 	state.ls_parseable = B_FALSE;
25340ba2cbe9Sxc151355 	state.ls_persist = B_FALSE;
25350ba2cbe9Sxc151355 	state.ls_header = B_TRUE;
25360ba2cbe9Sxc151355 	while ((option = getopt_long(argc, argv, ":p:cP",
25370ba2cbe9Sxc151355 	    prop_longopts, NULL)) != -1) {
25380ba2cbe9Sxc151355 		switch (option) {
25390ba2cbe9Sxc151355 		case 'p':
254033343a97Smeem 			if (parse_props(optarg, &proplist, B_TRUE) < 0)
254113994ee8Sxz162242 				die("invalid link properties specified");
25420ba2cbe9Sxc151355 			break;
25430ba2cbe9Sxc151355 		case 'c':
25440ba2cbe9Sxc151355 			state.ls_parseable = B_TRUE;
25450ba2cbe9Sxc151355 			break;
25460ba2cbe9Sxc151355 		case 'P':
25470ba2cbe9Sxc151355 			state.ls_persist = B_TRUE;
25480ba2cbe9Sxc151355 			break;
25490ba2cbe9Sxc151355 		default:
255033343a97Smeem 			die_opterr(optopt, option);
25510ba2cbe9Sxc151355 			break;
25520ba2cbe9Sxc151355 		}
25530ba2cbe9Sxc151355 	}
25540ba2cbe9Sxc151355 
25550ba2cbe9Sxc151355 	if (optind == (argc - 1))
25560ba2cbe9Sxc151355 		state.ls_link = argv[optind];
25570ba2cbe9Sxc151355 	else if (optind != argc)
25580ba2cbe9Sxc151355 		usage();
25590ba2cbe9Sxc151355 
2560f4b3ec61Sdh155122 	state.ls_proplist = proplist;
2561f4b3ec61Sdh155122 
2562f4b3ec61Sdh155122 	if (state.ls_link == NULL) {
2563f4b3ec61Sdh155122 		(void) dladm_walk(show_linkprop_onelink, &state);
2564f4b3ec61Sdh155122 	} else {
2565f4b3ec61Sdh155122 		show_linkprop_onelink(&state, state.ls_link);
2566f4b3ec61Sdh155122 	}
2567f4b3ec61Sdh155122 	free_props(proplist);
2568f4b3ec61Sdh155122 }
2569f4b3ec61Sdh155122 
2570f4b3ec61Sdh155122 static void
2571f4b3ec61Sdh155122 show_linkprop_onelink(void *arg, const char *link)
2572f4b3ec61Sdh155122 {
2573f4b3ec61Sdh155122 	int			i, fd;
2574f4b3ec61Sdh155122 	char			linkname[MAXPATHLEN];
2575f4b3ec61Sdh155122 	char			*buf;
2576f4b3ec61Sdh155122 	dladm_status_t		status;
2577f4b3ec61Sdh155122 	prop_list_t		*proplist = NULL;
2578f4b3ec61Sdh155122 	show_linkprop_state_t	*statep;
2579f4b3ec61Sdh155122 	const char		*savep;
2580f4b3ec61Sdh155122 
2581f4b3ec61Sdh155122 	statep = (show_linkprop_state_t *)arg;
2582f4b3ec61Sdh155122 	savep = statep->ls_link;
2583f4b3ec61Sdh155122 	statep->ls_link = link;
2584f4b3ec61Sdh155122 	proplist = statep->ls_proplist;
25850ba2cbe9Sxc151355 
25860ba2cbe9Sxc151355 	/*
25870ba2cbe9Sxc151355 	 * When some WiFi links are opened for the first time, their hardware
25880ba2cbe9Sxc151355 	 * automatically scans for APs and does other slow operations.	Thus,
25890ba2cbe9Sxc151355 	 * if there are no open links, the retrieval of link properties
25900ba2cbe9Sxc151355 	 * (below) will proceed slowly unless we hold the link open.
25910ba2cbe9Sxc151355 	 */
2592f4b3ec61Sdh155122 	(void) snprintf(linkname, MAXPATHLEN, "/dev/%s", link);
259333343a97Smeem 	if ((fd = open(linkname, O_RDWR)) < 0)
2594f4b3ec61Sdh155122 		die("cannot open %s: %s", link, strerror(errno));
25950ba2cbe9Sxc151355 
25960ba2cbe9Sxc151355 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS +
25970ba2cbe9Sxc151355 	    MAX_PROP_LINE);
259833343a97Smeem 	if (buf == NULL)
259933343a97Smeem 		die("insufficient memory");
260033343a97Smeem 
2601f4b3ec61Sdh155122 	statep->ls_propvals = (char **)(void *)buf;
26020ba2cbe9Sxc151355 	for (i = 0; i < MAX_PROP_VALS; i++) {
2603f4b3ec61Sdh155122 		statep->ls_propvals[i] = buf + sizeof (char *) * MAX_PROP_VALS +
26040ba2cbe9Sxc151355 		    i * DLADM_PROP_VAL_MAX;
26050ba2cbe9Sxc151355 	}
2606f4b3ec61Sdh155122 	statep->ls_line = buf +
26070ba2cbe9Sxc151355 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS;
26080ba2cbe9Sxc151355 
26090ba2cbe9Sxc151355 	if (proplist != NULL) {
26100ba2cbe9Sxc151355 		for (i = 0; i < proplist->pl_count; i++) {
2611f4b3ec61Sdh155122 			if (!show_linkprop(statep,
26120ba2cbe9Sxc151355 			    proplist->pl_info[i].pi_name))
26130ba2cbe9Sxc151355 				break;
26140ba2cbe9Sxc151355 		}
26150ba2cbe9Sxc151355 	} else {
2616f4b3ec61Sdh155122 		status = dladm_walk_prop(link, statep, show_linkprop);
261733343a97Smeem 		if (status != DLADM_STATUS_OK)
261833343a97Smeem 			die_dlerr(status, "show-linkprop");
26190ba2cbe9Sxc151355 	}
26200ba2cbe9Sxc151355 	(void) close(fd);
26210ba2cbe9Sxc151355 	free(buf);
2622f4b3ec61Sdh155122 	statep->ls_link = savep;
26230ba2cbe9Sxc151355 }
26240ba2cbe9Sxc151355 
26250ba2cbe9Sxc151355 static dladm_status_t
26260ba2cbe9Sxc151355 set_linkprop_persist(const char *link, const char *prop_name, char **prop_val,
26270ba2cbe9Sxc151355     uint_t val_cnt, boolean_t reset)
26280ba2cbe9Sxc151355 {
26290ba2cbe9Sxc151355 	dladm_status_t	status;
2630f4b3ec61Sdh155122 	char		*errprop;
26310ba2cbe9Sxc151355 
26320ba2cbe9Sxc151355 	status = dladm_set_prop(link, prop_name, prop_val, val_cnt,
2633f4b3ec61Sdh155122 	    DLADM_OPT_PERSIST, &errprop);
26340ba2cbe9Sxc151355 
26350ba2cbe9Sxc151355 	if (status != DLADM_STATUS_OK) {
26360ba2cbe9Sxc151355 		if (reset) {
263733343a97Smeem 			warn_dlerr(status, "cannot persistently reset link "
2638f4b3ec61Sdh155122 			    "property '%s' on '%s'", errprop, link);
26390ba2cbe9Sxc151355 		} else {
264033343a97Smeem 			warn_dlerr(status, "cannot persistently set link "
2641f4b3ec61Sdh155122 			    "property '%s' on '%s'", errprop, link);
26420ba2cbe9Sxc151355 		}
26430ba2cbe9Sxc151355 	}
26440ba2cbe9Sxc151355 	return (status);
26450ba2cbe9Sxc151355 }
26460ba2cbe9Sxc151355 
26470ba2cbe9Sxc151355 static void
26480ba2cbe9Sxc151355 set_linkprop(int argc, char **argv, boolean_t reset)
26490ba2cbe9Sxc151355 {
26500ba2cbe9Sxc151355 	int		i, option;
26510ba2cbe9Sxc151355 	char		errmsg[DLADM_STRSIZE];
26520ba2cbe9Sxc151355 	const char	*link = NULL;
26530ba2cbe9Sxc151355 	prop_list_t	*proplist = NULL;
26540ba2cbe9Sxc151355 	boolean_t	temp = B_FALSE;
26550ba2cbe9Sxc151355 	dladm_status_t	status = DLADM_STATUS_OK;
26560ba2cbe9Sxc151355 
26570ba2cbe9Sxc151355 	opterr = 0;
26580ba2cbe9Sxc151355 	while ((option = getopt_long(argc, argv, ":p:R:t",
26590ba2cbe9Sxc151355 	    prop_longopts, NULL)) != -1) {
26600ba2cbe9Sxc151355 		switch (option) {
26610ba2cbe9Sxc151355 		case 'p':
266233343a97Smeem 			if (parse_props(optarg, &proplist, reset) < 0)
266333343a97Smeem 				die("invalid link properties specified");
26640ba2cbe9Sxc151355 			break;
26650ba2cbe9Sxc151355 		case 't':
26660ba2cbe9Sxc151355 			temp = B_TRUE;
26670ba2cbe9Sxc151355 			break;
26680ba2cbe9Sxc151355 		case 'R':
26690ba2cbe9Sxc151355 			status = dladm_set_rootdir(optarg);
26700ba2cbe9Sxc151355 			if (status != DLADM_STATUS_OK) {
267133343a97Smeem 				die_dlerr(status, "invalid directory "
267233343a97Smeem 				    "specified");
26730ba2cbe9Sxc151355 			}
26740ba2cbe9Sxc151355 			break;
26750ba2cbe9Sxc151355 		default:
267633343a97Smeem 			die_opterr(optopt, option);
26770ba2cbe9Sxc151355 			break;
26780ba2cbe9Sxc151355 		}
26790ba2cbe9Sxc151355 	}
26800ba2cbe9Sxc151355 
26810ba2cbe9Sxc151355 	if (optind == (argc - 1))
26820ba2cbe9Sxc151355 		link = argv[optind];
26830ba2cbe9Sxc151355 	else if (optind != argc)
26840ba2cbe9Sxc151355 		usage();
26850ba2cbe9Sxc151355 
268633343a97Smeem 	if (link == NULL)
268733343a97Smeem 		die("link name must be specified");
26880ba2cbe9Sxc151355 
2689dbc95d79Sxz162242 	if (proplist == NULL) {
2690f4b3ec61Sdh155122 		char *errprop;
2691f4b3ec61Sdh155122 
2692dbc95d79Sxz162242 		if (!reset)
269333343a97Smeem 			die("link property must be specified");
269433343a97Smeem 
2695f4b3ec61Sdh155122 		status = dladm_set_prop(link, NULL, NULL, 0, DLADM_OPT_TEMP,
2696f4b3ec61Sdh155122 		    &errprop);
26970ba2cbe9Sxc151355 		if (status != DLADM_STATUS_OK) {
2698f4b3ec61Sdh155122 			warn_dlerr(status, "cannot reset link property '%s' "
2699f4b3ec61Sdh155122 			    "on '%s'", errprop, link);
27000ba2cbe9Sxc151355 		}
27010ba2cbe9Sxc151355 		if (!temp) {
270213994ee8Sxz162242 			dladm_status_t	s;
270313994ee8Sxz162242 
270413994ee8Sxz162242 			s = set_linkprop_persist(link, NULL, NULL, 0, reset);
270513994ee8Sxz162242 			if (s != DLADM_STATUS_OK)
270613994ee8Sxz162242 				status = s;
27070ba2cbe9Sxc151355 		}
27080ba2cbe9Sxc151355 		goto done;
27090ba2cbe9Sxc151355 	}
27100ba2cbe9Sxc151355 
27110ba2cbe9Sxc151355 	for (i = 0; i < proplist->pl_count; i++) {
27120ba2cbe9Sxc151355 		prop_info_t	*pip = &proplist->pl_info[i];
27130ba2cbe9Sxc151355 		char		**val;
27140ba2cbe9Sxc151355 		uint_t		count;
27150ba2cbe9Sxc151355 		dladm_status_t	s;
27160ba2cbe9Sxc151355 
27170ba2cbe9Sxc151355 		if (reset) {
27180ba2cbe9Sxc151355 			val = NULL;
27190ba2cbe9Sxc151355 			count = 0;
27200ba2cbe9Sxc151355 		} else {
27210ba2cbe9Sxc151355 			val = pip->pi_val;
27220ba2cbe9Sxc151355 			count = pip->pi_count;
27230ba2cbe9Sxc151355 			if (count == 0) {
272433343a97Smeem 				warn("no value specified for '%s'",
272533343a97Smeem 				    pip->pi_name);
27260ba2cbe9Sxc151355 				status = DLADM_STATUS_BADARG;
27270ba2cbe9Sxc151355 				continue;
27280ba2cbe9Sxc151355 			}
27290ba2cbe9Sxc151355 		}
27300ba2cbe9Sxc151355 		s = dladm_set_prop(link, pip->pi_name, val, count,
2731f4b3ec61Sdh155122 		    DLADM_OPT_TEMP, NULL);
27320ba2cbe9Sxc151355 		if (s == DLADM_STATUS_OK) {
27330ba2cbe9Sxc151355 			if (!temp) {
27340ba2cbe9Sxc151355 				s = set_linkprop_persist(link,
27350ba2cbe9Sxc151355 				    pip->pi_name, val, count, reset);
27360ba2cbe9Sxc151355 				if (s != DLADM_STATUS_OK)
27370ba2cbe9Sxc151355 					status = s;
27380ba2cbe9Sxc151355 			}
27390ba2cbe9Sxc151355 			continue;
27400ba2cbe9Sxc151355 		}
27410ba2cbe9Sxc151355 		status = s;
27420ba2cbe9Sxc151355 		switch (s) {
27430ba2cbe9Sxc151355 		case DLADM_STATUS_NOTFOUND:
274433343a97Smeem 			warn("invalid link property '%s'", pip->pi_name);
27450ba2cbe9Sxc151355 			break;
27460ba2cbe9Sxc151355 		case DLADM_STATUS_BADVAL: {
27470ba2cbe9Sxc151355 			int		j;
27480ba2cbe9Sxc151355 			char		*ptr, *lim;
27490ba2cbe9Sxc151355 			char		**propvals = NULL;
27500ba2cbe9Sxc151355 			uint_t		valcnt = MAX_PROP_VALS;
27510ba2cbe9Sxc151355 
27520ba2cbe9Sxc151355 			ptr = malloc((sizeof (char *) +
27530ba2cbe9Sxc151355 			    DLADM_PROP_VAL_MAX) * MAX_PROP_VALS +
27540ba2cbe9Sxc151355 			    MAX_PROP_LINE);
27550ba2cbe9Sxc151355 
27560ba2cbe9Sxc151355 			propvals = (char **)(void *)ptr;
275733343a97Smeem 			if (propvals == NULL)
275833343a97Smeem 				die("insufficient memory");
275933343a97Smeem 
27600ba2cbe9Sxc151355 			for (j = 0; j < MAX_PROP_VALS; j++) {
27610ba2cbe9Sxc151355 				propvals[j] = ptr + sizeof (char *) *
27620ba2cbe9Sxc151355 				    MAX_PROP_VALS +
27630ba2cbe9Sxc151355 				    j * DLADM_PROP_VAL_MAX;
27640ba2cbe9Sxc151355 			}
27650ba2cbe9Sxc151355 			s = dladm_get_prop(link, DLADM_PROP_VAL_MODIFIABLE,
27660ba2cbe9Sxc151355 			    pip->pi_name, propvals, &valcnt);
27670ba2cbe9Sxc151355 
27680ba2cbe9Sxc151355 			ptr = errmsg;
27690ba2cbe9Sxc151355 			lim = ptr + DLADM_STRSIZE;
27700ba2cbe9Sxc151355 			*ptr = '\0';
27710ba2cbe9Sxc151355 			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
27720ba2cbe9Sxc151355 				ptr += snprintf(ptr, lim - ptr, "%s,",
27730ba2cbe9Sxc151355 				    propvals[j]);
27740ba2cbe9Sxc151355 				if (ptr >= lim)
27750ba2cbe9Sxc151355 					break;
27760ba2cbe9Sxc151355 			}
2777f4b3ec61Sdh155122 			if (ptr > errmsg) {
27780ba2cbe9Sxc151355 				*(ptr - 1) = '\0';
277933343a97Smeem 				warn("link property '%s' must be one of: %s",
278033343a97Smeem 				    pip->pi_name, errmsg);
2781f4b3ec61Sdh155122 			} else
2782f4b3ec61Sdh155122 				warn("invalid link property '%s'", *val);
27830ba2cbe9Sxc151355 			free(propvals);
27840ba2cbe9Sxc151355 			break;
27850ba2cbe9Sxc151355 		}
27860ba2cbe9Sxc151355 		default:
27870ba2cbe9Sxc151355 			if (reset) {
278833343a97Smeem 				warn_dlerr(status, "cannot reset link property "
278933343a97Smeem 				    "'%s' on '%s'", pip->pi_name, link);
27900ba2cbe9Sxc151355 			} else {
279133343a97Smeem 				warn_dlerr(status, "cannot set link property "
279233343a97Smeem 				    "'%s' on '%s'", pip->pi_name, link);
27930ba2cbe9Sxc151355 			}
27940ba2cbe9Sxc151355 			break;
27950ba2cbe9Sxc151355 		}
27960ba2cbe9Sxc151355 	}
27970ba2cbe9Sxc151355 done:
27980ba2cbe9Sxc151355 	free_props(proplist);
27990ba2cbe9Sxc151355 	if (status != DLADM_STATUS_OK)
28000ba2cbe9Sxc151355 		exit(1);
28010ba2cbe9Sxc151355 }
28020ba2cbe9Sxc151355 
28030ba2cbe9Sxc151355 static void
28040ba2cbe9Sxc151355 do_set_linkprop(int argc, char **argv)
28050ba2cbe9Sxc151355 {
28060ba2cbe9Sxc151355 	set_linkprop(argc, argv, B_FALSE);
28070ba2cbe9Sxc151355 }
28080ba2cbe9Sxc151355 
28090ba2cbe9Sxc151355 static void
28100ba2cbe9Sxc151355 do_reset_linkprop(int argc, char **argv)
28110ba2cbe9Sxc151355 {
28120ba2cbe9Sxc151355 	set_linkprop(argc, argv, B_TRUE);
28130ba2cbe9Sxc151355 }
28140ba2cbe9Sxc151355 
28150ba2cbe9Sxc151355 static int
28160ba2cbe9Sxc151355 convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
28170ba2cbe9Sxc151355     dladm_secobj_class_t class)
28180ba2cbe9Sxc151355 {
28190ba2cbe9Sxc151355 	int error = 0;
28200ba2cbe9Sxc151355 
28210ba2cbe9Sxc151355 	if (class != DLADM_SECOBJ_CLASS_WEP)
28220ba2cbe9Sxc151355 		return (ENOENT);
28230ba2cbe9Sxc151355 
28240ba2cbe9Sxc151355 	switch (len) {
28250ba2cbe9Sxc151355 	case 5:			/* ASCII key sizes */
28260ba2cbe9Sxc151355 	case 13:
28270ba2cbe9Sxc151355 		(void) memcpy(obj_val, buf, len);
28280ba2cbe9Sxc151355 		*obj_lenp = len;
28290ba2cbe9Sxc151355 		break;
28300ba2cbe9Sxc151355 	case 10:		/* Hex key sizes, not preceded by 0x */
28310ba2cbe9Sxc151355 	case 26:
28320ba2cbe9Sxc151355 		error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
28330ba2cbe9Sxc151355 		break;
28340ba2cbe9Sxc151355 	case 12:		/* Hex key sizes, preceded by 0x */
28350ba2cbe9Sxc151355 	case 28:
28360ba2cbe9Sxc151355 		if (strncmp(buf, "0x", 2) != 0)
28370ba2cbe9Sxc151355 			return (EINVAL);
28380ba2cbe9Sxc151355 		error = hexascii_to_octet(buf + 2, len - 2, obj_val, obj_lenp);
28390ba2cbe9Sxc151355 		break;
28400ba2cbe9Sxc151355 	default:
28410ba2cbe9Sxc151355 		return (EINVAL);
28420ba2cbe9Sxc151355 	}
28430ba2cbe9Sxc151355 	return (error);
28440ba2cbe9Sxc151355 }
28450ba2cbe9Sxc151355 
28460ba2cbe9Sxc151355 /* ARGSUSED */
28470ba2cbe9Sxc151355 static void
28480ba2cbe9Sxc151355 defersig(int sig)
28490ba2cbe9Sxc151355 {
28500ba2cbe9Sxc151355 	signalled = sig;
28510ba2cbe9Sxc151355 }
28520ba2cbe9Sxc151355 
28530ba2cbe9Sxc151355 static int
28540ba2cbe9Sxc151355 get_secobj_from_tty(uint_t try, const char *objname, char *buf)
28550ba2cbe9Sxc151355 {
28560ba2cbe9Sxc151355 	uint_t		len = 0;
28570ba2cbe9Sxc151355 	int		c;
28580ba2cbe9Sxc151355 	struct termios	stored, current;
28590ba2cbe9Sxc151355 	void		(*sigfunc)(int);
28600ba2cbe9Sxc151355 
28610ba2cbe9Sxc151355 	/*
28620ba2cbe9Sxc151355 	 * Turn off echo -- but before we do so, defer SIGINT handling
28630ba2cbe9Sxc151355 	 * so that a ^C doesn't leave the terminal corrupted.
28640ba2cbe9Sxc151355 	 */
28650ba2cbe9Sxc151355 	sigfunc = signal(SIGINT, defersig);
28660ba2cbe9Sxc151355 	(void) fflush(stdin);
28670ba2cbe9Sxc151355 	(void) tcgetattr(0, &stored);
28680ba2cbe9Sxc151355 	current = stored;
28690ba2cbe9Sxc151355 	current.c_lflag &= ~(ICANON|ECHO);
28700ba2cbe9Sxc151355 	current.c_cc[VTIME] = 0;
28710ba2cbe9Sxc151355 	current.c_cc[VMIN] = 1;
28720ba2cbe9Sxc151355 	(void) tcsetattr(0, TCSANOW, &current);
28730ba2cbe9Sxc151355 again:
28740ba2cbe9Sxc151355 	if (try == 1)
28750ba2cbe9Sxc151355 		(void) printf(gettext("provide value for '%s': "), objname);
28760ba2cbe9Sxc151355 	else
28770ba2cbe9Sxc151355 		(void) printf(gettext("confirm value for '%s': "), objname);
28780ba2cbe9Sxc151355 
28790ba2cbe9Sxc151355 	(void) fflush(stdout);
28800ba2cbe9Sxc151355 	while (signalled == 0) {
28810ba2cbe9Sxc151355 		c = getchar();
28820ba2cbe9Sxc151355 		if (c == '\n' || c == '\r') {
28830ba2cbe9Sxc151355 			if (len != 0)
28840ba2cbe9Sxc151355 				break;
28850ba2cbe9Sxc151355 			(void) putchar('\n');
28860ba2cbe9Sxc151355 			goto again;
28870ba2cbe9Sxc151355 		}
28880ba2cbe9Sxc151355 
28890ba2cbe9Sxc151355 		buf[len++] = c;
28900ba2cbe9Sxc151355 		if (len >= DLADM_SECOBJ_VAL_MAX - 1)
28910ba2cbe9Sxc151355 			break;
28920ba2cbe9Sxc151355 		(void) putchar('*');
28930ba2cbe9Sxc151355 	}
28940ba2cbe9Sxc151355 
28950ba2cbe9Sxc151355 	(void) putchar('\n');
28960ba2cbe9Sxc151355 	(void) fflush(stdin);
28970ba2cbe9Sxc151355 
28980ba2cbe9Sxc151355 	/*
28990ba2cbe9Sxc151355 	 * Restore terminal setting and handle deferred signals.
29000ba2cbe9Sxc151355 	 */
29010ba2cbe9Sxc151355 	(void) tcsetattr(0, TCSANOW, &stored);
29020ba2cbe9Sxc151355 
29030ba2cbe9Sxc151355 	(void) signal(SIGINT, sigfunc);
29040ba2cbe9Sxc151355 	if (signalled != 0)
29050ba2cbe9Sxc151355 		(void) kill(getpid(), signalled);
29060ba2cbe9Sxc151355 
29070ba2cbe9Sxc151355 	return (len);
29080ba2cbe9Sxc151355 }
29090ba2cbe9Sxc151355 
29100ba2cbe9Sxc151355 static int
29110ba2cbe9Sxc151355 get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
29120ba2cbe9Sxc151355     dladm_secobj_class_t class, FILE *filep)
29130ba2cbe9Sxc151355 {
29140ba2cbe9Sxc151355 	int		rval;
29150ba2cbe9Sxc151355 	uint_t		len, len2;
29160ba2cbe9Sxc151355 	char		buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
29170ba2cbe9Sxc151355 
29180ba2cbe9Sxc151355 	if (filep == NULL) {
29190ba2cbe9Sxc151355 		len = get_secobj_from_tty(1, obj_name, buf);
29200ba2cbe9Sxc151355 		rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
29210ba2cbe9Sxc151355 		if (rval == 0) {
29220ba2cbe9Sxc151355 			len2 = get_secobj_from_tty(2, obj_name, buf2);
29230ba2cbe9Sxc151355 			if (len != len2 || memcmp(buf, buf2, len) != 0)
29240ba2cbe9Sxc151355 				rval = ENOTSUP;
29250ba2cbe9Sxc151355 		}
29260ba2cbe9Sxc151355 		return (rval);
29270ba2cbe9Sxc151355 	} else {
29280ba2cbe9Sxc151355 		for (;;) {
29290ba2cbe9Sxc151355 			if (fgets(buf, sizeof (buf), filep) == NULL)
29300ba2cbe9Sxc151355 				break;
29310ba2cbe9Sxc151355 			if (isspace(buf[0]))
29320ba2cbe9Sxc151355 				continue;
29330ba2cbe9Sxc151355 
29340ba2cbe9Sxc151355 			len = strlen(buf);
29350ba2cbe9Sxc151355 			if (buf[len - 1] == '\n') {
29360ba2cbe9Sxc151355 				buf[len - 1] = '\0';
29370ba2cbe9Sxc151355 				len--;
29380ba2cbe9Sxc151355 			}
29390ba2cbe9Sxc151355 			break;
29400ba2cbe9Sxc151355 		}
29410ba2cbe9Sxc151355 		(void) fclose(filep);
29420ba2cbe9Sxc151355 	}
29430ba2cbe9Sxc151355 	return (convert_secobj(buf, len, obj_val, obj_lenp, class));
29440ba2cbe9Sxc151355 }
29450ba2cbe9Sxc151355 
29460ba2cbe9Sxc151355 static boolean_t
29470ba2cbe9Sxc151355 check_auth(const char *auth)
29480ba2cbe9Sxc151355 {
29490ba2cbe9Sxc151355 	struct passwd	*pw;
29500ba2cbe9Sxc151355 
29510ba2cbe9Sxc151355 	if ((pw = getpwuid(getuid())) == NULL)
29520ba2cbe9Sxc151355 		return (B_FALSE);
29530ba2cbe9Sxc151355 
29540ba2cbe9Sxc151355 	return (chkauthattr(auth, pw->pw_name) != 0);
29550ba2cbe9Sxc151355 }
29560ba2cbe9Sxc151355 
29570ba2cbe9Sxc151355 static void
29580ba2cbe9Sxc151355 audit_secobj(char *auth, char *class, char *obj,
29590ba2cbe9Sxc151355     boolean_t success, boolean_t create)
29600ba2cbe9Sxc151355 {
29610ba2cbe9Sxc151355 	adt_session_data_t	*ah;
29620ba2cbe9Sxc151355 	adt_event_data_t	*event;
29630ba2cbe9Sxc151355 	au_event_t		flag;
29640ba2cbe9Sxc151355 	char			*errstr;
29650ba2cbe9Sxc151355 
29660ba2cbe9Sxc151355 	if (create) {
29670ba2cbe9Sxc151355 		flag = ADT_dladm_create_secobj;
29680ba2cbe9Sxc151355 		errstr = "ADT_dladm_create_secobj";
29690ba2cbe9Sxc151355 	} else {
29700ba2cbe9Sxc151355 		flag = ADT_dladm_delete_secobj;
29710ba2cbe9Sxc151355 		errstr = "ADT_dladm_delete_secobj";
29720ba2cbe9Sxc151355 	}
29730ba2cbe9Sxc151355 
297433343a97Smeem 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
297533343a97Smeem 		die("adt_start_session: %s", strerror(errno));
29760ba2cbe9Sxc151355 
297733343a97Smeem 	if ((event = adt_alloc_event(ah, flag)) == NULL)
297833343a97Smeem 		die("adt_alloc_event (%s): %s", errstr, strerror(errno));
29790ba2cbe9Sxc151355 
29800ba2cbe9Sxc151355 	/* fill in audit info */
29810ba2cbe9Sxc151355 	if (create) {
29820ba2cbe9Sxc151355 		event->adt_dladm_create_secobj.auth_used = auth;
29830ba2cbe9Sxc151355 		event->adt_dladm_create_secobj.obj_class = class;
29840ba2cbe9Sxc151355 		event->adt_dladm_create_secobj.obj_name = obj;
29850ba2cbe9Sxc151355 	} else {
29860ba2cbe9Sxc151355 		event->adt_dladm_delete_secobj.auth_used = auth;
29870ba2cbe9Sxc151355 		event->adt_dladm_delete_secobj.obj_class = class;
29880ba2cbe9Sxc151355 		event->adt_dladm_delete_secobj.obj_name = obj;
29890ba2cbe9Sxc151355 	}
29900ba2cbe9Sxc151355 
29910ba2cbe9Sxc151355 	if (success) {
29920ba2cbe9Sxc151355 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
299333343a97Smeem 			die("adt_put_event (%s, success): %s", errstr,
299433343a97Smeem 			    strerror(errno));
29950ba2cbe9Sxc151355 		}
29960ba2cbe9Sxc151355 	} else {
29970ba2cbe9Sxc151355 		if (adt_put_event(event, ADT_FAILURE,
29980ba2cbe9Sxc151355 		    ADT_FAIL_VALUE_AUTH) != 0) {
299933343a97Smeem 			die("adt_put_event: (%s, failure): %s", errstr,
300033343a97Smeem 			    strerror(errno));
30010ba2cbe9Sxc151355 		}
30020ba2cbe9Sxc151355 	}
30030ba2cbe9Sxc151355 
30040ba2cbe9Sxc151355 	adt_free_event(event);
30050ba2cbe9Sxc151355 	(void) adt_end_session(ah);
30060ba2cbe9Sxc151355 }
30070ba2cbe9Sxc151355 
30080ba2cbe9Sxc151355 #define	MAX_SECOBJS		32
30090ba2cbe9Sxc151355 #define	MAX_SECOBJ_NAMELEN	32
30100ba2cbe9Sxc151355 static void
30110ba2cbe9Sxc151355 do_create_secobj(int argc, char **argv)
30120ba2cbe9Sxc151355 {
30130ba2cbe9Sxc151355 	int			option, rval;
30140ba2cbe9Sxc151355 	FILE			*filep = NULL;
30150ba2cbe9Sxc151355 	char			*obj_name = NULL;
30160ba2cbe9Sxc151355 	char			*class_name = NULL;
30170ba2cbe9Sxc151355 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
30180ba2cbe9Sxc151355 	uint_t			obj_len;
30190ba2cbe9Sxc151355 	boolean_t		success, temp = B_FALSE;
30200ba2cbe9Sxc151355 	dladm_status_t		status;
30210ba2cbe9Sxc151355 	dladm_secobj_class_t	class = -1;
30220ba2cbe9Sxc151355 	uid_t			euid;
30230ba2cbe9Sxc151355 
30240ba2cbe9Sxc151355 	opterr = 0;
30250ba2cbe9Sxc151355 	(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
30260ba2cbe9Sxc151355 	while ((option = getopt_long(argc, argv, ":f:c:R:t",
30270ba2cbe9Sxc151355 	    wifi_longopts, NULL)) != -1) {
30280ba2cbe9Sxc151355 		switch (option) {
30290ba2cbe9Sxc151355 		case 'f':
30300ba2cbe9Sxc151355 			euid = geteuid();
30310ba2cbe9Sxc151355 			(void) seteuid(getuid());
30320ba2cbe9Sxc151355 			filep = fopen(optarg, "r");
30330ba2cbe9Sxc151355 			if (filep == NULL) {
303433343a97Smeem 				die("cannot open %s: %s", optarg,
303533343a97Smeem 				    strerror(errno));
30360ba2cbe9Sxc151355 			}
30370ba2cbe9Sxc151355 			(void) seteuid(euid);
30380ba2cbe9Sxc151355 			break;
30390ba2cbe9Sxc151355 		case 'c':
30400ba2cbe9Sxc151355 			class_name = optarg;
30410ba2cbe9Sxc151355 			status = dladm_str2secobjclass(optarg, &class);
30420ba2cbe9Sxc151355 			if (status != DLADM_STATUS_OK) {
304333343a97Smeem 				die("invalid secure object class '%s', "
304433343a97Smeem 				    "valid values are: wep", optarg);
30450ba2cbe9Sxc151355 			}
30460ba2cbe9Sxc151355 			break;
30470ba2cbe9Sxc151355 		case 't':
30480ba2cbe9Sxc151355 			temp = B_TRUE;
30490ba2cbe9Sxc151355 			break;
30500ba2cbe9Sxc151355 		case 'R':
30510ba2cbe9Sxc151355 			status = dladm_set_rootdir(optarg);
30520ba2cbe9Sxc151355 			if (status != DLADM_STATUS_OK) {
305333343a97Smeem 				die_dlerr(status, "invalid directory "
305433343a97Smeem 				    "specified");
30550ba2cbe9Sxc151355 			}
30560ba2cbe9Sxc151355 			break;
30570ba2cbe9Sxc151355 		default:
305833343a97Smeem 			die_opterr(optopt, option);
30590ba2cbe9Sxc151355 			break;
30600ba2cbe9Sxc151355 		}
30610ba2cbe9Sxc151355 	}
30620ba2cbe9Sxc151355 
30630ba2cbe9Sxc151355 	if (optind == (argc - 1))
30640ba2cbe9Sxc151355 		obj_name = argv[optind];
30650ba2cbe9Sxc151355 	else if (optind != argc)
30660ba2cbe9Sxc151355 		usage();
30670ba2cbe9Sxc151355 
306833343a97Smeem 	if (class == -1)
306933343a97Smeem 		die("secure object class required");
30700ba2cbe9Sxc151355 
307133343a97Smeem 	if (obj_name == NULL)
307233343a97Smeem 		die("secure object name required");
30730ba2cbe9Sxc151355 
30740ba2cbe9Sxc151355 	success = check_auth(LINK_SEC_AUTH);
30750ba2cbe9Sxc151355 	audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
307633343a97Smeem 	if (!success)
307733343a97Smeem 		die("authorization '%s' is required", LINK_SEC_AUTH);
30780ba2cbe9Sxc151355 
307933343a97Smeem 	rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
308033343a97Smeem 	if (rval != 0) {
30810ba2cbe9Sxc151355 		switch (rval) {
30820ba2cbe9Sxc151355 		case ENOENT:
308333343a97Smeem 			die("invalid secure object class");
30840ba2cbe9Sxc151355 			break;
30850ba2cbe9Sxc151355 		case EINVAL:
308633343a97Smeem 			die("invalid secure object value");
30870ba2cbe9Sxc151355 			break;
30880ba2cbe9Sxc151355 		case ENOTSUP:
308933343a97Smeem 			die("verification failed");
30900ba2cbe9Sxc151355 			break;
30910ba2cbe9Sxc151355 		default:
309233343a97Smeem 			die("invalid secure object: %s", strerror(rval));
30930ba2cbe9Sxc151355 			break;
30940ba2cbe9Sxc151355 		}
30950ba2cbe9Sxc151355 	}
30960ba2cbe9Sxc151355 
30970ba2cbe9Sxc151355 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
30980ba2cbe9Sxc151355 	    DLADM_OPT_CREATE | DLADM_OPT_TEMP);
30990ba2cbe9Sxc151355 	if (status != DLADM_STATUS_OK) {
310033343a97Smeem 		die_dlerr(status, "could not create secure object '%s'",
310133343a97Smeem 		    obj_name);
31020ba2cbe9Sxc151355 	}
31030ba2cbe9Sxc151355 	if (temp)
31040ba2cbe9Sxc151355 		return;
31050ba2cbe9Sxc151355 
31060ba2cbe9Sxc151355 	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
31070ba2cbe9Sxc151355 	    DLADM_OPT_PERSIST);
31080ba2cbe9Sxc151355 	if (status != DLADM_STATUS_OK) {
310933343a97Smeem 		warn_dlerr(status, "could not persistently create secure "
311033343a97Smeem 		    "object '%s'", obj_name);
31110ba2cbe9Sxc151355 	}
31120ba2cbe9Sxc151355 }
31130ba2cbe9Sxc151355 
31140ba2cbe9Sxc151355 static void
31150ba2cbe9Sxc151355 do_delete_secobj(int argc, char **argv)
31160ba2cbe9Sxc151355 {
31170ba2cbe9Sxc151355 	int		i, option;
31180ba2cbe9Sxc151355 	boolean_t	temp = B_FALSE;
31190ba2cbe9Sxc151355 	split_t		*sp = NULL;
31200ba2cbe9Sxc151355 	boolean_t	success;
31210ba2cbe9Sxc151355 	dladm_status_t	status, pstatus;
31220ba2cbe9Sxc151355 
31230ba2cbe9Sxc151355 	opterr = 0;
31240ba2cbe9Sxc151355 	status = pstatus = DLADM_STATUS_OK;
312533343a97Smeem 	while ((option = getopt_long(argc, argv, ":R:t",
31260ba2cbe9Sxc151355 	    wifi_longopts, NULL)) != -1) {
31270ba2cbe9Sxc151355 		switch (option) {
31280ba2cbe9Sxc151355 		case 't':
31290ba2cbe9Sxc151355 			temp = B_TRUE;
31300ba2cbe9Sxc151355 			break;
31310ba2cbe9Sxc151355 		case 'R':
31320ba2cbe9Sxc151355 			status = dladm_set_rootdir(optarg);
31330ba2cbe9Sxc151355 			if (status != DLADM_STATUS_OK) {
313433343a97Smeem 				die_dlerr(status, "invalid directory "
313533343a97Smeem 				    "specified");
31360ba2cbe9Sxc151355 			}
31370ba2cbe9Sxc151355 			break;
31380ba2cbe9Sxc151355 		default:
313933343a97Smeem 			die_opterr(optopt, option);
31400ba2cbe9Sxc151355 			break;
31410ba2cbe9Sxc151355 		}
31420ba2cbe9Sxc151355 	}
31430ba2cbe9Sxc151355 
31440ba2cbe9Sxc151355 	if (optind == (argc - 1)) {
31450ba2cbe9Sxc151355 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
31460ba2cbe9Sxc151355 		if (sp == NULL) {
314733343a97Smeem 			die("invalid secure object name(s): '%s'",
314833343a97Smeem 			    argv[optind]);
31490ba2cbe9Sxc151355 		}
31500ba2cbe9Sxc151355 	} else if (optind != argc)
31510ba2cbe9Sxc151355 		usage();
31520ba2cbe9Sxc151355 
315333343a97Smeem 	if (sp == NULL || sp->s_nfields < 1)
315433343a97Smeem 		die("secure object name required");
31550ba2cbe9Sxc151355 
31560ba2cbe9Sxc151355 	success = check_auth(LINK_SEC_AUTH);
31570ba2cbe9Sxc151355 	audit_secobj(LINK_SEC_AUTH, "wep", argv[optind], success, B_FALSE);
315833343a97Smeem 	if (!success)
315933343a97Smeem 		die("authorization '%s' is required", LINK_SEC_AUTH);
31600ba2cbe9Sxc151355 
31610ba2cbe9Sxc151355 	for (i = 0; i < sp->s_nfields; i++) {
31620ba2cbe9Sxc151355 		status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_TEMP);
31630ba2cbe9Sxc151355 		if (!temp) {
31640ba2cbe9Sxc151355 			pstatus = dladm_unset_secobj(sp->s_fields[i],
31650ba2cbe9Sxc151355 			    DLADM_OPT_PERSIST);
31660ba2cbe9Sxc151355 		} else {
31670ba2cbe9Sxc151355 			pstatus = DLADM_STATUS_OK;
31680ba2cbe9Sxc151355 		}
31690ba2cbe9Sxc151355 
31700ba2cbe9Sxc151355 		if (status != DLADM_STATUS_OK) {
317133343a97Smeem 			warn_dlerr(status, "could not delete secure object "
317233343a97Smeem 			    "'%s'", sp->s_fields[i]);
31730ba2cbe9Sxc151355 		}
31740ba2cbe9Sxc151355 		if (pstatus != DLADM_STATUS_OK) {
317533343a97Smeem 			warn_dlerr(pstatus, "could not persistently delete "
317633343a97Smeem 			    "secure object '%s'", sp->s_fields[i]);
31770ba2cbe9Sxc151355 		}
31780ba2cbe9Sxc151355 	}
31790ba2cbe9Sxc151355 	if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK)
31800ba2cbe9Sxc151355 		exit(1);
31810ba2cbe9Sxc151355 }
31820ba2cbe9Sxc151355 
31830ba2cbe9Sxc151355 typedef struct show_secobj_state {
31840ba2cbe9Sxc151355 	boolean_t	ss_persist;
31850ba2cbe9Sxc151355 	boolean_t	ss_parseable;
31860ba2cbe9Sxc151355 	boolean_t	ss_debug;
31870ba2cbe9Sxc151355 	boolean_t	ss_header;
31880ba2cbe9Sxc151355 } show_secobj_state_t;
31890ba2cbe9Sxc151355 
31900ba2cbe9Sxc151355 static void
31910ba2cbe9Sxc151355 print_secobj_head(show_secobj_state_t *statep)
31920ba2cbe9Sxc151355 {
31930ba2cbe9Sxc151355 	(void) printf("%-20s %-20s ", "OBJECT", "CLASS");
31940ba2cbe9Sxc151355 	if (statep->ss_debug)
31950ba2cbe9Sxc151355 		(void) printf("%-30s", "VALUE");
31960ba2cbe9Sxc151355 	(void) putchar('\n');
31970ba2cbe9Sxc151355 }
31980ba2cbe9Sxc151355 
31990ba2cbe9Sxc151355 static boolean_t
32000ba2cbe9Sxc151355 show_secobj(void *arg, const char *obj_name)
32010ba2cbe9Sxc151355 {
32020ba2cbe9Sxc151355 	uint_t			obj_len = DLADM_SECOBJ_VAL_MAX;
32030ba2cbe9Sxc151355 	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
32040ba2cbe9Sxc151355 	char			buf[DLADM_STRSIZE];
32050ba2cbe9Sxc151355 	uint_t			flags = 0;
32060ba2cbe9Sxc151355 	dladm_secobj_class_t	class;
32070ba2cbe9Sxc151355 	show_secobj_state_t	*statep = arg;
32080ba2cbe9Sxc151355 	dladm_status_t		status;
32090ba2cbe9Sxc151355 
32100ba2cbe9Sxc151355 	if (statep->ss_persist)
32110ba2cbe9Sxc151355 		flags |= DLADM_OPT_PERSIST;
32120ba2cbe9Sxc151355 
32130ba2cbe9Sxc151355 	status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags);
321433343a97Smeem 	if (status != DLADM_STATUS_OK)
321533343a97Smeem 		die_dlerr(status, "cannot get secure object '%s'", obj_name);
32160ba2cbe9Sxc151355 
32170ba2cbe9Sxc151355 	if (statep->ss_header) {
32180ba2cbe9Sxc151355 		statep->ss_header = B_FALSE;
32190ba2cbe9Sxc151355 		if (!statep->ss_parseable)
32200ba2cbe9Sxc151355 			print_secobj_head(statep);
32210ba2cbe9Sxc151355 	}
32220ba2cbe9Sxc151355 
32230ba2cbe9Sxc151355 	if (statep->ss_parseable) {
32240ba2cbe9Sxc151355 		(void) printf("OBJECT=\"%s\" CLASS=\"%s\" ", obj_name,
32250ba2cbe9Sxc151355 		    dladm_secobjclass2str(class, buf));
32260ba2cbe9Sxc151355 	} else {
32270ba2cbe9Sxc151355 		(void) printf("%-20s %-20s ", obj_name,
32280ba2cbe9Sxc151355 		    dladm_secobjclass2str(class, buf));
32290ba2cbe9Sxc151355 	}
32300ba2cbe9Sxc151355 
32310ba2cbe9Sxc151355 	if (statep->ss_debug) {
32320ba2cbe9Sxc151355 		char	val[DLADM_SECOBJ_VAL_MAX * 2];
32330ba2cbe9Sxc151355 		uint_t	len = sizeof (val);
32340ba2cbe9Sxc151355 
32350ba2cbe9Sxc151355 		if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0) {
32360ba2cbe9Sxc151355 			if (statep->ss_parseable)
32370ba2cbe9Sxc151355 				(void) printf("VALUE=\"0x%s\"", val);
32380ba2cbe9Sxc151355 			else
32390ba2cbe9Sxc151355 				(void) printf("0x%-30s", val);
32400ba2cbe9Sxc151355 		}
32410ba2cbe9Sxc151355 	}
32420ba2cbe9Sxc151355 	(void) putchar('\n');
32430ba2cbe9Sxc151355 	return (B_TRUE);
32440ba2cbe9Sxc151355 }
32450ba2cbe9Sxc151355 
32460ba2cbe9Sxc151355 static void
32470ba2cbe9Sxc151355 do_show_secobj(int argc, char **argv)
32480ba2cbe9Sxc151355 {
32490ba2cbe9Sxc151355 	int			option;
32500ba2cbe9Sxc151355 	show_secobj_state_t	state;
32510ba2cbe9Sxc151355 	dladm_status_t		status;
32520ba2cbe9Sxc151355 	uint_t			i;
32530ba2cbe9Sxc151355 	split_t			*sp;
32540ba2cbe9Sxc151355 	uint_t			flags;
32550ba2cbe9Sxc151355 
32560ba2cbe9Sxc151355 	opterr = 0;
32570ba2cbe9Sxc151355 	state.ss_persist = B_FALSE;
32580ba2cbe9Sxc151355 	state.ss_parseable = B_FALSE;
32590ba2cbe9Sxc151355 	state.ss_debug = B_FALSE;
32600ba2cbe9Sxc151355 	state.ss_header = B_TRUE;
32610ba2cbe9Sxc151355 	while ((option = getopt_long(argc, argv, ":pPd",
32620ba2cbe9Sxc151355 	    wifi_longopts, NULL)) != -1) {
32630ba2cbe9Sxc151355 		switch (option) {
32640ba2cbe9Sxc151355 		case 'p':
32650ba2cbe9Sxc151355 			state.ss_parseable = B_TRUE;
32660ba2cbe9Sxc151355 			break;
32670ba2cbe9Sxc151355 		case 'P':
32680ba2cbe9Sxc151355 			state.ss_persist = B_TRUE;
32690ba2cbe9Sxc151355 			break;
32700ba2cbe9Sxc151355 		case 'd':
327133343a97Smeem 			if (getuid() != 0)
327233343a97Smeem 				die("insufficient privileges");
32730ba2cbe9Sxc151355 			state.ss_debug = B_TRUE;
32740ba2cbe9Sxc151355 			break;
32750ba2cbe9Sxc151355 		default:
327633343a97Smeem 			die_opterr(optopt, option);
32770ba2cbe9Sxc151355 			break;
32780ba2cbe9Sxc151355 		}
32790ba2cbe9Sxc151355 	}
32800ba2cbe9Sxc151355 
32810ba2cbe9Sxc151355 	if (optind == (argc - 1)) {
32820ba2cbe9Sxc151355 		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
32830ba2cbe9Sxc151355 		if (sp == NULL) {
328433343a97Smeem 			die("invalid secure object name(s): '%s'",
328533343a97Smeem 			    argv[optind]);
32860ba2cbe9Sxc151355 		}
32870ba2cbe9Sxc151355 		for (i = 0; i < sp->s_nfields; i++) {
32880ba2cbe9Sxc151355 			if (!show_secobj(&state, sp->s_fields[i]))
32890ba2cbe9Sxc151355 				break;
32900ba2cbe9Sxc151355 		}
32910ba2cbe9Sxc151355 		splitfree(sp);
32920ba2cbe9Sxc151355 		return;
32930ba2cbe9Sxc151355 	} else if (optind != argc)
32940ba2cbe9Sxc151355 		usage();
32950ba2cbe9Sxc151355 
32960ba2cbe9Sxc151355 	flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
32970ba2cbe9Sxc151355 	status = dladm_walk_secobj(&state, show_secobj, flags);
329833343a97Smeem 	if (status != DLADM_STATUS_OK)
329933343a97Smeem 		die_dlerr(status, "show-secobj");
33000ba2cbe9Sxc151355 }
33010ba2cbe9Sxc151355 
33020ba2cbe9Sxc151355 /* ARGSUSED */
33030ba2cbe9Sxc151355 static void
33040ba2cbe9Sxc151355 do_init_linkprop(int argc, char **argv)
33050ba2cbe9Sxc151355 {
33060ba2cbe9Sxc151355 	dladm_status_t status;
33070ba2cbe9Sxc151355 
33080ba2cbe9Sxc151355 	status = dladm_init_linkprop();
330933343a97Smeem 	if (status != DLADM_STATUS_OK)
331033343a97Smeem 		die_dlerr(status, "link property initialization failed");
33110ba2cbe9Sxc151355 }
33120ba2cbe9Sxc151355 
33130ba2cbe9Sxc151355 /* ARGSUSED */
33140ba2cbe9Sxc151355 static void
33150ba2cbe9Sxc151355 do_init_secobj(int argc, char **argv)
33160ba2cbe9Sxc151355 {
33170ba2cbe9Sxc151355 	dladm_status_t status;
33180ba2cbe9Sxc151355 
33190ba2cbe9Sxc151355 	status = dladm_init_secobj();
332033343a97Smeem 	if (status != DLADM_STATUS_OK)
332133343a97Smeem 		die_dlerr(status, "secure object initialization failed");
332233343a97Smeem }
332333343a97Smeem 
332433343a97Smeem static boolean_t
332533343a97Smeem str2int(const char *str, int *valp)
332633343a97Smeem {
332733343a97Smeem 	int	val;
332833343a97Smeem 	char	*endp = NULL;
332933343a97Smeem 
333033343a97Smeem 	errno = 0;
333133343a97Smeem 	val = strtol(str, &endp, 10);
333233343a97Smeem 	if (errno != 0 || *endp != '\0')
333333343a97Smeem 		return (B_FALSE);
333433343a97Smeem 
333533343a97Smeem 	*valp = val;
333633343a97Smeem 	return (B_TRUE);
333733343a97Smeem }
333833343a97Smeem 
333933343a97Smeem /* PRINTFLIKE1 */
334033343a97Smeem static void
334133343a97Smeem warn(const char *format, ...)
334233343a97Smeem {
334333343a97Smeem 	va_list alist;
334433343a97Smeem 
334533343a97Smeem 	format = gettext(format);
334633343a97Smeem 	(void) fprintf(stderr, "%s: warning: ", progname);
334733343a97Smeem 
334833343a97Smeem 	va_start(alist, format);
334933343a97Smeem 	(void) vfprintf(stderr, format, alist);
335033343a97Smeem 	va_end(alist);
335133343a97Smeem 
335233343a97Smeem 	(void) putchar('\n');
335333343a97Smeem }
335433343a97Smeem 
335533343a97Smeem /* PRINTFLIKE2 */
335633343a97Smeem static void
335733343a97Smeem warn_wlerr(wladm_status_t err, const char *format, ...)
335833343a97Smeem {
335933343a97Smeem 	va_list alist;
336033343a97Smeem 	char	errmsg[WLADM_STRSIZE];
336133343a97Smeem 
336233343a97Smeem 	format = gettext(format);
336333343a97Smeem 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
336433343a97Smeem 
336533343a97Smeem 	va_start(alist, format);
336633343a97Smeem 	(void) vfprintf(stderr, format, alist);
336733343a97Smeem 	va_end(alist);
336833343a97Smeem 	(void) fprintf(stderr, ": %s\n", wladm_status2str(err, errmsg));
336933343a97Smeem }
337033343a97Smeem 
337133343a97Smeem /* PRINTFLIKE2 */
337233343a97Smeem static void
337333343a97Smeem warn_dlerr(dladm_status_t err, const char *format, ...)
337433343a97Smeem {
337533343a97Smeem 	va_list alist;
337633343a97Smeem 	char	errmsg[DLADM_STRSIZE];
337733343a97Smeem 
337833343a97Smeem 	format = gettext(format);
337933343a97Smeem 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
338033343a97Smeem 
338133343a97Smeem 	va_start(alist, format);
338233343a97Smeem 	(void) vfprintf(stderr, format, alist);
338333343a97Smeem 	va_end(alist);
338433343a97Smeem 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
338533343a97Smeem }
338633343a97Smeem 
338733343a97Smeem /* PRINTFLIKE2 */
338833343a97Smeem static void
338933343a97Smeem die_laerr(laadm_diag_t diag, const char *format, ...)
339033343a97Smeem {
339133343a97Smeem 	va_list alist;
339233343a97Smeem 	char	*errstr = strerror(errno);
339333343a97Smeem 
339433343a97Smeem 	format = gettext(format);
339533343a97Smeem 	(void) fprintf(stderr, "%s: ", progname);
339633343a97Smeem 
339733343a97Smeem 	va_start(alist, format);
339833343a97Smeem 	(void) vfprintf(stderr, format, alist);
339933343a97Smeem 	va_end(alist);
340033343a97Smeem 
340133343a97Smeem 	if (diag == 0)
340233343a97Smeem 		(void) fprintf(stderr, ": %s\n", errstr);
340333343a97Smeem 	else
340433343a97Smeem 		(void) fprintf(stderr, ": %s (%s)\n", errstr, laadm_diag(diag));
340533343a97Smeem 
340633343a97Smeem 	exit(EXIT_FAILURE);
340733343a97Smeem }
340833343a97Smeem 
340933343a97Smeem /* PRINTFLIKE2 */
341033343a97Smeem static void
341133343a97Smeem die_wlerr(wladm_status_t err, const char *format, ...)
341233343a97Smeem {
341333343a97Smeem 	va_list alist;
341433343a97Smeem 	char	errmsg[WLADM_STRSIZE];
341533343a97Smeem 
341633343a97Smeem 	format = gettext(format);
341733343a97Smeem 	(void) fprintf(stderr, "%s: ", progname);
341833343a97Smeem 
341933343a97Smeem 	va_start(alist, format);
342033343a97Smeem 	(void) vfprintf(stderr, format, alist);
342133343a97Smeem 	va_end(alist);
342233343a97Smeem 	(void) fprintf(stderr, ": %s\n", wladm_status2str(err, errmsg));
342333343a97Smeem 
342433343a97Smeem 	exit(EXIT_FAILURE);
342533343a97Smeem }
342633343a97Smeem 
342733343a97Smeem /* PRINTFLIKE2 */
342833343a97Smeem static void
342933343a97Smeem die_dlerr(dladm_status_t err, const char *format, ...)
343033343a97Smeem {
343133343a97Smeem 	va_list alist;
343233343a97Smeem 	char	errmsg[DLADM_STRSIZE];
343333343a97Smeem 
343433343a97Smeem 	format = gettext(format);
343533343a97Smeem 	(void) fprintf(stderr, "%s: ", progname);
343633343a97Smeem 
343733343a97Smeem 	va_start(alist, format);
343833343a97Smeem 	(void) vfprintf(stderr, format, alist);
343933343a97Smeem 	va_end(alist);
344033343a97Smeem 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
344133343a97Smeem 
344233343a97Smeem 	exit(EXIT_FAILURE);
344333343a97Smeem }
344433343a97Smeem 
344533343a97Smeem /* PRINTFLIKE1 */
344633343a97Smeem static void
344733343a97Smeem die(const char *format, ...)
344833343a97Smeem {
344933343a97Smeem 	va_list alist;
345033343a97Smeem 
345133343a97Smeem 	format = gettext(format);
345233343a97Smeem 	(void) fprintf(stderr, "%s: ", progname);
345333343a97Smeem 
345433343a97Smeem 	va_start(alist, format);
345533343a97Smeem 	(void) vfprintf(stderr, format, alist);
345633343a97Smeem 	va_end(alist);
345733343a97Smeem 
345833343a97Smeem 	(void) putchar('\n');
345933343a97Smeem 	exit(EXIT_FAILURE);
346033343a97Smeem }
346133343a97Smeem 
346233343a97Smeem static void
346333343a97Smeem die_optdup(int opt)
346433343a97Smeem {
346533343a97Smeem 	die("the option -%c cannot be specified more than once", opt);
346633343a97Smeem }
346733343a97Smeem 
346833343a97Smeem static void
346933343a97Smeem die_opterr(int opt, int opterr)
347033343a97Smeem {
347133343a97Smeem 	switch (opterr) {
347233343a97Smeem 	case ':':
347333343a97Smeem 		die("option '-%c' requires a value", opt);
347433343a97Smeem 		break;
347533343a97Smeem 	case '?':
347633343a97Smeem 	default:
347733343a97Smeem 		die("unrecognized option '-%c'", opt);
347833343a97Smeem 		break;
34790ba2cbe9Sxc151355 	}
34800ba2cbe9Sxc151355 }
3481