提高SDN控制器拓扑发现性能
原文由我发表在sdnlab.com。原文链接:http://www.sdnlab.com/15425.html
SDN网络的一大特点就是资源由控制器集中管理,控制器管理网络,最基本的当然需要知道网络的拓扑,而网络拓扑可能时时发生变化,所以控制器需要时时监测,对于整个网络来说,控制器担负了太多的计算任务,所以如果能够帮助控制器减压,则会提高整个网络的性能。本片文章将以ryu控制器为例,首先介绍传统网络和现在SDN网络的拓扑发现原理,然后介绍改进算法,最后讲解改写后的代码逻辑。
一. LLDP拓扑发现原理
传统网络中的链路发现协议为LLDP(Link Layer Discovery Protocol),LLDP允许局域网中的结点告诉其他结点他自己的capabilities和neighbours。在传统以太网交换机中,交换机从自己的每个端口发送LLDP数据包,这个数据包不会被其他交换机转发,寿命只有一跳,LLDP负载被封装在以太网帧中,结构如下图,其中深灰色的即为LLDP负载,Chassis ID TLV, Port ID TLV和Time to live TLV三个是强制字段,分别代表交换机标识符(在局域网中是独一无二的),端口号和TTL。这个数据发出并被邻居结点收到之后进行解析,就可以知道这条链路的源目的交换机以及源目的接口。

二. ryu拓扑发现原理
OpenFlow的官方没有规定标准的拓扑发现方法,现在的OFDP(OpenFlow Discovery Protocol)利用的仍然是传统网络中的链路发现协议LLDP,接下来介绍ryu如何利用LLDP发现拓扑,假设现在有两个OpenFlow交换机连接在控制器上,如下图,简述拓扑发现步骤(以S1作为主体,S2的类似):
- SDN控制器构造PacketOut消息向S1的三个端口分别发送上图所示的LLDP数据包,其中将Chassis ID TLV和Port ID TLV分别置为S1的dpid和端口号;
- 控制器向交换机S1中下发流表,流表规则为:将从Controller端口收到的LLDP数据包从他的对应端口发送出去;
- 控制器向交换机S2中下发流表,流表规则为:将从非Controller接收到LLDP数据包发送给控制器;
- 控制器通过解析LLDP数据包,得到链路的源交换机,源接口,通过收到的PacketIn消息知道目的交换机和目的接口;

