天天看点

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

学习 Neutron 系列文章:

      OVS bridge 有两种模式:“normal” 和 “flow”。“normal” 模式的 bridge 同普通的 Linux 桥,而 “flow” 模式的 bridge 是根据其流表(flow tables) 来进行转发的。Neutron 使用两种 OVS bridge:br-int 和 br-tun。其中,br-int 是一个 “normal” 模式的虚拟网桥,而 br-tun 是 “flow” 模式的,它比 br-int 复杂得多。

下面左图是 Open vSwitch 中流表的结构。右图这个流程图详细描述了数据包流通过一个 OpenFlow 交换机的过程。

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]
Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

Proxy ARP 就是通过一个主机(通常是Router)来作为指定的设备对另一个设备作出 ARP 的请求进行应答。

举个例子:主机A,IP地址是192.168.0.11/24;主机B,IP地址是192.168.1.22/24。主机A和主机B通过路由器R相连接,并且路由器R启用了Proxy ARP,并配置有路由。网络拓扑如下:

     eth0                eth0       eth1                        eth0

    A------------------------Router R----------------------B

192.168.0.11/24   192.168.0.0/24 eth0      192.168.1.22/24

                             192.168.1.0/24 eth1  

  在主机A上执行:ping 192.168.1.22,主机 A 不知道主机 B 的 MAC 地址是多少,首先要发送 ARP 查询报文,路由器 R 接收到主机 A 发出的 ARP 查询报文,并代替主机 B 作出应答,应答 ARP 报文中填入的就是路由器 R 的MAC地址。这样,主机A就会认为路由器R的地址是192.168.1.22。以后所有发往192.168.1.22的报文都发到路由器R,路由器R再根据已配置好的路由表将报文转发给主机B。

  这样做的好处就是,主机A上不需要设置任何默认网关或路由策略,不管路由器R的IP地址怎么变化,主机A都能通过路由器B到达主机B,也就是实现了所谓的透明代理。相反,若主机A上设置有默认网关或路由策略时,当主机A向192.168.1.22发送报文,首先要查找路由表,而主机A所在的网段是192.168.0.0/24,主机B所在网段是192.168.1.0/24,主机A只能通过默认网关将报文发送出去,这样代理ARP也就失去了作用。

优点: 

最主要的一个优点就是能够在不影响其他router的路由表的情况下在网络上添加一个新的router,这样使得子网的变化对主机是透明的。

proxy ARP应该使用在主机没有配置默认网关或没有任何路由策略的网络上 

缺点:

1.增加了某一网段上 ARP 流量 

2.主机需要更大的 ARP table 来处理IP地址到MAC地址的映射 

3.安全问题,比如 ARP 欺骗(spoofing) 

4.不会为不使用 ARP 来解析地址的网络工作 

5.不能够概括和推广网络拓扑

OpenStack 中,Neutron 作为 OVS 的 Controller,向 OVS 发出管理 tunnel port 的指令,以及提供流表。

  Neutron 定义了多种流表。以下面的配置(配置了 GRE 和 VXLAN 两种 tunnel types)为例:

其中,10.0.1.31 是计算节点1, 10.0.1.21 是网络节点, 10.0.1.39 是计算节点2。

计算节点1 上 ML2 Agent 启动后的 br-tun 的 flows:

表号

用途

例子

table=0, priority=1,in_port=3 actions=resubmit(,4) //从网络节点来的,转 4,结果被丢弃

table=0, priority=1,in_port=4 actions=resubmit(,3) //从网络节点来的,转 3

table=0, priority=1,in_port=5 actions=resubmit(,3) //从计算节点来的,转 3

table=0, priority=1,in_port=2 actions=resubmit(,4) //从计算节点来的,转 4,结果被丢弃

table=0, priority=1,in_port=1 actions=resubmit(,2) //从虚机来的,转 2

table=0, priority=0 actions=drop //其余的丢弃

DVR_PROCESS = 1

handle packets coming from patch_int unicasts go to table UCAST_TO_TUN where remote addresses are learnt

 用于 DVR

PATCH_LV_TO_TUN = 2

table=2, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20) //单播包,转 20

GRE_TUN_TO_LV = 3

table=3, priority=1,tun_id=0x4 actions=mod_vlan_vid:1,resubmit(,10) //将 tun_id 为 4 的,修改 vlan id 为1,转 10 处理

table=3, priority=0 actions=drop //其余的丢弃

VXLAN_TUN_TO_LV = 4

table=4, priority=0 actions=drop //丢弃

DVR_NOT_LEARN = 9

LEARN_FROM_TUN = 10

 学习table

table=10,priority=1 actions=learn(table=20,hard_timeout=300,priority=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:1

UCAST_TO_TUN = 20

//学习到的规则

table=20, priority=2,dl_vlan=1,dl_dst=fa:16:3e:7e:ab:cc actions=strip_vlan,set_tunnel:0x3e9,output:5 //如果vlan 为1,而且目的MAC地址等于 fa:16:3e:7e:ab:cc,设置 tunnel id,从端口 5 发出

table=20,priority=0 actions=resubmit(,22) //直接转 22

ARP_RESPONDER = 21

 ARP table

 当使用 arp_responder 和 l2population 时候用到

FLOOD_TO_TUN  = 22

 Flood table

table=22,dl_vlan=1 actions=strip_vlan,set_tunnel:0x4,output:5,output:4 //对于 dl_vlan 为1的,设置 tunnel id 为 4,从端口4 和 5 转出

table=22,priority=0 actions=drop

来个图简单些:

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

其中比较有意思的是:

(1)为什么从 VXLAN 过来的流量都被丢弃了,最后发出去也用的是 GRE 端口。看来同时有 GRE 和 VXLAN 隧道的话,OVS 只会选择 GRE。具体原因待查。

(2)MAC 地址学习:Table 10 会将学习到的规则(Local VLAN id + Src MAC Addr => IN_Port)放到 table 20。当表格20 发现一个单播地址是已知的时候,直接从一个特定的 GRE 端口发出;未知的话,视同组播地址从所有 GRE 端口发出。

学习规则:

table=20,hard_timeout=300,priority=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]

table=20:修改 table 20。这是个 MAC 学习流表。

hard_timeout:该 flow 的过期时间。

NXM_OF_VLAN_TCI[0..11] :记录 vlan tag,所以学习结果中有 dl_vlan=1

NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[] :将 mac source address 记录,所以结果中有 dl_dst=fa:16:3e:7e:ab:cc

load:0->NXM_OF_VLAN_TCI[]:在发送出去的时候,vlan tag设为0,所以结果中有 actions=strip_vlan

load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[] :发出去的时候,设置 tunnul id,所以结果中有set_tunnel:0x3e9

output:NXM_OF_IN_PORT[]:指定发送给哪个port,由于是从 port2 进来的,因而结果中有output:2。

学到的规则:

table=20, n_packets=1239, n_bytes=83620, idle_age=735, hard_age=65534, priority=2,dl_vlan=1,dl_dst=fa:16:3e:7e:ab:cc actions=strip_vlan,set_tunnel:0x3e9,output:2 

这里可以看到,通过 MAC 地址学习机制,Neutron 可以一定程度地优化网络流向,但是这种机制需要等待从别的节点的流量进来,只能算是一种被动的机制,效率不高。而且,这种机制只对单播帧有效,而对于多播和组播依然无效。其结果是网络成本依然很高。下图中,A 的广播包其实只对 3 和 4 有用,但是 2 和 5 也收到了。

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

  使用 ARP Responder 需要满足两个条件:

(1)设置 arp_responder = true 来使用 OVS 的ARP 处理能力 。这需要 OVS 2.1 (运行 ovs-vswitchd --version 来查看 OVS 版本) 和 ML2 l2population 驱动的支持。当使用隧道方式的时候,OVS 可以处理一个 ARP 请求而不是使用广播机制。如果 OVS 版本不够的话,Neutorn 是无法设置 arp responder entry 的,你会在 openvswitch agent 日志中看到 “Stderr: '2015-07-11T04:57:32Z|00001|meta_flow|WARN|destination field arp_op is not writable\novs-ofctl: -:2: actions are invalid with specified match (OFPBAC_BAD_SET_ARGUMENT)\n'”这样的错误,你也就不会在 ”ovs-ofctl dump-flows br-tun“ 命令的输出中看到相应的 ARP Responder 条目了。

(2)设置 l2_population = true。同时添加 mechanism_drivers = openvswitch,l2population。OVS 需要 Neutron 作为 SDN Controller 向其输入 ARP Table flows。

杀掉 neutron openvswitch, ovs-* 各种进程

#编译安装

去 http://openvswitch.org/download/ 下载最新版本的代码,解压,进入解压后的目录

安装依赖包,比如 gcc,make

uname -r

./configure --with-linux=/lib/modules/3.13.0-51-generic/build

make && make install

#查看安装的版本

root@compute2:/home/s1# ovs-vsctl --version

ovs-vsctl (Open vSwitch) 2.3.2

Compiled Jul 12 2015 09:09:42

DB Schema 7.6.2

#处理 db

rm /etc/openvswitch/conf.db (老的db要删除掉,否则会报错)

ovsdb-tool create /etc/openvswitch/conf.db vswitchd/vswitch.ovsschema

#启动 ovs

cp /usr/local/bin/ovs-* /usr/bin

ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/usr/local/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach --monitor

ovs-vswitchd unix:/usr/local/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir --log-file=/var/log/openvswitch/ovs-vswitchd.log --pidfile=/var/run/openvswitch/ovs-vswitchd.pid --detach --monitor

#启动 neutron openvswitch agent,确保log 文件中 ovs-vsctl 和 ovs-ofctl 调用没有错误

#修改 /usr/share/openvswitch/scripts/ovs-lib 文件,保证机器重启后 OVS 正常运行

将 rundir=${OVS_RUNDIR-'/var/run/openvswitch'} 改为 rundir=${OVS_RUNDIR-'/usr/local/var/run/openvswitch'}

有了 arp_responder 以后,br-tun 的流表增加了几项和处理:

(1)table 2 中增加一条 flow,是的从本地虚机来的 ARP 广播帧转到table 21

(2)在 table 21 中增加一条 flow 将其发发往 table 22

(3)由 L2 population 发来的 entry 来更新 table 21。

  table 21 是在新的 l2pop 地址进来的时候更新的。比如说,compute C 上增加了新的虚机 VM3,然后计算节点 A 和 B 收到一条 l2pop 消息说 VM3 (IP 是***,MAC 是 ***) 在 Host C 上,在 network "Z“ 中。然后,Compute A 和 B 会在 table 21 中增加相应的 flows。   

     table 21 中的每一条 flow,会和进来的帧的数据做匹配(ARP 协议,network,虚机的 IP)。如果匹配成功,则构造一个 ARP 响应包,其中包括了 IP 和 MAC,从原来的 port 发回到虚机。如果没有吻合的,那么转发到 table 22 做泛洪。

增加的 flow tables 在红色部分:

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

    l2pop 的原理也不复杂。Neutron 中保存每一个端口的状态,而端口保存了网络相关的数据。虚机启动过程中,其端口状态会从 down 到build 到 active。因此,在每次端口发生状态变化时,函数 update_port_postcommit  都将会被调用:

    在某些状态变化下: 

update_port_postcommit (down to active) -> _update_port_up -> add_fdb_entries -> fdb_add -> fdb_add_tun -> setup_tunnel_port  (如果 tunnel port 不存在,则创建 tunnel port), add_fdb_flow -> add FLOOD_TO_TUN flow (如果是 Flood port,则将端口添加到 Flood output ports); setup_entry_for_arp_reply('add'。如果不是 Flood port,那么 添加 ARP Responder entry (MAC -> IP)) 以及 add UCAST_TO_TUN flow Unicast Flow entry (MAC -> Tunnel port number)。

update_port_postcommit (active to down) -> _update_port_down -> remove_fdb_entries

