diff --git a/src/netdev.c b/src/netdev.c index e392335b..7a10bf0e 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -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: diff --git a/src/scan.c b/src/scan.c index 26c6650d..28641053 100644 --- a/src/scan.c +++ b/src/scan.c @@ -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; diff --git a/src/scan.h b/src/scan.h index 2fb677a8..355b4b5a 100644 --- a/src/scan.h +++ b/src/scan.h @@ -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);