/src/wireshark/epan/dissectors/packet-gmrp.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-gmrp.c |
2 | | * Routines for GMRP (GARP Multicast Registration Protocol) dissection |
3 | | * Copyright 2001, Markus Seehofer <mseehofe@nt.hirschmann.de> |
4 | | * |
5 | | * Based on the code from packet-gvrp.c (GVRP) from |
6 | | * Kevin Shi <techishi@ms22.hinet.net> Copyright 2000 |
7 | | * |
8 | | * Wireshark - Network traffic analyzer |
9 | | * By Gerald Combs <gerald@wireshark.org> |
10 | | * Copyright 1998 Gerald Combs |
11 | | * |
12 | | * SPDX-License-Identifier: GPL-2.0-or-later |
13 | | */ |
14 | | |
15 | | #include "config.h" |
16 | | |
17 | | #include <epan/packet.h> |
18 | | #include <epan/expert.h> |
19 | | #include <epan/llcsaps.h> |
20 | | |
21 | | void proto_register_gmrp(void); |
22 | | |
23 | | /* Initialize the protocol and registered fields */ |
24 | | static int proto_gmrp; |
25 | | static int hf_gmrp_proto_id; |
26 | | static int hf_gmrp_attribute_type; |
27 | | static int hf_gmrp_attribute_length; |
28 | | static int hf_gmrp_attribute_event; |
29 | | static int hf_gmrp_attribute_value_group_membership; |
30 | | static int hf_gmrp_attribute_value_service_requirement; |
31 | | static int hf_gmrp_end_of_mark; |
32 | | |
33 | | /* Initialize the subtree pointers */ |
34 | | static int ett_gmrp; |
35 | | static int ett_gmrp_message; |
36 | | static int ett_gmrp_attribute_list; |
37 | | /*static int ett_gmrp_attribute;*/ |
38 | | |
39 | | static expert_field ei_gmrp_proto_id; |
40 | | |
41 | | |
42 | | /* Constant definitions */ |
43 | 0 | #define GARP_DEFAULT_PROTOCOL_ID 0x0001 |
44 | 0 | #define GARP_END_OF_MARK 0x00 |
45 | | |
46 | 0 | #define GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP 0x01 |
47 | 0 | #define GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT 0x02 |
48 | | |
49 | | #define GMRP_SERVICE_REQUIREMENT_FORWARD_ALL 0x00 |
50 | | #define GMRP_SERVICE_REQUIREMENT_FORWARD_ALL_UNREGISTERED 0x01 |
51 | | |
52 | | static const value_string attribute_type_vals[] = { |
53 | | { GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP ,"Group Membership" }, |
54 | | { GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT ,"Service Requirement" }, |
55 | | { 0, NULL } |
56 | | }; |
57 | | |
58 | | /* The length of GMRP LeaveAll attribute should be 2 octets (one for length |
59 | | * and the other for event) */ |
60 | 0 | #define GMRP_LENGTH_LEAVEALL (int)(sizeof(uint8_t)+sizeof(uint8_t)) |
61 | | |
62 | | /* The length of GMRP attribute other than LeaveAll should be: |
63 | | * |
64 | | * 8 bytes for Group Membership (1 for length, 1 for event and 6 for mac address to register) |
65 | | * or |
66 | | * 3 bytes for Service Requirement (1 for length, 1 for event, 1 for attribute value) |
67 | | * |
68 | | */ |
69 | 0 | #define GMRP_GROUP_MEMBERSHIP_NON_LEAVEALL (int)(sizeof(uint8_t)+sizeof(uint8_t)+(6*sizeof(uint8_t))) |
70 | 0 | #define GMRP_SERVICE_REQUIREMENT_NON_LEAVEALL (int)(sizeof(uint8_t)+sizeof(uint8_t)+sizeof(uint8_t)) |
71 | | |
72 | | /* Packet offset definitions */ |
73 | 0 | #define GARP_PROTOCOL_ID 0 |
74 | | |
75 | | /* Event definitions */ |
76 | 0 | #define GMRP_EVENT_LEAVEALL 0 |
77 | 0 | #define GMRP_EVENT_JOINEMPTY 1 |
78 | 0 | #define GMRP_EVENT_JOININ 2 |
79 | 0 | #define GMRP_EVENT_LEAVEEMPTY 3 |
80 | 0 | #define GMRP_EVENT_LEAVEIN 4 |
81 | 0 | #define GMRP_EVENT_EMPTY 5 |
82 | | |
83 | | static const value_string event_vals[] = { |
84 | | { GMRP_EVENT_LEAVEALL, "Leave All" }, |
85 | | { GMRP_EVENT_JOINEMPTY, "Join Empty" }, |
86 | | { GMRP_EVENT_JOININ, "Join In" }, |
87 | | { GMRP_EVENT_LEAVEEMPTY, "Leave Empty" }, |
88 | | { GMRP_EVENT_LEAVEIN, "Leave In" }, |
89 | | { GMRP_EVENT_EMPTY, "Empty" }, |
90 | | { 0, NULL } |
91 | | }; |
92 | | |
93 | | |
94 | | /* Code to actually dissect the packets */ |
95 | | static int |
96 | | dissect_gmrp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
97 | 0 | { |
98 | 0 | proto_item *ti; |
99 | 0 | proto_tree *gmrp_tree, *msg_tree, *attr_tree; |
100 | 0 | uint16_t protocol_id; |
101 | 0 | uint8_t octet; |
102 | 0 | uint8_t attribute_type; |
103 | 0 | int msg_index, attr_index, offset = 0, length = tvb_reported_length(tvb); |
104 | |
|
105 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "GMRP"); |
106 | |
|
107 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "GMRP"); |
108 | |
|
109 | 0 | ti = proto_tree_add_item(tree, proto_gmrp, tvb, 0, -1, ENC_NA); |
110 | |
|
111 | 0 | gmrp_tree = proto_item_add_subtree(ti, ett_gmrp); |
112 | | |
113 | | /* Read in GARP protocol ID */ |
114 | 0 | protocol_id = tvb_get_ntohs(tvb, GARP_PROTOCOL_ID); |
115 | |
|
116 | 0 | ti = proto_tree_add_uint_format_value(gmrp_tree, hf_gmrp_proto_id, tvb, |
117 | 0 | GARP_PROTOCOL_ID, 2, protocol_id, |
118 | 0 | "0x%04x (%s)", protocol_id, |
119 | 0 | protocol_id == GARP_DEFAULT_PROTOCOL_ID ? |
120 | 0 | "GARP Multicast Registration Protocol" : |
121 | 0 | "Unknown Protocol"); |
122 | | |
123 | | /* Currently only one protocol ID is supported */ |
124 | 0 | if (protocol_id != GARP_DEFAULT_PROTOCOL_ID) |
125 | 0 | { |
126 | 0 | expert_add_info(pinfo, ti, &ei_gmrp_proto_id); |
127 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, GARP_PROTOCOL_ID + 2), |
128 | 0 | pinfo, tree); |
129 | 0 | return tvb_captured_length(tvb); |
130 | 0 | } |
131 | | |
132 | 0 | offset += 2; |
133 | 0 | length -= 2; |
134 | |
|
135 | 0 | msg_index = 0; |
136 | | |
137 | | /* Begin to parse GARP messages */ |
138 | 0 | while (length) |
139 | 0 | { |
140 | 0 | proto_item *msg_item; |
141 | 0 | int msg_start = offset; |
142 | | |
143 | | /* Read in attribute type. */ |
144 | 0 | attribute_type = octet = tvb_get_uint8(tvb, offset); |
145 | | |
146 | | /* Check for end of mark */ |
147 | 0 | if (octet == GARP_END_OF_MARK) |
148 | 0 | { |
149 | | /* End of GARP PDU */ |
150 | 0 | if (msg_index) |
151 | 0 | { |
152 | 0 | proto_tree_add_uint_format(gmrp_tree, hf_gmrp_end_of_mark, tvb, offset, 1, octet, "End of pdu"); |
153 | 0 | break; |
154 | 0 | } |
155 | 0 | else |
156 | 0 | { |
157 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), |
158 | 0 | pinfo, tree); |
159 | 0 | return tvb_captured_length(tvb); |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | 0 | offset += 1; |
164 | 0 | length -= 1; |
165 | |
|
166 | 0 | msg_tree = proto_tree_add_subtree_format(gmrp_tree, tvb, msg_start, -1, |
167 | 0 | ett_gmrp_message, &msg_item, "Message %d", msg_index + 1); |
168 | |
|
169 | 0 | proto_tree_add_uint(msg_tree, hf_gmrp_attribute_type, tvb, |
170 | 0 | msg_start, 1, octet); |
171 | | |
172 | | /* GMRP supports Group Membership and Service Requirement as attribute types */ |
173 | 0 | if ( (octet != GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP) && (octet != GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT) ) |
174 | 0 | { |
175 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), pinfo, |
176 | 0 | tree); |
177 | 0 | return tvb_captured_length(tvb); |
178 | 0 | } |
179 | | |
180 | 0 | attr_index = 0; |
181 | |
|
182 | 0 | while (length) |
183 | 0 | { |
184 | 0 | int attr_start = offset; |
185 | 0 | proto_item *attr_item; |
186 | | |
187 | | /* Read in attribute length. */ |
188 | 0 | octet = tvb_get_uint8(tvb, offset); |
189 | | |
190 | | /* Check for end of mark */ |
191 | 0 | if (octet == GARP_END_OF_MARK) |
192 | 0 | { |
193 | | /* If at least one message has been already read, |
194 | | * check for another end of mark. |
195 | | */ |
196 | 0 | if (attr_index) |
197 | 0 | { |
198 | 0 | proto_tree_add_uint_format(msg_tree, hf_gmrp_end_of_mark, tvb, offset, |
199 | 0 | 1, octet, " End of mark"); |
200 | |
|
201 | 0 | offset += 1; |
202 | 0 | length -= 1; |
203 | |
|
204 | 0 | proto_item_set_len(msg_item, offset - msg_start); |
205 | 0 | break; |
206 | 0 | } |
207 | 0 | else |
208 | 0 | { |
209 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), |
210 | 0 | pinfo, tree); |
211 | 0 | return tvb_captured_length(tvb); |
212 | 0 | } |
213 | 0 | } |
214 | 0 | else |
215 | 0 | { |
216 | 0 | uint8_t event; |
217 | |
|
218 | 0 | offset += 1; |
219 | 0 | length -= 1; |
220 | |
|
221 | 0 | attr_tree = proto_tree_add_subtree_format(msg_tree, tvb, attr_start, -1, |
222 | 0 | ett_gmrp_attribute_list, &attr_item, " Attribute %d", attr_index + 1); |
223 | |
|
224 | 0 | proto_tree_add_uint(attr_tree, hf_gmrp_attribute_length, |
225 | 0 | tvb, attr_start, 1, octet); |
226 | | |
227 | | /* Read in attribute event */ |
228 | 0 | event = tvb_get_uint8(tvb, offset); |
229 | |
|
230 | 0 | proto_tree_add_uint(attr_tree, hf_gmrp_attribute_event, |
231 | 0 | tvb, offset, 1, event); |
232 | |
|
233 | 0 | offset += 1; |
234 | 0 | length -= 1; |
235 | |
|
236 | 0 | switch (event) { |
237 | | |
238 | 0 | case GMRP_EVENT_LEAVEALL: |
239 | 0 | if (octet != GMRP_LENGTH_LEAVEALL) |
240 | 0 | { |
241 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), |
242 | 0 | pinfo, tree); |
243 | 0 | return tvb_captured_length(tvb); |
244 | 0 | } |
245 | 0 | break; |
246 | | |
247 | 0 | case GMRP_EVENT_JOINEMPTY: |
248 | 0 | case GMRP_EVENT_JOININ: |
249 | 0 | case GMRP_EVENT_LEAVEEMPTY: |
250 | 0 | case GMRP_EVENT_LEAVEIN: |
251 | 0 | case GMRP_EVENT_EMPTY: |
252 | 0 | if ( (octet != GMRP_GROUP_MEMBERSHIP_NON_LEAVEALL) && |
253 | 0 | (octet != GMRP_SERVICE_REQUIREMENT_NON_LEAVEALL) ) |
254 | 0 | { |
255 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), |
256 | 0 | pinfo, tree); |
257 | 0 | return tvb_captured_length(tvb); |
258 | 0 | } |
259 | | |
260 | | /* Show attribute value */ |
261 | | |
262 | 0 | if ( GMRP_ATTRIBUTE_TYPE_GROUP_MEMBERSHIP == attribute_type ) |
263 | 0 | { |
264 | | /* Group Membership */ |
265 | 0 | proto_tree_add_item(attr_tree, hf_gmrp_attribute_value_group_membership, |
266 | 0 | tvb, offset, 6, ENC_NA); |
267 | |
|
268 | 0 | offset += 6; |
269 | 0 | length -= 6; |
270 | 0 | } |
271 | 0 | else |
272 | 0 | if ( GMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT == attribute_type ) |
273 | 0 | { |
274 | | /* Service Requirement */ |
275 | 0 | proto_tree_add_item(attr_tree, hf_gmrp_attribute_value_service_requirement, |
276 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
277 | |
|
278 | 0 | offset += 1; |
279 | 0 | length -= 1; |
280 | 0 | } |
281 | 0 | else |
282 | 0 | { |
283 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), |
284 | 0 | pinfo, tree); |
285 | 0 | return tvb_captured_length(tvb); |
286 | 0 | } |
287 | | |
288 | 0 | break; |
289 | | |
290 | 0 | default: |
291 | 0 | call_data_dissector(tvb_new_subset_remaining(tvb, offset), |
292 | 0 | pinfo, tree); |
293 | 0 | return tvb_captured_length(tvb); |
294 | 0 | } |
295 | 0 | } |
296 | | |
297 | 0 | proto_item_set_len(attr_item, offset - attr_start); |
298 | |
|
299 | 0 | attr_index++; |
300 | 0 | } |
301 | | |
302 | 0 | msg_index++; |
303 | 0 | } |
304 | 0 | return tvb_captured_length(tvb); |
305 | 0 | } |
306 | | |
307 | | |
308 | | |
309 | | /* Register the protocol with Wireshark */ |
310 | | void |
311 | | proto_register_gmrp(void) |
312 | 14 | { |
313 | 14 | static hf_register_info hf[] = { |
314 | 14 | { &hf_gmrp_proto_id, |
315 | 14 | { "Protocol Identifier", "gmrp.protocol_id", |
316 | 14 | FT_UINT16, BASE_HEX, NULL, 0x0, |
317 | 14 | NULL , HFILL } |
318 | 14 | }, |
319 | 14 | { &hf_gmrp_attribute_type, |
320 | 14 | { "Type", "gmrp.attribute_type", |
321 | 14 | FT_UINT8, BASE_HEX, VALS(attribute_type_vals), 0x0, |
322 | 14 | NULL , HFILL } |
323 | 14 | }, |
324 | 14 | { &hf_gmrp_attribute_length, |
325 | 14 | { "Length", "gmrp.attribute_length", |
326 | 14 | FT_UINT8, BASE_DEC, NULL, 0x0, |
327 | 14 | NULL , HFILL } |
328 | 14 | }, |
329 | 14 | { &hf_gmrp_attribute_event, |
330 | 14 | { "Event", "gmrp.attribute_event", |
331 | 14 | FT_UINT8, BASE_DEC, VALS(event_vals), 0x0, |
332 | 14 | NULL , HFILL } |
333 | 14 | }, |
334 | 14 | { &hf_gmrp_attribute_value_group_membership, |
335 | 14 | { "Value", "gmrp.attribute_value_group_membership", |
336 | 14 | FT_ETHER, BASE_NONE, NULL, 0x0, |
337 | 14 | NULL , HFILL } |
338 | 14 | }, |
339 | 14 | { &hf_gmrp_attribute_value_service_requirement, |
340 | 14 | { "Value", "gmrp.attribute_value_service_requirement", |
341 | 14 | FT_UINT8, BASE_HEX, NULL, 0x0, |
342 | 14 | NULL , HFILL } |
343 | 14 | }, |
344 | 14 | { &hf_gmrp_end_of_mark, |
345 | 14 | { "End of mark", "gmrp.end_of_mark", |
346 | 14 | FT_UINT8, BASE_HEX, NULL, 0x0, |
347 | 14 | NULL , HFILL } |
348 | 14 | } |
349 | | |
350 | 14 | }; |
351 | | |
352 | 14 | static int *ett[] = { |
353 | 14 | &ett_gmrp, |
354 | 14 | &ett_gmrp_message, |
355 | 14 | &ett_gmrp_attribute_list |
356 | 14 | }; |
357 | | |
358 | 14 | static ei_register_info ei[] = { |
359 | 14 | { &ei_gmrp_proto_id, { "gmrp.protocol_id.not_gmrp", PI_UNDECODED, PI_WARN, "This version of Wireshark only knows about protocol id = 1", EXPFILL }}, |
360 | 14 | }; |
361 | | |
362 | 14 | expert_module_t* expert_gmrp; |
363 | | |
364 | | /* Register the protocol name and description for GMRP */ |
365 | 14 | proto_gmrp = proto_register_protocol("GARP Multicast Registration Protocol", "GMRP", "gmrp"); |
366 | | |
367 | | /* Required function calls to register the header fields and subtrees |
368 | | * used by GMRP */ |
369 | 14 | proto_register_field_array(proto_gmrp, hf, array_length(hf)); |
370 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
371 | 14 | expert_gmrp = expert_register_protocol(proto_gmrp); |
372 | 14 | expert_register_field_array(expert_gmrp, ei, array_length(ei)); |
373 | | |
374 | 14 | register_dissector("gmrp", dissect_gmrp, proto_gmrp); |
375 | | |
376 | 14 | } |
377 | | |
378 | | /* |
379 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
380 | | * |
381 | | * Local variables: |
382 | | * c-basic-offset: 4 |
383 | | * tab-width: 8 |
384 | | * indent-tabs-mode: nil |
385 | | * End: |
386 | | * |
387 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
388 | | * :indentSize=4:tabSize=8:noTabs=true: |
389 | | */ |