Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-redbackli.c
Line
Count
Source
1
/* packet-redbackli.c
2
 *
3
 * Redback Lawful Intercept Packet dissector
4
 *
5
 * Copyright 2008 Florian Lohoff <flo[AT]rfc822.org>
6
 *
7
 * Wireshark - Network traffic analyzer
8
 * By Gerald Combs <gerald[AT]wireshark.org>
9
 * Copyright 1998 Gerald Combs
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
#include "config.h"
15
16
#include <epan/packet.h>
17
18
void proto_register_redbackli(void);
19
void proto_reg_handoff_redbackli(void);
20
21
static int proto_redbackli;
22
23
static int hf_redbackli_avptype;
24
static int hf_redbackli_avplen;
25
static int hf_redbackli_seqno;    /* Sequence No */
26
static int hf_redbackli_liid;   /* LI Id */
27
static int hf_redbackli_sessid;   /* Session Id */
28
static int hf_redbackli_label;    /* Label */
29
static int hf_redbackli_acctid;   /* Accounting Session Id */
30
static int hf_redbackli_dir;    /* Direction */
31
static int hf_redbackli_eohpad;   /* End Of Header Padding */
32
static int hf_redbackli_unknownavp; /* Unknown AVP */
33
34
static int ett_redbackli;
35
36
static dissector_handle_t ip_handle;
37
static dissector_handle_t redbackli_handle;
38
39
40
21
#define RB_AVP_SEQNO  1
41
17
#define RB_AVP_LIID 2
42
32
#define RB_AVP_SESSID 3
43
109
#define RB_AVP_DIR  4
44
41
#define RB_AVP_LABEL  20
45
115
#define RB_AVP_ACCTID 40
46
33
#define RB_AVP_EOH  0
47
48
static const value_string avp_names[] = {
49
  {RB_AVP_SEQNO,    "Sequence No"},
50
  {RB_AVP_LIID,   "Lawful Intercept Id"},
51
  {RB_AVP_SESSID,   "Session Id"},
52
  {RB_AVP_LABEL,    "Label"},
53
  {RB_AVP_ACCTID,   "Accounting Session Id"},
54
  {RB_AVP_DIR,    "Direction"},
55
  {RB_AVP_EOH,    "End Of Header"},
56
  {0,     NULL}
57
};
58
59
static void
60
redbackli_dissect_avp(uint8_t avptype, uint8_t avplen, tvbuff_t *tvb, int offset, proto_tree *tree)
61
0
{
62
0
  const char  *avpname;
63
0
  proto_tree  *st = NULL;
64
65
0
  avpname = val_to_str_const(avptype, avp_names, "Unknown");
66
67
0
  st = proto_tree_add_subtree_format(tree, tvb, offset, avplen+2, ett_redbackli, NULL, "%s AVP", avpname);
68
69
0
  proto_tree_add_uint(st, hf_redbackli_avptype, tvb, offset, 1, avptype);
70
0
  proto_tree_add_uint(st, hf_redbackli_avplen, tvb, offset+1, 1, avplen);
71
72
0
  if (!avplen)
73
0
    return;
74
75
  /* XXX: ToDo: Validate the length (avplen) of the fixed length fields
76
     before calling proto_tree_add_item().
77
     Note that the field lengths have been validated when
78
     dissect_avp() is called from redbackli_dissect_heur().
79
  */
80
81
0
  switch (avptype) {
82
0
    case(RB_AVP_SEQNO):
83
0
      proto_tree_add_item(st, hf_redbackli_seqno, tvb,
84
0
              offset+2, avplen, ENC_BIG_ENDIAN);
85
0
      break;
86
0
    case(RB_AVP_LIID):
87
0
      proto_tree_add_item(st, hf_redbackli_liid, tvb,
88
0
              offset+2, avplen, ENC_BIG_ENDIAN);
89
0
      break;
90
0
    case(RB_AVP_SESSID):
91
0
      proto_tree_add_item(st, hf_redbackli_sessid, tvb,
92
0
              offset+2, avplen, ENC_BIG_ENDIAN);
93
0
      break;
94
0
    case(RB_AVP_LABEL):
95
0
      proto_tree_add_item(st, hf_redbackli_label, tvb,
96
0
              offset+2, avplen, ENC_ASCII);
97
0
      break;
98
0
    case(RB_AVP_EOH):
99
0
      proto_tree_add_item(st, hf_redbackli_eohpad, tvb,
100
0
              offset+2, avplen, ENC_NA);
101
0
      break;
102
0
    case(RB_AVP_DIR):
103
0
      proto_tree_add_item(st, hf_redbackli_dir, tvb,
104
0
          offset+2, avplen, ENC_NA);
105
0
      break;
106
0
    case(RB_AVP_ACCTID):
107
0
      proto_tree_add_item(st, hf_redbackli_acctid, tvb,
108
0
              offset+2, avplen, ENC_NA);
109
0
      break;
110
0
    default:
111
0
      proto_tree_add_item(st, hf_redbackli_unknownavp, tvb,
112
0
              offset+2, avplen, ENC_NA);
113
0
      break;
114
0
  }
115
116
0
  return;
117
0
}
118
119
static int
120
redbackli_dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
121
0
{
122
0
  uint8_t   avptype, avplen;
123
0
  int   len, offset = 0;
124
0
  bool  eoh;
125
0
  proto_item  *ti;
126
0
  proto_tree  *redbackli_tree = NULL;
127
0
  tvbuff_t  *next_tvb;
128
129
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "RBLI");
130
131
0
  ti = proto_tree_add_item(tree, proto_redbackli,
132
0
           tvb, 0, -1, ENC_NA);
