在本篇文章中,我将解释什么是Logical Flow以及如何使用ovn-trace去更好地理解它们。同时,我也会用一些例子来解释,为什么使用Logical Flow这种抽象模型能让新特性的添加变得出乎意料的简单。

But First, OpenFlow Basics

在深入Logical Flow之前,对于OpenFlow有一个基本的了解是非常必要的。OpenFlow是一个用来对Open vSwitch的packet processing pipeline进行编辑的协议。它能够让你定义一系列伴有rule(flow)的table,每个rule中还包含了priority,match,和一系列的action。对于每个table,能够匹配的flow中优先级最高(数字越大,优先级越高)的将被执行。

首先,让我们来想象一个简单的virtual switch,它有着两个端口,port1和port2

                        +--------+
(1) | | (2)
port1 -------| br-int |-------- port2
| |
+--------+

我们可以用以下命令创建一个有着两个port的bridge:

$ ovs-vsctl add-br br-int
$ ovs-vsctl add-port br-int port1
$ ovs-vsctl add-port br-int port2 $ ovs-vsctl show
3b1995d8-9683-45db-8929-36c62abdbd31
Bridge br-int
Port "port1"
Interface "port1"
Port br-int
Interface br-int
type: internal
Port "port2"
Interface "port2"

  

最简单的例子是创建一个table让所有从port1进入的包转发到port2,所有从port2进入的包转发到port1

    Table  Priority  Match      Actions
----- -------- ---------- -------
0 0 in_port=1 output:2
0 0 in_port=2 output:1

我们可以用ovs-ofctl命令来编辑ovs中的pipeline:

$ ovs-ofctl del-flows br-int
$ ovs-ofctl add-flow br-int
$ ovs-ofctl add-flow br-int "table=0, priority=0, in_port=1, actions=output:2"
$ ovs-ofctl add-flow br-int "table=0, priority=0, in_port=2, actions=output:1" $ ovs-ofctl dump-flows br-int
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=9.679s, table=0, n_packets=0, n_bytes=0, idle_age=9, priority=0,in_port=1 actions=output:2
cookie=0x0, duration=2.287s, table=0, n_packets=0, n_bytes=0, idle_age=2, priority=0,in_port=2 actions=output:1

  

我们还可以对这个例子进行扩展,从而来演示不同priority的使用。现在我们只允许源mac地址为00:00:00:00:01的包进入port1以及只允许源mac地址为00:00:00:00:00:02的包进入port2(basic source port security)。我们接着使用table 0来实现port security,再用table 1决定包的走向。

(当然,这些都可以在一个table中完成,但是这样就不能演示table和priority的使用了,而这恰恰是这个例子的主要目的)

    Table  Priority  Match                               Actions
----- -------- ----------------------------------- ------------
0 10 in_port=1,dl_src=00:00:00:00:00:01 resubmit(,1)
0 10 in_port=2,dl_src=00:00:00:00:00:02 resubmit(,1)
0 0 drop
1 0 in_port=1 output:2
1 0 in_port=2 output:1

同样,我们使用ovs-ofctl编辑如下pipeline:

$ ovs-ofctl del-flows br-int
$ ovs-ofctl add-flow br-int "table=0, priority=10, in_port=1, dl_src=00:00:00:00:00:01, actions=resubmit(,1)"
$ ovs-ofctl add-flow br-int "table=0, priority=10, in_port=2, dl_src=00:00:00:00:00:02, actions=resubmit(,1)"
$ ovs-ofctl add-flow br-int "table=0, priority=0, actions=drop"
$ ovs-ofctl add-flow br-int "table=1, priority=0, in_port=1,actions=output:2"
$ ovs-ofctl add-flow br-int "table=1, priority=0, in_port=2,actions=output:1" $ ovs-ofctl dump-flows br-int
NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=72.132s, table=0, n_packets=0, n_bytes=0, idle_age=72, priority=10,in_port=1,dl_src=00:00:00:00:00:01 actions=resubmit(,1)
cookie=0x0, duration=60.565s, table=0, n_packets=0, n_bytes=0, idle_age=60, priority=10,in_port=2,dl_src=00:00:00:00:00:02 actions=resubmit(,1)
cookie=0x0, duration=28.127s, table=0, n_packets=0, n_bytes=0, idle_age=28, priority=0 actions=drop
cookie=0x0, duration=13.887s, table=1, n_packets=0, n_bytes=0, idle_age=13, priority=0,in_port=1 actions=output:2
cookie=0x0, duration=4.023s, table=1, n_packets=0, n_bytes=0, idle_age=4, priority=0,in_port=2 actions=output:1

  

