mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-22 13:02:44 +01:00
station: try multiple roam candidates
This converts station to using ft_action/ft_authenticate and ft_associate and dropping the use of the netdev-only/auth-proto logic. Doing this allows for more flexibility if FT fails by letting IWD try another roam candidate instead of disconnecting.
This commit is contained in:
parent
ceebf0dc53
commit
0e6aaea2a9
227
src/station.c
227
src/station.c
@ -59,6 +59,7 @@
|
||||
#include "src/frame-xchg.h"
|
||||
#include "src/sysfs.h"
|
||||
#include "src/band.h"
|
||||
#include "src/ft.h"
|
||||
|
||||
static struct l_queue *station_list;
|
||||
static uint32_t netdev_watch;
|
||||
@ -115,6 +116,8 @@ struct station {
|
||||
|
||||
uint32_t wiphy_watch;
|
||||
|
||||
struct wiphy_radio_work_item ft_work;
|
||||
|
||||
bool preparing_roam : 1;
|
||||
bool roam_scan_full : 1;
|
||||
bool signal_low : 1;
|
||||
@ -1676,6 +1679,8 @@ static void station_roam_state_clear(struct station *station)
|
||||
}
|
||||
|
||||
l_queue_clear(station->roam_bss_list, l_free);
|
||||
|
||||
ft_clear_authentications(netdev_get_ifindex(station->netdev));
|
||||
}
|
||||
|
||||
static void station_reset_connection_state(struct station *station)
|
||||
@ -1987,7 +1992,8 @@ static void station_ft_ds_action_start(struct station *station)
|
||||
* Fire and forget. Netdev will maintain a cache of responses
|
||||
* and when the time comes these can be referenced for a roam
|
||||
*/
|
||||
netdev_fast_transition_over_ds_action(station->netdev, bss);
|
||||
ft_action(netdev_get_ifindex(station->netdev),
|
||||
station->connected_bss->frequency, bss);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2116,42 +2122,26 @@ static void station_reassociate_cb(struct netdev *netdev,
|
||||
station_roam_failed(station);
|
||||
}
|
||||
|
||||
static void station_fast_transition_cb(struct netdev *netdev,
|
||||
enum netdev_result result,
|
||||
void *event_data,
|
||||
void *user_data)
|
||||
{
|
||||
struct station *station = user_data;
|
||||
|
||||
l_debug("%u, result: %d", netdev_get_ifindex(station->netdev), result);
|
||||
|
||||
if (station->state != STATION_STATE_ROAMING)
|
||||
return;
|
||||
|
||||
if (result == NETDEV_RESULT_OK)
|
||||
station_roamed(station);
|
||||
else
|
||||
station_roam_failed(station);
|
||||
}
|
||||
|
||||
static void station_netdev_event(struct netdev *netdev, enum netdev_event event,
|
||||
void *event_data, void *user_data);
|
||||
|
||||
static void station_transition_reassociate(struct station *station,
|
||||
static int station_transition_reassociate(struct station *station,
|
||||
struct scan_bss *bss,
|
||||
struct handshake_state *new_hs)
|
||||
{
|
||||
if (netdev_reassociate(station->netdev, bss, station->connected_bss,
|
||||
int ret;
|
||||
|
||||
ret = netdev_reassociate(station->netdev, bss, station->connected_bss,
|
||||
new_hs, station_netdev_event,
|
||||
station_reassociate_cb, station) < 0) {
|
||||
handshake_state_free(new_hs);
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
station_reassociate_cb, station);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool bss_match_bssid(const void *a, const void *b)
|
||||
@ -2223,19 +2213,107 @@ static void station_preauthenticate_cb(struct netdev *netdev,
|
||||
handshake_state_set_supplicant_ie(new_hs, rsne_buf);
|
||||
}
|
||||
|
||||
station_transition_reassociate(station, bss, new_hs);
|
||||
if (station_transition_reassociate(station, bss, new_hs) < 0) {
|
||||
handshake_state_free(new_hs);
|
||||
station_roam_failed(station);
|
||||
}
|
||||
}
|
||||
|
||||
static void station_transition_start(struct station *station)
|
||||
static void station_transition_start(struct station *station);
|
||||
|
||||
static bool station_ft_work_ready(struct wiphy_radio_work_item *item)
|
||||
{
|
||||
struct station *station = l_container_of(item, struct station, ft_work);
|
||||
struct roam_bss *rbss = l_queue_pop_head(station->roam_bss_list);
|
||||
struct scan_bss *bss = network_bss_find_by_addr(
|
||||
station->connected_network, rbss->addr);
|
||||
int ret;
|
||||
|
||||
l_free(rbss);
|
||||
|
||||
/* Very unlikely, but the BSS could have gone away */
|
||||
if (!bss)
|
||||
goto try_next;
|
||||
|
||||
ret = ft_associate(netdev_get_ifindex(station->netdev), bss->addr);
|
||||
if (ret == -ENOENT) {
|
||||
try_next:
|
||||
station_transition_start(station);
|
||||
return true;
|
||||
} else if (ret < 0)
|
||||
goto assoc_failed;
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
|
||||
return true;
|
||||
|
||||
assoc_failed:
|
||||
station_roam_failed(station);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct wiphy_radio_work_item_ops ft_work_ops = {
|
||||
.do_work = station_ft_work_ready,
|
||||
};
|
||||
|
||||
static bool station_fast_transition(struct station *station,
|
||||
struct scan_bss *bss)
|
||||
{
|
||||
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
||||
struct network *connected = station->connected_network;
|
||||
const struct network_info *info = network_get_info(connected);
|
||||
const struct iovec *vendor_ies;
|
||||
size_t iov_elems = 0;
|
||||
int ret;
|
||||
|
||||
/* Rebuild handshake RSN for target AP */
|
||||
if (station_build_handshake_rsn(hs, station->wiphy,
|
||||
station->connected_network, bss) < 0)
|
||||
return false;
|
||||
|
||||
/* Reset the vendor_ies in case they're different */
|
||||
vendor_ies = network_info_get_extra_ies(info, bss, &iov_elems);
|
||||
handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
|
||||
|
||||
if ((hs->mde[4] & 1)) {
|
||||
ret = ft_associate(netdev_get_ifindex(station->netdev),
|
||||
bss->addr);
|
||||
/* No action responses from this BSS, try over air */
|
||||
if (ret == -ENOENT)
|
||||
goto try_over_air;
|
||||
else if (ret < 0)
|
||||
return false;
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
try_over_air:
|
||||
/*
|
||||
* Send FT-Authenticate and insert a work item which will be
|
||||
* gated until authentication completes
|
||||
*/
|
||||
ft_authenticate(netdev_get_ifindex(station->netdev), bss);
|
||||
|
||||
wiphy_radio_work_insert(station->wiphy, &station->ft_work,
|
||||
WIPHY_WORK_PRIORITY_CONNECT, &ft_work_ops);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool station_try_next_transition(struct station *station,
|
||||
struct scan_bss *bss)
|
||||
{
|
||||
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
||||
struct network *connected = station->connected_network;
|
||||
enum security security = network_get_security(connected);
|
||||
struct handshake_state *new_hs;
|
||||
struct ie_rsn_info cur_rsne, target_rsne;
|
||||
int ret;
|
||||
struct roam_bss *rbss = l_queue_peek_head(station->roam_bss_list);
|
||||
struct scan_bss *bss = network_bss_find_by_addr(connected, rbss->addr);
|
||||
|
||||
l_debug("%u, target %s", netdev_get_ifindex(station->netdev),
|
||||
util_address_to_string(bss->addr));
|
||||
@ -2244,54 +2322,8 @@ static void station_transition_start(struct station *station)
|
||||
station->ap_directed_roaming = false;
|
||||
|
||||
/* Can we use Fast Transition? */
|
||||
if (station_can_fast_transition(hs, bss)) {
|
||||
const struct network_info *info = network_get_info(connected);
|
||||
const struct iovec *vendor_ies;
|
||||
size_t iov_elems = 0;
|
||||
|
||||
/* Rebuild handshake RSN for target AP */
|
||||
if (station_build_handshake_rsn(hs, station->wiphy,
|
||||
station->connected_network, bss) < 0) {
|
||||
l_error("rebuilding handshake rsne failed");
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset the vendor_ies in case they're different */
|
||||
vendor_ies = network_info_get_extra_ies(info, bss, &iov_elems);
|
||||
handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
|
||||
|
||||
if ((hs->mde[4] & 1)) {
|
||||
ret = netdev_fast_transition_over_ds(station->netdev,
|
||||
bss, station->connected_bss,
|
||||
station_fast_transition_cb);
|
||||
/* No action responses from this BSS, try over air */
|
||||
if (ret == -ENOENT)
|
||||
goto try_over_air;
|
||||
else if (ret < 0) {
|
||||
/*
|
||||
* If we are here FT-over-air will not work
|
||||
* either (identical checks) so try again later.
|
||||
*/
|
||||
station_roam_retry(station);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try_over_air:
|
||||
if (netdev_fast_transition(station->netdev, bss,
|
||||
station->connected_bss,
|
||||
station_fast_transition_cb) < 0) {
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
station->connected_bss = bss;
|
||||
station->preparing_roam = false;
|
||||
station_enter_state(station, STATION_STATE_ROAMING);
|
||||
|
||||
return;
|
||||
}
|
||||
if (station_can_fast_transition(hs, bss))
|
||||
return station_fast_transition(station, bss);
|
||||
|
||||
/* Non-FT transition */
|
||||
|
||||
@ -2321,17 +2353,46 @@ try_over_air:
|
||||
if (netdev_preauthenticate(station->netdev, bss,
|
||||
station_preauthenticate_cb,
|
||||
station) >= 0)
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
new_hs = station_handshake_setup(station, connected, bss);
|
||||
if (!new_hs) {
|
||||
l_error("station_handshake_setup failed in reassociation");
|
||||
station_roam_failed(station);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
station_transition_reassociate(station, bss, new_hs);
|
||||
if (station_transition_reassociate(station, bss, new_hs) < 0) {
|
||||
handshake_state_free(new_hs);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void station_transition_start(struct station *station)
|
||||
{
|
||||
struct roam_bss *rbss;
|
||||
bool roaming = false;
|
||||
|
||||
/*
|
||||
* For each failed attempt pop the BSS leaving the head of the queue
|
||||
* with the current roam candidate.
|
||||
*/
|
||||
while ((rbss = l_queue_peek_head(station->roam_bss_list))) {
|
||||
struct scan_bss *bss = network_bss_find_by_addr(
|
||||
station->connected_network, rbss->addr);
|
||||
|
||||
roaming = station_try_next_transition(station, bss);
|
||||
if (roaming)
|
||||
break;
|
||||
|
||||
l_queue_pop_head(station->roam_bss_list);
|
||||
l_free(rbss);
|
||||
}
|
||||
|
||||
if (!roaming)
|
||||
station_roam_failed(station);
|
||||
}
|
||||
|
||||
static void station_roam_scan_triggered(int err, void *user_data)
|
||||
|
Loading…
Reference in New Issue
Block a user