Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-loratap.c
Line
Count
Source
1
/* packet-loratap.c
2
 * Dissector routines for the LoRaTap encapsulation
3
 * By Erik de Jong <erikdejong@gmail.com>
4
 * Copyright 2017 Erik de Jong
5
 *
6
 * LoRaTap encapsulation, version 1
7
 * By Ales Povalac <alpov@alpov.net>
8
 * Copyright 2022 Ales Povalac
9
 *
10
 * Wireshark - Network traffic analyzer
11
 * By Gerald Combs <gerald@wireshark.org>
12
 * Copyright 1998 Gerald Combs
13
 *
14
 * SPDX-License-Identifier: GPL-2.0-or-later
15
 */
16
17
#include "config.h"
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>
23
#include <epan/tfs.h>
24
#include <epan/unit_strings.h>
25
26
#include <wsutil/array.h>
27
28
29
void proto_reg_handoff_loratap(void);
30
void proto_register_loratap(void);
31
32
static dissector_handle_t loratap_handle;
33
34
static dissector_table_t loratap_dissector_table;
35
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;
64
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,
71
  NULL
72
};
73
74
static int ett_loratap;
75
static int ett_loratap_flags;
76
static int ett_loratap_channel;
77
static int ett_loratap_rssi;
78
79
static const value_string channel_bandwidth[] = {
80
  { 1, "125 kHz" },
81
  { 2, "250 kHz" },
82
  { 4, "500 kHz" },
83
  { 0, NULL}
84
};
85
86
static const value_string syncwords[] = {
87
  { 0x12, "Private LoRa" },
88
  { 0x34, "LoRaWAN" },
89
  { 0, NULL}
90
};
91
92
static const value_string coding_rates[] = {
93
  { 0, "none" },
94
  { 5, "4/5" },
95
  { 6, "4/6" },
96
  { 7, "4/7" },
97
  { 8, "4/8" },
98
  { 0, NULL}
99
};
100
101
static const value_string crc_state[] = {
102
  { 1, "CRC OK" },
103
  { 2, "CRC Bad" },
104
  { 4, "No CRC" },
105
  { 0, NULL}
106
};
107
108
static void
109
rssi_base_custom(char *buffer, uint32_t value)
110
0
{
111
0
  if (value == 255) {
112
0
    snprintf(buffer, ITEM_LABEL_LENGTH, "N/A");
113
0
  } else {
114
0
    snprintf(buffer, ITEM_LABEL_LENGTH, "%.0f dBm", -139. + (float)value);
115
0
  }
116
0
}
117
118
static void
119
snr_base_custom(char *buffer, uint32_t value)
120
0
{
121
0
  snprintf(buffer, ITEM_LABEL_LENGTH, "%.1f dB", ((int8_t)value) / 4.);
122
0
}
123
124
static void
125
loratap_prompt(packet_info *pinfo, char* result)
126
0
{
127
0
  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)));
128
0
}
129
130
static void *
131
loratap_value(packet_info *pinfo)
132
0
{
133
0
  return p_get_proto_data(pinfo->pool, pinfo, proto_loratap, 0);
134
0
}
135
136
static int
137
dissect_loratap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
138
0
{
139
0
  proto_item *ti, *channel_item, *rssi_item;
140
0
  proto_tree *loratap_tree, *channel_tree, *rssi_tree;
141
0
  int32_t current_offset = 0;
142
0
  int32_t header_v1_offset = 15;
143
0
  uint16_t length;
144
0
  uint32_t lt_length, lt_version;
145
0
  bool try_dissect;
146
0
  uint32_t syncword;
147
0
  tvbuff_t *next_tvb;
148
149
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "LoRaTap");
150
0
  col_clear(pinfo->cinfo, COL_INFO);
151
0
  length = tvb_get_uint16(tvb, 2, ENC_BIG_ENDIAN);
152
153
0
  ti = proto_tree_add_item(tree, proto_loratap, tvb, 0, length, ENC_NA);
154
0
  loratap_tree = proto_item_add_subtree(ti, ett_loratap);
155
0
  proto_tree_add_item_ret_uint(loratap_tree, hf_loratap_header_version_type, tvb, current_offset, 1, ENC_NA, &lt_version);
156
0
  current_offset++;
157
0
  proto_tree_add_item(loratap_tree, hf_loratap_header_padding, tvb, current_offset, 1, ENC_NA);
158
0
  current_offset++;
159
0
  proto_tree_add_item_ret_uint(loratap_tree, hf_loratap_header_length_type, tvb, current_offset, 2, ENC_BIG_ENDIAN, &lt_length);
160
0
  current_offset += 2;
