Ryu 学习总结

之前一直在用 ovs-vsctl 工具控制 Open vSwitch,决定使用专门的控制器,趁机学习下openflow协议细节,辗转了POX,Floodlight,OpenDaylight等多个控制器之后,发现还是喜欢Python语言的控制器ryu。
ryu是日本NTT公司推出的SDN控制器框架,它基于Python开发,模块清晰,可扩展性好,逐步取代了早期的NOX,支持OpenFlow 1.0到1.5版本,也支持Netconf,OF-CONIFG等其他南向协议,提供了丰富的组件,便于开发者构建SDN应用。

我的新书《LangChain编程从入门到实践》 已经开售!推荐正在学习AI应用开发的朋友购买阅读!
LangChain编程从入门到实践

ryu 代码结构

  1. ryu/app
    基于ryu控制器开发的app,内含ryu的图形界面、北向防火墙、北向QoS、简易L2交换机、L3路由器等。

  2. ryu/base
    内含app_manager.py脚本,其作用是RYU应用的管理中心,用于加载ryu应用程序,接受从APP发送过来的信息,同时也完成消息的路由。定义了两大基类:RyuApp和AppManager。RyuApp定义了App的基本属性,AppManager则定义了用于管理多个App的方法和属性。

  3. ryu/controller
    该文件夹中含有许多关键的ryu源码文件,controller.py文件中含有两个十分重要的类:OpenFlowController 定义了构建一个控制器的基本方法和属性,DataPath 是用于描述OpenFlow控制器和交换机是如何连接的类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    class OpenFlowController(object):
    def __init__(self):
    super(OpenFlowController, self).__init__()
    if not CONF.ofp_tcp_listen_port and not CONF.ofp_ssl_listen_port:
    self.ofp_tcp_listen_port = ofproto_common.OFP_TCP_PORT
    self.ofp_ssl_listen_port = ofproto_common.OFP_SSL_PORT
    # For the backward compatibility, we spawn a server loop
    # listening on the old OpenFlow listen port 6633.
    hub.spawn(self.server_loop,
    ofproto_common.OFP_TCP_PORT_OLD,
    ofproto_common.OFP_SSL_PORT_OLD)
    else:
    self.ofp_tcp_listen_port = CONF.ofp_tcp_listen_port
    self.ofp_ssl_listen_port = CONF.ofp_ssl_listen_port

    # Example:
    # self._clients = {
    # ('127.0.0.1', 6653): <instance of StreamClient>,
    # }
    self._clients = {}
    ...
    class Datapath(ofproto_protocol.ProtocolDesc):
    """
    A class to describe an OpenFlow switch connected to this controller.
    An instance has the following attributes.
    .. tabularcolumns:: |l|L|
    # entry point
    ==================================== ======================================
    Attribute Description
    ==================================== ======================================
    id 64-bit OpenFlow Datapath ID.
    Only available for
    ryu.controller.handler.MAIN_DISPATCHER
    phase.
    ofproto A module which exports OpenFlow
    definitions, mainly constants appeared
    in the specification, for the
    negotiated OpenFlow version. For
    example, ryu.ofproto.ofproto_v1_0 for
    OpenFlow 1.0.
    ofproto_parser A module which exports OpenFlow wire
    message encoder and decoder for the
    negotiated OpenFlow version.
    For example,
    ryu.ofproto.ofproto_v1_0_parser
    for OpenFlow 1.0.
    ofproto_parser.OFPxxxx(datapath,...) A callable to prepare an OpenFlow
    message for the given switch. It can
    be sent with Datapath.send_msg later.
    xxxx is a name of the message. For
    example OFPFlowMod for flow-mod
    message. Arguemnts depend on the
    message.
    set_xid(self, msg) Generate an OpenFlow XID and put it
    in msg.xid.
    send_msg(self, msg) Queue an OpenFlow message to send to
    the corresponding switch. If msg.xid
    is None, set_xid is automatically
    called on the message before queueing.
    send_packet_out deprecated
    send_flow_mod deprecated
    send_flow_del deprecated
    send_delete_all_flows deprecated
    send_barrier Queue an OpenFlow barrier message to
    send to the switch.
    send_nxt_set_flow_format deprecated
    is_reserved_port deprecated
    ==================================== ======================================
    """
    ...
  4. ryu/cmd
    定义了ryu的命令系统。

  5. ryu/contrib
    社区贡献者的代码。

  6. ryu/lib
    该目录下定义了一些数据报的格式和数据结构,及诸多网络协议。

  7. ryu/ofproto
    该目录下的文件分为两类,一类定义协议的数据结构,另外一类用于协议报文解析。

  8. ryu/services
    完成了bgp、vrrp协议及ovsdb的实现。

  9. ryu/tests
    用于测试。

  10. ryu/topology
    定义了自定义拓扑文件中需要的一些模块和方法,其中的switches.py(关键文件),完成了对一整套交换机处理流程的定义。

L2Switch example(来自官网)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_0

class L2Switch(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]

def __init__(self, *args, **kwargs):
super(L2Switch, self).__init__(*args, **kwargs)

# 调用装饰器,第一个参数表示发生的事件种类,第二个参数告诉交换机只有在握手之后被调用。
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dp = msg.datapath
ofp = dp.ofproto
ofp_parser = dp.ofproto_parser

actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
out = ofp_parser.OFPPacketOut(
datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
actions=actions)
dp.send_msg(out)

