Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-ehdlc.c
Line
Count
Source
1
/* packet-ehdlc.c
2
 * Routines for packet dissection of Ericsson HDLC as used in A-bis over IP
3
 * Copyright 2010-2012, 2016 by Harald Welte <laforge@gnumonks.org>
4
 *
5
 * This code is based on pure educational guesses while looking at protocol
6
 * traces, as there is no publicly available protocol description by Ericsson.
7
 * Even the name is a guess, since it looks quite a bit like HDLC and is used
8
 * by Ericsson, I called it EHDLC.
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
19
#include <epan/packet.h>
20
#include <epan/tfs.h>
21
#include "packet-xdlc.h"
22
23
void proto_register_ehdlc(void);
24
void proto_reg_handoff_ehdlc(void);
25
26
/* Initialize the protocol and registered fields */
27
static int proto_ehdlc;
28
29
static int hf_ehdlc_data_len;
30
static int hf_ehdlc_csapi;
31
static int hf_ehdlc_ctei;
32
33
static int hf_ehdlc_sapi;
34
static int hf_ehdlc_tei;
35
static int hf_ehdlc_c_r;
36
37
static int hf_ehdlc_xid_payload;
38
static int hf_ehdlc_xid_win_tx;
39
static int hf_ehdlc_xid_win_rx;
40
static int hf_ehdlc_xid_ack_tmr_ms;
41
static int hf_ehdlc_xid_format_id;
42
static int hf_ehdlc_xid_group_id;
43
static int hf_ehdlc_xid_len;
44
static int hf_ehdlc_control;
45
46
static int hf_ehdlc_p;
47
static int hf_ehdlc_f;
48
static int hf_ehdlc_u_modifier_cmd;
49
static int hf_ehdlc_u_modifier_resp;
50
static int hf_ehdlc_ftype_s_u;
51
52
static int hf_ehdlc_n_r;
53
static int hf_ehdlc_n_s;
54
static int hf_ehdlc_p_ext;
55
static int hf_ehdlc_f_ext;
56
static int hf_ehdlc_s_ftype;
57
static int hf_ehdlc_ftype_i;
58
static int hf_ehdlc_ftype_s_u_ext;
59
60
static dissector_handle_t ehdlc_handle;
61
62
/* Used only for U frames */
63
static const xdlc_cf_items ehdlc_cf_items = {
64
  NULL,
65
  NULL,
66
  &hf_ehdlc_p,
67
  &hf_ehdlc_f,
68
  NULL,
69
  &hf_ehdlc_u_modifier_cmd,
70
  &hf_ehdlc_u_modifier_resp,
71
  NULL,
72
  &hf_ehdlc_ftype_s_u
73
};
74
75
/* Used only for I and S frames */
76
static const xdlc_cf_items ehdlc_cf_items_ext = {
77
  &hf_ehdlc_n_r,
78
  &hf_ehdlc_n_s,
79
  &hf_ehdlc_p_ext,
80
  &hf_ehdlc_f_ext,
81
  &hf_ehdlc_s_ftype,
82
  NULL,
83
  NULL,
84
  &hf_ehdlc_ftype_i,
85
  &hf_ehdlc_ftype_s_u_ext,
86
};
87
88
/* Initialize the subtree pointers */
89
static int ett_ehdlc;
90
static int ett_ehdlc_xid;
91
static int ett_ehdlc_control;
92
93
enum {
94
  SUB_RSL,
95
  SUB_OML,
96
  SUB_TFP,
97
  SUB_PGSL,
98
  SUB_DATA,
99
100
  SUB_MAX
101
};
102
103
/* Determine TEI from Compressed TEI */
104
static uint8_t tei_from_ctei(uint8_t ctei)
105
0
{
106
0
  if (ctei < 12)
107
0
    return ctei;
108
0
  else
109
0
    return 60 + (ctei - 12);
110
0
}
111
112
static uint8_t c_r_from_csapi(uint8_t csapi)
113
0
{
114
0
  switch (csapi) {
115
0
  case 1:
116
0
  case 6:
117
0
    return 1;
118
0
  default:
119
0
    return 0;
120
0
  }
121
0
}
122
123
static uint8_t sapi_from_csapi(uint8_t csapi)
124
0
{
125
0
  switch (csapi) {
126
0
  case 0:
127
0
  case 1: /* RSL */
128
0
    return 0;
129
0
  case 2: /* TFP */
130
0
    return 10;
131
0
  case 3: /* TFP */
132
0
    return 11;
133
0
  case 4: /* P-GSL */
134
0
    return 12;
135
0
  case 5:
136
0
  case 6: /* OML */
137
0
    return 62;
138
0
  case 7:
139
0
  default:
140
    /* error! */
141
0
    return 0;
142
0
  }
143
0
}
144
145
static dissector_handle_t sub_handles[SUB_MAX];
146
147
static int
148
dissect_ehdlc_xid(proto_tree *tree, tvbuff_t *tvb, unsigned base_offset, unsigned len)
149
0
{
150
0
  unsigned offset = base_offset;
151
0
  proto_item *ti;
152
0
  proto_tree *xid_tree;
153
154
  /* XID is formatted like ISO 8885, typically we see
155
   * something like
156
   * 82   format identifier
157
   * 80   group identifier
158
   * 00 09  length
159
   * 07 01 05   Window Size Tx
160
   * 09 01 04 Ack Timer (msec)
161
   * 08 01 05 Window Size Rx */
162
0
  ti = proto_tree_add_item(tree, hf_ehdlc_xid_payload,
163
0
         tvb, offset, len, ENC_NA);
164
0
  xid_tree = proto_item_add_subtree(ti, ett_ehdlc_xid);
165
166
0
  proto_tree_add_item(xid_tree, hf_ehdlc_xid_format_id, tvb, offset++, 1, ENC_NA);
167
0
  proto_tree_add_item(xid_tree, hf_ehdlc_xid_group_id, tvb, offset++, 1, ENC_NA);
168
0
  proto_tree_add_item(xid_tree, hf_ehdlc_xid_len, tvb, offset, 2, ENC_BIG_ENDIAN);
169
0
  offset += 2;
170
171
0
  while (tvb_reported_length_remaining(tvb, offset) >= 2) {
172
0
    uint8_t iei = tvb_get_uint8(tvb, offset++);
173
0
    uint8_t ie_len = tvb_get_uint8(tvb, offset++);
174
175
0
    switch (iei) {
176
0
    case 0x07:
177
0
      proto_tree_add_item(xid_tree, hf_ehdlc_xid_win_tx, tvb,
178
0
          offset, ie_len, ENC_NA);
179
0
      break;
180
0
    case 0x08:
181
0
      proto_tree_add_item(xid_tree, hf_ehdlc_xid_win_rx, tvb,
182
0
          offset, ie_len, ENC_NA);
183
0
      break;
184
0
    case 0x09:
185
0
      proto_tree_add_item(xid_tree, hf_ehdlc_xid_ack_tmr_ms, tvb,
186
0
          offset, ie_len, ENC_NA);
187
0
      break;
188
0
    }
189
0
    offset += ie_len;
190
0
  }
191
192
0
  return offset - base_offset;
193
0
}
194
195
/* Code to actually dissect the packets */
196
static int
197
dissect_ehdlc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
198
0
{
199
0
  int  offset = 4;
200
201
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "EHDLC");
202
0
  col_clear(pinfo->cinfo, COL_INFO);
