通常,大多数网络都有几个路由器或交换机,为了便于网络管理,使用网络图或网络图来告诉网络中存在什么类型的设备,以及所有设备如何相互连接,使用的IP地址以及它们属于哪个VLAN的信息。

CDP是一种专有的第二层思科网络协议。它是一种网络发现工具,在cisco设备上运行,有助于发现连接到网络的cisco设备。CDP有助于有效地检查设备,而无需实际进行物理检查。CDP允许用户使用一些显示命令来查看连接设备的信息,例如有关本地端口、远程端口、主机名、设备平台等的信息。

默认情况下,从相邻的思科设备接收的CDP消息不会重定向到任何其他设备,即CDP只转发到直接连接的思科设备。所有支持CDP的设备都以表格格式存储从其相邻设备接收的所有消息,可以使用show CDP neighbors命令查看这些消息。

这些CDP消息在所有SNAP(子网络访问协议)报头兼容接口上每60秒传输一次。由于并非所有数据链路层介质类型都支持SNAP,因此支持的介质类型有令牌环、以太网、PPP(点对点协议)、FDDI(光纤分布式数据接口)、帧中继、ATM(异步传输模式)、HDLC(高级数据链路控制)。Cisco设备还将这些CDP消息发送到多播目标地址(01:00:0C:CC:CC:CC)。

CDP的工作原理

所有Cisco设备都会定期传输CDP数据包(默认时间间隔值为60秒,但可以调整)。这些数据包以秒为单位公布生存时间(TTL)值,该值指示在丢弃数据包之前必须保留的秒数(默认值为180秒)。

CDP工具架构 cdp技术架构_c++

启用接口后,发送的CDP数据包的生存时间值为非零,并且在接口即将关闭之前,生存时间值为零。这提供了快速的状态发现。

所有Cisco设备都接收CDP数据包,对其进行处理并缓存数据包中的信息。Cisco设备从不转发CDP数据包。如果任何信息与上次接收到的数据包相比发生了变化,则会缓存新信息,并丢弃旧信息,即使其生存时间值尚未过期。

CDP版本

CDPV1:-初始版本,它只能收集连接到下一端的设备信息。

CDP工具架构 cdp技术架构_网络_02


CDPV2:-是该协议的最新版本,提供了更智能的设备跟踪功能,如802.1Q中继上本地VLAN ID不匹配的实例,以及连接设备之间双工状态的不匹配。

CDP工具架构 cdp技术架构_c语言_03

  • 各字段定义
Version :包含设备软件版本信息。
TTL:接收器应保留此数据包中包含的信息的时间量,以秒为单位。180秒后默认。
Address :包含接收和发送设备的网络地址。
Port-ID :标识发送Cisco发现协议数据包的端口。 
Capabilities :标识设备类型,表示设备的功能能力。例如,一个开关。
Platform : 标识设备的硬件平台。例如,Cisco 4500。
Native :指示每个接口上未标记数据包的假定VLAN。Cisco发现协议学习接口的本机VLAN。该字段仅用于支持IEEE 802.1Q协议的接口。
VTP Management Domain: 播发配置的VLAN中继协议(VTP)-系统的管理域名。网络运营商使用此名称来验证相邻网络节点中的VTP域配置。
  • CDP Version1和Vesion 2 的区别

CDP Version1 和 Version2的主要区别在于 Version2 支持更多 TLV,比如交换机之间的 VVTP Domain,Duplex,Native VLAN ID 等等。这样极大的扩充了 CDP的功能。

思科发现协议第2版提供了比第1版更智能的设备跟踪功能。可用的功能之一是增强的报告机制,用于更快速的错误跟踪,这有助于减少网络停机时间。报告的错误包括连接端口上的本机VLAN ID(IEEE 802.1Q)不匹配以及连接设备之间的端口双工状态不匹配。有关报告错误的消息可以发送到控制台或日志服务器。

CDP监控和维护命令

clear cdp counters   #清除cdp计数器
clear cdp table     #清除cdp表
show cdp    #显示cdp
show cdp entry device-name [protocol | version]  #显示cdp入口设备名称[协议|版本]
show cdp interface [type number]  #显示cdp接口[类型号]
show cdp neighbors [type number] [detail]  #显示cdp邻居[类型编号][详细信息]
show cdp traffic  #显示cdp流量

什么是CDP欺骗

在CDP欺骗中,攻击者发送以多播mac地址(01:00:0c:cc:cc:cc)为目的地、以各种欺骗或伪造的mac地址为源的数据包。当Cisco设备接收到这些帧时,它开始在CDP表中添加信息,并且该表将开始变得更大,因为攻击者可能会向设备发送数千个CDP帧。如果设备无法处理此攻击,则设备可能会在一段时间后崩溃,这就是为什么建议在连接非思科设备、用户站的接口上禁用CDP。

CDP TLV的类型

