From 10a160a6e1e7b53f671b7c2d812e518ec63bdb34 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 31 Mar 2017 14:43:02 +0200 Subject: [PATCH] device: Non-FT roaming support Use netdev_reassociate if FT is not available. device_select_akm_suite is only moved up in the file and the reused code from device_connect is moved to a separate function. --- src/device.c | 362 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 212 insertions(+), 150 deletions(-) diff --git a/src/device.c b/src/device.c index c4b9e56b..07d9bbc5 100644 --- a/src/device.c +++ b/src/device.c @@ -573,6 +573,167 @@ static void device_disconnect_by_ap(struct device *device) device_disassociated(device); } +static enum ie_rsn_akm_suite device_select_akm_suite(struct network *network, + struct scan_bss *bss, + struct ie_rsn_info *info) +{ + enum security security = network_get_security(network); + + /* + * If FT is available, use FT authentication to keep the door open + * for fast transitions. Otherwise use SHA256 version if present. + */ + + if (security == SECURITY_8021X) { + if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_OVER_8021X) && + bss->rsne && bss->mde_present) + return IE_RSN_AKM_SUITE_FT_OVER_8021X; + + if (info->akm_suites & IE_RSN_AKM_SUITE_8021X_SHA256) + return IE_RSN_AKM_SUITE_8021X_SHA256; + + if (info->akm_suites & IE_RSN_AKM_SUITE_8021X) + return IE_RSN_AKM_SUITE_8021X; + } else if (security == SECURITY_PSK) { + if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_USING_PSK) && + bss->rsne && bss->mde_present) + return IE_RSN_AKM_SUITE_FT_USING_PSK; + + if (info->akm_suites & IE_RSN_AKM_SUITE_PSK_SHA256) + return IE_RSN_AKM_SUITE_PSK_SHA256; + + if (info->akm_suites & IE_RSN_AKM_SUITE_PSK) + return IE_RSN_AKM_SUITE_PSK; + } + + return 0; +} + +static struct handshake_state *device_handshake_setup(struct device *device, + struct network *network, + struct scan_bss *bss) +{ + enum security security = network_get_security(network); + struct wiphy *wiphy = device->wiphy; + struct handshake_state *hs; + bool add_mde = false; + + hs = handshake_state_new(netdev_get_ifindex(device->netdev)); + + if (security == SECURITY_PSK || security == SECURITY_8021X) { + const struct l_settings *settings = iwd_get_config(); + struct ie_rsn_info bss_info; + uint8_t rsne_buf[256]; + struct ie_rsn_info info; + const char *ssid; + uint32_t mfp_setting; + + memset(&info, 0, sizeof(info)); + + memset(&bss_info, 0, sizeof(bss_info)); + scan_bss_get_rsn_info(bss, &bss_info); + + info.akm_suites = device_select_akm_suite(network, bss, + &bss_info); + + if (!info.akm_suites) + goto not_supported; + + info.pairwise_ciphers = wiphy_select_cipher(wiphy, + bss_info.pairwise_ciphers); + info.group_cipher = wiphy_select_cipher(wiphy, + bss_info.group_cipher); + + if (!info.pairwise_ciphers || !info.group_cipher) + goto not_supported; + + if (!l_settings_get_uint(settings, "General", + "ManagementFrameProtection", &mfp_setting)) + mfp_setting = 1; + + if (mfp_setting > 2) { + l_error("Invalid MFP value, using default of 1"); + mfp_setting = 1; + } + + switch (mfp_setting) { + case 0: + break; + case 1: + info.group_management_cipher = + wiphy_select_cipher(wiphy, + bss_info.group_management_cipher); + info.mfpc = info.group_management_cipher != 0; + break; + case 2: + info.group_management_cipher = + wiphy_select_cipher(wiphy, + bss_info.group_management_cipher); + + /* + * MFP required on our side, but AP doesn't support MFP + * or cipher mismatch + */ + if (info.group_management_cipher == 0) + goto not_supported; + + info.mfpc = true; + info.mfpr = true; + break; + } + + if (bss_info.mfpr && !info.mfpc) + goto not_supported; + + ssid = network_get_ssid(network); + handshake_state_set_ssid(hs, (void *) ssid, strlen(ssid)); + + /* RSN takes priority */ + if (bss->rsne) { + ie_build_rsne(&info, rsne_buf); + handshake_state_set_ap_rsn(hs, bss->rsne); + handshake_state_set_own_rsn(hs, rsne_buf); + } else { + ie_build_wpa(&info, rsne_buf); + handshake_state_set_ap_wpa(hs, bss->wpa); + handshake_state_set_own_wpa(hs, rsne_buf); + } + + if (security == SECURITY_PSK) + handshake_state_set_pmk(hs, network_get_psk(network)); + else + handshake_state_set_8021x_config(hs, + network_get_settings(network)); + + if (info.akm_suites & (IE_RSN_AKM_SUITE_FT_OVER_8021X | + IE_RSN_AKM_SUITE_FT_USING_PSK | + IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256)) + add_mde = true; + } + + if (security == SECURITY_NONE) + /* Perform FT association if available */ + add_mde = bss->mde_present; + + if (add_mde) { + uint8_t mde[5]; + + /* The MDE advertised by the BSS must be passed verbatim */ + mde[0] = IE_TYPE_MOBILITY_DOMAIN; + mde[1] = 3; + memcpy(mde + 2, bss->mde, 3); + + handshake_state_set_mde(hs, mde); + } + + return hs; + +not_supported: + handshake_state_free(hs); + + return NULL; +} + static void device_roam_failed(struct device *device) { /* @@ -593,6 +754,30 @@ static void device_roam_failed(struct device *device) device_roam_timeout_rearm(device, 60); } +static void device_reassociate_cb(struct netdev *netdev, + enum netdev_result result, + void *user_data) +{ + struct device *device = user_data; + + l_debug("%d, result: %d", device->index, result); + + if (device->state != DEVICE_STATE_ROAMING) + return; + + if (result == NETDEV_RESULT_OK) { + /* + * New signal high/low notification should occur on the next + * beacon from new AP. + */ + device->signal_low = false; + device->roam_min_time.tv_sec = 0; + + device_enter_state(device, DEVICE_STATE_CONNECTED); + } else + device_roam_failed(device); +} + static void device_fast_transition_cb(struct netdev *netdev, enum netdev_result result, void *user_data) @@ -621,6 +806,7 @@ static void device_transition_start(struct device *device, struct scan_bss *bss) { struct handshake_state *hs = netdev_get_handshake(device->netdev); uint16_t mdid; + struct handshake_state *new_hs; l_debug("%d, target %s", device->index, util_address_to_string(bss->addr)); @@ -654,8 +840,27 @@ static void device_transition_start(struct device *device, struct scan_bss *bss) return; } - /* Non-FT transition not supported yet */ - device_roam_failed(device); + /* Non-FT transition */ + + new_hs = device_handshake_setup(device, device->connected_network, bss); + if (!new_hs) { + l_error("device_handshake_setup failed in reassociation"); + + device_roam_failed(device); + return; + } + + if (netdev_reassociate(device->netdev, bss, new_hs, + device_reassociate_cb) < 0) { + handshake_state_free(new_hs); + + device_roam_failed(device); + return; + } + + device->connected_bss = bss; + device->preparing_roam = false; + device_enter_state(device, DEVICE_STATE_ROAMING); } static void device_roam_scan_triggered(int err, void *user_data) @@ -1112,158 +1317,20 @@ bool device_set_autoconnect(struct device *device, bool autoconnect) return true; } -static enum ie_rsn_akm_suite device_select_akm_suite(struct network *network, - struct scan_bss *bss, - struct ie_rsn_info *info) -{ - enum security security = network_get_security(network); - - /* - * If FT is available, use FT authentication to keep the door open - * for fast transitions. Otherwise use SHA256 version if present. - */ - - if (security == SECURITY_8021X) { - if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_OVER_8021X) && - bss->rsne && bss->mde_present) - return IE_RSN_AKM_SUITE_FT_OVER_8021X; - - if (info->akm_suites & IE_RSN_AKM_SUITE_8021X_SHA256) - return IE_RSN_AKM_SUITE_8021X_SHA256; - - if (info->akm_suites & IE_RSN_AKM_SUITE_8021X) - return IE_RSN_AKM_SUITE_8021X; - } else if (security == SECURITY_PSK) { - if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_USING_PSK) && - bss->rsne && bss->mde_present) - return IE_RSN_AKM_SUITE_FT_USING_PSK; - - if (info->akm_suites & IE_RSN_AKM_SUITE_PSK_SHA256) - return IE_RSN_AKM_SUITE_PSK_SHA256; - - if (info->akm_suites & IE_RSN_AKM_SUITE_PSK) - return IE_RSN_AKM_SUITE_PSK; - } - - return 0; -} - void device_connect_network(struct device *device, struct network *network, struct scan_bss *bss, struct l_dbus_message *message) { - enum security security = network_get_security(network); - struct wiphy *wiphy = device->wiphy; struct l_dbus *dbus = dbus_get_bus(); struct handshake_state *hs; - bool add_mde = false; - hs = handshake_state_new(netdev_get_ifindex(device->netdev)); + hs = device_handshake_setup(device, network, bss); - if (security == SECURITY_PSK || security == SECURITY_8021X) { - const struct l_settings *settings = iwd_get_config(); - struct ie_rsn_info bss_info; - uint8_t rsne_buf[256]; - struct ie_rsn_info info; - const char *ssid; - uint32_t mfp_setting; + if (!hs) { + if (message) + l_dbus_send(dbus, dbus_error_not_supported(message)); - memset(&info, 0, sizeof(info)); - - memset(&bss_info, 0, sizeof(bss_info)); - scan_bss_get_rsn_info(bss, &bss_info); - - info.akm_suites = device_select_akm_suite(network, bss, - &bss_info); - - if (!info.akm_suites) - goto not_supported; - - info.pairwise_ciphers = wiphy_select_cipher(wiphy, - bss_info.pairwise_ciphers); - info.group_cipher = wiphy_select_cipher(wiphy, - bss_info.group_cipher); - - if (!info.pairwise_ciphers || !info.group_cipher) - goto not_supported; - - if (!l_settings_get_uint(settings, "General", - "ManagementFrameProtection", &mfp_setting)) - mfp_setting = 1; - - if (mfp_setting > 2) { - l_error("Invalid MFP value, using default of 1"); - mfp_setting = 1; - } - - switch (mfp_setting) { - case 0: - break; - case 1: - info.group_management_cipher = - wiphy_select_cipher(wiphy, - bss_info.group_management_cipher); - info.mfpc = info.group_management_cipher != 0; - break; - case 2: - info.group_management_cipher = - wiphy_select_cipher(wiphy, - bss_info.group_management_cipher); - - /* - * MFP required on our side, but AP doesn't support MFP - * or cipher mismatch - */ - if (info.group_management_cipher == 0) - goto not_supported; - - info.mfpc = true; - info.mfpr = true; - break; - } - - if (bss_info.mfpr && !info.mfpc) - goto not_supported; - - ssid = network_get_ssid(network); - handshake_state_set_ssid(hs, (void *) ssid, strlen(ssid)); - - /* RSN takes priority */ - if (bss->rsne) { - ie_build_rsne(&info, rsne_buf); - handshake_state_set_ap_rsn(hs, bss->rsne); - handshake_state_set_own_rsn(hs, rsne_buf); - } else { - ie_build_wpa(&info, rsne_buf); - handshake_state_set_ap_wpa(hs, bss->wpa); - handshake_state_set_own_wpa(hs, rsne_buf); - } - - if (security == SECURITY_PSK) - handshake_state_set_pmk(hs, network_get_psk(network)); - else - handshake_state_set_8021x_config(hs, - network_get_settings(network)); - - if (info.akm_suites & (IE_RSN_AKM_SUITE_FT_OVER_8021X | - IE_RSN_AKM_SUITE_FT_USING_PSK | - IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256)) - add_mde = true; - } - - if (security == SECURITY_NONE) - /* Perform FT association if available */ - add_mde = bss->mde_present; - - if (add_mde) { - uint8_t mde[5]; - - /* The MDE advertised by the BSS must be passed verbatim */ - mde[0] = IE_TYPE_MOBILITY_DOMAIN; - mde[1] = 3; - memcpy(mde + 2, bss->mde, 3); - - handshake_state_set_mde(hs, mde); + return; } if (netdev_connect(device->netdev, bss, hs, device_netdev_event, @@ -1287,11 +1354,6 @@ void device_connect_network(struct device *device, struct network *network, IWD_DEVICE_INTERFACE, "ConnectedNetwork"); l_dbus_property_changed(dbus, network_get_path(network), IWD_NETWORK_INTERFACE, "Connected"); - return; - -not_supported: - if (message) - l_dbus_send(dbus, dbus_error_not_supported(message)); } static void device_scan_triggered(int err, void *user_data)