203
204
0
  while (tvb_reported_length_remaining(tvb, offset) > 0) {
205
0
    proto_item *ti            = NULL;
206
0
    proto_tree *ehdlc_tree    = NULL;
207
0
    uint16_t    len, hdr2;
208
0
    uint8_t     csapi, ctei, sapi, tei, c_r;
209
0
    tvbuff_t   *next_tvb;
210
0
    uint16_t    control;
211
0
    bool        is_response   = false, is_extended = true;
212
0
    int         header_length = 2; /* Address + Length field */
213
214
0
    hdr2 = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
215
0
    len = hdr2 & 0x1FF;
216
0
    csapi = hdr2 >> 13;
217
0
    sapi = sapi_from_csapi(csapi);
218
0
    c_r = c_r_from_csapi(csapi);
219
0
    ctei = (hdr2 >> 9) & 0xF;
220
0
    tei = tei_from_ctei(ctei);
221
222
    /* Add TEI to INFO column */
223
0
    col_append_fstr(pinfo->cinfo, COL_INFO, " | TEI:%02u | ", tei);
224
0
    col_set_fence(pinfo->cinfo, COL_INFO);
225
226
0
    if (tree) {
227
      /* Use MIN(...,...) in the following to prevent a premature */
228
      /* exception before we try to dissect whatever is available. */
229
0
      ti = proto_tree_add_protocol_format(tree, proto_ehdlc,
230
0
          tvb, offset, MIN(len, tvb_captured_length_remaining(tvb,offset)),
231
0
          "Ericsson HDLC protocol");
232
0
      ehdlc_tree = proto_item_add_subtree(ti, ett_ehdlc);
233
234
0
      proto_tree_add_item(ehdlc_tree, hf_ehdlc_csapi,
235
0
              tvb, offset, 1, ENC_BIG_ENDIAN);
236
0
      proto_tree_add_item(ehdlc_tree, hf_ehdlc_ctei,
237
0
              tvb, offset, 1, ENC_BIG_ENDIAN);
238
0
      ti = proto_tree_add_uint(ehdlc_tree, hf_ehdlc_c_r,
239
0
               tvb, offset, 1, c_r);
240
0
      proto_item_set_generated(ti);
241
0
      ti = proto_tree_add_uint(ehdlc_tree, hf_ehdlc_sapi,
242
0
               tvb, offset, 1, sapi);
243
0
      proto_item_set_generated(ti);
244
0
      ti = proto_tree_add_uint(ehdlc_tree, hf_ehdlc_tei,
245
0
               tvb, offset, 1, tei);
246
0
      proto_item_set_generated(ti);
247
0
      proto_tree_add_item(ehdlc_tree, hf_ehdlc_data_len,
248
0
              tvb, offset, 2, ENC_BIG_ENDIAN);
249
0
    }
250
251
0
    if (sapi == 10 || sapi == 11) {
252
      /* Voice TRAU */
253
0
      next_tvb = tvb_new_subset_length(tvb, offset+2, len-2);
254
0
      call_dissector(sub_handles[SUB_TFP], next_tvb, pinfo, tree);
255
0
      offset += len;
256
0
      continue;
257
0
    } else if (sapi == 12) {
258
      /* GPRS TRAU */
259
0
      next_tvb = tvb_new_subset_length(tvb, offset+2, len-2);
260
0
      call_dissector(sub_handles[SUB_PGSL], next_tvb, pinfo, tree);
261
0
      offset += len;
262
0
      continue;
263
0
    }
264
265
0
    control = dissect_xdlc_control(tvb, offset+2, pinfo, ehdlc_tree, hf_ehdlc_control,
266
0
                 ett_ehdlc_control, &ehdlc_cf_items, &ehdlc_cf_items_ext,
267
0
                 NULL, NULL, is_response, is_extended, false);
268
0
    header_length += XDLC_CONTROL_LEN(control, is_extended);
269
270
0
    if (XDLC_IS_INFORMATION(control)) {
271
0
      next_tvb = tvb_new_subset_length(tvb, offset+header_length,
272
0
              len-header_length);
273
274
0
      switch (sapi) {
275
0
      case 0:
276
        /* len == 4 seems to be some kind of ACK */
277
0
        if (len <= 4)
278
0
          break;
279
0
        call_dissector(sub_handles[SUB_RSL], next_tvb, pinfo, tree);
280
0
        break;
281
0
      case 62:
282
        /* len == 4 seems to be some kind of ACK */
283
0
        if (len <= 4)
284
0
          break;
285
0
        call_dissector(sub_handles[SUB_OML], next_tvb, pinfo, tree);
286
0
        break;
287
0
      default:
288
0
        call_dissector(sub_handles[SUB_DATA], next_tvb, pinfo, tree);
289
0
        break;
290
0
      }
291
0
    } else if (control == (XDLC_U | XDLC_XID)) {
292
0
      dissect_ehdlc_xid(ehdlc_tree, tvb, offset+header_length,
293
0
            len-header_length);
294
0
    }
295
296
0
    if (len == 0)
297
0
      len = 1;
298
0
    offset += len;
299
0
  }