Open vSwitch还提供了一种能够追踪包从整个pipeline流通路径的机制。在这里,我们将追踪一个有着期望的mac地址的包从port1流入的过程。追踪程序的输出显示包被重新提交到了table 1并且之后从port 2输出了。输出的内容有点啰嗦,我们只要看"Rule"和"OpenFlow actions"这些列就可以知道哪些flow被执行了。

$ ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 -generate
Bridge: br-int
Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,dl_type=0x0000 Rule: table=0 cookie=0 priority=10,in_port=1,dl_src=00:00:00:00:00:01
OpenFlow actions=resubmit(,1) Resubmitted flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,dl_type=0x0000
Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
Resubmitted odp: drop
Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:01,dl_type=0x0000
Rule: table=1 cookie=0 priority=0,in_port=1
OpenFlow actions=output:2 Final flow: unchanged
Megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:01,dl_type=0x0000
Datapath actions: 3

  

OpenFlow可以用来构建更加复杂的pipeline。如果想知道更多细节,参见ovs-ofctl(8) man page。

OVN Logical Flows

上一节对OpenFlow进行了概述,展示了如何使用OpenFlow在单个switch里面构建packet processing pipelines。即使只在一台机器上对这些pipeline进行配置都让人觉得很麻烦,更不要说配置成千上万的机器了。但是只要有一个SDN controller,我们就能轻松地对许多switch进行配置,而这正是OVN在Open vSwtich这个项目中扮演的角色。OVN会完成所有OpenFlow的配置工作,用以实现你通过高层的接口配置的network topologies以及security policies。

那么OVN是怎么知道每台机器上需要的flow的呢?OVN解决这个问题的核心抽象模型就是Logical Flow。从概念上来说,Logical Flows和OpenFlow是类似的,它们都由table组成,table中包含flow,每个flow都有priority,match和action。两者最大的不同是,logical flow对整个网络的行为进行了详细地描述并且能扩展到任意数量的主机上。并且它把对于具体网络行为的定义和对于现实的物理设备的布局(有多少机器以及机器端口的分布)分离了开来。

OVN通过logical flow对网络进行编辑。之后,这些logical flow又会分发到每台机器上运行ovn-controller中。而ovn-controller知道根据当前的物理环境(本地端口在哪以及如何到达其他机器)将这些logical flow编译到OpenFlow中。

接着让我们用OVN来创建一个和上一节类似的例子。我们要创建的是一个带有两个logical ports的OVN logical switch。

$ ovn-nbctl ls-add sw0

$ ovn-nbctl lsp-add sw0 sw0-port1
$ ovn-nbctl lsp-set-address sw0-port1 00:00:00:00:00:01
$ ovn-nbctl lsp-set-port-security sw0-port1 00:00:00:00:00:01 $ ovn-nbctl lsp-add sw0 sw0-port2
$ ovn-nbctl lsp-set-addresses sw0-port2 00:00:00:00:00:02
$ ovn-nbctl lsp-set-port-security sw0-port2 00:00:00:00:00:02 $ ovn-nbctl show sw0
switch 48d5f699-7ffe-4627-a369-2fc905e44b32 (sw0)
port sw0-port1
addresses: ["00:00:00:00:00:01"]
port sw0-port2
addresses: ["00:00:00:00:00:02"]

  

OVN定义了一个logical switch,sw0,使用了两个pipeline:一个ingress pipeline以及一个egress pipeline。当一个包流入网络时,生成该包的机器上的ingress pipeline将被执行。如果该包的目的地是同一台机器,那么egress pipeline也将被执行。

sw0-port1 and sw0-port2 on the same host:

    +--------------------------------------------------------------------------+
| |
| Host A |
| |
| +---------+ +---------+ |
| |sw0-port1| --> ingress pipeline --> egress pipeline --> |sw0-port2| |
| +---------+ +---------+ |
| |
+--------------------------------------------------------------------------+

如果该包的目的地在远程机器上,那么该包首先要经过一个tunnel进行传输,然后在远程机器上执行egress pipeline

sw0-port1 and sw0-port2 on separate hosts:

    +--------------------------------------+
