/*
* Copied from Linux Monitor ( LiMon ) - Networking .
*
* Copyright 1994 - 2000 Neil Russell .
* ( See License )
* Copyright 2000 Roland Borde
* Copyright 2000 Paolo Scaffardi
* Copyright 2000 - 2002 Wolfgang Denk , wd @ denx . de
* SPDX - License - Identifier : GPL - 2.0
*/
# include <common.h>
# include <net.h>
# if defined(CONFIG_CDP_VERSION)
# include <timestamp.h>
# endif
# include "cdp.h"
/* Ethernet bcast address */
const uchar NetCDPAddr [ 6 ] = { 0x01 , 0x00 , 0x0c , 0xcc , 0xcc , 0xcc } ;
# define CDP_DEVICE_ID_TLV 0x0001
# define CDP_ADDRESS_TLV 0x0002
# define CDP_PORT_ID_TLV 0x0003
# define CDP_CAPABILITIES_TLV 0x0004
# define CDP_VERSION_TLV 0x0005
# define CDP_PLATFORM_TLV 0x0006
# define CDP_NATIVE_VLAN_TLV 0x000a
# define CDP_APPLIANCE_VLAN_TLV 0x000e
# define CDP_TRIGGER_TLV 0x000f
# define CDP_POWER_CONSUMPTION_TLV 0x0010
# define CDP_SYSNAME_TLV 0x0014
# define CDP_SYSOBJECT_TLV 0x0015
# define CDP_MANAGEMENT_ADDRESS_TLV 0x0016
# define CDP_TIMEOUT 250UL /* one packet every 250ms */
static int CDPSeq ;
static int CDPOK ;
ushort CDPNativeVLAN ;
ushort CDPApplianceVLAN ;
static const uchar CDP_SNAP_hdr [ 8 ] = {
0xAA , 0xAA , 0x03 , 0x00 , 0x00 , 0x0C , 0x20 , 0x00 } ;
static ushort
CDP_compute_csum ( const uchar * buff , ushort len )
{
ushort csum ;
int odd ;
ulong result = 0 ;
ushort leftover ;
ushort * p ;
if ( len > 0 ) {
odd = 1 & ( ulong ) buff ;
if ( odd ) {
result = * buff < < 8 ;
len - - ;
buff + + ;
}
while ( len > 1 ) {
p = ( ushort * ) buff ;
result + = * p + + ;
buff = ( uchar * ) p ;
if ( result & 0x80000000 )
result = ( result & 0xFFFF ) + ( result > > 16 ) ;
len - = 2 ;
}
if ( len ) {
leftover = ( signed short ) ( * ( const signed char * ) buff ) ;
/*
* CISCO SUCKS big time ! ( and blows too ) :
* CDP uses the IP checksum algorithm with a twist ;
* for the last byte it * sign * extends and sums .
*/
result = ( result & 0xffff0000 ) |
( ( result + leftover ) & 0x0000ffff ) ;
}
while ( result > > 16 )
result = ( result & 0xFFFF ) + ( result > > 16 ) ;
if ( odd )
result = ( ( result > > 8 ) & 0xff ) |
( ( result & 0xff ) < < 8 ) ;
}
/* add up 16-bit and 17-bit words for 17+c bits */
result = ( result & 0xffff ) + ( result > > 16 ) ;
/* add up 16-bit and 2-bit for 16+c bit */
result = ( result & 0xffff ) + ( result > > 16 ) ;
/* add up carry.. */
result = ( result & 0xffff ) + ( result > > 16 ) ;
/* negate */
csum = ~ ( ushort ) result ;
/* run time endian detection */
if ( csum ! = htons ( csum ) ) /* little endian */
csum = htons ( csum ) ;
return csum ;
}
static int
CDPSendTrigger ( void )
{
uchar * pkt ;
ushort * s ;
ushort * cp ;
struct ethernet_hdr * et ;
int len ;
ushort chksum ;
# if defined(CONFIG_CDP_DEVICE_ID) || defined(CONFIG_CDP_PORT_ID) || \
defined ( CONFIG_CDP_VERSION ) | | defined ( CONFIG_CDP_PLATFORM )
char buf [ 32 ] ;
# endif
pkt = NetTxPacket ;
et = ( struct ethernet_hdr * ) pkt ;
/* NOTE: trigger sent not on any VLAN */
/* form ethernet header */
memcpy ( et - > et_dest , NetCDPAddr , 6 ) ;
memcpy ( et - > et_src , NetOurEther , 6 ) ;
pkt + = ETHER_HDR_SIZE ;
/* SNAP header */
memcpy ( ( uchar * ) pkt , CDP_SNAP_hdr , sizeof ( CDP_SNAP_hdr ) ) ;
pkt + = sizeof ( CDP_SNAP_hdr ) ;
/* CDP header */
* pkt + + = 0x02 ; /* CDP version 2 */
* pkt + + = 180 ; /* TTL */
s = ( ushort * ) pkt ;
cp = s ;
/* checksum (0 for later calculation) */
* s + + = htons ( 0 ) ;
/* CDP fields */
# ifdef CONFIG_CDP_DEVICE_ID
* s + + = htons ( CDP_DEVICE_ID_TLV ) ;
* s + + = htons ( CONFIG_CDP_DEVICE_ID ) ;
sprintf ( buf , CONFIG_CDP_DEVICE_ID_PREFIX " %pm " , NetOurEther ) ;
memcpy ( ( uchar * ) s , buf , 16 ) ;
s + = 16 / 2 ;
# endif
# ifdef CONFIG_CDP_PORT_ID
* s + + = htons ( CDP_PORT_ID_TLV ) ;
memset ( buf , 0 , sizeof ( buf ) ) ;
sprintf ( buf , CONFIG_CDP_PORT_ID , eth_get_dev_index ( ) ) ;
len = strlen ( buf ) ;
if ( len & 1 ) /* make it even */
len + + ;
* s + + = htons ( len + 4 ) ;
memcpy ( ( uchar * ) s , buf , len ) ;
s + = len / 2 ;
# endif
# ifdef CONFIG_CDP_CAPABILITIES
* s + + = htons ( CDP_CAPABILITIES_TLV ) ;
* s + + = htons ( 8 ) ;
* ( ulong * ) s = htonl ( CONFIG_CDP_CAPABILITIES ) ;
s + = 2 ;
# endif
# ifdef CONFIG_CDP_VERSION
* s + + = htons ( CDP_VERSION_TLV ) ;
memset ( buf , 0 , sizeof ( buf ) ) ;
strcpy ( buf , CONFIG_CDP_VERSION ) ;
len = strlen ( buf ) ;
if ( len & 1 ) /* make it even */
len + + ;
* s + + = htons ( len + 4 ) ;
memcpy ( ( uchar * ) s , buf , len ) ;
s + = len / 2 ;
# endif
# ifdef CONFIG_CDP_PLATFORM
* s + + = htons ( CDP_PLATFORM_TLV ) ;
memset ( buf , 0 , sizeof ( buf ) ) ;
strcpy ( buf , CONFIG_CDP_PLATFORM ) ;
len = strlen ( buf ) ;
if ( len & 1 ) /* make it even */
len + + ;
* s + + = htons ( len + 4 ) ;
memcpy ( ( uchar * ) s , buf , len ) ;
s + = len / 2 ;
# endif
# ifdef CONFIG_CDP_TRIGGER
* s + + = htons ( CDP_TRIGGER_TLV ) ;
* s + + = htons ( 8 ) ;
* ( ulong * ) s = htonl ( CONFIG_CDP_TRIGGER ) ;
s + = 2 ;
# endif
# ifdef CONFIG_CDP_POWER_CONSUMPTION
* s + + = htons ( CDP_POWER_CONSUMPTION_TLV ) ;
* s + + = htons ( 6 ) ;
* s + + = htons ( CONFIG_CDP_POWER_CONSUMPTION ) ;
# endif
/* length of ethernet packet */
len = ( uchar * ) s - ( ( uchar * ) NetTxPacket + ETHER_HDR_SIZE ) ;
et - > et_protlen = htons ( len ) ;
len = ETHER_HDR_SIZE + sizeof ( CDP_SNAP_hdr ) ;
chksum = CDP_compute_csum ( ( uchar * ) NetTxPacket + len ,
( uchar * ) s - ( NetTxPacket + len ) ) ;
if ( chksum = = 0 )
chksum = 0xFFFF ;
* cp = htons ( chksum ) ;
NetSendPacket ( NetTxPacket , ( uchar * ) s - NetTxPacket ) ;
return 0 ;
}
static void
CDPTimeout ( void )
{
CDPSeq + + ;
if ( CDPSeq < 3 ) {
NetSetTimeout ( CDP_TIMEOUT , CDPTimeout ) ;
CDPSendTrigger ( ) ;
return ;
}
/* if not OK try again */
if ( ! CDPOK )
NetStartAgain ( ) ;
else
net_set_state ( NETLOOP_SUCCESS ) ;
}
void cdp_receive ( const uchar * pkt , unsigned len )
{
const uchar * t ;
const ushort * ss ;
ushort type , tlen ;
ushort vlan , nvlan ;
/* minimum size? */
if ( len < sizeof ( CDP_SNAP_hdr ) + 4 )
goto pkt_short ;
/* check for valid CDP SNAP header */
if ( memcmp ( pkt , CDP_SNAP_hdr , sizeof ( CDP_SNAP_hdr ) ) ! = 0 )
return ;
pkt + = sizeof ( CDP_SNAP_hdr ) ;
len - = sizeof ( CDP_SNAP_hdr ) ;
/* Version of CDP protocol must be >= 2 and TTL != 0 */
if ( pkt [ 0 ] < 0x02 | | pkt [ 1 ] = = 0 )
return ;
/*
* if version is greater than 0x02 maybe we ' ll have a problem ;
* output a warning
*/
if ( pkt [ 0 ] ! = 0x02 )
printf ( " **WARNING: CDP packet received with a protocol version "
" %d > 2 \n " , pkt [ 0 ] & 0xff ) ;
if ( CDP_compute_csum ( pkt , len ) ! = 0 )
return ;
pkt + = 4 ;
len - = 4 ;
vlan = htons ( - 1 ) ;
nvlan = htons ( - 1 ) ;
while ( len > 0 ) {
if ( len < 4 )
goto pkt_short ;
ss = ( const ushort * ) pkt ;
type = ntohs ( ss [ 0 ] ) ;
tlen = ntohs ( ss [ 1 ] ) ;
if ( tlen > len )
goto pkt_short ;
pkt + = tlen ;
len - = tlen ;
ss + = 2 ; /* point ss to the data of the TLV */
tlen - = 4 ;
switch ( type ) {
case CDP_DEVICE_ID_TLV :
break ;
case CDP_ADDRESS_TLV :
break ;
case CDP_PORT_ID_TLV :
break ;
case CDP_CAPABILITIES_TLV :
break ;
case CDP_VERSION_TLV :
break ;
case CDP_PLATFORM_TLV :
break ;
case CDP_NATIVE_VLAN_TLV :
nvlan = * ss ;
break ;
case CDP_APPLIANCE_VLAN_TLV :
t = ( const uchar * ) ss ;
while ( tlen > 0 ) {
if ( tlen < 3 )
goto pkt_short ;
ss = ( const ushort * ) ( t + 1 ) ;
# ifdef CONFIG_CDP_APPLIANCE_VLAN_TYPE
if ( t [ 0 ] = = CONFIG_CDP_APPLIANCE_VLAN_TYPE )
vlan = * ss ;
# else
/* XXX will this work; dunno */
vlan = ntohs ( * ss ) ;
# endif
t + = 3 ; tlen - = 3 ;
}
break ;
case CDP_TRIGGER_TLV :
break ;
case CDP_POWER_CONSUMPTION_TLV :
break ;
case CDP_SYSNAME_TLV :
break ;
case CDP_SYSOBJECT_TLV :
break ;
case CDP_MANAGEMENT_ADDRESS_TLV :
break ;
}
}
CDPApplianceVLAN = vlan ;
CDPNativeVLAN = nvlan ;
CDPOK = 1 ;
return ;
pkt_short :
printf ( " ** CDP packet is too short \n " ) ;
return ;
}
void
CDPStart ( void )
{
printf ( " Using %s device \n " , eth_get_name ( ) ) ;
CDPSeq = 0 ;
CDPOK = 0 ;
CDPNativeVLAN = htons ( - 1 ) ;
CDPApplianceVLAN = htons ( - 1 ) ;
NetSetTimeout ( CDP_TIMEOUT , CDPTimeout ) ;
CDPSendTrigger ( ) ;
}