Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >LLDP在ODL中的实现及源码分析(一)

LLDP在ODL中的实现及源码分析(一)

作者头像
SDNLAB
发布于 2018-03-30 09:28:19
发布于 2018-03-30 09:28:19
1.7K00
代码可运行
举报
文章被收录于专栏:SDNLABSDNLAB
运行总次数:0
代码可运行

本文中主要是与大家分享一下LLDP在ODL中的实现以及其源码分析,主要内容涉及ODL控制器中LLDP帧的产生及发送。文章都是个人理解,希望能够帮助到大家,更希望可以一起讨论看法不一的地方。

1 LLDP简介

ODL使用LLDP实现链路检测。LLDP,即链路层发现协议,是一种数据链路层协议,网络设备可以通过在本地网络中发送LLDPDU(Link Layer Discovery Protocol Data Unit)来通告其他设备自身的状态,是一种能够使网络中的设备互相发现并通告状态、交互信息的协议。

LLDP的以太网类型为0x88cc,一个标准的LLDP帧格式如图1:

图1

LLDPDU格式如图2:

图2

2 ODL中的LLDP

在ODL中,发送LLDP帧的工作由控制器来完成。如图3所示:

图3

在上图中,LLDP帧在控制器中生成,通过openflow协议packet_out消息发给交换机1,交换机1收到来自控制器的LLDP帧后,会转发给直邻交换机2,其直邻交换机2收到LLDP帧后,通过openflow的packet_in消息发给控制器,控制器据此做链路检测。

3 LLDP帧的产生和发送

LLDP帧产生和发送的源码位于ODL中openflowplugin子项目中,具体位置为:openflowplugin/applications/lldp-speaker,其源码目录如下图4:

图4

该模块主要分为两个功能:

♣ 监听MA-SAL中的inventory data tree中的端口信息,学习并检测端口信息的变化,产生与端口一一对应的LLDP帧,然后将其存入本地哈希nodeConnectorMap中,此哈希表键为端口IID,值为端口对应LLDP帧;

♣ 周期性遍历本地哈希表nodeConnectorMap,将哈希表中的LLDP帧通过packet_out消息发往相应的交换机。

3.1 监听端口信息

源码位于NodeConnectorInventoryEventTranslator.java中,首先构造监听节点的IID,源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//监听inventory data tree 下openflow的端口状态
    private static final InstanceIdentifier<State> II_TO_STATE 
        = InstanceIdentifier.builder(Nodes.class)
            .child(Node.class)
            .child(NodeConnector.class)
            .augmentation(FlowCapableNodeConnector.class)
            .child(State.class)
            .build();
    //监听inventory data tree 下openflow的端口
    private static final InstanceIdentifier<FlowCapableNodeConnector> II_TO_FLOW_CAPABLE_NODE_CONNECTOR
        = InstanceIdentifier.builder(Nodes.class)
            .child(Node.class)
            .child(NodeConnector.class)
            .augmentation(FlowCapableNodeConnector.class)
            .build();

由IID在MD-SAL中注册监听器,实现数据监听,源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//监听端口
dataChangeListenerRegistration = dataBroker.registerDataChangeListener(
                LogicalDatastoreType.OPERATIONAL,
                II_TO_FLOW_CAPABLE_NODE_CONNECTOR,
                this, AsyncDataBroker.DataChangeScope.BASE);
//监听端口状态
        listenerOnPortStateRegistration = dataBroker.registerDataChangeListener(
                LogicalDatastoreType.OPERATIONAL,
                II_TO_STATE,
                this, AsyncDataBroker.DataChangeScope.SUBTREE);

当监听到端口信息变化之后,需要依据端口状态,判定是否生成相应的LLDP帧。若需要生成LLDP帧,则生成之后存入本地哈希表nodeConnectorMap中,具体源码位于函数onDataChanged之中。即如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change)  {...}

其中对于change.getCreatedData(),其源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
            InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =
                    entry.getKey().firstIdentifierOf(NodeConnector.class);
            if (compareIITail(entry.getKey(),II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
                FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue();
                if (!isPortDown(flowConnector)) {
                    notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);  //如果端口开启
                } else {
                    iiToDownFlowCapableNodeConnectors.put(nodeConnectorInstanceId, flowConnector); //
                }
            }
        }