| |
| Host A |
| |
| +---------+ |
| |sw0-port1| --> ingress pipeline |
| +---------+ || |
| || |
+-------------------------||-----------+
||
\/
geneve tunnel
||
||
+-------------------------||-----------+
| || |
| Host B || |
| || |
| +---------+ \/ |
| |sw0-port2| < -- egress pipeline |
| +---------+ |
| |
+--------------------------------------+

你可以使用"ovn-sbctl lflow-list"命令来查看完整的logical flow。它看起来会和OpenFlow有些相似,但是还是有非常不一样的地方的:

  1. Ports是一个逻辑概念,它位于网络中的某处,而不是一个switch中的physical ports
  2. pipeline中的每一个table除了编号以外还多了一个名字。这个名字描述了pipeline在该阶段的作用
  3. 匹配更灵活。支持复杂的布尔运算,对于程序员应该是很熟悉的了
  4. OVN logical flows扩展的action远远超过了OpenFlow。我们可以用logical flow的语法控制一些高级的特性,例如DHCP。具体的match和syntax的语法可以参见ovn-sb(5)中关于Logical_Flow的文档

在本例中,pipeline中还保留了很多额外的stage,用于未被用到的特性,所以下面很多table的flow实际上没有做任何事情

    $ ovn-sbctl lflow-list
Datapath: "sw0" (d7bf4a7b-e915-4502-8f9d-5995d33f5d10) Pipeline: ingress
table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;)
table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;)
table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;)
table=1 (ls_in_port_sec_ip ), priority=0 , match=(1), action=(next;)
table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && arp.sha == 00:00:00:00:00:01), action=(next;)
table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:01) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:01)))), action=(next;)
table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && arp.sha == 00:00:00:00:00:02), action=(next;)
table=2 (ls_in_port_sec_nd ), priority=90 , match=(inport == "sw0-port2" && eth.src == 00:00:00:00:00:02 && ip6 && nd && ((nd.sll == 00:00:00:00:00:00 || nd.sll == 00:00:00:00:00:02) || ((nd.tll == 00:00:00:00:00:00 || nd.tll == 00:00:00:00:00:02)))), action=(next;)
table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port1" && (arp || nd)), action=(drop;)
table=2 (ls_in_port_sec_nd ), priority=80 , match=(inport == "sw0-port2" && (arp || nd)), action=(drop;)
table=2 (ls_in_port_sec_nd ), priority=0 , match=(1), action=(next;)
table=3 (ls_in_pre_acl ), priority=0 , match=(1), action=(next;)
table=4 (ls_in_pre_lb ), priority=0 , match=(1), action=(next;)
table=5 (ls_in_pre_stateful ), priority=100 , match=(reg0[0] == 1), action=(ct_next;)
table=5 (ls_in_pre_stateful ), priority=0 , match=(1), action=(next;)
table=6 (ls_in_acl ), priority=0 , match=(1), action=(next;)
table=7 (ls_in_qos_mark ), priority=0 , match=(1), action=(next;)
table=8 (ls_in_lb ), priority=0 , match=(1), action=(next;)
table=9 (ls_in_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit(ct_label=0/1); next;)
table=9 (ls_in_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;)
table=9 (ls_in_stateful ), priority=0 , match=(1), action=(next;)
table=10(ls_in_arp_rsp ), priority=0 , match=(1), action=(next;)
table=11(ls_in_dhcp_options ), priority=0 , match=(1), action=(next;)
table=12(ls_in_dhcp_response), priority=0 , match=(1), action=(next;)
table=13(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
table=13(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;)
table=13(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;)
Datapath: "sw0" (d7bf4a7b-e915-4502-8f9d-5995d33f5d10) Pipeline: egress
table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;)
table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;)
table=2 (ls_out_pre_stateful), priority=100 , match=(reg0[0] == 1), action=(ct_next;)
table=2 (ls_out_pre_stateful), priority=0 , match=(1), action=(next;)
table=3 (ls_out_lb ), priority=0 , match=(1), action=(next;)
table=4 (ls_out_acl ), priority=0 , match=(1), action=(next;)
table=5 (ls_out_qos_mark ), priority=0 , match=(1), action=(next;)
table=6 (ls_out_stateful ), priority=100 , match=(reg0[1] == 1), action=(ct_commit(ct_label=0/1); next;)
table=6 (ls_out_stateful ), priority=100 , match=(reg0[2] == 1), action=(ct_lb;)
table=6 (ls_out_stateful ), priority=0 , match=(1), action=(next;)
table=7 (ls_out_port_sec_ip ), priority=0 , match=(1), action=(next;)
table=8 (ls_out_port_sec_l2 ), priority=100 , match=(eth.mcast), action=(output;)
table=8 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;)
table=8 (ls_out_port_sec_l2 ), priority=50 , match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;)

  

