1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5# Copyright (C) 6WIND <olivier.matz@6wind.com>
6
7"""
8VRRP (Virtual Router Redundancy Protocol).
9"""
10
11from scapy.packet import Packet, bind_layers
12from scapy.fields import BitField, ByteField, FieldLenField, FieldListField, \
13 IPField, IP6Field, IntField, MultipleTypeField, StrField, XShortField
14from scapy.compat import chb, orb
15from scapy.layers.inet import IP, in4_chksum, checksum
16from scapy.layers.inet6 import IPv6, in6_chksum
17from scapy.error import warning
18
19IPPROTO_VRRP = 112
20
21# RFC 3768 - Virtual Router Redundancy Protocol (VRRP)
22
23
24class VRRP(Packet):
25 fields_desc = [
26 BitField("version", 2, 4),
27 BitField("type", 1, 4),
28 ByteField("vrid", 1),
29 ByteField("priority", 100),
30 FieldLenField("ipcount", None, count_of="addrlist", fmt="B"),
31 ByteField("authtype", 0),
32 ByteField("adv", 1),
33 XShortField("chksum", None),
34 FieldListField("addrlist", [], IPField("", "0.0.0.0"),
35 count_from=lambda pkt: pkt.ipcount),
36 IntField("auth1", 0),
37 IntField("auth2", 0)]
38
39 def post_build(self, p, pay):
40 if self.chksum is None:
41 ck = checksum(p)
42 p = p[:6] + chb(ck >> 8) + chb(ck & 0xff) + p[8:]
43 return p
44
45 @classmethod
46 def dispatch_hook(cls, _pkt=None, *args, **kargs):
47 if _pkt and len(_pkt) >= 9:
48 ver_n_type = orb(_pkt[0])
49 if ver_n_type >= 48 and ver_n_type <= 57: # Version == 3
50 return VRRPv3
51 return VRRP
52
53
54# RFC 5798 - Virtual Router Redundancy Protocol (VRRP) Version 3
55class VRRPv3(Packet):
56 fields_desc = [
57 BitField("version", 3, 4),
58 BitField("type", 1, 4),
59 ByteField("vrid", 1),
60 ByteField("priority", 100),
61 FieldLenField("ipcount", None, count_of="addrlist", fmt="B"),
62 BitField("res", 0, 4),
63 BitField("adv", 100, 12),
64 XShortField("chksum", None),
65 MultipleTypeField(
66 [
67 (FieldListField("addrlist", [], IPField("", "0.0.0.0"),
68 count_from=lambda pkt: pkt.ipcount),
69 lambda p: isinstance(p.underlayer, IP)),
70 (FieldListField("addrlist", [], IP6Field("", "::"),
71 count_from=lambda pkt: pkt.ipcount),
72 lambda p: isinstance(p.underlayer, IPv6)),
73 ],
74 StrField("addrlist", "")
75 )
76 ]
77
78 def post_build(self, p, pay):
79 if self.chksum is None:
80 if isinstance(self.underlayer, IP):
81 ck = in4_chksum(112, self.underlayer, p)
82 elif isinstance(self.underlayer, IPv6):
83 ck = in6_chksum(112, self.underlayer, p)
84 else:
85 warning("No IP(v6) layer to compute checksum on VRRP. "
86 "Leaving null")
87 ck = 0
88 p = p[:6] + chb(ck >> 8) + chb(ck & 0xff) + p[8:]
89 return p
90
91 @classmethod
92 def dispatch_hook(cls, _pkt=None, *args, **kargs):
93 if _pkt and len(_pkt) >= 16:
94 ver_n_type = orb(_pkt[0])
95 if ver_n_type < 48 or ver_n_type > 57: # Version != 3
96 return VRRP
97 return VRRPv3
98
99
100# IPv6 is supported only on VRRPv3
101# Warning: those layers need to be un-binded in the CARP contrib module.
102# If you add/remove any, remember to also edit the one in CARP.py
103bind_layers(IP, VRRP, proto=IPPROTO_VRRP)
104bind_layers(IP, VRRPv3, proto=IPPROTO_VRRP)
105bind_layers(IPv6, VRRPv3, nh=IPPROTO_VRRP)