Coverage Report

Created: 2025-08-04 07:15

/src/wireshark/epan/dissectors/packet-dect-dlc.c
Line
Count
Source (jump to first uncovered line)
1
/* packet-dect-dlc.c
2
 *
3
 * Dissector for the DECT (Digital Enhanced Cordless Telecommunications)
4
 * DLC protocol layer as described in ETSI EN 300 175-4 V2.7.1 (2017-11)
5
 *
6
 * DLC is sometimes also called LAPC, which is a derivative of LAPDm (GSM),
7
 * which is a derivative of LAPD (ISDN).
8
 *
9
 * Copyright 2018 by Harald Welte <laforge@gnumonks.org>
10
 *
11
 * Wireshark - Network traffic analyzer
12
 * By Gerald Combs <gerald@wireshark.org>
13
 * Copyright 1998 Gerald Combs
14
 *
15
 * SPDX-License-Identifier: GPL-2.0-or-later
16
 */
17
18
#include "config.h"
19
#include <epan/conversation.h>
20
#include <epan/packet.h>
21
#include <epan/prefs.h>
22
#include <epan/reassemble.h>
23
#include <epan/xdlc.h>
24
25
void proto_register_dect_dlc(void);
26
27
static int proto_dect_dlc;
28
29
static int hf_dect_dlc_address;
30
static int hf_dect_dlc_nlf;
31
static int hf_dect_dlc_lln;
32
static int hf_dect_dlc_sapi;
33
static int hf_dect_dlc_cr;
34
35
static int hf_dect_dlc_control;
36
static int hf_dect_dlc_n_r;
37
static int hf_dect_dlc_n_s;
38
static int hf_dect_dlc_p;
39
static int hf_dect_dlc_f;
40
static int hf_dect_dlc_s_ftype;
41
static int hf_dect_dlc_u_modifier_cmd;
42
static int hf_dect_dlc_u_modifier_resp;
43
static int hf_dect_dlc_ftype_i;
44
static int hf_dect_dlc_ftype_s_u;
45
46
static int hf_dect_dlc_length;
47
static int hf_dect_dlc_el;
48
static int hf_dect_dlc_m;
49
static int hf_dect_dlc_len;
50
51
static int ett_dect_dlc;
52
static int ett_dect_dlc_address;
53
static int ett_dect_dlc_control;
54
static int ett_dect_dlc_length;
55
56
static dissector_handle_t data_handle;
57
58
static dissector_table_t dlc_sapi_dissector_table;
59
60
static reassembly_table dect_dlc_reassembly_table;
61
62
static int hf_dect_dlc_fragment_data;
63
static int hf_dect_dlc_fragment;
64
static int hf_dect_dlc_fragments;
65
static int hf_dect_dlc_fragment_overlap;
66
static int hf_dect_dlc_fragment_overlap_conflicts;
67
static int hf_dect_dlc_fragment_multiple_tails;
68
static int hf_dect_dlc_fragment_too_long_fragment;
69
static int hf_dect_dlc_fragment_error;
70
static int hf_dect_dlc_fragment_count;
71
static int hf_dect_dlc_reassembled_in;
72
static int hf_dect_dlc_reassembled_length;
73
74
static int ett_dect_dlc_fragment;
75
static int ett_dect_dlc_fragments;
76
77
static const fragment_items dect_dlc_frag_items = {
78
    /* Fragment subtrees */
79
    &ett_dect_dlc_fragment,
80
    &ett_dect_dlc_fragments,
81
    /* Fragment fields */
82
    &hf_dect_dlc_fragments,
83
    &hf_dect_dlc_fragment,
84
    &hf_dect_dlc_fragment_overlap,
85
    &hf_dect_dlc_fragment_overlap_conflicts,
86
    &hf_dect_dlc_fragment_multiple_tails,
87
    &hf_dect_dlc_fragment_too_long_fragment,
88
    &hf_dect_dlc_fragment_error,
89
    &hf_dect_dlc_fragment_count,
90
    /* Reassembled in field */
91
    &hf_dect_dlc_reassembled_in,
92
    /* Reassembled length field */
93
    &hf_dect_dlc_reassembled_length,
94
    /* Reassembled data field */
95
    NULL,
96
    /* Tag */
97
    "fragments"
98
};
99
100
static wmem_map_t *dect_dlc_last_n_s_map;
101
102
2
#define DECT_DLC_M          0x02
103
2
#define DECT_DLC_M_SHIFT    1
104
105
static bool reassemble_dect_dlc = true;
106
107
static const xdlc_cf_items dect_dlc_cf_items = {
108
  &hf_dect_dlc_n_r,
109
  &hf_dect_dlc_n_s,
110
  &hf_dect_dlc_p,
111
  &hf_dect_dlc_f,
112
  &hf_dect_dlc_s_ftype,
113
  &hf_dect_dlc_u_modifier_cmd,
114
  &hf_dect_dlc_u_modifier_resp,
115
  &hf_dect_dlc_ftype_i,
116
  &hf_dect_dlc_ftype_s_u
117
};
118
119
static const value_string dect_dlc_sapi_vals[] = {
120
  { 0, "Connection oriented signalling" },
121
  { 3, "Connectionless signalling" },
122
  { 0, NULL }
123
};
124
125
static const value_string dect_dlc_lln_vals[] = {
126
  { 0, "U0" },
127
  { 1, "A1" },
128
  { 2, "B2" },
129
  { 3, "B3" },
130
  { 4, "B4" },
131
  { 5, "B5" },
132
  { 6, "B6" },
133
  { 7, "unassigned" },
134
  { 0, NULL }
135
};
136
137
static const value_string dect_dlc_m_vals[] = {
138
  { 0, "Last segment" },
139
  { 1, "More segments" },
140
  { 0, NULL }
141
};
142
143
static const value_string dect_dlc_el_vals[] = {
144
  { 0, "More octets" },
145
  { 1, "Final octet" },
146
  { 0, NULL }
147
};
148
149
150
static int dissect_dect_dlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void _U_ *data)
151
14
{
152
14
  proto_tree *dlc_tree, *addr_tree, *length_tree;
153
14
  proto_item *dlc_ti, *addr_ti, *length_ti;
154
14
  bool is_response = false;
155
14
  bool m;
156
14
  int available_length;
157
14
  int control;
158
14
  tvbuff_t *payload;
159
14
  uint8_t cr, sapi, length, len, n_s;
160
161
14
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "DECT-DLC");
162
163
14
  cr = tvb_get_uint8(tvb, 0) & 0x02;
