From 49a68e0d88890060a2b9b6c6ad1ec53eb50abccf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 7 Feb 2019 23:26:38 +0100 Subject: [PATCH] cfg80211: add various struct element finding helpers We currently have a number of helpers to find elements that just return a u8 *, change those to return a struct element and add inlines to deal with the u8 * compatibility. Note that the match behaviour is changed to start the natch at the data, so conversion from _ie_match to _elem_match need to be done carefully. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 137 ++++++++++++++++++++++++++++++++++++++--- net/wireless/scan.c | 39 +++++------- net/wireless/util.c | 6 +- 3 files changed, 149 insertions(+), 33 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7f2739a90bdb..c077de7fbd8a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2063,15 +2063,29 @@ struct cfg80211_bss { }; /** - * ieee80211_bss_get_ie - find IE with given ID + * ieee80211_bss_get_elem - find element with given ID * @bss: the bss to search - * @ie: the IE ID + * @id: the element ID * * Note that the return value is an RCU-protected pointer, so * rcu_read_lock() must be held when calling this function. * Return: %NULL if not found. */ -const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie); +const struct element *ieee80211_bss_get_elem(struct cfg80211_bss *bss, u8 id); + +/** + * ieee80211_bss_get_ie - find IE with given ID + * @bss: the bss to search + * @id: the element ID + * + * Note that the return value is an RCU-protected pointer, so + * rcu_read_lock() must be held when calling this function. + * Return: %NULL if not found. + */ +static inline const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 id) +{ + return (void *)ieee80211_bss_get_elem(bss, id); +} /** @@ -4999,6 +5013,33 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, unsigned int cfg80211_classify8021d(struct sk_buff *skb, struct cfg80211_qos_map *qos_map); +/** + * cfg80211_find_elem_match - match information element and byte array in data + * + * @eid: element ID + * @ies: data consisting of IEs + * @len: length of data + * @match: byte array to match + * @match_len: number of bytes in the match array + * @match_offset: offset in the IE data where the byte array should match. + * Note the difference to cfg80211_find_ie_match() which considers + * the offset to start from the element ID byte, but here we take + * the data portion instead. + * + * Return: %NULL if the element ID could not be found or if + * the element is invalid (claims to be longer than the given + * data) or if the byte array doesn't match; otherwise return the + * requested element struct. + * + * Note: There are no checks on the element length other than + * having to fit into the given data and being large enough for the + * byte array to match. + */ +const struct element * +cfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len, + const u8 *match, unsigned int match_len, + unsigned int match_offset); + /** * cfg80211_find_ie_match - match information element and byte array in data * @@ -5023,9 +5064,44 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb, * having to fit into the given data and being large enough for the * byte array to match. */ -const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len, - const u8 *match, int match_len, - int match_offset); +static inline const u8 * +cfg80211_find_ie_match(u8 eid, const u8 *ies, unsigned int len, + const u8 *match, unsigned int match_len, + unsigned int match_offset) +{ + /* match_offset can't be smaller than 2, unless match_len is + * zero, in which case match_offset must be zero as well. + */ + if (WARN_ON((match_len && match_offset < 2) || + (!match_len && match_offset))) + return NULL; + + return (void *)cfg80211_find_elem_match(eid, ies, len, + match, match_len, + match_offset ? + match_offset - 2 : 0); +} + +/** + * cfg80211_find_elem - find information element in data + * + * @eid: element ID + * @ies: data consisting of IEs + * @len: length of data + * + * Return: %NULL if the element ID could not be found or if + * the element is invalid (claims to be longer than the given + * data) or if the byte array doesn't match; otherwise return the + * requested element struct. + * + * Note: There are no checks on the element length other than + * having to fit into the given data. + */ +static inline const struct element * +cfg80211_find_elem(u8 eid, const u8 *ies, int len) +{ + return cfg80211_find_elem_match(eid, ies, len, NULL, 0, 0); +} /** * cfg80211_find_ie - find information element in data @@ -5047,6 +5123,28 @@ static inline const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) return cfg80211_find_ie_match(eid, ies, len, NULL, 0, 0); } +/** + * cfg80211_find_ext_elem - find information element with EID Extension in data + * + * @ext_eid: element ID Extension + * @ies: data consisting of IEs + * @len: length of data + * + * Return: %NULL if the etended element could not be found or if + * the element is invalid (claims to be longer than the given + * data) or if the byte array doesn't match; otherwise return the + * requested element struct. + * + * Note: There are no checks on the element length other than + * having to fit into the given data. + */ +static inline const struct element * +cfg80211_find_ext_elem(u8 ext_eid, const u8 *ies, int len) +{ + return cfg80211_find_elem_match(WLAN_EID_EXTENSION, ies, len, + &ext_eid, 1, 0); +} + /** * cfg80211_find_ext_ie - find information element with EID Extension in data * @@ -5068,6 +5166,25 @@ static inline const u8 *cfg80211_find_ext_ie(u8 ext_eid, const u8 *ies, int len) &ext_eid, 1, 2); } +/** + * cfg80211_find_vendor_elem - find vendor specific information element in data + * + * @oui: vendor OUI + * @oui_type: vendor-specific OUI type (must be < 0xff), negative means any + * @ies: data consisting of IEs + * @len: length of data + * + * Return: %NULL if the vendor specific element ID could not be found or if the + * element is invalid (claims to be longer than the given data); otherwise + * return the element structure for the requested element. + * + * Note: There are no checks on the element length other than having to fit into + * the given data. + */ +const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type, + const u8 *ies, + unsigned int len); + /** * cfg80211_find_vendor_ie - find vendor specific information element in data * @@ -5084,8 +5201,12 @@ static inline const u8 *cfg80211_find_ext_ie(u8 ext_eid, const u8 *ies, int len) * Note: There are no checks on the element length other than having to fit into * the given data. */ -const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type, - const u8 *ies, int len); +static inline const u8 * +cfg80211_find_vendor_ie(unsigned int oui, int oui_type, + const u8 *ies, unsigned int len) +{ + return (void *)cfg80211_find_vendor_elem(oui, oui_type, ies, len); +} /** * cfg80211_send_layer2_update - send layer 2 update frame diff --git a/net/wireless/scan.c b/net/wireless/scan.c index c7f64bb9c581..d2c9ca5f4f57 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -480,48 +480,43 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *rdev) __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE); } -const u8 *cfg80211_find_ie_match(u8 eid, const u8 *ies, int len, - const u8 *match, int match_len, - int match_offset) +const struct element * +cfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len, + const u8 *match, unsigned int match_len, + unsigned int match_offset) { const struct element *elem; - /* match_offset can't be smaller than 2, unless match_len is - * zero, in which case match_offset must be zero as well. - */ - if (WARN_ON((match_len && match_offset < 2) || - (!match_len && match_offset))) - return NULL; - for_each_element_id(elem, eid, ies, len) { - if (elem->datalen >= match_offset - 2 + match_len && - !memcmp(elem->data + match_offset - 2, match, match_len)) - return (void *)elem; + if (elem->datalen >= match_offset + match_len && + !memcmp(elem->data + match_offset, match, match_len)) + return elem; } return NULL; } -EXPORT_SYMBOL(cfg80211_find_ie_match); +EXPORT_SYMBOL(cfg80211_find_elem_match); -const u8 *cfg80211_find_vendor_ie(unsigned int oui, int oui_type, - const u8 *ies, int len) +const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type, + const u8 *ies, + unsigned int len) { - const u8 *ie; + const struct element *elem; u8 match[] = { oui >> 16, oui >> 8, oui, oui_type }; int match_len = (oui_type < 0) ? 3 : sizeof(match); if (WARN_ON(oui_type > 0xff)) return NULL; - ie = cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, ies, len, - match, match_len, 2); + elem = cfg80211_find_elem_match(WLAN_EID_VENDOR_SPECIFIC, ies, len, + match, match_len, 0); - if (ie && (ie[1] < 4)) + if (!elem || elem->datalen < 4) return NULL; - return ie; + return elem; } -EXPORT_SYMBOL(cfg80211_find_vendor_ie); +EXPORT_SYMBOL(cfg80211_find_vendor_elem); static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, const u8 *ssid, size_t ssid_len) diff --git a/net/wireless/util.c b/net/wireless/util.c index cd48cdd582c0..61fa33d0019e 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -776,7 +776,7 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb, } EXPORT_SYMBOL(cfg80211_classify8021d); -const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) +const struct element *ieee80211_bss_get_elem(struct cfg80211_bss *bss, u8 id) { const struct cfg80211_bss_ies *ies; @@ -784,9 +784,9 @@ const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) if (!ies) return NULL; - return cfg80211_find_ie(ie, ies->data, ies->len); + return cfg80211_find_elem(id, ies->data, ies->len); } -EXPORT_SYMBOL(ieee80211_bss_get_ie); +EXPORT_SYMBOL(ieee80211_bss_get_elem); void cfg80211_upload_connect_keys(struct wireless_dev *wdev) {