3 * Decode packets with a RFtap header
4 * Copyright 2016, Jonathan Brucker <jonathan.brucke@gmail.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 * The RFtap header is a simple meta-data header designed to provide
15 * RF (Radio Frequency) meta-data about frames, such as:
16 * - Accurate signal and noise power
17 * - Accurate timing and phase information
18 * - Accurate carrier and Doppler frequencies, and more.
19 * The RFtap protocol can be used to encapsulate any type of frame.
21 * Official specification:
22 * https://rftap.github.io
27 #include <epan/packet.h>
28 #include <epan/unit_strings.h>
30 #include <wsutil/array.h>
33 /* (Required to prevent [-Wmissing-prototypes] warnings */
34 void proto_reg_handoff_rftap(void);
35 void proto_register_rftap(void);
38 static int proto_rftap
;
40 /* rftap fixed fields */
41 static int hf_rftap_fixed_header
;
42 static int hf_rftap_magic
;
43 static int hf_rftap_len
; /* length in bytes */
44 static int hf_rftap_flags
;
46 /* rftap flags bit-field (16 bits) */
47 static int hf_rftap_present_dlt
;
48 static int hf_rftap_present_freq
;
49 static int hf_rftap_present_nomfreq
;
50 static int hf_rftap_present_freqofs
;
51 static int hf_rftap_power_is_in_dbm
;
52 static int hf_rftap_present_signal_power
;
53 static int hf_rftap_present_noise_power
;
54 static int hf_rftap_present_snr
;
55 static int hf_rftap_present_signal_quality
;
56 static int hf_rftap_time_is_unix_time
;
57 static int hf_rftap_present_time
;
58 static int hf_rftap_present_duration
;
59 static int hf_rftap_present_location
;
60 static int hf_rftap_present_reserved_field_13
;
61 static int hf_rftap_present_reserved_field_14
;
62 static int hf_rftap_present_reserved_field_15
;
64 /* rftap optional fields */
65 static int hf_rftap_dlt
;
66 static int hf_rftap_freq
;
67 static int hf_rftap_nomfreq
;
68 static int hf_rftap_freqofs
;
69 static int hf_rftap_signal_power
;
70 static int hf_rftap_noise_power
;
71 static int hf_rftap_snr
;
72 static int hf_rftap_signal_quality
;
73 static int hf_rftap_time_int
;
74 static int hf_rftap_time_frac
;
75 static int hf_rftap_time
;
76 static int hf_rftap_duration
;
77 static int hf_rftap_latitude
;
78 static int hf_rftap_longitude
;
79 static int hf_rftap_altitude
;
81 /* rftap tag IDs >= 16 */
82 static int hf_rftap_subdissector_name
;
84 /* subtree pointers */
86 static int ett_rftap_fixed_header
;
87 static int ett_rftap_flags
;
89 static dissector_handle_t pcap_pktdata_handle
;
91 #define RFTAP_MAGIC 0x61744652UL /* "RFta" */
96 RFTAP_TAG_NOM_FREQ
= 2,
97 RFTAP_TAG_FREQ_OFS
= 3,
98 RFTAP_TAG_POWER_IS_IN_DBM
= 4,
99 RFTAP_TAG_SIGNAL_POWER
= 5,
100 RFTAP_TAG_NOISE_POWER
= 6,
102 RFTAP_TAG_SIGNAL_QUALITY
= 8,
103 RFTAP_TAG_TIME_IS_UNIX_TIME
= 9,
105 RFTAP_TAG_DURATION
= 11,
106 RFTAP_TAG_LOCATION
= 12,
107 RFTAP_TAG_RESERVED_13
= 13,
108 RFTAP_TAG_RESERVED_14
= 14,
109 RFTAP_TAG_RESERVED_15
= 15,
110 RFTAP_TAG_DISSECTOR_NAME
= 16
113 /* This is the header as it is used by rftap-generating software.
114 * It is not used by the wireshark dissector and provided for reference only.
116 le32 magic; // "RFta"
117 le16 len32; // sizeof(rftap_hdr) / sizeof(le32)
118 le16 flags; // bitfield indicating presence of parameters
120 } __attribute__((packed));
123 /* dissect the rftap header part of the packet
124 * returns Data Link Type (dlt) and subdissector name
127 dissect_rftap_header(tvbuff_t
*tvb
, proto_tree
*tree
, packet_info
*pinfo
, uint32_t *dlt
, const uint8_t **subdissector_name
)
129 proto_item
*ti_header
;
130 proto_tree
*header_tree
;
142 static int * const flag_fields
[] = {
143 &hf_rftap_present_dlt
,
144 &hf_rftap_present_freq
,
145 &hf_rftap_present_nomfreq
,
146 &hf_rftap_present_freqofs
,
147 &hf_rftap_power_is_in_dbm
,
148 &hf_rftap_present_signal_power
,
149 &hf_rftap_present_noise_power
,
150 &hf_rftap_present_snr
,
151 &hf_rftap_present_signal_quality
,
152 &hf_rftap_time_is_unix_time
,
153 &hf_rftap_present_time
,
154 &hf_rftap_present_duration
,
155 &hf_rftap_present_location
,
156 &hf_rftap_present_reserved_field_13
,
157 &hf_rftap_present_reserved_field_14
,
158 &hf_rftap_present_reserved_field_15
,
163 *subdissector_name
= NULL
;
165 /* rftap fixed header sub-tree */
167 ti_header
= proto_tree_add_item(tree
, hf_rftap_fixed_header
, tvb
, 0, 8, ENC_NA
);
168 header_tree
= proto_item_add_subtree(ti_header
, ett_rftap_fixed_header
);
170 proto_tree_add_item(header_tree
, hf_rftap_magic
, tvb
, 0, 4, ENC_LITTLE_ENDIAN
);
171 len
= 4 * (int32_t) tvb_get_letohs(tvb
, 4); /* convert to length in bytes */
172 proto_tree_add_uint(header_tree
, hf_rftap_len
, tvb
, 4, 2, len
); /* show length in bytes */
173 proto_tree_add_bitmask_ret_uint64(header_tree
, tvb
, 6, hf_rftap_flags
,
174 ett_rftap_flags
, flag_fields
, ENC_LITTLE_ENDIAN
, &flags
);
176 /* rftap parameter fields */
178 power_units
= (flags
& (1 << RFTAP_TAG_POWER_IS_IN_DBM
)) ? "dBm" : "dB";
182 for (tag_id
= 0; tag_id
< 16; tag_id
++, flag_bit
<<=1) {
184 if (!(flags
& flag_bit
))
185 continue; /* parameter is not present, skip */
189 proto_tree_add_item_ret_uint(tree
, hf_rftap_dlt
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, dlt
);
193 proto_tree_add_item(tree
, hf_rftap_freq
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
196 case RFTAP_TAG_NOM_FREQ
:
197 proto_tree_add_item(tree
, hf_rftap_nomfreq
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
200 case RFTAP_TAG_FREQ_OFS
:
201 proto_tree_add_item(tree
, hf_rftap_freqofs
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
204 case RFTAP_TAG_POWER_IS_IN_DBM
:
205 /* do nothing, it's already decoded in flags bit-field */
207 case RFTAP_TAG_SIGNAL_POWER
:
208 float_val
= tvb_get_letohieee_float(tvb
, offset
);
209 proto_tree_add_float_format_value(tree
, hf_rftap_signal_power
, tvb
, offset
, 4, float_val
, "%.2f %s", float_val
, power_units
);
212 case RFTAP_TAG_NOISE_POWER
:
213 float_val
= tvb_get_letohieee_float(tvb
, offset
);
214 proto_tree_add_float_format_value(tree
, hf_rftap_noise_power
, tvb
, offset
, 4, float_val
, "%.2f %s", float_val
, power_units
);
218 float_val
= tvb_get_letohieee_float(tvb
, offset
);
219 proto_tree_add_float_format_value(tree
, hf_rftap_snr
, tvb
, offset
, 4, float_val
, "%.2f dB", float_val
);
222 case RFTAP_TAG_SIGNAL_QUALITY
:
223 proto_tree_add_item(tree
, hf_rftap_signal_quality
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
226 case RFTAP_TAG_TIME_IS_UNIX_TIME
:
227 /* do nothing, it's already decoded in flags bit-field */
230 double_val
= tvb_get_letohieee_double(tvb
, offset
);
231 proto_tree_add_double_format_value(tree
, hf_rftap_time_int
, tvb
, offset
, 8, double_val
, "%.0f seconds", double_val
);
232 double_val
= tvb_get_letohieee_double(tvb
, offset
+ 8);
233 proto_tree_add_double_format_value(tree
, hf_rftap_time_frac
, tvb
, offset
+8, 8, double_val
, "%.9f seconds", double_val
);
234 /* compute combined time: (not accurate, error is > 300 nanoseconds) */
235 double_val
+= tvb_get_letohieee_double(tvb
, offset
);
236 proto_tree_add_double_format_value(tree
, hf_rftap_time
, tvb
, offset
, 16, double_val
, "%.6f seconds", double_val
);
239 case RFTAP_TAG_DURATION
:
240 proto_tree_add_item(tree
, hf_rftap_duration
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
243 case RFTAP_TAG_LOCATION
:
244 proto_tree_add_item(tree
, hf_rftap_latitude
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
245 proto_tree_add_item(tree
, hf_rftap_longitude
, tvb
, offset
+8, 8, ENC_LITTLE_ENDIAN
);
246 proto_tree_add_item(tree
, hf_rftap_altitude
, tvb
, offset
+16, 8, ENC_LITTLE_ENDIAN
);
250 return; /* we've hit a parameter we can't decode, abort */
255 return; /* there are no tagged parameters to decode, goodbye */
257 /* rftap tagged parameter fields */
259 tag_id
= tvb_get_letohs(tvb
, offset
);
260 tag_len
= tvb_get_uint8(tvb
, offset
+2);
261 tag_flags
= tvb_get_uint8(tvb
, offset
+3);
263 if ((tag_id
!= RFTAP_TAG_DISSECTOR_NAME
) || (tag_len
== 0) || (tag_len
== 255) || (tag_flags
!= 255))
264 return; /* we've hit a tagged parameter we can't decode, abort */
266 proto_tree_add_item_ret_string(tree
, hf_rftap_subdissector_name
, tvb
,
267 offset
+4, tag_len
, ENC_ASCII
, pinfo
->pool
, subdissector_name
);
270 /* Main entry point to dissect the packets.
272 * Each packet consists of two parts:
273 * - The rftap header, containing all the RF metadata.
274 * - The encapsulated data packet, decoded by a sub-dissector.
277 dissect_rftap(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
281 proto_tree
*rftap_tree
;
283 tvbuff_t
*rftap_tvb
; /* the first part of the packet */
284 tvbuff_t
*subdissector_tvb
; /* the second part of the packet */
286 int32_t rftap_len
; /* length in bytes */
287 dissector_handle_t subdissector_handle
;
288 uint32_t subdissector_dlt
;
289 const uint8_t *subdissector_name
;
293 if (tvb_captured_length(tvb
) < 8) /* 4 magic + 2 len + 2 flags = 8 bytes */
296 if (tvb_get_letohl(tvb
, 0) != RFTAP_MAGIC
)
301 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "RFTAP");
302 col_clear(pinfo
->cinfo
, COL_INFO
);
303 clear_address(&pinfo
->src
);
304 clear_address(&pinfo
->dst
);
306 /* dissect part 1: rftap header */
308 rftap_len
= 4 * (int32_t) tvb_get_letohs(tvb
, 4);
309 rftap_tvb
= tvb_new_subset_length_caplen(tvb
, 0, rftap_len
, rftap_len
);
311 ti
= proto_tree_add_protocol_format(tree
, proto_rftap
, rftap_tvb
, 0, -1,
312 "RFtap Protocol (%d bytes)", rftap_len
);
313 rftap_tree
= proto_item_add_subtree(ti
, ett_rftap
);
315 dissect_rftap_header(rftap_tvb
, rftap_tree
, pinfo
, &subdissector_dlt
, &subdissector_name
);
317 /* dissect part 2: data packet */
319 subdissector_tvb
= tvb_new_subset_remaining(tvb
, rftap_len
);
321 /* try using data link type (DLT) */
322 if (subdissector_dlt
!= 0xffffffff) {
323 call_dissector_with_data(pcap_pktdata_handle
, subdissector_tvb
, pinfo
, tree
, &subdissector_dlt
);
324 return tvb_captured_length(tvb
);
327 /* try using dissector name */
328 if (subdissector_name
) {
329 subdissector_handle
= find_dissector(subdissector_name
);
330 if (subdissector_handle
) {
331 call_dissector_with_data(subdissector_handle
, subdissector_tvb
, pinfo
, tree
, NULL
);
332 return tvb_captured_length(tvb
);
336 /* fallback using plain data dissector */
337 call_data_dissector(subdissector_tvb
, pinfo
, tree
);
338 return tvb_captured_length(tvb
);
342 dissect_rftap_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
344 return dissect_rftap(tvb
, pinfo
, tree
, data
) != 0;
347 /* Register the protocol with Wireshark. */
349 proto_register_rftap(void)
351 /* Setup protocol subtree array */
352 static int *ett
[] = {
354 &ett_rftap_fixed_header
,
358 /* Setup list of header fields */
359 static hf_register_info hf
[] = {
361 /* rftap fixed header */
363 { &hf_rftap_fixed_header
, {
364 "RFtap Fixed header",
366 FT_NONE
, BASE_NONE
, NULL
, 0,
367 "RFtap Fixed 8-byte Header", HFILL
}},
372 FT_UINT32
, BASE_HEX
, NULL
, 0,
373 "RFtap signature: wikipedia.org/wiki/File_format#Magic_number", HFILL
}},
377 FT_UINT32
, BASE_DEC
, NULL
, 0,
378 "Length (in bytes) of entire rftap header, including tagged (optional) parameters", HFILL
}},
382 FT_UINT16
, BASE_HEX
, NULL
, 0,
383 "RFtap flags", HFILL
}},
385 /* flags bit-field */
387 {&hf_rftap_present_dlt
, {
390 FT_BOOLEAN
, 16, NULL
, 0x0001,
391 "Specifies if the DLT (Data Link Type) field is present", HFILL
}},
392 {&hf_rftap_present_freq
, {
394 "rftap.present.freq",
395 FT_BOOLEAN
, 16, NULL
, 0x0002,
396 "Specifies if the Frequency field is present", HFILL
}},
397 {&hf_rftap_present_nomfreq
, {
398 "Nominal Frequency Present",
399 "rftap.present.nomfreq",
400 FT_BOOLEAN
, 16, NULL
, 0x0004,
401 "Specifies if the Nominal Frequency field is present", HFILL
}},
402 {&hf_rftap_present_freqofs
, {
403 "Frequency Offset Present",
404 "rftap.present.freqofs",
405 FT_BOOLEAN
, 16, NULL
, 0x0008,
406 "Specifies if the Frequency Offset field is present", HFILL
}},
407 {&hf_rftap_power_is_in_dbm
, {
408 "Power is in dBm Units",
410 FT_BOOLEAN
, 16, NULL
, 0x0010,
411 "Specifies if the Power is specified in dBm units", HFILL
}},
412 {&hf_rftap_present_signal_power
, {
413 "Signal Power Present",
414 "rftap.present.power",
415 FT_BOOLEAN
, 16, NULL
, 0x0020,
416 "Specifies if the Signal Power field is present", HFILL
}},
417 {&hf_rftap_present_noise_power
, {
418 "Noise Power Present",
419 "rftap.present.noise",
420 FT_BOOLEAN
, 16, NULL
, 0x0040,
421 "Specifies if the Noise Power field is present", HFILL
}},
422 {&hf_rftap_present_snr
, {
425 FT_BOOLEAN
, 16, NULL
, 0x0080,
426 "Specifies if the SNR field is present", HFILL
}},
427 {&hf_rftap_present_signal_quality
, {
428 "Signal Quality Present",
429 "rftap.present.qual",
430 FT_BOOLEAN
, 16, NULL
, 0x0100,
431 "Specifies if the Signal Quality field is present", HFILL
}},
432 {&hf_rftap_time_is_unix_time
, {
433 "Time standard is Unix Time",
435 FT_BOOLEAN
, 16, NULL
, 0x0200,
436 "Specifies if the time standard is Unix Time: wikipedia.org/wiki/Unix_time", HFILL
}},
437 {&hf_rftap_present_time
, {
439 "rftap.present.time",
440 FT_BOOLEAN
, 16, NULL
, 0x0400,
441 "Specifies if the Time field is present", HFILL
}},
442 {&hf_rftap_present_duration
, {
444 "rftap.present.duration",
445 FT_BOOLEAN
, 16, NULL
, 0x0800,
446 "Specifies if the Duration field is present", HFILL
}},
447 {&hf_rftap_present_location
, {
449 "rftap.present.location",
450 FT_BOOLEAN
, 16, NULL
, 0x1000,
451 "Specifies if the Location field is present", HFILL
}},
452 {&hf_rftap_present_reserved_field_13
, {
453 "Reserved Field 13 Present",
454 "rftap.present.field13",
455 FT_BOOLEAN
, 16, NULL
, 0x2000,
456 "Specifies if the Reserved Field 13 is present", HFILL
}},
457 {&hf_rftap_present_reserved_field_14
, {
458 "Reserved Field 14 Present",
459 "rftap.present.field14",
460 FT_BOOLEAN
, 16, NULL
, 0x4000,
461 "Specifies if the Reserved Field 14 is present", HFILL
}},
462 {&hf_rftap_present_reserved_field_15
, {
463 "Reserved Field 15 Present",
464 "rftap.present.field15",
465 FT_BOOLEAN
, 16, NULL
, 0x8000,
466 "Specifies if the Reserved Field 15 is present", HFILL
}},
468 /* rftap parameters */
471 "Data Link Type (DLT)",
473 FT_UINT32
, BASE_DEC
, NULL
, 0,
474 "Data Link Type (DLT) of the encapsulated packet: www.tcpdump.org/linktypes.html", HFILL
}},
478 FT_DOUBLE
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_hz
), 0,
479 "Actual (measured) carrier frequency, in Hertz (not necessarily center frequency)", HFILL
}},
480 { &hf_rftap_nomfreq
, {
483 FT_DOUBLE
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_hz
), 0,
484 "Nominal carrier frequency, in Hertz (the ideal frequency, ignoring freq errors)", HFILL
}},
485 { &hf_rftap_freqofs
, {
488 FT_DOUBLE
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_hz
), 0,
489 "Carrier frequency offset, in Hertz: wikipedia.org/wiki/Carrier_frequency_offset", HFILL
}},
490 { &hf_rftap_signal_power
, {
493 FT_FLOAT
, BASE_NONE
, NULL
, 0,
494 "Signal power, in dB or dBm units: wikipedia.org/wiki/DBm", HFILL
}},
495 { &hf_rftap_noise_power
, {
498 FT_FLOAT
, BASE_NONE
, NULL
, 0,
499 "Noise power, in dB or dBm units: wikipedia.org/wiki/DBm", HFILL
}},
503 FT_FLOAT
, BASE_NONE
, NULL
, 0,
504 "Signal to Noise ratio (decibel units): wikipedia.org/wiki/Signal-to-noise_ratio", HFILL
}},
505 { &hf_rftap_signal_quality
, {
508 FT_FLOAT
, BASE_NONE
, NULL
, 0,
509 "Signal quality, arbitrary units from 0.0 (worst) to 1.0 (best)", HFILL
}},
510 { &hf_rftap_time_int
, {
511 "Time (integer part)",
513 FT_DOUBLE
, BASE_NONE
, NULL
, 0,
514 "The integer part of event time, in seconds, since epoch: wikipedia.org/wiki/Epoch_(reference_date)", HFILL
}},
515 { &hf_rftap_time_frac
, {
516 "Time (fractional part)",
518 FT_DOUBLE
, BASE_NONE
, NULL
, 0,
519 "The fractional part of event time, in seconds, since epoch: wikipedia.org/wiki/Epoch_(reference_date)", HFILL
}},
523 FT_DOUBLE
, BASE_NONE
, NULL
, 0,
524 "The event time, in seconds, since epoch: wikipedia.org/wiki/Epoch_(reference_date)", HFILL
}},
525 { &hf_rftap_duration
, {
528 FT_DOUBLE
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_second_seconds
), 0,
529 "The duration of the event (packet), in seconds", HFILL
}},
530 { &hf_rftap_latitude
, {
533 FT_DOUBLE
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_degree_degrees
), 0,
534 "Latitude of receiver (-90..90 degrees), using WGS 84 datum: wikipedia.org/wiki/World_Geodetic_System", HFILL
}},
535 { &hf_rftap_longitude
, {
538 FT_DOUBLE
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_degree_degrees
), 0,
539 "Longitude of receiver (-180..180 degrees), using WGS 84 datum: wikipedia.org/wiki/World_Geodetic_System", HFILL
}},
540 { &hf_rftap_altitude
, {
543 FT_DOUBLE
, BASE_NONE
|BASE_UNIT_STRING
, UNS(&units_meter_meters
), 0,
544 "Altitude of receiver, in meters, using WGS 84 datum: wikipedia.org/wiki/World_Geodetic_System", HFILL
}},
546 /* rftap tagged parameters */
548 { &hf_rftap_subdissector_name
, {
551 FT_STRING
, BASE_NONE
, NULL
, 0,
552 "Name of sub-dissector used for packet data (alternative to DLT field)", HFILL
}}
555 /* Register the protocol name and description */
556 proto_rftap
= proto_register_protocol("RFtap Protocol", "RFtap", "rftap");
558 /* Register the header fields and subtrees */
559 proto_register_field_array(proto_rftap
, hf
, array_length(hf
));
560 proto_register_subtree_array(ett
, array_length(ett
));
562 register_dissector("rftap", dissect_rftap
, proto_rftap
);
566 /* Protocol registration routine. This function is also called by
567 * Wireshark's preferences manager whenever "Apply" or "OK" are pressed.
570 proto_reg_handoff_rftap(void)
572 pcap_pktdata_handle
= find_dissector_add_dependency("pcap_pktdata", proto_rftap
);
573 heur_dissector_add("udp", dissect_rftap_heur
, "RFtap over UDP", "rftap", proto_rftap
, HEURISTIC_ENABLE
);
577 * Editor modelines - https://www.wireshark.org/tools/modelines.html
582 * indent-tabs-mode: nil
585 * vi: set shiftwidth=4 tabstop=8 expandtab:
586 * :indentSize=4:tabSize=8:noTabs=true: