2 * packet-ieee80211-netmon.c
3 * Decode packets with a Network Monitor 802.11 radio header
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include <epan/packet.h>
15 #include <epan/unit_strings.h>
17 #include <wiretap/wtap.h>
19 #include <wsutil/802_11-utils.h>
21 void proto_register_netmon_802_11(void);
22 void proto_reg_handoff_netmon_802_11(void);
25 static int proto_netmon_802_11
;
28 static dissector_handle_t netmon_802_11_handle
;
30 #define MIN_HEADER_LEN 32
33 #define OP_MODE_STA 0x00000001 /* station mode */
34 #define OP_MODE_AP 0x00000002 /* AP mode */
35 #define OP_MODE_STA_EXT 0x00000004 /* extensible station mode */
36 #define OP_MODE_MON 0x80000000 /* monitor mode */
40 * Augmented with phy types from
42 * https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/windot11/ne-windot11-_dot11_phy_type
44 #define PHY_TYPE_UNKNOWN 0
45 #define PHY_TYPE_FHSS 1
46 #define PHY_TYPE_DSSS 2
47 #define PHY_TYPE_IR_BASEBAND 3
48 #define PHY_TYPE_OFDM 4 /* 802.11a */
49 #define PHY_TYPE_HR_DSSS 5 /* 802.11b */
50 #define PHY_TYPE_ERP 6 /* 802.11g */
51 #define PHY_TYPE_HT 7 /* 802.11n */
52 #define PHY_TYPE_VHT 8 /* 802.11ac */
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
;
69 static int ett_netmon_802_11
;
70 static int ett_netmon_802_11_op_mode
;
72 static dissector_handle_t ieee80211_radio_handle
;
75 dissect_netmon_802_11(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
77 struct ieee_802_11_phdr phdr
;
78 proto_tree
*wlan_tree
= NULL
, *opmode_tree
;
85 uint32_t monitor_mode
;
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".
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.
105 memset(&phdr
, 0, sizeof(phdr
));
107 phdr
.decrypted
= false;
108 phdr
.datapad
= false;
109 phdr
.phy
= PHDR_802_11_PHY_UNKNOWN
;
111 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "WLAN");
112 col_clear(pinfo
->cinfo
, COL_INFO
);
115 version
= tvb_get_uint8(tvb
, offset
);
116 length
= tvb_get_letohs(tvb
, offset
+1);
117 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "NetMon WLAN Capture v%u, Length %u",
123 if (length
< MIN_HEADER_LEN
) {
128 /* Dissect the packet */
129 ti
= proto_tree_add_item(tree
, proto_netmon_802_11
, tvb
, 0, length
,
131 wlan_tree
= proto_item_add_subtree(ti
, ett_netmon_802_11
);
134 * XXX - is this the NDIS_OBJECT_HEADER structure:
136 * https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddndis/ns-ntddndis-_ndis_object_header
138 * at the beginning of a DOT11_EXTSTA_RECV_CONTEXT structure:
140 * https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/windot11/ns-windot11-dot11_extsta_recv_context
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.
145 proto_tree_add_item(wlan_tree
, hf_netmon_802_11_version
, tvb
, offset
, 1,
148 proto_tree_add_item(wlan_tree
, hf_netmon_802_11_length
, tvb
, offset
, 2,
153 * This isn't in the DOT11_EXTSTA_RECV_CONTEXT structure.
155 ti
= proto_tree_add_item(wlan_tree
, hf_netmon_802_11_op_mode
, tvb
, offset
,
156 4, ENC_LITTLE_ENDIAN
);
157 opmode_tree
= proto_item_add_subtree(ti
, ett_netmon_802_11_op_mode
);
158 proto_tree_add_item(opmode_tree
, hf_netmon_802_11_op_mode_sta
, tvb
, offset
,
159 4, ENC_LITTLE_ENDIAN
);
160 proto_tree_add_item(opmode_tree
, hf_netmon_802_11_op_mode_ap
, tvb
, offset
,
161 4, ENC_LITTLE_ENDIAN
);
162 proto_tree_add_item(opmode_tree
, hf_netmon_802_11_op_mode_sta_ext
, tvb
,
163 offset
, 4, ENC_LITTLE_ENDIAN
);
164 proto_tree_add_item_ret_uint(opmode_tree
, hf_netmon_802_11_op_mode_mon
, tvb
, offset
,
165 4, ENC_LITTLE_ENDIAN
, &monitor_mode
);
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 supporess treating the
173 * Protect flag as an indication that the frame was encrypted.
175 phdr
.decrypted
= true;
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.
182 phdr
.no_a_msdus
= true;
189 flags
= tvb_get_letohl(tvb
, offset
);
191 if (flags
!= 0xffffffff) {
195 phy_type
= tvb_get_letohl(tvb
, offset
);
196 memset(&phdr
.phy_info
, 0, sizeof(phdr
.phy_info
));
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).
205 case PHY_TYPE_UNKNOWN
:
206 phdr
.phy
= PHDR_802_11_PHY_UNKNOWN
;
210 phdr
.phy
= PHDR_802_11_PHY_11_FHSS
;
213 case PHY_TYPE_IR_BASEBAND
:
214 phdr
.phy
= PHDR_802_11_PHY_11_IR
;
218 phdr
.phy
= PHDR_802_11_PHY_11_DSSS
;
221 case PHY_TYPE_HR_DSSS
:
222 phdr
.phy
= PHDR_802_11_PHY_11B
;
226 phdr
.phy
= PHDR_802_11_PHY_11A
;
230 phdr
.phy
= PHDR_802_11_PHY_11G
;
234 phdr
.phy
= PHDR_802_11_PHY_11N
;
238 phdr
.phy
= PHDR_802_11_PHY_11AC
;
242 phdr
.phy
= PHDR_802_11_PHY_UNKNOWN
;
245 proto_tree_add_item(wlan_tree
, hf_netmon_802_11_phy_type
, tvb
, offset
, 4,
250 * uChCenterFrequency?
252 channel
= tvb_get_letohl(tvb
, offset
);
253 if (channel
< 1000) {
255 proto_tree_add_uint_format_value(wlan_tree
, hf_netmon_802_11_channel
,
256 tvb
, offset
, 4, channel
,
261 phdr
.has_channel
= true;
262 phdr
.channel
= channel
;
263 proto_tree_add_uint(wlan_tree
, hf_netmon_802_11_channel
,
264 tvb
, offset
, 4, channel
);
267 case PHDR_802_11_PHY_11B
:
268 case PHDR_802_11_PHY_11G
:
269 /* 2.4 GHz channel */
270 frequency
= ieee80211_chan_to_mhz(channel
, true);
273 case PHDR_802_11_PHY_11A
:
275 frequency
= ieee80211_chan_to_mhz(channel
, false);
282 if (frequency
!= 0) {
283 phdr
.has_frequency
= true;
284 phdr
.frequency
= frequency
;
288 phdr
.has_frequency
= true;
289 phdr
.frequency
= channel
;
290 proto_tree_add_uint(wlan_tree
, hf_netmon_802_11_frequency
,
291 tvb
, offset
, 4, channel
);
292 calc_channel
= ieee80211_mhz_to_chan(channel
);
293 if (calc_channel
!= -1) {
294 phdr
.has_channel
= true;
295 phdr
.channel
= calc_channel
;
301 * usNumberOfMPDUsReceived is missing.
307 rssi
= tvb_get_letohl(tvb
, offset
);
309 proto_tree_add_int_format_value(wlan_tree
, hf_netmon_802_11_rssi
,
310 tvb
, offset
, 4, rssi
,
313 phdr
.has_signal_dbm
= true;
314 phdr
.signal_dbm
= rssi
;
315 proto_tree_add_int_format_value(wlan_tree
, hf_netmon_802_11_rssi
,
316 tvb
, offset
, 4, rssi
,
324 rate
= tvb_get_uint8(tvb
, offset
);
326 proto_tree_add_uint_format_value(wlan_tree
, hf_netmon_802_11_datarate
,
327 tvb
, offset
, 1, rate
,
330 phdr
.has_data_rate
= true;
331 phdr
.data_rate
= rate
;
332 proto_tree_add_uint_format_value(wlan_tree
, hf_netmon_802_11_datarate
,
333 tvb
, offset
, 1, rate
,
343 * If so, should this check the presence flag in flags?
345 phdr
.has_tsf_timestamp
= true;
346 phdr
.tsf_timestamp
= tvb_get_letoh64(tvb
, offset
);
347 proto_tree_add_item(wlan_tree
, hf_netmon_802_11_timestamp
, tvb
, offset
, 8,
354 /* dissect the 802.11 packet next */
355 next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
356 call_dissector_with_data(ieee80211_radio_handle
, next_tvb
, pinfo
, tree
, &phdr
);
361 proto_register_netmon_802_11(void)
363 static const value_string phy_type
[] = {
364 { PHY_TYPE_UNKNOWN
, "Unknown" },
365 { PHY_TYPE_FHSS
, "802.11 FHSS" },
366 { PHY_TYPE_DSSS
, "802.11 DSSS" },
367 { PHY_TYPE_IR_BASEBAND
, "802.11 IR" },
368 { PHY_TYPE_OFDM
, "802.11a" },
369 { PHY_TYPE_HR_DSSS
, "802.11b" },
370 { PHY_TYPE_ERP
, "802.11g" },
371 { PHY_TYPE_HT
, "802.11n" },
372 { PHY_TYPE_VHT
, "802.11ac" },
376 static hf_register_info hf
[] = {
377 { &hf_netmon_802_11_version
, { "Header revision", "netmon_802_11.version", FT_UINT8
,
378 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
379 { &hf_netmon_802_11_length
, { "Header length", "netmon_802_11.length", FT_UINT16
,
380 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
381 { &hf_netmon_802_11_op_mode
, { "Operation mode", "netmon_802_11.op_mode", FT_UINT32
,
382 BASE_HEX
, NULL
, 0x0, NULL
, HFILL
} },
383 { &hf_netmon_802_11_op_mode_sta
, { "Station mode", "netmon_802_11.op_mode.sta", FT_UINT32
,
384 BASE_HEX
, NULL
, OP_MODE_STA
, NULL
, HFILL
} },
385 { &hf_netmon_802_11_op_mode_ap
, { "AP mode", "netmon_802_11.op_mode.ap", FT_UINT32
,
386 BASE_HEX
, NULL
, OP_MODE_AP
, NULL
, HFILL
} },
387 { &hf_netmon_802_11_op_mode_sta_ext
, { "Extensible station mode", "netmon_802_11.op_mode.sta_ext", FT_UINT32
,
388 BASE_HEX
, NULL
, OP_MODE_STA_EXT
, NULL
, HFILL
} },
389 { &hf_netmon_802_11_op_mode_mon
, { "Monitor mode", "netmon_802_11.op_mode.mon", FT_UINT32
,
390 BASE_HEX
, NULL
, OP_MODE_MON
, NULL
, HFILL
} },
392 { &hf_netmon_802_11_flags
, { "Flags", "netmon_802_11.flags", FT_UINT32
,
393 BASE_HEX
, NULL
, 0x0, NULL
, HFILL
} },
395 { &hf_netmon_802_11_phy_type
, { "PHY type", "netmon_802_11.phy_type", FT_UINT32
,
396 BASE_DEC
, VALS(phy_type
), 0x0, NULL
, HFILL
} },
397 { &hf_netmon_802_11_channel
, { "Channel", "netmon_802_11.channel", FT_UINT32
,
398 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
399 { &hf_netmon_802_11_frequency
, { "Center frequency", "netmon_802_11.frequency", FT_UINT32
,
400 BASE_DEC
|BASE_UNIT_STRING
, UNS(&units_mhz
), 0x0, NULL
, HFILL
} },
401 { &hf_netmon_802_11_rssi
, { "RSSI", "netmon_802_11.rssi", FT_INT32
,
402 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
403 { &hf_netmon_802_11_datarate
, { "Data rate", "netmon_802_11.datarate", FT_UINT32
,
404 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
406 * XXX - is this host, or MAC, time stamp?
407 * It might be a FILETIME.
409 { &hf_netmon_802_11_timestamp
, { "Timestamp", "netmon_802_11.timestamp", FT_UINT64
,
410 BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
412 static int *ett
[] = {
414 &ett_netmon_802_11_op_mode
417 proto_netmon_802_11
= proto_register_protocol("NetMon 802.11 capture header",
420 netmon_802_11_handle
= register_dissector("netmon_802_11", dissect_netmon_802_11
, proto_netmon_802_11
);
421 proto_register_field_array(proto_netmon_802_11
, hf
, array_length(hf
));
422 proto_register_subtree_array(ett
, array_length(ett
));
426 proto_reg_handoff_netmon_802_11(void)
428 /* handle for 802.11+radio information dissector */
429 ieee80211_radio_handle
= find_dissector_add_dependency("wlan_radio", proto_netmon_802_11
);
430 dissector_add_uint("wtap_encap", WTAP_ENCAP_IEEE_802_11_NETMON
, netmon_802_11_handle
);
434 * Editor modelines - https://www.wireshark.org/tools/modelines.html
439 * indent-tabs-mode: nil
442 * ex: set shiftwidth=2 tabstop=8 expandtab:
443 * :indentSize=2:tabSize=8:noTabs=true: