Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Ryu:如何在LLDP中添加自定义LLDPDU

Ryu:如何在LLDP中添加自定义LLDPDU

作者头像
SDNLAB
发布于 2018-04-02 05:58:13
发布于 2018-04-02 05:58:13
2.7K00
代码可运行
举报
文章被收录于专栏:SDNLABSDNLAB
运行总次数:0
代码可运行

在许多实验场景中,都需要使用链路发现协议(LLDP)来发现链路,从而构建网络拓扑。然而LLDP协议不仅仅可以用来发现拓扑,也可以用于时延检测等业务。LLDP通过添加对应的TLV格式的LLDPDU(LLDP数据单元)来携带对应的信息,从而为上层业务提供信息支撑。为实现LLDP数据单元的拓展,本文将以Ryu控制器为例,介绍如何添加自定义的LLDPDU,从而满足多种业务的需求。

添加自定义LLDPDU其实只需修改ryu/lib/packet/lldp.py即可,但是由于该文件仅定义了LLDP的相关类,如何使用还需要其他文件去调用,所以还需要其他的修改步骤。具体步骤将在文章后续介绍。

修改lldp.py文件

ryu/lib/packet/lldp.py文件是Ryu控制器中关于LLDP协议数据类的描述,其中定义了如LLDPBasicTLV类等重要的报文类。 以添加发送时间戳的TLV为例,我们需要完成TLV类型号的声明,以及TLV类的定义。

在文件开头处有关于LLDP TLV类型的声明,所以首先我们需要添加一个新的类型:LLDP\_TLV\_SEND\_TIME,其类型号为11。

然后设计此类型的LLDPDU格式,其格式仅包含一个长度为8字节的Double类型的时间戳数据。如何完成类的描述,可以参考TTL类,具体代码如下。

TimeStamp类中定义了该LLDPDU的格式,初始化函数以及序列化函数。

修改switches.py

完成LLDPDU的定义之后,还需要在某文件中对其进行初始化构造。如果另外重新编写一个LLDP的构造、发送以及接受解析模块,那么则需要重新写许多代码,所以此处推荐直接修改Ryu/topology/switches.py文件。

switches.py文件中的LLDPPacket类完成了LLDP数据包的初始化和序列化实现。

该类的lldp\_packet方法可以构造LLDP数据包,并返回序列化之后的数据。在此函数中,我们需要添加timestamp的TLV。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
在lldp\_parse方法中,需将获取到的字节流的数据解析为对应的LLDP数据包。由于在发送之前,我们加入了一个timestamp的TLV,所以解析时需要完成这个TLV的解析,并将TimeStamp作为返回值返回。