立即logical flow最好的方式就是使用ovn-trace命令。ovn-trace能够让你看到OVN对一个包是怎么处理的。

ovn-trace需要两个参数:

$ ovn-trace DATAPATH MICROFLOW

  

DATAPATH标识了sample packet开始进入的logical datapath(一个logical switch或者一个logical router)。MICROFLOW描述了模拟的sample packet。具体的细节详见ovn-trace(8) man page。

给定上文中的OVN配置,让我们来看看OVN会怎么处理一个由sw0-port1进入并将发往sw0-port2的包。ovn-trace的输出结果有多个详略程度可供选择。第一个是--minimal,它会告诉你这个包发生了些什么,但是会省略很多不必要的细节。在这个例子中,我们将看到的最终结果是该包将被发往sw0-port2。

$ ovn-trace --minimal sw0 'inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && eth.dst == 00:00:00:00:00:02'
# reg14=0x1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,dl_type=0x0000 output("sw0-port2");

  

下一个等级是--summary。在这个模式,我们能知道更多的关于包处理的细节,包括那个pipeline被执行了。如果我们对于同一个sample packet使用ovn-trace,我们将看到如下内容:

  1. 包从sw0-port1进入网络(sw0)并且运行了ingress pipeline
  2. 我们可以看到"sw0-port2"设置到了"output"这个变量中,这以为着该包的目的端口是"sw0-port2"
  3. 包从ingress pipeline输出,并且输出到"sw0"的egress pipeline并且输出变量设置为"sw0-port2"
  4. egress pipeline中的output action将被执行,它将把包输出到当前"output"变量对应的值中,即"sw0-port2"
    $ ovn-trace --summary sw0 'inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && eth.dst == 00:00:00:00:00:02'
# reg14=0x1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,dl_type=0x0000
ingress(dp="sw0", inport="sw0-port1") {
outport = "sw0-port2";
output;
egress(dp="sw0", inport="sw0-port1", outport="sw0-port2") {
output;
/* output to "sw0-port2", type "" */;
};
};

  

当你在debug或者要修改代码的时候,你可能需要更细节的输出。ovn-trace有一个--detail选项。在这种模式下,你将获得更多的细节,关于包流动过程中遇到的每一个meaningful logical flow。你可以看到table number,pipeline stage name,full match,以及flow的priority number。你还可以看到负责创建该logical flow的OVN源代码的位置

    $ ovn-trace --detailed sw0 'inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && eth.dst == 00:00:00:00:00:02'
# reg14=0x1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02,dl_type=0x0000 ingress(dp="sw0", inport="sw0-port1")
-------------------------------------
0. ls_in_port_sec_l2 (ovn-northd.c:2827): inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}, priority 50
next(1);
13. ls_in_l2_lkup (ovn-northd.c:3095): eth.dst == 00:00:00:00:00:02, priority 50
outport = "sw0-port2";
output; egress(dp="sw0", inport="sw0-port1", outport="sw0-port2")
---------------------------------------------------------
8. ls_out_port_sec_l2 (ovn-northd.c:3170): outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}, priority 50
output;
/* output to "sw0-port2", type "" */

  

另一个使用ovn-trace的例子是观察一个包为什么会被丢弃。我们已经设置了port security,现在让我们来看看从sw0-port1发出一个有着非预期源mac地址的包,它的执行路径是什么样的。从输出我们可以看到,这个包进入了sw0但是不能匹配table 0中任何一个flow,这意味着这个包会被丢弃。我们还可以看到table 0被命名为"ls_in_port_sec_l2",其实就是"Logical Switch ingress L2 port security"的缩写。

    $ ovn-trace --detailed sw0 'inport == "sw0-port1" && eth.src == 00:00:00:00:00:ff && eth.dst == 00:00:00:00:00:02'
# reg14=0x1,vlan_tci=0x0000,dl_src=00:00:00:00:00:ff,dl_dst=00:00:00:00:00:02,dl_type=0x0000 ingress(dp="sw0", inport="sw0-port1")
-------------------------------------
0. ls_in_port_sec_l2: no match (implicit drop)

  

