1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
# $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.
Cisco Discovery Protocol (CDP) is a proprietary Data Link Layer protocol developed by Cisco Systems in 1994
by Keith McCloghrie and Dino Farinacci. It is used to share information about other directly connected
Cisco equipment, such as the operating system version and IP address.
See more on
https://en.wikipedia.org/wiki/Cisco_Discovery_Protocol
Attributes:
__hdr__: Header fields of CDP.
version: (int): CDP protocol version. (1 byte)
ttl: (int): Time to live. The amount of time in seconds that a receiver should retain the information
contained in this packet. (1 byte)
sum: (int): Checksum. (2 bytes)
"""
__hdr__ = (
('version', 'B', 2),
('ttl', 'B', 180),
('sum', 'H', 0)
)
class TLV(dpkt.Packet):
"""Type–length–value
When constructing the packet, len is not mandatory:
if not provided, then self.data must be this exact TLV payload
Attributes:
__hdr__: Header fields of TLV.
type: (int): Type (2 bytes)
len: (int): The total length in bytes of the Type, Length and Data fields. (2 bytes)
"""
__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
|