盒子
盒子
文章目录
  1. Ryu控制器介绍
  2. Ryu的安装与使用
    1. 安装
    2. 使用
  3. Ryu源码的目录结构
  4. Ryu中关键变量和API的说明
  5. 编写基于IP转发的应用
  6. 总结
  7. 参考文献

基于Ryu的应用开发

Ryu控制器介绍

Ryu控制器是日本NTT公司使用Python语言开发的一款开源SDN/OpenFlow控制器。Ryu提供了简单的API接口,开发人员可以方便地编写网络控制程序。Ryu支持管理网络设备的各种协议,如OpenFlow、Netconf和OF-config等协议。此外,Ryu还被整合到了OpenStack的Neutron中。

Ryu的安装与使用

安装

第一种方式:

1
% pip install ryu

第二种方式:

1
2
% git clone git://github.com/osrg/ryu.git
% cd ryu; python ./setup.py install

使用

在安装好Ryu之后,可以通过ryu-manager appname.py的方式运行Ryu应用程序。通常,Ryu的使用是与mininet相结合的,mininet的作用是创建网络拓扑,关于mininet的具体使用方式可以参考官网教程。

Ryu源码的目录结构

  • app/:该目录下主要包含的是官方提供的应用例子。
  • base/:该目录下只有1个app_manager.py文件,其作用是Ryu应用的管理中心,用于加载Ryu应用程序,接受从App发过来的消息,同时也完成消息的路由。
  • controller/:该目录下主要实现了控制器和交换机之间的互联和事件处理。
  • lib/: 该目录下主要定义了需要使用到的基本数据结构,如dpid、mac和ip等数据结构。此外,如ICMP、DHCP和IGMP等协议也是在该目录下定义的。
  • ofproto/: 该目录下主要定义了与OpenFlow协议相关的数据结构,包含了不同版本OpenFlow协议的定义。
  • topology/: 该目录下定义了switch和host等相关的数据结构。
  • services/:该目录下主要包含了对BGP和vrrp的实现。

Ryu中关键变量和API的说明

  • buffer_id:该变量用于标记缓存在交换机中的数据报文id,通过bufferid将报文缓存在交换机中,减小性能开销。
  • set_ev_cls():该装饰器函数主要用来监听相应的事件,在具体实现中常常写作:
1
2
3
4
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packetin_handler(self, ev):
# 处理函数具体实现代码
pass

set_ev_cls()函数的第一个参数表示的是所监听的事件名,第二个参数表示所处的阶段。在上面的代码中,当Ryu和交换机握手过程(即hello, features request/reply, Set Config等)结束后(即处于MAIN_DISPATCHER阶段),当收到PacketIn消息才会调用packet_in_handler处理函数。

  • OFPFlowMod类,该类主要是对FlowMod消息的封装,由控制器下发到交换机上,对流表规则进行相应的修改。
  • DataPath类,该类主要就是交换机的抽象描述,其中的id变量即唯一指定了数据层中的一个交换机。
  • OFPInstructionActions类,该类主要是流表动作的抽象,开发者构造具体的流表动作,如转发到其他所有端口。该动作被包含在FlowMod消息中。

编写基于IP转发的应用

Ryu的代码采用面向对象的风格进行编写的,在开发我们自己的应用时,只需要导入相关的包,然后继承app_manager.RyuApp类,即:

1
2
3
4
5
6
from ryu.base import app_manager

class SIMPE_IP_FORWARD(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super(SIMPE_IP_FORWARD, self).__init__(*args, **kwargs)

通过这种方式就完成了APP的创建,然后我们需要添加默认规则到交换机上,因为交换机上的流表最开始是没有流规则的,而SDN交换机对数据包的处理全是基于流表规则进行的,如果没有流表规则,交换机则不知道该如何处理数据包,所以我们需要主动安装默认规则到交换机上,让交换机将不匹配的数据包上传到控制器上,交由控制器分析处理,代码如下:

1
2
3
4
5
6
7
8
9
10
""" 控制器与交换机建立连接时,下发默认规则到交换机上,默认将不匹配的流上传到控制器处理"""

@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)]
self.add_flow(datapath, 0, match, actions)

switch_features_handler()函数作为应用的成员函数而存在,其中装饰器函数set_ev_cls()表示的是监听控制器与交换机建立时互发的消息,当交换机将自己的相关信息告诉给控制器的时候,将触发该函数,注意这个信息不是通过流规则匹配而上传到控制器的,而是在建立连接时产生的,所以此时交换机上是不存在流规则的。parser.OFPActionOutput(ofproto.OFPP_CONTROLLER)表示将匹配的数据包上传到控制器。

当交换机上有了默认的流规则之后,遇到新流时,交换机将会产生PacketIn消息到控制器上,所以控制器必须对PacketIn消息进行处理,由此,我们需要添加处理函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
""" 控制器处理收到的PacketIn消息"""
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packetin_handler(self, ev):

if eth.ethertype == ether_types.ETH_TYPE_ARP:
arp_pkt = pkt.get_protocol(arp.arp)
dpid = datapath.id
src_mac = arp_pkt.src_mac
dst_mac = arp_pkt.dst_mac
temp = (dpid, src_mac, arp_pkt.dst_ip)

if not self.flag[temp]:
self.flag[temp] = True
self.arp_port[temp] = in_port
elif self.flag[temp] and self.arp_port[temp] != in_port:
"""记录了的消息来自于同一交换机的不同端口,则不处理,以此避免网络风暴"""
return

上面的代码只是处理函数的部分函数,该部分代码的作用是通过对相应的端口进行记录,避免产生网络风暴。因为网络拓扑的结构可能是环形的,所以需要对数据包进行处理,避免重复转发,我们利用ARP报文,提取了相关交换机的MAC地址,通过对MAC地址和端口进行记录和对比,避免对报文的重复转发。在此之后,我们还需要处理带有IP地址的数据包,从而实现基于IP的地址转发,关键点即是下发基于IP地址匹配的流规则到交换机上,流规则匹配到了对应的数据包,则按照指令进行数据包转发,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"""
1. 下发流规则到交换机
2. 将数据包转发出去
"""
match = ofp_parser.OFPMatch(eth_type=0x800, in_port=in_port, ipv4_src=ip_pkt.src, ipv4_dst=ip_pkt.dst)
actions = [ofp_parser.OFPActionOutput(out_port)]
self.add_flow(datapath, 10, match, actions)

data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data
out = ofp_parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out)

该部分代码在packetin_handler()对IP报文进行相关处理,主要是根据相关的记录,获取到出端口,然后下发匹配规则到交换机上,再转发出去。其中的匹配规则是通过OFPMatch()类所构造的,在其中主要就是指定ip地址。

以上就是实现一个基于IP转发的Ryu应用的关键点,更多的细节可以参考:https://github.com/MrSiz/SDN/blob/master/ryu/simple_ip_forward.py。

总结

本文主要对Ryu控制器进行了简单的介绍,并对实际开发一个Ryu应用进行了重点说明。在具体的实践过程中,大家可以在阅读Ryu源码的同时进行实际应用的开发,基于Python的简单以及良好的API接口,开发一个Ryu应用并非难事。

参考文献

[1] https://www.sdnlab.com/11688.html
[2] https://www.cnblogs.com/fjlinww/p/11904076.html
[3] https://www.cnblogs.com/zxqstrong/p/4789105.html
[4] https://www.cnblogs.com/fjlinww/p/11904076.html

想啥呢
想啥呢