另一个相似的例子是,一个包有着未知的目的mac地址。在这种情况下,我们将看到这个包成功通过了table 0,但是未能匹配table 13,"ls_in_l2_lkup","Logical Switch ingress L2 lookup"的缩写。

    $ ovn-trace --detailed sw0 'inport == "sw0-port1" && eth.src == 00:00:00:00:00:01 && eth.dst == 00:00:00:00:00:ff'
# reg14=0x1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:ff,dl_type=0x0000 ingress(dp="sw0", inport="sw0-port1")
-------------------------------------
0. ls_in_port_sec_l2 (ovn-northd.c:2827): inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}, priority 50
next(1);
13. ls_in_l2_lkup: no match (implicit drop)

  

到目前为止我们已经看过了在单个L2 logical network中的几个例子。接下来我们来创建一个新的环境用来演示ovn-trace怎么跨多个网络进行工作。我们将会创建两个network,每个有2个port,并且将他们连接到一个logical router上。

#!/bin/bash

# Create the first logical switch and its two ports.
ovn-nbctl ls-add sw0 ovn-nbctl lsp-add sw0 sw0-port1
ovn-nbctl lsp-set-addresses sw0-port1 "00:00:00:00:00:01 10.0.0.51"
ovn-nbctl lsp-set-port-security sw0-port1 "00:00:00:00:00:01 10.0.0.51" ovn-nbctl lsp-add sw0 sw0-port2
ovn-nbctl lsp-set-addresses sw0-port2 "00:00:00:00:00:02 10.0.0.52"
ovn-nbctl lsp-set-port-security sw0-port2 "00:00:00:00:00:02 10.0.0.52" # Create the second logical switch and its two ports.
ovn-nbctl ls-add sw1 ovn-nbctl lsp-add sw1 sw1-port1
ovn-nbctl lsp-set-addresses sw1-port1 "00:00:00:00:00:03 192.168.1.51"
ovn-nbctl lsp-set-port-security sw1-port1 "00:00:00:00:00:03 192.168.1.51" ovn-nbctl lsp-add sw1 sw1-port2
ovn-nbctl lsp-set-addresses sw1-port2 "00:00:00:00:00:04 192.168.1.52"
ovn-nbctl lsp-set-port-security sw1-port2 "00:00:00:00:00:04 192.168.1.52" # Create a logical router between sw0 and sw1.
ovn-nbctl create Logical_Router name=lr0 ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:ff:01 10.0.0.1/24
ovn-nbctl lsp-add sw0 sw0-lrp0 \
-- set Logical_Switch_Port sw0-lrp0 type=router \
options:router-port=lrp0 addresses='"00:00:00:00:ff:01"' ovn-nbctl lrp-add lr0 lrp1 00:00:00:00:ff:02 192.168.1.1/24
ovn-nbctl lsp-add sw1 sw1-lrp1 \
-- set Logical_Switch_Port sw1-lrp1 type=router \
options:router-port=lrp1 addresses='"00:00:00:00:ff:02"'

  

我们可以使用"ovn-nbctl show"命令来观察最终的逻辑网络的配置

$ ovn-nbctl show
switch bf4ba6c6-91c5-4f56-9981-72643816f923 (sw1)
port sw1-lrp1
addresses: ["00:00:00:00:ff:02"]
port sw1-port2
addresses: ["00:00:00:00:00:04 192.168.1.52"]
port sw1-port1
addresses: ["00:00:00:00:00:03 192.168.1.51"]
switch 13b80127-4b36-46ea-816a-1ba4ffd6ac57 (sw0)
port sw0-port1
addresses: ["00:00:00:00:00:01 10.0.0.51"]
port sw0-lrp0
addresses: ["00:00:00:00:ff:01"]
port sw0-port2
addresses: ["00:00:00:00:00:02 10.0.0.52"]
router 68935017-967a-4c4a-9dad-5d325a9f203a (lr0)
port lrp0
mac: "00:00:00:00:ff:01"
networks: ["10.0.0.1/24"]
port lrp1
mac: "00:00:00:00:ff:02"
networks: ["192.168.1.1/24"]

  

我们现在可以对一个包进行追踪,这个包从sw0-port1发往sw1-port2,中间需要经过路由器。minimal output将会显示最终的结果是,这个包会从sw1-port2输出。我们还将看到过程中对这个包所做的修改。当包经过logical router时,TTL会减小,源和目的mac地址都会针对下一跳进行更新。

