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). --- src/netdev.c | 112 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 41 deletions(-) diff --git a/src/netdev.c b/src/netdev.c index 65627b9f..c194e78f 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -169,7 +169,7 @@ struct netdev { struct l_genl_msg *auth_cmd; 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 associated : 1; @@ -687,6 +687,13 @@ static void netdev_preauth_destroy(void *data) 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) { if (netdev->work.id) @@ -754,8 +761,10 @@ static void netdev_connect_free(struct netdev *netdev) netdev->disconnect_cmd_id = 0; } - if (netdev->ft_ds_info) - ft_ds_info_free(&netdev->ft_ds_info->super); + if (netdev->ft_ds_list) { + 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, @@ -854,6 +863,11 @@ static void netdev_free(void *data) 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) WATCHLIST_NOTIFY(&netdev_watches, netdev_watch_func_t, netdev, NETDEV_WATCH_EVENT_DEL); @@ -1286,6 +1300,11 @@ static void netdev_connect_ok(struct netdev *netdev) 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) { netdev->connect_cb(netdev, NETDEV_RESULT_OK, NULL, netdev->user_data); @@ -3442,12 +3461,6 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss, if (err < 0) 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); netdev->associated = false; @@ -3676,12 +3689,6 @@ static int netdev_ft_tx_associate(struct iovec *ie_iov, size_t iov_len, 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; } @@ -3756,9 +3763,26 @@ static void netdev_ft_over_ds_auth_failed(struct netdev_ft_over_ds_info *info, if (info->cb) 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); +} - 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, @@ -3766,41 +3790,41 @@ static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr, int rssi, void *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; uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED; const uint8_t *aa; const uint8_t *spa; const uint8_t *ies; size_t ies_len; - - if (!info) - return; + struct ft_ds_finder finder; ret = ft_over_ds_parse_action_response(body, body_len, &spa, &aa, &ies, &ies_len); - if (ret != 0) + if (ret < 0) return; - if (memcmp(spa, info->super.spa, 6)) - return; - if (memcmp(aa, info->super.aa, 6)) + finder.spa = spa; + finder.aa = aa; + + 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) { + status_code = (uint16_t)ret; + goto ft_error; + } + ret = ft_over_ds_parse_action_ies(&info->super, netdev->handshake, ies, ies_len); if (ret < 0) - return; + goto ft_error; l_timeout_remove(info->timeout); info->timeout = NULL; - /* Now make sure the packet contained a success status code */ - if (ret > 0) { - status_code = (uint16_t)ret; - goto ft_error; - } - info->parsed = true; if (info->cb) @@ -3809,7 +3833,8 @@ static void netdev_ft_response_frame_event(const struct mmpdu_header *hdr, return; 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); } @@ -3887,10 +3912,8 @@ int netdev_fast_transition_over_ds(struct netdev *netdev, struct scan_bss *target_bss, netdev_connect_cb_t cb) { - struct netdev_ft_over_ds_info *info = netdev->ft_ds_info; - - if (!info || !info->parsed) - return -ENOENT; + struct netdev_ft_over_ds_info *info; + struct ft_ds_finder finder; if (!netdev->operational) return -ENOTCONN; @@ -3900,6 +3923,14 @@ int netdev_fast_transition_over_ds(struct netdev *netdev, l_get_le16(target_bss->mde)) 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); ft_over_ds_prepare_handshake(&info->super, netdev->handshake); @@ -3963,10 +3994,6 @@ int netdev_fast_transition_over_ds_action(struct netdev *netdev, uint8_t buf[512]; size_t len; - /* TODO: Just allow single outstanding action frame for now */ - if (netdev->ft_ds_info) - return -EALREADY; - if (!netdev->operational) return -ENOTCONN; @@ -4005,7 +4032,10 @@ int netdev_fast_transition_over_ds_action(struct netdev *netdev, 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, NULL); -- 2.31.1