分析具体的数据操作:

  • ev.msg:每一个事件类ev中都有msg成员,用于携带触发事件的数据包。
  • msg.datapath:已经格式化的msg其实就是一个packet_in报文,msg.datapath直接可以获得packet_in报文的datapath结构。datapath用于描述一个交换网桥,也是和控制器通信的实体单元。datapath.send_msg()函数用于发送数据到指定datapath,通过datapath.id可获得dpid数据。
  • datapath.ofproto对象是一个OpenFlow协议数据结构的对象,成员包含OpenFlow协议的数据结构,如动作类型OFPP_FLOOD。
  • datapath.ofp_parser则是一个按照OpenFlow解析的数据结构。
  • actions是一个列表,用于存放action list,可在其中添加动作。
  • 通过ofp_parser类,可以构造构造packet_out数据结构,括弧中填写对应字段的赋值即可。
  • 如果datapath.send_msg()函数发送的是一个OpenFlow的数据结构,RYU将把这个数据发送到对应的datapath。

一个标准的控制器处理事件的模板,@set_ev_cls(ofp_event.Event, DISPATCHER(s))装饰器的含义就是,当接收到DISPATCHER(s)情况的Event事件进行handler_function处理,其中handler_function是自定义的函数处理过程,命名可以任意指定;ofp_event.Event是由ofp_event.py提供的一系列事件。

1
2
3
@set_ev_cls(ofp_event.Event, DISPATCHER(s))
def handler_function(self, ev):
...

DISPATCHER(s)可以为单独一个,也可以为由多个DISPATCHER组成的列表,DISPATCHER描述的情况包括:

Defination Explanation
HANDSHAKE_DISPATCHER 交换HELLO消息
CONFIG_DISPATCHER 等待接收SwitchFeatures消息
MAIN_DISPATCHER 正常状态
DEAD_DISPATCHER 连接断开

ofp_event

ofp_event类位于ryu/controller/ofp_event.py,主要定义了OpenFlow中的各种事件,配合set_cls_ev可以对指定事件进行处理,接下来,在分析学习官方案例的同时,也会整合Ryu源码与OpenFlow1.3.3协议的内容,对这些事件进行深入的理解与分析。

EventOFPMsgBase

事件类描述了从交换机接收OpenFlow消息的过程,按照约定,它们被命名为ryu.controller.ofp_event.EventOFPxxxx,其中xxxx是相应的OpenFlow消息的名称。例如,EventOFPPacketIn用于packet-in message,Ryu的OpenFlow控制器部分自动解码从交换机接收到的OpenFlow消息,并将这些事件发送(ryu.controller.handler.set_ev_cls)到Ryu应用程序,EventOFPMsgBase事件类是所有EventOFPxxxx的基类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class EventOFPMsgBase(event.EventBase):
"""
The base class of OpenFlow event class.
OpenFlow event classes have at least the following attributes.
.. tabularcolumns:: |l|L|
============ ==============================================================
Attribute Description
============ ==============================================================
msg An object which describes the corresponding OpenFlow message.
msg.datapath A ryu.controller.controller.Datapath instance
which describes an OpenFlow switch from which we received
this OpenFlow message.
timestamp Timestamp when Datapath instance generated this event.
============ ==============================================================
The msg object has some more additional members whose values are extracted
from the original OpenFlow message.
"""

def __init__(self, msg):
self.timestamp = time.time()
super(EventOFPMsgBase, self).__init__()
self.msg = msg
EventOFPStateChange
发起事件 处理事件
交换机状态变化 EventOFPStateChange
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class EventOFPStateChange(event.EventBase):
"""
An event class for negotiation phase change notification.
An instance of this class is sent to observer after changing
the negotiation phase.
An instance has at least the following attributes.
========= =================================================================
Attribute Description
========= =================================================================
datapath ryu.controller.controller.Datapath instance of the switch
========= =================================================================
"""

def __init__(self, dp):
super(EventOFPStateChange, self).__init__()
self.datapath = dp

该class是处理协商阶段变更通知的事件,在协商更改后发生此消息给观察者,当我们使用以下的命令,我们就成为了观察者,发生此类消息时,便可以进行接收和处理。

1
2
@set_ev_cls(ofp_event.EventOFPStateChange,
[MAIN_DISPATCHER, DEAD_DISPATCHER])

在协商阶段,MAIN_DISPATCHER意味着有新的交换机接入,DEAD_DISPATCHER意味着有交换机脱离连接。

EventOFPPortStateChange
发起事件 处理事件
OFPPortStatsRequest EventOFPPortStateChange
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class EventOFPPortStateChange(event.EventBase):
"""
An event class to notify the port state changes of Dtatapath instance.
This event performs like EventOFPPortStatus, but Ryu will
send this event after updating ``ports`` dict of Datapath instances.
An instance has at least the following attributes.
========= =================================================================
Attribute Description
========= =================================================================
datapath ryu.controller.controller.Datapath instance of the switch
reason one of OFPPR_*
port_no Port number which state was changed
========= =================================================================
"""

def __init__(self, dp, reason, port_no):
super(EventOFPPortStateChange, self).__init__()
self.datapath = dp
self.reason = reason
self.port_no = port_no

参考链接

Welcome to RYU the Network Operating System(NOS)
RYU入门教程(李呈大神)

作者

莫尔索

发布于

2020-03-14

更新于

2024-05-24

许可协议

评论