300
0
  return tvb_captured_length(tvb);
301
0
}
302
303
void
304
proto_register_ehdlc(void)
305
14
{
306
14
  static hf_register_info hf[] = {
307
14
    { &hf_ehdlc_data_len,
308
14
      { "DataLen", "ehdlc.data_len",
309
14
        FT_UINT16, BASE_DEC, NULL, 0x01FF,
310
14
        "The length of the data (in bytes)", HFILL }
311
14
    },
312
14
    { &hf_ehdlc_csapi,
313
14
      { "Compressed SAPI", "ehdlc.csapi",
314
14
        FT_UINT8, BASE_DEC, NULL, 0xE0,
315
14
        NULL, HFILL}
316
14
    },
317
14
    { &hf_ehdlc_ctei,
318
14
      { "Compressed TEI", "ehdlc.ctei",
319
14
        FT_UINT8, BASE_DEC, NULL, 0x1E,
320
14
        NULL, HFILL}
321
14
    },
322
14
    { &hf_ehdlc_sapi,
323
14
      { "SAPI", "ehdlc.sapi",
324
14
        FT_UINT8, BASE_DEC, NULL, 0,
325
14
        NULL, HFILL }
326
14
    },
327
14
    { &hf_ehdlc_tei,
328
14
      { "TEI", "ehdlc.tei",
329
14
        FT_UINT8, BASE_DEC, NULL, 0,
330
14
        NULL, HFILL }
331
14
    },
332
14
    { &hf_ehdlc_c_r,
333
14
      { "C/R", "ehdlc.c_r",
334
14
        FT_UINT8, BASE_DEC, NULL, 0,
335
14
        NULL, HFILL }
336
14
    },
337
338
14
    { &hf_ehdlc_xid_payload,
339
14
      { "XID Payload", "ehdlc.xid_payload",
340
14
        FT_BYTES, BASE_NONE, NULL, 0,
341
14
        NULL, HFILL }
342
14
    },
343
14
    { &hf_ehdlc_xid_win_tx,
344
14
      { "Transmit Window", "ehdlc.xid.win_tx",
345
14
        FT_UINT8, BASE_DEC, NULL, 0,
346
14
        NULL, HFILL }
347
14
    },
348
14
    { &hf_ehdlc_xid_win_rx,
349
14
      { "Receive Window", "ehdlc.xid.win_rx",
350
14
        FT_UINT8, BASE_DEC, NULL, 0,
351
14
        NULL, HFILL }
352
14
    },
353
14
    { &hf_ehdlc_xid_ack_tmr_ms,
354
14
      { "Timer (ms)", "ehdlc.xid.ack_tmr_ms",
355
14
        FT_UINT8, BASE_DEC, NULL, 0,
356
14
        NULL, HFILL }
357
14
    },
358
14
    { &hf_ehdlc_xid_format_id,
359
14
      { "Format Identifier", "ehdlc.xid.format_id",
360
14
        FT_UINT8, BASE_HEX, NULL, 0,
361
14
        NULL, HFILL }
362
14
    },
363
14
    { &hf_ehdlc_xid_group_id,
364
14
      { "Group Identifier", "ehdlc.xid.group_id",
365
14
        FT_UINT8, BASE_HEX, NULL, 0,
366
14
        NULL, HFILL }
367
14
    },
368
14
    { &hf_ehdlc_xid_len,
369
14
      { "XID Length", "ehdlc.xid.len",
370
14
        FT_UINT16, BASE_DEC, NULL, 0,
371
14
        NULL, HFILL }
372
14
    },
373
14
    { &hf_ehdlc_control,
374
14
      { "Control Field", "ehdlc.control",
375
14
        FT_UINT16, BASE_HEX, NULL, 0,
376
14
        NULL, HFILL }
377
14
    },
378
14
    { &hf_ehdlc_n_r,
379
14
      { "N(R)", "ehdlc.control.n_r",
380
14
        FT_UINT16, BASE_DEC, NULL, XDLC_N_R_EXT_MASK,
381
14
        NULL, HFILL }
382
14
    },
383
14
    { &hf_ehdlc_n_s,
384
14
      { "N(S)", "ehdlc.control.n_s",
385
14
        FT_UINT16, BASE_DEC, NULL, XDLC_N_S_EXT_MASK,
386
14
        NULL, HFILL }
387
14
    },
388
14
    { &hf_ehdlc_p,
389
14
      { "Poll", "ehdlc.control.p",
390
14
        FT_BOOLEAN, 8, TFS(&tfs_set_notset), XDLC_P_F,
391
14
        NULL, HFILL }
392
14
    },
393
14
    { &hf_ehdlc_p_ext,
394
14
      { "Poll", "ehdlc.control.p",
395
14
        FT_BOOLEAN, 16, TFS(&tfs_set_notset), XDLC_P_F_EXT,
396
14
        NULL, HFILL }
397
14
    },
398
14
    { &hf_ehdlc_f,
399
14
      { "Final", "ehdlc.control.f",
400
14
        FT_BOOLEAN, 8, TFS(&tfs_set_notset), XDLC_P_F,
401
14
        NULL, HFILL }
402
14
    },
403
14
    { &hf_ehdlc_f_ext,
404
14
      { "Final", "ehdlc.control.f",
405
14
        FT_BOOLEAN, 16, TFS(&tfs_set_notset), XDLC_P_F_EXT,
406
14
        NULL, HFILL }
407
14
    },
408
14
    { &hf_ehdlc_s_ftype,
409
14
      { "Supervisory frame type", "ehdlc.control.s_ftype",
410
14
        FT_UINT16, BASE_HEX, VALS(stype_vals), XDLC_S_FTYPE_MASK,
411
14
        NULL, HFILL }
412
14
    },
413
14
    { &hf_ehdlc_u_modifier_cmd,
414
14
      { "Command", "ehdlc.control.u_modifier_cmd",
415
14
        FT_UINT8, BASE_HEX, VALS(modifier_vals_cmd), XDLC_U_MODIFIER_MASK,
416
14
        NULL, HFILL }
417
14
    },
418
14
    { &hf_ehdlc_u_modifier_resp,
419
14
      { "Response", "ehdlc.control.u_modifier_resp",
420
14
        FT_UINT8, BASE_HEX, VALS(modifier_vals_resp), XDLC_U_MODIFIER_MASK,
421
14
        NULL, HFILL }
422
14
    },
423
14
    { &hf_ehdlc_ftype_i,
424
14
      { "Frame Type", "ehdlc.control.ftype",
425
14
        FT_UINT16, BASE_HEX, VALS(ftype_vals), XDLC_I_MASK,
426
14
        NULL, HFILL }
427
14
    },
428
14
    { &hf_ehdlc_ftype_s_u,
429
14
      { "Frame Type", "ehdlc.control.ftype",
430
14
        FT_UINT8, BASE_HEX, VALS(ftype_vals), XDLC_S_U_MASK,
431
14
        NULL, HFILL }
432
14
    },
433
14
    { &hf_ehdlc_ftype_s_u_ext,
434
14
      { "Frame Type", "ehdlc.control.ftype",
435
14
        FT_UINT16, BASE_HEX, VALS(ftype_vals), XDLC_S_U_MASK,
436
14
        NULL, HFILL }
437
14
    },
438
14
  };
439
440
14
  static int *ett[] = {
441
14
    &ett_ehdlc,
442
14
    &ett_ehdlc_xid,
443
14
    &ett_ehdlc_control,
444
14
  };
445
446
14
  proto_ehdlc =
447
14
      proto_register_protocol("Ericsson HDLC",
448
14
            "Ericsson HDLC as used in A-bis over IP", "ehdlc");
449
450
14
  proto_register_field_array(proto_ehdlc, hf, array_length(hf));
451
14
  proto_register_subtree_array(ett, array_length(ett));
452
453
14
  ehdlc_handle = register_dissector("ehdlc", dissect_ehdlc, proto_ehdlc);
454
14
}
455
456
void
457
proto_reg_handoff_ehdlc(void)
458
14
{
459
14
  sub_handles[SUB_RSL]  = find_dissector_add_dependency("gsm_abis_rsl", proto_ehdlc);
460
14
  sub_handles[SUB_OML]  = find_dissector_add_dependency("gsm_abis_oml", proto_ehdlc);
461
14
  sub_handles[SUB_TFP]  = find_dissector_add_dependency("gsm_abis_tfp", proto_ehdlc);
462
14
  sub_handles[SUB_PGSL]  = find_dissector_add_dependency("gsm_abis_pgsl", proto_ehdlc);
463
14
  sub_handles[SUB_DATA] = find_dissector("data");
464
465
14
  dissector_add_for_decode_as("l2tp.pw_type", ehdlc_handle);
466
14
}
467
468
/*
469
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
470
 *
471
 * Local variables:
472
 * c-basic-offset: 8
473
 * tab-width: 8
474
 * indent-tabs-mode: t
475
 * End:
476
 *
477
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
478
 * :indentSize=8:tabSize=8:noTabs=false:
479
 */