diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /net/wireless/radiotap.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'net/wireless/radiotap.c')
-rw-r--r-- | net/wireless/radiotap.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/net/wireless/radiotap.c b/net/wireless/radiotap.c new file mode 100644 index 000000000..36f1b59a7 --- /dev/null +++ b/net/wireless/radiotap.c | |||
@@ -0,0 +1,371 @@ | |||
1 | /* | ||
2 | * Radiotap parser | ||
3 | * | ||
4 | * Copyright 2007 Andy Green <andy@warmcat.com> | ||
5 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * Alternatively, this software may be distributed under the terms of BSD | ||
12 | * license. | ||
13 | * | ||
14 | * See COPYING for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/export.h> | ||
19 | #include <net/cfg80211.h> | ||
20 | #include <net/ieee80211_radiotap.h> | ||
21 | #include <asm/unaligned.h> | ||
22 | |||
23 | /* function prototypes and related defs are in include/net/cfg80211.h */ | ||
24 | |||
25 | static const struct radiotap_align_size rtap_namespace_sizes[] = { | ||
26 | [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, | ||
27 | [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, | ||
28 | [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, | ||
29 | [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, | ||
30 | [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, | ||
31 | [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, | ||
32 | [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, | ||
33 | [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, | ||
34 | [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, | ||
35 | [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, | ||
36 | [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, | ||
37 | [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, | ||
38 | [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, | ||
39 | [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, | ||
40 | [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, | ||
41 | [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, | ||
42 | [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, | ||
43 | [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, | ||
44 | [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, | ||
45 | [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, | ||
46 | [IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, }, | ||
47 | /* | ||
48 | * add more here as they are defined in radiotap.h | ||
49 | */ | ||
50 | }; | ||
51 | |||
52 | static const struct ieee80211_radiotap_namespace radiotap_ns = { | ||
53 | .n_bits = ARRAY_SIZE(rtap_namespace_sizes), | ||
54 | .align_size = rtap_namespace_sizes, | ||
55 | }; | ||
56 | |||
57 | /** | ||
58 | * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization | ||
59 | * @iterator: radiotap_iterator to initialize | ||
60 | * @radiotap_header: radiotap header to parse | ||
61 | * @max_length: total length we can parse into (eg, whole packet length) | ||
62 | * @vns: vendor namespaces to parse | ||
63 | * | ||
64 | * Returns: 0 or a negative error code if there is a problem. | ||
65 | * | ||
66 | * This function initializes an opaque iterator struct which can then | ||
67 | * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap | ||
68 | * argument which is present in the header. It knows about extended | ||
69 | * present headers and handles them. | ||
70 | * | ||
71 | * How to use: | ||
72 | * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator | ||
73 | * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) | ||
74 | * checking for a good 0 return code. Then loop calling | ||
75 | * __ieee80211_radiotap_iterator_next()... it returns either 0, | ||
76 | * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. | ||
77 | * The iterator's @this_arg member points to the start of the argument | ||
78 | * associated with the current argument index that is present, which can be | ||
79 | * found in the iterator's @this_arg_index member. This arg index corresponds | ||
80 | * to the IEEE80211_RADIOTAP_... defines. | ||
81 | * | ||
82 | * Radiotap header length: | ||
83 | * You can find the CPU-endian total radiotap header length in | ||
84 | * iterator->max_length after executing ieee80211_radiotap_iterator_init() | ||
85 | * successfully. | ||
86 | * | ||
87 | * Alignment Gotcha: | ||
88 | * You must take care when dereferencing iterator.this_arg | ||
89 | * for multibyte types... the pointer is not aligned. Use | ||
90 | * get_unaligned((type *)iterator.this_arg) to dereference | ||
91 | * iterator.this_arg for type "type" safely on all arches. | ||
92 | * | ||
93 | * Example code: | ||
94 | * See Documentation/networking/radiotap-headers.rst | ||
95 | */ | ||
96 | |||
97 | int ieee80211_radiotap_iterator_init( | ||
98 | struct ieee80211_radiotap_iterator *iterator, | ||
99 | struct ieee80211_radiotap_header *radiotap_header, | ||
100 | int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) | ||
101 | { | ||
102 | /* check the radiotap header can actually be present */ | ||
103 | if (max_length < sizeof(struct ieee80211_radiotap_header)) | ||
104 | return -EINVAL; | ||
105 | |||
106 | /* Linux only supports version 0 radiotap format */ | ||
107 | if (radiotap_header->it_version) | ||
108 | return -EINVAL; | ||
109 | |||
110 | /* sanity check for allowed length and radiotap length field */ | ||
111 | if (max_length < get_unaligned_le16(&radiotap_header->it_len)) | ||
112 | return -EINVAL; | ||
113 | |||
114 | iterator->_rtheader = radiotap_header; | ||
115 | iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); | ||
116 | iterator->_arg_index = 0; | ||
117 | iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); | ||
118 | iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); | ||
119 | iterator->_reset_on_ext = 0; | ||
120 | iterator->_next_bitmap = &radiotap_header->it_present; | ||
121 | iterator->_next_bitmap++; | ||
122 | iterator->_vns = vns; | ||
123 | iterator->current_namespace = &radiotap_ns; | ||
124 | iterator->is_radiotap_ns = 1; | ||
125 | |||
126 | /* find payload start allowing for extended bitmap(s) */ | ||
127 | |||
128 | if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { | ||
129 | if ((unsigned long)iterator->_arg - | ||
130 | (unsigned long)iterator->_rtheader + sizeof(uint32_t) > | ||
131 | (unsigned long)iterator->_max_length) | ||
132 | return -EINVAL; | ||
133 | while (get_unaligned_le32(iterator->_arg) & | ||
134 | (1 << IEEE80211_RADIOTAP_EXT)) { | ||
135 | iterator->_arg += sizeof(uint32_t); | ||
136 | |||
137 | /* | ||
138 | * check for insanity where the present bitmaps | ||
139 | * keep claiming to extend up to or even beyond the | ||
140 | * stated radiotap header length | ||
141 | */ | ||
142 | |||
143 | if ((unsigned long)iterator->_arg - | ||
144 | (unsigned long)iterator->_rtheader + | ||
145 | sizeof(uint32_t) > | ||
146 | (unsigned long)iterator->_max_length) | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | iterator->_arg += sizeof(uint32_t); | ||
151 | |||
152 | /* | ||
153 | * no need to check again for blowing past stated radiotap | ||
154 | * header length, because ieee80211_radiotap_iterator_next | ||
155 | * checks it before it is dereferenced | ||
156 | */ | ||
157 | } | ||
158 | |||
159 | iterator->this_arg = iterator->_arg; | ||
160 | |||
161 | /* we are all initialized happily */ | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); | ||
166 | |||
167 | static void find_ns(struct ieee80211_radiotap_iterator *iterator, | ||
168 | uint32_t oui, uint8_t subns) | ||
169 | { | ||
170 | int i; | ||
171 | |||
172 | iterator->current_namespace = NULL; | ||
173 | |||
174 | if (!iterator->_vns) | ||
175 | return; | ||
176 | |||
177 | for (i = 0; i < iterator->_vns->n_ns; i++) { | ||
178 | if (iterator->_vns->ns[i].oui != oui) | ||
179 | continue; | ||
180 | if (iterator->_vns->ns[i].subns != subns) | ||
181 | continue; | ||
182 | |||
183 | iterator->current_namespace = &iterator->_vns->ns[i]; | ||
184 | break; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | |||
189 | |||
190 | /** | ||
191 | * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg | ||
192 | * @iterator: radiotap_iterator to move to next arg (if any) | ||
193 | * | ||
194 | * Returns: 0 if there is an argument to handle, | ||
195 | * -ENOENT if there are no more args or -EINVAL | ||
196 | * if there is something else wrong. | ||
197 | * | ||
198 | * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) | ||
199 | * in @this_arg_index and sets @this_arg to point to the | ||
200 | * payload for the field. It takes care of alignment handling and extended | ||
201 | * present fields. @this_arg can be changed by the caller (eg, | ||
202 | * incremented to move inside a compound argument like | ||
203 | * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in | ||
204 | * little-endian format whatever the endianess of your CPU. | ||
205 | * | ||
206 | * Alignment Gotcha: | ||
207 | * You must take care when dereferencing iterator.this_arg | ||
208 | * for multibyte types... the pointer is not aligned. Use | ||
209 | * get_unaligned((type *)iterator.this_arg) to dereference | ||
210 | * iterator.this_arg for type "type" safely on all arches. | ||
211 | */ | ||
212 | |||
213 | int ieee80211_radiotap_iterator_next( | ||
214 | struct ieee80211_radiotap_iterator *iterator) | ||
215 | { | ||
216 | while (1) { | ||
217 | int hit = 0; | ||
218 | int pad, align, size, subns; | ||
219 | uint32_t oui; | ||
220 | |||
221 | /* if no more EXT bits, that's it */ | ||
222 | if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && | ||
223 | !(iterator->_bitmap_shifter & 1)) | ||
224 | return -ENOENT; | ||
225 | |||
226 | if (!(iterator->_bitmap_shifter & 1)) | ||
227 | goto next_entry; /* arg not present */ | ||
228 | |||
229 | /* get alignment/size of data */ | ||
230 | switch (iterator->_arg_index % 32) { | ||
231 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | ||
232 | case IEEE80211_RADIOTAP_EXT: | ||
233 | align = 1; | ||
234 | size = 0; | ||
235 | break; | ||
236 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: | ||
237 | align = 2; | ||
238 | size = 6; | ||
239 | break; | ||
240 | default: | ||
241 | if (!iterator->current_namespace || | ||
242 | iterator->_arg_index >= iterator->current_namespace->n_bits) { | ||
243 | if (iterator->current_namespace == &radiotap_ns) | ||
244 | return -ENOENT; | ||
245 | align = 0; | ||
246 | } else { | ||
247 | align = iterator->current_namespace->align_size[iterator->_arg_index].align; | ||
248 | size = iterator->current_namespace->align_size[iterator->_arg_index].size; | ||
249 | } | ||
250 | if (!align) { | ||
251 | /* skip all subsequent data */ | ||
252 | iterator->_arg = iterator->_next_ns_data; | ||
253 | /* give up on this namespace */ | ||
254 | iterator->current_namespace = NULL; | ||
255 | goto next_entry; | ||
256 | } | ||
257 | break; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * arg is present, account for alignment padding | ||
262 | * | ||
263 | * Note that these alignments are relative to the start | ||
264 | * of the radiotap header. There is no guarantee | ||
265 | * that the radiotap header itself is aligned on any | ||
266 | * kind of boundary. | ||
267 | * | ||
268 | * The above is why get_unaligned() is used to dereference | ||
269 | * multibyte elements from the radiotap area. | ||
270 | */ | ||
271 | |||
272 | pad = ((unsigned long)iterator->_arg - | ||
273 | (unsigned long)iterator->_rtheader) & (align - 1); | ||
274 | |||
275 | if (pad) | ||
276 | iterator->_arg += align - pad; | ||
277 | |||
278 | if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { | ||
279 | int vnslen; | ||
280 | |||
281 | if ((unsigned long)iterator->_arg + size - | ||
282 | (unsigned long)iterator->_rtheader > | ||
283 | (unsigned long)iterator->_max_length) | ||
284 | return -EINVAL; | ||
285 | |||
286 | oui = (*iterator->_arg << 16) | | ||
287 | (*(iterator->_arg + 1) << 8) | | ||
288 | *(iterator->_arg + 2); | ||
289 | subns = *(iterator->_arg + 3); | ||
290 | |||
291 | find_ns(iterator, oui, subns); | ||
292 | |||
293 | vnslen = get_unaligned_le16(iterator->_arg + 4); | ||
294 | iterator->_next_ns_data = iterator->_arg + size + vnslen; | ||
295 | if (!iterator->current_namespace) | ||
296 | size += vnslen; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * this is what we will return to user, but we need to | ||
301 | * move on first so next call has something fresh to test | ||
302 | */ | ||
303 | iterator->this_arg_index = iterator->_arg_index; | ||
304 | iterator->this_arg = iterator->_arg; | ||
305 | iterator->this_arg_size = size; | ||
306 | |||
307 | /* internally move on the size of this arg */ | ||
308 | iterator->_arg += size; | ||
309 | |||
310 | /* | ||
311 | * check for insanity where we are given a bitmap that | ||
312 | * claims to have more arg content than the length of the | ||
313 | * radiotap section. We will normally end up equalling this | ||
314 | * max_length on the last arg, never exceeding it. | ||
315 | */ | ||
316 | |||
317 | if ((unsigned long)iterator->_arg - | ||
318 | (unsigned long)iterator->_rtheader > | ||
319 | (unsigned long)iterator->_max_length) | ||
320 | return -EINVAL; | ||
321 | |||
322 | /* these special ones are valid in each bitmap word */ | ||
323 | switch (iterator->_arg_index % 32) { | ||
324 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: | ||
325 | iterator->_reset_on_ext = 1; | ||
326 | |||
327 | iterator->is_radiotap_ns = 0; | ||
328 | /* | ||
329 | * If parser didn't register this vendor | ||
330 | * namespace with us, allow it to show it | ||
331 | * as 'raw. Do do that, set argument index | ||
332 | * to vendor namespace. | ||
333 | */ | ||
334 | iterator->this_arg_index = | ||
335 | IEEE80211_RADIOTAP_VENDOR_NAMESPACE; | ||
336 | if (!iterator->current_namespace) | ||
337 | hit = 1; | ||
338 | goto next_entry; | ||
339 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | ||
340 | iterator->_reset_on_ext = 1; | ||
341 | iterator->current_namespace = &radiotap_ns; | ||
342 | iterator->is_radiotap_ns = 1; | ||
343 | goto next_entry; | ||
344 | case IEEE80211_RADIOTAP_EXT: | ||
345 | /* | ||
346 | * bit 31 was set, there is more | ||
347 | * -- move to next u32 bitmap | ||
348 | */ | ||
349 | iterator->_bitmap_shifter = | ||
350 | get_unaligned_le32(iterator->_next_bitmap); | ||
351 | iterator->_next_bitmap++; | ||
352 | if (iterator->_reset_on_ext) | ||
353 | iterator->_arg_index = 0; | ||
354 | else | ||
355 | iterator->_arg_index++; | ||
356 | iterator->_reset_on_ext = 0; | ||
357 | break; | ||
358 | default: | ||
359 | /* we've got a hit! */ | ||
360 | hit = 1; | ||
361 | next_entry: | ||
362 | iterator->_bitmap_shifter >>= 1; | ||
363 | iterator->_arg_index++; | ||
364 | } | ||
365 | |||
366 | /* if we found a valid arg earlier, return it now */ | ||
367 | if (hit) | ||
368 | return 0; | ||
369 | } | ||
370 | } | ||
371 | EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); | ||