以太网基础 -- LLDP使用案例

LLDP使用技术报告

背景

链路层发现协议(Link Layer Discovery Protocol,LLDP)是一种网络协议,主要用于在以太网网络中发现相邻设备并与其交换信息。LLDP是一种开放的标准,由IEEE 802.1AB定义,能够跨多个厂商的设备进行互操作。它的主要功能是帮助网络管理员识别网络拓扑结构,便于管理和故障排除。

LLDP在网络设备之间周期性地发送和接收LLDP数据单元(LLDPDU),这些数据单元包含设备的配置信息和能力。每个LLDPDU由多个TLV(Type-Length-Value)字段组成,每个字段包含特定类型的信息。

最常用的字段

LLDPDU中的常用TLV字段包括:

  1. Chassis ID TLV:识别设备底盘的唯一标识符,通常是设备的MAC地址。
  2. Port ID TLV:标识发送LLDPDU的端口,可以是端口的MAC地址或端口号。
  3. Time to Live (TTL) TLV:指定LLDP信息的生存时间,单位为秒。
  4. Port Description TLV:描述端口的信息,如端口名称或类型。
  5. System Name TLV:设备的主机名。
  6. System Description TLV:设备的详细描述,包括制造商、型号和操作系统版本等信息。
  7. System Capabilities TLV:设备的功能和当前启用的功能,如交换机、路由器等。
  8. Management Address TLV:设备的管理地址,可以是IPv4或IPv6地址。

Chassis Subtype 的字段

Chassis Subtype TLV 包含以下几个字段:

  1. TLV 类型(Type):表示这是一个 Chassis Subtype TLV。
  2. TLV 长度(Length):表示该 TLV 的总长度。
  3. 子类型(Subtype):指定 Chassis Subtype 的类型,常见的子类型包括:
    • 1:MAC 地址:表示使用设备的 MAC 地址作为标识。
    • 2:网络地址:使用网络地址(如 IP 地址)作为标识。
    • 3:主机名:使用设备的主机名作为标识。
    • 4:UUID:使用通用唯一标识符(UUID)作为标识。
    • 5:描述:使用一个描述性字符串作为标识。
  4. 子类型值(Subtype Value):具体的标识值,根据子类型不同,这个值也不同。例如,如果子类型是 MAC 地址,那么子类型值就是一个具体的 MAC 地址。

使用示例

假设我们有一台设备,其 MAC 地址为 00:1A:2B:3C:4D:5E。它将使用 Chassis Subtype TLV 来广播它的 MAC 地址。

TLV 结构

  • Type: 1 (Chassis Subtype)
  • Length: 7
  • Subtype: 1 (MAC Address)
  • Subtype Value: 00:1A:2B:3C:4D:5E

自定义字段

除了标准的TLV字段外,LLDP还允许使用自定义TLV字段。这些字段可以携带特定的厂商信息或特定应用的配置信息。定义自定义TLV字段时,需要确保Type值在特定范围内,以避免与标准字段冲突。

实验设备

实验设备包括一台PC和一台嵌入式Linux设备。在本次实验中,我们将演示如何在PC端使用Python Qt实现设备发现功能,并在嵌入式Linux设备上使用C语言发送设备信息。

PC端代码 (Python Qt)

import sys
import threading
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QTextEdit
from scapy.all import *
from scapy.layers.l2 import Ether

# Define LLDP layer
class LLDPDU(Packet):
    name = "LLDPDU"
    fields_desc = [
        ByteField("type", 0),
        ByteField("length", 0),
        StrLenField("value", "", length_from=lambda pkt:pkt.length)
    ]

class SnifferThread(QObject):
    packet_received = pyqtSignal(str)

    def start_sniffing(self):
        thread = threading.Thread(target=self.sniff_packets)
        thread.daemon = True
        thread.start()

    def sniff_packets(self):
        print("Starting packet sniffing...")
        sniff(filter="ether proto 0x88cc", prn=self.process_packet, store=0)
        print("Packet sniffing stopped.")

    def process_packet(self, packet):
        if packet.haslayer(Ether) and packet[Ether].type == 0x88cc:
            info = f"LLDP packet received from {packet.src}\n"
            payload = bytes(packet.payload)
            i = 14  # skip Ethernet header
            while i < len(payload):
                tlv_type = (payload[i] >> 1) & 0x7F
                tlv_len = (payload[i] & 0x01) << 8 | payload[i + 1]
                tlv_value = payload[i + 2: i + 2 + tlv_len]
                info += f"TLV Type: {tlv_type}, Length: {tlv_len}, Value: {tlv_value}\n"
                i += 2 + tlv_len
            self.packet_received.emit(info)

