diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index fbf9afa77..a5ec42c74 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -501,6 +501,7 @@ #define WLAN_EID_EXT_EHT_CAPABILITIES 108 #define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109 #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110 +#define WLAN_EID_EXT_QOS_CHARACTERISTICS 113 #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114 /* Extended Capabilities field */ @@ -2768,6 +2769,26 @@ enum scs_request_type { SCS_REQ_CHANGE = 2, }; +/* + * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, + * Table 9-404s (Direction subfield encoding) + */ +enum scs_direction { + SCS_DIRECTION_UP = 0, + SCS_DIRECTION_DOWN = 1, + SCS_DIRECTION_DIRECT = 2, +}; + +/* + * IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, + * Figure 9-1001av (Control Info field format) + */ +#define EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET 0 +#define EHT_QOS_CONTROL_INFO_TID_OFFSET 2 +#define EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET 6 +#define EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET 9 +#define EHT_QOS_CONTROL_INFO_LINK_ID_OFFSET 25 + /* Optional subelement IDs for MSCS Descriptor element */ enum mscs_description_subelem { MCSC_SUBELEM_STATUS = 1, diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 9abfeb216..4f81175ff 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -11528,7 +11528,11 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, * [scs_id=] [scs_up=<0-7>] * [classifier_type=<4|10>] * [classifier params based on classifier type] - * [tclas_processing=<0|1>] [scs_id=] ... + * [tclas_processing=<0|1>] + * [qos_characteristics] [min_si=] + * [max_si=] [min_data_rate=] + * [delay_bound=] + * [scs_id=] ... */ pos1 = os_strstr(cmd, "scs_id="); if (!pos1) { @@ -11543,7 +11547,8 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, struct active_scs_elem *active_scs_desc; char *next_scs_desc; unsigned int num_tclas_elem = 0; - bool scsid_active = false; + bool scsid_active = false, tclas_present = false; + struct qos_characteristics *qos_elem = &desc_elem.qos_char_elem; desc_elem.scs_id = atoi(pos1 + 7); pos1 += 7; @@ -11619,8 +11624,9 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, pos = os_strstr(pos1, "classifier_type="); if (!pos) { wpa_printf(MSG_ERROR, "classifier type empty"); - goto free_scs_desc; + goto qos_characteristics; } + tclas_present = true; while (pos) { struct tclas_element elem = { 0 }, *n; @@ -11684,6 +11690,68 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s, desc_elem.tclas_processing = val; } + qos_characteristics: + pos1 = os_strstr(pos1, "qos_characteristics"); + if (!pos1 && !tclas_present) + goto free_scs_desc; + if (!pos1) + goto scs_desc_end; + + qos_elem->available = true; + if (os_strstr(pos1, "up ")) { + qos_elem->direction = SCS_DIRECTION_UP; + if (tclas_present) { + wpa_printf(MSG_ERROR, + "TCLAS with direction:UP not allowed"); + goto free_scs_desc; + } + } else if (os_strstr(pos1, "down ")) { + qos_elem->direction = SCS_DIRECTION_DOWN; + } else if (os_strstr(pos1, "direct ")) { + qos_elem->direction = SCS_DIRECTION_DIRECT; + } + + pos1 = os_strstr(pos1, "min_si="); + if (!pos1) { + wpa_printf(MSG_ERROR, "Min SI is required"); + goto free_scs_desc; + } + qos_elem->min_si = atoi(pos1 + 7); + + pos1 = os_strstr(pos1, "max_si="); + if (!pos1) { + wpa_printf(MSG_ERROR, "Max SI is required"); + goto free_scs_desc; + } + qos_elem->max_si = atoi(pos1 + 7); + + if (qos_elem->min_si && qos_elem->max_si && + qos_elem->max_si < qos_elem->min_si) { + wpa_printf(MSG_ERROR, "Invalid Max SI"); + goto free_scs_desc; + } + + pos1 = os_strstr(pos1, "min_data_rate="); + if (!pos1) { + wpa_printf(MSG_ERROR, "Min data rate is required"); + goto free_scs_desc; + } + qos_elem->min_data_rate = atoi(pos1 + 14); + + pos1 = os_strstr(pos1, "delay_bound="); + if (!pos1) { + wpa_printf(MSG_ERROR, "Delay Bound is required"); + goto free_scs_desc; + } + qos_elem->delay_bound = atoi(pos1 + 12); + + if (qos_elem->min_data_rate >= BIT(24) || + qos_elem->delay_bound >= BIT(24)) { + wpa_printf(MSG_ERROR, + "Invalid min_data_rate or delay_bound"); + goto free_scs_desc; + } + scs_desc_end: n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) * sizeof(struct scs_desc_elem)); diff --git a/wpa_supplicant/robust_av.c b/wpa_supplicant/robust_av.c index 4b4fe6494..6794e99ff 100644 --- a/wpa_supplicant/robust_av.c +++ b/wpa_supplicant/robust_av.c @@ -97,12 +97,27 @@ static int wpas_populate_type10_classifier(struct type10_params *type10_param, } +static bool tclas_elem_required(const struct qos_characteristics *qos_elem) +{ + if (!qos_elem || !qos_elem->available) + return true; + + if (qos_elem->direction == SCS_DIRECTION_DOWN) + return true; + + return false; +} + + static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem, - struct wpabuf *buf) + struct wpabuf *buf, + bool allow_scs_traffic_desc) { u8 *len, *len1; struct tclas_element *tclas_elem; unsigned int i; + struct qos_characteristics *qos_elem; + u32 control_info = 0; /* SCS Descriptor element */ wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR); @@ -112,6 +127,9 @@ static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem, if (desc_elem->request_type == SCS_REQ_REMOVE) goto end; + if (!tclas_elem_required(&desc_elem->qos_char_elem)) + goto skip_tclas_elem; + if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) { wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY); wpabuf_put_u8(buf, 1); @@ -164,6 +182,40 @@ static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem, wpabuf_put_u8(buf, desc_elem->tclas_processing); } +skip_tclas_elem: + if (allow_scs_traffic_desc && desc_elem->qos_char_elem.available) { + qos_elem = &desc_elem->qos_char_elem; + /* Element ID, Length, and Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + len1 = wpabuf_put(buf, 1); + wpabuf_put_u8(buf, WLAN_EID_EXT_QOS_CHARACTERISTICS); + + /* IEEE P802.11be/D4.0, 9.4.2.316 QoS Characteristics element, + * Figure 9-1001av (Control Info field format) + */ + control_info = ((u32) qos_elem->direction << + EHT_QOS_CONTROL_INFO_DIRECTION_OFFSET); + control_info |= ((u32) desc_elem->intra_access_priority << + EHT_QOS_CONTROL_INFO_TID_OFFSET); + control_info |= ((u32) desc_elem->intra_access_priority << + EHT_QOS_CONTROL_INFO_USER_PRIORITY_OFFSET); + control_info |= ((u32) qos_elem->mask << + EHT_QOS_CONTROL_INFO_PRESENCE_MASK_OFFSET); + + /* Control Info */ + wpabuf_put_le32(buf, control_info); + /* Minimum Service Interval */ + wpabuf_put_le32(buf, qos_elem->min_si); + /* Maximum Service Interval */ + wpabuf_put_le32(buf, qos_elem->max_si); + /* Minimum Data Rate */ + wpabuf_put_le24(buf, qos_elem->min_data_rate); + /* Delay Bound */ + wpabuf_put_le24(buf, qos_elem->delay_bound); + + *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1; + } + end: *len = (u8 *) wpabuf_put(buf, 0) - len - 1; return 0; @@ -273,8 +325,26 @@ static size_t tclas_elem_len(const struct tclas_element *elem) } +static size_t qos_char_len(const struct qos_characteristics *qos_elem) +{ + size_t buf_len = 0; + + buf_len += 1 + /* Element ID */ + 1 + /* Length */ + 1 + /* Element ID Extension */ + 4 + /* Control Info */ + 4 + /* Minimum Service Interval */ + 4 + /* Maximum Service Interval */ + 3 + /* Minimum Data Rate */ + 3; /* Delay Bound */ + + return buf_len; +} + + static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem, - unsigned int num_scs_desc) + unsigned int num_scs_desc, + bool allow_scs_traffic_desc) { struct wpabuf *buf; size_t buf_len = 0; @@ -292,6 +362,13 @@ static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem, if (desc_elem->request_type == SCS_REQ_REMOVE) continue; + if (allow_scs_traffic_desc && + desc_elem->qos_char_elem.available) + buf_len += qos_char_len(&desc_elem->qos_char_elem); + + if (!tclas_elem_required(&desc_elem->qos_char_elem)) + continue; + if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) buf_len += 3; @@ -369,8 +446,11 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) { struct wpabuf *buf = NULL; struct scs_desc_elem *desc_elem = NULL; + const struct ieee80211_eht_capabilities *eht; + const u8 *eht_ie; int ret = -1; unsigned int i; + bool allow_scs_traffic_desc = false; if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) return -1; @@ -385,8 +465,28 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) if (!desc_elem) return -1; + if (wpa_is_non_eht_scs_traffic_desc_supported(wpa_s->current_bss)) + allow_scs_traffic_desc = true; + + /* Allow SCS Traffic descriptor support for EHT connection */ + eht_ie = wpa_bss_get_ie_ext(wpa_s->current_bss, + WLAN_EID_EXT_EHT_CAPABILITIES); + if (wpa_s->connection_eht && eht_ie && + eht_ie[1] >= 1 + IEEE80211_EHT_CAPAB_MIN_LEN) { + eht = (const struct ieee80211_eht_capabilities *) &eht_ie[3]; + if (eht->mac_cap & EHT_MACCAP_SCS_TRAFFIC_DESC) + allow_scs_traffic_desc = true; + } + + if (!allow_scs_traffic_desc && desc_elem->qos_char_elem.available) { + wpa_dbg(wpa_s, MSG_INFO, + "Connection does not support EHT/non-EHT SCS Traffic Description - could not send SCS Request with QoS Characteristics"); + return -1; + } + buf = allocate_scs_buf(desc_elem, - wpa_s->scs_robust_av_req.num_scs_desc); + wpa_s->scs_robust_av_req.num_scs_desc, + allow_scs_traffic_desc); if (!buf) return -1; @@ -400,7 +500,8 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s) for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc; i++, desc_elem++) { /* SCS Descriptor element */ - if (wpas_populate_scs_descriptor_ie(desc_elem, buf) < 0) + if (wpas_populate_scs_descriptor_ie(desc_elem, buf, + allow_scs_traffic_desc) < 0) goto end; } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 478b36d86..d655595cf 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -596,6 +596,24 @@ struct tclas_element { }; +struct qos_characteristics { + bool available; + + /* Control Info Direction */ + u8 direction; + /* Presence Bitmap Of Additional Parameters */ + u16 mask; + /* Minimum Service Interval */ + u32 min_si; + /* Maximum Service Interval */ + u32 max_si; + /* Minimum Data Rate */ + u32 min_data_rate; + /* Delay Bound */ + u32 delay_bound; +}; + + struct scs_desc_elem { u8 scs_id; enum scs_request_type request_type; @@ -604,6 +622,7 @@ struct scs_desc_elem { struct tclas_element *tclas_elems; unsigned int num_tclas_elem; u8 tclas_processing; + struct qos_characteristics qos_char_elem; };