/* * packet-rftap.c * Decode packets with a RFtap header * Copyright 2016, Jonathan Brucker * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * The RFtap header is a simple meta-data header designed to provide * RF (Radio Frequency) meta-data about frames, such as: * - Accurate signal and noise power * - Accurate timing and phase information * - Accurate carrier and Doppler frequencies, and more. * The RFtap protocol can be used to encapsulate any type of frame. * * Official specification: * https://rftap.github.io */ #include #include #include /* Prototypes */ /* (Required to prevent [-Wmissing-prototypes] warnings */ void proto_reg_handoff_rftap(void); void proto_register_rftap(void); /* protocols */ static int proto_rftap = -1; /* rftap fixed fields */ static int hf_rftap_fixed_header = -1; static int hf_rftap_magic = -1; static int hf_rftap_len = -1; /* length in bytes */ static int hf_rftap_flags = -1; /* rftap flags bit-field (16 bits) */ static int hf_rftap_present_dlt = -1; static int hf_rftap_present_freq = -1; static int hf_rftap_present_nomfreq = -1; static int hf_rftap_present_freqofs = -1; static int hf_rftap_power_is_in_dbm = -1; static int hf_rftap_present_signal_power = -1; static int hf_rftap_present_noise_power = -1; static int hf_rftap_present_snr = -1; static int hf_rftap_present_signal_quality = -1; static int hf_rftap_time_is_unix_time = -1; static int hf_rftap_present_time = -1; static int hf_rftap_present_duration = -1; static int hf_rftap_present_location = -1; static int hf_rftap_present_reserved_field_13 = -1; static int hf_rftap_present_reserved_field_14 = -1; static int hf_rftap_present_reserved_field_15 = -1; /* rftap optional fields */ static int hf_rftap_dlt = -1; static int hf_rftap_freq = -1; static int hf_rftap_nomfreq = -1; static int hf_rftap_freqofs = -1; static int hf_rftap_signal_power = -1; static int hf_rftap_noise_power = -1; static int hf_rftap_snr = -1; static int hf_rftap_signal_quality = -1; static int hf_rftap_time_int = -1; static int hf_rftap_time_frac = -1; static int hf_rftap_time = -1; static int hf_rftap_duration = -1; static int hf_rftap_latitude = -1; static int hf_rftap_longitude = -1; static int hf_rftap_altitude = -1; /* rftap tag IDs >= 16 */ static int hf_rftap_subdissector_name = -1; /* subtree pointers */ static gint ett_rftap = -1; static gint ett_rftap_fixed_header = -1; static gint ett_rftap_flags = -1; static dissector_handle_t pcap_pktdata_handle; #define RFTAP_MAGIC 0x61744652UL /* "RFta" */ enum rftap_tag_id { RFTAP_TAG_DLT = 0, RFTAP_TAG_FREQ = 1, RFTAP_TAG_NOM_FREQ = 2, RFTAP_TAG_FREQ_OFS = 3, RFTAP_TAG_POWER_IS_IN_DBM = 4, RFTAP_TAG_SIGNAL_POWER = 5, RFTAP_TAG_NOISE_POWER = 6, RFTAP_TAG_SNR = 7, RFTAP_TAG_SIGNAL_QUALITY = 8, RFTAP_TAG_TIME_IS_UNIX_TIME = 9, RFTAP_TAG_TIME = 10, RFTAP_TAG_DURATION = 11, RFTAP_TAG_LOCATION = 12, RFTAP_TAG_RESERVED_13 = 13, RFTAP_TAG_RESERVED_14 = 14, RFTAP_TAG_RESERVED_15 = 15, RFTAP_TAG_DISSECTOR_NAME = 16 }; /* This is the header as it is used by rftap-generating software. * It is not used by the wireshark dissector and provided for reference only. struct rftap_hdr { le32 magic; // "RFta" le16 len32; // sizeof(rftap_hdr) / sizeof(le32) le16 flags; // bitfield indicating presence of parameters le32 data[]; } __attribute__((packed)); */ /* dissect the rftap header part of the packet * returns Data Link Type (dlt) and subdissector name */ static void dissect_rftap_header(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, guint32 *dlt, const guint8 **subdissector_name) { proto_item *ti_header; proto_tree *header_tree; gint32 offset; gint32 len; guint64 flags; guint32 flag_bit; guint32 tag_id; gint32 tag_len; guint32 tag_flags; gdouble double_val; gfloat float_val; char *power_units; static int * const flag_fields[] = { &hf_rftap_present_dlt, &hf_rftap_present_freq, &hf_rftap_present_nomfreq, &hf_rftap_present_freqofs, &hf_rftap_power_is_in_dbm, &hf_rftap_present_signal_power, &hf_rftap_present_noise_power, &hf_rftap_present_snr, &hf_rftap_present_signal_quality, &hf_rftap_time_is_unix_time, &hf_rftap_present_time, &hf_rftap_present_duration, &hf_rftap_present_location, &hf_rftap_present_reserved_field_13, &hf_rftap_present_reserved_field_14, &hf_rftap_present_reserved_field_15, NULL }; *dlt = 0xffffffff; *subdissector_name = NULL; /* rftap fixed header sub-tree */ ti_header = proto_tree_add_item(tree, hf_rftap_fixed_header, tvb, 0, 8, ENC_NA); header_tree = proto_item_add_subtree(ti_header, ett_rftap_fixed_header); proto_tree_add_item(header_tree, hf_rftap_magic, tvb, 0, 4, ENC_LITTLE_ENDIAN); len = 4 * (gint32) tvb_get_letohs(tvb, 4); /* convert to length in bytes */ proto_tree_add_uint(header_tree, hf_rftap_len, tvb, 4, 2, len); /* show length in bytes */ proto_tree_add_bitmask_ret_uint64(header_tree, tvb, 6, hf_rftap_flags, ett_rftap_flags, flag_fields, ENC_LITTLE_ENDIAN, &flags); /* rftap parameter fields */ power_units = (flags & (1 << RFTAP_TAG_POWER_IS_IN_DBM)) ? "dBm" : "dB"; offset = 8; flag_bit = 1; for (tag_id = 0; tag_id < 16; tag_id++, flag_bit<<=1) { if (!(flags & flag_bit)) continue; /* parameter is not present, skip */ switch (tag_id) { case RFTAP_TAG_DLT: proto_tree_add_item_ret_uint(tree, hf_rftap_dlt, tvb, offset, 4, ENC_LITTLE_ENDIAN, dlt); offset += 4; break; case RFTAP_TAG_FREQ: proto_tree_add_item(tree, hf_rftap_freq, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; break; case RFTAP_TAG_NOM_FREQ: proto_tree_add_item(tree, hf_rftap_nomfreq, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; break; case RFTAP_TAG_FREQ_OFS: proto_tree_add_item(tree, hf_rftap_freqofs, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; break; case RFTAP_TAG_POWER_IS_IN_DBM: /* do nothing, it's already decoded in flags bit-field */ break; case RFTAP_TAG_SIGNAL_POWER: float_val = tvb_get_letohieee_float(tvb, offset); proto_tree_add_float_format_value(tree, hf_rftap_signal_power, tvb, offset, 4, float_val, "%.2f %s", float_val, power_units); offset += 4; break; case RFTAP_TAG_NOISE_POWER: float_val = tvb_get_letohieee_float(tvb, offset); proto_tree_add_float_format_value(tree, hf_rftap_noise_power, tvb, offset, 4, float_val, "%.2f %s", float_val, power_units); offset += 4; break; case RFTAP_TAG_SNR: float_val = tvb_get_letohieee_float(tvb, offset); proto_tree_add_float_format_value(tree, hf_rftap_snr, tvb, offset, 4, float_val, "%.2f dB", float_val); offset += 4; break; case RFTAP_TAG_SIGNAL_QUALITY: proto_tree_add_item(tree, hf_rftap_signal_quality, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; break; case RFTAP_TAG_TIME_IS_UNIX_TIME: /* do nothing, it's already decoded in flags bit-field */ break; case RFTAP_TAG_TIME: double_val = tvb_get_letohieee_double(tvb, offset); proto_tree_add_double_format_value(tree, hf_rftap_time_int, tvb, offset, 8, double_val, "%.0f seconds", double_val); double_val = tvb_get_letohieee_double(tvb, offset + 8); proto_tree_add_double_format_value(tree, hf_rftap_time_frac, tvb, offset+8, 8, double_val, "%.9f seconds", double_val); /* compute combined time: (not accurate, error is > 300 nanoseconds) */ double_val += tvb_get_letohieee_double(tvb, offset); proto_tree_add_double_format_value(tree, hf_rftap_time, tvb, offset, 16, double_val, "%.6f seconds", double_val); offset += 16; break; case RFTAP_TAG_DURATION: proto_tree_add_item(tree, hf_rftap_duration, tvb, offset, 8, ENC_LITTLE_ENDIAN); offset += 8; break; case RFTAP_TAG_LOCATION: proto_tree_add_item(tree, hf_rftap_latitude, tvb, offset, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_rftap_longitude, tvb, offset+8, 8, ENC_LITTLE_ENDIAN); proto_tree_add_item(tree, hf_rftap_altitude, tvb, offset+16, 8, ENC_LITTLE_ENDIAN); offset += 24; break; default: return; /* we've hit a parameter we can't decode, abort */ } } if (offset >= len) return; /* there are no tagged parameters to decode, goodbye */ /* rftap tagged parameter fields */ tag_id = tvb_get_letohs(tvb, offset); tag_len = tvb_get_guint8(tvb, offset+2); tag_flags = tvb_get_guint8(tvb, offset+3); if ((tag_id != RFTAP_TAG_DISSECTOR_NAME) || (tag_len == 0) || (tag_len == 255) || (tag_flags != 255)) return; /* we've hit a tagged parameter we can't decode, abort */ proto_tree_add_item_ret_string(tree, hf_rftap_subdissector_name, tvb, offset+4, tag_len, ENC_ASCII, pinfo->pool, subdissector_name); } /* Main entry point to dissect the packets. * * Each packet consists of two parts: * - The rftap header, containing all the RF metadata. * - The encapsulated data packet, decoded by a sub-dissector. */ static int dissect_rftap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { proto_item *ti; proto_tree *rftap_tree; tvbuff_t *rftap_tvb; /* the first part of the packet */ tvbuff_t *subdissector_tvb; /* the second part of the packet */ gint32 rftap_len; /* length in bytes */ dissector_handle_t subdissector_handle; guint32 subdissector_dlt; const guint8 *subdissector_name; /* heuristics */ if (tvb_captured_length(tvb) < 8) /* 4 magic + 2 len + 2 flags = 8 bytes */ return 0; if (tvb_get_letohl(tvb, 0) != RFTAP_MAGIC) return 0; /* column info */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "RFTAP"); col_clear(pinfo->cinfo, COL_INFO); clear_address(&pinfo->src); clear_address(&pinfo->dst); /* dissect part 1: rftap header */ rftap_len = 4 * (gint32) tvb_get_letohs(tvb, 4); rftap_tvb = tvb_new_subset_length_caplen(tvb, 0, rftap_len, rftap_len); ti = proto_tree_add_protocol_format(tree, proto_rftap, rftap_tvb, 0, -1, "RFtap Protocol (%d bytes)", rftap_len); rftap_tree = proto_item_add_subtree(ti, ett_rftap); dissect_rftap_header(rftap_tvb, rftap_tree, pinfo, &subdissector_dlt, &subdissector_name); /* dissect part 2: data packet */ subdissector_tvb = tvb_new_subset_remaining(tvb, rftap_len); /* try using data link type (DLT) */ if (subdissector_dlt != 0xffffffff) { call_dissector_with_data(pcap_pktdata_handle, subdissector_tvb, pinfo, tree, &subdissector_dlt); return tvb_captured_length(tvb); } /* try using dissector name */ if (subdissector_name) { subdissector_handle = find_dissector(subdissector_name); if (subdissector_handle) { call_dissector_with_data(subdissector_handle, subdissector_tvb, pinfo, tree, NULL); return tvb_captured_length(tvb); } } /* fallback using plain data dissector */ call_data_dissector(subdissector_tvb, pinfo, tree); return tvb_captured_length(tvb); } static gboolean dissect_rftap_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { return dissect_rftap(tvb, pinfo, tree, data) != 0; } /* Register the protocol with Wireshark. */ void proto_register_rftap(void) { /* Setup protocol subtree array */ static gint *ett[] = { &ett_rftap, &ett_rftap_fixed_header, &ett_rftap_flags }; /* Setup list of header fields */ static hf_register_info hf[] = { /* rftap fixed header */ { &hf_rftap_fixed_header, { "RFtap Fixed header", "rftap.fixedheader", FT_NONE, BASE_NONE, NULL, 0, "RFtap Fixed 8-byte Header", HFILL }}, { &hf_rftap_magic, { "Magic", "rftap.magic", FT_UINT32, BASE_HEX, NULL, 0, "RFtap signature: wikipedia.org/wiki/File_format#Magic_number", HFILL }}, { &hf_rftap_len, { "Length", "rftap.len", FT_UINT32, BASE_DEC, NULL, 0, "Length (in bytes) of entire rftap header, including tagged (optional) parameters", HFILL }}, { &hf_rftap_flags, { "Flags", "rftap.flags", FT_UINT16, BASE_HEX, NULL, 0, "RFtap flags", HFILL }}, /* flags bit-field */ {&hf_rftap_present_dlt, { "DLT Present", "rftap.present.dlt", FT_BOOLEAN, 16, NULL, 0x0001, "Specifies if the DLT (Data Link Type) field is present", HFILL }}, {&hf_rftap_present_freq, { "Frequency Present", "rftap.present.freq", FT_BOOLEAN, 16, NULL, 0x0002, "Specifies if the Frequency field is present", HFILL }}, {&hf_rftap_present_nomfreq, { "Nominal Frequency Present", "rftap.present.nomfreq", FT_BOOLEAN, 16, NULL, 0x0004, "Specifies if the Nominal Frequency field is present", HFILL }}, {&hf_rftap_present_freqofs, { "Frequency Offset Present", "rftap.present.freqofs", FT_BOOLEAN, 16, NULL, 0x0008, "Specifies if the Frequency Offset field is present", HFILL }}, {&hf_rftap_power_is_in_dbm, { "Power is in dBm Units", "rftap.isdbm", FT_BOOLEAN, 16, NULL, 0x0010, "Specifies if the Power is specified in dBm units", HFILL }}, {&hf_rftap_present_signal_power, { "Signal Power Present", "rftap.present.power", FT_BOOLEAN, 16, NULL, 0x0020, "Specifies if the Signal Power field is present", HFILL }}, {&hf_rftap_present_noise_power, { "Noise Power Present", "rftap.present.noise", FT_BOOLEAN, 16, NULL, 0x0040, "Specifies if the Noise Power field is present", HFILL }}, {&hf_rftap_present_snr, { "SNR Present", "rftap.present.snr", FT_BOOLEAN, 16, NULL, 0x0080, "Specifies if the SNR field is present", HFILL }}, {&hf_rftap_present_signal_quality, { "Signal Quality Present", "rftap.present.qual", FT_BOOLEAN, 16, NULL, 0x0100, "Specifies if the Signal Quality field is present", HFILL }}, {&hf_rftap_time_is_unix_time, { "Time standard is Unix Time", "rftap.isunixtime", FT_BOOLEAN, 16, NULL, 0x0200, "Specifies if the time standard is Unix Time: wikipedia.org/wiki/Unix_time", HFILL }}, {&hf_rftap_present_time, { "Time Present", "rftap.present.time", FT_BOOLEAN, 16, NULL, 0x0400, "Specifies if the Time field is present", HFILL }}, {&hf_rftap_present_duration, { "Duration Present", "rftap.present.duration", FT_BOOLEAN, 16, NULL, 0x0800, "Specifies if the Duration field is present", HFILL }}, {&hf_rftap_present_location, { "Location Present", "rftap.present.location", FT_BOOLEAN, 16, NULL, 0x1000, "Specifies if the Location field is present", HFILL }}, {&hf_rftap_present_reserved_field_13, { "Reserved Field 13 Present", "rftap.present.field13", FT_BOOLEAN, 16, NULL, 0x2000, "Specifies if the Reserved Field 13 is present", HFILL }}, {&hf_rftap_present_reserved_field_14, { "Reserved Field 14 Present", "rftap.present.field14", FT_BOOLEAN, 16, NULL, 0x4000, "Specifies if the Reserved Field 14 is present", HFILL }}, {&hf_rftap_present_reserved_field_15, { "Reserved Field 15 Present", "rftap.present.field15", FT_BOOLEAN, 16, NULL, 0x8000, "Specifies if the Reserved Field 15 is present", HFILL }}, /* rftap parameters */ { &hf_rftap_dlt, { "Data Link Type (DLT)", "rftap.dlt", FT_UINT32, BASE_DEC, NULL, 0, "Data Link Type (DLT) of the encapsulated packet: www.tcpdump.org/linktypes.html", HFILL }}, { &hf_rftap_freq, { "Frequency", "rftap.freq", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_hz, 0, "Actual (measured) carrier frequency, in Hertz (not necessarily center frequency)", HFILL }}, { &hf_rftap_nomfreq, { "Nominal Frequency", "rftap.nomfreq", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_hz, 0, "Nominal carrier frequency, in Hertz (the ideal frequency, ignoring freq errors)", HFILL }}, { &hf_rftap_freqofs, { "Frequency Offset", "rftap.freqofs", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_hz, 0, "Carrier frequency offset, in Hertz: wikipedia.org/wiki/Carrier_frequency_offset", HFILL }}, { &hf_rftap_signal_power, { "Signal Power", "rftap.power", FT_FLOAT, BASE_NONE, NULL, 0, "Signal power, in dB or dBm units: wikipedia.org/wiki/DBm", HFILL }}, { &hf_rftap_noise_power, { "Noise Power", "rftap.noise", FT_FLOAT, BASE_NONE, NULL, 0, "Noise power, in dB or dBm units: wikipedia.org/wiki/DBm", HFILL }}, { &hf_rftap_snr, { "SNR", "rftap.snr", FT_FLOAT, BASE_NONE, NULL, 0, "Signal to Noise ratio (decibel units): wikipedia.org/wiki/Signal-to-noise_ratio", HFILL }}, { &hf_rftap_signal_quality, { "Signal Quality", "rftap.qual", FT_FLOAT, BASE_NONE, NULL, 0, "Signal quality, arbitrary units from 0.0 (worst) to 1.0 (best)", HFILL }}, { &hf_rftap_time_int, { "Time (integer part)", "rftap.timeint", FT_DOUBLE, BASE_NONE, NULL, 0, "The integer part of event time, in seconds, since epoch: wikipedia.org/wiki/Epoch_(reference_date)", HFILL }}, { &hf_rftap_time_frac, { "Time (fractional part)", "rftap.timefrac", FT_DOUBLE, BASE_NONE, NULL, 0, "The fractional part of event time, in seconds, since epoch: wikipedia.org/wiki/Epoch_(reference_date)", HFILL }}, { &hf_rftap_time, { "Time", "rftap.time", FT_DOUBLE, BASE_NONE, NULL, 0, "The event time, in seconds, since epoch: wikipedia.org/wiki/Epoch_(reference_date)", HFILL }}, { &hf_rftap_duration, { "Duration", "rftap.duration", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_second_seconds, 0, "The duration of the event (packet), in seconds", HFILL }}, { &hf_rftap_latitude, { "Latitude", "rftap.lat", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_degree_degrees, 0, "Latitude of receiver (-90..90 degrees), using WGS 84 datum: wikipedia.org/wiki/World_Geodetic_System", HFILL }}, { &hf_rftap_longitude, { "Longitude", "rftap.lon", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_degree_degrees, 0, "Longitude of receiver (-180..180 degrees), using WGS 84 datum: wikipedia.org/wiki/World_Geodetic_System", HFILL }}, { &hf_rftap_altitude, { "Altitude", "rftap.alt", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, &units_meter_meters, 0, "Altitude of receiver, in meters, using WGS 84 datum: wikipedia.org/wiki/World_Geodetic_System", HFILL }}, /* rftap tagged parameters */ { &hf_rftap_subdissector_name, { "Dissector Name", "rftap.dissector", FT_STRING, BASE_NONE, NULL, 0, "Name of sub-dissector used for packet data (alternative to DLT field)", HFILL }} }; /* Register the protocol name and description */ proto_rftap = proto_register_protocol( "RFtap Protocol", /* name */ "RFtap", /* short_name */ "rftap"); /* filter_name (used for display filter string) */ /* Register the header fields and subtrees */ proto_register_field_array(proto_rftap, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); register_dissector("rftap", dissect_rftap, proto_rftap); } /* Protocol registration routine. This function is also called by * Wireshark's preferences manager whenever "Apply" or "OK" are pressed. */ void proto_reg_handoff_rftap(void) { pcap_pktdata_handle = find_dissector_add_dependency("pcap_pktdata", proto_rftap); heur_dissector_add("udp", dissect_rftap_heur, "RFtap over UDP", "rftap", proto_rftap, HEURISTIC_ENABLE); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */