205 lines
6.6 KiB
Python
205 lines
6.6 KiB
Python
|
# 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 <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
# scapy.contrib.description = VLAN Trunking Protocol (VTP)
|
||
|
# scapy.contrib.status = loads
|
||
|
|
||
|
r"""
|
||
|
VTP Scapy Extension
|
||
|
~~~~~~~~~~~~~~~~~~~~~
|
||
|
|
||
|
:version: 2009-02-15
|
||
|
:copyright: 2009 by Jochen Bartl
|
||
|
:e-mail: lobo@c3a.de / jochen.bartl@gmail.com
|
||
|
:license: GPL v2
|
||
|
|
||
|
This program 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 program 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.
|
||
|
|
||
|
:TODO:
|
||
|
|
||
|
- Join messages
|
||
|
- RE MD5 hash calculation
|
||
|
- Have a closer look at 8 byte padding in summary adv:
|
||
|
|
||
|
"debug sw-vlan vtp packets" says the TLV length is invalid,
|
||
|
when I change the values:
|
||
|
|
||
|
``b'\x00\x00\x00\x01\x06\x01\x00\x02'``
|
||
|
|
||
|
* \x00\x00 ?
|
||
|
* \x00\x01 tlvtype?
|
||
|
* \x06 length?
|
||
|
* \x00\x02 value?
|
||
|
|
||
|
- h2i function for VTPTimeStampField
|
||
|
|
||
|
:References:
|
||
|
|
||
|
- | Understanding VLAN Trunk Protocol (VTP)
|
||
|
| http://www.cisco.com/en/US/tech/tk389/tk689/technologies_tech_note09186a0080094c52.shtml # noqa: E501
|
||
|
"""
|
||
|
|
||
|
from scapy.packet import Packet, bind_layers
|
||
|
from scapy.fields import ByteEnumField, ByteField, ConditionalField, \
|
||
|
FieldLenField, IPField, PacketListField, ShortField, SignedIntField, \
|
||
|
StrFixedLenField, StrLenField, XIntField
|
||
|
from scapy.layers.l2 import SNAP
|
||
|
from scapy.compat import chb
|
||
|
from scapy.config import conf
|
||
|
|
||
|
_VTP_VLAN_TYPE = {
|
||
|
1: 'Ethernet',
|
||
|
2: 'FDDI',
|
||
|
3: 'TrCRF',
|
||
|
4: 'FDDI-net',
|
||
|
5: 'TrBRF'
|
||
|
}
|
||
|
|
||
|
_VTP_VLANINFO_TLV_TYPE = {
|
||
|
0x01: 'Source-Routing Ring Number',
|
||
|
0x02: 'Source-Routing Bridge Number',
|
||
|
0x03: 'Spanning-Tree Protocol Type',
|
||
|
0x04: 'Parent VLAN',
|
||
|
0x05: 'Translationally Bridged VLANs',
|
||
|
0x06: 'Pruning',
|
||
|
0x07: 'Bridge Type',
|
||
|
0x08: 'Max ARE Hop Count',
|
||
|
0x09: 'Max STE Hop Count',
|
||
|
0x0A: 'Backup CRF Mode'
|
||
|
}
|
||
|
|
||
|
|
||
|
class VTPVlanInfoTlv(Packet):
|
||
|
name = "VTP VLAN Info TLV"
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 0, _VTP_VLANINFO_TLV_TYPE),
|
||
|
ByteField("length", 0),
|
||
|
StrLenField("value", None, length_from=lambda pkt: pkt.length + 1)
|
||
|
]
|
||
|
|
||
|
def guess_payload_class(self, p):
|
||
|
return conf.padding_layer
|
||
|
|
||
|
|
||
|
class VTPVlanInfo(Packet):
|
||
|
name = "VTP VLAN Info"
|
||
|
fields_desc = [
|
||
|
ByteField("len", None),
|
||
|
ByteEnumField("status", 0, {0: "active", 1: "suspended"}),
|
||
|
ByteEnumField("type", 1, _VTP_VLAN_TYPE),
|
||
|
FieldLenField("vlannamelen", None, "vlanname", "B"),
|
||
|
ShortField("vlanid", 1),
|
||
|
ShortField("mtu", 1500),
|
||
|
XIntField("dot10index", None),
|
||
|
StrLenField("vlanname", "default",
|
||
|
length_from=lambda pkt: 4 * ((pkt.vlannamelen + 3) // 4)),
|
||
|
ConditionalField(
|
||
|
PacketListField(
|
||
|
"tlvlist", [], VTPVlanInfoTlv,
|
||
|
length_from=lambda pkt: pkt.len - 12 - (4 * ((pkt.vlannamelen + 3) // 4)) # noqa: E501
|
||
|
),
|
||
|
lambda pkt:pkt.type not in [1, 2]
|
||
|
)
|
||
|
]
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
vlannamelen = 4 * ((len(self.vlanname) + 3) // 4)
|
||
|
|
||
|
if self.len is None:
|
||
|
tmp_len = vlannamelen + 12
|
||
|
p = chb(tmp_len & 0xff) + p[1:]
|
||
|
|
||
|
# Pad vlan name with zeros if vlannamelen > len(vlanname)
|
||
|
tmp_len = vlannamelen - len(self.vlanname)
|
||
|
if tmp_len != 0:
|
||
|
p += b"\x00" * tmp_len
|
||
|
|
||
|
p += pay
|
||
|
|
||
|
return p
|
||
|
|
||
|
def guess_payload_class(self, p):
|
||
|
return conf.padding_layer
|
||
|
|
||
|
|
||
|
_VTP_Types = {
|
||
|
1: 'Summary Advertisement',
|
||
|
2: 'Subset Advertisements',
|
||
|
3: 'Advertisement Request',
|
||
|
4: 'Join'
|
||
|
}
|
||
|
|
||
|
|
||
|
class VTPTimeStampField(StrFixedLenField):
|
||
|
def __init__(self, name, default):
|
||
|
StrFixedLenField.__init__(self, name, default, 12)
|
||
|
|
||
|
def i2repr(self, pkt, x):
|
||
|
return "%s-%s-%s %s:%s:%s" % (x[:2], x[2:4], x[4:6], x[6:8], x[8:10], x[10:12]) # noqa: E501
|
||
|
|
||
|
|
||
|
class VTP(Packet):
|
||
|
name = "VTP"
|
||
|
fields_desc = [
|
||
|
ByteField("ver", 2),
|
||
|
ByteEnumField("code", 1, _VTP_Types),
|
||
|
ConditionalField(ByteField("followers", 1),
|
||
|
lambda pkt:pkt.code == 1),
|
||
|
ConditionalField(ByteField("seq", 1),
|
||
|
lambda pkt:pkt.code == 2),
|
||
|
ConditionalField(ByteField("reserved", 0),
|
||
|
lambda pkt:pkt.code == 3),
|
||
|
ByteField("domnamelen", None),
|
||
|
StrFixedLenField("domname", "manbearpig", 32),
|
||
|
ConditionalField(SignedIntField("rev", 0),
|
||
|
lambda pkt:pkt.code == 1 or
|
||
|
pkt.code == 2),
|
||
|
# updater identity
|
||
|
ConditionalField(IPField("uid", "192.168.0.1"),
|
||
|
lambda pkt:pkt.code == 1),
|
||
|
ConditionalField(VTPTimeStampField("timestamp", '930301000000'),
|
||
|
lambda pkt:pkt.code == 1),
|
||
|
ConditionalField(StrFixedLenField("md5", b"\x00" * 16, 16),
|
||
|
lambda pkt:pkt.code == 1),
|
||
|
ConditionalField(
|
||
|
PacketListField("vlaninfo", [], VTPVlanInfo),
|
||
|
lambda pkt: pkt.code == 2),
|
||
|
ConditionalField(ShortField("startvalue", 0),
|
||
|
lambda pkt:pkt.code == 3)
|
||
|
]
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
if self.domnamelen is None:
|
||
|
domnamelen = len(self.domname.strip(b"\x00"))
|
||
|
p = p[:3] + chb(domnamelen & 0xff) + p[4:]
|
||
|
|
||
|
p += pay
|
||
|
|
||
|
return p
|
||
|
|
||
|
|
||
|
bind_layers(SNAP, VTP, code=0x2003)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
from scapy.main import interact
|
||
|
interact(mydict=globals(), mybanner="VTP")
|