Skip to content

Commit 8b15bb7

Browse files
authored
CDP improvment (#352)
improve CDP: more fields handled, well formed packets, and tests added
1 parent 4becd0d commit 8b15bb7

File tree

2 files changed

+80
-43
lines changed

2 files changed

+80
-43
lines changed

dpkt/cdp.py

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
class CDP(dpkt.Packet):
3030
"""Cisco Discovery Protocol.
3131
32-
See more about the BGP on \
32+
See more on
3333
https://en.wikipedia.org/wiki/Cisco_Discovery_Protocol
3434
3535
Attributes:
@@ -43,69 +43,106 @@ class CDP(dpkt.Packet):
4343
('sum', 'H', 0)
4444
)
4545

46-
class Address(dpkt.Packet):
47-
# XXX - only handle NLPID/IP for now
48-
__hdr__ = (
49-
('ptype', 'B', 1), # protocol type (NLPID)
50-
('plen', 'B', 1), # protocol length
51-
('p', 'B', 0xcc), # IP
52-
('alen', 'H', 4) # address length
53-
)
54-
55-
def unpack(self, buf):
56-
dpkt.Packet.unpack(self, buf)
57-
self.data = self.data[:self.alen]
58-
5946
class TLV(dpkt.Packet):
47+
"""When constructing the packet, len is not mandatory:
48+
if not provided, then self.data must be this exact TLV payload"""
49+
6050
__hdr__ = (
6151
('type', 'H', 0),
62-
('len', 'H', 4)
52+
('len', 'H', 0)
6353
)
6454

55+
def data_len(self):
56+
if self.len:
57+
return self.len - self.__hdr_len__
58+
return len(self.data)
59+
6560
def unpack(self, buf):
6661
dpkt.Packet.unpack(self, buf)
67-
self.data = self.data[:self.len - 4]
68-
if self.type == CDP_ADDRESS:
69-
n = struct.unpack('>I', self.data[:4])[0]
70-
buf = self.data[4:]
71-
l = []
72-
for i in range(n):
73-
a = CDP.Address(buf)
74-
l.append(a)
75-
buf = buf[len(a):]
76-
self.data = l
62+
self.data = self.data[:self.data_len()]
7763

7864
def __len__(self):
79-
if self.type == CDP_ADDRESS:
80-
n = 4 + sum(map(len, self.data))
81-
else:
82-
n = len(self.data)
83-
return self.__hdr_len__ + n
65+
return self.__hdr_len__ + len(self.data)
8466

8567
def __bytes__(self):
86-
self.len = len(self)
87-
if self.type == CDP_ADDRESS:
88-
s = struct.pack('>I', len(self.data)) + \
89-
b''.join(map(bytes, self.data))
90-
else:
91-
s = self.data
92-
return self.pack_hdr() + s
68+
if hasattr(self, 'len') and not self.len:
69+
self.len = len(self)
70+
return self.pack_hdr() + bytes(self.data)
71+
72+
class Address(TLV):
73+
# XXX - only handle NLPID/IP for now
74+
__hdr__ = (
75+
('ptype', 'B', 1), # protocol type (NLPID)
76+
('plen', 'B', 1), # protocol length
77+
('p', 'B', 0xcc), # IP
78+
('alen', 'H', 4) # address length
79+
)
80+
def data_len(self):
81+
return self.alen
82+
83+
class TLV_Addresses(TLV):
84+
__hdr__ = (
85+
('type', 'H', CDP_ADDRESS),
86+
('len', 'H', 0), #17),
87+
('Addresses', 'L', 1),
88+
)
9389

9490
def unpack(self, buf):
9591
dpkt.Packet.unpack(self, buf)
9692
buf = self.data
9793
l = []
9894
while buf:
99-
tlv = self.TLV(buf)
100-
l.append(tlv)
95+
# find the right TLV according to Type value
96+
tlv_find_type = self.TLV(buf).type
97+
# if this TLV is not in tlv_types, use the default TLV class
98+
tlv = self.tlv_types.get(tlv_find_type, self.TLV)(buf)
99+
l.append(bytes(tlv))
101100
buf = buf[len(tlv):]
102-
self.data = l
101+
self.tlvs = l
102+
self.data = b''.join(l)
103103

104104
def __len__(self):
105-
return self.__hdr_len__ + sum(map(len, self.data))
105+
return self.__hdr_len__ + len(self.data)
106106

107107
def __bytes__(self):
108-
data = b''.join(map(bytes, self.data))
108+
data = bytes(self.data)
109109
if not self.sum:
110110
self.sum = dpkt.in_cksum(self.pack_hdr() + data)
111111
return self.pack_hdr() + data
112+
113+
# keep here the TLV classes whose header is different from the generic TLV header (example : TLV_Addresses)
114+
tlv_types = {CDP_ADDRESS: TLV_Addresses}
115+
116+
117+
def test_cdp():
118+
import socket
119+
from . import ethernet
120+
121+
ss = (b'\x02\xb4\xdf\x93\x00\x01\x00\x09\x63\x69\x73\x63\x6f\x00\x02\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x01\x67')
122+
rr1 = CDP(ss)
123+
assert bytes(rr1) == ss
124+
125+
# construction
126+
ss = (b'\x02\xb4\xdf\x93\x00\x01\x00\x09\x63\x69\x73\x63\x6f\x00\x02\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x01\x67')
127+
p1 = CDP.TLV_Addresses(data=CDP.Address(data=socket.inet_aton('192.168.1.103')))
128+
p2 = CDP.TLV(type=CDP_DEVID, data=b'cisco')
129+
data = p2.pack() + p1.pack()
130+
rr2 = CDP(data=data)
131+
assert bytes(rr2) == ss
132+
133+
s = (b'\x01\x00\x0c\xcc\xcc\xcc\xc4\x022k\x00\x00\x01T\xaa\xaa\x03\x00\x00\x0c \x00\x02\xb4,B'
134+
b'\x00\x01\x00\x06R2\x00\x05\x00\xffCisco IOS Software, 3700 Software (C3745-ADVENTERPRI'
135+
b'SEK9_SNA-M), Version 12.4(25d), RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.'
136+
b'cisco.com/techsupport\nCopyright (c) 1986-2010 by Cisco Systems, Inc.\nCompiled Wed 18'
137+
b'-Aug-10 08:18 by prod_rel_team\x00\x06\x00\x0eCisco 3745\x00\x02\x00\x11\x00\x00\x00\x01'
138+
b'\x01\x01\xcc\x00\x04\n\x00\x00\x02\x00\x03\x00\x13FastEthernet0/0\x00\x04\x00\x08\x00'
139+
b'\x00\x00)\x00\t\x00\x04\x00\x0b\x00\x05\x00')
140+
eth = ethernet.Ethernet(s)
141+
assert isinstance(eth.data.data, CDP)
142+
assert len(eth.data.data.tlvs) == 8 # number of CDP TLVs; ensures they are decoded
143+
assert str(eth) == str(s)
144+
assert len(eth) == len(s)
145+
146+
if __name__ == '__main__':
147+
test_cdp()
148+
print('Tests Successful...')

dpkt/ethernet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ def test_eth_llc_snap_cdp(): # 802.3 Ethernet - LLC/SNAP - CDP
619619
# stack
620620
assert isinstance(eth.data, llc.LLC)
621621
assert isinstance(eth.data.data, cdp.CDP)
622-
assert len(eth.data.data.data) == 8 # number of CDP TLVs; ensures they are decoded
622+
assert len(eth.data.data.tlvs) == 8 # number of CDP TLVs; ensures they are decoded
623623
assert str(eth) == str(s), 'pack 1'
624624
assert str(eth) == str(s), 'pack 2'
625625
assert len(eth) == len(s)

0 commit comments

Comments
 (0)