预备知识
- RYU 控制器是众多 SDN 控制器中的一员,使用 Python 编写,对开发者非常友好。RYU 控制器的官方文档、API 文档和源码链接:
- 在 OpenFlow 中,数据是以流的形式进行处理的。流表是针对特定数据流的策略表,用于决定数据流的流向。OpenFlow 官方文档:本文的实验基于 OpenFlow 1.3 版本。
流表查看与匹配示例
使用 ryu-manager 执行 GitHub 上的 ryu 官方的控制器例程,来创建 SDN 控制器:
sdn@debian:~$ sudo ryu-manager ryu/ryu/app/simple_switch_13.py
loading app ryu/ryu/app/simple_switch_13.py
loading app ryu.controller.ofp_handler
instantiating app ryu/ryu/app/simple_switch_13.py of SimpleSwitch13
instantiating app ryu.controller.ofp_handler of OFPHandler
使用 mininet 连接 RYU 控制器,并创建只有一个交换机和两个主机的简单的网络拓扑:
sdn@debian:~$ sudo mn --controller=remote
*** Creating network
*** Adding controller
Connecting to remote controller at 127.0.0.1:6653
*** Adding hosts:
h1 h2
*** Adding switches:
s1
*** Adding links:
(h1, s1) (h2, s1)
*** Configuring hosts
h1 h2
*** Starting controller
c0
*** Starting 1 switches
s1 ...
*** Starting CLI:
mininet> net
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
s1 lo: s1-eth1:h1-eth0 s1-eth2:h2-eth0
c0
此时的网络拓扑结构如下图:

