diff --git a/scapy/contrib/isis.py b/scapy/contrib/isis.py index 1fb5dde6c0c1a2435d4d3ca4d4d5fe46bdc569b6..f1631c2580ec79f4188787116cf228c0588c1574 100644 --- a/scapy/contrib/isis.py +++ b/scapy/contrib/isis.py @@ -5,7 +5,7 @@ IS-IS Scapy Extension ~~~~~~~~~~~~~~~~~~~~~ - :copyright: 2014 BENOCS GmbH, Berlin (Germany) + :copyright: 2014, 2015 BENOCS GmbH, Berlin (Germany) :author: Marcel Patzlaff, mpatzlaff@benocs.com :license: GPLv2 @@ -23,7 +23,7 @@ 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) @@ -33,7 +33,7 @@ * RFC 5303 (three-way handshake) * RFC 5304 (cryptographic authentication) * RFC 5308 (routing IPv6 with IS-IS) - + :TODO: - packet relations (requests, responses) @@ -50,7 +50,7 @@ import struct from scapy.config import conf from scapy.fields import Field, FlagsField, ByteField, ByteEnumField, \ FieldLenField, ShortField, PacketListField, XIntField, \ - XShortField, ConditionalField, X3BytesField, IntField, BitField, IPField, \ + XShortField, ConditionalField, ThreeBytesField, IntField, BitField, IPField, \ FieldListField, MACField, BitFieldLenField, IPPrefixField, IP6PrefixField,\ BoundStrLenField from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol @@ -63,53 +63,60 @@ import random EXT_VERSION = "v0.0.1" -conf.debug_dissector= True +conf.debug_dissector = True + ####################################################################### ## ISIS Utilities + Fields ## ####################################################################### - def isis_area2str(area): return "".join(x.decode("hex") 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") - return fmt % tuple(map(ord,s)) + + numbytes = len(s[1:]) + fmt = "%02X" + (".%02X%02X" * (numbytes / 2)) + ("" if (numbytes % 2) == 0 else ".%02X") + return fmt % tuple(map(ord, s)) + def isis_sysid2str(sysid): return "".join(x.decode("hex") for x in sysid.split(".")) + def isis_str2sysid(s): return ("%02X%02X."*3)[:-1] % tuple(map(ord, s)) + def isis_nodeid2str(nodeid): return "%s%s" % (isis_sysid2str(nodeid[:-3]), nodeid[-2:].decode("hex")) - + + def isis_str2nodeid(s): return "%s.%02X" % (isis_str2sysid(s[:-1]), ord(s[-1])) + def isis_lspid2str(lspid): return "%s%s" % (isis_nodeid2str(lspid[:-3]), lspid[-2:].decode("hex")) - + + def isis_str2lspid(s): return "%s-%02X" % (isis_str2nodeid(s[:-1]), ord(s[-1])) class _ISIS_IdFieldBase(Field): 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) + 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 "\0"*self.length - + return self.to_str(x) def m2i(self, pkt, x): @@ -124,52 +131,53 @@ class _ISIS_IdFieldBase(Field): class _ISIS_RandId(RandString): def __init__(self, template): - self.bytecount= template.count("*") - self.format=template.replace("*","%02X") - + self.bytecount = template.count("*") + self.format = template.replace("*", "%02X") + def _fix(self): if self.bytecount == 0: return "" - - val=() - + + val = () + for _ in range(self.bytecount): - val+= (RandByte(),) - + val += (RandByte(),) + return self.format % val + class _ISIS_RandAreaId(_ISIS_RandId): def __init__(self, bytecount= None): - self.bytecount= random.randint(1,13) if bytecount is None else bytecount - self.format= "%02X" + (".%02X%02X" * ((self.bytecount-1) / 2)) + ("" if ((self.bytecount-1) % 2) == 0 else ".%02X") + self.bytecount = random.randint(1, 13) if bytecount is None else bytecount + self.format = "%02X" + (".%02X%02X" * ((self.bytecount-1) / 2)) + ("" if ((self.bytecount-1) % 2) == 0 else ".%02X") class ISIS_AreaIdField(Field): 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 - l= len(x) + l = len(x) # l/5 is the number of dots in the Area ID return (l - (l / 5)) / 2 - + def addfield(self, pkt, s, val): - sval= self.i2m(pkt, val) + sval = self.i2m(pkt, val) return s+struct.pack("!%is" % len(sval), sval) - + def getfield(self, pkt, s): - numbytes= self.length_from(pkt) + numbytes = self.length_from(pkt) return s[numbytes:], self.m2i(pkt, struct.unpack("!%is" % numbytes, s[:numbytes])[0]) - + def randval(self): return _ISIS_RandAreaId() @@ -177,7 +185,7 @@ class ISIS_AreaIdField(Field): class ISIS_SystemIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 6, isis_sysid2str, isis_str2sysid) - + def randval(self): return _ISIS_RandId("**.**.**") @@ -185,7 +193,7 @@ class ISIS_SystemIdField(_ISIS_IdFieldBase): class ISIS_NodeIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 7, isis_nodeid2str, isis_str2nodeid) - + def randval(self): return _ISIS_RandId("**.**.**.*") @@ -193,89 +201,88 @@ class ISIS_NodeIdField(_ISIS_IdFieldBase): class ISIS_LspIdField(_ISIS_IdFieldBase): def __init__(self, name, default): _ISIS_IdFieldBase.__init__(self, name, default, 8, isis_lspid2str, isis_str2lspid) - + def randval(self): return _ISIS_RandId("**.**.**.*-*") class ISIS_CircuitTypeField(FlagsField): - def __init__(self, name="circuittype", default=2, size=8, names=["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"]): + def __init__(self, name="circuittype", default=2, size=8, + names=["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"]): FlagsField.__init__(self, name, default, size, names) ####################################################################### ## 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_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" +_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): cls = conf.raw_layer if len(p) >= 2: @@ -300,7 +307,7 @@ 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)] - + def extract_padding(self, s): return "", s @@ -325,7 +332,6 @@ class ISIS_BufferSizeTlv(ISIS_GenericTlv): fields_desc = [ByteEnumField("type", 14, _isis_tlv_names), ByteField("len", 2), ShortField("lspbuffersize", 1497)] - class ISIS_ChecksumTlv(ISIS_GenericTlv): @@ -334,7 +340,7 @@ class ISIS_ChecksumTlv(ISIS_GenericTlv): ByteField("len", 2), XShortField("checksum", None)] - + class ISIS_DynamicHostnameTlv(ISIS_GenericTlv): name = "ISIS Dynamic Hostname TLV" fields_desc = [ByteEnumField("type", 137, _isis_tlv_names), @@ -342,7 +348,6 @@ class ISIS_DynamicHostnameTlv(ISIS_GenericTlv): BoundStrLenField("hostname", "", length_from=lambda pkt: pkt.len)] - class ISIS_GenericSubTlv(Packet): name = "ISIS Generic Sub-TLV" fields_desc = [ByteField("type", 0), @@ -352,22 +357,26 @@ class ISIS_GenericSubTlv(Packet): def guess_payload_class(self, p): return conf.padding_layer + def _isis_guess_subtlv_cls(p, **kargs): return ISIS_GenericSubTlv(p, **kargs) 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), - ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), - ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)] - + 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), + ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), + ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) + ] + def extract_padding(self, s): return "", s + class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IP Reachability TLV" @@ -379,12 +388,13 @@ class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv): class ISIS_ExtendedIsNeighbourEntry(Packet): name = "ISIS Extended IS Neighbour Entry" fields_desc = [ISIS_NodeIdField("neighbourid", "0102.0304.0506.07"), - X3BytesField("metric", 1), + ThreeBytesField("metric", 1), FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"), ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvslen > 0)] - + def extract_padding(self, s): return "", s + class ISIS_ExtendedIsReachabilityTlv(ISIS_GenericTlv): name = "ISIS Extended IS Reachability TLV" @@ -398,43 +408,48 @@ class ISIS_IpInterfaceAddressTlv(ISIS_GenericTlv): 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)] - + 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)] - + 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), - ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), - ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)] - + 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), + ConditionalField(FieldLenField("subtlvslen", None, length_of=lambda x: x.subtlvs, fmt= "B"), lambda pkt: pkt.subtlvindicator == 1), + ConditionalField(PacketListField("subtlvs", [], _isis_guess_subtlv_cls, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1) + ] + 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)] + 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)] - + class ISIS_LspEntry(Packet): name = "ISIS LSP Entry" @@ -442,31 +457,34 @@ class ISIS_LspEntry(Packet): 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)] + 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) + ] 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), @@ -476,19 +494,24 @@ class ISIS_P2PAdjacencyStateTlv(ISIS_GenericTlv): ConditionalField(ISIS_SystemIdField("neighboursystemid", None), lambda pkt: pkt.len >= 11), ConditionalField(IntField("neighbourextlocalcircuitid", None), lambda pkt: pkt.len == 15)] + # 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)] - + 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)] + 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) + ] ####################################################################### @@ -503,21 +526,27 @@ class ISIS_IpReachabilityEntry(Packet): 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)] + 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) + ] + 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)] + 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) + ] class ISIS_IsReachabilityEntry(Packet): @@ -531,32 +560,35 @@ class ISIS_IsReachabilityEntry(Packet): 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), - ByteField("virtual", 0), - PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from= lambda x: (x.len - 1) / 11)] - + fields_desc = [ + ByteEnumField("type", 2, _isis_tlv_names), + FieldLenField("len", None, fmt="B", length_of="neighbours", adjust=lambda pkt,x: x+1), + ByteField("virtual", 0), + PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from=lambda x: (x.len - 1) / 11) + ] ####################################################################### ## 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" + 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= [ + name = "ISIS Common Header" + fields_desc = [ ByteEnumField("nlpid", 0x83, network_layer_protocol_ids), ByteField("hdrlen", None), ByteField("version", 1), @@ -569,44 +601,45 @@ class ISIS_CommonHdr(Packet): def post_build(self, pkt, pay): # calculating checksum if requested - pdu= pkt + pay - checksumInfo= self[1].checksum_info(self.hdrlen) - + 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)) + 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 + checksumPosition = hdrlen for tlv in self.tlvs: if isinstance(tlv, ISIS_ChecksumTlv): - checksumPosition+= 2 + checksumPosition += 2 return (0, checksumPosition) else: - checksumPosition+= len(tlv) - + 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) - + FieldLenField.__init__(self, "pdulength", None, length_of="tlvs", adjust=lambda pkt,x: x + pkt.underlayer.hdrlen) + class _ISIS_TlvListField(PacketListField): def __init__(self): PacketListField.__init__(self, "tlvs", [], _ISIS_GuessTlvClass, count_from= None, length_from= lambda pkt: pkt.pdulength - pkt.underlayer.hdrlen) + class _ISIS_LAN_HelloBase(_ISIS_PduBase): - fields_desc= [ + fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), @@ -616,17 +649,19 @@ class _ISIS_LAN_HelloBase(_ISIS_PduBase): _ISIS_TlvListField() ] + class ISIS_L1_LAN_Hello(_ISIS_LAN_HelloBase): - name= "ISIS L1 LAN Hello PDU" + name = "ISIS L1 LAN Hello PDU" + class ISIS_L2_LAN_Hello(_ISIS_LAN_HelloBase): - name= "ISIS L2 LAN Hello PDU" + name = "ISIS L2 LAN Hello PDU" class ISIS_P2P_Hello(_ISIS_PduBase): - name= "ISIS Point-to-Point Hello PDU" - - fields_desc= [ + name = "ISIS Point-to-Point Hello PDU" + + fields_desc = [ ISIS_CircuitTypeField(), ISIS_SystemIdField("sourceid", "0102.0304.0506"), ShortField("holdingtime", 30), @@ -637,7 +672,7 @@ class ISIS_P2P_Hello(_ISIS_PduBase): class _ISIS_LSP_Base(_ISIS_PduBase): - fields_desc= [ + fields_desc = [ _ISIS_PduLengthField(), ShortField("lifetime", 1199), ISIS_LspIdField("lspid", "0102.0304.0506.00-00"), @@ -646,32 +681,35 @@ class _ISIS_LSP_Base(_ISIS_PduBase): FlagsField("typeblock", 0x03, 8, ["L1", "L2", "OL", "ADef", "ADel", "AExp", "AErr", "P"]), _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" - + 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" - + 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= [ + fields_desc = [ _ISIS_PduLengthField(), ISIS_NodeIdField("sourceid", "0102.0304.0506.00"), ISIS_LspIdField("startlspid", "0000.0000.0000.00-00"), @@ -679,48 +717,54 @@ class _ISIS_CSNP_Base(_ISIS_PduBase): _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" - + 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" + 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= [ + 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" - + 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" - + 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) \ No newline at end of file +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) \ No newline at end of file diff --git a/scapy/contrib/isis.uts b/scapy/contrib/isis.uts new file mode 100644 index 0000000000000000000000000000000000000000..78cc12e7c02134ef8412a5997e597f1d2a01eebe --- /dev/null +++ b/scapy/contrib/isis.uts @@ -0,0 +1,57 @@ +% IS-IS Tests +* Tests for the IS-IS layer + ++ Basic Layer Tests + += Layer Binding +p = Dot3()/LLC()/ISIS_CommonHdr()/ISIS_P2P_Hello() +assert(p[LLC].dsap == 0xfe) +assert(p[LLC].ssap == 0xfe) +assert(p[LLC].ctrl == 0x03) +assert(p[ISIS_CommonHdr].nlpid == 0x83) +assert(p[ISIS_CommonHdr].pdutype == 17) +assert(p[ISIS_CommonHdr].hdrlen == 20) + ++ Package Tests += LSP +p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_L2_LSP( + lifetime=863, lspid="1720.1600.8016.00-00", seqnum=0x1f0, typeblock="L1+L2", + tlvs=[ + ISIS_AreaTlv( + areas=[ISIS_AreaEntry(areaid="49.1000")] + ), + ISIS_ProtocolsSupportedTlv( + nlpids=["IPv4", "IPv6"] + ), + ISIS_DynamicHostnameTlv( + hostname="BR-HH" + ), + ISIS_IpInterfaceAddressTlv( + addresses=["172.16.8.16"] + ), + ISIS_GenericTlv( + type=134, + val="\xac\x10\x08\x10" + ), + ISIS_ExtendedIpReachabilityTlv( + pfxs=[ + ISIS_ExtendedIpPrefix(metric=0, pfx="172.16.8.16/32"), + ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.109/30"), + ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.181/30") + ] + ), + ISIS_Ipv6ReachabilityTlv( + pfxs=[ + ISIS_Ipv6Prefix(metric=0, pfx="fe10:1::10/128"), + ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1::/64"), + ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1:12::/64") + ] + ), + ISIS_ExtendedIsReachabilityTlv( + neighbours=[ISIS_ExtendedIsNeighbourEntry(neighbourid="1720.1600.8004.00", metric=10)] + ) + ] + ) +p = p.__class__(str(p)) +assert(p[ISIS_L2_LSP].pdulength == 150) +assert(p[ISIS_L2_LSP].checksum == 0x8701) \ No newline at end of file diff --git a/scapy/layers/clns.py b/scapy/layers/clns.py index 37a2f51748d2f6f608416fb7b66c1d4a1c008603..0422f7890117e3108951ec46d8e38200592f2304 100644 --- a/scapy/layers/clns.py +++ b/scapy/layers/clns.py @@ -2,7 +2,7 @@ CLNS Extension ~~~~~~~~~~~~~~~~~~~~~ - :copyright: 2014 BENOCS GmbH, Berlin (Germany) + :copyright: 2014, 2015 BENOCS GmbH, Berlin (Germany) :author: Marcel Patzlaff, mpatzlaff@benocs.com :license: GPLv2 @@ -18,69 +18,67 @@ :description: - This module provides a layer and registration function for - OSI Connectionless-mode Network Services (such as IS-IS). - - :TODO: - - - rework this if a better way is found/implemented to bind - protocols such as IS-IS (or if IS-IS remains the sole CLN - protocol) - + This module provides a registration function and a generic PDU + for OSI Connectionless-mode Network Services (such as IS-IS). """ import struct from scapy.config import conf from scapy.fields import ByteEnumField, PacketField from scapy.layers.l2 import LLC -from scapy.packet import Packet, bind_layers - - -network_layer_protocol_ids= { - 0x00 : "Null", - 0x08 : "Q.933", - 0x80 : "IEEE SNAP", - 0x81 : "ISO 8438 CLNP", - 0x82 : "ISO 9542 ES-IS", - 0x83 : "ISO 10589 IS-IS", - 0x8E : "IPv6", - 0xB0 : "FRF.9", - 0xB1 : "FRF.12", - 0xC0 : "TRILL", - 0xC1 : "IEEE 802.aq", - 0xCC : "IPv4", - 0xCF : "PPP" +from scapy.packet import Packet, bind_top_down, bind_bottom_up + + +network_layer_protocol_ids = { + 0x00: "Null", + 0x08: "Q.933", + 0x80: "IEEE SNAP", + 0x81: "ISO 8438 CLNP", + 0x82: "ISO 9542 ES-IS", + 0x83: "ISO 10589 IS-IS", + 0x8E: "IPv6", + 0xB0: "FRF.9", + 0xB1: "FRF.12", + 0xC0: "TRILL", + 0xC1: "IEEE 802.aq", + 0xCC: "IPv4", + 0xCF: "PPP" } -_cln_protocols= {} +_cln_protocols = {} + class _GenericClnsPdu(Packet): - name= "Generic CLNS PDU" - fields_desc= [ + name = "Generic CLNS PDU" + fields_desc = [ ByteEnumField("nlpid", 0x00, network_layer_protocol_ids), PacketField("rawdata", None, conf.raw_layer) ] - - -class ConnectionlessNetworkService(Packet): - name= "Connectionless-mode Network Service" - - def guess_payload_class(self, p): - cls= conf.raw_layer - - if len(p) >= 1: - nlpid = struct.unpack("!B", p[0])[0] - cls= _cln_protocols.get(nlpid, _GenericClnsPdu) - - return cls + + +def _create_cln_pdu(s, **kwargs): + pdu_cls = conf.raw_layer + + if len(s) >= 1: + nlpid = struct.unpack("!B", s[0])[0] + pdu_cls = _cln_protocols.get(nlpid, _GenericClnsPdu) + + return pdu_cls(s, **kwargs) + @conf.commands.register def register_cln_protocol(nlpid, cln_protocol_class): if nlpid is None or cln_protocol_class is None: return - - _cln_protocols[nlpid]= cln_protocol_class + + chk = _cln_protocols.get(nlpid, None) + if chk is not None and chk != cln_protocol_class: + raise ValueError("different protocol already registered!") + + _cln_protocols[nlpid] = cln_protocol_class + bind_top_down(LLC, cln_protocol_class, dsap=0xfe, ssap=0xfe, ctrl=3) -bind_layers(LLC, ConnectionlessNetworkService, dsap=0xfe, ssap=0xfe, ctrl=3) \ No newline at end of file +bind_top_down(LLC, _GenericClnsPdu, dsap=0xfe, ssap=0xfe, ctrl=3) +bind_bottom_up(LLC, _create_cln_pdu, dsap=0xfe, ssap=0xfe, ctrl=3)