/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 | | */ |