# $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