wireless.c revision b00044a2eb43864b8718585d21949611a2ee59ef
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file contains all the routines to handle wireless (more
* accurately, 802.11 "WiFi" family only at this moment) operations.
* This is only phase 0 work so the handling is pretty simple.
*
* When the daemon starts up, for each WiFi interface detected, it'll
* spawn a thread doing an access point (AP) scanning. After the scans
* finish and if one of the WiFi interfaces is chosen to be active, the
* code will send a message to the GUI, which then must gather the results.
*
* also maintains a list of known WiFi APs in the file KNOWN_WIFI_NETS.
* be added to that file. This file is used in the following way.
*
* If the AP scan results contain one known AP (plus any number of unknown
* APs), the code will automatically connect to that AP without contacting the
* GUI. But if the detected signal strength of that one known AP is weaker
* than any of the unknown APs, the code will block on the GUI.
*
* If the AP scan results contain more than one known APs or no known APs, the
* GUI is notified.
*
* Note that not all APs broadcast the Beacon. And some events may
* happen during the AP scan such that not all available APs are found.
* Thus, the GUI can specify an AP's data.
*
* The code also periodically (specified by wlan_scan_interval) checks
* for the health of the AP connection. If the signal strength of the
* connected AP drops below a threshold (specified by wireless_scan_level),
* the code will try to do another scan to find out other APs available.
* If there is currently no connected AP, a scan will also be done
* periodically to look for available APs. In both cases, if there are
* new APs, the above AP connection procedure will be performed.
*
* Lock ordering note: wifi_mutex and wifi_init_mutex are not held at the same
* time.
*/
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <syslog.h>
#include <limits.h>
#include <errno.h>
#include <syslog.h>
#include <pthread.h>
#include <stropts.h>
#include <fcntl.h>
#include <libdladm.h>
#include <libdllink.h>
#include <libinetutil.h>
#include <libgen.h>
#include "defines.h"
#include "structures.h"
#include "functions.h"
#include "variables.h"
static pthread_mutex_t wifi_mutex;
typedef enum {
ESSID = 0,
/*
* List of wireless interfaces; protected by wifi_mutex.
*/
static uint_t wi_link_count;
/*
* Is a wireless interface doing a scan currently? We only allow one
* wireless interface to do a scan at any one time. This is to
* avoid unnecessary interference. The following variable is used
* to store the interface doing the scan. It is protected by
* wifi_init_mutex.
*/
static char wifi_scan_intf[LIFNAMSIZ];
/*
* Array of wireless LAN entries; protected by wifi_mutex.
*/
static struct wireless_lan *wlans;
static boolean_t new_ap_found;
static int store_key(struct wireless_lan *);
static dladm_wlan_key_t *retrieve_key(const char *, const char *,
static struct wireless_lan *add_wlan_entry(const char *, const char *,
const char *, dladm_wlan_attr_t *);
wireless_if_t *);
static struct wireless_lan *find_wlan_entry(const char *, const char *,
const char *);
static void free_wireless_lan(struct wireless_lan *);
static int add_known_wifi_nets_file(const char *, const char *);
static boolean_t known_wifi_nets_lookup(const char *, const char *, char *);
#define WIRELESS_LAN_INIT_COUNT 8
/*
* The variable wlan_scan_interval controls the interval in seconds
* between periodic scans.
*/
/*
* The variable wireless_scan_level specifies the lowest signal level
* when a periodic wireless scan needs to be done.
*/
void
initialize_wireless(void)
{
(void) pthread_mutexattr_init(&wifi_mutex_attr);
(void) pthread_mutexattr_settype(&wifi_mutex_attr,
}
void
add_wireless_if(const char *ifname)
{
NULL);
if (pthread_mutex_lock(&wifi_mutex) == 0) {
(void) pthread_mutex_unlock(&wifi_mutex);
} else {
}
}
}
static wireless_if_t *
find_wireless_if(const char *ifname)
{
return (wip);
}
return (NULL);
}
void
remove_wireless_if(const char *ifname)
{
if (pthread_mutex_lock(&wifi_mutex) == 0) {
}
(void) pthread_mutex_unlock(&wifi_mutex);
}
}
/*
* wlan is expected to be non-NULL.
*/
static return_vals_t
{
/*
* First, test if we have key stored as secobj. If so,
* no need to prompt for it.
*/
dprintf("get_user_key: retrieve_key() returns non NULL");
return (SUCCESS);
} else if (request_wlan_key(wlan)) {
return (WAITING);
} else {
return (FAILURE);
}
}
/*
* This function assumes that wifi_mutex is held. If bssid is specified, then
* an exact match is returned. If it's not specified, then the best match is
* returned.
*/
static struct wireless_lan *
{
continue;
if (bssid[0] == '\0') {
} else {
return (wlan);
}
}
return (best);
}
static void
{
/* empty string is not allocated */
}
/*
* This function assumes that wifi_mutex is held.
*/
static struct wireless_lan *
{
char strength[DLADM_STRSIZE];
struct wireless_lan *wlan;
if (wireless_lan_used == wireless_lan_count) {
int newcnt;
newcnt = (wireless_lan_count == 0) ?
return (NULL);
}
}
/* do not do allocation for zero-length */
return (NULL);
}
return (wlan);
}
/*
* Remove entries that are no longer seen on the network. The caller does not
* hold wifi_mutex, but is the only thread that can modify the wlan list.
* Retain connected entries, as lack of visibility in a scan may just be a
* temporary condition (driver problem) and may not reflect an actual
* disconnect.
*/
static boolean_t
clear_unscanned_entries(const char *ifname)
{
if (pthread_mutex_lock(&wifi_mutex) != 0)
return (B_FALSE);
wlput++;
} else {
}
}
(void) pthread_mutex_unlock(&wifi_mutex);
return (dropped);
}
/*
* Verify if a WiFi NIC is associated with the given ESSID and BSSID. If the
* given ESSID is NULL, and if the NIC is already connected, return true.
* Otherwise,
*
* 2. If the NIC is not associated with any AP, return false.
* 3. If the NIC is associated with a different AP, tear down IP interface,
* tell the driver to disassociate with AP, and then return false.
*/
static boolean_t
{
char cur_essid[DLADM_STRSIZE];
char cur_bssid[DLADM_STRSIZE];
char errmsg[DLADM_STRSIZE];
if (status != DLADM_STATUS_OK) {
dprintf("check_wlan: dladm_wlan_get_linkattr() for %s "
return (B_FALSE);
}
return (B_FALSE);
/* If we're expecting "any" connection, then we're done. */
return (B_TRUE);
/* Is the NIC associated with the expected access point? */
dprintf("wrong ESSID: have %s expect %s; taking down",
goto unexpected;
}
return (B_TRUE);
return (B_TRUE);
dprintf("wrong BSSID: have %s expect %s; taking down",
if (sendevent) {
/* If not, then shut the interface down normally */
}
return (B_FALSE);
}
/*
* Examine all WLANs associated with an interface, and update the 'connected'
* and 'known' attributes appropriately. The caller holds wifi_mutex.
*/
static boolean_t
{
char essid[DLADM_STRSIZE];
char bssid[DLADM_STRSIZE];
int retries = 0;
/*
* This is awful, but some wireless drivers (particularly 'ath') will
* erroneously report "disconnected" if queried right after a scan. If
* we see 'down' reported here, we retry a few times to make sure.
*/
while (retries++ < 4) {
break;
}
bssid);
} else {
}
continue;
} else {
}
}
&attr.la_wlan_attr);
}
/*
* If we're already connected but not yet known, then it's
* clear that this is a "known wlan" for the user. He must
* have issued a dladm connect-wifi command to get here.
*/
}
}
return (connected);
}
/*
* Given a wireless interface, use it to scan for available networks. The
* caller must not hold wifi_mutex.
*/
static void
scan_wireless_nets(const char *ifname)
{
int i;
/*
* If there is already a scan in progress, defer until the scan is done
* to avoid radio interference. But if the interface doing the scan is
* the same as the one requesting the new scan, then wait for it to
* finish, and then we're done.
*/
if (pthread_mutex_lock(&wifi_init_mutex) != 0)
return;
while (wifi_scan_intf[0] != '\0') {
dprintf("scan_wireless_nets in progress: old %s new %s",
if (already_done) {
(void) pthread_mutex_unlock(&wifi_init_mutex);
return;
}
}
(void) pthread_mutex_unlock(&wifi_init_mutex);
/* Grab the linkid from the wireless interface */
if (pthread_mutex_lock(&wifi_mutex) != 0)
goto scan_end;
(void) pthread_mutex_unlock(&wifi_mutex);
dprintf("aborted scan on %s; unable to locate interface",
ifname);
goto scan_end;
}
(void) pthread_mutex_unlock(&wifi_mutex);
/*
* Since only one scan is allowed at any one time, and only scans can
* modify the list, there's no need to grab a lock in checking
* wireless_lan_used or the wlans list itself, or for the new_ap_found
* global.
*
* All other threads must hold the mutex when reading this data, and
* this thread must hold the mutex only when writing portions that
* those other threads may read.
*/
for (i = 0; i < wireless_lan_used; i++)
if (status == DLADM_STATUS_OK) {
} else {
}
/* Need to sample this global before clearing out scan lock */
if (pthread_mutex_lock(&wifi_mutex) == 0) {
(void) update_connected_wlan(wip);
}
(void) pthread_mutex_unlock(&wifi_mutex);
}
(void) pthread_mutex_lock(&wifi_init_mutex);
wifi_scan_intf[0] = '\0';
(void) pthread_cond_broadcast(&wifi_init_cond);
(void) pthread_mutex_unlock(&wifi_init_mutex);
if (status == DLADM_STATUS_OK)
if (new_found) {
}
}
/*
* Rescan all wireless interfaces. This routine intentionally does not hold
* wifi_mutex during the scan, as scans can take a long time to accomplish, and
* there may be more than one wireless interface. The counter is used to make
* sure that we don't run "forever" if the list is changing quickly.
*/
static void
rescan_wifi_no_lock(void)
{
if (pthread_mutex_lock(&wifi_mutex) != 0)
return;
/* Even less than "very weak" */
wip->wi_strength = 0;
(void) pthread_mutex_unlock(&wifi_mutex);
if (pthread_mutex_lock(&wifi_mutex) != 0)
return;
else
}
(void) pthread_mutex_unlock(&wifi_mutex);
}
/*
* This thread is given the name of the interface to scan, and must free that
* name when done.
*/
static void *
scan_thread(void *arg)
{
return (NULL);
}
/*
* Launch a thread to scan the given wireless interface. We copy the interface
* name over to allocated storage because it's not possible to hand off a lock
* on the interface list to the new thread, and the caller's storage (our input
* argument) isn't guaranteed to be stable after we return to the caller.
*/
int
launch_wireless_scan(const char *ifname)
{
int retv;
char *winame;
return (ENOMEM);
return (retv);
}
} else if (wip->wi_scan_running) {
retv = EINPROGRESS;
} else {
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr,
if (retv == 0)
}
(void) pthread_mutex_unlock(&wifi_mutex);
/* If thread not started, then discard the name. */
if (retv != 0)
return (retv);
}
/*
* Caller does not hold wifi_mutex.
*/
static boolean_t
{
struct wireless_lan *wlan;
char essid_name[DLADM_STRSIZE];
char bssid_name[DLADM_STRSIZE];
/*
* Check whether ESSID is "hidden".
* If so try to substitute it with the ESSID from the
* known_wifi_nets with the same BSSID
*/
if (essid_name[0] == '\0') {
essid_name) &&
dprintf("Using ESSID %s with BSSID %s",
}
}
if (pthread_mutex_lock(&wifi_mutex) != 0)
return (B_FALSE);
(void) pthread_mutex_unlock(&wifi_mutex);
return (B_FALSE);
}
/* Remember the strongest we encounter */
NULL) {
} else {
}
(void) pthread_mutex_unlock(&wifi_mutex);
return (retv);
}
/* ARGSUSED */
void *
periodic_wireless_scan(void *arg)
{
/*
* No periodic scan if the "-i" option is used to change the
* interval to 0.
*/
if (wlan_scan_interval == 0)
return (NULL);
for (;;) {
int ret;
if (ret == -1) {
continue;
break;
}
/* Get current profile name, if any */
/*
* We do a scan if
*
* 1. There is no active profile. Or
* 2. Profile is wireless and we're not connected to the AP. Or
* 3. The signal strength falls below a certain specified level.
*/
if (ifname[0] != '\0') {
if (ift != IF_WIRELESS)
continue;
/*
* If these things fail, it means that our wireless
* link isn't viable. Proceed in that way.
*/
NULL) != DLADM_STATUS_OK ||
}
continue;
}
}
/* Rescan the wireless interfaces */
if (ifname[0] != '\0') {
/*
* If we're still connected and there's nothing better
* around, then there's no point in switching now.
*/
if (pthread_mutex_lock(&wifi_mutex) != 0)
continue;
wip->wi_strength <=
(void) pthread_mutex_unlock(&
continue;
}
}
(void) pthread_mutex_unlock(&wifi_mutex);
/*
* Try to work around known driver bugs: if the driver
* says we're disconnected, then tell it to disconnect
* for sure.
*/
(void) dladm_wlan_disconnect(linkid);
/*
* Deactivate the original AP. If we reached this
* point, we either were not connected, or were
* connected with "very weak" signal strength; so we're
* assuming that having this llp active was not very
* useful. So we deactivate.
*/
(void) np_queue_add_event(
}
}
return (NULL);
}
/*
* and dladm_get_secobj().
*/
/*
* Convert key hexascii string to raw secobj value. This
* code is very similar to convert_secobj() in dladm.c, it would
* be good to have a libdladm function to convert values.
*/
static int
{
if (buf_len == 0) {
/* length zero means "delete" */
return (0);
}
if (class == DLADM_SECOBJ_CLASS_WPA) {
/*
* Per IEEE802.11i spec, the Pre-shared key (PSK) length should
* be between 8 and 63.
*/
"key_string_to_secobj_value:"
" invalid WPA key length: buf_len = %d", buf_len);
return (-1);
}
return (0);
}
switch (buf_len) {
case 5: /* ASCII key sizes */
case 13:
break;
case 10:
case 26: /* Hex key sizes, not preceded by 0x */
!= 0) {
"key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
case 12:
case 28: /* Hex key sizes, preceded by 0x */
obj_lenp) != 0) {
"key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
default:
"key_string_to_secobj_value: invalid WEP key length");
return (-1);
}
return (0);
}
/*
* Print the key format into the appropriate field, then convert any ":"
* characters to ".", as ":[1-4]" is the slot indicator, which otherwise
* would trip us up. The third parameter is expected to be of size
* DLADM_SECOBJ_NAME_MAX.
*
* (Note that much of the system uses DLADM_WLAN_MAX_KEYNAME_LEN, which is 64
* rather than 32, but that dladm_get_secobj will fail if a length greater than
* DLD_SECOBJ_NAME_MAX is seen, and that's 32. This is all horribly broken.)
*/
static void
{
if (bssid[0] == '\0')
else
for (i = 0; i < len; i++)
if (name[i] == ':')
name[i] = '.';
}
static int
{
char obj_name[DLADM_SECOBJ_NAME_MAX];
char errmsg[DLADM_STRSIZE];
/*
* Name key object for this WLAN so it can be later retrieved
*/
class) != 0) {
/* above function logs internally on failure */
return (-1);
}
/* we've validated the new key, so remove the old one */
"'%s' for key: %s", obj_name,
return (-1);
}
/* if we're just deleting the key, then we're done */
return (0);
if (status != DLADM_STATUS_OK) {
"'%s' for key: %s", obj_name,
return (-1);
}
/*
* We don't really need to retrieve the key we just stored, but
* we do need to set the cooked key, and the function below takes
* care of allocating memory and setting the length and slot ID
* besides just copying the value, so it is simpler just to call
* the retrieve function instead of doing it all here.
*
* Since we just stored the key, retrieve_key() "shouldn't"
* fail. If it does fail, it's not the end of the world; a NULL
* value for wlan->cooked_key simply means this particular
* attempt to connect will fail, and alternative connection
* options will be used.
*/
return (0);
}
/*
* retrieve_key returns NULL if no key was recovered from libdladm
*/
static dladm_wlan_key_t *
{
char errmsg[DLADM_STRSIZE];
/*
* Newly-allocated key must be freed by caller, or by
* subsequent call to retrieve_key().
*/
return (NULL);
}
/*
* Set name appropriately to retrieve key for this WLAN. Note that we
* cannot use the actual wk_name buffer size, as it's two times too
* large for dladm_get_secobj.
*/
dprintf("retrieve_key: len = %d, object = %s\n",
/* Try the kernel first, then fall back to persistent storage. */
if (status != DLADM_STATUS_OK) {
dprintf("retrieve_key: dladm_get_secobj(TEMP) failed: %s",
}
switch (status) {
case DLADM_STATUS_OK:
dprintf("retrieve_key: dladm_get_secobj succeeded: len %d",
cooked_key->wk_len);
break;
case DLADM_STATUS_NOTFOUND:
/*
* We do not want an error in the case that the secobj
* is not found, since we then prompt for it.
*/
return (NULL);
default:
return (NULL);
}
return (NULL);
}
return (cooked_key);
}
/*
* Add an entry to known_wifi_nets file given the parameters. The caller holds
* wifi_mutex.
*/
static int
{
int retv;
/* Create the NWAM directory in case it does not exist. */
} else {
/* now add this to the file */
retv = 0;
}
return (retv);
}
static int
{
char *cp;
int retv;
return (errno);
return (retv);
}
cp++;
continue;
}
/* skip over the essid to examine bssid */
cp++;
cp++;
/*
* Deleting with bssid empty means "all entries under this
* essid." As a result, deleting a wildcard entry for a bssid
* means deleting all entries for that bssid.
*/
if (bssidlen == 0 ||
/* delete this entry */
continue;
}
}
if (found) {
retv = 0;
} else {
(void) unlink(KNOWN_WIFI_TMP);
}
} else {
(void) unlink(KNOWN_WIFI_TMP);
}
return (retv);
}
/*
* Check if the given AP (ESSID, BSSID pair) is on the known AP list.
* If found_essid is non-NULL and the match is found (B_TRUE is returned)
* the matched ESSID is copied out into buffer pointed by found_essid.
* The buffer is expected to be at least DLADM_STRSIZE bytes long.
*/
static boolean_t
char *found_essid)
{
char *cp;
char *tok[MAX_FIELDS];
int line_num;
/*
* For now the file format is:
* essid\tbssid
* (essid followed by tab followed by bssid)
*/
return (B_FALSE);
cp++;
continue;
continue;
}
/*
* If BSSID match is found we check ESSID, which should
* either match as well, or be an empty string.
* In latter case we'll retrieve the ESSID from known_wifi_nets
* later.
*/
/*
* Got BSSID match, either ESSID was not specified,
* or it should match
*/
if (*new_essid == '\0' ||
break;
}
}
}
if (found) {
if (found_essid != NULL)
}
return (found);
}
static uint_t
{
char *cp;
char *tok[MAX_FIELDS];
char key[DLADM_SECOBJ_NAME_MAX];
cp++;
continue;
continue;
count++;
else
kap++;
}
}
return (count);
}
{
int retv;
return (kap);
}
NULL);
}
}
(void) pthread_mutex_unlock(&wifi_mutex);
return (kap);
}
int
{
int retv;
/*
* First check the current LLP. If there is one, then its connection
* state determines what to do after adding the known AP to the list.
* If not, then we act if there are no connected APs.
*/
return (retv);
/*
* If this is in our list of scanned APs and if no interface is
* connected, then we have a reevaluation event.
*/
wlan++) {
/*
* If LLP is selected, then ignore all others. Only
* the state of this one interface is at issue.
*/
if (ifname[0] != '\0' &&
continue;
(bssid[0] == '\0' ||
}
}
if (!any_connected && one_matches) {
(void) np_queue_add_event(EV_RESELECT,
}
}
(void) pthread_mutex_unlock(&wifi_mutex);
return (retv);
}
int
{
int retv;
struct wireless_lan *wlan;
return (retv);
if (retv == 0) {
wlan++) {
(bssid[0] == '\0' ||
(void) dladm_wlan_disconnect(wip->
}
(void) np_queue_add_event(EV_RESELECT,
wlan->wl_if_name);
}
}
}
(void) pthread_mutex_unlock(&wifi_mutex);
return (retv);
}
/*
* reqlan->essid is required (i.e., cannot be zero-length)
* reqlan->bssid is optional (i.e., may be zero-length)
*/
static return_vals_t
{
char errmsg[DLADM_STRSIZE];
/* try to apply essid selected by the user */
return (FAILURE);
/* If it is already connected to the required AP, just return. */
return (SUCCESS);
"connect_chosen_lan: invalid ESSID '%s' for '%s'",
return (FAILURE);
}
"connect_chosen_lan: invalid BSSID '%s' for '%s'",
return (FAILURE);
}
}
/* First check for the key */
/* Note that this happens only for known APs from the list */
return (rval);
}
keycount = 1;
dprintf("connect_chosen_lan: retrieved key");
} else {
keycount = 0;
}
/*
* Connect; only scan if a bssid was not specified.
* If it times out and we were trying with a bssid,
* try a second time with just the ESSID.
*/
dprintf("connect_chosen_lan: dladm_wlan_connect returned %s",
"trying again with just (%s)",
flags = 0;
}
if (status == DLADM_STATUS_OK) {
return (SUCCESS);
} else {
"connect_chosen_lan: connect to '%s' failed on '%s': %s",
return (FAILURE);
}
}
/*
* First attempt to connect to the network specified by essid.
* If that fails, attempt to connect using autoconf.
*/
static return_vals_t
{
"Could not connect to chosen WLAN %s, going to auto-conf",
}
return (rval);
}
/*
* is used by the GUI to check for connectivity before doing anything
* destructive.
*/
{
if (pthread_mutex_lock(&wifi_mutex) != 0)
return (B_FALSE);
} else {
}
(void) pthread_mutex_unlock(&wifi_mutex);
return (retv);
}
/*
* This is the entry point for GUI "select access point" requests. We attempt
* to do what the GUI requested. If we fail, then there will be a new request
* enqueued for the GUI to act on.
* Returns:
* 0 - ok (or more data requested with new event)
* ENXIO - no such interface
* ENODEV - requested access point unknown
* EINVAL - failed to perform requested action
*/
int
{
int retv;
return (EINVAL);
return (retv);
goto done;
}
/* This is an autoconf request. */
goto done;
}
wlan = &local_wlan;
}
retv = 0;
/*
* now attempt to connect to selection
*/
case WAITING:
break;
case SUCCESS:
/*
* Succeeded, so add entry to known_essid_list_file;
* but first make sure the wlan->bssid isn't empty.
* Note that empty bssid is never allocated.
*/
char lclbssid[DLADM_STRSIZE];
&attr);
if (status == DLADM_STATUS_OK) {
(void) dladm_wlan_bssid2str(
} else {
dprintf("failed to get linkattr after "
}
}
} else {
/* Don't leave it as NULL (for simplicity) */
}
break;
default:
break;
}
done:
if (retv != 0) {
/*
* Failed to connect. Set 'rescan' flag so that we treat this
* AP as new if it's seen again, because the wireless radio may
* have just been off briefly while we were trying to connect.
*/
}
(void) pthread_mutex_unlock(&wifi_mutex);
/*
* If this is the selected profile, then go ahead and bring up IP now.
*/
return (retv);
}
int
const char *key)
{
struct wireless_lan *wlan;
int retv;
if (ift == IF_UNKNOWN)
return (ENXIO);
if (ift != IF_WIRELESS)
return (EINVAL);
return (retv);
else
retv = 0;
(void) pthread_mutex_unlock(&wifi_mutex);
if (retv == 0)
return (retv);
}
static boolean_t
{
if (!autoconf)
return (B_FALSE);
}
/* If the NIC is already associated with something, just return. */
return (B_TRUE);
/*
* Do autoconf, relying on the heuristics used by dladm_wlan_connect()
* to cycle through WLANs detected in priority order, attempting
* to connect.
*/
DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, NULL, 0, 0);
if (status != DLADM_STATUS_OK) {
char errmsg[DLADM_STRSIZE];
"wlan_autoconf: dladm_wlan_connect failed for '%s': %s",
return (B_FALSE);
}
return (B_TRUE);
}
/*
* This function searches through the wlans[] array and determines which ones
* have been visited before.
*
* If exactly one has been visited before, and it has the highest signal
* strength, then we attempt to connect to it right away.
*
* In all other cases -- if none have been visited before, or more than one was
* visited, or if the one that was visited doesn't have the highest signal
* strength, or if the automatic connect attempt fails for any reason -- then
* we hand over the data to the GUI for resolution. The user will have to be
* prompted for a choice.
*
* If no GUI exists, we'll get back FAILURE (instead of WAITING), which will
* cause the autoconf mechanism to run instead.
*/
handle_wireless_lan(const char *ifname)
{
struct wireless_lan *most_recent;
/*
* We wait while a scan is in progress. Since we allow a user
* to initiate a re-scan, we can proceed even when no scan
* has been done to fill in the AP list.
*/
if (pthread_mutex_lock(&wifi_init_mutex) != 0)
return (FAILURE);
while (wifi_scan_intf[0] != '\0')
(void) pthread_mutex_unlock(&wifi_init_mutex);
if (pthread_mutex_lock(&wifi_mutex) != 0)
return (FAILURE);
goto finished;
}
if (wip->wi_wireless_done) {
dprintf("handle_wireless_lan: skipping policy scan; done");
goto finished;
}
dprintf("handle_wireless_lan: starting policy scan");
most_recent = NULL;
/*
* Try to see if any of the wifi nets currently available
* has been used previously. If more than one available
* nets has been used before, then prompt user with
* all the applicable previously wifi nets, and ask which
* one to connect to.
*/
/* Find the AP with the highest signal. */
NULL))
/*
* The ESSID comparison here mimics what the "already
* in visited wlan list" function once did, but
* slightly better as we also pay attention to signal
* strength to pick the best of the duplicates.
*/
if (most_recent == NULL) {
most_recent->essid) != 0) {
if (most_recent->connected) {
(void) dladm_wlan_disconnect(
}
}
}
/* Reset any security information we may have had. */
}
if (most_recent->connected) {
most_recent->essid);
} else {
most_recent->essid);
}
dprintf("%s is unknown and not connected; requested help",
ifname);
} else {
}
(void) pthread_mutex_unlock(&wifi_mutex);
return (connect_result);
}
void
disconnect_wlan(const char *ifname)
{
struct wireless_lan *wlan;
if (pthread_mutex_lock(&wifi_mutex) == 0) {
}
}
}
(void) pthread_mutex_unlock(&wifi_mutex);
}
}
void
{
if (pthread_mutex_lock(&wifi_mutex) == 0) {
}
(void) pthread_mutex_unlock(&wifi_mutex);
}
}
void
print_wireless_status(void)
{
struct wireless_lan *wlan;
if (pthread_mutex_lock(&wifi_mutex) == 0) {
dprintf("WIF %s linkid %d scan %srunning "
"wireless %sdone %sneed key strength %d",
wip->wi_strength);
}
dprintf("WLAN I/F %s ESS %s BSS %s signal %s key %sset "
"%sknown %sconnected %sscanned",
}
(void) pthread_mutex_unlock(&wifi_mutex);
}
}