1 #!/usr/bin/env python 2 3 # This file is part of Scapy 4 # Scapy is free software: you can redistribute it and/or modify 5 # it under the terms of the GNU General Public License as published by 6 # the Free Software Foundation, either version 2 of the License, or 7 # any later version. 8 # 9 # Scapy is distributed in the hope that it will be useful, 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # GNU General Public License for more details. 13 # 14 # You should have received a copy of the GNU General Public License 15 # along with Scapy. If not, see <http://www.gnu.org/licenses/>. 16 17 # scapy.contrib.description = EIGRP 18 # scapy.contrib.status = loads 19 20 """ 21 EIGRP Scapy Extension 22 ~~~~~~~~~~~~~~~~~~~~~ 23 24 :version: 2009-08-13 25 :copyright: 2009 by Jochen Bartl 26 :e-mail: lobo (at] c3a.de / jochen.bartl (at] gmail.com 27 :license: GPL v2 28 29 :TODO 30 31 - Replace TLV code with a more generic solution 32 * http://trac.secdev.org/scapy/ticket/90 33 - Write function for calculating authentication data 34 35 :Known bugs: 36 37 - 38 39 :Thanks: 40 41 - TLV code derived from the CDP implementation of scapy. (Thanks to Nicolas Bareil and Arnaud Ebalard) 42 http://trac.secdev.org/scapy/ticket/18 43 - IOS / EIGRP Version Representation FIX by Dirk Loss 44 """ 45 46 from __future__ import absolute_import 47 from scapy.packet import * 48 from scapy.fields import * 49 from scapy.layers.inet import IP 50 from scapy.layers.inet6 import * 51 from scapy.compat import chb, raw 52 53 class EigrpIPField(StrField, IPField): 54 """ 55 This is a special field type for handling ip addresses of destination networks in internal and 56 external route updates. 57 58 EIGRP removes zeros from the host portion of the ip address if the netmask is 8, 16 or 24 bits. 59 """ 60 61 __slots__ = ["length_from"] 62 63 def __init__(self, name, default, length=None, length_from=None): 64 StrField.__init__(self, name, default) 65 self.length_from = length_from 66 if length is not None: 67 self.length_from = lambda pkt,length=length: length 68 69 def h2i(self, pkt, x): 70 return IPField.h2i(self, pkt, x) 71 72 def i2m(self, pkt, x): 73 x = inet_aton(x) 74 l = self.length_from(pkt) 75 76 if l <= 8: 77 return x[:1] 78 elif l <= 16: 79 return x[:2] 80 elif l <= 24: 81 return x[:3] 82 else: 83 return x 84 85 def m2i(self, pkt, x): 86 l = self.length_from(pkt) 87 88 if l <= 8: 89 x += b"\x00\x00\x00" 90 elif l <= 16: 91 x += b"\x00\x00" 92 elif l <= 24: 93 x += b"\x00" 94 95 return inet_ntoa(x) 96 97 def prefixlen_to_bytelen(self, l): 98 if l <= 8: 99 l = 1 100 elif l <= 16: 101 l = 2 102 elif l <= 24: 103 l = 3 104 else: 105 l = 4 106 107 return l 108 109 def i2len(self, pkt, x): 110 l = self.length_from(pkt) 111 l = self.prefixlen_to_bytelen(l) 112 return l 113 114 def getfield(self, pkt, s): 115 l = self.length_from(pkt) 116 l = self.prefixlen_to_bytelen(l) 117 return s[l:], self.m2i(pkt, s[:l]) 118 119 def randval(self): 120 return IPField.randval(self) 121 122 class EigrpIP6Field(StrField, IP6Field): 123 """ 124 This is a special field type for handling ip addresses of destination networks in internal and 125 external route updates. 126 127 """ 128 129 __slots__ = ["length_from"] 130 131 def __init__(self, name, default, length=None, length_from=None): 132 StrField.__init__(self, name, default) 133 self.length_from = length_from 134 if length is not None: 135 self.length_from = lambda pkt,length=length: length 136 137 def any2i(self, pkt, x): 138 return IP6Field.any2i(self, pkt, x) 139 140 def i2repr(self, pkt, x): 141 return IP6Field.i2repr(self, pkt, x) 142 143 def h2i(self, pkt, x): 144 return IP6Field.h2i(self, pkt, x) 145 146 def i2m(self, pkt, x): 147 x = inet_pton(socket.AF_INET6, x) 148 l = self.length_from(pkt) 149 l = self.prefixlen_to_bytelen(l) 150 151 return x[:l] 152 153 def m2i(self, pkt, x): 154 l = self.length_from(pkt) 155 156 prefixlen = self.prefixlen_to_bytelen(l) 157 if l > 128: 158 warning("EigrpIP6Field: Prefix length is > 128. Dissection of this packet will fail") 159 else: 160 pad = b"\x00" * (16 - prefixlen) 161 x += pad 162 163 return inet_ntop(socket.AF_INET6, x) 164 165 def prefixlen_to_bytelen(self, l): 166 l = l // 8 167 168 if l < 16: 169 l += 1 170 171 return l 172 173 def i2len(self, pkt, x): 174 l = self.length_from(pkt) 175 l = self.prefixlen_to_bytelen(l) 176 return l 177 178 def getfield(self, pkt, s): 179 l = self.length_from(pkt) 180 l = self.prefixlen_to_bytelen(l) 181 return s[l:], self.m2i(pkt, s[:l]) 182 183 def randval(self): 184 return IP6Field.randval(self) 185 186 class EIGRPGeneric(Packet): 187 name = "EIGRP Generic TLV" 188 fields_desc = [ XShortField("type", 0x0000), 189 FieldLenField("len", None, "value", "!H", adjust=lambda pkt,x: x + 4), 190 StrLenField("value", b"\x00", length_from=lambda pkt: pkt.len - 4)] 191 192 def guess_payload_class(self, p): 193 return conf.padding_layer 194 195 class EIGRPParam(EIGRPGeneric): 196 name = "EIGRP Parameters" 197 fields_desc = [ XShortField("type", 0x0001), 198 ShortField("len", 12), 199 # Bandwidth 200 ByteField("k1", 1), 201 # Load 202 ByteField("k2", 0), 203 # Delay 204 ByteField("k3", 1), 205 # Reliability 206 ByteField("k4", 0), 207 # MTU 208 ByteField("k5", 0), 209 ByteField("reserved", 0), 210 ShortField("holdtime", 15) 211 ] 212 213 class EIGRPAuthData(EIGRPGeneric): 214 name = "EIGRP Authentication Data" 215 fields_desc = [ XShortField("type", 0x0002), 216 FieldLenField("len", None, "authdata", "!H", adjust=lambda pkt,x: x + 24), 217 ShortEnumField("authtype", 2, {2 : "MD5"}), 218 ShortField("keysize", None), 219 IntField("keyid", 1), 220 StrFixedLenField("nullpad", b"\x00" * 12, 12), 221 StrLenField("authdata", RandString(16), length_from=lambda pkt: pkt.keysize) 222 ] 223 224 def post_build(self, p, pay): 225 p += pay 226 227 if self.keysize is None: 228 keysize = len(self.authdata) 229 p = p[:6] + chb((keysize >> 8) & 0xff) + chb(keysize & 0xff) + p[8:] 230 231 return p 232 233 class EIGRPSeq(EIGRPGeneric): 234 name = "EIGRP Sequence" 235 fields_desc = [ XShortField("type", 0x0003), 236 ShortField("len", None), 237 ByteField("addrlen", 4), 238 ConditionalField(IPField("ipaddr", "192.168.0.1"), 239 lambda pkt:pkt.addrlen == 4), 240 ConditionalField(IP6Field("ip6addr", "2001::"), 241 lambda pkt:pkt.addrlen == 16) 242 ] 243 244 def post_build(self, p, pay): 245 p += pay 246 247 if self.len is None: 248 l = len(p) 249 p = p[:2] + chb((l >> 8) & 0xff) + chb(l & 0xff) + p[4:] 250 251 return p 252 253 class ShortVersionField(ShortField): 254 def i2repr(self, pkt, x): 255 try: 256 minor = x & 0xff 257 major = (x >> 8) & 0xff 258 except TypeError: 259 return "unknown" 260 else: 261 # We print a leading 'v' so that these values don't look like floats 262 return "v%s.%s" % (major, minor) 263 264 def h2i(self, pkt, x): 265 """The field accepts string values like v12.1, v1.1 or integer values. 266 String values have to start with a "v" folled by a floating point number. 267 Valid numbers are between 0 and 255. 268 """ 269 270 if isinstance(x, str) and x.startswith("v") and len(x) <= 8: 271 major = int(x.split(".")[0][1:]) 272 minor = int(x.split(".")[1]) 273 274 return (major << 8) | minor 275 276 elif isinstance(x, int) and 0 <= x <= 65535: 277 return x 278 else: 279 if self.default != None: 280 warning("set value to default. Format of %r is invalid" % x) 281 return self.default 282 else: 283 raise Scapy_Exception("Format of value is invalid") 284 285 def randval(self): 286 return RandShort() 287 288 class EIGRPSwVer(EIGRPGeneric): 289 name = "EIGRP Software Version" 290 fields_desc = [ XShortField("type", 0x0004), 291 ShortField("len", 8), 292 ShortVersionField("ios", "v12.0"), 293 ShortVersionField("eigrp", "v1.2") 294 ] 295 296 class EIGRPNms(EIGRPGeneric): 297 name = "EIGRP Next Multicast Sequence" 298 fields_desc = [ XShortField("type", 0x0005), 299 ShortField("len", 8), 300 IntField("nms", 2) 301 ] 302 303 # Don't get confused by the term "receive-only". This flag is always set, when you configure 304 # one of the stub options. It's also the only flag set, when you configure "eigrp stub receive-only". 305 _EIGRP_STUB_FLAGS = ["connected", "static", "summary", "receive-only", "redistributed", "leak-map"] 306 307 class EIGRPStub(EIGRPGeneric): 308 name = "EIGRP Stub Router" 309 fields_desc = [ XShortField("type", 0x0006), 310 ShortField("len", 6), 311 FlagsField("flags", 0x000d, 16, _EIGRP_STUB_FLAGS)] 312 313 # Delay 0xffffffff == Destination Unreachable 314 class EIGRPIntRoute(EIGRPGeneric): 315 name = "EIGRP Internal Route" 316 fields_desc = [ XShortField("type", 0x0102), 317 FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 25), 318 IPField("nexthop", "192.168.0.0"), 319 IntField("delay", 128000), 320 IntField("bandwidth", 256), 321 ThreeBytesField("mtu", 1500), 322 ByteField("hopcount", 0), 323 ByteField("reliability", 255), 324 ByteField("load", 0), 325 XShortField("reserved", 0), 326 ByteField("prefixlen", 24), 327 EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen), 328 ] 329 330 _EIGRP_EXTERNAL_PROTOCOL_ID = { 331 0x01 : "IGRP", 332 0x02 : "EIGRP", 333 0x03 : "Static Route", 334 0x04 : "RIP", 335 0x05 : "Hello", 336 0x06 : "OSPF", 337 0x07 : "IS-IS", 338 0x08 : "EGP", 339 0x09 : "BGP", 340 0x0A : "IDRP", 341 0x0B : "Connected Link" 342 } 343 344 _EIGRP_EXTROUTE_FLAGS = ["external", "candidate-default"] 345 346 class EIGRPExtRoute(EIGRPGeneric): 347 name = "EIGRP External Route" 348 fields_desc = [ XShortField("type", 0x0103), 349 FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 45), 350 IPField("nexthop", "192.168.0.0"), 351 IPField("originrouter", "192.168.0.1"), 352 IntField("originasn", 0), 353 IntField("tag", 0), 354 IntField("externalmetric", 0), 355 ShortField("reserved", 0), 356 ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), 357 FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), 358 IntField("delay", 0), 359 IntField("bandwidth", 256), 360 ThreeBytesField("mtu", 1500), 361 ByteField("hopcount", 0), 362 ByteField("reliability", 255), 363 ByteField("load", 0), 364 XShortField("reserved2", 0), 365 ByteField("prefixlen", 24), 366 EigrpIPField("dst", "192.168.1.0", length_from=lambda pkt: pkt.prefixlen) 367 ] 368 369 class EIGRPv6IntRoute(EIGRPGeneric): 370 name = "EIGRP for IPv6 Internal Route" 371 fields_desc = [ XShortField("type", 0x0402), 372 FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 37), 373 IP6Field("nexthop", "::"), 374 IntField("delay", 128000), 375 IntField("bandwidth", 256000), 376 ThreeBytesField("mtu", 1500), 377 ByteField("hopcount", 1), 378 ByteField("reliability", 255), 379 ByteField("load", 0), 380 XShortField("reserved", 0), 381 ByteField("prefixlen", 16), 382 EigrpIP6Field("dst", "2001::", length_from=lambda pkt: pkt.prefixlen) 383 ] 384 385 class EIGRPv6ExtRoute(EIGRPGeneric): 386 name = "EIGRP for IPv6 External Route" 387 fields_desc = [ XShortField("type", 0x0403), 388 FieldLenField("len", None, "dst", "!H", adjust=lambda pkt,x: x + 57), 389 IP6Field("nexthop", "::"), 390 IPField("originrouter", "192.168.0.1"), 391 IntField("originasn", 0), 392 IntField("tag", 0), 393 IntField("externalmetric", 0), 394 ShortField("reserved", 0), 395 ByteEnumField("extprotocolid", 3, _EIGRP_EXTERNAL_PROTOCOL_ID), 396 FlagsField("flags", 0, 8, _EIGRP_EXTROUTE_FLAGS), 397 IntField("delay", 0), 398 IntField("bandwidth", 256000), 399 ThreeBytesField("mtu", 1500), 400 ByteField("hopcount", 1), 401 ByteField("reliability", 0), 402 ByteField("load", 1), 403 XShortField("reserved2", 0), 404 ByteField("prefixlen", 8), 405 EigrpIP6Field("dst", "::", length_from=lambda pkt: pkt.prefixlen) 406 ] 407 408 _eigrp_tlv_cls = { 409 0x0001: "EIGRPParam", 410 0x0002: "EIGRPAuthData", 411 0x0003: "EIGRPSeq", 412 0x0004: "EIGRPSwVer", 413 0x0005: "EIGRPNms", 414 0x0006: "EIGRPStub", 415 0x0102: "EIGRPIntRoute", 416 0x0103: "EIGRPExtRoute", 417 0x0402: "EIGRPv6IntRoute", 418 0x0403: "EIGRPv6ExtRoute" 419 } 420 421 class RepeatedTlvListField(PacketListField): 422 def __init__(self, name, default, cls): 423 PacketField.__init__(self, name, default, cls) 424 425 def getfield(self, pkt, s): 426 lst = [] 427 remain = s 428 while len(remain) > 0: 429 p = self.m2i(pkt, remain) 430 if conf.padding_layer in p: 431 pad = p[conf.padding_layer] 432 remain = pad.load 433 del(pad.underlayer.payload) 434 else: 435 remain = b"" 436 lst.append(p) 437 return remain,lst 438 439 def addfield(self, pkt, s, val): 440 return s + b"".join(raw(v) for v in val) 441 442 def _EIGRPGuessPayloadClass(p, **kargs): 443 cls = conf.raw_layer 444 if len(p) >= 2: 445 t = struct.unpack("!H", p[:2])[0] 446 clsname = _eigrp_tlv_cls.get(t, "EIGRPGeneric") 447 cls = globals()[clsname] 448 return cls(p, **kargs) 449 450 _EIGRP_OPCODES = { 1 : "Update", 451 2 : "Request", 452 3 : "Query", 453 4 : "Replay", 454 5 : "Hello", 455 6 : "IPX SAP", 456 10 : "SIA Query", 457 11 : "SIA Reply" } 458 459 # The Conditional Receive bit is used for reliable multicast communication. 460 # Update-Flag: Not sure if Cisco calls it that way, but it's set when neighbors 461 # are exchanging routing information 462 _EIGRP_FLAGS = ["init", "cond-recv", "unknown", "update"] 463 464 class EIGRP(Packet): 465 name = "EIGRP" 466 fields_desc = [ ByteField("ver", 2), 467 ByteEnumField("opcode", 5, _EIGRP_OPCODES), 468 XShortField("chksum", None), 469 FlagsField("flags", 0, 32, _EIGRP_FLAGS), 470 IntField("seq", 0), 471 IntField("ack", 0), 472 IntField("asn", 100), 473 RepeatedTlvListField("tlvlist", [], _EIGRPGuessPayloadClass) 474 ] 475 476 def post_build(self, p, pay): 477 p += pay 478 if self.chksum is None: 479 c = checksum(p) 480 p = p[:2] + chb((c >> 8) & 0xff) + chb(c & 0xff) + p[4:] 481 return p 482 483 def mysummary(self): 484 summarystr = "EIGRP (AS=%EIGRP.asn% Opcode=%EIGRP.opcode%" 485 if self.opcode == 5 and self.ack != 0: 486 summarystr += " (ACK)" 487 if self.flags != 0: 488 summarystr += " Flags=%EIGRP.flags%" 489 490 return self.sprintf(summarystr + ")") 491 492 bind_layers(IP, EIGRP, proto=88) 493 bind_layers(IPv6, EIGRP, nh=88) 494 495 if __name__ == "__main__": 496 from scapy.main import interact 497 interact(mydict=globals(), mybanner="EIGRP") 498 499