164
14
  if (pinfo->p2p_dir == P2P_DIR_RECV)
165
14
    is_response = cr ? false : true;
166
0
  else if (pinfo->p2p_dir == P2P_DIR_SENT)
167
0
    is_response = cr ? true : false;
168
169
14
  dlc_ti = proto_tree_add_item(tree, proto_dect_dlc, tvb, 0, 3, ENC_NA);
170
14
  dlc_tree = proto_item_add_subtree(dlc_ti, ett_dect_dlc);
171
172
14
  addr_ti = proto_tree_add_item(dlc_tree, hf_dect_dlc_address, tvb, 0, 1, ENC_NA);
173
14
  addr_tree = proto_item_add_subtree(addr_ti, ett_dect_dlc_address);
174
175
14
  sapi = (tvb_get_uint8(tvb, 0) & 0x0C) >> 2;
176
14
  proto_tree_add_item(addr_tree, hf_dect_dlc_nlf, tvb, 0, 1, ENC_NA);
177
14
  proto_tree_add_item(addr_tree, hf_dect_dlc_lln, tvb, 0, 1, ENC_NA);
178
14
  proto_tree_add_item(addr_tree, hf_dect_dlc_sapi, tvb, 0, 1, ENC_NA);
179
14
  proto_tree_add_item(addr_tree, hf_dect_dlc_cr, tvb, 0, 1, ENC_NA);
180
181
14
  control = dissect_xdlc_control(tvb, 1, pinfo, dlc_tree, hf_dect_dlc_control,
182
14
        ett_dect_dlc_control, &dect_dlc_cf_items, NULL, NULL, NULL,
183
14
        is_response, false, false);
184
14
  n_s = (control & XDLC_N_S_MASK) >> XDLC_N_S_SHIFT;
185
186
14
  length_ti = proto_tree_add_item(dlc_tree, hf_dect_dlc_length, tvb, 2, 1, ENC_NA);
187
14
  length_tree = proto_item_add_subtree(length_ti, ett_dect_dlc_length);
188
14
  length = tvb_get_uint8(tvb, 2);
189
14
  proto_tree_add_uint(length_tree, hf_dect_dlc_len, tvb, 2, 1, length);
190
14
  proto_tree_add_uint(length_tree, hf_dect_dlc_m, tvb, 2, 1, length);
191
14
  proto_tree_add_uint(length_tree, hf_dect_dlc_el, tvb, 2, 1, length);
192
14
  len = length >> 2;
193
194
14
  available_length = tvb_captured_length(tvb) - 3;
