diff options
Diffstat (limited to 'src/common/ieee802_11_common.c')
-rw-r--r-- | src/common/ieee802_11_common.c | 900 |
1 files changed, 765 insertions, 135 deletions
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 44335de8..cd1b198f 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -199,11 +199,76 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, } +static int ieee802_11_parse_mle(const u8 *pos, size_t elen, size_t **total_len, + struct ieee802_11_elems *elems, + int show_errors) +{ + u8 mle_type = pos[0] & MULTI_LINK_CONTROL_TYPE_MASK; + + switch (mle_type) { + case MULTI_LINK_CONTROL_TYPE_BASIC: + elems->basic_mle = pos; + elems->basic_mle_len = elen; + *total_len = &elems->basic_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_PROBE_REQ: + elems->probe_req_mle = pos; + elems->probe_req_mle_len = elen; + *total_len = &elems->probe_req_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_RECONF: + elems->reconf_mle = pos; + elems->reconf_mle_len = elen; + *total_len = &elems->reconf_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_TDLS: + elems->tdls_mle = pos; + elems->tdls_mle_len = elen; + *total_len = &elems->tdls_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS: + elems->prior_access_mle = pos; + elems->prior_access_mle_len = elen; + *total_len = &elems->prior_access_mle_len; + break; + default: + if (show_errors) { + wpa_printf(MSG_MSGDUMP, + "Unknown Multi-Link element type %u", + mle_type); + } + return -1; + } + + return 0; +} + + +static size_t ieee802_11_fragments_length(struct ieee802_11_elems *elems, + const u8 *start, size_t len) +{ + const struct element *elem; + size_t frags_len = 0; + + for_each_element(elem, start, len) { + if (elem->id != WLAN_EID_FRAGMENT) + break; + + frags_len += elem->datalen + 2; + elems->num_frag_elems++; + } + + return frags_len; +} + + static int ieee802_11_parse_extension(const u8 *pos, size_t elen, struct ieee802_11_elems *elems, + const u8 *start, size_t len, int show_errors) { u8 ext_id; + size_t *total_len = NULL; if (elen < 1) { if (show_errors) { @@ -216,8 +281,6 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, ext_id = *pos++; elen--; - elems->frag_ies.last_eid_ext = 0; - switch (ext_id) { case WLAN_EID_EXT_ASSOC_DELAY_INFO: if (elen != 1) @@ -244,6 +307,7 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, break; elems->fils_hlp = pos; elems->fils_hlp_len = elen; + total_len = &elems->fils_hlp_len; break; case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN: if (elen < 1) @@ -260,6 +324,7 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, case WLAN_EID_EXT_WRAPPED_DATA: elems->wrapped_data = pos; elems->wrapped_data_len = elen; + total_len = &elems->wrapped_data_len; break; case WLAN_EID_EXT_FILS_PUBLIC_KEY: if (elen < 1) @@ -312,13 +377,28 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->pasn_params_len = elen; break; case WLAN_EID_EXT_EHT_CAPABILITIES: + if (elen < EHT_CAPABILITIES_IE_MIN_LEN) + break; elems->eht_capabilities = pos; elems->eht_capabilities_len = elen; break; case WLAN_EID_EXT_EHT_OPERATION: + if (elen < EHT_OPERATION_IE_MIN_LEN) + break; elems->eht_operation = pos; elems->eht_operation_len = elen; break; + case WLAN_EID_EXT_MULTI_LINK: + if (elen < 2) + break; + if (ieee802_11_parse_mle(pos, elen, &total_len, elems, + show_errors)) + return -1; + break; + case WLAN_EID_EXT_KNOWN_BSSID: + elems->mbssid_known_bss = pos; + elems->mbssid_known_bss_len = elen; + break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, @@ -328,56 +408,21 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, return -1; } - if (elen == 254) - elems->frag_ies.last_eid_ext = ext_id; + if (elen == 254 && total_len) + *total_len += ieee802_11_fragments_length( + elems, pos + elen, (start + len) - (pos + elen)); return 0; } -static void ieee802_11_parse_fragment(struct frag_ies_info *frag_ies, - const u8 *pos, u8 elen) -{ - if (frag_ies->n_frags >= MAX_NUM_FRAG_IES_SUPPORTED) { - wpa_printf(MSG_MSGDUMP, "Too many element fragments - skip"); - return; - } - - /* - * Note: while EID == 0 is a valid ID (SSID IE), it should not be - * fragmented. - */ - if (!frag_ies->last_eid) { - wpa_printf(MSG_MSGDUMP, - "Fragment without a valid last element - skip"); - return; - } - - frag_ies->frags[frag_ies->n_frags].ie = pos; - frag_ies->frags[frag_ies->n_frags].ie_len = elen; - frag_ies->frags[frag_ies->n_frags].eid = frag_ies->last_eid; - frag_ies->frags[frag_ies->n_frags].eid_ext = frag_ies->last_eid_ext; - frag_ies->n_frags++; -} - - -/** - * ieee802_11_parse_elems - Parse information elements in management frames - * @start: Pointer to the start of IEs - * @len: Length of IE buffer in octets - * @elems: Data structure for parsed elements - * @show_errors: Whether to show parsing errors in debug log - * Returns: Parsing result - */ -ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, - struct ieee802_11_elems *elems, - int show_errors) +static ParseRes __ieee802_11_parse_elems(const u8 *start, size_t len, + struct ieee802_11_elems *elems, + int show_errors) { const struct element *elem; int unknown = 0; - os_memset(elems, 0, sizeof(*elems)); - if (!start) return ParseOK; @@ -385,6 +430,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, u8 id = elem->id, elen = elem->datalen; const u8 *pos = elem->data; + if (id == WLAN_EID_FRAGMENT && elems->num_frag_elems > 0) { + elems->num_frag_elems--; + continue; + } + elems->num_frag_elems = 0; + switch (id) { case WLAN_EID_SSID: if (elen > SSID_MAX_LEN) { @@ -588,11 +639,13 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->s1g_capab = pos; break; case WLAN_EID_FRAGMENT: - ieee802_11_parse_fragment(&elems->frag_ies, pos, elen); + wpa_printf(MSG_MSGDUMP, + "Fragment without a valid last element - skip"); + break; case WLAN_EID_EXTENSION: - if (ieee802_11_parse_extension(pos, elen, elems, - show_errors)) + if (ieee802_11_parse_extension(pos, elen, elems, start, + len, show_errors)) unknown++; break; default: @@ -604,12 +657,6 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, id, elen); break; } - - if (id != WLAN_EID_FRAGMENT && elen == 255) - elems->frag_ies.last_eid = id; - - if (id == WLAN_EID_EXTENSION && !elems->frag_ies.last_eid_ext) - elems->frag_ies.last_eid = 0; } if (!for_each_element_completed(elem, start, len)) { @@ -627,6 +674,431 @@ done: } +/** + * ieee802_11_parse_elems - Parse information elements in management frames + * @start: Pointer to the start of IEs + * @len: Length of IE buffer in octets + * @elems: Data structure for parsed elements + * @show_errors: Whether to show parsing errors in debug log + * Returns: Parsing result + */ +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, + struct ieee802_11_elems *elems, + int show_errors) +{ + os_memset(elems, 0, sizeof(*elems)); + + return __ieee802_11_parse_elems(start, len, elems, show_errors); +} + + +/** + * ieee802_11_elems_clear_ids - Clear the data for the given element IDs + * @ids: Array of element IDs for which data should be cleared. + * @num: The number of entries in the array + */ +void ieee802_11_elems_clear_ids(struct ieee802_11_elems *elems, + const u8 *ids, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) { + switch (ids[i]) { + case WLAN_EID_SSID: + elems->ssid = NULL; + elems->ssid_len = 0; + break; + case WLAN_EID_SUPP_RATES: + elems->supp_rates = NULL; + elems->supp_rates_len = 0; + break; + case WLAN_EID_DS_PARAMS: + elems->ds_params = NULL; + break; + case WLAN_EID_CHALLENGE: + elems->challenge = NULL; + elems->challenge_len = 0; + break; + case WLAN_EID_ERP_INFO: + elems->erp_info = NULL; + break; + case WLAN_EID_EXT_SUPP_RATES: + elems->ext_supp_rates = NULL; + elems->ext_supp_rates_len = 0; + break; + case WLAN_EID_RSN: + elems->rsn_ie = NULL; + elems->rsn_ie_len = 0; + break; + case WLAN_EID_RSNX: + elems->rsnxe = NULL; + elems->rsnxe_len = 0; + break; + case WLAN_EID_PWR_CAPABILITY: + elems->power_capab = NULL; + elems->power_capab_len = 0; + break; + case WLAN_EID_SUPPORTED_CHANNELS: + elems->supp_channels = NULL; + elems->supp_channels_len = 0; + break; + case WLAN_EID_MOBILITY_DOMAIN: + elems->mdie = NULL; + elems->mdie_len = 0; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + elems->ftie = NULL; + elems->ftie_len = 0; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + elems->timeout_int = NULL; + break; + case WLAN_EID_HT_CAP: + elems->ht_capabilities = NULL; + break; + case WLAN_EID_HT_OPERATION: + elems->ht_operation = NULL; + break; + case WLAN_EID_MESH_CONFIG: + elems->mesh_config = NULL; + elems->mesh_config_len = 0; + break; + case WLAN_EID_MESH_ID: + elems->mesh_id = NULL; + elems->mesh_id_len = 0; + break; + case WLAN_EID_PEER_MGMT: + elems->peer_mgmt = NULL; + elems->peer_mgmt_len = 0; + break; + case WLAN_EID_VHT_CAP: + elems->vht_capabilities = NULL; + break; + case WLAN_EID_VHT_OPERATION: + elems->vht_operation = NULL; + break; + case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: + elems->vht_opmode_notif = NULL; + break; + case WLAN_EID_LINK_ID: + elems->link_id = NULL; + break; + case WLAN_EID_INTERWORKING: + elems->interworking = NULL; + elems->interworking_len = 0; + break; + case WLAN_EID_QOS_MAP_SET: + elems->qos_map_set = NULL; + elems->qos_map_set_len = 0; + break; + case WLAN_EID_EXT_CAPAB: + elems->ext_capab = NULL; + elems->ext_capab_len = 0; + break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + elems->bss_max_idle_period = NULL; + break; + case WLAN_EID_SSID_LIST: + elems->ssid_list = NULL; + elems->ssid_list_len = 0; + break; + case WLAN_EID_AMPE: + elems->ampe = NULL; + elems->ampe_len = 0; + break; + case WLAN_EID_MIC: + elems->mic = NULL; + elems->mic_len = 0; + break; + case WLAN_EID_MULTI_BAND: + os_memset(&elems->mb_ies, 0, sizeof(elems->mb_ies)); + elems->mb_ies.nof_ies = 0; + break; + case WLAN_EID_SUPPORTED_OPERATING_CLASSES: + elems->supp_op_classes = NULL; + elems->supp_op_classes_len = 0; + break; + case WLAN_EID_RRM_ENABLED_CAPABILITIES: + elems->rrm_enabled = NULL; + elems->rrm_enabled_len = 0; + break; + case WLAN_EID_CAG_NUMBER: + elems->cag_number = NULL; + elems->cag_number_len = 0; + break; + case WLAN_EID_AP_CSN: + elems->ap_csn = NULL; + break; + case WLAN_EID_FILS_INDICATION: + elems->fils_indic = NULL; + elems->fils_indic_len = 0; + break; + case WLAN_EID_DILS: + elems->dils = NULL; + elems->dils_len = 0; + break; + case WLAN_EID_S1G_CAPABILITIES: + elems->s1g_capab = NULL; + break; + } + } +} + + +/** + * ieee802_11_elems_clear_ext_ids - Clear the data for the given element + * extension IDs + * @ids: Array of element extension IDs for which data should be cleared. + * @num: The number of entries in the array + */ +void ieee802_11_elems_clear_ext_ids(struct ieee802_11_elems *elems, + const u8 *ids, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) { + switch (ids[i]) { + case WLAN_EID_EXT_ASSOC_DELAY_INFO: + elems->assoc_delay_info = NULL; + break; + case WLAN_EID_EXT_FILS_REQ_PARAMS: + elems->fils_req_params = NULL; + elems->fils_req_params_len = 0; + break; + case WLAN_EID_EXT_FILS_KEY_CONFIRM: + elems->fils_key_confirm = NULL; + elems->fils_key_confirm_len = 0; + break; + case WLAN_EID_EXT_FILS_SESSION: + elems->fils_session = NULL; + break; + case WLAN_EID_EXT_FILS_HLP_CONTAINER: + elems->fils_hlp = NULL; + elems->fils_hlp_len = 0; + break; + case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN: + elems->fils_ip_addr_assign = NULL; + elems->fils_ip_addr_assign_len = 0; + break; + case WLAN_EID_EXT_KEY_DELIVERY: + elems->key_delivery = NULL; + elems->key_delivery_len = 0; + break; + case WLAN_EID_EXT_WRAPPED_DATA: + elems->wrapped_data = NULL; + elems->wrapped_data_len = 0; + break; + case WLAN_EID_EXT_FILS_PUBLIC_KEY: + elems->fils_pk = NULL; + elems->fils_pk_len = 0; + break; + case WLAN_EID_EXT_FILS_NONCE: + elems->fils_nonce = NULL; + break; + case WLAN_EID_EXT_OWE_DH_PARAM: + elems->owe_dh = NULL; + elems->owe_dh_len = 0; + break; + case WLAN_EID_EXT_PASSWORD_IDENTIFIER: + elems->password_id = NULL; + elems->password_id_len = 0; + break; + case WLAN_EID_EXT_HE_CAPABILITIES: + elems->he_capabilities = NULL; + elems->he_capabilities_len = 0; + break; + case WLAN_EID_EXT_HE_OPERATION: + elems->he_operation = NULL; + elems->he_operation_len = 0; + break; + case WLAN_EID_EXT_OCV_OCI: + elems->oci = NULL; + elems->oci_len = 0; + break; + case WLAN_EID_EXT_SHORT_SSID_LIST: + elems->short_ssid_list = NULL; + elems->short_ssid_list_len = 0; + break; + case WLAN_EID_EXT_HE_6GHZ_BAND_CAP: + elems->he_6ghz_band_cap = NULL; + break; + case WLAN_EID_EXT_PASN_PARAMS: + elems->pasn_params = NULL; + elems->pasn_params_len = 0; + break; + case WLAN_EID_EXT_MULTI_LINK: + elems->basic_mle = NULL; + elems->probe_req_mle = NULL; + elems->reconf_mle = NULL; + elems->tdls_mle = NULL; + elems->prior_access_mle = NULL; + + elems->basic_mle_len = 0; + elems->probe_req_mle_len = 0; + elems->reconf_mle_len = 0; + elems->tdls_mle_len = 0; + elems->prior_access_mle_len = 0; + break; + case WLAN_EID_EXT_EHT_CAPABILITIES: + elems->eht_capabilities = NULL; + elems->eht_capabilities_len = 0; + break; + case WLAN_EID_EXT_EHT_OPERATION: + elems->eht_operation = NULL; + elems->eht_operation_len = 0; + break; + } + } +} + + +ParseRes ieee802_11_parse_link_assoc_req(const u8 *start, size_t len, + struct ieee802_11_elems *elems, + struct wpabuf *mlbuf, + u8 link_id, bool show_errors) +{ + const struct ieee80211_eht_ml *ml; + const u8 *pos; + ParseRes res = ParseFailed; + + pos = wpabuf_head(mlbuf); + len = wpabuf_len(mlbuf); + + /* Must have control and common info length */ + if (len < sizeof(*ml) + 1 || len < sizeof(*ml) + pos[sizeof(*ml)]) + goto out; + + ml = (const struct ieee80211_eht_ml *) pos; + + /* As we are interested with the Per-STA profile, ignore other types */ + if ((le_to_host16(ml->ml_control) & MULTI_LINK_CONTROL_TYPE_MASK) != + MULTI_LINK_CONTROL_TYPE_BASIC) + goto out; + + /* Skip the common info */ + len -= sizeof(*ml) + pos[sizeof(*ml)]; + pos += sizeof(*ml) + pos[sizeof(*ml)]; + + while (len > 2) { + size_t sub_elem_len = *(pos + 1); + size_t sta_info_len; + u16 link_info_control; + const u8 *non_inherit; + + wpa_printf(MSG_DEBUG, + "MLD: sub element: len=%zu, sub_elem_len=%zu", + len, sub_elem_len); + + if (2 + sub_elem_len > len) { + if (show_errors) + wpa_printf(MSG_DEBUG, + "MLD: error: len=%zu, sub_elem_len=%zu", + len, sub_elem_len); + goto out; + } + + if (*pos != 0) { + pos += 2 + sub_elem_len; + len -= 2 + sub_elem_len; + continue; + } + + if (sub_elem_len < 3) { + if (show_errors) + wpa_printf(MSG_DEBUG, + "MLD: error: sub_elem_len=%zu < 5", + sub_elem_len); + goto out; + } + + link_info_control = WPA_GET_LE16(pos + 2); + if ((link_info_control & BASIC_MLE_STA_CTRL_LINK_ID_MASK) != + link_id) { + pos += 2 + sub_elem_len; + len -= 2 + sub_elem_len; + continue; + } + + sta_info_len = *(pos + 4); + if (sub_elem_len < sta_info_len + 3 || sta_info_len < 1) { + if (show_errors) + wpa_printf(MSG_DEBUG, + "MLD: error: sub_elem_len=%zu, sta_info_len=%zu", + sub_elem_len, sta_info_len); + goto out; + } + + pos += sta_info_len + 4; + sub_elem_len -= sta_info_len + 2; + + if (sub_elem_len < 2) { + if (show_errors) + wpa_printf(MSG_DEBUG, + "MLD: missing capability info"); + goto out; + } + + pos += 2; + sub_elem_len -= 2; + + /* Handle non-inheritance */ + non_inherit = get_ie_ext(pos, sub_elem_len, + WLAN_EID_EXT_NON_INHERITANCE); + if (non_inherit && non_inherit[1] > 1) { + u8 non_inherit_len = non_inherit[1] - 1; + + /* + * Do not include the Non-Inheritance element when + * parsing below. It should be the last element in the + * subelement. + */ + if (3U + non_inherit_len > sub_elem_len) + goto out; + sub_elem_len -= 3 + non_inherit_len; + + /* Skip the ID, length and extension ID */ + non_inherit += 3; + + if (non_inherit_len < 1UL + non_inherit[0]) { + if (show_errors) + wpa_printf(MSG_DEBUG, + "MLD: Invalid inheritance"); + goto out; + } + + ieee802_11_elems_clear_ids(elems, &non_inherit[1], + non_inherit[0]); + + non_inherit_len -= 1 + non_inherit[0]; + non_inherit += 1 + non_inherit[0]; + + if (non_inherit_len < 1UL + non_inherit[0]) { + if (show_errors) + wpa_printf(MSG_DEBUG, + "MLD: Invalid inheritance"); + goto out; + } + + ieee802_11_elems_clear_ext_ids(elems, &non_inherit[1], + non_inherit[0]); + } + + wpa_printf(MSG_DEBUG, "MLD: link: sub_elem_len=%zu", + sub_elem_len); + + if (sub_elem_len) + res = __ieee802_11_parse_elems(pos, sub_elem_len, + elems, show_errors); + else + res = ParseOK; + break; + } + +out: + return res; +} + + int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { const struct element *elem; @@ -886,7 +1358,7 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) { u8 op_class; - return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT, + return ieee80211_freq_to_channel_ext(freq, 0, CONF_OPER_CHWIDTH_USE_HT, &op_class, channel); } @@ -896,15 +1368,15 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) * for HT40, VHT, and HE. DFS channels are not covered. * @freq: Frequency (MHz) to convert * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below - * @chanwidth: VHT/EDMG channel width (CHANWIDTH_*) + * @chanwidth: VHT/EDMG/etc. channel width * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure */ -enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, - int sec_channel, - int chanwidth, - u8 *op_class, u8 *channel) +enum hostapd_hw_mode +ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, + enum oper_chan_width chanwidth, + u8 *op_class, u8 *channel) { u8 vht_opclass; @@ -952,13 +1424,13 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, } switch (chanwidth) { - case CHANWIDTH_80MHZ: + case CONF_OPER_CHWIDTH_80MHZ: vht_opclass = 128; break; - case CHANWIDTH_160MHZ: + case CONF_OPER_CHWIDTH_160MHZ: vht_opclass = 129; break; - case CHANWIDTH_80P80MHZ: + case CONF_OPER_CHWIDTH_80P80MHZ: vht_opclass = 130; break; default: @@ -1057,15 +1529,18 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return NUM_HOSTAPD_MODES; switch (chanwidth) { - case CHANWIDTH_80MHZ: + case CONF_OPER_CHWIDTH_80MHZ: *op_class = 133; break; - case CHANWIDTH_160MHZ: + case CONF_OPER_CHWIDTH_160MHZ: *op_class = 134; break; - case CHANWIDTH_80P80MHZ: + case CONF_OPER_CHWIDTH_80P80MHZ: *op_class = 135; break; + case CONF_OPER_CHWIDTH_320MHZ: + *op_class = 137; + break; default: if (sec_channel) *op_class = 132; @@ -1090,12 +1565,12 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return NUM_HOSTAPD_MODES; switch (chanwidth) { - case CHANWIDTH_USE_HT: - case CHANWIDTH_2160MHZ: + case CONF_OPER_CHWIDTH_USE_HT: + case CONF_OPER_CHWIDTH_2160MHZ: *channel = (freq - 56160) / 2160; *op_class = 180; break; - case CHANWIDTH_4320MHZ: + case CONF_OPER_CHWIDTH_4320MHZ: /* EDMG channels 9 - 13 */ if (freq > 56160 + 2160 * 5) return NUM_HOSTAPD_MODES; @@ -1103,7 +1578,7 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, *channel = (freq - 56160) / 2160 + 8; *op_class = 181; break; - case CHANWIDTH_6480MHZ: + case CONF_OPER_CHWIDTH_6480MHZ: /* EDMG channels 17 - 20 */ if (freq > 56160 + 2160 * 4) return NUM_HOSTAPD_MODES; @@ -1111,7 +1586,7 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, *channel = (freq - 56160) / 2160 + 16; *op_class = 182; break; - case CHANWIDTH_8640MHZ: + case CONF_OPER_CHWIDTH_8640MHZ: /* EDMG channels 25 - 27 */ if (freq > 56160 + 2160 * 3) return NUM_HOSTAPD_MODES; @@ -1140,28 +1615,31 @@ int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, case CHAN_WIDTH_20_NOHT: case CHAN_WIDTH_20: case CHAN_WIDTH_40: - cw = CHANWIDTH_USE_HT; + cw = CONF_OPER_CHWIDTH_USE_HT; break; case CHAN_WIDTH_80: - cw = CHANWIDTH_80MHZ; + cw = CONF_OPER_CHWIDTH_80MHZ; break; case CHAN_WIDTH_80P80: - cw = CHANWIDTH_80P80MHZ; + cw = CONF_OPER_CHWIDTH_80P80MHZ; break; case CHAN_WIDTH_160: - cw = CHANWIDTH_160MHZ; + cw = CONF_OPER_CHWIDTH_160MHZ; break; case CHAN_WIDTH_2160: - cw = CHANWIDTH_2160MHZ; + cw = CONF_OPER_CHWIDTH_2160MHZ; break; case CHAN_WIDTH_4320: - cw = CHANWIDTH_4320MHZ; + cw = CONF_OPER_CHWIDTH_4320MHZ; break; case CHAN_WIDTH_6480: - cw = CHANWIDTH_6480MHZ; + cw = CONF_OPER_CHWIDTH_6480MHZ; break; case CHAN_WIDTH_8640: - cw = CHANWIDTH_8640MHZ; + cw = CONF_OPER_CHWIDTH_8640MHZ; + break; + case CHAN_WIDTH_320: + cw = CONF_OPER_CHWIDTH_320MHZ; break; } @@ -1263,8 +1741,9 @@ static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) if (chan < 25 || chan > 29) return -1; return 56160 + 2160 * (chan - 24); + default: + return -1; } - return -1; } @@ -1313,8 +1792,9 @@ static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan) if (chan != 25) return -1; return 56160 + 2160 * (chan - 24); + default: + return -1; } - return -1; } @@ -1369,8 +1849,9 @@ static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan) if (chan != 25) return -1; return 56160 + 2160 * (chan - 24); + default: + return -1; } - return -1; } @@ -1395,8 +1876,9 @@ static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan) if (chan < 149 || chan > 165) return -1; return 5000 + 5 * chan; + default: + return -1; } - return -1; } @@ -1458,6 +1940,7 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */ case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */ case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */ + case 137: /* UHB channels, 320 MHz: 31, 63, 95, 127, 159, 191 */ if (chan < 1 || chan > 233) return -1; return 5950 + chan * 5; @@ -1481,8 +1964,9 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) if (chan < 25 || chan > 29) return -1; return 56160 + 2160 * (chan - 24); + default: + return -1; } - return -1; } /** @@ -1822,6 +2306,9 @@ const char * status2str(u16 status) S2S(DENIED_HE_NOT_SUPPORTED) S2S(SAE_HASH_TO_ELEMENT) S2S(SAE_PK) + S2S(INVALID_PUBLIC_KEY) + S2S(PASN_BASE_AKMP_FAILED) + S2S(OCI_MISMATCH) } return "UNKNOWN"; #undef S2S @@ -1911,11 +2398,14 @@ const struct oper_class_map global_op_class[] = { /* * IEEE Std 802.11ax-2021, Table E-4 actually talks about channel center - * frequency index 42, 58, 106, 122, 138, 155, 171 with channel spacing - * of 80 MHz, but currently use the following definition for simplicity + * frequency index for operation classes 128, 129, 130, 132, 133, 134, + * and 135, but currently use the lowest 20 MHz channel for simplicity * (these center frequencies are not actual channels, which makes - * wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take - * care of removing invalid channels. + * wpas_p2p_verify_channel() fail). + * Specially for the operation class 136, it is also defined to use the + * 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. */ { HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP }, { HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP }, @@ -2272,6 +2762,9 @@ int center_idx_to_bw_6ghz(u8 idx) /* channels 15, 47, 79...*/ if ((idx & 0x1f) == 0xf) return 3; /* 160 MHz */ + /* channels 31, 63, 95, 127, 159, 191 */ + if ((idx & 0x1f) == 0x1f && idx < 192) + return 4; /* 320 MHz */ return -1; } @@ -2294,7 +2787,7 @@ bool is_6ghz_freq(int freq) bool is_6ghz_op_class(u8 op_class) { - return op_class >= 131 && op_class <= 136; + return op_class >= 131 && op_class <= 137; } @@ -2600,6 +3093,8 @@ int op_class_to_bandwidth(u8 op_class) return 160; case 136: /* UHB channels, 20 MHz: 2 */ return 20; + case 137: /* UHB channels, 320 MHz: 31, 63, 95, 127, 159, 191 */ + return 320; case 180: /* 60 GHz band, channels 1..8 */ return 2160; case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */ @@ -2608,103 +3103,111 @@ int op_class_to_bandwidth(u8 op_class) return 6480; case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */ return 8640; + default: + return 20; } - - return 20; } -int op_class_to_ch_width(u8 op_class) +enum oper_chan_width op_class_to_ch_width(u8 op_class) { switch (op_class) { case 81: case 82: - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 83: /* channels 1..9; 40 MHz */ case 84: /* channels 5..13; 40 MHz */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 115: /* channels 36,40,44,48; indoor only */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 116: /* channels 36,44; 40 MHz; indoor only */ case 117: /* channels 40,48; 40 MHz; indoor only */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 118: /* channels 52,56,60,64; dfs */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 119: /* channels 52,60; 40 MHz; dfs */ case 120: /* channels 56,64; 40 MHz; dfs */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 121: /* channels 100-140 */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 122: /* channels 100-142; 40 MHz */ case 123: /* channels 104-136; 40 MHz */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 124: /* channels 149,153,157,161 */ case 125: /* channels 149,153,157,161,165,169,171 */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 126: /* channels 149,157,165, 173; 40 MHz */ case 127: /* channels 153,161,169,177; 40 MHz */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */ - return CHANWIDTH_80MHZ; + return CONF_OPER_CHWIDTH_80MHZ; case 129: /* center freqs 50, 114, 163; 160 MHz */ - return CHANWIDTH_160MHZ; + return CONF_OPER_CHWIDTH_160MHZ; case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */ - return CHANWIDTH_80P80MHZ; + return CONF_OPER_CHWIDTH_80P80MHZ; case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */ - return CHANWIDTH_80MHZ; + return CONF_OPER_CHWIDTH_80MHZ; case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */ - return CHANWIDTH_160MHZ; + return CONF_OPER_CHWIDTH_160MHZ; case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */ - return CHANWIDTH_80P80MHZ; + return CONF_OPER_CHWIDTH_80P80MHZ; case 136: /* UHB channels, 20 MHz: 2 */ - return CHANWIDTH_USE_HT; + return CONF_OPER_CHWIDTH_USE_HT; + case 137: /* UHB channels, 320 MHz: 31, 63, 95, 127, 159, 191 */ + return CONF_OPER_CHWIDTH_320MHZ; case 180: /* 60 GHz band, channels 1..8 */ - return CHANWIDTH_2160MHZ; + return CONF_OPER_CHWIDTH_2160MHZ; case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */ - return CHANWIDTH_4320MHZ; + return CONF_OPER_CHWIDTH_4320MHZ; case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */ - return CHANWIDTH_6480MHZ; + return CONF_OPER_CHWIDTH_6480MHZ; case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */ - return CHANWIDTH_8640MHZ; + return CONF_OPER_CHWIDTH_8640MHZ; + default: + return CONF_OPER_CHWIDTH_USE_HT; } - return CHANWIDTH_USE_HT; } -struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems, - u8 eid, u8 eid_ext, - const u8 *data, u8 len) + +struct wpabuf * ieee802_11_defrag_data(const u8 *data, size_t len, + bool ext_elem) { - struct frag_ies_info *frag_ies = &elems->frag_ies; struct wpabuf *buf; - unsigned int i; + const u8 *pos, *end = data + len; + size_t min_defrag_len = ext_elem ? 255 : 256; - if (!elems || !data || !len) + if (!data || !len) return NULL; - buf = wpabuf_alloc_copy(data, len); + if (len < min_defrag_len) + return wpabuf_alloc_copy(data, len); + + buf = wpabuf_alloc_copy(data, min_defrag_len - 1); if (!buf) return NULL; - for (i = 0; i < frag_ies->n_frags; i++) { + pos = &data[min_defrag_len - 1]; + len -= min_defrag_len - 1; + while (len > 2 && pos[0] == WLAN_EID_FRAGMENT && pos[1]) { int ret; + size_t elen = 2 + pos[1]; - if (frag_ies->frags[i].eid != eid || - frag_ies->frags[i].eid_ext != eid_ext) - continue; - - ret = wpabuf_resize(&buf, frag_ies->frags[i].ie_len); + if (elen > (size_t) (end - pos) || elen > len) + break; + ret = wpabuf_resize(&buf, pos[1]); if (ret < 0) { wpabuf_free(buf); return NULL; } /* Copy only the fragment data (without the EID and length) */ - wpabuf_put_data(buf, frag_ies->frags[i].ie, - frag_ies->frags[i].ie_len); + wpabuf_put_data(buf, &pos[2], pos[1]); + pos += elen; + len -= elen; } return buf; @@ -2715,7 +3218,7 @@ struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems, u8 eid, u8 eid_ext) { const u8 *data; - u8 len; + size_t len; /* * TODO: Defragmentation mechanism can be supported for all IEs. For now @@ -2745,7 +3248,7 @@ struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems, return NULL; } - return ieee802_11_defrag_data(elems, eid, eid_ext, data, len); + return ieee802_11_defrag_data(data, len, true); } /* Parse HT capabilities to get maximum number of supported spatial streams */ @@ -2837,6 +3340,7 @@ struct supported_chan_width get_supported_channel_width( struct supported_chan_width supported_width; supported_width.is_160_supported = 0; supported_width.is_80p80_supported = 0; + supported_width.is_320_supported = 0; if (elems == NULL) return supported_width; @@ -2844,6 +3348,8 @@ struct supported_chan_width get_supported_channel_width( (struct ieee80211_vht_capabilities *) elems->vht_capabilities; struct ieee80211_he_capabilities *hecaps = (struct ieee80211_he_capabilities *) elems->he_capabilities; + struct ieee80211_eht_capabilities *ehtcaps = + (struct ieee80211_eht_capabilities *) elems->eht_capabilities; if (vhtcaps) { le32 vht_capabilities_info = @@ -2861,8 +3367,16 @@ struct supported_chan_width get_supported_channel_width( if (channel_width_set & HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G) supported_width.is_80p80_supported = 1; } - wpa_printf(MSG_DEBUG, " IE indicate 160 supported: %u, 80+80 supported: %u", - supported_width.is_160_supported, supported_width.is_80p80_supported); + if (ehtcaps) { + if (ehtcaps->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] & + EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK) + supported_width.is_320_supported = 1; + } + wpa_printf(MSG_DEBUG, + " IE indicates 320 supported: %u, 160 supported: %u, 80+80 supported: %u", + supported_width.is_320_supported, + supported_width.is_160_supported, + supported_width.is_80p80_supported); return supported_width; } @@ -2972,6 +3486,39 @@ static enum chan_width get_he_operation_channel_width( return channel_width; } +/* Parse EHT operation IE to get EHT operation channel width */ +static enum chan_width get_eht_operation_channel_width( + struct ieee80211_eht_operation *eht_oper, + int eht_oper_len) +{ + enum chan_width channel_width = CHAN_WIDTH_UNKNOWN; + if (!(eht_oper->oper_params & EHT_OPER_INFO_PRESENT) || + eht_oper_len < (EHT_OPERATION_IE_MIN_LEN + EHT_OPER_INFO_MIN_LEN)) + return channel_width; + + switch (eht_oper->oper_info.control & EHT_OPER_CHANNEL_WIDTH_MASK) { + case EHT_OPER_CHANNEL_WIDTH_20MHZ: + channel_width = CHAN_WIDTH_20; + break; + case EHT_OPER_CHANNEL_WIDTH_40MHZ: + channel_width = CHAN_WIDTH_40; + break; + case EHT_OPER_CHANNEL_WIDTH_80MHZ: + channel_width = CHAN_WIDTH_80; + break; + case EHT_OPER_CHANNEL_WIDTH_160MHZ: + channel_width = CHAN_WIDTH_160; + break; + case EHT_OPER_CHANNEL_WIDTH_320MHZ: + channel_width = CHAN_WIDTH_320; + break; + default: + break; + } + wpa_printf(MSG_DEBUG, " EHT operation CBW: %u", channel_width); + return channel_width; +} + /* Parse HT/VHT/HE operation IEs to get operation channel width */ enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems) { @@ -2985,7 +3532,14 @@ enum chan_width get_operation_channel_width(struct ieee802_11_elems *elems) (struct ieee80211_vht_operation_info *) elems->vht_operation; struct ieee80211_he_operation *he_oper = (struct ieee80211_he_operation *) elems->he_operation; - if (he_oper) + struct ieee80211_eht_operation *eht_oper = + (struct ieee80211_eht_operation *) elems->eht_operation; + + if (eht_oper) + channel_width = get_eht_operation_channel_width( + eht_oper, elems->eht_operation_len); + + if (channel_width == CHAN_WIDTH_UNKNOWN && he_oper) channel_width = get_he_operation_channel_width( he_oper, elems->he_operation_len); @@ -3009,7 +3563,11 @@ enum chan_width get_sta_operation_chan_width( enum chan_width ap_operation_chan_width, struct supported_chan_width sta_supported_chan_width) { - if (ap_operation_chan_width == CHAN_WIDTH_160) + if (ap_operation_chan_width == CHAN_WIDTH_320 && + sta_supported_chan_width.is_320_supported) + return CHAN_WIDTH_320; + if (ap_operation_chan_width == CHAN_WIDTH_160 || + ap_operation_chan_width == CHAN_WIDTH_320) return (sta_supported_chan_width.is_160_supported) ? CHAN_WIDTH_160 : CHAN_WIDTH_80; if (ap_operation_chan_width == CHAN_WIDTH_80P80) @@ -3017,3 +3575,75 @@ enum chan_width get_sta_operation_chan_width( ? CHAN_WIDTH_80P80 : CHAN_WIDTH_80; return ap_operation_chan_width; } + +const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type) +{ + const struct element *elem; + + if (!ies) + return NULL; + + for_each_element_extid(elem, WLAN_EID_EXT_MULTI_LINK, ies, len) { + if (elem->datalen >= 2 && + (elem->data[1] & MULTI_LINK_CONTROL_TYPE_MASK) == type) + return &elem->id; + } + + return NULL; +} + + +const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len) +{ + const size_t mld_addr_pos = + 2 /* Control field */ + + 1 /* Common Info Length field */; + const size_t fixed_len = mld_addr_pos + + ETH_ALEN /* MLD MAC Address field */; + + if (len < fixed_len) + return NULL; + + if ((buf[0] & MULTI_LINK_CONTROL_TYPE_MASK) != + MULTI_LINK_CONTROL_TYPE_BASIC) + return NULL; + + return &buf[mld_addr_pos]; +} + + +struct wpabuf * ieee802_11_defrag_mle(struct ieee802_11_elems *elems, u8 type) +{ + const u8 *data; + size_t len; + + switch (type) { + case MULTI_LINK_CONTROL_TYPE_BASIC: + data = elems->basic_mle; + len = elems->basic_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_PROBE_REQ: + data = elems->probe_req_mle; + len = elems->probe_req_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_RECONF: + data = elems->reconf_mle; + len = elems->reconf_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_TDLS: + data = elems->tdls_mle; + len = elems->tdls_mle_len; + break; + case MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS: + data = elems->prior_access_mle; + len = elems->prior_access_mle_len; + break; + default: + wpa_printf(MSG_DEBUG, + "Defragmentation not supported for Multi-Link element type=%u", + type); + return NULL; + } + + return ieee802_11_defrag_data(data, len, true); +} |