18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Radiotap parser
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright 2007		Andy Green <andy@warmcat.com>
58c2ecf20Sopenharmony_ci * Copyright 2009		Johannes Berg <johannes@sipsolutions.net>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
88c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as
98c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of BSD
128c2ecf20Sopenharmony_ci * license.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * See COPYING for more details.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/export.h>
198c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
208c2ecf20Sopenharmony_ci#include <net/ieee80211_radiotap.h>
218c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* function prototypes and related defs are in include/net/cfg80211.h */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic const struct radiotap_align_size rtap_namespace_sizes[] = {
268c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
278c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
288c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
298c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
308c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
318c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
328c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
338c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
348c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
358c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
368c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
378c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
388c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
398c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
408c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
418c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
428c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
438c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
448c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
458c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
468c2ecf20Sopenharmony_ci	[IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, },
478c2ecf20Sopenharmony_ci	/*
488c2ecf20Sopenharmony_ci	 * add more here as they are defined in radiotap.h
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct ieee80211_radiotap_namespace radiotap_ns = {
538c2ecf20Sopenharmony_ci	.n_bits = ARRAY_SIZE(rtap_namespace_sizes),
548c2ecf20Sopenharmony_ci	.align_size = rtap_namespace_sizes,
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/**
588c2ecf20Sopenharmony_ci * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
598c2ecf20Sopenharmony_ci * @iterator: radiotap_iterator to initialize
608c2ecf20Sopenharmony_ci * @radiotap_header: radiotap header to parse
618c2ecf20Sopenharmony_ci * @max_length: total length we can parse into (eg, whole packet length)
628c2ecf20Sopenharmony_ci * @vns: vendor namespaces to parse
638c2ecf20Sopenharmony_ci *
648c2ecf20Sopenharmony_ci * Returns: 0 or a negative error code if there is a problem.
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci * This function initializes an opaque iterator struct which can then
678c2ecf20Sopenharmony_ci * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
688c2ecf20Sopenharmony_ci * argument which is present in the header.  It knows about extended
698c2ecf20Sopenharmony_ci * present headers and handles them.
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci * How to use:
728c2ecf20Sopenharmony_ci * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
738c2ecf20Sopenharmony_ci * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
748c2ecf20Sopenharmony_ci * checking for a good 0 return code.  Then loop calling
758c2ecf20Sopenharmony_ci * __ieee80211_radiotap_iterator_next()... it returns either 0,
768c2ecf20Sopenharmony_ci * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
778c2ecf20Sopenharmony_ci * The iterator's @this_arg member points to the start of the argument
788c2ecf20Sopenharmony_ci * associated with the current argument index that is present, which can be
798c2ecf20Sopenharmony_ci * found in the iterator's @this_arg_index member.  This arg index corresponds
808c2ecf20Sopenharmony_ci * to the IEEE80211_RADIOTAP_... defines.
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * Radiotap header length:
838c2ecf20Sopenharmony_ci * You can find the CPU-endian total radiotap header length in
848c2ecf20Sopenharmony_ci * iterator->max_length after executing ieee80211_radiotap_iterator_init()
858c2ecf20Sopenharmony_ci * successfully.
868c2ecf20Sopenharmony_ci *
878c2ecf20Sopenharmony_ci * Alignment Gotcha:
888c2ecf20Sopenharmony_ci * You must take care when dereferencing iterator.this_arg
898c2ecf20Sopenharmony_ci * for multibyte types... the pointer is not aligned.  Use
908c2ecf20Sopenharmony_ci * get_unaligned((type *)iterator.this_arg) to dereference
918c2ecf20Sopenharmony_ci * iterator.this_arg for type "type" safely on all arches.
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * Example code:
948c2ecf20Sopenharmony_ci * See Documentation/networking/radiotap-headers.rst
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciint ieee80211_radiotap_iterator_init(
988c2ecf20Sopenharmony_ci	struct ieee80211_radiotap_iterator *iterator,
998c2ecf20Sopenharmony_ci	struct ieee80211_radiotap_header *radiotap_header,
1008c2ecf20Sopenharmony_ci	int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	/* check the radiotap header can actually be present */
1038c2ecf20Sopenharmony_ci	if (max_length < sizeof(struct ieee80211_radiotap_header))
1048c2ecf20Sopenharmony_ci		return -EINVAL;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Linux only supports version 0 radiotap format */
1078c2ecf20Sopenharmony_ci	if (radiotap_header->it_version)
1088c2ecf20Sopenharmony_ci		return -EINVAL;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* sanity check for allowed length and radiotap length field */
1118c2ecf20Sopenharmony_ci	if (max_length < get_unaligned_le16(&radiotap_header->it_len))
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	iterator->_rtheader = radiotap_header;
1158c2ecf20Sopenharmony_ci	iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
1168c2ecf20Sopenharmony_ci	iterator->_arg_index = 0;
1178c2ecf20Sopenharmony_ci	iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
1188c2ecf20Sopenharmony_ci	iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
1198c2ecf20Sopenharmony_ci	iterator->_reset_on_ext = 0;
1208c2ecf20Sopenharmony_ci	iterator->_next_bitmap = &radiotap_header->it_present;
1218c2ecf20Sopenharmony_ci	iterator->_next_bitmap++;
1228c2ecf20Sopenharmony_ci	iterator->_vns = vns;
1238c2ecf20Sopenharmony_ci	iterator->current_namespace = &radiotap_ns;
1248c2ecf20Sopenharmony_ci	iterator->is_radiotap_ns = 1;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* find payload start allowing for extended bitmap(s) */
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
1298c2ecf20Sopenharmony_ci		if ((unsigned long)iterator->_arg -
1308c2ecf20Sopenharmony_ci		    (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
1318c2ecf20Sopenharmony_ci		    (unsigned long)iterator->_max_length)
1328c2ecf20Sopenharmony_ci			return -EINVAL;
1338c2ecf20Sopenharmony_ci		while (get_unaligned_le32(iterator->_arg) &
1348c2ecf20Sopenharmony_ci					(1 << IEEE80211_RADIOTAP_EXT)) {
1358c2ecf20Sopenharmony_ci			iterator->_arg += sizeof(uint32_t);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci			/*
1388c2ecf20Sopenharmony_ci			 * check for insanity where the present bitmaps
1398c2ecf20Sopenharmony_ci			 * keep claiming to extend up to or even beyond the
1408c2ecf20Sopenharmony_ci			 * stated radiotap header length
1418c2ecf20Sopenharmony_ci			 */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci			if ((unsigned long)iterator->_arg -
1448c2ecf20Sopenharmony_ci			    (unsigned long)iterator->_rtheader +
1458c2ecf20Sopenharmony_ci			    sizeof(uint32_t) >
1468c2ecf20Sopenharmony_ci			    (unsigned long)iterator->_max_length)
1478c2ecf20Sopenharmony_ci				return -EINVAL;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		iterator->_arg += sizeof(uint32_t);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		/*
1538c2ecf20Sopenharmony_ci		 * no need to check again for blowing past stated radiotap
1548c2ecf20Sopenharmony_ci		 * header length, because ieee80211_radiotap_iterator_next
1558c2ecf20Sopenharmony_ci		 * checks it before it is dereferenced
1568c2ecf20Sopenharmony_ci		 */
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	iterator->this_arg = iterator->_arg;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* we are all initialized happily */
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void find_ns(struct ieee80211_radiotap_iterator *iterator,
1688c2ecf20Sopenharmony_ci		    uint32_t oui, uint8_t subns)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	int i;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	iterator->current_namespace = NULL;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (!iterator->_vns)
1758c2ecf20Sopenharmony_ci		return;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	for (i = 0; i < iterator->_vns->n_ns; i++) {
1788c2ecf20Sopenharmony_ci		if (iterator->_vns->ns[i].oui != oui)
1798c2ecf20Sopenharmony_ci			continue;
1808c2ecf20Sopenharmony_ci		if (iterator->_vns->ns[i].subns != subns)
1818c2ecf20Sopenharmony_ci			continue;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		iterator->current_namespace = &iterator->_vns->ns[i];
1848c2ecf20Sopenharmony_ci		break;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/**
1918c2ecf20Sopenharmony_ci * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
1928c2ecf20Sopenharmony_ci * @iterator: radiotap_iterator to move to next arg (if any)
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci * Returns: 0 if there is an argument to handle,
1958c2ecf20Sopenharmony_ci * -ENOENT if there are no more args or -EINVAL
1968c2ecf20Sopenharmony_ci * if there is something else wrong.
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
1998c2ecf20Sopenharmony_ci * in @this_arg_index and sets @this_arg to point to the
2008c2ecf20Sopenharmony_ci * payload for the field.  It takes care of alignment handling and extended
2018c2ecf20Sopenharmony_ci * present fields.  @this_arg can be changed by the caller (eg,
2028c2ecf20Sopenharmony_ci * incremented to move inside a compound argument like
2038c2ecf20Sopenharmony_ci * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
2048c2ecf20Sopenharmony_ci * little-endian format whatever the endianess of your CPU.
2058c2ecf20Sopenharmony_ci *
2068c2ecf20Sopenharmony_ci * Alignment Gotcha:
2078c2ecf20Sopenharmony_ci * You must take care when dereferencing iterator.this_arg
2088c2ecf20Sopenharmony_ci * for multibyte types... the pointer is not aligned.  Use
2098c2ecf20Sopenharmony_ci * get_unaligned((type *)iterator.this_arg) to dereference
2108c2ecf20Sopenharmony_ci * iterator.this_arg for type "type" safely on all arches.
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ciint ieee80211_radiotap_iterator_next(
2148c2ecf20Sopenharmony_ci	struct ieee80211_radiotap_iterator *iterator)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	while (1) {
2178c2ecf20Sopenharmony_ci		int hit = 0;
2188c2ecf20Sopenharmony_ci		int pad, align, size, subns;
2198c2ecf20Sopenharmony_ci		uint32_t oui;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		/* if no more EXT bits, that's it */
2228c2ecf20Sopenharmony_ci		if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
2238c2ecf20Sopenharmony_ci		    !(iterator->_bitmap_shifter & 1))
2248c2ecf20Sopenharmony_ci			return -ENOENT;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (!(iterator->_bitmap_shifter & 1))
2278c2ecf20Sopenharmony_ci			goto next_entry; /* arg not present */
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		/* get alignment/size of data */
2308c2ecf20Sopenharmony_ci		switch (iterator->_arg_index % 32) {
2318c2ecf20Sopenharmony_ci		case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
2328c2ecf20Sopenharmony_ci		case IEEE80211_RADIOTAP_EXT:
2338c2ecf20Sopenharmony_ci			align = 1;
2348c2ecf20Sopenharmony_ci			size = 0;
2358c2ecf20Sopenharmony_ci			break;
2368c2ecf20Sopenharmony_ci		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
2378c2ecf20Sopenharmony_ci			align = 2;
2388c2ecf20Sopenharmony_ci			size = 6;
2398c2ecf20Sopenharmony_ci			break;
2408c2ecf20Sopenharmony_ci		default:
2418c2ecf20Sopenharmony_ci			if (!iterator->current_namespace ||
2428c2ecf20Sopenharmony_ci			    iterator->_arg_index >= iterator->current_namespace->n_bits) {
2438c2ecf20Sopenharmony_ci				if (iterator->current_namespace == &radiotap_ns)
2448c2ecf20Sopenharmony_ci					return -ENOENT;
2458c2ecf20Sopenharmony_ci				align = 0;
2468c2ecf20Sopenharmony_ci			} else {
2478c2ecf20Sopenharmony_ci				align = iterator->current_namespace->align_size[iterator->_arg_index].align;
2488c2ecf20Sopenharmony_ci				size = iterator->current_namespace->align_size[iterator->_arg_index].size;
2498c2ecf20Sopenharmony_ci			}
2508c2ecf20Sopenharmony_ci			if (!align) {
2518c2ecf20Sopenharmony_ci				/* skip all subsequent data */
2528c2ecf20Sopenharmony_ci				iterator->_arg = iterator->_next_ns_data;
2538c2ecf20Sopenharmony_ci				/* give up on this namespace */
2548c2ecf20Sopenharmony_ci				iterator->current_namespace = NULL;
2558c2ecf20Sopenharmony_ci				goto next_entry;
2568c2ecf20Sopenharmony_ci			}
2578c2ecf20Sopenharmony_ci			break;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		/*
2618c2ecf20Sopenharmony_ci		 * arg is present, account for alignment padding
2628c2ecf20Sopenharmony_ci		 *
2638c2ecf20Sopenharmony_ci		 * Note that these alignments are relative to the start
2648c2ecf20Sopenharmony_ci		 * of the radiotap header.  There is no guarantee
2658c2ecf20Sopenharmony_ci		 * that the radiotap header itself is aligned on any
2668c2ecf20Sopenharmony_ci		 * kind of boundary.
2678c2ecf20Sopenharmony_ci		 *
2688c2ecf20Sopenharmony_ci		 * The above is why get_unaligned() is used to dereference
2698c2ecf20Sopenharmony_ci		 * multibyte elements from the radiotap area.
2708c2ecf20Sopenharmony_ci		 */
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		pad = ((unsigned long)iterator->_arg -
2738c2ecf20Sopenharmony_ci		       (unsigned long)iterator->_rtheader) & (align - 1);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		if (pad)
2768c2ecf20Sopenharmony_ci			iterator->_arg += align - pad;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
2798c2ecf20Sopenharmony_ci			int vnslen;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci			if ((unsigned long)iterator->_arg + size -
2828c2ecf20Sopenharmony_ci			    (unsigned long)iterator->_rtheader >
2838c2ecf20Sopenharmony_ci			    (unsigned long)iterator->_max_length)
2848c2ecf20Sopenharmony_ci				return -EINVAL;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci			oui = (*iterator->_arg << 16) |
2878c2ecf20Sopenharmony_ci				(*(iterator->_arg + 1) << 8) |
2888c2ecf20Sopenharmony_ci				*(iterator->_arg + 2);
2898c2ecf20Sopenharmony_ci			subns = *(iterator->_arg + 3);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci			find_ns(iterator, oui, subns);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci			vnslen = get_unaligned_le16(iterator->_arg + 4);
2948c2ecf20Sopenharmony_ci			iterator->_next_ns_data = iterator->_arg + size + vnslen;
2958c2ecf20Sopenharmony_ci			if (!iterator->current_namespace)
2968c2ecf20Sopenharmony_ci				size += vnslen;
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		/*
3008c2ecf20Sopenharmony_ci		 * this is what we will return to user, but we need to
3018c2ecf20Sopenharmony_ci		 * move on first so next call has something fresh to test
3028c2ecf20Sopenharmony_ci		 */
3038c2ecf20Sopenharmony_ci		iterator->this_arg_index = iterator->_arg_index;
3048c2ecf20Sopenharmony_ci		iterator->this_arg = iterator->_arg;
3058c2ecf20Sopenharmony_ci		iterator->this_arg_size = size;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		/* internally move on the size of this arg */
3088c2ecf20Sopenharmony_ci		iterator->_arg += size;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		/*
3118c2ecf20Sopenharmony_ci		 * check for insanity where we are given a bitmap that
3128c2ecf20Sopenharmony_ci		 * claims to have more arg content than the length of the
3138c2ecf20Sopenharmony_ci		 * radiotap section.  We will normally end up equalling this
3148c2ecf20Sopenharmony_ci		 * max_length on the last arg, never exceeding it.
3158c2ecf20Sopenharmony_ci		 */
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		if ((unsigned long)iterator->_arg -
3188c2ecf20Sopenharmony_ci		    (unsigned long)iterator->_rtheader >
3198c2ecf20Sopenharmony_ci		    (unsigned long)iterator->_max_length)
3208c2ecf20Sopenharmony_ci			return -EINVAL;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		/* these special ones are valid in each bitmap word */
3238c2ecf20Sopenharmony_ci		switch (iterator->_arg_index % 32) {
3248c2ecf20Sopenharmony_ci		case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
3258c2ecf20Sopenharmony_ci			iterator->_reset_on_ext = 1;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci			iterator->is_radiotap_ns = 0;
3288c2ecf20Sopenharmony_ci			/*
3298c2ecf20Sopenharmony_ci			 * If parser didn't register this vendor
3308c2ecf20Sopenharmony_ci			 * namespace with us, allow it to show it
3318c2ecf20Sopenharmony_ci			 * as 'raw. Do do that, set argument index
3328c2ecf20Sopenharmony_ci			 * to vendor namespace.
3338c2ecf20Sopenharmony_ci			 */
3348c2ecf20Sopenharmony_ci			iterator->this_arg_index =
3358c2ecf20Sopenharmony_ci				IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
3368c2ecf20Sopenharmony_ci			if (!iterator->current_namespace)
3378c2ecf20Sopenharmony_ci				hit = 1;
3388c2ecf20Sopenharmony_ci			goto next_entry;
3398c2ecf20Sopenharmony_ci		case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
3408c2ecf20Sopenharmony_ci			iterator->_reset_on_ext = 1;
3418c2ecf20Sopenharmony_ci			iterator->current_namespace = &radiotap_ns;
3428c2ecf20Sopenharmony_ci			iterator->is_radiotap_ns = 1;
3438c2ecf20Sopenharmony_ci			goto next_entry;
3448c2ecf20Sopenharmony_ci		case IEEE80211_RADIOTAP_EXT:
3458c2ecf20Sopenharmony_ci			/*
3468c2ecf20Sopenharmony_ci			 * bit 31 was set, there is more
3478c2ecf20Sopenharmony_ci			 * -- move to next u32 bitmap
3488c2ecf20Sopenharmony_ci			 */
3498c2ecf20Sopenharmony_ci			iterator->_bitmap_shifter =
3508c2ecf20Sopenharmony_ci				get_unaligned_le32(iterator->_next_bitmap);
3518c2ecf20Sopenharmony_ci			iterator->_next_bitmap++;
3528c2ecf20Sopenharmony_ci			if (iterator->_reset_on_ext)
3538c2ecf20Sopenharmony_ci				iterator->_arg_index = 0;
3548c2ecf20Sopenharmony_ci			else
3558c2ecf20Sopenharmony_ci				iterator->_arg_index++;
3568c2ecf20Sopenharmony_ci			iterator->_reset_on_ext = 0;
3578c2ecf20Sopenharmony_ci			break;
3588c2ecf20Sopenharmony_ci		default:
3598c2ecf20Sopenharmony_ci			/* we've got a hit! */
3608c2ecf20Sopenharmony_ci			hit = 1;
3618c2ecf20Sopenharmony_ci next_entry:
3628c2ecf20Sopenharmony_ci			iterator->_bitmap_shifter >>= 1;
3638c2ecf20Sopenharmony_ci			iterator->_arg_index++;
3648c2ecf20Sopenharmony_ci		}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci		/* if we found a valid arg earlier, return it now */
3678c2ecf20Sopenharmony_ci		if (hit)
3688c2ecf20Sopenharmony_ci			return 0;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_radiotap_iterator_next);
372