查看交换机 s1 的流表信息,此时 s1 中只有一条流表项:
mininet> dpctl dump-flows
*** s1 ------------------------------------------------------------------------
cookie=0x0, duration=40.319s, table=0, n_packets=16, n_bytes=1272, priority=0 actions=CONTROLLER:65535
actions=CONTROLLER:65535
是交换机的 Table-miss 流表项,该流表项的匹配域为通配,即匹配任何报文,优先级为 0(优先级最低)。也就是说,当收到的数据包没有匹配到任意一条流表项时,则将该数据包发送给 controller,且限制数据包最大长度为 65535 字节。
在 mininet 中执行
pingall
,来测试 h1、h2 的联通性:
mininet> pingall
*** Ping: testing ping reachability
h1 -> h2
h2 -> h1
*** Results: 0% dropped (2/2 received)
再查看 s1 的流表信息:
mininet> dpctl dump-flows
*** s1 ------------------------------------------------------------------------
cookie=0x0, duration=39.658s, table=0, n_packets=3, n_bytes=238, priority=1,in_port="s1-eth2",dl_src=62:cc:b8:e8:2f:6b,dl_dst=d2:12:9f:77:82:71 actions=output:"s1-eth1"
cookie=0x0, duration=39.655s, table=0, n_packets=2, n_bytes=140, priority=1,in_port="s1-eth1",dl_src=d2:12:9f:77:82:71,dl_dst=62:cc:b8:e8:2f:6b actions=output:"s1-eth2"
cookie=0x0, duration=198.655s, table=0, n_packets=23, n_bytes=1734, priority=0 actions=CONTROLLER:65535
此时 s1 中新增了两条流表项:
cookie=0x0, duration=39.658s, table=0, n_packets=3, n_bytes=238, priority=1,in_port="s1-eth2",dl_src=62:cc:b8:e8:2f:6b,dl_dst=d2:12:9f:77:82:71 actions=output:"s1-eth1"
cookie=0x0, duration=39.655s, table=0, n_packets=2, n_bytes=140, priority=1,in_port="s1-eth1",dl_src=d2:12:9f:77:82:71,dl_dst=62:cc:b8:e8:2f:6b actions=output:"s1-eth2"
in_port="s1-eth2",dl_src=62:cc:b8:e8:2f:6b,dl_dst=d2:12:9f:77:82:71 actions=output:"s1-eth1"
指示 s1,当端口
s1-eth2
接收到源 MAC 为
62:cc:b8:e8:2f:6b
,目标 MAC 为
d2:12:9f:77:82:71
的数据包时,则将该数据包发送到与端口
s1-eth1
连接的主机。而 h1 和 h2 的 MAC 地址,正好分别是
d2:12:9f:77:82:71
和
62:cc:b8:e8:2f:6b
:
mininet> h1 ifconfig h1-eth0
h1-eth0: flags=4163<up,broadcast,running,multicast> mtu 1500
inet 10.0.0.1 netmask 255.0.0.0 broadcast 10.255.255.255
inet6 fe80::d012:9fff:fe77:8271 prefixlen 64 scopeid 0x20<link>
ether d2:12:9f:77:82:71 txqueuelen 1000 (Ethernet)
RX packets 35 bytes 3498 (3.4 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 16 bytes 1216 (1.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
mininet> h2 ifconfig h2-eth0
h2-eth0: flags=4163<up,broadcast,running,multicast> mtu 1500
inet 10.0.0.2 netmask 255.0.0.0 broadcast 10.255.255.255
inet6 fe80::60cc:b8ff:fee8:2f6b prefixlen 64 scopeid 0x20<link>
ether 62:cc:b8:e8:2f:6b txqueuelen 1000 (Ethernet)
RX packets 35 bytes 3498 (3.4 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 16 bytes 1216 (1.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
换而言之,
in_port="s1-eth2",dl_src=62:cc:b8:e8:2f:6b,dl_dst=d2:12:9f:77:82:71 actions=output:"s1-eth1"
s1-eth2
接收到 h2 发送给 h1 的数据包时,则将该数据包发送到与端口
s1-eth1
连接的主机,也就是 h1:
mininet> net
h1 h1-eth0:s1-eth1
h2 h2-eth0:s1-eth2
s1 lo: s1-eth1:h1-eth0 s1-eth2:h2-eth0
c0
in_port="s1-eth1",dl_src=d2:12:9f:77:82:71,dl_dst=62:cc:b8:e8:2f:6b actions=output:"s1-eth2"
同理,不再赘述。
上述例子仅简单演示了 OpenFlow 流表的查看与匹配,细节请参考官方文档
流表项如何下发
流表项由 SDN 控制器生成并发送给 SDN 交换机。SDN 交换机维护了流表项,用于决定数据包的流向。由于本文的 SDN 控制器 RYU 与交换机之间使用的南向接口是 OpenFlow,所以接下来的内容依旧是基于 OpenFlow 进行分析。
TCP 握手
在 OpenFlow 中,控制平面与数据平面的传输协议是 TCP。
在上节中,使用 ryu-manager 执行官方例程来启动 RYU 控制器后,控制器监听在本地的 6653 端口:
tcp 0 0 0.0.0.0:6653 0.0.0.0:* LISTEN 6264/python
在 mininet 中连接 RYU 控制器时,控制器与交换机的 TCP 握手如下:
16:42:18.461536 IP localhost.51678 > localhost.6653: Flags [S], seq 1969037218, win 43690, options [mss 65495,sackOK,TS val 2494429579 ecr 0,nop,wscale 9], length 0
16:42:18.461556 IP localhost.6653 > localhost.51678: Flags [S.], seq 1946739924, ack 1969037219, win 43690, options [mss 65495,sackOK,TS val 2494429579 ecr 2494429579,nop,wscale 9], length 0
16:42:18.461574 IP localhost.51678 > localhost.6653: Flags [.], ack 1946739925, win 86, options [nop,nop,TS val 2494429579 ecr 2494429579], length 0
OpenFlow 版本协商
TCP 三次握手完成后,RYU 控制器与交换机相互发送 OFPT_HELLO 报文(省略其他无关的报文,如 TCP ACK 报文段和重传报文段)。HELLO 报文用于协商 OpenFLow 版本。通过协商后,通信双方决定使用的 OpenFlow 版本号为 1.3.0:
Frame 4: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 6653, Dst Port: 51676, Seq: 1, Ack: 1, Len: 8
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_HELLO (0)
Length: 8
Transaction ID: 3155619061
Frame 12: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 6653, Dst Port: 51678, Seq: 1, Ack: 1, Len: 8
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_HELLO (0)
Length: 8
Transaction ID: 1369246796
获取交换机的功能特性
在完成 OpenFlow 版本协商后,控制器需要根据交换机的功能特性,来完成相关配置。因此,RYU 控制器往交换机发送一个 OFPT_FEATURES_REQUEST 报文,以获取交换机的相关特性。作为响应,交换机回以一个 OFPT_FEATURES_REPLY 报文,其中包含了 datapath_id(在 OpenFlow 中,datapath 用于描述一个与控制器相连后的交换机)、n_buffers(单次数据包的接收最大缓冲)、n_tables(该交换机所能支持的流表数目)等信息:
Frame 22: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 6653, Dst Port: 51678, Seq: 9, Ack: 249, Len: 8
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_FEATURES_REQUEST (5)
Length: 8
Transaction ID: 1369246797
Frame 23: 98 bytes on wire (784 bits), 98 bytes captured (784 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 51678, Dst Port: 6653, Seq: 249, Ack: 17, Len: 32
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_FEATURES_REPLY (6)
Length: 32
Transaction ID: 1369246797
datapath_id: 0x0000000000000001
n_buffers: 0
n_tables: 254
auxiliary_id: 0
Pad: 0
capabilities: 0x0000004f
.... .... .... .... .... .... .... ...1 = OFPC_FLOW_STATS: True
.... .... .... .... .... .... .... ..1. = OFPC_TABLE_STATS: True
.... .... .... .... .... .... .... .1.. = OFPC_PORT_STATS: True
.... .... .... .... .... .... .... 1... = OFPC_GROUP_STATS: True
.... .... .... .... .... .... ..0. .... = OFPC_IP_REASM: False
.... .... .... .... .... .... .1.. .... = OFPC_QUEUE_STATS: True
.... .... .... .... .... ...0 .... .... = OFPC_PORT_BLOCKED: False
Reserved: 0x00000000
交换机发送端口状态信息
当交换机增删改端口时,需要发送 OFPT_PORT_STATUS 报文,以此通知控制器:
Frame 16: 146 bytes on wire (1168 bits), 146 bytes captured (1168 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 51678, Dst Port: 6653, Seq: 9, Ack: 9, Len: 80
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_PORT_STATUS (12)
Length: 80
Transaction ID: 0
Reason: OFPPR_MODIFY (2)
Pad: 00000000000000
Port
Port no: 2
Pad: 00000000
Hw addr: 1e:09:45:71:69:a6 (1e:09:45:71:69:a6)
Pad: 0000
Name: s1-eth2
(略)
修改交换机的流表项信息
控制器通过往交换机发送 OFPT_FLOW_MOD 报文,来调整交换机的流表。同时,在初始化阶段,控制器往交换机发送了一条 Match 字段为空、数据最大长度为 65535 的 OFPT_FLOW_MOD 报文,可见这条报文就是用于设置交换机的 Table-miss 流表项:
Frame 26: 146 bytes on wire (1168 bits), 146 bytes captured (1168 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 6653, Dst Port: 51678, Seq: 33, Ack: 489, Len: 80
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_FLOW_MOD (14)
Length: 80
Transaction ID: 1369246799
Cookie: 0x0000000000000000
Cookie mask: 0x0000000000000000
Table ID: 0
Command: OFPFC_ADD (0)
Idle timeout: 0
Hard timeout: 0
Priority: 0
Buffer ID: OFP_NO_BUFFER (4294967295)
Out port: 0
Out group: 0
Flags: 0x0000
.... .... .... ...0 = Send flow removed: False
.... .... .... ..0. = Check overlap: False
.... .... .... .0.. = Reset counts: False
.... .... .... 0... = Don't count packets: False
.... .... ...0 .... = Don't count bytes: False
Pad: 0000
Match
Type: OFPMT_OXM (1)
Length: 4
Pad: 00000000
Instruction
Type: OFPIT_APPLY_ACTIONS (4)
Length: 24
Pad: 00000000
Action
Type: OFPAT_OUTPUT (0)
Length: 16
Port: OFPP_CONTROLLER (4294967293)
Max length: OFPCML_NO_BUFFER (65535)
Pad: 000000000000
PACKET_IN 和 PACKET_OUT 报文
上文提到,交换机在初始阶段只有一条默认流表项:
mininet> dpctl dump-flows
*** s1 ------------------------------------------------------------------------
cookie=0x0, duration=161.361s, table=0, n_packets=19, n_bytes=1466, priority=0 actions=CONTROLLER:65535
因此, 当交换机收到主机发的第一个数据包时(如,在 mininet 执行
h1 ping h2
),会通过 PACKET_IN 报文, 将该数据包转发到控制器,
Frame 4: 150 bytes on wire (1200 bits), 150 bytes captured (1200 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 51750, Dst Port: 6653, Seq: 9, Ack: 9, Len: 84
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_PACKET_IN (10)
Length: 84
Transaction ID: 0
Buffer ID: OFP_NO_BUFFER (4294967295)
Total length: 42
Reason: OFPR_NO_MATCH (0)
Table ID: 0
Cookie: 0x0000000000000000
Match
(略)
控制器收到 PACKET_IN 报文,会根据程序逻辑(本文中就是 ryu/ryu/app/simple_switch_13.py 中的处理逻辑),生成相应的流表项,然后以 PACKET_OUT 报文,将流表项返回给交换机:
Frame 5: 148 bytes on wire (1184 bits), 148 bytes captured (1184 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 6653, Dst Port: 51750, Seq: 9, Ack: 93, Len: 82
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_PACKET_OUT (13)
Length: 82
Transaction ID: 3977916314
Buffer ID: OFP_NO_BUFFER (4294967295)
In port: 1
Actions length: 16
Pad: 000000000000
Action
(略)
交换机收到 PACKET_OUT 报文后,将流表项应用于数据包的转发。
ECHO 报文
控制器与交换机之间,通过 ECHO 报文来保持连接的活性:
Frame 1: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 51750, Dst Port: 6653, Seq: 1, Ack: 1, Len: 8
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_ECHO_REQUEST (2)
Length: 8
Transaction ID: 0
Frame 2: 74 bytes on wire (592 bits), 74 bytes captured (592 bits) on interface 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 6653, Dst Port: 51750, Seq: 1, Ack: 9, Len: 8
OpenFlow 1.3
Version: 1.3 (0x04)
Type: OFPT_ECHO_REPLY (3)
Length: 8
Transaction ID: 0
总结
RYU 控制器是 SDN 软件定义网络中的重要一环,使用 OpenFlow 协议作为南向接口来与交换机进行通信。RYU 对开发者而言是非常友好和容易理解的。至于交换机接收到 RYU 的流表信息,如何作用到数据包的转发,则需要在后续的学习中找到答案。