2 * Dissector routines for the LoRaTap encapsulation
3 * By Erik de Jong <erikdejong@gmail.com>
4 * Copyright 2017 Erik de Jong
6 * LoRaTap encapsulation, version 1
7 * By Ales Povalac <alpov@alpov.net>
8 * Copyright 2022 Ales Povalac
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
14 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <epan/packet.h>
19 #include <epan/capture_dissectors.h>
20 #include <epan/decode_as.h>
21 #include <epan/proto_data.h>
22 #include <epan/to_str.h>
24 #include <epan/unit_strings.h>
26 #include <wsutil/array.h>
29 void proto_reg_handoff_loratap(void);
30 void proto_register_loratap(void);
32 static dissector_handle_t loratap_handle
;
34 static dissector_table_t loratap_dissector_table
;
36 static int proto_loratap
;
37 static int hf_loratap_header_version_type
;
38 static int hf_loratap_header_length_type
;
39 static int hf_loratap_header_padding
;
40 static int hf_loratap_header_channel_type
;
41 static int hf_loratap_header_channel_frequency_type
;
42 static int hf_loratap_header_channel_bandwidth_type
;
43 static int hf_loratap_header_channel_sf_type
;
44 static int hf_loratap_header_rssi_type
;
45 static int hf_loratap_header_rssi_packet_type
;
46 static int hf_loratap_header_rssi_max_type
;
47 static int hf_loratap_header_rssi_current_type
;
48 static int hf_loratap_header_rssi_snr_type
;
49 static int hf_loratap_header_syncword_type
;
50 static int hf_loratap_header_tag_type
;
51 static int hf_loratap_header_payload_type
;
52 static int hf_loratap_header_source_gw_type
;
53 static int hf_loratap_header_timestamp_type
;
54 static int hf_loratap_header_datarate_type
;
55 static int hf_loratap_header_if_channel_type
;
56 static int hf_loratap_header_rf_chain_type
;
57 static int hf_loratap_header_cr_type
;
58 static int hf_loratap_header_flags_type
;
59 static int hf_loratap_header_flags_mod_fsk_type
;
60 static int hf_loratap_header_flags_iq_inverted_type
;
61 static int hf_loratap_header_flags_implicit_hdr_type
;
62 static int hf_loratap_header_flags_crc_type
;
63 static int hf_loratap_header_flags_padding_type
;
65 static int * const hfx_loratap_header_flags
[] = {
66 &hf_loratap_header_flags_mod_fsk_type
,
67 &hf_loratap_header_flags_iq_inverted_type
,
68 &hf_loratap_header_flags_implicit_hdr_type
,
69 &hf_loratap_header_flags_crc_type
,
70 &hf_loratap_header_flags_padding_type
,
74 static int ett_loratap
;
75 static int ett_loratap_flags
;
76 static int ett_loratap_channel
;
77 static int ett_loratap_rssi
;
79 static const value_string channel_bandwidth
[] = {
86 static const value_string syncwords
[] = {
87 { 0x12, "Private LoRa" },
92 static const value_string coding_rates
[] = {
101 static const value_string crc_state
[] = {
109 rssi_base_custom(char *buffer
, uint32_t value
)
112 snprintf(buffer
, ITEM_LABEL_LENGTH
, "N/A");
114 snprintf(buffer
, ITEM_LABEL_LENGTH
, "%.0f dBm", -139. + (float)value
);
119 snr_base_custom(char *buffer
, uint32_t value
)
121 snprintf(buffer
, ITEM_LABEL_LENGTH
, "%.1f dB", ((int8_t)value
) / 4.);
125 loratap_prompt(packet_info
*pinfo
, char* result
)
127 snprintf(result
, MAX_DECODE_AS_PROMPT_LEN
, "LoRaTap syncword 0x%02x as", GPOINTER_TO_UINT(p_get_proto_data(pinfo
->pool
, pinfo
, proto_loratap
, 0)));
131 loratap_value(packet_info
*pinfo
)
133 return p_get_proto_data(pinfo
->pool
, pinfo
, proto_loratap
, 0);
137 dissect_loratap(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree _U_
, void *data _U_
)
139 proto_item
*ti
, *channel_item
, *rssi_item
;
140 proto_tree
*loratap_tree
, *channel_tree
, *rssi_tree
;
141 int32_t current_offset
= 0;
142 int32_t header_v1_offset
= 15;
144 uint32_t lt_length
, lt_version
;
149 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "LoRaTap");
150 col_clear(pinfo
->cinfo
, COL_INFO
);
151 length
= tvb_get_uint16(tvb
, 2, ENC_BIG_ENDIAN
);
153 ti
= proto_tree_add_item(tree
, proto_loratap
, tvb
, 0, length
, ENC_NA
);
154 loratap_tree
= proto_item_add_subtree(ti
, ett_loratap
);
155 proto_tree_add_item_ret_uint(loratap_tree
, hf_loratap_header_version_type
, tvb
, current_offset
, 1, ENC_NA
, <_version
);
157 proto_tree_add_item(loratap_tree
, hf_loratap_header_padding
, tvb
, current_offset
, 1, ENC_NA
);
159 proto_tree_add_item_ret_uint(loratap_tree
, hf_loratap_header_length_type
, tvb
, current_offset
, 2, ENC_NA
, <_length
);
161 if (lt_version
== 1) {
162 proto_tree_add_item(loratap_tree
, hf_loratap_header_source_gw_type
, tvb
, header_v1_offset
, 8, ENC_NA
);
163 set_address_tvb(&pinfo
->dl_src
, AT_EUI64
, 8, tvb
, header_v1_offset
);
164 copy_address_shallow(&pinfo
->src
, &pinfo
->dl_src
);
165 proto_item_append_text(ti
, ", Src: %s", address_to_display(pinfo
->pool
, &pinfo
->src
));
166 header_v1_offset
+= 8;
167 proto_tree_add_item(loratap_tree
, hf_loratap_header_timestamp_type
, tvb
, header_v1_offset
, 4, ENC_NA
);
168 header_v1_offset
+= 4;
169 proto_tree_add_bitmask(loratap_tree
, tvb
, header_v1_offset
, hf_loratap_header_flags_type
, ett_loratap_flags
, hfx_loratap_header_flags
, ENC_NA
);
170 try_dissect
= (tvb_get_uint8(tvb
, header_v1_offset
) & 0x28); /* Only try the next dissector for CRC OK and No CRC packets with v1 encapsulation */
173 try_dissect
= true; /* Always try the next dissector for v0 encapsulation */
176 channel_item
= proto_tree_add_item(loratap_tree
, hf_loratap_header_channel_type
, tvb
, current_offset
, 0, ENC_NA
);
177 channel_tree
= proto_item_add_subtree(channel_item
, ett_loratap_channel
);
178 proto_tree_add_item(channel_tree
, hf_loratap_header_channel_frequency_type
, tvb
, current_offset
, 4, ENC_NA
);
180 proto_tree_add_item(channel_tree
, hf_loratap_header_channel_bandwidth_type
, tvb
, current_offset
, 1, ENC_NA
);
182 proto_tree_add_item(channel_tree
, hf_loratap_header_channel_sf_type
, tvb
, current_offset
, 1, ENC_NA
);
184 if (lt_version
== 1) {
185 proto_tree_add_item(channel_tree
, hf_loratap_header_cr_type
, tvb
, header_v1_offset
, 1, ENC_NA
);
187 proto_tree_add_item(channel_tree
, hf_loratap_header_datarate_type
, tvb
, header_v1_offset
, 2, ENC_NA
);
188 header_v1_offset
+= 2;
189 proto_tree_add_item(channel_tree
, hf_loratap_header_if_channel_type
, tvb
, header_v1_offset
, 1, ENC_NA
);
191 proto_tree_add_item(channel_tree
, hf_loratap_header_rf_chain_type
, tvb
, header_v1_offset
, 1, ENC_NA
);
195 rssi_item
= proto_tree_add_item(loratap_tree
, hf_loratap_header_rssi_type
, tvb
, current_offset
, 0, ENC_NA
);
196 rssi_tree
= proto_item_add_subtree(rssi_item
, ett_loratap_rssi
);
197 proto_tree_add_item(rssi_tree
, hf_loratap_header_rssi_packet_type
, tvb
, current_offset
, 1, ENC_NA
);
199 proto_tree_add_item(rssi_tree
, hf_loratap_header_rssi_max_type
, tvb
, current_offset
, 1, ENC_NA
);
201 proto_tree_add_item(rssi_tree
, hf_loratap_header_rssi_current_type
, tvb
, current_offset
, 1, ENC_NA
);
203 proto_tree_add_item(rssi_tree
, hf_loratap_header_rssi_snr_type
, tvb
, current_offset
, 1, ENC_NA
);
205 proto_tree_add_item_ret_uint(loratap_tree
, hf_loratap_header_syncword_type
, tvb
, current_offset
, 1, ENC_NA
, &syncword
);
207 if (lt_version
== 1) {
208 proto_tree_add_item(loratap_tree
, hf_loratap_header_tag_type
, tvb
, header_v1_offset
, 2, ENC_NA
);
211 /* Seek to data - skip lt_length of header, this allows future extensions */
212 current_offset
= lt_length
;
213 length
= tvb_reported_length_remaining(tvb
, lt_length
);
214 proto_tree_add_bytes_format_value(loratap_tree
, hf_loratap_header_payload_type
, tvb
, current_offset
, length
, NULL
, "%d bytes", length
);
216 p_add_proto_data(pinfo
->pool
, pinfo
, proto_loratap
, 0, GUINT_TO_POINTER((unsigned)syncword
));
217 next_tvb
= tvb_new_subset_length(tvb
, current_offset
, length
);
219 if (!try_dissect
|| !dissector_try_uint_with_data(loratap_dissector_table
, syncword
, next_tvb
, pinfo
, tree
, true, NULL
)) {
220 call_data_dissector(next_tvb
, pinfo
, tree
);
223 return tvb_captured_length(tvb
);
227 proto_reg_handoff_loratap(void)
229 dissector_add_uint("wtap_encap", WTAP_ENCAP_LORATAP
, loratap_handle
);
230 dissector_add_for_decode_as("udp.port", loratap_handle
);
234 proto_register_loratap(void)
236 static hf_register_info hf
[] = {
237 { &hf_loratap_header_version_type
,
238 { "Header Version", "loratap.version",
243 { &hf_loratap_header_length_type
,
244 { "Header Length", "loratap.header_length",
245 FT_UINT16
, BASE_DEC
|BASE_UNIT_STRING
,
246 UNS(&units_byte_bytes
), 0x0,
249 { &hf_loratap_header_padding
,
250 { "Header Padding", "loratap.padding",
255 { &hf_loratap_header_channel_type
,
256 { "Channel", "loratap.channel",
261 { &hf_loratap_header_channel_frequency_type
,
262 { "Frequency", "loratap.channel.frequency",
263 FT_UINT32
, BASE_DEC
|BASE_UNIT_STRING
,
267 { &hf_loratap_header_channel_bandwidth_type
,
268 { "Bandwidth", "loratap.channel.bandwidth",
270 VALS(channel_bandwidth
), 0x0,
273 { &hf_loratap_header_channel_sf_type
,
274 { "Spreading Factor", "loratap.channel.sf",
279 { &hf_loratap_header_rssi_type
,
280 { "RSSI / SNR", "loratap.rssi",
285 { &hf_loratap_header_rssi_packet_type
,
286 { "Packet RSSI", "loratap.rssi.packet",
287 FT_UINT8
, BASE_CUSTOM
,
288 CF_FUNC(rssi_base_custom
), 0x0,
291 { &hf_loratap_header_rssi_max_type
,
292 { "Max RSSI", "loratap.rssi.max",
293 FT_UINT8
, BASE_CUSTOM
,
294 CF_FUNC(rssi_base_custom
), 0x0,
297 { &hf_loratap_header_rssi_current_type
,
298 { "Current RSSI", "loratap.rssi.current",
299 FT_UINT8
, BASE_CUSTOM
,
300 CF_FUNC(rssi_base_custom
), 0x0,
303 { &hf_loratap_header_rssi_snr_type
,
304 { "SNR", "loratap.rssi.snr",
305 FT_UINT8
, BASE_CUSTOM
,
306 CF_FUNC(snr_base_custom
), 0x0,
309 { &hf_loratap_header_syncword_type
,
310 { "Sync Word", "loratap.syncword",
312 VALS(syncwords
), 0x0,
315 { &hf_loratap_header_tag_type
,
316 { "Tag", "loratap.tag",
321 { &hf_loratap_header_payload_type
,
322 { "Payload", "loratap.payload",
327 { &hf_loratap_header_source_gw_type
,
328 { "Source", "loratap.srcgw",
333 { &hf_loratap_header_timestamp_type
,
334 { "Timestamp", "loratap.timestamp",
339 { &hf_loratap_header_datarate_type
,
340 { "FSK datarate", "loratap.channel.datarate",
341 FT_UINT16
, BASE_DEC
|BASE_UNIT_STRING
,
342 UNS(&units_bit_sec
), 0x0,
345 { &hf_loratap_header_if_channel_type
,
346 { "IF channel", "loratap.channel.if_channel",
351 { &hf_loratap_header_rf_chain_type
,
352 { "RF chain", "loratap.channel.rf_chain",
357 { &hf_loratap_header_cr_type
,
358 { "Coding Rate", "loratap.channel.cr",
360 VALS(coding_rates
), 0x0,
363 { &hf_loratap_header_flags_type
,
364 { "Flags", "loratap.flags",
369 { &hf_loratap_header_flags_mod_fsk_type
,
370 { "FSK Modulation", "loratap.flags.mod_fsk",
372 TFS(&tfs_set_notset
), 0x01,
375 { &hf_loratap_header_flags_iq_inverted_type
,
376 { "IQ Inverted", "loratap.flags.iq_inverted",
378 TFS(&tfs_set_notset
), 0x02,
381 { &hf_loratap_header_flags_implicit_hdr_type
,
382 { "Implicit Header", "loratap.flags.implicit_hdr",
384 TFS(&tfs_set_notset
), 0x04,
387 { &hf_loratap_header_flags_crc_type
,
388 { "Checksum", "loratap.flags.crc",
390 VALS(crc_state
), 0x38,
393 { &hf_loratap_header_flags_padding_type
,
394 { "Padding", "loratap.flags.padding",
401 /* Register for decode as */
402 static build_valid_func loratap_da_build_value
[1] = {loratap_value
};
403 static decode_as_value_t loratap_da_values
= {loratap_prompt
, 1, loratap_da_build_value
};
404 static decode_as_t loratap_da
= {"loratap", "loratap.syncword", 1, 0, &loratap_da_values
, NULL
, NULL
, decode_as_default_populate_list
, decode_as_default_reset
, decode_as_default_change
, NULL
};
406 /* Setup protocol subtree array */
407 static int *ett
[] = {
410 &ett_loratap_channel
,
414 proto_loratap
= proto_register_protocol (
415 "LoRaTap header", /* name */
416 "LoRaTap", /* short name */
417 "loratap" /* abbrev */
420 loratap_handle
= register_dissector("loratap", dissect_loratap
, proto_loratap
);
421 proto_register_field_array(proto_loratap
, hf
, array_length(hf
));
422 proto_register_subtree_array(ett
, array_length(ett
));
423 loratap_dissector_table
= register_dissector_table("loratap.syncword", "LoRa Syncword", proto_loratap
, FT_UINT8
, BASE_HEX
);
424 register_decode_as(&loratap_da
);
428 * Editor modelines - https://www.wireshark.org/tools/modelines.html
433 * indent-tabs-mode: t
436 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
437 * :indentSize=8:tabSize=8:noTabs=false: