mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2025-02-20 11:40:40 +01:00
netdev: handle multiple concurrent FT-over-DS action frames
The beauty of FT-over-DS is that a station can send and receive action frames to many APs to prepare for a future roam. Each AP authenticates the station and when a roam happens the station can immediately move to reassociation. To handle this a queue of netdev_ft_over_ds_info structs is used instead of a single entry. Using the new ft.c parser APIs these info structs can be looked up when responses come in. For now the timeouts/callbacks are kept but these will be removed as it really does not matter if the AP sends a response (keeps station happy until the next patch).
This commit is contained in:
parent
ff333a112b
commit
9b7d761db5
116
src/netdev.c
116
src/netdev.c
@ -169,7 +169,7 @@ struct netdev {
|
|||||||
struct l_genl_msg *auth_cmd;
|
struct l_genl_msg *auth_cmd;
|
||||||
struct wiphy_radio_work_item work;
|
struct wiphy_radio_work_item work;
|
||||||
|
|
||||||
struct netdev_ft_over_ds_info *ft_ds_info;
|
struct l_queue *ft_ds_list;
|
||||||
|
|
||||||
bool connected : 1;
|
bool connected : 1;
|
||||||
bool associated : 1;
|
bool associated : 1;
|
||||||
@ -687,6 +687,13 @@ static void netdev_preauth_destroy(void *data)
|
|||||||
l_free(state);
|
l_free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void netdev_ft_ds_entry_free(void *data)
|
||||||
|
{
|
||||||
|
struct netdev_ft_over_ds_info *info = data;
|
||||||
|
|
||||||
|
ft_ds_info_free(&info->super);
|
||||||
|
}
|
||||||
|
|
||||||
static void netdev_connect_free(struct netdev *netdev)
|
static void netdev_connect_free(struct netdev *netdev)
|
||||||
{
|
{
|
||||||
if (netdev->work.id)
|
if (netdev->work.id)
|
||||||
@ -754,8 +761,10 @@ static void netdev_connect_free(struct netdev *netdev)
|
|||||||
netdev->disconnect_cmd_id = 0;
|
netdev->disconnect_cmd_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (netdev->ft_ds_info)
|
if (netdev->ft_ds_list) {
|
||||||
ft_ds_info_free(&netdev->ft_ds_info->super);
|
l_queue_destroy(netdev->ft_ds_list, netdev_ft_ds_entry_free);
|
||||||
|
netdev->ft_ds_list = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netdev_connect_failed(struct netdev *netdev,
|
static void netdev_connect_failed(struct netdev *netdev,
|
||||||
@ -859,6 +868,11 @@ static void netdev_free(void *data)
|
|||||||
netdev->disconnect_idle = NULL;
|
netdev->disconnect_idle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (netdev->ft_ds_list) {
|
||||||
|
l_queue_destroy(netdev->ft_ds_list, netdev_ft_ds_entry_free);
|
||||||
|
netdev->ft_ds_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (netdev->events_ready)
|
if (netdev->events_ready)
|
||||||
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t,
|
||||||
netdev, NETDEV_WATCH_EVENT_DEL);
|
netdev, NETDEV_WATCH_EVENT_DEL);
|
||||||
@ -1291,6 +1305,11 @@ static void netdev_connect_ok(struct netdev *netdev)
|
|||||||
netdev->fw_roam_bss = NULL;
|
netdev->fw_roam_bss = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (netdev->ft_ds_list) {
|
||||||
|
l_queue_destroy(netdev->ft_ds_list, netdev_ft_ds_entry_free);
|
||||||
|
netdev->ft_ds_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@ -3447,12 +3466,6 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* In case of a previous failed over-DS attempt */
|
|
||||||
if (netdev->ft_ds_info) {
|
|
||||||
ft_ds_info_free(&netdev->ft_ds_info->super);
|
|
||||||
netdev->ft_ds_info = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(netdev->prev_bssid, orig_bss->addr, ETH_ALEN);
|
memcpy(netdev->prev_bssid, orig_bss->addr, ETH_ALEN);
|
||||||
|
|
||||||
netdev->associated = false;
|
netdev->associated = false;
|
||||||
@ -3681,12 +3694,6 @@ static int netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len,
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No need to keep this around at this point */
|
|
||||||
if (netdev->ft_ds_info) {
|
|
||||||
ft_ds_info_free(&netdev->ft_ds_info->super);
|
|
||||||
netdev->ft_ds_info = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3761,9 +3768,26 @@ static void netdev_ft_over_ds_auth_failed(struct netdev_ft_over_ds_info *info,
|
|||||||
if (info->cb)
|
if (info->cb)
|
||||||
info->cb(info->netdev, status, info->super.aa, info->user_data);
|
info->cb(info->netdev, status, info->super.aa, info->user_data);
|
||||||
|
|
||||||
|
l_queue_remove(info->netdev->ft_ds_list, info);
|
||||||
ft_ds_info_free(&info->super);
|
ft_ds_info_free(&info->super);
|
||||||
|
}
|
||||||
|
|
||||||
info->netdev->ft_ds_info = NULL;
|
struct ft_ds_finder {
|
||||||
|
const uint8_t *spa;
|
||||||
|
const uint8_t *aa;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool match_ft_ds_info(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct netdev_ft_over_ds_info *info = a;
|
||||||
|
const struct ft_ds_finder *finder = b;
|
||||||
|
|
||||||
|
if (memcmp(info->super.spa, finder->spa, 6))
|
||||||
|
return false;
|
||||||
|
if (memcmp(info->super.aa, finder->aa, 6))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
|
static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
|
||||||
@ -3771,41 +3795,41 @@ static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
|
|||||||
int rssi, void *user_data)
|
int rssi, void *user_data)
|
||||||
{
|
{
|
||||||
struct netdev *netdev = user_data;
|
struct netdev *netdev = user_data;
|
||||||
struct netdev_ft_over_ds_info *info = netdev->ft_ds_info;
|
struct netdev_ft_over_ds_info *info;
|
||||||
int ret;
|
int ret;
|
||||||
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
||||||
const uint8_t *aa;
|
const uint8_t *aa;
|
||||||
const uint8_t *spa;
|
const uint8_t *spa;
|
||||||
const uint8_t *ies;
|
const uint8_t *ies;
|
||||||
size_t ies_len;
|
size_t ies_len;
|
||||||
|
struct ft_ds_finder finder;
|
||||||
if (!info)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ret = ft_over_ds_parse_action_response(body, body_len, &spa, &aa,
|
ret = ft_over_ds_parse_action_response(body, body_len, &spa, &aa,
|
||||||
&ies, &ies_len);
|
&ies, &ies_len);
|
||||||
if (ret != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (memcmp(spa, info->super.spa, 6))
|
|
||||||
return;
|
|
||||||
if (memcmp(aa, info->super.aa, 6))
|
|
||||||
return;
|
|
||||||
|
|
||||||
ret = ft_over_ds_parse_action_ies(&info->super, netdev->handshake,
|
|
||||||
ies, ies_len);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
l_timeout_remove(info->timeout);
|
finder.spa = spa;
|
||||||
info->timeout = NULL;
|
finder.aa = aa;
|
||||||
|
|
||||||
/* Now make sure the packet contained a success status code */
|
info = l_queue_find(netdev->ft_ds_list, match_ft_ds_info, &finder);
|
||||||
|
if (!info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Lookup successful, now check the status code */
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
status_code = (uint16_t)ret;
|
status_code = (uint16_t)ret;
|
||||||
goto ft_error;
|
goto ft_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = ft_over_ds_parse_action_ies(&info->super, netdev->handshake,
|
||||||
|
ies, ies_len);
|
||||||
|
if (ret < 0)
|
||||||
|
goto ft_error;
|
||||||
|
|
||||||
|
l_timeout_remove(info->timeout);
|
||||||
|
info->timeout = NULL;
|
||||||
|
|
||||||
info->parsed = true;
|
info->parsed = true;
|
||||||
|
|
||||||
if (info->cb)
|
if (info->cb)
|
||||||
@ -3814,7 +3838,8 @@ static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ft_error:
|
ft_error:
|
||||||
l_debug("FT-over-DS to "MAC" failed", MAC_STR(info->super.aa));
|
l_debug("FT-over-DS to "MAC" failed (%d)", MAC_STR(info->super.aa),
|
||||||
|
status_code);
|
||||||
|
|
||||||
netdev_ft_over_ds_auth_failed(info, status_code);
|
netdev_ft_over_ds_auth_failed(info, status_code);
|
||||||
}
|
}
|
||||||
@ -3892,10 +3917,8 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
|
|||||||
struct scan_bss *target_bss,
|
struct scan_bss *target_bss,
|
||||||
netdev_connect_cb_t cb)
|
netdev_connect_cb_t cb)
|
||||||
{
|
{
|
||||||
struct netdev_ft_over_ds_info *info = netdev->ft_ds_info;
|
struct netdev_ft_over_ds_info *info;
|
||||||
|
struct ft_ds_finder finder;
|
||||||
if (!info || !info->parsed)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
if (!netdev->operational)
|
if (!netdev->operational)
|
||||||
return -ENOTCONN;
|
return -ENOTCONN;
|
||||||
@ -3905,6 +3928,14 @@ int netdev_fast_transition_over_ds(struct netdev *netdev,
|
|||||||
l_get_le16(target_bss->mde))
|
l_get_le16(target_bss->mde))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
finder.spa = netdev->addr;
|
||||||
|
finder.aa = target_bss->addr;
|
||||||
|
|
||||||
|
info = l_queue_find(netdev->ft_ds_list, match_ft_ds_info, &finder);
|
||||||
|
|
||||||
|
if (!info || !info->parsed)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
prepare_ft(netdev, target_bss);
|
prepare_ft(netdev, target_bss);
|
||||||
|
|
||||||
ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
|
ft_over_ds_prepare_handshake(&info->super, netdev->handshake);
|
||||||
@ -3968,10 +3999,6 @@ int netdev_fast_transition_over_ds_action(struct netdev *netdev,
|
|||||||
uint8_t buf[512];
|
uint8_t buf[512];
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
/* TODO: Just allow single outstanding action frame for now */
|
|
||||||
if (netdev->ft_ds_info)
|
|
||||||
return -EALREADY;
|
|
||||||
|
|
||||||
if (!netdev->operational)
|
if (!netdev->operational)
|
||||||
return -ENOTCONN;
|
return -ENOTCONN;
|
||||||
|
|
||||||
@ -4010,7 +4037,10 @@ int netdev_fast_transition_over_ds_action(struct netdev *netdev,
|
|||||||
|
|
||||||
iovs[2].iov_base = NULL;
|
iovs[2].iov_base = NULL;
|
||||||
|
|
||||||
netdev->ft_ds_info = info;
|
if (!netdev->ft_ds_list)
|
||||||
|
netdev->ft_ds_list = l_queue_new();
|
||||||
|
|
||||||
|
l_queue_push_head(netdev->ft_ds_list, info);
|
||||||
|
|
||||||
info->timeout = l_timeout_create_ms(300, netdev_ft_over_ds_timeout,
|
info->timeout = l_timeout_create_ms(300, netdev_ft_over_ds_timeout,
|
||||||
info, NULL);
|
info, NULL);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user