Ticket #18: scapy-cdp.py

File scapy-cdp.py, 9.1 kB (added by nico, 2 years ago)
Line 
1 #! /usr/bin/env python
2
3 #############################################################################
4 ##                                                                         ##
5 ## cdp.py --- Cisco Discovery Protocol (CDP) extension for Scapy           ##
6 ##                                                                         ##
7 ## Copyright (C) 2006    Nicolas Bareil  <nicolas.bareil AT eads DOT net>  ##
8 ##                       Arnaud Ebalard  <arnaud.ebalard AT eads DOT net>  ##
9 ##                       EADS/CRC security team                            ##
10 ##                                                                         ##
11 ## This program is free software; you can redistribute it and/or modify it ##
12 ## under the terms of the GNU General Public License version 2 as          ##
13 ## published by the Free Software Foundation; version 2.                   ##
14 ##                                                                         ##
15 ## This program is distributed in the hope that it will be useful, but     ##
16 ## WITHOUT ANY WARRANTY; without even the implied warranty of              ##
17 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       ##
18 ## General Public License for more details.                                ##
19 ##                                                                         ##
20 #############################################################################
21
22 from scapy import *
23 import __builtin__
24
25 #####################################################################
26 # Helpers and constants
27 #####################################################################
28
29 # An helper
30 def get_cls(name, fallback_cls):
31     return __builtin__.__dict__.get(name, fallback_cls)
32
33
34 # CDP TLV classes keyed by type
35 _cdp_tlv_cls = { 0x0001: "CDPMsgDeviceID",
36                  0x0002: "CDPMsgAddr",
37                  0x0003: "CDPMsgPortID",
38                  0x0004: "CDPMsgCapabilities",
39                  0x0005: "CDPMsgSoftwareVersion",
40                  0x0006: "CDPMsgPlatform",
41                  0x0007: "CDPMsgIPPrefix",
42                  0x0008: "CDPMsgProtoHello",
43                  0x0009: "CDPMsgVTPMgmtDomain", # CDPv2
44                  0x000a: "CDPMsgNativeVLAN",    # CDPv2
45                  0x000b: "CDPMsgDuplex",        #
46                  0x000c: "CDPMsgGeneric",
47                  0x000d: "CDPMsgGeneric",
48                  0x000e: "CDPMsgVoIPVLANReply",
49                  0x000f: "CDPMsgVoIPVLANQuery",
50                  0x0010: "CDPMsgPower",         
51                  0x0011: "CDPMsgMTU",           
52                  0x0012: "CDPMsgTrustBitmap",   
53                  0x0013: "CDPMsgUntrustedPortCoS",     
54                  0x0014: "CDPMsgSystemName",   
55                  0x0015: "CDPMsgSystemOID",     
56                  0x0016: "CDPMsgMgmtAddr",         
57                  0x0017: "CDPMsgLocation",
58                  0x0019: "CDPMsgUnknown19" }
59
60 _cdp_tlv_types = { 0x0001: "Device ID",
61                    0x0002: "Addresses",
62                    0x0003: "Port ID",
63                    0x0004: "Capabilities",
64                    0x0005: "Software Version",
65                    0x0006: "Platform",
66                    0x0007: "IP Prefix",
67                    0x0008: "Protocol Hello",
68                    0x0009: "VTP Mangement Domain", # CDPv2
69                    0x000a: "Native VLAN",    # CDPv2
70                    0x000b: "Duplex",        #
71                    0x000c: "CDP Unknown command (send us a pcap file)",
72                    0x000d: "CDP Unknown command (send us a pcap file)",
73                    0x000e: "VoIP VLAN Reply",   
74                    0x000f: "VoIP VLAN Query",   
75                    0x0010: "Power",             
76                    0x0011: "MTU",               
77                    0x0012: "Trust Bitmap",     
78                    0x0013: "Untrusted Port CoS",       
79                    0x0014: "System Name",       
80                    0x0015: "System OID",       
81                    0x0016: "Management Address",         
82                    0x0017: "Location",
83                    0x0018: "CDP Unknown command (send us a pcap file)",
84                    0x0019: "CDP Unknown command (send us a pcap file)"}
85
86 def _CDPGuessPayloadClass(p, **kargs):
87         cls = Raw
88         if len(p) >= 2:
89             t = struct.unpack("!H", p[:2])[0]
90             cls = get_cls(_cdp_tlv_cls.get(t, "Raw"), Raw)
91         return cls(p, **kargs)
92
93 class CDPMsgGeneric(Packet):
94     name = "CDP Generic Message"
95     fields_desc = [ XShortEnumField("type", None, _cdp_tlv_types),
96                     FieldLenField("len", None, "val", "!H"),
97                     StrLenField("val", "", "len", shift=4) ]
98
99     def guess_payload_class(self, p):
100         return Padding # _CDPGuessPayloadClass
101
102 class CDPMsgDeviceID(CDPMsgGeneric):
103     name = "Device ID"
104     __metaclass__ = NewDefaultValues
105     type = 0x0001
106
107
108 class CDPAddrRecord(Packet):
109     name = "CDP Addresses"
110     fields_desc = [ ByteEnumField("ptype", 0x01, {0x01: "NLPID",
111                                                    0x02: "802.2" }),
112                     XByteField("plen", 1),
113                     # Following field should have a variable length
114                     # based on 'plen' field. To be implemented (later)
115                     ByteEnumField("proto", 0xCC, {0xCC: "IP"}),
116                     ShortField("addrlen", 4),
117                     IPField("ipaddr", "0.0.0.0") ]
118
119     def guess_payload_class(self, p):
120         return Padding
121
122
123 class CDPMsgAddr(CDPMsgGeneric):
124     name = "Addresses"
125     fields_desc = [XShortEnumField("type", 0x0002, _cdp_tlv_types),
126                    ShortField("len", None),
127                    FieldLenField("naddr", None, "addr", "!I"),
128                    PacketListField("addr", [], CDPAddrRecord, "naddr") ]
129
130     def post_build(self, pkt, pay):
131         if self.len is None:
132             l = 8 + len(self.addr) * 9
133             pkt = pkt[:2] + struct.pack("!H", l) + pkt[4:]
134         p = pkt + pay
135         return p
136
137
138 class CDPMsgPortID(CDPMsgGeneric):
139     name = "Port ID"
140     fields_desc = [ XShortEnumField("type", 0x0003, _cdp_tlv_types),
141                     FieldLenField("len", None, "iface", "!H"),
142                     StrLenField("iface", "Port 1", "len", shift=4) ]
143
144
145 _cdp_capabilities = ["Router",
146                      "TransparentBridge",
147                      "SourceRouteBridge",
148                      "Switch",
149                      "Host",
150                      "IGMPCapable",
151                      "Repeater"] + map(lambda x: "Bit%d" % x, range(25,0,-1))
152
153
154 class CDPMsgCapabilities(CDPMsgGeneric):
155     name = "Capabilities"
156     fields_desc = [ XShortEnumField("type", 0x0004, _cdp_tlv_types),
157                     ShortField("len", 8),
158                     FlagsField("cap", 0, 32,  _cdp_capabilities) ]
159
160
161 class CDPMsgSoftwareVersion(CDPMsgGeneric):
162     name = "Software Version"
163     __metaclass__ = NewDefaultValues
164     type = 0x0005
165
166
167 class CDPMsgPlatform(CDPMsgGeneric):
168     name = "Platform"
169     __metaclass__ = NewDefaultValues
170     type = 0x0006
171
172 _cdp_duplex = { 0x00: "Half",
173                 0x01: "Full" }
174
175 # TODO : Do me !!!!!! 0x0007
176 class CDPMsgIPPrefix(CDPMsgGeneric):
177     name = "IP Prefix"
178
179
180 # TODO : Do me !!!!!! 0x0008
181 class CDPMsgProtoHello(CDPMsgGeneric):
182     name = "Protocol Hello"
183
184 class CDPMsgVTPMgmtDomain(CDPMsgGeneric):
185     name = "VTP Management Domain"
186     __metaclass__ = NewDefaultValues
187     type = 0x0009
188
189 # TODO : Do me !!!!!! 0
190 class CDPMsgNativeVLAN(CDPMsgGeneric):
191     name = "VTP Native VLAN"
192
193 class CDPMsgDuplex(CDPMsgGeneric):
194     name = "Duplex"
195     fields_desc = [XShortEnumField("type", 0x000b, _cdp_tlv_types),
196                    ShortField("len", 5),
197                    ByteEnumField("duplex", 0x00, _cdp_duplex) ]
198
199 class CDPMsgVoIPVLANReply(CDPMsgGeneric):
200     name = "VoIP VLAN Reply"
201     fields_desc = [ XShortEnumField("type", 0x000e, _cdp_tlv_types),
202                     ShortField("len", 7),
203                     ByteField("status?", 1),
204                     ShortField("vlan", 1)]
205
206
207 # TODO : Do me !!! 0x000F
208 class CDPMsgVoIPVLANQuery(CDPMsgGeneric):
209     name = "VoIP VLAN Query"
210
211 #    fields_desc = [XShortEnumField("type", 0x000f, _cdp_tlv_types),
212 #                  FieldLenField("len", None, "val", "!H") ]
213     
214
215 class _CDPPowerField(ShortField):
216     def i2repr(self, pkt, x):
217         if x is None:
218             x = 0
219         return "%d mW" % x
220
221
222 class CDPMsgPower(CDPMsgGeneric):
223     name = "Power"
224     # Check if field length is fixed (2 bytes)
225     fields_desc = [XShortEnumField("type", 0x0010, _cdp_tlv_types),
226                    ShortField("len", 6),
227                    _CDPPowerField("power", 1337)]
228
229    
230 class CDPMsgMTU(CDPMsgGeneric):
231     name = "MTU"
232     # Check if field length is fixed (2 bytes)
233     fields_desc = [XShortEnumField("type", 0x0011, _cdp_tlv_types),
234                    ShortField("len", 6),
235                    ShortField("mtu", 1500)]
236
237
238 class CDPMsgUnknown19(CDPMsgGeneric):
239     name = "Unknown CDP Message"
240     __metaclass__ = NewDefaultValues
241     type = 0x0019
242
243
244
245
246 class CDPMsg(CDPMsgGeneric):
247     name = "CDP "
248     fields_desc = [XShortEnumField("type", None, _cdp_tlv_types),
249                    FieldLenField("len", None, "val", "!H") ]
250    
251
252 class _CDPChecksum:
253     def post_build(self, pkt, pay):
254         p = pkt + pay
255         if self.cksum is None:
256             cksum = checksum(p)
257             p = p[:2] + struct.pack("!H", cksum) + p[4:]
258         return p
259
260
261 class AllPacketListField(PacketListField):
262
263     def __init__(self, name, default, cls):
264         PacketField.__init__(self, name, default, cls)
265
266     def getfield(self, pkt, s):
267         lst = []
268         remain = s
269         while len(remain)>0:
270             p = self.m2i(pkt,remain)
271             if Padding in p:
272                 pad = p[Padding]
273                 remain = pad.load
274                 del(pad.underlayer.payload)
275             else:
276                 remain = ""
277             lst.append(p)
278         return remain,lst
279     def addfield(self, pkt, s, val):
280         return s+reduce(str.__add__, map(str, val),"")
281
282 class CDPv2_HDR(_CDPChecksum, CDPMsgGeneric):
283     name = "Cisco Discovery Protocol version 2"
284     fields_desc = [ByteField("vers", 2),
285                    ByteField("ttl", 180),
286                    XShortField("cksum", None),
287                    AllPacketListField("msg", [], _CDPGuessPayloadClass) ]
288
289 bind_layers(SNAP, CDPv2_HDR, { "code": 0x2000, "OUI": 0xC })
290    
291 if __name__ == "__main__":
292     interact(mydict=globals(),mybanner="Welcome to Cisco Discovery Protocol add-on")
293
294
295
296
297
298 It's  still incomplete,  we are  waiting for  the Cisco  (and  scapy :p)
299 documentation... or not :)))
300
301 --
302 Nicolas Bareil                                  http://chdir.org/~nico/
303 OpenPGP=0xAE4F7057 Fingerprint=34DB22091049FB2F33E6B71580F314DAAE4F7057
304
305