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 = VLAN Trunking Protocol (VTP)
     16 # scapy.contrib.status = loads
     17 
     18 """
     19     VTP Scapy Extension
     20     ~~~~~~~~~~~~~~~~~~~~~
     21 
     22     :version:   2009-02-15
     23     :copyright: 2009 by Jochen Bartl
     24     :e-mail:    lobo (at] c3a.de / jochen.bartl (at] gmail.com
     25     :license:   GPL v2
     26 
     27         This program 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 program 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     :TODO
     38 
     39         - Join messages
     40         - RE MD5 hash calculation
     41         - Have a closer look at 8 byte padding in summary adv.
     42             "debug sw-vlan vtp packets" sais the TLV length is invalid,
     43             when I change the values
     44             b'\x00\x00\x00\x01\x06\x01\x00\x02'
     45                 * \x00\x00 ?
     46                 * \x00\x01 tlvtype?
     47                 * \x06 length?
     48                 * \x00\x02 value?
     49         - h2i function for VTPTimeStampField
     50 
     51     :References:
     52 
     53         - Understanding VLAN Trunk Protocol (VTP)
     54         http://www.cisco.com/en/US/tech/tk389/tk689/technologies_tech_note09186a0080094c52.shtml
     55 """
     56 
     57 from scapy.packet import *
     58 from scapy.fields import *
     59 from scapy.layers.l2 import *
     60 
     61 _VTP_VLAN_TYPE = {
     62             1 : 'Ethernet',
     63             2 : 'FDDI',
     64             3 : 'TrCRF',
     65             4 : 'FDDI-net',
     66             5 : 'TrBRF'
     67         }
     68 
     69 _VTP_VLANINFO_TLV_TYPE = {
     70             0x01 : 'Source-Routing Ring Number',
     71             0x02 : 'Source-Routing Bridge Number',
     72             0x03 : 'Spanning-Tree Protocol Type',
     73             0x04 : 'Parent VLAN',
     74             0x05 : 'Translationally Bridged VLANs',
     75             0x06 : 'Pruning',
     76             0x07 : 'Bridge Type',
     77             0x08 : 'Max ARE Hop Count',
     78             0x09 : 'Max STE Hop Count',
     79             0x0A : 'Backup CRF Mode'
     80         }
     81 
     82 
     83 class VTPVlanInfoTlv(Packet):
     84     name = "VTP VLAN Info TLV"
     85     fields_desc = [
     86             ByteEnumField("type", 0, _VTP_VLANINFO_TLV_TYPE),
     87             ByteField("length", 0),
     88             StrLenField("value", None, length_from=lambda pkt : pkt.length + 1)
     89             ]
     90 
     91     def guess_payload_class(self, p):
     92         return conf.padding_layer
     93 
     94 class VTPVlanInfo(Packet):
     95     name = "VTP VLAN Info"
     96     fields_desc = [
     97                     ByteField("len", None), # FIXME: compute length
     98                     ByteEnumField("status", 0, {0 : "active", 1 : "suspended"}),
     99                     ByteEnumField("type", 1, _VTP_VLAN_TYPE),
    100                     FieldLenField("vlannamelen", None, "vlanname", "B"),
    101                     ShortField("vlanid", 1),
    102                     ShortField("mtu", 1500),
    103                     XIntField("dot10index", None),
    104                     StrLenField("vlanname", "default", length_from=lambda pkt:4 * ((pkt.vlannamelen + 3) / 4)),
    105                     ConditionalField(PacketListField("tlvlist", [], VTPVlanInfoTlv,
    106                             length_from=lambda pkt:pkt.len - 12 - (4 * ((pkt.vlannamelen + 3) / 4))),
    107                             lambda pkt:pkt.type not in [1, 2])
    108             ]
    109 
    110     def post_build(self, p, pay):
    111         vlannamelen = 4 * ((len(self.vlanname) + 3) / 4)
    112 
    113         if self.len == None:
    114             l = vlannamelen + 12
    115             p = chr(l & 0xff) + p[1:]
    116 
    117         # Pad vlan name with zeros if vlannamelen > len(vlanname)
    118         l = vlannamelen - len(self.vlanname)
    119         if l != 0:
    120             p += b"\x00" * l
    121 
    122         p += pay
    123 
    124         return p
    125 
    126     def guess_payload_class(self, p):
    127         return conf.padding_layer
    128 
    129 _VTP_Types = {
    130             1 : 'Summary Advertisement',
    131             2 : 'Subset Advertisements',
    132             3 : 'Advertisement Request',
    133             4 : 'Join'
    134             }
    135 
    136 class VTPTimeStampField(StrFixedLenField):
    137     def __init__(self, name, default):
    138         StrFixedLenField.__init__(self, name, default, 12)
    139 
    140     def i2repr(self, pkt, x):
    141         return "%s-%s-%s %s:%s:%s" % (x[:2], x[2:4], x[4:6], x[6:8], x[8:10], x[10:12])
    142 
    143 class VTP(Packet):
    144     name = "VTP"
    145     fields_desc = [
    146                     ByteField("ver", 2),
    147                     ByteEnumField("code", 1, _VTP_Types),
    148                     ConditionalField(ByteField("followers", 1),
    149                                         lambda pkt:pkt.code == 1),
    150                     ConditionalField(ByteField("seq", 1),
    151                                         lambda pkt:pkt.code == 2),
    152                     ConditionalField(ByteField("reserved", 0),
    153                                         lambda pkt:pkt.code == 3),
    154                     ByteField("domnamelen", None),
    155                     StrFixedLenField("domname", "manbearpig", 32),
    156                     ConditionalField(SignedIntField("rev", 0),
    157                                         lambda pkt:pkt.code == 1 or
    158                                                    pkt.code == 2),
    159                     # updater identity
    160                     ConditionalField(IPField("uid", "192.168.0.1"),
    161                                         lambda pkt:pkt.code == 1),
    162                     ConditionalField(VTPTimeStampField("timestamp", '930301000000'),
    163                                         lambda pkt:pkt.code == 1),
    164                     ConditionalField(StrFixedLenField("md5", b"\x00" * 16, 16),
    165                                         lambda pkt:pkt.code == 1),
    166                     ConditionalField(
    167                         PacketListField("vlaninfo", [], VTPVlanInfo),
    168                         lambda pkt: pkt.code == 2),
    169                     ConditionalField(ShortField("startvalue", 0),
    170                                         lambda pkt:pkt.code == 3)
    171                     ]
    172 
    173     def post_build(self, p, pay):
    174         if self.domnamelen == None:
    175             domnamelen = len(self.domname.strip(b"\x00"))
    176             p = p[:3] + chr(domnamelen & 0xff) + p[4:]
    177 
    178         p += pay
    179 
    180         return p
    181 
    182 bind_layers(SNAP, VTP, code=0x2003)
    183 
    184 if __name__ == '__main__':
    185     from scapy.main import interact
    186     interact(mydict=globals(), mybanner="VTP")
    187