195
14
  if (available_length > 0) {
196
14
    payload = tvb_new_subset_length_caplen(tvb, 3, MIN(len, available_length), len);
197
198
    /* Potentially segmented I frame */
199
14
    if( (control & XDLC_I_MASK) == XDLC_I && reassemble_dect_dlc && !pinfo->flags.in_error_pkt )
200
2
    {
201
2
      fragment_head *fd_m = NULL;
202
2
      tvbuff_t *reassembled = NULL;
203
2
      uint32_t fragment_id;
204
2
      bool save_fragmented = pinfo->fragmented, add_frag;
205
206
2
      m = (length & DECT_DLC_M) >> DECT_DLC_M_SHIFT;
207
2
      pinfo->fragmented = m;
208
209
2
      fragment_id = (conversation_get_id_from_elements(pinfo, CONVERSATION_NONE, USE_LAST_ENDPOINT) << 3) | ( sapi << 1) | pinfo->p2p_dir;
210
211
2
      if (!PINFO_FD_VISITED(pinfo)) {
212
        /* Check if new N(S) is equal to previous N(S) (to avoid adding retransmissions in reassembly table)
213
        As GUINT_TO_POINTER macro does not allow to differentiate NULL from 0, use 1-8 range instead of 0-7 */
214
2
        unsigned *p_last_n_s = (unsigned*)wmem_map_lookup(dect_dlc_last_n_s_map, GUINT_TO_POINTER(fragment_id));
215
2
        if (GPOINTER_TO_UINT(p_last_n_s) == (unsigned)(n_s+1)) {
216
1
          add_frag = false;
217
1
        } else {
218
1
          add_frag = true;
219
1
          wmem_map_insert(dect_dlc_last_n_s_map, GUINT_TO_POINTER(fragment_id), GUINT_TO_POINTER(n_s+1));
220
1
        }
221
2
      } else {
222
0
        add_frag = true;
223
0
      }
224
225
2
      if (add_frag) {
226
        /* This doesn't seem the best way of doing it as doesn't
227
        take N(S) into account, but N(S) isn't always 0 for
228
        the first fragment! */
229
1
        fd_m = fragment_add_seq_next (&dect_dlc_reassembly_table, payload, 0,
230
1
                      pinfo,
231
1
                      fragment_id, /* uint32_t ID for fragments belonging together */
232
1
                      NULL,
233
                      /*n_s uint32_t fragment sequence number */
234
1
                      len, /* uint32_t fragment length */
235
1
                      m); /* More fragments? */
236
237
1
        reassembled = process_reassembled_data(payload, 0, pinfo,
238
1
                          "Reassembled DLC", fd_m, &dect_dlc_frag_items,
239
1
                          NULL, dlc_tree);
240
241
        /* Reassembled into this packet */
242
1
        if (fd_m && pinfo->num == fd_m->reassembled_in) {
243
1
          if (!dissector_try_uint(dlc_sapi_dissector_table, sapi,
244
1
                      reassembled, pinfo, tree))
245
1
            call_data_dissector(reassembled, pinfo, tree);
246
1
        }
247
0
        else {
248
0
          col_append_str(pinfo->cinfo, COL_INFO, " (Fragment)");
249
0
          proto_tree_add_item(dlc_tree, hf_dect_dlc_fragment_data, payload, 0, -1, ENC_NA);
250
0
        }
251
1
      }
252
253
      /* Now reset fragmentation information in pinfo */
254
2
      pinfo->fragmented = save_fragmented;
255
2
    }
256
12
    else
257
12
    {
258
12
      if (!PINFO_FD_VISITED(pinfo) && ((control & XDLC_S_U_MASK) == XDLC_U) && ((control & XDLC_U_MODIFIER_MASK) == XDLC_SABM)) {
259
        /* SABM frame; reset the last N(S) to an invalid value */
260
0
        uint32_t fragment_id = (conversation_get_id_from_elements(pinfo, CONVERSATION_GSMTAP, USE_LAST_ENDPOINT) << 3) | (sapi << 1) | pinfo->p2p_dir;
261
0
        wmem_map_insert(dect_dlc_last_n_s_map, GUINT_TO_POINTER(fragment_id), GUINT_TO_POINTER(0));
262
0
      }
263
12
      if (!dissector_try_uint(dlc_sapi_dissector_table, sapi, payload, pinfo, tree))
264
1
        call_data_dissector(payload, pinfo, tree);
265
12
    }
266
14
  }
267
268
14
  return tvb_captured_length(tvb);