现在的ryu发现拓扑是对整个数据平面的所有交换机的所有端口发送PacketOut数据包,对于Fattree等网络来说,端口的数量是交换机数量的k倍,因此导致了很多资源的消耗,所以是否可以对这个拓扑发现的机制进行改进,让发送的PacketOut消息和交换机的数量相同?
三. 改进后的ryu拓扑发现机理
为了实现上面所提到的改进目标,需要将LLDP负载中的Port ID TLV进行改进,或者有其他的域和Port ID TLV一一映射也可以,这里提供一种解决办法,在LLDP数据包从交换机端口转发出去的时候,将这个以太网数据包的源MAC地址替换成为这个端口的MAC地址,而控制器在早先的配置阶段已经获得了关于交换机的端口的所有信息,所以对控制器来说,MAC地址和交换机的端口号是一一对应的,下面详细讲述改进方案。
- 更新控制器的LLDP PacketOut消息数量,由一个端口一个,改为一个交换机一个PacketOut消息,LLDP数据包负载中的域Port ID TLV值置为零;
- 控制器向流表下发一条规则:所有从端口Controller接收到的LLDP数据包,依次将其源MAC地址置为端口MAC地址,然后从相应的端口转发出去;
- 更新控制器的PacketIn消息处理机制,根据LLDP数据包的来源,可以得到目的交换机,目的端口,通过解析LLDP数据包,得到源MAC和源交换机,通过源MAC地址查找对应的端口号;
- 由于是修改的代码,所以不要忘了删除原来的以端口主导的相关代码。
四. 代码分析
首选需要添加的一些变量和类
- SwitchData类:包含了时间戳以及交换机所包含的LLDP数据
class SwitchData(object):
#store the lldp information
#send one LLDP information per switch
def __init__(self, lldp_data):
super(SwitchData, self).__init__()
self.lldp_data = lldp_data
self.timestamp = None
self.sent = 0
def lldp_sent(self):
self.timestamp = time.time()
self.sent += 1
def lldp_received(self):
self.sent = 0
def lldp_dropped(self):
return self.sent
def clear_timestamp(self):
self.timestamp = None
def __str__(self):
return 'SwitchData<timestamp=%s, sent=%d>' \
% (self.timestamp, self.sent)
- SwitchDataState:类似于PortDataState类,继承自字典,保存从Switch类到SwitchData类的映射,维护了一个类似双向链表的数据结构
class SwitchDataState(dict):
# dict: Switch class -> SwitchData class
# slimed down version of OrderedDict as python 2.6 doesn't support it.
_PREV = 0
_NEXT = 1
_KEY = 2
def __init__(self):
super(SwitchDataState, self).__init__()
self._root = root = [] # sentinel node
root[:] = [root, root, None] # [_PREV, _NEXT, _KEY]
# doubly linked list
self._map = {}
def _remove_key(self, key):
link_prev, link_next, key = self._map.pop(key)
link_prev[self._NEXT] = link_next
link_next[self._PREV] = link_prev
def _append_key(self, key):
root = self._root
last = root[self._PREV]
last[self._NEXT] = root[self._PREV] = self._map[key] = [last, root, key]
def _prepend_key(self, key):
root = self._root
first = root[self._NEXT]
first[self._PREV] = root[self._NEXT] = self._map[key] = [root, first, key]
def _move_last_key(self, key):
self._remove_key(key)
self._append_key(key)
def _move_front_key(self, key):
self._remove_key(key)
self._prepend_key(key)
def add_switch(self, dp, lldp_data):
if dp not in self:
self._prepend_key(dp)
self[dp] = SwitchData( lldp_data)
def lldp_sent(self, dp):
switch_data = self[dp]
switch_data.lldp_sent()
self._move_last_key(dp)
return switch_data
def lldp_received(self, dp):
self[dp].lldp_received()
def move_front(self, dp):
switch_data = self.get(dp, None)
if switch_data is not None:
switch_data.clear_timestamp()
self._move_front_key(dp)
def get_switch(self, dp):
return self[dp]
def del_port(self, dp):
del self[dp]
self._remove_key(dp)
def __iter__(self):
root = self._root
curr = root[self._NEXT]
while curr is not root:
yield curr[self._KEY]
curr = curr[self._NEXT]
def clear(self):
for node in self._map.values():
del node[:]
root = self._root
root[:] = [root, root, None]
self._map.clear()
dict.clear(self)
def items(self):
'od.items() -> list of (key, value) pairs in od'
return [(key, self[key]) for key in self]
def iteritems(self):
'od.iteritems -> an iterator over the (key, value) pairs in od'
for k in self:
yield (k, self[k])
接着简述修改的核心代码,对应上面第三部分提到的四点
- 更新控制器的LLDP PacketOut消息数量,由一个端口一个,改为一个交换机一个PacketOut消息,LLDP数据包负载中的域Port ID TLV值置为零;
#construct LLDP packet for switch
def _switch_added(self, dp):
lldp_data = LLDPPacket.lldp_packet(
dp.dp.id, 0, '00:00:00:00:00:00', self.DEFAULT_TTL)
self.switches.add_switch(dp, lldp_data)
2.控制器向流表下发一条规则:所有从端口Controller接收到的LLDP数据包,依次将其源MAC地址置为端口MAC地址,然后从相应的端口转发出去;
if dp.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION:
for port_infor in self.port_state[dp.id].values():
if port_infor.name != "tap:":
actions.append(dp.ofproto_parser.OFPActionSetField(eth_src=port_infor.hw_addr))
actions.append(dp.ofproto_parser.OFPActionOutput(port_infor.port_no))
#actions = [dp.ofproto_parser.OFPActionOutput(self.port_state[dp].port_no)]
out = dp.ofproto_parser.OFPPacketOut(
datapath=dp, in_port=dp.ofproto.OFPP_CONTROLLER,
buffer_id=dp.ofproto.OFP_NO_BUFFER, actions=actions,
data=switch_data.lldp_data)
dp.send_msg(out)
else:
LOG.error('cannot send lldp packet. unsupported version. %x',
dp.ofproto.OFP_VERSION)
3.更新控制器的PacketIn消息处理机制,根据LLDP数据包的来源,可以得到目的交换机,目的端口,通过解析LLDP数据包,得到源MAC和源交换机,通过源MAC地址查找对应的端口号;
for port in self.port_state[src_dpid].values():
if port.hw_addr == src_mac:
src_port_no = port.port_no
4.由于是修改的代码,所以不要忘了删除原来的以端口主导的相关代码。
完整的代码见github
五. 实验验证
用Mininet建立一个二层二叉树,s3作为根节点分别连接s1和s2。如下图:

写个简单的ryu应用来调用拓扑发现模块提供的API接口,应用为topo_learner.py,代码见github
用wireshark抓取OpenFlow和LLDP数据包来进行验证

首先抓取交换机对控制器的响应消息,查看交换机的端口以及对应的MAC地址,从解析可以看到这是s3交换机,拥有四个端口(分别连接控制器,s1,s2,h3),下图是截取到的一个LLDP数据包,可以看出图中蓝色背景的LLDP的数据包的源MAC地址是s3交换机的3端口的MAC地址,说明前面的代码修改成功。