代码分析:对于change.getCreatedData(),即为datastore中新增端口,首先调用函数isPortDown(flowConnector)判断端口此时状态:若返回true,端口为UP,直接生成端口对应LLDP帧,然后存入哈希表nodeConnectorMap;若返回false,端口为DOWN,则将端口存入哈希表iiToDownFlowCapableNodeConnectors,之后如果端口状态变为UP,依然会生成对应LLDP帧,存入哈希表nodeConnectorMap中。

此处说明一下,端口为down,一般指端口或者端口处link存在问题,无法正常转发数据包。

对于change.getUpdatedData(),其源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Iterate over updated node connectors (port down state may change)
        for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData.entrySet()) {
            InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =
                    entry.getKey().firstIdentifierOf(NodeConnector.class);
            if (compareIITail(entry.getKey(),II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
                FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) entry.getValue();
                if (isPortDown(flowConnector)) {
                    notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
                } else {
                    notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);
                }
            } else if (compareIITail(entry.getKey(),II_TO_STATE)) {
                FlowCapableNodeConnector flowNodeConnector = iiToDownFlowCapableNodeConnectors.get(nodeConnectorInstanceId);
                if (flowNodeConnector != null) {
                    State state = (State)entry.getValue();
                    if (!state.isLinkDown()) {
                        FlowCapableNodeConnectorBuilder flowCapableNodeConnectorBuilder = new FlowCapableNodeConnectorBuilder(flowNodeConnector);
                        flowCapableNodeConnectorBuilder.setState(state);
                        notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowCapableNodeConnectorBuilder.build());
                        iiToDownFlowCapableNodeConnectors.remove(nodeConnectorInstanceId);
                    }
                }
            }
        }

代码分析:对于更新数据为端口,调用函数isPortDown(flowConnector),判断端口状态:若端口为UP,则直接调用函数notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector);存入哈希表nodeConnectorMap;若端口为DOWN,则调用函数notifyNodeConnectorDisappeared(nodeConnectorInstanceId),从哈希表nodeConnectorMap中移除。

若更新数据为端口状态,则检查其对应端口是否在哈希表iiToDownFlowCapableNodeConnectors中,若存在且其端口链路状态为true,则生成此端口对应LLDP帧,存入哈希表nodeConnectorMap中,并且将其从哈希表iiToDownFlowCapableNodeConnectors中移出。

对应change.getRemovedPaths(),源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Iterate over removed node connectors
        for (InstanceIdentifier<?> removed : change.getRemovedPaths()) {
            if (compareIITail(removed,II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) {
                InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = removed.firstIdentifierOf(NodeConnector.class);
                notifyNodeConnectorDisappeared(nodeConnectorInstanceId);
            }
        }
    }

代码分析:对于删除的端口,则直接将其从哈希表nodeConnectorMap中删除即可。

3.2 LLDP帧生成

根据前文分析,当检测到新的端口时,会调用函数notifyNodeConnectorAppeared先生成对应LLDP帧,然后存入哈希表nodeConnectorMap中。其中生成LLDP帧源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Generate packet with destination switch and port
        TransmitPacketInput packet = new TransmitPacketInputBuilder()
                .setEgress(new NodeConnectorRef(nodeConnectorInstanceId))
                .setNode(new NodeRef(nodeInstanceId))
                .setPayload(LLDPUtil.buildLldpFrame(
                        nodeId, nodeConnectorId, srcMacAddress, outputPortNo, addressDestionation)).build();

代码分析:其中负责生成LLDP以太网帧的函数为

LLDPUtil.buildLldpFrame(nodeId, nodeConnectorId, srcMacAddress, outputPortNo, addressDestionation)

此函数在LLDPUtil.java文件中实现,主要逻辑是依次生成LLDPDU中的ChassisID TLV,PortID TLV,TTL TLV等字段,然后添加LLDP帧的以太网包头。由于代码逻辑比较清晰,此处不再详细叙述。

3.3 LLDP帧周期性发送

ODL中会周期性的发送LLDP帧,去检测网络设备间link的有效性。LLDP帧周期性发送在LLDPSpeaker.java中实现,采用以下代码实现周期性的执行任务:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private final ScheduledExecutorService scheduledExecutorService;
private ScheduledFuture<?> scheduledSpeakerTask;
scheduledSpeakerTask = this.scheduledExecutorService.scheduleAtFixedRate(this, LLDP_FLOOD_PERIOD,LLDP_FLOOD_PERIOD, TimeUnit.SECONDS);

