netdev: Scan & Retry CMD_AUTHENTICATE

Handle situations where the BSS we're trying to connect to is no longer
in the kernel scan result cache.  Normally, the kernel will re-scan the
target frequency if this happens on the CMD_CONNECT path, and retry the
connection.

Unfortunately, CMD_AUTHENTICATE path used for WPA3, OWE and FILS does
not have this scanning behavior.  CMD_AUTHENTICATE simply fails with
a -ENOENT error.  Work around this by trying a limited scan of the
target frequency and re-trying CMD_AUTHENTICATE once.
This commit is contained in:
Denis Kenzior 2021-02-05 11:21:33 -06:00
parent a8768e354d
commit fb217479d2
3 changed files with 144 additions and 4 deletions

View File

@ -143,6 +143,7 @@ struct netdev {
struct l_io *pae_io; /* for drivers without EAPoL over NL80211 */
struct l_genl_msg *connect_cmd;
struct l_genl_msg *auth_cmd;
struct wiphy_radio_work_item work;
bool connected : 1;
@ -157,6 +158,7 @@ struct netdev {
bool expect_connect_failure : 1;
bool aborting : 1;
bool events_ready : 1;
bool retry_auth : 1;
};
struct netdev_preauth_state {
@ -2335,18 +2337,73 @@ static struct l_genl_msg *netdev_build_cmd_authenticate(struct netdev *netdev,
return msg;
}
static void netdev_auth_cb(struct l_genl_msg *msg, void *user_data)
static void netdev_scan_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
if (l_genl_msg_get_error(msg) < 0) {
l_error("Error sending CMD_AUTHENTICATE");
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
return;
}
netdev->retry_auth = true;
}
static void netdev_auth_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
struct handshake_state *hs = netdev->handshake;
int err = l_genl_msg_get_error(msg);
struct l_genl_msg *scan_msg;
if (!err) {
l_genl_msg_unref(netdev->auth_cmd);
netdev->auth_cmd = NULL;
return;
}
l_debug("Error during auth: %d", err);
if (!netdev->auth_cmd || err != -ENOENT) {
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
return;
}
/* Kernel can't find the BSS in its cache, scan and retry */
scan_msg = scan_build_trigger_scan_bss(netdev->index, netdev->wiphy,
netdev->frequency,
hs->ssid, hs->ssid_len);
if (!l_genl_family_send(nl80211, scan_msg,
netdev_scan_cb, netdev, NULL)) {
l_genl_msg_unref(scan_msg);
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
}
}
static void netdev_new_scan_results_event(struct l_genl_msg *msg,
struct netdev *netdev)
{
if (!netdev->retry_auth)
return;
l_debug("");
if (!l_genl_family_send(nl80211, netdev->auth_cmd,
netdev_auth_cb, netdev, NULL)) {
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
return;
}
netdev->auth_cmd = NULL;
}
static void netdev_assoc_cb(struct l_genl_msg *msg, void *user_data)
@ -2368,7 +2425,6 @@ static void netdev_sae_tx_authenticate(const uint8_t *body,
struct l_genl_msg *msg;
msg = netdev_build_cmd_authenticate(netdev, NL80211_AUTHTYPE_SAE);
l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_DATA, body_len, body);
if (!l_genl_family_send(nl80211, msg, netdev_auth_cb, netdev, NULL)) {
@ -2376,7 +2432,10 @@ static void netdev_sae_tx_authenticate(const uint8_t *body,
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
return;
}
netdev->auth_cmd = l_genl_msg_ref(msg);
}
static void netdev_sae_tx_associate(void *user_data)
@ -2421,7 +2480,10 @@ static void netdev_owe_tx_authenticate(void *user_data)
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
return;
}
netdev->auth_cmd = l_genl_msg_ref(msg);
}
static void netdev_owe_tx_associate(struct iovec *ie_iov, size_t iov_len,
@ -2459,7 +2521,10 @@ static void netdev_fils_tx_authenticate(const uint8_t *body,
netdev_connect_failed(netdev,
NETDEV_RESULT_AUTHENTICATION_FAILED,
MMPDU_STATUS_CODE_UNSPECIFIED);
return;
}
netdev->auth_cmd = l_genl_msg_ref(msg);
}
static void netdev_fils_tx_associate(struct iovec *iov, size_t iov_len,
@ -2821,6 +2886,8 @@ static bool netdev_connection_work_ready(struct wiphy_radio_work_item *item)
{
struct netdev *netdev = l_container_of(item, struct netdev, work);
netdev->retry_auth = false;
if (mac_per_ssid) {
int ret = netdev_start_powered_mac_change(netdev);
@ -2842,8 +2909,21 @@ failed:
return true;
}
static void netdev_connection_work_destroy(struct wiphy_radio_work_item *item)
{
struct netdev *netdev = l_container_of(item, struct netdev, work);
if (netdev->auth_cmd) {
l_genl_msg_unref(netdev->auth_cmd);
netdev->auth_cmd = NULL;
}
netdev->retry_auth = false;
}
static const struct wiphy_radio_work_item_ops connect_work_ops = {
.do_work = netdev_connection_work_ready,
.destroy = netdev_connection_work_destroy,
};
static int netdev_connect_common(struct netdev *netdev,
@ -3805,6 +3885,26 @@ static void netdev_station_event(struct l_genl_msg *msg,
netdev_station_watch_func_t, netdev, mac, added);
}
static void netdev_scan_notify(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = NULL;
uint32_t ifindex;
if (nl80211_parse_attrs(msg, NL80211_ATTR_IFINDEX, &ifindex,
NL80211_ATTR_UNSPEC) < 0)
return;
netdev = netdev_find(ifindex);
if (!netdev)
return;
switch (l_genl_msg_get_command(msg)) {
case NL80211_CMD_NEW_SCAN_RESULTS:
netdev_new_scan_results_event(msg, netdev);
break;
}
}
static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = NULL;
@ -5106,6 +5206,10 @@ static int netdev_init(void)
NULL, NULL))
l_error("Registering for MLME notification failed");
if (!l_genl_family_register(nl80211, "scan", netdev_scan_notify,
NULL, NULL))
l_error("Registering for scan notifications failed");
return 0;
fail_netlink:

