/src/wireshark/epan/dissectors/packet-vrrp.c
Line | Count | Source |
1 | | /* packet-vrrp.c |
2 | | * Routines for the Virtual Router Redundancy Protocol (VRRP) |
3 | | * |
4 | | * VRRPv2: RFC3768 (superseeding RFC2338) |
5 | | * VRRPv3: RFC5798 |
6 | | * |
7 | | * Heikki Vatiainen <hessu@cs.tut.fi> |
8 | | * |
9 | | * Wireshark - Network traffic analyzer |
10 | | * By Gerald Combs <gerald@wireshark.org> |
11 | | * Copyright 1998 Gerald Combs |
12 | | * |
13 | | * SPDX-License-Identifier: GPL-2.0-or-later |
14 | | */ |
15 | | |
16 | | #include "config.h" |
17 | | |
18 | | #include <epan/packet.h> |
19 | | #include <epan/in_cksum.h> |
20 | | #include <epan/expert.h> |
21 | | #include <epan/prefs.h> |
22 | | #include <epan/iana-info.h> |
23 | | |
24 | | void proto_register_vrrp(void); |
25 | | void proto_reg_handoff_vrrp(void); |
26 | | |
27 | | static dissector_handle_t vrrp_handle; |
28 | | |
29 | | static int proto_vrrp; |
30 | | static int ett_vrrp; |
31 | | static int ett_vrrp_ver_type; |
32 | | |
33 | | static int hf_vrrp_ver_type; |
34 | | static int hf_vrrp_version; |
35 | | static int hf_vrrp_type; |
36 | | static int hf_vrrp_virt_rtr_id; |
37 | | static int hf_vrrp_prio; |
38 | | static int hf_vrrp_addr_count; |
39 | | static int hf_vrrp_checksum; |
40 | | static int hf_vrrp_checksum_status; |
41 | | static int hf_vrrp_auth_type; |
42 | | static int hf_vrrp_adver_int; |
43 | | static int hf_vrrp_reserved_mbz; |
44 | | static int hf_vrrp_short_adver_int; |
45 | | static int hf_vrrp_ip; |
46 | | static int hf_vrrp_ip6; |
47 | | static int hf_vrrp_auth_string; |
48 | | static int hf_vrrp_md5_auth_data; |
49 | | |
50 | | static bool g_vrrp_v3_checksum_as_in_v2 = true; |
51 | | |
52 | | static expert_field ei_vrrp_checksum; |
53 | | |
54 | 16 | #define VRRP_VERSION_MASK 0xf0 |
55 | 16 | #define VRRP_TYPE_MASK 0x0f |
56 | 2 | #define VRRP_AUTH_DATA_LEN 8 |
57 | | |
58 | | #define VRRP_TYPE_ADVERTISEMENT 1 |
59 | | static const value_string vrrp_type_vals[] = { |
60 | | {VRRP_TYPE_ADVERTISEMENT, "Advertisement"}, |
61 | | {0, NULL} |
62 | | }; |
63 | | |
64 | 32 | #define VRRP_AUTH_TYPE_NONE 0 |
65 | 32 | #define VRRP_AUTH_TYPE_SIMPLE_TEXT 1 |
66 | | #define VRRP_AUTH_TYPE_IP_AUTH_HDR 2 |
67 | 31 | #define VRRP_AUTH_TYPE_IP_MD5 254 |
68 | | static const value_string vrrp_auth_vals[] = { |
69 | | {VRRP_AUTH_TYPE_NONE, "No Authentication"}, |
70 | | {VRRP_AUTH_TYPE_SIMPLE_TEXT, "Simple Text Authentication [RFC 2338] / Reserved [RFC 3768]"}, |
71 | | {VRRP_AUTH_TYPE_IP_AUTH_HDR, "IP Authentication Header [RFC 2338] / Reserved [RFC 3768]"}, |
72 | | {VRRP_AUTH_TYPE_IP_MD5, "Cisco VRRP MD5 authentication"}, |
73 | | {0, NULL} |
74 | | }; |
75 | | |
76 | | #define VRRP_PRIORITY_MASTER_STOPPING 0 |
77 | | /* Values between 1 and 254 inclusive are for backup VRRP routers */ |
78 | | #define VRRP_PRIORITY_DEFAULT 100 |
79 | | #define VRRP_PRIORITY_OWNER 255 |
80 | | static const value_string vrrp_prio_vals[] = { |
81 | | {VRRP_PRIORITY_MASTER_STOPPING, "Current Master has stopped participating in VRRP"}, |
82 | | {VRRP_PRIORITY_DEFAULT, "Default priority for a backup VRRP router"}, |
83 | | {VRRP_PRIORITY_OWNER, "This VRRP router owns the virtual router's IP address(es)"}, |
84 | | {0, NULL } |
85 | | }; |
86 | | |
87 | | |
88 | | static int |
89 | | dissect_vrrp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
90 | 32 | { |
91 | 32 | int offset = 0; |
92 | 32 | int vrrp_len; |
93 | 32 | uint8_t ver_type; |
94 | 32 | vec_t cksum_vec[4]; |
95 | 32 | uint32_t phdr[2]; |
96 | 32 | bool is_ipv6; |
97 | 32 | proto_item *ti, *tv; |
98 | 32 | proto_tree *vrrp_tree, *ver_type_tree; |
99 | 32 | uint8_t priority, addr_count = 0, auth_type = VRRP_AUTH_TYPE_NONE; |
100 | 32 | uint16_t computed_cksum = 0; |
101 | | |
102 | 32 | is_ipv6 = (pinfo->src.type == AT_IPv6); |
103 | | |
104 | 32 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "VRRP"); |
105 | 32 | col_clear(pinfo->cinfo, COL_INFO); |
106 | | |
107 | 32 | ver_type = tvb_get_uint8(tvb, 0); |
108 | 32 | col_add_fstr(pinfo->cinfo, COL_INFO, "Announcement (v%u)", |
109 | 32 | hi_nibble(ver_type)); |
110 | | |
111 | 32 | ti = proto_tree_add_item(tree, proto_vrrp, tvb, 0, -1, ENC_NA); |
112 | 32 | vrrp_tree = proto_item_add_subtree(ti, ett_vrrp); |
113 | | |
114 | 32 | priority = tvb_get_uint8(tvb, 2); |
115 | 32 | addr_count = tvb_get_uint8(tvb, 3); |
116 | | |
117 | 32 | tv = proto_tree_add_uint_format(vrrp_tree, hf_vrrp_ver_type, |
118 | 32 | tvb, offset, 1, ver_type, |
119 | 32 | "Version %u, Packet type %u (%s)", |
120 | 32 | hi_nibble(ver_type), lo_nibble(ver_type), |
121 | 32 | val_to_str_const(lo_nibble(ver_type), vrrp_type_vals, "Unknown")); |
122 | 32 | ver_type_tree = proto_item_add_subtree(tv, ett_vrrp_ver_type); |
123 | | |
124 | 32 | if(ver_type_tree){ |
125 | 31 | proto_tree_add_uint(ver_type_tree, hf_vrrp_version, tvb, |
126 | 31 | offset, 1, ver_type); |
127 | 31 | proto_tree_add_uint(ver_type_tree, hf_vrrp_type, tvb, offset, 1, ver_type); |
128 | 31 | offset += 1; |
129 | | |
130 | 31 | proto_tree_add_item(vrrp_tree, hf_vrrp_virt_rtr_id, tvb, offset, 1, ENC_BIG_ENDIAN); |
131 | 31 | offset += 1; |
132 | | |
133 | 31 | proto_tree_add_uint_format_value(vrrp_tree, hf_vrrp_prio, tvb, offset, 1, priority, "%u (%s)", |
134 | 31 | priority, |
135 | 31 | val_to_str_const(priority, vrrp_prio_vals, "Non-default backup priority")); |
136 | 31 | offset += 1; |
137 | | |
138 | 31 | proto_tree_add_uint(vrrp_tree, hf_vrrp_addr_count, tvb, |
139 | 31 | offset, 1, addr_count); |
140 | 31 | offset += 1; |
141 | | |
142 | 31 | switch(hi_nibble(ver_type)) { |
143 | 4 | case 3: |
144 | | /* 4 bits reserved (mbz) + 12 bits interval */ |
145 | 4 | proto_tree_add_item(vrrp_tree, hf_vrrp_reserved_mbz, tvb, offset, 1, ENC_BIG_ENDIAN); |
146 | 4 | proto_tree_add_item(vrrp_tree, hf_vrrp_short_adver_int, tvb, offset, 2, ENC_BIG_ENDIAN); |
147 | 4 | offset += 2; |
148 | 4 | break; |
149 | 2 | case 2: |
150 | 27 | default: |
151 | | /* 1 byte auth type + 1 byte interval */ |
152 | 27 | proto_tree_add_item_ret_uint8(vrrp_tree, hf_vrrp_auth_type, tvb, offset, 1, ENC_BIG_ENDIAN, &auth_type); |
153 | 27 | offset += 1; |
154 | | |
155 | 27 | proto_tree_add_item(vrrp_tree, hf_vrrp_adver_int, tvb, offset, 1, ENC_BIG_ENDIAN); |
156 | 27 | offset += 1; |
157 | 27 | break; |
158 | 31 | } |
159 | 31 | }else{ |
160 | 1 | offset += 6; |
161 | 1 | } |
162 | | |
163 | 32 | vrrp_len = (int)tvb_reported_length(tvb); |
164 | 32 | if (!pinfo->fragmented && (int)tvb_captured_length(tvb) >= vrrp_len) { |
165 | | /* The packet isn't part of a fragmented datagram |
166 | | and isn't truncated, so we can checksum it. */ |
167 | 26 | switch(hi_nibble(ver_type)) { |
168 | 4 | case 3: |
169 | 4 | if((g_vrrp_v3_checksum_as_in_v2 == false)||(pinfo->src.type == AT_IPv6)){ |
170 | | /* Set up the fields of the pseudo-header. */ |
171 | 2 | SET_CKSUM_VEC_PTR(cksum_vec[0], (const uint8_t *)pinfo->src.data, pinfo->src.len); |
172 | 2 | SET_CKSUM_VEC_PTR(cksum_vec[1], (const uint8_t *)pinfo->dst.data, pinfo->dst.len); |
173 | 2 | phdr[0] = g_htonl(vrrp_len); |
174 | 2 | phdr[1] = g_htonl(IP_PROTO_VRRP); |
175 | 2 | SET_CKSUM_VEC_PTR(cksum_vec[2], (const uint8_t *)&phdr, 8); |
176 | 2 | SET_CKSUM_VEC_TVB(cksum_vec[3], tvb, 0, vrrp_len); |
177 | 2 | computed_cksum = in_cksum(cksum_vec, 4); |
178 | 2 | break; |
179 | 2 | } |
180 | | /* FALL THROUGH */ |
181 | 4 | case 2: |
182 | 24 | default: |
183 | 24 | SET_CKSUM_VEC_TVB(cksum_vec[0], tvb, 0, vrrp_len); |
184 | 24 | computed_cksum = in_cksum(&cksum_vec[0], 1); |
185 | 24 | break; |
186 | 26 | } |
187 | | |
188 | 26 | proto_tree_add_checksum(vrrp_tree, tvb, offset, hf_vrrp_checksum, hf_vrrp_checksum_status, &ei_vrrp_checksum, pinfo, computed_cksum, |
189 | 26 | ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM); |
190 | 26 | } else { |
191 | 6 | proto_tree_add_checksum(vrrp_tree, tvb, offset, hf_vrrp_checksum, hf_vrrp_checksum_status, &ei_vrrp_checksum, pinfo, 0, |
192 | 6 | ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS); |
193 | 6 | } |
194 | 32 | offset += 2; |
195 | | |
196 | 451 | while (addr_count > 0) { |
197 | 419 | if (is_ipv6) { |
198 | 67 | proto_tree_add_item(vrrp_tree, hf_vrrp_ip6, tvb, offset, 16, ENC_NA); |
199 | 67 | offset += 16; |
200 | 352 | } else { |
201 | 352 | proto_tree_add_item(vrrp_tree, hf_vrrp_ip, tvb, offset, 4, ENC_BIG_ENDIAN); |
202 | 352 | offset += 4; |
203 | 352 | } |
204 | 419 | addr_count--; |
205 | 419 | } |
206 | | |
207 | 32 | if (auth_type == VRRP_AUTH_TYPE_SIMPLE_TEXT) { |
208 | 1 | proto_tree_add_item(vrrp_tree, hf_vrrp_auth_string, tvb, offset, VRRP_AUTH_DATA_LEN, ENC_ASCII); |
209 | 1 | offset += VRRP_AUTH_DATA_LEN; |
210 | 31 | } else if (auth_type == VRRP_AUTH_TYPE_IP_MD5) { |
211 | 2 | if (vrrp_len - offset >= 16) { |
212 | 1 | proto_tree_add_item(vrrp_tree, hf_vrrp_md5_auth_data, tvb, vrrp_len - 16, 16, ENC_NA); |
213 | 1 | } |
214 | 2 | } |
215 | | |
216 | 32 | return offset; |
217 | 32 | } |
218 | | |
219 | | |
220 | | void proto_register_vrrp(void) |
221 | 16 | { |
222 | 16 | static hf_register_info hf[] = { |
223 | 16 | { &hf_vrrp_ver_type, |
224 | 16 | {"VRRP message version and type", "vrrp.typever", |
225 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
226 | 16 | NULL, HFILL }}, |
227 | | |
228 | 16 | { &hf_vrrp_version, |
229 | 16 | {"VRRP protocol version", "vrrp.version", |
230 | 16 | FT_UINT8, BASE_DEC, NULL, VRRP_VERSION_MASK, |
231 | 16 | NULL, HFILL }}, |
232 | | |
233 | 16 | { &hf_vrrp_type, |
234 | 16 | {"VRRP packet type", "vrrp.type", |
235 | 16 | FT_UINT8, BASE_DEC, VALS(vrrp_type_vals), VRRP_TYPE_MASK, |
236 | 16 | NULL, HFILL }}, |
237 | | |
238 | 16 | { &hf_vrrp_virt_rtr_id, |
239 | 16 | {"Virtual Rtr ID", "vrrp.virt_rtr_id", |
240 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
241 | 16 | "Virtual router this packet is reporting status for", HFILL }}, |
242 | | |
243 | 16 | { &hf_vrrp_prio, |
244 | 16 | {"Priority", "vrrp.prio", |
245 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
246 | 16 | "Sending VRRP router's priority for the virtual router", HFILL }}, |
247 | | |
248 | 16 | { &hf_vrrp_addr_count, |
249 | 16 | {"Addr Count", "vrrp.addr_count", |
250 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
251 | 16 | "The number of addresses contained in this VRRP advertisement", HFILL }}, |
252 | | |
253 | 16 | { &hf_vrrp_checksum, |
254 | 16 | { "Checksum", "vrrp.checksum", |
255 | 16 | FT_UINT16, BASE_HEX, NULL, 0x0, |
256 | 16 | "Used to detect data corruption in the VRRP message", HFILL }}, |
257 | | |
258 | 16 | { &hf_vrrp_checksum_status, |
259 | 16 | { "Checksum Status", "vrrp.checksum.status", |
260 | 16 | FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, |
261 | 16 | NULL, HFILL }}, |
262 | | |
263 | 16 | { &hf_vrrp_auth_type, |
264 | 16 | {"Auth Type", "vrrp.auth_type", |
265 | 16 | FT_UINT8, BASE_DEC, VALS(vrrp_auth_vals), 0x0, |
266 | 16 | "The authentication method being utilized", HFILL }}, |
267 | | |
268 | 16 | { &hf_vrrp_adver_int, |
269 | 16 | {"Adver Int", "vrrp.adver_int", |
270 | 16 | FT_UINT8, BASE_DEC, NULL, 0x0, |
271 | 16 | "Time interval (in seconds) between ADVERTISEMENTS", HFILL }}, |
272 | | |
273 | 16 | { &hf_vrrp_reserved_mbz, |
274 | 16 | {"Reserved", "vrrp.reserved_mbz", |
275 | 16 | FT_UINT8, BASE_DEC, NULL, 0xF0, |
276 | 16 | "Must be zero", HFILL }}, |
277 | | |
278 | 16 | { &hf_vrrp_short_adver_int, |
279 | 16 | {"Adver Int", "vrrp.short_adver_int", |
280 | 16 | FT_UINT16, BASE_DEC, NULL, 0x0FFF, |
281 | 16 | "Time interval (in centiseconds) between ADVERTISEMENTS", HFILL }}, |
282 | | |
283 | 16 | { &hf_vrrp_ip, |
284 | 16 | {"IP Address", "vrrp.ip_addr", |
285 | 16 | FT_IPv4, BASE_NONE, NULL, 0x0, |
286 | 16 | "IP address associated with the virtual router", HFILL }}, |
287 | | |
288 | 16 | { &hf_vrrp_ip6, |
289 | 16 | {"IPv6 Address", "vrrp.ipv6_addr", |
290 | 16 | FT_IPv6, BASE_NONE, NULL, 0x0, |
291 | 16 | "IPv6 address associated with the virtual router", HFILL }}, |
292 | | |
293 | 16 | { &hf_vrrp_auth_string, |
294 | 16 | {"Authentication String", "vrrp.auth_string", |
295 | 16 | FT_STRING, BASE_NONE, NULL, 0x0, |
296 | 16 | NULL, HFILL }}, |
297 | | |
298 | 16 | { &hf_vrrp_md5_auth_data, |
299 | 16 | {"MD5 Authentication Data", "vrrp.md5_auth_data", |
300 | 16 | FT_BYTES, BASE_NONE, NULL, 0x0, |
301 | 16 | "MD5 digest string is contained.", HFILL }}, |
302 | 16 | }; |
303 | | |
304 | 16 | static int *ett[] = { |
305 | 16 | &ett_vrrp, |
306 | 16 | &ett_vrrp_ver_type |
307 | 16 | }; |
308 | | |
309 | 16 | static ei_register_info ei[] = { |
310 | 16 | { &ei_vrrp_checksum, { "vrrp.checksum_bad.expert", PI_CHECKSUM, PI_WARN, "Bad checksum", EXPFILL }}, |
311 | 16 | }; |
312 | | |
313 | 16 | expert_module_t* expert_vrrp; |
314 | 16 | module_t *vrrp_module; |
315 | | |
316 | 16 | proto_vrrp = proto_register_protocol("Virtual Router Redundancy Protocol", "VRRP", "vrrp"); |
317 | 16 | proto_register_field_array(proto_vrrp, hf, array_length(hf)); |
318 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
319 | | |
320 | 16 | vrrp_handle = register_dissector("vrrp", dissect_vrrp, proto_vrrp); |
321 | | |
322 | 16 | expert_vrrp = expert_register_protocol(proto_vrrp); |
323 | 16 | expert_register_field_array(expert_vrrp, ei, array_length(ei)); |
324 | | |
325 | 16 | vrrp_module = prefs_register_protocol(proto_vrrp, NULL); |
326 | | |
327 | 16 | prefs_register_bool_preference(vrrp_module, "v3_checksum_as_in_v2", |
328 | 16 | "Calculate V3 checksum as in V2 for IPv4 packets", |
329 | 16 | "There was some ambiguity on how to calculate IPv4 V3 checksums. " |
330 | 16 | "As in v2 will not use a pseudo header (some manufacturers add a pseudo header for IPv4 checksum " |
331 | 16 | "since RFC5798 was ambiguous). RFC9568 specifies that there is no pseudo header for IPv4.", |
332 | 16 | &g_vrrp_v3_checksum_as_in_v2); |
333 | 16 | } |
334 | | |
335 | | void |
336 | | proto_reg_handoff_vrrp(void) |
337 | 16 | { |
338 | 16 | dissector_add_uint("ip.proto", IP_PROTO_VRRP, vrrp_handle); |
339 | 16 | } |
340 | | |
341 | | /* |
342 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
343 | | * |
344 | | * Local variables: |
345 | | * c-basic-offset: 4 |
346 | | * tab-width: 8 |
347 | | * indent-tabs-mode: nil |
348 | | * End: |
349 | | * |
350 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
351 | | * :indentSize=4:tabSize=8:noTabs=true: |
352 | | */ |