Home | History | Annotate | Download | only in contrib
      1 # This file is part of Scapy
      2 # Scapy is free software: you can redistribute it and/or modify
      3 # it under the terms of the GNU General Public License as published by
      4 # the Free Software Foundation, either version 2 of the License, or
      5 # any later version.
      6 #
      7 # Scapy is distributed in the hope that it will be useful,
      8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     10 # GNU General Public License for more details.
     11 #
     12 # You should have received a copy of the GNU General Public License
     13 # along with Scapy. If not, see <http://www.gnu.org/licenses/>.
     14 
     15 # scapy.contrib.description = ISIS
     16 # scapy.contrib.status = loads
     17 
     18 """
     19     IS-IS Scapy Extension
     20     ~~~~~~~~~~~~~~~~~~~~~
     21 
     22     :copyright: 2014-2016 BENOCS GmbH, Berlin (Germany)
     23     :author:    Marcel Patzlaff, mpatzlaff (at] benocs.com
     24                 Michal Kaliszan, mkaliszan (at] benocs.com
     25     :license:   GPLv2
     26 
     27         This module is free software; you can redistribute it and/or
     28         modify it under the terms of the GNU General Public License
     29         as published by the Free Software Foundation; either version 2
     30         of the License, or (at your option) any later version.
     31 
     32         This module is distributed in the hope that it will be useful,
     33         but WITHOUT ANY WARRANTY; without even the implied warranty of
     34         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     35         GNU General Public License for more details.
     36 
     37     :description:
     38 
     39         This module provides Scapy layers for the Intermediate System
     40         to Intermediate System routing protocol as defined in RFC 1195.
     41 
     42         Currently it (partially) supports the packaging/encoding
     43         requirements of the following RFCs:
     44          * RFC 1195 (only the TCP/IP related part)
     45          * RFC 3358 (optional checksums)
     46          * RFC 5301 (dynamic hostname extension)
     47          * RFC 5302 (domain-wide prefix distribution)
     48          * RFC 5303 (three-way handshake)
     49          * RFC 5304 (cryptographic authentication)
     50          * RFC 5308 (routing IPv6 with IS-IS)
     51 
     52     :TODO:
     53 
     54         - packet relations (requests, responses)
     55         - support for recent RFCs:
     56           * RFC 5305 (traffic engineering)
     57           * RFC 5307 (support for G-MPLS)
     58           * RFC 5310 (generic cryptographic authentication)
     59           * RFC 5316 (inter-AS MPLS and G-MPLS TE)
     60 
     61 """
     62 
     63 from __future__ import absolute_import
     64 import struct
     65 import random
     66 
     67 from scapy.config import conf
     68 from scapy.fields import *
     69 from scapy.packet import *
     70 from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol
     71 from scapy.layers.inet6 import IP6ListField, IP6Field
     72 from scapy.utils import fletcher16_checkbytes
     73 from scapy.volatile import RandString, RandByte
     74 import random
     75 from scapy.modules.six.moves import range
     76 from scapy.compat import raw
     77 
     78 EXT_VERSION = "v0.0.2"
     79 
     80 conf.debug_dissector = True
     81 
     82 
     83 #######################################################################
     84 ##  ISIS Utilities + Fields                                          ##
     85 #######################################################################
     86 def isis_area2str(area):
     87     return b"".join(hex_bytes(x) for x in area.split("."))
     88 
     89 
     90 def isis_str2area(s):
     91     if len(s) == 0:
     92         return ""
     93 
     94     numbytes = len(s[1:])
     95     fmt = "%02X" + (".%02X%02X" * (numbytes // 2)) + ("" if (numbytes % 2) == 0 else ".%02X")
     96     return fmt % tuple(orb(x) for x in s)
     97 
     98 
     99 def isis_sysid2str(sysid):
    100     return b"".join(hex_bytes(x) for x in sysid.split("."))
    101 
    102 
    103 def isis_str2sysid(s):
    104     return ("%02X%02X."*3)[:-1] % tuple(orb(x) for x in s)
    105 
    106 
    107 def isis_nodeid2str(nodeid):
    108     return isis_sysid2str(nodeid[:-3]) + hex_bytes(nodeid[-2:])
    109 
    110 
    111 def isis_str2nodeid(s):
    112     return "%s.%02X" % (isis_str2sysid(s[:-1]), orb(s[-1]))
    113 
    114 
    115 def isis_lspid2str(lspid):
    116     return isis_nodeid2str(lspid[:-3]) + hex_bytes(lspid[-2:])
    117 
    118 
    119 def isis_str2lspid(s):
    120     return "%s-%02X" % (isis_str2nodeid(s[:-1]), orb(s[-1]))
    121 
    122 
    123 class _ISIS_IdFieldBase(Field):
    124     __slots__ = ["to_str", "to_id", "length"]
    125     def __init__(self, name, default, length, to_str, to_id):
    126         self.to_str = to_str
    127         self.to_id = to_id
    128         self.length = length
    129         Field.__init__(self, name, default, "%is" % length)
    130 
    131     def i2m(self, pkt, x):
    132         if x is None:
    133             return b"\0"*self.length
    134 
    135         return self.to_str(x)
    136 
    137     def m2i(self, pkt, x):
    138         return self.to_id(x)
    139 
    140     def any2i(self, pkt, x):
    141         if isinstance(x, str) and len(x) == self.length:
    142             return self.m2i(pkt, x)
    143 
    144         return x
    145 
    146 
    147 class _ISIS_RandId(RandString):
    148     def __init__(self, template):
    149         self.bytecount = template.count("*")
    150         self.format = template.replace("*", "%02X")
    151 
    152     def _fix(self):
    153         if self.bytecount == 0:
    154             return ""
    155 
    156         val = ()
    157 
    158         for _ in range(self.bytecount):
    159             val += (RandByte(),)
    160 
    161         return self.format % val
    162 
    163 
    164 class _ISIS_RandAreaId(_ISIS_RandId):
    165     def __init__(self, bytecount= None):
    166         self.bytecount = random.randint(1, 13) if bytecount is None else bytecount
    167         self.format = "%02X" + (".%02X%02X" * ((self.bytecount-1) // 2)) + ("" if ((self.bytecount-1) % 2) == 0 else ".%02X")
    168 
    169 
    170 class ISIS_AreaIdField(Field):
    171     __slots__ = ["length_from"]
    172 
    173     def __init__(self, name, default, length_from):
    174         Field.__init__(self, name, default)
    175         self.length_from = length_from
    176 
    177     def i2m(self, pkt, x):
    178         return isis_area2str(x)
    179 
    180     def m2i(self, pkt, x):
    181         return isis_str2area(x)
    182 
    183     def i2len(self, pkt, x):
    184         if x is None:
    185             return 0
    186         l = len(x)
    187         # l/5 is the number of dots in the Area ID
    188         return (l - (l // 5)) // 2
    189 
    190     def addfield(self, pkt, s, val):
    191         sval = self.i2m(pkt, val)
    192         return s+struct.pack("!%is" % len(sval), sval)
    193 
    194     def getfield(self, pkt, s):
    195         numbytes = self.length_from(pkt)
    196         return s[numbytes:], self.m2i(pkt, struct.unpack("!%is" % numbytes, s[:numbytes])[0])
    197 
    198     def randval(self):
    199         return _ISIS_RandAreaId()
    200 
    201 
    202 class ISIS_SystemIdField(_ISIS_IdFieldBase):
    203     def __init__(self, name, default):
    204         _ISIS_IdFieldBase.__init__(self, name, default, 6, isis_sysid2str, isis_str2sysid)
    205 
    206     def randval(self):
    207         return _ISIS_RandId("**.**.**")
    208 
    209 
    210 class ISIS_NodeIdField(_ISIS_IdFieldBase):
    211     def __init__(self, name, default):
    212         _ISIS_IdFieldBase.__init__(self, name, default, 7, isis_nodeid2str, isis_str2nodeid)
    213 
    214     def randval(self):
    215         return _ISIS_RandId("**.**.**.*")
    216 
    217 
    218 class ISIS_LspIdField(_ISIS_IdFieldBase):
    219     def __init__(self, name, default):
    220         _ISIS_IdFieldBase.__init__(self, name, default, 8, isis_lspid2str, isis_str2lspid)
    221 
    222     def randval(self):
    223         return _ISIS_RandId("**.**.**.*-*")
    224 
    225 
    226 class ISIS_CircuitTypeField(FlagsField):
    227     def __init__(self, name="circuittype", default=2, size=8,
    228                  names=None):
    229         if names is None:
    230             names = ["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"]
    231         FlagsField.__init__(self, name, default, size, names)
    232 
    233 
    234 def _ISIS_GuessTlvClass_Helper(tlv_classes, defaultname, p, **kargs):
    235     cls = conf.raw_layer
    236     if len(p) >= 2:
    237         tlvtype = orb(p[0])
    238         clsname = tlv_classes.get(tlvtype, defaultname)
    239         cls = globals()[clsname]
    240 
    241     return cls(p, **kargs)
    242 
    243 
    244 class _ISIS_GenericTlv_Base(Packet):
    245     fields_desc = [ByteField("type", 0),
    246                    FieldLenField("len", None, length_of="val", fmt="B"),
    247                    BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)]
    248 
    249     def guess_payload_class(self, p):
    250         return conf.padding_layer
    251 
    252 
    253 class ISIS_GenericTlv(_ISIS_GenericTlv_Base):
    254     name = "ISIS Generic TLV"
    255 
    256 
    257 class ISIS_GenericSubTlv(_ISIS_GenericTlv_Base):
    258     name = "ISIS Generic Sub-TLV"
    259 
    260 
    261 #######################################################################
    262 ##  ISIS Sub-TLVs for TLVs 22, 23, 141, 222, 223                     ##
    263 #######################################################################
    264 _isis_subtlv_classes_1 = {
    265     4:  "ISIS_LinkLocalRemoteIdentifiersSubTlv",
    266     6:  "ISIS_IPv4InterfaceAddressSubTlv",
    267     8:  "ISIS_IPv4NeighborAddressSubTlv",
    268     12: "ISIS_IPv6InterfaceAddressSubTlv",
    269     13: "ISIS_IPv6NeighborAddressSubTlv"
    270 }
    271 
    272 _isis_subtlv_names_1 = {
    273     4:  "Link Local/Remote Identifiers",
    274     6:  "IPv4 Interface Address",
    275     8:  "IPv4 Neighbor Address",
    276     12: "IPv6 Interface Address",
    277     13: "IPv6 Neighbor Address"
    278 }
    279 
    280 
    281 def _ISIS_GuessSubTlvClass_1(p, **kargs):
    282     return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_1, "ISIS_GenericSubTlv", p, **kargs)
    283 
    284 
    285 class ISIS_IPv4InterfaceAddressSubTlv(ISIS_GenericSubTlv):
    286     name = "ISIS IPv4 Interface Address (S)"
    287     fields_desc = [ByteEnumField("type", 6, _isis_subtlv_names_1),
    288                    FieldLenField("len", None, length_of= "address", fmt="B"),
    289                    IPField("address", "0.0.0.0")]
    290 
    291 
    292 class ISIS_IPv4NeighborAddressSubTlv(ISIS_GenericSubTlv):
    293     name = "ISIS IPv4 Neighbor Address (S)"
    294     fields_desc = [ByteEnumField("type", 8, _isis_subtlv_names_1),
    295                    FieldLenField("len", None, length_of= "address", fmt="B"),
    296                    IPField("address", "0.0.0.0")]
    297 
    298 
    299 class ISIS_LinkLocalRemoteIdentifiersSubTlv(ISIS_GenericSubTlv):
    300     name = "ISIS Link Local/Remote Identifiers (S)"
    301     fields_desc = [ByteEnumField("type", 4, _isis_subtlv_names_1),
    302                    FieldLenField("len", 8, fmt="B"),
    303                    IntField("localid", "0"),
    304                    IntField("remoteid", "0")]
    305 
    306 
    307 class ISIS_IPv6InterfaceAddressSubTlv(ISIS_GenericSubTlv):
    308     name = "ISIS IPv6 Interface Address (S)"
    309     fields_desc = [ByteEnumField("type", 12, _isis_subtlv_names_1),
    310                    FieldLenField("len", None, length_of= "address", fmt="B"),
    311                    IP6Field("address", "::")]
    312 
    313 
    314 class ISIS_IPv6NeighborAddressSubTlv(ISIS_GenericSubTlv):
    315     name = "ISIS IPv6 Neighbor Address (S)"
    316     fields_desc = [ByteEnumField("type", 13, _isis_subtlv_names_1),
    317                    FieldLenField("len", None, length_of= "address", fmt="B"),
    318                    IP6Field("address", "::")]
    319 
    320 
    321 #######################################################################
    322 ##  ISIS Sub-TLVs for TLVs 135, 235, 236, and 237                    ##
    323 #######################################################################
    324 _isis_subtlv_classes_2 = {
    325     1:  "ISIS_32bitAdministrativeTagSubTlv",
    326     2:  "ISIS_64bitAdministrativeTagSubTlv"
    327 }
    328 
    329 _isis_subtlv_names_2 = {
    330     1:  "32-bit Administrative Tag",
    331     2:  "64-bit Administrative Tag"
    332 }
    333 
    334 
    335 def _ISIS_GuessSubTlvClass_2(p, **kargs):
    336     return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_2, "ISIS_GenericSubTlv", p, **kargs)
    337 
    338 
    339 class ISIS_32bitAdministrativeTagSubTlv(ISIS_GenericSubTlv):
    340     name = "ISIS 32-bit Administrative Tag (S)"
    341     fields_desc = [ByteEnumField("type", 1, _isis_subtlv_names_2),
    342                    FieldLenField("len", None, length_of= "tags", fmt="B"),
    343                    FieldListField("tags", [], IntField("", 0), count_from= lambda pkt: pkt.len // 4)]
    344 
    345 
    346 class ISIS_64bitAdministrativeTagSubTlv(ISIS_GenericSubTlv):
    347     name = "ISIS 64-bit Administrative Tag (S)"
    348     fields_desc = [ByteEnumField("type", 2, _isis_subtlv_names_2),
    349                    FieldLenField("len", None, length_of= "tags", fmt="B"),
    350                    FieldListField("tags", [], LongField("", 0), count_from= lambda pkt: pkt.len // 8)]
    351 
    352 
    353 #######################################################################
    354 ##  ISIS TLVs                                                        ##
    355 #######################################################################
    356 _isis_tlv_classes = { 
    357     1: "ISIS_AreaTlv",
    358     2: "ISIS_IsReachabilityTlv",
    359     6: "ISIS_IsNeighbourTlv",
    360     8: "ISIS_PaddingTlv",
    361     9: "ISIS_LspEntryTlv",
    362    10: "ISIS_AuthenticationTlv",
    363    12: "ISIS_ChecksumTlv",
    364    14: "ISIS_BufferSizeTlv",
    365    22: "ISIS_ExtendedIsReachabilityTlv",
    366   128: "ISIS_InternalIpReachabilityTlv",
    367   129: "ISIS_ProtocolsSupportedTlv",
    368   130: "ISIS_ExternalIpReachabilityTlv",
    369   132: "ISIS_IpInterfaceAddressTlv",
    370   135: "ISIS_ExtendedIpReachabilityTlv",
    371   137: "ISIS_DynamicHostnameTlv",
    372   232: "ISIS_Ipv6InterfaceAddressTlv",
    373   236: "ISIS_Ipv6ReachabilityTlv",
    374   240: "ISIS_P2PAdjacencyStateTlv"
    375 }
    376 
    377 _isis_tlv_names = {
    378     1: "Area TLV",
    379     2: "IS Reachability TLV",
    380     6: "IS Neighbour TLV",
    381     7: "Instance Identifier TLV",
    382     8: "Padding TLV",
    383     9: "LSP Entries TLV",
    384    10: "Authentication TLV",
    385    12: "Optional Checksum TLV",
    386    13: "Purge Originator Identification TLV", 
    387    14: "LSP Buffer Size TLV",
    388    22: "Extended IS-Reachability TLV",
    389    23: "IS Neighbour Attribute TLV",
    390    24: "IS Alias ID",
    391   128: "IP Internal Reachability TLV",
    392   129: "Protocols Supported TLV",
    393   130: "IP External Reachability TLV",
    394   131: "Inter-Domain Routing Protocol Information TLV",
    395   132: "IP Interface Address TLV",
    396   134: "Traffic Engineering Router ID TLV",
    397   135: "Extended IP Reachability TLV",
    398   137: "Dynamic Hostname TLV",
    399   138: "GMPLS Shared Risk Link Group TLV",
    400   139: "IPv6 Shared Risk Link Group TLV",
    401   140: "IPv6 Traffic Engineering Router ID TLV",
    402   141: "Inter-AS Reachability Information TLV",
    403   142: "Group Address TLV",
    404   143: "Multi-Topology-Aware Port Capability TLV",
    405   144: "Multi-Topology Capability TLV",
    406   145: "TRILL Neighbour TLV",
    407   147: "MAC-Reachability TLV",
    408   148: "BFD-Enabled TLV",
    409   211: "Restart TLV",
    410   222: "Multi-Topology Intermediate Systems TLV",
    411   223: "Multi-Topology IS Neighbour Attributes TLV",
    412   229: "Multi-Topology TLV",
    413   232: "IPv6 Interface Address TLV",
    414   233: "IPv6 Global Interface Address TLV",
    415   235: "Multi-Topology IPv4 Reachability TLV",
    416   236: "IPv6 Reachability TLV",
    417   237: "Multi-Topology IPv6 Reachability TLV",
    418   240: "Point-to-Point Three-Way Adjacency TLV",
    419   242: "IS-IS Router Capability TLV",
    420   251: "Generic Information TLV"
    421 }
    422 
    423 
    424 def _ISIS_GuessTlvClass(p, **kargs):
    425     return _ISIS_GuessTlvClass_Helper(_isis_tlv_classes, "ISIS_GenericTlv", p, **kargs)
    426 
    427 
    428 class ISIS_AreaEntry(Packet):
    429     name = "ISIS Area Entry"
    430     fields_desc = [FieldLenField("arealen", None, length_of="areaid", fmt="B"),
    431                    ISIS_AreaIdField("areaid", "49", length_from=lambda pkt: pkt.arealen)]
    432 
    433     def extract_padding(self, s):
    434         return "", s
    435 
    436 
    437 class ISIS_AreaTlv(ISIS_GenericTlv):
    438     name = "ISIS Area TLV"
    439     fields_desc = [ByteEnumField("type", 1, _isis_tlv_names),
    440                    FieldLenField("len", None, length_of= "areas", fmt="B"),
    441                    PacketListField("areas", [], ISIS_AreaEntry, length_from=lambda x: x.len)]
    442 
    443 
    444 class ISIS_AuthenticationTlv(ISIS_GenericTlv):
    445     name = "ISIS Authentication TLV"
    446     fields_desc = [ByteEnumField("type", 10, _isis_tlv_names),
    447                    FieldLenField("len", None, length_of= "password", adjust=lambda pkt,x: x + 1, fmt="B"),
    448                    ByteEnumField("authtype", 1, {1: "Plain", 17: "HMAC-MD5"}),
    449                    BoundStrLenField("password", "", maxlen= 254, length_from=lambda pkt: pkt.len - 1)]
    450 
    451 
    452 class ISIS_BufferSizeTlv(ISIS_GenericTlv):
    453     name = "ISIS Buffer Size TLV"
    454     fields_desc = [ByteEnumField("type", 14, _isis_tlv_names),
    455                    ByteField("len", 2),
    456                    ShortField("lspbuffersize", 1497)]
    457 
    458 
    459 class ISIS_ChecksumTlv(ISIS_GenericTlv):
    460     name = "ISIS Optional Checksum TLV"
    461     fields_desc = [ByteEnumField("type", 12, _isis_tlv_names),
    462                    ByteField("len", 2),
    463                    XShortField("checksum", None)]
    464 
    465 
    466 class ISIS_DynamicHostnameTlv(ISIS_GenericTlv):
    467     name = "ISIS Dynamic Hostname TLV"
    468     fields_desc = [ByteEnumField("type", 137, _isis_tlv_names),
    469                    FieldLenField("len", None, length_of= "hostname", fmt="B"),
    470                    BoundStrLenField("hostname", "", length_from=lambda pkt: pkt.len)]
    471 
    472 
    473 class ISIS_ExtendedIpPrefix(Packet):
    474     name = "ISIS Extended IP Prefix"
    475     fields_desc = [
    476         IntField("metric", 1),
    477         BitField("updown", 0, 1),
    478         BitField("subtlvindicator", 0, 1),
    479         BitFieldLenField("pfxlen", None, 6, length_of="pfx"),
    480         IPPrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen),
    481         ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"), lambda pkt: pkt.subtlvindicator == 1),
    482         ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)
    483     ]
    484 
    485     def extract_padding(self, s):
    486         return "", s
    487 
    488  
    489 class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv):
    490     name = "ISIS Extended IP Reachability TLV"
    491     fields_desc = [ByteEnumField("type", 135, _isis_tlv_names),
    492                    FieldLenField("len", None, length_of="pfxs", fmt="B"),
    493                    PacketListField("pfxs", [], ISIS_ExtendedIpPrefix, length_from= lambda pkt: pkt.len)]
    494 
    495 
    496 class ISIS_ExtendedIsNeighbourEntry(Packet):
    497     name = "ISIS Extended IS Neighbour Entry"
    498     fields_desc = [
    499         ISIS_NodeIdField("neighbourid", "0102.0304.0506.07"),
    500         ThreeBytesField("metric", 1),
    501         FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"),
    502         PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_1, length_from=lambda x: x.subtlvslen)
    503     ]
    504 
    505     def extract_padding(self, s):
    506         return "", s
    507 
    508 
    509 class ISIS_ExtendedIsReachabilityTlv(ISIS_GenericTlv):
    510     name = "ISIS Extended IS Reachability TLV"
    511     fields_desc = [ByteEnumField("type", 22, _isis_tlv_names),
    512                    FieldLenField("len", None, length_of="neighbours", fmt="B"),
    513                    PacketListField("neighbours", [], ISIS_ExtendedIsNeighbourEntry, length_from=lambda x: x.len)]
    514 
    515 
    516 class ISIS_IpInterfaceAddressTlv(ISIS_GenericTlv):
    517     name = "ISIS IP Interface Address TLV"
    518     fields_desc = [ByteEnumField("type", 132, _isis_tlv_names),
    519                    FieldLenField("len", None, length_of= "addresses", fmt="B"),
    520                    FieldListField("addresses", [], IPField("", "0.0.0.0"), count_from= lambda pkt: pkt.len // 4)]
    521 
    522 
    523 class ISIS_Ipv6InterfaceAddressTlv(ISIS_GenericTlv):
    524     name = "ISIS IPv6 Interface Address TLV"
    525     fields_desc = [
    526         ByteEnumField("type", 232, _isis_tlv_names),
    527         FieldLenField("len", None, length_of="addresses", fmt="B"),
    528         IP6ListField("addresses", [], count_from=lambda pkt: pkt.len // 16)
    529     ]
    530 
    531 
    532 class ISIS_Ipv6Prefix(Packet):
    533     name = "ISIS IPv6 Prefix"
    534     fields_desc = [
    535         IntField("metric", 1),
    536         BitField("updown", 0, 1),
    537         BitField("external", 0, 1),
    538         BitField("subtlvindicator", 0, 1),
    539         BitField("reserved", 0, 5),
    540         FieldLenField("pfxlen", None, length_of="pfx", fmt="B"),
    541         IP6PrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen),
    542         ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"), lambda pkt: pkt.subtlvindicator == 1),
    543         ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)
    544     ]
    545 
    546     def extract_padding(self, s):
    547         return "", s
    548 
    549 
    550 class ISIS_Ipv6ReachabilityTlv(ISIS_GenericTlv):
    551     name= "ISIS IPv6 Reachability TLV"
    552     fields_desc = [ByteEnumField("type", 236, _isis_tlv_names),
    553                    FieldLenField("len", None, length_of= "pfxs", fmt="B"),
    554                    PacketListField("pfxs", [], ISIS_Ipv6Prefix, length_from= lambda pkt: pkt.len)]
    555 
    556 
    557 class ISIS_IsNeighbourTlv(ISIS_GenericTlv):
    558     name = "ISIS IS Neighbour TLV"
    559     fields_desc = [ByteEnumField("type", 6, _isis_tlv_names),
    560                    FieldLenField("len", None, length_of= "neighbours", fmt="B"),
    561                    FieldListField("neighbours", [], MACField("", "00.00.00.00.00.00"), count_from= lambda pkt: pkt.len // 6)]
    562 
    563 
    564 class ISIS_LspEntry(Packet):
    565     name = "ISIS LSP Entry"
    566     fields_desc = [ShortField("lifetime", 1200),
    567                    ISIS_LspIdField("lspid", "0102.0304.0506.07-08"),
    568                    XIntField("seqnum", 0x00000001),
    569                    XShortField("checksum", None)]
    570 
    571     def extract_padding(self, s):
    572         return "", s
    573 
    574 
    575 class ISIS_LspEntryTlv(ISIS_GenericTlv):
    576     name = "ISIS LSP Entry TLV"
    577     fields_desc = [
    578         ByteEnumField("type", 9, _isis_tlv_names),
    579         FieldLenField("len", None, length_of="entries", fmt="B"),
    580         PacketListField("entries", [], ISIS_LspEntry, count_from=lambda pkt: pkt.len // 16)
    581     ]
    582 
    583 
    584 class _AdjacencyStateTlvLenField(Field):
    585     def i2m(self, pkt, x):
    586         if pkt.neighbourextlocalcircuitid is not None:
    587             return 15
    588 
    589         if pkt.neighboursystemid is not None:
    590             return 11
    591 
    592         if pkt.extlocalcircuitid is not None:
    593             return 5
    594 
    595         return 1
    596 
    597 
    598 class ISIS_P2PAdjacencyStateTlv(ISIS_GenericTlv):
    599     name = "ISIS P2P Adjacency State TLV"
    600     fields_desc = [ByteEnumField("type", 240, _isis_tlv_names),
    601                _AdjacencyStateTlvLenField("len", None, fmt="B"),
    602                ByteEnumField("state", "Down", {0x2 : "Down", 0x1 : "Initialising", 0x0 : "Up"}),
    603                ConditionalField(IntField("extlocalcircuitid", None), lambda pkt: pkt.len >= 5),
    604                ConditionalField(ISIS_SystemIdField("neighboursystemid", None), lambda pkt: pkt.len >= 11),
    605                ConditionalField(IntField("neighbourextlocalcircuitid", None), lambda pkt: pkt.len == 15)]
    606 
    607 
    608 # TODO dynamically allocate sufficient size
    609 class ISIS_PaddingTlv(ISIS_GenericTlv):
    610     name = "ISIS Padding TLV"
    611     fields_desc = [
    612         ByteEnumField("type", 8, _isis_tlv_names),
    613         FieldLenField("len", None, length_of="padding", fmt="B"),
    614         BoundStrLenField("padding", "", length_from=lambda pkt: pkt.len)
    615     ]
    616 
    617 
    618 class ISIS_ProtocolsSupportedTlv(ISIS_GenericTlv):
    619     name = "ISIS Protocols Supported TLV"
    620     fields_desc = [
    621         ByteEnumField("type", 129, _isis_tlv_names),
    622         FieldLenField("len", None, count_of="nlpids", fmt="B"),
    623         FieldListField("nlpids", [], ByteEnumField("", "IPv4", network_layer_protocol_ids), count_from=lambda pkt: pkt.len)
    624     ]
    625 
    626 
    627 #######################################################################
    628 ##  ISIS Old-Style TLVs                                              ##
    629 #######################################################################
    630 
    631 class ISIS_IpReachabilityEntry(Packet):
    632     name = "ISIS IP Reachability"
    633     fields_desc = [ByteField("defmetric", 1),
    634                    ByteField("delmetric", 0x80),
    635                    ByteField("expmetric", 0x80),
    636                    ByteField("errmetric", 0x80),
    637                    IPField("ipaddress", "0.0.0.0"),
    638                    IPField("subnetmask", "255.255.255.255")]
    639 
    640     def extract_padding(self, s):
    641         return "", s
    642 
    643 
    644 class ISIS_InternalIpReachabilityTlv(ISIS_GenericTlv):
    645     name = "ISIS Internal IP Reachability TLV"
    646     fields_desc = [
    647         ByteEnumField("type", 128, _isis_tlv_names),
    648         FieldLenField("len", None, length_of="entries", fmt="B"),
    649         PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12)
    650     ]
    651 
    652 
    653 class ISIS_ExternalIpReachabilityTLV(ISIS_GenericTlv):
    654     name = "ISIS External IP Reachability TLV"
    655     fields_desc = [
    656         ByteEnumField("type", 130, _isis_tlv_names),
    657         FieldLenField("len", None, length_of="entries", fmt="B"),
    658         PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12)
    659     ]
    660 
    661 
    662 class ISIS_IsReachabilityEntry(Packet):
    663     name = "ISIS IS Reachability"
    664     fields_desc = [ByteField("defmetric", 1),
    665                    ByteField("delmetric", 0x80),
    666                    ByteField("expmetric", 0x80),
    667                    ByteField("errmetric", 0x80),
    668                    ISIS_NodeIdField("neighbourid", "0102.0304.0506.07")]
    669 
    670     def extract_padding(self, s):
    671         return "", s
    672 
    673 
    674 class ISIS_IsReachabilityTlv(ISIS_GenericTlv):
    675     name = "ISIS IS Reachability TLV"
    676     fields_desc = [
    677         ByteEnumField("type", 2, _isis_tlv_names),
    678         FieldLenField("len", None, fmt="B", length_of="neighbours", adjust=lambda pkt,x: x+1),
    679         ByteField("virtual", 0),
    680         PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from=lambda x: (x.len - 1) // 11)
    681     ]
    682 
    683 #######################################################################
    684 ##  ISIS PDU Packets                                                 ##
    685 #######################################################################
    686 _isis_pdu_names = {
    687     15: "L1 LAN Hello",
    688     16: "L2 LAN Hello",
    689     17: "P2P Hello",
    690     18: "L1 LSP",
    691     20: "L2 LSP",
    692     24: "L1 CSNP",
    693     25: "L2 CSNP",
    694     26: "L1 PSNP",
    695     27: "L2 PSNP"
    696 }
    697 
    698 
    699 class ISIS_CommonHdr(Packet):
    700     name = "ISIS Common Header"
    701     fields_desc = [
    702         ByteEnumField("nlpid", 0x83, network_layer_protocol_ids),
    703         ByteField("hdrlen", None),
    704         ByteField("version", 1),
    705         ByteField("idlen", 0),
    706         ByteEnumField("pdutype", None, _isis_pdu_names),
    707         ByteField("pduversion", 1),
    708         ByteField("hdrreserved", 0),
    709         ByteField("maxareaaddr", 0)
    710     ]
    711 
    712     def post_build(self, pkt, pay):
    713         # calculating checksum if requested
    714         pdu = pkt + pay
    715         checksumInfo = self[1].checksum_info(self.hdrlen)
    716 
    717         if checksumInfo is not None:
    718             (cbegin, cpos) = checksumInfo
    719             checkbytes = fletcher16_checkbytes(pdu[cbegin:], (cpos - cbegin))
    720             pdu = pdu[:cpos] + checkbytes + pdu[cpos+2:]
    721 
    722         return pdu
    723 
    724 
    725 class _ISIS_PduBase(Packet):
    726     def checksum_info(self, hdrlen):
    727         checksumPosition = hdrlen
    728         for tlv in self.tlvs:
    729             if isinstance(tlv, ISIS_ChecksumTlv):
    730                 checksumPosition += 2
    731                 return (0, checksumPosition)
    732             else:
    733                 checksumPosition += len(tlv)
    734 
    735         return None
    736 
    737     def guess_payload_class(self, p):
    738         return conf.padding_layer
    739 
    740 
    741 class _ISIS_PduLengthField(FieldLenField):
    742     def __init__(self):
    743         FieldLenField.__init__(self, "pdulength", None, length_of="tlvs", adjust=lambda pkt,x: x + pkt.underlayer.hdrlen)
    744 
    745 
    746 class _ISIS_TlvListField(PacketListField):
    747     def __init__(self):
    748         PacketListField.__init__(self, "tlvs", [], _ISIS_GuessTlvClass, length_from= lambda pkt: pkt.pdulength - pkt.underlayer.hdrlen)
    749 
    750 
    751 class _ISIS_LAN_HelloBase(_ISIS_PduBase):
    752     fields_desc = [
    753         ISIS_CircuitTypeField(),
    754         ISIS_SystemIdField("sourceid", "0102.0304.0506"),
    755         ShortField("holdingtime", 30),
    756         _ISIS_PduLengthField(),
    757         ByteField("priority", 1),
    758         ISIS_NodeIdField("lanid", "0000.0000.0000.00"),
    759         _ISIS_TlvListField()
    760     ]
    761 
    762 
    763 class ISIS_L1_LAN_Hello(_ISIS_LAN_HelloBase):
    764     name = "ISIS L1 LAN Hello PDU"
    765 
    766 
    767 class ISIS_L2_LAN_Hello(_ISIS_LAN_HelloBase):
    768     name = "ISIS L2 LAN Hello PDU"
    769 
    770 
    771 class ISIS_P2P_Hello(_ISIS_PduBase):
    772     name = "ISIS Point-to-Point Hello PDU"
    773 
    774     fields_desc = [
    775         ISIS_CircuitTypeField(),
    776         ISIS_SystemIdField("sourceid", "0102.0304.0506"),
    777         ShortField("holdingtime", 30),
    778         _ISIS_PduLengthField(),
    779         ByteField("localcircuitid", 0),
    780         _ISIS_TlvListField()
    781     ]
    782 
    783 
    784 class _ISIS_LSP_Base(_ISIS_PduBase):
    785     fields_desc = [
    786         _ISIS_PduLengthField(),
    787         ShortField("lifetime", 1199),
    788         ISIS_LspIdField("lspid", "0102.0304.0506.00-00"),
    789         XIntField("seqnum", 0x00000001),
    790         XShortField("checksum", None),
    791         FlagsField("typeblock", 0x03, 8, ["L1", "L2", "OL", "ADef", "ADel", "AExp", "AErr", "P"]),
    792         _ISIS_TlvListField()
    793     ]
    794 
    795     def checksum_info(self, hdrlen):
    796         if self.checksum is not None:
    797             return None
    798 
    799         return (12, 24)
    800 
    801 
    802 def _lsp_answers(lsp, other, clsname):
    803     # TODO
    804     return 0
    805 
    806 
    807 class ISIS_L1_LSP(_ISIS_LSP_Base):
    808     name = "ISIS L1 Link State PDU"
    809 
    810     def answers(self, other):
    811         return _lsp_answers(self, other, "ISIS_L1_PSNP")
    812 
    813 
    814 class ISIS_L2_LSP(_ISIS_LSP_Base):
    815     name = "ISIS L2 Link State PDU"
    816 
    817     def answers(self, other):
    818         return _lsp_answers(self, other, "ISIS_L2_PSNP")
    819 
    820 
    821 class _ISIS_CSNP_Base(_ISIS_PduBase):
    822     fields_desc = [
    823         _ISIS_PduLengthField(),
    824         ISIS_NodeIdField("sourceid", "0102.0304.0506.00"),
    825         ISIS_LspIdField("startlspid", "0000.0000.0000.00-00"),
    826         ISIS_LspIdField("endlspid", "FFFF.FFFF.FFFF.FF-FF"),
    827         _ISIS_TlvListField()
    828     ]
    829 
    830 
    831 def _snp_answers(snp, other, clsname):
    832     # TODO
    833     return 0
    834 
    835 
    836 class ISIS_L1_CSNP(_ISIS_CSNP_Base):
    837     name = "ISIS L1 Complete Sequence Number Packet"
    838 
    839     def answers(self, other):
    840         return _snp_answers(self, other, "ISIS_L1_LSP")
    841 
    842 
    843 class ISIS_L2_CSNP(_ISIS_CSNP_Base):
    844     name = "ISIS L2 Complete Sequence Number Packet"
    845 
    846     def answers(self, other):
    847         return _snp_answers(self, other, "ISIS_L2_LSP")
    848 
    849 
    850 class _ISIS_PSNP_Base(_ISIS_PduBase):
    851     fields_desc = [
    852         _ISIS_PduLengthField(),
    853         ISIS_NodeIdField("sourceid", "0102.0304.0506.00"),
    854         _ISIS_TlvListField()
    855     ]
    856 
    857 
    858 class ISIS_L1_PSNP(_ISIS_PSNP_Base):
    859     name = "ISIS L1 Partial Sequence Number Packet"
    860 
    861     def answers(self, other):
    862         return _snp_answers(self, other, "ISIS_L1_LSP")
    863 
    864 
    865 class ISIS_L2_PSNP(_ISIS_PSNP_Base):
    866     name = "ISIS L2 Partial Sequence Number Packet"
    867 
    868     def answers(self, other):
    869         return _snp_answers(self, other, "ISIS_L2_LSP")
    870 
    871 register_cln_protocol(0x83, ISIS_CommonHdr)
    872 bind_layers(ISIS_CommonHdr, ISIS_L1_LAN_Hello, hdrlen=27, pdutype=15)
    873 bind_layers(ISIS_CommonHdr, ISIS_L2_LAN_Hello, hdrlen=27, pdutype=16)
    874 bind_layers(ISIS_CommonHdr, ISIS_P2P_Hello, hdrlen=20, pdutype=17)
    875 bind_layers(ISIS_CommonHdr, ISIS_L1_LSP, hdrlen=27, pdutype=18)
    876 bind_layers(ISIS_CommonHdr, ISIS_L2_LSP, hdrlen=27, pdutype=20)
    877 bind_layers(ISIS_CommonHdr, ISIS_L1_CSNP, hdrlen=33, pdutype=24)
    878 bind_layers(ISIS_CommonHdr, ISIS_L2_CSNP, hdrlen=33, pdutype=25)
    879 bind_layers(ISIS_CommonHdr, ISIS_L1_PSNP, hdrlen=17, pdutype=26)
    880 bind_layers(ISIS_CommonHdr, ISIS_L2_PSNP, hdrlen=17, pdutype=27)
    881 
    882