2-octet operating classes in Support Operating Classes element

A previous workaround was used to move the special operating class 130
to the end of the Supported Operating Classes element to avoid getting
any following entry being ignored or misunderstood. That is not really
the correct way of encoding the special cases, i.e., 80+80 MHz channels
that use two nonadjacent frequency segments.

Add support for encoding the 80+80 MHz channel with the 2-octet
operating class design using the Operating Class Duple sequence field of
the Supported Operating Classes element instead of listing the operating
classes that have the 80+ behavior limit set indication in Table E-4
(i.e., opclass 130 and 135) as 1-octet operating classes in the
Operating Classes field.

Fixes: a92660a00e ("Work around Supported Operating Classes element issues for 6 GHz")
Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
Jouni Malinen 2024-03-05 16:07:22 +02:00 committed by Jouni Malinen
parent 460df51ed8
commit 9e90486bce
3 changed files with 61 additions and 7 deletions

View file

@ -2040,6 +2040,13 @@ int is_dfs_global_op_class(u8 op_class)
}
bool is_80plus_op_class(u8 op_class)
{
/* Operating classes with "80+" behavior indication in Table E-4 */
return op_class == 130 || op_class == 135;
}
static int is_11b(u8 rate)
{
return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
@ -2406,9 +2413,17 @@ const struct oper_class_map global_op_class[] = {
* channel center frequency index value, but it happens to be a 20 MHz
* channel and the channel number in the channel set would match the
* value in for the frequency center.
*
* Operating class value pair 128 and 130 is used to describe a 80+80
* MHz channel on the 5 GHz band. 130 is identified with "80+", so this
* is encoded with two octets 130 and 128. Similarly, operating class
* value pair 133 and 135 is used to describe a 80+80 MHz channel on
* the 6 GHz band (135 being the one with "80+" indication). All other
* operating classes listed here are used as 1-octet values.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 132, 1, 233, 8, BW40, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 133, 1, 233, 16, BW80, P2P_SUPP },
@ -2429,11 +2444,6 @@ const struct oper_class_map global_op_class[] = {
{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
/* Keep the operating class 130 as the last entry as a workaround for
* the OneHundredAndThirty Delimiter value used in the Supported
* Operating Classes element to indicate the end of the Operating
* Classes field. */
{ HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP },
{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
};

View file

@ -235,6 +235,7 @@ int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
u16 num_modes);
int is_dfs_global_op_class(u8 op_class);
bool is_80plus_op_class(u8 op_class);
enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
int supp_rates_11b_only(struct ieee802_11_elems *elems);

View file

@ -522,6 +522,7 @@ size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s,
u8 op, current, chan;
u8 *ie_len;
size_t res;
bool op128 = false, op130 = false, op133 = false, op135 = false;
/*
* Determine the current operating class correct mode based on
@ -549,8 +550,50 @@ size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s,
wpabuf_put_u8(buf, current);
for (op = 0; global_op_class[op].op_class; op++) {
if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op]))
wpabuf_put_u8(buf, global_op_class[op].op_class);
bool supp;
u8 op_class = global_op_class[op].op_class;
supp = wpas_op_class_supported(wpa_s, ssid,
&global_op_class[op]);
if (!supp)
continue;
switch (op_class) {
case 128:
op128 = true;
break;
case 130:
op130 = true;
break;
case 133:
op133 = true;
break;
case 135:
op135 = true;
break;
}
if (is_80plus_op_class(op_class))
continue;
/* Add a 1-octet operating class to the Operating Class field */
wpabuf_put_u8(buf, global_op_class[op].op_class);
}
/* Add the 2-octet operating classes (i.e., 80+80 MHz cases), if any */
if ((op128 && op130) || (op133 && op135)) {
/* Operating Class Duple Sequence field */
/* Zero Delimiter */
wpabuf_put_u8(buf, 0);
/* Operating Class Duple List */
if (op128 && op130) {
wpabuf_put_u8(buf, 130);
wpabuf_put_u8(buf, 128);
}
if (op133 && op135) {
wpabuf_put_u8(buf, 135);
wpabuf_put_u8(buf, 133);
}
}
*ie_len = wpabuf_len(buf) - 2;