dladm.c revision 3448:aaf16568054b
1298770Sdelphij/*
2275970Scy * CDDL HEADER START
3275970Scy *
4275970Scy * The contents of this file are subject to the terms of the
5275970Scy * Common Development and Distribution License (the "License").
6298770Sdelphij * You may not use this file except in compliance with the License.
7275970Scy *
8275970Scy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9275970Scy * or http://www.opensolaris.org/os/licensing.
10275970Scy * See the License for the specific language governing permissions
11275970Scy * and limitations under the License.
12275970Scy *
13275970Scy * When distributing Covered Code, include this CDDL HEADER in each
14275970Scy * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15275970Scy * If applicable, add the following below this CDDL HEADER, with the
16275970Scy * fields enclosed by brackets "[]" replaced with your own identifying
17275970Scy * information: Portions Copyright [yyyy] [name of copyright owner]
18275970Scy *
19275970Scy * CDDL HEADER END
20275970Scy */
21275970Scy/*
22275970Scy * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23275970Scy * Use is subject to license terms.
24275970Scy */
25275970Scy
26275970Scy#pragma ident	"%Z%%M%	%I%	%E% SMI"
27275970Scy
28275970Scy#include <stdio.h>
29275970Scy#include <ctype.h>
30275970Scy#include <locale.h>
31275970Scy#include <signal.h>
32275970Scy#include <stdarg.h>
33275970Scy#include <stdlib.h>
34275970Scy#include <fcntl.h>
35275970Scy#include <stdarg.h>
36275970Scy#include <string.h>
37275970Scy#include <stropts.h>
38275970Scy#include <errno.h>
39275970Scy#include <kstat.h>
40275970Scy#include <strings.h>
41275970Scy#include <getopt.h>
42275970Scy#include <unistd.h>
43275970Scy#include <priv.h>
44275970Scy#include <termios.h>
45275970Scy#include <pwd.h>
46275970Scy#include <auth_attr.h>
47275970Scy#include <auth_list.h>
48275970Scy#include <libintl.h>
49275970Scy#include <libdlpi.h>
50275970Scy#include <libdladm.h>
51275970Scy#include <liblaadm.h>
52275970Scy#include <libmacadm.h>
53275970Scy#include <libwladm.h>
54275970Scy#include <libinetutil.h>
55275970Scy#include <bsm/adt.h>
56275970Scy#include <bsm/adt_event.h>
57275970Scy
58275970Scy#define	AGGR_DRV	"aggr"
59275970Scy#define	MAXPORT		256
60275970Scy#define	DUMP_LACP_FORMAT	"    %-9s %-8s %-7s %-12s "	\
61275970Scy	"%-5s %-4s %-4s %-9s %-7s\n"
62275970Scy
63275970Scytypedef struct pktsum_s {
64275970Scy	uint64_t	ipackets;
65275970Scy	uint64_t	opackets;
66275970Scy	uint64_t	rbytes;
67275970Scy	uint64_t	obytes;
68275970Scy	uint32_t	ierrors;
69275970Scy	uint32_t	oerrors;
70275970Scy} pktsum_t;
71275970Scy
72275970Scytypedef struct show_link_state {
73275970Scy	boolean_t	ls_firstonly;
74275970Scy	boolean_t	ls_donefirst;
75275970Scy	boolean_t	ls_stats;
76275970Scy	pktsum_t	ls_prevstats;
77275970Scy	boolean_t	ls_parseable;
78275970Scy} show_link_state_t;
79275970Scy
80275970Scytypedef struct show_grp_state {
81275970Scy	uint32_t	gs_key;
82275970Scy	boolean_t	gs_lacp;
83275970Scy	boolean_t	gs_found;
84275970Scy	boolean_t	gs_stats;
85275970Scy	boolean_t	gs_firstonly;
86275970Scy	pktsum_t	gs_prevstats[MAXPORT];
87275970Scy	boolean_t	gs_parseable;
88275970Scy} show_grp_state_t;
89275970Scy
90275970Scytypedef struct show_mac_state {
91275970Scy	boolean_t	ms_firstonly;
92275970Scy	boolean_t	ms_donefirst;
93275970Scy	pktsum_t	ms_prevstats;
94275970Scy	boolean_t	ms_parseable;
95275970Scy} show_mac_state_t;
96275970Scy
97275970Scytypedef struct port_state {
98275970Scy	char			*state_name;
99275970Scy	aggr_port_state_t	state_num;
100275970Scy} port_state_t;
101275970Scy
102275970Scystatic port_state_t port_states[] = {
103275970Scy	{"standby", AGGR_PORT_STATE_STANDBY },
104275970Scy	{"attached", AGGR_PORT_STATE_ATTACHED }
105275970Scy};
106275970Scy
107275970Scy#define	NPORTSTATES	(sizeof (port_states) / sizeof (port_state_t))
108275970Scy
109275970Scytypedef	void cmdfunc_t(int, char **);
110275970Scy
111275970Scystatic cmdfunc_t do_show_link, do_show_dev, do_show_wifi;
112275970Scystatic cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
113275970Scystatic cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr, do_down_aggr;
114298770Sdelphijstatic cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
115298770Sdelphijstatic cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
116298770Sdelphijstatic cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
117298770Sdelphijstatic cmdfunc_t do_init_linkprop, do_init_secobj;
118298770Sdelphij
119298770Sdelphijstatic void	show_linkprop_onelink(void *, const char *);
120298770Sdelphij
121298770Sdelphijstatic void	link_stats(const char *, uint_t);
122275970Scystatic void	aggr_stats(uint32_t, uint_t);
123275970Scystatic void	dev_stats(const char *dev, uint32_t);
124275970Scy
125275970Scystatic void	get_mac_stats(const char *, pktsum_t *);
126275970Scystatic void	get_link_stats(const char *, pktsum_t *);
127275970Scystatic uint64_t	mac_ifspeed(const char *);
128275970Scystatic char	*mac_link_state(const char *);
129275970Scystatic char	*mac_link_duplex(const char *);
130275970Scystatic void	stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
131275970Scystatic void	stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
132275970Scy
133275970Scystatic boolean_t str2int(const char *, int *);
134275970Scystatic void	die(const char *, ...);
135275970Scystatic void	die_optdup(int);
136275970Scystatic void	die_opterr(int, int);
137275970Scystatic void	die_laerr(laadm_diag_t, const char *, ...);
138275970Scystatic void	die_wlerr(wladm_status_t, const char *, ...);
139275970Scystatic void	die_dlerr(dladm_status_t, const char *, ...);
140275970Scystatic void	warn(const char *, ...);
141275970Scystatic void	warn_wlerr(wladm_status_t, const char *, ...);
142275970Scystatic void	warn_dlerr(dladm_status_t, const char *, ...);
143275970Scy
144275970Scytypedef struct	cmd {
145275970Scy	char		*c_name;
146275970Scy	cmdfunc_t	*c_fn;
147275970Scy} cmd_t;
148275970Scy
149275970Scystatic cmd_t	cmds[] = {
150275970Scy	{ "show-link",		do_show_link		},
151275970Scy	{ "show-dev",		do_show_dev		},
152275970Scy	{ "create-aggr",	do_create_aggr		},
153275970Scy	{ "delete-aggr",	do_delete_aggr		},
154275970Scy	{ "add-aggr",		do_add_aggr		},
155275970Scy	{ "remove-aggr",	do_remove_aggr		},
156298770Sdelphij	{ "modify-aggr",	do_modify_aggr		},
157275970Scy	{ "show-aggr",		do_show_aggr		},
158275970Scy	{ "up-aggr",		do_up_aggr		},
159275970Scy	{ "down-aggr",		do_down_aggr		},
160275970Scy	{ "scan-wifi",		do_scan_wifi		},
161275970Scy	{ "connect-wifi",	do_connect_wifi		},
162275970Scy	{ "disconnect-wifi",	do_disconnect_wifi	},
163275970Scy	{ "show-wifi",		do_show_wifi		},
164298770Sdelphij	{ "show-linkprop",	do_show_linkprop	},
165298770Sdelphij	{ "set-linkprop",	do_set_linkprop		},
166275970Scy	{ "reset-linkprop",	do_reset_linkprop	},
167275970Scy	{ "create-secobj",	do_create_secobj	},
168275970Scy	{ "delete-secobj",	do_delete_secobj	},
169275970Scy	{ "show-secobj",	do_show_secobj		},
170275970Scy	{ "init-linkprop",	do_init_linkprop	},
171275970Scy	{ "init-secobj",	do_init_secobj		}
172275970Scy};
173298770Sdelphij
174275970Scystatic const struct option longopts[] = {
175275970Scy	{"vlan-id",	required_argument,	0, 'v'	},
176275970Scy	{"dev",		required_argument,	0, 'd'	},
177275970Scy	{"policy",	required_argument,	0, 'P'	},
178275970Scy	{"lacp-mode",	required_argument,	0, 'l'	},
179275970Scy	{"lacp-timer",	required_argument,	0, 'T'	},
180275970Scy	{"unicast",	required_argument,	0, 'u'	},
181275970Scy	{"statistics",	no_argument,		0, 's'	},
182275970Scy	{"interval",	required_argument,	0, 'i'	},
183275970Scy	{"lacp",	no_argument,		0, 'L'	},
184275970Scy	{"temporary",	no_argument,		0, 't'	},
185275970Scy	{"root-dir",	required_argument,	0, 'r'	},
186275970Scy	{"parseable",	no_argument,		0, 'p'	},
187275970Scy	{ 0, 0, 0, 0 }
188275970Scy};
189275970Scy
190275970Scystatic const struct option prop_longopts[] = {
191275970Scy	{"temporary",	no_argument,		0, 't'	},
192275970Scy	{"root-dir",	required_argument,	0, 'R'	},
193275970Scy	{"prop",	required_argument,	0, 'p'	},
194275970Scy	{"parseable",	no_argument,		0, 'c'	},
195275970Scy	{"persistent",	no_argument,		0, 'P'	},
196275970Scy	{ 0, 0, 0, 0 }
197275970Scy};
198275970Scy
199275970Scystatic const struct option wifi_longopts[] = {
200275970Scy	{"parseable",	no_argument,		0, 'p'	},
201275970Scy	{"output",	required_argument,	0, 'o'	},
202275970Scy	{"essid",	required_argument,	0, 'e'	},
203275970Scy	{"bsstype",	required_argument,	0, 'b'	},
204275970Scy	{"mode",	required_argument,	0, 'm'	},
205275970Scy	{"key",		required_argument,	0, 'k'	},
206275970Scy	{"sec",		required_argument,	0, 's'	},
207275970Scy	{"auth",	required_argument,	0, 'a'	},
208275970Scy	{"create-ibss",	required_argument,	0, 'c'	},
209275970Scy	{"timeout",	required_argument,	0, 'T'	},
210275970Scy	{"all-links",	no_argument,		0, 'a'	},
211275970Scy	{"temporary",	no_argument,		0, 't'	},
212275970Scy	{"root-dir",	required_argument,	0, 'R'	},
213275970Scy	{"persistent",	no_argument,		0, 'P'	},
214275970Scy	{"file",	required_argument,	0, 'f'	},
215275970Scy	{ 0, 0, 0, 0 }
216275970Scy};
217275970Scy
218275970Scystatic char *progname;
219275970Scystatic sig_atomic_t signalled;
220275970Scy
221275970Scystatic void
222275970Scyusage(void)
223275970Scy{
224275970Scy	(void) fprintf(stderr, gettext("usage:	dladm <subcommand> <args> ...\n"
225275970Scy	    "\tshow-link       [-p] [-s [-i <interval>]] [<name>]\n"
226275970Scy	    "\tshow-dev        [-p] [-s [-i <interval>]] [<dev>]\n"
227275970Scy	    "\n"
228275970Scy	    "\tcreate-aggr     [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n"
229275970Scy	    "\t                [-T <time>] [-u <address>] -d <dev> ... <key>\n"
230275970Scy	    "\tmodify-aggr     [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n"
231275970Scy	    "\t                [-T <time>] [-u <address>] <key>\n"
232275970Scy	    "\tdelete-aggr     [-t] [-R <root-dir>] <key>\n"
233275970Scy	    "\tadd-aggr        [-t] [-R <root-dir>] -d <dev> ... <key>\n"
234275970Scy	    "\tremove-aggr     [-t] [-R <root-dir>] -d <dev> ... <key>\n"
235275970Scy	    "\tshow-aggr       [-pL][-s [-i <interval>]] [<key>]\n"
236275970Scy	    "\n"
237275970Scy	    "\tscan-wifi       [-p] [-o <field>,...] [<name>]\n"
238275970Scy	    "\tconnect-wifi    [-e <essid>] [-i <bssid>] [-k <key>,...]"
239275970Scy	    " [-s wep]\n"
240275970Scy	    "\t                [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
241275970Scy	    "\t                [-T <time>] [<name>]\n"
242275970Scy	    "\tdisconnect-wifi [-a] [<name>]\n"
243275970Scy	    "\tshow-wifi       [-p] [-o <field>,...] [<name>]\n"
244275970Scy	    "\n"
245275970Scy	    "\tset-linkprop    [-t] [-R <root-dir>]  -p <prop>=<value>[,...]"
246275970Scy	    " <name>\n"
247275970Scy	    "\treset-linkprop  [-t] [-R <root-dir>] [-p <prop>,...] <name>\n"
248275970Scy	    "\tshow-linkprop   [-cP][-p <prop>,...] <name>\n"
249275970Scy	    "\n"
250275970Scy	    "\tcreate-secobj   [-t] [-R <root-dir>] [-f <file>] -c <class>"
251275970Scy	    " <secobj>\n"
252275970Scy	    "\tdelete-secobj   [-t] [-R <root-dir>] <secobj>[,...]\n"
253275970Scy	    "\tshow-secobj     [-pP][<secobj>,...]\n"));
254275970Scy	exit(1);
255275970Scy}
256275970Scy
257275970Scyint
258275970Scymain(int argc, char *argv[])
259275970Scy{
260275970Scy	int	i;
261275970Scy	cmd_t	*cmdp;
262275970Scy
263275970Scy	(void) setlocale(LC_ALL, "");
264275970Scy#if !defined(TEXT_DOMAIN)
265275970Scy#define	TEXT_DOMAIN "SYS_TEST"
266275970Scy#endif
267275970Scy	(void) textdomain(TEXT_DOMAIN);
268275970Scy
269275970Scy	progname = argv[0];
270275970Scy
271275970Scy	if (argc < 2)
272275970Scy		usage();
273275970Scy
274275970Scy	if (!priv_ineffect(PRIV_SYS_NET_CONFIG) ||
275275970Scy	    !priv_ineffect(PRIV_NET_RAWACCESS))
276275970Scy		die("insufficient privileges");
277275970Scy
278275970Scy	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
279275970Scy		cmdp = &cmds[i];
280275970Scy		if (strcmp(argv[1], cmdp->c_name) == 0) {
281275970Scy			cmdp->c_fn(argc - 1, &argv[1]);
282275970Scy			exit(0);
283275970Scy		}
284275970Scy	}
285275970Scy
286275970Scy	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
287275970Scy	    progname, argv[1]);
288275970Scy	usage();
289275970Scy
290275970Scy	return (0);
291275970Scy}
292275970Scy
293275970Scystatic void
294275970Scydo_create_aggr(int argc, char *argv[])
295275970Scy{
296275970Scy	char			option;
297275970Scy	int			key;
298275970Scy	uint32_t		policy = AGGR_POLICY_L4;
299298770Sdelphij	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
300298770Sdelphij	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
301298770Sdelphij	laadm_port_attr_db_t	port[MAXPORT];
302275970Scy	uint_t			nport = 0;
303275970Scy	uint8_t			mac_addr[ETHERADDRL];
304275970Scy	boolean_t		mac_addr_fixed = B_FALSE;
305275970Scy	boolean_t		P_arg = B_FALSE;
306275970Scy	boolean_t		l_arg = B_FALSE;
307275970Scy	boolean_t		t_arg = B_FALSE;
308275970Scy	boolean_t		u_arg = B_FALSE;
309275970Scy	boolean_t		T_arg = B_FALSE;
310275970Scy	char			*altroot = NULL;
311275970Scy	laadm_diag_t		diag = 0;
312298770Sdelphij
313298770Sdelphij	opterr = 0;
314298770Sdelphij	while ((option = getopt_long(argc, argv, ":d:l:P:R:tu:T:",
315275970Scy	    longopts, NULL)) != -1) {
316275970Scy		switch (option) {
317275970Scy		case 'd':
318275970Scy			if (nport >= MAXPORT)
319275970Scy				die("too many <dev> arguments");
320275970Scy
321275970Scy			if (strlcpy(port[nport].lp_devname, optarg,
322275970Scy			    MAXNAMELEN) >= MAXNAMELEN)
323275970Scy				die("device name too long");
324275970Scy
325275970Scy			nport++;
326275970Scy			break;
327275970Scy		case 'P':
328275970Scy			if (P_arg)
329275970Scy				die_optdup(option);
330275970Scy
331275970Scy			P_arg = B_TRUE;
332275970Scy			if (!laadm_str_to_policy(optarg, &policy))
333275970Scy				die("invalid policy '%s'", optarg);
334275970Scy			break;
335275970Scy		case 'u':
336275970Scy			if (u_arg)
337275970Scy				die_optdup(option);
338275970Scy
339275970Scy			u_arg = B_TRUE;
340275970Scy			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
341275970Scy			    mac_addr))
342275970Scy				die("invalid MAC address '%s'", optarg);
343275970Scy			break;
344275970Scy		case 'l':
345275970Scy			if (l_arg)
346275970Scy				die_optdup(option);
347298770Sdelphij
348298770Sdelphij			l_arg = B_TRUE;
349298770Sdelphij			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode))
350298770Sdelphij				die("invalid LACP mode '%s'", optarg);
351298770Sdelphij			break;
352275970Scy		case 'T':
353275970Scy			if (T_arg)
354275970Scy				die_optdup(option);
355275970Scy
356275970Scy			T_arg = B_TRUE;
357275970Scy			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer))
358275970Scy				die("invalid LACP timer value '%s'", optarg);
359275970Scy			break;
360275970Scy		case 't':
361275970Scy			t_arg = B_TRUE;
362275970Scy			break;
363275970Scy		case 'R':
364298770Sdelphij			altroot = optarg;
365298770Sdelphij			break;
366298770Sdelphij		default:
367298770Sdelphij			die_opterr(optopt, option);
368298770Sdelphij			break;
369275970Scy		}
370275970Scy	}
371275970Scy
372275970Scy	if (nport == 0)
373275970Scy		usage();
374275970Scy
375275970Scy	/* get key value (required last argument) */
376275970Scy	if (optind != (argc-1))
377275970Scy		usage();
378275970Scy
379275970Scy	if (!str2int(argv[optind], &key) || key < 1)
380275970Scy		die("invalid key value '%s'", argv[optind]);
381275970Scy
382275970Scy	if (laadm_create(key, nport, port, policy, mac_addr_fixed,
383275970Scy	    mac_addr, lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0)
384275970Scy		die_laerr(diag, "create operation failed");
385275970Scy}
386275970Scy
387298770Sdelphijstatic void
388298770Sdelphijdo_delete_aggr(int argc, char *argv[])
389298770Sdelphij{
390298770Sdelphij	int			key;
391298770Sdelphij	char			option;
392298770Sdelphij	boolean_t		t_arg = B_FALSE;
393275970Scy	char			*altroot = NULL;
394275970Scy	laadm_diag_t		diag = 0;
395275970Scy
396275970Scy	opterr = 0;
397275970Scy	while ((option = getopt_long(argc, argv, ":R:t", longopts,
398275970Scy	    NULL)) != -1) {
399275970Scy		switch (option) {
400275970Scy		case 't':
401275970Scy			t_arg = B_TRUE;
402275970Scy			break;
403275970Scy		case 'R':
404275970Scy			altroot = optarg;
405275970Scy			break;
406275970Scy		default:
407275970Scy			die_opterr(optopt, option);
408275970Scy			break;
409275970Scy		}
410275970Scy	}
411275970Scy
412275970Scy	/* get key value (required last argument) */
413275970Scy	if (optind != (argc-1))
414275970Scy		usage();
415275970Scy
416275970Scy	if (!str2int(argv[optind], &key) || key < 1)
417275970Scy		die("invalid key value '%s'", argv[optind]);
418275970Scy
419275970Scy	if (laadm_delete(key, t_arg, altroot, &diag) < 0)
420275970Scy		die_laerr(diag, "delete operation failed");
421275970Scy}
422275970Scy
423275970Scystatic void
424275970Scydo_add_aggr(int argc, char *argv[])
425275970Scy{
426275970Scy	char			option;
427275970Scy	int			key;
428275970Scy	laadm_port_attr_db_t	port[MAXPORT];
429275970Scy	uint_t			nport = 0;
430275970Scy	boolean_t		t_arg = B_FALSE;
431275970Scy	char			*altroot = NULL;
432275970Scy	laadm_diag_t		diag = 0;
433275970Scy
434275970Scy	opterr = 0;
435280849Scy	while ((option = getopt_long(argc, argv, ":d:R:t", longopts,
436280849Scy	    NULL)) != -1) {
437280849Scy		switch (option) {
438280849Scy		case 'd':
439280849Scy			if (nport >= MAXPORT)
440280849Scy				die("too many <dev> arguments");
441280849Scy
442280849Scy			if (strlcpy(port[nport].lp_devname, optarg,
443280849Scy			    MAXNAMELEN) >= MAXNAMELEN)
444280849Scy				die("device name too long");
445280849Scy
446280849Scy			nport++;
447280849Scy			break;
448280849Scy		case 't':
449275970Scy			t_arg = B_TRUE;
450275970Scy			break;
451275970Scy		case 'R':
452275970Scy			altroot = optarg;
453275970Scy			break;
454275970Scy		default:
455275970Scy			die_opterr(optopt, option);
456275970Scy			break;
457275970Scy		}
458275970Scy	}
459275970Scy
460275970Scy	if (nport == 0)
461275970Scy		usage();
462275970Scy
463275970Scy	/* get key value (required last argument) */
464275970Scy	if (optind != (argc-1))
465275970Scy		usage();
466275970Scy
467275970Scy	if (!str2int(argv[optind], &key) || key < 1)
468275970Scy		die("invalid key value '%s'", argv[optind]);
469275970Scy
470275970Scy	if (laadm_add(key, nport, port, t_arg, altroot, &diag) < 0) {
471275970Scy		/*
472275970Scy		 * checking ENOTSUP is a temporary workaround
473275970Scy		 * and should be removed once 6399681 is fixed.
474275970Scy		 */
475275970Scy		if (errno == ENOTSUP) {
476275970Scy			(void) fprintf(stderr,
477275970Scy			    gettext("%s: add operation failed: %s\n"),
478275970Scy			    progname,
479275970Scy			    gettext("device capabilities don't match"));
480275970Scy			exit(ENOTSUP);
481275970Scy		}
482275970Scy		die_laerr(diag, "add operation failed");
483275970Scy	}
484275970Scy}
485275970Scy
486275970Scystatic void
487275970Scydo_remove_aggr(int argc, char *argv[])
488275970Scy{
489275970Scy	char			option;
490275970Scy	int			key;
491275970Scy	laadm_port_attr_db_t	port[MAXPORT];
492275970Scy	uint_t			nport = 0;
493275970Scy	boolean_t		t_arg = B_FALSE;
494275970Scy	char			*altroot = NULL;
495275970Scy	laadm_diag_t		diag = 0;
496275970Scy
497275970Scy	opterr = 0;
498275970Scy	while ((option = getopt_long(argc, argv, ":d:R:t",
499275970Scy	    longopts, NULL)) != -1) {
500275970Scy		switch (option) {
501275970Scy		case 'd':
502275970Scy			if (nport >= MAXPORT)
503275970Scy				die("too many <dev> arguments");
504275970Scy
505275970Scy			if (strlcpy(port[nport].lp_devname, optarg,
506275970Scy			    MAXNAMELEN) >= MAXNAMELEN)
507275970Scy				die("device name too long");
508275970Scy
509275970Scy			nport++;
510275970Scy			break;
511275970Scy		case 't':
512275970Scy			t_arg = B_TRUE;
513275970Scy			break;
514275970Scy		case 'R':
515275970Scy			altroot = optarg;
516275970Scy			break;
517275970Scy		default:
518275970Scy			die_opterr(optopt, option);
519275970Scy			break;
520275970Scy		}
521275970Scy	}
522275970Scy
523275970Scy	if (nport == 0)
524275970Scy		usage();
525275970Scy
526275970Scy	/* get key value (required last argument) */
527275970Scy	if (optind != (argc-1))
528275970Scy		usage();
529275970Scy
530275970Scy	if (!str2int(argv[optind], &key) || key < 1)
531275970Scy		die("invalid key value '%s'", argv[optind]);
532275970Scy
533275970Scy	if (laadm_remove(key, nport, port, t_arg, altroot, &diag) < 0)
534275970Scy		die_laerr(diag, "remove operation failed");
535275970Scy}
536275970Scy
537275970Scystatic void
538298770Sdelphijdo_modify_aggr(int argc, char *argv[])
539275970Scy{
540275970Scy	char			option;
541275970Scy	int			key;
542275970Scy	uint32_t		policy = AGGR_POLICY_L4;
543275970Scy	aggr_lacp_mode_t	lacp_mode = AGGR_LACP_OFF;
544275970Scy	aggr_lacp_timer_t	lacp_timer = AGGR_LACP_TIMER_SHORT;
545275970Scy	uint8_t			mac_addr[ETHERADDRL];
546275970Scy	boolean_t		mac_addr_fixed = B_FALSE;
547275970Scy	uint8_t			modify_mask = 0;
548275970Scy	boolean_t		t_arg = B_FALSE;
549275970Scy	char			*altroot = NULL;
550275970Scy	laadm_diag_t		diag = 0;
551275970Scy
552275970Scy	opterr = 0;
553275970Scy	while ((option = getopt_long(argc, argv, ":l:P:R:tu:T:", longopts,
554275970Scy	    NULL)) != -1) {
555275970Scy		switch (option) {
556275970Scy		case 'P':
557275970Scy			if (modify_mask & LAADM_MODIFY_POLICY)
558275970Scy				die_optdup(option);
559275970Scy
560275970Scy			modify_mask |= LAADM_MODIFY_POLICY;
561275970Scy
562275970Scy			if (!laadm_str_to_policy(optarg, &policy))
563275970Scy				die("invalid policy '%s'", optarg);
564275970Scy			break;
565275970Scy		case 'u':
566275970Scy			if (modify_mask & LAADM_MODIFY_MAC)
567275970Scy				die_optdup(option);
568275970Scy
569275970Scy			modify_mask |= LAADM_MODIFY_MAC;
570275970Scy
571275970Scy			if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed,
572275970Scy			    mac_addr))
573275970Scy				die("invalid MAC address '%s'", optarg);
574275970Scy			break;
575275970Scy		case 'l':
576275970Scy			if (modify_mask & LAADM_MODIFY_LACP_MODE)
577275970Scy				die_optdup(option);
578275970Scy
579275970Scy			modify_mask |= LAADM_MODIFY_LACP_MODE;
580275970Scy
581275970Scy			if (!laadm_str_to_lacp_mode(optarg, &lacp_mode))
582275970Scy				die("invalid LACP mode '%s'", optarg);
583275970Scy			break;
584275970Scy		case 'T':
585275970Scy			if (modify_mask & LAADM_MODIFY_LACP_TIMER)
586275970Scy				die_optdup(option);
587275970Scy
588275970Scy			modify_mask |= LAADM_MODIFY_LACP_TIMER;
589275970Scy
590275970Scy			if (!laadm_str_to_lacp_timer(optarg, &lacp_timer))
591275970Scy				die("invalid LACP timer value '%s'", optarg);
592275970Scy			break;
593275970Scy		case 't':
594275970Scy			t_arg = B_TRUE;
595275970Scy			break;
596275970Scy		case 'R':
597275970Scy			altroot = optarg;
598275970Scy			break;
599275970Scy		default:
600275970Scy			die_opterr(optopt, option);
601275970Scy			break;
602275970Scy		}
603275970Scy	}
604275970Scy
605275970Scy	if (modify_mask == 0)
606275970Scy		die("at least one of the -PulT options must be specified");
607275970Scy
608275970Scy	/* get key value (required last argument) */
609275970Scy	if (optind != (argc-1))
610275970Scy		usage();
611275970Scy
612275970Scy	if (!str2int(argv[optind], &key) || key < 1)
613275970Scy		die("invalid key value '%s'", argv[optind]);
614275970Scy
615275970Scy	if (laadm_modify(key, modify_mask, policy, mac_addr_fixed, mac_addr,
616275970Scy	    lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0)
617275970Scy		die_laerr(diag, "modify operation failed");
618275970Scy}
619275970Scy
620275970Scystatic void
621275970Scydo_up_aggr(int argc, char *argv[])
622275970Scy{
623275970Scy	int		key = 0;
624275970Scy	laadm_diag_t	diag = 0;
625275970Scy
626275970Scy	/* get aggregation key (optional last argument) */
627275970Scy	if (argc == 2) {
628275970Scy		if (!str2int(argv[1], &key) || key < 1)
629275970Scy			die("invalid key value '%s'", argv[1]);
630275970Scy	} else if (argc > 2) {
631275970Scy		usage();
632275970Scy	}
633275970Scy
634275970Scy	if (laadm_up(key, NULL, &diag) < 0) {
635275970Scy		if (key != 0) {
636275970Scy			die_laerr(diag, "could not bring up aggregation '%u'",
637275970Scy			    key);
638275970Scy		} else {
639275970Scy			die_laerr(diag, "could not bring aggregations up");
640275970Scy		}
641275970Scy	}
642275970Scy}
643275970Scy
644275970Scystatic void
645275970Scydo_down_aggr(int argc, char *argv[])
646275970Scy{
647275970Scy	int	key = 0;
648275970Scy
649275970Scy	/* get aggregation key (optional last argument) */
650275970Scy	if (argc == 2) {
651275970Scy		if (!str2int(argv[1], &key) || key < 1)
652275970Scy			die("invalid key value '%s'", argv[1]);
653275970Scy	} else if (argc > 2) {
654275970Scy		usage();
655275970Scy	}
656275970Scy
657275970Scy	if (laadm_down(key) < 0) {
658275970Scy		if (key != 0) {
659275970Scy			die("could not bring down aggregation '%u': %s",
660275970Scy			    key, strerror(errno));
661275970Scy		} else {
662275970Scy			die("could not bring down aggregations: %s",
663275970Scy			    strerror(errno));
664275970Scy		}
665275970Scy	}
666275970Scy}
667275970Scy
668275970Scy#define	TYPE_WIDTH	10
669275970Scy
670275970Scystatic void
671275970Scyprint_link_parseable(const char *name, dladm_attr_t *dap, boolean_t legacy)
672275970Scy{
673275970Scy	char		type[TYPE_WIDTH];
674275970Scy
675275970Scy	if (!legacy) {
676275970Scy		char	drv[LIFNAMSIZ];
677275970Scy		int	instance;
678275970Scy
679275970Scy		if (dap->da_vid != 0) {
680275970Scy			(void) snprintf(type, TYPE_WIDTH, "vlan %u",
681275970Scy			    dap->da_vid);
682275970Scy		} else {
683275970Scy			(void) snprintf(type, TYPE_WIDTH, "non-vlan");
684275970Scy		}
685275970Scy		if (dlpi_if_parse(dap->da_dev, drv, &instance) != 0)
686275970Scy			return;
687275970Scy		if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) {
688275970Scy			(void) printf("%s type=%s mtu=%d key=%u\n",
689275970Scy			    name, type, dap->da_max_sdu, instance);
690275970Scy		} else {
691275970Scy			(void) printf("%s type=%s mtu=%d device=%s\n",
692275970Scy			    name, type, dap->da_max_sdu, dap->da_dev);
693275970Scy		}
694275970Scy	} else {
695275970Scy		(void) printf("%s type=legacy mtu=%d device=%s\n",
696275970Scy		    name, dap->da_max_sdu, name);
697275970Scy	}
698275970Scy}
699275970Scy
700275970Scystatic void
701275970Scyprint_link(const char *name, dladm_attr_t *dap, boolean_t legacy)
702275970Scy{
703275970Scy	char		type[TYPE_WIDTH];
704275970Scy
705275970Scy	if (!legacy) {
706275970Scy		char drv[LIFNAMSIZ];
707275970Scy		int instance;
708275970Scy
709275970Scy		if (dap->da_vid != 0) {
710275970Scy			(void) snprintf(type, TYPE_WIDTH, gettext("vlan %u"),
711275970Scy			    dap->da_vid);
712275970Scy		} else {
713275970Scy			(void) snprintf(type, TYPE_WIDTH, gettext("non-vlan"));
714275970Scy		}
715275970Scy		if (dlpi_if_parse(dap->da_dev, drv, &instance) != 0)
716275970Scy			return;
717275970Scy		if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) {
718275970Scy			(void) printf(gettext("%-9s\ttype: %s\tmtu: %d"
719275970Scy			    "\taggregation: key %u\n"), name, type,
720275970Scy			    dap->da_max_sdu, instance);
721275970Scy		} else {
722275970Scy			(void) printf(gettext("%-9s\ttype: %s\tmtu: "
723275970Scy			    "%d\tdevice: %s\n"), name, type, dap->da_max_sdu,
724275970Scy			    dap->da_dev);
725275970Scy		}
726275970Scy	} else {
727275970Scy		(void) printf(gettext("%-9s\ttype: legacy\tmtu: "
728298770Sdelphij		    "%d\tdevice: %s\n"), name, dap->da_max_sdu, name);
729275970Scy	}
730275970Scy}
731275970Scy
732275970Scystatic int
733275970Scyget_if_info(const char *name, dladm_attr_t *dlattrp, boolean_t *legacy)
734275970Scy{
735275970Scy	int	err;
736275970Scy
737275970Scy	if ((err = dladm_info(name, dlattrp)) == 0) {
738275970Scy		*legacy = B_FALSE;
739275970Scy	} else if (err < 0 && errno == ENODEV) {
740275970Scy		int		fd;
741275970Scy		dlpi_if_attr_t	dia;
742275970Scy		dl_info_ack_t	dlia;
743275970Scy
744275970Scy		/*
745275970Scy		 * A return value of ENODEV means that the specified
746275970Scy		 * device is not gldv3.
747275970Scy		 */
748275970Scy		if ((fd = dlpi_if_open(name, &dia, B_FALSE)) != -1 &&
749275970Scy		    dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL,
750275970Scy		    NULL, NULL) != -1) {
751275970Scy			(void) dlpi_close(fd);
752275970Scy
753275970Scy			*legacy = B_TRUE;
754275970Scy			bzero(dlattrp, sizeof (*dlattrp));
755275970Scy			dlattrp->da_max_sdu = (uint_t)dlia.dl_max_sdu;
756275970Scy		} else {
757275970Scy			errno = ENOENT;
758275970Scy			return (-1);
759275970Scy		}
760275970Scy	} else {
761275970Scy		/*
762275970Scy		 * If the return value is not ENODEV, this means that
763275970Scy		 * user is either passing in a bogus interface name
764275970Scy		 * or a vlan interface name that doesn't exist yet.
765275970Scy		 */
766275970Scy		errno = ENOENT;
767275970Scy		return (-1);
768275970Scy	}
769275970Scy	return (0);
770275970Scy}
771275970Scy
772275970Scy/* ARGSUSED */
773275970Scystatic void
774275970Scyshow_link(void *arg, const char *name)
775275970Scy{
776275970Scy	dladm_attr_t	dlattr;
777275970Scy	boolean_t	legacy = B_TRUE;
778275970Scy	show_link_state_t *state = (show_link_state_t *)arg;
779275970Scy
780275970Scy	if (get_if_info(name, &dlattr, &legacy) < 0)
781275970Scy		die("invalid link '%s'", name);
782275970Scy
783275970Scy	if (state->ls_parseable) {
784275970Scy		print_link_parseable(name, &dlattr, legacy);
785275970Scy	} else {
786275970Scy		print_link(name, &dlattr, legacy);
787275970Scy	}
788275970Scy}
789275970Scy
790275970Scystatic void
791275970Scyshow_link_stats(void *arg, const char *name)
792275970Scy{
793275970Scy	show_link_state_t *state = (show_link_state_t *)arg;
794275970Scy	pktsum_t stats, diff_stats;
795275970Scy
796275970Scy	if (state->ls_firstonly) {
797275970Scy		if (state->ls_donefirst)
798275970Scy			return;
799275970Scy		state->ls_donefirst = B_TRUE;
800275970Scy	} else {
801275970Scy		bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
802275970Scy	}
803275970Scy
804275970Scy	get_link_stats(name, &stats);
805275970Scy	stats_diff(&diff_stats, &stats, &state->ls_prevstats);
806275970Scy
807275970Scy	(void) printf("%s", name);
808275970Scy	(void) printf("\t\t%-10llu", diff_stats.ipackets);
809275970Scy	(void) printf("%-12llu", diff_stats.rbytes);
810275970Scy	(void) printf("%-8u", diff_stats.ierrors);
811275970Scy	(void) printf("%-10llu", diff_stats.opackets);
812275970Scy	(void) printf("%-12llu", diff_stats.obytes);
813275970Scy	(void) printf("%-8u\n", diff_stats.oerrors);
814275970Scy
815275970Scy	state->ls_prevstats = stats;
816275970Scy}
817275970Scy
818275970Scystatic void
819275970Scydump_grp(laadm_grp_attr_sys_t	*grp, boolean_t parseable)
820275970Scy{
821275970Scy	char policy_str[LAADM_POLICY_STR_LEN];
822275970Scy	char addr_str[ETHERADDRL * 3];
823275970Scy
824275970Scy	if (!parseable) {
825275970Scy		(void) printf(gettext("key: %d (0x%04x)"),
826275970Scy		    grp->lg_key, grp->lg_key);
827275970Scy
828275970Scy		(void) printf(gettext("\tpolicy: %s"),
829275970Scy		    laadm_policy_to_str(grp->lg_policy, policy_str));
830275970Scy
831275970Scy		(void) printf(gettext("\taddress: %s (%s)\n"),
832275970Scy		    laadm_mac_addr_to_str(grp->lg_mac, addr_str),
833275970Scy		    (grp->lg_mac_fixed) ? gettext("fixed") : gettext("auto"));
834275970Scy	} else {
835275970Scy		(void) printf("aggr key=%d", grp->lg_key);
836275970Scy
837275970Scy		(void) printf(" policy=%s",
838275970Scy		    laadm_policy_to_str(grp->lg_policy, policy_str));
839275970Scy
840275970Scy		(void) printf(" address=%s",
841275970Scy		    laadm_mac_addr_to_str(grp->lg_mac, addr_str));
842275970Scy
843275970Scy		(void) printf(" address-type=%s\n",
844275970Scy		    (grp->lg_mac_fixed) ? "fixed" : "auto");
845275970Scy	}
846275970Scy}
847275970Scy
848275970Scystatic void
849275970Scydump_grp_lacp(laadm_grp_attr_sys_t *grp, boolean_t parseable)
850275970Scy{
851275970Scy	const char *lacp_mode_str = laadm_lacp_mode_to_str(grp->lg_lacp_mode);
852275970Scy	const char *lacp_timer_str =
853275970Scy	    laadm_lacp_timer_to_str(grp->lg_lacp_timer);
854275970Scy
855275970Scy	if (!parseable) {
856275970Scy		(void) printf(gettext("\t\tLACP mode: %s"), lacp_mode_str);
857275970Scy		(void) printf(gettext("\tLACP timer: %s\n"), lacp_timer_str);
858275970Scy	} else {
859275970Scy		(void) printf(" lacp-mode=%s", lacp_mode_str);
860275970Scy		(void) printf(" lacp-timer=%s\n", lacp_timer_str);
861275970Scy	}
862275970Scy}
863275970Scy
864275970Scystatic void
865275970Scydump_grp_stats(laadm_grp_attr_sys_t *grp)
866275970Scy{
867275970Scy	(void) printf("key: %d", grp->lg_key);
868275970Scy	(void) printf("\tipackets  rbytes      opackets	 obytes		 ");
869275970Scy	(void) printf("%%ipkts	%%opkts\n");
870275970Scy}
871275970Scy
872275970Scystatic void
873275970Scydump_ports_lacp_head(void)
874275970Scy{
875275970Scy	(void) printf(DUMP_LACP_FORMAT, gettext("device"), gettext("activity"),
876275970Scy	    gettext("timeout"), gettext("aggregatable"), gettext("sync"),
877275970Scy	    gettext("coll"), gettext("dist"), gettext("defaulted"),
878275970Scy	    gettext("expired"));
879275970Scy}
880275970Scy
881275970Scystatic void
882275970Scydump_ports_head(void)
883275970Scy{
884275970Scy	(void) printf(gettext("	   device\taddress\t\t	speed\t\tduplex\tlink\t"
885275970Scy	    "state\n"));
886275970Scy}
887275970Scy
888275970Scystatic char *
889275970Scyport_state_to_str(aggr_port_state_t state_num)
890275970Scy{
891275970Scy	int			i;
892275970Scy	port_state_t		*state;
893275970Scy
894275970Scy	for (i = 0; i < NPORTSTATES; i++) {
895275970Scy		state = &port_states[i];
896275970Scy		if (state->state_num == state_num)
897275970Scy			return (state->state_name);
898275970Scy	}
899275970Scy
900275970Scy	return ("unknown");
901275970Scy}
902275970Scy
903275970Scystatic void
904275970Scydump_port(laadm_port_attr_sys_t *port, boolean_t parseable)
905275970Scy{
906275970Scy	char *dev = port->lp_devname;
907275970Scy	char buf[ETHERADDRL * 3];
908275970Scy
909275970Scy	if (!parseable) {
910275970Scy		(void) printf("	   %-9s\t%s", dev, laadm_mac_addr_to_str(
911275970Scy		    port->lp_mac, buf));
912275970Scy		(void) printf("\t %5uMb", (int)(mac_ifspeed(dev) /
913275970Scy		    1000000ull));
914275970Scy		(void) printf("\t%s", mac_link_duplex(dev));
915275970Scy		(void) printf("\t%s", mac_link_state(dev));
916275970Scy		(void) printf("\t%s\n", port_state_to_str(port->lp_state));
917275970Scy
918275970Scy	} else {
919275970Scy		(void) printf(" device=%s address=%s", dev,
920275970Scy		    laadm_mac_addr_to_str(port->lp_mac, buf));
921275970Scy		(void) printf(" speed=%u", (int)(mac_ifspeed(dev) /
922275970Scy		    1000000ull));
923275970Scy		(void) printf(" duplex=%s", mac_link_duplex(dev));
924275970Scy		(void) printf(" link=%s", mac_link_state(dev));
925275970Scy		(void) printf(" port=%s", port_state_to_str(port->lp_state));
926298770Sdelphij	}
927298770Sdelphij}
928275970Scy
929275970Scystatic void
930275970Scydump_port_lacp(laadm_port_attr_sys_t *port)
931275970Scy{
932275970Scy	aggr_lacp_state_t *state = &port->lp_lacp_state;
933275970Scy
934275970Scy	(void) printf(DUMP_LACP_FORMAT,
935275970Scy	    port->lp_devname, state->bit.activity ? "active" : "passive",
936275970Scy	    state->bit.timeout ? "short" : "long",
937298770Sdelphij	    state->bit.aggregation ? "yes" : "no",
938275970Scy	    state->bit.sync ? "yes" : "no",
939275970Scy	    state->bit.collecting ? "yes" : "no",
940275970Scy	    state->bit.distributing ? "yes" : "no",
941275970Scy	    state->bit.defaulted ? "yes" : "no",
942275970Scy	    state->bit.expired ? "yes" : "no");
943275970Scy}
944275970Scy
945275970Scystatic void
946275970Scydump_port_stat(int index, show_grp_state_t *state, pktsum_t *port_stats,
947275970Scy    pktsum_t *tot_stats)
948275970Scy{
949275970Scy	pktsum_t	diff_stats;
950275970Scy	pktsum_t	*old_stats = &state->gs_prevstats[index];
951275970Scy
952275970Scy	stats_diff(&diff_stats, port_stats, old_stats);
953275970Scy
954275970Scy	(void) printf("\t%-10llu", diff_stats.ipackets);
955275970Scy	(void) printf("%-12llu", diff_stats.rbytes);
956275970Scy	(void) printf("%-10llu", diff_stats.opackets);
957275970Scy	(void) printf("%-12llu", diff_stats.obytes);
958275970Scy
959275970Scy	if (tot_stats->ipackets == 0)
960275970Scy		(void) printf("\t-");
961275970Scy	else
962275970Scy		(void) printf("\t%-6.1f", (double)diff_stats.ipackets/
963275970Scy		    (double)tot_stats->ipackets * 100);
964275970Scy
965275970Scy	if (tot_stats->opackets == 0)
966275970Scy		(void) printf("\t-");
967275970Scy	else
968275970Scy		(void) printf("\t%-6.1f", (double)diff_stats.opackets/
969275970Scy		    (double)tot_stats->opackets * 100);
970275970Scy
971275970Scy	(void) printf("\n");
972275970Scy
973275970Scy	*old_stats = *port_stats;
974275970Scy}
975275970Scy
976275970Scystatic int
977275970Scyshow_key(void *arg, laadm_grp_attr_sys_t *grp)
978275970Scy{
979275970Scy	show_grp_state_t	*state = (show_grp_state_t *)arg;
980275970Scy	int			i;
981275970Scy	pktsum_t		pktsumtot, port_stat;
982275970Scy
983275970Scy	if (state->gs_key != 0 && state->gs_key != grp->lg_key)
984275970Scy		return (0);
985275970Scy	if (state->gs_firstonly) {
986275970Scy		if (state->gs_found)
987275970Scy			return (0);
988275970Scy	} else {
989275970Scy		bzero(&state->gs_prevstats, sizeof (state->gs_prevstats));
990275970Scy	}
991275970Scy
992275970Scy	state->gs_found = B_TRUE;
993275970Scy
994275970Scy	if (state->gs_stats) {
995275970Scy		/* show statistics */
996275970Scy		dump_grp_stats(grp);
997275970Scy
998275970Scy		/* sum the ports statistics */
999275970Scy		bzero(&pktsumtot, sizeof (pktsumtot));
1000275970Scy		for (i = 0; i < grp->lg_nports; i++) {
1001275970Scy			get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat);
1002275970Scy			stats_total(&pktsumtot, &port_stat,
1003275970Scy			    &state->gs_prevstats[i]);
1004275970Scy		}
1005275970Scy
1006275970Scy		(void) printf("	   Total");
1007275970Scy		(void) printf("\t%-10llu", pktsumtot.ipackets);
1008275970Scy		(void) printf("%-12llu", pktsumtot.rbytes);
1009275970Scy		(void) printf("%-10llu", pktsumtot.opackets);
1010275970Scy		(void) printf("%-12llu\n", pktsumtot.obytes);
1011275970Scy
1012275970Scy		for (i = 0; i < grp->lg_nports; i++) {
1013275970Scy			get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat);
1014275970Scy			(void) printf("	   %s", grp->lg_ports[i].lp_devname);
1015275970Scy			dump_port_stat(i, state, &port_stat, &pktsumtot);
1016275970Scy		}
1017275970Scy	} else if (state->gs_lacp) {
1018275970Scy		/* show LACP info */
1019275970Scy		dump_grp(grp, state->gs_parseable);
1020275970Scy		dump_grp_lacp(grp, state->gs_parseable);
1021275970Scy		dump_ports_lacp_head();
1022275970Scy		for (i = 0; i < grp->lg_nports; i++)
1023275970Scy			dump_port_lacp(&grp->lg_ports[i]);
1024275970Scy	} else {
1025275970Scy		dump_grp(grp, state->gs_parseable);
1026275970Scy		if (!state->gs_parseable)
1027275970Scy			dump_ports_head();
1028275970Scy		for (i = 0; i < grp->lg_nports; i++) {
1029275970Scy			if (state->gs_parseable)
1030275970Scy				(void) printf("dev key=%d", grp->lg_key);
1031275970Scy			dump_port(&grp->lg_ports[i], state->gs_parseable);
1032275970Scy			if (state->gs_parseable)
1033275970Scy				(void) printf("\n");
1034275970Scy		}
1035275970Scy	}
1036275970Scy
1037275970Scy	return (0);
1038275970Scy}
1039275970Scy
1040275970Scystatic int
1041275970Scykstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
1042275970Scy{
1043275970Scy	kstat_named_t	*knp;
1044275970Scy
1045275970Scy	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
1046275970Scy		return (-1);
1047275970Scy
1048275970Scy	if (knp->data_type != type)
1049275970Scy		return (-1);
1050275970Scy
1051275970Scy	switch (type) {
1052275970Scy	case KSTAT_DATA_UINT64:
1053275970Scy		*(uint64_t *)buf = knp->value.ui64;
1054275970Scy		break;
1055275970Scy	case KSTAT_DATA_UINT32:
1056275970Scy		*(uint32_t *)buf = knp->value.ui32;
1057275970Scy		break;
1058275970Scy	default:
1059275970Scy		return (-1);
1060275970Scy	}
1061275970Scy
1062275970Scy	return (0);
1063275970Scy}
1064275970Scy
1065275970Scystatic void
1066275970Scyshow_dev(void *arg, const char *dev)
1067275970Scy{
1068275970Scy	show_mac_state_t *state = (show_mac_state_t *)arg;
1069275970Scy
1070275970Scy	(void) printf("%s", dev);
1071275970Scy
1072275970Scy	if (!state->ms_parseable) {
1073275970Scy		(void) printf(gettext("\t\tlink: %s"),
1074275970Scy		    mac_link_state(dev));
1075275970Scy		(void) printf(gettext("\tspeed: %5uMb"),
1076275970Scy		    (unsigned int)(mac_ifspeed(dev) / 1000000ull));
1077275970Scy		(void) printf(gettext("\tduplex: %s\n"),
1078298770Sdelphij		    mac_link_duplex(dev));
1079275970Scy	} else {
1080275970Scy		(void) printf(" link=%s", mac_link_state(dev));
1081275970Scy		(void) printf(" speed=%u",
1082275970Scy		    (unsigned int)(mac_ifspeed(dev) / 1000000ull));
1083275970Scy		(void) printf(" duplex=%s\n", mac_link_duplex(dev));
1084275970Scy	}
1085275970Scy}
1086275970Scy
1087275970Scy/*ARGSUSED*/
1088275970Scystatic void
1089275970Scyshow_dev_stats(void *arg, const char *dev)
1090275970Scy{
1091275970Scy	show_mac_state_t *state = (show_mac_state_t *)arg;
1092275970Scy	pktsum_t stats, diff_stats;
1093275970Scy
1094275970Scy	if (state->ms_firstonly) {
1095275970Scy		if (state->ms_donefirst)
1096275970Scy			return;
1097275970Scy		state->ms_donefirst = B_TRUE;
1098275970Scy	} else {
1099275970Scy		bzero(&state->ms_prevstats, sizeof (state->ms_prevstats));
1100275970Scy	}
1101275970Scy
1102275970Scy	get_mac_stats(dev, &stats);
1103275970Scy	stats_diff(&diff_stats, &stats, &state->ms_prevstats);
1104275970Scy
1105275970Scy	(void) printf("%s", dev);
1106275970Scy	(void) printf("\t\t%-10llu", diff_stats.ipackets);
1107275970Scy	(void) printf("%-12llu", diff_stats.rbytes);
1108275970Scy	(void) printf("%-8u", diff_stats.ierrors);
1109275970Scy	(void) printf("%-10llu", diff_stats.opackets);
1110275970Scy	(void) printf("%-12llu", diff_stats.obytes);
1111275970Scy	(void) printf("%-8u\n", diff_stats.oerrors);
1112275970Scy
1113275970Scy	state->ms_prevstats = stats;
1114275970Scy}
1115275970Scy
1116275970Scystatic void
1117275970Scydo_show_link(int argc, char *argv[])
1118275970Scy{
1119275970Scy	char		*name = NULL;
1120275970Scy	int		option;
1121275970Scy	boolean_t	s_arg = B_FALSE;
1122275970Scy	boolean_t	i_arg = B_FALSE;
1123275970Scy	int		interval = 0;
1124275970Scy	show_link_state_t state;
1125275970Scy
1126275970Scy	state.ls_stats = B_FALSE;
1127275970Scy	state.ls_parseable = B_FALSE;
1128275970Scy
1129275970Scy	opterr = 0;
1130275970Scy	while ((option = getopt_long(argc, argv, ":psi:",
1131275970Scy	    longopts, NULL)) != -1) {
1132275970Scy		switch (option) {
1133275970Scy		case 'p':
1134275970Scy			state.ls_parseable = B_TRUE;
1135275970Scy			break;
1136275970Scy		case 's':
1137275970Scy			if (s_arg)
1138275970Scy				die_optdup(option);
1139275970Scy
1140275970Scy			s_arg = B_TRUE;
1141275970Scy			break;
1142275970Scy		case 'i':
1143275970Scy			if (i_arg)
1144275970Scy				die_optdup(option);
1145275970Scy
1146275970Scy			i_arg = B_TRUE;
1147275970Scy			if (!str2int(optarg, &interval) || interval == 0)
1148275970Scy				die("invalid interval value '%s'", optarg);
1149275970Scy			break;
1150275970Scy		default:
1151275970Scy			die_opterr(optopt, option);
1152275970Scy			break;
1153275970Scy		}
1154275970Scy	}
1155275970Scy
1156275970Scy	if (i_arg && !s_arg)
1157275970Scy		die("the option -i can be used only with -s");
1158275970Scy
1159275970Scy	/* get link name (optional last argument) */
1160275970Scy	if (optind == (argc-1))
1161275970Scy		name = argv[optind];
1162275970Scy	else if (optind != argc)
1163275970Scy		usage();
1164275970Scy
1165275970Scy	if (s_arg) {
1166275970Scy		link_stats(name, interval);
1167275970Scy		return;
1168275970Scy	}
1169275970Scy
1170275970Scy	if (name == NULL) {
1171275970Scy		(void) dladm_walk(show_link, &state);
1172275970Scy	} else {
1173275970Scy		show_link(&state, name);
1174275970Scy	}
1175275970Scy}
1176275970Scy
1177275970Scystatic void
1178275970Scydo_show_aggr(int argc, char *argv[])
1179275970Scy{
1180275970Scy	int			option;
1181275970Scy	int			key = 0;
1182275970Scy	boolean_t		L_arg = B_FALSE;
1183275970Scy	boolean_t		s_arg = B_FALSE;
1184275970Scy	boolean_t		i_arg = B_FALSE;
1185275970Scy	show_grp_state_t	state;
1186275970Scy	int			interval = 0;
1187275970Scy
1188275970Scy	state.gs_stats = B_FALSE;
1189275970Scy	state.gs_lacp = B_FALSE;
1190275970Scy	state.gs_parseable = B_FALSE;
1191275970Scy
1192275970Scy	opterr = 0;
1193275970Scy	while ((option = getopt_long(argc, argv, ":Lpsi:",
1194275970Scy	    longopts, NULL)) != -1) {
1195275970Scy		switch (option) {
1196275970Scy		case 'L':
1197275970Scy			if (L_arg)
1198275970Scy				die_optdup(option);
1199275970Scy
1200275970Scy			if (s_arg || i_arg) {
1201275970Scy				die("the option -L cannot be used with -i "
1202275970Scy				    "or -s");
1203275970Scy			}
1204275970Scy
1205275970Scy			L_arg = B_TRUE;
1206275970Scy			state.gs_lacp = B_TRUE;
1207275970Scy			break;
1208275970Scy		case 'p':
1209275970Scy			state.gs_parseable = B_TRUE;
1210275970Scy			break;
1211275970Scy		case 's':
1212275970Scy			if (s_arg)
1213275970Scy				die_optdup(option);
1214275970Scy
1215275970Scy			if (L_arg)
1216275970Scy				die("the option -s cannot be used with -L");
1217275970Scy
1218275970Scy			s_arg = B_TRUE;
1219275970Scy			break;
1220275970Scy		case 'i':
1221275970Scy			if (i_arg)
1222275970Scy				die_optdup(option);
1223275970Scy
1224275970Scy			if (L_arg)
1225275970Scy				die("the option -i cannot be used with -L");
1226275970Scy
1227275970Scy			i_arg = B_TRUE;
1228275970Scy			if (!str2int(optarg, &interval) || interval == 0)
1229275970Scy				die("invalid interval value '%s'", optarg);
1230275970Scy			break;
1231275970Scy		default:
1232275970Scy			die_opterr(optopt, option);
1233275970Scy			break;
1234275970Scy		}
1235275970Scy	}
1236275970Scy
1237275970Scy	if (i_arg && !s_arg)
1238275970Scy		die("the option -i can be used only with -s");
1239275970Scy
1240275970Scy	/* get aggregation key (optional last argument) */
1241275970Scy	if (optind == (argc-1)) {
1242275970Scy		if (!str2int(argv[optind], &key) || key < 1)
1243275970Scy			die("invalid key value '%s'", argv[optind]);
1244275970Scy	} else if (optind != argc) {
1245275970Scy		usage();
1246275970Scy	}
1247275970Scy
1248275970Scy	if (s_arg) {
1249275970Scy		aggr_stats(key, interval);
1250275970Scy		return;
1251275970Scy	}
1252275970Scy
1253275970Scy	state.gs_key = key;
1254275970Scy	state.gs_found = B_FALSE;
1255275970Scy
1256275970Scy	(void) laadm_walk_sys(show_key, &state);
1257275970Scy
1258275970Scy	if (key != 0 && !state.gs_found)
1259275970Scy		die("non-existent aggregation key '%u'", key);
1260275970Scy}
1261275970Scy
1262275970Scystatic void
1263275970Scydo_show_dev(int argc, char *argv[])
1264275970Scy{
1265275970Scy	int		option;
1266275970Scy	char		*dev = NULL;
1267275970Scy	boolean_t	s_arg = B_FALSE;
1268275970Scy	boolean_t	i_arg = B_FALSE;
1269275970Scy	int		interval = 0;
1270275970Scy	show_mac_state_t state;
1271275970Scy
1272275970Scy	state.ms_parseable = B_FALSE;
1273275970Scy
1274275970Scy	opterr = 0;
1275275970Scy	while ((option = getopt_long(argc, argv, ":psi:",
1276275970Scy	    longopts, NULL)) != -1) {
1277275970Scy		switch (option) {
1278275970Scy		case 'p':
1279275970Scy			state.ms_parseable = B_TRUE;
1280275970Scy			break;
1281275970Scy		case 's':
1282275970Scy			if (s_arg)
1283275970Scy				die_optdup(option);
1284275970Scy
1285275970Scy			s_arg = B_TRUE;
1286275970Scy			break;
1287275970Scy		case 'i':
1288275970Scy			if (i_arg)
1289275970Scy				die_optdup(option);
1290275970Scy
1291275970Scy			i_arg = B_TRUE;
1292275970Scy			if (!str2int(optarg, &interval) || interval == 0)
1293275970Scy				die("invalid interval value '%s'", optarg);
1294275970Scy			break;
1295275970Scy		default:
1296275970Scy			die_opterr(optopt, option);
1297275970Scy			break;
1298275970Scy		}
1299275970Scy	}
1300275970Scy
1301275970Scy	if (i_arg && !s_arg)
1302275970Scy		die("the option -i can be used only with -s");
1303275970Scy
1304275970Scy	/* get dev name (optional last argument) */
1305275970Scy	if (optind == (argc-1))
1306275970Scy		dev = argv[optind];
1307275970Scy	else if (optind != argc)
1308275970Scy		usage();
1309275970Scy
1310275970Scy	if (dev != NULL) {
1311275970Scy		int		index;
1312275970Scy		char		drv[LIFNAMSIZ];
1313275970Scy		dladm_attr_t	dlattr;
1314275970Scy		boolean_t	legacy;
1315275970Scy
1316275970Scy		/*
1317275970Scy		 * Check for invalid devices.
1318275970Scy		 * aggregations and vlans are not considered devices.
1319275970Scy		 */
1320275970Scy		if (strncmp(dev, "aggr", 4) == 0 ||
1321275970Scy		    dlpi_if_parse(dev, drv, &index) < 0 ||
1322275970Scy		    index >= 1000 || get_if_info(dev, &dlattr, &legacy) < 0)
1323275970Scy			die("invalid device '%s'", dev);
1324275970Scy	}
1325275970Scy
1326275970Scy	if (s_arg) {
1327275970Scy		dev_stats(dev, interval);
1328275970Scy		return;
1329275970Scy	}
1330275970Scy
1331275970Scy	if (dev == NULL)
1332275970Scy		(void) macadm_walk(show_dev, &state, B_TRUE);
1333275970Scy	else
1334275970Scy		show_dev(&state, dev);
1335275970Scy}
1336275970Scy
1337275970Scy/* ARGSUSED */
1338275970Scystatic void
1339275970Scylink_stats(const char *link, uint_t interval)
1340275970Scy{
1341275970Scy	dladm_attr_t		dlattr;
1342275970Scy	boolean_t		legacy;
1343275970Scy	show_link_state_t	state;
1344275970Scy
1345275970Scy	if (link != NULL && get_if_info(link, &dlattr, &legacy) < 0)
1346275970Scy		die("invalid link '%s'", link);
1347275970Scy
1348275970Scy	bzero(&state, sizeof (state));
1349275970Scy
1350275970Scy	/*
1351275970Scy	 * If an interval is specified, continuously show the stats
1352275970Scy	 * only for the first MAC port.
1353275970Scy	 */
1354275970Scy	state.ls_firstonly = (interval != 0);
1355275970Scy
1356275970Scy	for (;;) {
1357275970Scy		(void) printf("\t\tipackets  rbytes	 ierrors ");
1358275970Scy		(void) printf("opackets	 obytes	     oerrors\n");
1359275970Scy
1360275970Scy		state.ls_donefirst = B_FALSE;
1361275970Scy		if (link == NULL)
1362275970Scy			(void) dladm_walk(show_link_stats, &state);
1363275970Scy		else
1364275970Scy			show_link_stats(&state, link);
1365275970Scy
1366275970Scy		if (interval == 0)
1367275970Scy			break;
1368275970Scy
1369275970Scy		(void) sleep(interval);
1370275970Scy	}
1371275970Scy}
1372275970Scy
1373275970Scy/* ARGSUSED */
1374275970Scystatic void
1375275970Scyaggr_stats(uint32_t key, uint_t interval)
1376275970Scy{
1377275970Scy	show_grp_state_t state;
1378275970Scy
1379275970Scy	bzero(&state, sizeof (state));
1380275970Scy	state.gs_stats = B_TRUE;
1381275970Scy	state.gs_key = key;
1382275970Scy
1383275970Scy	/*
1384275970Scy	 * If an interval is specified, continuously show the stats
1385275970Scy	 * only for the first group.
1386275970Scy	 */
1387275970Scy	state.gs_firstonly = (interval != 0);
1388275970Scy
1389275970Scy	for (;;) {
1390275970Scy		state.gs_found = B_FALSE;
1391275970Scy		(void) laadm_walk_sys(show_key, &state);
1392275970Scy		if (state.gs_key != 0 && !state.gs_found)
1393275970Scy			die("non-existent aggregation key '%u'", key);
1394275970Scy
1395275970Scy		if (interval == 0)
1396275970Scy			break;
1397275970Scy
1398275970Scy		(void) sleep(interval);
1399275970Scy	}
1400275970Scy}
1401275970Scy
1402275970Scy/* ARGSUSED */
1403275970Scystatic void
1404275970Scydev_stats(const char *dev, uint32_t interval)
1405275970Scy{
1406275970Scy	show_mac_state_t state;
1407275970Scy
1408275970Scy	bzero(&state, sizeof (state));
1409275970Scy
1410275970Scy	/*
1411275970Scy	 * If an interval is specified, continuously show the stats
1412298770Sdelphij	 * only for the first MAC port.
1413298770Sdelphij	 */
1414298770Sdelphij	state.ms_firstonly = (interval != 0);
1415275970Scy
1416275970Scy	for (;;) {
1417275970Scy
1418275970Scy		(void) printf("\t\tipackets  rbytes	 ierrors ");
1419275970Scy		(void) printf("opackets	 obytes	     oerrors\n");
1420275970Scy
1421275970Scy		state.ms_donefirst = B_FALSE;
1422275970Scy		if (dev == NULL)
1423275970Scy			(void) macadm_walk(show_dev_stats, &state, B_TRUE);
1424275970Scy		else
1425275970Scy			show_dev_stats(&state, dev);
1426275970Scy
1427275970Scy		if (interval == 0)
1428275970Scy			break;
1429275970Scy
1430275970Scy		(void) sleep(interval);
1431275970Scy	}
1432275970Scy}
1433275970Scy
1434275970Scy/* accumulate stats (s1 += (s2 - s3)) */
1435275970Scystatic void
1436275970Scystats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
1437275970Scy{
1438275970Scy	s1->ipackets += (s2->ipackets - s3->ipackets);
1439275970Scy	s1->opackets += (s2->opackets - s3->opackets);
1440275970Scy	s1->rbytes += (s2->rbytes - s3->rbytes);
1441275970Scy	s1->obytes += (s2->obytes - s3->obytes);
1442275970Scy	s1->ierrors += (s2->ierrors - s3->ierrors);
1443275970Scy	s1->oerrors += (s2->oerrors - s3->oerrors);
1444275970Scy}
1445275970Scy
1446275970Scy/* compute stats differences (s1 = s2 - s3) */
1447275970Scystatic void
1448275970Scystats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
1449275970Scy{
1450275970Scy	s1->ipackets = s2->ipackets - s3->ipackets;
1451275970Scy	s1->opackets = s2->opackets - s3->opackets;
1452275970Scy	s1->rbytes = s2->rbytes - s3->rbytes;
1453275970Scy	s1->obytes = s2->obytes - s3->obytes;
1454275970Scy	s1->ierrors = s2->ierrors - s3->ierrors;
1455275970Scy	s1->oerrors = s2->oerrors - s3->oerrors;
1456275970Scy}
1457298770Sdelphij
1458298770Sdelphij/*
1459298770Sdelphij * In the following routines, we do the first kstat_lookup() assuming that
1460275970Scy * the device is gldv3-based and that the kstat name is the one passed in
1461275970Scy * as the "name" argument. If the lookup fails, we redo the kstat_lookup()
1462275970Scy * omitting the kstat name. This second lookup is needed for getting kstats
1463275970Scy * from legacy devices. This can fail too if the device is not attached or
1464275970Scy * the device is legacy and doesn't export the kstats we need.
1465275970Scy */
1466275970Scystatic void
1467275970Scyget_stats(char *module, int instance, char *name, pktsum_t *stats)
1468275970Scy{
1469298770Sdelphij	kstat_ctl_t	*kcp;
1470275970Scy	kstat_t		*ksp;
1471298770Sdelphij
1472275970Scy	if ((kcp = kstat_open()) == NULL) {
1473275970Scy		warn("kstat open operation failed");
1474275970Scy		return;
1475275970Scy	}
1476275970Scy
1477275970Scy	if ((ksp = kstat_lookup(kcp, module, instance, name)) == NULL &&
1478275970Scy	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) {
1479275970Scy		/*
1480275970Scy		 * The kstat query could fail if the underlying MAC
1481275970Scy		 * driver was already detached.
1482275970Scy		 */
1483275970Scy		(void) kstat_close(kcp);
1484275970Scy		return;
1485275970Scy	}
1486275970Scy
1487275970Scy	if (kstat_read(kcp, ksp, NULL) == -1)
1488275970Scy		goto bail;
1489275970Scy
1490275970Scy	if (kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
1491275970Scy	    &stats->ipackets) < 0)
1492275970Scy		goto bail;
1493275970Scy
1494275970Scy	if (kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
1495275970Scy	    &stats->opackets) < 0)
1496275970Scy		goto bail;
1497275970Scy
1498275970Scy	if (kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
1499275970Scy	    &stats->rbytes) < 0)
1500275970Scy		goto bail;
1501275970Scy
1502275970Scy	if (kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
1503275970Scy	    &stats->obytes) < 0)
1504275970Scy		goto bail;
1505275970Scy
1506275970Scy	if (kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
1507275970Scy	    &stats->ierrors) < 0)
1508275970Scy		goto bail;
1509275970Scy
1510275970Scy	if (kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
1511275970Scy	    &stats->oerrors) < 0)
1512275970Scy		goto bail;
1513275970Scy
1514275970Scy	(void) kstat_close(kcp);
1515275970Scy	return;
1516275970Scy
1517275970Scybail:
1518275970Scy	(void) kstat_close(kcp);
1519275970Scy}
1520275970Scy
1521275970Scystatic void
1522275970Scyget_mac_stats(const char *dev, pktsum_t *stats)
1523275970Scy{
1524275970Scy	char	module[LIFNAMSIZ];
1525275970Scy	int	instance;
1526275970Scy
1527275970Scy	if (dlpi_if_parse(dev, module, &instance) != 0)
1528275970Scy		return;
1529298770Sdelphij	bzero(stats, sizeof (*stats));
1530298770Sdelphij	get_stats(module, instance, "mac", stats);
1531298770Sdelphij}
1532275970Scy
1533275970Scystatic void
1534275970Scyget_link_stats(const char *link, pktsum_t *stats)
1535275970Scy{
1536275970Scy	char	module[LIFNAMSIZ];
1537275970Scy	int	instance;
1538275970Scy
1539275970Scy	if (dlpi_if_parse(link, module, &instance) != 0)
1540275970Scy		return;
1541275970Scy	bzero(stats, sizeof (*stats));
1542275970Scy	get_stats(module, instance, (char *)link, stats);
1543275970Scy}
1544275970Scy
1545275970Scystatic int
1546275970Scyget_single_mac_stat(const char *dev, const char *name, uint8_t type,
1547275970Scy    void *val)
1548275970Scy{
1549275970Scy	char		module[LIFNAMSIZ];
1550275970Scy	int		instance;
1551275970Scy	kstat_ctl_t	*kcp;
1552275970Scy	kstat_t		*ksp;
1553275970Scy
1554275970Scy	if ((kcp = kstat_open()) == NULL) {
1555275970Scy		warn("kstat open operation failed");
1556275970Scy		return (-1);
1557275970Scy	}
1558275970Scy
1559275970Scy	if (dlpi_if_parse(dev, module, &instance) != 0)
1560275970Scy		return (-1);
1561275970Scy	if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
1562275970Scy	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) {
1563275970Scy		/*
1564275970Scy		 * The kstat query could fail if the underlying MAC
1565275970Scy		 * driver was already detached.
1566275970Scy		 */
1567275970Scy		goto bail;
1568275970Scy	}
1569275970Scy
1570275970Scy	if (kstat_read(kcp, ksp, NULL) == -1) {
1571275970Scy		warn("kstat read failed");
1572275970Scy		goto bail;
1573275970Scy	}
1574275970Scy
1575275970Scy	if (kstat_value(ksp, name, type, val) < 0)
1576275970Scy		goto bail;
1577275970Scy
1578275970Scy	(void) kstat_close(kcp);
1579275970Scy	return (0);
1580275970Scy
1581275970Scybail:
1582275970Scy	(void) kstat_close(kcp);
1583275970Scy	return (-1);
1584275970Scy}
1585275970Scy
1586275970Scystatic uint64_t
1587275970Scymac_ifspeed(const char *dev)
1588275970Scy{
1589275970Scy	uint64_t ifspeed = 0;
1590298770Sdelphij
1591298770Sdelphij	(void) get_single_mac_stat(dev, "ifspeed", KSTAT_DATA_UINT64, &ifspeed);
1592298770Sdelphij	return (ifspeed);
1593275970Scy}
1594275970Scy
1595275970Scystatic char *
1596275970Scymac_link_state(const char *dev)
1597275970Scy{
1598275970Scy	link_state_t	link_state;
1599275970Scy	char		*state_str = "unknown";
1600275970Scy
1601275970Scy	if (get_single_mac_stat(dev, "link_state", KSTAT_DATA_UINT32,
1602275970Scy	    &link_state) != 0) {
1603275970Scy		return (state_str);
1604275970Scy	}
1605275970Scy
1606275970Scy	switch (link_state) {
1607275970Scy	case LINK_STATE_UP:
1608275970Scy		state_str = "up";
1609275970Scy		break;
1610275970Scy	case LINK_STATE_DOWN:
1611275970Scy		state_str = "down";
1612275970Scy		break;
1613275970Scy	default:
1614275970Scy		break;
1615275970Scy	}
1616275970Scy
1617275970Scy	return (state_str);
1618275970Scy}
1619275970Scy
1620275970Scy
1621275970Scystatic char *
1622275970Scymac_link_duplex(const char *dev)
1623275970Scy{
1624275970Scy	link_duplex_t	link_duplex;
1625275970Scy	char		*duplex_str = "unknown";
1626275970Scy
1627275970Scy	if (get_single_mac_stat(dev, "link_duplex", KSTAT_DATA_UINT32,
1628275970Scy	    &link_duplex) != 0) {
1629275970Scy		return (duplex_str);
1630275970Scy	}
1631275970Scy
1632275970Scy	switch (link_duplex) {
1633275970Scy	case LINK_DUPLEX_FULL:
1634275970Scy		duplex_str = "full";
1635275970Scy		break;
1636275970Scy	case LINK_DUPLEX_HALF:
1637275970Scy		duplex_str = "half";
1638275970Scy		break;
1639275970Scy	default:
1640275970Scy		break;
1641275970Scy	}
1642275970Scy
1643275970Scy	return (duplex_str);
1644275970Scy}
1645275970Scy
1646275970Scy#define	WIFI_CMD_SCAN	0x00000001
1647275970Scy#define	WIFI_CMD_SHOW	0x00000002
1648275970Scy#define	WIFI_CMD_ALL	(WIFI_CMD_SCAN | WIFI_CMD_SHOW)
1649275970Scytypedef struct wifi_field {
1650275970Scy	const char	*wf_name;
1651275970Scy	const char	*wf_header;
1652298770Sdelphij	uint_t		wf_width;
1653298770Sdelphij	uint_t		wf_mask;
1654298770Sdelphij	uint_t		wf_cmdtype;
1655275970Scy} wifi_field_t;
1656275970Scy
1657275970Scystatic wifi_field_t wifi_fields[] = {
1658275970Scy{ "link",	"LINK",		10,	0,			WIFI_CMD_ALL},
1659275970Scy{ "essid",	"ESSID",	19,	WLADM_WLAN_ATTR_ESSID,	WIFI_CMD_ALL},
1660275970Scy{ "bssid",	"BSSID/IBSSID", 17,	WLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
1661275970Scy{ "ibssid",	"BSSID/IBSSID", 17,	WLADM_WLAN_ATTR_BSSID,	WIFI_CMD_ALL},
1662275970Scy{ "mode",	"MODE",		6,	WLADM_WLAN_ATTR_MODE,	WIFI_CMD_ALL},
1663275970Scy{ "speed",	"SPEED",	6,	WLADM_WLAN_ATTR_SPEED,	WIFI_CMD_ALL},
1664275970Scy{ "auth",	"AUTH",		8,	WLADM_WLAN_ATTR_AUTH,	WIFI_CMD_SHOW},
1665275970Scy{ "bsstype",	"BSSTYPE",	8,	WLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL},
1666275970Scy{ "sec",	"SEC",		6,	WLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL},
1667275970Scy{ "status",	"STATUS",	17,	WLADM_LINK_ATTR_STATUS, WIFI_CMD_SHOW},
1668275970Scy{ "strength",	"STRENGTH",	10,	WLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}}
1669275970Scy;
1670275970Scy
1671275970Scystatic char *all_scan_wifi_fields =
1672275970Scy	"link,essid,bssid,sec,strength,mode,speed,bsstype";
1673275970Scystatic char *all_show_wifi_fields =
1674275970Scy	"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
1675275970Scystatic char *def_scan_wifi_fields =
1676275970Scy	"link,essid,bssid,sec,strength,mode,speed";
1677275970Scystatic char *def_show_wifi_fields =
1678275970Scy	"link,status,essid,sec,strength,mode,speed";
1679275970Scy
1680275970Scy#define	WIFI_MAX_FIELDS		(sizeof (wifi_fields) / sizeof (wifi_field_t))
1681275970Scy#define	WIFI_MAX_FIELD_LEN	32
1682275970Scy
1683275970Scytypedef struct {
1684275970Scy	char	*s_buf;
1685275970Scy	char	**s_fields;	/* array of pointer to the fields in s_buf */
1686275970Scy	uint_t	s_nfields;	/* the number of fields in s_buf */
1687275970Scy} split_t;
1688275970Scy
1689275970Scy/*
1690275970Scy * Free the split_t structure pointed to by `sp'.
1691275970Scy */
1692275970Scystatic void
1693275970Scysplitfree(split_t *sp)
1694275970Scy{
1695275970Scy	free(sp->s_buf);
1696275970Scy	free(sp->s_fields);
1697275970Scy	free(sp);
1698275970Scy}
1699275970Scy
1700275970Scy/*
1701275970Scy * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
1702275970Scy * length.  Return a pointer to a split_t containing the split fields, or NULL
1703275970Scy * on failure.
1704275970Scy */
1705275970Scystatic split_t *
1706275970Scysplit(const char *str, uint_t maxfields, uint_t maxlen)
1707275970Scy{
1708275970Scy	char	*field, *token, *lasts = NULL;
1709275970Scy	split_t	*sp;
1710275970Scy
1711275970Scy	if (*str == '\0' || maxfields == 0 || maxlen == 0)
1712275970Scy		return (NULL);
1713275970Scy
1714275970Scy	sp = calloc(sizeof (split_t), 1);
1715275970Scy	if (sp == NULL)
1716275970Scy		return (NULL);
1717275970Scy
1718275970Scy	sp->s_buf = strdup(str);
1719275970Scy	sp->s_fields = malloc(sizeof (char *) * maxfields);
1720275970Scy	if (sp->s_buf == NULL || sp->s_fields == NULL)
1721298770Sdelphij		goto fail;
1722298770Sdelphij
1723298770Sdelphij	token = sp->s_buf;
1724298770Sdelphij	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
1725298770Sdelphij		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
1726275970Scy			goto fail;
1727275970Scy		token = NULL;
1728275970Scy		sp->s_fields[sp->s_nfields++] = field;
1729275970Scy	}
1730275970Scy	return (sp);
1731275970Scyfail:
1732275970Scy	splitfree(sp);
1733275970Scy	return (NULL);
1734275970Scy}
1735275970Scy
1736275970Scystatic int
1737275970Scyparse_wifi_fields(char *str, wifi_field_t ***fields, uint_t *countp,
1738275970Scy    uint_t cmdtype)
1739275970Scy{
1740275970Scy	uint_t		i, j;
1741275970Scy	wifi_field_t	**wf = NULL;
1742275970Scy	split_t		*sp;
1743275970Scy	boolean_t	good_match = B_FALSE;
1744275970Scy
1745275970Scy	if (cmdtype == WIFI_CMD_SCAN) {
1746275970Scy		if (str == NULL)
1747275970Scy			str = def_scan_wifi_fields;
1748275970Scy		if (strcasecmp(str, "all") == 0)
1749275970Scy			str = all_scan_wifi_fields;
1750275970Scy	} else if (cmdtype == WIFI_CMD_SHOW) {
1751275970Scy		if (str == NULL)
1752275970Scy			str = def_show_wifi_fields;
1753275970Scy		if (strcasecmp(str, "all") == 0)
1754275970Scy			str = all_show_wifi_fields;
1755275970Scy	} else {
1756275970Scy		return (-1);
1757275970Scy	}
1758275970Scy
1759275970Scy	sp = split(str, WIFI_MAX_FIELDS, WIFI_MAX_FIELD_LEN);
1760275970Scy	if (sp == NULL)
1761275970Scy		return (-1);
1762275970Scy
1763275970Scy	wf = malloc(sp->s_nfields * sizeof (wifi_field_t *));
1764275970Scy	if (wf == NULL)
1765275970Scy		goto fail;
1766275970Scy
1767275970Scy	for (i = 0; i < sp->s_nfields; i++) {
1768275970Scy		for (j = 0; j < WIFI_MAX_FIELDS; j++) {
1769275970Scy			if (strcasecmp(sp->s_fields[i],
1770275970Scy			    wifi_fields[j].wf_name) == 0) {
1771275970Scy				good_match = wifi_fields[j].
1772275970Scy				    wf_cmdtype & cmdtype;
1773275970Scy				break;
1774275970Scy			}
1775275970Scy		}
1776275970Scy		if (!good_match)
1777275970Scy			goto fail;
1778275970Scy
1779275970Scy		good_match = B_FALSE;
1780275970Scy		wf[i] = &wifi_fields[j];
1781275970Scy	}
1782275970Scy	*countp = i;
1783275970Scy	*fields = wf;
1784275970Scy	splitfree(sp);
1785275970Scy	return (0);
1786275970Scyfail:
1787275970Scy	free(wf);
1788275970Scy	splitfree(sp);
1789275970Scy	return (-1);
1790275970Scy}
1791275970Scy
1792275970Scytypedef struct print_wifi_state {
1793275970Scy	const char	*ws_link;
1794275970Scy	boolean_t	ws_parseable;
1795298770Sdelphij	boolean_t	ws_header;
1796298770Sdelphij	wifi_field_t	**ws_fields;
1797298770Sdelphij	uint_t		ws_nfields;
1798275970Scy	boolean_t	ws_lastfield;
1799275970Scy	uint_t		ws_overflow;
1800275970Scy} print_wifi_state_t;
1801275970Scy
1802275970Scystatic void
1803275970Scyprint_wifi_head(print_wifi_state_t *statep)
1804275970Scy{
1805275970Scy	int		i;
1806275970Scy	wifi_field_t	*wfp;
1807275970Scy
1808275970Scy	for (i = 0; i < statep->ws_nfields; i++) {
1809275970Scy		wfp = statep->ws_fields[i];
1810275970Scy		if (i + 1 < statep->ws_nfields)
1811275970Scy			(void) printf("%-*s ", wfp->wf_width, wfp->wf_header);
1812275970Scy		else
1813275970Scy			(void) printf("%s", wfp->wf_header);
1814275970Scy	}
1815275970Scy	(void) printf("\n");
1816275970Scy}
1817275970Scy
1818275970Scystatic void
1819275970Scyprint_wifi_field(print_wifi_state_t *statep, wifi_field_t *wfp,
1820275970Scy    const char *value)
1821275970Scy{
1822275970Scy	uint_t	width = wfp->wf_width;
1823275970Scy	uint_t	valwidth = strlen(value);
1824275970Scy	uint_t	compress;
1825275970Scy
1826275970Scy	if (statep->ws_parseable) {
1827275970Scy		(void) printf("%s=\"%s\"", wfp->wf_header, value);
1828275970Scy	} else {
1829275970Scy		if (value[0] == '\0')
1830275970Scy			value = "--";
1831275970Scy		if (statep->ws_lastfield) {
1832275970Scy			(void) printf("%s", value);
1833275970Scy			return;
1834275970Scy		}
1835275970Scy
1836275970Scy		if (valwidth > width) {
1837275970Scy			statep->ws_overflow += valwidth - width;
1838275970Scy		} else if (valwidth < width && statep->ws_overflow > 0) {
1839275970Scy			compress = min(statep->ws_overflow, width - valwidth);
1840275970Scy			statep->ws_overflow -= compress;
1841275970Scy			width -= compress;
1842275970Scy		}
1843275970Scy		(void) printf("%-*s", width, value);
1844275970Scy	}
1845275970Scy
1846275970Scy	if (!statep->ws_lastfield)
1847275970Scy		(void) putchar(' ');
1848275970Scy}
1849275970Scy
1850275970Scystatic void
1851275970Scyprint_wlan_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
1852275970Scy    wladm_wlan_attr_t *attrp)
1853275970Scy{
1854275970Scy	char		buf[WLADM_STRSIZE];
1855275970Scy	const char	*str = "";
1856275970Scy
1857275970Scy	if (wfp->wf_mask == 0) {
1858275970Scy		print_wifi_field(statep, wfp, statep->ws_link);
1859275970Scy		return;
1860275970Scy	}
1861275970Scy
1862275970Scy	if ((wfp->wf_mask & attrp->wa_valid) == 0) {
1863275970Scy		print_wifi_field(statep, wfp, "");
1864275970Scy		return;
1865275970Scy	}
1866275970Scy
1867275970Scy	switch (wfp->wf_mask) {
1868275970Scy	case WLADM_WLAN_ATTR_ESSID:
1869275970Scy		str = wladm_essid2str(&attrp->wa_essid, buf);
1870275970Scy		break;
1871275970Scy	case WLADM_WLAN_ATTR_BSSID:
1872275970Scy		str = wladm_bssid2str(&attrp->wa_bssid, buf);
1873275970Scy		break;
1874275970Scy	case WLADM_WLAN_ATTR_SECMODE:
1875275970Scy		str = wladm_secmode2str(&attrp->wa_secmode, buf);
1876275970Scy		break;
1877275970Scy	case WLADM_WLAN_ATTR_STRENGTH:
1878275970Scy		str = wladm_strength2str(&attrp->wa_strength, buf);
1879275970Scy		break;
1880275970Scy	case WLADM_WLAN_ATTR_MODE:
1881275970Scy		str = wladm_mode2str(&attrp->wa_mode, buf);
1882275970Scy		break;
1883275970Scy	case WLADM_WLAN_ATTR_SPEED:
1884275970Scy		str = wladm_speed2str(&attrp->wa_speed, buf);
1885275970Scy		(void) strlcat(buf, "Mb", sizeof (buf));
1886275970Scy		break;
1887275970Scy	case WLADM_WLAN_ATTR_AUTH:
1888275970Scy		str = wladm_auth2str(&attrp->wa_auth, buf);
1889275970Scy		break;
1890275970Scy	case WLADM_WLAN_ATTR_BSSTYPE:
1891275970Scy		str = wladm_bsstype2str(&attrp->wa_bsstype, buf);
1892275970Scy		break;
1893275970Scy	}
1894275970Scy
1895275970Scy	print_wifi_field(statep, wfp, str);
1896275970Scy}
1897275970Scy
1898275970Scystatic boolean_t
1899275970Scyprint_scan_results(void *arg, wladm_wlan_attr_t *attrp)
1900275970Scy{
1901275970Scy	print_wifi_state_t	*statep = arg;
1902275970Scy	int			i;
1903275970Scy
1904275970Scy	if (statep->ws_header) {
1905275970Scy		statep->ws_header = B_FALSE;
1906275970Scy		if (!statep->ws_parseable)
1907275970Scy			print_wifi_head(statep);
1908275970Scy	}
1909275970Scy
1910275970Scy	statep->ws_overflow = 0;
1911275970Scy	for (i = 0; i < statep->ws_nfields; i++) {
1912275970Scy		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
1913275970Scy		print_wlan_attr(statep, statep->ws_fields[i], attrp);
1914275970Scy	}
1915275970Scy	(void) putchar('\n');
1916275970Scy	return (B_TRUE);
1917275970Scy}
1918275970Scy
1919275970Scystatic boolean_t
1920275970Scyscan_wifi(void *arg, const char *link)
1921275970Scy{
1922275970Scy	print_wifi_state_t	*statep = arg;
1923275970Scy	wladm_status_t		status;
1924275970Scy
1925275970Scy	statep->ws_link = link;
1926275970Scy	status = wladm_scan(link, statep, print_scan_results);
1927275970Scy	if (status != WLADM_STATUS_OK)
1928275970Scy		die_wlerr(status, "cannot scan link '%s'", link);
1929275970Scy
1930275970Scy	return (B_TRUE);
1931275970Scy}
1932275970Scy
1933275970Scystatic void
1934275970Scyprint_link_attr(print_wifi_state_t *statep, wifi_field_t *wfp,
1935275970Scy    wladm_link_attr_t *attrp)
1936275970Scy{
1937275970Scy	char		buf[WLADM_STRSIZE];
1938275970Scy	const char	*str = "";
1939275970Scy
1940275970Scy	if (strcmp(wfp->wf_name, "status") == 0) {
1941275970Scy		if ((wfp->wf_mask & attrp->la_valid) != 0)
1942275970Scy			str = wladm_linkstatus2str(&attrp->la_status, buf);
1943275970Scy		print_wifi_field(statep, wfp, str);
1944275970Scy		return;
1945275970Scy	}
1946275970Scy	print_wlan_attr(statep, wfp, &attrp->la_wlan_attr);
1947275970Scy}
1948275970Scy
1949275970Scystatic boolean_t
1950289997Sglebiusshow_wifi(void *arg, const char *link)
1951275970Scy{
1952275970Scy	int			i;
1953275970Scy	print_wifi_state_t	*statep = arg;
1954275970Scy	wladm_link_attr_t	attr;
1955275970Scy	wladm_status_t		status;
1956275970Scy
1957275970Scy	status = wladm_get_link_attr(link, &attr);
1958275970Scy	if (status != WLADM_STATUS_OK)
1959275970Scy		die_wlerr(status, "cannot get link attributes for '%s'", link);
1960275970Scy
1961275970Scy	if (statep->ws_header) {
1962275970Scy		statep->ws_header = B_FALSE;
1963275970Scy		if (!statep->ws_parseable)
1964275970Scy			print_wifi_head(statep);
1965275970Scy	}
1966275970Scy
1967275970Scy	statep->ws_link = link;
1968275970Scy	statep->ws_overflow = 0;
1969275970Scy	for (i = 0; i < statep->ws_nfields; i++) {
1970275970Scy		statep->ws_lastfield = (i + 1 == statep->ws_nfields);
1971275970Scy		print_link_attr(statep, statep->ws_fields[i], &attr);
1972275970Scy	}
1973275970Scy	(void) putchar('\n');
1974275970Scy	return (B_TRUE);
1975275970Scy}
1976275970Scy
1977275970Scystatic void
1978275970Scydo_display_wifi(int argc, char **argv, int cmd)
1979275970Scy{
1980275970Scy	int			option;
1981275970Scy	char			*fields_str = NULL;
1982275970Scy	wifi_field_t		**fields;
1983275970Scy	boolean_t		(*callback)(void *, const char *);
1984275970Scy	uint_t			nfields;
1985275970Scy	print_wifi_state_t	state;
1986275970Scy	wladm_status_t		status;
1987275970Scy
1988275970Scy	if (cmd == WIFI_CMD_SCAN)
1989275970Scy		callback = scan_wifi;
1990275970Scy	else if (cmd == WIFI_CMD_SHOW)
1991275970Scy		callback = show_wifi;
1992275970Scy	else
1993275970Scy		return;
1994275970Scy
1995275970Scy	state.ws_link = NULL;
1996275970Scy	state.ws_parseable = B_FALSE;
1997275970Scy	state.ws_header = B_TRUE;
1998275970Scy	opterr = 0;
1999275970Scy	while ((option = getopt_long(argc, argv, ":o:p",
2000275970Scy	    wifi_longopts, NULL)) != -1) {
2001275970Scy		switch (option) {
2002275970Scy		case 'o':
2003275970Scy			fields_str = optarg;
2004275970Scy			break;
2005275970Scy		case 'p':
2006275970Scy			state.ws_parseable = B_TRUE;
2007275970Scy			if (fields_str == NULL)
2008275970Scy				fields_str = "all";
2009275970Scy			break;
2010275970Scy		default:
2011275970Scy			die_opterr(optopt, option);
2012275970Scy			break;
2013275970Scy		}
2014275970Scy	}
2015275970Scy
2016275970Scy	if (optind == (argc - 1))
2017275970Scy		state.ws_link = argv[optind];
2018275970Scy	else if (optind != argc)
2019275970Scy		usage();
2020275970Scy
2021275970Scy	if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0)
2022275970Scy		die("invalid field(s) specified");
2023275970Scy
2024275970Scy	state.ws_fields = fields;
2025275970Scy	state.ws_nfields = nfields;
2026275970Scy
2027275970Scy	if (state.ws_link == NULL) {
2028275970Scy		status = wladm_walk(&state, callback);
2029275970Scy		if (status != WLADM_STATUS_OK)
2030275970Scy			die_wlerr(status, "cannot walk wifi links");
2031275970Scy	} else {
2032275970Scy		(void) (*callback)(&state, state.ws_link);
2033275970Scy	}
2034275970Scy	free(fields);
2035275970Scy}
2036275970Scy
2037275970Scystatic void
2038275970Scydo_scan_wifi(int argc, char **argv)
2039275970Scy{
2040275970Scy	do_display_wifi(argc, argv, WIFI_CMD_SCAN);
2041275970Scy}
2042289997Sglebius
2043275970Scystatic void
2044275970Scydo_show_wifi(int argc, char **argv)
2045275970Scy{
2046275970Scy	do_display_wifi(argc, argv, WIFI_CMD_SHOW);
2047275970Scy}
2048275970Scy
2049275970Scytypedef struct wlan_count_attr {
2050275970Scy	uint_t		wc_count;
2051275970Scy	const char	*wc_link;
2052275970Scy} wlan_count_attr_t;
2053275970Scy
2054275970Scystatic boolean_t
2055275970Scydo_count_wlan(void *arg, const char *link)
2056275970Scy{
2057275970Scy	wlan_count_attr_t *cp = arg;
2058275970Scy
2059275970Scy	if (cp->wc_count == 0)
2060275970Scy		cp->wc_link = strdup(link);
2061275970Scy	cp->wc_count++;
2062275970Scy	return (B_TRUE);
2063275970Scy}
2064275970Scy
2065275970Scystatic int
2066275970Scyparse_wep_keys(char *str, wladm_wep_key_t **keys, uint_t *key_countp)
2067275970Scy{
2068275970Scy	uint_t		i;
2069275970Scy	split_t		*sp;
2070275970Scy	wladm_wep_key_t	*wk;
2071275970Scy
2072275970Scy	sp = split(str, WLADM_MAX_WEPKEYS, WLADM_MAX_WEPKEYNAME_LEN);
2073275970Scy	if (sp == NULL)
2074275970Scy		return (-1);
2075275970Scy
2076275970Scy	wk = malloc(sp->s_nfields * sizeof (wladm_wep_key_t));
2077275970Scy	if (wk == NULL)
2078275970Scy		goto fail;
2079275970Scy
2080275970Scy	for (i = 0; i < sp->s_nfields; i++) {
2081275970Scy		char			*s;
2082275970Scy		dladm_secobj_class_t	class;
2083275970Scy		dladm_status_t		status;
2084275970Scy
2085275970Scy		(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
2086275970Scy		    WLADM_MAX_WEPKEYNAME_LEN);
2087275970Scy
2088275970Scy		wk[i].wk_idx = 1;
2089275970Scy		if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
2090275970Scy			if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
2091275970Scy				goto fail;
2092275970Scy
2093275970Scy			wk[i].wk_idx = (uint_t)(s[1] - '0');
2094275970Scy			*s = '\0';
2095275970Scy		}
2096275970Scy		wk[i].wk_len = WLADM_MAX_WEPKEY_LEN;
2097275970Scy
2098275970Scy		status = dladm_get_secobj(wk[i].wk_name, &class,
2099275970Scy		    wk[i].wk_val, &wk[i].wk_len, 0);
2100275970Scy		if (status != DLADM_STATUS_OK) {
2101275970Scy			if (status == DLADM_STATUS_NOTFOUND) {
2102275970Scy				status = dladm_get_secobj(wk[i].wk_name,
2103275970Scy				    &class, wk[i].wk_val, &wk[i].wk_len,
2104275970Scy				    DLADM_OPT_PERSIST);
2105275970Scy			}
2106275970Scy			if (status != DLADM_STATUS_OK)
2107275970Scy				goto fail;
2108275970Scy		}
2109275970Scy	}
2110275970Scy	*keys = wk;
2111275970Scy	*key_countp = i;
2112275970Scy	splitfree(sp);
2113275970Scy	return (0);
2114275970Scyfail:
2115275970Scy	free(wk);
2116275970Scy	splitfree(sp);
2117275970Scy	return (-1);
2118275970Scy}
2119275970Scy
2120275970Scystatic void
2121275970Scydo_connect_wifi(int argc, char **argv)
2122275970Scy{
2123275970Scy	int			option;
2124275970Scy	wladm_wlan_attr_t	attr, *attrp;
2125275970Scy	wladm_status_t		status = WLADM_STATUS_OK;
2126275970Scy	int			timeout = WLADM_CONNECT_TIMEOUT_DEFAULT;
2127275970Scy	const char		*link = NULL;
2128275970Scy	wladm_wep_key_t		*keys = NULL;
2129275970Scy	uint_t			key_count = 0;
2130275970Scy	uint_t			flags = 0;
2131275970Scy	wladm_secmode_t		keysecmode = WLADM_SECMODE_NONE;
2132275970Scy
2133275970Scy	opterr = 0;
2134275970Scy	(void) memset(&attr, 0, sizeof (attr));
2135275970Scy	while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
2136275970Scy	    wifi_longopts, NULL)) != -1) {
2137275970Scy		switch (option) {
2138275970Scy		case 'e':
2139275970Scy			status = wladm_str2essid(optarg, &attr.wa_essid);
2140275970Scy			if (status != WLADM_STATUS_OK)
2141275970Scy				die("invalid ESSID '%s'", optarg);
2142275970Scy
2143275970Scy			attr.wa_valid |= WLADM_WLAN_ATTR_ESSID;
2144275970Scy			/*
2145275970Scy			 * Try to connect without doing a scan.
2146275970Scy			 */
2147275970Scy			flags |= WLADM_OPT_NOSCAN;
2148275970Scy			break;
2149275970Scy		case 'i':
2150275970Scy			status = wladm_str2bssid(optarg, &attr.wa_bssid);
2151275970Scy			if (status != WLADM_STATUS_OK)
2152275970Scy				die("invalid BSSID %s", optarg);
2153275970Scy
2154275970Scy			attr.wa_valid |= WLADM_WLAN_ATTR_BSSID;
2155275970Scy			break;
2156275970Scy		case 'a':
2157275970Scy			status = wladm_str2auth(optarg, &attr.wa_auth);
2158275970Scy			if (status != WLADM_STATUS_OK)
2159275970Scy				die("invalid authentication mode '%s'", optarg);
2160275970Scy
2161275970Scy			attr.wa_valid |= WLADM_WLAN_ATTR_AUTH;
2162275970Scy			break;
2163275970Scy		case 'm':
2164275970Scy			status = wladm_str2mode(optarg, &attr.wa_mode);
2165275970Scy			if (status != WLADM_STATUS_OK)
2166275970Scy				die("invalid mode '%s'", optarg);
2167275970Scy
2168275970Scy			attr.wa_valid |= WLADM_WLAN_ATTR_MODE;
2169275970Scy			break;
2170275970Scy		case 'b':
2171275970Scy			status = wladm_str2bsstype(optarg, &attr.wa_bsstype);
2172275970Scy			if (status != WLADM_STATUS_OK)
2173275970Scy				die("invalid bsstype '%s'", optarg);
2174275970Scy
2175275970Scy			attr.wa_valid |= WLADM_WLAN_ATTR_BSSTYPE;
2176275970Scy			break;
2177275970Scy		case 's':
2178275970Scy			status = wladm_str2secmode(optarg, &attr.wa_secmode);
2179275970Scy			if (status != WLADM_STATUS_OK)
2180275970Scy				die("invalid security mode '%s'", optarg);
2181275970Scy
2182275970Scy			attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE;
2183275970Scy			break;
2184275970Scy		case 'k':
2185275970Scy			if (parse_wep_keys(optarg, &keys, &key_count) < 0)
2186275970Scy				die("invalid key(s) '%s'", optarg);
2187275970Scy
2188275970Scy			keysecmode = WLADM_SECMODE_WEP;
2189275970Scy			break;
2190275970Scy		case 'T':
2191275970Scy			if (strcasecmp(optarg, "forever") == 0) {
2192275970Scy				timeout = -1;
2193275970Scy				break;
2194275970Scy			}
2195275970Scy			if (!str2int(optarg, &timeout) || timeout < 0)
2196275970Scy				die("invalid timeout value '%s'", optarg);
2197275970Scy			break;
2198275970Scy		case 'c':
2199275970Scy			flags |= WLADM_OPT_CREATEIBSS;
2200275970Scy			break;
2201275970Scy		default:
2202275970Scy			die_opterr(optopt, option);
2203275970Scy			break;
2204275970Scy		}
2205275970Scy	}
2206275970Scy
2207275970Scy	if (keysecmode == WLADM_SECMODE_NONE) {
2208275970Scy		if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 &&
2209275970Scy		    attr.wa_secmode == WLADM_SECMODE_WEP)
2210275970Scy			die("key required for security mode 'wep'");
2211275970Scy	} else {
2212275970Scy		if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 &&
2213275970Scy		    attr.wa_secmode != keysecmode)
2214275970Scy			die("incompatible -s and -k options");
2215275970Scy	}
2216275970Scy	attr.wa_secmode = keysecmode;
2217275970Scy	attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE;
2218275970Scy
2219275970Scy	if (optind == (argc - 1))
2220275970Scy		link = argv[optind];
2221275970Scy	else if (optind != argc)
2222275970Scy		usage();
2223275970Scy
2224275970Scy	if (link == NULL) {
2225275970Scy		wlan_count_attr_t wcattr;
2226275970Scy
2227275970Scy		wcattr.wc_link = NULL;
2228275970Scy		wcattr.wc_count = 0;
2229275970Scy		(void) wladm_walk(&wcattr, do_count_wlan);
2230275970Scy		if (wcattr.wc_count == 0) {
2231275970Scy			die("no wifi links are available");
2232275970Scy		} else if (wcattr.wc_count > 1) {
2233275970Scy			die("link name is required when more than one wifi "
2234275970Scy			    "link is available");
2235275970Scy		}
2236275970Scy		link = wcattr.wc_link;
2237275970Scy	}
2238275970Scy	attrp = (attr.wa_valid == 0) ? NULL : &attr;
2239275970Scyagain:
2240275970Scy	status = wladm_connect(link, attrp, timeout, keys, key_count, flags);
2241275970Scy	if (status != WLADM_STATUS_OK) {
2242275970Scy		if ((flags & WLADM_OPT_NOSCAN) != 0) {
2243275970Scy			/*
2244275970Scy			 * Try again with scanning and filtering.
2245275970Scy			 */
2246275970Scy			flags &= ~WLADM_OPT_NOSCAN;
2247275970Scy			goto again;
2248275970Scy		}
2249275970Scy
2250275970Scy		if (status == WLADM_STATUS_NOTFOUND) {
2251275970Scy			if (attr.wa_valid == 0) {
2252275970Scy				die("no wifi networks are available");
2253275970Scy			} else {
2254275970Scy				die("no wifi networks with the specified "
2255275970Scy				    "criteria are available");
2256275970Scy			}
2257275970Scy		}
2258275970Scy		die_wlerr(status, "cannot connect link '%s'", link);
2259275970Scy	}
2260275970Scy	free(keys);
2261275970Scy}
2262275970Scy
2263275970Scy/* ARGSUSED */
2264275970Scystatic boolean_t
2265275970Scydo_all_disconnect_wifi(void *arg, const char *link)
2266275970Scy{
2267275970Scy	wladm_status_t	status;
2268275970Scy
2269275970Scy	status = wladm_disconnect(link);
2270275970Scy	if (status != WLADM_STATUS_OK)
2271275970Scy		warn_wlerr(status, "cannot disconnect link '%s'", link);
2272275970Scy
2273275970Scy	return (B_TRUE);
2274275970Scy}
2275275970Scy
2276275970Scystatic void
2277275970Scydo_disconnect_wifi(int argc, char **argv)
2278275970Scy{
2279275970Scy	int			option;
2280275970Scy	const char		*link = NULL;
2281275970Scy	boolean_t		all_links = B_FALSE;
2282275970Scy	wladm_status_t		status;
2283275970Scy	wlan_count_attr_t	wcattr;
2284275970Scy
2285275970Scy	opterr = 0;
2286275970Scy	while ((option = getopt_long(argc, argv, ":a",
2287275970Scy	    wifi_longopts, NULL)) != -1) {
2288275970Scy		switch (option) {
2289275970Scy		case 'a':
2290275970Scy			all_links = B_TRUE;
2291275970Scy			break;
2292275970Scy		default:
2293275970Scy			die_opterr(optopt, option);
2294275970Scy			break;
2295275970Scy		}
2296275970Scy	}
2297275970Scy
2298275970Scy	if (optind == (argc - 1))
2299275970Scy		link = argv[optind];
2300275970Scy	else if (optind != argc)
2301275970Scy		usage();
2302275970Scy
2303275970Scy	if (link == NULL) {
2304275970Scy		if (!all_links) {
2305275970Scy			wcattr.wc_link = NULL;
2306275970Scy			wcattr.wc_count = 0;
2307275970Scy			(void) wladm_walk(&wcattr, do_count_wlan);
2308275970Scy			if (wcattr.wc_count == 0) {
2309275970Scy				die("no wifi links are available");
2310275970Scy			} else if (wcattr.wc_count > 1) {
2311275970Scy				die("link name is required when more than "
2312275970Scy				    "one wifi link is available");
2313275970Scy			}
2314275970Scy			link = wcattr.wc_link;
2315275970Scy		} else {
2316275970Scy			(void) wladm_walk(&all_links, do_all_disconnect_wifi);
2317275970Scy			return;
2318275970Scy		}
2319275970Scy	}
2320275970Scy	status = wladm_disconnect(link);
2321275970Scy	if (status != WLADM_STATUS_OK)
2322275970Scy		die_wlerr(status, "cannot disconnect link '%s'", link);
2323275970Scy}
2324275970Scy
2325275970Scy#define	MAX_PROPS		32
2326275970Scy#define	MAX_PROP_VALS		32
2327275970Scy#define	MAX_PROP_LINE		512
2328275970Scy
2329275970Scytypedef struct prop_info {
2330275970Scy	char		*pi_name;
2331275970Scy	char		*pi_val[MAX_PROP_VALS];
2332275970Scy	uint_t		pi_count;
2333275970Scy} prop_info_t;
2334275970Scy
2335275970Scytypedef struct prop_list {
2336275970Scy	prop_info_t	pl_info[MAX_PROPS];
2337275970Scy	uint_t		pl_count;
2338275970Scy	char		*pl_buf;
2339275970Scy} prop_list_t;
2340275970Scy
2341275970Scytypedef struct show_linkprop_state {
2342275970Scy	const char	*ls_link;
2343275970Scy	char		*ls_line;
2344275970Scy	char		**ls_propvals;
2345275970Scy	prop_list_t	*ls_proplist;
2346275970Scy	boolean_t	ls_parseable;
2347275970Scy	boolean_t	ls_persist;
2348275970Scy	boolean_t	ls_header;
2349275970Scy} show_linkprop_state_t;
2350275970Scy
2351275970Scystatic void
2352275970Scyfree_props(prop_list_t *list)
2353275970Scy{
2354275970Scy	if (list != NULL) {
2355275970Scy		free(list->pl_buf);
2356275970Scy		free(list);
2357275970Scy	}
2358275970Scy}
2359275970Scy
2360275970Scystatic int
2361275970Scyparse_props(char *str, prop_list_t **listp, boolean_t novalues)
2362275970Scy{
2363275970Scy	prop_list_t	*list;
2364275970Scy	prop_info_t	*pip;
2365275970Scy	char		*buf, *curr;
2366275970Scy	int		len, i;
2367275970Scy
2368275970Scy	list = malloc(sizeof (prop_list_t));
2369275970Scy	if (list == NULL)
2370275970Scy		return (-1);
2371275970Scy
2372275970Scy	list->pl_count = 0;
2373275970Scy	list->pl_buf = buf = strdup(str);
2374275970Scy	if (buf == NULL)
2375275970Scy		goto fail;
2376275970Scy
2377275970Scy	curr = buf;
2378275970Scy	len = strlen(buf);
2379275970Scy	pip = NULL;
2380275970Scy	for (i = 0; i < len; i++) {
2381275970Scy		char		c = buf[i];
2382275970Scy		boolean_t	match = (c == '=' || c == ',');
2383275970Scy
2384275970Scy		if (!match && i != len - 1)
2385275970Scy			continue;
2386275970Scy
2387275970Scy		if (match) {
2388275970Scy			buf[i] = '\0';
2389275970Scy			if (*curr == '\0')
2390275970Scy				goto fail;
2391275970Scy		}
2392275970Scy
2393275970Scy		if (pip != NULL && c != '=') {
2394275970Scy			if (pip->pi_count > MAX_PROP_VALS)
2395275970Scy				goto fail;
2396275970Scy
2397275970Scy			if (novalues)
2398275970Scy				goto fail;
2399275970Scy
2400275970Scy			pip->pi_val[pip->pi_count] = curr;
2401275970Scy			pip->pi_count++;
2402275970Scy		} else {
2403275970Scy			if (list->pl_count > MAX_PROPS)
2404275970Scy				goto fail;
2405275970Scy
2406275970Scy			pip = &list->pl_info[list->pl_count];
2407275970Scy			pip->pi_name = curr;
2408275970Scy			pip->pi_count = 0;
2409275970Scy			list->pl_count++;
2410275970Scy			if (c == ',')
2411275970Scy				pip = NULL;
2412275970Scy		}
2413275970Scy		curr = buf + i + 1;
2414275970Scy	}
2415275970Scy	*listp = list;
2416275970Scy	return (0);
2417275970Scy
2418275970Scyfail:
2419275970Scy	free_props(list);
2420275970Scy	return (-1);
2421275970Scy}
2422275970Scy
2423275970Scystatic void
2424275970Scyprint_linkprop_head(void)
2425275970Scy{
2426275970Scy	(void) printf("%-12s %-15s %-14s %-14s %-20s \n",
2427275970Scy	    "LINK", "PROPERTY", "VALUE", "DEFAULT", "POSSIBLE");
2428275970Scy}
2429275970Scy
2430275970Scystatic void
2431275970Scyprint_linkprop(show_linkprop_state_t *statep, const char *propname,
2432275970Scy    dladm_prop_type_t type, const char *typename, const char *format,
2433285612Sdelphij    char **pptr)
2434285612Sdelphij{
2435298770Sdelphij	int		i;
2436298770Sdelphij	char		*ptr, *lim;
2437275970Scy	char		buf[DLADM_STRSIZE];
2438275970Scy	char		*unknown = "?", *notsup = "";
2439275970Scy	char		**propvals = statep->ls_propvals;
2440275970Scy	uint_t		valcnt = MAX_PROP_VALS;
2441294569Sdelphij	dladm_status_t	status;
2442294569Sdelphij
2443294569Sdelphij	status = dladm_get_prop(statep->ls_link, type, propname,
2444275970Scy	    propvals, &valcnt);
2445275970Scy	if (status != DLADM_STATUS_OK) {
2446275970Scy		if (status == DLADM_STATUS_NOTSUP || statep->ls_persist) {
2447275970Scy			valcnt = 1;
2448275970Scy			if (type == DLADM_PROP_VAL_CURRENT)
2449275970Scy				propvals = &unknown;
2450294569Sdelphij			else
2451294569Sdelphij				propvals = &notsup;
2452294569Sdelphij		} else {
2453275970Scy			die_dlerr(status, "cannot get link property '%s'",
2454275970Scy			    propname);
2455275970Scy		}
2456275970Scy	}
2457275970Scy
2458275970Scy	ptr = buf;
2459275970Scy	lim = buf + DLADM_STRSIZE;
2460275970Scy	for (i = 0; i < valcnt; i++) {
2461275970Scy		if (propvals[i][0] == '\0' && !statep->ls_parseable)
2462275970Scy			ptr += snprintf(ptr, lim - ptr, "--,");
2463275970Scy		else
2464275970Scy			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
2465275970Scy		if (ptr >= lim)
2466275970Scy			break;
2467275970Scy	}
2468275970Scy	if (valcnt > 0)
2469275970Scy		buf[strlen(buf) - 1] = '\0';
2470275970Scy
2471275970Scy	lim = statep->ls_line + MAX_PROP_LINE;
2472275970Scy	if (statep->ls_parseable) {
2473275970Scy		*pptr += snprintf(*pptr, lim - *pptr,
2474275970Scy		    "%s=\"%s\" ", typename, buf);
2475275970Scy	} else {
2476275970Scy		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
2477275970Scy	}
2478275970Scy}
2479275970Scy
2480275970Scystatic boolean_t
2481275970Scyshow_linkprop(void *arg, const char *propname)
2482275970Scy{
2483275970Scy	show_linkprop_state_t	*statep = arg;
2484275970Scy	char			*ptr = statep->ls_line;
2485275970Scy	char			*lim = ptr + MAX_PROP_LINE;
2486275970Scy
2487275970Scy	if (statep->ls_persist && dladm_is_prop_temponly(propname, NULL))
2488275970Scy		return (B_TRUE);
2489275970Scy
2490275970Scy	if (statep->ls_parseable)
2491275970Scy		ptr += snprintf(ptr, lim - ptr, "LINK=\"%s\" ",
2492275970Scy		    statep->ls_link);
2493275970Scy	else
2494275970Scy		ptr += snprintf(ptr, lim - ptr, "%-12s ", statep->ls_link);
2495275970Scy
2496275970Scy	if (statep->ls_parseable)
2497275970Scy		ptr += snprintf(ptr, lim - ptr, "PROPERTY=\"%s\" ", propname);
2498275970Scy	else
2499275970Scy		ptr += snprintf(ptr, lim - ptr, "%-15s ", propname);
2500275970Scy
2501275970Scy	print_linkprop(statep, propname,
2502275970Scy	    statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
2503275970Scy	    DLADM_PROP_VAL_CURRENT, "VALUE", "%-14s ", &ptr);
2504275970Scy	print_linkprop(statep, propname, DLADM_PROP_VAL_DEFAULT,
2505275970Scy	    "DEFAULT", "%-14s ", &ptr);
2506275970Scy	print_linkprop(statep, propname, DLADM_PROP_VAL_MODIFIABLE,
2507275970Scy	    "POSSIBLE", "%-20s ", &ptr);
2508275970Scy
2509275970Scy	if (statep->ls_header) {
2510275970Scy		statep->ls_header = B_FALSE;
2511275970Scy		if (!statep->ls_parseable)
2512275970Scy			print_linkprop_head();
2513275970Scy	}
2514275970Scy	(void) printf("%s\n", statep->ls_line);
2515275970Scy	return (B_TRUE);
2516275970Scy}
2517275970Scy
2518275970Scystatic void
2519275970Scydo_show_linkprop(int argc, char **argv)
2520275970Scy{
2521275970Scy	int			option;
2522275970Scy	prop_list_t		*proplist = NULL;
2523275970Scy	show_linkprop_state_t	state;
2524275970Scy
2525275970Scy	opterr = 0;
2526294569Sdelphij	state.ls_link = NULL;
2527294569Sdelphij	state.ls_propvals = NULL;
2528294569Sdelphij	state.ls_line = NULL;
2529294569Sdelphij	state.ls_parseable = B_FALSE;
2530294569Sdelphij	state.ls_persist = B_FALSE;
2531294569Sdelphij	state.ls_header = B_TRUE;
2532294569Sdelphij	while ((option = getopt_long(argc, argv, ":p:cP",
2533294569Sdelphij	    prop_longopts, NULL)) != -1) {
2534294569Sdelphij		switch (option) {
2535294569Sdelphij		case 'p':
2536294569Sdelphij			if (parse_props(optarg, &proplist, B_TRUE) < 0)
2537294569Sdelphij				die("invalid link properties specified");
2538294569Sdelphij			break;
2539294569Sdelphij		case 'c':
2540294569Sdelphij			state.ls_parseable = B_TRUE;
2541294569Sdelphij			break;
2542294569Sdelphij		case 'P':
2543294569Sdelphij			state.ls_persist = B_TRUE;
2544294569Sdelphij			break;
2545294569Sdelphij		default:
2546294569Sdelphij			die_opterr(optopt, option);
2547294569Sdelphij			break;
2548294569Sdelphij		}
2549294569Sdelphij	}
2550294569Sdelphij
2551294569Sdelphij	if (optind == (argc - 1))
2552294569Sdelphij		state.ls_link = argv[optind];
2553294569Sdelphij	else if (optind != argc)
2554294569Sdelphij		usage();
2555294569Sdelphij
2556294569Sdelphij	state.ls_proplist = proplist;
2557294569Sdelphij
2558294569Sdelphij	if (state.ls_link == NULL) {
2559294569Sdelphij		(void) dladm_walk(show_linkprop_onelink, &state);
2560294569Sdelphij	} else {
2561294569Sdelphij		show_linkprop_onelink(&state, state.ls_link);
2562294569Sdelphij	}
2563294569Sdelphij	free_props(proplist);
2564294569Sdelphij}
2565294569Sdelphij
2566294569Sdelphijstatic void
2567294569Sdelphijshow_linkprop_onelink(void *arg, const char *link)
2568294569Sdelphij{
2569294569Sdelphij	int			i, fd;
2570294569Sdelphij	char			linkname[MAXPATHLEN];
2571294569Sdelphij	char			*buf;
2572294569Sdelphij	dladm_status_t		status;
2573294569Sdelphij	prop_list_t		*proplist = NULL;
2574294569Sdelphij	show_linkprop_state_t	*statep;
2575294569Sdelphij	const char		*savep;
2576294569Sdelphij
2577294569Sdelphij	statep = (show_linkprop_state_t *)arg;
2578294569Sdelphij	savep = statep->ls_link;
2579294569Sdelphij	statep->ls_link = link;
2580294569Sdelphij	proplist = statep->ls_proplist;
2581294569Sdelphij
2582294569Sdelphij	/*
2583294569Sdelphij	 * When some WiFi links are opened for the first time, their hardware
2584294569Sdelphij	 * automatically scans for APs and does other slow operations.	Thus,
2585294569Sdelphij	 * if there are no open links, the retrieval of link properties
2586294569Sdelphij	 * (below) will proceed slowly unless we hold the link open.
2587275970Scy	 */
2588275970Scy	(void) snprintf(linkname, MAXPATHLEN, "/dev/%s", link);
2589275970Scy	if ((fd = open(linkname, O_RDWR)) < 0)
2590275970Scy		die("cannot open %s: %s", link, strerror(errno));
2591275970Scy
2592275970Scy	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS +
2593275970Scy	    MAX_PROP_LINE);
2594275970Scy	if (buf == NULL)
2595275970Scy		die("insufficient memory");
2596275970Scy
2597275970Scy	statep->ls_propvals = (char **)(void *)buf;
2598275970Scy	for (i = 0; i < MAX_PROP_VALS; i++) {
2599285612Sdelphij		statep->ls_propvals[i] = buf + sizeof (char *) * MAX_PROP_VALS +
2600285612Sdelphij		    i * DLADM_PROP_VAL_MAX;
2601285612Sdelphij	}
2602285612Sdelphij	statep->ls_line = buf +
2603285612Sdelphij	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS;
2604285612Sdelphij
2605285612Sdelphij	if (proplist != NULL) {
2606285612Sdelphij		for (i = 0; i < proplist->pl_count; i++) {
2607285612Sdelphij			if (!show_linkprop(statep,
2608285612Sdelphij			    proplist->pl_info[i].pi_name))
2609285612Sdelphij				break;
2610285612Sdelphij		}
2611285612Sdelphij	} else {
2612275970Scy		status = dladm_walk_prop(link, statep, show_linkprop);
2613275970Scy		if (status != DLADM_STATUS_OK)
2614275970Scy			die_dlerr(status, "show-linkprop");
2615275970Scy	}
2616275970Scy	(void) close(fd);
2617275970Scy	free(buf);
2618275970Scy	statep->ls_link = savep;
2619275970Scy}
2620275970Scy
2621275970Scystatic dladm_status_t
2622275970Scyset_linkprop_persist(const char *link, const char *prop_name, char **prop_val,
2623275970Scy    uint_t val_cnt, boolean_t reset)
2624275970Scy{
2625275970Scy	dladm_status_t	status;
2626275970Scy	char		*errprop;
2627275970Scy
2628275970Scy	status = dladm_set_prop(link, prop_name, prop_val, val_cnt,
2629275970Scy	    DLADM_OPT_PERSIST, &errprop);
2630275970Scy
2631275970Scy	if (status != DLADM_STATUS_OK) {
2632275970Scy		if (reset) {
2633275970Scy			warn_dlerr(status, "cannot persistently reset link "
2634275970Scy			    "property '%s' on '%s'", errprop, link);
2635275970Scy		} else {
2636275970Scy			warn_dlerr(status, "cannot persistently set link "
2637275970Scy			    "property '%s' on '%s'", errprop, link);
2638275970Scy		}
2639275970Scy	}
2640275970Scy	return (status);
2641275970Scy}
2642275970Scy
2643275970Scystatic void
2644275970Scyset_linkprop(int argc, char **argv, boolean_t reset)
2645275970Scy{
2646275970Scy	int		i, option;
2647275970Scy	char		errmsg[DLADM_STRSIZE];
2648275970Scy	const char	*link = NULL;
2649275970Scy	prop_list_t	*proplist = NULL;
2650275970Scy	boolean_t	temp = B_FALSE;
2651275970Scy	dladm_status_t	status = DLADM_STATUS_OK;
2652275970Scy
2653275970Scy	opterr = 0;
2654275970Scy	while ((option = getopt_long(argc, argv, ":p:R:t",
2655275970Scy	    prop_longopts, NULL)) != -1) {
2656275970Scy		switch (option) {
2657275970Scy		case 'p':
2658275970Scy			if (parse_props(optarg, &proplist, reset) < 0)
2659275970Scy				die("invalid link properties specified");
2660275970Scy			break;
2661275970Scy		case 't':
2662275970Scy			temp = B_TRUE;
2663275970Scy			break;
2664275970Scy		case 'R':
2665275970Scy			status = dladm_set_rootdir(optarg);
2666275970Scy			if (status != DLADM_STATUS_OK) {
2667275970Scy				die_dlerr(status, "invalid directory "
2668275970Scy				    "specified");
2669275970Scy			}
2670275970Scy			break;
2671275970Scy		default:
2672275970Scy			die_opterr(optopt, option);
2673275970Scy			break;
2674275970Scy		}
2675298770Sdelphij	}
2676298770Sdelphij
2677275970Scy	if (optind == (argc - 1))
2678275970Scy		link = argv[optind];
2679275970Scy	else if (optind != argc)
2680275970Scy		usage();
2681275970Scy
2682275970Scy	if (link == NULL)
2683275970Scy		die("link name must be specified");
2684275970Scy
2685275970Scy	if (proplist == NULL) {
2686275970Scy		char *errprop;
2687275970Scy
2688275970Scy		if (!reset)
2689275970Scy			die("link property must be specified");
2690275970Scy
2691275970Scy		status = dladm_set_prop(link, NULL, NULL, 0, DLADM_OPT_TEMP,
2692275970Scy		    &errprop);
2693275970Scy		if (status != DLADM_STATUS_OK) {
2694275970Scy			warn_dlerr(status, "cannot reset link property '%s' "
2695275970Scy			    "on '%s'", errprop, link);
2696275970Scy		}
2697275970Scy		if (!temp) {
2698275970Scy			dladm_status_t	s;
2699275970Scy
2700298770Sdelphij			s = set_linkprop_persist(link, NULL, NULL, 0, reset);
2701298770Sdelphij			if (s != DLADM_STATUS_OK)
2702298770Sdelphij				status = s;
2703275970Scy		}
2704275970Scy		goto done;
2705275970Scy	}
2706275970Scy
2707275970Scy	for (i = 0; i < proplist->pl_count; i++) {
2708275970Scy		prop_info_t	*pip = &proplist->pl_info[i];
2709275970Scy		char		**val;
2710275970Scy		uint_t		count;
2711275970Scy		dladm_status_t	s;
2712275970Scy
2713275970Scy		if (reset) {
2714275970Scy			val = NULL;
2715275970Scy			count = 0;
2716275970Scy		} else {
2717275970Scy			val = pip->pi_val;
2718275970Scy			count = pip->pi_count;
2719275970Scy			if (count == 0) {
2720275970Scy				warn("no value specified for '%s'",
2721275970Scy				    pip->pi_name);
2722275970Scy				status = DLADM_STATUS_BADARG;
2723275970Scy				continue;
2724275970Scy			}
2725275970Scy		}
2726275970Scy		s = dladm_set_prop(link, pip->pi_name, val, count,
2727275970Scy		    DLADM_OPT_TEMP, NULL);
2728275970Scy		if (s == DLADM_STATUS_OK) {
2729275970Scy			if (!temp) {
2730275970Scy				s = set_linkprop_persist(link,
2731275970Scy				    pip->pi_name, val, count, reset);
2732275970Scy				if (s != DLADM_STATUS_OK)
2733275970Scy					status = s;
2734275970Scy			}
2735275970Scy			continue;
2736275970Scy		}
2737275970Scy		status = s;
2738275970Scy		switch (s) {
2739275970Scy		case DLADM_STATUS_NOTFOUND:
2740275970Scy			warn("invalid link property '%s'", pip->pi_name);
2741275970Scy			break;
2742275970Scy		case DLADM_STATUS_BADVAL: {
2743275970Scy			int		j;
2744275970Scy			char		*ptr, *lim;
2745275970Scy			char		**propvals = NULL;
2746275970Scy			uint_t		valcnt = MAX_PROP_VALS;
2747275970Scy
2748285612Sdelphij			ptr = malloc((sizeof (char *) +
2749285612Sdelphij			    DLADM_PROP_VAL_MAX) * MAX_PROP_VALS +
2750285612Sdelphij			    MAX_PROP_LINE);
2751275970Scy
2752275970Scy			propvals = (char **)(void *)ptr;
2753275970Scy			if (propvals == NULL)
2754275970Scy				die("insufficient memory");
2755275970Scy
2756275970Scy			for (j = 0; j < MAX_PROP_VALS; j++) {
2757275970Scy				propvals[j] = ptr + sizeof (char *) *
2758275970Scy				    MAX_PROP_VALS +
2759275970Scy				    j * DLADM_PROP_VAL_MAX;
2760275970Scy			}
2761275970Scy			s = dladm_get_prop(link, DLADM_PROP_VAL_MODIFIABLE,
2762275970Scy			    pip->pi_name, propvals, &valcnt);
2763275970Scy
2764275970Scy			ptr = errmsg;
2765275970Scy			lim = ptr + DLADM_STRSIZE;
2766275970Scy			*ptr = '\0';
2767275970Scy			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
2768275970Scy				ptr += snprintf(ptr, lim - ptr, "%s,",
2769275970Scy				    propvals[j]);
2770275970Scy				if (ptr >= lim)
2771275970Scy					break;
2772275970Scy			}
2773275970Scy			if (ptr > errmsg) {
2774275970Scy				*(ptr - 1) = '\0';
2775275970Scy				warn("link property '%s' must be one of: %s",
2776275970Scy				    pip->pi_name, errmsg);
2777275970Scy			} else
2778275970Scy				warn("invalid link property '%s'", *val);
2779275970Scy			free(propvals);
2780275970Scy			break;
2781275970Scy		}
2782275970Scy		default:
2783275970Scy			if (reset) {
2784275970Scy				warn_dlerr(status, "cannot reset link property "
2785275970Scy				    "'%s' on '%s'", pip->pi_name, link);
2786275970Scy			} else {
2787275970Scy				warn_dlerr(status, "cannot set link property "
2788275970Scy				    "'%s' on '%s'", pip->pi_name, link);
2789275970Scy			}
2790275970Scy			break;
2791275970Scy		}
2792275970Scy	}
2793275970Scydone:
2794275970Scy	free_props(proplist);
2795275970Scy	if (status != DLADM_STATUS_OK)
2796275970Scy		exit(1);
2797275970Scy}
2798275970Scy
2799275970Scystatic void
2800275970Scydo_set_linkprop(int argc, char **argv)
2801275970Scy{
2802275970Scy	set_linkprop(argc, argv, B_FALSE);
2803275970Scy}
2804275970Scy
2805275970Scystatic void
2806275970Scydo_reset_linkprop(int argc, char **argv)
2807275970Scy{
2808275970Scy	set_linkprop(argc, argv, B_TRUE);
2809275970Scy}
2810285612Sdelphij
2811285612Sdelphijstatic int
2812285612Sdelphijconvert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
2813285612Sdelphij    dladm_secobj_class_t class)
2814285612Sdelphij{
2815285612Sdelphij	int error = 0;
2816285612Sdelphij
2817285612Sdelphij	if (class != DLADM_SECOBJ_CLASS_WEP)
2818285612Sdelphij		return (ENOENT);
2819285612Sdelphij
2820285612Sdelphij	switch (len) {
2821285612Sdelphij	case 5:			/* ASCII key sizes */
2822285612Sdelphij	case 13:
2823275970Scy		(void) memcpy(obj_val, buf, len);
2824275970Scy		*obj_lenp = len;
2825275970Scy		break;
2826275970Scy	case 10:		/* Hex key sizes, not preceded by 0x */
2827275970Scy	case 26:
2828275970Scy		error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
2829275970Scy		break;
2830275970Scy	case 12:		/* Hex key sizes, preceded by 0x */
2831275970Scy	case 28:
2832275970Scy		if (strncmp(buf, "0x", 2) != 0)
2833275970Scy			return (EINVAL);
2834275970Scy		error = hexascii_to_octet(buf + 2, len - 2, obj_val, obj_lenp);
2835275970Scy		break;
2836275970Scy	default:
2837275970Scy		return (EINVAL);
2838275970Scy	}
2839289997Sglebius	return (error);
2840289997Sglebius}
2841289997Sglebius
2842275970Scy/* ARGSUSED */
2843275970Scystatic void
2844275970Scydefersig(int sig)
2845289997Sglebius{
2846289997Sglebius	signalled = sig;
2847289997Sglebius}
2848275970Scy
2849275970Scystatic int
2850275970Scyget_secobj_from_tty(uint_t try, const char *objname, char *buf)
2851275970Scy{
2852275970Scy	uint_t		len = 0;
2853285612Sdelphij	int		c;
2854298770Sdelphij	struct termios	stored, current;
2855298770Sdelphij	void		(*sigfunc)(int);
2856275970Scy
2857275970Scy	/*
2858275970Scy	 * Turn off echo -- but before we do so, defer SIGINT handling
2859275970Scy	 * so that a ^C doesn't leave the terminal corrupted.
2860275970Scy	 */
2861275970Scy	sigfunc = signal(SIGINT, defersig);
2862275970Scy	(void) fflush(stdin);
2863275970Scy	(void) tcgetattr(0, &stored);
2864275970Scy	current = stored;
2865275970Scy	current.c_lflag &= ~(ICANON|ECHO);
2866275970Scy	current.c_cc[VTIME] = 0;
2867275970Scy	current.c_cc[VMIN] = 1;
2868275970Scy	(void) tcsetattr(0, TCSANOW, &current);
2869275970Scyagain:
2870275970Scy	if (try == 1)
2871275970Scy		(void) printf(gettext("provide value for '%s': "), objname);
2872275970Scy	else
2873275970Scy		(void) printf(gettext("confirm value for '%s': "), objname);
2874275970Scy
2875275970Scy	(void) fflush(stdout);
2876275970Scy	while (signalled == 0) {
2877275970Scy		c = getchar();
2878275970Scy		if (c == '\n' || c == '\r') {
2879275970Scy			if (len != 0)
2880275970Scy				break;
2881275970Scy			(void) putchar('\n');
2882275970Scy			goto again;
2883275970Scy		}
2884275970Scy
2885275970Scy		buf[len++] = c;
2886275970Scy		if (len >= DLADM_SECOBJ_VAL_MAX - 1)
2887275970Scy			break;
2888275970Scy		(void) putchar('*');
2889275970Scy	}
2890275970Scy
2891275970Scy	(void) putchar('\n');
2892275970Scy	(void) fflush(stdin);
2893275970Scy
2894275970Scy	/*
2895275970Scy	 * Restore terminal setting and handle deferred signals.
2896275970Scy	 */
2897275970Scy	(void) tcsetattr(0, TCSANOW, &stored);
2898275970Scy
2899275970Scy	(void) signal(SIGINT, sigfunc);
2900275970Scy	if (signalled != 0)
2901275970Scy		(void) kill(getpid(), signalled);
2902275970Scy
2903275970Scy	return (len);
2904275970Scy}
2905275970Scy
2906275970Scystatic int
2907275970Scyget_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
2908275970Scy    dladm_secobj_class_t class, FILE *filep)
2909275970Scy{
2910275970Scy	int		rval;
2911275970Scy	uint_t		len, len2;
2912275970Scy	char		buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
2913275970Scy
2914275970Scy	if (filep == NULL) {
2915275970Scy		len = get_secobj_from_tty(1, obj_name, buf);
2916275970Scy		rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
2917275970Scy		if (rval == 0) {
2918275970Scy			len2 = get_secobj_from_tty(2, obj_name, buf2);
2919275970Scy			if (len != len2 || memcmp(buf, buf2, len) != 0)
2920275970Scy				rval = ENOTSUP;
2921275970Scy		}
2922275970Scy		return (rval);
2923275970Scy	} else {
2924275970Scy		for (;;) {
2925275970Scy			if (fgets(buf, sizeof (buf), filep) == NULL)
2926275970Scy				break;
2927275970Scy			if (isspace(buf[0]))
2928275970Scy				continue;
2929275970Scy
2930275970Scy			len = strlen(buf);
2931275970Scy			if (buf[len - 1] == '\n') {
2932275970Scy				buf[len - 1] = '\0';
2933275970Scy				len--;
2934275970Scy			}
2935275970Scy			break;
2936275970Scy		}
2937275970Scy		(void) fclose(filep);
2938275970Scy	}
2939275970Scy	return (convert_secobj(buf, len, obj_val, obj_lenp, class));
2940275970Scy}
2941275970Scy
2942275970Scystatic boolean_t
2943275970Scycheck_auth(const char *auth)
2944275970Scy{
2945275970Scy	struct passwd	*pw;
2946275970Scy
2947280849Scy	if ((pw = getpwuid(getuid())) == NULL)
2948275970Scy		return (B_FALSE);
2949294569Sdelphij
2950275970Scy	return (chkauthattr(auth, pw->pw_name) != 0);
2951275970Scy}
2952275970Scy
2953275970Scystatic void
2954275970Scyaudit_secobj(char *auth, char *class, char *obj,
2955275970Scy    boolean_t success, boolean_t create)
2956275970Scy{
2957275970Scy	adt_session_data_t	*ah;
2958275970Scy	adt_event_data_t	*event;
2959275970Scy	au_event_t		flag;
2960275970Scy	char			*errstr;
2961275970Scy
2962275970Scy	if (create) {
2963275970Scy		flag = ADT_dladm_create_secobj;
2964275970Scy		errstr = "ADT_dladm_create_secobj";
2965275970Scy	} else {
2966275970Scy		flag = ADT_dladm_delete_secobj;
2967275970Scy		errstr = "ADT_dladm_delete_secobj";
2968275970Scy	}
2969
2970	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
2971		die("adt_start_session: %s", strerror(errno));
2972
2973	if ((event = adt_alloc_event(ah, flag)) == NULL)
2974		die("adt_alloc_event (%s): %s", errstr, strerror(errno));
2975
2976	/* fill in audit info */
2977	if (create) {
2978		event->adt_dladm_create_secobj.auth_used = auth;
2979		event->adt_dladm_create_secobj.obj_class = class;
2980		event->adt_dladm_create_secobj.obj_name = obj;
2981	} else {
2982		event->adt_dladm_delete_secobj.auth_used = auth;
2983		event->adt_dladm_delete_secobj.obj_class = class;
2984		event->adt_dladm_delete_secobj.obj_name = obj;
2985	}
2986
2987	if (success) {
2988		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
2989			die("adt_put_event (%s, success): %s", errstr,
2990			    strerror(errno));
2991		}
2992	} else {
2993		if (adt_put_event(event, ADT_FAILURE,
2994		    ADT_FAIL_VALUE_AUTH) != 0) {
2995			die("adt_put_event: (%s, failure): %s", errstr,
2996			    strerror(errno));
2997		}
2998	}
2999
3000	adt_free_event(event);
3001	(void) adt_end_session(ah);
3002}
3003
3004#define	MAX_SECOBJS		32
3005#define	MAX_SECOBJ_NAMELEN	32
3006static void
3007do_create_secobj(int argc, char **argv)
3008{
3009	int			option, rval;
3010	FILE			*filep = NULL;
3011	char			*obj_name = NULL;
3012	char			*class_name = NULL;
3013	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
3014	uint_t			obj_len;
3015	boolean_t		success, temp = B_FALSE;
3016	dladm_status_t		status;
3017	dladm_secobj_class_t	class = -1;
3018	uid_t			euid;
3019
3020	opterr = 0;
3021	(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
3022	while ((option = getopt_long(argc, argv, ":f:c:R:t",
3023	    wifi_longopts, NULL)) != -1) {
3024		switch (option) {
3025		case 'f':
3026			euid = geteuid();
3027			(void) seteuid(getuid());
3028			filep = fopen(optarg, "r");
3029			if (filep == NULL) {
3030				die("cannot open %s: %s", optarg,
3031				    strerror(errno));
3032			}
3033			(void) seteuid(euid);
3034			break;
3035		case 'c':
3036			class_name = optarg;
3037			status = dladm_str2secobjclass(optarg, &class);
3038			if (status != DLADM_STATUS_OK) {
3039				die("invalid secure object class '%s', "
3040				    "valid values are: wep", optarg);
3041			}
3042			break;
3043		case 't':
3044			temp = B_TRUE;
3045			break;
3046		case 'R':
3047			status = dladm_set_rootdir(optarg);
3048			if (status != DLADM_STATUS_OK) {
3049				die_dlerr(status, "invalid directory "
3050				    "specified");
3051			}
3052			break;
3053		default:
3054			die_opterr(optopt, option);
3055			break;
3056		}
3057	}
3058
3059	if (optind == (argc - 1))
3060		obj_name = argv[optind];
3061	else if (optind != argc)
3062		usage();
3063
3064	if (class == -1)
3065		die("secure object class required");
3066
3067	if (obj_name == NULL)
3068		die("secure object name required");
3069
3070	success = check_auth(LINK_SEC_AUTH);
3071	audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
3072	if (!success)
3073		die("authorization '%s' is required", LINK_SEC_AUTH);
3074
3075	rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
3076	if (rval != 0) {
3077		switch (rval) {
3078		case ENOENT:
3079			die("invalid secure object class");
3080			break;
3081		case EINVAL:
3082			die("invalid secure object value");
3083			break;
3084		case ENOTSUP:
3085			die("verification failed");
3086			break;
3087		default:
3088			die("invalid secure object: %s", strerror(rval));
3089			break;
3090		}
3091	}
3092
3093	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
3094	    DLADM_OPT_CREATE | DLADM_OPT_TEMP);
3095	if (status != DLADM_STATUS_OK) {
3096		die_dlerr(status, "could not create secure object '%s'",
3097		    obj_name);
3098	}
3099	if (temp)
3100		return;
3101
3102	status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
3103	    DLADM_OPT_PERSIST);
3104	if (status != DLADM_STATUS_OK) {
3105		warn_dlerr(status, "could not persistently create secure "
3106		    "object '%s'", obj_name);
3107	}
3108}
3109
3110static void
3111do_delete_secobj(int argc, char **argv)
3112{
3113	int		i, option;
3114	boolean_t	temp = B_FALSE;
3115	split_t		*sp = NULL;
3116	boolean_t	success;
3117	dladm_status_t	status, pstatus;
3118
3119	opterr = 0;
3120	status = pstatus = DLADM_STATUS_OK;
3121	while ((option = getopt_long(argc, argv, ":R:t",
3122	    wifi_longopts, NULL)) != -1) {
3123		switch (option) {
3124		case 't':
3125			temp = B_TRUE;
3126			break;
3127		case 'R':
3128			status = dladm_set_rootdir(optarg);
3129			if (status != DLADM_STATUS_OK) {
3130				die_dlerr(status, "invalid directory "
3131				    "specified");
3132			}
3133			break;
3134		default:
3135			die_opterr(optopt, option);
3136			break;
3137		}
3138	}
3139
3140	if (optind == (argc - 1)) {
3141		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
3142		if (sp == NULL) {
3143			die("invalid secure object name(s): '%s'",
3144			    argv[optind]);
3145		}
3146	} else if (optind != argc)
3147		usage();
3148
3149	if (sp == NULL || sp->s_nfields < 1)
3150		die("secure object name required");
3151
3152	success = check_auth(LINK_SEC_AUTH);
3153	audit_secobj(LINK_SEC_AUTH, "wep", argv[optind], success, B_FALSE);
3154	if (!success)
3155		die("authorization '%s' is required", LINK_SEC_AUTH);
3156
3157	for (i = 0; i < sp->s_nfields; i++) {
3158		status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_TEMP);
3159		if (!temp) {
3160			pstatus = dladm_unset_secobj(sp->s_fields[i],
3161			    DLADM_OPT_PERSIST);
3162		} else {
3163			pstatus = DLADM_STATUS_OK;
3164		}
3165
3166		if (status != DLADM_STATUS_OK) {
3167			warn_dlerr(status, "could not delete secure object "
3168			    "'%s'", sp->s_fields[i]);
3169		}
3170		if (pstatus != DLADM_STATUS_OK) {
3171			warn_dlerr(pstatus, "could not persistently delete "
3172			    "secure object '%s'", sp->s_fields[i]);
3173		}
3174	}
3175	if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK)
3176		exit(1);
3177}
3178
3179typedef struct show_secobj_state {
3180	boolean_t	ss_persist;
3181	boolean_t	ss_parseable;
3182	boolean_t	ss_debug;
3183	boolean_t	ss_header;
3184} show_secobj_state_t;
3185
3186static void
3187print_secobj_head(show_secobj_state_t *statep)
3188{
3189	(void) printf("%-20s %-20s ", "OBJECT", "CLASS");
3190	if (statep->ss_debug)
3191		(void) printf("%-30s", "VALUE");
3192	(void) putchar('\n');
3193}
3194
3195static boolean_t
3196show_secobj(void *arg, const char *obj_name)
3197{
3198	uint_t			obj_len = DLADM_SECOBJ_VAL_MAX;
3199	uint8_t			obj_val[DLADM_SECOBJ_VAL_MAX];
3200	char			buf[DLADM_STRSIZE];
3201	uint_t			flags = 0;
3202	dladm_secobj_class_t	class;
3203	show_secobj_state_t	*statep = arg;
3204	dladm_status_t		status;
3205
3206	if (statep->ss_persist)
3207		flags |= DLADM_OPT_PERSIST;
3208
3209	status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags);
3210	if (status != DLADM_STATUS_OK)
3211		die_dlerr(status, "cannot get secure object '%s'", obj_name);
3212
3213	if (statep->ss_header) {
3214		statep->ss_header = B_FALSE;
3215		if (!statep->ss_parseable)
3216			print_secobj_head(statep);
3217	}
3218
3219	if (statep->ss_parseable) {
3220		(void) printf("OBJECT=\"%s\" CLASS=\"%s\" ", obj_name,
3221		    dladm_secobjclass2str(class, buf));
3222	} else {
3223		(void) printf("%-20s %-20s ", obj_name,
3224		    dladm_secobjclass2str(class, buf));
3225	}
3226
3227	if (statep->ss_debug) {
3228		char	val[DLADM_SECOBJ_VAL_MAX * 2];
3229		uint_t	len = sizeof (val);
3230
3231		if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0) {
3232			if (statep->ss_parseable)
3233				(void) printf("VALUE=\"0x%s\"", val);
3234			else
3235				(void) printf("0x%-30s", val);
3236		}
3237	}
3238	(void) putchar('\n');
3239	return (B_TRUE);
3240}
3241
3242static void
3243do_show_secobj(int argc, char **argv)
3244{
3245	int			option;
3246	show_secobj_state_t	state;
3247	dladm_status_t		status;
3248	uint_t			i;
3249	split_t			*sp;
3250	uint_t			flags;
3251
3252	opterr = 0;
3253	state.ss_persist = B_FALSE;
3254	state.ss_parseable = B_FALSE;
3255	state.ss_debug = B_FALSE;
3256	state.ss_header = B_TRUE;
3257	while ((option = getopt_long(argc, argv, ":pPd",
3258	    wifi_longopts, NULL)) != -1) {
3259		switch (option) {
3260		case 'p':
3261			state.ss_parseable = B_TRUE;
3262			break;
3263		case 'P':
3264			state.ss_persist = B_TRUE;
3265			break;
3266		case 'd':
3267			if (getuid() != 0)
3268				die("insufficient privileges");
3269			state.ss_debug = B_TRUE;
3270			break;
3271		default:
3272			die_opterr(optopt, option);
3273			break;
3274		}
3275	}
3276
3277	if (optind == (argc - 1)) {
3278		sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
3279		if (sp == NULL) {
3280			die("invalid secure object name(s): '%s'",
3281			    argv[optind]);
3282		}
3283		for (i = 0; i < sp->s_nfields; i++) {
3284			if (!show_secobj(&state, sp->s_fields[i]))
3285				break;
3286		}
3287		splitfree(sp);
3288		return;
3289	} else if (optind != argc)
3290		usage();
3291
3292	flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
3293	status = dladm_walk_secobj(&state, show_secobj, flags);
3294	if (status != DLADM_STATUS_OK)
3295		die_dlerr(status, "show-secobj");
3296}
3297
3298/* ARGSUSED */
3299static void
3300do_init_linkprop(int argc, char **argv)
3301{
3302	dladm_status_t status;
3303
3304	status = dladm_init_linkprop();
3305	if (status != DLADM_STATUS_OK)
3306		die_dlerr(status, "link property initialization failed");
3307}
3308
3309/* ARGSUSED */
3310static void
3311do_init_secobj(int argc, char **argv)
3312{
3313	dladm_status_t status;
3314
3315	status = dladm_init_secobj();
3316	if (status != DLADM_STATUS_OK)
3317		die_dlerr(status, "secure object initialization failed");
3318}
3319
3320static boolean_t
3321str2int(const char *str, int *valp)
3322{
3323	int	val;
3324	char	*endp = NULL;
3325
3326	errno = 0;
3327	val = strtol(str, &endp, 10);
3328	if (errno != 0 || *endp != '\0')
3329		return (B_FALSE);
3330
3331	*valp = val;
3332	return (B_TRUE);
3333}
3334
3335/* PRINTFLIKE1 */
3336static void
3337warn(const char *format, ...)
3338{
3339	va_list alist;
3340
3341	format = gettext(format);
3342	(void) fprintf(stderr, "%s: warning: ", progname);
3343
3344	va_start(alist, format);
3345	(void) vfprintf(stderr, format, alist);
3346	va_end(alist);
3347
3348	(void) putchar('\n');
3349}
3350
3351/* PRINTFLIKE2 */
3352static void
3353warn_wlerr(wladm_status_t err, const char *format, ...)
3354{
3355	va_list alist;
3356	char	errmsg[WLADM_STRSIZE];
3357
3358	format = gettext(format);
3359	(void) fprintf(stderr, gettext("%s: warning: "), progname);
3360
3361	va_start(alist, format);
3362	(void) vfprintf(stderr, format, alist);
3363	va_end(alist);
3364	(void) fprintf(stderr, ": %s\n", wladm_status2str(err, errmsg));
3365}
3366
3367/* PRINTFLIKE2 */
3368static void
3369warn_dlerr(dladm_status_t err, const char *format, ...)
3370{
3371	va_list alist;
3372	char	errmsg[DLADM_STRSIZE];
3373
3374	format = gettext(format);
3375	(void) fprintf(stderr, gettext("%s: warning: "), progname);
3376
3377	va_start(alist, format);
3378	(void) vfprintf(stderr, format, alist);
3379	va_end(alist);
3380	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
3381}
3382
3383/* PRINTFLIKE2 */
3384static void
3385die_laerr(laadm_diag_t diag, const char *format, ...)
3386{
3387	va_list alist;
3388	char	*errstr = strerror(errno);
3389
3390	format = gettext(format);
3391	(void) fprintf(stderr, "%s: ", progname);
3392
3393	va_start(alist, format);
3394	(void) vfprintf(stderr, format, alist);
3395	va_end(alist);
3396
3397	if (diag == 0)
3398		(void) fprintf(stderr, ": %s\n", errstr);
3399	else
3400		(void) fprintf(stderr, ": %s (%s)\n", errstr, laadm_diag(diag));
3401
3402	exit(EXIT_FAILURE);
3403}
3404
3405/* PRINTFLIKE2 */
3406static void
3407die_wlerr(wladm_status_t err, const char *format, ...)
3408{
3409	va_list alist;
3410	char	errmsg[WLADM_STRSIZE];
3411
3412	format = gettext(format);
3413	(void) fprintf(stderr, "%s: ", progname);
3414
3415	va_start(alist, format);
3416	(void) vfprintf(stderr, format, alist);
3417	va_end(alist);
3418	(void) fprintf(stderr, ": %s\n", wladm_status2str(err, errmsg));
3419
3420	exit(EXIT_FAILURE);
3421}
3422
3423/* PRINTFLIKE2 */
3424static void
3425die_dlerr(dladm_status_t err, const char *format, ...)
3426{
3427	va_list alist;
3428	char	errmsg[DLADM_STRSIZE];
3429
3430	format = gettext(format);
3431	(void) fprintf(stderr, "%s: ", progname);
3432
3433	va_start(alist, format);
3434	(void) vfprintf(stderr, format, alist);
3435	va_end(alist);
3436	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
3437
3438	exit(EXIT_FAILURE);
3439}
3440
3441/* PRINTFLIKE1 */
3442static void
3443die(const char *format, ...)
3444{
3445	va_list alist;
3446
3447	format = gettext(format);
3448	(void) fprintf(stderr, "%s: ", progname);
3449
3450	va_start(alist, format);
3451	(void) vfprintf(stderr, format, alist);
3452	va_end(alist);
3453
3454	(void) putchar('\n');
3455	exit(EXIT_FAILURE);
3456}
3457
3458static void
3459die_optdup(int opt)
3460{
3461	die("the option -%c cannot be specified more than once", opt);
3462}
3463
3464static void
3465die_opterr(int opt, int opterr)
3466{
3467	switch (opterr) {
3468	case ':':
3469		die("option '-%c' requires a value", opt);
3470		break;
3471	case '?':
3472	default:
3473		die("unrecognized option '-%c'", opt);
3474		break;
3475	}
3476}
3477