Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/hsrp.py: 68%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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) Mathieu RENARD <mathieu.renard(at)gmail.com>
6# scapy.contrib.description = Hot Standby Router Protocol (HSRP)
7# scapy.contrib.status = loads
9"""
10HSRP (Hot Standby Router Protocol)
11A proprietary redundancy protocol for Cisco routers.
13- HSRP Version 1:
14 https://tools.ietf.org/html/rfc2281
15- HSRP Version 2:
16 http://www.smartnetworks.jp/2006/02/hsrp_8_hsrp_version_2.html
17"""
19import struct
20from scapy.config import conf
21from scapy.fields import (
22 ByteEnumField,
23 ByteField,
24 ConditionalField,
25 IntField,
26 IP6Field,
27 IPField,
28 MultipleTypeField,
29 ShortEnumField,
30 ShortField,
31 SourceIPField,
32 StrField,
33 StrFixedLenField,
34 XIntField,
35 XShortField,
36)
37from scapy.packet import Packet, bind_layers, bind_bottom_up
38from scapy.utils import valid_net, valid_net6
40from scapy.layers.l2 import SourceMACField
41from scapy.layers.inet import DestIPField, UDP
43_HSRP_OPCODES = {0: "Hello", 1: "Coup", 2: "Resign", 3: "Advertise"}
44_HSRP_STATES = {
45 0: "Initial",
46 1: "Learn",
47 2: "Listen",
48 4: "Speak",
49 8: "Standby",
50 16: "Active",
51}
52_HSRP_ADVERTISE_TYPES = {1: "HSRP interface state"}
53_HSRP_ADVERTISE_STATES = {1: "Active", 2: "Passive"}
56class HSRP(Packet):
57 """
58 HSRP version 1
59 """
61 name = "HSRPv1"
62 fields_desc = [
63 ByteField("version", 0),
64 ByteEnumField("opcode", 0, _HSRP_OPCODES),
65 ByteEnumField("state", 16, _HSRP_STATES),
66 ByteField("hellotime", 3),
67 ByteField("holdtime", 10),
68 ByteField("priority", 120),
69 ByteField("group", 1),
70 ByteField("reserved", 0),
71 StrFixedLenField("auth", b"cisco" + b"\x00" * 3, 8),
72 IPField("virtualIP", "192.168.1.1"),
73 ]
75 @classmethod
76 def dispatch_hook(cls, _pkt=None, *args, **kargs):
77 """
78 Dissects proper version of HSRP packet
79 based on the first byte.
80 """
81 if _pkt:
82 if _pkt[0] == 0:
83 if len(_pkt) >= 2 and _pkt[1] == 3:
84 return HSRPAdvertise
85 return HSRP
86 if _pkt[0] == 1:
87 return HSRPv2
88 return HSRP
90 def guess_payload_class(self, payload):
91 if self.underlayer.len > 28:
92 return HSRPmd5
93 else:
94 return Packet.guess_payload_class(self, payload)
97class HSRPAdvertise(Packet):
98 name = "HSRP Advertise"
99 fields_desc = [
100 ByteField("version", 0),
101 ByteEnumField("opcode", 3, _HSRP_OPCODES),
102 ShortEnumField("type", 1, _HSRP_ADVERTISE_TYPES),
103 ShortField("length", 10),
104 ByteEnumField("state", 1, _HSRP_ADVERTISE_STATES),
105 ByteField("reserved1", 0),
106 ShortField("activegroups", 0),
107 ShortField("passivegroups", 0),
108 IntField("reserved2", 0),
109 ]
111 @classmethod
112 def dispatch_hook(cls, _pkt=None, *args, **kargs):
113 if _pkt and len(_pkt) >= 2 and _pkt[1] != 3:
114 return HSRP
115 return cls
118class HSRPv2(Packet):
119 """
120 HSRP version 2.
121 """
123 name = "HSRPv2"
124 fields_desc = [
125 ByteEnumField("type", 1, {1: "Group state TLV"}),
126 ByteField("len", None),
127 ByteField("version", 2),
128 ByteEnumField(
129 "opcode", 0, {0: "Hello", 1: "Coup", 2: "Resign", 3: "Advertise"}
130 ),
131 ByteEnumField(
132 "state",
133 16,
134 {
135 0: "Initial",
136 1: "Learn",
137 2: "Listen",
138 4: "Speak",
139 8: "Standby",
140 16: "Active",
141 },
142 ),
143 ByteEnumField("ipVer", None, {4: "IPv4", 6: "IPv6"}),
144 XShortField("group", 1),
145 SourceMACField("identifier"),
146 IntField("priority", 100),
147 IntField("hellotime", 3000),
148 IntField("holdtime", 10000),
149 MultipleTypeField(
150 [
151 (
152 IPField("virtualIP", "0.0.0.0"),
153 (
154 lambda p: p.ipVer == 4,
155 lambda p, val: p.ipVer != 6 and (val is None or valid_net(val)),
156 ),
157 ),
158 (
159 IP6Field("virtualIP", "::"),
160 (
161 lambda p: p.ipVer == 6,
162 lambda p, val: p.ipVer != 4
163 and (val is None or valid_net6(val)),
164 ),
165 ),
166 ],
167 StrField("virtualIP", None), # By default
168 ),
169 # The virtualIP field's expected size is always the size of an IPv6
170 # address. If IPv4 is used, padding is required.
171 ConditionalField(
172 StrFixedLenField("padding", b"\x00" * 12, 12),
173 lambda pkt: valid_net(pkt.virtualIP),
174 ),
175 ]
177 def post_build(self, pkt, pay):
178 if self.ipVer is None:
179 ip_ver = 4
181 if valid_net6(self.virtualIP):
182 ip_ver = 6
184 pkt = pkt[:5] + struct.pack("B", ip_ver) + pkt[6:]
186 if self.len is None:
187 pkt = pkt[:1] + struct.pack("B", len(pkt) - 2) + pkt[2:]
188 return pkt + pay
190 def guess_payload_class(self, payload):
191 if payload:
192 hsrp_auth_payload_type = payload[0]
193 if hsrp_auth_payload_type == 3:
194 return HSRPv2TextAuth
195 elif hsrp_auth_payload_type == 4:
196 return HSRPmd5
197 return Packet.guess_payload_class(self, payload)
200class HSRPmd5(Packet):
201 """
202 MD5 Authentication header for HSRP (version 1 and 2).
203 """
205 name = "HSRP MD5 Authentication"
206 fields_desc = [
207 ByteEnumField("type", 4, {4: "MD5 authentication"}),
208 ByteField("len", None),
209 ByteEnumField("algo", 1, {1: "MD5"}),
210 ByteField("padding", 0x00),
211 XShortField("flags", 0x00),
212 SourceIPField("sourceip"),
213 XIntField("keyid", 0x00),
214 StrFixedLenField("authdigest", b"\x00" * 16, 16),
215 ]
217 def post_build(self, pkt, pay):
218 if self.len is None:
219 pkt = pkt[:1] + struct.pack("B", len(pkt) - 2) + pkt[2:]
220 return pkt + pay
223class HSRPv2TextAuth(Packet):
224 """
225 Default plain text authentication header.
226 This is only used with HSRP version 2.
227 """
229 name = "HSRP Authentication"
230 fields_desc = [
231 ByteEnumField("type", 3, {3: "Text Authentication TLV"}),
232 ByteField("len", 8),
233 StrFixedLenField("auth", b"cisco" + b"\x00" * 3, 8),
234 ]
237bind_bottom_up(UDP, HSRP, dport=1985)
238bind_bottom_up(UDP, HSRP, sport=1985)
239bind_bottom_up(UDP, HSRPv2, dport=1985)
240bind_bottom_up(UDP, HSRPv2, sport=1985)
241bind_bottom_up(UDP, HSRPv2, dport=2029)
242bind_bottom_up(UDP, HSRPv2, sport=2029)
244bind_layers(UDP, HSRP, dport=1985, sport=1985)
245bind_layers(UDP, HSRPv2, dport=1985, sport=1985) # HSRP v2 with IPv4
246bind_layers(UDP, HSRPv2, dport=2029, sport=2029) # HSRP v2 with IPv6
248DestIPField.bind_addr(UDP, "224.0.0.2", dport=1985)
249DestIPField.bind_addr(UDP, "224.0.0.102", dport=1985)
251if conf.ipv6_enabled:
252 from scapy.layers.inet6 import DestIP6Field
254 DestIP6Field.bind_addr(UDP, "ff02::66", dport=2029)