#define TYPE_DEVICE_ID      0x0001 
#define TYPE_ADDRESS        0x0002 
#define TYPE_PORT_ID        0x0003 
#define TYPE_CAPABILITIES   0x0004 
#define TYPE_IOS_VERSION    0x0005 
#define TYPE_PLATFORM       0x0006 
#define TYPE_IP_PREFIX      0x0007 
#define TYPE_PROTOCOL_HELLO     0x0008 
#define TYPE_VTP_MGMT_DOMAIN    0x0009 
#define TYPE_NATIVE_VLAN        0x000a 
#define TYPE_DUPLEX             0x000b 
/*                              0x000c */
/*                              0x000d */
#define TYPE_VOIP_VLAN_REPLY    0x000e
#define TYPE_VOIP_VLAN_QUERY    0x000f
#define TYPE_POWER              0x0010
#define TYPE_MTU                0x0011 
#define TYPE_TRUST_BITMAP       0x0012 
#define TYPE_UNTRUSTED_COS      0x0013 
#define TYPE_SYSTEM_NAME        0x0014 
#define TYPE_SYSTEM_OID         0x0015 
#define TYPE_MANAGEMENT_ADDR    0x0016 
#define TYPE_LOCATION           0x0017
#define TYPE_EXT_PORT_ID        0x0018
#define TYPE_POWER_REQUESTED    0x0019
#define TYPE_POWER_AVAILABLE    0x001a 
#define TYPE_PORT_UNIDIR        0x001b

功能名称

char CAPABILITIES_NAMES[7][19] = {
    "Router",
    "Transparent bridge",
    "Source Route Bridge",
    "Switch",
    "Host",
    "IGMP",
    "Repeater"
};

CDP工具架构 cdp技术架构_网络_04

CDP思科发现协议解析及C/C++代码实现

int main( int argc, char *argv[] ) 
{

    ...
    // 获取命令行参数
    while( ( c = getopt( argc, argv, "i:dhw:r:" ) ) != -1 ) 
    {
        switch( c ) 
        {
            case 'i':
                dev = optarg;
                handle = pcap_open_live( dev, BUFSIZ, 1, 269000, errbuf );
                
                if( handle == NULL ) 
                {
                    fprintf( stderr, "Couldn't open device %s: %s\n", dev, errbuf );
                    return( 2 );
                }
                
                // 获取设备
                if( pcap_lookupnet( dev, &net, &mask, errbuf ) == -1 ) 
                {
                    fprintf( stderr, "Couldn't get netmask for device %s: %s\n",
                             dev, errbuf );
                    net = 0;
                    mask = 0;
                }
                break;
                
            case 'd':
                debug = 1;
                break;
            
            case 'r':
                dumpfile = optarg;
                handle = pcap_open_offline( dumpfile, errbuf );
                if( !handle )
                {
                    fprintf( stderr, "Couldn't open file %s: %s\n",
                             dumpfile, errbuf );
                    return( 2 );
                }
                
                net = 0;
                mask = 0;
                break;
                                
            case 'w':
                dumper = pcap_dump_open( handle, optarg );
                if( !dumper )
                {
                    fprintf( stderr, "Couldn't write to file %s: %s\n",
                             optarg, errbuf );
                }
                break;
                
            case '?':
                if( optopt == 'd' || optopt == 'w' ) 
                {
                    fprintf( stderr, "Option -%c requires an argument.\n",
                             optopt );
                } 
                else if( isprint ( optopt ) ) 
                {
                    fprintf( stderr, "Unknown option `-%c'.\n", optopt );
                } 
                else 
                {
                    fprintf( stderr, "Unknown option character `\\x%x'.\n",
                             optopt );
                }
                return 1;

           case 'h':
           default:
               print_help( argv );
               return( 0 );
             
           }
    }
    
    if( !dev && !dumpfile ) 
    {
        print_help( argv );
        return( 0 );
    }
    
...
    
    printf( "Reading packets from %s.\n", ( dev ) ? dev : dumpfile );
    printf( "Waiting for a CDP packet...\n\n" );
    
    // 死循环 
    while( pcap_packet == 1 ) 
    {
        
        // 抓一包
        pcap_packet =
            pcap_next_ex( handle, &header, (const u_char **) &data );
        
        // 一旦数据包到达,就将其写入转储文件      
        if( dumper )
        {
            pcap_dump( (u_char *)dumper, header, data );
            pcap_dump_flush( dumper );
        }
        
...
        
...
        
        // 解析TLV树
        tlv_parse( data, header->len );
        
...
    }
    
...
}

运行结果:

CDP Version1 解析:

CDP工具架构 cdp技术架构_c++_05


CDP Version2 解析:

CDP工具架构 cdp技术架构_网络_06


CDP工具架构 cdp技术架构_c++_07


If you need the complete source code, please add the WeChat number (c17865354792)

总结

Cisco发现协议是一种第2层、独立于媒体和网络的协议,网络应用程序使用该协议来了解附近的直接连接设备。默认情况下启用Cisco发现协议。为Cisco发现协议配置的每个设备至少播发一个地址,设备可以在该地址接收消息,并将定期播发(消息)发送到众所周知的多播地址01:00:0C:CC:CC:CC。设备通过监听该地址来发现彼此。当其他设备上的接口打开或关闭时,他们也会听消息来学习。