Coverage Report

Created: 2026-07-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-mrd.c
Line
Count
Source
1
/* packet-mrd.c   2001 Ronnie Sahlberg <See AUTHORS for email>
2
 * Routines for IGMP/MRD packet disassembly
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998 Gerald Combs
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
/*
11
12
13
      MRDISC
14
  code
15
16
  0x24    draft-06
17
  0x25    draft-06
18
  0x26    draft-06
19
20
  0x30    RFC 4286
21
  0x31    RFC 4286
22
  0x32    RFC 4286
23
24
  MRD: IGMP Multicast Router Discovery
25
26
  Originally defined in draft-ietf-idmr-igmp-mrd-06.txt
27
  TTL==1 and IP.DST==224.0.0.2 (All-Routers) for all packets.
28
29
  In draft 08 to draft 10, MRA and MRT IPv4 destination is
30
  All-Systems (224.0.0.1),
31
32
  https://datatracker.ietf.org/doc/html/rfc4286
33
  As defined in RFC 4286, MRA and MRT IPv4 destination is
34
  All-Snoopers (224.0.0.106).
35
*/
36
37
#include "config.h"
38
39
#include <epan/packet.h>
40
#include <epan/expert.h>
41
#include <epan/unit_strings.h>
42
43
#include <wsutil/inet_addr.h>
44
#include <wsutil/pint.h>
45
46
#include "packet-igmp.h"
47
48
void proto_register_mrd(void);
49
void proto_reg_handoff_mrd(void);
50
51
static dissector_handle_t mrd_handle;
52
53
static int proto_mrd;
54
static int hf_checksum;
55
static int hf_checksum_status;
56
static int hf_type;
57
static int hf_advint;
58
static int hf_numopts;
59
static int hf_options;
60
static int hf_option;
61
static int hf_option_len;
62
static int hf_qi;
63
static int hf_rv;
64
static int hf_option_bytes;
65
66
static int ett_mrd;
67
static int ett_options;
68
69
static expert_field ei_checksum;
70
static expert_field ei_mrd_type_deprecated;
71
static expert_field ei_mrd_dest_not_local;
72
73
0
#define MRDISC_MRA_OLD  0x24
74
0
#define MRDISC_MRS_OLD  0x25
75
3
#define MRDISC_MRT_OLD  0x26
76
24
#define MRDISC_MRA  0x30
77
20
#define MRDISC_MRS  0x31
78
22
#define MRDISC_MRT  0x32
79
static const value_string mrd_types[] = {
80
  {MRDISC_MRA_OLD,  "Multicast Router Advertisement"},
81
  {MRDISC_MRS_OLD,  "Multicast Router Solicitation"},
82
  {MRDISC_MRT_OLD,  "Multicast Router Termination"},
83
  {MRDISC_MRA,  "Multicast Router Advertisement"},
84
  {MRDISC_MRS,  "Multicast Router Solicitation"},
85
  {MRDISC_MRT,  "Multicast Router Termination"},
86
  {0,         NULL}
87
};
88
89
0
#define MRDISC_QI 0x01
90
0
#define MRDISC_RV 0x02
91
static const value_string mrd_options[] = {
92
  {MRDISC_QI, "Query Interval"},
93
  {MRDISC_RV, "Robustness Variable"},
94
  {0,         NULL}
95
};
96
97
98
static int
99
dissect_mrd_mra_old(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset)
100
0
{
101
0
  uint16_t num;
102
103
  /* Advertising Interval */
104
0
  proto_tree_add_item(parent_tree, hf_advint, tvb, offset, 1, ENC_BIG_ENDIAN);
105
0
  offset += 1;
106
107
  /* checksum */
108
0
  igmp_checksum(parent_tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
109
0
  offset += 2;
110
111
  /* skip unused bytes */
112
0
  offset += 2;
113
114
  /* number of options */
115
0
  num = tvb_get_ntohs(tvb, offset);
116
0
  proto_tree_add_uint(parent_tree, hf_numopts, tvb,
117
0
    offset, 2, num);
118
0
  offset += 2;
119
120
  /* process any options */
121
0
  while (num--) {
122
0
    proto_tree *tree;
123
0
    proto_item *item;
124
0
    uint8_t type,len;
125
0
    int old_offset = offset;
126
127
0
    item = proto_tree_add_item(parent_tree, hf_options,
128
0
      tvb, offset, -1, ENC_NA);
129
0
    tree = proto_item_add_subtree(item, ett_options);
130
131
0
    type = tvb_get_uint8(tvb, offset);
132
0
    proto_tree_add_uint(tree, hf_option, tvb, offset, 1, type);
133
0
    offset += 1;
134
135
0
    len = tvb_get_uint8(tvb, offset);
136
0
    proto_tree_add_uint(tree, hf_option_len, tvb, offset, 1, len);
137
0
    offset += 1;
138
139
0
    switch (type) {
140
0
    case MRDISC_QI:
141
0
      proto_item_set_text(item,"Option: %s == %d",
142
0
          val_to_str(pinfo->pool, type, mrd_options, "unknown %x"),
143
0
          tvb_get_ntohs(tvb, offset));
144
0
      proto_tree_add_item(tree, hf_qi, tvb, offset, len,
145
0
        ENC_BIG_ENDIAN);
146
0
      offset += len;
147
0
      break;
148
0
    case MRDISC_RV:
149
0
      proto_item_set_text(item,"Option: %s == %d",
150
0
          val_to_str(pinfo->pool, type, mrd_options, "unknown %x"),
151
0
          tvb_get_ntohs(tvb, offset));
152
0
      proto_tree_add_item(tree, hf_rv, tvb, offset, len,
153
0
        ENC_BIG_ENDIAN);
154
0
      offset += len;
155
0
      break;
156
0
    default:
157
0
      proto_item_set_text(item,"Option: unknown");
158
159
0
      proto_tree_add_item(tree, hf_option_bytes,
160
0
        tvb, offset, len, ENC_NA);
161
0
      offset += len;
162
0
    }
163
0
    proto_item_set_len(item, offset-old_offset);
164
0
  }
165
166
0
  return offset;
167
0
}
168
169
170
static int
171
dissect_mrd_mra(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset)
172
9
{
173
  /* Advertising Interval */
174
9
  proto_tree_add_item(parent_tree, hf_advint, tvb, offset, 1, ENC_BIG_ENDIAN);
175
9
  offset += 1;
176
177
  /* checksum */
178
9
  igmp_checksum(parent_tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
179
9
  offset += 2;
180
181
  /* Query Interval */
182
9
  proto_tree_add_item(parent_tree, hf_qi, tvb, offset, 2, ENC_BIG_ENDIAN);
183
9
  offset += 2;
184
185
  /* Robustness Variable */
186
9
  proto_tree_add_item(parent_tree, hf_rv, tvb, offset, 2, ENC_BIG_ENDIAN);
187
9
  offset += 2;
188
189
9
  return offset;
190
9
}
191
192
193
static int
194
dissect_mrd_mrst(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset)
195
7
{
196
  /* skip reserved byte */
197
7
  offset += 1;
198
199
  /* checksum */
200
7
  igmp_checksum(parent_tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
201
7
  offset += 2;
202
203
7
  return offset;
204
7
}
205
206
207
/* This function is only called from the IGMP dissector.
208
 * It's also part of ICMPv6 MLD, but that has a separate implementation. */
209
static int
210
dissect_mrd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
211
16
{
212
16
  proto_tree *tree;
213
16
  proto_item *item;
214
16
  uint8_t type;
215
16
  unsigned offset = 0;
216
217
16
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "MRD");
218
16
  col_clear(pinfo->cinfo, COL_INFO);
219
220
16
  item = proto_tree_add_item(parent_tree, proto_mrd, tvb, offset, 4, ENC_NA);
221
16
  tree = proto_item_add_subtree(item, ett_mrd);
222
223
16
  type = tvb_get_uint8(tvb, offset);
224
16
  col_add_str(pinfo->cinfo, COL_INFO,
225
16
      val_to_str(pinfo->pool, type, mrd_types,
226
16
        "Unknown Type:0x%02x"));
227
228
  /* type of command */
229
16
  item = proto_tree_add_uint(tree, hf_type, tvb, offset, 1, type);
230
16
  offset += 1;
231
16
  if ((pinfo->dst.type == AT_IPv4) && !in4_addr_is_local_network_control_block(pntohu32(pinfo->dst.data))) {
232
4
    proto_tree_add_expert(tree, pinfo, &ei_mrd_dest_not_local, tvb, 0, 0);
233
4
  }
234
235
16
  switch (type) {
236
0
  case MRDISC_MRA_OLD:
237
0
    expert_add_info(pinfo, item, &ei_mrd_type_deprecated);
238
0
    offset = dissect_mrd_mra_old(tvb, pinfo, tree, offset);
239
0
    break;
240
9
  case MRDISC_MRA:
241
9
    offset = dissect_mrd_mra(tvb, pinfo, tree, offset);
242
9
    break;
243
0
  case MRDISC_MRS_OLD:
244
3
  case MRDISC_MRT_OLD:
245
3
    expert_add_info(pinfo, item, &ei_mrd_type_deprecated);
246
    /* FALLTHROUGH */
247
5
  case MRDISC_MRS:
248
7
  case MRDISC_MRT:
249
    /* MRS and MRT packets looks the same */
250
7
    offset = dissect_mrd_mrst(tvb, pinfo, tree, offset);
251
7
    break;
252
16
  }
253
14
        proto_item_set_end(tree, tvb, offset);
254
14
  return offset;
255
16
}
256
257
258
void
259
proto_register_mrd(void)
260
15
{
261
15
  static hf_register_info hf[] = {
262
15
    { &hf_type,
263
15
      { "Type", "mrd.type", FT_UINT8, BASE_HEX,
264
15
        VALS(mrd_types), 0, "MRDISC Packet Type", HFILL }},
265
266
15
    { &hf_checksum,
267
15
      { "Checksum", "mrd.checksum", FT_UINT16, BASE_HEX,
268
15
        NULL, 0, "MRDISC Checksum", HFILL }},
269
270
15
    { &hf_checksum_status,
271
15
      { "Checksum Status", "mrd.checksum.status", FT_UINT8, BASE_NONE,
272
15
        VALS(proto_checksum_vals), 0x0, NULL, HFILL }},
273
274
15
    { &hf_advint,
275
15
      { "Advertising Interval", "mrd.adv_int", FT_UINT8, BASE_DEC|BASE_UNIT_STRING,
276
15
        UNS(&units_seconds), 0, NULL, HFILL }},
277
278
15
    { &hf_numopts,
279
15
      { "Number Of Options", "mrd.num_opts", FT_UINT16, BASE_DEC,
280
15
        NULL, 0, "MRDISC Number Of Options", HFILL }},
281
282
15
    { &hf_options,
283
15
      { "Options", "mrd.options", FT_NONE, BASE_NONE,
284
15
        NULL, 0, "MRDISC Options", HFILL }},
285
286
15
    { &hf_option,
287
15
      { "Option", "mrd.option", FT_UINT8, BASE_DEC,
288
15
        VALS(mrd_options), 0, "MRDISC Option Type", HFILL }},
289
290
15
    { &hf_option_len,
291
15
      { "Length", "mrd.opt_len", FT_UINT8, BASE_DEC,
292
15
        NULL, 0, "MRDISC Option Length", HFILL }},
293
294
15
    { &hf_qi,
295
15
      { "Query Interval", "mrd.query_int", FT_UINT16, BASE_DEC,
296
15
        NULL, 0, "MRDISC Query Interval", HFILL }},
297
298
15
    { &hf_rv,
299
15
      { "Robustness Variable", "mrd.rob_var", FT_UINT16, BASE_DEC,
300
15
        NULL, 0, "MRDISC Robustness Variable", HFILL }},
301
302
15
    { &hf_option_bytes,
303
15
      { "Data", "mrd.option_data", FT_BYTES, BASE_NONE,
304
15
        NULL, 0, "MRDISC Unknown Option Data", HFILL }},
305
306
15
  };
307
15
  static int *ett[] = {
308
15
    &ett_mrd,
309
15
    &ett_options,
310
15
  };
311
312
15
  static ei_register_info ei[] = {
313
15
    { &ei_checksum, { "mrd.bad_checksum", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
314
15
    { &ei_mrd_type_deprecated, { "mrd.type.deprecated", PI_DEPRECATED, PI_NOTE, "Draft 06 or earlier type (IANA unassigned)", EXPFILL }},
315
15
    { &ei_mrd_dest_not_local, { "mrd.dest.not_local", PI_PROTOCOL, PI_WARN, "Destination address must be in the local network control block (224.0.0/24)", EXPFILL }},
316
15
  };
317
318
15
  expert_module_t* expert_mrd;
319
320
15
  proto_mrd = proto_register_protocol("Multicast Router Discovery", "MRD", "mrd");
321
15
  proto_register_alias(proto_mrd, "mrdisc");
322
15
  proto_register_field_array(proto_mrd, hf, array_length(hf));
323
15
  proto_register_subtree_array(ett, array_length(ett));
324
15
  expert_mrd = expert_register_protocol(proto_mrd);
325
15
  expert_register_field_array(expert_mrd, ei, array_length(ei));
326
327
15
  mrd_handle = register_dissector("mrd", dissect_mrd, proto_mrd);
328
15
}
329
330
void
331
proto_reg_handoff_mrd(void)
332
15
{
333
  /* XXX - 0x24 and 0x25 conflict with another draft, MSNIP. Adding them
334
   * doesn't really work since the IGMP type table isn't Decode As. */
335
15
  dissector_add_uint("igmp.type", IGMP_TYPE_0x24, mrd_handle);
336
15
  dissector_add_uint("igmp.type", IGMP_TYPE_0x25, mrd_handle);
337
15
  dissector_add_uint("igmp.type", IGMP_TYPE_0x26, mrd_handle);
338
15
  dissector_add_uint("igmp.type", MRDISC_MRA, mrd_handle);
339
15
  dissector_add_uint("igmp.type", MRDISC_MRS, mrd_handle);
340
15
  dissector_add_uint("igmp.type", MRDISC_MRT, mrd_handle);
341
15
}
342
343
/*
344
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
345
 *
346
 * Local variables:
347
 * c-basic-offset: 8
348
 * tab-width: 8
349
 * indent-tabs-mode: t
350
 * End:
351
 *
352
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
353
 * :indentSize=8:tabSize=8:noTabs=false:
354
 */