common: Refactor element defragmentation

Instead of saving the pointers to the fragment elements during parsing
of the frame, append all fragments found right after the element to the
element length. Defragmentation of the element can be done by parsing
appended fragment elements. This approach removes the limit on the
maximum number of fragmented elements supported in a frame.

Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
This commit is contained in:
Veerendranath Jakkam 2022-10-19 19:43:52 +05:30 committed by Jouni Malinen
parent 347ea8f0a5
commit ec03b71ee9
2 changed files with 67 additions and 78 deletions

View file

@ -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);
}

View file

@ -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);