Coverage Report

Created: 2025-08-04 07:15

/src/wireshark/epan/dissectors/packet-ieee80211-radiotap-iter.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Radiotap parser
3
 *
4
 * Copyright 2007   Andy Green <andy@warmcat.com>
5
 * Copyright 2009   Johannes Berg <johannes@sipsolutions.net>
6
 *
7
 * SPDX-License-Identifier: (ISC OR GPL-2.0-only)
8
 */
9
10
#include "config.h"
11
12
#include <errno.h>
13
14
#include <epan/packet.h>
15
16
#include <wsutil/pint.h>
17
#include <wsutil/array.h>
18
19
#define le16_to_cpu   GINT16_FROM_LE
20
#define le32_to_cpu   GINT32_FROM_LE
21
1.55k
#define get_unaligned_le16  pletoh16
22
5.18k
#define get_unaligned_le32  pletoh32
23
24
#include "packet-ieee80211-radiotap-iter.h"
25
26
/* function prototypes and related defs are in radiotap_iter.h */
27
28
static const struct radiotap_align_size rtap_namespace_sizes[] = {
29
  /* [IEEE80211_RADIOTAP_TSFT] = 0 */     { 8, 8 },
30
  /* [IEEE80211_RADIOTAP_FLAGS] = 1 */      { 1, 1 },
31
  /* [IEEE80211_RADIOTAP_RATE] = 2 */     { 1, 1 },
32
  /* [IEEE80211_RADIOTAP_CHANNEL] = 3 */      { 2, 4 },
33
  /* [IEEE80211_RADIOTAP_FHSS] = 4 */     { 2, 2 },
34
  /* [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 5 */    { 1, 1 },
35
  /* [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 6 */   { 1, 1 },
36
  /* [IEEE80211_RADIOTAP_LOCK_QUALITY] = 7 */   { 2, 2 },
37
  /* [IEEE80211_RADIOTAP_TX_ATTENUATION] = 8 */   { 2, 2 },
38
  /* [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 9 */  { 2, 2 },
39
  /* [IEEE80211_RADIOTAP_DBM_TX_POWER] = 10 */    { 1, 1 },
40
  /* [IEEE80211_RADIOTAP_ANTENNA] = 11 */     { 1, 1 },
41
  /* [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 12 */    { 1, 1 },
42
  /* [IEEE80211_RADIOTAP_DB_ANTNOISE] = 13 */   { 1, 1 },
43
  /* [IEEE80211_RADIOTAP_RX_FLAGS] = 14 */    { 2, 2 },
44
  /* [IEEE80211_RADIOTAP_TX_FLAGS] = 15 */    { 2, 2 },
45
  /* [IEEE80211_RADIOTAP_RTS_RETRIES] = 16 */   { 1, 1 },
46
  /* [IEEE80211_RADIOTAP_DATA_RETRIES] = 17 */    { 1, 1 },
47
  /* [IEEE80211_RADIOTAP_XCHANNEL] = 18 */    { 0, 0 }, /* Unofficial, used by FreeBSD */
48
  /* [IEEE80211_RADIOTAP_MCS] = 19 */     { 1, 3 },
49
  /* [IEEE80211_RADIOTAP_AMPDU_STATUS] = 20 */    { 4, 8 },
50
  /* [IEEE80211_RADIOTAP_VHT] = 21 */     { 2, 12 },
51
  /* [IEEE80211_RADIOTAP_TIMESTAMP] = 22 */   { 8, 12 },
52
  /* [IEEE80211_RADIOTAP_HE] = 23 */      { 2, 12 },
53
  /* [IEEE80211_RADIOTAP_HE_MU] = 24 */     { 2, 12 },
54
  /* [IEEE80211_RADIOTAP_HE_MU_USER = 25 notdef */  { 0, 0 },
55
  /* [IEEE80211_RADIOTAP_0_LENGTH_PSDU = 26 */    { 1, 1 },
56
  /* [IEEE80211_RADIOTAP_L_SIG = 27 */      { 2, 4 },
57
  /* [IEEE80211_RADIOTAP_TLV = 28 */                      { 4, 10 },
58
  /*
59
   * add more here as they are defined in
60
   * include/net/ieee80211_radiotap.h
61
   */
62
};
63
64
static const struct ieee80211_radiotap_namespace radiotap_ns = {
65
  rtap_namespace_sizes,
66
  (int)array_length(rtap_namespace_sizes),
67
  0,
68
  0
69
};
70
71
/*
72
 * Sanity check.
73
 */
