nl80211: Recover from auth req ENOENT with a scan
cfg80211 rejects NL80211_CMD_AUTHENTICATE with ENOENT if the BSS entry for the target BSS is not available. This can happen if the cfg80211 entry has expired before wpa_supplicant entry (e.g., during a suspend). To recover from this quickly, run a single channel scan to get the cfg80211 entry back and then retry authentication command again. This is handled within driver_nl80211.c to keep the core wpa_supplicant implementation cleaner. Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
ed57c5907e
commit
536fd62dba
1 changed files with 149 additions and 0 deletions
|
@ -238,6 +238,8 @@ struct wpa_driver_nl80211_data {
|
|||
unsigned int device_ap_sme:1;
|
||||
unsigned int poll_command_supported:1;
|
||||
unsigned int data_tx_status:1;
|
||||
unsigned int scan_for_auth:1;
|
||||
unsigned int retry_auth:1;
|
||||
|
||||
u64 remain_on_chan_cookie;
|
||||
u64 send_action_cookie;
|
||||
|
@ -261,6 +263,20 @@ struct wpa_driver_nl80211_data {
|
|||
int last_freq;
|
||||
int last_freq_ht;
|
||||
#endif /* HOSTAPD */
|
||||
|
||||
/* From failed authentication command */
|
||||
int auth_freq;
|
||||
u8 auth_bssid_[ETH_ALEN];
|
||||
u8 auth_ssid[32];
|
||||
size_t auth_ssid_len;
|
||||
int auth_alg;
|
||||
u8 *auth_ie;
|
||||
size_t auth_ie_len;
|
||||
u8 auth_wep_key[4][16];
|
||||
size_t auth_wep_key_len[4];
|
||||
int auth_wep_tx_keyidx;
|
||||
int auth_local_state_change;
|
||||
int auth_p2p;
|
||||
};
|
||||
|
||||
|
||||
|
@ -313,6 +329,8 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
|
|||
int ifindex, int disabled);
|
||||
|
||||
static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
|
||||
static int wpa_driver_nl80211_authenticate_retry(
|
||||
struct wpa_driver_nl80211_data *drv);
|
||||
|
||||
|
||||
static int is_ap_interface(enum nl80211_iftype nlmode)
|
||||
|
@ -1271,6 +1289,14 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
|
|||
int freqs[MAX_REPORT_FREQS];
|
||||
int num_freqs = 0;
|
||||
|
||||
if (drv->scan_for_auth) {
|
||||
drv->scan_for_auth = 0;
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
|
||||
"cfg80211 BSS entry");
|
||||
wpa_driver_nl80211_authenticate_retry(drv);
|
||||
return;
|
||||
}
|
||||
|
||||
os_memset(&event, 0, sizeof(event));
|
||||
info = &event.scan_info;
|
||||
info->aborted = aborted;
|
||||
|
@ -2775,6 +2801,8 @@ static void wpa_driver_nl80211_deinit(void *priv)
|
|||
|
||||
os_free(drv->filter_ssids);
|
||||
|
||||
os_free(drv->auth_ie);
|
||||
|
||||
if (drv->in_interface_list)
|
||||
dl_list_del(&drv->list);
|
||||
|
||||
|
@ -2818,6 +2846,8 @@ static int wpa_driver_nl80211_scan(void *priv,
|
|||
struct nl_msg *msg, *ssids, *freqs, *rates;
|
||||
size_t i;
|
||||
|
||||
drv->scan_for_auth = 0;
|
||||
|
||||
msg = nlmsg_alloc();
|
||||
ssids = nlmsg_alloc();
|
||||
freqs = nlmsg_alloc();
|
||||
|
@ -3798,6 +3828,52 @@ static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr,
|
|||
}
|
||||
|
||||
|
||||
static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
|
||||
struct wpa_driver_auth_params *params)
|
||||
{
|
||||
int i;
|
||||
|
||||
drv->auth_freq = params->freq;
|
||||
drv->auth_alg = params->auth_alg;
|
||||
drv->auth_wep_tx_keyidx = params->wep_tx_keyidx;
|
||||
drv->auth_local_state_change = params->local_state_change;
|
||||
drv->auth_p2p = params->p2p;
|
||||
|
||||
if (params->bssid)
|
||||
os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN);
|
||||
else
|
||||
os_memset(drv->auth_bssid_, 0, ETH_ALEN);
|
||||
|
||||
if (params->ssid) {
|
||||
os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len);
|
||||
drv->auth_ssid_len = params->ssid_len;
|
||||
} else
|
||||
drv->auth_ssid_len = 0;
|
||||
|
||||
|
||||
os_free(drv->auth_ie);
|
||||
drv->auth_ie = NULL;
|
||||
drv->auth_ie_len = 0;
|
||||
if (params->ie) {
|
||||
drv->auth_ie = os_malloc(params->ie_len);
|
||||
if (drv->auth_ie) {
|
||||
os_memcpy(drv->auth_ie, params->ie, params->ie_len);
|
||||
drv->auth_ie_len = params->ie_len;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (params->wep_key[i] && params->wep_key_len[i] &&
|
||||
params->wep_key_len[i] <= 16) {
|
||||
os_memcpy(drv->auth_wep_key[i], params->wep_key[i],
|
||||
params->wep_key_len[i]);
|
||||
drv->auth_wep_key_len[i] = params->wep_key_len[i];
|
||||
} else
|
||||
drv->auth_wep_key_len[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int wpa_driver_nl80211_authenticate(
|
||||
void *priv, struct wpa_driver_auth_params *params)
|
||||
{
|
||||
|
@ -3808,6 +3884,10 @@ static int wpa_driver_nl80211_authenticate(
|
|||
enum nl80211_auth_type type;
|
||||
enum nl80211_iftype nlmode;
|
||||
int count = 0;
|
||||
int is_retry;
|
||||
|
||||
is_retry = drv->retry_auth;
|
||||
drv->retry_auth = 0;
|
||||
|
||||
drv->associated = 0;
|
||||
os_memset(drv->auth_bssid, 0, ETH_ALEN);
|
||||
|
@ -3903,6 +3983,36 @@ retry:
|
|||
nlmsg_free(msg);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (ret == -ENOENT && params->freq && !is_retry) {
|
||||
/*
|
||||
* cfg80211 has likely expired the BSS entry even
|
||||
* though it was previously available in our internal
|
||||
* BSS table. To recover quickly, start a single
|
||||
* channel scan on the specified channel.
|
||||
*/
|
||||
struct wpa_driver_scan_params scan;
|
||||
int freqs[2];
|
||||
|
||||
os_memset(&scan, 0, sizeof(scan));
|
||||
scan.num_ssids = 1;
|
||||
if (params->ssid) {
|
||||
scan.ssids[0].ssid = params->ssid;
|
||||
scan.ssids[0].ssid_len = params->ssid_len;
|
||||
}
|
||||
freqs[0] = params->freq;
|
||||
freqs[1] = 0;
|
||||
scan.freqs = freqs;
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Trigger single "
|
||||
"channel scan to refresh cfg80211 BSS "
|
||||
"entry");
|
||||
ret = wpa_driver_nl80211_scan(bss, &scan);
|
||||
if (ret == 0) {
|
||||
nl80211_copy_auth_params(drv, params);
|
||||
drv->scan_for_auth = 1;
|
||||
}
|
||||
}
|
||||
|
||||
goto nla_put_failure;
|
||||
}
|
||||
ret = 0;
|
||||
|
@ -3915,6 +4025,45 @@ nla_put_failure:
|
|||
}
|
||||
|
||||
|
||||
static int wpa_driver_nl80211_authenticate_retry(
|
||||
struct wpa_driver_nl80211_data *drv)
|
||||
{
|
||||
struct wpa_driver_auth_params params;
|
||||
struct i802_bss *bss = &drv->first_bss;
|
||||
int i;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.freq = drv->auth_freq;
|
||||
params.auth_alg = drv->auth_alg;
|
||||
params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
|
||||
params.local_state_change = drv->auth_local_state_change;
|
||||
params.p2p = drv->auth_p2p;
|
||||
|
||||
if (!is_zero_ether_addr(drv->auth_bssid_))
|
||||
params.bssid = drv->auth_bssid_;
|
||||
|
||||
if (drv->auth_ssid_len) {
|
||||
params.ssid = drv->auth_ssid;
|
||||
params.ssid_len = drv->auth_ssid_len;
|
||||
}
|
||||
|
||||
params.ie = drv->auth_ie;
|
||||
params.ie_len = drv->auth_ie_len;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (drv->auth_wep_key_len[i]) {
|
||||
params.wep_key[i] = drv->auth_wep_key[i];
|
||||
params.wep_key_len[i] = drv->auth_wep_key_len[i];
|
||||
}
|
||||
}
|
||||
|
||||
drv->retry_auth = 1;
|
||||
return wpa_driver_nl80211_authenticate(bss, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
struct phy_info_arg {
|
||||
u16 *num_modes;
|
||||
struct hostapd_hw_modes *modes;
|
||||
|
|
Loading…
Reference in a new issue