Scenario(场景)
我们将为具有四个端口,支持VLAN的MAC学习的交换机构建Open vSwitch流表:
p1
在OpenFlow端口1上承载所有VLAN的trunk 端口。
p2
在OpenFlow端口2上的VLAN 20的access端口。
p3,p4
分别在OpenFlow端口3和4上的VLAN 30的两个access端口。
note:
端口的名称不重要。你可以称它们为eth1到eth4,或者任何其他你喜欢的名字。
OpenFlow交换机也总是有一个“local”端口。这种情况不会使用local端口。
我们的交换机设计将由五个主流表组成,每个主流表实现交换机流水线中的一个阶段:
Table 0
Admission control.(准入控制)
Table 1
VLAN input processing.
Table 2
Learn source MAC and VLAN for ingress port.
Table 3
为目的MAC和VLAN查找学习到的端口。(Look up learned port for destination MAC and VLAN.)
Table 4
Output processing.
下面的部分描述了如何实现scenario。
Setup
To get started, 请启动ovs-sandbox。在启动的交互式shell中,运行以下命令:
$ ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure
此命令创建一个新的网桥“br0”,并将“br0“置于所谓的“故障保护(fail-secure)”模式。出于我们的目的,这只是意味着OpenFlow流表一开始是空的。
到目前为止,新桥上只有一个端口,即“本地端口”br0。我们需要添加p1、p2、p3和p4。通过shell的循环语句实现:
for i in 1 2 3 4; do ovs-vsctl add-port br0 p$i -- set Interface p$i ofport_request=$i ovs-ofctl mod-port br0 p$i up done
除了添加一个端口外,上面的ovs-vsctl命令还设置其ofport_request列,以确保端口p1被分配给OpenFlow端口1,p2被分配给OpenFlow端口2,依此类推。
note:
我们可以省略设置port_request,让Open vSwitch为我们选择端口号,但对于本教程来说,这很方便,因为我们可以讨论OpenFlow端口1,并知道它对应于p1。
上面的ovs-ofctl命令使用OpenFlow请求打开模拟的接口,这些接口最初是关闭的。效果类似于 ip link up, 但sandbox的接口对操作系统不可见,因此ip不会影响它们。
我们没有配置任何与VLAN或MAC学习相关的内容。这是因为我们将在流表中实现这些特性。
添加流表之前查看br0上的流表为空
Implementing Table 0: Admission control
术语解释:
-
openflow中dl_src=和dl_dst= 匹配源或者目标的 MAC 地址
-
dl_dst=xx:xx:xx:xx:xx:xx 匹配指定的链路层目的MAC地址
-
dl_dst=xx:xx:xx:xx:xx:xx/xx:xx:xx:xx:xx:xx 匹配指定的链路层MAC地址,MAC地址格式为ADDR/MASK,当MASK值为01:00:00:00:00:00时,仅匹配多播位。当dl_dst=01:00:00:00:00:00/01:00:00:00:00:00时,匹配所有的组播报文和广播报文。dl_dst=00:00:00:00:00:00/01:00:00:00:00:00匹配所有的单播报文。
Table 0是数据包进入交换机的位置。我们在这一步丢弃由于某种原因而无效的数据包。例如,具有多播源地址的数据包是无效的,因此我们可以添加一个流,将它们丢弃在交换机的入口,方法是:
屏蔽所有进入 OVS 的以太网广播数据包
$ ovs-ofctl add-flow br0 \ "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop"
要查看到目前为止我们为启动scenario所做的工作,您可以运行类似ovs-vsctl show或ovs-ofctl show br0的命令。
交换机也不应该转发IEEE 802.1D Spanning Tree Protocol (STP)数据包,因此我们也可以添加流来丢弃那些和其他具有保留多播协议的数据包:
屏蔽 STP 协议的广播数据包
$ ovs-ofctl add-flow br0 \ "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"
我们可以添加流来丢弃其他协议,上面这些流表展示了该如何添加流表。
我们还需要一个优先级低于默认值的流,这样与我们上面添加的任何一个“丢弃”流都不匹配的流就可以进入OpenFlow 的table 1(步骤1):
$ ovs-ofctl add-flow br0 "table=0, priority=0, actions=resubmit(,1)"
Implementing Table 1: VLAN Input Processing
进入表1的数据包已通过表0中的基本验证。表1的目的是根据数据包进入交换机的交换机端口的VLAN配置来验证数据包的VLAN。我们还将使用它将VLAN标头附加到到达access端口的数据包,这允许稍后的处理阶段依赖于数据包的VLAN始终是VLAN标头的一部分,从而减少特殊情况。
让我们先添加一个丢弃所有数据包的低优先级流,然后再添加通过可接受数据包的流。您可以将其视为“默认丢弃”流:
$ ovs-ofctl add-flow br0 "table=1, priority=0, actions=drop"
我们的trunk port p1在OpenFlow port 1是一个简单的例子。p1接受任何数据包,无论它是否具有VLAN标头或VLAN是什么,因此我们可以添加一个流,将输入端口1上的所有内容重新提交到下一个表:
$ ovs-ofctl add-flow br0 \ "table=1, priority=99, in_port=1, actions=resubmit(,2)"
在access端口上,我们希望接受任何没有VLAN标头的数据包,用access端口的VLAN编号对其进行标记,然后将其传递到下一阶段:
$ ovs-ofctl add-flows br0 - <<'EOF' table=1, priority=99, in_port=2, vlan_tci=0, actions=mod_vlan_vid:20, resubmit(,2) table=1, priority=99, in_port=3, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) table=1, priority=99, in_port=4, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) EOF
Implementing Table 2: MAC+VLAN Learning for Ingress Port
此表允许我们实现的交换机学习数据包的源MAC通过数据包ingress的端口及数据包的VLAN。
只需要一条流就可以做到这一点。以下命令将添加它:
$ ovs-ofctl add-flow br0 \ "table=2 actions=learn(table=10, NXM_OF_VLAN_TCI[0..11], \ NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], \ load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]), \ resubmit(,3)"
“学习”操作(OpenFlow的Open vSwitch扩展)根据当前正在处理的流的内容修改流表。以下是您如何解释上述“学习”动作的每个部分:
table=10
修改流表10。这将是MAC学习表。
NXM_OF_VLAN_TCI[0..11]
使我们添加到流表10中的流与我们当前处理的数据包包含的相同VLAN ID匹配。这有效地将MAC学习条目范围限定到单个VLAN,这是VLAN-aware(感知VLAN)交换机的常见行为。
NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]
使我们添加到流表10中的流与我们当前处理的数据包的以太网源地址匹配,作为学习到的路由表中以太网目的地址。
load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]
前面的部分指定了新的流要匹配的字段,而这指定了流在匹配时要采取的操作。该操作用于流将当前数据包的入口端口号加载到寄存器0(一个特殊字段,是OpenFlow的Open vSwitch扩展)。
Implementing Table 3: Look Up Destination Port
此表根据目的地MAC和VLAN计算出应将数据包发送到哪个端口。也就是说,如果我们已经了解了目的地的位置(从表2中处理的先前的以该目的mac为源mac的数据包),那么我们希望将数据包发送到那里。
We need only one flow to do the lookup:
$ ovs-ofctl add-flow br0 \ "table=3 priority=50 actions=resubmit(,10), resubmit(,4)"
流的第一个操作重新提交到表10,即“学习”操作修改的表。如前所述,此表中的学习流将学习端口写入寄存器0。如果我们的数据包的目的地还没有被获知,那么就没有匹配的流,因此“重新提交”变成了no-op。因为寄存器被初始化为0,我们可以在下一个流水线阶段使用寄存器0值0作为信号来flood (泛洪)数据包。
第二个操作重新提交到表4,继续到下一个流水线阶段。
我们可以添加另一个流来跳过多播和广播数据包的学习表查找,因为这些数据包应该总是会泛洪(flooded):
$ ovs-ofctl add-flow br0 \ "table=3 priority=99 dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 \ actions=resubmit(,4)"
Implementing Table 4: Output Processing
我们知道寄存器0包含所需的输出端口,或者如果数据包应该被flooded(泛洪),则为零。我们还知道,数据包的VLAN在其802.1Q标头中,即使VLAN是隐式的,因为数据包来自access端口。
最后一个流水线阶段的工作是实际输出数据包。对于输出到我们的trunk端口p1来说,这项工作是微不足道的:
$ ovs-ofctl add-flow br0 "table=4 reg0=1 actions=1"
对于到access端口的输出,我们只需要在输出数据包之前剥离VLAN标头:
$ ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=2 actions=strip_vlan,2 table=4 reg0=3 actions=strip_vlan,3 table=4 reg0=4 actions=strip_vlan,4 EOF
唯一稍微棘手的部分是用未学习的目的地泛洪(flooding)多播和广播数据包以及单播数据包。对于这些,我们需要确保我们只将数据包输出到承载数据包VLAN的端口,并且我们在到trunk端口的副本输出中包括802.1Q标头,但在到access端口的副本中不包含:
$ ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=0 priority=99 dl_vlan=20 actions=1,strip_vlan,2 table=4 reg0=0 priority=99 dl_vlan=30 actions=1,strip_vlan,3,4 table=4 reg0=0 priority=50 actions=1 EOF
Testing Table
Example 1: Broadcast, Multicast, and Unknown Destination
尝试跟踪到达p1的VLAN 30广播数据包:
$ ovs-appctl ofproto/trace br0 \ in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=30
输出的有趣部分是最后一行,它显示交换机将删除802.1Q报头,然后将数据包输出到p3和p4,这两个端口是VLAN 30的访问端口。
类似地,如果我们追踪到达p3的广播数据包:
$ ovs-appctl ofproto/trace br0 in_port=3,dl_dst=ff:ff:ff:ff:ff:ff
则我们看到它被输出到具有802.1Q标签的p1,然后被输出到没有802.1Q标签。
可以在多播数据包和目的地尚未获知的单播数据包中看到相同的行为,例如:
$ ovs-appctl ofproto/trace br0 \ in_port=4,dl_dst=01:00:00:00:00:00 $ ovs-appctl ofproto/trace br0 \ in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=20 $ ovs-appctl ofproto/trace br0 \ in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=30
Example 2: MAC Learning
让我们遵循与表3相同的模式。首先学习p1端口VLAN 30中MAC:
$ ovs-appctl ofproto/trace br0 \ in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 \ -generate
您可以从最后一行输出中看到,数据包的目的地是未知的,因此它被泛洪到p3和p4,即VLAN 30的其他端口。
然后反转MAC并在端口p4上学习第一个流的目的地:
$ ovs-appctl ofproto/trace br0 \ in_port=4,dl_src=20:00:00:00:00:01,dl_dst=10:00:00:00:00:01 -generate
最后一行输出显示,从之前的数据包学习到的地址,当前包的目的地是发送到p1端口。
现在,如果我们重新运行第一个命令:
$ ovs-appctl ofproto/trace br0 \ in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 \ -generate
我们可以看到,结果不再是泛洪,而是到达指定的学习目的地端口p4。