blob: daeb22097f477e5a1011c6ff76e162d7aea8e856 [file] [log] [blame]
# $Id: cdp.py 23 2006-11-08 15:45:33Z dugsong $
# -*- coding: utf-8 -*-
"""Cisco Discovery Protocol."""
from __future__ import absolute_import
from . import dpkt
CDP_DEVID = 1 # string
CDP_ADDRESS = 2
CDP_PORTID = 3 # string
CDP_CAPABILITIES = 4 # 32-bit bitmask
CDP_VERSION = 5 # string
CDP_PLATFORM = 6 # string
CDP_IPPREFIX = 7
CDP_VTP_MGMT_DOMAIN = 9 # string
CDP_NATIVE_VLAN = 10 # 16-bit integer
CDP_DUPLEX = 11 # 8-bit boolean
CDP_TRUST_BITMAP = 18 # 8-bit bitmask0x13
CDP_UNTRUST_COS = 19 # 8-bit port
CDP_SYSTEM_NAME = 20 # string
CDP_SYSTEM_OID = 21 # 10-byte binary string
CDP_MGMT_ADDRESS = 22 # 32-bit number of addrs, Addresses
CDP_LOCATION = 23 # string
class CDP(dpkt.Packet):
"""Cisco Discovery Protocol.
See more on
https://en.wikipedia.org/wiki/Cisco_Discovery_Protocol
Attributes:
__hdr__: Header fields of CDP.
#TODO
"""
__hdr__ = (
('version', 'B', 2),
('ttl', 'B', 180),
('sum', 'H', 0)
)
class TLV(dpkt.Packet):
"""When constructing the packet, len is not mandatory:
if not provided, then self.data must be this exact TLV payload
"""
__hdr__ = (
('type', 'H', 0),
('len', 'H', 0)
)
def data_len(self):
if self.len:
return self.len - self.__hdr_len__
return len(self.data)
def unpack(self, buf):
dpkt.Packet.unpack(self, buf)
self.data = self.data[:self.data_len()]
def __len__(self):
return self.__hdr_len__ + len(self.data)
def __bytes__(self):
if hasattr(self, 'len') and not self.len:
self.len = len(self)
return self.pack_hdr() + bytes(self.data)
class Address(TLV):
# XXX - only handle NLPID/IP for now
__hdr__ = (
('ptype', 'B', 1), # protocol type (NLPID)
('plen', 'B', 1), # protocol length
('p', 'B', 0xcc), # IP
('alen', 'H', 4) # address length
)
def data_len(self):
return self.alen
class TLV_Addresses(TLV):
__hdr__ = (
('type', 'H', CDP_ADDRESS),
('len', 'H', 0), # 17),
('Addresses', 'L', 1),
)
def unpack(self, buf):
dpkt.Packet.unpack(self, buf)
buf = self.data
l_ = []
while buf:
# find the right TLV according to Type value
tlv_find_type = self.TLV(buf).type
# if this TLV is not in tlv_types, use the default TLV class
tlv = self.tlv_types.get(tlv_find_type, self.TLV)(buf)
l_.append(bytes(tlv))
buf = buf[len(tlv):]
self.tlvs = l_
self.data = b''.join(l_)
def __len__(self):
return self.__hdr_len__ + len(self.data)
def __bytes__(self):
data = bytes(self.data)
if not self.sum:
self.sum = dpkt.in_cksum(self.pack_hdr() + data)
return self.pack_hdr() + data
# keep here the TLV classes whose header is different from the generic TLV header (example : TLV_Addresses)
tlv_types = {CDP_ADDRESS: TLV_Addresses}
def test_cdp():
import socket
from . import ethernet
ss = (b'\x02\xb4\xdf\x93\x00\x01\x00\x09\x63\x69\x73\x63\x6f\x00\x02\x00\x11\x00\x00\x00\x01'
b'\x01\x01\xcc\x00\x04\xc0\xa8\x01\x67')
rr1 = CDP(ss)
assert bytes(rr1) == ss
# construction
ss = (b'\x02\xb4\xdf\x93\x00\x01\x00\x09\x63\x69\x73\x63\x6f\x00\x02\x00\x11\x00\x00\x00\x01'
b'\x01\x01\xcc\x00\x04\xc0\xa8\x01\x67')
p1 = CDP.TLV_Addresses(data=CDP.Address(data=socket.inet_aton('192.168.1.103')))
p2 = CDP.TLV(type=CDP_DEVID, data=b'cisco')
data = p2.pack() + p1.pack()
rr2 = CDP(data=data)
assert bytes(rr2) == ss
s = (b'\x01\x00\x0c\xcc\xcc\xcc\xc4\x022k\x00\x00\x01T\xaa\xaa\x03\x00\x00\x0c \x00\x02\xb4,B'
b'\x00\x01\x00\x06R2\x00\x05\x00\xffCisco IOS Software, 3700 Software (C3745-ADVENTERPRI'
b'SEK9_SNA-M), Version 12.4(25d), RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.'
b'cisco.com/techsupport\nCopyright (c) 1986-2010 by Cisco Systems, Inc.\nCompiled Wed 18'
b'-Aug-10 08:18 by prod_rel_team\x00\x06\x00\x0eCisco 3745\x00\x02\x00\x11\x00\x00\x00\x01'
b'\x01\x01\xcc\x00\x04\n\x00\x00\x02\x00\x03\x00\x13FastEthernet0/0\x00\x04\x00\x08\x00'
b'\x00\x00)\x00\t\x00\x04\x00\x0b\x00\x05\x00')
eth = ethernet.Ethernet(s)
assert isinstance(eth.data.data, CDP)
assert len(eth.data.data.tlvs) == 8 # number of CDP TLVs; ensures they are decoded
assert str(eth) == str(s)
assert len(eth) == len(s)
def test_tlv():
from binascii import unhexlify
# len field set to 0
buf_no_len = unhexlify(
'0000' # type
'0000' # len
'abcd' # data
)
buf_with_len = unhexlify(
'0000' # type
'0006' # len
'abcd' # data
)
tlv = CDP.TLV(buf_no_len)
assert tlv.type == 0
assert tlv.len == 0
assert tlv.data_len() == 2
assert tlv.data == b'\xab\xcd'
assert bytes(tlv) == buf_with_len
# len field set manually
tlv = CDP.TLV(buf_with_len)
assert tlv.type == 0
assert tlv.len == 6
assert tlv.data_len() == 2
assert tlv.data == b'\xab\xcd'
assert bytes(tlv) == buf_with_len
def test_address():
from binascii import unhexlify
buf = unhexlify(
'00' # ptype
'11' # plen
'22' # p
'3333' # alen
)
address = CDP.Address(buf)
assert address.data_len() == 0x3333