github.com/gopacket/gopacket@v1.1.0/layers/cdp.go (about)

     1  // Copyright 2012 Google, Inc. All rights reserved.
     2  //
     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
     5  // tree.
     6  
     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
    11  
    12  package layers
    13  
    14  import (
    15  	"encoding/binary"
    16  	"errors"
    17  	"fmt"
    18  	"net"
    19  
    20  	"github.com/gopacket/gopacket"
    21  )
    22  
    23  // CDPTLVType is the type of each TLV value in a CiscoDiscovery packet.
    24  type CDPTLVType uint16
    25  
    26  // CDPTLVType values.
    27  const (
    28  	CDPTLVDevID              CDPTLVType = 0x0001
    29  	CDPTLVAddress            CDPTLVType = 0x0002
    30  	CDPTLVPortID             CDPTLVType = 0x0003
    31  	CDPTLVCapabilities       CDPTLVType = 0x0004
    32  	CDPTLVVersion            CDPTLVType = 0x0005
    33  	CDPTLVPlatform           CDPTLVType = 0x0006
    34  	CDPTLVIPPrefix           CDPTLVType = 0x0007
    35  	CDPTLVHello              CDPTLVType = 0x0008
    36  	CDPTLVVTPDomain          CDPTLVType = 0x0009
    37  	CDPTLVNativeVLAN         CDPTLVType = 0x000a
    38  	CDPTLVFullDuplex         CDPTLVType = 0x000b
    39  	CDPTLVVLANReply          CDPTLVType = 0x000e
    40  	CDPTLVVLANQuery          CDPTLVType = 0x000f
    41  	CDPTLVPower              CDPTLVType = 0x0010
    42  	CDPTLVMTU                CDPTLVType = 0x0011
    43  	CDPTLVExtendedTrust      CDPTLVType = 0x0012
    44  	CDPTLVUntrustedCOS       CDPTLVType = 0x0013
    45  	CDPTLVSysName            CDPTLVType = 0x0014
    46  	CDPTLVSysOID             CDPTLVType = 0x0015
    47  	CDPTLVMgmtAddresses      CDPTLVType = 0x0016
    48  	CDPTLVLocation           CDPTLVType = 0x0017
    49  	CDPTLVExternalPortID     CDPTLVType = 0x0018
    50  	CDPTLVPowerRequested     CDPTLVType = 0x0019
    51  	CDPTLVPowerAvailable     CDPTLVType = 0x001a
    52  	CDPTLVPortUnidirectional CDPTLVType = 0x001b
    53  	CDPTLVEnergyWise         CDPTLVType = 0x001d
    54  	CDPTLVSparePairPOE       CDPTLVType = 0x001f
    55  )
    56  
    57  // CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer.
    58  type CiscoDiscoveryValue struct {
    59  	Type   CDPTLVType
    60  	Length uint16
    61  	Value  []byte
    62  }
    63  
    64  // CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol.
    65  // See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885
    66  type CiscoDiscovery struct {
    67  	BaseLayer
    68  	Version  byte
    69  	TTL      byte
    70  	Checksum uint16
    71  	Values   []CiscoDiscoveryValue
    72  }
    73  
    74  // CDPCapability is the set of capabilities advertised by a CDP device.
    75  type CDPCapability uint32
    76  
    77  // CDPCapability values.
    78  const (
    79  	CDPCapMaskRouter     CDPCapability = 0x0001
    80  	CDPCapMaskTBBridge   CDPCapability = 0x0002
    81  	CDPCapMaskSPBridge   CDPCapability = 0x0004
    82  	CDPCapMaskSwitch     CDPCapability = 0x0008
    83  	CDPCapMaskHost       CDPCapability = 0x0010
    84  	CDPCapMaskIGMPFilter CDPCapability = 0x0020
    85  	CDPCapMaskRepeater   CDPCapability = 0x0040
    86  	CDPCapMaskPhone      CDPCapability = 0x0080
    87  	CDPCapMaskRemote     CDPCapability = 0x0100
    88  )
    89  
    90  // CDPCapabilities represents the capabilities of a device
    91  type CDPCapabilities struct {
    92  	L3Router        bool
    93  	TBBridge        bool
    94  	SPBridge        bool
    95  	L2Switch        bool
    96  	IsHost          bool
    97  	IGMPFilter      bool
    98  	L1Repeater      bool
    99  	IsPhone         bool
   100  	RemotelyManaged bool
   101  }
   102  
   103  // CDP Power-over-Ethernet values.
   104  const (
   105  	CDPPoEFourWire  byte = 0x01
   106  	CDPPoEPDArch    byte = 0x02
   107  	CDPPoEPDRequest byte = 0x04
   108  	CDPPoEPSE       byte = 0x08
   109  )
   110  
   111  // CDPSparePairPoE provides information on PoE.
   112  type CDPSparePairPoE struct {
   113  	PSEFourWire  bool // Supported / Not supported
   114  	PDArchShared bool // Shared / Independent
   115  	PDRequestOn  bool // On / Off
   116  	PSEOn        bool // On / Off
   117  }
   118  
   119  // CDPVLANDialogue encapsulates a VLAN Query/Reply
   120  type CDPVLANDialogue struct {
   121  	ID   uint8
   122  	VLAN uint16
   123  }
   124  
   125  // CDPPowerDialogue encapsulates a Power Query/Reply
   126  type CDPPowerDialogue struct {
   127  	ID     uint16
   128  	MgmtID uint16
   129  	Values []uint32
   130  }
   131  
   132  // CDPLocation provides location information for a CDP device.
   133  type CDPLocation struct {
   134  	Type     uint8 // Undocumented
   135  	Location string
   136  }
   137  
   138  // CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields)
   139  type CDPHello struct {
   140  	OUI              []byte
   141  	ProtocolID       uint16
   142  	ClusterMaster    net.IP
   143  	Unknown1         net.IP
   144  	Version          byte
   145  	SubVersion       byte
   146  	Status           byte
   147  	Unknown2         byte
   148  	ClusterCommander net.HardwareAddr
   149  	SwitchMAC        net.HardwareAddr
   150  	Unknown3         byte
   151  	ManagementVLAN   uint16
   152  }
   153  
   154  // CDPEnergyWiseSubtype is used within CDP to define TLV values.
   155  type CDPEnergyWiseSubtype uint32
   156  
   157  // CDPEnergyWiseSubtype values.
   158  const (
   159  	CDPEnergyWiseRole    CDPEnergyWiseSubtype = 0x00000007
   160  	CDPEnergyWiseDomain  CDPEnergyWiseSubtype = 0x00000008
   161  	CDPEnergyWiseName    CDPEnergyWiseSubtype = 0x00000009
   162  	CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017
   163  )
   164  
   165  // CDPEnergyWise is used by CDP to monitor and control power usage.
   166  type CDPEnergyWise struct {
   167  	EncryptedData  []byte
   168  	Unknown1       uint32
   169  	SequenceNumber uint32
   170  	ModelNumber    string
   171  	Unknown2       uint16
   172  	HardwareID     string
   173  	SerialNum      string
   174  	Unknown3       []byte
   175  	Role           string
   176  	Domain         string
   177  	Name           string
   178  	ReplyUnknown1  []byte
   179  	ReplyPort      []byte
   180  	ReplyAddress   []byte
   181  	ReplyUnknown2  []byte
   182  	ReplyUnknown3  []byte
   183  }
   184  
   185  // CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues
   186  type CiscoDiscoveryInfo struct {
   187  	BaseLayer
   188  	CDPHello
   189  	DeviceID         string
   190  	Addresses        []net.IP
   191  	PortID           string
   192  	Capabilities     CDPCapabilities
   193  	Version          string
   194  	Platform         string
   195  	IPPrefixes       []net.IPNet
   196  	VTPDomain        string
   197  	NativeVLAN       uint16
   198  	FullDuplex       bool
   199  	VLANReply        CDPVLANDialogue
   200  	VLANQuery        CDPVLANDialogue
   201  	PowerConsumption uint16
   202  	MTU              uint32
   203  	ExtendedTrust    uint8
   204  	UntrustedCOS     uint8
   205  	SysName          string
   206  	SysOID           string
   207  	MgmtAddresses    []net.IP
   208  	Location         CDPLocation
   209  	PowerRequest     CDPPowerDialogue
   210  	PowerAvailable   CDPPowerDialogue
   211  	SparePairPoe     CDPSparePairPoE
   212  	EnergyWise       CDPEnergyWise
   213  	Unknown          []CiscoDiscoveryValue
   214  }
   215  
   216  // LayerType returns gopacket.LayerTypeCiscoDiscovery.
   217  func (c *CiscoDiscovery) LayerType() gopacket.LayerType {
   218  	return LayerTypeCiscoDiscovery
   219  }
   220  
   221  func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
   222  	c := &CiscoDiscovery{
   223  		Version:  data[0],
   224  		TTL:      data[1],
   225  		Checksum: binary.BigEndian.Uint16(data[2:4]),
   226  	}
   227  	if c.Version != 1 && c.Version != 2 {
   228  		return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
   229  	}
   230  	var err error
   231  	c.Values, err = decodeCiscoDiscoveryTLVs(data[4:], p)
   232  	if err != nil {
   233  		return err
   234  	}
   235  	c.Contents = data[0:4]
   236  	c.Payload = data[4:]
   237  	p.AddLayer(c)
   238  	return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo))
   239  }
   240  
   241  // LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo.
   242  func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
   243  	return LayerTypeCiscoDiscoveryInfo
   244  }
   245  
   246  func decodeCiscoDiscoveryTLVs(data []byte, p gopacket.PacketBuilder) (values []CiscoDiscoveryValue, err error) {
   247  	for len(data) > 0 {
   248  		if len(data) < 4 {
   249  			p.SetTruncated()
   250  			return nil, errors.New("CDP TLV < 4 bytes")
   251  		}
   252  		val := CiscoDiscoveryValue{
   253  			Type:   CDPTLVType(binary.BigEndian.Uint16(data[:2])),
   254  			Length: binary.BigEndian.Uint16(data[2:4]),
   255  		}
   256  		if val.Length < 4 {
   257  			err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length)
   258  			break
   259  		} else if len(data) < int(val.Length) {
   260  			p.SetTruncated()
   261  			return nil, fmt.Errorf("CDP TLV < length %d", val.Length)
   262  		}
   263  		val.Value = data[4:val.Length]
   264  		values = append(values, val)
   265  		data = data[val.Length:]
   266  	}
   267  	return
   268  }
   269  
   270  func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
   271  	var err error
   272  	info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
   273  	p.AddLayer(info)
   274  	values, err := decodeCiscoDiscoveryTLVs(data, p)
   275  	if err != nil { // Unlikely, as parent decode will fail, but better safe...
   276  		return err
   277  	}
   278  	for _, val := range values {
   279  		switch val.Type {
   280  		case CDPTLVDevID:
   281  			info.DeviceID = string(val.Value)
   282  		case CDPTLVAddress:
   283  			if err = checkCDPTLVLen(val, 4); err != nil {
   284  				return err
   285  			}
   286  			info.Addresses, err = decodeAddresses(val.Value)
   287  			if err != nil {
   288  				return err
   289  			}
   290  		case CDPTLVPortID:
   291  			info.PortID = string(val.Value)
   292  		case CDPTLVCapabilities:
   293  			if err = checkCDPTLVLen(val, 4); err != nil {
   294  				return err
   295  			}
   296  			val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4]))
   297  			info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0)
   298  			info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0)
   299  			info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0)
   300  			info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0)
   301  			info.Capabilities.IsHost = (val&CDPCapMaskHost > 0)
   302  			info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0)
   303  			info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0)
   304  			info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0)
   305  			info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0)
   306  		case CDPTLVVersion:
   307  			info.Version = string(val.Value)
   308  		case CDPTLVPlatform:
   309  			info.Platform = string(val.Value)
   310  		case CDPTLVIPPrefix:
   311  			v := val.Value
   312  			l := len(v)
   313  			if l%5 == 0 && l >= 5 {
   314  				for len(v) > 0 {
   315  					_, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4]))
   316  					info.IPPrefixes = append(info.IPPrefixes, *ipnet)
   317  					v = v[5:]
   318  				}
   319  			} else {
   320  				return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value))
   321  			}
   322  		case CDPTLVHello:
   323  			if err = checkCDPTLVLen(val, 32); err != nil {
   324  				return err
   325  			}
   326  			v := val.Value
   327  			info.CDPHello.OUI = v[0:3]
   328  			info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5])
   329  			info.CDPHello.ClusterMaster = v[5:9]
   330  			info.CDPHello.Unknown1 = v[9:13]
   331  			info.CDPHello.Version = v[13]
   332  			info.CDPHello.SubVersion = v[14]
   333  			info.CDPHello.Status = v[15]
   334  			info.CDPHello.Unknown2 = v[16]
   335  			info.CDPHello.ClusterCommander = v[17:23]
   336  			info.CDPHello.SwitchMAC = v[23:29]
   337  			info.CDPHello.Unknown3 = v[29]
   338  			info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32])
   339  		case CDPTLVVTPDomain:
   340  			info.VTPDomain = string(val.Value)
   341  		case CDPTLVNativeVLAN:
   342  			if err = checkCDPTLVLen(val, 2); err != nil {
   343  				return err
   344  			}
   345  			info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2])
   346  		case CDPTLVFullDuplex:
   347  			if err = checkCDPTLVLen(val, 1); err != nil {
   348  				return err
   349  			}
   350  			info.FullDuplex = (val.Value[0] == 1)
   351  		case CDPTLVVLANReply:
   352  			if err = checkCDPTLVLen(val, 3); err != nil {
   353  				return err
   354  			}
   355  			info.VLANReply.ID = uint8(val.Value[0])
   356  			info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
   357  		case CDPTLVVLANQuery:
   358  			if err = checkCDPTLVLen(val, 3); err != nil {
   359  				return err
   360  			}
   361  			info.VLANQuery.ID = uint8(val.Value[0])
   362  			info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
   363  		case CDPTLVPower:
   364  			if err = checkCDPTLVLen(val, 2); err != nil {
   365  				return err
   366  			}
   367  			info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2])
   368  		case CDPTLVMTU:
   369  			if err = checkCDPTLVLen(val, 4); err != nil {
   370  				return err
   371  			}
   372  			info.MTU = binary.BigEndian.Uint32(val.Value[0:4])
   373  		case CDPTLVExtendedTrust:
   374  			if err = checkCDPTLVLen(val, 1); err != nil {
   375  				return err
   376  			}
   377  			info.ExtendedTrust = uint8(val.Value[0])
   378  		case CDPTLVUntrustedCOS:
   379  			if err = checkCDPTLVLen(val, 1); err != nil {
   380  				return err
   381  			}
   382  			info.UntrustedCOS = uint8(val.Value[0])
   383  		case CDPTLVSysName:
   384  			info.SysName = string(val.Value)
   385  		case CDPTLVSysOID:
   386  			info.SysOID = string(val.Value)
   387  		case CDPTLVMgmtAddresses:
   388  			if err = checkCDPTLVLen(val, 4); err != nil {
   389  				return err
   390  			}
   391  			info.MgmtAddresses, err = decodeAddresses(val.Value)
   392  			if err != nil {
   393  				return err
   394  			}
   395  		case CDPTLVLocation:
   396  			if err = checkCDPTLVLen(val, 2); err != nil {
   397  				return err
   398  			}
   399  			info.Location.Type = uint8(val.Value[0])
   400  			info.Location.Location = string(val.Value[1:])
   401  
   402  			//		case CDPTLVLExternalPortID:
   403  			//			Undocumented
   404  		case CDPTLVPowerRequested:
   405  			if err = checkCDPTLVLen(val, 4); err != nil {
   406  				return err
   407  			}
   408  			info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2])
   409  			info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
   410  			for n := 4; n < len(val.Value); n += 4 {
   411  				info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
   412  			}
   413  		case CDPTLVPowerAvailable:
   414  			if err = checkCDPTLVLen(val, 4); err != nil {
   415  				return err
   416  			}
   417  			info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2])
   418  			info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
   419  			for n := 4; n < len(val.Value); n += 4 {
   420  				info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
   421  			}
   422  			//		case CDPTLVPortUnidirectional
   423  			//			Undocumented
   424  		case CDPTLVEnergyWise:
   425  			if err = checkCDPTLVLen(val, 72); err != nil {
   426  				return err
   427  			}
   428  			info.EnergyWise.EncryptedData = val.Value[0:20]
   429  			info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24])
   430  			info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28])
   431  			info.EnergyWise.ModelNumber = string(val.Value[28:44])
   432  			info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46])
   433  			info.EnergyWise.HardwareID = string(val.Value[46:49])
   434  			info.EnergyWise.SerialNum = string(val.Value[49:60])
   435  			info.EnergyWise.Unknown3 = val.Value[60:68]
   436  			tlvLen := binary.BigEndian.Uint16(val.Value[68:70])
   437  			tlvNum := binary.BigEndian.Uint16(val.Value[70:72])
   438  			data := val.Value[72:]
   439  			if len(data) < int(tlvLen) {
   440  				return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data))
   441  			}
   442  			numSeen := 0
   443  			for len(data) > 8 {
   444  				numSeen++
   445  				if numSeen > int(tlvNum) { // Too many TLV's ?
   446  					return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen)
   447  				}
   448  				tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4]))
   449  				tLen := int(binary.BigEndian.Uint32(data[4:8]))
   450  				if tLen > len(data)-8 {
   451  					return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8)
   452  				}
   453  				data = data[8:]
   454  				switch tType {
   455  				case CDPEnergyWiseRole:
   456  					info.EnergyWise.Role = string(data[:])
   457  				case CDPEnergyWiseDomain:
   458  					info.EnergyWise.Domain = string(data[:])
   459  				case CDPEnergyWiseName:
   460  					info.EnergyWise.Name = string(data[:])
   461  				case CDPEnergyWiseReplyTo:
   462  					if len(data) >= 18 {
   463  						info.EnergyWise.ReplyUnknown1 = data[0:2]
   464  						info.EnergyWise.ReplyPort = data[2:4]
   465  						info.EnergyWise.ReplyAddress = data[4:8]
   466  						info.EnergyWise.ReplyUnknown2 = data[8:10]
   467  						info.EnergyWise.ReplyUnknown3 = data[10:14]
   468  					}
   469  				}
   470  				data = data[tLen:]
   471  			}
   472  		case CDPTLVSparePairPOE:
   473  			if err = checkCDPTLVLen(val, 1); err != nil {
   474  				return err
   475  			}
   476  			v := val.Value[0]
   477  			info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0)
   478  			info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0)
   479  			info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0)
   480  			info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0)
   481  		default:
   482  			info.Unknown = append(info.Unknown, val)
   483  		}
   484  	}
   485  	return nil
   486  }
   487  
   488  // CDP Protocol Types
   489  const (
   490  	CDPProtocolTypeNLPID byte = 1
   491  	CDPProtocolType802_2 byte = 2
   492  )
   493  
   494  // CDPAddressType is used to define TLV values within CDP addresses.
   495  type CDPAddressType uint64
   496  
   497  // CDP Address types.
   498  const (
   499  	CDPAddressTypeCLNP      CDPAddressType = 0x81
   500  	CDPAddressTypeIPV4      CDPAddressType = 0xcc
   501  	CDPAddressTypeIPV6      CDPAddressType = 0xaaaa030000000800
   502  	CDPAddressTypeDECNET    CDPAddressType = 0xaaaa030000006003
   503  	CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b
   504  	CDPAddressTypeIPX       CDPAddressType = 0xaaaa030000008137
   505  	CDPAddressTypeVINES     CDPAddressType = 0xaaaa0300000080c4
   506  	CDPAddressTypeXNS       CDPAddressType = 0xaaaa030000000600
   507  	CDPAddressTypeAPOLLO    CDPAddressType = 0xaaaa030000008019
   508  )
   509  
   510  func decodeAddresses(v []byte) (addresses []net.IP, err error) {
   511  	numaddr := int(binary.BigEndian.Uint32(v[0:4]))
   512  	if numaddr < 1 {
   513  		return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr)
   514  	}
   515  	v = v[4:]
   516  	if len(v) < numaddr*8 {
   517  		return nil, fmt.Errorf("Invalid Address TLV length %d", len(v))
   518  	}
   519  	for i := 0; i < numaddr; i++ {
   520  		prottype := v[0]
   521  		if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type
   522  			return nil, fmt.Errorf("Invalid Address Protocol %d", prottype)
   523  		}
   524  		protlen := int(v[1])
   525  		if (prottype == CDPProtocolTypeNLPID && protlen != 1) ||
   526  			(prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length
   527  			return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen)
   528  		}
   529  		plen := make([]byte, 8)
   530  		copy(plen[8-protlen:], v[2:2+protlen])
   531  		protocol := CDPAddressType(binary.BigEndian.Uint64(plen))
   532  		v = v[2+protlen:]
   533  		addrlen := binary.BigEndian.Uint16(v[0:2])
   534  		ab := v[2 : 2+addrlen]
   535  		if protocol == CDPAddressTypeIPV4 && addrlen == 4 {
   536  			addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3]))
   537  		} else if protocol == CDPAddressTypeIPV6 && addrlen == 16 {
   538  			addresses = append(addresses, net.IP(ab))
   539  		} else {
   540  			// only handle IPV4 & IPV6 for now
   541  		}
   542  		v = v[2+addrlen:]
   543  		if len(v) < 8 {
   544  			break
   545  		}
   546  	}
   547  	return
   548  }
   549  
   550  func (t CDPTLVType) String() (s string) {
   551  	switch t {
   552  	case CDPTLVDevID:
   553  		s = "Device ID"
   554  	case CDPTLVAddress:
   555  		s = "Addresses"
   556  	case CDPTLVPortID:
   557  		s = "Port ID"
   558  	case CDPTLVCapabilities:
   559  		s = "Capabilities"
   560  	case CDPTLVVersion:
   561  		s = "Software Version"
   562  	case CDPTLVPlatform:
   563  		s = "Platform"
   564  	case CDPTLVIPPrefix:
   565  		s = "IP Prefix"
   566  	case CDPTLVHello:
   567  		s = "Protocol Hello"
   568  	case CDPTLVVTPDomain:
   569  		s = "VTP Management Domain"
   570  	case CDPTLVNativeVLAN:
   571  		s = "Native VLAN"
   572  	case CDPTLVFullDuplex:
   573  		s = "Full Duplex"
   574  	case CDPTLVVLANReply:
   575  		s = "VoIP VLAN Reply"
   576  	case CDPTLVVLANQuery:
   577  		s = "VLANQuery"
   578  	case CDPTLVPower:
   579  		s = "Power consumption"
   580  	case CDPTLVMTU:
   581  		s = "MTU"
   582  	case CDPTLVExtendedTrust:
   583  		s = "Extended Trust Bitmap"
   584  	case CDPTLVUntrustedCOS:
   585  		s = "Untrusted Port CoS"
   586  	case CDPTLVSysName:
   587  		s = "System Name"
   588  	case CDPTLVSysOID:
   589  		s = "System OID"
   590  	case CDPTLVMgmtAddresses:
   591  		s = "Management Addresses"
   592  	case CDPTLVLocation:
   593  		s = "Location"
   594  	case CDPTLVExternalPortID:
   595  		s = "External Port ID"
   596  	case CDPTLVPowerRequested:
   597  		s = "Power Requested"
   598  	case CDPTLVPowerAvailable:
   599  		s = "Power Available"
   600  	case CDPTLVPortUnidirectional:
   601  		s = "Port Unidirectional"
   602  	case CDPTLVEnergyWise:
   603  		s = "Energy Wise"
   604  	case CDPTLVSparePairPOE:
   605  		s = "Spare Pair POE"
   606  	default:
   607  		s = "Unknown"
   608  	}
   609  	return
   610  }
   611  
   612  func (a CDPAddressType) String() (s string) {
   613  	switch a {
   614  	case CDPAddressTypeCLNP:
   615  		s = "Connectionless Network Protocol"
   616  	case CDPAddressTypeIPV4:
   617  		s = "IPv4"
   618  	case CDPAddressTypeIPV6:
   619  		s = "IPv6"
   620  	case CDPAddressTypeDECNET:
   621  		s = "DECnet Phase IV"
   622  	case CDPAddressTypeAPPLETALK:
   623  		s = "Apple Talk"
   624  	case CDPAddressTypeIPX:
   625  		s = "Novell IPX"
   626  	case CDPAddressTypeVINES:
   627  		s = "Banyan VINES"
   628  	case CDPAddressTypeXNS:
   629  		s = "Xerox Network Systems"
   630  	case CDPAddressTypeAPOLLO:
   631  		s = "Apollo"
   632  	default:
   633  		s = "Unknown"
   634  	}
   635  	return
   636  }
   637  
   638  func (t CDPEnergyWiseSubtype) String() (s string) {
   639  	switch t {
   640  	case CDPEnergyWiseRole:
   641  		s = "Role"
   642  	case CDPEnergyWiseDomain:
   643  		s = "Domain"
   644  	case CDPEnergyWiseName:
   645  		s = "Name"
   646  	case CDPEnergyWiseReplyTo:
   647  		s = "ReplyTo"
   648  	default:
   649  		s = "Unknown"
   650  	}
   651  	return
   652  }
   653  
   654  func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) {
   655  	if len(v.Value) < l {
   656  		err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value))
   657  	}
   658  	return
   659  }