269
14
}
270
271
void proto_register_dect_dlc(void)
272
14
{
273
14
  static hf_register_info hf[] =
274
14
  {
275
14
    { &hf_dect_dlc_address,
276
14
      { "Address Field", "dect_dlc.address_field", FT_UINT8, BASE_HEX,
277
14
         NULL, 0x0, NULL, HFILL
278
14
      }
279
14
    },
280
14
    { &hf_dect_dlc_nlf,
281
14
      { "NLF", "dect_dlc.nlf", FT_UINT8, BASE_DEC,
282
14
        NULL, 0x80, "New Link Flag", HFILL
283
14
      }
284
14
    },
285
14
    { &hf_dect_dlc_lln,
286
14
      { "LLN", "dect_dlc.lln", FT_UINT8, BASE_DEC,
287
14
        VALS(dect_dlc_lln_vals), 0x70, "Logical Link Number", HFILL
288
14
      }
289
14
    },
290
14
    { &hf_dect_dlc_sapi,
291
14
      { "SAPI", "dect_dlc.sapi", FT_UINT8, BASE_DEC,
292
14
        VALS(dect_dlc_sapi_vals), 0x0C, "Service Access Point Identifier", HFILL
293
14
      }
294
14
    },
295
14
    { &hf_dect_dlc_cr,
296
14
      { "C/R", "dect_dlc.cr", FT_UINT8, BASE_DEC,
297
14
        NULL, 0x02, "Command/Response field bit", HFILL
298
14
      }
299
14
    },
300
14
    { &hf_dect_dlc_control,
301
14
      { "Control Field", "dect_dlc.control_field", FT_UINT8, BASE_HEX,
302
14
        NULL, 0x0, NULL, HFILL
303
14
      }
304
14
    },
305
14
    { &hf_dect_dlc_n_r,
306
14
      { "N(R)", "dect_dlc.control.n_r", FT_UINT8, BASE_DEC,
307
14
        NULL, 0xE0, NULL, HFILL
308
14
      }
309
14
    },
310
14
    { &hf_dect_dlc_n_s,
311
14
      { "N(S)", "dect_dlc.control.n_s", FT_UINT8, BASE_DEC,
312
14
        NULL, 0x0E, NULL, HFILL
313
14
      }
314
14
    },
315
14
    { &hf_dect_dlc_p,
316
14
      { "Poll", "dect_dlc.control.p", FT_BOOLEAN, 8,
317
14
        NULL, 0x10, NULL, HFILL
318
14
      }
319
14
    },
320
14
    { &hf_dect_dlc_f,
321
14
      { "Final", "dect_dlc.control.f", FT_BOOLEAN, 8,
322
14
        NULL, 0x10, NULL, HFILL
323
14
      }
324
14
    },
325
14
    { &hf_dect_dlc_s_ftype,
326
14
      { "Supervisory frame type", "dect_dlc.control.s_ftype", FT_UINT8, BASE_HEX,
327
14
        VALS(stype_vals), XDLC_S_FTYPE_MASK, NULL, HFILL
328
14
      }
329
14
    },
330
14
    { &hf_dect_dlc_u_modifier_cmd,
331
14
      { "Command", "dect_dlc.control.u_modifier_cmd", FT_UINT8, BASE_HEX,
332
14
        VALS(modifier_vals_cmd), XDLC_U_MODIFIER_MASK, NULL, HFILL
333
14
      }
334
14
    },
335
14
    { &hf_dect_dlc_u_modifier_resp,
336
14
      { "Response", "dect_dlc.control.u_modifier_resp", FT_UINT8, BASE_HEX,
337
14
        VALS(modifier_vals_resp), XDLC_U_MODIFIER_MASK, NULL, HFILL
338
14
      }
339
14
    },
340
14
    { &hf_dect_dlc_ftype_i,
341
14
      { "Frame type", "dect_dlc.control.ftype", FT_UINT8, BASE_HEX,
342
14
        VALS(ftype_vals), XDLC_I_MASK, NULL, HFILL
343
14
      }
344
14
    },
345
14
    { &hf_dect_dlc_ftype_s_u,
346
14
      { "Frame type", "dect_dlc.control.ftype", FT_UINT8, BASE_HEX,
347
14
        VALS(ftype_vals), XDLC_S_U_MASK, NULL, HFILL
348
14
      }
349
14
    },
350
14
    { &hf_dect_dlc_length,
351
14
      { "Length Field", "dect_dlc.length_field", FT_UINT8, BASE_HEX,
352
14
        NULL, 0x0, NULL, HFILL
353
14
      }
354
14
    },
355
14
    { &hf_dect_dlc_el,
356
14
      { "EL", "dect_dlc.el", FT_UINT8, BASE_DEC,
357
14
        VALS(dect_dlc_el_vals), 0x01, "Length indicator field extension bit", HFILL
358
14
      }
359
14
    },
360
14
    { &hf_dect_dlc_m,
361
14
      { "M", "dect_dlc.m", FT_UINT8, BASE_DEC,
362
14
        VALS(dect_dlc_m_vals), 0x02, "More data bit", HFILL
363
14
      }
364
14
    },
365
14
    { &hf_dect_dlc_len,
366
14
      { "Length", "dect_dlc.length", FT_UINT8, BASE_DEC,
367
14
        NULL, 0xFC, "Length indicator", HFILL
368
14
      }
369
14
    },
370
371
    /* Fragment reassembly */
372
14
    { &hf_dect_dlc_fragment_data,
373
14
      { "Fragment Data", "dect_dlc.fragment_data", FT_NONE, BASE_NONE,
374
14
        NULL, 0x00, NULL, HFILL
375
14
      }
376
14
    },
377
14
    { &hf_dect_dlc_fragments,
378
14
      { "Message fragments", "dect_dlc.fragments",
379
14
        FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL
380
14
      }
381
14
    },
382
14
    { &hf_dect_dlc_fragment,
383
14
      { "Message fragment", "dect_dlc.fragment",
384
14
        FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL
385
14
      }
386
14
    },
387
14
    { &hf_dect_dlc_fragment_overlap,
388
14
      { "Message fragment overlap", "dect_dlc.fragment.overlap",
389
14
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL
390
14
      }
391
14
    },
392
14
    { &hf_dect_dlc_fragment_overlap_conflicts,
393
14
      { "Message fragment overlapping with conflicting data",
394
14
        "dect_dlc.fragment.overlap.conflicts",
395
14
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL
396
14
      }
397
14
    },
398
14
    { &hf_dect_dlc_fragment_multiple_tails,
399
14
      { "Message has multiple tail fragments",
400
14
        "dect_dlc.fragment.multiple_tails",
401
14
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL
402
14
      }
403
14
    },
404
14
    { &hf_dect_dlc_fragment_too_long_fragment,
405
14
      { "Message fragment too long", "dect_dlc.fragment.too_long_fragment",
406
14
        FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL
407
14
      }
408
14
    },
409
14
    { &hf_dect_dlc_fragment_error,
410
14
      { "Message defragmentation error", "dect_dlc.fragment.error",
411
14
        FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL
412
14
      }
413
14
    },
414
14
    { &hf_dect_dlc_fragment_count,
415
14
      { "Message fragment count", "dect_dlc.fragment.count",
416
14
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL
417
14
      }
418
14
    },
419
14
    { &hf_dect_dlc_reassembled_in,
420
14
      { "Reassembled in", "dect_dlc.reassembled.in",
421
14
        FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL
422
14
      }
423
14
    },
424
14
    { &hf_dect_dlc_reassembled_length,
425
14
      { "Reassembled length", "dect_dlc.reassembled.length",
426
14
        FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL
427
14
      }
428
14
    },
429
14
  };
430
431
14
  static int *ett[] = {
432
14
    &ett_dect_dlc,
433
14
    &ett_dect_dlc_address,
434
14
    &ett_dect_dlc_control,
435
14
    &ett_dect_dlc_length,
436
14
    &ett_dect_dlc_fragment,
437
14
    &ett_dect_dlc_fragments,
438
14
  };
439
440
  /* Register protocol */
441
14
  proto_dect_dlc = proto_register_protocol("DECT DLC (LAPC)", "DECT-DLC", "dect_dlc");
442
443
14
  proto_register_subtree_array(ett, array_length(ett));
444
14
  proto_register_field_array(proto_dect_dlc, hf, array_length(hf));
445
446
14
  register_dissector("dect_dlc", dissect_dect_dlc, proto_dect_dlc);
447
448
14
  dlc_sapi_dissector_table = register_dissector_table("dect_dlc.sapi", "DECT DLC SAPI", proto_dect_dlc, FT_UINT8, BASE_DEC);
449
450
14
  data_handle = find_dissector("data");
451
452
14
  reassembly_table_register(&dect_dlc_reassembly_table,
453
14
                           &addresses_reassembly_table_functions);
454
14
  dect_dlc_last_n_s_map = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal);
455
14
}
456
457
/*
458
 * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
459
 *
460
 * Local variables:
461
 * c-basic-offset: 8
462
 * tab-width: 8
463
 * indent-tabs-mode: t
464
 * End:
465
 *
466
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
467
 * :indentSize=8:tabSize=8:noTabs=false:
468
 */