diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 966861c17..cece78f75 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -239,11 +239,31 @@ static int ieee802_11_parse_mle(const u8 *pos, size_t elen, } +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) { @@ -256,8 +276,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) @@ -284,6 +302,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) @@ -300,6 +319,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) @@ -370,39 +390,14 @@ 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 @@ -427,6 +422,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) { @@ -630,11 +631,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: @@ -646,12 +649,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)) { @@ -2734,37 +2731,41 @@ enum oper_chan_width op_class_to_ch_width(u8 op_class) } -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; @@ -2775,7 +2776,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 @@ -2805,7 +2806,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); } diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index ff6d2a89f..d3453da25 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -21,7 +21,6 @@ struct element { struct hostapd_hw_modes; #define MAX_NOF_MB_IES_SUPPORTED 5 -#define MAX_NUM_FRAG_IES_SUPPORTED 3 struct mb_ies_info { struct { @@ -31,21 +30,6 @@ struct mb_ies_info { u8 nof_ies; }; -struct frag_ies_info { - struct { - u8 eid; - u8 eid_ext; - const u8 *ie; - u8 ie_len; - } frags[MAX_NUM_FRAG_IES_SUPPORTED]; - - u8 n_frags; - - /* the last parsed element ID and element extension ID */ - u8 last_eid; - u8 last_eid_ext; -}; - /* Parsed Information Elements */ struct ieee802_11_elems { const u8 *ssid; @@ -162,10 +146,10 @@ struct ieee802_11_elems { u8 dils_len; u8 fils_req_params_len; u8 fils_key_confirm_len; - u8 fils_hlp_len; + size_t fils_hlp_len; u8 fils_ip_addr_assign_len; u8 key_delivery_len; - u8 wrapped_data_len; + size_t wrapped_data_len; u8 fils_pk_len; u8 owe_dh_len; u8 power_capab_len; @@ -187,7 +171,12 @@ struct ieee802_11_elems { u8 prior_access_mle_len; struct mb_ies_info mb_ies; - struct frag_ies_info frag_ies; + + /* + * The number of fragment elements to be skipped after a known + * fragmented element. + */ + unsigned int num_frag_elems; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -348,9 +337,8 @@ void hostapd_encode_edmg_chan(int edmg_enable, u8 edmg_channel, int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed, struct ieee80211_edmg_config requested); -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 wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems, u8 eid, u8 eid_ext); const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type);