nl80211: Work around EALREADY from connect command
cfg80211 does not currently allow NL80211_CMD_CONNECT when there is already an association. This can result in wpa_supplicant being unable to request a connection if the interface is already in connected state. Work around this by requesting disconnection if the connect command fails with EALREADY error. This allows wpa_supplicant to recover from the unexpected state and complete the connect request on next attempt.
This commit is contained in:
parent
1b414f59fc
commit
20f5a4c24c
1 changed files with 91 additions and 16 deletions
|
@ -248,6 +248,7 @@ struct nl80211_bss_info_arg {
|
|||
struct wpa_driver_nl80211_data *drv;
|
||||
struct wpa_scan_results *res;
|
||||
unsigned int assoc_freq;
|
||||
u8 assoc_bssid[ETH_ALEN];
|
||||
};
|
||||
|
||||
static int bss_info_handler(struct nl_msg *msg, void *arg);
|
||||
|
@ -757,6 +758,29 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
|
|||
}
|
||||
|
||||
|
||||
static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
|
||||
struct nlattr *reason, struct nlattr *addr)
|
||||
{
|
||||
union wpa_event_data data;
|
||||
|
||||
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
|
||||
/*
|
||||
* Avoid reporting two disassociation events that could
|
||||
* confuse the core code.
|
||||
*/
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
|
||||
"event when using userspace SME");
|
||||
return;
|
||||
}
|
||||
|
||||
drv->associated = 0;
|
||||
os_memset(&data, 0, sizeof(data));
|
||||
if (reason)
|
||||
data.disassoc_info.reason_code = nla_get_u16(reason);
|
||||
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
|
||||
}
|
||||
|
||||
|
||||
static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
|
||||
enum nl80211_commands cmd, struct nlattr *addr)
|
||||
{
|
||||
|
@ -1452,7 +1476,6 @@ static int process_event(struct nl_msg *msg, void *arg)
|
|||
struct wpa_driver_nl80211_data *drv = arg;
|
||||
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||
union wpa_event_data data;
|
||||
|
||||
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
genlmsg_attrlen(gnlh, 0), NULL);
|
||||
|
@ -1518,21 +1541,8 @@ static int process_event(struct nl_msg *msg, void *arg)
|
|||
tb[NL80211_ATTR_RESP_IE]);
|
||||
break;
|
||||
case NL80211_CMD_DISCONNECT:
|
||||
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
|
||||
/*
|
||||
* Avoid reporting two disassociation events that could
|
||||
* confuse the core code.
|
||||
*/
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
|
||||
"event when using userspace SME");
|
||||
break;
|
||||
}
|
||||
drv->associated = 0;
|
||||
os_memset(&data, 0, sizeof(data));
|
||||
if (tb[NL80211_ATTR_REASON_CODE])
|
||||
data.disassoc_info.reason_code =
|
||||
nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
|
||||
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
|
||||
mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
|
||||
tb[NL80211_ATTR_MAC]);
|
||||
break;
|
||||
case NL80211_CMD_MICHAEL_MIC_FAILURE:
|
||||
mlme_event_michael_mic_failure(drv, tb);
|
||||
|
@ -2648,6 +2658,13 @@ static int bss_info_handler(struct nl_msg *msg, void *arg)
|
|||
wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
|
||||
_arg->assoc_freq);
|
||||
}
|
||||
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
|
||||
bss[NL80211_BSS_BSSID]) {
|
||||
os_memcpy(_arg->assoc_bssid,
|
||||
nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Associated with "
|
||||
MACSTR, MAC2STR(_arg->assoc_bssid));
|
||||
}
|
||||
}
|
||||
if (!res)
|
||||
return NL_SKIP;
|
||||
|
@ -4978,6 +4995,56 @@ nla_put_failure:
|
|||
}
|
||||
|
||||
|
||||
static unsigned int nl80211_get_assoc_bssid(struct wpa_driver_nl80211_data *drv,
|
||||
u8 *bssid)
|
||||
{
|
||||
struct nl_msg *msg;
|
||||
int ret;
|
||||
struct nl80211_bss_info_arg arg;
|
||||
|
||||
os_memset(&arg, 0, sizeof(arg));
|
||||
msg = nlmsg_alloc();
|
||||
if (!msg)
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
|
||||
NL80211_CMD_GET_SCAN, 0);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
|
||||
|
||||
arg.drv = drv;
|
||||
ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
|
||||
msg = NULL;
|
||||
if (ret == 0) {
|
||||
if (is_zero_ether_addr(arg.assoc_bssid))
|
||||
return -ENOTCONN;
|
||||
os_memcpy(bssid, arg.assoc_bssid, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
|
||||
"(%s)", ret, strerror(-ret));
|
||||
nla_put_failure:
|
||||
nlmsg_free(msg);
|
||||
return drv->assoc_freq;
|
||||
}
|
||||
|
||||
|
||||
static int nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
|
||||
const u8 *bssid)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
if (bssid == NULL) {
|
||||
int res = nl80211_get_assoc_bssid(drv, addr);
|
||||
if (res)
|
||||
return res;
|
||||
bssid = addr;
|
||||
}
|
||||
|
||||
return wpa_driver_nl80211_disconnect(drv, bssid,
|
||||
WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||
}
|
||||
|
||||
|
||||
static int wpa_driver_nl80211_connect(
|
||||
struct wpa_driver_nl80211_data *drv,
|
||||
struct wpa_driver_associate_params *params)
|
||||
|
@ -5127,6 +5194,14 @@ skip_auth_type:
|
|||
if (ret) {
|
||||
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
|
||||
"(%s)", ret, strerror(-ret));
|
||||
/*
|
||||
* cfg80211 does not currently accept new connection if we are
|
||||
* already connected. As a workaround, force disconnection and
|
||||
* try again once the driver indicates it completed
|
||||
* disconnection.
|
||||
*/
|
||||
if (ret == -EALREADY)
|
||||
nl80211_disconnect(drv, params->bssid);
|
||||
goto nla_put_failure;
|
||||
}
|
||||
ret = 0;
|
||||
|
|
Loading…
Reference in a new issue