其中LLDP_FLOOD_PERIOD值为5,即每过5秒会将哈希表nodeConnectorMap中所有的LLDP帧发送给相应交换机。

函数setOperationalStatus将会设置LLDP机制的工作状态,RUN表示运行状态,即会周期性的发送LLDP帧,进行链路检测;STANDBY表示待命状态,即没有开启LLDP链路检测,不会周期性的发送LLDP帧。源码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void setOperationalStatus(final OperStatus operationalStatus) {
        LOG.info("Setting operational status to {}", operationalStatus);
        this.operationalStatus = operationalStatus;
        if (operationalStatus.equals(OperStatus.STANDBY)) {
            scheduledSpeakerTask.cancel(false);  //参数false表示,如果任务在执行,则等待任务结束后再取消;若为参数true表示立即取消
        } else if (operationalStatus.equals(OperStatus.RUN)) {
//如果没有任务,或者任务已经取消,则开启定时任务
            if (scheduledSpeakerTask == null || scheduledSpeakerTask.isCancelled()) {
                scheduledSpeakerTask = this.scheduledExecutorService.scheduleAtFixedRate(this, LLDP_FLOOD_PERIOD,
                        LLDP_FLOOD_PERIOD, TimeUnit.SECONDS);           
 }
        }
}

遍历哈希表nodeConnectorMap,发送LLDP帧的操作在run()函数完成中,其中是调用rpc方法transmit-packet发送LLDP帧,此rpc在packet-processing.yang中被定义。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
    public void run() {
        if (OperStatus.RUN.equals(operationalStatus)) {
            LOG.debug("Sending LLDP frames to {} ports...", nodeConnectorMap.keySet().size()); //发送端口的数量
            for (InstanceIdentifier<NodeConnector> nodeConnectorInstanceId : nodeConnectorMap.keySet()) {
                NodeConnectorId nodeConnectorId = InstanceIdentifier.keyOf(nodeConnectorInstanceId).getId();
                LOG.trace("Sending LLDP through port {}", nodeConnectorId.getValue());  //发送端口的端口号
                packetProcessingService.transmitPacket(nodeConnectorMap.get(nodeConnectorInstanceId)); //调用rpc方法transmit-packet发送LLDP包
            }
        }
    }

由于篇幅有限,源码只选择重要部分粘贴,完整源码请从以下链接下载:

https://github.com/opendaylight/openflowplugin/tree/stable/beryllium。

四、总结

