LLDP(链路层发现协议)是一种IEEE标准协议(IEEE 802.1AB),它定义了封装在以太网帧中的消息,目的是通过默认情况下每30秒从每个端口定期重传一次,为设备提供一种向LAN(局域网)上的其他设备宣布基本设备信息的方式。它是一个类似于CDP(思科发现协议)的标准化协议。它是一个独立于供应商的链路层协议,用于网络拓扑、故障排除和网络管理自动化。
LLDP的好处
LLDP具有以下优点:
1.简化了网络管理工具在多供应商环境中的使用。2.准确发现物理网络拓扑,简化企业网络中的故障排除。3.支持在多供应商环境中发现工作站。4.提供设备功能,并支持可选的系统名称和描述以及管理地址。5.提供可用于检测双工和速度不匹配的信息。6.发现IP地址配置错误或无法访问的设备。
LLDP的缺点:
1.LLDP运行成本高于CDP。2.对某些设备的有限支持:虽然LLDP是一个开放的标准协议,但某些设备可能不完全支持它,或者支持有限。例如,在某些虚拟交换机(如VMware)上,设备发现仅支持CDP。3.实现成本更高:与CDP等其他协议相比,LLDP的实现可能需要更多的资源和配置,这可能会导致更高的实现成本。4.安全风险:LLDP可能会暴露有关网络设备的敏感信息,攻击者可能会利用这些信息。例如,攻击者可以使用LLDP来识别网络拓扑、设备类型和软件版本,以计划攻击或收集有关网络的情报。5.有限的向后兼容性:LLDP可能与不支持该协议的旧设备或遗留系统不向后兼容。这可能会限制其在新旧设备混合的环境中的使用。6.功能有限:LLDP仅提供有关网络设备的基本信息,如设备类型、软件版本和端口信息。对于一些需要更高级功能(例如服务质量(QoS)或网络策略实施)的应用程序来说,这可能是不够的。7.互操作性问题:尽管LLDP是一个开放的标准协议,但不同的供应商可能会以不同的方式实现它,这可能会导致互操作性的问题。这可能使得在来自不同供应商的设备之间交换信息或将LLDP与其他网络管理系统集成变得困难。
Frame format
在LLDP中,每个设备以以太网帧的形式以固定的间隔从其每个接口发送信息。每个帧包含一个LLDP数据单元(LLDPDU)。每个LLDPDU都是一个类型长度值(TLV)结构序列。EtherType字段设置为0x88cc。每个LLDP帧都以机箱ID、端口ID和TTL(生存时间)或跃点限制开始。该帧以一个名为LLDPDU结束的特殊TLV结束,其中类型和长度字段均为0。LLDP规范允许组织定义和编码自己的TLV。这些被称为组织特定TLV,它们以LLDP TLV类型值127开始。
LLDPDU types
LLDPDU有两种类型:普通LLDPDU和停机咨询LLDPU。普通LLDPDU将关于本地设备的管理信息提供给该设备的邻居。这是传输强制性和可选TLV的一个。同时,当端口被禁用、LLDP被禁用或交换机被重新启动时,LLDP关闭帧被传输到相邻单元,用信号表示LLDP信息不再有效。
LLDP消息的结构
LLDP通过称为LLDPDU的特定数据单元交换信息。这些数据单元由TLV组成,并且每个TLV字段对应于特定类型和长度。LLDP标准IEEE 802.1AB有三个TLV,它们在LLDP的开头是强制性的,顺序如下:
类型1=机箱ID(标识设备)
类型2=端口ID(标识端口)
类型3=生存时间(告诉接收设备接收的信息应保持有效的时间)
类型4=端口描述(显示有关端口的详细信息)
类型5=系统名称(显示设备的给定名称) 类型6=系统说明(显示软件版本) 类型7=系统功能(告诉设备的主要功能和功能) 类型8=管理地址(显示设备的IP或MAC地址)
在LLDPDU的末尾,以下TLV是强制性的:
类型0=LLDPDU结束(表示数据单元结束)
IEEE 802.1 Subtypes
static const value_string ieee_802_1_subtypes[] = {{0x01,"Port VLAN ID" },/* 802.1Q - D.2.1 */{0x02, "Port and Protocol VLAN ID" },/* 802.1Q - D.2.2 */{0x03, "VLAN Name" },/* 802.1Q - D.2.3 */{0x04, "Protocol Identity" },/* 802.1Q - D.2.4 */{0x05, "VID Usage Digest" },/* 802.1Q - D.2.5 */{0x06, "Management VID" },/* 802.1Q - D.2.6 */{0x07,"Link Aggregation" },/* 802.1Q - D.2.7 */{0x08,"Congestion Notification" },/* 802.1Q - D.2.8 */{0x09, "ETS Configuration" },/* 802.1Q - D.2.9 */{0x0A, "ETS Recommendation" },/* 802.1Q - D.2.10 */{0x0B, "Priority Flow Control Configuration" },/* 802.1Q - D.2.11 */{0x0C, "Application Protocol" },/* 802.1Q - D.2.12 */{0x0D, "EVB" },/* 802.1Q - D.2.13 */{0x0E, "CDCP" },/* 802.1Q - D.2.14 */{0x0F, "Port extension" },/* 802.1BR - B.2 */{0x10, "Application VLAN" },/* 802.1Q - D.2.15 */{0x11, "LRP ECP Discovery" },/* 802.1CS - C.2.1 */{0x12, "LRP TCP Discovery" },/* 802.1CS - C.2.2 */{0x13, "Congestion Isolation" },/* 802.1Qcz - D.2.15 */{0x14, "Topology Recognition" },/* 802.1Qcz - D.2.16 */{0, NULL }};
组织特定TLV
LLDP规范允许各种组织定义和编码他们自己的TLV。这些被称为组织特定TLV。所有特定于组织的TLV都以LLDP TLV类型值127开头。
Organizationally Specific TLV (Type = 127)
组织特定TLV的长度字段后面跟着3个八位位组(24位)的组织唯一标识符(OUI)值,该值后面跟着1个八位位位组的组织定义的子类型。
IEEE 802.3 Subtypes
static const value_string ieee_802_3_subtypes[] = {{0x01,"MAC/PHY Configuration/Status" },{0x02,"Power Via MDI" },{0x03,"Link Aggregation" },{0x04,"Maximum Frame Size" },{0x05,"EEE (Energy-Efficient Ethernet)" },{0x07,"IEEE 802.3br Additional Ethernet capabilities" },{0, NULL }};
Media 相关信息
LLDP-MED是LLDP的扩展。该协议专门用于支持IP语音(VOIP)应用。LLDP-MED能够在网络连接设备和媒体端点(如软电话、IP电话、VOIP网关和会议桥)之间进行网络发现。默认情况下,网络设备只发送LLDP数据包,直到它从端点设备接收到LLDP-MED数据包。然后,它将继续发送LLDP-MED数据包,直到它所连接的远程设备停止具有LLDP-MED能力为止。
/* Media Subtypes */static const value_string media_subtypes[] = {{1,"Media Capabilities" },{2,"Network Policy" },{3,"Location Identification" },{4,"Extended Power-via-MDI" },{5,"Inventory - Hardware Revision" },{6,"Inventory - Firmware Revision" },{7,"Inventory - Software Revision" },{8,"Inventory - Serial Number" },{9,"Inventory - Manufacturer Name" },{10,"Inventory - Model Name" },{11,"Inventory - Asset ID" },{0, NULL }};/* Media Class Values */static const value_string media_class_values[] = {{0,"Type Not Defined" },{1,"Endpoint Class I" },{2,"Endpoint Class II" },{3,"Endpoint Class III" },{4,"Network Connectivity" },{0, NULL }};/* Media Application Types */static const value_string media_application_type[] = {{0,"Reserved" },{1,"Voice" },{2,"Voice Signaling" },{3,"Guest Voice" },{4,"Guest Voice Signaling" },{5,"Softphone Voice" },{6,"Video Conferencing" },{7,"Streaming Video" },{8,"Video Signaling" },{0, NULL }};
LLDP操作模式
LLDP代理以以下三种模式中的任何一种模式操作:
仅传输模式:代理只能传输有关本地系统的功能和当前状态的信息。仅接收模式:代理只能接收有关远程系统的功能和当前状态的信息。传输和接收模式:代理可以传输本地系统的能力和状态信息,也可以接收远程系统的能力与状态信息。
无论何时传输倒计时计数器到期,或者如果LLDP信息已经改变,LLDP代理都会向启用LLDP的相邻设备发送LLDP帧。LLDP管理器获取MIB(管理信息库)内部的信息,并将其格式化为TLV并插入LLDPDU。当代理接收到此LLDPDU时,它会进行检查以确保它包含正确的强制TLV序列,然后验证可选TLV。如果出现错误,它将被删除。而有效的TLV存储在邻居数据库中。
LLDP(链路层发现协议)详解及C/C++代码实现
/* TLV globals */#define TLV_LLDPDU_END 0x00#define TLV_CHASSIS_ID 0x01#define TLV_PORT_ID 0x02#define TLV_TTL 0x03#define TLV_PORT_DESC 0x04#define TLV_SYSNAME 0x05#define TLV_SYS_DESC 0x06#define TLV_SYS_CAP 0x07#define TLV_MGMT_ADDR 0x08#define TLV_RESV_START 0x09 // Reserved Start#define TLV_RESV_END 0x7e // Reserved End#define TLV_ORG_SPEC 0x7f // Organization specific, we probably wont support any of these/* CHASSIS ID SUBTYPES */#define CHASSIS_ID_CHASSIS_COMPONENT 0x01#define CHASSIS_ID_INTERFACE_ALIAS 0x02#define CHASSIS_ID_PORT_COMPONANT 0x3#define CHASSIS_ID_MAC_ADDRESS 0x4#define CHASSIS_ID_NETWORK_ADDRESS 0x5#define CHASSIS_ID_INTERFACE_NAME 0x6#define CHASSIS_ID_LOCALLY_ASSIGNED 0x7/* PORT ID SUBTYPES */#define PORT_ID_INTERFACE_ALIAS 1#define PORT_ID_PORT_COMPONENT 2#define PORT_ID_MAC_ADDRESS3#define PORT_ID_NETWORK_ADDRESS 4#define PORT_ID_INTERFACE_NAME 5#define PORT_ID_AGENT_CIRCUIT_ID 6#define PORT_ID_LOCALLY_ASSIGNED 7/* SYSTEM CAPABILITIES */#define SYSTEM_CAPABILITY_OTHER1#define SYSTEM_CAPABILITY_REPEATER 2#define SYSTEM_CAPABILITY_BRIDGE 4#define SYSTEM_CAPABILITY_WLAN8#define SYSTEM_CAPABILITY_ROUTER 16#define SYSTEM_CAPABILITY_TELEPHONE 32#define SYSTEM_CAPABILITY_DOCSIS 64#define SYSTEM_CAPABILITY_STATION 128/* MANAGEMENT ADDRESS IANA TYPES *//* IANA Family Number Assignments *//* /assignments/address-family-numbers */#define IANA_RESERVED_LOW0#define IANA_IP1#define IANA_IP6 2#define IANA_NSAP 3#define IANA_HDLC 4#define IANA_BBN_1822 5#define IANA_802 6#define IANA_E_163 7#define IANA_E_164_ATM 8#define IANA_F_69 9#define IANA_X_121 10#define IANA_IPX 11#define IANA_APPLETALK 12#define IANA_DECNET_IV 13#define IANA_BANYAN_VINES 14#define IANA_E_164_NSAP15#define IANA_DNS 16#define IANA_DISTINGUISHED 17#define IANA_AS_NUMBER 18#define IANA_XTP_IPV4 19#define IANA_XTP_IPV6 20#define IANA_XTP_XTP 21#define IANA_FIBRE_PORT_NAME 22#define IANA_FIBRE_NODE_NAME 23#define IANA_GWID 24#define IANA_AFI_L2VPN 25// Everything from 26 to 65534 is Unassigned#define IANA_RESERVED_HIGH 65535/* End IANA Family Number Assignments */TLV *get_tlv(lldp_tlv_list *head, uint16_t tlv_type) {lldp_tlv_list * current = head;while(current->tlv != NULL) {if (current->tlv->type == tlv_type)return current->tlv;current = current->next;}return NULL;}void parse_lldp_packet(uint8_t *packet, lldp_tlv_list *head) {/** 在TLV上迭代。分解TLV并将其存储在链接列表中。应在调用此函数之前进行验证 */...do {tlv_header = (uint16_t *) &packet[calc_offset(tlv_offset)];current_tlv = (TLV *) calloc(1, sizeof(TLV));current_tlv->type = TLV_TYPE(htons(*tlv_header));current_tlv->length = TLV_LENGTH(htons(*tlv_header));if (current_tlv->length > 0) {current_tlv->data = (uint8_t *) calloc(1, current_tlv->length);memcpy(current_tlv->data, &packet[calc_offset(tlv_offset) + sizeof(*tlv_header)], current_tlv->length);} elsecurrent_tlv->data = NULL;tlv_offset += sizeof(*tlv_header) + current_tlv->length;printf("Pushing TLV type : %d\n", current_tlv->type);tlv_list_push(head, current_tlv);} while(current_tlv->type != 0);...}uint8_t * fetch_lldp_packet(char * ifname, time_t timeout) {...if(packet == NULL) {perror("Could not allocate memory for packet buffer");exit(EXIT_FAILURE);}memset(packet, 0, IP_MAXPACKET * sizeof(uint8_t));if ((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0){perror("socket() failed");exit (EXIT_FAILURE);}strncpy(ifr.ifr_name, ifname, strnlen(ifname, 20) + 1);ioctl(sock, SIOCGIFINDEX, &ifr);if(ifr.ifr_ifindex == 0) {fprintf(stderr, "Interface %s does not seem to exist\n", ifname);exit(EXIT_FAILURE);}saddr_ll.sll_ifindex = ifr.ifr_ifindex;saddr_ll.sll_family = AF_PACKET;printf("Binding to interface %s, if_index %d\n", ifname, saddr_ll.sll_ifindex);if ((bind(sock, (struct sockaddr *) &saddr_ll, sizeof(saddr_ll))) < 0) {perror("Bind failed");exit(EXIT_FAILURE);}ioctl(sock, SIOCGIFHWADDR, &ifr);memcpy(&mac_address, (void *) ifr.ifr_hwaddr.sa_data, 6);printf("HWADDR: %s\n", mac_address_fmt(mac_address));printf("Entering promiscuous mode on %s\n", ifname);ioctl(sock, SIOCGIFFLAGS, &ifr);ifr.ifr_flags |= IFF_PROMISC;ioctl(sock, SIOCSIFFLAGS, &ifr);recv_t(sock, packet, mac_address, timeout);printf("Disabling promiscuous mode\n");ifr.ifr_flags ^= IFF_PROMISC;ioctl(sock, SIOCSIFFLAGS, &ifr);printf("Closing socket\n");close(sock);return packet;}...int main(int argc, char **argv){...packet = (uint8_t *) malloc(IP_MAXPACKET * sizeof(uint8_t));packet_len = fread(packet, sizeof(uint8_t), IP_MAXPACKET, fd);if (packet_len < sizeof(ethernet_header)) {printf("Packet is not large enough, need at least %zu bytes, only read: %lu", sizeof(ethernet_header), packet_len);exit(EXIT_FAILURE);}tlv_list = tlv_list_create();...parse_lldp_packet(packet, tlv_list);...current = tlv_list;if (current->tlv == NULL) {log_info("No TLVs print");exit(EXIT_SUCCESS);}while (current->next != NULL) {print_tlv(current->tlv);current = current->next;}print_tlv(current->tlv);...tlv_list_destroy(tlv_list);...}
运行结果:
If you need the complete source code, please add the WeChat number (c17865354792)
总结
LLDP代表链路层发现协议。它是一个开放的IEEE标准(802.1AB)第2层协议。LLDP是CDP(思科发现协议)的开源替代方案,CDP也是一种设备发现协议,仅在思科制造的设备(路由器、网桥、访问服务器和交换机)上运行第2层(数据链路层)。
Welcome to follow WeChat official account【程序猿编码】
参考:1./ieee/802.1ABcu/7131/
2./c/dam/en/us/solutions/collateral/workforce-experience/digital-building/digital-building-partner-guide.pdf
3.rfc 4836
4./assignments/address-family-numbers