参考论文:Efficient Topology Discovery in SDN
提高SDN控制器拓扑发现性能的更多相关文章
- OpenFlow 1.3 控制器与交换机的交互,以及拓扑发现
前言 最近纠结于控制器如何发现拓扑,于是就翻起了OpenFlow 1.3进行查看,以及一些相关协议 OF 1.3 安全通道,即交互消息 OpenFlow Switch Specification 1. ...
- 万台规模下的SDN控制器集群部署实践
目前在网络世界里,云计算.虚拟化.SDN.NFV这些话题都非常热.今天借这个机会我跟大家一起来一场SDN的深度之旅,从概念一直到实践一直到一些具体的技术. 本次分享分为三个主要部分: SDN & ...
- 提高 ASP.NET Web 应用性能
转载:http://www.codeceo.com/article/24-ways-improve-aspnet-web.html 在这篇文章中,将介绍一些提高 ASP.NET Web 应用性能的方法 ...
- 提高ASP.NET应用程序性能的十大方法
一.返回多个数据集 检查你的访问数据库的代码,看是否存在着要返回多次的请求.每次往返降低了你的应用程序的每秒能够响应请求的次数.通过在单个数据库请求中返回多个结果集,可以减少与数据库通信的时间,使你的 ...
- 26种提高ASP.NET网站访问性能的优化方法 .
1. 数据库访问性能优化 数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源. ASP.NET中提供了连接池 ...
- 性能调优之提高 ASP.NET Web 应用性能的 24 种方法和技巧
性能调优之提高 ASP.NET Web 应用性能的 24 种方法和技巧 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对 ...
- 基于RYU的拓扑发现
基于RYU的拓扑发现 前言 本次实验是一个基于RYU的拓扑发现功能.参考了呈神的实现方式,并加了一些自己实现方式,做了一些数据结构的改动. 数据结构 link_to_port 字典 有两种关系: 一是 ...
- 提高 ASP.NET Web 应用性能的 24 种方法和技巧(转载)
在这篇文章中,将介绍一些提高 ASP.NET Web 应用性能的方法和技巧.众所周知,解决性能问题是一项繁琐的工作,当出现性能问题,每个人都会归咎于编写代码的开发人员. 以下为译文 那性能问题到底该如 ...
- 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制
[原创]分布式之数据库和缓存双写一致性方案解析(三) 正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...
随机推荐
- 基于vue单页应用的例子
代码地址如下:http://www.demodashi.com/demo/13374.html 目录结构 src目录 主要的代码目录 components 存放项目组件 router 路由文件 sto ...
- CSS3实现文字扫光效果
本篇文章由:http://xinpure.com/css3-text-light-sweep-effect/ CSS3 实现的文字扫光效果,几乎可以和 Flash 相媲美了 效果解析 我们分析一下实现 ...
- Tomcat、Websphere和Jboss类加载机制
http://blog.csdn.net/lshxy320/article/details/6448972 2 Tomcat 类加载机制 Tomcat Server 在启动的时候将构造一个 ...
- Redis Key过期通知
概述 键空间通知使得客户端可以通过订阅频道或模式, 来接收那些以某种方式改动了 Redis 数据集的事件.如Redis数据库中键的过期事件也是通过订阅功能实现.本文主要基于Azure PaaS Red ...
- Windows 环境下分布式跨域Session共享
为什么还是那句话,在网上找了N篇Session共享,但真正可以直接解决问题的还是没有找到. 一.以下为本人亲测,为防止环境不一致,对本文产生歧义,限定环境如下: 1. IIS7.0 2. Asp.ne ...
- js - 关于this、new、原型
1.this误区 # 第三方学习 http://www.cnblogs.com/wangfupeng1988/p/3988422.html - this不是函数自身的引用,this实际上是在函数被调用 ...
- yii xss模型安全
在这篇文章里,我们将描述一个基于WEB应用下避免不合法的内容注入. 我们要在一个行为里使用htmlpurifier类,用这种行为可以加强任何模型并表明各属性我们想让它们XSS安全. 我写了以下行为: ...
- Hyper-V 虚拟机无法上网的解决方法
创建一个虚拟机网络交换机 2 创建一个 外部网络如下图所示: 3 添加一个旧版的网络适配器: 4 设置旧版网络适配器: END XP系统的情况 1 启动虚拟机,设置虚拟机IP: 2 输入http:// ...
- TypeScript 入门指南
你是否听过 TypeScript? TypeScript 是微软开发的 JavaScript 的超集,TypeScript兼容JavaScript,可以载入JavaScript代码然后运行.TypeS ...
- Linux 连续运行多条命令
每条命令使用";"隔开.则不管前边的命令运行成功与否都会继续运行下一条命令 这里,有益将第二条命令中的echo多写了一个o.命令运行出错,但并不影响兴许命令的运行 能够这么想,假设 ...