# This file is part of Scapy # Scapy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # Scapy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Scapy. If not, see . # scapy.contrib.description = Intermediate System to Intermediate System (ISIS) # scapy.contrib.status = loads """ IS-IS Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ :copyright: 2014-2016 BENOCS GmbH, Berlin (Germany) :author: Marcel Patzlaff, mpatzlaff@benocs.com Michal Kaliszan, mkaliszan@benocs.com :license: GPLv2 This module is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. :description: This module provides Scapy layers for the Intermediate System to Intermediate System routing protocol as defined in RFC 1195. Currently it (partially) supports the packaging/encoding requirements of the following RFCs: * RFC 1195 (only the TCP/IP related part) * RFC 3358 (optional checksums) * RFC 5301 (dynamic hostname extension) * RFC 5302 (domain-wide prefix distribution) * RFC 5303 (three-way handshake) * RFC 5304 (cryptographic authentication) * RFC 5308 (routing IPv6 with IS-IS) :TODO: - packet relations (requests, responses) - support for recent RFCs: * RFC 5305 (traffic engineering) * RFC 5307 (support for G-MPLS) * RFC 5310 (generic cryptographic authentication) * RFC 5316 (inter-AS MPLS and G-MPLS TE) """ from __future__ import absolute_import import struct import random from scapy.config import conf from scapy.fields import BitField, BitFieldLenField, BoundStrLenField, \ ByteEnumField, ByteField, ConditionalField, Field, FieldLenField, \ FieldListField, FlagsField, IEEEFloatField, IP6PrefixField, IPField, \ IPPrefixField, IntField, LongField, MACField, PacketListField, \ ShortField, ThreeBytesField, XIntField, XShortField from scapy.packet import bind_layers, Packet from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol from scapy.layers.inet6 import IP6ListField, IP6Field from scapy.utils import fletcher16_checkbytes from scapy.volatile import RandString, RandByte from scapy.modules.six.moves import range from scapy.compat import orb, hex_bytes EXT_VERSION = "v0.0.2" ####################################################################### # ISIS Utilities + Fields # ####################################################################### def isis_area2str(area): return b"".join(hex_bytes(x) for x in area.split(".")) def isis_str2area(s): if len(s) == 0: return "" numbytes = len(s[1:]) fmt = "%02X" + (".%02X%02X" * (numbytes // 2)) + ("" if (numbytes % 2) == 0 else ".%02X") # noqa: E501 return fmt % tuple(orb(x) for x in s) def isis_sysid2str(sysid): return b"".join(hex_bytes(x) for x in sysid.split(".")) def isis_str2sysid(s): return ("%02X%02X." * 3)[:-1] % tuple(orb(x) for x in s) def isis_nodeid2str(nodeid): return isis_sysid2str(nodeid[:-3]) + hex_bytes(nodeid[-2:]) def isis_str2nodeid(s): return "%s.%02X" % (isis_str2sysid(s[:-1]), orb(s[-1])) def isis_lspid2str(lspid): return isis_nodeid2str(lspid[:-3]) + hex_bytes(lspid[-2:]) def isis_str2lspid(s): return "%s-%02X" % (isis_str2nodeid(s[:-1]), orb(s[-1])) class _ISIS_IdFieldBase(Field): __slots__ = ["to_str", "to_id", "length"] def __init__(self, name, default, length, to_str, to_id): self.to_str = to_str self.to_id = to_id self.length = length Field.__init__(self, name, default, "%is" % length) def i2m(self, pkt, x): if x is None: return b"\0" * self.length return self.to_str(x) def m2i(self, pkt, x): return self.to_id(x) def any2i(self, pkt, x): if isinstance(x, str) and len(x) == self.length: return self.m2i(pkt, x) return x class _ISIS_RandId(RandString): def __init__(self, template): RandString.__init__(self) self.bytecount = template.count("*") self.format = template.replace("*", "%02X") def _fix(self): if self.bytecount == 0: return "" val = () for _ in range(self.bytecount): val += (RandByte(),) return self.format % val class _ISIS_RandAreaId(_ISIS_RandId): def __init__(self, bytecount=None): template = "*" + ( ".**" * ((self.bytecount - 1) // 2) ) + ( "" if ((self.bytecount - 1) % 2) == 0 else ".*" ) super(_ISIS_RandAreaId, self).__init__(template) if bytecount is None: self.bytecount = random.randint(1, 13) else: self.bytecount = bytecount class ISIS_AreaIdField(Field): __slots__ = ["length_from"] def __init__(self, name, default, length_from): Field.__init__(self, name, default) self.length_from = length_from def i2m(self, pkt, x): return isis_area2str(x) def m2i(self, pkt, x): return isis_str2area(x) def i2len(self, pkt, x): if x is None: return 0 tmp_len = len(x) # l/5 is the number of dots in the Area ID return (tmp_len - (tmp_len // 5)) // 2 def addfield(self, pkt, s, val): sval = self.i2m(pkt, val) return s + struct.pack("!%is" % len(sval), sval) def getfield(self, pkt, s): numbytes = self.length_from(pkt) return s[numbytes:], self.m2i(pkt, struct.unpack("!%is" % numbytes, s[:numbytes])[0]) # noqa: E501 def randval(self): return _ISIS_RandAreaId() class ISIS_SystemIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 6, isis_sysid2str, isis_str2sysid) # noqa: E501 def randval(self): return _ISIS_RandId("**.**.**") class ISIS_NodeIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 7, isis_nodeid2str, isis_str2nodeid) # noqa: E501 def randval(self): return _ISIS_RandId("**.**.**.*") class ISIS_LspIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 8, isis_lspid2str, isis_str2lspid) # noqa: E501 def randval(self): return _ISIS_RandId("**.**.**.*-*") class ISIS_CircuitTypeField(FlagsField): def __init__(self, name="circuittype", default=2, size=8, names=None): if names is None: names = ["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"] FlagsField.__init__(self, name, default, size, names) def _ISIS_GuessTlvClass_Helper(tlv_classes, defaultname, p, **kargs): cls = conf.raw_layer if len(p) >= 2: tlvtype = orb(p[0]) clsname = tlv_classes.get(tlvtype, defaultname) cls = globals()[clsname] return cls(p, **kargs) class _ISIS_GenericTlv_Base(Packet): fields_desc = [ByteField("type", 0), FieldLenField("len", None, length_of="val", fmt="B"), BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)] # noqa: E501 def guess_payload_class(self, p): return conf.padding_layer class ISIS_GenericTlv(_ISIS_GenericTlv_Base): name = "ISIS Generic TLV" class ISIS_GenericSubTlv(_ISIS_GenericTlv_Base): name = "ISIS Generic Sub-TLV" ####################################################################### # ISIS Sub-TLVs for TLVs 22, 23, 141, 222, 223 # ####################################################################### _isis_subtlv_classes_1 = { 3: "ISIS_AdministrativeGroupSubTlv", 4: "ISIS_LinkLocalRemoteIdentifiersSubTlv", 6: "ISIS_IPv4InterfaceAddressSubTlv", 8: "ISIS_IPv4NeighborAddressSubTlv", 9: "ISIS_MaximumLinkBandwidthSubTlv", 10: "ISIS_MaximumReservableLinkBandwidthSubTlv", 11: "ISIS_UnreservedBandwidthSubTlv", 12: "ISIS_IPv6InterfaceAddressSubTlv", 13: "ISIS_IPv6NeighborAddressSubTlv", 18: "ISIS_TEDefaultMetricSubTlv" } _isis_subtlv_names_1 = { 3: "Administrative Group (Color)", 4: "Link Local/Remote Identifiers", 6: "IPv4 Interface Address", 8: "IPv4 Neighbor Address", 9: "Maximum Link Bandwidth", 10: "Maximum Reservable Link Bandwidth", 11: "Unreserved Bandwidth", 12: "IPv6 Interface Address", 13: "IPv6 Neighbor Address", 18: "TE Default Metric" } def _ISIS_GuessSubTlvClass_1(p, **kargs): return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_1, "ISIS_GenericSubTlv", p, **kargs) # noqa: E501 class ISIS_IPv4InterfaceAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv4 Interface Address (S)" fields_desc = [ByteEnumField("type", 6, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IPField("address", "0.0.0.0")] class ISIS_IPv4NeighborAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv4 Neighbor Address (S)" fields_desc = [ByteEnumField("type", 8, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IPField("address", "0.0.0.0")] class ISIS_LinkLocalRemoteIdentifiersSubTlv(ISIS_GenericSubTlv): name = "ISIS Link Local/Remote Identifiers (S)" fields_desc = [ByteEnumField("type", 4, _isis_subtlv_names_1), FieldLenField("len", 8, fmt="B"), IntField("localid", "0"), IntField("remoteid", "0")] class ISIS_IPv6InterfaceAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv6 Interface Address (S)" fields_desc = [ByteEnumField("type", 12, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IP6Field("address", "::")] class ISIS_IPv6NeighborAddressSubTlv(ISIS_GenericSubTlv): name = "ISIS IPv6 Neighbor Address (S)" fields_desc = [ByteEnumField("type", 13, _isis_subtlv_names_1), FieldLenField("len", None, length_of="address", fmt="B"), IP6Field("address", "::")] class ISIS_AdministrativeGroupSubTlv(ISIS_GenericSubTlv): name = "Administrative Group SubTLV (Color)" fields_desc = [ByteEnumField("code", 3, _isis_subtlv_names_1), FieldLenField("len", None, length_of="admingroup", fmt="B"), IPField("admingroup", "0.0.0.1")] class ISIS_MaximumLinkBandwidthSubTlv(ISIS_GenericSubTlv): name = "Maximum Link Bandwidth SubTLV" fields_desc = [ByteEnumField("type", 9, _isis_subtlv_names_1), FieldLenField("len", None, length_of="maxbw", fmt="B"), IEEEFloatField("maxbw", 1000)] # in B/s class ISIS_MaximumReservableLinkBandwidthSubTlv(ISIS_GenericSubTlv): name = "Maximum Reservable Link Bandwidth SubTLV" fields_desc = [ByteEnumField("type", 10, _isis_subtlv_names_1), FieldLenField("len", None, length_of="maxrsvbw", fmt="B"), IEEEFloatField("maxrsvbw", 1000)] # in B/s class ISIS_UnreservedBandwidthSubTlv(ISIS_GenericSubTlv): name = "Unreserved Bandwidth SubTLV" fields_desc = [ByteEnumField("type", 11, _isis_subtlv_names_1), FieldLenField("len", None, length_of="unrsvbw", fmt="B"), FieldListField("unrsvbw", [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000], IEEEFloatField("", 1000), count_from=lambda pkt: pkt.len / 4)] # in B/s # noqa: E501 class ISIS_TEDefaultMetricSubTlv(ISIS_GenericSubTlv): name = "TE Default Metric SubTLV" fields_desc = [ByteEnumField("type", 18, _isis_subtlv_names_1), FieldLenField("len", None, length_of="temetric", adjust=lambda pkt, x:x - 1, fmt="B"), # noqa: E501 ThreeBytesField("temetric", 1000)] ####################################################################### # ISIS Sub-TLVs for TLVs 135, 235, 236, and 237 # ####################################################################### _isis_subtlv_classes_2 = { 1: "ISIS_32bitAdministrativeTagSubTlv", 2: "ISIS_64bitAdministrativeTagSubTlv" } _isis_subtlv_names_2 = { 1: "32-bit Administrative Tag", 2: "64-bit Administrative Tag" } def _ISIS_GuessSubTlvClass_2(p, **kargs): return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_2, "ISIS_GenericSubTlv", p, **kargs) # noqa: E501 class ISIS_32bitAdministrativeTagSubTlv(ISIS_GenericSubTlv): name = "ISIS 32-bit Administrative Tag (S)" fields_desc = [ByteEnumField("type", 1, _isis_subtlv_names_2), FieldLenField("len", None, length_of="tags", fmt="B"), FieldListField("tags", [], IntField("", 0), count_from=lambda pkt: pkt.len // 4)] # noqa: E501 class ISIS_64bitAdministrativeTagSubTlv(ISIS_GenericSubTlv): name = "ISIS 64-bit Administrative Tag (S)" fields_desc = [ByteEnumField("type", 2, _isis_subtlv_names_2), FieldLenField("len", None, length_of="tags", fmt="B"), FieldListField("tags", [], LongField("", 0), count_from=lambda pkt: pkt.len // 8)] # noqa: E501 ####################################################################### # ISIS TLVs # ####################################################################### _isis_tlv_classes = { 1: "ISIS_AreaTlv", 2: "ISIS_IsReachabilityTlv", 6: "ISIS_IsNeighbourTlv", 8: "ISIS_PaddingTlv", 9: "ISIS_LspEntryTlv", 10: "ISIS_AuthenticationTlv", 12: "ISIS_ChecksumTlv", 14: "ISIS_BufferSizeTlv", 22: "ISIS_ExtendedIsReachabilityTlv", 128: "ISIS_InternalIpReachabilityTlv", 129: "ISIS_ProtocolsSupportedTlv", 130: "ISIS_ExternalIpReachabilityTlv", 132: "ISIS_IpInterfaceAddressTlv", 135: "ISIS_ExtendedIpReachabilityTlv", 137: "ISIS_DynamicHostnameTlv", 232: "ISIS_Ipv6InterfaceAddressTlv", 236: "ISIS_Ipv6ReachabilityTlv", 240: "ISIS_P2PAdjacencyStateTlv" } _isis_tlv_names = { 1: "Area TLV", 2: "IS Reachability TLV", 6: "IS Neighbour TLV", 7: "Instance Identifier TLV", 8: "Padding TLV", 9: "LSP Entries TLV", 10: "Authentication TLV", 12: "Optional Checksum TLV", 13: "Purge Originator Identification TLV", 14: "LSP Buffer Size TLV", 22: "Extended IS-Reachability TLV", 23: "IS Neighbour Attribute TLV", 24: "IS Alias ID", 128: "IP Internal Reachability TLV", 129: "Protocols Supported TLV", 130: "IP External Reachability TLV", 131: "Inter-Domain Routing Protocol Information TLV", 132: "IP Interface Address TLV", 134: "Traffic Engineering Router ID TLV", 135: "Extended IP Reachability TLV", 137: "Dynamic Hostname TLV", 138: "GMPLS Shared Risk Link Group TLV", 139: "IPv6 Shared Risk Link Group TLV", 140: "IPv6 Traffic Engineering Router ID TLV", 141: "Inter-AS Reachability Information TLV", 142: "Group Address TLV", 143: "Multi-Topology-Aware Port Capability TLV", 144: "Multi-Topology Capability TLV", 145: "TRILL Neighbour TLV", 147: "MAC-Reachability TLV", 148: "BFD-Enabled TLV", 211: "Restart TLV", 222: "Multi-Topology Intermediate Systems TLV", 223: "Multi-Topology IS Neighbour Attributes TLV", 229: "Multi-Topology TLV", 232: "IPv6 Interface Address TLV", 233: "IPv6 Global Interface Address TLV", 235: "Multi-Topology IPv4 Reachability TLV", 236: "IPv6 Reachability TLV", 237: "Multi-Topology IPv6 Reachability TLV", 240: "Point-to-Point Three-Way Adjacency TLV", 242: "IS-IS Router Capability TLV", 251: "Generic Information TLV" } def _ISIS_GuessTlvClass(p, **kargs): return _ISIS_GuessTlvClass_Helper(_isis_tlv_classes, "ISIS_GenericTlv", p, **kargs) # noqa: E501 class ISIS_AreaEntry(Packet): name = "ISIS Area Entry" fields_desc = [FieldLenField("arealen", None, length_of="areaid", fmt="B"), ISIS_AreaIdField("areaid", "49", length_from=lambda pkt: pkt.arealen)] # noqa: E501 def extract_padding(self, s): return "", s class ISIS_AreaTlv(ISIS_GenericTlv): name = "ISIS Area TLV" fields_desc = [ByteEnumField("type", 1, _isis_tlv_names), FieldLenField("len", None, length_of="areas", fmt="B"), PacketListField("areas", [], ISIS_AreaEntry, length_from=lambda x: x.len)] # noqa: E501 class ISIS_AuthenticationTlv(ISIS_GenericTlv): name = "ISIS Authentication TLV" fields_desc = [ByteEnumField("type", 10, _isis_tlv_names), FieldLenField("len", None, length_of="password", adjust=lambda pkt, x: x + 1, fmt="B"), # noqa: E501 ByteEnumField("authtype", 1, {1: "Plain", 17: "HMAC-MD5"}), BoundStrLenField("password", "", maxlen=254, length_from=lambda pkt: pkt.len - 1)] # noqa: E501 class ISIS_BufferSizeTlv(ISIS_GenericTlv): name = "ISIS Buffer Size TLV" fields_desc = [ByteEnumField("type", 14, _isis_tlv_names), ByteField("len", 2), ShortField("lspbuffersize", 1497)] class ISIS_ChecksumTlv(ISIS_GenericTlv): name = "ISIS Optional Checksum TLV" fields_desc = [ByteEnumField("type", 12, _isis_tlv_names), ByteField("len", 2), XShortField("checksum", None)] class ISIS_DynamicHostnameTlv(ISIS_GenericTlv): name = "ISIS Dynamic Hostname TLV" fields_desc = [ByteEnumField("type", 137, _isis_tlv_names), FieldLenField("len", None, length_of="hostname", fmt="B"), BoundStrLenField("hostname", "", length_from=lambda pkt: pkt.len)] # noqa: E501 class ISIS_ExtendedIpPrefix(Packet): name = "ISIS Extended IP Prefix" fields_desc = [ IntField("metric", 1), BitField("updown", 0, 1), BitField("subtlvindicator", 0, 1), BitFieldLenField("pfxlen", None, 6, length_of="pfx"), IPPrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen), # noqa: E501 ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt="B"), lambda pkt: pkt.subtlvindicator == 1), # noqa: E501 ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) # noqa: E501 ] def extract_padding(self, s): return "", s class ISIS_TERouterIDTlv(ISIS_GenericTlv): name = "ISIS TE Router ID TLV" fields_desc = [ByteEnumField("type", 134, _isis_tlv_names), FieldLenField("len", None, length_of="routerid", fmt="B"), IPField("routerid", "0.0.0.0")] class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IP Reachability TLV" fields_desc = [ByteEnumField("type", 135, _isis_tlv_names), FieldLenField("len", None, length_of="pfxs", fmt="B"), PacketListField("pfxs", [], ISIS_ExtendedIpPrefix, length_from=lambda pkt: pkt.len)] # noqa: E501 class ISIS_ExtendedIsNeighbourEntry(Packet): name = "ISIS Extended IS Neighbour Entry" fields_desc = [ ISIS_NodeIdField("neighbourid", "0102.0304.0506.07"), ThreeBytesField("metric", 1), FieldLenField("subtlvslen", None, length_of="subtlvs", fmt="B"), PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_1, length_from=lambda x: x.subtlvslen) # noqa: E501 ] def extract_padding(self, s): return "", s class ISIS_ExtendedIsReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IS Reachability TLV" fields_desc = [ByteEnumField("type", 22, _isis_tlv_names), FieldLenField("len", None, length_of="neighbours", fmt="B"), PacketListField("neighbours", [], ISIS_ExtendedIsNeighbourEntry, length_from=lambda x: x.len)] # noqa: E501 class ISIS_IpInterfaceAddressTlv(ISIS_GenericTlv): name = "ISIS IP Interface Address TLV" fields_desc = [ByteEnumField("type", 132, _isis_tlv_names), FieldLenField("len", None, length_of="addresses", fmt="B"), FieldListField("addresses", [], IPField("", "0.0.0.0"), count_from=lambda pkt: pkt.len // 4)] # noqa: E501 class ISIS_Ipv6InterfaceAddressTlv(ISIS_GenericTlv): name = "ISIS IPv6 Interface Address TLV" fields_desc = [ ByteEnumField("type", 232, _isis_tlv_names), FieldLenField("len", None, length_of="addresses", fmt="B"), IP6ListField("addresses", [], count_from=lambda pkt: pkt.len // 16) ] class ISIS_Ipv6Prefix(Packet): name = "ISIS IPv6 Prefix" fields_desc = [ IntField("metric", 1), BitField("updown", 0, 1), BitField("external", 0, 1), BitField("subtlvindicator", 0, 1), BitField("reserved", 0, 5), FieldLenField("pfxlen", None, length_of="pfx", fmt="B"), IP6PrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen), # noqa: E501 ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt="B"), lambda pkt: pkt.subtlvindicator == 1), # noqa: E501 ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) # noqa: E501 ] def extract_padding(self, s): return "", s class ISIS_Ipv6ReachabilityTlv(ISIS_GenericTlv): name = "ISIS IPv6 Reachability TLV" fields_desc = [ByteEnumField("type", 236, _isis_tlv_names), FieldLenField("len", None, length_of="pfxs", fmt="B"), PacketListField("pfxs", [], ISIS_Ipv6Prefix, length_from=lambda pkt: pkt.len)] # noqa: E501 class ISIS_IsNeighbourTlv(ISIS_GenericTlv): name = "ISIS IS Neighbour TLV" fields_desc = [ByteEnumField("type", 6, _isis_tlv_names), FieldLenField("len", None, length_of="neighbours", fmt="B"), FieldListField("neighbours", [], MACField("", "00.00.00.00.00.00"), count_from=lambda pkt: pkt.len // 6)] # noqa: E501 class ISIS_LspEntry(Packet): name = "ISIS LSP Entry" fields_desc = [ShortField("lifetime", 1200), ISIS_LspIdField("lspid", "0102.0304.0506.07-08"), XIntField("seqnum", 0x00000001), XShortField("checksum", None)] def extract_padding(self, s): return "", s class ISIS_LspEntryTlv(ISIS_GenericTlv): name = "ISIS LSP Entry TLV" fields_desc = [ ByteEnumField("type", 9, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_LspEntry, count_from=lambda pkt: pkt.len // 16) # noqa: E501 ] class _AdjacencyStateTlvLenField(Field): def i2m(self, pkt, x): if pkt.neighbourextlocalcircuitid is not None: return 15 if pkt.neighboursystemid is not None: return 11 if pkt.extlocalcircuitid is not None: return 5 return 1 class ISIS_P2PAdjacencyStateTlv(ISIS_GenericTlv): name = "ISIS P2P Adjacency State TLV" fields_desc = [ByteEnumField("type", 240, _isis_tlv_names), _AdjacencyStateTlvLenField("len", None, fmt="B"), ByteEnumField("state", "Down", {0x2: "Down", 0x1: "Initialising", 0x0: "Up"}), # noqa: E501 ConditionalField(IntField("extlocalcircuitid", None), lambda pkt: pkt.len >= 5), # noqa: E501 ConditionalField(ISIS_SystemIdField("neighboursystemid", None), lambda pkt: pkt.len >= 11), # noqa: E501 ConditionalField(IntField("neighbourextlocalcircuitid", None), lambda pkt: pkt.len == 15)] # noqa: E501 # TODO dynamically allocate sufficient size class ISIS_PaddingTlv(ISIS_GenericTlv): name = "ISIS Padding TLV" fields_desc = [ ByteEnumField("type", 8, _isis_tlv_names), FieldLenField("len", None, length_of="padding", fmt="B"), BoundStrLenField("padding", "", length_from=lambda pkt: pkt.len) ] class ISIS_ProtocolsSupportedTlv(ISIS_GenericTlv): name = "ISIS Protocols Supported TLV" fields_desc = [ ByteEnumField("type", 129, _isis_tlv_names), FieldLenField("len", None, count_of="nlpids", fmt="B"), FieldListField("nlpids", [], ByteEnumField("", "IPv4", network_layer_protocol_ids), count_from=lambda pkt: pkt.len) # noqa: E501 ] ####################################################################### # ISIS Old-Style TLVs # ####################################################################### class ISIS_IpReachabilityEntry(Packet): name = "ISIS IP Reachability" fields_desc = [ByteField("defmetric", 1), ByteField("delmetric", 0x80), ByteField("expmetric", 0x80), ByteField("errmetric", 0x80), IPField("ipaddress", "0.0.0.0"), IPField("subnetmask", "255.255.255.255")] def extract_padding(self, s): return "", s class ISIS_InternalIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS Internal IP Reachability TLV" fields_desc = [ ByteEnumField("type", 128, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12) # noqa: E501 ] class ISIS_ExternalIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS External IP Reachability TLV" fields_desc = [ ByteEnumField("type", 130, _isis_tlv_names), FieldLenField("len", None, length_of="entries", fmt="B"), PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12) # noqa: E501 ] class ISIS_IsReachabilityEntry(Packet): name = "ISIS IS Reachability" fields_desc = [ByteField("defmetric", 1), ByteField("delmetric", 0x80), ByteField("expmetric", 0x80), ByteField("errmetric", 0x80), ISIS_NodeIdField("neighbourid", "0102.0304.0506.07")] def extract_padding(self, s): return "", s class ISIS_IsReachabilityTlv(ISIS_GenericTlv): name = "ISIS IS Reachability TLV" fields_desc = [ ByteEnumField("type", 2, _isis_tlv_names), FieldLenField("len", None, fmt="B", length_of="neighbours", adjust=lambda pkt, x: x + 1), # noqa: E501 ByteField("virtual", 0), PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from=lambda x: (x.len - 1) // 11) # noqa: E501 ] ####################################################################### # ISIS PDU Packets # ####################################################################### _isis_pdu_names = { 15: "L1 LAN Hello", 16: "L2 LAN Hello", 17: "P2P Hello", 18: "L1 LSP", 20: "L2 LSP", 24: "L1 CSNP", 25: "L2 CSNP", 26: "L1 PSNP", 27: "L2 PSNP" } class ISIS_CommonHdr(Packet): name = "ISIS Common Header" fields_desc = [ ByteEnumField("nlpid", 0x83, network_layer_protocol_ids), ByteField("hdrlen", None), ByteField("version", 1), ByteField("idlen", 0), ByteEnumField("pdutype", None, _isis_pdu_names), ByteField("pduversion", 1), ByteField("hdrreserved", 0), ByteField("maxareaaddr", 0) ] def post_build(self, pkt, pay): # calculating checksum if requested pdu = pkt + pay checksumInfo = self[1].checksum_info(self.hdrlen) if checksumInfo is not None: (cbegin, cpos) = checksumInfo checkbytes = fletcher16_checkbytes(pdu[cbegin:], (cpos - cbegin)) pdu = pdu[:cpos] + checkbytes + pdu[cpos + 2:] return pdu class _ISIS_PduBase(Packet): def checksum_info(self, hdrlen): checksumPosition = hdrlen for tlv in self.tlvs: if isinstance(tlv, ISIS_ChecksumTlv): checksumPosition += 2 return (0, checksumPosition) else: checksumPosition += len(tlv) return None def guess_payload_class(self, p): return conf.padding_layer class _ISIS_PduLengthField(FieldLenField): def __init__(self): FieldLenField.__init__(self, "pdulength", None, length_of="tlvs", adjust=lambda pkt, x: x + pkt.underlayer.hdrlen) # noqa: E501 class _ISIS_TlvListField(PacketListField): def __init__(self): PacketListField.__init__(self, "tlvs", [], _ISIS_GuessTlvClass, length_from=lambda pkt: pkt.pdulength - pkt.underlayer.hdrlen) # noqa: E501 class _ISIS_LAN_HelloBase(_ISIS_PduBase): fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), _ISIS_PduLengthField(), ByteField("priority", 1), ISIS_NodeIdField("lanid", "0000.0000.0000.00"), _ISIS_TlvListField() ] class ISIS_L1_LAN_Hello(_ISIS_LAN_HelloBase): name = "ISIS L1 LAN Hello PDU" class ISIS_L2_LAN_Hello(_ISIS_LAN_HelloBase): name = "ISIS L2 LAN Hello PDU" class ISIS_P2P_Hello(_ISIS_PduBase): name = "ISIS Point-to-Point Hello PDU" fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), _ISIS_PduLengthField(), ByteField("localcircuitid", 0), _ISIS_TlvListField() ] class _ISIS_LSP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ShortField("lifetime", 1199), ISIS_LspIdField("lspid", "0102.0304.0506.00-00"), XIntField("seqnum", 0x00000001), XShortField("checksum", None), FlagsField("typeblock", 0x03, 8, ["L1", "L2", "OL", "ADef", "ADel", "AExp", "AErr", "P"]), # noqa: E501 _ISIS_TlvListField() ] def checksum_info(self, hdrlen): if self.checksum is not None: return None return (12, 24) def _lsp_answers(lsp, other, clsname): # TODO return 0 class ISIS_L1_LSP(_ISIS_LSP_Base): name = "ISIS L1 Link State PDU" def answers(self, other): return _lsp_answers(self, other, "ISIS_L1_PSNP") class ISIS_L2_LSP(_ISIS_LSP_Base): name = "ISIS L2 Link State PDU" def answers(self, other): return _lsp_answers(self, other, "ISIS_L2_PSNP") class _ISIS_CSNP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), ISIS_LspIdField("startlspid", "0000.0000.0000.00-00"), ISIS_LspIdField("endlspid", "FFFF.FFFF.FFFF.FF-FF"), _ISIS_TlvListField() ] def _snp_answers(snp, other, clsname): # TODO return 0 class ISIS_L1_CSNP(_ISIS_CSNP_Base): name = "ISIS L1 Complete Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L1_LSP") class ISIS_L2_CSNP(_ISIS_CSNP_Base): name = "ISIS L2 Complete Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L2_LSP") class _ISIS_PSNP_Base(_ISIS_PduBase): fields_desc = [ _ISIS_PduLengthField(), ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), _ISIS_TlvListField() ] class ISIS_L1_PSNP(_ISIS_PSNP_Base): name = "ISIS L1 Partial Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L1_LSP") class ISIS_L2_PSNP(_ISIS_PSNP_Base): name = "ISIS L2 Partial Sequence Number Packet" def answers(self, other): return _snp_answers(self, other, "ISIS_L2_LSP") register_cln_protocol(0x83, ISIS_CommonHdr) bind_layers(ISIS_CommonHdr, ISIS_L1_LAN_Hello, hdrlen=27, pdutype=15) bind_layers(ISIS_CommonHdr, ISIS_L2_LAN_Hello, hdrlen=27, pdutype=16) bind_layers(ISIS_CommonHdr, ISIS_P2P_Hello, hdrlen=20, pdutype=17) bind_layers(ISIS_CommonHdr, ISIS_L1_LSP, hdrlen=27, pdutype=18) bind_layers(ISIS_CommonHdr, ISIS_L2_LSP, hdrlen=27, pdutype=20) bind_layers(ISIS_CommonHdr, ISIS_L1_CSNP, hdrlen=33, pdutype=24) bind_layers(ISIS_CommonHdr, ISIS_L2_CSNP, hdrlen=33, pdutype=25) bind_layers(ISIS_CommonHdr, ISIS_L1_PSNP, hdrlen=17, pdutype=26) bind_layers(ISIS_CommonHdr, ISIS_L2_PSNP, hdrlen=17, pdutype=27)