$ ovn-trace --minimal sw0 'inport == "sw0-port1" && \
eth.src == 00:00:00:00:00:01 && \
ip4.src == 10.0.0.51 && \
eth.dst == 00:00:00:00:ff:01 && \
ip4.dst == 192.168.1.52 && \
ip.ttl == 32'
# ip,reg14=0x1,vlan_tci=0x0000,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:ff:01,nw_src=10.0.0.51,nw_dst=192.168.1.52,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=32
ip.ttl--;
eth.src = 00:00:00:00:ff:02;
eth.dst = 00:00:00:00:00:04;
output("sw1-port2");

  

如果你想要观察得更仔细,可以同样执行上述命令,将--minimal改为--summary或者--detailed即可。你也可以对sample packet进行改变,看看到底会发生什么。

A Powerful Abstraction

我在文章的最开始就已经说过,我觉得OVN的Logical Flows是非常强大的抽象,它可以让对OVN添加特性变得异常简单。既然现在我们对logical flow有了一定的了解,下面我将说明一些最近新添加的特性,用于演示向OVN添加特性是多么地简单。

SOURCE BASED ROUTING

OVN已经支持了L3 gateways,它能够提供OVN logical network和physical network的连接。一个典型的OVN网络一般会有一个L3 gateway位于某台机器上。使用单个L3 gateway的缺陷是所有发往physical network的流量必须流经L3 gateway所在的机器。

最近Gurucharan Shetty添加了在OVN logical network中对于multiple L3 gateway的支持。它能够基于源IP地址在gateway之间分发流量。

我们并不需要知道这一改变的所有细节。我只想说明的是我们仅仅需要改动非常少量的代码就能实现这一特性。让我们来看一看diffstat。

Documentation:
NEWS | 1
ovn/ovn-nb.xml | 28 +++++
ovn/utilities/ovn-nbctl.8.xml | 8 + Database schema update:
ovn/ovn-nb.ovsschema | 8 + Command line utility support for new db schema additions:
ovn/utilities/ovn-nbctl.c | 43 ++++++-- Changes to how OVN builds logical flows to add support for this feature:
ovn/northd/ovn-northd.c | 24 +++- Test code:
tests/ovn-nbctl.at | 42 ++++----
tests/ovn.at | 219 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 334 insertions(+), 39 deletions(-)

  

最终要的是ovn-northd.c中的24行代码变动。这些代码用于更新logical flow的创建。这让我非常震惊,因为这个特性对于网络的行为有着非常重要的影响,但是却只用几行的C代码就搞定了。

DSCP

另一个通过logical flow添加特性的例子是这个patch,它基于arbitrary traffic classifier为设定IP包中的DSCP域提供了支持。这个patch在OVN northbound database中添加了一个新的QoS table。在这个table中定义了一个match(或者traffic classfier)以及相应DSCP值,用于设置那些匹配该classfier的包。

这个改变对应了ovn-northd.c中的80行代码。这个patch看起来比它实际的要大一些,因为它会为QoS创建一个新的pipeline stage并且还需要对它之后的stage进行重新编号。实现这个特性,只需要能插入能够匹配配置好的match(traffic classfier),之后在执行OVN logical flow actions"ip.dscp=VALUE; next"的flow就可以了。

DHCP

OVN支持DHCPv4和DHCPv6,所有这些都是通过logical flow实现的。在很多情况下,如果要改变它们的行为,只需要改变那些生成DHCP相关的logical flow的代码就可以了。

最近的一个例子是这个patch,它添加了对无状态DHCPv6的支持。有了无状态DHCPv6,我们能够提供一些configuration entries(例如DNS地址),但是不提供IPv6地址。这对于DHCPv6的logical flows只是一个小小的调整,只要OVN创建的应答能够有选择地包含IPv6地址选项就可以了。

Conclusion

我希望你对OVN Logical Flows已经有了更深的理解,它其实就是一个用于定义逻辑网络行为的,可扩展到多台机器上的match-action pipeline。

