1 // Copyright 2012 Google, Inc. All rights reserved.
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
7 // Enum types courtesy of...
8 // http://search.cpan.org/~mchapman/Net-CDP-0.09/lib/Net/CDP.pm
9 // https://code.google.com/p/ladvd/
10 // http://anonsvn.wireshark.org/viewvc/releases/wireshark-1.8.6/epan/dissectors/packet-cdp.c
19 "github.com/google/gopacket"
22 // CDPTLVType is the type of each TLV value in a CiscoDiscovery packet.
23 type CDPTLVType uint16
27 CDPTLVDevID CDPTLVType = 0x0001
28 CDPTLVAddress CDPTLVType = 0x0002
29 CDPTLVPortID CDPTLVType = 0x0003
30 CDPTLVCapabilities CDPTLVType = 0x0004
31 CDPTLVVersion CDPTLVType = 0x0005
32 CDPTLVPlatform CDPTLVType = 0x0006
33 CDPTLVIPPrefix CDPTLVType = 0x0007
34 CDPTLVHello CDPTLVType = 0x0008
35 CDPTLVVTPDomain CDPTLVType = 0x0009
36 CDPTLVNativeVLAN CDPTLVType = 0x000a
37 CDPTLVFullDuplex CDPTLVType = 0x000b
38 CDPTLVVLANReply CDPTLVType = 0x000e
39 CDPTLVVLANQuery CDPTLVType = 0x000f
40 CDPTLVPower CDPTLVType = 0x0010
41 CDPTLVMTU CDPTLVType = 0x0011
42 CDPTLVExtendedTrust CDPTLVType = 0x0012
43 CDPTLVUntrustedCOS CDPTLVType = 0x0013
44 CDPTLVSysName CDPTLVType = 0x0014
45 CDPTLVSysOID CDPTLVType = 0x0015
46 CDPTLVMgmtAddresses CDPTLVType = 0x0016
47 CDPTLVLocation CDPTLVType = 0x0017
48 CDPTLVExternalPortID CDPTLVType = 0x0018
49 CDPTLVPowerRequested CDPTLVType = 0x0019
50 CDPTLVPowerAvailable CDPTLVType = 0x001a
51 CDPTLVPortUnidirectional CDPTLVType = 0x001b
52 CDPTLVEnergyWise CDPTLVType = 0x001d
53 CDPTLVSparePairPOE CDPTLVType = 0x001f
56 // CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer.
57 type CiscoDiscoveryValue struct {
63 // CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol.
64 // See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885
65 type CiscoDiscovery struct {
70 Values []CiscoDiscoveryValue
73 // CDPCapability is the set of capabilities advertised by a CDP device.
74 type CDPCapability uint32
76 // CDPCapability values.
78 CDPCapMaskRouter CDPCapability = 0x0001
79 CDPCapMaskTBBridge CDPCapability = 0x0002
80 CDPCapMaskSPBridge CDPCapability = 0x0004
81 CDPCapMaskSwitch CDPCapability = 0x0008
82 CDPCapMaskHost CDPCapability = 0x0010
83 CDPCapMaskIGMPFilter CDPCapability = 0x0020
84 CDPCapMaskRepeater CDPCapability = 0x0040
85 CDPCapMaskPhone CDPCapability = 0x0080
86 CDPCapMaskRemote CDPCapability = 0x0100
89 // CDPCapabilities represents the capabilities of a device
90 type CDPCapabilities struct {
102 // CDP Power-over-Ethernet values.
104 CDPPoEFourWire byte = 0x01
105 CDPPoEPDArch byte = 0x02
106 CDPPoEPDRequest byte = 0x04
107 CDPPoEPSE byte = 0x08
110 // CDPSparePairPoE provides information on PoE.
111 type CDPSparePairPoE struct {
112 PSEFourWire bool // Supported / Not supported
113 PDArchShared bool // Shared / Independent
114 PDRequestOn bool // On / Off
115 PSEOn bool // On / Off
118 // CDPVLANDialogue encapsulates a VLAN Query/Reply
119 type CDPVLANDialogue struct {
124 // CDPPowerDialogue encapsulates a Power Query/Reply
125 type CDPPowerDialogue struct {
131 // CDPLocation provides location information for a CDP device.
132 type CDPLocation struct {
133 Type uint8 // Undocumented
137 // CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields)
138 type CDPHello struct {
147 ClusterCommander net.HardwareAddr
148 SwitchMAC net.HardwareAddr
150 ManagementVLAN uint16
153 // CDPEnergyWiseSubtype is used within CDP to define TLV values.
154 type CDPEnergyWiseSubtype uint32
156 // CDPEnergyWiseSubtype values.
158 CDPEnergyWiseRole CDPEnergyWiseSubtype = 0x00000007
159 CDPEnergyWiseDomain CDPEnergyWiseSubtype = 0x00000008
160 CDPEnergyWiseName CDPEnergyWiseSubtype = 0x00000009
161 CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017
164 // CDPEnergyWise is used by CDP to monitor and control power usage.
165 type CDPEnergyWise struct {
168 SequenceNumber uint32
184 // CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues
185 type CiscoDiscoveryInfo struct {
191 Capabilities CDPCapabilities
194 IPPrefixes []net.IPNet
198 VLANReply CDPVLANDialogue
199 VLANQuery CDPVLANDialogue
200 PowerConsumption uint16
206 MgmtAddresses []net.IP
208 PowerRequest CDPPowerDialogue
209 PowerAvailable CDPPowerDialogue
210 SparePairPoe CDPSparePairPoE
211 EnergyWise CDPEnergyWise
212 Unknown []CiscoDiscoveryValue
215 // LayerType returns gopacket.LayerTypeCiscoDiscovery.
216 func (c *CiscoDiscovery) LayerType() gopacket.LayerType {
217 return LayerTypeCiscoDiscovery
220 func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
221 c := &CiscoDiscovery{
224 Checksum: binary.BigEndian.Uint16(data[2:4]),
226 if c.Version != 1 && c.Version != 2 {
227 return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
230 c.Values, err = decodeCiscoDiscoveryTLVs(data[4:])
234 c.Contents = data[0:4]
237 return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo))
240 // LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo.
241 func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
242 return LayerTypeCiscoDiscoveryInfo
245 func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err error) {
247 val := CiscoDiscoveryValue{
248 Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])),
249 Length: binary.BigEndian.Uint16(data[2:4]),
252 err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length)
255 val.Value = data[4:val.Length]
256 values = append(values, val)
257 data = data[val.Length:]
262 func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
264 info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
266 values, err := decodeCiscoDiscoveryTLVs(data)
267 if err != nil { // Unlikely, as parent decode will fail, but better safe...
270 for _, val := range values {
273 info.DeviceID = string(val.Value)
275 if err = checkCDPTLVLen(val, 4); err != nil {
278 info.Addresses, err = decodeAddresses(val.Value)
283 info.PortID = string(val.Value)
284 case CDPTLVCapabilities:
285 if err = checkCDPTLVLen(val, 4); err != nil {
288 val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4]))
289 info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0)
290 info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0)
291 info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0)
292 info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0)
293 info.Capabilities.IsHost = (val&CDPCapMaskHost > 0)
294 info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0)
295 info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0)
296 info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0)
297 info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0)
299 info.Version = string(val.Value)
301 info.Platform = string(val.Value)
305 if l%5 == 0 && l >= 5 {
307 _, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4]))
308 info.IPPrefixes = append(info.IPPrefixes, *ipnet)
312 return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value))
315 if err = checkCDPTLVLen(val, 32); err != nil {
319 info.CDPHello.OUI = v[0:3]
320 info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5])
321 info.CDPHello.ClusterMaster = v[5:9]
322 info.CDPHello.Unknown1 = v[9:13]
323 info.CDPHello.Version = v[13]
324 info.CDPHello.SubVersion = v[14]
325 info.CDPHello.Status = v[15]
326 info.CDPHello.Unknown2 = v[16]
327 info.CDPHello.ClusterCommander = v[17:23]
328 info.CDPHello.SwitchMAC = v[23:29]
329 info.CDPHello.Unknown3 = v[29]
330 info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32])
331 case CDPTLVVTPDomain:
332 info.VTPDomain = string(val.Value)
333 case CDPTLVNativeVLAN:
334 if err = checkCDPTLVLen(val, 2); err != nil {
337 info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2])
338 case CDPTLVFullDuplex:
339 if err = checkCDPTLVLen(val, 1); err != nil {
342 info.FullDuplex = (val.Value[0] == 1)
343 case CDPTLVVLANReply:
344 if err = checkCDPTLVLen(val, 3); err != nil {
347 info.VLANReply.ID = uint8(val.Value[0])
348 info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
349 case CDPTLVVLANQuery:
350 if err = checkCDPTLVLen(val, 3); err != nil {
353 info.VLANQuery.ID = uint8(val.Value[0])
354 info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
356 if err = checkCDPTLVLen(val, 2); err != nil {
359 info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2])
361 if err = checkCDPTLVLen(val, 4); err != nil {
364 info.MTU = binary.BigEndian.Uint32(val.Value[0:4])
365 case CDPTLVExtendedTrust:
366 if err = checkCDPTLVLen(val, 1); err != nil {
369 info.ExtendedTrust = uint8(val.Value[0])
370 case CDPTLVUntrustedCOS:
371 if err = checkCDPTLVLen(val, 1); err != nil {
374 info.UntrustedCOS = uint8(val.Value[0])
376 info.SysName = string(val.Value)
378 info.SysOID = string(val.Value)
379 case CDPTLVMgmtAddresses:
380 if err = checkCDPTLVLen(val, 4); err != nil {
383 info.MgmtAddresses, err = decodeAddresses(val.Value)
388 if err = checkCDPTLVLen(val, 2); err != nil {
391 info.Location.Type = uint8(val.Value[0])
392 info.Location.Location = string(val.Value[1:])
394 // case CDPTLVLExternalPortID:
396 case CDPTLVPowerRequested:
397 if err = checkCDPTLVLen(val, 4); err != nil {
400 info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2])
401 info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
402 for n := 4; n < len(val.Value); n += 4 {
403 info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
405 case CDPTLVPowerAvailable:
406 if err = checkCDPTLVLen(val, 4); err != nil {
409 info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2])
410 info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
411 for n := 4; n < len(val.Value); n += 4 {
412 info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
414 // case CDPTLVPortUnidirectional
416 case CDPTLVEnergyWise:
417 if err = checkCDPTLVLen(val, 72); err != nil {
420 info.EnergyWise.EncryptedData = val.Value[0:20]
421 info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24])
422 info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28])
423 info.EnergyWise.ModelNumber = string(val.Value[28:44])
424 info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46])
425 info.EnergyWise.HardwareID = string(val.Value[46:49])
426 info.EnergyWise.SerialNum = string(val.Value[49:60])
427 info.EnergyWise.Unknown3 = val.Value[60:68]
428 tlvLen := binary.BigEndian.Uint16(val.Value[68:70])
429 tlvNum := binary.BigEndian.Uint16(val.Value[70:72])
430 data := val.Value[72:]
431 if len(data) < int(tlvLen) {
432 return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data))
437 if numSeen > int(tlvNum) { // Too many TLV's ?
438 return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen)
440 tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4]))
441 tLen := int(binary.BigEndian.Uint32(data[4:8]))
442 if tLen > len(data)-8 {
443 return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8)
447 case CDPEnergyWiseRole:
448 info.EnergyWise.Role = string(data[:])
449 case CDPEnergyWiseDomain:
450 info.EnergyWise.Domain = string(data[:])
451 case CDPEnergyWiseName:
452 info.EnergyWise.Name = string(data[:])
453 case CDPEnergyWiseReplyTo:
455 info.EnergyWise.ReplyUnknown1 = data[0:2]
456 info.EnergyWise.ReplyPort = data[2:4]
457 info.EnergyWise.ReplyAddress = data[4:8]
458 info.EnergyWise.ReplyUnknown2 = data[8:10]
459 info.EnergyWise.ReplyUnknown3 = data[10:14]
464 case CDPTLVSparePairPOE:
465 if err = checkCDPTLVLen(val, 1); err != nil {
469 info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0)
470 info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0)
471 info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0)
472 info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0)
474 info.Unknown = append(info.Unknown, val)
480 // CDP Protocol Types
482 CDPProtocolTypeNLPID byte = 1
483 CDPProtocolType802_2 byte = 2
486 // CDPAddressType is used to define TLV values within CDP addresses.
487 type CDPAddressType uint64
489 // CDP Address types.
491 CDPAddressTypeCLNP CDPAddressType = 0x81
492 CDPAddressTypeIPV4 CDPAddressType = 0xcc
493 CDPAddressTypeIPV6 CDPAddressType = 0xaaaa030000000800
494 CDPAddressTypeDECNET CDPAddressType = 0xaaaa030000006003
495 CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b
496 CDPAddressTypeIPX CDPAddressType = 0xaaaa030000008137
497 CDPAddressTypeVINES CDPAddressType = 0xaaaa0300000080c4
498 CDPAddressTypeXNS CDPAddressType = 0xaaaa030000000600
499 CDPAddressTypeAPOLLO CDPAddressType = 0xaaaa030000008019
502 func decodeAddresses(v []byte) (addresses []net.IP, err error) {
503 numaddr := int(binary.BigEndian.Uint32(v[0:4]))
505 return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr)
508 if len(v) < numaddr*8 {
509 return nil, fmt.Errorf("Invalid Address TLV length %d", len(v))
511 for i := 0; i < numaddr; i++ {
513 if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type
514 return nil, fmt.Errorf("Invalid Address Protocol %d", prottype)
517 if (prottype == CDPProtocolTypeNLPID && protlen != 1) ||
518 (prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length
519 return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen)
521 plen := make([]byte, 8)
522 copy(plen[8-protlen:], v[2:2+protlen])
523 protocol := CDPAddressType(binary.BigEndian.Uint64(plen))
525 addrlen := binary.BigEndian.Uint16(v[0:2])
526 ab := v[2 : 2+addrlen]
527 if protocol == CDPAddressTypeIPV4 && addrlen == 4 {
528 addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3]))
529 } else if protocol == CDPAddressTypeIPV6 && addrlen == 16 {
530 addresses = append(addresses, net.IP(ab))
532 // only handle IPV4 & IPV6 for now
542 func (t CDPTLVType) String() (s string) {
550 case CDPTLVCapabilities:
553 s = "Software Version"
560 case CDPTLVVTPDomain:
561 s = "VTP Management Domain"
562 case CDPTLVNativeVLAN:
564 case CDPTLVFullDuplex:
566 case CDPTLVVLANReply:
567 s = "VoIP VLAN Reply"
568 case CDPTLVVLANQuery:
571 s = "Power consumption"
574 case CDPTLVExtendedTrust:
575 s = "Extended Trust Bitmap"
576 case CDPTLVUntrustedCOS:
577 s = "Untrusted Port CoS"
582 case CDPTLVMgmtAddresses:
583 s = "Management Addresses"
586 case CDPTLVExternalPortID:
587 s = "External Port ID"
588 case CDPTLVPowerRequested:
589 s = "Power Requested"
590 case CDPTLVPowerAvailable:
591 s = "Power Available"
592 case CDPTLVPortUnidirectional:
593 s = "Port Unidirectional"
594 case CDPTLVEnergyWise:
596 case CDPTLVSparePairPOE:
604 func (a CDPAddressType) String() (s string) {
606 case CDPAddressTypeCLNP:
607 s = "Connectionless Network Protocol"
608 case CDPAddressTypeIPV4:
610 case CDPAddressTypeIPV6:
612 case CDPAddressTypeDECNET:
613 s = "DECnet Phase IV"
614 case CDPAddressTypeAPPLETALK:
616 case CDPAddressTypeIPX:
618 case CDPAddressTypeVINES:
620 case CDPAddressTypeXNS:
621 s = "Xerox Network Systems"
622 case CDPAddressTypeAPOLLO:
630 func (t CDPEnergyWiseSubtype) String() (s string) {
632 case CDPEnergyWiseRole:
634 case CDPEnergyWiseDomain:
636 case CDPEnergyWiseName:
638 case CDPEnergyWiseReplyTo:
646 func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) {
647 if len(v.Value) < l {
648 err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value))