74
#define ITERATOR_VALID(iterator, size) \
75
24.5k
    (((iterator)->_arg + (size) - (unsigned char *)((iterator)->_rtheader)) <= \
76
24.5k
        (ptrdiff_t)(iterator)->_max_length)
77
78
/**
79
 * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
80
 * @iterator: radiotap_iterator to initialize
81
 * @radiotap_header: radiotap header to parse
82
 * @max_length: total length we can parse into (eg, whole packet length)
83
 *
84
 * Returns: 0 or a negative error code if there is a problem.
85
 *
86
 * This function initializes an opaque iterator struct which can then
87
 * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
88
 * argument which is present in the header.  It knows about extended
89
 * present headers and handles them.
90
 *
91
 * How to use:
92
 * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
93
 * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
94
 * checking for a good 0 return code.  Then loop calling
95
 * __ieee80211_radiotap_iterator_next()... it returns either 0,
96
 * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
97
 * The iterator's @this_arg member points to the start of the argument
98
 * associated with the current argument index that is present, which can be
99
 * found in the iterator's @this_arg_index member.  This arg index corresponds
100
 * to the IEEE80211_RADIOTAP_... defines.
101
 *
102
 * Radiotap header length:
103
 * You can find the CPU-endian total radiotap header length in
104
 * iterator->max_length after executing ieee80211_radiotap_iterator_init()
105
 * successfully.
106
 *
107
 * Alignment Gotcha:
108
 * You must take care when dereferencing iterator.this_arg
109
 * for multibyte types... the pointer is not aligned.  Use
110
 * get_unaligned((type *)iterator.this_arg) to dereference
111
 * iterator.this_arg for type "type" safely on all arches.
112
 *
113
 * Example code:
114
 * See https://www.kernel.org/doc/Documentation/networking/radiotap-headers.txt
115
 */
