/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 | | */ |