SCS: Add support for QoS Characteristics in SCS request

Add support to configure the mandatory QoS Characteristics parameters
per IEEE P802.11be/D4.0, 9.4.2.316 (QoS Characteristics element), in SCS
request:
 - Minimum Service Interval
 - Maximum Service Interval
 - Minimum Data Rate
 - Delay Bound

Enable STA SCS traffic descriptor support for EHT connection when the
connected EHT AP advertises SCS traffic descriptor capability in the EHT
Capabilities element.

Signed-off-by: Purushottam Kushwaha <quic_pkushwah@quicinc.com>
This commit is contained in:
Purushottam Kushwaha 2023-04-18 19:15:06 +05:30 committed by Jouni Malinen
parent c437665041
commit 33da386553
4 changed files with 216 additions and 7 deletions

View file

@ -501,6 +501,7 @@
#define WLAN_EID_EXT_EHT_CAPABILITIES 108 #define WLAN_EID_EXT_EHT_CAPABILITIES 108
#define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109 #define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109
#define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110 #define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
#define WLAN_EID_EXT_QOS_CHARACTERISTICS 113
#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114 #define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
/* Extended Capabilities field */ /* Extended Capabilities field */
@ -2768,6 +2769,26 @@ enum scs_request_type {
SCS_REQ_CHANGE = 2, 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 */ /* Optional subelement IDs for MSCS Descriptor element */
enum mscs_description_subelem { enum mscs_description_subelem {
MCSC_SUBELEM_STATUS = 1, MCSC_SUBELEM_STATUS = 1,

View file

@ -11528,7 +11528,11 @@ static int wpas_ctrl_iface_configure_scs(struct wpa_supplicant *wpa_s,
* [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>] * [scs_id=<decimal number>] <add|remove|change> [scs_up=<0-7>]
* [classifier_type=<4|10>] * [classifier_type=<4|10>]
* [classifier params based on classifier type] * [classifier params based on classifier type]
* [tclas_processing=<0|1>] [scs_id=<decimal number>] ... * [tclas_processing=<0|1>]
* [qos_characteristics] <up/down/direct> [min_si=<decimal number>]
* [max_si=<decimal number>] [min_data_rate=<decimal number>]
* [delay_bound=<decimal number>]
* [scs_id=<decimal number>] ...
*/ */
pos1 = os_strstr(cmd, "scs_id="); pos1 = os_strstr(cmd, "scs_id=");
if (!pos1) { 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; struct active_scs_elem *active_scs_desc;
char *next_scs_desc; char *next_scs_desc;
unsigned int num_tclas_elem = 0; 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); desc_elem.scs_id = atoi(pos1 + 7);
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="); pos = os_strstr(pos1, "classifier_type=");
if (!pos) { if (!pos) {
wpa_printf(MSG_ERROR, "classifier type empty"); wpa_printf(MSG_ERROR, "classifier type empty");
goto free_scs_desc; goto qos_characteristics;
} }
tclas_present = true;
while (pos) { while (pos) {
struct tclas_element elem = { 0 }, *n; 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; 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: scs_desc_end:
n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) * n1 = os_realloc(scs_data->scs_desc_elems, (num_scs_desc + 1) *
sizeof(struct scs_desc_elem)); sizeof(struct scs_desc_elem));

View file

@ -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, 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; u8 *len, *len1;
struct tclas_element *tclas_elem; struct tclas_element *tclas_elem;
unsigned int i; unsigned int i;
struct qos_characteristics *qos_elem;
u32 control_info = 0;
/* SCS Descriptor element */ /* SCS Descriptor element */
wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR); 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) if (desc_elem->request_type == SCS_REQ_REMOVE)
goto end; 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) { 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, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
wpabuf_put_u8(buf, 1); 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); 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: end:
*len = (u8 *) wpabuf_put(buf, 0) - len - 1; *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
return 0; 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, 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; struct wpabuf *buf;
size_t buf_len = 0; 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) if (desc_elem->request_type == SCS_REQ_REMOVE)
continue; 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) if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
buf_len += 3; buf_len += 3;
@ -369,8 +446,11 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
{ {
struct wpabuf *buf = NULL; struct wpabuf *buf = NULL;
struct scs_desc_elem *desc_elem = NULL; struct scs_desc_elem *desc_elem = NULL;
const struct ieee80211_eht_capabilities *eht;
const u8 *eht_ie;
int ret = -1; int ret = -1;
unsigned int i; unsigned int i;
bool allow_scs_traffic_desc = false;
if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
return -1; return -1;
@ -385,8 +465,28 @@ int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
if (!desc_elem) if (!desc_elem)
return -1; 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, 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) if (!buf)
return -1; 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; for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
i++, desc_elem++) { i++, desc_elem++) {
/* SCS Descriptor element */ /* 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; goto end;
} }

View file

@ -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 { struct scs_desc_elem {
u8 scs_id; u8 scs_id;
enum scs_request_type request_type; enum scs_request_type request_type;
@ -604,6 +622,7 @@ struct scs_desc_elem {
struct tclas_element *tclas_elems; struct tclas_element *tclas_elems;
unsigned int num_tclas_elem; unsigned int num_tclas_elem;
u8 tclas_processing; u8 tclas_processing;
struct qos_characteristics qos_char_elem;
}; };