netdev: allow PSK offload for FT AKMs

This adds a new connection type, TYPE_PSK_OFFLOAD, which
allows the 4-way handshake to be offloaded by the firmware.
Offloading will be used if the driver advertises support.

The CMD_ROAM event path was also modified to take into account
handshake offloading. If the handshake is offloaded we still
must issue GET_SCAN, but not start eapol since the firmware
takes care of this.
This commit is contained in:
James Prestwood 2021-04-02 15:06:17 -07:00 committed by Denis Kenzior
parent f5c5efa033
commit 9e412f9fdd
1 changed files with 32 additions and 18 deletions

View File

@ -71,6 +71,7 @@ enum connection_type {
CONNECTION_TYPE_SOFTMAC, CONNECTION_TYPE_SOFTMAC,
CONNECTION_TYPE_FULLMAC, CONNECTION_TYPE_FULLMAC,
CONNECTION_TYPE_SAE_OFFLOAD, CONNECTION_TYPE_SAE_OFFLOAD,
CONNECTION_TYPE_PSK_OFFLOAD,
}; };
static uint32_t unicast_watch; static uint32_t unicast_watch;
@ -202,6 +203,7 @@ static inline bool is_offload(struct handshake_state *hs)
case CONNECTION_TYPE_FULLMAC: case CONNECTION_TYPE_FULLMAC:
return false; return false;
case CONNECTION_TYPE_SAE_OFFLOAD: case CONNECTION_TYPE_SAE_OFFLOAD:
case CONNECTION_TYPE_PSK_OFFLOAD:
return true; return true;
} }
@ -1238,11 +1240,6 @@ static void netdev_connect_ok(struct netdev *netdev)
netdev->fw_roam_bss = NULL; netdev->fw_roam_bss = NULL;
} }
/* Allow station to sync the PSK to disk */
if (is_offload(netdev->handshake))
handshake_event(netdev->handshake,
HANDSHAKE_EVENT_SETTING_KEYS);
if (netdev->connect_cb) { if (netdev->connect_cb) {
netdev->connect_cb(netdev, NETDEV_RESULT_OK, NULL, netdev->connect_cb(netdev, NETDEV_RESULT_OK, NULL,
netdev->user_data); netdev->user_data);
@ -2012,14 +2009,6 @@ process_resp_ies:
netdev_send_qos_map_set(netdev, qos_set, qos_len); netdev_send_qos_map_set(netdev, qos_set, qos_len);
} }
/*
* TODO: Only SAE/WPA3-personal offload is supported. In this case IWD
* is 'done'. In the case of 8021x offload EAP still needs to take
* place, so this must be updated accordingly when that is implemented.
*/
if (is_offload(netdev->handshake))
goto done;
if (netdev->sm) { if (netdev->sm) {
/* /*
* Start processing EAPoL frames now that the state machine * Start processing EAPoL frames now that the state machine
@ -2031,7 +2020,11 @@ process_resp_ies:
return; return;
} }
done: /* Allow station to sync the PSK to disk */
if (is_offload(netdev->handshake))
handshake_event(netdev->handshake,
HANDSHAKE_EVENT_SETTING_KEYS);
netdev_connect_ok(netdev); netdev_connect_ok(netdev);
return; return;
@ -2663,6 +2656,9 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD, l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD,
strlen(hs->passphrase), hs->passphrase); strlen(hs->passphrase), hs->passphrase);
break; break;
case CONNECTION_TYPE_PSK_OFFLOAD:
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK, 32, hs->pmk);
break;
} }
if (prev_bssid) if (prev_bssid)
@ -3036,15 +3032,19 @@ static int netdev_handshake_state_setup_connection_type(
return -ENOTSUP; return -ENOTSUP;
switch (hs->akm_suite) { switch (hs->akm_suite) {
case IE_RSN_AKM_SUITE_PSK:
case IE_RSN_AKM_SUITE_FT_USING_PSK:
case IE_RSN_AKM_SUITE_PSK_SHA256:
if (wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
goto psk_offload;
/* fall through */
case IE_RSN_AKM_SUITE_8021X: case IE_RSN_AKM_SUITE_8021X:
case IE_RSN_AKM_SUITE_FT_OVER_8021X: case IE_RSN_AKM_SUITE_FT_OVER_8021X:
case IE_RSN_AKM_SUITE_8021X_SHA256: case IE_RSN_AKM_SUITE_8021X_SHA256:
case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256: case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256:
case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384: case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384:
case IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384: case IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384:
case IE_RSN_AKM_SUITE_PSK:
case IE_RSN_AKM_SUITE_FT_USING_PSK:
case IE_RSN_AKM_SUITE_PSK_SHA256:
if (softmac) if (softmac)
goto softmac; goto softmac;
@ -3086,6 +3086,9 @@ fullmac:
sae_offload: sae_offload:
nhs->type = CONNECTION_TYPE_SAE_OFFLOAD; nhs->type = CONNECTION_TYPE_SAE_OFFLOAD;
return 0; return 0;
psk_offload:
nhs->type = CONNECTION_TYPE_PSK_OFFLOAD;
return 0;
} }
static int netdev_connect_common(struct netdev *netdev, static int netdev_connect_common(struct netdev *netdev,
@ -4126,6 +4129,11 @@ static bool netdev_get_fw_scan_cb(int err, struct l_queue *bss_list,
handshake_state_set_authenticator_ie(netdev->handshake, bss->rsne); handshake_state_set_authenticator_ie(netdev->handshake, bss->rsne);
if (is_offload(netdev->handshake)) {
netdev_connect_ok(netdev);
return false;
}
if (netdev->sm) { if (netdev->sm) {
if (!eapol_start(netdev->sm)) if (!eapol_start(netdev->sm))
goto failed; goto failed;
@ -4184,14 +4192,20 @@ static void netdev_roam_event(struct l_genl_msg *msg, struct netdev *netdev)
goto failed; goto failed;
} }
/* Handshake completed in firmware, just get the roamed BSS */
if (is_offload(netdev->handshake))
goto get_fw_scan;
/* Reset handshake state */ /* Reset handshake state */
nhs->complete = false; nhs->complete = false;
nhs->ptk_installed = false; nhs->ptk_installed = false;
nhs->gtk_installed = true; nhs->gtk_installed = true;
nhs->igtk_installed = true; nhs->igtk_installed = true;
handshake_state_set_authenticator_address(netdev->handshake, mac);
netdev->handshake->ptk_complete = false; netdev->handshake->ptk_complete = false;
get_fw_scan:
handshake_state_set_authenticator_address(netdev->handshake, mac);
if (!scan_get_firmware_scan(netdev->wdev_id, netdev_get_fw_scan_cb, if (!scan_get_firmware_scan(netdev->wdev_id, netdev_get_fw_scan_cb,
netdev, NULL)) netdev, NULL))
goto failed; goto failed;