116
117
int ieee80211_radiotap_iterator_init(
118
  struct ieee80211_radiotap_iterator *iterator,
119
  struct ieee80211_radiotap_header *radiotap_header,
120
  int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
121
449
{
122
  /* XXX - in Wireshark, we've already checked for this */
123
449
  if (max_length < (int)sizeof(struct ieee80211_radiotap_header))
124
0
    return -EINVAL;
125
126
  /* Linux only supports version 0 radiotap format */
127
  /* XXX - this is Wireshark, not Linux, and we should report an expert info */
128
449
  if (radiotap_header->it_version)
129
1
    return -EINVAL;
130
131
  /* sanity check for allowed length and radiotap length field */
132
  /* XXX - in Wireshark, this compares the length against itself. */
133
448
  if (max_length < get_unaligned_le16(&radiotap_header->it_len))
134
0
    return -EINVAL;
135
136
448
  iterator->_rtheader = radiotap_header;
137
448
  iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
138
448
  iterator->_arg_index = 0;
139
448
  iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
140
448
  iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
141
448
  iterator->_reset_on_ext = 0;
142
448
  iterator->_next_ns_data = NULL;
143
448
  iterator->_next_bitmap = &radiotap_header->it_present;
144
448
  iterator->_next_bitmap++;
145
448
  iterator->_vns = vns;
146
448
  iterator->current_namespace = &radiotap_ns;
147
448
  iterator->is_radiotap_ns = 1;
148
448
  iterator->tlv_mode = 0;
149
448
#ifdef RADIOTAP_SUPPORT_OVERRIDES
150
448
  iterator->n_overrides = 0;
151
448
  iterator->overrides = NULL;
152
448
#endif
153
154
  /* find payload start allowing for extended bitmap(s) */
155
448
  if (iterator->_bitmap_shifter & (1U << IEEE80211_RADIOTAP_EXT)) {
156
    /* XXX - we should report an expert info here */
157
403
    if (!ITERATOR_VALID(iterator, sizeof(uint32_t)))
158
1
      return -EINVAL;
159
1.89k
    while (get_unaligned_le32(iterator->_arg) &
160
1.89k
          (1U << IEEE80211_RADIOTAP_EXT)) {
161
1.49k
      iterator->_arg += sizeof(uint32_t);
162
163
      /*
164
       * check for insanity where the present bitmaps
165
       * keep claiming to extend up to or even beyond the
166
       * stated radiotap header length
167
       */
168
      /* XXX - we should report an expert info here */
169
1.49k
      if (!ITERATOR_VALID(iterator, sizeof(uint32_t)))
170
1
        return -EINVAL;
171
172
      /* XXX - we should report an expert info here */
173
1.49k
      if ((get_unaligned_le32(iterator->_arg) &
174
1.49k
          (1U << IEEE80211_RADIOTAP_TLVS)) &&
175
1.49k
          (get_unaligned_le32(iterator->_arg) &
176
87
          (1U << IEEE80211_RADIOTAP_EXT)))
177
5
        return -EINVAL;
178
1.49k
    }
179
180
396
    iterator->_arg += sizeof(uint32_t);
181
182
    /*
183
     * no need to check again for blowing past stated radiotap
184
     * header length, because ieee80211_radiotap_iterator_next
185
     * checks it before it is dereferenced
186
     */
187
396
  }
188
189
441
  iterator->this_arg = iterator->_arg;
190
191
  /* we are all initialized happily */
192
193
441
  return 0;
194
448
}
195
196
static void find_ns(struct ieee80211_radiotap_iterator *iterator,
197
        uint32_t oui, uint8_t subns)
198
45
{
199
45
  int i;
200
201
45
  iterator->current_namespace = NULL;
202
203
45
  if (!iterator->_vns)
204
45
    return;
205
206
0
  for (i = 0; i < iterator->_vns->n_ns; i++) {
207
0
    if (iterator->_vns->ns[i].oui != oui)
208
0
      continue;
209
0
    if (iterator->_vns->ns[i].subns != subns)
210
0
      continue;
211
212
0
    iterator->current_namespace = &iterator->_vns->ns[i];
213
0
    break;
214
0
  }
215
0
}
216
217
#ifdef RADIOTAP_SUPPORT_OVERRIDES
218
static int find_override(struct ieee80211_radiotap_iterator *iterator,
219
       int *align, int *size)
220
18.9k
{
221
18.9k
  int i;
222
223
18.9k
  if (!iterator->overrides)
224
0
    return 0;
225
226
37.3k
  for (i = 0; i < iterator->n_overrides; i++) {
227
18.9k
    if (iterator->_arg_index == iterator->overrides[i].field) {
228
512
      *align = iterator->overrides[i].align;
229
512
      *size = iterator->overrides[i].size;
230
512
      if (!*align) /* erroneous override */
231
0
        return 0;
232
512
      return 1;
233
512
    }
234
18.9k
  }
235
236
18.4k
  return 0;
237
18.9k
}
238
#endif
239
240
241
/**
242
 * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
243
 * @iterator: radiotap_iterator to move to next arg (if any)
244
 *
245
 * Returns: 0 if there is an argument to handle,
246
 * -ENOENT if there are no more args or -EINVAL
247
 * if there is something else wrong.
248
 *
249
 * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
250
 * in @this_arg_index and sets @this_arg to point to the
251
 * payload for the field.  It takes care of alignment handling and extended
252
 * present fields.  @this_arg can be changed by the caller (eg,
253
 * incremented to move inside a compound argument like
254
 * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
255
 * little-endian format whatever the endianess of your CPU.
256
 *
257
 * Alignment Gotcha:
258
 * You must take care when dereferencing iterator.this_arg
259
 * for multibyte types... the pointer is not aligned.  Use
260
 * get_unaligned((type *)iterator.this_arg) to dereference
261
 * iterator.this_arg for type "type" safely on all arches.
262
 */
263
264
int ieee80211_radiotap_iterator_next(
265
  struct ieee80211_radiotap_iterator *iterator)
266
19.3k
{
267
19.3k
  if (iterator->tlv_mode) {
268
222
    struct ieee80211_radiotap_tlv *tlv;
269
222
    uint32_t size;
270
271
444
#define TLV_LEN_ALIGN(x) ((x + 3) & ~3)
272
222
    size = sizeof(*tlv) + TLV_LEN_ALIGN(iterator->this_arg_size);
273
274
    /*
275
     * We know that without the alignment padding it was valid, so
276
     * ignore arbitrary padding and return that we finished if no
277
     * further TLV could fit.
278
     */
279
222
    if (!ITERATOR_VALID(iterator, size))
280
0
      return -ENOENT;
281
282
    /* move to next entry */
283
222
    iterator->_arg += sizeof(*tlv) + TLV_LEN_ALIGN(iterator->this_arg_size);
284
285
320
return_tlv:
286
    /* and check again if we reached the end */
287
320
    if (!ITERATOR_VALID(iterator, 1))
288
3
      return -ENOENT;
289
290
    /* if it's not the end but a new TLV won't fit - error out */
291
317
    if (!ITERATOR_VALID(iterator, sizeof(*tlv)))
292
9
      return -EINVAL;
293
294
308
    tlv = (struct ieee80211_radiotap_tlv *)iterator->_arg;
295
296
308
    iterator->this_arg_index = get_unaligned_le16(&tlv->type);
297
308
    iterator->this_arg_size = get_unaligned_le16(&tlv->datalen);
298
308
    iterator->this_arg = tlv->data;
299
308
    iterator->is_radiotap_ns =
300
308
      iterator->this_arg_index != IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
301
302
308
    if (!ITERATOR_VALID(iterator, sizeof(*tlv) + iterator->this_arg_size))
303
70
      return -EINVAL;
304
238
    return 0;
305
308
  }
306
307
50.3k
  while (1) {
308
50.3k
    int hit = 0;
309
50.3k
    int pad, align, size, subns;
310
50.3k
    uint32_t oui;
311
312
    /* if no more EXT bits, that's it */
313
50.3k
    if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
314
50.3k
        !(iterator->_bitmap_shifter & 1))
315
47
      return -ENOENT;
316
317
50.3k
    if (!(iterator->_bitmap_shifter & 1))
318
28.7k
      goto next_entry; /* arg not present */
319
320
    /* get alignment/size of data */
321
21.5k
    switch (iterator->_arg_index % 32) {
322
98
    case IEEE80211_RADIOTAP_TLVS:
323
98
      align = 4;
324
98
      size = 0;
325
98
      break;
326
1.23k
    case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
327
2.49k
    case IEEE80211_RADIOTAP_EXT:
328
2.49k
      align = 1;
329
2.49k
      size = 0;
330
2.49k
      break;
331
46
    case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
332
46
      align = 2;
333
46
      size = 6;
334
46
      break;
335
18.9k
    default:
336
18.9k
#ifdef RADIOTAP_SUPPORT_OVERRIDES
337
18.9k
      if (find_override(iterator, &align, &size)) {
338
        /* all set */
339
512
      } else
340
18.4k
#endif
341
18.4k
      if (!iterator->current_namespace ||
342
18.4k
          iterator->_arg_index >= iterator->current_namespace->n_bits) {
343
37
        if (iterator->current_namespace == &radiotap_ns)
344
2
          return -ENOENT;
345
35
        align = 0;
346
18.3k
      } else {
347
18.3k
        align = iterator->current_namespace->align_size[iterator->_arg_index].align;
348
18.3k
        size = iterator->current_namespace->align_size[iterator->_arg_index].size;
349
18.3k
      }
350
18.9k
      if (!align) {
351
        /* skip all subsequent data */
352
66
        int skip_size = IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE - (iterator->_arg_index % 32);
353
        /* XXX - we should report an expert info here */
354
66
        if (!iterator->_next_ns_data)
355
31
          return -EINVAL;
356
35
        iterator->_arg = iterator->_next_ns_data;
357
        /* give up on this namespace */
358
35
        iterator->current_namespace = NULL;
359
35
        iterator->_next_ns_data = NULL;
360
        // Remove 1 because jump to next_entry will also shift bitmap by 1
361
35
        iterator->_bitmap_shifter >>= skip_size - 1;
362
35
        iterator->_arg_index += skip_size - 1;
363
        /* XXX - we should report an expert info here */
364
35
        if (!ITERATOR_VALID(iterator, 0))
365
0
          return -EINVAL;
366
35
        goto next_entry;
367
35
      }
368
18.8k
      break;
369
21.5k
    }
370
371
    /*
372
     * arg is present, account for alignment padding
373
     *
374
     * Note that these alignments are relative to the start
375
     * of the radiotap header.  There is no guarantee
376
     * that the radiotap header itself is aligned on any
377
     * kind of boundary.
378
     *
379
     * The above is why get_unaligned() is used to dereference
380
     * multibyte elements from the radiotap area.
381
     */
382
383
21.5k
    pad = (int)((iterator->_arg - (unsigned char *)iterator->_rtheader) & (align - 1));
384
385
21.5k
    if (pad)
386
3.52k
      iterator->_arg += align - pad;
387
388
21.5k
    if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
389
46
      int vnslen;
390
391
      /* XXX - we should report an expert info here */
392
46
      if (!ITERATOR_VALID(iterator, size))
393
1
        return -EINVAL;
394
395
45
      oui = (*iterator->_arg << 16) |
396
45
        (*(iterator->_arg + 1) << 8) |
397
45
        *(iterator->_arg + 2);
398
45
      subns = *(iterator->_arg + 3);
399
400
45
      find_ns(iterator, oui, subns);
401
402
45
      vnslen = get_unaligned_le16(iterator->_arg + 4);
403
45
      iterator->_next_ns_data = iterator->_arg + size + vnslen;
404
45
      if (!iterator->current_namespace)
405
45
        size += vnslen;
406
21.4k
    } else if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_TLVS) {
407
98
      iterator->tlv_mode = 1;
408
98
      goto return_tlv;
409
98
    }
