13871Syz147064 /*
23871Syz147064  * CDDL HEADER START
33871Syz147064  *
43871Syz147064  * The contents of this file are subject to the terms of the
53871Syz147064  * Common Development and Distribution License (the "License").
63871Syz147064  * You may not use this file except in compliance with the License.
73871Syz147064  *
83871Syz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93871Syz147064  * or http://www.opensolaris.org/os/licensing.
103871Syz147064  * See the License for the specific language governing permissions
113871Syz147064  * and limitations under the License.
123871Syz147064  *
133871Syz147064  * When distributing Covered Code, include this CDDL HEADER in each
143871Syz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153871Syz147064  * If applicable, add the following below this CDDL HEADER, with the
163871Syz147064  * fields enclosed by brackets "[]" replaced with your own identifying
173871Syz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
183871Syz147064  *
193871Syz147064  * CDDL HEADER END
203871Syz147064  */
213871Syz147064 /*
223871Syz147064  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
233871Syz147064  * Use is subject to license terms.
243871Syz147064  */
253871Syz147064 
263871Syz147064 #pragma ident	"%Z%%M%	%I%	%E% SMI"
273871Syz147064 
283871Syz147064 #include <libintl.h>
293871Syz147064 #include <stdio.h>
303871Syz147064 #include <stdlib.h>
313871Syz147064 #include <stddef.h>
323871Syz147064 #include <unistd.h>
333871Syz147064 #include <fcntl.h>
343871Syz147064 #include <string.h>
353871Syz147064 #include <stropts.h>
363871Syz147064 #include <libdevinfo.h>
373871Syz147064 #include <net/if.h>
383871Syz147064 #include <net/if_dl.h>
393871Syz147064 #include <net/if_types.h>
40*4126Szf162725 #include <libscf.h>
413871Syz147064 #include <libdlwlan.h>
423871Syz147064 #include <libdlwlan_impl.h>
43*4126Szf162725 #include <net/wpa.h>
443871Syz147064 
453871Syz147064 typedef struct val_desc {
463871Syz147064 	char		*vd_name;
473871Syz147064 	uint_t		vd_val;
483871Syz147064 } val_desc_t;
493871Syz147064 
503871Syz147064 struct prop_desc;
513871Syz147064 
523871Syz147064 typedef dladm_status_t	wl_pd_getf_t(int, wldp_t *, char **, uint_t *);
533871Syz147064 typedef dladm_status_t	wl_pd_setf_t(int, wldp_t *, val_desc_t *, uint_t);
543871Syz147064 typedef dladm_status_t	wl_pd_checkf_t(int, wldp_t *, struct prop_desc *,
553871Syz147064 			    char **, uint_t, val_desc_t **);
563871Syz147064 typedef struct prop_desc {
573871Syz147064 	char		*pd_name;
583871Syz147064 	val_desc_t	pd_defval;
593871Syz147064 	val_desc_t	*pd_modval;
603871Syz147064 	uint_t		pd_nmodval;
613871Syz147064 	wl_pd_setf_t	*pd_set;
623871Syz147064 	wl_pd_getf_t	*pd_getmod;
633871Syz147064 	wl_pd_getf_t	*pd_get;
643871Syz147064 	wl_pd_checkf_t	*pd_check;
653871Syz147064 } prop_desc_t;
663871Syz147064 
67*4126Szf162725 static int	wpa_instance_create(const char *, void *);
68*4126Szf162725 static int	wpa_instance_delete(const char *);
69*4126Szf162725 
703871Syz147064 static int 	do_get_bsstype(int, wldp_t *);
713871Syz147064 static int 	do_get_essid(int, wldp_t *);
723871Syz147064 static int 	do_get_bssid(int, wldp_t *);
733871Syz147064 static int 	do_get_signal(int, wldp_t *);
743871Syz147064 static int 	do_get_encryption(int, wldp_t *);
753871Syz147064 static int 	do_get_authmode(int, wldp_t *);
763871Syz147064 static int 	do_get_linkstatus(int, wldp_t *);
773871Syz147064 static int	do_get_esslist(int, wldp_t *);
783871Syz147064 static int 	do_get_rate(int, wldp_t *);
793871Syz147064 static int	do_get_phyconf(int, wldp_t *);
803871Syz147064 static int	do_get_powermode(int, wldp_t *);
813871Syz147064 static int	do_get_radio(int, wldp_t *);
823871Syz147064 static int	do_get_mode(int, wldp_t *);
83*4126Szf162725 static int	do_get_capability(int, wldp_t *);
84*4126Szf162725 static int	do_get_wpamode(int, wldp_t *);
853871Syz147064 
863871Syz147064 static int	do_set_bsstype(int, wldp_t *, dladm_wlan_bsstype_t *);
873871Syz147064 static int	do_set_authmode(int, wldp_t *, dladm_wlan_auth_t *);
883871Syz147064 static int	do_set_encryption(int, wldp_t *, dladm_wlan_secmode_t *);
893871Syz147064 static int	do_set_essid(int, wldp_t *, dladm_wlan_essid_t *);
903871Syz147064 static int	do_set_createibss(int, wldp_t *, boolean_t *);
91*4126Szf162725 static int	do_set_key(int, wldp_t *, dladm_wlan_key_t *, uint_t);
923871Syz147064 static int	do_set_rate(int, wldp_t *, dladm_wlan_rates_t *);
933871Syz147064 static int	do_set_powermode(int, wldp_t *, dladm_wlan_powermode_t *);
943871Syz147064 static int	do_set_radio(int, wldp_t *, dladm_wlan_radio_t *);
953871Syz147064 static int	do_set_channel(int, wldp_t *, dladm_wlan_channel_t *);
963871Syz147064 
973871Syz147064 static int	open_link(const char *);
983871Syz147064 static int	do_scan(int, wldp_t *);
99*4126Szf162725 static int	do_disconnect(const char *, int, wldp_t *);
1003871Syz147064 static boolean_t find_val_by_name(const char *, val_desc_t *, uint_t, uint_t *);
1013871Syz147064 static boolean_t find_name_by_val(uint_t, val_desc_t *, uint_t, char **);
1023871Syz147064 static void	generate_essid(dladm_wlan_essid_t *);
1033871Syz147064 
1043871Syz147064 static dladm_status_t	dladm_wlan_wlresult2status(wldp_t *);
1053871Syz147064 
1063871Syz147064 static wl_pd_getf_t	do_get_rate_mod, do_get_rate_prop, do_get_channel_prop,
1073871Syz147064 			do_get_powermode_prop, do_get_radio_prop;
1083871Syz147064 static wl_pd_setf_t 	do_set_rate_prop, do_set_powermode_prop,
1093871Syz147064 			do_set_radio_prop;
1103871Syz147064 static wl_pd_checkf_t	do_check_prop, do_check_rate;
1113871Syz147064 
1123871Syz147064 static val_desc_t	linkstatus_vals[] = {
1133871Syz147064 	{ "disconnected", 	DLADM_WLAN_LINKSTATUS_DISCONNECTED	},
1143871Syz147064 	{ "connected",		DLADM_WLAN_LINKSTATUS_CONNECTED	}
1153871Syz147064 };
1163871Syz147064 
1173871Syz147064 static val_desc_t 	secmode_vals[] = {
1183871Syz147064 	{ "none",	DLADM_WLAN_SECMODE_NONE		},
119*4126Szf162725 	{ "wep",	DLADM_WLAN_SECMODE_WEP		},
120*4126Szf162725 	{ "wpa",	DLADM_WLAN_SECMODE_WPA		}
1213871Syz147064 };
1223871Syz147064 
1233871Syz147064 static val_desc_t 	strength_vals[] = {
1243871Syz147064 	{ "very weak",	DLADM_WLAN_STRENGTH_VERY_WEAK 	},
1253871Syz147064 	{ "weak",	DLADM_WLAN_STRENGTH_WEAK		},
1263871Syz147064 	{ "good", 	DLADM_WLAN_STRENGTH_GOOD		},
1273871Syz147064 	{ "very good",	DLADM_WLAN_STRENGTH_VERY_GOOD	},
1283871Syz147064 	{ "excellent",	DLADM_WLAN_STRENGTH_EXCELLENT	}
1293871Syz147064 };
1303871Syz147064 
1313871Syz147064 static val_desc_t	mode_vals[] = {
1323871Syz147064 	{ "a",		DLADM_WLAN_MODE_80211A		},
1333871Syz147064 	{ "b",		DLADM_WLAN_MODE_80211B		},
1343871Syz147064 	{ "g",		DLADM_WLAN_MODE_80211G		},
1353871Syz147064 };
1363871Syz147064 
1373871Syz147064 static val_desc_t	auth_vals[] = {
1383871Syz147064 	{ "open",	DLADM_WLAN_AUTH_OPEN			},
1393871Syz147064 	{ "shared",	DLADM_WLAN_AUTH_SHARED		}
1403871Syz147064 };
1413871Syz147064 
1423871Syz147064 static val_desc_t	bsstype_vals[] = {
1433871Syz147064 	{ "bss",	DLADM_WLAN_BSSTYPE_BSS		},
1443871Syz147064 	{ "ibss",	DLADM_WLAN_BSSTYPE_IBSS		},
1453871Syz147064 	{ "any",	DLADM_WLAN_BSSTYPE_ANY		}
1463871Syz147064 };
1473871Syz147064 
1483871Syz147064 static val_desc_t	radio_vals[] = {
1493871Syz147064 	{ "on",		DLADM_WLAN_RADIO_ON			},
1503871Syz147064 	{ "off",	DLADM_WLAN_RADIO_OFF			}
1513871Syz147064 };
1523871Syz147064 
1533871Syz147064 static val_desc_t	powermode_vals[] = {
1543871Syz147064 	{ "off",	DLADM_WLAN_PM_OFF			},
1553871Syz147064 	{ "fast",	DLADM_WLAN_PM_FAST			},
1563871Syz147064 	{ "max",	DLADM_WLAN_PM_MAX			}
1573871Syz147064 };
1583871Syz147064 
1593871Syz147064 #define	VALCNT(vals)	(sizeof ((vals)) / sizeof (val_desc_t))
1603871Syz147064 static	prop_desc_t	prop_table[] = {
1613871Syz147064 
1623871Syz147064 	{ "channel",	{ NULL, 0 }, NULL, 0,
1633871Syz147064 	    NULL, NULL, do_get_channel_prop, do_check_prop},
1643871Syz147064 
1653871Syz147064 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF }, powermode_vals,
1663871Syz147064 	    VALCNT(powermode_vals),
1673871Syz147064 	    do_set_powermode_prop, NULL,
1683871Syz147064 	    do_get_powermode_prop, do_check_prop},
1693871Syz147064 
1703871Syz147064 	{ "radio", 	{ "on", DLADM_WLAN_RADIO_ON }, radio_vals,
1713871Syz147064 	    VALCNT(radio_vals),
1723871Syz147064 	    do_set_radio_prop, NULL,
1733871Syz147064 	    do_get_radio_prop, do_check_prop},
1743871Syz147064 
1753871Syz147064 	{ "speed",	{ "", 0 }, NULL, 0,
1763871Syz147064 	    do_set_rate_prop, do_get_rate_mod,
1773871Syz147064 	    do_get_rate_prop, do_check_rate}
1783871Syz147064 };
1793871Syz147064 /*
1803871Syz147064  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
1813871Syz147064  * rates to be retrieved. However, we cannot increase it at this
1823871Syz147064  * time because it will break binary comatibility with unbundled
1833871Syz147064  * WiFi drivers and utilities. So for now we define an additional
1843871Syz147064  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
1853871Syz147064  */
1863871Syz147064 #define	MAX_SUPPORT_RATES	64
1873871Syz147064 #define	DLADM_WLAN_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
1883871Syz147064 #define	IS_CONNECTED(gbuf) \
1893871Syz147064 	((*(wl_linkstatus_t *)((gbuf)->wldp_buf) == WL_CONNECTED))
1903871Syz147064 
1913871Syz147064 static dladm_status_t
1923871Syz147064 dladm_wlan_wlresult2status(wldp_t *gbuf)
1933871Syz147064 {
1943871Syz147064 	switch (gbuf->wldp_result) {
1953871Syz147064 	case WL_SUCCESS:
1963871Syz147064 		return (DLADM_STATUS_OK);
1973871Syz147064 
1983871Syz147064 	case WL_NOTSUPPORTED:
1993871Syz147064 	case WL_LACK_FEATURE:
2003871Syz147064 		return (DLADM_STATUS_NOTSUP);
2013871Syz147064 
2023871Syz147064 	case WL_READONLY:
2033871Syz147064 		return (DLADM_STATUS_PROPRDONLY);
2043871Syz147064 
2053871Syz147064 	default:
2063871Syz147064 		break;
2073871Syz147064 	}
2083871Syz147064 
2093871Syz147064 	return (DLADM_STATUS_FAILED);
2103871Syz147064 }
2113871Syz147064 
2123871Syz147064 static int
2133871Syz147064 open_link(const char *link)
2143871Syz147064 {
2153871Syz147064 	char	linkname[MAXPATHLEN];
2163871Syz147064 	wldp_t	*gbuf;
2173871Syz147064 	int	fd;
2183871Syz147064 
2193871Syz147064 	if (link == NULL)
2203871Syz147064 		return (-1);
2213871Syz147064 
2223871Syz147064 	(void) snprintf(linkname, MAXPATHLEN, "/dev/%s", link);
2233871Syz147064 	if ((fd = open(linkname, O_RDWR)) < 0)
2243871Syz147064 		return (-1);
2253871Syz147064 
2263871Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
2273871Syz147064 		(void) close(fd);
2283871Syz147064 		return (-1);
2293871Syz147064 	}
2303871Syz147064 
2313871Syz147064 	/*
2323871Syz147064 	 * Check to see if the link is wireless.
2333871Syz147064 	 */
2343871Syz147064 	if (do_get_bsstype(fd, gbuf) < 0) {
2353871Syz147064 		free(gbuf);
2363871Syz147064 		(void) close(fd);
2373871Syz147064 		return (-1);
2383871Syz147064 	}
2393871Syz147064 
2403871Syz147064 	free(gbuf);
2413871Syz147064 	return (fd);
2423871Syz147064 }
2433871Syz147064 
2443871Syz147064 static dladm_wlan_mode_t
2453871Syz147064 do_convert_mode(wl_phy_conf_t *phyp)
2463871Syz147064 {
2473871Syz147064 	switch (phyp->wl_phy_fhss_conf.wl_fhss_subtype) {
2483871Syz147064 	case WL_ERP:
2493871Syz147064 		return (DLADM_WLAN_MODE_80211G);
2503871Syz147064 	case WL_OFDM:
2513871Syz147064 		return (DLADM_WLAN_MODE_80211A);
2523871Syz147064 	case WL_DSSS:
2533871Syz147064 	case WL_FHSS:
2543871Syz147064 		return (DLADM_WLAN_MODE_80211B);
2553871Syz147064 	default:
2563871Syz147064 		break;
2573871Syz147064 	}
2583871Syz147064 
2593871Syz147064 	return (DLADM_WLAN_MODE_NONE);
2603871Syz147064 }
2613871Syz147064 
2623871Syz147064 static boolean_t
2633871Syz147064 do_convert_chan(wl_phy_conf_t *phyp, uint32_t *channelp)
2643871Syz147064 {
2653871Syz147064 	wl_fhss_t *wlfp = &phyp->wl_phy_fhss_conf;
2663871Syz147064 	wl_ofdm_t *wlop = &phyp->wl_phy_ofdm_conf;
2673871Syz147064 
2683871Syz147064 	switch (wlfp->wl_fhss_subtype) {
2693871Syz147064 	case WL_FHSS:
2703871Syz147064 	case WL_DSSS:
2713871Syz147064 	case WL_IRBASE:
2723871Syz147064 	case WL_HRDS:
2733871Syz147064 	case WL_ERP:
2743871Syz147064 		*channelp = wlfp->wl_fhss_channel;
2753871Syz147064 		break;
2763871Syz147064 	case WL_OFDM:
2773871Syz147064 		*channelp = DLADM_WLAN_OFDM2CHAN(wlop->wl_ofdm_frequency);
2783871Syz147064 		break;
2793871Syz147064 	default:
2803871Syz147064 		return (B_FALSE);
2813871Syz147064 	}
2823871Syz147064 	return (B_TRUE);
2833871Syz147064 }
2843871Syz147064 
2853871Syz147064 #define	IEEE80211_RATE	0x7f
2863871Syz147064 static void
2873871Syz147064 fill_wlan_attr(wl_ess_conf_t *wlp, dladm_wlan_attr_t *attrp)
2883871Syz147064 {
2893871Syz147064 	int		i;
2903871Syz147064 
2913871Syz147064 	(void) memset(attrp, 0, sizeof (*attrp));
2923871Syz147064 
2933871Syz147064 	(void) snprintf(attrp->wa_essid.we_bytes, DLADM_WLAN_MAX_ESSID_LEN,
2943871Syz147064 	    "%s", wlp->wl_ess_conf_essid.wl_essid_essid);
2953871Syz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_ESSID;
2963871Syz147064 
2973871Syz147064 	(void) memcpy(attrp->wa_bssid.wb_bytes, wlp->wl_ess_conf_bssid,
2983871Syz147064 	    DLADM_WLAN_BSSID_LEN);
2993871Syz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_BSSID;
3003871Syz147064 
3013871Syz147064 	attrp->wa_secmode = (wlp->wl_ess_conf_wepenabled ==
3023871Syz147064 	    WL_ENC_WEP ? DLADM_WLAN_SECMODE_WEP : DLADM_WLAN_SECMODE_NONE);
303*4126Szf162725 	if (wlp->wl_ess_conf_reserved[0] > 0)
304*4126Szf162725 		attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
3053871Syz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_SECMODE;
3063871Syz147064 
3073871Syz147064 	attrp->wa_bsstype = (wlp->wl_ess_conf_bsstype == WL_BSS_BSS ?
3083871Syz147064 	    DLADM_WLAN_BSSTYPE_BSS : DLADM_WLAN_BSSTYPE_IBSS);
3093871Syz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
3103871Syz147064 
3113871Syz147064 	attrp->wa_auth = (wlp->wl_ess_conf_authmode == 0 ?
3123871Syz147064 	    DLADM_WLAN_AUTH_OPEN : DLADM_WLAN_AUTH_SHARED);
3133871Syz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_AUTH;
3143871Syz147064 
3153871Syz147064 	attrp->wa_strength = DLADM_WLAN_SIGNAL2STRENGTH(wlp->wl_ess_conf_sl);
3163871Syz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_STRENGTH;
3173871Syz147064 
3183871Syz147064 	attrp->wa_mode = do_convert_mode((wl_phy_conf_t *)&wlp->wl_phy_conf);
3193871Syz147064 	attrp->wa_valid |= DLADM_WLAN_ATTR_MODE;
3203871Syz147064 
3213871Syz147064 	for (i = 0; i < MAX_SCAN_SUPPORT_RATES; i++) {
3223871Syz147064 		wlp->wl_supported_rates[i] &= IEEE80211_RATE;
3233871Syz147064 		if (wlp->wl_supported_rates[i] > attrp->wa_speed)
3243871Syz147064 			attrp->wa_speed = wlp->wl_supported_rates[i];
3253871Syz147064 	}
3263871Syz147064 	if (attrp->wa_speed > 0)
3273871Syz147064 		attrp->wa_valid |= DLADM_WLAN_ATTR_SPEED;
3283871Syz147064 
3293871Syz147064 	if (do_convert_chan((wl_phy_conf_t *)&wlp->wl_phy_conf,
3303871Syz147064 	    &attrp->wa_channel))
3313871Syz147064 		attrp->wa_valid |= DLADM_WLAN_ATTR_CHANNEL;
3323871Syz147064 }
3333871Syz147064 
3343871Syz147064 dladm_status_t
3353871Syz147064 dladm_wlan_scan(const char *link, void *arg,
3363871Syz147064     boolean_t (*func)(void *, dladm_wlan_attr_t *))
3373871Syz147064 {
3383871Syz147064 	int			fd, i;
3393871Syz147064 	uint32_t		count;
3403871Syz147064 	wl_ess_conf_t		*wlp;
3413871Syz147064 	wldp_t 			*gbuf;
3423871Syz147064 	dladm_wlan_attr_t	wlattr;
3433871Syz147064 	dladm_status_t		status;
3443871Syz147064 	boolean_t		connected;
3453871Syz147064 
3463871Syz147064 	if ((fd = open_link(link)) < 0)
3473871Syz147064 		return (DLADM_STATUS_LINKINVAL);
3483871Syz147064 
3493871Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
3503871Syz147064 		status = DLADM_STATUS_NOMEM;
3513871Syz147064 		goto done;
3523871Syz147064 	}
3533871Syz147064 
3543871Syz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
3553871Syz147064 		status = DLADM_STATUS_FAILED;
3563871Syz147064 		goto done;
3573871Syz147064 	}
3583871Syz147064 	connected = IS_CONNECTED(gbuf);
3593871Syz147064 
3603871Syz147064 	if (do_scan(fd, gbuf) < 0) {
3613871Syz147064 		status = DLADM_STATUS_FAILED;
3623871Syz147064 		goto done;
3633871Syz147064 	}
3643871Syz147064 
365*4126Szf162725 	if (func == NULL) {
366*4126Szf162725 		status = DLADM_STATUS_OK;
367*4126Szf162725 		goto done;
368*4126Szf162725 	}
369*4126Szf162725 
3703871Syz147064 	if (do_get_esslist(fd, gbuf) < 0) {
3713871Syz147064 		status = DLADM_STATUS_FAILED;
3723871Syz147064 		goto done;
3733871Syz147064 	}
3743871Syz147064 
3753871Syz147064 	wlp = ((wl_ess_list_t *)gbuf->wldp_buf)->wl_ess_list_ess;
3763871Syz147064 	count = ((wl_ess_list_t *)(gbuf->wldp_buf))->wl_ess_list_num;
3773871Syz147064 
3783871Syz147064 	for (i = 0; i < count; i++, wlp++) {
3793871Syz147064 		fill_wlan_attr(wlp, &wlattr);
3803871Syz147064 		if (!func(arg, &wlattr))
3813871Syz147064 			break;
3823871Syz147064 	}
3833871Syz147064 
3843871Syz147064 	if (!connected) {
3853871Syz147064 		if (do_get_linkstatus(fd, gbuf) < 0) {
3863871Syz147064 			status = DLADM_STATUS_FAILED;
3873871Syz147064 			goto done;
3883871Syz147064 		}
3893871Syz147064 		if (IS_CONNECTED(gbuf))
390*4126Szf162725 			(void) do_disconnect(link, fd, gbuf);
3913871Syz147064 	}
3923871Syz147064 
3933871Syz147064 	status = DLADM_STATUS_OK;
3943871Syz147064 done:
3953871Syz147064 	free(gbuf);
3963871Syz147064 	(void) close(fd);
3973871Syz147064 	return (status);
3983871Syz147064 }
3993871Syz147064 
4003871Syz147064 /*
4013871Syz147064  * Structures used in building the list of eligible WLANs to connect to.
4023871Syz147064  * Specifically, `connect_state' has the WLAN attributes that must be matched
4033871Syz147064  * (in `cs_attr') and a growing list of WLANs that matched those attributes
4043871Syz147064  * chained through `cs_list'.  Each element in the list is of type `attr_node'
4053871Syz147064  * and has the matching WLAN's attributes and a pointer to the next element.
4063871Syz147064  * For convenience, `cs_count' tracks the number of elements in the list.
4073871Syz147064  */
4083871Syz147064 typedef struct attr_node {
4093871Syz147064 	dladm_wlan_attr_t	an_attr;
4103871Syz147064 	struct attr_node	*an_next;
4113871Syz147064 } attr_node_t;
4123871Syz147064 
4133871Syz147064 typedef struct connect_state {
4143871Syz147064 	dladm_wlan_attr_t	*cs_attr;
4153871Syz147064 	uint_t			cs_count;
4163871Syz147064 	attr_node_t		*cs_list;
4173871Syz147064 } connect_state_t;
4183871Syz147064 
4193871Syz147064 /*
4203871Syz147064  * Compare two sets of WLAN attributes.  For now, we only consider strength
4213871Syz147064  * and speed (in that order), which matches the documented default policy for
4223871Syz147064  * dladm_wlan_connect().
4233871Syz147064  */
4243871Syz147064 static int
4253871Syz147064 attr_compare(const void *p1, const void *p2)
4263871Syz147064 {
4273871Syz147064 	dladm_wlan_attr_t *attrp1, *attrp2;
4283871Syz147064 
4293871Syz147064 	attrp1 = (*(dladm_wlan_attr_t **)p1);
4303871Syz147064 	attrp2 = (*(dladm_wlan_attr_t **)p2);
4313871Syz147064 
4323871Syz147064 	if (attrp1->wa_strength < attrp2->wa_strength)
4333871Syz147064 		return (1);
4343871Syz147064 
4353871Syz147064 	if (attrp1->wa_strength > attrp2->wa_strength)
4363871Syz147064 		return (-1);
4373871Syz147064 
4383871Syz147064 	return (attrp2->wa_speed - attrp1->wa_speed);
4393871Syz147064 }
4403871Syz147064 
4413871Syz147064 /*
4423871Syz147064  * Callback function used by dladm_wlan_connect() to filter out unwanted
4433871Syz147064  * WLANs when scanning for available WLANs.  Always returns B_TRUE to
4443871Syz147064  * continue the scan.
4453871Syz147064  */
4463871Syz147064 static boolean_t
4473871Syz147064 connect_cb(void *arg, dladm_wlan_attr_t *attrp)
4483871Syz147064 {
4493871Syz147064 	attr_node_t		*nodep;
4503871Syz147064 	dladm_wlan_attr_t	*fattrp;
4513871Syz147064 	connect_state_t		*statep = (connect_state_t *)arg;
4523871Syz147064 
4533871Syz147064 	fattrp = statep->cs_attr;
4543871Syz147064 	if (fattrp == NULL)
4553871Syz147064 		goto append;
4563871Syz147064 
4573871Syz147064 	if ((fattrp->wa_valid & attrp->wa_valid) != fattrp->wa_valid)
4583871Syz147064 		return (B_TRUE);
4593871Syz147064 
4603871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_ESSID) != 0 &&
4613871Syz147064 	    strncmp(fattrp->wa_essid.we_bytes, attrp->wa_essid.we_bytes,
4623871Syz147064 	    DLADM_WLAN_MAX_ESSID_LEN) != 0)
4633871Syz147064 		return (B_TRUE);
4643871Syz147064 
4653871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
4663871Syz147064 	    fattrp->wa_secmode != attrp->wa_secmode)
4673871Syz147064 		return (B_TRUE);
4683871Syz147064 
4693871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_MODE) != 0 &&
4703871Syz147064 	    fattrp->wa_mode != attrp->wa_mode)
4713871Syz147064 		return (B_TRUE);
4723871Syz147064 
4733871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_STRENGTH) != 0 &&
4743871Syz147064 	    fattrp->wa_strength != attrp->wa_strength)
4753871Syz147064 		return (B_TRUE);
4763871Syz147064 
4773871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_SPEED) != 0 &&
4783871Syz147064 	    fattrp->wa_speed != attrp->wa_speed)
4793871Syz147064 		return (B_TRUE);
4803871Syz147064 
4813871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_AUTH) != 0) {
4823871Syz147064 		attrp->wa_auth = fattrp->wa_auth;
4833871Syz147064 		attrp->wa_valid |= DLADM_WLAN_ATTR_AUTH;
4843871Syz147064 	}
4853871Syz147064 
4863871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_BSSTYPE) != 0 &&
4873871Syz147064 	    fattrp->wa_bsstype != attrp->wa_bsstype)
4883871Syz147064 		return (B_TRUE);
4893871Syz147064 
4903871Syz147064 	if ((fattrp->wa_valid & DLADM_WLAN_ATTR_BSSID) != 0 &&
4913871Syz147064 	    memcmp(fattrp->wa_bssid.wb_bytes, attrp->wa_bssid.wb_bytes,
4923871Syz147064 	    DLADM_WLAN_BSSID_LEN) != 0)
4933871Syz147064 		return (B_TRUE);
4943871Syz147064 append:
4953871Syz147064 	nodep = malloc(sizeof (attr_node_t));
4963871Syz147064 	if (nodep == NULL)
4973871Syz147064 		return (B_TRUE);
4983871Syz147064 
4993871Syz147064 	(void) memcpy(&nodep->an_attr, attrp, sizeof (dladm_wlan_attr_t));
5003871Syz147064 	nodep->an_next = statep->cs_list;
5013871Syz147064 	statep->cs_list = nodep;
5023871Syz147064 	statep->cs_count++;
5033871Syz147064 
5043871Syz147064 	return (B_TRUE);
5053871Syz147064 }
5063871Syz147064 
507*4126Szf162725 #define	IEEE80211_C_WPA		0x01800000
508*4126Szf162725 
5093871Syz147064 static dladm_status_t
510*4126Szf162725 do_connect(const char *link, int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
5113871Syz147064     boolean_t create_ibss, void *keys, uint_t key_count, int timeout)
5123871Syz147064 {
5133871Syz147064 	dladm_wlan_secmode_t		secmode;
5143871Syz147064 	dladm_wlan_auth_t		authmode;
5153871Syz147064 	dladm_wlan_bsstype_t		bsstype;
5163871Syz147064 	dladm_wlan_essid_t		essid;
517*4126Szf162725 	boolean_t			essid_valid = B_FALSE;
5183871Syz147064 	dladm_wlan_channel_t		channel;
519*4126Szf162725 	hrtime_t			start;
520*4126Szf162725 	wl_capability_t			*caps;
5213871Syz147064 
5223871Syz147064 	if ((attrp->wa_valid & DLADM_WLAN_ATTR_CHANNEL) != 0) {
5233871Syz147064 		channel = attrp->wa_channel;
5243871Syz147064 		if (do_set_channel(fd, gbuf, &channel) < 0)
5253871Syz147064 			goto fail;
5263871Syz147064 	}
5273871Syz147064 
5283871Syz147064 	secmode = ((attrp->wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) ?
5293871Syz147064 	    attrp->wa_secmode : DLADM_WLAN_SECMODE_NONE;
5303871Syz147064 
5313871Syz147064 	if (do_set_encryption(fd, gbuf, &secmode) < 0)
5323871Syz147064 		goto fail;
5333871Syz147064 
5343871Syz147064 	authmode = ((attrp->wa_valid & DLADM_WLAN_ATTR_AUTH) != 0) ?
5353871Syz147064 	    attrp->wa_auth : DLADM_WLAN_AUTH_OPEN;
5363871Syz147064 
5373871Syz147064 	if (do_set_authmode(fd, gbuf, &authmode) < 0)
5383871Syz147064 		goto fail;
5393871Syz147064 
5403871Syz147064 	bsstype = ((attrp->wa_valid & DLADM_WLAN_ATTR_BSSTYPE) != 0) ?
5413871Syz147064 	    attrp->wa_bsstype : DLADM_WLAN_BSSTYPE_BSS;
5423871Syz147064 
5433871Syz147064 	if (do_set_bsstype(fd, gbuf, &bsstype) < 0)
5443871Syz147064 		goto fail;
5453871Syz147064 
5463871Syz147064 	if (secmode == DLADM_WLAN_SECMODE_WEP) {
5473871Syz147064 		if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
5483871Syz147064 			return (DLADM_STATUS_BADARG);
549*4126Szf162725 		if (do_set_key(fd, gbuf, keys, key_count) < 0)
5503871Syz147064 			goto fail;
551*4126Szf162725 	} else if (secmode == DLADM_WLAN_SECMODE_WPA) {
552*4126Szf162725 		if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
553*4126Szf162725 			return (DLADM_STATUS_BADARG);
554*4126Szf162725 		if (do_get_capability(fd, gbuf) < 0)
555*4126Szf162725 			goto fail;
556*4126Szf162725 		caps = (wl_capability_t *)(gbuf->wldp_buf);
557*4126Szf162725 		if ((caps->caps & IEEE80211_C_WPA) == 0)
558*4126Szf162725 			return (DLADM_STATUS_NOTSUP);
5593871Syz147064 	}
5603871Syz147064 
5613871Syz147064 	if (create_ibss) {
5623871Syz147064 		if (do_set_channel(fd, gbuf, &channel) < 0)
5633871Syz147064 			goto fail;
5643871Syz147064 
5653871Syz147064 		if (do_set_createibss(fd, gbuf, &create_ibss) < 0)
5663871Syz147064 			goto fail;
5673871Syz147064 
5683871Syz147064 		if ((attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0) {
5693871Syz147064 			generate_essid(&essid);
5703871Syz147064 			essid_valid = B_TRUE;
5713871Syz147064 		}
5723871Syz147064 	}
5733871Syz147064 
5743871Syz147064 	if ((attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) != 0) {
5753871Syz147064 		essid = attrp->wa_essid;
5763871Syz147064 		essid_valid = B_TRUE;
5773871Syz147064 	}
5783871Syz147064 
5793871Syz147064 	if (!essid_valid)
5803871Syz147064 		return (DLADM_STATUS_FAILED);
5813871Syz147064 	if (do_set_essid(fd, gbuf, &essid) < 0)
5823871Syz147064 		goto fail;
5833871Syz147064 
584*4126Szf162725 	/*
585*4126Szf162725 	 * Because wpa daemon needs getting essid from driver,
586*4126Szf162725 	 * we need call do_set_essid() first, then call wpa_instance_create().
587*4126Szf162725 	 */
588*4126Szf162725 	if (secmode == DLADM_WLAN_SECMODE_WPA && keys != NULL)
589*4126Szf162725 		(void) wpa_instance_create(link, keys);
590*4126Szf162725 
5913871Syz147064 	start = gethrtime();
5923871Syz147064 	for (;;) {
5933871Syz147064 		if (do_get_linkstatus(fd, gbuf) < 0)
5943871Syz147064 			goto fail;
5953871Syz147064 
5963871Syz147064 		if (IS_CONNECTED(gbuf))
5973871Syz147064 			break;
5983871Syz147064 
5993871Syz147064 		(void) poll(NULL, 0, DLADM_WLAN_CONNECT_POLLRATE);
6003871Syz147064 		if ((timeout >= 0) && (gethrtime() - start) /
6013871Syz147064 		    NANOSEC >= timeout)
6023871Syz147064 			return (DLADM_STATUS_TIMEDOUT);
6033871Syz147064 	}
6043871Syz147064 	return (DLADM_STATUS_OK);
6053871Syz147064 fail:
6063871Syz147064 	return (dladm_wlan_wlresult2status(gbuf));
6073871Syz147064 }
6083871Syz147064 
6093871Syz147064 dladm_status_t
6103871Syz147064 dladm_wlan_connect(const char *link, dladm_wlan_attr_t *attrp,
6113871Syz147064     int timeout, void *keys, uint_t key_count, uint_t flags)
6123871Syz147064 {
6133871Syz147064 	int			fd, i;
6143871Syz147064 	wldp_t 			*gbuf = NULL;
6153871Syz147064 	connect_state_t		state = {0, NULL, NULL};
6163871Syz147064 	attr_node_t		*nodep = NULL;
6173871Syz147064 	boolean_t		create_ibss, set_authmode;
6183871Syz147064 	dladm_wlan_attr_t	**wl_list = NULL;
6193871Syz147064 	dladm_status_t		status = DLADM_STATUS_FAILED;
6203871Syz147064 
6213871Syz147064 	if ((fd = open_link(link)) < 0)
6223871Syz147064 		return (DLADM_STATUS_LINKINVAL);
6233871Syz147064 
6243871Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
6253871Syz147064 		status = DLADM_STATUS_NOMEM;
6263871Syz147064 		goto done;
6273871Syz147064 	}
6283871Syz147064 
6293871Syz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
6303871Syz147064 		status = DLADM_STATUS_FAILED;
6313871Syz147064 		goto done;
6323871Syz147064 	}
6333871Syz147064 
6343871Syz147064 	if (IS_CONNECTED(gbuf)) {
6353871Syz147064 		status = DLADM_STATUS_ISCONN;
6363871Syz147064 		goto done;
6373871Syz147064 	}
6383871Syz147064 
6393871Syz147064 	set_authmode = ((attrp != NULL) &&
6403871Syz147064 	    (attrp->wa_valid & DLADM_WLAN_ATTR_MODE) != 0);
6413871Syz147064 	create_ibss = ((flags & DLADM_WLAN_CONNECT_CREATEIBSS) != 0 &&
6423871Syz147064 	    attrp != NULL &&
6433871Syz147064 	    (attrp->wa_valid & DLADM_WLAN_ATTR_BSSTYPE) != 0 &&
6443871Syz147064 	    attrp->wa_bsstype == DLADM_WLAN_BSSTYPE_IBSS);
6453871Syz147064 
6463871Syz147064 	if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0 ||
6473871Syz147064 	    (create_ibss && attrp != NULL &&
6483871Syz147064 	    (attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0)) {
649*4126Szf162725 		status = do_connect(link, fd, gbuf, attrp,
6503871Syz147064 		    create_ibss, keys, key_count, timeout);
6513871Syz147064 		goto done;
6523871Syz147064 	}
6533871Syz147064 
6543871Syz147064 	state.cs_attr = attrp;
6553871Syz147064 	state.cs_list = NULL;
6563871Syz147064 	state.cs_count = 0;
6573871Syz147064 
6583871Syz147064 	status = dladm_wlan_scan(link, &state, connect_cb);
6593871Syz147064 	if (status != DLADM_STATUS_OK)
6603871Syz147064 		goto done;
6613871Syz147064 
6623871Syz147064 	if (state.cs_count == 0) {
6633871Syz147064 		if (!create_ibss) {
6643871Syz147064 			status = DLADM_STATUS_NOTFOUND;
6653871Syz147064 			goto done;
6663871Syz147064 		}
667*4126Szf162725 		status = do_connect(link, fd, gbuf, attrp, create_ibss,
6683871Syz147064 		    keys, key_count, timeout);
6693871Syz147064 		goto done;
6703871Syz147064 	}
6713871Syz147064 
6723871Syz147064 	wl_list = malloc(state.cs_count * sizeof (dladm_wlan_attr_t *));
6733871Syz147064 	if (wl_list == NULL) {
6743871Syz147064 		status = DLADM_STATUS_NOMEM;
6753871Syz147064 		goto done;
6763871Syz147064 	}
6773871Syz147064 
6783871Syz147064 	nodep = state.cs_list;
6793871Syz147064 	for (i = 0; i < state.cs_count; i++) {
6803871Syz147064 		wl_list[i] = &nodep->an_attr;
6813871Syz147064 		nodep = nodep->an_next;
6823871Syz147064 	}
6833871Syz147064 	qsort(wl_list, state.cs_count, sizeof (dladm_wlan_attr_t *),
6843871Syz147064 	    attr_compare);
6853871Syz147064 
6863871Syz147064 	for (i = 0; i < state.cs_count; i++) {
6873871Syz147064 		dladm_wlan_attr_t	*ap = wl_list[i];
6883871Syz147064 
689*4126Szf162725 		status = do_connect(link, fd, gbuf, ap, create_ibss, keys,
6903871Syz147064 		    key_count, timeout);
6913871Syz147064 		if (status == DLADM_STATUS_OK)
6923871Syz147064 			break;
6933871Syz147064 
6943871Syz147064 		if (!set_authmode) {
6953871Syz147064 			ap->wa_auth = DLADM_WLAN_AUTH_SHARED;
6963871Syz147064 			ap->wa_valid |= DLADM_WLAN_ATTR_AUTH;
697*4126Szf162725 			status = do_connect(link, fd, gbuf, ap, create_ibss,
698*4126Szf162725 			    keys, key_count, timeout);
6993871Syz147064 			if (status == DLADM_STATUS_OK)
7003871Syz147064 				break;
7013871Syz147064 		}
7023871Syz147064 	}
7033871Syz147064 done:
7043871Syz147064 	if ((status != DLADM_STATUS_OK) && (status != DLADM_STATUS_ISCONN))
705*4126Szf162725 		(void) do_disconnect(link, fd, gbuf);
7063871Syz147064 
7073871Syz147064 	while (state.cs_list != NULL) {
7083871Syz147064 		nodep = state.cs_list;
7093871Syz147064 		state.cs_list = nodep->an_next;
7103871Syz147064 		free(nodep);
7113871Syz147064 	}
7123871Syz147064 	free(gbuf);
7133871Syz147064 	free(wl_list);
7143871Syz147064 	(void) close(fd);
7153871Syz147064 	return (status);
7163871Syz147064 }
7173871Syz147064 
7183871Syz147064 dladm_status_t
7193871Syz147064 dladm_wlan_disconnect(const char *link)
7203871Syz147064 {
7213871Syz147064 	int		fd;
7223871Syz147064 	wldp_t		*gbuf;
7233871Syz147064 	dladm_status_t	status;
7243871Syz147064 
7253871Syz147064 	if ((fd = open_link(link)) < 0)
7263871Syz147064 		return (DLADM_STATUS_BADARG);
7273871Syz147064 
7283871Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
7293871Syz147064 		status = DLADM_STATUS_NOMEM;
7303871Syz147064 		goto done;
7313871Syz147064 	}
7323871Syz147064 
7333871Syz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
7343871Syz147064 		status = DLADM_STATUS_FAILED;
7353871Syz147064 		goto done;
7363871Syz147064 	}
7373871Syz147064 
7383871Syz147064 	if (!IS_CONNECTED(gbuf)) {
7393871Syz147064 		status = DLADM_STATUS_NOTCONN;
7403871Syz147064 		goto done;
7413871Syz147064 	}
7423871Syz147064 
743*4126Szf162725 	if (do_disconnect(link, fd, gbuf) < 0) {
7443871Syz147064 		status = DLADM_STATUS_FAILED;
7453871Syz147064 		goto done;
7463871Syz147064 	}
7473871Syz147064 
7483871Syz147064 	if (do_get_linkstatus(fd, gbuf) < 0) {
7493871Syz147064 		status = DLADM_STATUS_FAILED;
7503871Syz147064 		goto done;
7513871Syz147064 	}
7523871Syz147064 
7533871Syz147064 	if (IS_CONNECTED(gbuf)) {
7543871Syz147064 		status = DLADM_STATUS_FAILED;
7553871Syz147064 		goto done;
7563871Syz147064 	}
7573871Syz147064 
7583871Syz147064 	status = DLADM_STATUS_OK;
7593871Syz147064 done:
7603871Syz147064 	free(gbuf);
7613871Syz147064 	(void) close(fd);
7623871Syz147064 	return (status);
7633871Syz147064 }
7643871Syz147064 
7653871Syz147064 typedef struct dladm_wlan_linkname {
7663871Syz147064 	char			wl_name[MAXNAMELEN];
7673871Syz147064 	struct dladm_wlan_linkname	*wl_next;
7683871Syz147064 } dladm_wlan_linkname_t;
7693871Syz147064 
7703871Syz147064 typedef struct dladm_wlan_walk {
7713871Syz147064 	dladm_wlan_linkname_t	*ww_list;
7723871Syz147064 	dladm_status_t		ww_status;
7733871Syz147064 } dladm_wlan_walk_t;
7743871Syz147064 
7753871Syz147064 /* ARGSUSED */
7763871Syz147064 static int
7773871Syz147064 append_linkname(di_node_t node, di_minor_t minor, void *arg)
7783871Syz147064 {
7793871Syz147064 	dladm_wlan_walk_t		*statep = arg;
7803871Syz147064 	dladm_wlan_linkname_t	**lastp = &statep->ww_list;
7813871Syz147064 	dladm_wlan_linkname_t	*wlp = *lastp;
7823871Syz147064 	char			name[MAXNAMELEN];
7833871Syz147064 
7843871Syz147064 	(void) snprintf(name, MAXNAMELEN, "%s%d",
7853871Syz147064 	    di_driver_name(node), di_instance(node));
7863871Syz147064 
7873871Syz147064 	while (wlp != NULL) {
7883871Syz147064 		if (strcmp(wlp->wl_name, name) == 0)
7893871Syz147064 			return (DI_WALK_CONTINUE);
7903871Syz147064 
7913871Syz147064 		lastp = &wlp->wl_next;
7923871Syz147064 		wlp = wlp->wl_next;
7933871Syz147064 	}
7943871Syz147064 	if ((wlp = malloc(sizeof (*wlp))) == NULL) {
7953871Syz147064 		statep->ww_status = DLADM_STATUS_NOMEM;
7963871Syz147064 		return (DI_WALK_CONTINUE);
7973871Syz147064 	}
7983871Syz147064 
7993871Syz147064 	(void) strlcpy(wlp->wl_name, name, MAXNAMELEN);
8003871Syz147064 	wlp->wl_next = NULL;
8013871Syz147064 	*lastp = wlp;
8023871Syz147064 
8033871Syz147064 	return (DI_WALK_CONTINUE);
8043871Syz147064 }
8053871Syz147064 
8063871Syz147064 dladm_status_t
8073871Syz147064 dladm_wlan_walk(void *arg, boolean_t (*func)(void *, const char *))
8083871Syz147064 {
8093871Syz147064 	di_node_t		root;
8103871Syz147064 	dladm_wlan_walk_t		state;
8113871Syz147064 	dladm_wlan_linkname_t	*wlp, *wlp_next;
8123871Syz147064 	boolean_t		cont = B_TRUE;
8133871Syz147064 
8143871Syz147064 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
8153871Syz147064 		return (DLADM_STATUS_FAILED);
8163871Syz147064 
8173871Syz147064 	state.ww_list = NULL;
8183871Syz147064 	state.ww_status = DLADM_STATUS_OK;
8193871Syz147064 	(void) di_walk_minor(root, DDI_NT_NET_WIFI, DI_CHECK_ALIAS,
8203871Syz147064 	    &state, append_linkname);
8213871Syz147064 	di_fini(root);
8223871Syz147064 
8233871Syz147064 	for (wlp = state.ww_list; wlp != NULL; wlp = wlp_next) {
8243871Syz147064 		/*
8253871Syz147064 		 * NOTE: even if (*func)() returns B_FALSE, the loop continues
8263871Syz147064 		 * since all memory must be freed.
8273871Syz147064 		 */
8283871Syz147064 		if (cont)
8293871Syz147064 			cont = (*func)(arg, wlp->wl_name);
8303871Syz147064 		wlp_next = wlp->wl_next;
8313871Syz147064 		free(wlp);
8323871Syz147064 	}
8333871Syz147064 	return (state.ww_status);
8343871Syz147064 }
8353871Syz147064 
8363871Syz147064 dladm_status_t
8373871Syz147064 dladm_wlan_get_linkattr(const char *link, dladm_wlan_linkattr_t *attrp)
8383871Syz147064 {
8393871Syz147064 	int			fd;
8403871Syz147064 	wldp_t			*gbuf;
8413871Syz147064 	wl_rssi_t		signal;
8423871Syz147064 	wl_bss_type_t		bsstype;
8433871Syz147064 	wl_authmode_t		authmode;
8443871Syz147064 	wl_encryption_t		encryption;
8453871Syz147064 	wl_rates_t		*ratesp;
8463871Syz147064 	dladm_wlan_attr_t	*wl_attrp;
8473871Syz147064 	dladm_status_t		status = DLADM_STATUS_FAILED;
8483871Syz147064 
8493871Syz147064 	if (attrp == NULL)
8503871Syz147064 		return (DLADM_STATUS_BADARG);
8513871Syz147064 
8523871Syz147064 	if ((fd = open_link(link)) < 0)
8533871Syz147064 		return (DLADM_STATUS_LINKINVAL);
8543871Syz147064 
8553871Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
8563871Syz147064 		status = DLADM_STATUS_NOMEM;
8573871Syz147064 		goto done;
8583871Syz147064 	}
8593871Syz147064 
8603871Syz147064 	(void) memset(attrp, 0, sizeof (*attrp));
8613871Syz147064 	wl_attrp = &attrp->la_wlan_attr;
8623871Syz147064 
8633871Syz147064 	if (do_get_linkstatus(fd, gbuf) < 0)
8643871Syz147064 		goto done;
8653871Syz147064 
8663871Syz147064 	attrp->la_valid |= DLADM_WLAN_LINKATTR_STATUS;
8673871Syz147064 	if (!IS_CONNECTED(gbuf)) {
8683871Syz147064 		attrp->la_status = DLADM_WLAN_LINKSTATUS_DISCONNECTED;
869*4126Szf162725 	} else {
870*4126Szf162725 		attrp->la_status = DLADM_WLAN_LINKSTATUS_CONNECTED;
8713871Syz147064 	}
8723871Syz147064 
8733871Syz147064 	if (do_get_essid(fd, gbuf) < 0)
8743871Syz147064 		goto done;
8753871Syz147064 
8763871Syz147064 	(void) strlcpy(wl_attrp->wa_essid.we_bytes,
8773871Syz147064 	    ((wl_essid_t *)(gbuf->wldp_buf))->wl_essid_essid,
8783871Syz147064 	    DLADM_WLAN_MAX_ESSID_LEN);
8793871Syz147064 
8803871Syz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_ESSID;
8813871Syz147064 
8823871Syz147064 	if (do_get_bssid(fd, gbuf) < 0)
8833871Syz147064 		goto done;
8843871Syz147064 
8853871Syz147064 	(void) memcpy(wl_attrp->wa_bssid.wb_bytes, gbuf->wldp_buf,
8863871Syz147064 	    DLADM_WLAN_BSSID_LEN);
8873871Syz147064 
8883871Syz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_BSSID;
8893871Syz147064 
890*4126Szf162725 	if (attrp->la_status == DLADM_WLAN_LINKSTATUS_DISCONNECTED) {
891*4126Szf162725 		attrp->la_valid |= DLADM_WLAN_LINKATTR_WLAN;
892*4126Szf162725 		status = DLADM_STATUS_OK;
893*4126Szf162725 		goto done;
894*4126Szf162725 	}
895*4126Szf162725 
8963871Syz147064 	if (do_get_encryption(fd, gbuf) < 0)
8973871Syz147064 		goto done;
8983871Syz147064 
8993871Syz147064 	encryption = *(wl_encryption_t *)(gbuf->wldp_buf);
9003871Syz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_SECMODE;
9013871Syz147064 
9023871Syz147064 	switch (encryption) {
9033871Syz147064 	case WL_NOENCRYPTION:
9043871Syz147064 		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_NONE;
9053871Syz147064 		break;
9063871Syz147064 	case WL_ENC_WEP:
9073871Syz147064 		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WEP;
9083871Syz147064 		break;
909*4126Szf162725 	case WL_ENC_WPA:
910*4126Szf162725 		wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
911*4126Szf162725 		break;
9123871Syz147064 	default:
9133871Syz147064 		wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_SECMODE;
9143871Syz147064 		break;
9153871Syz147064 	}
9163871Syz147064 
9173871Syz147064 	if (do_get_signal(fd, gbuf) < 0)
9183871Syz147064 		goto done;
9193871Syz147064 
9203871Syz147064 	signal = *(wl_rssi_t *)(gbuf->wldp_buf);
9213871Syz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_STRENGTH;
9223871Syz147064 	wl_attrp->wa_strength = DLADM_WLAN_SIGNAL2STRENGTH(signal);
9233871Syz147064 
9243871Syz147064 	if (do_get_rate(fd, gbuf) < 0)
9253871Syz147064 		goto done;
9263871Syz147064 
9273871Syz147064 	ratesp = (wl_rates_t *)(gbuf->wldp_buf);
9283871Syz147064 	if (ratesp->wl_rates_num > 0) {
9293871Syz147064 		uint_t	i, r = 0;
9303871Syz147064 
9313871Syz147064 		for (i = 0; i < ratesp->wl_rates_num; i++) {
9323871Syz147064 			if (ratesp->wl_rates_rates[i] > r)
9333871Syz147064 				r = ratesp->wl_rates_rates[i];
9343871Syz147064 		}
9353871Syz147064 		wl_attrp->wa_speed = r;
9363871Syz147064 		wl_attrp->wa_valid |= DLADM_WLAN_ATTR_SPEED;
9373871Syz147064 	}
9383871Syz147064 
9393871Syz147064 	if (do_get_authmode(fd, gbuf) < 0)
9403871Syz147064 		goto done;
9413871Syz147064 
9423871Syz147064 	authmode = *(wl_authmode_t *)(gbuf->wldp_buf);
9433871Syz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_AUTH;
9443871Syz147064 
9453871Syz147064 	switch (authmode) {
9463871Syz147064 	case WL_OPENSYSTEM:
9473871Syz147064 		wl_attrp->wa_auth = DLADM_WLAN_AUTH_OPEN;
9483871Syz147064 		break;
9493871Syz147064 	case WL_SHAREDKEY:
9503871Syz147064 		wl_attrp->wa_auth = DLADM_WLAN_AUTH_SHARED;
9513871Syz147064 		break;
9523871Syz147064 	default:
9533871Syz147064 		wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_AUTH;
9543871Syz147064 		break;
9553871Syz147064 	}
9563871Syz147064 
9573871Syz147064 	if (do_get_bsstype(fd, gbuf) < 0)
9583871Syz147064 		goto done;
9593871Syz147064 
9603871Syz147064 	bsstype = *(wl_bss_type_t *)(gbuf->wldp_buf);
9613871Syz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
9623871Syz147064 
9633871Syz147064 	switch (bsstype) {
9643871Syz147064 	case WL_BSS_BSS:
9653871Syz147064 		wl_attrp->wa_bsstype = DLADM_WLAN_BSSTYPE_BSS;
9663871Syz147064 		break;
9673871Syz147064 	case WL_BSS_IBSS:
9683871Syz147064 		wl_attrp->wa_bsstype = DLADM_WLAN_BSSTYPE_IBSS;
9693871Syz147064 		break;
9703871Syz147064 	case WL_BSS_ANY:
9713871Syz147064 		wl_attrp->wa_bsstype = DLADM_WLAN_BSSTYPE_ANY;
9723871Syz147064 		break;
9733871Syz147064 	default:
9743871Syz147064 		wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_BSSTYPE;
9753871Syz147064 		break;
9763871Syz147064 	}
9773871Syz147064 
9783871Syz147064 	if (do_get_mode(fd, gbuf) < 0)
9793871Syz147064 		goto done;
9803871Syz147064 
9813871Syz147064 	wl_attrp->wa_mode = do_convert_mode((wl_phy_conf_t *)(gbuf->wldp_buf));
9823871Syz147064 	wl_attrp->wa_valid |= DLADM_WLAN_ATTR_MODE;
9833871Syz147064 	if (wl_attrp->wa_mode != DLADM_WLAN_MODE_NONE)
9843871Syz147064 		wl_attrp->wa_valid |= DLADM_WLAN_ATTR_MODE;
9853871Syz147064 
9863871Syz147064 	attrp->la_valid |= DLADM_WLAN_LINKATTR_WLAN;
9873871Syz147064 	status = DLADM_STATUS_OK;
9883871Syz147064 
9893871Syz147064 done:
9903871Syz147064 	free(gbuf);
9913871Syz147064 	(void) close(fd);
9923871Syz147064 	return (status);
9933871Syz147064 }
9943871Syz147064 
9953871Syz147064 boolean_t
9963871Syz147064 dladm_wlan_is_valid(const char *link)
9973871Syz147064 {
9983871Syz147064 	int fd = open_link(link);
9993871Syz147064 
10003871Syz147064 	if (fd < 0)
10013871Syz147064 		return (B_FALSE);
10023871Syz147064 
10033871Syz147064 	(void) close(fd);
10043871Syz147064 	return (B_TRUE);
10053871Syz147064 }
10063871Syz147064 
10073871Syz147064 /* ARGSUSED */
10083871Syz147064 static dladm_status_t
10093871Syz147064 do_check_prop(int fd, wldp_t *guf, prop_desc_t *pdp, char **prop_val,
10103871Syz147064     uint_t val_cnt, val_desc_t **vdpp)
10113871Syz147064 {
10123871Syz147064 	int		i;
10133871Syz147064 	val_desc_t	*vdp;
10143871Syz147064 
10153871Syz147064 	if (pdp->pd_nmodval == 0)
10163871Syz147064 		return (DLADM_STATUS_PROPRDONLY);
10173871Syz147064 
10183871Syz147064 	if (val_cnt != 1)
10193871Syz147064 		return (DLADM_STATUS_BADVALCNT);
10203871Syz147064 
10213871Syz147064 	for (i = 0; i < pdp->pd_nmodval; i++)
10223871Syz147064 		if (strcasecmp(*prop_val, pdp->pd_modval[i].vd_name) == 0)
10233871Syz147064 			break;
10243871Syz147064 
10253871Syz147064 	if (i == pdp->pd_nmodval)
10263871Syz147064 		return (DLADM_STATUS_BADVAL);
10273871Syz147064 
10283871Syz147064 	vdp = malloc(sizeof (val_desc_t));
10293871Syz147064 	if (vdp == NULL)
10303871Syz147064 		return (DLADM_STATUS_NOMEM);
10313871Syz147064 
10323871Syz147064 	(void) memcpy(vdp, &pdp->pd_modval[i], sizeof (val_desc_t));
10333871Syz147064 	*vdpp = vdp;
10343871Syz147064 	return (DLADM_STATUS_OK);
10353871Syz147064 }
10363871Syz147064 
10373871Syz147064 static dladm_status_t
10383871Syz147064 do_set_prop(int fd, wldp_t *gbuf, prop_desc_t *pdp,
10393871Syz147064     char **prop_val, uint_t val_cnt)
10403871Syz147064 {
10413871Syz147064 	dladm_status_t	status;
10423871Syz147064 	val_desc_t	*vdp = NULL;
10433871Syz147064 	uint_t		cnt;
10443871Syz147064 
10453871Syz147064 	if (pdp->pd_set == NULL)
10463871Syz147064 		return (DLADM_STATUS_PROPRDONLY);
10473871Syz147064 
10483871Syz147064 	if (prop_val != NULL) {
10493871Syz147064 		status = pdp->pd_check(fd, gbuf, pdp, prop_val,
10503871Syz147064 		    val_cnt, &vdp);
10513871Syz147064 
10523871Syz147064 		if (status != DLADM_STATUS_OK)
10533871Syz147064 			return (status);
10543871Syz147064 
10553871Syz147064 		cnt = val_cnt;
10563871Syz147064 	} else {
10573871Syz147064 		if (pdp->pd_defval.vd_name == NULL)
10583871Syz147064 			return (DLADM_STATUS_NOTSUP);
10593871Syz147064 
10603871Syz147064 		if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
10613871Syz147064 			return (DLADM_STATUS_NOMEM);
10623871Syz147064 
10633871Syz147064 		*vdp = pdp->pd_defval;
10643871Syz147064 		cnt = 1;
10653871Syz147064 	}
10663871Syz147064 	status = pdp->pd_set(fd, gbuf, vdp, cnt);
10673871Syz147064 	if (status == DLADM_STATUS_OK) {
10683871Syz147064 		/*
10693871Syz147064 		 * Some ioctls return 0 but store error code in
10703871Syz147064 		 * wldp_result. Need to fix them.
10713871Syz147064 		 */
10723871Syz147064 		if (gbuf->wldp_result != WL_SUCCESS)
10733871Syz147064 			status = dladm_wlan_wlresult2status(gbuf);
10743871Syz147064 	}
10753871Syz147064 	free(vdp);
10763871Syz147064 	return (status);
10773871Syz147064 }
10783871Syz147064 
10793871Syz147064 dladm_status_t
10803871Syz147064 dladm_wlan_set_prop(const char *link, const char *prop_name,
10813871Syz147064     char **prop_val, uint_t val_cnt, char **errprop)
10823871Syz147064 {
10833871Syz147064 	int		fd, i;
10843871Syz147064 	wldp_t		*gbuf = NULL;
10853871Syz147064 	boolean_t	found = B_FALSE;
10863871Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
10873871Syz147064 
10883871Syz147064 	if ((prop_name == NULL && prop_val != NULL) ||
10893871Syz147064 	    (prop_val != NULL && val_cnt == 0))
10903871Syz147064 		return (DLADM_STATUS_BADARG);
10913871Syz147064 
10923871Syz147064 	if ((fd = open_link(link)) < 0)
10933871Syz147064 		return (DLADM_STATUS_LINKINVAL);
10943871Syz147064 
10953871Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
10963871Syz147064 		status = DLADM_STATUS_NOMEM;
10973871Syz147064 		goto done;
10983871Syz147064 	}
10993871Syz147064 
11003871Syz147064 	for (i = 0; i < DLADM_WLAN_MAX_PROPS; i++) {
11013871Syz147064 		prop_desc_t	*pdp = &prop_table[i];
11023871Syz147064 		dladm_status_t	s;
11033871Syz147064 
11043871Syz147064 		if (prop_name != NULL &&
11053871Syz147064 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
11063871Syz147064 			continue;
11073871Syz147064 
11083871Syz147064 		found = B_TRUE;
11093871Syz147064 		s = do_set_prop(fd, gbuf, pdp, prop_val, val_cnt);
11103871Syz147064 
11113871Syz147064 		if (prop_name != NULL) {
11123871Syz147064 			status = s;
11133871Syz147064 			break;
11143871Syz147064 		} else {
11153871Syz147064 			if (s != DLADM_STATUS_OK &&
11163871Syz147064 			    s != DLADM_STATUS_NOTSUP) {
11173871Syz147064 				if (errprop != NULL)
11183871Syz147064 					*errprop = pdp->pd_name;
11193871Syz147064 				status = s;
11203871Syz147064 				break;
11213871Syz147064 			}
11223871Syz147064 		}
11233871Syz147064 	}
11243871Syz147064 	if (!found)
11253871Syz147064 		status = DLADM_STATUS_NOTFOUND;
11263871Syz147064 done:
11273871Syz147064 	free(gbuf);
11283871Syz147064 	(void) close(fd);
11293871Syz147064 	return (status);
11303871Syz147064 }
11313871Syz147064 
11323871Syz147064 /* ARGSUSED */
11333871Syz147064 dladm_status_t
11343871Syz147064 dladm_wlan_walk_prop(const char *link, void *arg,
11353871Syz147064     boolean_t (*func)(void *, const char *))
11363871Syz147064 {
11373871Syz147064 	int	i;
11383871Syz147064 
11393871Syz147064 	for (i = 0; i < DLADM_WLAN_MAX_PROPS; i++) {
11403871Syz147064 		if (!func(arg, prop_table[i].pd_name))
11413871Syz147064 			break;
11423871Syz147064 	}
11433871Syz147064 	return (DLADM_STATUS_OK);
11443871Syz147064 }
11453871Syz147064 
11463871Syz147064 dladm_status_t
11473871Syz147064 dladm_wlan_get_prop(const char *link, dladm_prop_type_t type,
11483871Syz147064     const char *prop_name, char **prop_val, uint_t *val_cnt)
11493871Syz147064 {
11503871Syz147064 	int		fd;
11513871Syz147064 	int		i;
11523871Syz147064 	wldp_t		*gbuf;
11533871Syz147064 	dladm_status_t	status;
11543871Syz147064 	uint_t		cnt;
11553871Syz147064 	prop_desc_t	*pdp;
11563871Syz147064 
11573871Syz147064 	if (prop_val == NULL || val_cnt == NULL || *val_cnt == 0)
11583871Syz147064 		return (DLADM_STATUS_BADARG);
11593871Syz147064 
11603871Syz147064 	for (i = 0; i < DLADM_WLAN_MAX_PROPS; i++)
11613871Syz147064 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
11623871Syz147064 			break;
11633871Syz147064 
11643871Syz147064 	if (i == DLADM_WLAN_MAX_PROPS)
11653871Syz147064 		return (DLADM_STATUS_NOTFOUND);
11663871Syz147064 
11673871Syz147064 	if ((fd = open_link(link)) < 0)
11683871Syz147064 		return (DLADM_STATUS_LINKINVAL);
11693871Syz147064 
11703871Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
11713871Syz147064 		status = DLADM_STATUS_NOMEM;
11723871Syz147064 		goto done;
11733871Syz147064 	}
11743871Syz147064 	pdp = &prop_table[i];
11753871Syz147064 	status = DLADM_STATUS_OK;
11763871Syz147064 
11773871Syz147064 	switch (type) {
11783871Syz147064 	case DLADM_PROP_VAL_CURRENT:
11793871Syz147064 		status = pdp->pd_get(fd, gbuf, prop_val, val_cnt);
11803871Syz147064 		break;
11813871Syz147064 
11823871Syz147064 	case DLADM_PROP_VAL_DEFAULT:
11833871Syz147064 		if (pdp->pd_defval.vd_name == NULL) {
11843871Syz147064 			status = DLADM_STATUS_NOTSUP;
11853871Syz147064 			break;
11863871Syz147064 		}
11873871Syz147064 		(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
11883871Syz147064 		*val_cnt = 1;
11893871Syz147064 		break;
11903871Syz147064 
11913871Syz147064 	case DLADM_PROP_VAL_MODIFIABLE:
11923871Syz147064 		if (pdp->pd_getmod != NULL) {
11933871Syz147064 			status = pdp->pd_getmod(fd, gbuf, prop_val, val_cnt);
11943871Syz147064 			break;
11953871Syz147064 		}
11963871Syz147064 		cnt = pdp->pd_nmodval;
11973871Syz147064 		if (cnt == 0) {
11983871Syz147064 			status = DLADM_STATUS_NOTSUP;
11993871Syz147064 		} else if (cnt > *val_cnt) {
12003871Syz147064 			status = DLADM_STATUS_TOOSMALL;
12013871Syz147064 		} else {
12023871Syz147064 			for (i = 0; i < cnt; i++) {
12033871Syz147064 				(void) strcpy(prop_val[i],
12043871Syz147064 				    pdp->pd_modval[i].vd_name);
12053871Syz147064 			}
12063871Syz147064 			*val_cnt = cnt;
12073871Syz147064 		}
12083871Syz147064 		break;
12093871Syz147064 	default:
12103871Syz147064 		status = DLADM_STATUS_BADARG;
12113871Syz147064 		break;
12123871Syz147064 	}
12133871Syz147064 done:
12143871Syz147064 	free(gbuf);
12153871Syz147064 	(void) close(fd);
12163871Syz147064 	return (status);
12173871Syz147064 }
12183871Syz147064 
12193871Syz147064 static boolean_t
12203871Syz147064 find_val_by_name(const char *str, val_desc_t *vdp, uint_t cnt, uint_t *valp)
12213871Syz147064 {
12223871Syz147064 	int	i;
12233871Syz147064 
12243871Syz147064 	for (i = 0; i < cnt; i++) {
12253871Syz147064 		if (strcasecmp(str, vdp[i].vd_name) == 0) {
12263871Syz147064 			*valp = vdp[i].vd_val;
12273871Syz147064 			return (B_TRUE);
12283871Syz147064 		}
12293871Syz147064 	}
12303871Syz147064 	return (B_FALSE);
12313871Syz147064 }
12323871Syz147064 
12333871Syz147064 static boolean_t
12343871Syz147064 find_name_by_val(uint_t val, val_desc_t *vdp, uint_t cnt, char **strp)
12353871Syz147064 {
12363871Syz147064 	int	i;
12373871Syz147064 
12383871Syz147064 	for (i = 0; i < cnt; i++) {
12393871Syz147064 		if (val == vdp[i].vd_val) {
12403871Syz147064 			*strp = vdp[i].vd_name;
12413871Syz147064 			return (B_TRUE);
12423871Syz147064 		}
12433871Syz147064 	}
12443871Syz147064 	return (B_FALSE);
12453871Syz147064 }
12463871Syz147064 
12473871Syz147064 const char *
12483871Syz147064 dladm_wlan_essid2str(dladm_wlan_essid_t *essid, char *buf)
12493871Syz147064 {
12503871Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", essid->we_bytes);
12513871Syz147064 	return (buf);
12523871Syz147064 }
12533871Syz147064 
12543871Syz147064 const char *
12553871Syz147064 dladm_wlan_bssid2str(dladm_wlan_bssid_t *bssid, char *buf)
12563871Syz147064 {
12573871Syz147064 	return (_link_ntoa(bssid->wb_bytes, buf, DLADM_WLAN_BSSID_LEN,
12583871Syz147064 	    IFT_OTHER));
12593871Syz147064 }
12603871Syz147064 
12613871Syz147064 static const char *
12623871Syz147064 dladm_wlan_val2str(uint_t val, val_desc_t *vdp, uint_t cnt, char *buf)
12633871Syz147064 {
12643871Syz147064 	char	*s;
12653871Syz147064 
12663871Syz147064 	if (!find_name_by_val(val, vdp, cnt, &s))
12673871Syz147064 		s = "";
12683871Syz147064 
12693871Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
12703871Syz147064 	return (buf);
12713871Syz147064 }
12723871Syz147064 
12733871Syz147064 const char *
12743871Syz147064 dladm_wlan_secmode2str(dladm_wlan_secmode_t *secmode, char *buf)
12753871Syz147064 {
12763871Syz147064 	return (dladm_wlan_val2str((uint_t)*secmode, secmode_vals,
12773871Syz147064 	    VALCNT(secmode_vals), buf));
12783871Syz147064 }
12793871Syz147064 
12803871Syz147064 const char *
12813871Syz147064 dladm_wlan_strength2str(dladm_wlan_strength_t *strength, char *buf)
12823871Syz147064 {
12833871Syz147064 	return (dladm_wlan_val2str((uint_t)*strength, strength_vals,
12843871Syz147064 	    VALCNT(strength_vals), buf));
12853871Syz147064 }
12863871Syz147064 
12873871Syz147064 const char *
12883871Syz147064 dladm_wlan_mode2str(dladm_wlan_mode_t *mode, char *buf)
12893871Syz147064 {
12903871Syz147064 	return (dladm_wlan_val2str((uint_t)*mode, mode_vals,
12913871Syz147064 	    VALCNT(mode_vals), buf));
12923871Syz147064 }
12933871Syz147064 
12943871Syz147064 const char *
12953871Syz147064 dladm_wlan_speed2str(dladm_wlan_speed_t *speed, char *buf)
12963871Syz147064 {
12973871Syz147064 	(void) snprintf(buf, DLADM_STRSIZE, "%.*f", *speed % 2,
12983871Syz147064 	    (float)(*speed) / 2);
12993871Syz147064 	return (buf);
13003871Syz147064 }
13013871Syz147064 
13023871Syz147064 const char *
13033871Syz147064 dladm_wlan_auth2str(dladm_wlan_auth_t *auth, char *buf)
13043871Syz147064 {
13053871Syz147064 	return (dladm_wlan_val2str((uint_t)*auth, auth_vals,
13063871Syz147064 	    VALCNT(auth_vals), buf));
13073871Syz147064 }
13083871Syz147064 
13093871Syz147064 const char *
13103871Syz147064 dladm_wlan_bsstype2str(dladm_wlan_bsstype_t *bsstype, char *buf)
13113871Syz147064 {
13123871Syz147064 	return (dladm_wlan_val2str((uint_t)*bsstype, bsstype_vals,
13133871Syz147064 	    VALCNT(bsstype_vals), buf));
13143871Syz147064 }
13153871Syz147064 
13163871Syz147064 const char *
13173871Syz147064 dladm_wlan_linkstatus2str(dladm_wlan_linkstatus_t *linkstatus, char *buf)
13183871Syz147064 {
13193871Syz147064 	return (dladm_wlan_val2str((uint_t)*linkstatus, linkstatus_vals,
13203871Syz147064 	    VALCNT(linkstatus_vals), buf));
13213871Syz147064 }
13223871Syz147064 
13233871Syz147064 dladm_status_t
13243871Syz147064 dladm_wlan_str2essid(const char *str, dladm_wlan_essid_t *essid)
13253871Syz147064 {
13263871Syz147064 	if (str[0] == '\0')
13273871Syz147064 		return (DLADM_STATUS_BADARG);
13283871Syz147064 
13293871Syz147064 	(void) strlcpy(essid->we_bytes, str, DLADM_WLAN_MAX_ESSID_LEN);
13303871Syz147064 	return (DLADM_STATUS_OK);
13313871Syz147064 }
13323871Syz147064 
13333871Syz147064 dladm_status_t
13343871Syz147064 dladm_wlan_str2bssid(const char *str, dladm_wlan_bssid_t *bssid)
13353871Syz147064 {
13363871Syz147064 	int	len;
13373871Syz147064 	uchar_t	*buf;
13383871Syz147064 
13393871Syz147064 	buf = _link_aton(str, &len);
13403871Syz147064 	if (buf == NULL)
13413871Syz147064 		return (DLADM_STATUS_BADARG);
13423871Syz147064 
13433871Syz147064 	if (len != DLADM_WLAN_BSSID_LEN) {
13443871Syz147064 		free(buf);
13453871Syz147064 		return (DLADM_STATUS_BADARG);
13463871Syz147064 	}
13473871Syz147064 
13483871Syz147064 	(void) memcpy(bssid->wb_bytes, buf, len);
13493871Syz147064 	free(buf);
13503871Syz147064 	return (DLADM_STATUS_OK);
13513871Syz147064 }
13523871Syz147064 
13533871Syz147064 dladm_status_t
13543871Syz147064 dladm_wlan_str2secmode(const char *str, dladm_wlan_secmode_t *secmode)
13553871Syz147064 {
13563871Syz147064 	uint_t	val;
13573871Syz147064 
13583871Syz147064 	if (!find_val_by_name(str, secmode_vals, VALCNT(secmode_vals), &val))
13593871Syz147064 		return (DLADM_STATUS_BADARG);
13603871Syz147064 
13613871Syz147064 	*secmode = (dladm_wlan_secmode_t)val;
13623871Syz147064 	return (DLADM_STATUS_OK);
13633871Syz147064 }
13643871Syz147064 
13653871Syz147064 dladm_status_t
13663871Syz147064 dladm_wlan_str2strength(const char *str, dladm_wlan_strength_t *strength)
13673871Syz147064 {
13683871Syz147064 	uint_t	val;
13693871Syz147064 
13703871Syz147064 	if (!find_val_by_name(str, strength_vals, VALCNT(strength_vals), &val))
13713871Syz147064 		return (DLADM_STATUS_BADARG);
13723871Syz147064 
13733871Syz147064 	*strength = (dladm_wlan_strength_t)val;
13743871Syz147064 	return (DLADM_STATUS_OK);
13753871Syz147064 }
13763871Syz147064 
13773871Syz147064 dladm_status_t
13783871Syz147064 dladm_wlan_str2mode(const char *str, dladm_wlan_mode_t *mode)
13793871Syz147064 {
13803871Syz147064 	uint_t	val;
13813871Syz147064 
13823871Syz147064 	if (!find_val_by_name(str, mode_vals, VALCNT(mode_vals), &val))
13833871Syz147064 		return (DLADM_STATUS_BADARG);
13843871Syz147064 
13853871Syz147064 	*mode = (dladm_wlan_mode_t)val;
13863871Syz147064 	return (DLADM_STATUS_OK);
13873871Syz147064 }
13883871Syz147064 
13893871Syz147064 dladm_status_t
13903871Syz147064 dladm_wlan_str2speed(const char *str, dladm_wlan_speed_t *speed)
13913871Syz147064 {
13923871Syz147064 	*speed = (dladm_wlan_speed_t)(atof(str) * 2);
13933871Syz147064 	return (DLADM_STATUS_OK);
13943871Syz147064 }
13953871Syz147064 
13963871Syz147064 dladm_status_t
13973871Syz147064 dladm_wlan_str2auth(const char *str, dladm_wlan_auth_t *auth)
13983871Syz147064 {
13993871Syz147064 	uint_t	val;
14003871Syz147064 
14013871Syz147064 	if (!find_val_by_name(str, auth_vals, VALCNT(auth_vals), &val))
14023871Syz147064 		return (DLADM_STATUS_BADARG);
14033871Syz147064 
14043871Syz147064 	*auth = (dladm_wlan_auth_t)val;
14053871Syz147064 	return (DLADM_STATUS_OK);
14063871Syz147064 }
14073871Syz147064 
14083871Syz147064 dladm_status_t
14093871Syz147064 dladm_wlan_str2bsstype(const char *str, dladm_wlan_bsstype_t *bsstype)
14103871Syz147064 {
14113871Syz147064 	uint_t	val;
14123871Syz147064 
14133871Syz147064 	if (!find_val_by_name(str, bsstype_vals, VALCNT(bsstype_vals), &val))
14143871Syz147064 		return (DLADM_STATUS_BADARG);
14153871Syz147064 
14163871Syz147064 	*bsstype = (dladm_wlan_bsstype_t)val;
14173871Syz147064 	return (DLADM_STATUS_OK);
14183871Syz147064 }
14193871Syz147064 
14203871Syz147064 dladm_status_t
14213871Syz147064 dladm_wlan_str2linkstatus(const char *str, dladm_wlan_linkstatus_t *linkstatus)
14223871Syz147064 {
14233871Syz147064 	uint_t	val;
14243871Syz147064 
14253871Syz147064 	if (!find_val_by_name(str, linkstatus_vals, VALCNT(linkstatus_vals),
14263871Syz147064 	    &val))
14273871Syz147064 		return (DLADM_STATUS_BADARG);
14283871Syz147064 
14293871Syz147064 	*linkstatus = (dladm_wlan_linkstatus_t)val;
14303871Syz147064 	return (DLADM_STATUS_OK);
14313871Syz147064 }
14323871Syz147064 
14333871Syz147064 static int
14343871Syz147064 do_ioctl(int fd, wldp_t *gbuf, uint_t id, size_t len, uint_t cmd, size_t cmdlen)
14353871Syz147064 {
14363871Syz147064 	int			rc;
14373871Syz147064 	struct	strioctl	stri;
14383871Syz147064 
14393871Syz147064 	gbuf->wldp_type = NET_802_11;
14403871Syz147064 	gbuf->wldp_id	= id;
14413871Syz147064 	gbuf->wldp_length = len;
14423871Syz147064 
14433871Syz147064 	stri.ic_timout	= 0;
14443871Syz147064 	stri.ic_dp	= (char *)gbuf;
14453871Syz147064 	stri.ic_cmd	= cmd;
14463871Syz147064 	stri.ic_len	= cmdlen;
14473871Syz147064 
14483871Syz147064 	if ((rc = ioctl(fd, I_STR, &stri)) != 0) {
14493871Syz147064 		if (rc > 0)
14503871Syz147064 			errno = rc;
14513871Syz147064 		return (-1);
14523871Syz147064 	}
14533871Syz147064 	return (0);
14543871Syz147064 }
14553871Syz147064 
14563871Syz147064 static int
14573871Syz147064 do_get_ioctl(int fd, wldp_t *gbuf, uint_t id)
14583871Syz147064 {
14593871Syz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
14603871Syz147064 	return (do_ioctl(fd, gbuf, id, MAX_BUF_LEN, WLAN_GET_PARAM,
14613871Syz147064 	    MAX_BUF_LEN));
14623871Syz147064 }
14633871Syz147064 
14643871Syz147064 static int
14653871Syz147064 do_set_ioctl(int fd, wldp_t *gbuf, uint_t id, void *buf, uint_t buflen)
14663871Syz147064 {
14673871Syz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
14683871Syz147064 	(void) memcpy(gbuf->wldp_buf, buf, buflen);
14693871Syz147064 	buflen += WIFI_BUF_OFFSET;
14703871Syz147064 	return (do_ioctl(fd, gbuf, id, buflen, WLAN_SET_PARAM, buflen));
14713871Syz147064 }
14723871Syz147064 
14733871Syz147064 static int
14743871Syz147064 do_cmd_ioctl(int fd, wldp_t *gbuf, uint_t cmd)
14753871Syz147064 {
14763871Syz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
14773871Syz147064 	return (do_ioctl(fd, gbuf, cmd, sizeof (wldp_t), WLAN_COMMAND,
14783871Syz147064 	    sizeof (wldp_t)));
14793871Syz147064 }
14803871Syz147064 
14813871Syz147064 static int
14823871Syz147064 do_scan(int fd, wldp_t *gbuf)
14833871Syz147064 {
14843871Syz147064 	return (do_cmd_ioctl(fd, gbuf, WL_SCAN));
14853871Syz147064 }
14863871Syz147064 
14873871Syz147064 static int
1488*4126Szf162725 do_disconnect(const char *link, int fd, wldp_t *gbuf)
14893871Syz147064 {
1490*4126Szf162725 	if (do_get_wpamode(fd, gbuf) == 0 && ((wl_wpa_t *)(gbuf->
1491*4126Szf162725 	    wldp_buf))->wpa_flag > 0)
1492*4126Szf162725 		(void) wpa_instance_delete(link);
1493*4126Szf162725 
14943871Syz147064 	return (do_cmd_ioctl(fd, gbuf, WL_DISASSOCIATE));
14953871Syz147064 }
14963871Syz147064 
14973871Syz147064 static int
14983871Syz147064 do_get_esslist(int fd, wldp_t *gbuf)
14993871Syz147064 {
15003871Syz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
15013871Syz147064 	return (do_ioctl(fd, gbuf, WL_ESS_LIST, MAX_BUF_LEN,
15023871Syz147064 	    WLAN_GET_PARAM, sizeof (wldp_t)));
15033871Syz147064 }
15043871Syz147064 
15053871Syz147064 static int
15063871Syz147064 do_get_bssid(int fd, wldp_t *gbuf)
15073871Syz147064 {
15083871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_BSSID));
15093871Syz147064 }
15103871Syz147064 
15113871Syz147064 static int
15123871Syz147064 do_get_essid(int fd, wldp_t *gbuf)
15133871Syz147064 {
15143871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_ESSID));
15153871Syz147064 }
15163871Syz147064 
15173871Syz147064 static int
15183871Syz147064 do_get_bsstype(int fd, wldp_t *gbuf)
15193871Syz147064 {
15203871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_BSS_TYPE));
15213871Syz147064 }
15223871Syz147064 
15233871Syz147064 static int
15243871Syz147064 do_get_linkstatus(int fd, wldp_t *gbuf)
15253871Syz147064 {
15263871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_LINKSTATUS));
15273871Syz147064 }
15283871Syz147064 
15293871Syz147064 static int
15303871Syz147064 do_get_rate(int fd, wldp_t *gbuf)
15313871Syz147064 {
15323871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_DESIRED_RATES));
15333871Syz147064 }
15343871Syz147064 
15353871Syz147064 static int
15363871Syz147064 do_get_phyconf(int fd, wldp_t *gbuf)
15373871Syz147064 {
15383871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_PHY_CONFIG));
15393871Syz147064 }
15403871Syz147064 
15413871Syz147064 static int
15423871Syz147064 do_get_powermode(int fd, wldp_t *gbuf)
15433871Syz147064 {
15443871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_POWER_MODE));
15453871Syz147064 }
15463871Syz147064 
15473871Syz147064 static int
15483871Syz147064 do_get_radio(int fd, wldp_t *gbuf)
15493871Syz147064 {
15503871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_RADIO));
15513871Syz147064 }
15523871Syz147064 
15533871Syz147064 static int
15543871Syz147064 do_get_authmode(int fd, wldp_t *gbuf)
15553871Syz147064 {
15563871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_AUTH_MODE));
15573871Syz147064 }
15583871Syz147064 
15593871Syz147064 static int
15603871Syz147064 do_get_encryption(int fd, wldp_t *gbuf)
15613871Syz147064 {
15623871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_ENCRYPTION));
15633871Syz147064 }
15643871Syz147064 
15653871Syz147064 static int
15663871Syz147064 do_get_signal(int fd, wldp_t *gbuf)
15673871Syz147064 {
15683871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_RSSI));
15693871Syz147064 }
15703871Syz147064 
15713871Syz147064 static int
15723871Syz147064 do_get_mode(int fd, wldp_t *gbuf)
15733871Syz147064 {
15743871Syz147064 	return (do_get_ioctl(fd, gbuf, WL_PHY_CONFIG));
15753871Syz147064 }
15763871Syz147064 
15773871Syz147064 static dladm_status_t
15783871Syz147064 do_get_rate_common(wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
15793871Syz147064 {
15803871Syz147064 	wl_rates_t	*wrp = (wl_rates_t *)gbuf->wldp_buf;
15813871Syz147064 	uint_t		cnt = wrp->wl_rates_num;
15823871Syz147064 	uint_t		i;
15833871Syz147064 
15843871Syz147064 	if (cnt > *val_cnt)
15853871Syz147064 		return (DLADM_STATUS_TOOSMALL);
15863871Syz147064 	if (wrp->wl_rates_rates[0] == 0) {
15873871Syz147064 		prop_val[0][0] = '\0';
15883871Syz147064 		*val_cnt = 1;
15893871Syz147064 		return (DLADM_STATUS_OK);
15903871Syz147064 	}
15913871Syz147064 
15923871Syz147064 	for (i = 0; i < cnt; i++) {
15933871Syz147064 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
15943871Syz147064 		    wrp->wl_rates_rates[i] % 2,
15953871Syz147064 		    (float)wrp->wl_rates_rates[i] / 2);
15963871Syz147064 	}
15973871Syz147064 	*val_cnt = cnt;
15983871Syz147064 	return (DLADM_STATUS_OK);
15993871Syz147064 }
16003871Syz147064 
16013871Syz147064 static dladm_status_t
16023871Syz147064 do_get_rate_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
16033871Syz147064 {
16043871Syz147064 	if (do_get_rate(fd, gbuf) < 0)
16053871Syz147064 		return (dladm_wlan_wlresult2status(gbuf));
16063871Syz147064 
16073871Syz147064 	return (do_get_rate_common(gbuf, prop_val, val_cnt));
16083871Syz147064 }
16093871Syz147064 
16103871Syz147064 static dladm_status_t
16113871Syz147064 do_get_rate_mod(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
16123871Syz147064 {
16133871Syz147064 	if (do_get_ioctl(fd, gbuf, WL_SUPPORTED_RATES) < 0)
16143871Syz147064 		return (DLADM_STATUS_FAILED);
16153871Syz147064 
16163871Syz147064 	return (do_get_rate_common(gbuf, prop_val, val_cnt));
16173871Syz147064 }
16183871Syz147064 
16193871Syz147064 static dladm_status_t
16203871Syz147064 do_get_channel_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
16213871Syz147064 {
16223871Syz147064 	uint32_t	channel;
16233871Syz147064 
16243871Syz147064 	if (do_get_phyconf(fd, gbuf) < 0)
16253871Syz147064 		return (dladm_wlan_wlresult2status(gbuf));
16263871Syz147064 
16273871Syz147064 	if (!do_convert_chan((wl_phy_conf_t *)gbuf->wldp_buf, &channel))
16283871Syz147064 		return (DLADM_STATUS_NOTFOUND);
16293871Syz147064 
16303871Syz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
16313871Syz147064 	*val_cnt = 1;
16323871Syz147064 
16333871Syz147064 	return (DLADM_STATUS_OK);
16343871Syz147064 }
16353871Syz147064 
16363871Syz147064 static dladm_status_t
16373871Syz147064 do_get_powermode_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
16383871Syz147064 {
16393871Syz147064 	wl_ps_mode_t	*mode;
16403871Syz147064 	const char	*s;
16413871Syz147064 
16423871Syz147064 	if (do_get_powermode(fd, gbuf) < 0)
16433871Syz147064 		return (dladm_wlan_wlresult2status(gbuf));
16443871Syz147064 
16453871Syz147064 	mode = (wl_ps_mode_t *)(gbuf->wldp_buf);
16463871Syz147064 	switch (mode->wl_ps_mode) {
16473871Syz147064 	case WL_PM_AM:
16483871Syz147064 		s = "off";
16493871Syz147064 		break;
16503871Syz147064 	case WL_PM_MPS:
16513871Syz147064 		s = "max";
16523871Syz147064 		break;
16533871Syz147064 	case WL_PM_FAST:
16543871Syz147064 		s = "fast";
16553871Syz147064 		break;
16563871Syz147064 	default:
16573871Syz147064 		return (DLADM_STATUS_NOTFOUND);
16583871Syz147064 	}
16593871Syz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
16603871Syz147064 	*val_cnt = 1;
16613871Syz147064 
16623871Syz147064 	return (DLADM_STATUS_OK);
16633871Syz147064 }
16643871Syz147064 
16653871Syz147064 static dladm_status_t
16663871Syz147064 do_get_radio_prop(int fd, wldp_t *gbuf, char **prop_val, uint_t *val_cnt)
16673871Syz147064 {
16683871Syz147064 	wl_radio_t	radio;
16693871Syz147064 	const char	*s;
16703871Syz147064 
16713871Syz147064 	if (do_get_radio(fd, gbuf) < 0)
16723871Syz147064 		return (dladm_wlan_wlresult2status(gbuf));
16733871Syz147064 
16743871Syz147064 	radio = *(wl_radio_t *)(gbuf->wldp_buf);
16753871Syz147064 	switch (radio) {
16763871Syz147064 	case B_TRUE:
16773871Syz147064 		s = "on";
16783871Syz147064 		break;
16793871Syz147064 	case B_FALSE:
16803871Syz147064 		s = "off";
16813871Syz147064 		break;
16823871Syz147064 	default:
16833871Syz147064 		return (DLADM_STATUS_NOTFOUND);
16843871Syz147064 	}
16853871Syz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
16863871Syz147064 	*val_cnt = 1;
16873871Syz147064 
16883871Syz147064 	return (DLADM_STATUS_OK);
16893871Syz147064 }
16903871Syz147064 
16913871Syz147064 static int
16923871Syz147064 do_set_bsstype(int fd, wldp_t *gbuf, dladm_wlan_bsstype_t *bsstype)
16933871Syz147064 {
16943871Syz147064 	wl_bss_type_t	ibsstype;
16953871Syz147064 
16963871Syz147064 	switch (*bsstype) {
16973871Syz147064 	case DLADM_WLAN_BSSTYPE_BSS:
16983871Syz147064 		ibsstype = WL_BSS_BSS;
16993871Syz147064 		break;
17003871Syz147064 	case DLADM_WLAN_BSSTYPE_IBSS:
17013871Syz147064 		ibsstype = WL_BSS_IBSS;
17023871Syz147064 		break;
17033871Syz147064 	default:
17043871Syz147064 		ibsstype = WL_BSS_ANY;
17053871Syz147064 		break;
17063871Syz147064 	}
17073871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_BSS_TYPE, &ibsstype,
17083871Syz147064 	    sizeof (ibsstype)));
17093871Syz147064 }
17103871Syz147064 
17113871Syz147064 static int
17123871Syz147064 do_set_authmode(int fd, wldp_t *gbuf, dladm_wlan_auth_t *auth)
17133871Syz147064 {
17143871Syz147064 	wl_authmode_t	auth_mode;
17153871Syz147064 
17163871Syz147064 	switch (*auth) {
17173871Syz147064 	case DLADM_WLAN_AUTH_OPEN:
17183871Syz147064 		auth_mode = WL_OPENSYSTEM;
17193871Syz147064 		break;
17203871Syz147064 	case DLADM_WLAN_AUTH_SHARED:
17213871Syz147064 		auth_mode = WL_SHAREDKEY;
17223871Syz147064 		break;
17233871Syz147064 	default:
17243871Syz147064 		return (-1);
17253871Syz147064 	}
17263871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_AUTH_MODE, &auth_mode,
17273871Syz147064 	    sizeof (auth_mode)));
17283871Syz147064 }
17293871Syz147064 
17303871Syz147064 static int
17313871Syz147064 do_set_encryption(int fd, wldp_t *gbuf, dladm_wlan_secmode_t *secmode)
17323871Syz147064 {
17333871Syz147064 	wl_encryption_t	encryption;
17343871Syz147064 
17353871Syz147064 	switch (*secmode) {
17363871Syz147064 	case DLADM_WLAN_SECMODE_NONE:
17373871Syz147064 		encryption = WL_NOENCRYPTION;
17383871Syz147064 		break;
17393871Syz147064 	case DLADM_WLAN_SECMODE_WEP:
17403871Syz147064 		encryption = WL_ENC_WEP;
17413871Syz147064 		break;
1742*4126Szf162725 	case DLADM_WLAN_SECMODE_WPA:
1743*4126Szf162725 		return (0);
17443871Syz147064 	default:
17453871Syz147064 		return (-1);
17463871Syz147064 	}
17473871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_ENCRYPTION, &encryption,
17483871Syz147064 	    sizeof (encryption)));
17493871Syz147064 }
17503871Syz147064 
17513871Syz147064 static int
1752*4126Szf162725 do_set_key(int fd, wldp_t *gbuf, dladm_wlan_key_t *keys,
17533871Syz147064     uint_t key_count)
17543871Syz147064 {
17553871Syz147064 	int			i;
17563871Syz147064 	wl_wep_key_t		*wkp;
17573871Syz147064 	wl_wep_key_tab_t	wepkey_tab;
1758*4126Szf162725 	dladm_wlan_key_t	*kp;
17593871Syz147064 
17603871Syz147064 	if (key_count == 0 || key_count > MAX_NWEPKEYS || keys == NULL)
17613871Syz147064 		return (-1);
17623871Syz147064 
17633871Syz147064 	(void) memset(wepkey_tab, 0, sizeof (wepkey_tab));
17643871Syz147064 	for (i = 0; i < MAX_NWEPKEYS; i++)
17653871Syz147064 		wepkey_tab[i].wl_wep_operation = WL_NUL;
17663871Syz147064 
17673871Syz147064 	for (i = 0; i < key_count; i++) {
17683871Syz147064 		kp = &keys[i];
17693871Syz147064 		if (kp->wk_idx == 0 || kp->wk_idx > MAX_NWEPKEYS)
17703871Syz147064 			return (-1);
17713871Syz147064 		if (kp->wk_len != DLADM_WLAN_WEPKEY64_LEN &&
17723871Syz147064 		    kp->wk_len != DLADM_WLAN_WEPKEY128_LEN)
17733871Syz147064 			return (-1);
17743871Syz147064 
17753871Syz147064 		wkp = &wepkey_tab[kp->wk_idx - 1];
17763871Syz147064 		wkp->wl_wep_operation = WL_ADD;
17773871Syz147064 		wkp->wl_wep_length = kp->wk_len;
17783871Syz147064 		(void) memcpy(wkp->wl_wep_key, kp->wk_val, kp->wk_len);
17793871Syz147064 	}
17803871Syz147064 
17813871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_WEP_KEY_TAB, &wepkey_tab,
17823871Syz147064 	    sizeof (wepkey_tab)));
17833871Syz147064 }
17843871Syz147064 
17853871Syz147064 static int
17863871Syz147064 do_set_essid(int fd, wldp_t *gbuf, dladm_wlan_essid_t *essid)
17873871Syz147064 {
17883871Syz147064 	wl_essid_t	iessid;
17893871Syz147064 
17903871Syz147064 	(void) memset(&iessid, 0, sizeof (essid));
17913871Syz147064 
17923871Syz147064 	if (essid != NULL && essid->we_bytes[0] != '\0') {
17933871Syz147064 		iessid.wl_essid_length = strlen(essid->we_bytes);
17943871Syz147064 		(void) strlcpy(iessid.wl_essid_essid, essid->we_bytes,
17953871Syz147064 		    sizeof (iessid.wl_essid_essid));
17963871Syz147064 	} else {
17973871Syz147064 		return (-1);
17983871Syz147064 	}
17993871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_ESSID, &iessid, sizeof (iessid)));
18003871Syz147064 }
18013871Syz147064 
18023871Syz147064 /* ARGSUSED */
18033871Syz147064 static dladm_status_t
18043871Syz147064 do_check_rate(int fd, wldp_t *gbuf, prop_desc_t *pdp, char **prop_val,
18053871Syz147064     uint_t val_cnt, val_desc_t **vdpp)
18063871Syz147064 {
18073871Syz147064 	int		i;
18083871Syz147064 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
18093871Syz147064 	char		*buf, **modval;
18103871Syz147064 	dladm_status_t	status;
18113871Syz147064 	val_desc_t	*vdp = NULL;
18123871Syz147064 
18133871Syz147064 	if (val_cnt != 1)
18143871Syz147064 		return (DLADM_STATUS_BADVALCNT);
18153871Syz147064 
18163871Syz147064 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) * MAX_SUPPORT_RATES);
18173871Syz147064 	if (buf == NULL)
18183871Syz147064 		goto done;
18193871Syz147064 
18203871Syz147064 	modval = (char **)(void *)buf;
18213871Syz147064 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
18223871Syz147064 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
18233871Syz147064 		    i * DLADM_STRSIZE;
18243871Syz147064 	}
18253871Syz147064 
18263871Syz147064 	status = do_get_rate_mod(fd, gbuf, modval, &modval_cnt);
18273871Syz147064 	if (status != DLADM_STATUS_OK)
18283871Syz147064 		goto done;
18293871Syz147064 
18303871Syz147064 	vdp = malloc(sizeof (val_desc_t));
18313871Syz147064 	if (vdp == NULL) {
18323871Syz147064 		status = DLADM_STATUS_NOMEM;
18333871Syz147064 		goto done;
18343871Syz147064 	}
18353871Syz147064 
18363871Syz147064 	for (i = 0; i < modval_cnt; i++) {
18373871Syz147064 		if (strcasecmp(*prop_val, modval[i]) == 0) {
18383871Syz147064 			vdp->vd_val = (uint_t)(atof(*prop_val) * 2);
18393871Syz147064 			status = DLADM_STATUS_OK;
18403871Syz147064 			*vdpp = vdp;
18413871Syz147064 			vdp = NULL;
18423871Syz147064 			break;
18433871Syz147064 		}
18443871Syz147064 	}
18453871Syz147064 	if (i == modval_cnt)
18463871Syz147064 		status = DLADM_STATUS_BADVAL;
18473871Syz147064 done:
18483871Syz147064 	free(buf);
18493871Syz147064 	free(vdp);
18503871Syz147064 	return (status);
18513871Syz147064 }
18523871Syz147064 
18533871Syz147064 static dladm_status_t
18543871Syz147064 do_set_rate_prop(int fd, wldp_t *gbuf, val_desc_t *vdp, uint_t val_cnt)
18553871Syz147064 {
18563871Syz147064 	dladm_wlan_rates_t	rates;
18573871Syz147064 
18583871Syz147064 	if (val_cnt != 1)
18593871Syz147064 		return (DLADM_STATUS_BADVALCNT);
18603871Syz147064 
18613871Syz147064 	rates.wr_cnt = 1;
18623871Syz147064 	rates.wr_rates[0] = vdp[0].vd_val;
18633871Syz147064 
18643871Syz147064 	if (do_set_rate(fd, gbuf, &rates) < 0)
18653871Syz147064 		return (dladm_wlan_wlresult2status(gbuf));
18663871Syz147064 
18673871Syz147064 	return (DLADM_STATUS_OK);
18683871Syz147064 }
18693871Syz147064 
18703871Syz147064 static int
18713871Syz147064 do_set_rate(int fd, wldp_t *gbuf, dladm_wlan_rates_t *rates)
18723871Syz147064 {
18733871Syz147064 	int		i;
18743871Syz147064 	uint_t		len;
18753871Syz147064 	wl_rates_t	*wrp = (wl_rates_t *)gbuf->wldp_buf;
18763871Syz147064 
18773871Syz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
18783871Syz147064 
18793871Syz147064 	for (i = 0; i < rates->wr_cnt; i++)
18803871Syz147064 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
18813871Syz147064 	wrp->wl_rates_num = rates->wr_cnt;
18823871Syz147064 
18833871Syz147064 	len = offsetof(wl_rates_t, wl_rates_rates) +
18843871Syz147064 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
18853871Syz147064 	return (do_ioctl(fd, gbuf, WL_DESIRED_RATES, len, WLAN_SET_PARAM, len));
18863871Syz147064 }
18873871Syz147064 
18883871Syz147064 /* ARGSUSED */
18893871Syz147064 static dladm_status_t
18903871Syz147064 do_set_powermode_prop(int fd, wldp_t *gbuf, val_desc_t *vdp, uint_t val_cnt)
18913871Syz147064 {
18923871Syz147064 	dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
18933871Syz147064 
18943871Syz147064 	if (do_set_powermode(fd, gbuf, &powermode) < 0)
18953871Syz147064 		return (dladm_wlan_wlresult2status(gbuf));
18963871Syz147064 
18973871Syz147064 	return (DLADM_STATUS_OK);
18983871Syz147064 }
18993871Syz147064 
19003871Syz147064 static int
19013871Syz147064 do_set_powermode(int fd, wldp_t *gbuf, dladm_wlan_powermode_t *pm)
19023871Syz147064 {
19033871Syz147064 	wl_ps_mode_t	ps_mode;
19043871Syz147064 
19053871Syz147064 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
19063871Syz147064 
19073871Syz147064 	switch (*pm) {
19083871Syz147064 	case DLADM_WLAN_PM_OFF:
19093871Syz147064 		ps_mode.wl_ps_mode = WL_PM_AM;
19103871Syz147064 		break;
19113871Syz147064 	case DLADM_WLAN_PM_MAX:
19123871Syz147064 		ps_mode.wl_ps_mode = WL_PM_MPS;
19133871Syz147064 		break;
19143871Syz147064 	case DLADM_WLAN_PM_FAST:
19153871Syz147064 		ps_mode.wl_ps_mode = WL_PM_FAST;
19163871Syz147064 		break;
19173871Syz147064 	default:
19183871Syz147064 		return (-1);
19193871Syz147064 	}
19203871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_POWER_MODE, &ps_mode,
19213871Syz147064 	    sizeof (ps_mode)));
19223871Syz147064 }
19233871Syz147064 
19243871Syz147064 /* ARGSUSED */
19253871Syz147064 static dladm_status_t
19263871Syz147064 do_set_radio_prop(int fd, wldp_t *gbuf, val_desc_t *vdp, uint_t val_cnt)
19273871Syz147064 {
19283871Syz147064 	dladm_wlan_radio_t	radio = (dladm_wlan_radio_t)vdp->vd_val;
19293871Syz147064 
19303871Syz147064 	if (do_set_radio(fd, gbuf, &radio) < 0)
19313871Syz147064 		return (dladm_wlan_wlresult2status(gbuf));
19323871Syz147064 
19333871Syz147064 	return (DLADM_STATUS_OK);
19343871Syz147064 }
19353871Syz147064 
19363871Syz147064 static int
19373871Syz147064 do_set_radio(int fd, wldp_t *gbuf, dladm_wlan_radio_t *radio)
19383871Syz147064 {
19393871Syz147064 	wl_radio_t r;
19403871Syz147064 
19413871Syz147064 	switch (*radio) {
19423871Syz147064 	case DLADM_WLAN_RADIO_ON:
19433871Syz147064 		r = B_TRUE;
19443871Syz147064 		break;
19453871Syz147064 	case DLADM_WLAN_RADIO_OFF:
19463871Syz147064 		r = B_FALSE;
19473871Syz147064 		break;
19483871Syz147064 	default:
19493871Syz147064 		return (-1);
19503871Syz147064 	}
19513871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_RADIO, &r, sizeof (r)));
19523871Syz147064 }
19533871Syz147064 
19543871Syz147064 static int
19553871Syz147064 do_set_channel(int fd, wldp_t *gbuf, dladm_wlan_channel_t *channel)
19563871Syz147064 {
19573871Syz147064 	wl_phy_conf_t phy_conf;
19583871Syz147064 
19593871Syz147064 	if (*channel > MAX_CHANNEL_NUM)
19603871Syz147064 		return (-1);
19613871Syz147064 
19623871Syz147064 	(void) memset(&phy_conf, 0xff, sizeof (phy_conf));
19633871Syz147064 	phy_conf.wl_phy_dsss_conf.wl_dsss_channel = *channel;
19643871Syz147064 
19653871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_PHY_CONFIG, &phy_conf,
19663871Syz147064 	    sizeof (phy_conf)));
19673871Syz147064 }
19683871Syz147064 
19693871Syz147064 static int
19703871Syz147064 do_set_createibss(int fd, wldp_t *gbuf, boolean_t *create_ibss)
19713871Syz147064 {
19723871Syz147064 	wl_create_ibss_t cr = (wl_create_ibss_t)(*create_ibss);
19733871Syz147064 
19743871Syz147064 	return (do_set_ioctl(fd, gbuf, WL_CREATE_IBSS, &cr, sizeof (cr)));
19753871Syz147064 }
19763871Syz147064 
19773871Syz147064 static void
19783871Syz147064 generate_essid(dladm_wlan_essid_t *essid)
19793871Syz147064 {
19803871Syz147064 	srandom(gethrtime());
19813871Syz147064 	(void) snprintf(essid->we_bytes, DLADM_WLAN_MAX_ESSID_LEN, "%d",
19823871Syz147064 	    random());
19833871Syz147064 }
1984*4126Szf162725 
1985*4126Szf162725 static int
1986*4126Szf162725 do_get_capability(int fd, wldp_t *gbuf)
1987*4126Szf162725 {
1988*4126Szf162725 	return (do_get_ioctl(fd, gbuf, WL_CAPABILITY));
1989*4126Szf162725 }
1990*4126Szf162725 
1991*4126Szf162725 static int
1992*4126Szf162725 do_get_wpamode(int fd, wldp_t *gbuf)
1993*4126Szf162725 {
1994*4126Szf162725 	return (do_get_ioctl(fd, gbuf, WL_WPA));
1995*4126Szf162725 }
1996*4126Szf162725 
1997*4126Szf162725 static dladm_status_t
1998*4126Szf162725 ioctl_get(const char *link, int id, void *gbuf)
1999*4126Szf162725 {
2000*4126Szf162725 	int		fd;
2001*4126Szf162725 
2002*4126Szf162725 	if ((fd = open_link(link)) < 0)
2003*4126Szf162725 		return (DLADM_STATUS_LINKINVAL);
2004*4126Szf162725 	(void) do_get_ioctl(fd, gbuf, id);
2005*4126Szf162725 
2006*4126Szf162725 	(void) close(fd);
2007*4126Szf162725 	return (dladm_wlan_wlresult2status(gbuf));
2008*4126Szf162725 }
2009*4126Szf162725 
2010*4126Szf162725 static dladm_status_t
2011*4126Szf162725 ioctl_set(const char *link, int id, void *buf, uint_t buflen)
2012*4126Szf162725 {
2013*4126Szf162725 	int		fd;
2014*4126Szf162725 	wldp_t 		*gbuf;
2015*4126Szf162725 	dladm_status_t	status;
2016*4126Szf162725 
2017*4126Szf162725 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
2018*4126Szf162725 		return (DLADM_STATUS_NOMEM);
2019*4126Szf162725 
2020*4126Szf162725 	if ((fd = open_link(link)) < 0)
2021*4126Szf162725 		return (DLADM_STATUS_LINKINVAL);
2022*4126Szf162725 	(void) do_set_ioctl(fd, gbuf, id, buf, buflen);
2023*4126Szf162725 
2024*4126Szf162725 	(void) close(fd);
2025*4126Szf162725 	status = dladm_wlan_wlresult2status(gbuf);
2026*4126Szf162725 	free(gbuf);
2027*4126Szf162725 
2028*4126Szf162725 	return (status);
2029*4126Szf162725 }
2030*4126Szf162725 
2031*4126Szf162725 dladm_status_t
2032*4126Szf162725 dladm_wlan_wpa_get_sr(const char *link, dladm_wlan_ess_t *sr, uint_t escnt,
2033*4126Szf162725     uint_t *estot)
2034*4126Szf162725 {
2035*4126Szf162725 	int		i, n;
2036*4126Szf162725 	wldp_t 		*gbuf;
2037*4126Szf162725 	wl_wpa_ess_t	*es;
2038*4126Szf162725 	dladm_status_t	status;
2039*4126Szf162725 
2040*4126Szf162725 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
2041*4126Szf162725 		return (DLADM_STATUS_NOMEM);
2042*4126Szf162725 
2043*4126Szf162725 	status = ioctl_get(link, WL_SCANRESULTS, gbuf);
2044*4126Szf162725 
2045*4126Szf162725 	if (status == DLADM_STATUS_OK) {
2046*4126Szf162725 		es = (wl_wpa_ess_t *)(gbuf->wldp_buf);
2047*4126Szf162725 		n = (es->count > escnt) ? escnt : es->count;
2048*4126Szf162725 		for (i = 0; i < n; i ++) {
2049*4126Szf162725 			(void) memcpy(sr[i].we_bssid.wb_bytes, es->ess[i].bssid,
2050*4126Szf162725 			    DLADM_WLAN_BSSID_LEN);
2051*4126Szf162725 			sr[i].we_ssid_len = es->ess[i].ssid_len;
2052*4126Szf162725 			(void) memcpy(sr[i].we_ssid.we_bytes, es->ess[i].ssid,
2053*4126Szf162725 			    es->ess[i].ssid_len);
2054*4126Szf162725 			sr[i].we_wpa_ie_len = es->ess[i].wpa_ie_len;
2055*4126Szf162725 			(void) memcpy(sr[i].we_wpa_ie, es->ess[i].wpa_ie,
2056*4126Szf162725 			    es->ess[i].wpa_ie_len);
2057*4126Szf162725 			sr[i].we_freq = es->ess[i].freq;
2058*4126Szf162725 		}
2059*4126Szf162725 		*estot = n;
2060*4126Szf162725 	}
2061*4126Szf162725 
2062*4126Szf162725 	free(gbuf);
2063*4126Szf162725 	return (status);
2064*4126Szf162725 }
2065*4126Szf162725 
2066*4126Szf162725 dladm_status_t
2067*4126Szf162725 dladm_wlan_wpa_set_ie(const char *link, uint8_t *wpa_ie,
2068*4126Szf162725     uint_t wpa_ie_len)
2069*4126Szf162725 {
2070*4126Szf162725 	wl_wpa_ie_t *ie;
2071*4126Szf162725 	uint_t len;
2072*4126Szf162725 	dladm_status_t	status;
2073*4126Szf162725 
2074*4126Szf162725 	if (wpa_ie_len > DLADM_WLAN_MAX_WPA_IE_LEN)
2075*4126Szf162725 		return (DLADM_STATUS_BADARG);
2076*4126Szf162725 	len = sizeof (wl_wpa_ie_t) + wpa_ie_len;
2077*4126Szf162725 	ie = malloc(len);
2078*4126Szf162725 	if (ie == NULL)
2079*4126Szf162725 		return (DLADM_STATUS_NOMEM);
2080*4126Szf162725 
2081*4126Szf162725 	(void) memset(ie, 0, len);
2082*4126Szf162725 	ie->wpa_ie_len = wpa_ie_len;
2083*4126Szf162725 	(void) memcpy(ie->wpa_ie, wpa_ie, wpa_ie_len);
2084*4126Szf162725 
2085*4126Szf162725 	status = ioctl_set(link, WL_SETOPTIE, ie, len);
2086*4126Szf162725 	free(ie);
2087*4126Szf162725 
2088*4126Szf162725 	return (status);
2089*4126Szf162725 }
2090*4126Szf162725 
2091*4126Szf162725 dladm_status_t
2092*4126Szf162725 dladm_wlan_wpa_set_wpa(const char *link, boolean_t flag)
2093*4126Szf162725 {
2094*4126Szf162725 	wl_wpa_t wpa;
2095*4126Szf162725 
2096*4126Szf162725 	wpa.wpa_flag = flag;
2097*4126Szf162725 	return (ioctl_set(link, WL_WPA, &wpa, sizeof (wl_wpa_t)));
2098*4126Szf162725 }
2099*4126Szf162725 
2100*4126Szf162725 dladm_status_t
2101*4126Szf162725 dladm_wlan_wpa_del_key(const char *link, uint_t key_idx,
2102*4126Szf162725     const dladm_wlan_bssid_t *addr)
2103*4126Szf162725 {
2104*4126Szf162725 	wl_del_key_t wk;
2105*4126Szf162725 
2106*4126Szf162725 	wk.idk_keyix = key_idx;
2107*4126Szf162725 	if (addr != NULL)
2108*4126Szf162725 		(void) memcpy((char *)wk.idk_macaddr, addr->wb_bytes,
2109*4126Szf162725 		    DLADM_WLAN_BSSID_LEN);
2110*4126Szf162725 
2111*4126Szf162725 	return (ioctl_set(link, WL_DELKEY, &wk, sizeof (wl_del_key_t)));
2112*4126Szf162725 }
2113*4126Szf162725 
2114*4126Szf162725 dladm_status_t
2115*4126Szf162725 dladm_wlan_wpa_set_key(const char *link, dladm_wlan_cipher_t cipher,
2116*4126Szf162725     const dladm_wlan_bssid_t *addr, boolean_t set_tx, uint64_t seq,
2117*4126Szf162725     uint_t key_idx, uint8_t *key, uint_t key_len)
2118*4126Szf162725 {
2119*4126Szf162725 	wl_key_t wk;
2120*4126Szf162725 
2121*4126Szf162725 	(void) memset(&wk, 0, sizeof (wl_key_t));
2122*4126Szf162725 	switch (cipher) {
2123*4126Szf162725 	case DLADM_WLAN_CIPHER_WEP:
2124*4126Szf162725 		wk.ik_type = IEEE80211_CIPHER_WEP;
2125*4126Szf162725 		break;
2126*4126Szf162725 	case DLADM_WLAN_CIPHER_TKIP:
2127*4126Szf162725 		wk.ik_type = IEEE80211_CIPHER_TKIP;
2128*4126Szf162725 		break;
2129*4126Szf162725 	case DLADM_WLAN_CIPHER_AES_OCB:
2130*4126Szf162725 		wk.ik_type = IEEE80211_CIPHER_AES_OCB;
2131*4126Szf162725 		break;
2132*4126Szf162725 	case DLADM_WLAN_CIPHER_AES_CCM:
2133*4126Szf162725 		wk.ik_type = IEEE80211_CIPHER_AES_CCM;
2134*4126Szf162725 		break;
2135*4126Szf162725 	case DLADM_WLAN_CIPHER_CKIP:
2136*4126Szf162725 		wk.ik_type = IEEE80211_CIPHER_CKIP;
2137*4126Szf162725 		break;
2138*4126Szf162725 	case DLADM_WLAN_CIPHER_NONE:
2139*4126Szf162725 		wk.ik_type = IEEE80211_CIPHER_NONE;
2140*4126Szf162725 		break;
2141*4126Szf162725 	default:
2142*4126Szf162725 		return (DLADM_STATUS_BADARG);
2143*4126Szf162725 	}
2144*4126Szf162725 	wk.ik_flags = IEEE80211_KEY_RECV;
2145*4126Szf162725 	if (set_tx) {
2146*4126Szf162725 		wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT;
2147*4126Szf162725 		(void) memcpy(wk.ik_macaddr, addr->wb_bytes,
2148*4126Szf162725 		    DLADM_WLAN_BSSID_LEN);
2149*4126Szf162725 	} else
2150*4126Szf162725 		(void) memset(wk.ik_macaddr, 0, DLADM_WLAN_BSSID_LEN);
2151*4126Szf162725 	wk.ik_keyix = key_idx;
2152*4126Szf162725 	wk.ik_keylen = key_len;
2153*4126Szf162725 	(void) memcpy(&wk.ik_keyrsc, &seq, 6);	/* only use 48-bit of seq */
2154*4126Szf162725 	(void) memcpy(wk.ik_keydata, key, key_len);
2155*4126Szf162725 
2156*4126Szf162725 	return (ioctl_set(link, WL_KEY, &wk, sizeof (wl_key_t)));
2157*4126Szf162725 }
2158*4126Szf162725 
2159*4126Szf162725 dladm_status_t
2160*4126Szf162725 dladm_wlan_wpa_set_mlme(const char *link, dladm_wlan_mlme_op_t op,
2161*4126Szf162725     dladm_wlan_reason_t reason, dladm_wlan_bssid_t *bssid)
2162*4126Szf162725 {
2163*4126Szf162725 	wl_mlme_t mlme;
2164*4126Szf162725 
2165*4126Szf162725 	(void) memset(&mlme, 0, sizeof (wl_mlme_t));
2166*4126Szf162725 	switch (op) {
2167*4126Szf162725 	case DLADM_WLAN_MLME_ASSOC:
2168*4126Szf162725 		mlme.im_op = IEEE80211_MLME_ASSOC;
2169*4126Szf162725 		break;
2170*4126Szf162725 	case DLADM_WLAN_MLME_DISASSOC:
2171*4126Szf162725 		mlme.im_op = IEEE80211_MLME_DISASSOC;
2172*4126Szf162725 		break;
2173*4126Szf162725 	default:
2174*4126Szf162725 		return (DLADM_STATUS_BADARG);
2175*4126Szf162725 	}
2176*4126Szf162725 	mlme.im_reason = reason;
2177*4126Szf162725 	if (bssid != NULL)
2178*4126Szf162725 		(void) memcpy(mlme.im_macaddr, bssid->wb_bytes,
2179*4126Szf162725 		    DLADM_WLAN_BSSID_LEN);
2180*4126Szf162725 
2181*4126Szf162725 	return (ioctl_set(link, WL_MLME, &mlme, sizeof (wl_mlme_t)));
2182*4126Szf162725 }
2183*4126Szf162725 
2184*4126Szf162725 /*
2185*4126Szf162725  * routines of create instance
2186*4126Szf162725  */
2187*4126Szf162725 static scf_propertygroup_t *
2188*4126Szf162725 add_property_group_to_instance(scf_handle_t *handle, scf_instance_t *instance,
2189*4126Szf162725     const char *pg_name, const char *pg_type)
2190*4126Szf162725 {
2191*4126Szf162725 	scf_propertygroup_t *pg;
2192*4126Szf162725 
2193*4126Szf162725 	pg = scf_pg_create(handle);
2194*4126Szf162725 	if (pg == NULL)
2195*4126Szf162725 		return (NULL);
2196*4126Szf162725 
2197*4126Szf162725 	if (scf_instance_add_pg(instance, pg_name, pg_type, 0, pg) != 0) {
2198*4126Szf162725 		scf_pg_destroy(pg);
2199*4126Szf162725 		return (NULL);
2200*4126Szf162725 	}
2201*4126Szf162725 
2202*4126Szf162725 	return (pg);
2203*4126Szf162725 }
2204*4126Szf162725 
2205*4126Szf162725 static int
2206*4126Szf162725 add_new_property(scf_handle_t *handle, const char *prop_name,
2207*4126Szf162725     scf_type_t type, const char *val, scf_transaction_t *tx)
2208*4126Szf162725 {
2209*4126Szf162725 	scf_value_t *value = NULL;
2210*4126Szf162725 	scf_transaction_entry_t *entry = NULL;
2211*4126Szf162725 
2212*4126Szf162725 	entry = scf_entry_create(handle);
2213*4126Szf162725 	if (entry == NULL)
2214*4126Szf162725 		goto out;
2215*4126Szf162725 
2216*4126Szf162725 	value = scf_value_create(handle);
2217*4126Szf162725 	if (value == NULL)
2218*4126Szf162725 		goto out;
2219*4126Szf162725 
2220*4126Szf162725 	if (scf_transaction_property_new(tx, entry, prop_name, type) != 0)
2221*4126Szf162725 		goto out;
2222*4126Szf162725 
2223*4126Szf162725 	if (scf_value_set_from_string(value, type, val) != 0)
2224*4126Szf162725 		goto out;
2225*4126Szf162725 
2226*4126Szf162725 	if (scf_entry_add_value(entry, value) != 0)
2227*4126Szf162725 		goto out;
2228*4126Szf162725 
2229*4126Szf162725 	return (DLADM_WLAN_SVC_SUCCESS);
2230*4126Szf162725 
2231*4126Szf162725 out:
2232*4126Szf162725 	if (value != NULL)
2233*4126Szf162725 		scf_value_destroy(value);
2234*4126Szf162725 	if (entry != NULL)
2235*4126Szf162725 		scf_entry_destroy(entry);
2236*4126Szf162725 
2237*4126Szf162725 	return (DLADM_WLAN_SVC_FAILURE);
2238*4126Szf162725 }
2239*4126Szf162725 
2240*4126Szf162725 /*
2241*4126Szf162725  * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
2242*4126Szf162725  */
2243*4126Szf162725 static int
2244*4126Szf162725 add_pg_method(scf_handle_t *handle, scf_instance_t *instance,
2245*4126Szf162725     const char *pg_name, const char *flags)
2246*4126Szf162725 {
2247*4126Szf162725 	int			rv, size;
2248*4126Szf162725 	int			status = DLADM_WLAN_SVC_FAILURE;
2249*4126Szf162725 	char			*command = NULL;
2250*4126Szf162725 	scf_transaction_t	*tran = NULL;
2251*4126Szf162725 	scf_propertygroup_t	*pg;
2252*4126Szf162725 
2253*4126Szf162725 	pg = add_property_group_to_instance(handle, instance,
2254*4126Szf162725 	    pg_name, SCF_GROUP_METHOD);
2255*4126Szf162725 	if (pg == NULL)
2256*4126Szf162725 		goto out;
2257*4126Szf162725 
2258*4126Szf162725 	tran = scf_transaction_create(handle);
2259*4126Szf162725 	if (tran == NULL)
2260*4126Szf162725 		goto out;
2261*4126Szf162725 
2262*4126Szf162725 	size = strlen(SVC_METHOD) + strlen("  ") + strlen(flags) + 1;
2263*4126Szf162725 	command = malloc(size);
2264*4126Szf162725 	if (command == NULL) {
2265*4126Szf162725 		status = DLADM_WLAN_SVC_APP_FAILURE;
2266*4126Szf162725 		goto out;
2267*4126Szf162725 	}
2268*4126Szf162725 	(void) snprintf(command, size, "%s %s", SVC_METHOD, flags);
2269*4126Szf162725 
2270*4126Szf162725 	do {
2271*4126Szf162725 		if (scf_transaction_start(tran, pg) != 0)
2272*4126Szf162725 			goto out;
2273*4126Szf162725 
2274*4126Szf162725 		if (add_new_property(handle, SCF_PROPERTY_EXEC,
2275*4126Szf162725 		    SCF_TYPE_ASTRING, command, tran) !=
2276*4126Szf162725 		    DLADM_WLAN_SVC_SUCCESS) {
2277*4126Szf162725 			goto out;
2278*4126Szf162725 		}
2279*4126Szf162725 
2280*4126Szf162725 		rv = scf_transaction_commit(tran);
2281*4126Szf162725 		switch (rv) {
2282*4126Szf162725 		case 1:
2283*4126Szf162725 			status = DLADM_WLAN_SVC_SUCCESS;
2284*4126Szf162725 			goto out;
2285*4126Szf162725 		case 0:
2286*4126Szf162725 			scf_transaction_destroy_children(tran);
2287*4126Szf162725 			if (scf_pg_update(pg) == -1) {
2288*4126Szf162725 				goto out;
2289*4126Szf162725 			}
2290*4126Szf162725 			break;
2291*4126Szf162725 		case -1:
2292*4126Szf162725 		default:
2293*4126Szf162725 			goto out;
2294*4126Szf162725 		}
2295*4126Szf162725 	} while (rv == 0);
2296*4126Szf162725 
2297*4126Szf162725 out:
2298*4126Szf162725 	if (tran != NULL) {
2299*4126Szf162725 		scf_transaction_destroy_children(tran);
2300*4126Szf162725 		scf_transaction_destroy(tran);
2301*4126Szf162725 	}
2302*4126Szf162725 
2303*4126Szf162725 	if (pg != NULL)
2304*4126Szf162725 		scf_pg_destroy(pg);
2305*4126Szf162725 
2306*4126Szf162725 	if (command != NULL)
2307*4126Szf162725 		free(command);
2308*4126Szf162725 
2309*4126Szf162725 	return (status);
2310*4126Szf162725 }
2311*4126Szf162725 
2312*4126Szf162725 static int
2313*4126Szf162725 do_create_instance(scf_handle_t *handle, scf_service_t *svc,
2314*4126Szf162725     const char *instance_name, const char *command)
2315*4126Szf162725 {
2316*4126Szf162725 	int status = DLADM_WLAN_SVC_FAILURE;
2317*4126Szf162725 	char *buf;
2318*4126Szf162725 	ssize_t max_fmri_len;
2319*4126Szf162725 	scf_instance_t *instance;
2320*4126Szf162725 
2321*4126Szf162725 	instance = scf_instance_create(handle);
2322*4126Szf162725 	if (instance == NULL)
2323*4126Szf162725 		goto out;
2324*4126Szf162725 
2325*4126Szf162725 	if (scf_service_add_instance(svc, instance_name, instance) != 0) {
2326*4126Szf162725 		if (scf_error() == SCF_ERROR_EXISTS)
2327*4126Szf162725 			/* Let the caller deal with the duplicate instance */
2328*4126Szf162725 			status = DLADM_WLAN_SVC_INSTANCE_EXISTS;
2329*4126Szf162725 		goto out;
2330*4126Szf162725 	}
2331*4126Szf162725 
2332*4126Szf162725 	if (add_pg_method(handle, instance, "start",
2333*4126Szf162725 	    command) != DLADM_WLAN_SVC_SUCCESS) {
2334*4126Szf162725 		goto out;
2335*4126Szf162725 	}
2336*4126Szf162725 
2337*4126Szf162725 	/* enabling the instance */
2338*4126Szf162725 	max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2339*4126Szf162725 	if ((buf = malloc(max_fmri_len + 1)) == NULL)
2340*4126Szf162725 		goto out;
2341*4126Szf162725 
2342*4126Szf162725 	if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
2343*4126Szf162725 		if ((smf_disable_instance(buf, 0) != 0) ||
2344*4126Szf162725 		    (smf_enable_instance(buf, SMF_TEMPORARY) != 0)) {
2345*4126Szf162725 			goto out;
2346*4126Szf162725 		}
2347*4126Szf162725 		status = DLADM_WLAN_SVC_SUCCESS;
2348*4126Szf162725 	}
2349*4126Szf162725 
2350*4126Szf162725 out:
2351*4126Szf162725 	if (instance != NULL)
2352*4126Szf162725 		scf_instance_destroy(instance);
2353*4126Szf162725 	return (status);
2354*4126Szf162725 }
2355*4126Szf162725 
2356*4126Szf162725 static int
2357*4126Szf162725 create_instance(const char *instance_name, const char *command)
2358*4126Szf162725 {
2359*4126Szf162725 	int status = DLADM_WLAN_SVC_FAILURE;
2360*4126Szf162725 	scf_service_t *svc = NULL;
2361*4126Szf162725 	scf_handle_t *handle = NULL;
2362*4126Szf162725 
2363*4126Szf162725 	handle = scf_handle_create(SCF_VERSION);
2364*4126Szf162725 	if (handle == NULL)
2365*4126Szf162725 		goto out;
2366*4126Szf162725 
2367*4126Szf162725 	if (scf_handle_bind(handle) == -1)
2368*4126Szf162725 		goto out;
2369*4126Szf162725 
2370*4126Szf162725 	if ((svc = scf_service_create(handle)) == NULL)
2371*4126Szf162725 		goto out;
2372*4126Szf162725 
2373*4126Szf162725 	if (scf_handle_decode_fmri(handle, SERVICE_NAME, NULL, svc,
2374*4126Szf162725 	    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
2375*4126Szf162725 		goto out;
2376*4126Szf162725 
2377*4126Szf162725 	status = do_create_instance(handle, svc, instance_name, command);
2378*4126Szf162725 
2379*4126Szf162725 out:
2380*4126Szf162725 	if (svc != NULL)
2381*4126Szf162725 		scf_service_destroy(svc);
2382*4126Szf162725 
2383*4126Szf162725 	if (handle != NULL) {
2384*4126Szf162725 		(void) scf_handle_unbind(handle);
2385*4126Szf162725 		scf_handle_destroy(handle);
2386*4126Szf162725 	}
2387*4126Szf162725 
2388*4126Szf162725 	return (status);
2389*4126Szf162725 }
2390*4126Szf162725 
2391*4126Szf162725 /*
2392*4126Szf162725  * routines of delete instance
2393*4126Szf162725  */
2394*4126Szf162725 #define	DEFAULT_TIMEOUT	60000000
2395*4126Szf162725 #define	INIT_WAIT_USECS	50000
2396*4126Szf162725 
2397*4126Szf162725 static void
2398*4126Szf162725 wait_until_disabled(scf_handle_t *handle, char *fmri)
2399*4126Szf162725 {
2400*4126Szf162725 	char		*state;
2401*4126Szf162725 	useconds_t	max;
2402*4126Szf162725 	useconds_t	usecs;
2403*4126Szf162725 	uint64_t	*cp = NULL;
2404*4126Szf162725 	scf_simple_prop_t *sp = NULL;
2405*4126Szf162725 
2406*4126Szf162725 	max = DEFAULT_TIMEOUT;
2407*4126Szf162725 
2408*4126Szf162725 	if (((sp = scf_simple_prop_get(handle, fmri, "stop",
2409*4126Szf162725 	    SCF_PROPERTY_TIMEOUT)) != NULL) &&
2410*4126Szf162725 	    ((cp = scf_simple_prop_next_count(sp)) != NULL) && (*cp != 0))
2411*4126Szf162725 		max = (*cp) * 1000000;	/* convert to usecs */
2412*4126Szf162725 
2413*4126Szf162725 	if (sp != NULL)
2414*4126Szf162725 		scf_simple_prop_free(sp);
2415*4126Szf162725 
2416*4126Szf162725 	for (usecs = INIT_WAIT_USECS; max > 0; max -= usecs) {
2417*4126Szf162725 		/* incremental wait */
2418*4126Szf162725 		usecs *= 2;
2419*4126Szf162725 		usecs = (usecs > max) ? max : usecs;
2420*4126Szf162725 
2421*4126Szf162725 		(void) usleep(usecs);
2422*4126Szf162725 
2423*4126Szf162725 		/* Check state after the wait */
2424*4126Szf162725 		if ((state = smf_get_state(fmri)) != NULL) {
2425*4126Szf162725 			if (strcmp(state, "disabled") == 0)
2426*4126Szf162725 				return;
2427*4126Szf162725 		}
2428*4126Szf162725 	}
2429*4126Szf162725 }
2430*4126Szf162725 
2431*4126Szf162725 static int
2432*4126Szf162725 delete_instance(const char *instance_name)
2433*4126Szf162725 {
2434*4126Szf162725 	int		status = DLADM_WLAN_SVC_FAILURE;
2435*4126Szf162725 	char		*buf;
2436*4126Szf162725 	ssize_t		max_fmri_len;
2437*4126Szf162725 	scf_scope_t	*scope = NULL;
2438*4126Szf162725 	scf_service_t	*svc = NULL;
2439*4126Szf162725 	scf_handle_t	*handle = NULL;
2440*4126Szf162725 	scf_instance_t	*instance;
2441*4126Szf162725 
2442*4126Szf162725 	handle = scf_handle_create(SCF_VERSION);
2443*4126Szf162725 	if (handle == NULL)
2444*4126Szf162725 		goto out;
2445*4126Szf162725 
2446*4126Szf162725 	if (scf_handle_bind(handle) == -1)
2447*4126Szf162725 		goto out;
2448*4126Szf162725 
2449*4126Szf162725 	if ((scope = scf_scope_create(handle)) == NULL)
2450*4126Szf162725 		goto out;
2451*4126Szf162725 
2452*4126Szf162725 	if ((svc = scf_service_create(handle)) == NULL)
2453*4126Szf162725 		goto out;
2454*4126Szf162725 
2455*4126Szf162725 	if (scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, scope) == -1)
2456*4126Szf162725 		goto out;
2457*4126Szf162725 
2458*4126Szf162725 	if (scf_scope_get_service(scope, SERVICE_NAME, svc) < 0)
2459*4126Szf162725 		goto out;
2460*4126Szf162725 
2461*4126Szf162725 	instance = scf_instance_create(handle);
2462*4126Szf162725 	if (instance == NULL)
2463*4126Szf162725 		goto out;
2464*4126Szf162725 
2465*4126Szf162725 	if (scf_service_get_instance(svc, instance_name, instance) != 0) {
2466*4126Szf162725 		scf_error_t scf_errnum = scf_error();
2467*4126Szf162725 
2468*4126Szf162725 		if (scf_errnum == SCF_ERROR_NOT_FOUND)
2469*4126Szf162725 			status = DLADM_WLAN_SVC_SUCCESS;
2470*4126Szf162725 
2471*4126Szf162725 		scf_instance_destroy(instance);
2472*4126Szf162725 		goto out;
2473*4126Szf162725 	}
2474*4126Szf162725 
2475*4126Szf162725 	max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
2476*4126Szf162725 	if ((buf = malloc(max_fmri_len + 1)) == NULL) {
2477*4126Szf162725 		scf_instance_destroy(instance);
2478*4126Szf162725 		goto out;
2479*4126Szf162725 	}
2480*4126Szf162725 
2481*4126Szf162725 	if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
2482*4126Szf162725 		char *state;
2483*4126Szf162725 
2484*4126Szf162725 		state = smf_get_state(buf);
2485*4126Szf162725 		if (state && (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
2486*4126Szf162725 		    strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)) {
2487*4126Szf162725 			if (smf_disable_instance(buf, 0) == 0) {
2488*4126Szf162725 				/*
2489*4126Szf162725 				 * Wait for some time till timeout to avoid
2490*4126Szf162725 				 * a race with scf_instance_delete() below.
2491*4126Szf162725 				 */
2492*4126Szf162725 				wait_until_disabled(handle, buf);
2493*4126Szf162725 			}
2494*4126Szf162725 		}
2495*4126Szf162725 	}
2496*4126Szf162725 
2497*4126Szf162725 	if (scf_instance_delete(instance) != 0) {
2498*4126Szf162725 		scf_instance_destroy(instance);
2499*4126Szf162725 		goto out;
2500*4126Szf162725 	}
2501*4126Szf162725 
2502*4126Szf162725 	scf_instance_destroy(instance);
2503*4126Szf162725 
2504*4126Szf162725 	status = DLADM_WLAN_SVC_SUCCESS;
2505*4126Szf162725 
2506*4126Szf162725 out:
2507*4126Szf162725 	if (svc != NULL)
2508*4126Szf162725 		scf_service_destroy(svc);
2509*4126Szf162725 
2510*4126Szf162725 	if (scope != NULL)
2511*4126Szf162725 		scf_scope_destroy(scope);
2512*4126Szf162725 
2513*4126Szf162725 	if (handle != NULL) {
2514*4126Szf162725 		(void) scf_handle_unbind(handle);
2515*4126Szf162725 		scf_handle_destroy(handle);
2516*4126Szf162725 	}
2517*4126Szf162725 
2518*4126Szf162725 	return (status);
2519*4126Szf162725 }
2520*4126Szf162725 
2521*4126Szf162725 /*
2522*4126Szf162725  * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
2523*4126Szf162725  */
2524*4126Szf162725 static int
2525*4126Szf162725 wpa_instance_create(const char *instance_name, void *key)
2526*4126Szf162725 {
2527*4126Szf162725 	int		status = DLADM_WLAN_SVC_FAILURE;
2528*4126Szf162725 	char		*command = NULL;
2529*4126Szf162725 	char		*wk_name = ((dladm_wlan_key_t *)key)->wk_name;
2530*4126Szf162725 	int		size;
2531*4126Szf162725 
2532*4126Szf162725 	size = strlen(instance_name) + strlen(" -i  -k ") + strlen(wk_name) + 1;
2533*4126Szf162725 	command = malloc(size);
2534*4126Szf162725 	if (command == NULL) {
2535*4126Szf162725 		status = DLADM_WLAN_SVC_APP_FAILURE;
2536*4126Szf162725 		goto out;
2537*4126Szf162725 	}
2538*4126Szf162725 	(void) snprintf(command, size, "-i %s -k %s", instance_name, wk_name);
2539*4126Szf162725 
2540*4126Szf162725 	status = create_instance(instance_name, command);
2541*4126Szf162725 	if (status == DLADM_WLAN_SVC_INSTANCE_EXISTS) {
2542*4126Szf162725 		/*
2543*4126Szf162725 		 * Delete the existing instance and create a new instance
2544*4126Szf162725 		 * with the supplied arguments.
2545*4126Szf162725 		 */
2546*4126Szf162725 		if ((status = delete_instance(instance_name)) ==
2547*4126Szf162725 		    DLADM_WLAN_SVC_SUCCESS) {
2548*4126Szf162725 			status = create_instance(instance_name, command);
2549*4126Szf162725 		}
2550*4126Szf162725 	}
2551*4126Szf162725 
2552*4126Szf162725 out:
2553*4126Szf162725 	if (command != NULL)
2554*4126Szf162725 		free(command);
2555*4126Szf162725 
2556*4126Szf162725 	return (status);
2557*4126Szf162725 }
2558*4126Szf162725 
2559*4126Szf162725 static int
2560*4126Szf162725 wpa_instance_delete(const char *instance_name)
2561*4126Szf162725 {
2562*4126Szf162725 	int status;
2563*4126Szf162725 
2564*4126Szf162725 	status = delete_instance(instance_name);
2565*4126Szf162725 
2566*4126Szf162725 	return (status);
2567*4126Szf162725 }
2568