133
0
  redbackli_tree = proto_item_add_subtree(ti, ett_redbackli);
134
135
0
  len = tvb_reported_length(tvb);
136
0
  offset = 0;
137
0
  eoh = false;
138
0
  while (!eoh && (len > 2)) {
139
0
    avptype = tvb_get_uint8(tvb, offset+0);
140
0
    avplen = tvb_get_uint8(tvb, offset+1);
141
142
0
    if ((len-2) < avplen)   /* AVP Complete ? */
143
0
      break;
144
145
0
    if (tree)
146
0
      redbackli_dissect_avp(avptype, avplen, tvb, offset, redbackli_tree);
147
148
0
    if (avptype == RB_AVP_EOH)
149
0
      eoh = true;
150
151
0
    offset += 2 + avplen;
152
0
    len    -= 2 + avplen;
153
0
  }
154
155
0
  next_tvb = tvb_new_subset_remaining(tvb, offset);
156
0
  call_dissector(ip_handle, next_tvb, pinfo, tree);
157
158
0
  return tvb_captured_length(tvb);
159
0
}
160
161
162
912
#define REDBACKLI_INTSIZE 6
163
912
#define REDBACKLI_EOHSIZE 2
164
912
#define MIN_REDBACKLI_SIZE  (3*REDBACKLI_INTSIZE+REDBACKLI_EOHSIZE)
165
166
static bool
167
redbackli_dissect_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
168
912
{
169
912
  int   len, offset = 0;
170
912
  bool  eoh = false;
171
912
  uint8_t   avptype, avplen;
172
912
  uint32_t    avpfound = 0;
173
174
912
  len = tvb_captured_length(tvb);
175
912
  if (len < MIN_REDBACKLI_SIZE)
176
501
    return false;
177
178
  /*
179
   * We scan the possible AVPs and look out for mismatches.
180
   * An int AVP needs to be 4 byte long, and the eoh must be 0 or 1
181
   * long .. Unknown AVPs also mean not for us ...
182
   *
183
   */
184
534
  while ((len > 2) && !eoh) {
185
522
    avptype = tvb_get_uint8(tvb, offset+0);
186
522
    avplen = tvb_get_uint8(tvb, offset+1);
187
188
522
    switch (avptype) {
189
9
      case(RB_AVP_SEQNO):
190
17
      case(RB_AVP_LIID):
191
31
      case(RB_AVP_SESSID):
192
31
        if (avplen != 4)
193
25
          return false;
194
6
        avpfound |= 1<<avptype;
195
6
        break;
196
33
      case(RB_AVP_EOH):
197
33
        if (avplen > 1 || offset == 0)
198
31
          return false;
199
2
        eoh = true;
200
2
        break;
201
41
      case(RB_AVP_LABEL):
202
109
      case(RB_AVP_DIR):   /* Is this correct? the hf_ originally had FT_UINT8 for DIR */
203
115
      case(RB_AVP_ACCTID):
204
115
        break;
205
343
      default:
206
343
        return false;
207
522
    }
208
123
    offset += 2 + avplen;
209
123
    len    -= 2 + avplen;
210
123
  }
211
212
12
  if (!(avpfound & (1<<RB_AVP_SEQNO)))
213
11
    return false;
214
1
  if (!(avpfound & (1<<RB_AVP_SESSID)))
215
1
    return false;
216
0
  if (!(avpfound & (1<<RB_AVP_LIID)))
217
0
    return false;
218
219
0
  redbackli_dissect(tvb, pinfo, tree, data);
220
221
0
  return true;
222
0
}
223
15
void proto_register_redbackli(void) {
224
15
  static hf_register_info hf[] = {
225
15
    { &hf_redbackli_avptype,
226
15
      { "AVP Type", "redbackli.avptype", FT_UINT8, BASE_DEC, NULL, 0x0,
227
15
      NULL, HFILL }},
228
15
    { &hf_redbackli_avplen,
229
15
      { "AVP Length", "redbackli.avplen", FT_UINT8, BASE_DEC, NULL, 0x0,
230
15
      NULL, HFILL }},
231
15
    { &hf_redbackli_seqno,
232
15
      { "Sequence No", "redbackli.seqno", FT_UINT32, BASE_DEC, NULL, 0x0,
233
15
      NULL, HFILL }},
234
15
    { &hf_redbackli_liid,
235
15
      { "Lawful Intercept Id", "redbackli.liid", FT_UINT32, BASE_DEC, NULL, 0x0,
236
15
      "LI Identifier", HFILL }},
237
15
    { &hf_redbackli_sessid,
238
15
      { "Session Id", "redbackli.sessid", FT_UINT32, BASE_DEC, NULL, 0x0,
239
15
      "Session Identifier", HFILL }},
240
    /* XXX: If one goes by the heuristic then this field can be variable length ??
241
     * In the absence of any documentation We'll assume that's the case
242
     * (even though 'direction' sounds like a fixed length field
243
     */
244
15
    { &hf_redbackli_dir,
245
15
      { "Direction", "redbackli.dir", FT_BYTES, BASE_NONE, NULL, 0x0,
246
15
      NULL, HFILL }},
247
15
    { &hf_redbackli_label,
248
15
      { "Label", "redbackli.label", FT_STRING, BASE_NONE, NULL, 0x0,
249
15
      NULL, HFILL }},
250
15
    { &hf_redbackli_acctid,
251
15
      { "Acctid", "redbackli.acctid", FT_BYTES, BASE_NONE, NULL, 0x0,
252
15
      NULL, HFILL }},
253
15
    { &hf_redbackli_eohpad,
254
15
      { "End of Header Padding", "redbackli.eohpad", FT_BYTES, BASE_NONE, NULL, 0x0,
255
15
      NULL, HFILL }},
256
15
    { &hf_redbackli_unknownavp,
257
15
      { "Unknown AVP", "redbackli.unknownavp", FT_BYTES, BASE_NONE, NULL, 0x0,
258
15
      NULL, HFILL }}
259
15
    };
260
261
15
  static int *ett[] = {
262
15
    &ett_redbackli
263
15
  };
264
265
15
  proto_redbackli = proto_register_protocol("Redback Lawful Intercept", "RedbackLI", "redbackli");
266
267
15
  proto_register_field_array(proto_redbackli, hf, array_length(hf));
268
15
  proto_register_subtree_array(ett, array_length(ett));
269
270
15
  redbackli_handle = register_dissector("redbackli", redbackli_dissect, proto_redbackli);
271
15
}
272
273
15
void proto_reg_handoff_redbackli(void) {
274
15
  ip_handle = find_dissector_add_dependency("ip", proto_redbackli);
275
276
15
  dissector_add_for_decode_as_with_preference("udp.port", redbackli_handle);
277
278
15
  heur_dissector_add("udp", redbackli_dissect_heur, "Redback Lawful Intercept over UDP", "redbackli_udp", proto_redbackli, HEURISTIC_ENABLE);
279
15
}
280
281
282
/*
283
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
284
 *
285
 * Local variables:
286
 * c-basic-offset: 8
287
 * tab-width: 8
288
 * indent-tabs-mode: t
289
 * End:
290
 *
291
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
292
 * :indentSize=8:tabSize=8:noTabs=false:
293
 */