410
411
    /*
412
     * this is what we will return to user, but we need to
413
     * move on first so next call has something fresh to test
414
     */
415
21.4k
    iterator->this_arg_index = iterator->_arg_index;
416
21.4k
    iterator->this_arg = iterator->_arg;
417
21.4k
    iterator->this_arg_size = size;
418
419
    /* internally move on the size of this arg */
420
21.4k
    iterator->_arg += size;
421
422
    /*
423
     * check for insanity where we are given a bitmap that
424
     * claims to have more arg content than the length of the
425
     * radiotap section.  We will normally end up equalling this
426
     * max_length on the last arg, never exceeding it.
427
     */
428
    /* XXX - we should report an expert info here */
429
21.4k
    if (!ITERATOR_VALID(iterator, 0))
430
258
      return -EINVAL;
431
432
    /* these special ones are valid in each bitmap word */
433
21.1k
    switch (iterator->_arg_index % 32) {
434
39
    case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
435
39
      iterator->_reset_on_ext = 1;
436
437
39
      iterator->is_radiotap_ns = 0;
438
      /*
439
       * If parser didn't register this vendor
440
       * namespace with us, allow it to show it
441
       * as 'raw. Do do that, set argument index
442
       * to vendor namespace.
443
       */
444
39
      iterator->this_arg_index =
445
39
        IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
446
39
      if (!iterator->current_namespace)
447
39
        hit = 1;
448
39
      goto next_entry;
449
1.23k
    case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
450
1.23k
      iterator->_reset_on_ext = 1;
451
1.23k
      iterator->current_namespace = &radiotap_ns;
452
1.23k
      iterator->is_radiotap_ns = 1;
453
1.23k
      goto next_entry;
454
1.26k
    case IEEE80211_RADIOTAP_EXT:
455
      /*
456
       * bit 31 was set, there is more
457
       * -- move to next u32 bitmap
458
       */
459
1.26k
      iterator->_bitmap_shifter =
460
1.26k
        get_unaligned_le32(iterator->_next_bitmap);
461
1.26k
      iterator->_next_bitmap++;
462
1.26k
      if (iterator->_reset_on_ext)
463
1.26k
        iterator->_arg_index = 0;
464
2
      else
465
2
        iterator->_arg_index++;
466
1.26k
      iterator->_reset_on_ext = 0;
467
1.26k
      break;
468
18.6k
    default:
469
      /* we've got a hit! */
470
18.6k
      hit = 1;
471
48.6k
 next_entry:
472
48.6k
      iterator->_bitmap_shifter >>= 1;
473
48.6k
      iterator->_arg_index++;
474
21.1k
    }
475
476
    /* if we found a valid arg earlier, return it now */
477
49.9k
    if (hit)
478
18.6k
      return 0;
479
49.9k
  }
480
19.0k
}
481
482
/*
483
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
484
 *
485
 * Local variables:
486
 * c-basic-offset: 8
487
 * tab-width: 8
488
 * indent-tabs-mode: t
489
 * End:
490
 *
491
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
492
 * :indentSize=8:tabSize=8:noTabs=false:
493
 */