16ba597c5SAnurag S. Maskey /*
26ba597c5SAnurag S. Maskey  * CDDL HEADER START
36ba597c5SAnurag S. Maskey  *
46ba597c5SAnurag S. Maskey  * The contents of this file are subject to the terms of the
56ba597c5SAnurag S. Maskey  * Common Development and Distribution License (the "License").
66ba597c5SAnurag S. Maskey  * You may not use this file except in compliance with the License.
76ba597c5SAnurag S. Maskey  *
86ba597c5SAnurag S. Maskey  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96ba597c5SAnurag S. Maskey  * or http://www.opensolaris.org/os/licensing.
106ba597c5SAnurag S. Maskey  * See the License for the specific language governing permissions
116ba597c5SAnurag S. Maskey  * and limitations under the License.
126ba597c5SAnurag S. Maskey  *
136ba597c5SAnurag S. Maskey  * When distributing Covered Code, include this CDDL HEADER in each
146ba597c5SAnurag S. Maskey  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156ba597c5SAnurag S. Maskey  * If applicable, add the following below this CDDL HEADER, with the
166ba597c5SAnurag S. Maskey  * fields enclosed by brackets "[]" replaced with your own identifying
176ba597c5SAnurag S. Maskey  * information: Portions Copyright [yyyy] [name of copyright owner]
186ba597c5SAnurag S. Maskey  *
196ba597c5SAnurag S. Maskey  * CDDL HEADER END
206ba597c5SAnurag S. Maskey  */
216ba597c5SAnurag S. Maskey 
226ba597c5SAnurag S. Maskey /*
2383ca9a11SAnurag S. Maskey  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
246ba597c5SAnurag S. Maskey  */
256ba597c5SAnurag S. Maskey 
266ba597c5SAnurag S. Maskey #include <assert.h>
276ba597c5SAnurag S. Maskey #include <ctype.h>
286ba597c5SAnurag S. Maskey #include <err.h>
296ba597c5SAnurag S. Maskey #include <errno.h>
306ba597c5SAnurag S. Maskey #include <execinfo.h>
316ba597c5SAnurag S. Maskey #include <kstat.h>
326ba597c5SAnurag S. Maskey #include <libdladm.h>
336ba597c5SAnurag S. Maskey #include <libdllink.h>
346ba597c5SAnurag S. Maskey #include <libdlstat.h>
356ba597c5SAnurag S. Maskey #include <libdlwlan.h>
366ba597c5SAnurag S. Maskey #include <libnwam.h>
376ba597c5SAnurag S. Maskey #include <limits.h>
386ba597c5SAnurag S. Maskey #include <pthread.h>
396ba597c5SAnurag S. Maskey #include <stdio.h>
406ba597c5SAnurag S. Maskey #include <stdlib.h>
416ba597c5SAnurag S. Maskey #include <string.h>
426ba597c5SAnurag S. Maskey #include <strings.h>
436ba597c5SAnurag S. Maskey #include <sys/stat.h>
446ba597c5SAnurag S. Maskey #include <sys/time.h>
456ba597c5SAnurag S. Maskey #include <sys/types.h>
466ba597c5SAnurag S. Maskey #include <unistd.h>
476ba597c5SAnurag S. Maskey #include <libdlpi.h>
486ba597c5SAnurag S. Maskey #include <ucontext.h>
496ba597c5SAnurag S. Maskey 
506ba597c5SAnurag S. Maskey #include "events.h"
516ba597c5SAnurag S. Maskey #include "llp.h"
526ba597c5SAnurag S. Maskey #include "objects.h"
536ba597c5SAnurag S. Maskey #include "ncp.h"
546ba597c5SAnurag S. Maskey #include "ncu.h"
556ba597c5SAnurag S. Maskey #include "known_wlans.h"
566ba597c5SAnurag S. Maskey #include "util.h"
576ba597c5SAnurag S. Maskey 
586ba597c5SAnurag S. Maskey /*
596ba597c5SAnurag S. Maskey  * ncu_phys.c - contains routines that are physical-link specific.
606ba597c5SAnurag S. Maskey  * Mostly WiFi code.
616ba597c5SAnurag S. Maskey  */
626ba597c5SAnurag S. Maskey 
636ba597c5SAnurag S. Maskey /*
646ba597c5SAnurag S. Maskey  * Get link state from kstats. Used to determine initial link state for
656ba597c5SAnurag S. Maskey  * cases where drivers do not support DL_NOTE_LINK_UP/DOWN.  If link
666ba597c5SAnurag S. Maskey  * state is LINK_STATE_UNKNOWN, we assume the link is up and the IP NCU
676ba597c5SAnurag S. Maskey  * timeout will cause us to move on to other links.
686ba597c5SAnurag S. Maskey  */
696ba597c5SAnurag S. Maskey link_state_t
nwamd_get_link_state(const char * name)706ba597c5SAnurag S. Maskey nwamd_get_link_state(const char *name)
716ba597c5SAnurag S. Maskey {
726ba597c5SAnurag S. Maskey 	kstat_ctl_t *kcp;
736ba597c5SAnurag S. Maskey 	kstat_t *ksp;
746ba597c5SAnurag S. Maskey 	char module[DLPI_LINKNAME_MAX];
756ba597c5SAnurag S. Maskey 	uint_t instance;
766ba597c5SAnurag S. Maskey 	link_state_t link_state = LINK_STATE_UNKNOWN;
776ba597c5SAnurag S. Maskey 
786ba597c5SAnurag S. Maskey 	if ((kcp = kstat_open()) == NULL)
796ba597c5SAnurag S. Maskey 		return (link_state);
806ba597c5SAnurag S. Maskey 
816ba597c5SAnurag S. Maskey 	if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
826ba597c5SAnurag S. Maskey 		goto out;
836ba597c5SAnurag S. Maskey 
846ba597c5SAnurag S. Maskey 	if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL) {
856ba597c5SAnurag S. Maskey 		/*
866ba597c5SAnurag S. Maskey 		 * The kstat query could fail if the underlying MAC
876ba597c5SAnurag S. Maskey 		 * driver was already detached.
886ba597c5SAnurag S. Maskey 		 */
896ba597c5SAnurag S. Maskey 		goto out;
906ba597c5SAnurag S. Maskey 	}
916ba597c5SAnurag S. Maskey 
926ba597c5SAnurag S. Maskey 	if (kstat_read(kcp, ksp, NULL) == -1)
936ba597c5SAnurag S. Maskey 		goto out;
946ba597c5SAnurag S. Maskey 
956ba597c5SAnurag S. Maskey 	(void) dladm_kstat_value(ksp, "link_state", KSTAT_DATA_UINT32,
966ba597c5SAnurag S. Maskey 	    &link_state);
976ba597c5SAnurag S. Maskey 
986ba597c5SAnurag S. Maskey out:
996ba597c5SAnurag S. Maskey 	(void) kstat_close(kcp);
1006ba597c5SAnurag S. Maskey 
1016ba597c5SAnurag S. Maskey 	return (link_state);
1026ba597c5SAnurag S. Maskey }
1036ba597c5SAnurag S. Maskey 
1046ba597c5SAnurag S. Maskey /*
1056ba597c5SAnurag S. Maskey  * Set/unset link propeties.  At present, these are MAC address, link MTU and
1066ba597c5SAnurag S. Maskey  * autopush modules.  We set MAC address last as setting it may cause a chip
1076ba597c5SAnurag S. Maskey  * reset which can prevent other device property setting succeeding.
1086ba597c5SAnurag S. Maskey  */
1096ba597c5SAnurag S. Maskey void
nwamd_set_unset_link_properties(nwamd_ncu_t * ncu,boolean_t set)1106ba597c5SAnurag S. Maskey nwamd_set_unset_link_properties(nwamd_ncu_t *ncu, boolean_t set)
1116ba597c5SAnurag S. Maskey {
112f6da83d4SAnurag S. Maskey 	dlpi_handle_t dh = ncu->ncu_link.nwamd_link_dhp;
113f6da83d4SAnurag S. Maskey 	char *addr = set ? ncu->ncu_link.nwamd_link_mac_addr : NULL;
114f6da83d4SAnurag S. Maskey 	uint64_t mtu = set ? ncu->ncu_link.nwamd_link_mtu : 0;
115f6da83d4SAnurag S. Maskey 	char **autopush = set ? ncu->ncu_link.nwamd_link_autopush : NULL;
116f6da83d4SAnurag S. Maskey 	uint_t num_autopush = set ? ncu->ncu_link.nwamd_link_num_autopush : 0;
1176ba597c5SAnurag S. Maskey 	uchar_t *hwaddr = NULL, curraddr[DLPI_PHYSADDR_MAX];
1186ba597c5SAnurag S. Maskey 	size_t hwaddrlen = DLPI_PHYSADDR_MAX;
1196ba597c5SAnurag S. Maskey 	int retval;
1206ba597c5SAnurag S. Maskey 	dladm_status_t status;
1216ba597c5SAnurag S. Maskey 	char mtustr[DLADM_PROP_VAL_MAX];
1226ba597c5SAnurag S. Maskey 	char *cp;
1236ba597c5SAnurag S. Maskey 	char errmsg[DLADM_STRSIZE];
1246ba597c5SAnurag S. Maskey 	uint_t cnt = 1;
1256ba597c5SAnurag S. Maskey 
1266ba597c5SAnurag S. Maskey 	/*
1276ba597c5SAnurag S. Maskey 	 * Set MTU here - either default value (if mtu == 0 indicating it has
1286ba597c5SAnurag S. Maskey 	 * not been set) or specified value.
1296ba597c5SAnurag S. Maskey 	 */
1306ba597c5SAnurag S. Maskey 	if (mtu == 0) {
1316ba597c5SAnurag S. Maskey 		cp = mtustr;
1326ba597c5SAnurag S. Maskey 		status = dladm_get_linkprop(dld_handle,
133f6da83d4SAnurag S. Maskey 		    ncu->ncu_link.nwamd_link_id, DLADM_PROP_VAL_DEFAULT, "mtu",
134f6da83d4SAnurag S. Maskey 		    &cp, &cnt);
1356ba597c5SAnurag S. Maskey 		if (status != DLADM_STATUS_OK) {
1366ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
1376ba597c5SAnurag S. Maskey 			    "dladm_get_linkprop failed: %s",
1386ba597c5SAnurag S. Maskey 			    dladm_status2str(status, errmsg));
1396ba597c5SAnurag S. Maskey 			return;
1406ba597c5SAnurag S. Maskey 		}
1416ba597c5SAnurag S. Maskey 	} else {
1426ba597c5SAnurag S. Maskey 		(void) snprintf(mtustr, DLADM_PROP_VAL_MAX, "%lld", mtu);
1436ba597c5SAnurag S. Maskey 	}
1446ba597c5SAnurag S. Maskey 
1456ba597c5SAnurag S. Maskey 	cp = mtustr;
1466ba597c5SAnurag S. Maskey 
1476ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_set_unset_link_properties: setting MTU of %s "
1486ba597c5SAnurag S. Maskey 	    "for link %s", mtustr, ncu->ncu_name);
149f6da83d4SAnurag S. Maskey 	status = dladm_set_linkprop(dld_handle, ncu->ncu_link.nwamd_link_id,
150f6da83d4SAnurag S. Maskey 	    "mtu", &cp, 1, DLADM_OPT_ACTIVE);
1516ba597c5SAnurag S. Maskey 	if (status != DLADM_STATUS_OK) {
1526ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
1536ba597c5SAnurag S. Maskey 		    "dladm_set_linkprop failed: %s",
1546ba597c5SAnurag S. Maskey 		    dladm_status2str(status, errmsg));
1556ba597c5SAnurag S. Maskey 	}
1566ba597c5SAnurag S. Maskey 
1576ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_set_unset_link_properties: setting %d "
1586ba597c5SAnurag S. Maskey 	    "autopush module for link %s", num_autopush, ncu->ncu_name);
159f6da83d4SAnurag S. Maskey 	status = dladm_set_linkprop(dld_handle, ncu->ncu_link.nwamd_link_id,
160f6da83d4SAnurag S. Maskey 	    "autopush", autopush, num_autopush, DLADM_OPT_ACTIVE);
1616ba597c5SAnurag S. Maskey 	if (status != DLADM_STATUS_OK) {
1626ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
1636ba597c5SAnurag S. Maskey 		    "dladm_set_linkprop failed for autopush property: %s",
1646ba597c5SAnurag S. Maskey 		    dladm_status2str(status, errmsg));
1656ba597c5SAnurag S. Maskey 	}
1666ba597c5SAnurag S. Maskey 
1676ba597c5SAnurag S. Maskey 	/*
1686ba597c5SAnurag S. Maskey 	 * Set physical address - either factory (if link_mac_addr is NULL
1696ba597c5SAnurag S. Maskey 	 * or we are unsetting properties) or specified MAC address string.
1706ba597c5SAnurag S. Maskey 	 */
1716ba597c5SAnurag S. Maskey 	if (addr == NULL) {
1726ba597c5SAnurag S. Maskey 		if ((hwaddr = calloc(1, DLPI_PHYSADDR_MAX)) == NULL) {
1736ba597c5SAnurag S. Maskey 			nlog(LOG_ERR,
1746ba597c5SAnurag S. Maskey 			    "nwamd_set_unset_link_properties: malloc() failed");
1756ba597c5SAnurag S. Maskey 			return;
1766ba597c5SAnurag S. Maskey 		}
1776ba597c5SAnurag S. Maskey 		if ((retval = dlpi_get_physaddr(dh, DL_FACT_PHYS_ADDR,
1786ba597c5SAnurag S. Maskey 		    hwaddr, &hwaddrlen)) != DLPI_SUCCESS) {
1796ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
1806ba597c5SAnurag S. Maskey 			    "could not get physical address for %s: %s",
1816ba597c5SAnurag S. Maskey 			    ncu->ncu_name, dlpi_strerror(retval));
1826ba597c5SAnurag S. Maskey 			free(hwaddr);
1836ba597c5SAnurag S. Maskey 			return;
1846ba597c5SAnurag S. Maskey 		}
1856ba597c5SAnurag S. Maskey 	} else {
1866ba597c5SAnurag S. Maskey 		int addrlen = hwaddrlen;
1876ba597c5SAnurag S. Maskey 		if ((hwaddr = _link_aton(addr, &addrlen)) == NULL) {
1886ba597c5SAnurag S. Maskey 			if (addrlen == -1) {
1896ba597c5SAnurag S. Maskey 				nlog(LOG_ERR,
1906ba597c5SAnurag S. Maskey 				    "nwamd_set_unset_link_properties: "
1916ba597c5SAnurag S. Maskey 				    "%s: bad address for %s",
1926ba597c5SAnurag S. Maskey 				    addr, ncu->ncu_name);
1936ba597c5SAnurag S. Maskey 				return;
1946ba597c5SAnurag S. Maskey 			} else {
1956ba597c5SAnurag S. Maskey 				nlog(LOG_ERR, "nwamd_set_unset_link_properties:"
1966ba597c5SAnurag S. Maskey 				    " malloc() failed");
1976ba597c5SAnurag S. Maskey 				return;
1986ba597c5SAnurag S. Maskey 			}
1996ba597c5SAnurag S. Maskey 		}
2006ba597c5SAnurag S. Maskey 		hwaddrlen = addrlen;
2016ba597c5SAnurag S. Maskey 	}
2026ba597c5SAnurag S. Maskey 
2036ba597c5SAnurag S. Maskey 	/*
2046ba597c5SAnurag S. Maskey 	 * Only set physical address if desired address differs from current -
2056ba597c5SAnurag S. Maskey 	 * this avoids unnecessary chip resets for some drivers.
2066ba597c5SAnurag S. Maskey 	 */
2076ba597c5SAnurag S. Maskey 	retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, curraddr,
2086ba597c5SAnurag S. Maskey 	    &hwaddrlen);
2096ba597c5SAnurag S. Maskey 	if (retval != DLPI_SUCCESS || bcmp(curraddr, hwaddr, hwaddrlen) != 0) {
2106ba597c5SAnurag S. Maskey 		retval = dlpi_set_physaddr(dh, DL_CURR_PHYS_ADDR, hwaddr,
2116ba597c5SAnurag S. Maskey 		    hwaddrlen);
2126ba597c5SAnurag S. Maskey 		if (retval != DLPI_SUCCESS) {
2136ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "nwamd_set_unset_link_properties:"
2146ba597c5SAnurag S. Maskey 			    "failed setting mac address on %s: %s",
2156ba597c5SAnurag S. Maskey 			    ncu->ncu_name, dlpi_strerror(retval));
2166ba597c5SAnurag S. Maskey 		}
2176ba597c5SAnurag S. Maskey 	}
2186ba597c5SAnurag S. Maskey 	free(hwaddr);
2196ba597c5SAnurag S. Maskey }
2206ba597c5SAnurag S. Maskey 
2216ba597c5SAnurag S. Maskey #define	WLAN_ENC(sec)						\
2226ba597c5SAnurag S. Maskey 	((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : 		\
2236ba597c5SAnurag S. Maskey 	(sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none")))
2246ba597c5SAnurag S. Maskey 
2256ba597c5SAnurag S. Maskey #define	NEED_ENC(sec)						\
2266ba597c5SAnurag S. Maskey 	(sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
2276ba597c5SAnurag S. Maskey 
2286ba597c5SAnurag S. Maskey #define	WIRELESS_LAN_INIT_COUNT	8
2296ba597c5SAnurag S. Maskey 
2306ba597c5SAnurag S. Maskey /*
2316ba597c5SAnurag S. Maskey  * The variable wireless_scan_level specifies the signal level
2326ba597c5SAnurag S. Maskey  * that we will initiate connections to previously-visited APs
2336ba597c5SAnurag S. Maskey  * at when we are in the connected state.
2346ba597c5SAnurag S. Maskey  */
2356ba597c5SAnurag S. Maskey dladm_wlan_strength_t wireless_scan_level = DLADM_WLAN_STRENGTH_WEAK;
2366ba597c5SAnurag S. Maskey 
2376ba597c5SAnurag S. Maskey /*
2386ba597c5SAnurag S. Maskey  * The variable wireless_scan_interval specifies how often the periodic
2396ba597c5SAnurag S. Maskey  * scan occurs.
2406ba597c5SAnurag S. Maskey  */
2416ba597c5SAnurag S. Maskey uint64_t wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT;
2426ba597c5SAnurag S. Maskey 
2436ba597c5SAnurag S. Maskey /*
2446ba597c5SAnurag S. Maskey  * The variable wireless_autoconf specifies if we use dladm_wlan_autoconf()
2456ba597c5SAnurag S. Maskey  * to connect.
2466ba597c5SAnurag S. Maskey  */
2476ba597c5SAnurag S. Maskey boolean_t wireless_autoconf = B_FALSE;
2486ba597c5SAnurag S. Maskey 
2496ba597c5SAnurag S. Maskey /*
2506ba597c5SAnurag S. Maskey  * The variable wireless_strict_bssid specifies if we only connect
2516ba597c5SAnurag S. Maskey  * to WLANs with BSSIDs that we previously connected to.
2526ba597c5SAnurag S. Maskey  */
2536ba597c5SAnurag S. Maskey boolean_t wireless_strict_bssid = B_FALSE;
2546ba597c5SAnurag S. Maskey 
2556ba597c5SAnurag S. Maskey /*
2566ba597c5SAnurag S. Maskey  * We need to ensure scan or connect threads do not run concurrently
2576ba597c5SAnurag S. Maskey  * on any links - otherwise we get radio interference.  Acquire this
2586ba597c5SAnurag S. Maskey  * lock on entering scan/connect threads to prevent this.
2596ba597c5SAnurag S. Maskey  */
2606ba597c5SAnurag S. Maskey pthread_mutex_t wireless_mutex = PTHREAD_MUTEX_INITIALIZER;
2616ba597c5SAnurag S. Maskey 
2626ba597c5SAnurag S. Maskey static void
scanconnect_entry(void)2636ba597c5SAnurag S. Maskey scanconnect_entry(void)
2646ba597c5SAnurag S. Maskey {
2656ba597c5SAnurag S. Maskey 	(void) pthread_mutex_lock(&wireless_mutex);
2666ba597c5SAnurag S. Maskey }
2676ba597c5SAnurag S. Maskey 
2686ba597c5SAnurag S. Maskey static void
scanconnect_exit(void)2696ba597c5SAnurag S. Maskey scanconnect_exit(void)
2706ba597c5SAnurag S. Maskey {
2716ba597c5SAnurag S. Maskey 	(void) pthread_mutex_unlock(&wireless_mutex);
2726ba597c5SAnurag S. Maskey }
2736ba597c5SAnurag S. Maskey 
2746ba597c5SAnurag S. Maskey /*
2756ba597c5SAnurag S. Maskey  * Below are functions used to handle storage/retrieval of keys
2766ba597c5SAnurag S. Maskey  * for a given WLAN. The keys are stored/retrieved using dladm_set_secobj()
2776ba597c5SAnurag S. Maskey  * and dladm_get_secobj().
2786ba597c5SAnurag S. Maskey  */
2796ba597c5SAnurag S. Maskey 
2806ba597c5SAnurag S. Maskey /*
2816ba597c5SAnurag S. Maskey  * Convert key hexascii string to raw secobj value. This
2826ba597c5SAnurag S. Maskey  * code is very similar to convert_secobj() in dladm.c, it would
2836ba597c5SAnurag S. Maskey  * be good to have a libdladm function to convert values.
2846ba597c5SAnurag S. Maskey  */
2856ba597c5SAnurag S. Maskey static int
key_string_to_secobj_value(char * buf,uint8_t * obj_val,uint_t * obj_lenp,dladm_secobj_class_t class)2866ba597c5SAnurag S. Maskey key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
2876ba597c5SAnurag S. Maskey     dladm_secobj_class_t class)
2886ba597c5SAnurag S. Maskey {
2896ba597c5SAnurag S. Maskey 	size_t buf_len = strlen(buf);
2906ba597c5SAnurag S. Maskey 
2916ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "before: key_string_to_secobj_value: buf_len = %d",
2926ba597c5SAnurag S. Maskey 	    buf_len);
2936ba597c5SAnurag S. Maskey 	if (buf_len == 0) {
2946ba597c5SAnurag S. Maskey 		/* length zero means "delete" */
2956ba597c5SAnurag S. Maskey 		return (0);
2966ba597c5SAnurag S. Maskey 	}
2976ba597c5SAnurag S. Maskey 
2986ba597c5SAnurag S. Maskey 	if (buf[buf_len - 1] == '\n')
2996ba597c5SAnurag S. Maskey 		buf[--buf_len] = '\0';
3006ba597c5SAnurag S. Maskey 
3016ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "after: key_string_to_secobj_value: buf_len = %d",
3026ba597c5SAnurag S. Maskey 	    buf_len);
3036ba597c5SAnurag S. Maskey 
3046ba597c5SAnurag S. Maskey 	if (class == DLADM_SECOBJ_CLASS_WPA) {
3056ba597c5SAnurag S. Maskey 		/*
3066ba597c5SAnurag S. Maskey 		 * Per IEEE802.11i spec, the Pre-shared key (PSK) length should
3076ba597c5SAnurag S. Maskey 		 * be between 8 and 63.
3086ba597c5SAnurag S. Maskey 		 */
3096ba597c5SAnurag S. Maskey 		if (buf_len < 8 || buf_len > 63) {
3106ba597c5SAnurag S. Maskey 			nlog(LOG_ERR,
3116ba597c5SAnurag S. Maskey 			    "key_string_to_secobj_value:"
3126ba597c5SAnurag S. Maskey 			    " invalid WPA key length: buf_len = %d", buf_len);
3136ba597c5SAnurag S. Maskey 			return (-1);
3146ba597c5SAnurag S. Maskey 		}
3156ba597c5SAnurag S. Maskey 		(void) memcpy(obj_val, buf, (uint_t)buf_len);
3166ba597c5SAnurag S. Maskey 		*obj_lenp = buf_len;
3176ba597c5SAnurag S. Maskey 		return (0);
3186ba597c5SAnurag S. Maskey 	}
3196ba597c5SAnurag S. Maskey 
3206ba597c5SAnurag S. Maskey 	switch (buf_len) {
3216ba597c5SAnurag S. Maskey 	case 5:		/* ASCII key sizes */
3226ba597c5SAnurag S. Maskey 	case 13:
3236ba597c5SAnurag S. Maskey 		(void) memcpy(obj_val, buf, (uint_t)buf_len);
3246ba597c5SAnurag S. Maskey 		*obj_lenp = (uint_t)buf_len;
3256ba597c5SAnurag S. Maskey 		break;
3266ba597c5SAnurag S. Maskey 	case 10:
3276ba597c5SAnurag S. Maskey 	case 26:	/* Hex key sizes, not preceded by 0x */
3286ba597c5SAnurag S. Maskey 		if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp)
3296ba597c5SAnurag S. Maskey 		    != 0) {
3306ba597c5SAnurag S. Maskey 			nlog(LOG_ERR,
3316ba597c5SAnurag S. Maskey 			    "key_string_to_secobj_value: invalid WEP key");
3326ba597c5SAnurag S. Maskey 			return (-1);
3336ba597c5SAnurag S. Maskey 		}
3346ba597c5SAnurag S. Maskey 		break;
3356ba597c5SAnurag S. Maskey 	case 12:
3366ba597c5SAnurag S. Maskey 	case 28:	/* Hex key sizes, preceded by 0x */
3376ba597c5SAnurag S. Maskey 		if (strncmp(buf, "0x", 2) != 0 ||
3386ba597c5SAnurag S. Maskey 		    hexascii_to_octet(buf + 2, (uint_t)buf_len - 2, obj_val,
3396ba597c5SAnurag S. Maskey 		    obj_lenp) != 0) {
3406ba597c5SAnurag S. Maskey 			nlog(LOG_ERR,
3416ba597c5SAnurag S. Maskey 			    "key_string_to_secobj_value: invalid WEP key");
3426ba597c5SAnurag S. Maskey 			return (-1);
3436ba597c5SAnurag S. Maskey 		}
3446ba597c5SAnurag S. Maskey 		break;
3456ba597c5SAnurag S. Maskey 	default:
3466ba597c5SAnurag S. Maskey 		syslog(LOG_ERR,
3476ba597c5SAnurag S. Maskey 		    "key_string_to_secobj_value: invalid WEP key length");
3486ba597c5SAnurag S. Maskey 		return (-1);
3496ba597c5SAnurag S. Maskey 	}
3506ba597c5SAnurag S. Maskey 	return (0);
3516ba597c5SAnurag S. Maskey }
3526ba597c5SAnurag S. Maskey 
353c1976b83Senricop /*
354c1976b83Senricop  * Callback used on each known WLAN:
355c1976b83Senricop  * return 1 if a secobj, linked with an existing kwown wlan, has the same name
356c1976b83Senricop  * of the secobj that is being created.
357c1976b83Senricop  */
358c1976b83Senricop 
359c1976b83Senricop static int
find_keyname_cb(nwam_known_wlan_handle_t kwh,void * new_keyname)360c1976b83Senricop find_keyname_cb(nwam_known_wlan_handle_t kwh, void *new_keyname)
361c1976b83Senricop {
362c1976b83Senricop 	nwam_error_t err;
363c1976b83Senricop 	nwam_value_t old_key;
364c1976b83Senricop 
365c1976b83Senricop 	char **old_keyname;
366c1976b83Senricop 	uint_t num_old_keyname, i;
367c1976b83Senricop 
368c1976b83Senricop 	if ((err = nwam_known_wlan_get_prop_value(kwh,
369c1976b83Senricop 	    NWAM_KNOWN_WLAN_PROP_KEYNAME, &old_key)) != NWAM_SUCCESS) {
370c1976b83Senricop 		nlog(LOG_ERR, "find_keyname_cb: nwam_known_wlan_get_prop: %s",
371c1976b83Senricop 		    nwam_strerror(err));
372c1976b83Senricop 		return (0);
373c1976b83Senricop 	}
374c1976b83Senricop 	if ((err = nwam_value_get_string_array(old_key, &old_keyname,
375c1976b83Senricop 	    &num_old_keyname))
376c1976b83Senricop 	    != NWAM_SUCCESS) {
377c1976b83Senricop 		nlog(LOG_ERR, "find_keyname_cb: nwam_value_get_string: %s",
378c1976b83Senricop 		    nwam_strerror(err));
379c1976b83Senricop 		nwam_value_free(old_key);
380c1976b83Senricop 		return (0);
381c1976b83Senricop 	}
382c1976b83Senricop 	nwam_value_free(old_key);
383c1976b83Senricop 	for (i = 0; i < num_old_keyname; i++) {
384c1976b83Senricop 		if (strcmp(old_keyname[i], (const char *)new_keyname) == 0)
385c1976b83Senricop 			/* Found matching keyname so terminate walk */
386c1976b83Senricop 			return (1);
387c1976b83Senricop 	}
388c1976b83Senricop 	return (0);
389c1976b83Senricop }
390c1976b83Senricop 
3916ba597c5SAnurag S. Maskey /*
3926ba597c5SAnurag S. Maskey  * Print the key name format into the appropriate field, then convert any ":"
3936ba597c5SAnurag S. Maskey  * characters to ".", as ":[1-4]" is the slot indicator, which otherwise
3946ba597c5SAnurag S. Maskey  * would trip us up.  Invalid characters for secobj names are ignored.
3956ba597c5SAnurag S. Maskey  * The fourth parameter is expected to be of size DLADM_SECOBJ_NAME_MAX.
3966ba597c5SAnurag S. Maskey  *
3976ba597c5SAnurag S. Maskey  * (Note that much of the system uses DLADM_WLAN_MAX_KEYNAME_LEN, which is 64
3986ba597c5SAnurag S. Maskey  * rather than 32, but that dladm_get_secobj will fail if a length greater than
3996ba597c5SAnurag S. Maskey  * DLD_SECOBJ_NAME_MAX is seen, and that's 32.  This is all horribly broken.)
4006ba597c5SAnurag S. Maskey  */
4016ba597c5SAnurag S. Maskey void
nwamd_set_key_name(const char * essid,const char * bssid,char * name,size_t nsz)4026ba597c5SAnurag S. Maskey nwamd_set_key_name(const char *essid, const char *bssid, char *name, size_t nsz)
4036ba597c5SAnurag S. Maskey {
4046ba597c5SAnurag S. Maskey 	int i, j;
4056ba597c5SAnurag S. Maskey 	char secobj_name[DLADM_WLAN_MAX_KEYNAME_LEN];
4066ba597c5SAnurag S. Maskey 
4076ba597c5SAnurag S. Maskey 	/* create a concatenated string with essid and bssid */
4086ba597c5SAnurag S. Maskey 	if (bssid == NULL || bssid[0] == '\0') {
4096ba597c5SAnurag S. Maskey 		(void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s",
4106ba597c5SAnurag S. Maskey 		    essid);
4116ba597c5SAnurag S. Maskey 	} else {
4126ba597c5SAnurag S. Maskey 		(void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s-%s",
4136ba597c5SAnurag S. Maskey 		    essid, bssid);
4146ba597c5SAnurag S. Maskey 	}
4156ba597c5SAnurag S. Maskey 
4166ba597c5SAnurag S. Maskey 	/* copy only valid chars to the return string, terminating with \0 */
4176ba597c5SAnurag S. Maskey 	i = 0; /* index into secobj_name */
4186ba597c5SAnurag S. Maskey 	j = 0; /* index into name */
4196ba597c5SAnurag S. Maskey 	while (secobj_name[i] != '\0') {
4206ba597c5SAnurag S. Maskey 		if (j == nsz - 1)
4216ba597c5SAnurag S. Maskey 			break;
4226ba597c5SAnurag S. Maskey 
4236ba597c5SAnurag S. Maskey 		if (secobj_name[i] == ':') {
4246ba597c5SAnurag S. Maskey 			name[j] = '.';
4256ba597c5SAnurag S. Maskey 			j++;
4266ba597c5SAnurag S. Maskey 		} else if (isalnum(secobj_name[i]) ||
4276ba597c5SAnurag S. Maskey 		    secobj_name[i] == '.' || secobj_name[i] == '-' ||
4286ba597c5SAnurag S. Maskey 		    secobj_name[i] == '_') {
4296ba597c5SAnurag S. Maskey 			name[j] = secobj_name[i];
4306ba597c5SAnurag S. Maskey 			j++;
4316ba597c5SAnurag S. Maskey 		}
4326ba597c5SAnurag S. Maskey 		i++;
4336ba597c5SAnurag S. Maskey 	}
4346ba597c5SAnurag S. Maskey 	name[j] = '\0';
4356ba597c5SAnurag S. Maskey }
4366ba597c5SAnurag S. Maskey 
4376ba597c5SAnurag S. Maskey nwam_error_t
nwamd_wlan_set_key(const char * linkname,const char * essid,const char * bssid,uint32_t security_mode,uint_t keyslot,char * raw_key)4386ba597c5SAnurag S. Maskey nwamd_wlan_set_key(const char *linkname, const char *essid, const char *bssid,
4396ba597c5SAnurag S. Maskey     uint32_t security_mode, uint_t keyslot, char *raw_key)
4406ba597c5SAnurag S. Maskey {
4416ba597c5SAnurag S. Maskey 	nwamd_object_t ncu_obj;
4426ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu;
4436ba597c5SAnurag S. Maskey 	nwamd_link_t *link;
444c1976b83Senricop 	int ret = 0;
4456ba597c5SAnurag S. Maskey 	uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
4466ba597c5SAnurag S. Maskey 	uint_t obj_len = sizeof (obj_val);
4476ba597c5SAnurag S. Maskey 	char obj_name[DLADM_SECOBJ_NAME_MAX];
448c1976b83Senricop 	char obj_tempname[DLADM_SECOBJ_NAME_MAX];
4496ba597c5SAnurag S. Maskey 	dladm_status_t status;
4506ba597c5SAnurag S. Maskey 	char errmsg[DLADM_STRSIZE];
4516ba597c5SAnurag S. Maskey 	dladm_secobj_class_t class;
4526ba597c5SAnurag S. Maskey 
4536ba597c5SAnurag S. Maskey 	if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
4546ba597c5SAnurag S. Maskey 	    == NULL) {
4556ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_set_key: could not find object  "
4566ba597c5SAnurag S. Maskey 		    "for link %s", linkname);
4576ba597c5SAnurag S. Maskey 		return (NWAM_ENTITY_NOT_FOUND);
4586ba597c5SAnurag S. Maskey 	}
4596ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
460f6da83d4SAnurag S. Maskey 	link = &ncu->ncu_link;
4616ba597c5SAnurag S. Maskey 
4626ba597c5SAnurag S. Maskey 	class = (security_mode == DLADM_WLAN_SECMODE_WEP ?
4636ba597c5SAnurag S. Maskey 	    DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
4646ba597c5SAnurag S. Maskey 	if (key_string_to_secobj_value(raw_key, obj_val, &obj_len,
4656ba597c5SAnurag S. Maskey 	    class) != 0) {
4666ba597c5SAnurag S. Maskey 		/* above function logs internally on failure */
4676ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
4686ba597c5SAnurag S. Maskey 		return (NWAM_ERROR_INTERNAL);
4696ba597c5SAnurag S. Maskey 	}
4706ba597c5SAnurag S. Maskey 
471c1976b83Senricop 	nlog(LOG_DEBUG, "nwamd_wlan_set_key: running for link %s", linkname);
472c1976b83Senricop 	/*
473c1976b83Senricop 	 * Name key object for this WLAN so it can be later retrieved.
474c1976b83Senricop 	 * (bssid is appended if an object, with the same keyname,
475c1976b83Senricop 	 * already exists and is associated to a known wlan)
476c1976b83Senricop 	 */
477c1976b83Senricop 	nwamd_set_key_name(essid, NULL, obj_tempname, sizeof (obj_tempname));
478c1976b83Senricop 	(void) nwam_walk_known_wlans(find_keyname_cb, obj_tempname, 0, &ret);
479c1976b83Senricop 	/*
480c1976b83Senricop 	 * We also check if the keyval is the same. The user might want
481c1976b83Senricop 	 * to use the same key for more APs with the same ESSID.
482c1976b83Senricop 	 * This can result in a known wlan with multiple BSSIDs
483c1976b83Senricop 	 */
484c1976b83Senricop 	if (ret == 1) {
485c1976b83Senricop 		dladm_wlan_key_t *old_secobj = nwamd_wlan_get_key_named(
486c1976b83Senricop 		    obj_tempname, security_mode);
487c1976b83Senricop 		nlog(LOG_DEBUG, "found existing obj_name %s", obj_tempname);
488c1976b83Senricop 		ret = memcmp((*old_secobj).wk_val, obj_val, obj_len);
489c1976b83Senricop 		nwamd_set_key_name(essid, ret ? bssid : NULL, obj_name,
490c1976b83Senricop 		    sizeof (obj_name));
491c1976b83Senricop 		free(old_secobj);
492c1976b83Senricop 	} else {
493c1976b83Senricop 		nwamd_set_key_name(essid, NULL, obj_name,
494c1976b83Senricop 		    sizeof (obj_name));
495c1976b83Senricop 	}
496c1976b83Senricop 	nlog(LOG_DEBUG, "store_key: obj_name is %s", obj_name);
497c1976b83Senricop 
498c1976b83Senricop 	/*
499c1976b83Senricop 	 * We have validated the new key, so remove the old one.
500c1976b83Senricop 	 * This will actually delete the keyobj only if the user had set
501c1976b83Senricop 	 * a wrong key and is replacing it with a new one for the same AP.
502c1976b83Senricop 	 */
5036ba597c5SAnurag S. Maskey 	status = dladm_unset_secobj(dld_handle, obj_name,
5046ba597c5SAnurag S. Maskey 	    DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
5056ba597c5SAnurag S. Maskey 	if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND) {
5066ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "store_key: could not remove old secure object "
5076ba597c5SAnurag S. Maskey 		    "'%s' for key: %s", obj_name,
5086ba597c5SAnurag S. Maskey 		    dladm_status2str(status, errmsg));
5096ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
5106ba597c5SAnurag S. Maskey 		return (NWAM_ERROR_INTERNAL);
5116ba597c5SAnurag S. Maskey 	}
5126ba597c5SAnurag S. Maskey 
5136ba597c5SAnurag S. Maskey 	/* if we're just deleting the key, then we're done */
5146ba597c5SAnurag S. Maskey 	if (raw_key[0] == '\0') {
5156ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
5166ba597c5SAnurag S. Maskey 		return (NWAM_SUCCESS);
5176ba597c5SAnurag S. Maskey 	}
5186ba597c5SAnurag S. Maskey 
5196ba597c5SAnurag S. Maskey 	status = dladm_set_secobj(dld_handle, obj_name, class,
5206ba597c5SAnurag S. Maskey 	    obj_val, obj_len,
5216ba597c5SAnurag S. Maskey 	    DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE);
5226ba597c5SAnurag S. Maskey 	if (status != DLADM_STATUS_OK) {
5236ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "store_key: could not create secure object "
5246ba597c5SAnurag S. Maskey 		    "'%s' for key: %s", obj_name,
5256ba597c5SAnurag S. Maskey 		    dladm_status2str(status, errmsg));
5266ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
5276ba597c5SAnurag S. Maskey 		return (NWAM_ERROR_INTERNAL);
5286ba597c5SAnurag S. Maskey 	}
5296ba597c5SAnurag S. Maskey 	link->nwamd_link_wifi_key = nwamd_wlan_get_key_named(obj_name,
5306ba597c5SAnurag S. Maskey 	    security_mode);
5316ba597c5SAnurag S. Maskey 	(void) strlcpy(link->nwamd_link_wifi_keyname, obj_name,
5326ba597c5SAnurag S. Maskey 	    sizeof (link->nwamd_link_wifi_keyname));
5336ba597c5SAnurag S. Maskey 	link->nwamd_link_wifi_security_mode = security_mode;
5346ba597c5SAnurag S. Maskey 	if (security_mode == DLADM_WLAN_SECMODE_WEP) {
5356ba597c5SAnurag S. Maskey 		link->nwamd_link_wifi_key->wk_idx =
5366ba597c5SAnurag S. Maskey 		    (keyslot >= 1 && keyslot <= 4) ? keyslot : 1;
5376ba597c5SAnurag S. Maskey 	}
5386ba597c5SAnurag S. Maskey 
5396ba597c5SAnurag S. Maskey 	/* If link NCU is offline* or online, (re)connect. */
5406ba597c5SAnurag S. Maskey 	switch (ncu_obj->nwamd_object_state) {
5416ba597c5SAnurag S. Maskey 	case NWAM_STATE_ONLINE:
5426ba597c5SAnurag S. Maskey 		/* if changing the key of the connected WLAN, reconnect */
5436ba597c5SAnurag S. Maskey 		if (strcmp(essid, link->nwamd_link_wifi_essid) == 0)
5446ba597c5SAnurag S. Maskey 			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
5456ba597c5SAnurag S. Maskey 			    ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE,
5466ba597c5SAnurag S. Maskey 			    NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
5476ba597c5SAnurag S. Maskey 		break;
5486ba597c5SAnurag S. Maskey 	case NWAM_STATE_OFFLINE_TO_ONLINE:
5496ba597c5SAnurag S. Maskey 		/* if we are waiting for the key, connect */
5506ba597c5SAnurag S. Maskey 		if (ncu_obj->nwamd_object_aux_state ==
5516ba597c5SAnurag S. Maskey 		    NWAM_AUX_STATE_LINK_WIFI_NEED_KEY)
5526ba597c5SAnurag S. Maskey 			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
5536ba597c5SAnurag S. Maskey 			    ncu_obj->nwamd_object_name,
5546ba597c5SAnurag S. Maskey 			    NWAM_STATE_OFFLINE_TO_ONLINE,
5556ba597c5SAnurag S. Maskey 			    NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
5566ba597c5SAnurag S. Maskey 		break;
5576ba597c5SAnurag S. Maskey 	default:
5586ba597c5SAnurag S. Maskey 		break;
5596ba597c5SAnurag S. Maskey 	}
5606ba597c5SAnurag S. Maskey 	nwamd_object_release(ncu_obj);
5616ba597c5SAnurag S. Maskey 
5626ba597c5SAnurag S. Maskey 	return (NWAM_SUCCESS);
5636ba597c5SAnurag S. Maskey }
5646ba597c5SAnurag S. Maskey 
5656ba597c5SAnurag S. Maskey /*
5666ba597c5SAnurag S. Maskey  * returns NULL if no key was recovered from libdladm.  Passing in
5676ba597c5SAnurag S. Maskey  * security mode of 0 means we don't care what key type it is.
5686ba597c5SAnurag S. Maskey  */
5696ba597c5SAnurag S. Maskey dladm_wlan_key_t *
nwamd_wlan_get_key_named(const char * name,uint32_t security_mode)5706ba597c5SAnurag S. Maskey nwamd_wlan_get_key_named(const char *name, uint32_t security_mode)
5716ba597c5SAnurag S. Maskey {
5726ba597c5SAnurag S. Maskey 	dladm_status_t status;
5736ba597c5SAnurag S. Maskey 	char errmsg[DLADM_STRSIZE];
5746ba597c5SAnurag S. Maskey 	dladm_wlan_key_t *cooked_key;
5756ba597c5SAnurag S. Maskey 	dladm_secobj_class_t class;
5766ba597c5SAnurag S. Maskey 
5776ba597c5SAnurag S. Maskey 	if (security_mode == DLADM_WLAN_SECMODE_NONE)
5786ba597c5SAnurag S. Maskey 		return (NULL);
5796ba597c5SAnurag S. Maskey 
5806ba597c5SAnurag S. Maskey 	/*
5816ba597c5SAnurag S. Maskey 	 * Newly-allocated key must be freed by caller, or by
5826ba597c5SAnurag S. Maskey 	 * subsequent call to nwamd_wlan_get_key_named().
5836ba597c5SAnurag S. Maskey 	 */
5846ba597c5SAnurag S. Maskey 	if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) {
5856ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_get_key_named: malloc failed");
5866ba597c5SAnurag S. Maskey 		return (NULL);
5876ba597c5SAnurag S. Maskey 	}
5886ba597c5SAnurag S. Maskey 
5896ba597c5SAnurag S. Maskey 	/*
5906ba597c5SAnurag S. Maskey 	 * Set name appropriately to retrieve key for this WLAN.  Note that we
5916ba597c5SAnurag S. Maskey 	 * cannot use the actual wk_name buffer size, as it's two times too
5926ba597c5SAnurag S. Maskey 	 * large for dladm_get_secobj.
5936ba597c5SAnurag S. Maskey 	 */
5946ba597c5SAnurag S. Maskey 	(void) strlcpy(cooked_key->wk_name, name, DLADM_SECOBJ_NAME_MAX);
5956ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: len = %d, object = %s\n",
5966ba597c5SAnurag S. Maskey 	    strlen(cooked_key->wk_name), cooked_key->wk_name);
5976ba597c5SAnurag S. Maskey 	cooked_key->wk_len = sizeof (cooked_key->wk_val);
5986ba597c5SAnurag S. Maskey 	cooked_key->wk_idx = 1;
5996ba597c5SAnurag S. Maskey 
6006ba597c5SAnurag S. Maskey 	/* Try the kernel first, then fall back to persistent storage. */
6016ba597c5SAnurag S. Maskey 	status = dladm_get_secobj(dld_handle, cooked_key->wk_name, &class,
6026ba597c5SAnurag S. Maskey 	    cooked_key->wk_val, &cooked_key->wk_len,
6036ba597c5SAnurag S. Maskey 	    DLADM_OPT_ACTIVE);
6046ba597c5SAnurag S. Maskey 	if (status != DLADM_STATUS_OK) {
6056ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: "
6066ba597c5SAnurag S. Maskey 		    "dladm_get_secobj(TEMP) failed: %s",
6076ba597c5SAnurag S. Maskey 		    dladm_status2str(status, errmsg));
6086ba597c5SAnurag S. Maskey 		status = dladm_get_secobj(dld_handle, cooked_key->wk_name,
6096ba597c5SAnurag S. Maskey 		    &class, cooked_key->wk_val, &cooked_key->wk_len,
6106ba597c5SAnurag S. Maskey 		    DLADM_OPT_PERSIST);
6116ba597c5SAnurag S. Maskey 	}
6126ba597c5SAnurag S. Maskey 
6136ba597c5SAnurag S. Maskey 	switch (status) {
6146ba597c5SAnurag S. Maskey 	case DLADM_STATUS_OK:
6156ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: "
6166ba597c5SAnurag S. Maskey 		    "dladm_get_secobj succeeded: len %d", cooked_key->wk_len);
6176ba597c5SAnurag S. Maskey 		break;
6186ba597c5SAnurag S. Maskey 	case DLADM_STATUS_NOTFOUND:
6196ba597c5SAnurag S. Maskey 		/*
6206ba597c5SAnurag S. Maskey 		 * We do not want an error in the case that the secobj
6216ba597c5SAnurag S. Maskey 		 * is not found, since we then prompt for it.
6226ba597c5SAnurag S. Maskey 		 */
6236ba597c5SAnurag S. Maskey 		free(cooked_key);
6246ba597c5SAnurag S. Maskey 		return (NULL);
6256ba597c5SAnurag S. Maskey 	default:
6266ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_get_key_named: could not get key "
6276ba597c5SAnurag S. Maskey 		    "from secure object '%s': %s", cooked_key->wk_name,
6286ba597c5SAnurag S. Maskey 		    dladm_status2str(status, errmsg));
6296ba597c5SAnurag S. Maskey 		free(cooked_key);
6306ba597c5SAnurag S. Maskey 		return (NULL);
6316ba597c5SAnurag S. Maskey 	}
6326ba597c5SAnurag S. Maskey 
6336ba597c5SAnurag S. Maskey 	if (security_mode != 0) {
6346ba597c5SAnurag S. Maskey 		switch (class) {
6356ba597c5SAnurag S. Maskey 		case DLADM_SECOBJ_CLASS_WEP:
6366ba597c5SAnurag S. Maskey 			if (security_mode == DLADM_WLAN_SECMODE_WEP)
6376ba597c5SAnurag S. Maskey 				return (cooked_key);
6386ba597c5SAnurag S. Maskey 			break;
6396ba597c5SAnurag S. Maskey 		case DLADM_SECOBJ_CLASS_WPA:
6406ba597c5SAnurag S. Maskey 			if (security_mode == DLADM_WLAN_SECMODE_WPA)
6416ba597c5SAnurag S. Maskey 				return (cooked_key);
6426ba597c5SAnurag S. Maskey 			break;
6436ba597c5SAnurag S. Maskey 		default:
6446ba597c5SAnurag S. Maskey 			/* shouldn't happen */
6456ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "nwamd_wlan_get_key: invalid class %d",
6466ba597c5SAnurag S. Maskey 			    class);
6476ba597c5SAnurag S. Maskey 			break;
6486ba597c5SAnurag S. Maskey 		}
6496ba597c5SAnurag S. Maskey 		/* key type mismatch */
6506ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_get_key: key type mismatch"
6516ba597c5SAnurag S. Maskey 		    " from secure object '%s'", cooked_key->wk_name);
6526ba597c5SAnurag S. Maskey 		free(cooked_key);
6536ba597c5SAnurag S. Maskey 		return (NULL);
6546ba597c5SAnurag S. Maskey 	}
6556ba597c5SAnurag S. Maskey 
6566ba597c5SAnurag S. Maskey 	return (cooked_key);
6576ba597c5SAnurag S. Maskey }
6586ba597c5SAnurag S. Maskey 
6596ba597c5SAnurag S. Maskey static dladm_wlan_key_t *
nwamd_wlan_get_key(const char * essid,const char * bssid,uint32_t security_mode)6606ba597c5SAnurag S. Maskey nwamd_wlan_get_key(const char *essid, const char *bssid, uint32_t security_mode)
6616ba597c5SAnurag S. Maskey {
6626ba597c5SAnurag S. Maskey 	char keyname[DLADM_SECOBJ_NAME_MAX];
6636ba597c5SAnurag S. Maskey 
6646ba597c5SAnurag S. Maskey 	nwamd_set_key_name(essid, bssid, keyname, DLADM_SECOBJ_NAME_MAX);
6656ba597c5SAnurag S. Maskey 
6666ba597c5SAnurag S. Maskey 	return (nwamd_wlan_get_key_named(keyname, security_mode));
6676ba597c5SAnurag S. Maskey }
6686ba597c5SAnurag S. Maskey 
6696ba597c5SAnurag S. Maskey /*
6706ba597c5SAnurag S. Maskey  * Checks if a wireless network can be selected or not.  A wireless network
6716ba597c5SAnurag S. Maskey  * CANNOT be selected if the NCU is DISABLED, or the NCU is OFFLINE or
6726ba597c5SAnurag S. Maskey  * ONLINE* and has lower priority than the currently active priority-group.
6736ba597c5SAnurag S. Maskey  * Called with object lock held.
6746ba597c5SAnurag S. Maskey  */
6756ba597c5SAnurag S. Maskey static boolean_t
wireless_selection_possible(nwamd_object_t object)6766ba597c5SAnurag S. Maskey wireless_selection_possible(nwamd_object_t object)
6776ba597c5SAnurag S. Maskey {
6786ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu = object->nwamd_object_data;
6796ba597c5SAnurag S. Maskey 
680f6da83d4SAnurag S. Maskey 	if (ncu->ncu_link.nwamd_link_media != DL_WIFI)
6816ba597c5SAnurag S. Maskey 		return (B_FALSE);
6826ba597c5SAnurag S. Maskey 
6836ba597c5SAnurag S. Maskey 	(void) pthread_mutex_lock(&active_ncp_mutex);
6846ba597c5SAnurag S. Maskey 	if (object->nwamd_object_state == NWAM_STATE_DISABLED ||
6856ba597c5SAnurag S. Maskey 	    ((object->nwamd_object_state == NWAM_STATE_OFFLINE ||
6866ba597c5SAnurag S. Maskey 	    object->nwamd_object_state == NWAM_STATE_ONLINE_TO_OFFLINE) &&
687f6da83d4SAnurag S. Maskey 	    ncu->ncu_link.nwamd_link_activation_mode ==
6886ba597c5SAnurag S. Maskey 	    NWAM_ACTIVATION_MODE_PRIORITIZED &&
6896ba597c5SAnurag S. Maskey 	    (current_ncu_priority_group == INVALID_PRIORITY_GROUP ||
690f6da83d4SAnurag S. Maskey 	    ncu->ncu_link.nwamd_link_priority_group >
6916ba597c5SAnurag S. Maskey 	    current_ncu_priority_group))) {
6926ba597c5SAnurag S. Maskey 		(void) pthread_mutex_unlock(&active_ncp_mutex);
6936ba597c5SAnurag S. Maskey 		return (B_FALSE);
6946ba597c5SAnurag S. Maskey 	}
6956ba597c5SAnurag S. Maskey 	(void) pthread_mutex_unlock(&active_ncp_mutex);
6966ba597c5SAnurag S. Maskey 
6976ba597c5SAnurag S. Maskey 	return (B_TRUE);
6986ba597c5SAnurag S. Maskey }
6996ba597c5SAnurag S. Maskey 
7006ba597c5SAnurag S. Maskey /*
7016ba597c5SAnurag S. Maskey  * Update the selected and/or connected values for the
7026ba597c5SAnurag S. Maskey  * scan data.  If these change, we need to trigger a scan
7036ba597c5SAnurag S. Maskey  * event since the updated values need to be communicated
7046ba597c5SAnurag S. Maskey  * to the GUI.
7056ba597c5SAnurag S. Maskey  */
7066ba597c5SAnurag S. Maskey void
nwamd_set_selected_connected(nwamd_ncu_t * ncu,boolean_t selected,boolean_t connected)7076ba597c5SAnurag S. Maskey nwamd_set_selected_connected(nwamd_ncu_t *ncu, boolean_t selected,
7086ba597c5SAnurag S. Maskey     boolean_t connected)
7096ba597c5SAnurag S. Maskey {
710f6da83d4SAnurag S. Maskey 	nwamd_link_t *link = &ncu->ncu_link;
7116ba597c5SAnurag S. Maskey 	nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan;
7126ba597c5SAnurag S. Maskey 	int i;
7136ba597c5SAnurag S. Maskey 	boolean_t trigger_scan_event = B_FALSE;
7146ba597c5SAnurag S. Maskey 
7156ba597c5SAnurag S. Maskey 	for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
7166ba597c5SAnurag S. Maskey 		if (strcmp(s->nwamd_wifi_scan_curr[i].nww_essid,
7176ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_essid) != 0 ||
7186ba597c5SAnurag S. Maskey 		    (link->nwamd_link_wifi_bssid[0] != '\0' &&
7196ba597c5SAnurag S. Maskey 		    strcmp(s->nwamd_wifi_scan_curr[i].nww_bssid,
7206ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_bssid) != 0))
7216ba597c5SAnurag S. Maskey 			continue;
7226ba597c5SAnurag S. Maskey 		if (selected) {
7236ba597c5SAnurag S. Maskey 			if (!s->nwamd_wifi_scan_curr[i].nww_selected)
7246ba597c5SAnurag S. Maskey 				trigger_scan_event = B_TRUE;
7256ba597c5SAnurag S. Maskey 			s->nwamd_wifi_scan_curr[i].nww_selected = B_TRUE;
7266ba597c5SAnurag S. Maskey 		} else {
7276ba597c5SAnurag S. Maskey 			if (s->nwamd_wifi_scan_curr[i].nww_selected)
7286ba597c5SAnurag S. Maskey 				trigger_scan_event = B_TRUE;
7296ba597c5SAnurag S. Maskey 			s->nwamd_wifi_scan_curr[i].nww_selected = B_FALSE;
7306ba597c5SAnurag S. Maskey 		}
7316ba597c5SAnurag S. Maskey 		if (connected) {
7326ba597c5SAnurag S. Maskey 			if (!s->nwamd_wifi_scan_curr[i].nww_connected)
7336ba597c5SAnurag S. Maskey 				trigger_scan_event = B_TRUE;
7346ba597c5SAnurag S. Maskey 			s->nwamd_wifi_scan_curr[i].nww_connected = B_TRUE;
7356ba597c5SAnurag S. Maskey 		} else {
7366ba597c5SAnurag S. Maskey 			if (s->nwamd_wifi_scan_curr[i].nww_connected)
7376ba597c5SAnurag S. Maskey 				trigger_scan_event = B_TRUE;
7386ba597c5SAnurag S. Maskey 			s->nwamd_wifi_scan_curr[i].nww_connected = B_FALSE;
7396ba597c5SAnurag S. Maskey 		}
7406ba597c5SAnurag S. Maskey 	}
7416ba597c5SAnurag S. Maskey 
7426ba597c5SAnurag S. Maskey 	if (trigger_scan_event || s->nwamd_wifi_scan_changed) {
7436ba597c5SAnurag S. Maskey 		nwamd_event_t scan_event = nwamd_event_init_wlan
7446ba597c5SAnurag S. Maskey 		    (ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_SCAN_REPORT, connected,
7456ba597c5SAnurag S. Maskey 		    s->nwamd_wifi_scan_curr, s->nwamd_wifi_scan_curr_num);
7466ba597c5SAnurag S. Maskey 		if (scan_event != NULL) {
7476ba597c5SAnurag S. Maskey 			/* Avoid sending same scan data multiple times */
7486ba597c5SAnurag S. Maskey 			s->nwamd_wifi_scan_changed = B_FALSE;
7496ba597c5SAnurag S. Maskey 			nwamd_event_enqueue(scan_event);
7506ba597c5SAnurag S. Maskey 		}
7516ba597c5SAnurag S. Maskey 	}
7526ba597c5SAnurag S. Maskey }
7536ba597c5SAnurag S. Maskey 
75419cbc0aaSAnurag S. Maskey /*
75519cbc0aaSAnurag S. Maskey  * Callback used on each known WLAN - if the BSSID is matched, set
75619cbc0aaSAnurag S. Maskey  * the ESSID of the hidden WLAN to the known WLAN name.
75719cbc0aaSAnurag S. Maskey  */
75819cbc0aaSAnurag S. Maskey static int
find_bssid_cb(nwam_known_wlan_handle_t kwh,void * data)75919cbc0aaSAnurag S. Maskey find_bssid_cb(nwam_known_wlan_handle_t kwh, void *data)
76019cbc0aaSAnurag S. Maskey {
76119cbc0aaSAnurag S. Maskey 	nwamd_link_t *link = data;
76219cbc0aaSAnurag S. Maskey 	nwam_error_t err;
76319cbc0aaSAnurag S. Maskey 	nwam_value_t bssidval;
76419cbc0aaSAnurag S. Maskey 	char **bssids, *name;
76519cbc0aaSAnurag S. Maskey 	uint_t num_bssids, i;
76619cbc0aaSAnurag S. Maskey 
76719cbc0aaSAnurag S. Maskey 	if ((err = nwam_known_wlan_get_prop_value(kwh,
76819cbc0aaSAnurag S. Maskey 	    NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidval)) != NWAM_SUCCESS) {
76919cbc0aaSAnurag S. Maskey 		nlog(LOG_ERR, "find_bssid_cb: nwam_known_wlan_get_prop: %s",
77019cbc0aaSAnurag S. Maskey 		    nwam_strerror(err));
77119cbc0aaSAnurag S. Maskey 		return (0);
77219cbc0aaSAnurag S. Maskey 	}
77319cbc0aaSAnurag S. Maskey 	if ((err = nwam_value_get_string_array(bssidval, &bssids, &num_bssids))
77419cbc0aaSAnurag S. Maskey 	    != NWAM_SUCCESS) {
77519cbc0aaSAnurag S. Maskey 		nlog(LOG_ERR, "find_bssid_cb: nwam_value_get_string_array: %s",
77619cbc0aaSAnurag S. Maskey 		    nwam_strerror(err));
77719cbc0aaSAnurag S. Maskey 		nwam_value_free(bssidval);
77819cbc0aaSAnurag S. Maskey 		return (0);
77919cbc0aaSAnurag S. Maskey 	}
78019cbc0aaSAnurag S. Maskey 	for (i = 0; i < num_bssids; i++) {
78119cbc0aaSAnurag S. Maskey 		if (strcmp(bssids[i], link->nwamd_link_wifi_bssid) == 0) {
78219cbc0aaSAnurag S. Maskey 			if ((err = nwam_known_wlan_get_name(kwh, &name))
78319cbc0aaSAnurag S. Maskey 			    != NWAM_SUCCESS) {
78419cbc0aaSAnurag S. Maskey 				nlog(LOG_ERR, "find_bssid_cb: "
78519cbc0aaSAnurag S. Maskey 				    "nwam_known_wlan_get_name: %s",
78619cbc0aaSAnurag S. Maskey 				    nwam_strerror(err));
78719cbc0aaSAnurag S. Maskey 				continue;
78819cbc0aaSAnurag S. Maskey 			}
78919cbc0aaSAnurag S. Maskey 			(void) strlcpy(link->nwamd_link_wifi_essid, name,
79019cbc0aaSAnurag S. Maskey 			    sizeof (link->nwamd_link_wifi_essid));
79119cbc0aaSAnurag S. Maskey 			free(name);
79219cbc0aaSAnurag S. Maskey 			nwam_value_free(bssidval);
79319cbc0aaSAnurag S. Maskey 			/* Found ESSID for BSSID so terminate walk */
79419cbc0aaSAnurag S. Maskey 			return (1);
79519cbc0aaSAnurag S. Maskey 		}
79619cbc0aaSAnurag S. Maskey 	}
79719cbc0aaSAnurag S. Maskey 	nwam_value_free(bssidval);
79819cbc0aaSAnurag S. Maskey 
79919cbc0aaSAnurag S. Maskey 	return (0);
80019cbc0aaSAnurag S. Maskey }
80119cbc0aaSAnurag S. Maskey 
80219cbc0aaSAnurag S. Maskey /*
80319cbc0aaSAnurag S. Maskey  * We may have encountered a BSSID for a hidden WLAN before and as a result
80419cbc0aaSAnurag S. Maskey  * may have a known WLAN entry with this BSSID.  Walk known WLANs, searching
80519cbc0aaSAnurag S. Maskey  * for a BSSID match.  Called with object lock held.
80619cbc0aaSAnurag S. Maskey  */
80719cbc0aaSAnurag S. Maskey static void
check_if_hidden_wlan_was_visited(nwamd_link_t * link)80819cbc0aaSAnurag S. Maskey check_if_hidden_wlan_was_visited(nwamd_link_t *link)
80919cbc0aaSAnurag S. Maskey {
81019cbc0aaSAnurag S. Maskey 	(void) nwam_walk_known_wlans(find_bssid_cb, link,
81119cbc0aaSAnurag S. Maskey 	    NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL);
81219cbc0aaSAnurag S. Maskey }
81319cbc0aaSAnurag S. Maskey 
8146ba597c5SAnurag S. Maskey nwam_error_t
nwamd_wlan_select(const char * linkname,const char * essid,const char * bssid,uint32_t security_mode,boolean_t add_to_known_wlans)8156ba597c5SAnurag S. Maskey nwamd_wlan_select(const char *linkname, const char *essid, const char *bssid,
8166ba597c5SAnurag S. Maskey     uint32_t security_mode, boolean_t add_to_known_wlans)
8176ba597c5SAnurag S. Maskey {
8186ba597c5SAnurag S. Maskey 	nwamd_object_t ncu_obj;
8196ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu;
8206ba597c5SAnurag S. Maskey 	nwamd_link_t *link;
821c1976b83Senricop 	boolean_t found_key = B_FALSE;
8226ba597c5SAnurag S. Maskey 
8236ba597c5SAnurag S. Maskey 	if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
8246ba597c5SAnurag S. Maskey 	    == NULL) {
8256ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_select: could not find object  "
8266ba597c5SAnurag S. Maskey 		    "for link %s", linkname);
8276ba597c5SAnurag S. Maskey 		return (NWAM_ENTITY_NOT_FOUND);
8286ba597c5SAnurag S. Maskey 	}
8296ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
830f6da83d4SAnurag S. Maskey 	link = &ncu->ncu_link;
8316ba597c5SAnurag S. Maskey 
8326ba597c5SAnurag S. Maskey 	/*
8336ba597c5SAnurag S. Maskey 	 * If wireless selection is not possible because of the current
8346ba597c5SAnurag S. Maskey 	 * state or priority-group, then stop.
8356ba597c5SAnurag S. Maskey 	 */
8366ba597c5SAnurag S. Maskey 	if (!wireless_selection_possible(ncu_obj)) {
8376ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
8386ba597c5SAnurag S. Maskey 		return (NWAM_ENTITY_INVALID_STATE);
8396ba597c5SAnurag S. Maskey 	}
8406ba597c5SAnurag S. Maskey 
8416ba597c5SAnurag S. Maskey 	/* unset selected, connected flag for previously connected wlan */
8426ba597c5SAnurag S. Maskey 	nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE);
8436ba597c5SAnurag S. Maskey 
84419cbc0aaSAnurag S. Maskey 	/* Disconnect to allow new selection to go ahead */
84519cbc0aaSAnurag S. Maskey 	(void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id);
84619cbc0aaSAnurag S. Maskey 
8476ba597c5SAnurag S. Maskey 	(void) strlcpy(link->nwamd_link_wifi_essid, essid,
8486ba597c5SAnurag S. Maskey 	    sizeof (link->nwamd_link_wifi_essid));
8496ba597c5SAnurag S. Maskey 	(void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
8506ba597c5SAnurag S. Maskey 	    sizeof (link->nwamd_link_wifi_bssid));
8516ba597c5SAnurag S. Maskey 	link->nwamd_link_wifi_security_mode = security_mode;
8526ba597c5SAnurag S. Maskey 	link->nwamd_link_wifi_add_to_known_wlans = add_to_known_wlans;
8536ba597c5SAnurag S. Maskey 
85419cbc0aaSAnurag S. Maskey 	/* If this is a hidden wlan, then essid is empty */
85519cbc0aaSAnurag S. Maskey 	if (link->nwamd_link_wifi_essid[0] == '\0')
85619cbc0aaSAnurag S. Maskey 		check_if_hidden_wlan_was_visited(link);
8576ba597c5SAnurag S. Maskey 
8586ba597c5SAnurag S. Maskey 	/* set selected flag for newly-selected WLAN */
8596ba597c5SAnurag S. Maskey 	nwamd_set_selected_connected(ncu, B_TRUE, B_FALSE);
8606ba597c5SAnurag S. Maskey 
8616ba597c5SAnurag S. Maskey 	/* does this WLAN require a key? If so go to NEED_KEY */
8626ba597c5SAnurag S. Maskey 	if (NEED_ENC(link->nwamd_link_wifi_security_mode)) {
8636ba597c5SAnurag S. Maskey 		/*
864c1976b83Senricop 		 * nwam secobjs can have two formats: nwam-ESSID-BSSID and
865c1976b83Senricop 		 * nwam-ESSID. There is no reason for searching through known
866c1976b83Senricop 		 * wlan keynames since this is only the selection process.
8676ba597c5SAnurag S. Maskey 		 */
868c1976b83Senricop 		if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key
869c1976b83Senricop 		    (link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid,
8706ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_security_mode)) != NULL) {
871c1976b83Senricop 			/*
872c1976b83Senricop 			 * Found old key format,
873c1976b83Senricop 			 * known wlans with similar names might exist
874c1976b83Senricop 			 */
875c1976b83Senricop 			nwamd_set_key_name(link->nwamd_link_wifi_essid,
876c1976b83Senricop 			    link->nwamd_link_wifi_bssid,
8776ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_keyname,
8786ba597c5SAnurag S. Maskey 			    DLADM_SECOBJ_NAME_MAX);
879c1976b83Senricop 			nlog(LOG_DEBUG, "nwamd_wlan_select: got old format "
880c1976b83Senricop 			    "WLAN key %s",
8816ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_keyname);
8826ba597c5SAnurag S. Maskey 			found_key = B_TRUE;
8836ba597c5SAnurag S. Maskey 		} else if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key
884c1976b83Senricop 		    (link->nwamd_link_wifi_essid, NULL,
8856ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_security_mode)) != NULL) {
8866ba597c5SAnurag S. Maskey 			nwamd_set_key_name(link->nwamd_link_wifi_essid, NULL,
8876ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_keyname,
8886ba597c5SAnurag S. Maskey 			    DLADM_SECOBJ_NAME_MAX);
889c1976b83Senricop 			nlog(LOG_DEBUG, "nwamd_wlan_select: got WLAN key %s",
8906ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_keyname);
891c1976b83Senricop 			found_key = B_TRUE;
8926ba597c5SAnurag S. Maskey 		} else {
8936ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "nwamd_wlan_select: could not "
8946ba597c5SAnurag S. Maskey 			    "find key for WLAN '%s'",
8956ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_essid);
8966ba597c5SAnurag S. Maskey 		}
8976ba597c5SAnurag S. Maskey 	} else {
8986ba597c5SAnurag S. Maskey 		free(link->nwamd_link_wifi_key);
8996ba597c5SAnurag S. Maskey 		link->nwamd_link_wifi_key = NULL;
9006ba597c5SAnurag S. Maskey 		link->nwamd_link_wifi_keyname[0] = '\0';
9016ba597c5SAnurag S. Maskey 	}
9026ba597c5SAnurag S. Maskey 
9036ba597c5SAnurag S. Maskey 	if (NEED_ENC(link->nwamd_link_wifi_security_mode) && !found_key) {
9046ba597c5SAnurag S. Maskey 		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
9056ba597c5SAnurag S. Maskey 		    ncu_obj->nwamd_object_name,
9066ba597c5SAnurag S. Maskey 		    NWAM_STATE_OFFLINE_TO_ONLINE,
9076ba597c5SAnurag S. Maskey 		    NWAM_AUX_STATE_LINK_WIFI_NEED_KEY);
9086ba597c5SAnurag S. Maskey 	} else {
9096ba597c5SAnurag S. Maskey 		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
9106ba597c5SAnurag S. Maskey 		    ncu_obj->nwamd_object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
9116ba597c5SAnurag S. Maskey 		    NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
9126ba597c5SAnurag S. Maskey 	}
9136ba597c5SAnurag S. Maskey 	nwamd_object_release(ncu_obj);
9146ba597c5SAnurag S. Maskey 
9156ba597c5SAnurag S. Maskey 	return (NWAM_SUCCESS);
9166ba597c5SAnurag S. Maskey }
9176ba597c5SAnurag S. Maskey 
9186ba597c5SAnurag S. Maskey /*
9196ba597c5SAnurag S. Maskey  * See if BSSID is in visited list of BSSIDs for known WLAN. Used for
9206ba597c5SAnurag S. Maskey  * strict BSSID matching (depends on wireless_strict_bssid property value).
9216ba597c5SAnurag S. Maskey  */
922c1976b83Senricop static int
bssid_match(nwam_known_wlan_handle_t kwh,void * bssid)923c1976b83Senricop bssid_match(nwam_known_wlan_handle_t kwh, void *bssid)
9246ba597c5SAnurag S. Maskey {
9256ba597c5SAnurag S. Maskey 	nwam_value_t bssidsval;
9266ba597c5SAnurag S. Maskey 	nwam_error_t err;
9276ba597c5SAnurag S. Maskey 	char **bssids;
9286ba597c5SAnurag S. Maskey 	uint_t nelem, i;
929c1976b83Senricop 	int found = 0;
9306ba597c5SAnurag S. Maskey 
9316ba597c5SAnurag S. Maskey 	if ((err = nwam_known_wlan_get_prop_value(kwh,
9326ba597c5SAnurag S. Maskey 	    NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval)) != NWAM_SUCCESS) {
9336ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "bssid_match: %s", nwam_strerror(err));
934c1976b83Senricop 		return (0);
9356ba597c5SAnurag S. Maskey 	}
9366ba597c5SAnurag S. Maskey 	if ((err = nwam_value_get_string_array(bssidsval, &bssids, &nelem))
9376ba597c5SAnurag S. Maskey 	    != NWAM_SUCCESS) {
9386ba597c5SAnurag S. Maskey 		nwam_value_free(bssidsval);
939c1976b83Senricop 		return (0);
9406ba597c5SAnurag S. Maskey 	}
9416ba597c5SAnurag S. Maskey 	for (i = 0; i < nelem; i++) {
942c1976b83Senricop 		if (strcmp((const char *)bssid, bssids[i]) == 0) {
943c1976b83Senricop 			found = 1;
9446ba597c5SAnurag S. Maskey 			break;
9456ba597c5SAnurag S. Maskey 		}
9466ba597c5SAnurag S. Maskey 	}
9476ba597c5SAnurag S. Maskey 	nwam_value_free(bssidsval);
9486ba597c5SAnurag S. Maskey 
9496ba597c5SAnurag S. Maskey 	return (found);
9506ba597c5SAnurag S. Maskey }
9516ba597c5SAnurag S. Maskey 
9526ba597c5SAnurag S. Maskey /* Find most prioritized AP with strongest signal in scan data. */
9536ba597c5SAnurag S. Maskey static int
find_best_wlan_cb(nwam_known_wlan_handle_t kwh,void * data)9546ba597c5SAnurag S. Maskey find_best_wlan_cb(nwam_known_wlan_handle_t kwh, void *data)
9556ba597c5SAnurag S. Maskey {
9566ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu = data;
957f6da83d4SAnurag S. Maskey 	nwamd_link_t *link = &ncu->ncu_link;
9586ba597c5SAnurag S. Maskey 	nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan;
9596ba597c5SAnurag S. Maskey 	nwam_error_t err;
9606ba597c5SAnurag S. Maskey 	char *name = NULL;
9616ba597c5SAnurag S. Maskey 	int i;
9626ba597c5SAnurag S. Maskey 	dladm_wlan_strength_t curr_strength = 0;
9636ba597c5SAnurag S. Maskey 	dladm_wlan_strength_t max_strength = 0;
9646ba597c5SAnurag S. Maskey 	boolean_t found = B_FALSE;
9656ba597c5SAnurag S. Maskey 
9666ba597c5SAnurag S. Maskey 	if ((err = nwam_known_wlan_get_name(kwh, &name)) != NWAM_SUCCESS) {
9676ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "find_best_wlan_cb: could not look up name: %s",
9686ba597c5SAnurag S. Maskey 		    nwam_strerror(err));
9696ba597c5SAnurag S. Maskey 		return (0);
9706ba597c5SAnurag S. Maskey 	}
9716ba597c5SAnurag S. Maskey 
9726ba597c5SAnurag S. Maskey 	if (link->nwamd_link_wifi_connected) {
9736ba597c5SAnurag S. Maskey 		(void) dladm_wlan_str2strength
9746ba597c5SAnurag S. Maskey 		    (link->nwamd_link_wifi_signal_strength, &curr_strength);
9756ba597c5SAnurag S. Maskey 	}
9766ba597c5SAnurag S. Maskey 
9776ba597c5SAnurag S. Maskey 	/*
9786ba597c5SAnurag S. Maskey 	 * If we're >= scan level, don't pick another Known WLAN if still
9796ba597c5SAnurag S. Maskey 	 * connected (even if a Known WLAN with higher priority is available).
9806ba597c5SAnurag S. Maskey 	 * If the user wants to connect to a different Known WLAN, it can be
981*bbf21555SRichard Lowe 	 * done from the GUI or select-wifi subcommand of nwamadm(8).
9826ba597c5SAnurag S. Maskey 	 */
9836ba597c5SAnurag S. Maskey 	if (curr_strength >= wireless_scan_level &&
9846ba597c5SAnurag S. Maskey 	    link->nwamd_link_wifi_connected) {
9856ba597c5SAnurag S. Maskey 		free(name);
9866ba597c5SAnurag S. Maskey 		return (1);
9876ba597c5SAnurag S. Maskey 	}
9886ba597c5SAnurag S. Maskey 
9896ba597c5SAnurag S. Maskey 	for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
9906ba597c5SAnurag S. Maskey 		nwam_wlan_t *cur_wlan = &(s->nwamd_wifi_scan_curr[i]);
991c1976b83Senricop 		int b_match = bssid_match(kwh, cur_wlan->nww_bssid);
9926ba597c5SAnurag S. Maskey 
9936ba597c5SAnurag S. Maskey 		/*
9946ba597c5SAnurag S. Maskey 		 * We need to either match the scanned essid, or in the case
9956ba597c5SAnurag S. Maskey 		 * where the essid was not broadcast, match the scanned bssid.
9966ba597c5SAnurag S. Maskey 		 */
9976ba597c5SAnurag S. Maskey 		if (strcmp(cur_wlan->nww_essid, name) != 0 &&
9986ba597c5SAnurag S. Maskey 		    !(cur_wlan->nww_essid[0] == '\0' && b_match))
9996ba597c5SAnurag S. Maskey 			continue;
10006ba597c5SAnurag S. Maskey 		/*
10016ba597c5SAnurag S. Maskey 		 * If wireless_strict_bssid is specified, need to match
10026ba597c5SAnurag S. Maskey 		 * BSSID too.
10036ba597c5SAnurag S. Maskey 		 */
10046ba597c5SAnurag S. Maskey 		if (wireless_strict_bssid && !b_match)
10056ba597c5SAnurag S. Maskey 			continue;
10066ba597c5SAnurag S. Maskey 		/*
10076ba597c5SAnurag S. Maskey 		 * Found a match. Since we walk known WLANs in
10086ba597c5SAnurag S. Maskey 		 * priority order, it's guaranteed to be the
10096ba597c5SAnurag S. Maskey 		 * most prioritized. It may not be the strongest though -
10106ba597c5SAnurag S. Maskey 		 * we continue the walk and record the strength along
10116ba597c5SAnurag S. Maskey 		 * with the ESSID and BSSID, so that if we encounter
10126ba597c5SAnurag S. Maskey 		 * another AP with the same ESSID but a higher signal strength,
10136ba597c5SAnurag S. Maskey 		 * we will choose it - but only if the currently-connected
10146ba597c5SAnurag S. Maskey 		 * WLAN is at or below wireless_scan_level.
10156ba597c5SAnurag S. Maskey 		 */
10166ba597c5SAnurag S. Maskey 		(void) dladm_wlan_str2strength
10176ba597c5SAnurag S. Maskey 		    (cur_wlan->nww_signal_strength, &curr_strength);
10186ba597c5SAnurag S. Maskey 
10196ba597c5SAnurag S. Maskey 		if (curr_strength > max_strength) {
10206ba597c5SAnurag S. Maskey 			(void) strlcpy(link->nwamd_link_wifi_essid,
10216ba597c5SAnurag S. Maskey 			    cur_wlan->nww_essid,
10226ba597c5SAnurag S. Maskey 			    sizeof (link->nwamd_link_wifi_essid));
102383ca9a11SAnurag S. Maskey 			/*
102483ca9a11SAnurag S. Maskey 			 * Set BSSID if wireless_strict_bssid is specified or
102583ca9a11SAnurag S. Maskey 			 * if this is a hidden WLAN.  Store the BSSID here and
102683ca9a11SAnurag S. Maskey 			 * then later determine the hidden WLAN's name in the
102783ca9a11SAnurag S. Maskey 			 * connect thread.
102883ca9a11SAnurag S. Maskey 			 */
102983ca9a11SAnurag S. Maskey 			if (wireless_strict_bssid ||
103083ca9a11SAnurag S. Maskey 			    cur_wlan->nww_essid[0] == '\0') {
10316ba597c5SAnurag S. Maskey 				(void) strlcpy(link->nwamd_link_wifi_bssid,
10326ba597c5SAnurag S. Maskey 				    cur_wlan->nww_bssid,
10336ba597c5SAnurag S. Maskey 				    sizeof (link->nwamd_link_wifi_bssid));
10346ba597c5SAnurag S. Maskey 			}
10356ba597c5SAnurag S. Maskey 			(void) strlcpy(link->nwamd_link_wifi_signal_strength,
10366ba597c5SAnurag S. Maskey 			    cur_wlan->nww_signal_strength,
10376ba597c5SAnurag S. Maskey 			    sizeof (link->nwamd_link_wifi_signal_strength));
10386ba597c5SAnurag S. Maskey 			link->nwamd_link_wifi_security_mode =
10396ba597c5SAnurag S. Maskey 			    cur_wlan->nww_security_mode;
10406ba597c5SAnurag S. Maskey 			found = B_TRUE;
10416ba597c5SAnurag S. Maskey 		}
10426ba597c5SAnurag S. Maskey 		(void) dladm_wlan_str2strength
10436ba597c5SAnurag S. Maskey 		    (link->nwamd_link_wifi_signal_strength, &max_strength);
10446ba597c5SAnurag S. Maskey 	}
10456ba597c5SAnurag S. Maskey 	free(name);
10466ba597c5SAnurag S. Maskey 	return (found ? 1 : 0);
10476ba597c5SAnurag S. Maskey }
10486ba597c5SAnurag S. Maskey 
10496ba597c5SAnurag S. Maskey static boolean_t
nwamd_find_known_wlan(nwamd_object_t ncu_obj)10506ba597c5SAnurag S. Maskey nwamd_find_known_wlan(nwamd_object_t ncu_obj)
10516ba597c5SAnurag S. Maskey {
10526ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data;
10536ba597c5SAnurag S. Maskey 	int ret;
10546ba597c5SAnurag S. Maskey 
10556ba597c5SAnurag S. Maskey 	/*
10566ba597c5SAnurag S. Maskey 	 * Walk known WLANs, finding lowest priority (preferred) WLAN
10576ba597c5SAnurag S. Maskey 	 * in our scan results.
10586ba597c5SAnurag S. Maskey 	 */
10596ba597c5SAnurag S. Maskey 	(void) nwam_walk_known_wlans(find_best_wlan_cb, ncu,
10606ba597c5SAnurag S. Maskey 	    NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, &ret);
10616ba597c5SAnurag S. Maskey 
10626ba597c5SAnurag S. Maskey 	return (ret == 1);
10636ba597c5SAnurag S. Maskey }
10646ba597c5SAnurag S. Maskey 
10656ba597c5SAnurag S. Maskey /*
10666ba597c5SAnurag S. Maskey  * WLAN scan code for WIFI link NCUs.
10676ba597c5SAnurag S. Maskey  */
10686ba597c5SAnurag S. Maskey 
10696ba597c5SAnurag S. Maskey /* Create periodic scan event for object.  Called with object lock held. */
10706ba597c5SAnurag S. Maskey void
nwamd_ncu_create_periodic_scan_event(nwamd_object_t ncu_obj)10716ba597c5SAnurag S. Maskey nwamd_ncu_create_periodic_scan_event(nwamd_object_t ncu_obj)
10726ba597c5SAnurag S. Maskey {
10736ba597c5SAnurag S. Maskey 	nwamd_event_t scan_event;
10746ba597c5SAnurag S. Maskey 
10756ba597c5SAnurag S. Maskey 	if (wireless_scan_interval == 0) {
10766ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "nwamd_ncu_create_periodic_scan_event: "
10776ba597c5SAnurag S. Maskey 		    "wireless_scan_interval set to 0 so no periodic scanning");
10786ba597c5SAnurag S. Maskey 		return;
10796ba597c5SAnurag S. Maskey 	}
10806ba597c5SAnurag S. Maskey 	scan_event = nwamd_event_init(NWAM_EVENT_TYPE_PERIODIC_SCAN,
10816ba597c5SAnurag S. Maskey 	    NWAM_OBJECT_TYPE_NCU, 0, ncu_obj->nwamd_object_name);
10826ba597c5SAnurag S. Maskey 	if (scan_event != NULL) {
10836ba597c5SAnurag S. Maskey 		nwamd_event_enqueue_timed(scan_event,
10846ba597c5SAnurag S. Maskey 		    wireless_scan_interval > WIRELESS_SCAN_INTERVAL_MIN ?
10856ba597c5SAnurag S. Maskey 		    wireless_scan_interval : WIRELESS_SCAN_INTERVAL_MIN);
10866ba597c5SAnurag S. Maskey 	}
10876ba597c5SAnurag S. Maskey }
10886ba597c5SAnurag S. Maskey 
10896ba597c5SAnurag S. Maskey /* Handle periodic scan event (which puts link into WIFI_INIT state */
10906ba597c5SAnurag S. Maskey void
nwamd_ncu_handle_periodic_scan_event(nwamd_event_t event)10916ba597c5SAnurag S. Maskey nwamd_ncu_handle_periodic_scan_event(nwamd_event_t event)
10926ba597c5SAnurag S. Maskey {
10936ba597c5SAnurag S. Maskey 	nwamd_object_t ncu_obj;
10946ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu;
10956ba597c5SAnurag S. Maskey 
10966ba597c5SAnurag S. Maskey 	ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
10976ba597c5SAnurag S. Maskey 	    event->event_object);
10986ba597c5SAnurag S. Maskey 	if (ncu_obj == NULL) {
10996ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_ncu_handle_periodic_scan_event: "
11006ba597c5SAnurag S. Maskey 		    "no object %s", event->event_object);
11016ba597c5SAnurag S. Maskey 		return;
11026ba597c5SAnurag S. Maskey 	}
11036ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
11046ba597c5SAnurag S. Maskey 
11056ba597c5SAnurag S. Maskey 	/* Only rescan if state is offline* or online */
11066ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_ncu_handle_periodic_scan_event: doing rescan..");
11076ba597c5SAnurag S. Maskey 
11086ba597c5SAnurag S. Maskey 	if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE ||
11096ba597c5SAnurag S. Maskey 	    ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE) {
11106ba597c5SAnurag S. Maskey 		/* rescan, then create periodic scan event */
11116ba597c5SAnurag S. Maskey 		(void) nwamd_wlan_scan(ncu->ncu_name);
11126ba597c5SAnurag S. Maskey 		nwamd_ncu_create_periodic_scan_event(ncu_obj);
11136ba597c5SAnurag S. Maskey 	}
11146ba597c5SAnurag S. Maskey 	nwamd_object_release(ncu_obj);
11156ba597c5SAnurag S. Maskey }
11166ba597c5SAnurag S. Maskey 
11176ba597c5SAnurag S. Maskey static boolean_t
get_scan_results(void * arg,dladm_wlan_attr_t * attrp)11186ba597c5SAnurag S. Maskey get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
11196ba597c5SAnurag S. Maskey {
11206ba597c5SAnurag S. Maskey 	nwamd_wifi_scan_t *s = arg;
11216ba597c5SAnurag S. Maskey 	const char *linkname = s->nwamd_wifi_scan_link;
11226ba597c5SAnurag S. Maskey 	char essid_name[DLADM_STRSIZE];
11236ba597c5SAnurag S. Maskey 	char bssid_name[DLADM_STRSIZE];
11246ba597c5SAnurag S. Maskey 	char strength[DLADM_STRSIZE];
11256ba597c5SAnurag S. Maskey 	uint_t i, index = 0;
11266ba597c5SAnurag S. Maskey 	boolean_t found = B_FALSE;
11276ba597c5SAnurag S. Maskey 
11286ba597c5SAnurag S. Maskey 	(void) dladm_wlan_essid2str(&attrp->wa_essid, essid_name);
11296ba597c5SAnurag S. Maskey 	(void) dladm_wlan_bssid2str(&attrp->wa_bssid, bssid_name);
11306ba597c5SAnurag S. Maskey 	(void) dladm_wlan_strength2str(&attrp->wa_strength, strength);
11316ba597c5SAnurag S. Maskey 
11326ba597c5SAnurag S. Maskey 	index = s->nwamd_wifi_scan_curr_num;
11336ba597c5SAnurag S. Maskey 	if (index == NWAMD_MAX_NUM_WLANS) {
11346ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "get_scan_results: truncating WLAN scan results "
11356ba597c5SAnurag S. Maskey 		    "for link %s: ommiting (%s, %s)", linkname, essid_name,
11366ba597c5SAnurag S. Maskey 		    bssid_name);
11376ba597c5SAnurag S. Maskey 		return (B_TRUE);
11386ba597c5SAnurag S. Maskey 	}
11396ba597c5SAnurag S. Maskey 
11406ba597c5SAnurag S. Maskey 	(void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_essid, essid_name,
11416ba597c5SAnurag S. Maskey 	    sizeof (s->nwamd_wifi_scan_curr[index].nww_essid));
11426ba597c5SAnurag S. Maskey 	(void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_bssid, bssid_name,
11436ba597c5SAnurag S. Maskey 	    sizeof (s->nwamd_wifi_scan_curr[index].nww_bssid));
11446ba597c5SAnurag S. Maskey 	(void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_signal_strength,
11456ba597c5SAnurag S. Maskey 	    strength,
11466ba597c5SAnurag S. Maskey 	    sizeof (s->nwamd_wifi_scan_curr[index].nww_signal_strength));
11476ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_security_mode = attrp->wa_secmode;
11486ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_speed = attrp->wa_speed;
11496ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_channel = attrp->wa_channel;
11506ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_bsstype = attrp->wa_bsstype;
11516ba597c5SAnurag S. Maskey 
11526ba597c5SAnurag S. Maskey 	/*
11536ba597c5SAnurag S. Maskey 	 * We fill in actual values for selected/connected/key later when we
11546ba597c5SAnurag S. Maskey 	 * reacquire the object lock.
11556ba597c5SAnurag S. Maskey 	 */
11566ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_selected = B_FALSE;
11576ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_connected = B_FALSE;
11586ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_have_key = B_FALSE;
11596ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr[index].nww_keyindex = 1;
11606ba597c5SAnurag S. Maskey 	s->nwamd_wifi_scan_curr_num++;
11616ba597c5SAnurag S. Maskey 
11626ba597c5SAnurag S. Maskey 	/* Check if this AP was in previous scan results */
11636ba597c5SAnurag S. Maskey 	for (i = 0; i < s->nwamd_wifi_scan_last_num; i++) {
11646ba597c5SAnurag S. Maskey 		found = (strcmp(s->nwamd_wifi_scan_last[i].nww_essid,
11656ba597c5SAnurag S. Maskey 		    essid_name) == 0 &&
11666ba597c5SAnurag S. Maskey 		    strcmp(s->nwamd_wifi_scan_last[i].nww_bssid,
11676ba597c5SAnurag S. Maskey 		    bssid_name) == 0);
11686ba597c5SAnurag S. Maskey 		if (found)
11696ba597c5SAnurag S. Maskey 			break;
11706ba597c5SAnurag S. Maskey 	}
11716ba597c5SAnurag S. Maskey 	if (!found)
11726ba597c5SAnurag S. Maskey 		s->nwamd_wifi_scan_changed = B_TRUE;
11736ba597c5SAnurag S. Maskey 
11746ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "get_scan_results(%s, %d): ESSID %s, BSSID %s",
11756ba597c5SAnurag S. Maskey 	    linkname, index, essid_name, bssid_name);
11766ba597c5SAnurag S. Maskey 
11776ba597c5SAnurag S. Maskey 	return (B_TRUE);
11786ba597c5SAnurag S. Maskey }
11796ba597c5SAnurag S. Maskey 
11806ba597c5SAnurag S. Maskey /*
11816ba597c5SAnurag S. Maskey  * Check if we're connected to the expected WLAN, or in the case of autoconf
11826ba597c5SAnurag S. Maskey  * record the WLAN we're connected to.
11836ba597c5SAnurag S. Maskey  */
11846ba597c5SAnurag S. Maskey boolean_t
nwamd_wlan_connected(nwamd_object_t ncu_obj)11856ba597c5SAnurag S. Maskey nwamd_wlan_connected(nwamd_object_t ncu_obj)
11866ba597c5SAnurag S. Maskey {
11876ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data;
1188f6da83d4SAnurag S. Maskey 	nwamd_link_t *link = &ncu->ncu_link;
11896ba597c5SAnurag S. Maskey 	dladm_wlan_linkattr_t attr;
11906ba597c5SAnurag S. Maskey 	char essid[DLADM_STRSIZE];
11916ba597c5SAnurag S. Maskey 	char bssid[DLADM_STRSIZE];
11926ba597c5SAnurag S. Maskey 	boolean_t connected = B_FALSE;
11936ba597c5SAnurag S. Maskey 	int retries = 0;
11946ba597c5SAnurag S. Maskey 
11956ba597c5SAnurag S. Maskey 	/*
11966ba597c5SAnurag S. Maskey 	 * This is awful, but some wireless drivers
11976ba597c5SAnurag S. Maskey 	 * (particularly 'ath') will erroneously report
11986ba597c5SAnurag S. Maskey 	 * "disconnected" if queried right after a scan.  If we
11996ba597c5SAnurag S. Maskey 	 * see 'down' reported here, we retry a few times to
12006ba597c5SAnurag S. Maskey 	 * make sure it's really down.
12016ba597c5SAnurag S. Maskey 	 */
12026ba597c5SAnurag S. Maskey 	while (retries++ < 4) {
12036ba597c5SAnurag S. Maskey 		if (dladm_wlan_get_linkattr(dld_handle, link->nwamd_link_id,
12046ba597c5SAnurag S. Maskey 		    &attr) != DLADM_STATUS_OK) {
12056ba597c5SAnurag S. Maskey 			attr.la_status = DLADM_WLAN_LINK_DISCONNECTED;
12066ba597c5SAnurag S. Maskey 		} else if (attr.la_status == DLADM_WLAN_LINK_CONNECTED) {
12076ba597c5SAnurag S. Maskey 			break;
12086ba597c5SAnurag S. Maskey 		}
12096ba597c5SAnurag S. Maskey 	}
12106ba597c5SAnurag S. Maskey 
12116ba597c5SAnurag S. Maskey 	if (attr.la_status == DLADM_WLAN_LINK_CONNECTED) {
12126ba597c5SAnurag S. Maskey 		(void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid, essid);
12136ba597c5SAnurag S. Maskey 		(void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid, bssid);
12146ba597c5SAnurag S. Maskey 		connected = B_TRUE;
12156ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "nwamd_wlan_connected: %s connected to %s %s",
12166ba597c5SAnurag S. Maskey 		    ncu->ncu_name, essid, bssid);
12176ba597c5SAnurag S. Maskey 	} else {
12186ba597c5SAnurag S. Maskey 		return (B_FALSE);
12196ba597c5SAnurag S. Maskey 	}
12206ba597c5SAnurag S. Maskey 	/*
12216ba597c5SAnurag S. Maskey 	 * If we're using autoconf,  we have no control over what we connect to,
12226ba597c5SAnurag S. Maskey 	 * so rather than verifying ESSSID, simply record ESSID/BSSID.
12236ba597c5SAnurag S. Maskey 	 */
12246ba597c5SAnurag S. Maskey 	if (link->nwamd_link_wifi_autoconf) {
12256ba597c5SAnurag S. Maskey 		(void) strlcpy(link->nwamd_link_wifi_essid, essid,
12266ba597c5SAnurag S. Maskey 		    sizeof (link->nwamd_link_wifi_essid));
12276ba597c5SAnurag S. Maskey 		(void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
12286ba597c5SAnurag S. Maskey 		    sizeof (link->nwamd_link_wifi_bssid));
12296ba597c5SAnurag S. Maskey 	}
12306ba597c5SAnurag S. Maskey 	/*
12316ba597c5SAnurag S. Maskey 	 * Are we connected to expected WLAN? Note:
12326ba597c5SAnurag S. Maskey 	 * we'd like to verify BSSID, but we cannot due to CR 6772510.
12336ba597c5SAnurag S. Maskey 	 */
12346ba597c5SAnurag S. Maskey 	if (strcmp(essid, link->nwamd_link_wifi_essid) == 0) {
12356ba597c5SAnurag S. Maskey 		/* Update connected signal strength */
12366ba597c5SAnurag S. Maskey 		(void) dladm_wlan_strength2str(&attr.la_wlan_attr.wa_strength,
12376ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_signal_strength);
12386ba597c5SAnurag S. Maskey 
12396ba597c5SAnurag S. Maskey 		/* Store current BSSID */
12406ba597c5SAnurag S. Maskey 		(void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
12416ba597c5SAnurag S. Maskey 		    sizeof (link->nwamd_link_wifi_bssid));
12426ba597c5SAnurag S. Maskey 
12436ba597c5SAnurag S. Maskey 		if (attr.la_wlan_attr.wa_strength < wireless_scan_level) {
12446ba597c5SAnurag S. Maskey 			/*
12456ba597c5SAnurag S. Maskey 			 * We're connected, but we've dropped below
12466ba597c5SAnurag S. Maskey 			 * scan threshold.  Initiate a scan.
12476ba597c5SAnurag S. Maskey 			 */
12486ba597c5SAnurag S. Maskey 			nlog(LOG_DEBUG, "nwamd_wlan_connected: "
12496ba597c5SAnurag S. Maskey 			    "connected but signal under threshold...");
12506ba597c5SAnurag S. Maskey 			(void) nwamd_wlan_scan(ncu->ncu_name);
12516ba597c5SAnurag S. Maskey 		}
12526ba597c5SAnurag S. Maskey 		return (connected);
12536ba597c5SAnurag S. Maskey 	} else if (strlen(essid) == 0) {
12546ba597c5SAnurag S. Maskey 		/*
12556ba597c5SAnurag S. Maskey 		 * For hidden WLANs, no ESSID is specified, so we cannot verify
12566ba597c5SAnurag S. Maskey 		 * WLAN name.
12576ba597c5SAnurag S. Maskey 		 */
12586ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG,
12596ba597c5SAnurag S. Maskey 		    "nwamd_wlan_connected: connected to hidden WLAN, cannot "
12606ba597c5SAnurag S. Maskey 		    "verify connection details");
12616ba597c5SAnurag S. Maskey 		return (connected);
12626ba597c5SAnurag S. Maskey 	} else {
12636ba597c5SAnurag S. Maskey 		(void) nlog(LOG_ERR,
12646ba597c5SAnurag S. Maskey 		    "nwamd_wlan_connected: wrong AP on %s; expected %s %s",
12656ba597c5SAnurag S. Maskey 		    ncu->ncu_name, link->nwamd_link_wifi_essid,
12666ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_bssid);
12676ba597c5SAnurag S. Maskey 		(void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id);
12686ba597c5SAnurag S. Maskey 		link->nwamd_link_wifi_connected = B_FALSE;
12696ba597c5SAnurag S. Maskey 		return (B_FALSE);
12706ba597c5SAnurag S. Maskey 	}
12716ba597c5SAnurag S. Maskey }
12726ba597c5SAnurag S. Maskey 
12736ba597c5SAnurag S. Maskey /*
12746ba597c5SAnurag S. Maskey  * WLAN scan thread. Called with the per-link WiFi mutex held.
12756ba597c5SAnurag S. Maskey  */
12766ba597c5SAnurag S. Maskey static void *
wlan_scan_thread(void * arg)12776ba597c5SAnurag S. Maskey wlan_scan_thread(void *arg)
12786ba597c5SAnurag S. Maskey {
12796ba597c5SAnurag S. Maskey 	char *linkname = arg;
12806ba597c5SAnurag S. Maskey 	nwamd_object_t ncu_obj;
12816ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu;
12826ba597c5SAnurag S. Maskey 	nwamd_link_t *link;
12836ba597c5SAnurag S. Maskey 	dladm_status_t status;
12846ba597c5SAnurag S. Maskey 	char essid[DLADM_STRSIZE];
12856ba597c5SAnurag S. Maskey 	char bssid[DLADM_STRSIZE];
12866ba597c5SAnurag S. Maskey 	uint32_t now, link_id;
12876ba597c5SAnurag S. Maskey 	nwamd_wifi_scan_t s;
12886ba597c5SAnurag S. Maskey 	int i;
12896ba597c5SAnurag S. Maskey 
12906ba597c5SAnurag S. Maskey 	if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
12916ba597c5SAnurag S. Maskey 	    == NULL) {
12926ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "wlan_scan_thread: could not find object  "
12936ba597c5SAnurag S. Maskey 		    "for link %s", linkname);
12946ba597c5SAnurag S. Maskey 		free(linkname);
12956ba597c5SAnurag S. Maskey 		return (NULL);
12966ba597c5SAnurag S. Maskey 	}
12976ba597c5SAnurag S. Maskey 
12986ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
1299f6da83d4SAnurag S. Maskey 	link = &ncu->ncu_link;
13006ba597c5SAnurag S. Maskey 
13016ba597c5SAnurag S. Maskey 	/*
13026ba597c5SAnurag S. Maskey 	 * It is possible multiple scan threads have queued up waiting for the
13036ba597c5SAnurag S. Maskey 	 * object lock.  We try to prevent excessive scanning by limiting the
13046ba597c5SAnurag S. Maskey 	 * interval between scans to WIRELESS_SCAN_REQUESTED_INTERVAL_MIN sec.
13056ba597c5SAnurag S. Maskey 	 */
13066ba597c5SAnurag S. Maskey 	now = NSEC_TO_SEC(gethrtime());
13076ba597c5SAnurag S. Maskey 	if ((now - link->nwamd_link_wifi_scan.nwamd_wifi_scan_last_time) <
13086ba597c5SAnurag S. Maskey 	    WIRELESS_SCAN_REQUESTED_INTERVAL_MIN) {
13096ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "wlan_scan_thread: last scan for %s "
13106ba597c5SAnurag S. Maskey 		    "was < %d sec ago, ignoring scan request",
13116ba597c5SAnurag S. Maskey 		    linkname, WIRELESS_SCAN_REQUESTED_INTERVAL_MIN);
13126ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
13136ba597c5SAnurag S. Maskey 		free(linkname);
13146ba597c5SAnurag S. Maskey 		return (NULL);
13156ba597c5SAnurag S. Maskey 	}
13166ba597c5SAnurag S. Maskey 
13176ba597c5SAnurag S. Maskey 	/*
13186ba597c5SAnurag S. Maskey 	 * Prepare scan data - copy link name and copy previous "current"
13196ba597c5SAnurag S. Maskey 	 * scan results from the nwamd_link_t to the last scan results for
13206ba597c5SAnurag S. Maskey 	 * the next scan so that we can compare results to find if things
13216ba597c5SAnurag S. Maskey 	 * have changed since last time.
13226ba597c5SAnurag S. Maskey 	 */
13236ba597c5SAnurag S. Maskey 	(void) bzero(&s, sizeof (nwamd_wifi_scan_t));
13246ba597c5SAnurag S. Maskey 	(void) strlcpy(s.nwamd_wifi_scan_link, ncu->ncu_name,
13256ba597c5SAnurag S. Maskey 	    sizeof (s.nwamd_wifi_scan_link));
13266ba597c5SAnurag S. Maskey 	s.nwamd_wifi_scan_last_num =
13276ba597c5SAnurag S. Maskey 	    link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num;
13286ba597c5SAnurag S. Maskey 	if (s.nwamd_wifi_scan_last_num > 0) {
13296ba597c5SAnurag S. Maskey 		(void) memcpy(s.nwamd_wifi_scan_last,
13306ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr,
13316ba597c5SAnurag S. Maskey 		    s.nwamd_wifi_scan_last_num * sizeof (nwam_wlan_t));
13326ba597c5SAnurag S. Maskey 	}
13336ba597c5SAnurag S. Maskey 	link_id = link->nwamd_link_id;
13346ba597c5SAnurag S. Maskey 	nwamd_object_release(ncu_obj);
13356ba597c5SAnurag S. Maskey 
13366ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "wlan_scan_thread: initiating scan on %s",
13376ba597c5SAnurag S. Maskey 	    s.nwamd_wifi_scan_link);
13386ba597c5SAnurag S. Maskey 
13396ba597c5SAnurag S. Maskey 	scanconnect_entry();
13406ba597c5SAnurag S. Maskey 	status = dladm_wlan_scan(dld_handle, link_id, &s, get_scan_results);
13416ba597c5SAnurag S. Maskey 	s.nwamd_wifi_scan_last_time = NSEC_TO_SEC(gethrtime());
13426ba597c5SAnurag S. Maskey 	if (!s.nwamd_wifi_scan_changed) {
13436ba597c5SAnurag S. Maskey 		/* Scan may have lost WLANs, if so this qualifies as change */
13446ba597c5SAnurag S. Maskey 		s.nwamd_wifi_scan_changed = (s.nwamd_wifi_scan_curr_num !=
13456ba597c5SAnurag S. Maskey 		    s.nwamd_wifi_scan_last_num);
13466ba597c5SAnurag S. Maskey 	}
13476ba597c5SAnurag S. Maskey 	scanconnect_exit();
13486ba597c5SAnurag S. Maskey 
13496ba597c5SAnurag S. Maskey 	if (status != DLADM_STATUS_OK) {
13506ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "wlan_scan_thread: cannot scan link %s",
13516ba597c5SAnurag S. Maskey 		    s.nwamd_wifi_scan_link);
13526ba597c5SAnurag S. Maskey 		free(linkname);
13536ba597c5SAnurag S. Maskey 		return (NULL);
13546ba597c5SAnurag S. Maskey 	}
13556ba597c5SAnurag S. Maskey 
13566ba597c5SAnurag S. Maskey 	if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
13576ba597c5SAnurag S. Maskey 	    == NULL) {
13586ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "wlan_scan_thread: could not find object  "
13596ba597c5SAnurag S. Maskey 		    "for link %s after doing scan", linkname);
13606ba597c5SAnurag S. Maskey 		free(linkname);
13616ba597c5SAnurag S. Maskey 		return (NULL);
13626ba597c5SAnurag S. Maskey 	}
13636ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
1364f6da83d4SAnurag S. Maskey 	link = &ncu->ncu_link;
13656ba597c5SAnurag S. Maskey 
13666ba597c5SAnurag S. Maskey 	/* For new scan data, add key info from known WLANs */
13676ba597c5SAnurag S. Maskey 	for (i = 0; i < s.nwamd_wifi_scan_curr_num; i++) {
13686ba597c5SAnurag S. Maskey 		if (NEED_ENC(s.nwamd_wifi_scan_curr[i].nww_security_mode)) {
13696ba597c5SAnurag S. Maskey 			char keyname[NWAM_MAX_VALUE_LEN];
13706ba597c5SAnurag S. Maskey 			dladm_wlan_key_t *key = NULL;
13716ba597c5SAnurag S. Maskey 
1372c1976b83Senricop 			/*
1373c1976b83Senricop 			 * If strict_bssid is true, we start checking for
1374c1976b83Senricop 			 * known wlans with the same BSSID.
1375c1976b83Senricop 			 * This would prevent the selection of secobjs
1376c1976b83Senricop 			 * that actually are referenced by different kwl
1377c1976b83Senricop 			 * with the same ESSID.
1378c1976b83Senricop 			 */
1379c1976b83Senricop 			if (wireless_strict_bssid) {
1380c1976b83Senricop 				int b_match = 0;
1381c1976b83Senricop 				(void) nwam_walk_known_wlans(bssid_match,
1382c1976b83Senricop 				    s.nwamd_wifi_scan_curr[i].nww_bssid, 0,
1383c1976b83Senricop 				    &b_match);
1384c1976b83Senricop 				if (b_match == 0)
1385c1976b83Senricop 					continue;
1386c1976b83Senricop 			}
1387c1976b83Senricop 
13886ba597c5SAnurag S. Maskey 			if (known_wlan_get_keyname
13896ba597c5SAnurag S. Maskey 			    (s.nwamd_wifi_scan_curr[i].nww_essid, keyname)
13906ba597c5SAnurag S. Maskey 			    == NWAM_SUCCESS &&
13916ba597c5SAnurag S. Maskey 			    (key = nwamd_wlan_get_key_named(keyname,
13926ba597c5SAnurag S. Maskey 			    s.nwamd_wifi_scan_curr[i].nww_security_mode))
13936ba597c5SAnurag S. Maskey 			    != NULL) {
13946ba597c5SAnurag S. Maskey 				s.nwamd_wifi_scan_curr[i].nww_have_key =
13956ba597c5SAnurag S. Maskey 				    B_TRUE;
13966ba597c5SAnurag S. Maskey 				s.nwamd_wifi_scan_curr[i].nww_keyindex =
13976ba597c5SAnurag S. Maskey 				    s.nwamd_wifi_scan_curr[i].
13986ba597c5SAnurag S. Maskey 				    nww_security_mode ==
13996ba597c5SAnurag S. Maskey 				    DLADM_WLAN_SECMODE_WEP ?
14006ba597c5SAnurag S. Maskey 				    key->wk_idx : 1;
1401c1976b83Senricop 				nlog(LOG_DEBUG, "found matching keyname for \
1402c1976b83Senricop 				    %s", s.nwamd_wifi_scan_curr[i].nww_bssid);
14036ba597c5SAnurag S. Maskey 				free(key);
14046ba597c5SAnurag S. Maskey 			}
14056ba597c5SAnurag S. Maskey 		}
14066ba597c5SAnurag S. Maskey 	}
14076ba597c5SAnurag S. Maskey 	/* Copy scan data into nwamd_link_t */
14086ba597c5SAnurag S. Maskey 	link->nwamd_link_wifi_scan = s;
14096ba597c5SAnurag S. Maskey 	/* Set selected, connected and send scan event if we've got new data */
14106ba597c5SAnurag S. Maskey 	nwamd_set_selected_connected(ncu,
14116ba597c5SAnurag S. Maskey 	    link->nwamd_link_wifi_essid[0] != '\0',
14126ba597c5SAnurag S. Maskey 	    link->nwamd_link_wifi_connected);
14136ba597c5SAnurag S. Maskey 
14146ba597c5SAnurag S. Maskey 	/*
14156ba597c5SAnurag S. Maskey 	 * If wireless selection is not possible because of the current
14166ba597c5SAnurag S. Maskey 	 * state or priority-group, then this was just a scan request.
14176ba597c5SAnurag S. Maskey 	 * Nothing else to do.
14186ba597c5SAnurag S. Maskey 	 */
14196ba597c5SAnurag S. Maskey 	if (!wireless_selection_possible(ncu_obj)) {
14206ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
14216ba597c5SAnurag S. Maskey 		free(linkname);
14226ba597c5SAnurag S. Maskey 		return (NULL);
14236ba597c5SAnurag S. Maskey 	}
14246ba597c5SAnurag S. Maskey 
14256ba597c5SAnurag S. Maskey 	/*
14266ba597c5SAnurag S. Maskey 	 * Check if WLAN is on our known WLAN list. If no
14276ba597c5SAnurag S. Maskey 	 * previously-visited WLANs are found in scan data, set
14286ba597c5SAnurag S. Maskey 	 * new state to NEED_SELECTION (provided we're not currently
14296ba597c5SAnurag S. Maskey 	 * connected, as can be the case during a periodic scan or
14306ba597c5SAnurag S. Maskey 	 * monitor-triggered scan where the signal strength recovers.
14316ba597c5SAnurag S. Maskey 	 */
14326ba597c5SAnurag S. Maskey 	if (!nwamd_find_known_wlan(ncu_obj)) {
14336ba597c5SAnurag S. Maskey 		if (!nwamd_wlan_connected(ncu_obj)) {
14346ba597c5SAnurag S. Maskey 			if (link->nwamd_link_wifi_connected) {
14356ba597c5SAnurag S. Maskey 				nlog(LOG_DEBUG, "wlan_scan_thread: "
14366ba597c5SAnurag S. Maskey 				    "unexpected disconnect after scan");
14376ba597c5SAnurag S. Maskey 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
14386ba597c5SAnurag S. Maskey 				    ncu_obj->nwamd_object_name,
14396ba597c5SAnurag S. Maskey 				    NWAM_STATE_ONLINE_TO_OFFLINE,
14406ba597c5SAnurag S. Maskey 				    NWAM_AUX_STATE_DOWN);
14416ba597c5SAnurag S. Maskey 			} else {
14426ba597c5SAnurag S. Maskey 				nlog(LOG_DEBUG, "wlan_scan_thread: "
14436ba597c5SAnurag S. Maskey 				    "no known WLANs - ask user");
14446ba597c5SAnurag S. Maskey 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
14456ba597c5SAnurag S. Maskey 				    ncu_obj->nwamd_object_name,
14466ba597c5SAnurag S. Maskey 				    NWAM_STATE_OFFLINE_TO_ONLINE,
14476ba597c5SAnurag S. Maskey 				    NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION);
14486ba597c5SAnurag S. Maskey 			}
14496ba597c5SAnurag S. Maskey 		} else {
14506ba597c5SAnurag S. Maskey 			/* still connected. if not online, change to online */
14516ba597c5SAnurag S. Maskey 			nlog(LOG_DEBUG, "wlan_scan_thread: still connected to "
14526ba597c5SAnurag S. Maskey 			    "%s %s", link->nwamd_link_wifi_essid,
14536ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_bssid);
14546ba597c5SAnurag S. Maskey 			if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
14556ba597c5SAnurag S. Maskey 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
14566ba597c5SAnurag S. Maskey 				    ncu_obj->nwamd_object_name,
14576ba597c5SAnurag S. Maskey 				    NWAM_STATE_OFFLINE_TO_ONLINE,
14586ba597c5SAnurag S. Maskey 				    NWAM_AUX_STATE_UP);
14596ba597c5SAnurag S. Maskey 			}
14606ba597c5SAnurag S. Maskey 		}
14616ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
14626ba597c5SAnurag S. Maskey 
14636ba597c5SAnurag S. Maskey 	} else {
14646ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "wlan_scan_thread: found known WLAN %s %s",
14656ba597c5SAnurag S. Maskey 		    link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid);
14666ba597c5SAnurag S. Maskey 
14676ba597c5SAnurag S. Maskey 		if (!nwamd_wlan_connected(ncu_obj)) {
14686ba597c5SAnurag S. Maskey 			/* Copy selected ESSID/BSSID, unlock, call select */
14696ba597c5SAnurag S. Maskey 			(void) strlcpy(essid, link->nwamd_link_wifi_essid,
14706ba597c5SAnurag S. Maskey 			    sizeof (essid));
14716ba597c5SAnurag S. Maskey 			(void) strlcpy(bssid, link->nwamd_link_wifi_bssid,
14726ba597c5SAnurag S. Maskey 			    sizeof (bssid));
14736ba597c5SAnurag S. Maskey 			nwamd_object_release(ncu_obj);
14746ba597c5SAnurag S. Maskey 			(void) nwamd_wlan_select(linkname, essid, bssid,
14756ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_security_mode, B_TRUE);
14766ba597c5SAnurag S. Maskey 		} else {
14776ba597c5SAnurag S. Maskey 			/* still connected.  if not online, change to online */
14786ba597c5SAnurag S. Maskey 			nlog(LOG_DEBUG, "wlan_scan_thread: still connected to "
14796ba597c5SAnurag S. Maskey 			    "known WLAN %s %s", link->nwamd_link_wifi_essid,
14806ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_bssid);
14816ba597c5SAnurag S. Maskey 			if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
14826ba597c5SAnurag S. Maskey 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
14836ba597c5SAnurag S. Maskey 				    ncu_obj->nwamd_object_name,
14846ba597c5SAnurag S. Maskey 				    NWAM_STATE_OFFLINE_TO_ONLINE,
14856ba597c5SAnurag S. Maskey 				    NWAM_AUX_STATE_UP);
14866ba597c5SAnurag S. Maskey 			}
14876ba597c5SAnurag S. Maskey 			nwamd_object_release(ncu_obj);
14886ba597c5SAnurag S. Maskey 		}
14896ba597c5SAnurag S. Maskey 	}
14906ba597c5SAnurag S. Maskey 	free(linkname);
14916ba597c5SAnurag S. Maskey 	return (NULL);
14926ba597c5SAnurag S. Maskey }
14936ba597c5SAnurag S. Maskey 
14946ba597c5SAnurag S. Maskey nwam_error_t
nwamd_wlan_scan(const char * linkname)14956ba597c5SAnurag S. Maskey nwamd_wlan_scan(const char *linkname)
14966ba597c5SAnurag S. Maskey {
14976ba597c5SAnurag S. Maskey 	pthread_t wifi_thread;
14986ba597c5SAnurag S. Maskey 	char *link = strdup(linkname);
14996ba597c5SAnurag S. Maskey 
15006ba597c5SAnurag S. Maskey 	if (link == NULL) {
15016ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_scan: out of memory");
15026ba597c5SAnurag S. Maskey 		return (NWAM_NO_MEMORY);
15036ba597c5SAnurag S. Maskey 	}
15046ba597c5SAnurag S. Maskey 
15056ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_wlan_scan: WLAN scan for %s",
15066ba597c5SAnurag S. Maskey 	    link);
15076ba597c5SAnurag S. Maskey 
15086ba597c5SAnurag S. Maskey 	if (pthread_create(&wifi_thread, NULL, wlan_scan_thread,
15096ba597c5SAnurag S. Maskey 	    link) != 0) {
15106ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_scan: could not start scan");
15116ba597c5SAnurag S. Maskey 		free(link);
15126ba597c5SAnurag S. Maskey 		return (NWAM_ERROR_INTERNAL);
15136ba597c5SAnurag S. Maskey 	}
15146ba597c5SAnurag S. Maskey 	/* detach thread so that it doesn't become a zombie */
15156ba597c5SAnurag S. Maskey 	(void) pthread_detach(wifi_thread);
15166ba597c5SAnurag S. Maskey 	return (NWAM_SUCCESS);
15176ba597c5SAnurag S. Maskey }
15186ba597c5SAnurag S. Maskey 
15196ba597c5SAnurag S. Maskey /*
15206ba597c5SAnurag S. Maskey  * WLAN connection code.
15216ba597c5SAnurag S. Maskey  */
15226ba597c5SAnurag S. Maskey 
15236ba597c5SAnurag S. Maskey static dladm_status_t
do_connect(uint32_t link_id,dladm_wlan_attr_t * attrp,dladm_wlan_key_t * key,uint_t keycount,uint_t flags)15246ba597c5SAnurag S. Maskey do_connect(uint32_t link_id, dladm_wlan_attr_t *attrp, dladm_wlan_key_t *key,
15256ba597c5SAnurag S. Maskey     uint_t keycount, uint_t flags)
15266ba597c5SAnurag S. Maskey {
15276ba597c5SAnurag S. Maskey 	dladm_status_t status;
15286ba597c5SAnurag S. Maskey 	char errmsg[DLADM_STRSIZE];
15296ba597c5SAnurag S. Maskey 
15306ba597c5SAnurag S. Maskey 	scanconnect_entry();
15316ba597c5SAnurag S. Maskey 	status = dladm_wlan_connect(dld_handle, link_id, attrp,
15326ba597c5SAnurag S. Maskey 	    DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, key, keycount, flags);
15336ba597c5SAnurag S. Maskey 	scanconnect_exit();
15346ba597c5SAnurag S. Maskey 
15356ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_do_connect: dladm_wlan_connect returned %s",
15366ba597c5SAnurag S. Maskey 	    dladm_status2str(status, errmsg));
15376ba597c5SAnurag S. Maskey 
15386ba597c5SAnurag S. Maskey 	return (status);
15396ba597c5SAnurag S. Maskey }
15406ba597c5SAnurag S. Maskey 
15416ba597c5SAnurag S. Maskey static void *
wlan_connect_thread(void * arg)15426ba597c5SAnurag S. Maskey wlan_connect_thread(void *arg)
15436ba597c5SAnurag S. Maskey {
15446ba597c5SAnurag S. Maskey 	char *linkname = arg;
15456ba597c5SAnurag S. Maskey 	nwamd_object_t ncu_obj;
15466ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu;
15476ba597c5SAnurag S. Maskey 	nwamd_link_t *link;
15486ba597c5SAnurag S. Maskey 	nwam_error_t err;
15496ba597c5SAnurag S. Maskey 	uint_t	keycount;
15506ba597c5SAnurag S. Maskey 	uint32_t link_id;
15516ba597c5SAnurag S. Maskey 	dladm_wlan_key_t *key = NULL;
15526ba597c5SAnurag S. Maskey 	dladm_wlan_attr_t attr;
15536ba597c5SAnurag S. Maskey 	dladm_status_t status;
15546ba597c5SAnurag S. Maskey 	boolean_t autoconf = B_FALSE;
15556ba597c5SAnurag S. Maskey 
15566ba597c5SAnurag S. Maskey 	if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
15576ba597c5SAnurag S. Maskey 	    == NULL) {
15586ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "wlan_connect_thread: could not find object  "
15596ba597c5SAnurag S. Maskey 		    "for link %s", linkname);
15606ba597c5SAnurag S. Maskey 		free(linkname);
15616ba597c5SAnurag S. Maskey 		return (NULL);
15626ba597c5SAnurag S. Maskey 	}
15636ba597c5SAnurag S. Maskey 
15646ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
1565f6da83d4SAnurag S. Maskey 	link = &ncu->ncu_link;
15666ba597c5SAnurag S. Maskey 
15676ba597c5SAnurag S. Maskey 	if (!wireless_selection_possible(ncu_obj)) {
15686ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "wlan_connect_thread: %s in invalid state or "
15696ba597c5SAnurag S. Maskey 		    "has lower priority", ncu->ncu_name);
15706ba597c5SAnurag S. Maskey 		goto done;
15716ba597c5SAnurag S. Maskey 	}
15726ba597c5SAnurag S. Maskey 
15736ba597c5SAnurag S. Maskey 	/* If it is already connected to the required AP, just return. */
15746ba597c5SAnurag S. Maskey 	if (nwamd_wlan_connected(ncu_obj)) {
15756ba597c5SAnurag S. Maskey 		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
15766ba597c5SAnurag S. Maskey 		    ncu_obj->nwamd_object_name,
15776ba597c5SAnurag S. Maskey 		    ncu_obj->nwamd_object_state, NWAM_AUX_STATE_UP);
15786ba597c5SAnurag S. Maskey 		goto done;
15796ba597c5SAnurag S. Maskey 	}
15806ba597c5SAnurag S. Maskey 
158119cbc0aaSAnurag S. Maskey 	(void) memset(&attr, 0, sizeof (attr));
15826ba597c5SAnurag S. Maskey 	if (dladm_wlan_str2essid(link->nwamd_link_wifi_essid, &attr.wa_essid)
15836ba597c5SAnurag S. Maskey 	    != DLADM_STATUS_OK) {
15846ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "wlan_connect_thread: invalid ESSID '%s' "
15856ba597c5SAnurag S. Maskey 		    "for '%s'", link->nwamd_link_wifi_essid, ncu->ncu_name);
15866ba597c5SAnurag S. Maskey 		goto done;
15876ba597c5SAnurag S. Maskey 	}
15886ba597c5SAnurag S. Maskey 	attr.wa_valid = DLADM_WLAN_ATTR_ESSID;
15896ba597c5SAnurag S. Maskey 
15906ba597c5SAnurag S. Maskey 	/* note: bssid logic here is non-functional */
15916ba597c5SAnurag S. Maskey 	if (link->nwamd_link_wifi_bssid[0] != '\0') {
15926ba597c5SAnurag S. Maskey 		if (dladm_wlan_str2bssid(link->nwamd_link_wifi_bssid,
15936ba597c5SAnurag S. Maskey 		    &attr.wa_bssid) != DLADM_STATUS_OK) {
15946ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "wlan_connect_thread: invalid BSSID '%s'",
15956ba597c5SAnurag S. Maskey 			    "for '%s'", link->nwamd_link_wifi_bssid,
15966ba597c5SAnurag S. Maskey 			    ncu->ncu_name);
15976ba597c5SAnurag S. Maskey 		} else {
15986ba597c5SAnurag S. Maskey 			attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
15996ba597c5SAnurag S. Maskey 		}
16006ba597c5SAnurag S. Maskey 	}
16016ba597c5SAnurag S. Maskey 
16026ba597c5SAnurag S. Maskey 	/* First check for the key */
16036ba597c5SAnurag S. Maskey 	if (NEED_ENC(link->nwamd_link_wifi_security_mode)) {
16046ba597c5SAnurag S. Maskey 		if (link->nwamd_link_wifi_key == NULL) {
16056ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "wlan_connect_thread: could not find "
16066ba597c5SAnurag S. Maskey 			    "key for WLAN '%s'", link->nwamd_link_wifi_essid);
16076ba597c5SAnurag S. Maskey 			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
16086ba597c5SAnurag S. Maskey 			    ncu_obj->nwamd_object_name,
16096ba597c5SAnurag S. Maskey 			    NWAM_STATE_OFFLINE_TO_ONLINE,
16106ba597c5SAnurag S. Maskey 			    NWAM_AUX_STATE_LINK_WIFI_NEED_KEY);
16116ba597c5SAnurag S. Maskey 			goto done;
16126ba597c5SAnurag S. Maskey 		}
16136ba597c5SAnurag S. Maskey 		/* Make a copy of the key as we need to unlock the object */
16146ba597c5SAnurag S. Maskey 		if ((key = calloc(1, sizeof (dladm_wlan_key_t))) == NULL) {
16156ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "wlan_connect_thread: out of memory");
16166ba597c5SAnurag S. Maskey 			goto done;
16176ba597c5SAnurag S. Maskey 		}
16186ba597c5SAnurag S. Maskey 		(void) memcpy(key, link->nwamd_link_wifi_key,
16196ba597c5SAnurag S. Maskey 		    sizeof (dladm_wlan_key_t));
16206ba597c5SAnurag S. Maskey 
16216ba597c5SAnurag S. Maskey 		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
16226ba597c5SAnurag S. Maskey 		attr.wa_secmode = link->nwamd_link_wifi_security_mode;
16236ba597c5SAnurag S. Maskey 		keycount = 1;
16246ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "wlan_connect_thread: retrieved key");
16256ba597c5SAnurag S. Maskey 	} else {
16266ba597c5SAnurag S. Maskey 		key = NULL;
16276ba597c5SAnurag S. Maskey 		keycount = 0;
16286ba597c5SAnurag S. Maskey 	}
16296ba597c5SAnurag S. Maskey 
16306ba597c5SAnurag S. Maskey 	/*
16316ba597c5SAnurag S. Maskey 	 * Connect; only scan if a bssid was not specified.  If it times out,
16326ba597c5SAnurag S. Maskey 	 * try a second time using autoconf.  Drop the object lock during the
16336ba597c5SAnurag S. Maskey 	 * connect attempt since connecting may take some time, and access to
16346ba597c5SAnurag S. Maskey 	 * the link object during that period would be impossible if we held the
16356ba597c5SAnurag S. Maskey 	 * lock.
16366ba597c5SAnurag S. Maskey 	 */
16376ba597c5SAnurag S. Maskey 
16386ba597c5SAnurag S. Maskey 	link->nwamd_link_wifi_autoconf = B_FALSE;
16396ba597c5SAnurag S. Maskey 	link_id = link->nwamd_link_id;
16406ba597c5SAnurag S. Maskey 
16416ba597c5SAnurag S. Maskey 	nwamd_object_release(ncu_obj);
16426ba597c5SAnurag S. Maskey 
16436ba597c5SAnurag S. Maskey 	status = do_connect(link_id, &attr, key, keycount,
16446ba597c5SAnurag S. Maskey 	    DLADM_WLAN_CONNECT_NOSCAN);
16456ba597c5SAnurag S. Maskey 	if (status != DLADM_STATUS_OK) {
16466ba597c5SAnurag S. Maskey 		/* Connect failed, try autoconf */
16476ba597c5SAnurag S. Maskey 		if (!wireless_autoconf || (status = do_connect(link_id, &attr,
16486ba597c5SAnurag S. Maskey 		    NULL, 0, 0)) != DLADM_STATUS_OK) {
16496ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "wlan_connect_thread: connect failed for "
16506ba597c5SAnurag S. Maskey 			    "%s", linkname);
16516ba597c5SAnurag S. Maskey 			goto done_unlocked;
16526ba597c5SAnurag S. Maskey 		}
16536ba597c5SAnurag S. Maskey 		if (status == DLADM_STATUS_OK)
16546ba597c5SAnurag S. Maskey 			autoconf = B_TRUE;
16556ba597c5SAnurag S. Maskey 	}
16566ba597c5SAnurag S. Maskey 
16576ba597c5SAnurag S. Maskey 	/* Connect succeeded, reacquire object */
16586ba597c5SAnurag S. Maskey 	if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
16596ba597c5SAnurag S. Maskey 	    == NULL) {
16606ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "wlan_connect_thread: could not find object  "
16616ba597c5SAnurag S. Maskey 		    "for link %s", linkname);
16626ba597c5SAnurag S. Maskey 		goto done_unlocked;
16636ba597c5SAnurag S. Maskey 	}
16646ba597c5SAnurag S. Maskey 
16656ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
1666f6da83d4SAnurag S. Maskey 	link = &ncu->ncu_link;
16676ba597c5SAnurag S. Maskey 
16686ba597c5SAnurag S. Maskey 	if (autoconf)
16696ba597c5SAnurag S. Maskey 		link->nwamd_link_wifi_autoconf = B_TRUE;
16706ba597c5SAnurag S. Maskey 
16716ba597c5SAnurag S. Maskey 	/*
16726ba597c5SAnurag S. Maskey 	 * If WLAN is WEP/WPA, we would like to test the connection as the key
16736ba597c5SAnurag S. Maskey 	 * may be wrong.  It is difficult to find a reliable test that works
16746ba597c5SAnurag S. Maskey 	 * across APs however.  Do nothing for now.
16756ba597c5SAnurag S. Maskey 	 */
16766ba597c5SAnurag S. Maskey 	link->nwamd_link_wifi_connected = nwamd_wlan_connected(ncu_obj);
16776ba597c5SAnurag S. Maskey 
16786ba597c5SAnurag S. Maskey 	if (link->nwamd_link_wifi_connected) {
16796ba597c5SAnurag S. Maskey 		if (link->nwamd_link_wifi_add_to_known_wlans) {
16806ba597c5SAnurag S. Maskey 			/* add to known WLANs */
16816ba597c5SAnurag S. Maskey 			nlog(LOG_DEBUG, "wlan_connect_thread: "
16826ba597c5SAnurag S. Maskey 			    "add '%s' to known WLANs",
16836ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_essid);
16846ba597c5SAnurag S. Maskey 			if ((err = nwam_known_wlan_add_to_known_wlans
16856ba597c5SAnurag S. Maskey 			    (link->nwamd_link_wifi_essid,
16866ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_bssid[0] != '\0' ?
16876ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_bssid : NULL,
16886ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_security_mode,
16896ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_security_mode ==
16906ba597c5SAnurag S. Maskey 			    DLADM_WLAN_SECMODE_WEP ?
16916ba597c5SAnurag S. Maskey 			    (uint_t)link->nwamd_link_wifi_key->wk_idx : 1,
16926ba597c5SAnurag S. Maskey 			    NEED_ENC(link->nwamd_link_wifi_security_mode) ?
16936ba597c5SAnurag S. Maskey 			    link->nwamd_link_wifi_keyname : NULL))
16946ba597c5SAnurag S. Maskey 			    != NWAM_SUCCESS) {
16956ba597c5SAnurag S. Maskey 				nlog(LOG_ERR, "wlan_connect_thread: "
16966ba597c5SAnurag S. Maskey 				    "could not add to known WLANs: %s",
16976ba597c5SAnurag S. Maskey 				    nwam_strerror(err));
16986ba597c5SAnurag S. Maskey 			}
16996ba597c5SAnurag S. Maskey 		}
17006ba597c5SAnurag S. Maskey 		nwamd_set_selected_connected(ncu, B_TRUE, B_TRUE);
17016ba597c5SAnurag S. Maskey 		nlog(LOG_DEBUG, "wlan_connect_thread: connect "
17026ba597c5SAnurag S. Maskey 		    "succeeded, setting state online");
17036ba597c5SAnurag S. Maskey 		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
17046ba597c5SAnurag S. Maskey 		    ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE,
17056ba597c5SAnurag S. Maskey 		    NWAM_AUX_STATE_UP);
17066ba597c5SAnurag S. Maskey 	}
17076ba597c5SAnurag S. Maskey 
17086ba597c5SAnurag S. Maskey done:
17096ba597c5SAnurag S. Maskey 	nwamd_object_release(ncu_obj);
17106ba597c5SAnurag S. Maskey done_unlocked:
17116ba597c5SAnurag S. Maskey 	free(linkname);
17126ba597c5SAnurag S. Maskey 	free(key);
17136ba597c5SAnurag S. Maskey 
17146ba597c5SAnurag S. Maskey 	return (NULL);
17156ba597c5SAnurag S. Maskey }
17166ba597c5SAnurag S. Maskey 
17176ba597c5SAnurag S. Maskey void
nwamd_wlan_connect(const char * linkname)17186ba597c5SAnurag S. Maskey nwamd_wlan_connect(const char *linkname)
17196ba597c5SAnurag S. Maskey {
17206ba597c5SAnurag S. Maskey 	pthread_t wifi_thread;
17216ba597c5SAnurag S. Maskey 	char *link = strdup(linkname);
17226ba597c5SAnurag S. Maskey 
17236ba597c5SAnurag S. Maskey 	if (link == NULL) {
17246ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_connect: out of memory");
17256ba597c5SAnurag S. Maskey 		return;
17266ba597c5SAnurag S. Maskey 	}
17276ba597c5SAnurag S. Maskey 
17286ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_wlan_connect: WLAN connect for %s",
17296ba597c5SAnurag S. Maskey 	    link);
17306ba597c5SAnurag S. Maskey 
17316ba597c5SAnurag S. Maskey 	if (pthread_create(&wifi_thread, NULL, wlan_connect_thread, link) != 0)
17326ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_connect: could not start connect");
17336ba597c5SAnurag S. Maskey 
17346ba597c5SAnurag S. Maskey 	/* detach thread so that it doesn't become a zombie */
17356ba597c5SAnurag S. Maskey 	(void) pthread_detach(wifi_thread);
17366ba597c5SAnurag S. Maskey }
17376ba597c5SAnurag S. Maskey 
17386ba597c5SAnurag S. Maskey /*
17396ba597c5SAnurag S. Maskey  * Launch signal strength-monitoring thread which periodically
17406ba597c5SAnurag S. Maskey  * checks connection and signal strength.  If we become disconnected
17416ba597c5SAnurag S. Maskey  * or signal drops below threshold specified by wireless_scan_level,
17426ba597c5SAnurag S. Maskey  * initiate a scan.  The scan initiation is taken care of by
17436ba597c5SAnurag S. Maskey  * the call to nwamd_wlan_connected().
17446ba597c5SAnurag S. Maskey  */
17456ba597c5SAnurag S. Maskey static void *
wlan_monitor_signal_thread(void * arg)17466ba597c5SAnurag S. Maskey wlan_monitor_signal_thread(void *arg)
17476ba597c5SAnurag S. Maskey {
17486ba597c5SAnurag S. Maskey 	char *linkname = arg;
17496ba597c5SAnurag S. Maskey 	nwamd_object_t ncu_obj;
17506ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu;
17516ba597c5SAnurag S. Maskey 	nwamd_link_t *link;
17526ba597c5SAnurag S. Maskey 	boolean_t first_time = B_TRUE;
17536ba597c5SAnurag S. Maskey 
17546ba597c5SAnurag S. Maskey 	for (;;) {
17556ba597c5SAnurag S. Maskey 		if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK,
17566ba597c5SAnurag S. Maskey 		    linkname)) == NULL) {
17576ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "wlan_monitor_signal_thread: could "
17586ba597c5SAnurag S. Maskey 			    "not find object for link %s", linkname);
17596ba597c5SAnurag S. Maskey 			break;
17606ba597c5SAnurag S. Maskey 		}
17616ba597c5SAnurag S. Maskey 		ncu = ncu_obj->nwamd_object_data;
1762f6da83d4SAnurag S. Maskey 		link = &ncu->ncu_link;
17636ba597c5SAnurag S. Maskey 
17646ba597c5SAnurag S. Maskey 		/* If the NCU is DISABLED/OFFLINE, exit the monitoring thread */
17656ba597c5SAnurag S. Maskey 		if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE ||
17666ba597c5SAnurag S. Maskey 		    ncu_obj->nwamd_object_state == NWAM_STATE_DISABLED) {
17676ba597c5SAnurag S. Maskey 			nlog(LOG_INFO, "wlan_monitor_signal_thread: "
17686ba597c5SAnurag S. Maskey 			    "%s is %s, stopping thread", linkname,
17696ba597c5SAnurag S. Maskey 			    nwam_state_to_string(ncu_obj->nwamd_object_state));
17706ba597c5SAnurag S. Maskey 			link->nwamd_link_wifi_monitor_thread = 0;
17716ba597c5SAnurag S. Maskey 			nwamd_object_release(ncu_obj);
17726ba597c5SAnurag S. Maskey 			break;
17736ba597c5SAnurag S. Maskey 		}
17746ba597c5SAnurag S. Maskey 
17756ba597c5SAnurag S. Maskey 		/*
17766ba597c5SAnurag S. Maskey 		 * First time thru loop, we check if there is another
17776ba597c5SAnurag S. Maskey 		 * link monitoring thread in operation - if so exit this
17786ba597c5SAnurag S. Maskey 		 * thread.
17796ba597c5SAnurag S. Maskey 		 */
17806ba597c5SAnurag S. Maskey 		if (first_time) {
17816ba597c5SAnurag S. Maskey 			first_time = B_FALSE;
17826ba597c5SAnurag S. Maskey 
17836ba597c5SAnurag S. Maskey 			if (link->nwamd_link_wifi_monitor_thread != 0) {
17846ba597c5SAnurag S. Maskey 				/* Already have a monitor thread for link? */
17856ba597c5SAnurag S. Maskey 				nwamd_object_release(ncu_obj);
17866ba597c5SAnurag S. Maskey 				break;
17876ba597c5SAnurag S. Maskey 			} else {
17886ba597c5SAnurag S. Maskey 				link->nwamd_link_wifi_monitor_thread =
17896ba597c5SAnurag S. Maskey 				    pthread_self();
17906ba597c5SAnurag S. Maskey 			}
17916ba597c5SAnurag S. Maskey 		}
17926ba597c5SAnurag S. Maskey 		if (!nwamd_wlan_connected(ncu_obj)) {
17936ba597c5SAnurag S. Maskey 			nlog(LOG_ERR, "wlan_monitor_signal_thread: "
17946ba597c5SAnurag S. Maskey 			    "disconnect occured for WLAN on link %s", linkname);
17956ba597c5SAnurag S. Maskey 			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
17966ba597c5SAnurag S. Maskey 			    ncu_obj->nwamd_object_name,
17976ba597c5SAnurag S. Maskey 			    NWAM_STATE_ONLINE_TO_OFFLINE,
17986ba597c5SAnurag S. Maskey 			    NWAM_AUX_STATE_DOWN);
17996ba597c5SAnurag S. Maskey 			link->nwamd_link_wifi_monitor_thread = 0;
18006ba597c5SAnurag S. Maskey 			nwamd_object_release(ncu_obj);
18016ba597c5SAnurag S. Maskey 			break;
18026ba597c5SAnurag S. Maskey 		}
18036ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
18046ba597c5SAnurag S. Maskey 		(void) sleep(WIRELESS_MONITOR_SIGNAL_INTERVAL);
18056ba597c5SAnurag S. Maskey 	}
18066ba597c5SAnurag S. Maskey 	free(linkname);
18076ba597c5SAnurag S. Maskey 
18086ba597c5SAnurag S. Maskey 	return (NULL);
18096ba597c5SAnurag S. Maskey }
18106ba597c5SAnurag S. Maskey 
18116ba597c5SAnurag S. Maskey void
nwamd_wlan_monitor_signal(const char * linkname)18126ba597c5SAnurag S. Maskey nwamd_wlan_monitor_signal(const char *linkname)
18136ba597c5SAnurag S. Maskey {
18146ba597c5SAnurag S. Maskey 	pthread_t wifi_thread;
18156ba597c5SAnurag S. Maskey 	char *link = strdup(linkname);
18166ba597c5SAnurag S. Maskey 
18176ba597c5SAnurag S. Maskey 	if (link == NULL) {
18186ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_monitor_signal: out of memory");
18196ba597c5SAnurag S. Maskey 		return;
18206ba597c5SAnurag S. Maskey 	}
18216ba597c5SAnurag S. Maskey 
18226ba597c5SAnurag S. Maskey 	nlog(LOG_DEBUG, "nwamd_wlan_monitor_signal: WLAN monitor for %s",
18236ba597c5SAnurag S. Maskey 	    link);
18246ba597c5SAnurag S. Maskey 
18256ba597c5SAnurag S. Maskey 	if (pthread_create(&wifi_thread, NULL, wlan_monitor_signal_thread,
18266ba597c5SAnurag S. Maskey 	    link) != 0) {
18276ba597c5SAnurag S. Maskey 		nlog(LOG_ERR, "nwamd_wlan_monitor_signal: could not monitor "
18286ba597c5SAnurag S. Maskey 		    "link %s", link);
18296ba597c5SAnurag S. Maskey 		free(link);
18306ba597c5SAnurag S. Maskey 		return;
18316ba597c5SAnurag S. Maskey 	}
18326ba597c5SAnurag S. Maskey 
18336ba597c5SAnurag S. Maskey 	/* detach thread so that it doesn't become a zombie */
18346ba597c5SAnurag S. Maskey 	(void) pthread_detach(wifi_thread);
18356ba597c5SAnurag S. Maskey }
18366ba597c5SAnurag S. Maskey 
18376ba597c5SAnurag S. Maskey void
nwamd_ncu_handle_link_state_event(nwamd_event_t event)18386ba597c5SAnurag S. Maskey nwamd_ncu_handle_link_state_event(nwamd_event_t event)
18396ba597c5SAnurag S. Maskey {
18406ba597c5SAnurag S. Maskey 	nwam_event_t evm;
18416ba597c5SAnurag S. Maskey 	nwamd_object_t ncu_obj;
18426ba597c5SAnurag S. Maskey 	nwamd_ncu_t *ncu;
18436ba597c5SAnurag S. Maskey 	nwamd_link_t *link;
18446ba597c5SAnurag S. Maskey 
18456ba597c5SAnurag S. Maskey 	ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object);
18466ba597c5SAnurag S. Maskey 	if (ncu_obj == NULL) {
1847f6904bc3SRenee Danson Sommerfeld 		nlog(LOG_INFO, "nwamd_ncu_handle_link_state_event: no object "
1848f6904bc3SRenee Danson Sommerfeld 		    "%s", event->event_object);
18496ba597c5SAnurag S. Maskey 		nwamd_event_do_not_send(event);
18506ba597c5SAnurag S. Maskey 		return;
18516ba597c5SAnurag S. Maskey 	}
18526ba597c5SAnurag S. Maskey 	ncu = ncu_obj->nwamd_object_data;
1853f6da83d4SAnurag S. Maskey 	link = &ncu->ncu_link;
18546ba597c5SAnurag S. Maskey 	evm = event->event_msg;
18556ba597c5SAnurag S. Maskey 
18566ba597c5SAnurag S. Maskey 	/*
18576ba597c5SAnurag S. Maskey 	 * We ignore link state events for WiFi because it is very flaky.
18586ba597c5SAnurag S. Maskey 	 * Instead we use the monitor thread and drive WiFi state changes from
18596ba597c5SAnurag S. Maskey 	 * there.
18606ba597c5SAnurag S. Maskey 	 */
18616ba597c5SAnurag S. Maskey 	if (link->nwamd_link_media == DL_WIFI) {
18626ba597c5SAnurag S. Maskey 		nwamd_object_release(ncu_obj);
18636ba597c5SAnurag S. Maskey 		return;
18646ba597c5SAnurag S. Maskey 	}
18656ba597c5SAnurag S. Maskey 
18666ba597c5SAnurag S. Maskey 	/*
18676ba597c5SAnurag S. Maskey 	 * If it's a link up event and we're not disabled, go online.
18686ba597c5SAnurag S. Maskey 	 */
18696ba597c5SAnurag S. Maskey 	if (evm->nwe_data.nwe_link_state.nwe_link_up &&
18706ba597c5SAnurag S. Maskey 	    ncu_obj->nwamd_object_state != NWAM_STATE_DISABLED) {
18716ba597c5SAnurag S. Maskey 
18726ba597c5SAnurag S. Maskey 		if (link->nwamd_link_activation_mode ==
18736ba597c5SAnurag S. Maskey 		    NWAM_ACTIVATION_MODE_PRIORITIZED) {
18746ba597c5SAnurag S. Maskey 			int64_t priority_group;
18756ba597c5SAnurag S. Maskey 
18766ba597c5SAnurag S. Maskey 			(void) pthread_mutex_lock(&active_ncp_mutex);
18776ba597c5SAnurag S. Maskey 			priority_group = current_ncu_priority_group;
18786ba597c5SAnurag S. Maskey 			(void) pthread_mutex_unlock(&active_ncp_mutex);
18796ba597c5SAnurag S. Maskey 
18806ba597c5SAnurag S. Maskey 			/* compare priority groups */
18816ba597c5SAnurag S. Maskey 			if (link->nwamd_link_priority_group > priority_group) {
18826ba597c5SAnurag S. Maskey 				nlog(LOG_DEBUG,
18836ba597c5SAnurag S. Maskey 				    "nwamd_ncu_handle_link_state_event: "
18846ba597c5SAnurag S. Maskey 				    "got LINK UP event for priority group "
18856ba597c5SAnurag S. Maskey 				    "%lld, less preferred than current %lld, "
18866ba597c5SAnurag S. Maskey 				    "ignoring",
18876ba597c5SAnurag S. Maskey 				    link->nwamd_link_priority_group,
18886ba597c5SAnurag S. Maskey 				    priority_group);
18896ba597c5SAnurag S. Maskey 
18906ba597c5SAnurag S. Maskey 			} else if (link->nwamd_link_priority_group ==
18916ba597c5SAnurag S. Maskey 			    priority_group) {
18926ba597c5SAnurag S. Maskey 				nlog(LOG_DEBUG,
18936ba597c5SAnurag S. Maskey 				    "nwamd_ncu_handle_link_state_event: "
18946ba597c5SAnurag S. Maskey 				    "got LINK UP event for priority group "
18956ba597c5SAnurag S. Maskey 				    "%lld, same as current %lld",
18966ba597c5SAnurag S. Maskey 				    link->nwamd_link_priority_group,
18976ba597c5SAnurag S. Maskey 				    priority_group);
18986ba597c5SAnurag S. Maskey 				/*
18996ba597c5SAnurag S. Maskey 				 * Change link state to UP.  It will be
19006ba597c5SAnurag S. Maskey 				 * propagated to IP state machine.  Only do
19016ba597c5SAnurag S. Maskey 				 * the NCU check if and when the interface
19026ba597c5SAnurag S. Maskey 				 * NCU is online.
19036ba597c5SAnurag S. Maskey 				 */
19046ba597c5SAnurag S. Maskey 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
19056ba597c5SAnurag S. Maskey 				    event->event_object,
19066ba597c5SAnurag S. Maskey 				    NWAM_STATE_OFFLINE_TO_ONLINE,
19076ba597c5SAnurag S. Maskey 				    NWAM_AUX_STATE_UP);
19086ba597c5SAnurag S. Maskey 			} else {
19096ba597c5SAnurag S. Maskey 				nlog(LOG_DEBUG,
19106ba597c5SAnurag S. Maskey 				    "nwamd_ncu_handle_link_state_event: "
19116ba597c5SAnurag S. Maskey 				    "got LINK UP event for priority group "
19126ba597c5SAnurag S. Maskey 				    "%lld, more preferred than current %lld",
19136ba597c5SAnurag S. Maskey 				    link->nwamd_link_priority_group,
19146ba597c5SAnurag S. Maskey 				    priority_group);
19156ba597c5SAnurag S. Maskey 
19166ba597c5SAnurag S. Maskey 				/*
19176ba597c5SAnurag S. Maskey 				 * We need to mark the link as up so that when
19186ba597c5SAnurag S. Maskey 				 * it is activated we will bring the interface
19196ba597c5SAnurag S. Maskey 				 * up.
19206ba597c5SAnurag S. Maskey 				 */
19216ba597c5SAnurag S. Maskey 				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
19226ba597c5SAnurag S. Maskey 				    event->event_object,
19236ba597c5SAnurag S. Maskey 				    NWAM_STATE_OFFLINE_TO_ONLINE,
19246ba597c5SAnurag S. Maskey 				    NWAM_AUX_STATE_UP);
19256ba597c5SAnurag S. Maskey 				nwamd_object_release(ncu_obj);
19266ba597c5SAnurag S. Maskey 				nwamd_ncp_deactivate_priority_group
19276ba597c5SAnurag S. Maskey 				    (priority_group);
19286ba597c5SAnurag S. Maskey 				nwamd_ncp_activate_priority_group
19296ba597c5SAnurag S. Maskey 				    (link->nwamd_link_priority_group);
19306ba597c5SAnurag S. Maskey 				return;
19316ba597c5SAnurag S. Maskey 			}
19326ba597c5SAnurag S. Maskey 
19336ba597c5SAnurag S. Maskey 		} else if (link->nwamd_link_activation_mode ==
19346ba597c5SAnurag S. Maskey 		    NWAM_ACTIVATION_MODE_MANUAL) {
19356ba597c5SAnurag S. Maskey 			nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: "
19366ba597c5SAnurag S. Maskey 			    "got LINK UP event for manual NCU %s",
19376ba597c5SAnurag S. Maskey 			    ncu_obj->nwamd_object_name);
19386ba597c5SAnurag S. Maskey 
19396ba597c5SAnurag S. Maskey 			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
19406ba597c5SAnurag S. Maskey 			    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
19416ba597c5SAnurag S. Maskey 			    NWAM_AUX_STATE_UP);
19426ba597c5SAnurag S. Maskey 		}
19436ba597c5SAnurag S. Maskey 	}
19446ba597c5SAnurag S. Maskey 
19456ba597c5SAnurag S. Maskey 	/*
19466ba597c5SAnurag S. Maskey 	 * If the link is down then start or continue transition down.
19476ba597c5SAnurag S. Maskey 	 */
19486ba597c5SAnurag S. Maskey 	if (!evm->nwe_data.nwe_link_state.nwe_link_up &&
19496ba597c5SAnurag S. Maskey 	    (ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE ||
19506ba597c5SAnurag S. Maskey 	    ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE)) {
19516ba597c5SAnurag S. Maskey 
19526ba597c5SAnurag S. Maskey 		if (link->nwamd_link_activation_mode ==
19536ba597c5SAnurag S. Maskey 		    NWAM_ACTIVATION_MODE_PRIORITIZED) {
19546ba597c5SAnurag S. Maskey 			nlog(LOG_DEBUG,
19556ba597c5SAnurag S. Maskey 			    "nwamd_ncu_handle_link_state_event: "
19566ba597c5SAnurag S. Maskey 			    "got LINK DOWN for priority group %lld",
19576ba597c5SAnurag S. Maskey 			    link->nwamd_link_priority_group);
19586ba597c5SAnurag S. Maskey 			/* Moving to offline checks priority group */
19596ba597c5SAnurag S. Maskey 		} else {
19606ba597c5SAnurag S. Maskey 			nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: "
19616ba597c5SAnurag S. Maskey 			    "got LINK DOWN event for manual NCU %s",
19626ba597c5SAnurag S. Maskey 			    ncu_obj->nwamd_object_name);
19636ba597c5SAnurag S. Maskey 		}
19646ba597c5SAnurag S. Maskey 		nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
19656ba597c5SAnurag S. Maskey 		    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
19666ba597c5SAnurag S. Maskey 		    NWAM_AUX_STATE_DOWN);
19676ba597c5SAnurag S. Maskey 	}
19686ba597c5SAnurag S. Maskey 
19696ba597c5SAnurag S. Maskey 	nwamd_object_release(ncu_obj);
19706ba597c5SAnurag S. Maskey }
1971