netdev: Handle deauth frames prior to association

In some cases the AP can send a deauthenticate frame right after
accepting our authentication.  In this case the kernel never properly
sends a CMD_CONNECT event with a failure, even though CMD_COONNECT was
used to initiate the connection.  Try to work around that by detecting
that a Deauthenticate event arrives prior to any Associte or Connect
events and handle this case as a connect failure.
This commit is contained in:
Denis Kenzior 2021-01-13 13:31:23 -06:00
parent 83e1265c6b
commit 2bebb4bdc7
1 changed files with 49 additions and 1 deletions

View File

@ -146,6 +146,7 @@ struct netdev {
struct wiphy_radio_work_item work;
bool connected : 1;
bool associated : 1;
bool operational : 1;
bool rekey_offload_support : 1;
bool pae_over_nl80211 : 1;
@ -649,6 +650,7 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->group_handshake_timeout = NULL;
}
netdev->associated = false;
netdev->operational = false;
netdev->connected = false;
netdev->connect_cb = NULL;
@ -1056,7 +1058,48 @@ static struct l_genl_msg *netdev_build_cmd_disconnect(struct netdev *netdev,
static void netdev_deauthenticate_event(struct l_genl_msg *msg,
struct netdev *netdev)
{
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
const struct mmpdu_header *hdr = NULL;
uint16_t reason_code;
l_debug("");
/*
* If we got to the association phase, process the connect event
* instead
*/
if (!netdev->connected || netdev->associated)
return;
/*
* Handle the bizarre case of AP accepting authentication, then
* deauthenticating immediately afterwards
*/
if (L_WARN_ON(!l_genl_attr_init(&attr, msg)))
return;
while (l_genl_attr_next(&attr, &type, &len, &data)) {
switch (type) {
case NL80211_ATTR_FRAME:
hdr = mpdu_validate(data, len);
break;
}
}
if (L_WARN_ON(!hdr))
return;
reason_code = l_get_u8(mmpdu_body(hdr));
l_info("deauth event, src="MAC" dest="MAC" bssid="MAC" reason=%u",
MAC_STR(hdr->address_2), MAC_STR(hdr->address_1),
MAC_STR(hdr->address_3), reason_code);
netdev_connect_failed(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
}
static struct l_genl_msg *netdev_build_cmd_deauthenticate(struct netdev *netdev,
@ -2155,8 +2198,10 @@ static void netdev_associate_event(struct l_genl_msg *msg,
if (!netdev->connected || netdev->aborting)
return;
if (!netdev->ap)
if (!netdev->ap) {
netdev->associated = true;
return;
}
if (!l_genl_attr_init(&attr, msg)) {
l_debug("attr init failed");
@ -2212,6 +2257,7 @@ static void netdev_associate_event(struct l_genl_msg *msg,
false);
netdev->in_ft = false;
netdev->associated = true;
return;
} else if (ret == -EAGAIN) {
/*
@ -2970,6 +3016,7 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
memcpy(netdev->prev_bssid, orig_bss->addr, ETH_ALEN);
netdev->associated = false;
netdev->operational = false;
netdev_rssi_polling_update(netdev);
@ -3332,6 +3379,7 @@ static int fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
target_bss->rsne);
memcpy(netdev->handshake->mde + 2, target_bss->mde, 3);
netdev->associated = false;
netdev->operational = false;
netdev->in_ft = true;
netdev->connect_cb = cb;