支持Open Flow是Open vSwitch的一块核心内容,在SDN越来越成熟,发展越来越强劲的情况下,学习Open Flow也愈加显得有必要了。本文是我对OVS中Open Flow学习的一个简要分享,内容有限,并且如有错误的地方欢迎指正。

OpenFlow概述

在支持OpenFlow的交换机中包含了若干个Flow table,Flow table可以用来控制数据包的处理,交换机会执行与flow相匹配的表项中所罗列的动作。

OpenFlow controller通过使用OpenFlow协议来管理交换机,并且controller也能通过使用OpenFlow协议来获取交换机上的端口、流量的统计信息或其他情报,并可以根据这些信息来调整各端口的流量。

交换机中维护的每个flow table都包含有很多个条目,这些条目会根据自身的一个优先级从高到底进行排序,优先级最高的条目位于flow table的顶部。

当一个数据包进入到交换机时,先和优先级最高的条目进行匹配,如果匹配成功,那么就停止继续匹配动作,去执行该条目中的一组action。如果匹配失败,则按照优先级高低,继续匹配下一条。如果flow table中所有的条目都不匹配,这个数据包就会被丢弃或发送到控制器(问题:如何决定是丢弃还是发送到控制器)。

OpenFlow协议的版本已经从1.0更新到了1.5,其中为了保证OpenFlow能有一个稳定的发展平台,把1.0和1.3版本作为其中长期支持的稳定版本。1.0版本功能比较简单,本次主要学习其中的1.3版本。

Flow Tables介绍

在OpenFlow中用来控制flow的table主要涉及到flow table和group table,先简单了解一下这两张table的构成。

Flow table

flow table.png

Flow table中每个条目中包含的字段如上图所示,其中每个字段的含义可以参考下面的表格。

字段描述
Match Fields该字段用来匹配数据包
Priority匹配流表项的优先级
Counters当数据包匹被成功配时会更新该字段,主要用来流量统计
Instructions用来修改动作集或流水线处理
Timeouts流表项的超时时间
Cookie一些不透明的数据值。 控制器可以使用它来过滤流量统计,流量修改和流量删除。 在处理数据包时不使用这些数据值。

每个Flow table中的条目(表项)都是由Match Fields和Priority两个字段来共同唯一标识的。

Group table

group table.png

同样的,上图列出了Group table中每个条目包含的字段,其含义参考下表:

字段描述
Group Identifier组的唯一标识,一个32位的无符号整数
Group Type组的类型(目前有四种类型:all, select, indirect, fast failover)
Counters成功匹配计数器,主要用来进行流量统计
Action Buckets一个有序的action buckets列表,每个action buckets包含一组动作和相关的参数

这里简单了解一下两张table的结构,至于table中每个字段的具体含义和用途我们先不着急去了解,在后面结合具体的流程来更方便的去理解。

Pipeline Processing

在了解了上述两张table后,我们不禁会想:在交换机运行过程中是如何配合这两张表来实现流量转发的呢?

这就不得不提到openflow中的一个重要概念:pipeline processing。什么是pipeline?如果把每个table当成一道加工工艺的话,pipeline就可以看成是一条加工流水线。在这条流水线上,多个flow table按照其数字编号从小到大排列着(table0, table1, table2 …… table N),进入交换机的数据包都是从pipeline的第一个flow table也就是table0开始处理,按照上文描述的那样,对table0中的条目一条一条开始匹配,如果匹配成功则执行相应的动作(这些动作可能导致从flow table跳到group table中),若果没有所有条目都没有成功匹配,则根据table自身的不同配置执行相应的处理(Table Miss Flow Entry):

  • drop
  • 转发给下一个table
  • 发送给controller
Table Miss Flow Entry是flow table中专门处理没有成功匹配的数据包的一个条目,它的Match Fields能够匹配任何数据包,并且Priority为0,也就是会被最后执行。

Table Miss Flow Entry不是必须存在的,当不存在的时候,对没成功匹配的数据包会执行table中的默认动作。

形象点的话我们可以通过下面这张图来理解:

pipeline.jpg

这张图是我从网络上复制过来的,相关文章可以在最后的参考链接中查看。

在这张图中我们可以很形象的看到flow table和group table在数据转发过程中的地位。

在Open vSwitch上的应用

OVS对Open Flow的支持情况

在使用open vswitch来实际应用open flow之前,我们有必要先了解一下ovs目前对openflow协议各版本的支持情况。为此,我在官网找到了如下这张表(2018年4月):

