diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 126a7892c..8da5683c2 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -157,6 +157,8 @@ extern "C" { #define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " #define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK " +/** Result of SCS setup */ +#define WPA_EVENT_SCS_RESULT "CTRL-EVENT-SCS-RESULT " /* WPS ER events */ #define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD " diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 8eb033c78..49b2f8664 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2505,6 +2505,10 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0)) ret = -1; + /* Robust AV SCS Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x13\x01", 2) < 0) + ret = -1; + /* Robust AV MSCS Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x13\x05", 2) < 0) ret = -1; diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 0773c0452..170d8c19c 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -11180,6 +11180,12 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, int val; unsigned int num_scs_desc = 0; + if (wpa_s->ongoing_scs_req) { + wpa_printf(MSG_ERROR, "%s: SCS Request already in queue", + __func__); + return -1; + } + /** * format: * [scs_id=] [scs_up=<0-7>] @@ -11197,8 +11203,10 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, while (pos1) { struct scs_desc_elem *n1; + struct active_scs_elem *active_scs_desc; char *next_scs_desc; unsigned int num_tclas_elem = 0; + bool scsid_active = false; desc_elem.scs_id = atoi(pos1 + 7); pos1 += 7; @@ -11218,13 +11226,36 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, pos1[next_scs_desc - pos1 - 1] = '\0'; } + dl_list_for_each(active_scs_desc, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + if (desc_elem.scs_id == active_scs_desc->scs_id) { + scsid_active = true; + break; + } + } + if (os_strstr(pos1, "add ")) { desc_elem.request_type = SCS_REQ_ADD; + if (scsid_active) { + wpa_printf(MSG_ERROR, "SCSID %d already active", + desc_elem.scs_id); + return -1; + } } else if (os_strstr(pos1, "remove")) { desc_elem.request_type = SCS_REQ_REMOVE; + if (!scsid_active) { + wpa_printf(MSG_ERROR, "SCSID %d not active", + desc_elem.scs_id); + return -1; + } goto scs_desc_end; } else if (os_strstr(pos1, "change ")) { desc_elem.request_type = SCS_REQ_CHANGE; + if (!scsid_active) { + wpa_printf(MSG_ERROR, "SCSID %d not active", + desc_elem.scs_id); + return -1; + } } else { wpa_printf(MSG_ERROR, "SCS Request type invalid"); goto free_scs_desc; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a565e658f..44f2c41f2 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -4259,6 +4259,13 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_DPP */ + if (category == WLAN_ACTION_ROBUST_AV_STREAMING && + payload[0] == ROBUST_AV_SCS_RESP) { + wpas_handle_robust_av_scs_recv_action(wpa_s, mgmt->sa, + payload + 1, plen - 1); + return; + } + if (category == WLAN_ACTION_ROBUST_AV_STREAMING && payload[0] == ROBUST_AV_MSCS_RESP) { wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa, diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c index 598bbdedf..a8b77d1d4 100644 --- a/wpa_supplicant/robust_av.c +++ b/wpa_supplicant/robust_av.c @@ -8,6 +8,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_common.h" #include "wpa_supplicant_i.h" @@ -15,6 +16,9 @@ #include "bss.h" +#define SCS_RESP_TIMEOUT 1 + + void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av, struct wpabuf *buf) { @@ -323,6 +327,43 @@ static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem, } +static void scs_request_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct active_scs_elem *scs_desc, *prev; + + if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) + return; + + /* Once timeout is over, remove all SCS descriptors with no response */ + dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + u8 bssid[ETH_ALEN] = { 0 }; + const u8 *src; + + if (scs_desc->status == SCS_DESC_SUCCESS) + continue; + + if (wpa_s->current_bss) + src = wpa_s->current_bss->bssid; + else + src = bssid; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR + " SCSID=%u status_code=timedout", MAC2STR(src), + scs_desc->scs_id); + + dl_list_del(&scs_desc->list); + wpa_printf(MSG_INFO, "%s: SCSID %d removed after timeout", + __func__, scs_desc->scs_id); + os_free(scs_desc); + } + + eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); + wpa_s->ongoing_scs_req = false; +} + + int wpas_send_scs_req(struct wpa_supplicant *wpa_s) { struct wpabuf *buf = NULL; @@ -369,7 +410,33 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) if (ret < 0) { wpa_dbg(wpa_s, MSG_ERROR, "SCS: Failed to send SCS Request"); wpa_s->scs_dialog_token--; + goto end; } + + desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems; + for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc; + i++, desc_elem++) { + struct active_scs_elem *active_scs_elem; + + if (desc_elem->request_type != SCS_REQ_ADD) + continue; + + active_scs_elem = os_malloc(sizeof(struct active_scs_elem)); + if (!active_scs_elem) + break; + active_scs_elem->scs_id = desc_elem->scs_id; + active_scs_elem->status = SCS_DESC_SENT; + dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list); + } + + /* + * Register a timeout after which this request will be removed from + * the cache. + */ + eloop_register_timeout(SCS_RESP_TIMEOUT, 0, scs_request_timer, wpa_s, + NULL); + wpa_s->ongoing_scs_req = true; + end: wpabuf_free(buf); free_up_scs_desc(&wpa_s->scs_robust_av_req); @@ -481,3 +548,114 @@ void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, " status_code=%u", MAC2STR(bssid), status); wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS; } + + +void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *buf, + size_t len) +{ + u8 dialog_token; + unsigned int i, count; + struct active_scs_elem *scs_desc, *prev; + + if (len < 2) + return; + if (!wpa_s->ongoing_scs_req) { + wpa_printf(MSG_INFO, + "SCS: Drop received response due to no ongoing request"); + return; + } + + dialog_token = *buf++; + len--; + if (dialog_token != wpa_s->scs_dialog_token) { + wpa_printf(MSG_INFO, + "SCS: Drop received frame due to dialog token mismatch: received:%u expected:%u", + dialog_token, wpa_s->scs_dialog_token); + return; + } + + /* This Count field does not exist in the IEEE Std 802.11-2020 + * definition of the SCS Response frame. However, it was accepted to + * be added into REVme per REVme/D0.0 CC35 CID 49 (edits in document + * 11-21-0688-07). */ + count = *buf++; + len--; + if (count == 0 || count * 3 > len) { + wpa_printf(MSG_INFO, + "SCS: Drop received frame due to invalid count: %u (remaining %zu octets)", + count, len); + return; + } + + for (i = 0; i < count; i++) { + u8 id; + u16 status; + bool scs_desc_found = false; + + id = *buf++; + status = WPA_GET_LE16(buf); + buf += 2; + len -= 3; + + dl_list_for_each(scs_desc, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + if (id == scs_desc->scs_id) { + scs_desc_found = true; + break; + } + } + + if (!scs_desc_found) { + wpa_printf(MSG_INFO, "SCS: SCS ID invalid %u", id); + continue; + } + + if (status != WLAN_STATUS_SUCCESS) { + dl_list_del(&scs_desc->list); + os_free(scs_desc); + } else if (status == WLAN_STATUS_SUCCESS) { + scs_desc->status = SCS_DESC_SUCCESS; + } + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR + " SCSID=%u status_code=%u", MAC2STR(src), id, status); + } + + eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); + wpa_s->ongoing_scs_req = false; + + dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids, + struct active_scs_elem, list) { + if (scs_desc->status != SCS_DESC_SUCCESS) { + wpa_msg(wpa_s, MSG_INFO, + WPA_EVENT_SCS_RESULT "bssid=" MACSTR + " SCSID=%u status_code=response_not_received", + MAC2STR(src), scs_desc->scs_id); + dl_list_del(&scs_desc->list); + os_free(scs_desc); + } + } +} + + +static void wpas_clear_active_scs_ids(struct wpa_supplicant *wpa_s) +{ + struct active_scs_elem *scs_elem; + + while ((scs_elem = dl_list_first(&wpa_s->active_scs_ids, + struct active_scs_elem, list))) { + dl_list_del(&scs_elem->list); + os_free(scs_elem); + } +} + + +void wpas_scs_deinit(struct wpa_supplicant *wpa_s) +{ + free_up_scs_desc(&wpa_s->scs_robust_av_req); + wpa_s->scs_dialog_token = 0; + wpas_clear_active_scs_ids(wpa_s); + eloop_cancel_timeout(scs_request_timer, wpa_s, NULL); + wpa_s->ongoing_scs_req = false; +} diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 4eda013fc..d994de4bf 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -743,8 +743,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #ifdef CONFIG_PASN wpas_pasn_auth_stop(wpa_s); #endif /* CONFIG_PASN */ - free_up_scs_desc(&wpa_s->scs_robust_av_req); - wpa_s->scs_dialog_token = 0; + wpas_scs_deinit(wpa_s); } @@ -3974,8 +3973,7 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); - free_up_scs_desc(&wpa_s->scs_robust_av_req); - wpa_s->scs_dialog_token = 0; + wpas_scs_deinit(wpa_s); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); } @@ -5174,6 +5172,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) #ifdef CONFIG_TESTING_OPTIONS dl_list_init(&wpa_s->drv_signal_override); #endif /* CONFIG_TESTING_OPTIONS */ + dl_list_init(&wpa_s->active_scs_ids); return wpa_s; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 417a479aa..6c394fe89 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -650,6 +650,19 @@ struct scs_robust_av_data { }; +enum scs_response_status { + SCS_DESC_SENT = 0, + SCS_DESC_SUCCESS = 1, +}; + + +struct active_scs_elem { + struct dl_list list; + u8 scs_id; + enum scs_response_status status; +}; + + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -1481,6 +1494,8 @@ struct wpa_supplicant { #ifdef CONFIG_TESTING_OPTIONS unsigned int disable_scs_support:1; #endif /* CONFIG_TESTING_OPTIONS */ + struct dl_list active_scs_ids; + bool ongoing_scs_req; }; @@ -1816,6 +1831,10 @@ void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid, int wpas_send_scs_req(struct wpa_supplicant *wpa_s); void free_up_tclas_elem(struct scs_desc_elem *elem); void free_up_scs_desc(struct scs_robust_av_data *data); +void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *buf, + size_t len); +void wpas_scs_deinit(struct wpa_supplicant *wpa_s); int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid, int akmp, int cipher,