delete_port_postcommit (active to down) -> _update_port_down -> remove_fdb_entries -> fdb_remove -> fdb_remove_tun -> cleanup_tunnel_port, del_fdb_flow -> mod/del FLOOD_TO_TUN flow; setup_entry_for_arp_reply ('remove'), delete UCAST_TO_TUN flow

update_port_postcommit (fixed ip changed) -> _fixed_ips_changed -> update_fdb_entries

    通过这种机制,每个节点上的如下数据得到了实时更新,从而避免了不必要的隧道连接和广播。

Tunnel port

FLOOD_TO_TUN (table 22)flow

ARP responder flow

UCAST_TO_TUN (table 20) flow

有和没有 l2pop 的效果:

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

1. def tunnel_sync(self) 函数除了上报自己的 local_ip 外不再自己见 tunnels,一切等 l2pop 的通知。

2. 在 compute1 上添加第一个虚机 81.1.180.8

neutron-server:

通知 compute1: {'segment_id': 6L, 'ports': {u'10.0.1.21': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:87:40:f3', u'81.1.180.1']]}, 'network_type': u'gre'}}

通知所有 agent: {'segment_id': 6L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:b3:e7:7a', u'81.1.180.8']]}, 'network_type': u'gre'}} 

compute1:

添加和网络节点的tunnel options: {df_default="true", in_key=flow, local_ip="10.0.1.31", out_key=flow, remote_ip="10.0.1.21"}

添加到网段网关的 Unicast flow:table=20, n_packets=0, n_bytes=0, idle_age=130, priority=2,dl_vlan=2,dl_dst=fa:16:3e:87:40:f3 actions=strip_vlan,set_tunnel:0x6,output:4

增加网段 81.1.180.1 网关的 ARP flows:table=21, n_packets=0, n_bytes=0, idle_age=130, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.1 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:87:40:f3,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163e8740f3->NXM_NX_ARP_SHA[],load:0x5101b401->NXM_OF_ARP_SPA[],IN_PORT

修改 Flood flow

compute 2 节点:因为它上面还没有运行虚机,所以不做操作。

3. 在 compute 2 上添加一个虚机 81.1.180.9

neutron server:

通知 compute 2 : {'segment_id': 6L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:b3:e7:7a', u'81.1.180.8']], u'10.0.1.21': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:87:40:f3', u'81.1.180.1']]}, 'network_type': u'gre'}}

