wifi: cfg80211: add element defragmentation helper

This is already needed within mac80211 and support is also needed by
cfg80211 to parse ML elements.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230616094949.29c3ebeed10d.I009c049289dd0162c2e858ed8b68d2875a672ed6@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Benjamin Berg 2023-06-16 09:54:05 +03:00 committed by Johannes Berg
parent 39432f8a37
commit f837a653a0
2 changed files with 82 additions and 0 deletions

View File

@ -6676,6 +6676,28 @@ cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
return (const void *)cfg80211_find_vendor_elem(oui, oui_type, ies, len); return (const void *)cfg80211_find_vendor_elem(oui, oui_type, ies, len);
} }
/**
* cfg80211_defragment_element - Defrag the given element data into a buffer
*
* @elem: the element to defragment
* @ies: elements where @elem is contained
* @ieslen: length of @ies
* @data: buffer to store element data
* @data_len: length of @data
* @frag_id: the element ID of fragments
*
* Return: length of @data, or -EINVAL on error
*
* Copy out all data from an element that may be fragmented into @data, while
* skipping all headers.
*
* The function uses memmove() internally. It is acceptable to defragment an
* element in-place.
*/
ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
size_t ieslen, u8 *data, size_t data_len,
u8 frag_id);
/** /**
* cfg80211_send_layer2_update - send layer 2 update frame * cfg80211_send_layer2_update - send layer 2 update frame
* *

View File

@ -2288,6 +2288,66 @@ out:
kfree(profile); kfree(profile);
} }
ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
size_t ieslen, u8 *data, size_t data_len,
u8 frag_id)
{
const struct element *next;
ssize_t copied;
u8 elem_datalen;
if (!elem)
return -EINVAL;
/* elem might be invalid after the memmove */
next = (void *)(elem->data + elem->datalen);
elem_datalen = elem->datalen;
if (elem->id == WLAN_EID_EXTENSION) {
copied = elem->datalen - 1;
if (copied > data_len)
return -ENOSPC;
memmove(data, elem->data + 1, copied);
} else {
copied = elem->datalen;
if (copied > data_len)
return -ENOSPC;
memmove(data, elem->data, copied);
}
/* Fragmented elements must have 255 bytes */
if (elem_datalen < 255)
return copied;
for (elem = next;
elem->data < ies + ieslen &&
elem->data + elem->datalen < ies + ieslen;
elem = next) {
/* elem might be invalid after the memmove */
next = (void *)(elem->data + elem->datalen);
if (elem->id != frag_id)
break;
elem_datalen = elem->datalen;
if (copied + elem_datalen > data_len)
return -ENOSPC;
memmove(data + copied, elem->data, elem_datalen);
copied += elem_datalen;
/* Only the last fragment may be short */
if (elem_datalen != 255)
break;
}
return copied;
}
EXPORT_SYMBOL(cfg80211_defragment_element);
struct cfg80211_bss * struct cfg80211_bss *
cfg80211_inform_bss_data(struct wiphy *wiphy, cfg80211_inform_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data, struct cfg80211_inform_bss *data,