py
    class LLDPPacket(object):
        # make a LLDP packet for link discovery.
        CHASSIS_ID_PREFIX = 'dpid:'
        CHASSIS_ID_PREFIX_LEN = len(CHASSIS_ID_PREFIX)
        CHASSIS_ID_FMT = CHASSIS_ID_PREFIX + '%s'
        PORT_ID_STR = '!I'      # uint32_t
        PORT_ID_SIZE = 4
        DOMAIN_ID_PREFIX = 'domain_id:'
        DOMAIN_ID_PREFIX_LEN = len(DOMAIN_ID_PREFIX)
        DOMAIN_ID_FMT = DOMAIN_ID_PREFIX + '%s'
        VPORT_ID_STR = '!I'      # uint32_t
        VPORT_ID_SIZE = 4
        class LLDPUnknownFormat(RyuException):
            message = '%(msg)s'
        @staticmethod
        def lldp_packet(dpid, port_no, dl_addr, ttl, timestamp,
                        vport_no=ofproto_v1_0.OFPP_NONE):
            pkt = packet.Packet()
            dst = lldp.LLDP_MAC_NEAREST_BRIDGE
            src = dl_addr
            ethertype = ETH_TYPE_LLDP
            eth_pkt = ethernet.ethernet(dst, src, ethertype)
            pkt.add_protocol(eth_pkt)
            tlv_chassis_id = lldp.ChassisID(
                subtype=lldp.ChassisID.SUB_LOCALLY_ASSIGNED,
                chassis_id=LLDPPacket.CHASSIS_ID_FMT %
                dpid_to_str(dpid))
            tlv_port_id = lldp.PortID(subtype=lldp.PortID.SUB_PORT_COMPONENT,
                                      port_id=struct.pack(
                                          LLDPPacket.PORT_ID_STR,
                                          port_no))
            tlv_ttl = lldp.TTL(ttl=ttl)
            tlv_timestamp = lldp.TimeStamp(timestamp=timestamp)
            tlv_end = lldp.End()
            tlvs = (tlv_chassis_id, tlv_port_id, tlv_ttl, tlv_timestamp, tlv_end)
            lldp_pkt = lldp.lldp(tlvs)
            pkt.add_protocol(lldp_pkt)
            pkt.serialize()
            return pkt.data
        @staticmethod
        def lldp_parse(data):
            pkt = packet.Packet(data)
            i = iter(pkt)
            eth_pkt = i.next()
            assert type(eth_pkt) == ethernet.ethernet
            lldp_pkt = i.next()
            if type(lldp_pkt) != lldp.lldp:
                raise LLDPPacket.LLDPUnknownFormat()
            tlv_chassis_id = lldp_pkt.tlvs[0]
            if tlv_chassis_id.subtype != lldp.ChassisID.SUB_LOCALLY_ASSIGNED:
                raise LLDPPacket.LLDPUnknownFormat(
                    msg='unknown chassis id subtype %d' % tlv_chassis_id.subtype)
            chassis_id = tlv_chassis_id.chassis_id
            if not chassis_id.startswith(LLDPPacket.CHASSIS_ID_PREFIX):
                raise LLDPPacket.LLDPUnknownFormat(
                    msg='unknown chassis id format %s' % chassis_id)
            src_dpid = str_to_dpid(chassis_id[LLDPPacket.CHASSIS_ID_PREFIX_LEN:])
            tlv_port_id = lldp_pkt.tlvs[1]
            if tlv_port_id.subtype != lldp.PortID.SUB_PORT_COMPONENT:
                raise LLDPPacket.LLDPUnknownFormat(
                    msg='unknown port id subtype %d' % tlv_port_id.subtype)
            port_id = tlv_port_id.port_id
            if len(port_id) != LLDPPacket.PORT_ID_SIZE:
                raise LLDPPacket.LLDPUnknownFormat(
                    msg='unknown port id %d' % port_id)
            (src_port_no, ) = struct.unpack(LLDPPacket.PORT_ID_STR, port_id)
            tlv_timestamp = lldp_pkt.tlvs[3]
            timestamp = tlv_timestamp.timestamp
            return src_dpid, src_port_no, timestamp

到此为止,完成了LLDP的构造和解析的定义。但是由于修改了构造函数的参数列表,和解析函数的返回值,所以在构造LLDP数据包和解析LLDP数据包时,均需要做一些改动。示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
py
    def _port_added(self, port):
        _time = time.time()
        lldp_data = LLDPPacket.lldp_packet(port.dpid, port.port_no,
                                           port.hw_addr, self.DEFAULT_TTL, _time)
py
        @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
        def packet_in_handler(self, ev):
            if not self.link_discovery:
                return
            msg = ev.msg
            try:
                src_dpid, src_port_no, timestamp = LLDPPacket.lldp_parse(msg.data)
            except LLDPPacket.LLDPUnknownFormat as e:
                # This handler can receive all the packtes which can be
                # not-LLDP packet. Ignore it silently
                return

此处需要提醒读者的是,在Ryu的Switches模块中,被发送的LLDP都是一次构造之后保存起来,发送时直接发送的,所以添加的时间戳会固定在第一次构造时的时间。所以如果希望正确地插入发送时间戳,还需要进行额外的逻辑修改。但是这也许就破坏了Ryu设计的完整性,所以如何操作还需要读者自行斟酌。

然而,像VPort\_ID之类的不随时间而改变的TLV,则可以直接使用。添加VPort\_ID的步骤和以上的例子同理,其VPort\_ID类的示例代码如下所示:

py

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @lldp.set_tlv_type(LLDP_TLV_VPORT_ID)
    class VPortID(LLDPBasicTLV):
        _PACK_STR = '!B'
        _PACK_SIZE = struct.calcsize(_PACK_STR)
        # subtype id(1 octet) + port id length(1 - 255 octet)
        _LEN_MIN = 2
        _LEN_MAX = 256
        # VPort ID subtype
        SUB_INTERFACE_ALIAS = 1     # ifAlias (IETF RFC 2863)
        SUB_PORT_COMPONENT = 2      # entPhysicalAlias (IETF RFC 4133)
        SUB_MAC_ADDRESS = 3         # MAC address (IEEE Std 802)
        SUB_NETWORK_ADDRESS = 4     # networkAddress
        SUB_INTERFACE_NAME = 5      # ifName (IETF RFC 2863)
        SUB_AGENT_CIRCUIT_ID = 6    # agent circuit ID(IETF RFC 3046)
        SUB_LOCALLY_ASSIGNED = 7    # local
        def __init__(self, buf=None, *args, **kwargs):
            super(VPortID, self).__init__(buf, *args, **kwargs)
            if buf:
                (self.subtype, ) = struct.unpack(
                    self._PACK_STR, self.tlv_info[:self._PACK_SIZE])
                self.vport_id = self.tlv_info[self._PACK_SIZE:]
            else:
                self.subtype = kwargs['subtype']
                self.vport_id = kwargs['vport_id']
                self.len = self._PACK_SIZE + len(self.vport_id)
                assert self._len_valid()
                self.typelen = (self.tlv_type << LLDP_TLV_TYPE_SHIFT)|self.len
        def serialize(self):
            return struct.pack('!HB', self.typelen, self.subtype) +self.vport_id

总结