通知所有 agent: {'segment_id': 6L, 'ports': {u'10.0.1.39': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:73:49:41', u'81.1.180.9']]}, 'network_type': u'gre'}

建立 tunnel(ID 5):  {df_default="true", in_key=flow, local_ip="10.0.1.31", out_key=flow, remote_ip="10.0.1.39"}

增加 arp responder flow(compute2 上新的虚机 IP -> MAC):table=21, n_packets=0, n_bytes=0, idle_age=79, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.9 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:73:49:41,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163e734941->NXM_NX_ARP_SHA[],load:0x5101b409->NXM_OF_ARP_SPA[],IN_PORT

增加 unicast flow (新虚机的 MAC -> Tunnel port):table=20, n_packets=0, n_bytes=0, idle_age=79, priority=2,dl_vlan=2,dl_dst=fa:16:3e:73:49:41 actions=strip_vlan,set_tunnel:0x6,output:5

添加新的 Tunnel port 到 Flood flow:table=22, n_packets=13, n_bytes=1717, idle_age=255, hard_age=78, dl_vlan=2 actions=strip_vlan,set_tunnel:0x6,output:5,output:4

compute2:

建立和计算节点以及compute1的tunnel:options: {df_default="true", in_key=flow, local_ip="10.0.1.39", out_key=flow, remote_ip="10.0.1.21"},options: {df_default="true", in_key=flow, local_ip="10.0.1.39", out_key=flow, remote_ip="10.0.1.31"}

增加 ARP flow(compute 1 上的虚机的 MAC -> IP):table=21, n_packets=0, n_bytes=0, idle_age=268, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.8 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:b3:e7:7a,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163eb3e77a->NXM_NX_ARP_SHA[],load:0x5101b408->NXM_OF_ARP_SPA[],IN_PORT

增加 Unicast flow (compute 1 上的虚机 MAC -> Tunnel port):table=20, n_packets=0, n_bytes=0, idle_age=268, priority=2,dl_vlan=2,dl_dst=fa:16:3e:b3:e7:7a actions=strip_vlan,set_tunnel:0x6,output:4

增加 ARP flow(新虚机的网关的 MAC -> IP) table=21, n_packets=0, n_bytes=0, idle_age=268, priority=1,arp,dl_vlan=2,arp_tpa=81.1.180.1 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:87:40:f3,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163e8740f3->NXM_NX_ARP_SHA[],load:0x5101b401->NXM_OF_ARP_SPA[],IN_PORT

修改 Flood flow(添加到 Compute 1 的 port):table=22, n_packets=13, n_bytes=1717, idle_age=128, dl_vlan=2 actions=strip_vlan,set_tunnel:0x6,output:5,output:4

3. 删除 compute1 上的一个vm(也是唯一的一个)

通知所有 agent: {'segment_id': 6L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:b3:e7:7a', u'81.1.180.8']]}, 'network_type': u'gre'}

compute 1:

因为没有别的虚机了,删除所有 tunnel ports

修改或者删除 ARP, Unicast 和 Flood flows

compute 2:

删除了 compute1 的 tunnel

删除该虚机对应的 ARP flow 

 4. 在 compute1 上创建第一个不同网络的虚机

通知 compute 1: {u'e2022937-ec2a-467a-8cf1-f642a3f777b6': {'segment_id': 4L, 'ports': {u'10.0.1.21': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:90:e5:50', u'91.1.180.1'], [u'fa:16:3e:17:c9:26', u'90.1.180.1'], [u'fa:16:3e:69:92:30', u'90.1.180.3'], [u'fa:16:3e:69:92:30', u'91.1.180.2']]}, 'network_type': u'gre'}}

通知所有 agent:{u'e2022937-ec2a-467a-8cf1-f642a3f777b6': {'segment_id': 4L, 'ports': {u'10.0.1.31': [['00:00:00:00:00:00', '0.0.0.0'], [u'fa:16:3e:e9:ee:0c', u'91.1.180.9']]}, 'network_type': u'gre'}}

compute 1:建立和网络节点的 tunnel port;更新 Flood flows;添加 ARP flows

compute 2:没什么action,因为该节点上没有新建虚机的网络内的虚机

过程的大概说明:

虚机在收到 fannout FDB entries 后,检查其中每个 port 的 network_id(即 “segment_id”)。如果本机上有该 network 内的 port,那么就处理 entries 中的 “ports”部分;否则,不处理该 entries。

因此,当计算节点上没有运行任何虚机时,不会建立任何 tunnel。如果两个虚机上有相同网络内的虚机,那么建立会建立 tunnel。

这种机制能实时建立 tunnel port,Flood entry (创建 Tunnel port 同时添加到 Flood output ports 列表), Unicast flow (虚机和网关 MAC -> Tunnel port) 和 ARP Responder entry  (虚机和网关 MAC -> IP)。下图中的蓝色部分的流表都会被及时更新。

Neutron server 在端口创建/删除/修改时,如果是该节点上的第一个虚机,首先发送直接消息;然后发通知消息给所有的计算和网络节点。 

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

    应该说 l2pop 的原理和实现都很直接,但是在大规模部署环境中,这种通知机制(通知所有的 ML2 Agent 节点)可能会给 MQ 造成很大的负担。一旦 MQ 不能及时处理消息,虚机之间的网络将受到影响。下面是 l2pop 中通知机制代码: 

Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]
Neutron 理解 (4): Neutron OVS OpenFlow 流表 和 L2 Population [Netruon OVS OpenFlow tables + L2 Population]

    不知道这个数目有没有上限?数目很多的情况下会不会有性能问题?OVS 有没有处理能力上限?这些问题也许得在实际的生产环境中才能得到证实和答案。

    本文转自SammyLiu博客园博客,原文链接:http://www.cnblogs.com/sammyliu/p/4633814.html,如需转载请自行联系原作者