New upstream version 0~20200622
Sophie Brun
3 years ago
0 | cmake_minimum_required(VERSION 2.6) | |
1 | project(radiotap) | |
2 | ||
3 | add_definitions("-D_BSD_SOURCE -DRADIOTAP_SUPPORT_OVERRIDES") | |
4 | ||
5 | add_library(radiotap SHARED radiotap.c) | |
6 | set_target_properties(radiotap PROPERTIES | |
7 | COMPILE_FLAGS "-Wall -Wextra") | |
8 | ||
9 | install(TARGETS radiotap DESTINATION lib) | |
10 | install(FILES radiotap.h radiotap_iter.h DESTINATION include) | |
11 | ||
12 | add_executable(parse parse.c) | |
13 | set_target_properties(parse PROPERTIES | |
14 | COMPILE_FLAGS "-Wall -Wextra") | |
15 | target_link_libraries(parse radiotap) | |
16 | ||
17 | ||
18 | add_custom_target(radiotap_check ALL | |
19 | COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/check/check.sh ${CMAKE_CURRENT_BINARY_DIR} | |
20 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/check/* | |
21 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/check/ | |
22 | COMMENT "Check examples") | |
23 | add_dependencies(radiotap_check parse) |
0 | Copyright (c) 2007-2009 Andy Green <[email protected]> | |
1 | Copyright (c) 2007-2009 Johannes Berg <[email protected]> | |
2 | ||
3 | Permission to use, copy, modify, and/or distribute this software for any | |
4 | purpose with or without fee is hereby granted, provided that the above | |
5 | copyright notice and this permission notice appear in all copies. | |
6 | ||
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
Binary diff not shown
0 | TSFT: 9833440827789222417 |
Binary diff not shown
0 | --fcshdr |
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | TSFT: 9833440827789222417 | |
1 | vendor NS (00-00-00:1, 4 bytes) | |
2 | ff ee dd cc | |
3 | TSFT: 1225260500033256362 |
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | flags: 33 |
0 | #!/bin/sh | |
1 | ||
2 | bin="$1/parse" | |
3 | ||
4 | for t in *.bin ; do | |
5 | echo -n "Checking $t: " | |
6 | args="" | |
7 | base="$(basename "$t" .bin)" | |
8 | if [ -f "$base.args" ] ; then | |
9 | args="$(cat "$base.args")" | |
10 | fi | |
11 | "$bin" $args $t | diff "$base.out" - && echo "OK" || echo "FAIL" | |
12 | done |
Binary diff not shown
Binary diff not shown
0 | flags: 10 | |
1 | rate: 1.000000 | |
2 | RX flags: 0000 | |
3 | vendor NS (ff-ff-ff:255, 2 bytes) | |
4 | de ad | |
5 | rate: 2.000000 |
0 | #include <sys/types.h> | |
1 | #include <sys/stat.h> | |
2 | #include <sys/mman.h> | |
3 | #include <fcntl.h> | |
4 | #include <unistd.h> | |
5 | #include <stdio.h> | |
6 | #include <errno.h> | |
7 | #include <string.h> | |
8 | #if defined(__APPLE__) | |
9 | #include <machine/endian.h> | |
10 | #else | |
11 | #include <endian.h> | |
12 | #endif | |
13 | ||
14 | #include "radiotap_iter.h" | |
15 | ||
16 | static int fcshdr = 0; | |
17 | ||
18 | static const struct radiotap_align_size align_size_000000_00[] = { | |
19 | [0] = { .align = 1, .size = 4, }, | |
20 | [52] = { .align = 1, .size = 4, }, | |
21 | }; | |
22 | ||
23 | static const struct ieee80211_radiotap_namespace vns_array[] = { | |
24 | { | |
25 | .oui = 0x000000, | |
26 | .subns = 0, | |
27 | .n_bits = sizeof(align_size_000000_00), | |
28 | .align_size = align_size_000000_00, | |
29 | }, | |
30 | }; | |
31 | ||
32 | static const struct ieee80211_radiotap_vendor_namespaces vns = { | |
33 | .ns = vns_array, | |
34 | .n_ns = sizeof(vns_array)/sizeof(vns_array[0]), | |
35 | }; | |
36 | ||
37 | static void print_radiotap_namespace(struct ieee80211_radiotap_iterator *iter) | |
38 | { | |
39 | switch (iter->this_arg_index) { | |
40 | case IEEE80211_RADIOTAP_TSFT: | |
41 | printf("\tTSFT: %llu\n", le64toh(*(unsigned long long *)iter->this_arg)); | |
42 | break; | |
43 | case IEEE80211_RADIOTAP_FLAGS: | |
44 | printf("\tflags: %02x\n", *iter->this_arg); | |
45 | break; | |
46 | case IEEE80211_RADIOTAP_RATE: | |
47 | printf("\trate: %lf\n", (double)*iter->this_arg/2); | |
48 | break; | |
49 | case IEEE80211_RADIOTAP_CHANNEL: | |
50 | case IEEE80211_RADIOTAP_FHSS: | |
51 | case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: | |
52 | case IEEE80211_RADIOTAP_DBM_ANTNOISE: | |
53 | case IEEE80211_RADIOTAP_LOCK_QUALITY: | |
54 | case IEEE80211_RADIOTAP_TX_ATTENUATION: | |
55 | case IEEE80211_RADIOTAP_DB_TX_ATTENUATION: | |
56 | case IEEE80211_RADIOTAP_DBM_TX_POWER: | |
57 | case IEEE80211_RADIOTAP_ANTENNA: | |
58 | case IEEE80211_RADIOTAP_DB_ANTSIGNAL: | |
59 | case IEEE80211_RADIOTAP_DB_ANTNOISE: | |
60 | case IEEE80211_RADIOTAP_TX_FLAGS: | |
61 | break; | |
62 | case IEEE80211_RADIOTAP_RX_FLAGS: | |
63 | if (fcshdr) { | |
64 | printf("\tFCS in header: %.8x\n", | |
65 | le32toh(*(uint32_t *)iter->this_arg)); | |
66 | break; | |
67 | } | |
68 | printf("\tRX flags: %#.4x\n", | |
69 | le16toh(*(uint16_t *)iter->this_arg)); | |
70 | break; | |
71 | case IEEE80211_RADIOTAP_RTS_RETRIES: | |
72 | case IEEE80211_RADIOTAP_DATA_RETRIES: | |
73 | break; | |
74 | default: | |
75 | printf("\tBOGUS DATA\n"); | |
76 | break; | |
77 | } | |
78 | } | |
79 | ||
80 | static void print_test_namespace(struct ieee80211_radiotap_iterator *iter) | |
81 | { | |
82 | switch (iter->this_arg_index) { | |
83 | case 0: | |
84 | case 52: | |
85 | printf("\t00:00:00-00|%d: %.2x/%.2x/%.2x/%.2x\n", | |
86 | iter->this_arg_index, | |
87 | *iter->this_arg, *(iter->this_arg + 1), | |
88 | *(iter->this_arg + 2), *(iter->this_arg + 3)); | |
89 | break; | |
90 | default: | |
91 | printf("\tBOGUS DATA - vendor ns %d\n", iter->this_arg_index); | |
92 | break; | |
93 | } | |
94 | } | |
95 | ||
96 | static const struct radiotap_override overrides[] = { | |
97 | { .field = 14, .align = 4, .size = 4, } | |
98 | }; | |
99 | ||
100 | int main(int argc, char *argv[]) | |
101 | { | |
102 | struct ieee80211_radiotap_iterator iter; | |
103 | struct stat statbuf; | |
104 | int fd, err, fnidx = 1, i; | |
105 | void *data; | |
106 | ||
107 | if (argc != 2 && argc != 3) { | |
108 | fprintf(stderr, "usage: parse [--fcshdr] <file>\n"); | |
109 | fprintf(stderr, " --fcshdr: read bit 14 as FCS\n"); | |
110 | return 2; | |
111 | } | |
112 | ||
113 | if (strcmp(argv[1], "--fcshdr") == 0) { | |
114 | fcshdr = 1; | |
115 | fnidx++; | |
116 | } | |
117 | ||
118 | fd = open(argv[fnidx], O_RDONLY); | |
119 | if (fd < 0) { | |
120 | fprintf(stderr, "cannot open file %s\n", argv[fnidx]); | |
121 | return 2; | |
122 | } | |
123 | ||
124 | if (fstat(fd, &statbuf)) { | |
125 | perror("fstat"); | |
126 | return 2; | |
127 | } | |
128 | ||
129 | data = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); | |
130 | ||
131 | err = ieee80211_radiotap_iterator_init(&iter, data, statbuf.st_size, &vns); | |
132 | if (err) { | |
133 | printf("malformed radiotap header (init returns %d)\n", err); | |
134 | return 3; | |
135 | } | |
136 | ||
137 | if (fcshdr) { | |
138 | iter.overrides = overrides; | |
139 | iter.n_overrides = sizeof(overrides)/sizeof(overrides[0]); | |
140 | } | |
141 | ||
142 | while (!(err = ieee80211_radiotap_iterator_next(&iter))) { | |
143 | if (iter.this_arg_index == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { | |
144 | printf("\tvendor NS (%.2x-%.2x-%.2x:%d, %d bytes)\n", | |
145 | iter.this_arg[0], iter.this_arg[1], | |
146 | iter.this_arg[2], iter.this_arg[3], | |
147 | iter.this_arg_size - 6); | |
148 | for (i = 6; i < iter.this_arg_size; i++) { | |
149 | if (i % 8 == 6) | |
150 | printf("\t\t"); | |
151 | else | |
152 | printf(" "); | |
153 | printf("%.2x", iter.this_arg[i]); | |
154 | } | |
155 | printf("\n"); | |
156 | } else if (iter.is_radiotap_ns) | |
157 | print_radiotap_namespace(&iter); | |
158 | else if (iter.current_namespace == &vns_array[0]) | |
159 | print_test_namespace(&iter); | |
160 | } | |
161 | ||
162 | if (err != -ENOENT) { | |
163 | printf("malformed radiotap data\n"); | |
164 | return 3; | |
165 | } | |
166 | ||
167 | return 0; | |
168 | } |
0 | #include <stddef.h> | |
1 | #include <errno.h> | |
2 | #if defined(__APPLE__) | |
3 | #include <machine/endian.h> | |
4 | #else | |
5 | #include <endian.h> | |
6 | #endif | |
7 | ||
8 | #define le16_to_cpu le16toh | |
9 | #define le32_to_cpu le32toh | |
10 | #define get_unaligned(p) \ | |
11 | ({ \ | |
12 | struct packed_dummy_struct { \ | |
13 | typeof(*(p)) __val; \ | |
14 | } __attribute__((packed)) *__ptr = (void *) (p); \ | |
15 | \ | |
16 | __ptr->__val; \ | |
17 | }) | |
18 | #define get_unaligned_le16(p) le16_to_cpu(get_unaligned((uint16_t *)(p))) | |
19 | #define get_unaligned_le32(p) le32_to_cpu(get_unaligned((uint32_t *)(p))) |
0 | /* | |
1 | * Radiotap parser | |
2 | * | |
3 | * Copyright 2007 Andy Green <[email protected]> | |
4 | * Copyright 2009 Johannes Berg <[email protected]> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * Alternatively, this software may be distributed under the terms of ISC | |
11 | * license, see COPYING for more details. | |
12 | */ | |
13 | #include "radiotap_iter.h" | |
14 | #include "platform.h" | |
15 | ||
16 | /* function prototypes and related defs are in radiotap_iter.h */ | |
17 | ||
18 | static const struct radiotap_align_size rtap_namespace_sizes[] = { | |
19 | [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, | |
20 | [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, | |
21 | [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, | |
22 | [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, | |
23 | [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, | |
24 | [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, | |
25 | [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, | |
26 | [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, | |
27 | [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, | |
28 | [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, | |
29 | [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, | |
30 | [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, | |
31 | [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, | |
32 | [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, | |
33 | [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, | |
34 | [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, | |
35 | [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, | |
36 | [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, | |
37 | [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, }, | |
38 | [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, }, | |
39 | [IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, }, | |
40 | [IEEE80211_RADIOTAP_TIMESTAMP] = { .align = 8, .size = 12, }, | |
41 | /* | |
42 | * add more here as they are defined in radiotap.h | |
43 | */ | |
44 | }; | |
45 | ||
46 | static const struct ieee80211_radiotap_namespace radiotap_ns = { | |
47 | .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]), | |
48 | .align_size = rtap_namespace_sizes, | |
49 | }; | |
50 | ||
51 | /** | |
52 | * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization | |
53 | * @iterator: radiotap_iterator to initialize | |
54 | * @radiotap_header: radiotap header to parse | |
55 | * @max_length: total length we can parse into (eg, whole packet length) | |
56 | * | |
57 | * Returns: 0 or a negative error code if there is a problem. | |
58 | * | |
59 | * This function initializes an opaque iterator struct which can then | |
60 | * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap | |
61 | * argument which is present in the header. It knows about extended | |
62 | * present headers and handles them. | |
63 | * | |
64 | * How to use: | |
65 | * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator | |
66 | * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) | |
67 | * checking for a good 0 return code. Then loop calling | |
68 | * __ieee80211_radiotap_iterator_next()... it returns either 0, | |
69 | * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. | |
70 | * The iterator's @this_arg member points to the start of the argument | |
71 | * associated with the current argument index that is present, which can be | |
72 | * found in the iterator's @this_arg_index member. This arg index corresponds | |
73 | * to the IEEE80211_RADIOTAP_... defines. | |
74 | * | |
75 | * Radiotap header length: | |
76 | * You can find the CPU-endian total radiotap header length in | |
77 | * iterator->max_length after executing ieee80211_radiotap_iterator_init() | |
78 | * successfully. | |
79 | * | |
80 | * Alignment Gotcha: | |
81 | * You must take care when dereferencing iterator.this_arg | |
82 | * for multibyte types... the pointer is not aligned. Use | |
83 | * get_unaligned((type *)iterator.this_arg) to dereference | |
84 | * iterator.this_arg for type "type" safely on all arches. | |
85 | * | |
86 | * Example code: parse.c | |
87 | */ | |
88 | ||
89 | int ieee80211_radiotap_iterator_init( | |
90 | struct ieee80211_radiotap_iterator *iterator, | |
91 | struct ieee80211_radiotap_header *radiotap_header, | |
92 | int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) | |
93 | { | |
94 | /* must at least have the radiotap header */ | |
95 | if (max_length < (int)sizeof(struct ieee80211_radiotap_header)) | |
96 | return -EINVAL; | |
97 | ||
98 | /* Linux only supports version 0 radiotap format */ | |
99 | if (radiotap_header->it_version) | |
100 | return -EINVAL; | |
101 | ||
102 | /* sanity check for allowed length and radiotap length field */ | |
103 | if (max_length < get_unaligned_le16(&radiotap_header->it_len)) | |
104 | return -EINVAL; | |
105 | ||
106 | iterator->_rtheader = radiotap_header; | |
107 | iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); | |
108 | iterator->_arg_index = 0; | |
109 | iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); | |
110 | iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); | |
111 | iterator->_reset_on_ext = 0; | |
112 | iterator->_next_bitmap = &radiotap_header->it_present; | |
113 | iterator->_next_bitmap++; | |
114 | iterator->_vns = vns; | |
115 | iterator->current_namespace = &radiotap_ns; | |
116 | iterator->is_radiotap_ns = 1; | |
117 | #ifdef RADIOTAP_SUPPORT_OVERRIDES | |
118 | iterator->n_overrides = 0; | |
119 | iterator->overrides = NULL; | |
120 | #endif | |
121 | ||
122 | /* find payload start allowing for extended bitmap(s) */ | |
123 | ||
124 | if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { | |
125 | if ((unsigned long)iterator->_arg - | |
126 | (unsigned long)iterator->_rtheader + sizeof(uint32_t) > | |
127 | (unsigned long)iterator->_max_length) | |
128 | return -EINVAL; | |
129 | while (get_unaligned_le32(iterator->_arg) & | |
130 | (1 << IEEE80211_RADIOTAP_EXT)) { | |
131 | iterator->_arg += sizeof(uint32_t); | |
132 | ||
133 | /* | |
134 | * check for insanity where the present bitmaps | |
135 | * keep claiming to extend up to or even beyond the | |
136 | * stated radiotap header length | |
137 | */ | |
138 | ||
139 | if ((unsigned long)iterator->_arg - | |
140 | (unsigned long)iterator->_rtheader + | |
141 | sizeof(uint32_t) > | |
142 | (unsigned long)iterator->_max_length) | |
143 | return -EINVAL; | |
144 | } | |
145 | ||
146 | iterator->_arg += sizeof(uint32_t); | |
147 | ||
148 | /* | |
149 | * no need to check again for blowing past stated radiotap | |
150 | * header length, because ieee80211_radiotap_iterator_next | |
151 | * checks it before it is dereferenced | |
152 | */ | |
153 | } | |
154 | ||
155 | iterator->this_arg = iterator->_arg; | |
156 | ||
157 | /* we are all initialized happily */ | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
162 | static void find_ns(struct ieee80211_radiotap_iterator *iterator, | |
163 | uint32_t oui, uint8_t subns) | |
164 | { | |
165 | int i; | |
166 | ||
167 | iterator->current_namespace = NULL; | |
168 | ||
169 | if (!iterator->_vns) | |
170 | return; | |
171 | ||
172 | for (i = 0; i < iterator->_vns->n_ns; i++) { | |
173 | if (iterator->_vns->ns[i].oui != oui) | |
174 | continue; | |
175 | if (iterator->_vns->ns[i].subns != subns) | |
176 | continue; | |
177 | ||
178 | iterator->current_namespace = &iterator->_vns->ns[i]; | |
179 | break; | |
180 | } | |
181 | } | |
182 | ||
183 | #ifdef RADIOTAP_SUPPORT_OVERRIDES | |
184 | static int find_override(struct ieee80211_radiotap_iterator *iterator, | |
185 | int *align, int *size) | |
186 | { | |
187 | int i; | |
188 | ||
189 | if (!iterator->overrides) | |
190 | return 0; | |
191 | ||
192 | for (i = 0; i < iterator->n_overrides; i++) { | |
193 | if (iterator->_arg_index == iterator->overrides[i].field) { | |
194 | *align = iterator->overrides[i].align; | |
195 | *size = iterator->overrides[i].size; | |
196 | if (!*align) /* erroneous override */ | |
197 | return 0; | |
198 | return 1; | |
199 | } | |
200 | } | |
201 | ||
202 | return 0; | |
203 | } | |
204 | #endif | |
205 | ||
206 | ||
207 | /** | |
208 | * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg | |
209 | * @iterator: radiotap_iterator to move to next arg (if any) | |
210 | * | |
211 | * Returns: 0 if there is an argument to handle, | |
212 | * -ENOENT if there are no more args or -EINVAL | |
213 | * if there is something else wrong. | |
214 | * | |
215 | * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) | |
216 | * in @this_arg_index and sets @this_arg to point to the | |
217 | * payload for the field. It takes care of alignment handling and extended | |
218 | * present fields. @this_arg can be changed by the caller (eg, | |
219 | * incremented to move inside a compound argument like | |
220 | * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in | |
221 | * little-endian format whatever the endianness of your CPU. | |
222 | * | |
223 | * Alignment Gotcha: | |
224 | * You must take care when dereferencing iterator.this_arg | |
225 | * for multibyte types... the pointer is not aligned. Use | |
226 | * get_unaligned((type *)iterator.this_arg) to dereference | |
227 | * iterator.this_arg for type "type" safely on all arches. | |
228 | */ | |
229 | ||
230 | int ieee80211_radiotap_iterator_next( | |
231 | struct ieee80211_radiotap_iterator *iterator) | |
232 | { | |
233 | while (1) { | |
234 | int hit = 0; | |
235 | int pad, align, size, subns; | |
236 | uint32_t oui; | |
237 | ||
238 | /* if no more EXT bits, that's it */ | |
239 | if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && | |
240 | !(iterator->_bitmap_shifter & 1)) | |
241 | return -ENOENT; | |
242 | ||
243 | if (!(iterator->_bitmap_shifter & 1)) | |
244 | goto next_entry; /* arg not present */ | |
245 | ||
246 | /* get alignment/size of data */ | |
247 | switch (iterator->_arg_index % 32) { | |
248 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | |
249 | case IEEE80211_RADIOTAP_EXT: | |
250 | align = 1; | |
251 | size = 0; | |
252 | break; | |
253 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: | |
254 | align = 2; | |
255 | size = 6; | |
256 | break; | |
257 | default: | |
258 | #ifdef RADIOTAP_SUPPORT_OVERRIDES | |
259 | if (find_override(iterator, &align, &size)) { | |
260 | /* all set */ | |
261 | } else | |
262 | #endif | |
263 | if (!iterator->current_namespace || | |
264 | iterator->_arg_index >= iterator->current_namespace->n_bits) { | |
265 | if (iterator->current_namespace == &radiotap_ns) | |
266 | return -ENOENT; | |
267 | align = 0; | |
268 | } else { | |
269 | align = iterator->current_namespace->align_size[iterator->_arg_index].align; | |
270 | size = iterator->current_namespace->align_size[iterator->_arg_index].size; | |
271 | } | |
272 | if (!align) { | |
273 | /* skip all subsequent data */ | |
274 | iterator->_arg = iterator->_next_ns_data; | |
275 | /* give up on this namespace */ | |
276 | iterator->current_namespace = NULL; | |
277 | goto next_entry; | |
278 | } | |
279 | break; | |
280 | } | |
281 | ||
282 | /* | |
283 | * arg is present, account for alignment padding | |
284 | * | |
285 | * Note that these alignments are relative to the start | |
286 | * of the radiotap header. There is no guarantee | |
287 | * that the radiotap header itself is aligned on any | |
288 | * kind of boundary. | |
289 | * | |
290 | * The above is why get_unaligned() is used to dereference | |
291 | * multibyte elements from the radiotap area. | |
292 | */ | |
293 | ||
294 | pad = ((unsigned long)iterator->_arg - | |
295 | (unsigned long)iterator->_rtheader) & (align - 1); | |
296 | ||
297 | if (pad) | |
298 | iterator->_arg += align - pad; | |
299 | ||
300 | if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { | |
301 | int vnslen; | |
302 | ||
303 | if ((unsigned long)iterator->_arg + size - | |
304 | (unsigned long)iterator->_rtheader > | |
305 | (unsigned long)iterator->_max_length) | |
306 | return -EINVAL; | |
307 | ||
308 | oui = (*iterator->_arg << 16) | | |
309 | (*(iterator->_arg + 1) << 8) | | |
310 | *(iterator->_arg + 2); | |
311 | subns = *(iterator->_arg + 3); | |
312 | ||
313 | find_ns(iterator, oui, subns); | |
314 | ||
315 | vnslen = get_unaligned_le16(iterator->_arg + 4); | |
316 | iterator->_next_ns_data = iterator->_arg + size + vnslen; | |
317 | if (!iterator->current_namespace) | |
318 | size += vnslen; | |
319 | } | |
320 | ||
321 | /* | |
322 | * this is what we will return to user, but we need to | |
323 | * move on first so next call has something fresh to test | |
324 | */ | |
325 | iterator->this_arg_index = iterator->_arg_index; | |
326 | iterator->this_arg = iterator->_arg; | |
327 | iterator->this_arg_size = size; | |
328 | ||
329 | /* internally move on the size of this arg */ | |
330 | iterator->_arg += size; | |
331 | ||
332 | /* | |
333 | * check for insanity where we are given a bitmap that | |
334 | * claims to have more arg content than the length of the | |
335 | * radiotap section. We will normally end up equalling this | |
336 | * max_length on the last arg, never exceeding it. | |
337 | */ | |
338 | ||
339 | if ((unsigned long)iterator->_arg - | |
340 | (unsigned long)iterator->_rtheader > | |
341 | (unsigned long)iterator->_max_length) | |
342 | return -EINVAL; | |
343 | ||
344 | /* these special ones are valid in each bitmap word */ | |
345 | switch (iterator->_arg_index % 32) { | |
346 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: | |
347 | iterator->_reset_on_ext = 1; | |
348 | ||
349 | iterator->is_radiotap_ns = 0; | |
350 | /* | |
351 | * If parser didn't register this vendor | |
352 | * namespace with us, allow it to show it | |
353 | * as 'raw. Do do that, set argument index | |
354 | * to vendor namespace. | |
355 | */ | |
356 | iterator->this_arg_index = | |
357 | IEEE80211_RADIOTAP_VENDOR_NAMESPACE; | |
358 | if (!iterator->current_namespace) | |
359 | hit = 1; | |
360 | goto next_entry; | |
361 | case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: | |
362 | iterator->_reset_on_ext = 1; | |
363 | iterator->current_namespace = &radiotap_ns; | |
364 | iterator->is_radiotap_ns = 1; | |
365 | goto next_entry; | |
366 | case IEEE80211_RADIOTAP_EXT: | |
367 | /* | |
368 | * bit 31 was set, there is more | |
369 | * -- move to next u32 bitmap | |
370 | */ | |
371 | iterator->_bitmap_shifter = | |
372 | get_unaligned_le32(iterator->_next_bitmap); | |
373 | iterator->_next_bitmap++; | |
374 | if (iterator->_reset_on_ext) | |
375 | iterator->_arg_index = 0; | |
376 | else | |
377 | iterator->_arg_index++; | |
378 | iterator->_reset_on_ext = 0; | |
379 | break; | |
380 | default: | |
381 | /* we've got a hit! */ | |
382 | hit = 1; | |
383 | next_entry: | |
384 | iterator->_bitmap_shifter >>= 1; | |
385 | iterator->_arg_index++; | |
386 | } | |
387 | ||
388 | /* if we found a valid arg earlier, return it now */ | |
389 | if (hit) | |
390 | return 0; | |
391 | } | |
392 | } |
0 | /* | |
1 | * Copyright (c) 2017 Intel Deutschland GmbH | |
2 | * | |
3 | * Permission to use, copy, modify, and/or distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | #ifndef __RADIOTAP_H | |
16 | #define __RADIOTAP_H | |
17 | ||
18 | #if defined(__APPLE__) | |
19 | #include <libkern/OSByteOrder.h> | |
20 | #define bswap_16 OSSwapInt16 | |
21 | #define bswap_32 OSSwapInt32 | |
22 | #define bswap_64 OSSwapInt64 | |
23 | #include <machine/endian.h> | |
24 | #define le16toh(x) OSSwapLittleToHostInt16(x) | |
25 | #define le32toh(x) OSSwapLittleToHostInt32(x) | |
26 | #define le64toh(x) OSSwapLittleToHostInt64(x) | |
27 | #endif | |
28 | ||
29 | /** | |
30 | * struct ieee82011_radiotap_header - base radiotap header | |
31 | */ | |
32 | struct ieee80211_radiotap_header { | |
33 | /** | |
34 | * @it_version: radiotap version, always 0 | |
35 | */ | |
36 | uint8_t it_version; | |
37 | ||
38 | /** | |
39 | * @it_pad: padding (or alignment) | |
40 | */ | |
41 | uint8_t it_pad; | |
42 | ||
43 | /** | |
44 | * @it_len: overall radiotap header length | |
45 | */ | |
46 | uint16_t it_len; | |
47 | ||
48 | /** | |
49 | * @it_present: (first) present word | |
50 | */ | |
51 | uint32_t it_present; | |
52 | } __attribute__((__packed__)); | |
53 | ||
54 | /* version is always 0 */ | |
55 | #define PKTHDR_RADIOTAP_VERSION 0 | |
56 | ||
57 | /* see the radiotap website for the descriptions */ | |
58 | enum ieee80211_radiotap_presence { | |
59 | IEEE80211_RADIOTAP_TSFT = 0, | |
60 | IEEE80211_RADIOTAP_FLAGS = 1, | |
61 | IEEE80211_RADIOTAP_RATE = 2, | |
62 | IEEE80211_RADIOTAP_CHANNEL = 3, | |
63 | IEEE80211_RADIOTAP_FHSS = 4, | |
64 | IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, | |
65 | IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, | |
66 | IEEE80211_RADIOTAP_LOCK_QUALITY = 7, | |
67 | IEEE80211_RADIOTAP_TX_ATTENUATION = 8, | |
68 | IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, | |
69 | IEEE80211_RADIOTAP_DBM_TX_POWER = 10, | |
70 | IEEE80211_RADIOTAP_ANTENNA = 11, | |
71 | IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, | |
72 | IEEE80211_RADIOTAP_DB_ANTNOISE = 13, | |
73 | IEEE80211_RADIOTAP_RX_FLAGS = 14, | |
74 | IEEE80211_RADIOTAP_TX_FLAGS = 15, | |
75 | IEEE80211_RADIOTAP_RTS_RETRIES = 16, | |
76 | IEEE80211_RADIOTAP_DATA_RETRIES = 17, | |
77 | /* 18 is XChannel, but it's not defined yet */ | |
78 | IEEE80211_RADIOTAP_MCS = 19, | |
79 | IEEE80211_RADIOTAP_AMPDU_STATUS = 20, | |
80 | IEEE80211_RADIOTAP_VHT = 21, | |
81 | IEEE80211_RADIOTAP_TIMESTAMP = 22, | |
82 | ||
83 | /* valid in every it_present bitmap, even vendor namespaces */ | |
84 | IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, | |
85 | IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, | |
86 | IEEE80211_RADIOTAP_EXT = 31 | |
87 | }; | |
88 | ||
89 | /* for IEEE80211_RADIOTAP_FLAGS */ | |
90 | enum ieee80211_radiotap_flags { | |
91 | IEEE80211_RADIOTAP_F_CFP = 0x01, | |
92 | IEEE80211_RADIOTAP_F_SHORTPRE = 0x02, | |
93 | IEEE80211_RADIOTAP_F_WEP = 0x04, | |
94 | IEEE80211_RADIOTAP_F_FRAG = 0x08, | |
95 | IEEE80211_RADIOTAP_F_FCS = 0x10, | |
96 | IEEE80211_RADIOTAP_F_DATAPAD = 0x20, | |
97 | IEEE80211_RADIOTAP_F_BADFCS = 0x40, | |
98 | }; | |
99 | ||
100 | /* for IEEE80211_RADIOTAP_CHANNEL */ | |
101 | enum ieee80211_radiotap_channel_flags { | |
102 | IEEE80211_CHAN_CCK = 0x0020, | |
103 | IEEE80211_CHAN_OFDM = 0x0040, | |
104 | IEEE80211_CHAN_2GHZ = 0x0080, | |
105 | IEEE80211_CHAN_5GHZ = 0x0100, | |
106 | IEEE80211_CHAN_DYN = 0x0400, | |
107 | IEEE80211_CHAN_HALF = 0x4000, | |
108 | IEEE80211_CHAN_QUARTER = 0x8000, | |
109 | }; | |
110 | ||
111 | /* for IEEE80211_RADIOTAP_RX_FLAGS */ | |
112 | enum ieee80211_radiotap_rx_flags { | |
113 | IEEE80211_RADIOTAP_F_RX_BADPLCP = 0x0002, | |
114 | }; | |
115 | ||
116 | /* for IEEE80211_RADIOTAP_TX_FLAGS */ | |
117 | enum ieee80211_radiotap_tx_flags { | |
118 | IEEE80211_RADIOTAP_F_TX_FAIL = 0x0001, | |
119 | IEEE80211_RADIOTAP_F_TX_CTS = 0x0002, | |
120 | IEEE80211_RADIOTAP_F_TX_RTS = 0x0004, | |
121 | IEEE80211_RADIOTAP_F_TX_NOACK = 0x0008, | |
122 | }; | |
123 | ||
124 | /* for IEEE80211_RADIOTAP_MCS "have" flags */ | |
125 | enum ieee80211_radiotap_mcs_have { | |
126 | IEEE80211_RADIOTAP_MCS_HAVE_BW = 0x01, | |
127 | IEEE80211_RADIOTAP_MCS_HAVE_MCS = 0x02, | |
128 | IEEE80211_RADIOTAP_MCS_HAVE_GI = 0x04, | |
129 | IEEE80211_RADIOTAP_MCS_HAVE_FMT = 0x08, | |
130 | IEEE80211_RADIOTAP_MCS_HAVE_FEC = 0x10, | |
131 | IEEE80211_RADIOTAP_MCS_HAVE_STBC = 0x20, | |
132 | }; | |
133 | ||
134 | enum ieee80211_radiotap_mcs_flags { | |
135 | IEEE80211_RADIOTAP_MCS_BW_MASK = 0x03, | |
136 | IEEE80211_RADIOTAP_MCS_BW_20 = 0, | |
137 | IEEE80211_RADIOTAP_MCS_BW_40 = 1, | |
138 | IEEE80211_RADIOTAP_MCS_BW_20L = 2, | |
139 | IEEE80211_RADIOTAP_MCS_BW_20U = 3, | |
140 | ||
141 | IEEE80211_RADIOTAP_MCS_SGI = 0x04, | |
142 | IEEE80211_RADIOTAP_MCS_FMT_GF = 0x08, | |
143 | IEEE80211_RADIOTAP_MCS_FEC_LDPC = 0x10, | |
144 | IEEE80211_RADIOTAP_MCS_STBC_MASK = 0x60, | |
145 | IEEE80211_RADIOTAP_MCS_STBC_1 = 1, | |
146 | IEEE80211_RADIOTAP_MCS_STBC_2 = 2, | |
147 | IEEE80211_RADIOTAP_MCS_STBC_3 = 3, | |
148 | IEEE80211_RADIOTAP_MCS_STBC_SHIFT = 5, | |
149 | }; | |
150 | ||
151 | /* for IEEE80211_RADIOTAP_AMPDU_STATUS */ | |
152 | enum ieee80211_radiotap_ampdu_flags { | |
153 | IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN = 0x0001, | |
154 | IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN = 0x0002, | |
155 | IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN = 0x0004, | |
156 | IEEE80211_RADIOTAP_AMPDU_IS_LAST = 0x0008, | |
157 | IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR = 0x0010, | |
158 | IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN = 0x0020, | |
159 | }; | |
160 | ||
161 | /* for IEEE80211_RADIOTAP_VHT */ | |
162 | enum ieee80211_radiotap_vht_known { | |
163 | IEEE80211_RADIOTAP_VHT_KNOWN_STBC = 0x0001, | |
164 | IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA = 0x0002, | |
165 | IEEE80211_RADIOTAP_VHT_KNOWN_GI = 0x0004, | |
166 | IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS = 0x0008, | |
167 | IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM = 0x0010, | |
168 | IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED = 0x0020, | |
169 | IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH = 0x0040, | |
170 | IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID = 0x0080, | |
171 | IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID = 0x0100, | |
172 | }; | |
173 | ||
174 | enum ieee80211_radiotap_vht_flags { | |
175 | IEEE80211_RADIOTAP_VHT_FLAG_STBC = 0x01, | |
176 | IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA = 0x02, | |
177 | IEEE80211_RADIOTAP_VHT_FLAG_SGI = 0x04, | |
178 | IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 = 0x08, | |
179 | IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM = 0x10, | |
180 | IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED = 0x20, | |
181 | }; | |
182 | ||
183 | enum ieee80211_radiotap_vht_coding { | |
184 | IEEE80211_RADIOTAP_CODING_LDPC_USER0 = 0x01, | |
185 | IEEE80211_RADIOTAP_CODING_LDPC_USER1 = 0x02, | |
186 | IEEE80211_RADIOTAP_CODING_LDPC_USER2 = 0x04, | |
187 | IEEE80211_RADIOTAP_CODING_LDPC_USER3 = 0x08, | |
188 | }; | |
189 | ||
190 | /* for IEEE80211_RADIOTAP_TIMESTAMP */ | |
191 | enum ieee80211_radiotap_timestamp_unit_spos { | |
192 | IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MASK = 0x000F, | |
193 | IEEE80211_RADIOTAP_TIMESTAMP_UNIT_MS = 0x0000, | |
194 | IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US = 0x0001, | |
195 | IEEE80211_RADIOTAP_TIMESTAMP_UNIT_NS = 0x0003, | |
196 | IEEE80211_RADIOTAP_TIMESTAMP_SPOS_MASK = 0x00F0, | |
197 | IEEE80211_RADIOTAP_TIMESTAMP_SPOS_BEGIN_MDPU = 0x0000, | |
198 | IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ = 0x0010, | |
199 | IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_PPDU = 0x0020, | |
200 | IEEE80211_RADIOTAP_TIMESTAMP_SPOS_EO_MPDU = 0x0030, | |
201 | IEEE80211_RADIOTAP_TIMESTAMP_SPOS_UNKNOWN = 0x00F0, | |
202 | }; | |
203 | ||
204 | enum ieee80211_radiotap_timestamp_flags { | |
205 | IEEE80211_RADIOTAP_TIMESTAMP_FLAG_64BIT = 0x00, | |
206 | IEEE80211_RADIOTAP_TIMESTAMP_FLAG_32BIT = 0x01, | |
207 | IEEE80211_RADIOTAP_TIMESTAMP_FLAG_ACCURACY = 0x02, | |
208 | }; | |
209 | ||
210 | #endif /* __RADIOTAP_H */ |
0 | #ifndef __RADIOTAP_ITER_H | |
1 | #define __RADIOTAP_ITER_H | |
2 | ||
3 | #include <stdint.h> | |
4 | #include "radiotap.h" | |
5 | ||
6 | /* Radiotap header iteration | |
7 | * implemented in radiotap.c | |
8 | */ | |
9 | ||
10 | struct radiotap_override { | |
11 | uint8_t field; | |
12 | uint8_t align:4, size:4; | |
13 | }; | |
14 | ||
15 | struct radiotap_align_size { | |
16 | uint8_t align:4, size:4; | |
17 | }; | |
18 | ||
19 | struct ieee80211_radiotap_namespace { | |
20 | const struct radiotap_align_size *align_size; | |
21 | int n_bits; | |
22 | uint32_t oui; | |
23 | uint8_t subns; | |
24 | }; | |
25 | ||
26 | struct ieee80211_radiotap_vendor_namespaces { | |
27 | const struct ieee80211_radiotap_namespace *ns; | |
28 | int n_ns; | |
29 | }; | |
30 | ||
31 | /** | |
32 | * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args | |
33 | * @this_arg_index: index of current arg, valid after each successful call | |
34 | * to ieee80211_radiotap_iterator_next() | |
35 | * @this_arg: pointer to current radiotap arg; it is valid after each | |
36 | * call to ieee80211_radiotap_iterator_next() but also after | |
37 | * ieee80211_radiotap_iterator_init() where it will point to | |
38 | * the beginning of the actual data portion | |
39 | * @this_arg_size: length of the current arg, for convenience | |
40 | * @current_namespace: pointer to the current namespace definition | |
41 | * (or internally %NULL if the current namespace is unknown) | |
42 | * @is_radiotap_ns: indicates whether the current namespace is the default | |
43 | * radiotap namespace or not | |
44 | * | |
45 | * @overrides: override standard radiotap fields | |
46 | * @n_overrides: number of overrides | |
47 | * | |
48 | * @_rtheader: pointer to the radiotap header we are walking through | |
49 | * @_max_length: length of radiotap header in cpu byte ordering | |
50 | * @_arg_index: next argument index | |
51 | * @_arg: next argument pointer | |
52 | * @_next_bitmap: internal pointer to next present u32 | |
53 | * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present | |
54 | * @_vns: vendor namespace definitions | |
55 | * @_next_ns_data: beginning of the next namespace's data | |
56 | * @_reset_on_ext: internal; reset the arg index to 0 when going to the | |
57 | * next bitmap word | |
58 | * | |
59 | * Describes the radiotap parser state. Fields prefixed with an underscore | |
60 | * must not be used by users of the parser, only by the parser internally. | |
61 | */ | |
62 | ||
63 | struct ieee80211_radiotap_iterator { | |
64 | struct ieee80211_radiotap_header *_rtheader; | |
65 | const struct ieee80211_radiotap_vendor_namespaces *_vns; | |
66 | const struct ieee80211_radiotap_namespace *current_namespace; | |
67 | ||
68 | unsigned char *_arg, *_next_ns_data; | |
69 | uint32_t *_next_bitmap; | |
70 | ||
71 | unsigned char *this_arg; | |
72 | const struct radiotap_override *overrides; /* Only for RADIOTAP_SUPPORT_OVERRIDES */ | |
73 | int n_overrides; /* Only for RADIOTAP_SUPPORT_OVERRIDES */ | |
74 | int this_arg_index; | |
75 | int this_arg_size; | |
76 | ||
77 | int is_radiotap_ns; | |
78 | ||
79 | int _max_length; | |
80 | int _arg_index; | |
81 | uint32_t _bitmap_shifter; | |
82 | int _reset_on_ext; | |
83 | }; | |
84 | ||
85 | #ifdef __cplusplus | |
86 | #define CALLING_CONVENTION "C" | |
87 | #else | |
88 | #define CALLING_CONVENTION | |
89 | #endif | |
90 | ||
91 | extern CALLING_CONVENTION int ieee80211_radiotap_iterator_init( | |
92 | struct ieee80211_radiotap_iterator *iterator, | |
93 | struct ieee80211_radiotap_header *radiotap_header, | |
94 | int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns); | |
95 | ||
96 | extern CALLING_CONVENTION int ieee80211_radiotap_iterator_next( | |
97 | struct ieee80211_radiotap_iterator *iterator); | |
98 | ||
99 | #endif /* __RADIOTAP_ITER_H */ |