wireless: add utility function to get P2P attribute
authorJohannes Berg <johannes.berg@intel.com>
Mon, 29 Oct 2012 18:48:40 +0000 (19:48 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 6 Nov 2012 12:24:52 +0000 (13:24 +0100)
Parsing the P2P attributes can be tricky as their
contents can be split across multiple (vendor) IEs.
Thus, it's not possible to parse them like IEs (by
returning a pointer to the data.) Instead, provide
a function that copies the attribute data into a
caller-provided buffer and returns the size needed
(useful in case the buffer was too small.)

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
net/wireless/util.c

index 10c9fc6..81d7250 100644 (file)
@@ -3617,6 +3617,25 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate);
  */
 void cfg80211_unregister_wdev(struct wireless_dev *wdev);
 
+/**
+ * cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer
+ * @ies: the input IE buffer
+ * @len: the input length
+ * @attr: the attribute ID to find
+ * @buf: output buffer, can be %NULL if the data isn't needed, e.g.
+ *     if the function is only called to get the needed buffer size
+ * @bufsize: size of the output buffer
+ *
+ * The function finds a given P2P attribute in the (vendor) IEs and
+ * copies its contents to the given buffer.
+ *
+ * The return value is a negative error code (-%EILSEQ or -%ENOENT) if
+ * the data is malformed or the attribute can't be found (respectively),
+ * or the length of the found attribute (which can be zero).
+ */
+unsigned int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
+                                  u8 attr, u8 *buf, unsigned int bufsize);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index 5b6c1df..b99f01c 100644 (file)
@@ -980,6 +980,105 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
 }
 EXPORT_SYMBOL(cfg80211_calculate_bitrate);
 
+unsigned int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
+                                  u8 attr, u8 *buf, unsigned int bufsize)
+{
+       u8 *out = buf;
+       u16 attr_remaining = 0;
+       bool desired_attr = false;
+       u16 desired_len = 0;
+
+       while (len > 0) {
+               unsigned int iedatalen;
+               unsigned int copy;
+               const u8 *iedata;
+
+               if (len < 2)
+                       return -EILSEQ;
+               iedatalen = ies[1];
+               if (iedatalen + 2 > len)
+                       return -EILSEQ;
+
+               if (ies[0] != WLAN_EID_VENDOR_SPECIFIC)
+                       goto cont;
+
+               if (iedatalen < 4)
+                       goto cont;
+
+               iedata = ies + 2;
+
+               /* check WFA OUI, P2P subtype */
+               if (iedata[0] != 0x50 || iedata[1] != 0x6f ||
+                   iedata[2] != 0x9a || iedata[3] != 0x09)
+                       goto cont;
+
+               iedatalen -= 4;
+               iedata += 4;
+
+               /* check attribute continuation into this IE */
+               copy = min_t(unsigned int, attr_remaining, iedatalen);
+               if (copy && desired_attr) {
+                       desired_len += copy;
+                       if (out) {
+                               memcpy(out, iedata, min(bufsize, copy));
+                               out += min(bufsize, copy);
+                               bufsize -= min(bufsize, copy);
+                       }
+
+
+                       if (copy == attr_remaining)
+                               return desired_len;
+               }
+
+               attr_remaining -= copy;
+               if (attr_remaining)
+                       goto cont;
+
+               iedatalen -= copy;
+               iedata += copy;
+
+               while (iedatalen > 0) {
+                       u16 attr_len;
+
+                       /* P2P attribute ID & size must fit */
+                       if (iedatalen < 3)
+                               return -EILSEQ;
+                       desired_attr = iedata[0] == attr;
+                       attr_len = get_unaligned_le16(iedata + 1);
+                       iedatalen -= 3;
+                       iedata += 3;
+
+                       copy = min_t(unsigned int, attr_len, iedatalen);
+
+                       if (desired_attr) {
+                               desired_len += copy;
+                               if (out) {
+                                       memcpy(out, iedata, min(bufsize, copy));
+                                       out += min(bufsize, copy);
+                                       bufsize -= min(bufsize, copy);
+                               }
+
+                               if (copy == attr_len)
+                                       return desired_len;
+                       }
+
+                       iedata += copy;
+                       iedatalen -= copy;
+                       attr_remaining = attr_len - copy;
+               }
+
+ cont:
+               len -= ies[1] + 2;
+               ies += ies[1] + 2;
+       }
+
+       if (attr_remaining && desired_attr)
+               return -EILSEQ;
+
+       return -ENOENT;
+}
+EXPORT_SYMBOL(cfg80211_get_p2p_attr);
+
 int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
                                 u32 beacon_int)
 {