class LLDPDiscoveryApp(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("LLDP Discovery")
        self.setGeometry(100, 100, 600, 400)

        self.textEdit = QTextEdit(self)
        self.setCentralWidget(self.textEdit)

        self.sniffer_thread = SnifferThread()
        self.sniffer_thread.packet_received.connect(self.display_packet_info)
        self.sniffer_thread.start_sniffing()

    def display_packet_info(self, info):
        self.textEdit.append(info)

def main():
    app = QApplication(sys.argv)
    window = LLDPDiscoveryApp()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()


嵌入式Linux设备端代码 (C语言)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
//#include <netinet/if_ether.h>
//#include <netpacket/packet.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <sys/ioctl.h>

#define MAX_TLV_SIZE 512
#define LLDP_MULTICAST_ADDR "01:80:C2:00:00:0E"

typedef struct {
    uint8_t type;
    uint8_t length;
    uint8_t value[MAX_TLV_SIZE];
} TLV;

void addTLV(uint8_t *buffer, size_t *offset, uint8_t type, uint8_t length, uint8_t *value) {
    buffer[*offset] = (type << 1) | ((length >> 8) & 0x01);
    buffer[*offset + 1] = length & 0xFF;
    memcpy(&buffer[*offset + 2], value, length);
    *offset += length + 2;
}

void sendLLDPDU(char *interface) {
    int sockfd;
    struct ifreq ifr;
    struct sockaddr_ll sa;
    uint8_t lldpdu[MAX_TLV_SIZE];
    uint8_t ether_frame[MAX_TLV_SIZE + ETH_HLEN];
    size_t offset = 0;

    // Create a raw socket
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // Get the interface index
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1);
    if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
        perror("ioctl");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    int ifindex = ifr.ifr_ifindex;

    // Get the MAC address of the interface
    if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
        perror("ioctl");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    uint8_t *src_mac = (uint8_t *)ifr.ifr_hwaddr.sa_data;

    // Set up destination MAC address
    uint8_t dst_mac[6];
    sscanf(LLDP_MULTICAST_ADDR, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
           &dst_mac[0], &dst_mac[1], &dst_mac[2],
           &dst_mac[3], &dst_mac[4], &dst_mac[5]);

    // Construct LLDPDU
    // Add Chassis ID TLV
    uint8_t chassisID[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
    addTLV(lldpdu, &offset, 1, sizeof(chassisID), chassisID);

    // Add Port ID TLV
    uint8_t portID[] = { 'E', 't', 'h', '0' };
    addTLV(lldpdu, &offset, 2, sizeof(portID), portID);

    // Add TTL TLV
    uint8_t ttl[] = { 0x00, 0x78 };  // 120 seconds
    addTLV(lldpdu, &offset, 3, sizeof(ttl), ttl);

    // Add End TLV
    addTLV(lldpdu, &offset, 0, 0, NULL);

    // Construct Ethernet frame
    memcpy(ether_frame, dst_mac, 6);
    memcpy(ether_frame + 6, src_mac, 6);
    ether_frame[12] = 0x88;
    ether_frame[13] = 0xcc;
    memcpy(ether_frame + ETH_HLEN, lldpdu, offset);

    // Set up sockaddr_ll
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = AF_PACKET;
    sa.sll_ifindex = ifindex;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_halen = ETH_ALEN;
    memcpy(sa.sll_addr, dst_mac, 6);

    // Send the packet
    if (sendto(sockfd, ether_frame, offset + ETH_HLEN, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        perror("sendto");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    close(sockfd);
}

int main() {
    char *interface = "eth0";
    sendLLDPDU(interface);
    return 0;
}

总结

通过使用LLDP协议,可以有效地发现网络中的相邻设备并交换信息,这对于网络管理和故障排除具有重要意义。本次实验展示了如何在PC和嵌入式设备上实现LLDP功能,希望对相关技术的学习和应用有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值