Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-ieee80211-netmon.c
Line
Count
Source
1
/*
2
 *  packet-ieee80211-netmon.c
3
 *       Decode packets with a Network Monitor 802.11 radio header
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
14
#include <epan/packet.h>
15
#include <epan/unit_strings.h>
16
17
#include <wiretap/wtap.h>
18
19
#include <wsutil/802_11-utils.h>
20
21
void proto_register_netmon_802_11(void);
22
void proto_reg_handoff_netmon_802_11(void);
23
24
/* protocol */
25
static int proto_netmon_802_11;
26
27
/* Dissector */
28
static dissector_handle_t netmon_802_11_handle;
29
30
0
#define MIN_HEADER_LEN  32
31
32
/* op_mode */
33
14
#define OP_MODE_STA     0x00000001      /* station mode */
34
14
#define OP_MODE_AP      0x00000002      /* AP mode */
35
14
#define OP_MODE_STA_EXT 0x00000004      /* extensible station mode */
36
14
#define OP_MODE_MON     0x80000000      /* monitor mode */
37
38
/* phy_type */
39
/*
40
 * Augmented with phy types from
41
 *
42
 *    https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/windot11/ne-windot11-_dot11_phy_type
43
 */
44
14
#define PHY_TYPE_UNKNOWN     0
45
14
#define PHY_TYPE_FHSS        1
46
14
#define PHY_TYPE_DSSS        2
47
14
#define PHY_TYPE_IR_BASEBAND 3
48
14
#define PHY_TYPE_OFDM        4 /* 802.11a */
49
14
#define PHY_TYPE_HR_DSSS     5 /* 802.11b */
50
14
#define PHY_TYPE_ERP         6 /* 802.11g */
51
14
#define PHY_TYPE_HT          7 /* 802.11n */
52
14
#define PHY_TYPE_VHT         8 /* 802.11ac */
53
54
static int hf_netmon_802_11_version;
55
static int hf_netmon_802_11_length;
56
static int hf_netmon_802_11_op_mode;
57
static int hf_netmon_802_11_op_mode_sta;
58
static int hf_netmon_802_11_op_mode_ap;
59
static int hf_netmon_802_11_op_mode_sta_ext;
60
static int hf_netmon_802_11_op_mode_mon;
61
/* static int hf_netmon_802_11_flags; */
62
static int hf_netmon_802_11_phy_type;
63
static int hf_netmon_802_11_channel;
64
static int hf_netmon_802_11_frequency;
65
static int hf_netmon_802_11_rssi;
66
static int hf_netmon_802_11_datarate;
67
static int hf_netmon_802_11_timestamp;
68
69
static int ett_netmon_802_11;
70
static int ett_netmon_802_11_op_mode;
71
72
static dissector_handle_t ieee80211_radio_handle;
73
74
static int
75
dissect_netmon_802_11(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
76
0
{
77
0
  struct ieee_802_11_phdr phdr;
78
0
  proto_tree *wlan_tree = NULL, *opmode_tree;
79
0
  proto_item *ti;
80
0
  tvbuff_t   *next_tvb;
81
0
  int         offset;
82
0
  uint8_t     version;
83
0
  uint16_t    length;
84
0
  uint32_t    phy_type;
85
0
  uint32_t    monitor_mode;
86
0
  uint32_t    flags;
87
0
  uint32_t    channel;
88
0
  int         calc_channel;
89
0
  int32_t     rssi;
90
0
  uint8_t     rate;
91
92
  /*
93
   * It appears to be the case that management frames (and control and
94
   * extension frames ?) may or may not have an FCS and data frames don't.
95
   * (Netmon capture files have been seen for this encapsulation
96
   * management frames either completely with or without an FCS. Also:
97
   * instances have been  seen where both Management and Control frames
98
   * do not have an FCS).  An "FCS length" of -2 means "NetMon weirdness".
99
   *
100
   * The metadata header also has a bit indicating whether the adapter
101
   * was in monitor mode or not; if it isn't, we set "decrypted" to true,
102
   * as, for those frames, the Protected bit is preserved in received
103
   * frames, but the frame is decrypted.
104
   */
105
0
  memset(&phdr, 0, sizeof(phdr));
106
0
  phdr.fcs_len = -2;
107
0
  phdr.decrypted = false;
108
0
  phdr.datapad = false;
109
0
  phdr.phy = PHDR_802_11_PHY_UNKNOWN;
110
111
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "WLAN");
112
0
  col_clear(pinfo->cinfo, COL_INFO);
113
0
  offset = 0;
114
115
0
  version = tvb_get_uint8(tvb, offset);
116
0
  length = tvb_get_letohs(tvb, offset+1);
117
0
  col_add_fstr(pinfo->cinfo, COL_INFO, "NetMon WLAN Capture v%u, Length %u",
118
0
               version, length);
119
0
  if (version != 2) {
120
    /* XXX - complain */
121
0
    goto skip;
122
0
  }
123
0
  if (length < MIN_HEADER_LEN) {
124
    /* XXX - complain */
125
0
    goto skip;
126
0
  }
127
128
  /* Dissect the packet */
129
0
  ti = proto_tree_add_item(tree, proto_netmon_802_11, tvb, 0, length,
130
0
                           ENC_NA);
131
0
  wlan_tree = proto_item_add_subtree(ti, ett_netmon_802_11);
132
133
  /*
134
   * XXX - is this the NDIS_OBJECT_HEADER structure:
135
   *
136
   *    https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddndis/ns-ntddndis-_ndis_object_header
137
   *
138
   * at the beginning of a DOT11_EXTSTA_RECV_CONTEXT structure:
139
   *
140
   *    https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/windot11/ns-windot11-dot11_extsta_recv_context
141
   *
142
   * If so, the byte at an offset of 0 would be the appropriate type for the
143
   * structure following it, i.e. NDIS_OBJECT_TYPE_DEFAULT.
144
   */
145
0
  proto_tree_add_item(wlan_tree, hf_netmon_802_11_version, tvb, offset, 1,
146
0
                      ENC_LITTLE_ENDIAN);
147
0
  offset += 1;
148
0
  proto_tree_add_item(wlan_tree, hf_netmon_802_11_length, tvb, offset, 2,
149
0
                      ENC_LITTLE_ENDIAN);
150
0
  offset += 2;
151
152
  /*
153
   * This isn't in the DOT11_EXTSTA_RECV_CONTEXT structure.
154
   */
155
0
  ti = proto_tree_add_item(wlan_tree, hf_netmon_802_11_op_mode, tvb, offset,
156
0
                      4, ENC_LITTLE_ENDIAN);
157
0
  opmode_tree = proto_item_add_subtree(ti, ett_netmon_802_11_op_mode);
158
0
  proto_tree_add_item(opmode_tree, hf_netmon_802_11_op_mode_sta, tvb, offset,
159
0
                      4, ENC_LITTLE_ENDIAN);
160
0
  proto_tree_add_item(opmode_tree, hf_netmon_802_11_op_mode_ap, tvb, offset,
161
0
                      4, ENC_LITTLE_ENDIAN);
162
0
  proto_tree_add_item(opmode_tree, hf_netmon_802_11_op_mode_sta_ext, tvb,
163
0
                      offset, 4, ENC_LITTLE_ENDIAN);
164
0
  proto_tree_add_item_ret_uint(opmode_tree, hf_netmon_802_11_op_mode_mon, tvb, offset,
165
0
                               4, ENC_LITTLE_ENDIAN, &monitor_mode);
166
0
  if (!monitor_mode) {
167
    /*
168
     * If a NetMon capture is not done in monitor mode, we may see frames
169
     * with the Protect bit set (because they were encrypted on the air)
170
     * but that aren't encrypted (because they've been decrypted before
171
     * being written to the file).  This wasn't done in monitor mode, as
172
     * the "monitor mode" flag wasn't set, so suppress treating the
173
     * Protect flag as an indication that the frame was encrypted.
174
     */
175
0
    phdr.decrypted = true;
176
177
    /*
178
     * Furthermore, we may see frames with the A-MSDU Present flag set
179
     * in the QoS Control field but that have a regular frame, not a
180
     * sequence of A-MSDUs, in the payload.
181
     */
182
0
    phdr.no_a_msdus = true;
183
0
  }
184
0
  offset += 4;
185
186
  /*
187
   * uReceiveFlags?
188
   */
189
0
  flags = tvb_get_letohl(tvb, offset);
190
0
  offset += 4;
191
0
  if (flags != 0xffffffff) {
192
    /*
193
     * uPhyId?
194
     */
195
0
    phy_type = tvb_get_letohl(tvb, offset);
196
0
    memset(&phdr.phy_info, 0, sizeof(phdr.phy_info));
197
198
    /*
199
     * Unlike the channel flags in radiotap, this appears
200
     * to correctly indicate the modulation for this packet
201
     * (no cases seen where this doesn't match the data rate).
202
     */
203
0
    switch (phy_type) {
204
205
0
    case PHY_TYPE_UNKNOWN:
206
0
        phdr.phy = PHDR_802_11_PHY_UNKNOWN;
207
0
        break;
208
209
0
    case PHY_TYPE_FHSS:
210
0
        phdr.phy = PHDR_802_11_PHY_11_FHSS;
211
0
        break;
212
213
0
    case PHY_TYPE_IR_BASEBAND:
214
0
        phdr.phy = PHDR_802_11_PHY_11_IR;
215
0
        break;
216
217
0
    case PHY_TYPE_DSSS:
218
0
        phdr.phy = PHDR_802_11_PHY_11_DSSS;
219
0
        break;
220
221
0
    case PHY_TYPE_HR_DSSS:
222
0
        phdr.phy = PHDR_802_11_PHY_11B;
223
0
        break;
224
225
0
    case PHY_TYPE_OFDM:
226
0
        phdr.phy = PHDR_802_11_PHY_11A;
227
0
        break;
228
229
0
    case PHY_TYPE_ERP:
230
0
        phdr.phy = PHDR_802_11_PHY_11G;
231
0
        break;
232
233
0
    case PHY_TYPE_HT:
234
0
        phdr.phy = PHDR_802_11_PHY_11N;
235
0
        break;
236
237
0
    case PHY_TYPE_VHT:
238
0
        phdr.phy = PHDR_802_11_PHY_11AC;
239
0
        break;
240
241
0
    default:
242
0
        phdr.phy = PHDR_802_11_PHY_UNKNOWN;
243
0
        break;
244
0
    }
245
0
    proto_tree_add_item(wlan_tree, hf_netmon_802_11_phy_type, tvb, offset, 4,
246
0
                        ENC_LITTLE_ENDIAN);
247
0
    offset += 4;
248
249
    /*
250
     * uChCenterFrequency?
251
     */
252
0
    channel = tvb_get_letohl(tvb, offset);
253
0
    if (channel < 1000) {
254
0
      if (channel == 0) {
255
0
        proto_tree_add_uint_format_value(wlan_tree, hf_netmon_802_11_channel,
256
0
                                         tvb, offset, 4, channel,
257
0
                                         "Unknown");
258
0
      } else {
259
0
        unsigned frequency;
260
261
0
        phdr.has_channel = true;
262
0
        phdr.channel = channel;
263
0
        proto_tree_add_uint(wlan_tree, hf_netmon_802_11_channel,
264
0
                            tvb, offset, 4, channel);
265
0
        switch (phdr.phy) {
266
267
0
        case PHDR_802_11_PHY_11B:
268
0
        case PHDR_802_11_PHY_11G:
269
          /* 2.4 GHz channel */
270
0
          frequency = ieee80211_chan_to_mhz(channel, true);
271
0
          break;
272
273
0
        case PHDR_802_11_PHY_11A:
274
          /* 5 GHz channel */
275
0
          frequency = ieee80211_chan_to_mhz(channel, false);
276
0
          break;
277
278
0
        default:
279
0
          frequency = 0;
280
0
          break;
281
0
        }
282
0
        if (frequency != 0) {
283
0
          phdr.has_frequency = true;
284
0
          phdr.frequency = frequency;
285
0
        }
286
0
      }
287
0
    } else {
288
0
      phdr.has_frequency = true;
289
0
      phdr.frequency = channel;
290
0
      proto_tree_add_uint(wlan_tree, hf_netmon_802_11_frequency,
291
0
                                       tvb, offset, 4, channel);
292
0
      calc_channel = ieee80211_mhz_to_chan(channel);
293
0
      if (calc_channel != -1) {
294
0
        phdr.has_channel = true;
295
0
        phdr.channel = calc_channel;
296
0
      }
297
0
    }
298
0
    offset += 4;
299
300
    /*
301
     * usNumberOfMPDUsReceived is missing.
302
     */
303
304
    /*
305
     * lRSSI?
306
     */
307
0
    rssi = tvb_get_letohl(tvb, offset);
308
0
    if (rssi == 0) {
309
0
      proto_tree_add_int_format_value(wlan_tree, hf_netmon_802_11_rssi,
310
0
                                      tvb, offset, 4, rssi,
311
0
                                      "Unknown");
312
0
    } else {
313
0
      phdr.has_signal_dbm = true;
314
0
      phdr.signal_dbm = rssi;
315
0
      proto_tree_add_int_format_value(wlan_tree, hf_netmon_802_11_rssi,
316
0
                                      tvb, offset, 4, rssi,
317
0
                                      "%d dBm", rssi);
318
0
    }
319
0
    offset += 4;
320
321
    /*
322
     * ucDataRate?
323
     */
324
0
    rate = tvb_get_uint8(tvb, offset);
325
0
    if (rate == 0) {
326
0
      proto_tree_add_uint_format_value(wlan_tree, hf_netmon_802_11_datarate,
327
0
                                       tvb, offset, 1, rate,
328
0
                                       "Unknown");
329
0
    } else {
330
0
      phdr.has_data_rate = true;
331
0
      phdr.data_rate = rate;
332
0
      proto_tree_add_uint_format_value(wlan_tree, hf_netmon_802_11_datarate,
333
0
                                       tvb, offset, 1, rate,
334
0
                                       "%f Mb/s", rate*.5);
335
0
    }
336
0
    offset += 1;
337
0
  } else
338
0
    offset += 13;
339
340
  /*
341
   * ullTimestamp?
342
   *
343
   * If so, should this check the presence flag in flags?
344
   */
345
0
  phdr.has_tsf_timestamp = true;
346
0
  phdr.tsf_timestamp = tvb_get_letoh64(tvb, offset);
347
0
  proto_tree_add_item(wlan_tree, hf_netmon_802_11_timestamp, tvb, offset, 8,
348
0
                      ENC_LITTLE_ENDIAN);
349
  /*offset += 8;*/
350
351
0
skip:
352
0
  offset = length;
353
354
  /* dissect the 802.11 packet next */
355
0
  next_tvb = tvb_new_subset_remaining(tvb, offset);
356
0
  call_dissector_with_data(ieee80211_radio_handle, next_tvb, pinfo, tree, &phdr);
357
0
  return offset;
358
0
}
359
360
void
361
proto_register_netmon_802_11(void)
362
14
{
363
14
  static const value_string phy_type[] = {
364
14
    { PHY_TYPE_UNKNOWN,     "Unknown" },
365
14
    { PHY_TYPE_FHSS,        "802.11 FHSS" },
366
14
    { PHY_TYPE_DSSS,        "802.11 DSSS" },
367
14
    { PHY_TYPE_IR_BASEBAND, "802.11 IR" },
368
14
    { PHY_TYPE_OFDM,        "802.11a" },
369
14
    { PHY_TYPE_HR_DSSS,     "802.11b" },
370
14
    { PHY_TYPE_ERP,         "802.11g" },
371
14
    { PHY_TYPE_HT,          "802.11n" },
372
14
    { PHY_TYPE_VHT,         "802.11ac" },
373
14
    { 0, NULL },
374
14
  };
375
376
14
  static hf_register_info hf[] = {
377
14
    { &hf_netmon_802_11_version, { "Header revision", "netmon_802_11.version", FT_UINT8,
378
14
                          BASE_DEC, NULL, 0x0, NULL, HFILL } },
379
14
    { &hf_netmon_802_11_length, { "Header length", "netmon_802_11.length", FT_UINT16,
380
14
                          BASE_DEC, NULL, 0x0, NULL, HFILL } },
381
14
    { &hf_netmon_802_11_op_mode, { "Operation mode", "netmon_802_11.op_mode", FT_UINT32,
382
14
                          BASE_HEX, NULL, 0x0, NULL, HFILL } },
383
14
    { &hf_netmon_802_11_op_mode_sta, { "Station mode", "netmon_802_11.op_mode.sta", FT_UINT32,
384
14
                          BASE_HEX, NULL, OP_MODE_STA, NULL, HFILL } },
385
14
    { &hf_netmon_802_11_op_mode_ap, { "AP mode", "netmon_802_11.op_mode.ap", FT_UINT32,
386
14
                          BASE_HEX, NULL, OP_MODE_AP, NULL, HFILL } },
387
14
    { &hf_netmon_802_11_op_mode_sta_ext, { "Extensible station mode", "netmon_802_11.op_mode.sta_ext", FT_UINT32,
388
14
                          BASE_HEX, NULL, OP_MODE_STA_EXT, NULL, HFILL } },
389
14
    { &hf_netmon_802_11_op_mode_mon, { "Monitor mode", "netmon_802_11.op_mode.mon", FT_UINT32,
390
14
                          BASE_HEX, NULL, OP_MODE_MON, NULL, HFILL } },
391
#if 0
392
    { &hf_netmon_802_11_flags, { "Flags", "netmon_802_11.flags", FT_UINT32,
393
                          BASE_HEX, NULL, 0x0, NULL, HFILL } },
394
#endif
395
14
    { &hf_netmon_802_11_phy_type, { "PHY type", "netmon_802_11.phy_type", FT_UINT32,
396
14
                          BASE_DEC, VALS(phy_type), 0x0, NULL, HFILL } },
397
14
    { &hf_netmon_802_11_channel, { "Channel", "netmon_802_11.channel", FT_UINT32,
398
14
                          BASE_DEC, NULL, 0x0, NULL, HFILL } },
399
14
    { &hf_netmon_802_11_frequency, { "Center frequency", "netmon_802_11.frequency", FT_UINT32,
400
14
                          BASE_DEC|BASE_UNIT_STRING, UNS(&units_mhz), 0x0, NULL, HFILL } },
401
14
    { &hf_netmon_802_11_rssi, { "RSSI", "netmon_802_11.rssi", FT_INT32,
402
14
                          BASE_DEC, NULL, 0x0, NULL, HFILL } },
403
14
    { &hf_netmon_802_11_datarate, { "Data rate", "netmon_802_11.datarate", FT_UINT32,
404
14
                          BASE_DEC, NULL, 0x0, NULL, HFILL } },
405
    /*
406
     * XXX - is this host, or MAC, time stamp?
407
     * It might be a FILETIME.
408
     */
409
14
    { &hf_netmon_802_11_timestamp, { "Timestamp", "netmon_802_11.timestamp", FT_UINT64,
410
14
                          BASE_DEC, NULL, 0x0, NULL, HFILL } },
411
14
  };
412
14
  static int *ett[] = {
413
14
    &ett_netmon_802_11,
414
14
    &ett_netmon_802_11_op_mode
415
14
  };
416
417
14
  proto_netmon_802_11 = proto_register_protocol("NetMon 802.11 capture header",
418
14
                                                "NetMon 802.11",
419
14
                                                "netmon_802_11");
420
14
  netmon_802_11_handle = register_dissector("netmon_802_11", dissect_netmon_802_11, proto_netmon_802_11);
421
14
  proto_register_field_array(proto_netmon_802_11, hf, array_length(hf));
422
14
  proto_register_subtree_array(ett, array_length(ett));
423
14
}
424
425
void
426
proto_reg_handoff_netmon_802_11(void)
427
14
{
428
  /* handle for 802.11+radio information dissector */
429
14
  ieee80211_radio_handle = find_dissector_add_dependency("wlan_radio", proto_netmon_802_11);
430
14
  dissector_add_uint("wtap_encap", WTAP_ENCAP_IEEE_802_11_NETMON, netmon_802_11_handle);
431
14
}
432
433
/*
434
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
435
 *
436
 * Local Variables:
437
 * c-basic-offset: 2
438
 * tab-width: 8
439
 * indent-tabs-mode: nil
440
 * End:
441
 *
442
 * ex: set shiftwidth=2 tabstop=8 expandtab:
443
 * :indentSize=2:tabSize=8:noTabs=true:
444
 */