wireshark/epan/dissectors/packet-rftap.c

601 lines
22 KiB
C

/*
* packet-rftap.c
* Decode packets with a RFtap header
* Copyright 2016, Jonathan Brucker <jonathan.brucke@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* 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 <config.h>
#include <epan/packet.h>
#include <epan/prefs.h>
/* 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, 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 const int *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, wmem_packet_scope(), 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, &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:
*/