161
0
  if (lt_version == 1) {
162
0
    proto_tree_add_item(loratap_tree, hf_loratap_header_source_gw_type, tvb, header_v1_offset, 8, ENC_NA);
163
0
    set_address_tvb(&pinfo->dl_src, AT_EUI64, 8, tvb, header_v1_offset);
164
0
    copy_address_shallow(&pinfo->src, &pinfo->dl_src);
165
0
    proto_item_append_text(ti, ", Src: %s", address_to_display(pinfo->pool, &pinfo->src));
166
0
    header_v1_offset += 8;
167
0
    proto_tree_add_item(loratap_tree, hf_loratap_header_timestamp_type, tvb, header_v1_offset, 4, ENC_BIG_ENDIAN);
168
0
    header_v1_offset += 4;
169
0
    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
0
    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 */
171
0
    header_v1_offset++;
172
0
  } else {
173
0
    try_dissect = true; /* Always try the next dissector for v0 encapsulation */
174
0
  }
175
176
0
  channel_item = proto_tree_add_item(loratap_tree, hf_loratap_header_channel_type, tvb, current_offset, 0, ENC_NA);
177
0
  channel_tree = proto_item_add_subtree(channel_item, ett_loratap_channel);
178
0
  proto_tree_add_item(channel_tree, hf_loratap_header_channel_frequency_type, tvb, current_offset, 4, ENC_BIG_ENDIAN);
179
0
  current_offset += 4;
180
0
  proto_tree_add_item(channel_tree, hf_loratap_header_channel_bandwidth_type, tvb, current_offset, 1, ENC_NA);
181
0
  current_offset++;
182
0
  proto_tree_add_item(channel_tree, hf_loratap_header_channel_sf_type, tvb, current_offset, 1, ENC_NA);
183
0
  current_offset++;
184
0
  if (lt_version == 1) {
185
0
    proto_tree_add_item(channel_tree, hf_loratap_header_cr_type, tvb, header_v1_offset, 1, ENC_NA);
186
0
    header_v1_offset++;
187
0
    proto_tree_add_item(channel_tree, hf_loratap_header_datarate_type, tvb, header_v1_offset, 2, ENC_BIG_ENDIAN);
188
0
    header_v1_offset += 2;
189
0
    proto_tree_add_item(channel_tree, hf_loratap_header_if_channel_type, tvb, header_v1_offset, 1, ENC_NA);
190
0
    header_v1_offset++;
191
0
    proto_tree_add_item(channel_tree, hf_loratap_header_rf_chain_type, tvb, header_v1_offset, 1, ENC_NA);
192
0
    header_v1_offset++;
193
0
  }
194
195
0
  rssi_item = proto_tree_add_item(loratap_tree, hf_loratap_header_rssi_type, tvb, current_offset, 0, ENC_NA);
196
0
  rssi_tree = proto_item_add_subtree(rssi_item, ett_loratap_rssi);
197
0
  proto_tree_add_item(rssi_tree, hf_loratap_header_rssi_packet_type, tvb, current_offset, 1, ENC_NA);
198
0
  current_offset++;
199
0
  proto_tree_add_item(rssi_tree, hf_loratap_header_rssi_max_type, tvb, current_offset, 1, ENC_NA);
200
0
  current_offset++;
201
0
  proto_tree_add_item(rssi_tree, hf_loratap_header_rssi_current_type, tvb, current_offset, 1, ENC_NA);
202
0
  current_offset++;
203
0
  proto_tree_add_item(rssi_tree, hf_loratap_header_rssi_snr_type, tvb, current_offset, 1, ENC_NA);
204
0
  current_offset++;
205
0
  proto_tree_add_item_ret_uint(loratap_tree, hf_loratap_header_syncword_type, tvb, current_offset, 1, ENC_NA, &syncword);
206
0
  current_offset++;
207
0
  if (lt_version == 1) {
208
0
    proto_tree_add_item(loratap_tree, hf_loratap_header_tag_type, tvb, header_v1_offset, 2, ENC_BIG_ENDIAN);
209
0
  }
210
211
  /* Seek to data - skip lt_length of header, this allows future extensions */
212
0
  current_offset = lt_length;
213
0
  length = tvb_reported_length_remaining(tvb, lt_length);
214
0
  proto_tree_add_bytes_format_value(loratap_tree, hf_loratap_header_payload_type, tvb, current_offset, length, NULL, "%d bytes", length);
215
216
0
  p_add_proto_data(pinfo->pool, pinfo, proto_loratap, 0, GUINT_TO_POINTER((unsigned)syncword));
217
0
  next_tvb = tvb_new_subset_length(tvb, current_offset, length);
218
219
0
  if (!try_dissect || !dissector_try_uint_with_data(loratap_dissector_table, syncword, next_tvb, pinfo, tree, true, NULL)) {
220
0
    call_data_dissector(next_tvb, pinfo, tree);
221
0
  }
222
223
0
  return tvb_captured_length(tvb);