Open vSwitch versionOF1.0OF1.1OF1.2OF1.3OF1.4OF1.5OF1.6
<= 1.9yes
1.10, 1.11yes(*)(*)
2.0, 2.1yse(*)(*)(*)
2.2yes(*)(*)(*)(%)(*)
2.3, 2.4yesyesyesyes(*)(*)
2.5, 2.6, 2.7yesyesyesyes(*)(*)(*)
2.8yesyesyesyesyes(*)(*)
  • —: Not supported.
  • yes: Supported and enabled by default
  • (*): Supported, but missing features, and must be enabled by user.
  • (%): Experimental, unsafe implementation.

本文中使用的Open vSwitch版本为2.5.0,默认支持Open Flow 1.3。

操作Open Flow的工具

在实际的环境中,基本上都是由controller来操作修改交换机的open flow。本实验将通过使用ovs-ofctl工具手动来配置修改OVS交换机的open flow。

ovs-ofctl的使用方法可以参考官方文档:Open vSwitch Manual : ovs-ofctl

基本实验环境

环境拓扑

ovs-flow-env.png

环境构筑

# 创建namespace
ip netns add ns1
ip netns add ns2
ip netns add ns3
ip netns add ns4

# 创建tap设备
ip link add tap0 type veth peer name tap0_br
ip link add tap1 type veth peer name tap1_br
ip link add tap2 type veth peer name tap2_br
ip link add tap3 type veth peer name tap3_br
ip link add tap4 type veth peer name tap4_br
ip link add tap5 type veth peer name tap5_br
ip link add tap6 type veth peer name tap6_br
ip link add tap7 type veth peer name tap7_br

# 设置tap设备的namespace
ip link set tap0 netns ns1
ip link set tap1 netns ns1
ip link set tap2 netns ns2
ip link set tap3 netns ns2
ip link set tap4 netns ns3
ip link set tap5 netns ns3
ip link set tap6 netns ns4
ip link set tap7 netns ns4

# 创建OVS网桥
ovs-vsctl add-br vswitch0

# 将tap设备另一端绑到网桥
ovs-vsctl add-port vswitch0 tap0_br
ovs-vsctl add-port vswitch0 tap1_br
ovs-vsctl add-port vswitch0 tap2_br
ovs-vsctl add-port vswitch0 tap3_br
ovs-vsctl add-port vswitch0 tap4_br
ovs-vsctl add-port vswitch0 tap5_br
ovs-vsctl add-port vswitch0 tap6_br
ovs-vsctl add-port vswitch0 tap7_br

实验一(单个table及验证其flow的优先级)

ovs_flow_project1.png

实验准备

环境配置

# 启动tap0和tap3及它们的对端
ip netns exec ns1 ip link set tap0 up
ip netns exec ns2 ip link set tap3 up
ip link set tap0_br up
ip link set tap3_br up

# 设置tap0和tap3的ip地址
ip netns exec ns1 ip addr add 192.168.1.100 dev tap0
ip netns exec ns2 ip addr add 192.168.1.200 dev tap3

# 配置路由
ip netns exec ns1 route add -net 192.168.1.0 netmask 255.255.255.0 dev tap0
ip netns exec ns2 route add -net 192.168.1.0 netmask 255.255.255.0 dev tap3

# 测试网络连通性
ip netns exec ns1 ping 192.168.1.200
>> 64 bytes from 192.168.1.200: icmp_seq=1 ttl=64 time=0.030 ms
>> 64 bytes from 192.168.1.200: icmp_seq=2 ttl=64 time=0.055 ms

查看tap设备对应的ovs port

# ovs-vsctl list interface tap0_br | grep "ofport "
ofport              : 1
# ovs-vsctl list interface tap3_br | grep "ofport "
ofport              : 4
实验过程

首先查看一下vswitch0上的flow table:

# ovs-ofctl dump-flows vswitch0
cookie=0x0, duration=267197.837s, table=0, n_packets=459, n_bytes=42190, idle_age=387, hard_age=65534, priority=0 actions=NORMAL

发现有一条actions为NORMAL的流表项,这是默认存在的,用以实现交换机的基本动作。

当我们把这条flow删除后发现两个tap设备之间已无法ping通:

# ovs-ofctl del-flows vswitch0
# ovs-ofctl dump-flows vswitch0
NXST_FLOW reply (xid=0x4):
# ip netns exec ns1 ping 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
^C
--- 192.168.1.200 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2052ms

简单的讲,如果要实现tap0和tap3之间流量互通,其实就是要交换机将port1上的数据发往port4,port4上的数据发往port1(port1和port4分别是tap0和tap3对应的ovs端口,查看方法见上),所以我们可以添加下面两条flow:

# ovs-ofctl add-flow vswitch0 "priority=1,in_port=1,actions=output:4"
# ovs-ofctl add-flow vswitch0 "priority=2,in_port=4,actions=output:1"

当添加完这两条flow后发现tap0和tap3之间又能相互ping通了

