wifi: cfg80211: add element defragmentation helper
authorBenjamin Berg <benjamin.berg@intel.com>
Fri, 16 Jun 2023 06:54:05 +0000 (09:54 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 19 Jun 2023 10:05:29 +0000 (12:05 +0200)
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>
include/net/cfg80211.h
net/wireless/scan.c

index e912b7c..9972de1 100644 (file)
@@ -6677,6 +6677,28 @@ cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
 }
 
 /**
+ * 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
  *
  * @dev: network device
index 75e6e03..dc71c6a 100644 (file)
@@ -2288,6 +2288,66 @@ out:
        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 *
 cfg80211_inform_bss_data(struct wiphy *wiphy,
                         struct cfg80211_inform_bss *data,