index fd04ad2e75116a7795f112e9e3e20831f9fcabd8..191748a8afe89bf3bc561e8b8d9e8eb85d40bd36 100644 (file)
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
/*
* WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
}
}
+void wpa_supplicant_mark_roaming(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->roaming_in_progress = 1;
+ os_memcpy(wpa_s->prev_bssid, wpa_s->bssid, ETH_ALEN);
+ wpa_s->prev_ssid = wpa_s->current_ssid;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Saving prev AP info for roaming recovery - "
+ "SSID ID: %d BSSID: " MACSTR,
+ wpa_s->prev_ssid->id, MAC2STR(wpa_s->prev_bssid));
+}
+
+void wpa_supplicant_clear_roaming(struct wpa_supplicant *wpa_s,
+ int ignore_deauth_event)
+{
+ wpa_s->roaming_in_progress = 0;
+ wpa_s->ignore_deauth_event = ignore_deauth_event;
+ wpa_s->prev_ssid = NULL;
+ os_memset(wpa_s->prev_bssid, 0, ETH_ALEN);
+}
void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
{
return;
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ wpa_s->conf->ap_scan = DEFAULT_AP_SCAN;
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+#ifdef CONFIG_P2P
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+#endif /* CONFIG_P2P */
wpa_s->current_bss = NULL;
wpa_s->assoc_freq = 0;
#ifdef CONFIG_IEEE80211R
else
type = SCARD_GSM_SIM_ONLY;
- wpa_s->scard = scard_init(type);
+ wpa_s->scard = scard_init(type, NULL);
if (wpa_s->scard == NULL) {
wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
"(pcsc-lite)");
}
}
+ if (wpa_s->roaming &&
+ os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN) == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - current bssid (roaming)");
+ return NULL;
+ }
+
if (ssid_len == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known");
return NULL;
}
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING &&
+ wpa_s->current_ssid &&
+ os_memcmp(wpa_s->current_ssid->ssid, ssid_, ssid_len) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - block roaming to "
+ "a different SSID while connected");
+ return NULL;
+ }
+
wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
for (ssid = group; ssid; ssid = ssid->pnext) {
break;
}
- if (selected == NULL && wpa_s->blacklist) {
+ if (selected == NULL && wpa_s->blacklist &&
+ !wpa_s->countermeasures) {
wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear "
"blacklist and try again");
wpa_blacklist_clear(wpa_s);
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
#ifdef CONFIG_P2P
- if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1)
+ if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1) {
+ wpa_supplicant_clear_roaming(wpa_s, 0);
return -1;
+ }
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
wpas_wps_cancel(wpa_s);
#endif /* CONFIG_WPS */
+ wpa_supplicant_clear_roaming(wpa_s, 0);
return -1;
}
0))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
- return 0;
+ wpa_supplicant_clear_roaming(wpa_s, 0);
}
wpa_msg(wpa_s, MSG_DEBUG, "Request association: "
"reassociate: %d selected: "MACSTR " bssid: " MACSTR
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the "
"selected AP");
+ wpa_supplicant_clear_roaming(wpa_s, 0);
}
return 0;
if (wpa_s->reassociate)
return 1; /* explicit request to reassociate */
+ if (wpa_s->roaming)
+ return 1; /* explicit request to roam */
if (wpa_s->wpa_state < WPA_ASSOCIATED)
return 1; /* we are not associated; continue */
if (wpa_s->current_ssid == NULL)
return 1; /* unknown current SSID */
+ if (wpa_s->roaming_disabled) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - "
+ "driver doesn't allow roaming now");
+ return 0;
+ }
if (wpa_s->current_ssid != ssid)
return 1; /* different network block */
if (!current_bss)
return 1; /* current BSS not seen in scan results */
+#ifndef CONFIG_NO_ROAMING
wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
MAC2STR(current_bss->bssid), current_bss->level);
return 0;
}
+ wpa_supplicant_mark_roaming(wpa_s);
return 1;
+#else /* CONFIG_NO_ROAMING */
+ return 0;
+#endif /* CONFIG_NO_ROAMING */
}
+
/* Return < 0 if no scan results could be fetched. */
static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
return 0;
}
#endif /* CONFIG_P2P */
- if (wpa_supplicant_req_sched_scan(wpa_s))
- wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
- timeout_usec);
+ wpa_supplicant_req_sched_scan(wpa_s);
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
}
}
return 0;
}
+static struct wpa_bss * wpa_supplicant_get_new_bss(
+ struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bss *bss = NULL;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid->ssid_len > 0)
+ bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+ if (!bss)
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+ return bss;
+}
+
+
+static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
+{
+ const u8 *bss_wpa = NULL, *bss_rsn = NULL;
+
+ if (!wpa_s->current_bss || !wpa_s->current_ssid)
+ return -1;
+
+ if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
+ return 0;
+
+ bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+ WPA_IE_VENDOR_TYPE);
+ bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
+
+ if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+ bss_wpa ? 2 + bss_wpa[1] : 0) ||
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+ bss_rsn ? 2 + bss_rsn[1] : 0))
+ return -1;
+
+ return 0;
+}
+
+
static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
}
if (wpa_s->current_ssid) {
struct wpa_bss *bss = NULL;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid->ssid_len > 0)
- bss = wpa_bss_get(wpa_s, bssid,
- ssid->ssid, ssid->ssid_len);
- if (!bss)
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+ bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+ if (!bss) {
+ wpa_supplicant_update_scan_results(wpa_s);
+
+ /* Get the BSS from the new scan results */
+ bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+ }
+
if (bss)
wpa_s->current_bss = bss;
}
+
+ if (wpa_s->conf->ap_scan == 1 &&
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
+ if (wpa_supplicant_assoc_update_ie(wpa_s) < 0)
+ wpa_msg(wpa_s, MSG_WARNING,
+ "WPA/RSN IEs not updated");
+ }
}
#ifdef CONFIG_SME
/* Timeout for receiving the first EAPOL packet */
wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
}
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
struct os_time now, age;
os_get_time(&now);
os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
- if (age.sec == 0 && age.usec < 100000 &&
+ if (age.sec == 0 && age.usec < 500000 &&
os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
- u16 reason_code)
+ u16 reason_code,
+ int locally_generated,
+ const u8 *addr)
{
const u8 *bssid;
int authenticating;
return;
}
+ if (addr && wpa_s->bssid && os_memcmp(wpa_s->bssid, addr, ETH_ALEN)) {
+ /* This may occur during roaming */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disconnect from"
+ " a BSS which is not the current one");
+ return;
+ }
+
if (wpa_s->wpa_state == WPA_4WAY_HANDSHAKE &&
wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
if (wpa_s->wpa_state == WPA_COMPLETED &&
wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ !locally_generated &&
disconnect_reason_recoverable(reason_code)) {
/*
* It looks like the AP has dropped association with
fast_reconnect = wpa_s->current_bss;
fast_reconnect_ssid = wpa_s->current_ssid;
} else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
- wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ wpa_supplicant_req_scan(wpa_s, 0, 500000);
else
wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
"immediate scan");
"try to re-connect");
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
}
bssid = wpa_s->bssid;
if (is_zero_ether_addr(bssid))
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpas_connection_failed(wpa_s, bssid);
wpa_sm_notify_disassoc(wpa_s->wpa);
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
- " reason=%d",
- MAC2STR(bssid), reason_code);
+ if (!is_zero_ether_addr(bssid) ||
+ wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+ " reason=%d%s",
+ MAC2STR(bssid), reason_code,
+ locally_generated ? " locally_generated=1" : "");
+ }
if (wpa_supplicant_dynamic_keys(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
wpa_s->keys_cleared = 0;
/* initialize countermeasures */
wpa_s->countermeasures = 1;
+
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+
wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started");
/*
wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
"driver after interface was added");
}
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
break;
case EVENT_INTERFACE_REMOVED:
wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
wpa_s->interface_removed = 1;
wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = NULL;
#ifdef CONFIG_IBSS_RSN
{
struct wpa_supplicant *wpa_s = ctx;
u16 reason_code = 0;
+ int locally_generated = 0;
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
event != EVENT_INTERFACE_ENABLED &&
- event != EVENT_INTERFACE_STATUS) {
+ event != EVENT_INTERFACE_STATUS &&
+ event != EVENT_SCHED_SCAN_STOPPED) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Ignore event %s (%d) while interface is disabled",
event_to_string(event), event);
case EVENT_DISASSOC:
wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
if (data) {
- wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u",
- data->disassoc_info.reason_code);
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
+ data->disassoc_info.reason_code,
+ data->disassoc_info.locally_generated ?
+ " (locally generated)" : "");
if (data->disassoc_info.addr)
wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
MAC2STR(data->disassoc_info.addr));
#endif /* CONFIG_AP */
if (data) {
reason_code = data->disassoc_info.reason_code;
+ locally_generated =
+ data->disassoc_info.locally_generated;
wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
data->disassoc_info.ie,
data->disassoc_info.ie_len);
"Deauthentication notification");
if (data) {
reason_code = data->deauth_info.reason_code;
- wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u",
- data->deauth_info.reason_code);
+ locally_generated =
+ data->deauth_info.locally_generated;
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
+ data->deauth_info.reason_code,
+ data->deauth_info.locally_generated ?
+ " (locally generated)" : "");
if (data->deauth_info.addr) {
wpa_dbg(wpa_s, MSG_DEBUG, " * address "
MACSTR,
break;
}
#endif /* CONFIG_AP */
- wpa_supplicant_event_disassoc(wpa_s, reason_code);
+
+ if (data->deauth_info.reason_code
+ == WLAN_REASON_PREV_AUTH_NOT_VALID &&
+ wpa_s->ignore_deauth_event) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event"
+ " with reason=2");
+ wpa_s->ignore_deauth_event = 0;
+ } else {
+ wpa_supplicant_event_disassoc(wpa_s,
+ reason_code,
+ locally_generated,
+ data->deauth_info.addr);
+ }
break;
case EVENT_MICHAEL_MIC_FAILURE:
wpa_supplicant_event_michael_mic_failure(wpa_s, data);
data->signal_change.current_noise,
data->signal_change.current_txrate);
break;
+ case EVENT_ROAMING_ENABLED:
+ wpa_supplicant_enable_roaming(wpa_s);
+ break;
+ case EVENT_ROAMING_DISABLED:
+ wpa_supplicant_disable_roaming(wpa_s);
+ break;
+
+ case EVENT_START_ROAMING:
+ if (!is_zero_ether_addr(wpa_s->bssid)) {
+ wpa_s->roaming = 1;
+ bgscan_notify_beacon_loss(wpa_s);
+ }
+ break;
+
case EVENT_INTERFACE_ENABLED:
wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
case EVENT_CHANNEL_LIST_CHANGED:
if (wpa_s->drv_priv == NULL)
break; /* Ignore event during drv initialization */
+
+ free_hw_features(wpa_s);
+ wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+ wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
#ifdef CONFIG_P2P
wpas_p2p_update_channel_list(wpa_s);
#endif /* CONFIG_P2P */
break;
case EVENT_SCHED_SCAN_STOPPED:
wpa_s->sched_scanning = 0;
- wpa_supplicant_notify_scanning(wpa_s, 0);
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ break;
/*
* If we timed out, start a new sched scan to continue
* searching for more SSIDs.
*/
- if (wpa_s->sched_scan_timed_out)
- wpa_supplicant_req_sched_scan(wpa_s);
+ if (wpa_s->override_sched_scan) {
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_new_scan(wpa_s,
+ wpa_s->scan_interval, 0);
+ }
break;
case EVENT_WPS_BUTTON_PUSHED:
#ifdef CONFIG_WPS