# ip netns exec ns1 ping 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
64 bytes from 192.168.1.200: icmp_seq=1 ttl=64 time=0.396 ms
64 bytes from 192.168.1.200: icmp_seq=2 ttl=64 time=0.050 ms
^C
--- 192.168.1.200 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1030ms
rtt min/avg/max/mdev = 0.050/0.223/0.396/0.173 ms

可以看到我们给这两个flow设置了优先级分别是1和2,根据上文所讲,flow优先级越高,会优先匹配,所以我们再增加下面的flow来测试:

ovs-ofctl add-flow vswitch0 "priority=3,in_port=1,actions=drop"

这条flow是将所有从port1上的数据全部drop掉,如果成功匹配,则tap0和tap3将不再互通。

# ip netns exec ns1 ping 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
^C
--- 192.168.1.200 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2057ms

发现果然ping不通了,这就验证了上文的描述:优先级越高,优先匹配。

实验二(多个table)

ovs_flow_project1.png

在实验一的基础上继续往下做。

首先清除所有的flow,重新开始配置

# ovs-ofctl del-flows vswitch0

还记得转发的两条flow吗?在实验一中我们配置到了table0,现在将他们配置到table1

# ovs-ofctl add-flow vswitch0 "table=1,priority=1,in_port=1,actions=output:4"
# ovs-ofctl add-flow vswitch0 "table=1,priority=2,in_port=4,actions=output:1"
# ovs-ofctl dump-flows vswitch0
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=3.485s, table=1, n_packets=0, n_bytes=0, idle_age=3, priority=1,in_port=1 actions=output:4
 cookie=0x0, duration=3.033s, table=1, n_packets=0, n_bytes=0, idle_age=3, priority=2,in_port=4 actions=output:1

尝试ping对端设备,发现无法ping通,因为数据包从table0开始处理,当前table0中没有匹配的flow,所以数据包被drop掉了。

# ip netns exec ns1 ping 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
^C
--- 192.168.1.200 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2026ms

现在给table0加上一条将数据包发送到table1处理的flow

# ovs-ofctl add-flow vswitch0 "table=0,actions=goto_table=1"

再次尝试ping对端的设备,可以正常ping通

# ip netns exec ns1 ping 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
64 bytes from 192.168.1.200: icmp_seq=1 ttl=64 time=0.309 ms
64 bytes from 192.168.1.200: icmp_seq=2 ttl=64 time=0.050 ms
^C
--- 192.168.1.200 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1054ms
rtt min/avg/max/mdev = 0.050/0.179/0.309/0.130 ms

该实验简单演示了多个table之间是如何协调工作的。

实验三(group table)

ovs_flow_project1.png

继续使用这个环境,第一步还是清除所有的flow

# ovs-ofctl del-flows vswitch0

查看交换机上存在的group表,发现当前没有任何group table。

# ovs-ofctl -O OpenFlow13 dump-groups vswitch0
OFPST_GROUP_DESC reply (OF1.3) (xid=0x2):

我们计划让数据包从table0发送到group table处理,再发送到table1处理。

首先创建一个group table,作用是将数据包发往table1处理。

# ovs-ofctl -O OpenFlow13 add-group vswitch0 "group_id=1,type=select,bucket=resubmit(,1)"
# ovs-ofctl -O OpenFlow13 dump-groups vswitch0
OFPST_GROUP_DESC reply (OF1.3) (xid=0x2):
 group_id=1,type=select,bucket=actions=resubmit(,1)

在table0中增加两条flow,目的是将数据包发送到group table1

# ovs-ofctl -O OpenFlow13 add-flow vswitch0 "table=0,in_port=1,actions=group:1"
# ovs-ofctl -O OpenFlow13 add-flow vswitch0 "table=0,in_port=4,actions=group:1"

向table1中增加两条flow,真正的数据转发在table1中进行。

# ovs-ofctl add-flow vswitch0 "table=1,priority=1,in_port=1,actions=output:4"
# ovs-ofctl add-flow vswitch0 "table=1,priority=2,in_port=4,actions=output:1"

尝试ping对端,可以成功ping通,说明数据包被正确处理。

# ip netns exec ns1 ping 192.168.1.200
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
64 bytes from 192.168.1.200: icmp_seq=1 ttl=64 time=0.329 ms
64 bytes from 192.168.1.200: icmp_seq=2 ttl=64 time=0.048 ms
^C
--- 192.168.1.200 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1048ms
rtt min/avg/max/mdev = 0.048/0.188/0.329/0.141 ms

通过上面三个实验,可以对openflow的工作原理及流程有个大概的认识,比较简单。实际应用的话会更复杂一些,比如vlan id的映射,数据包的修改等,最主要还是看场景和需求,人的想象力和执行力是无限的,要相信没有做不到,只有想不到。

参考:

( 1 ): 基于 OpenFlow 实现网络虚拟化

( 2 ): openflow-spec-v1.3.1