LLDP协议可添加自定义TLV格式的特性,使其可以灵活地被修改,进而应用到不同的业务场景中,十分方便。本文就以Ryu控制器为例,介绍了如何添加自定义LLDPDU的详细流程,希望对读者有一定的帮助。此外,为计算时延,还可以通过switches模块中的PortDatak类的发送时间戳来实现,无需修改LLDP数据包格式。如何在Ryu中完成时延测试的内容将在下一篇文章中详细介绍,敬请关注。

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

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
数据链路层学习之LLDP「建议收藏」
随着网络技术的发展,接入网络的设备的种类越来越多,配置越来越复杂,来自不同设备厂商的设备也往往会增加自己特有的功能,这就导致在一个网络中往往会有很多具有不同特性的、来自不同厂商的设备,为了方便对这样的网络进行管理,就需要使得不同厂商的设备能够在网络中相互发现并交互各自的系统及配置信息。 LLDP(Link Layer Discovery Protocol,链路层发现协议)就是用于这个目的的协议。LLDP定义在802.1ab中,它是一个二层协议,它提供了一种标准的链路层发现方式。LLDP协议使得接入网络的一台设备可以将其主要的能力,管理地址,设备标识,接口标识等信息发送给接入同一个局域网络的其它设备。当一个设备从网络中接收到其它设备的这些信息时,它就将这些信息以MIB的形式存储起来。 这些MIB信息可用于发现设备的物理拓扑结构以及管理配置信息。需要注意的是LLDP仅仅被设计用于进行信息通告,它被用于通告一个设备的信息并可以获得其它设备的信息,进而得到相关的MIB信息。它不是一个配置、控制协议,无法通过该协议对远端设备进行配置,它只是提供了关于网络拓扑以及管理配置的信息,这些信息可以被用于管理、配置的目的,如何用取决于信息的使用者。
全栈程序员站长
2022/07/21
1.3K0
数据链路层学习之LLDP「建议收藏」
Ryu:网络时延探测应用
之前,笔者已经发布了网络感知应用和基于跳数的最短路径转发应用。本文将介绍笔者开发的网络时延探测应用。该应用通过LLDP数据包的时延和Echo数据包的时延计算得出链路的时延数据,从而实现网络链路时延的感知。详细原理和实现步骤将在文章中详细介绍。 测试原理 网络时延探测应用利用了Ryu自带的Switches模块的数据,获取到了LLDP数据发送时的时间戳,然后和收到的时间戳进行相减,得到了LLDP数据包从控制器下发到交换机A,然后从交换机A到交换机B,再上报给控制器的时延T1,示例见图1的蓝色箭头。同理反向的时延
SDNLAB
2018/04/02
1.6K0
Ryu:网络时延探测应用
RYU基础整理[统计整理是统计调查的基础]
大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说RYU基础整理[统计整理是统计调查的基础],希望能够帮助大家进步!!!
Java架构师必看
2022/02/23
1.2K0
RYU基础整理[统计整理是统计调查的基础]
lldp 命令「建议收藏」
简单说来,LLDP是一种邻近发现协议。它为以太网网络设备,如交换机、路由器和无线局域网接入点定义了一种标准的方法,使其可以向网络中其他节点公告自身的存在,并保存各个邻近设备的发现信息。例如设备配置和设备识别等详细信息都可以用该协议进行公告。
全栈程序员站长
2022/09/01
1.9K0
LLDP在ODL中的实现及源码分析(一)
本文中主要是与大家分享一下LLDP在ODL中的实现以及其源码分析,主要内容涉及ODL控制器中LLDP帧的产生及发送。文章都是个人理解,希望能够帮助到大家,更希望可以一起讨论看法不一的地方。 1 LLDP简介 ODL使用LLDP实现链路检测。LLDP,即链路层发现协议,是一种数据链路层协议,网络设备可以通过在本地网络中发送LLDPDU(Link Layer Discovery Protocol Data Unit)来通告其他设备自身的状态,是一种能够使网络中的设备互相发现并通告状态、交互信息的协议。 LLDP
SDNLAB
2018/03/30
1.7K0
LLDP在ODL中的实现及源码分析(一)
网络协议之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
2.6K0
网络协议之LLDP
RyuBook1.0案例三:REST L
RYU本身提供了一个类似WSGI的web服务器功能。借助这个功能,我们可以创建一个REST API。 基于创建的REST API,可以快速的将RYU系统与其他系统或者是浏览器相连接,非常实用的一个功能。
py3study
2020/01/19
7500
基于Ryu打造自定义控制器
控制器是SDN网络中最重要的组成部分。在开发SDN应用时,需要基于某一个控制器开发,而大部分开源控制器都是一个框架或者平台,更多个性化的设置和应用需要开发者自己完成。对于开发者而言,一个自定义的控制器
SDNLAB
2018/04/03
1.5K0
基于Ryu打造自定义控制器
Ryu:OpenFlow协议源码分析
Ryu支持OpenFlow所有的版本,是所有SDN控制器中对OpenFlow支持最好的控制器之一。这得益于Ryu的代码设计,Ryu中关于OpenFlow协议的代码量不多。阅读Ryu源码,不仅让我了解到
SDNLAB
2018/04/03
1.5K0
Ryu:OpenFlow协议源码分析
基于SDN的网络状态测量
为了更好地管理和运行网络,非常有必要收集网络资源及其状态信息。在很多网络场景中,SDN控制器的决策都取决时延,带宽和拓扑等网络状态。在开发SDN应用的过程中,笔者总结了一些有用的网络状态测量的解决方案
SDNLAB
2018/04/02
1.9K0
基于SDN的网络状态测量
基于网络流量的SDN最短路径转发应用
网络的转发是通信的基本功能,其完成信息在网络中传递,实现有序的数据交换。通过SDN控制器的集中控制,可以轻松实现基础的转发算法有二层MAC学习转发和基于跳数的最短路径算法。然而,网络跳数并不是决定路径
SDNLAB
2018/04/03
2.1K0
基于网络流量的SDN最短路径转发应用
Linux用户态协议栈与DPDK构建高性能应用
这里使用了已经搭建好的dpdk环境,dpdk的搭建过程网上有很多教程可以参考,后面有空再做一篇dpdk环境搭建文章吧! (1)检查网卡状态
Lion Long
2024/09/15
2830
Linux用户态协议栈与DPDK构建高性能应用
SDN中的LLDP和Openflow协议[通俗易懂]
OpenFlow交换机把传统网络中,完全由交换机/路由器控制的报文转换为由交换机和控制器来共同完成数据的转发操作,从而实现数据的转发与路由控制的分离。控制器则通过事先规定好的接口操作OpenFlow交换机中的流表,从而达到数据转发的目的。
全栈程序员站长
2022/08/26
1.7K0
SDN中的LLDP和Openflow协议[通俗易懂]
Python黑客编程3网络数据监听和过滤
课程的实验环境如下: • 操作系统:kali Linux 2.0 • 编程工具:Wing IDE • Python版本:2.7.9 • 涉及到的主要python模块:pypcap,dpkt,scapy,scapy-http 涉及到的几个python网络抓包和分析的模块,dpkt和scapy在kali linux 2.0 中默认已经被安装,如果你的系统中没有需要手动安装一下,下面是软件包安装的简单说明。 在kali下安装pypcap需要两步(如果在其他系统上可能还需要安装python-dev): apt-g
用户1631416
2018/04/11
4.7K0
Python黑客编程3网络数据监听和过滤
用户空间协议栈设计和netmap综合指南,将网络效率提升到新高度
应用层: 最接近用户的一层,为用户程序提供网络服务。主要协议有HTTP、FTP、TFTP、SMTP、DNS、POP3、DHCP等。 表示层: 数据的表示、安全、压缩。管理数据的解密和加密。 会话层: 负责在网络中的两个节点之间的建立、维持和终止通信。 传输层: 模型中最重要的一层,负责传输协议的流控和差错校验。数据包离开网卡后进入的就是传输层;主要协议有:TCP、UDP等。 网络层: 将网络地址翻译成对应的物理地址。主要协议有:ICMP、IP等。 数据链路层: 建立逻辑连接、进行硬件地址寻址、差错校验等功能,解决两台相连主机之间的通信问题。主要协议有SLIP、以太网协议/MAC帧协议、ARP和RARP等。 物理层: 模型的最低层,建立、维护、断开物理连接,传输比特流。常见的物理媒介有光纤、电缆、中继器等。主要协议有RS232等。
Lion Long
2024/09/14
1210
用户空间协议栈设计和netmap综合指南,将网络效率提升到新高度
LINC switch系列之配置与运行
前言: LINC switch是一个由flowforwarding. org主导开发的一款基于Apache2.0协议开源的Openflow交换机软件。本文在安装指南的基础上,介绍了其运行时的配置与使用。 介绍: LINC switch基于Erlang构建,因而它的配置文件要基于Erlang语法。在编辑配置文件时可以准备一个支持括号补全的编辑器或Erlang IDE。 推荐使用sublime text,当然你也可以用Eclipse与Erlang插件集成或IntelliJ IDEA加Erlang插件的IDE编辑
SDNLAB
2018/04/03
8550
LINC switch系列之配置与运行
数据包处理利器——Scapy基础知识
Scapy是功能强大的交互式数据包处理程序。它能够伪造或解码各种协议的数据包,在线发送,捕获,匹配请求和响应等。它可以轻松处理大多数经典任务,例如扫描,跟踪路由,探测,单元测试,攻击或网络发现,它可以代替hping,arpspoof,arp-sk,arping,p0f甚至Nmap,tcpdump和tshark的某些部分。。它在其他工具无法处理的许多其他特定任务上也表现出色,例如发送无效帧,组合技术(VLAN跳变+ ARP缓存中毒,WEP加密通道上的VOIP解码等等)
没有故事的陈师傅
2023/05/01
4.5K0
数据包处理利器——Scapy基础知识
Python 运用Dpkt库解析数据包
dpkt项目是一个python模块,用于快速、简单的数据包解析,并定义了基本TCP/IP协议,使用该库可以快速解析通过各类抓包工具抓到的数据包,从而提取分析包内的参数。
王瑞MVP
2022/12/28
1.9K0
SeedLab——DNS Attack Lab
DNS(Domain Name System)是一个用于将域名转换为与之关联的IP地址的分布式命名系统。它充当了互联网上的电话簿,将人类可读的域名(例如example.com)映射到计算机可理解的IP地址(例如192.0.2.1)。
Andromeda
2023/12/14
1.1K0
SeedLab——DNS Attack Lab
learning:vpp/classify(2)
作用如下图所示从table内存池上申请table内存,在main_heap上申请桶占用空间,从系统内存映射classify session中匹配规则表配置空间。这里操作和bihash一致。
dpdk-vpp源码解读
2023/01/04
1.8K0
learning:vpp/classify(2)
相关推荐
数据链路层学习之LLDP「建议收藏」
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验