2 * Routines for the Virtual Router Redundancy Protocol (VRRP)
4 * VRRPv2: RFC3768 (superseeding RFC2338)
7 * Heikki Vatiainen <hessu@cs.tut.fi>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <epan/packet.h>
19 #include <epan/ipproto.h>
20 #include <epan/in_cksum.h>
21 #include <epan/expert.h>
22 #include <epan/prefs.h>
24 void proto_register_vrrp(void);
25 void proto_reg_handoff_vrrp(void);
27 static dissector_handle_t vrrp_handle
;
29 static int proto_vrrp
;
31 static int ett_vrrp_ver_type
;
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
;
50 static bool g_vrrp_v3_checksum_as_in_v2
= true;
52 static expert_field ei_vrrp_checksum
;
54 #define VRRP_VERSION_MASK 0xf0
55 #define VRRP_TYPE_MASK 0x0f
56 #define VRRP_AUTH_DATA_LEN 8
58 #define VRRP_TYPE_ADVERTISEMENT 1
59 static const value_string vrrp_type_vals
[] = {
60 {VRRP_TYPE_ADVERTISEMENT
, "Advertisement"},
64 #define VRRP_AUTH_TYPE_NONE 0
65 #define VRRP_AUTH_TYPE_SIMPLE_TEXT 1
66 #define VRRP_AUTH_TYPE_IP_AUTH_HDR 2
67 #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"},
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)"},
89 dissect_vrrp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
98 proto_tree
*vrrp_tree
, *ver_type_tree
;
99 uint8_t priority
, addr_count
= 0, auth_type
= VRRP_AUTH_TYPE_NONE
;
100 uint16_t computed_cksum
= 0;
102 is_ipv6
= (pinfo
->src
.type
== AT_IPv6
);
104 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "VRRP");
105 col_clear(pinfo
->cinfo
, COL_INFO
);
107 ver_type
= tvb_get_uint8(tvb
, 0);
108 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Announcement (v%u)",
109 hi_nibble(ver_type
));
111 ti
= proto_tree_add_item(tree
, proto_vrrp
, tvb
, 0, -1, ENC_NA
);
112 vrrp_tree
= proto_item_add_subtree(ti
, ett_vrrp
);
114 priority
= tvb_get_uint8(tvb
, 2);
115 addr_count
= tvb_get_uint8(tvb
, 3);
117 tv
= proto_tree_add_uint_format(vrrp_tree
, hf_vrrp_ver_type
,
118 tvb
, offset
, 1, ver_type
,
119 "Version %u, Packet type %u (%s)",
120 hi_nibble(ver_type
), lo_nibble(ver_type
),
121 val_to_str_const(lo_nibble(ver_type
), vrrp_type_vals
, "Unknown"));
122 ver_type_tree
= proto_item_add_subtree(tv
, ett_vrrp_ver_type
);
125 proto_tree_add_uint(ver_type_tree
, hf_vrrp_version
, tvb
,
126 offset
, 1, ver_type
);
127 proto_tree_add_uint(ver_type_tree
, hf_vrrp_type
, tvb
, offset
, 1, ver_type
);
130 proto_tree_add_item(vrrp_tree
, hf_vrrp_virt_rtr_id
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
133 proto_tree_add_uint_format(vrrp_tree
, hf_vrrp_prio
, tvb
, offset
, 1, priority
, "Priority: %u (%s)",
135 val_to_str_const(priority
, vrrp_prio_vals
, "Non-default backup priority"));
138 proto_tree_add_uint(vrrp_tree
, hf_vrrp_addr_count
, tvb
,
139 offset
, 1, addr_count
);
142 switch(hi_nibble(ver_type
)) {
144 /* 4 bits reserved (mbz) + 12 bits interval */
145 proto_tree_add_item(vrrp_tree
, hf_vrrp_reserved_mbz
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
146 proto_tree_add_item(vrrp_tree
, hf_vrrp_short_adver_int
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
151 /* 1 byte auth type + 1 byte interval */
152 auth_type
= tvb_get_uint8(tvb
, offset
);
153 proto_tree_add_item(vrrp_tree
, hf_vrrp_auth_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
156 proto_tree_add_item(vrrp_tree
, hf_vrrp_adver_int
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
164 vrrp_len
= (int)tvb_reported_length(tvb
);
165 if (!pinfo
->fragmented
&& (int)tvb_captured_length(tvb
) >= vrrp_len
) {
166 /* The packet isn't part of a fragmented datagram
167 and isn't truncated, so we can checksum it. */
168 switch(hi_nibble(ver_type
)) {
170 if((g_vrrp_v3_checksum_as_in_v2
== false)||(pinfo
->src
.type
== AT_IPv6
)){
171 /* Set up the fields of the pseudo-header. */
172 SET_CKSUM_VEC_PTR(cksum_vec
[0], (const uint8_t *)pinfo
->src
.data
, pinfo
->src
.len
);
173 SET_CKSUM_VEC_PTR(cksum_vec
[1], (const uint8_t *)pinfo
->dst
.data
, pinfo
->dst
.len
);
174 phdr
[0] = g_htonl(vrrp_len
);
175 phdr
[1] = g_htonl(IP_PROTO_VRRP
);
176 SET_CKSUM_VEC_PTR(cksum_vec
[2], (const uint8_t *)&phdr
, 8);
177 SET_CKSUM_VEC_TVB(cksum_vec
[3], tvb
, 0, vrrp_len
);
178 computed_cksum
= in_cksum(cksum_vec
, 4);
184 SET_CKSUM_VEC_TVB(cksum_vec
[0], tvb
, 0, vrrp_len
);
185 computed_cksum
= in_cksum(&cksum_vec
[0], 1);
189 proto_tree_add_checksum(vrrp_tree
, tvb
, offset
, hf_vrrp_checksum
, hf_vrrp_checksum_status
, &ei_vrrp_checksum
, pinfo
, computed_cksum
,
190 ENC_BIG_ENDIAN
, PROTO_CHECKSUM_VERIFY
|PROTO_CHECKSUM_IN_CKSUM
);
192 proto_tree_add_checksum(vrrp_tree
, tvb
, offset
, hf_vrrp_checksum
, hf_vrrp_checksum_status
, &ei_vrrp_checksum
, pinfo
, 0,
193 ENC_BIG_ENDIAN
, PROTO_CHECKSUM_NO_FLAGS
);
197 while (addr_count
> 0) {
199 proto_tree_add_item(vrrp_tree
, hf_vrrp_ip6
, tvb
, offset
, 16, ENC_NA
);
202 proto_tree_add_item(vrrp_tree
, hf_vrrp_ip
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
208 if (auth_type
== VRRP_AUTH_TYPE_SIMPLE_TEXT
) {
209 proto_tree_add_item(vrrp_tree
, hf_vrrp_auth_string
, tvb
, offset
, VRRP_AUTH_DATA_LEN
, ENC_ASCII
);
210 offset
+= VRRP_AUTH_DATA_LEN
;
211 } else if (auth_type
== VRRP_AUTH_TYPE_IP_MD5
) {
212 if (vrrp_len
- offset
>= 16) {
213 proto_tree_add_item(vrrp_tree
, hf_vrrp_md5_auth_data
, tvb
, vrrp_len
- 16, 16, ENC_NA
);
221 void proto_register_vrrp(void)
223 static hf_register_info hf
[] = {
225 {"VRRP message version and type", "vrrp.typever",
226 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
230 {"VRRP protocol version", "vrrp.version",
231 FT_UINT8
, BASE_DEC
, NULL
, VRRP_VERSION_MASK
,
235 {"VRRP packet type", "vrrp.type",
236 FT_UINT8
, BASE_DEC
, VALS(vrrp_type_vals
), VRRP_TYPE_MASK
,
239 { &hf_vrrp_virt_rtr_id
,
240 {"Virtual Rtr ID", "vrrp.virt_rtr_id",
241 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
242 "Virtual router this packet is reporting status for", HFILL
}},
245 {"Priority", "vrrp.prio",
246 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
247 "Sending VRRP router's priority for the virtual router", HFILL
}},
249 { &hf_vrrp_addr_count
,
250 {"Addr Count", "vrrp.addr_count",
251 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
252 "The number of addresses contained in this VRRP advertisement", HFILL
}},
255 { "Checksum", "vrrp.checksum",
256 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
257 "Used to detect data corruption in the VRRP message", HFILL
}},
259 { &hf_vrrp_checksum_status
,
260 { "Checksum Status", "vrrp.checksum.status",
261 FT_UINT8
, BASE_NONE
, VALS(proto_checksum_vals
), 0x0,
264 { &hf_vrrp_auth_type
,
265 {"Auth Type", "vrrp.auth_type",
266 FT_UINT8
, BASE_DEC
, VALS(vrrp_auth_vals
), 0x0,
267 "The authentication method being utilized", HFILL
}},
269 { &hf_vrrp_adver_int
,
270 {"Adver Int", "vrrp.adver_int",
271 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
272 "Time interval (in seconds) between ADVERTISEMENTS", HFILL
}},
274 { &hf_vrrp_reserved_mbz
,
275 {"Reserved", "vrrp.reserved_mbz",
276 FT_UINT8
, BASE_DEC
, NULL
, 0xF0,
277 "Must be zero", HFILL
}},
279 { &hf_vrrp_short_adver_int
,
280 {"Adver Int", "vrrp.short_adver_int",
281 FT_UINT16
, BASE_DEC
, NULL
, 0x0FFF,
282 "Time interval (in centiseconds) between ADVERTISEMENTS", HFILL
}},
285 {"IP Address", "vrrp.ip_addr",
286 FT_IPv4
, BASE_NONE
, NULL
, 0x0,
287 "IP address associated with the virtual router", HFILL
}},
290 {"IPv6 Address", "vrrp.ipv6_addr",
291 FT_IPv6
, BASE_NONE
, NULL
, 0x0,
292 "IPv6 address associated with the virtual router", HFILL
}},
294 { &hf_vrrp_auth_string
,
295 {"Authentication String", "vrrp.auth_string",
296 FT_STRING
, BASE_NONE
, NULL
, 0x0,
299 { &hf_vrrp_md5_auth_data
,
300 {"MD5 Authentication Data", "vrrp.md5_auth_data",
301 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
302 "MD5 digest string is contained.", HFILL
}},
305 static int *ett
[] = {
310 static ei_register_info ei
[] = {
311 { &ei_vrrp_checksum
, { "vrrp.checksum_bad.expert", PI_CHECKSUM
, PI_WARN
, "Bad checksum", EXPFILL
}},
314 expert_module_t
* expert_vrrp
;
315 module_t
*vrrp_module
;
317 proto_vrrp
= proto_register_protocol("Virtual Router Redundancy Protocol", "VRRP", "vrrp");
318 proto_register_field_array(proto_vrrp
, hf
, array_length(hf
));
319 proto_register_subtree_array(ett
, array_length(ett
));
321 vrrp_handle
= register_dissector("vrrp", dissect_vrrp
, proto_vrrp
);
323 expert_vrrp
= expert_register_protocol(proto_vrrp
);
324 expert_register_field_array(expert_vrrp
, ei
, array_length(ei
));
326 vrrp_module
= prefs_register_protocol(proto_vrrp
, NULL
);
328 prefs_register_bool_preference(vrrp_module
, "v3_checksum_as_in_v2",
329 "Calculate V3 checksum as in V2 for IPv4 packets",
330 "There was some ambiguity on how to calculate IPv4 V3 checksums. "
331 "As in v2 will not use a pseudo header (some manufacturers add a pseudo header for IPv4 checksum "
332 "since RFC5798 was ambiguous). RFC9568 specifies that there is no pseudo header for IPv4.",
333 &g_vrrp_v3_checksum_as_in_v2
);
337 proto_reg_handoff_vrrp(void)
339 dissector_add_uint("ip.proto", IP_PROTO_VRRP
, vrrp_handle
);
343 * Editor modelines - https://www.wireshark.org/tools/modelines.html
348 * indent-tabs-mode: nil
351 * vi: set shiftwidth=4 tabstop=8 expandtab:
352 * :indentSize=4:tabSize=8:noTabs=true: