接下来,看看openlldp的源代码src目录:
这其中,platform目录包含了各种不同平台收发报文和获取网络接口信息的接口,主要是bpf和linux的。tlv模块包含了lldpdu中报文的创建,验收,销毁等等工作。lldp_debug模块包含了调试信息打印接口。lldp_neighbor模块包含了获取系统信息以及提取lldp邻居信息接口。lldpneighbors模块为查看邻居信息的小工具。msap模块则包含了邻居信息的创建,更新和增加接口。rx_sm模块和tx_sm模块为接收和发送端口状态机的实现,lldp_main模块通过对于定时对接收和发送状态机接口的调用实现lldp协议的运转。
各种数据结构定义如下:
struct lldp_port { /*端口数据结构为链表*/ struct lldp_port *next; int socket; // The socket descriptor for this interface. /*该接口的发送接收所使用到的套接字*/ char *if_name; // The interface name. /*网卡接口名*/ uint32_t if_index; // The interface index. /*网卡索引*/ uint32_t mtu; // The interface MTU. /*mtu*/ uint8_t source_mac[6]; /*源MAC地址,也即本机网卡的MAC地址*/ uint8_t source_ipaddr[4]; /*源IP地址,本机的IP地址*/ struct lldp_rx_port rx; /*发送端口*/ struct lldp_tx_port tx; /*接收端口*/ uint8_t portEnabled; /*端口使能*/ uint8_t adminStatus; /*端口状态*/ /* I'm not sure where this goes... the state machine indicates it's per-port */ uint8_t rxChanges; // I'm really unsure about the best way to handle this... uint8_t tick; time_t last_tick; struct lldp_msap *msap_cache; // 802.1AB Appendix G flag variables. uint8_t auto_neg_status; uint16_t auto_neg_advertized_capabilities; uint16_t operational_mau_type; };
每个网络接口都对应了这样一个数据结构,它用来存储网络接口的接口名,接口索引,mac地址,创建的套接字描述符,和邻居信息指针(这里问lldp_msap)以及最为重要的接收和发送端口缓存(具体为lldp_rx_port结构体和lldp_tx_port结构体)等等。
struct lldp_tx_port { uint8_t *frame; /**< The tx frame buffer */ uint64_t sendsize; /**< The size of our tx frame */ uint8_t state; /**< The tx state for this interface */ uint8_t somethingChangedLocal; /**< IEEE 802.1AB var (from where?) */ uint16_t txTTL;/**< IEEE 802.1AB var (from where?) */ struct lldp_tx_port_timers timers; /**< The lldp tx state machine timers for this interface */ struct lldp_tx_port_statistics statistics; /**< The lldp tx statistics for this interface */ };
该结构体为存储发送缓存以及发送端口状态机相关参数。其中,frame为缓存发送报文的指针,sendsize为待发送的缓存字节数。其他一些参数则是和tx状态机相关的变量。它们在收发报文时,被改变,从而影响了发送状态机的运转轨迹。somethingChangedLocal在本端信息改变时被置位。它被置一标志着lldp开启快速发送机制,会将本端信息快速的传递给直连邻居。
struct lldp_rx_port { uint8_t *frame; ssize_t recvsize; uint8_t state; uint8_t badFrame; uint8_t rcvFrame; uint8_t rxInfoAge; uint8_t somethingChangedRemote; uint8_t tooManyNeighbors; struct lldp_rx_port_timers timers; struct lldp_rx_port_statistics statistics; };这个结构体和上面的类似,只不过这个缓存的是接收报文。somethingChangedRemote为接收邻居信息后,发现邻居信息有改变则将其置一。
struct lldp_msap { struct lldp_msap *next; uint8_t *id; uint8_t length; struct lldp_tlv_list *tlv_list; // XXX Revisit this // A pointer to the TTL TLV // This is a hack to decrement // the timer properly for // lldpneighbors output struct lldp_tlv *ttl_tlv; /* IEEE 802.1AB MSAP-specific counters */ uint16_t rxInfoTTL; };
这个数据结构主要是存储一条邻居信息。多个邻居信息,则以链表的形式组织在lldp_port结构体中。id为lldpdu中必填的tlv字段的chassis subtype id和 port subtype id组成。基本上不同的邻居信息会有不同的lldp_msap id。length为经过格式化之后的全部lldp报文tlv长度。rxInfoTTL则为老化时间。tlv_list为存储tlv的链表,每个节点是一个tlv。该tlv经过了“格式化”而非lldpdu中的tlv。只有经过了什么样的格式化,后续会讲述。
struct lldp_tlv_list { struct lldp_tlv_list *next; struct lldp_tlv *tlv; };
tlv为一条lldp的tlv,多个tlv被组织为链表的形式。
struct lldp_tlv { uint8_t type; uint16_t length; uint8_t *info_string; };
tlv的数据结构定义,严格的T(type) L(length)V(value)形式。
所以,总的来说,该系统中关键数据结构的组织如下图,就是链表套链表,链表再套链表的形式。