上文已经具体分析了LLDP帧的生成和周期发送的逻辑以及关键代码。当LLDP帧发送,经过转发再次被发给控制器时,控制器需要从中提取出链路信息,并以此检测已经发现的链路是否老化,以及是否发现新的链路。这部分的源码将在下一篇文章《LLDP在ODL中的实现及源码分析(二)》中分析。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 SDNLAB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SDN开发笔记(八):L2switch源码分析(下)
L2switch架构概述 L2switch作为odl的网络基础模块,依赖于openflowplugin模块的消息,同时也调用它的流表下发功能,整个L2switch模块的架构可以粗略的理解为以下架构图。
SDNLAB
2018/03/30
7760
SDN开发笔记(八):L2switch源码分析(下)
源码解读ODL的MAC地址学习(一)
1 简介 我们知道同一子网中主机之间互相传送信息需要用到MAC地址,而我们第一次发送信息的时候只有IP地址而没有MAC地址,所以我们就要进行MAC地址自学习。 交换机中的MAC地址自学习是指在交换机中有一个MAC地址与交换机每个接口的对应表,每当有数据包经过交换机转发的时候,如果它的表中没有这个MAC地址的对应关系就会往所有端口转发数据包,当目标机从某个端口返回信息的时候它就知道了这个MAC地址对应的哪个端口,于是会把这个对应关系加入表中,这个过程就是交换机的MAC地址自学习。 2 ODL中MAC地址原理
SDNLAB
2018/03/30
2.2K0
源码解读ODL的MAC地址学习(一)
源码解读ODL与OpenFlow交换机建立过程
编者按:OpenDaylight两大技术特色:1.采用了OSGi框架;2.引入了SAL,而今天我们主要介绍服务抽象层(SAL)适配的南向协议之一OF协议模块。 OF协议模块启动与消息处理 OSGi框架
SDNLAB
2018/04/03
1.1K0
源码解读ODL与OpenFlow交换机建立过程
CDP和LLDP「建议收藏」
启动CDP进行思科设备发现的时候,我们可以在相关的设备上查看到每台邻接设备的信息:
全栈程序员站长
2022/08/31
1.3K0
CDP和LLDP「建议收藏」
如何通过LLDP获取网络拓扑?
cloudman 主要专注于云计算方向,openstack研发 热爱技术和生活 导 言 在某些应用场景中,需要获取网络中的拓扑信息,比如服务器网口和交换机的连接关系。为了满足这个要求,可以利用lldp协议,来实现该要求。 1 LLDP协议 LLDP(Link Layer Discovery Protocol)链路层发现协议,协议设计的主要目的是进行信息的通告,进而获得关于网络拓扑以及相关管理配置信息。这是一个二层协议,它提供了一种标准方式来发现链路连接关系的能力,使得接入网络的一台设备可以将其主要
腾讯云TStack
2019/12/10
9.1K0
如何通过LLDP获取网络拓扑?
网络协议之LLDP
一、协议简介 为什么会出现LLDP? 随着网络技术的发展,接入网络的设备的种类越来越多,配置越来越复杂,来自不同设备厂商的设备也往往会增加自己特有的功能,这就导致在一个网络中往往会有很多具有不同特性的、来自不同厂商的设备,为了方便对这样的网络进行管理,就需要使得不同厂商的设备能够在网络中相互发现并交互各自的系统及配置信息。 LLDP(Link Layer Discovery Protocol,链路层发现协议)就是用于这个目的的协议。 它提供了一种标准的链路层发现方式,可以将本端设备的的主要能力、管理地址、设备标识、接口标识等信息组织成不同的TLV(Type/Length/Value,类型/长度/值),并封装在LLDPDU(Link Layer Discovery Protocol Data Unit,链路层发现协议数据单元)中发布给与自己直连的邻居,邻居收到这些信息后将其以标准MIB(Management Information Base,管理信息库)的形式保存起来,以供网络管理系统查询及判断链路的通信状况。 二、基本概念 LLDP报文封装有两种格式,一是ethernet II另外一个是SNAP。
全栈程序员站长
2022/09/02
3K0
网络协议之LLDP
源码解读ODL的MAC地址学习(二)
1 简介 上一篇文章(源码解读ODL的MAC地址学习(一))已经分析了MAC地址学习中的ARP请求的部分源码,下面将接着上一篇文章,介绍一下ARP响应和生成流表的源码。设想的场景不变,如下图所示: 2
SDNLAB
2018/03/30
1.1K0
源码解读ODL的MAC地址学习(二)
ODL碳版本模块开发及流程梳理
文章主要基于ODL碳版本,进行简单插件的构建、安装、部署,以一个插件开发为例,介绍ODL新版本开发过程中的一些具体问题。 一、碳版本简易开发流程 1.1 开发环境搭建 1.安装java1.8以上环境,
SDNLAB
2018/03/29
1.9K0
ODL碳版本模块开发及流程梳理
SDN开发笔记(七):L2switch源码分析(上)
前言 一般按照odl官方文档或者wiki安装L2switch组件会采用在karaf控制台上输入feature:install odl-l2switch-all命令,该命令是将L2switch组件全部安
SDNLAB
2018/03/30
1K0
SDN开发笔记(七):L2switch源码分析(上)
SDN中的LLDP和Openflow协议[通俗易懂]
OpenFlow交换机把传统网络中,完全由交换机/路由器控制的报文转换为由交换机和控制器来共同完成数据的转发操作,从而实现数据的转发与路由控制的分离。控制器则通过事先规定好的接口操作OpenFlow交换机中的流表,从而达到数据转发的目的。
全栈程序员站长
2022/08/26
1.7K0
SDN中的LLDP和Openflow协议[通俗易懂]
ONOS 中的LLDP协议,用到一个固定的MAC
使用ONOS+openflow 硬件交换机的环境中,在交换机上可以看到02-EB-9F-67-C9-42 这个MAC及相关流表。其并不是某个交换机、host的MAC,而是ONOS LLDP app中定义的一个源MAC,用于ONOS的链路发现功能。
全栈程序员站长
2022/09/02
5600
ODL源码分析之OpenFlowjava框架分析及hello消息流程
基于ODL开发已经有一段时间了,对于一个全新的平台,总是喜欢每隔一段时间就总结一番,本篇算是第一篇吧。下面会介绍以下内容: 1.openflow服务注册 2.netty服务创建 3.序列化、反序列化工作原理 4.从hello到of-session 一、前提 Openflowjava是基于netty进行的开发,在阅读Openflowjava的源码的时候需要简单了解一下netty原理。 Openflowjava是一个独立的线程,功能入口函数是SwitchConnectionProviderImpl.jav
SDNLAB
2018/04/02
1.2K0
ODL源码分析之OpenFlowjava框架分析及hello消息流程
Ryu:如何在LLDP中添加自定义LLDPDU
在许多实验场景中,都需要使用链路发现协议(LLDP)来发现链路,从而构建网络拓扑。然而LLDP协议不仅仅可以用来发现拓扑,也可以用于时延检测等业务。LLDP通过添加对应的TLV格式的LLDPDU(LL
SDNLAB
2018/04/02
2.8K0
Ryu:如何在LLDP中添加自定义LLDPDU
ODL源码分析之flowmod下发流程
上一篇简单分析了openflowjava到openflowplugin(介绍的hello消息),本篇介绍如何从openflowplugin到openflowjava,即介绍flowmod消息。 一、
SDNLAB
2018/04/02
7860
ODL源码分析之flowmod下发流程
OpenDaylight VTN源码及架构分析
VTN是opendaylight中负责租户隔离的工程,最近对源码和架构研究了一段时间,现将总结如下。 从VTN架构图我们可以看出,VTN共分为两个模块:VTN Manager和VTN Coordina
SDNLAB
2018/04/03
1.4K0
OpenDaylight VTN源码及架构分析
LLDP协议、STP协议 笔记
随着网络技术的发展,接入网络的设备的种类越来越多,配置越来越复杂,来自不同设备厂商的设备也往往会增加自己特有的功能,这就导致在一个网络中往往会有很多具有不同特性的、来自不同厂商的设备,为了方便对这样的网络进行管理,就需要使得不同厂商的设备能够在网络中相互发现并交互各自的系统及配置信息。
全栈程序员站长
2022/09/05
1.5K0
LLDP协议、STP协议 笔记
SDN中”软件”如何定义”网络”
SDN(Software Defined Network)软件定义网络,字面释义都说了是“软件”来定义“网络”,但有心之人会想:这个“软件”到底是如何定义了我们所熟知的“网络”?字字珠玑,今天就来扒一
SDNLAB
2018/04/03
1.2K0
SDN中”软件”如何定义”网络”
OpenDaylight Lithium版本简单应用及流表操作指南
OpenDaylight(以下简写为ODL)的Lithium(锂)版本的最新版Lithium-SR2已经与2015年10月8日发布,具体详情可参考ODL官网。Lithium(锂)版本发布至今已经发布了二个版本,即Lithium-SR1与Lithium-SR2。下载链接地址为https://www.opendaylight.org/software/downloads/lithium-sr2。官网中分别共享了版本、安装向导、用户向导、开发者向导手册,可进行下载学习。 1 OpenDayLight的简单应用 1
SDNLAB
2018/04/03
2.4K0
OpenDaylight Lithium版本简单应用及流表操作指南
脱坑神器,让你一步了解ODL控制器集群
一、控制器集群基本知识 1.1 Consensus一致性 Consensus一致性是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服务器可能会崩溃或变得不可靠,它就不能和其他服务器达成一致状态。这样就需要一种Consensus协议,一致性协议是为了确保容错性,也就是即使系统中有一两个服务器宕机,也不会影响其处理过程。 为了以容错方式达成一致,我们不可能要求所有服务器100%都达成一致状态,只要超过半数的大多数服务器达成一致就可以了,假设有N台服务器,N/2 +1就超过半数,代表
SDNLAB
2018/03/29
1.6K0
脱坑神器,让你一步了解ODL控制器集群
LLDP简介
目前,网络设备的种类日益繁多且各自的配置错综复杂,为了使不同厂商的设备能够在网络中相互发现并交互各自的系统及配置信息,需要有一个标准的信息交流平台。
全栈程序员站长
2022/09/01
3.3K0
相关推荐
SDN开发笔记(八):L2switch源码分析(下)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验