《OVN Logical Flows and ovn-trace》翻译的更多相关文章

  1. OVN实战---《The OVN Load Balancer》翻译

    Overview 基于前面几篇文章的基础之上,我们接下来将要探索OVN中的load balancingz这一特性.但是在开始之前,我们先来回顾一下上一个lab中创建好的拓扑结构. The lab ne ...

  2. OVN实战---《The OVN Gateway Router》翻译

    Overview 在本文中我将在前文的基础上添加一个OVN gateway router.gateway router将使得lab network能访问我们的overlay network The l ...

  3. ovs ovn 学习资料

    0.A Primer on OVN http://blog.spinhirne.com/2016/09/a-primer-on-ovn.html 1.Open Virtual Networking W ...

  4. OVN实战---《OVN and Containers》翻译

    Overview 在本篇文章中,我们要讨论的是OVN和容器的集成.到本次实验中,我们将会创建一个包含有一对容器的“虚拟机”,这些容器会直接和OVN logical switch相连,并且可以供逻辑网络 ...

  5. OVN实战---《A Primer on OVN》翻译

    overview 在本文中,我们将在三个host之间创建一个简单的二层overlay network.首先,我们来简单看一下,整个系统是怎么工作的.OVN基于分布式的control plane,其中各 ...

  6. OVN架构翻译

    概述 ovn-controller是OVN在虚拟机上的agent,北向连接OVN的南向数据库,学习OVN的配置和状态,并使用虚拟机的状态来填充PN表以及Binding表的Chassis列:南向连接op ...

  7. OVN架构

    原文地址 OVN架构 1.简介 OVN,即Open Virtual Network,是一个支持虚拟网络抽象的系统. OVN补充了OVS的现有功能,增加了对虚拟网络抽象的原生(native)支持,比如虚 ...

  8. 如何借助 OVN 来提高 OVS 在云计算环境中的性能

    众所周知,OpenvSwitch 以其丰富的功能和不错的性能,已经成为 Openstack 部署中最受欢迎的虚拟交换机.由于 Openstack Neutron 的架构引入了一些性能问题,比如 neu ...

  9. OVN入门

    参考链接 如何借助 OVN 来提高 OVS 在云计算环境中的性能 OVN简介 Open vSwitch Documentation OVSDB介绍及在OpenDaylight中的调用 OpenDayl ...

随机推荐

  1. Android基础总结(七)BroadcastReceiver

    广播(掌握) 广播的概念 现实:电台通过发送广播发布消息,买个收音机,就能收听 Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件. Andro ...

  2. asp.net 列表样式

    找了好一段时间,找到一个不错的文章列表样式,留起来备用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&qu ...

  3. MongoDB(一):关系型数据库和非关系型数据库

    一.关系型数据库 1.概念 关系型数据库:是指采用了关系模型来组织数据的数据库,是目前各类数据库中使用最为广泛的数据库系统.简单的说,关系模型指的就是二维表格模型,一个关系型数据库就是由二维表及其之间 ...

  4. 超酷的JavaScript叙事性时间轴(Timeline)类库

    在线演示 Timeline 是我见过的最酷的展示事件随时间发展的javascript实现.你可以基于时间使用讲故事的方式来创建时间轴特效,整个时间轴以幻灯的方式来展示,你可以穿插图片,视频或者是网站, ...

  5. 嵌入式驱动开发之uboot---uboot 中的常见命令参数参数

    Uboot相关命令介绍 bootm bootp cmp cp crc32 echo erase flinfo go minfo loadb loads mw 14mw 用指定的数据填充内存 15md ...

  6. 【NOIP模拟题】Incr(dp)

    太水的dp没啥好说的.. #include <cstdio> #include <cstring> #include <cmath> #include <st ...

  7. Android SDK代理server解决国内不能更新下载问题

    读者须知:本篇文章中最靠谱的是第三种方式,近期有读者反映第三种方式也不行了,以下提供一点其它途径的开源镜像网站: 国内高校的开源镜像站 中国科学技术大学(debian.ustc.edu.cn) 上海交 ...

  8. 转载:pyqt的signal和solit

    转自:http://blog.csdn.net/hlqyq/article/details/6713828 import sysfrom PyQt5.QtCore import pyqtSignal, ...

  9. 【代码备份】原图降采样后进行NLM滤波

    文件路径: 滤波算法main.m: %% 测试函数 %NLM滤波及滤波与 clc,clear all,close all; ima_ori=double(imread('F:\Users\****n\ ...

  10. 编程之美 set 11 买书问题

    题目 书店搞促销, 同时购买多卷书时, 有机会享受优惠 2本优惠 5%, 3本 10%, 4 本 20% 5 本 25% 设计算法, 求解购买一本书的最低价格 分析 1. 第一个感觉是一次购买的越多省 ...