224
0
}
225
226
void
227
proto_reg_handoff_loratap(void)
228
16
{
229
16
  dissector_add_uint("wtap_encap", WTAP_ENCAP_LORATAP, loratap_handle);
230
16
  dissector_add_for_decode_as("udp.port", loratap_handle);
231
16
}
232
233
void
234
proto_register_loratap(void)
235
16
{
236
16
  static hf_register_info hf[] = {
237
16
  { &hf_loratap_header_version_type,
238
16
    { "Header Version", "loratap.version",
239
16
    FT_UINT8, BASE_DEC,
240
16
    NULL, 0x0,
241
16
    NULL, HFILL }
242
16
  },
243
16
  { &hf_loratap_header_length_type,
244
16
    { "Header Length", "loratap.header_length",
245
16
    FT_UINT16, BASE_DEC|BASE_UNIT_STRING,
246
16
    UNS(&units_byte_bytes), 0x0,
247
16
    NULL, HFILL }
248
16
  },
249
16
  { &hf_loratap_header_padding,
250
16
    { "Header Padding", "loratap.padding",
251
16
    FT_BYTES, BASE_NONE,
252
16
    NULL, 0x0,
253
16
    NULL, HFILL }
254
16
  },
255
16
  { &hf_loratap_header_channel_type,
256
16
    { "Channel", "loratap.channel",
257
16
    FT_NONE, BASE_NONE,
258
16
    NULL, 0x0,
259
16
    NULL, HFILL }
260
16
  },
261
16
  { &hf_loratap_header_channel_frequency_type,
262
16
    { "Frequency", "loratap.channel.frequency",
263
16
    FT_UINT32, BASE_DEC|BASE_UNIT_STRING,
264
16
    UNS(&units_hz), 0x0,
265
16
    NULL, HFILL }
266
16
  },
267
16
  { &hf_loratap_header_channel_bandwidth_type,
268
16
    { "Bandwidth", "loratap.channel.bandwidth",
269
16
    FT_UINT8, BASE_DEC,
270
16
    VALS(channel_bandwidth), 0x0,
271
16
    NULL, HFILL }
272
16
  },
273
16
  { &hf_loratap_header_channel_sf_type,
274
16
    { "Spreading Factor", "loratap.channel.sf",
275
16
    FT_UINT8, BASE_DEC,
276
16
    NULL, 0x0,
277
16
    NULL, HFILL }
278
16
  },
279
16
  { &hf_loratap_header_rssi_type,
280
16
    { "RSSI / SNR", "loratap.rssi",
281
16
    FT_NONE, BASE_NONE,
282
16
    NULL, 0x0,
283
16
    NULL, HFILL }
284
16
  },
285
16
  { &hf_loratap_header_rssi_packet_type,
286
16
    { "Packet RSSI", "loratap.rssi.packet",
287
16
    FT_UINT8, BASE_CUSTOM,
288
16
    CF_FUNC(rssi_base_custom), 0x0,
289
16
    NULL, HFILL }
290
16
  },
291
16
  { &hf_loratap_header_rssi_max_type,
292
16
    { "Max RSSI", "loratap.rssi.max",
293
16
    FT_UINT8, BASE_CUSTOM,
294
16
    CF_FUNC(rssi_base_custom), 0x0,
295
16
    NULL, HFILL }
296
16
  },
297
16
  { &hf_loratap_header_rssi_current_type,
298
16
    { "Current RSSI", "loratap.rssi.current",
299
16
    FT_UINT8, BASE_CUSTOM,
300
16
    CF_FUNC(rssi_base_custom), 0x0,
301
16
    NULL, HFILL }
302
16
  },
303
16
  { &hf_loratap_header_rssi_snr_type,
304
16
    { "SNR", "loratap.rssi.snr",
305
16
    FT_UINT8, BASE_CUSTOM,
306
16
    CF_FUNC(snr_base_custom), 0x0,
307
16
    NULL, HFILL }
308
16
  },
309
16
  { &hf_loratap_header_syncword_type,
310
16
    { "Sync Word", "loratap.syncword",
311
16
    FT_UINT8, BASE_HEX,
312
16
    VALS(syncwords), 0x0,
313
16
    NULL, HFILL }
314
16
  },
315
16
  { &hf_loratap_header_tag_type,
316
16
    { "Tag", "loratap.tag",
317
16
    FT_UINT16, BASE_DEC,
318
16
    NULL, 0x0,
319
16
    NULL, HFILL }
320
16
  },
321
16
  { &hf_loratap_header_payload_type,
322
16
    { "Payload", "loratap.payload",
323
16
    FT_BYTES, BASE_NONE,
324
16
    NULL, 0x0,
325
16
    NULL, HFILL }
326
16
  },
327
16
  { &hf_loratap_header_source_gw_type,
328
16
    { "Source", "loratap.srcgw",
329
16
    FT_BYTES, BASE_NONE,
330
16
    NULL, 0x0,
331
16
    NULL, HFILL }
332
16
  },
333
16
  { &hf_loratap_header_timestamp_type,
334
16
    { "Timestamp", "loratap.timestamp",
335
16
    FT_UINT32, BASE_DEC,
336
16
    NULL, 0x0,
337
16
    NULL, HFILL }
338
16
  },
339
16
  { &hf_loratap_header_datarate_type,
340
16
    { "FSK datarate", "loratap.channel.datarate",
341
16
    FT_UINT16, BASE_DEC|BASE_UNIT_STRING,
342
16
    UNS(&units_bit_sec), 0x0,
343
16
    NULL, HFILL }
344
16
  },
345
16
  { &hf_loratap_header_if_channel_type,
346
16
    { "IF channel", "loratap.channel.if_channel",
347
16
    FT_UINT8, BASE_DEC,
348
16
    NULL, 0x0,
349
16
    NULL, HFILL }
350
16
  },
351
16
  { &hf_loratap_header_rf_chain_type,
352
16
    { "RF chain", "loratap.channel.rf_chain",
353
16
    FT_UINT8, BASE_DEC,
354
16
    NULL, 0x0,
355
16
    NULL, HFILL }
356
16
  },
357
16
  { &hf_loratap_header_cr_type,
358
16
    { "Coding Rate", "loratap.channel.cr",
359
16
    FT_UINT8, BASE_DEC,
360
16
    VALS(coding_rates), 0x0,
361
16
    NULL, HFILL }
362
16
  },
363
16
  { &hf_loratap_header_flags_type,
364
16
    { "Flags", "loratap.flags",
365
16
    FT_UINT8, BASE_HEX,
366
16
    NULL, 0x0,
367
16
    NULL, HFILL }
368
16
  },
369
16
  { &hf_loratap_header_flags_mod_fsk_type,
370
16
    { "FSK Modulation", "loratap.flags.mod_fsk",
371
16
    FT_BOOLEAN, 8,
372
16
    TFS(&tfs_set_notset), 0x01,
373
16
    NULL, HFILL }
374
16
  },
375
16
  { &hf_loratap_header_flags_iq_inverted_type,
376
16
    { "IQ Inverted", "loratap.flags.iq_inverted",
377
16
    FT_BOOLEAN, 8,
378
16
    TFS(&tfs_set_notset), 0x02,
379
16
    NULL, HFILL }
380
16
  },
381
16
  { &hf_loratap_header_flags_implicit_hdr_type,
382
16
    { "Implicit Header", "loratap.flags.implicit_hdr",
383
16
    FT_BOOLEAN, 8,
384
16
    TFS(&tfs_set_notset), 0x04,
385
16
    NULL, HFILL }
386
16
  },
387
16
  { &hf_loratap_header_flags_crc_type,
388
16
    { "Checksum", "loratap.flags.crc",
389
16
    FT_UINT8, BASE_HEX,
390
16
    VALS(crc_state), 0x38,
391
16
    NULL, HFILL }
392
16
  },
393
16
  { &hf_loratap_header_flags_padding_type,
394
16
    { "Padding", "loratap.flags.padding",
395
16
    FT_UINT8, BASE_DEC,
396
16
    NULL, 0xC0,
397
16
    NULL, HFILL }
398
16
  },
399
16
  };
400
401
  /* Register for decode as */
402
16
  static build_valid_func loratap_da_build_value[1] = {loratap_value};
403
16
  static decode_as_value_t loratap_da_values = {loratap_prompt, 1, loratap_da_build_value};
404
16
  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, NULL, NULL };
405
406
  /* Setup protocol subtree array */
407
16
  static int *ett[] = {
408
16
    &ett_loratap,
409
16
    &ett_loratap_flags,
410
16
    &ett_loratap_channel,
411
16
    &ett_loratap_rssi
412
16
  };
413
414
16
  proto_loratap = proto_register_protocol ("LoRaTap header", "LoRaTap", "loratap");
415
416
16
  loratap_handle = register_dissector("loratap", dissect_loratap, proto_loratap);
417
16
  proto_register_field_array(proto_loratap, hf, array_length(hf));
418
16
  proto_register_subtree_array(ett, array_length(ett));
419
16
  loratap_dissector_table = register_dissector_table("loratap.syncword", "LoRa Syncword", proto_loratap, FT_UINT8, BASE_HEX);
420
16
  register_decode_as(&loratap_da);
421
16
}
422
423
/*
424
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
425
 *
426
 * Local variables:
427
 * c-basic-offset: 8
428
 * tab-width: 8
429
 * indent-tabs-mode: t
430
 * End:
431
 *
432
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
433
 * :indentSize=8:tabSize=8:noTabs=false:
434
 */