View File

@ -436,6 +436,35 @@ done:
return msg;
}
struct l_genl_msg *scan_build_trigger_scan_bss(uint32_t ifindex,
struct wiphy *wiphy,
uint32_t frequency,
const uint8_t *ssid,
uint32_t ssid_len)
{
struct l_genl_msg *msg = l_genl_msg_new(NL80211_CMD_TRIGGER_SCAN);
uint32_t flags = 0;
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
l_genl_msg_enter_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES);
l_genl_msg_append_attr(msg, 0, 4, &frequency);
l_genl_msg_leave_nested(msg);
if (wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_SCAN_RANDOM_SN))
flags |= NL80211_SCAN_FLAG_RANDOM_SN;
if (flags)
l_genl_msg_append_attr(msg, NL80211_ATTR_SCAN_FLAGS, 4, &flags);
/* direct probe request scan */
l_genl_msg_enter_nested(msg, NL80211_ATTR_SCAN_SSIDS);
l_genl_msg_append_attr(msg, 0, ssid_len, ssid);
l_genl_msg_leave_nested(msg);
return msg;
}
struct scan_cmds_add_data {
struct scan_context *sc;
const struct scan_parameters *params;

View File

@ -26,6 +26,7 @@ struct p2p_probe_resp;
struct p2p_probe_req;
struct p2p_beacon;
struct mmpdu_header;
struct wiphy;
enum scan_band {
SCAN_BAND_2_4_GHZ = 0x1,
@ -120,6 +121,12 @@ static inline bool scan_bss_addr_eq(const struct scan_bss *a1,
return !memcmp(a1->addr, a2->addr, sizeof(a1->addr));
}
struct l_genl_msg *scan_build_trigger_scan_bss(uint32_t ifindex,
struct wiphy *wiphy,
uint32_t frequency,
const uint8_t *ssid,
uint32_t ssid_len);
uint32_t scan_passive(uint64_t wdev_id, struct scan_freq_set *freqs,
scan_trigger_func_t trigger, scan_notify_func_t notify,
void *userdata, scan_destroy_func_t destroy);