Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/hsrp.py: 82%
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 (
92 self.underlayer is not None
93 and getattr(self.underlayer, "len", None) is not None
94 and self.underlayer.len > 28
95 ):
96 return HSRPmd5
97 return Packet.guess_payload_class(self, payload)
100class HSRPAdvertise(Packet):
101 name = "HSRP Advertise"
102 fields_desc = [
103 ByteField("version", 0),
104 ByteEnumField("opcode", 3, _HSRP_OPCODES),
105 ShortEnumField("type", 1, _HSRP_ADVERTISE_TYPES),
106 ShortField("length", 10),
107 ByteEnumField("state", 1, _HSRP_ADVERTISE_STATES),
108 ByteField("reserved1", 0),
109 ShortField("activegroups", 0),
110 ShortField("passivegroups", 0),
111 IntField("reserved2", 0),
112 ]
114 @classmethod
115 def dispatch_hook(cls, _pkt=None, *args, **kargs):
116 if _pkt and len(_pkt) >= 2 and _pkt[1] != 3:
117 return HSRP
118 return cls
121class HSRPv2(Packet):
122 """
123 HSRP version 2.
124 """
126 name = "HSRPv2"
127 fields_desc = [
128 ByteEnumField("type", 1, {1: "Group state TLV"}),
129 ByteField("len", None),
130 ByteField("version", 2),
131 ByteEnumField(
132 "opcode", 0, {0: "Hello", 1: "Coup", 2: "Resign", 3: "Advertise"}
133 ),
134 ByteEnumField(
135 "state",
136 16,
137 {
138 0: "Initial",
139 1: "Learn",
140 2: "Listen",
141 4: "Speak",
142 8: "Standby",
143 16: "Active",
144 },
145 ),
146 ByteEnumField("ipVer", None, {4: "IPv4", 6: "IPv6"}),
147 XShortField("group", 1),
148 SourceMACField("identifier"),
149 IntField("priority", 100),
150 IntField("hellotime", 3000),
151 IntField("holdtime", 10000),
152 MultipleTypeField(
153 [
154 (
155 IPField("virtualIP", "0.0.0.0"),
156 (
157 lambda p: p.ipVer == 4,
158 lambda p, val: p.ipVer != 6 and (val is None or valid_net(val)),
159 ),
160 ),
161 (
162 IP6Field("virtualIP", "::"),
163 (
164 lambda p: p.ipVer == 6,
165 lambda p, val: p.ipVer != 4
166 and (val is None or valid_net6(val)),
167 ),
168 ),
169 ],
170 StrField("virtualIP", None), # By default
171 ),
172 # The virtualIP field's expected size is always the size of an IPv6
173 # address. If IPv4 is used, padding is required.
174 ConditionalField(
175 StrFixedLenField("padding", b"\x00" * 12, 12),
176 lambda pkt: valid_net(pkt.virtualIP),
177 ),
178 ]
180 def post_build(self, pkt, pay):
181 if self.ipVer is None:
182 ip_ver = 4
184 if valid_net6(self.virtualIP):
185 ip_ver = 6
187 pkt = pkt[:5] + struct.pack("B", ip_ver) + pkt[6:]
189 if self.len is None:
190 pkt = pkt[:1] + struct.pack("B", len(pkt) - 2) + pkt[2:]
191 return pkt + pay
193 def guess_payload_class(self, payload):
194 if payload:
195 hsrp_auth_payload_type = payload[0]
196 if hsrp_auth_payload_type == 3:
197 return HSRPv2TextAuth
198 elif hsrp_auth_payload_type == 4:
199 return HSRPmd5
200 return Packet.guess_payload_class(self, payload)
203class HSRPmd5(Packet):
204 """
205 MD5 Authentication header for HSRP (version 1 and 2).
206 """
208 name = "HSRP MD5 Authentication"
209 fields_desc = [
210 ByteEnumField("type", 4, {4: "MD5 authentication"}),
211 ByteField("len", None),
212 ByteEnumField("algo", 1, {1: "MD5"}),
213 ByteField("padding", 0x00),
214 XShortField("flags", 0x00),
215 SourceIPField("sourceip"),
216 XIntField("keyid", 0x00),
217 StrFixedLenField("authdigest", b"\x00" * 16, 16),
218 ]
220 def post_build(self, pkt, pay):
221 if self.len is None:
222 pkt = pkt[:1] + struct.pack("B", len(pkt) - 2) + pkt[2:]
223 return pkt + pay
226class HSRPv2TextAuth(Packet):
227 """
228 Default plain text authentication header.
229 This is only used with HSRP version 2.
230 """
232 name = "HSRP Authentication"
233 fields_desc = [
234 ByteEnumField("type", 3, {3: "Text Authentication TLV"}),
235 ByteField("len", 8),
236 StrFixedLenField("auth", b"cisco" + b"\x00" * 3, 8),
237 ]
240bind_bottom_up(UDP, HSRP, dport=1985)
241bind_bottom_up(UDP, HSRP, sport=1985)
242bind_bottom_up(UDP, HSRPv2, dport=1985)
243bind_bottom_up(UDP, HSRPv2, sport=1985)
244bind_bottom_up(UDP, HSRPv2, dport=2029)
245bind_bottom_up(UDP, HSRPv2, sport=2029)
247bind_layers(UDP, HSRP, dport=1985, sport=1985)
248bind_layers(UDP, HSRPv2, dport=1985, sport=1985) # HSRP v2 with IPv4
249bind_layers(UDP, HSRPv2, dport=2029, sport=2029) # HSRP v2 with IPv6
251DestIPField.bind_addr(UDP, "224.0.0.2", dport=1985)
252DestIPField.bind_addr(UDP, "224.0.0.102", dport=1985)
254if conf.ipv6_enabled:
255 from scapy.layers.inet6 import DestIP6Field
257 DestIP6Field.bind_addr(UDP, "ff02::66", dport=2029)