...

https://docs.openstack.org/newton/networking-guide/deploy-ovs-ha-dvr.html

On the compute node containing the instance, verify creation of the fip namespace with the same ID as the provider network.

ip netns

fip-4bfa3075-b4b2-4f7d-b88e-df1113942d43
On each compute node, verify creation of a qrouter namespace with the same ID.
Compute node 1:

ip netns

qrouter-78d2f628-137c-4f26-a257-25fc20f203c1

Compute node 2:

ip netns

qrouter-78d2f628-137c-4f26-a257-25fc20f203c1

On the network node, verify creation of the snat and qrouter namespaces with the same ID.

ip netns

snat-78d2f628-137c-4f26-a257-25fc20f203c1
qrouter-78d2f628-137c-4f26-a257-25fc20f203c1

局域网两个不同网段互相访问
https://www.cnblogs.com/embedded-linux/p/10200831.html

管理外部网络和浮动IP地址
https://www.ibm.com/support/knowledgecenter/en/SST55W_4.2.0/liaca/liaca_manage_floating_ip.html

OpenStack网络基础知识: OpenvSwitch使用指南
http://fishcried.com/2016-02-09/openvswitch-ops-guide/

https://www.ibm.com/developerworks/cn/cloud/library/1401_zhaoyi_openswitch/index.html

https://wiki.openstack.org/wiki/Ovs-flow-logic#OVS_flows_logic_with_local_ARP_responder

Linux系列—策略路由、ip rule、ip route(重复路由生效问题)
https://www.cnblogs.com/wanstack/p/7728785.html

重复路由,查看哪个生效。用ip route get xxx看下

概念
Neutron 功能介绍
为了在分布式环境下面给虚拟机提供网卡,此外,Neutron还要虚拟化网络功能的其它元件,比如抽象交换机,抽象负载均衡器,抽象防火墙,抽象NAT。

虚拟网络定义类型

组件名称

实现方式

网卡

OpenvSwitch

防火墙

iptables

交换机

OpenvSwitch,Linux

路由器

Linux ip协议栈和iptables

负载均衡器

haproxy

网络命名空间(nets)
网络命名空间本质上是多个逻辑上隔离的网络环境运行在同一个物理主机上
每个网络空间都有自己的网卡设备、路由表、防火墙规则
veth pari是虚拟网络设备,成对出现,通常用于两个网络命名空间进行通信
网络分类
按照数据中心来分
OpenStack Cloud network,是OpenStack所管理的网络
External network,是数据中心所管理的公司网
Internet,是由各大运营商管理的公共网络,使用公共IP
按照创建网络的用户权限来分类
提供者网络(Provider network),管理员创建的和物理网络有直接映射关系的虚拟网络之间的通信
租户网络(Project network),普通租户创建的虚拟网络

按照OpenStack功能划分
管理网络,用于OpenStack各个组件之间的内部通信
数据网络,用于云部署中虚拟数据
外部网络,公共网络,外部或者Internet可以访问的网络
API网络,暴露所有的OpenStack APIs
虚拟网络定义类型

Local
只允许在本服务器内通信的虚拟网络,不能跨服务器通信,通常用于单节点测试

Flat

所有租户都在一个网络内,没有进行网络隔离,容易产生广播风暴
一般用于提供者网络
Vlan

基于物理Vlan网络实现的虚拟网络,共享同一个物理网络的多个Vlan网络是相互隔离的,甚至可以使用重叠的IP地址空间。每个支持Vlan network的物理网络可以被视为一个分离的Valn trunk,它使用一组独占的Vlan ID,有效的VLAN ID范围是1到4094
一般只用于提供者网络
创建命令如下

GRE
(Generic Routing Encapsulation)通用路由协议封装协议,是一种Ip-over-IP的隧道
GRE是L3层的隧道技术,本质是在隧道的两端的L4层建立UDP连接传输重新包装的L3层包头,在目的地再取出包装后的包头进行解析
GRE封装的数据包基于路由表来进行路由,因此GRE network不和具体的物理网络绑定
一般只适用于租户网络

常用到的对象模型

网络(network)
是一个隔离的二层网段,类似于物理网络世界中的LAN

子网(subnet)
子网是一组IPv4或者IPv6地址以及其有关联的配置,它是一个地址池,OpenStack可以从中向虚拟机分配IP地址,每个子网指定为一个无类别域间路由范围,必须和一个网络相关联

端口(Port)
一个Port代表虚拟机网络交换机上面的一个虚拟交换端口。虚拟机的网卡会被连接到port上,当虚拟机的网卡(VIF-Virtual Interface)连接到Port以后,这个vNIC就会拥有MAC地址和IP地址,Port的IP地址是从subnet中分配的
4.虚拟交换机(Virtual switch)
Neutron默认采用的是开源的OpenvSwitch作为其虚拟机交换机,同时也支持Linux Bridge

虚拟路由器(Virtual router)
一个Virtual router提供不同网段的IP包路由功能,由Neutron L3 agent负责管理

7.Neutorn(网络服务组件)

主要功能:为云计算提供虚拟的网络功能,为每个不同的租户建立独立的网路环境。

三种不同的网络模式(Flat模式 Flat DHCP模式,Vlan模式)

相对于交换机整个系统来说,Neutron其实是系统平台的位置,提供配置命令及参数检查,并把网络功能用一种逻辑组织起来;但是无论底层的plugin最终是用软件SDN还是硬件交换机来加速,Neutron自身并不提供任何网络功能,它只是一个架子。Neutron的网络功能大部分是Plugin提供的,除了DHCP和L3-agent等的某些部分功能。

Neutron将网络按照三层交换机的概念分为:

Network:相当于交换机根据vlan创建的一个三层接口;

Subnet:相当于交换机创建了一个三层接口地址;

Port:相当于交换机的一个物理端口,但是这个端口有一个MAC地址;

架构
neutron-server 是 OpenStack Networking 服务器的主要流程。它是一个 Python 后台进程,将用户请求从 OpenStack Networking API 中继到配置的插件。OpenStack Networking 还包含 3 个代理,它们通过消息队列或标准 OpenStack Networking API 与主要 Neutron 进程交互:

neutron-dhcp-agent 向所有租户网络提供动态主机配置协议 (Dynamic Host Configuration Protocol, DHCP) 服务。

neutron-l3-agent 执行 L3/网络地址转换 (Network Address Translation) 转发,以支持网络网络访问租户网络上的 VM。

一个特定于插件的可选代理 (neutron-*-agent) 在每个虚拟机管理程序上执行本地虚拟交换机配置。

比较令人困惑的概念和字段有:运营商网络(Provider Network)、物理网络(对应provider:physical_network)、segments和vlan_transparent。

租户网

外部网络
是指的租户网络以外的网络。租户网络是由 Neutron 创建和维护的网络。 外部网络不由 Neutron 创建。如果是私有云,外部网络通常指的是公司 intranet;如果是公有云,外部网络通常指的是 internet。

DVR,Distributed Virtual Router,分布式虚拟路由器
CIDR,Classless Inter-Domain Routing:
无类别域间路由,是一种在Internet上创建附加地址的方法,这些地址提供给服务提供商(ISP),再由ISP分配给客户

LB ,
oad Balance,负载均衡

HA ,
igh Availability

VPC,
Virtual Private Cloud,私有网络

netfilter、iptables

SNAT是指源NAT,或者当数据包离开路由器的外部设备时更改数据包的源地址。这用于源自没有附加浮动IP的VM的流量。路由器从外部网络分配一个IP地址,该IP地址在连接到路由器所连接的所有子网的所有VM之间共享。会话根据(源IP,目标IP,源端口,目标端口)的完整元组进行区分。这通常称为网络世界中的“PAT”或端口地址转换

(1)Neutron 所实现的网络虚拟化

(2)Neutron OpenvSwitch + VLAN 虚拟网络

(3)Neutron OpenvSwitch + GRE/VxLAN 虚拟网络

(6)Neutron OVS OpenFlow 流表 和 L2 Population

(7)Neutron DHCP Agent

(8)Neutron L3 Agent

(9)Neutron LBaas

(10)Neutron Security Group

(11)Neutron FWaas 和 Nova Security Group

(12)Neutron VPNaas

(13)Neutron DVR

(14)Neutron VRRP

(15)High Availability (HA)

VRRP
VRRP 是一种路由器选择协议,它可以把一个虚拟路由器的责任动态分配到 VRRP 组内的多个路由器中的一台。控制虚拟路由器 IP 地址的 VRRP 路由器称为主路由器,它负责转发数据包到这些虚拟 IP 地址。一旦主路由器不可用,这种选择过程就提供了动态的故障转移机制,这就虚拟允许路由器的 IP 地址可以作为终端主机的默认第一跳路由器。使用 VRRP 的好处是有更高的默认路径的可用性而无需在每个终端主机上配置动态路由或路由发现协议。

VRRP 包封装在 IP 包中发送,用于 VRRP 组内设备的互相通信比如保持心跳等。它只定义了一种报文即 VRRP 报文,这是一种组播报文,由 Master 路由器定时发出来通告组内的路由器它的存在。

VRRP 中定义了三种状态模型,初始状态 Initialize,活动状态 Master 和备份状态 Backup,其中只有活动状态的交换机可以为到虚拟IP地址的的转发请求提供服务。

neutron各个组件的作用

通过 dhcp-agent 访问 Metadata
OpenStack 默认通过 l3-agent 创建和管理 neutron-ns-metadata-proxy,进而与 nova-metadata-api 通信。但不是所有环境都有 l3-agent,比如直接用物理 router 的场景。这时就需要走另一条路:让 dhcp-agent 来创建和管理 neutron-ns-metadata-proxy。

/etc/neutron/dhcp_agent.ini,force_metadata

此进程通过 --network_id 关联到 test_net,这就是 dhcp-agent 启动的 neutron-ns-metadata-proxy,用于接收 test_net 网络上 instance 的metadata请求。每一个network都有与之对应的 neutron-ns-metadata-proxy。

虚机里的路由:

虚机的启动日志,dhcp方式请求ip和hostname配置

访问 169.254.169.254 的路由是193.1.1.1 是 dhcp-agent 在test_net 上的 IP。这条路由是由 dhcp-agent 添加进去的。正是因为这条路由的存在,即便 l3-agent 与 dhcp-agent 同时提供 neutron-ns-metadata-proxy 服务,metadata 请求也只会发送给 dhcp-agent。

同时我们也看到,dhcp-agent 已经将 IP 169.254.169.254 配置到了自己身上。也就是说:虚机 访问 metadata 的请求 http://169.254.169.254 实际上是发送到了 dhcp-agent 的 80 端口。而监听 80 端口的正是 dhcp-agent 启动的 neutron-ns-metadata-proxy 进程。

这两个进程id对应的进程为:

后面的数据流向就与 l3-agent 的场景一样了:neutron-ns-metadata-proxy 将请求通过unix domain socket发给neutron-metadata-agent,后者再通过网络发给nova-api-metadata。

对于 169.254.169.254:

l3-agent 用 iptables 规则来转发。

dhcp-agent 则是将此 IP 配置到自己的 interface 上。

router所在的命名空间的iptables规则

监听在9697端口上的neutron-ns-metadata-proxy服务

总结
169.254.169.253: DNS服务。
169.254.169.123: NTP服务。
OpenStack虚拟机也是通过http://169.254.169.254获取虚拟机的初始化配置信息:
$ curl -sL 169.254.169.254/openstack/latest/meta_data.json {"uuid": "daf32a70-42c9-4d30-8ec5-3a5d975}

Metadata 服务为用户自定义配置虚拟机提供了有效的解决方案。OpenStack 提供 metadata 服务的两种机制:config drive 和 RESTful 服务。Config drive 机制主要用于配置虚拟机的网络信息,包括 IP、子网掩码、网关等。当虚拟机无法通过 DHCP 正确获取网络信息时,config drive 是获取 metadata 信息的必要方式。如果虚拟机能够自动正确配置网络,那么可以通过 RESTful 服务的方式获取 metadata 信息。

UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC),它不需要经过网络协议栈实现将应用层数据从一个进程拷贝到另一个进程,有点类似于Unix管道(pipeline)。

--metadata_proxy_socket=/var/lib/neutron/openstack-helm/metadata_proxy

我们从haproxy配置看,监听的地址是0.0.0.0:80,那如果有多个网络同时都监听80端口岂不是会出现端口冲突吗?
socket只能用于同一主机的进程间通信,如果Nova Metadata服务与Neutron dhcp-agent不在同一个主机,则显然还是无法通信。

lsof -i :80

OpenStack虚拟机访问Nova Metadata服务:

curl 169.254.169.254 -> haproxy(80端口) -> UNIX Socket文件 -> neutron-metadata-agent -> nova-api-metadata

OpenStack是通过neutron-metadata-agent获取虚拟机的uuid的。我们知道,在同一个Neutron network中,即使有多个subnet,也不允许IP重复,即通过IP地址能够唯一确定Neutron的port信息。而neutron port会设置device_id标识消费者信息,对于虚拟机来说,即虚拟机的uuid。

因此neutron-metadata-agent通过network uuid以及虚拟机ip即可获取虚拟机的uuid。

http-request add-header X-Neutron-Network-ID 2c4b658c-f2a0-4a17-9ad2-c07e45e13a8a

即haproxy转发之前会把network id添加到请求头部中,而IP可以通过HTTP的头部X-Forwarded-For中获取。因此neutron-metadata-agent具备获取虚拟机的uuid以及project id(租户id)条件,我们可以查看neutron-metadata-agent获取虚拟机uuid以及project id实现,代码位于neutron/agent/metadata/agent.py:

源码:

title OpenStack Metadata WorkFlow

participant vm

participant haproxy

participant UNIX Socket

participant neutron-metadata-agent

participant nova-api-metadata

vm -> haproxy: curl 169.254.169.254(第一次转发)

note over haproxy: Add header X-Neutron-Network-ID

haproxy -> UNIX Socket: 第二次转发

UNIX Socket -> neutron-metadata-agent: 第二次转发

note over neutron-metadata-agent: get_instance_and_tenant_id

note over neutron-metadata-agent: sign_instance_id

neutron-metadata-agent -> nova-api-metadata: 第三次转发

note over nova-api-metadata: get_metadata_by_instance_id

nova-api-metadata -> neutron-metadata-agent: metadata

neutron-metadata-agent -> UNIX Socket: metadata

UNIX Socket -> haproxy: metadata

haproxy -> vm: metadata

  • Neutron 三层技术简介
    分布式路由(DVR)

    DVR东西向流量

    DVR南北向流量

高可用路由

浮动IP、端口映射

NAT流量模型

计算节点除了L2 Agent外,还要安装L3 Agent以及Metadata Agent。也就是说,通过使用dvr,计算节点也有了网络节点的三层转发和NAT功能,起到了分流的作用。

网络走向:
没有floating-ip的instance的南北向网络流通情况
连接到OVS生成的br-int网桥,该instance在三层网络的第一跳便是到与该br-int相连的Disk router namespace的网关接口,因为没有floating-ip ,所以不会通过FIP namespace,而是通过patch设备到br-tun,再到网络节点的br-int,到达snat namespace,进行snat(源地址转换),最后通过ovs-provider-bridge与外网联通。

有floating-ip的instance的南北向网络流通情况
刚开始与第一种情况类似,但是跳到本地router namespace后,接下来会跳到本地的RIP namespace,做snat,然后直接通过本地的ovs-provider-bridge连接到外网。

不同子网但有虚拟路由连接的instance东西向网络
刚开始类似,之后在本地router namespace进行路由选择,并通过br-int,br-tun进入对应虚拟机的计算节点(该部分工作由ovs 的openflow完成,同时还完成了snat),到了目标计算节点上,依次被 br-tun,br-int 处理,直到通过 tap 设备进入另一instance。

  1. Neutron L3 Agent 的实现原理
    每个 L3 Agent 运行在一个 network namespace 中,每个 namespace 由 qrouter-命名,比如 qrouter-e506f8fe-3260-4880-bd06-32246225aeae。网络节点如果不支持 Linux namespace 的话,只能运行一个 Virtual Router。也可以通过设置配置项 use_namespaces = True/False 来使用或者不使用 namespace。

    Neutron L3 Agent 负责路由(routing)、浮动 IP 分配(floatingip allocation), 地址转换(SNAT/DNAT)和 Security Group 管理

2.1 Router 作为浮动 IP 地址的ARP Proxy
虚机的浮动 IP 其实不是真实网卡的 IP 地址,而是一个虚拟地址。那么,使用浮动 IP 和虚机通信的机器怎么获得 MAC 地址呢?Router 在这个过程中作为一个 ARP Proxy,其 IP 协议栈会向 ARP 广播请求回应浮动 IP 对应所在的外部端口的 MAC 地址。下面的例子中,该 router 挂接的子网内有两个浮动 IP,L3 Agent 都将它们添加到 Router 的外部端口 qg-3c8d6a68-97 上:

42: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97
valid_lft forever preferred_lft forever
inet 192.168.1.104/32 brd 192.168.1.104 scope global qg-3c8d6a68-97
valid_lft forever preferred_lft forever
inet 192.168.1.111/32 brd 192.168.1.111 scope global qg-3c8d6a68-97
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe2e:5b23/64 scope link
valid_lft forever preferred_lft forever
先插播一句。上图可以看到 qg 下面有个3个 ip 地址,第一个是 VR 自己的 fip,后面两个是后面虚机的 fip。也就是说,要给虚机分配 fip,必须先给 VR 分配 fip,因为这样才能有 qg。

这么做的目的,由于外网中的机器和虚机浮动 IP 是同一个网段的,外网中的机器通过浮动 IP 访问虚机之前,需要通过 ARP 获取该浮动 IP 的 MAC 地址。浮动IP 其实是个虚机IP,没有对应一个网络设备,因此,Neutron 将它们添加到 external port 上,共享 external port 的 MAC 地址。这样,在 router network namespace IP 协议栈收到 ARP 广播后,就可以该 IP 对应 的 MAC 地址,然后外网中的虚机就会使用该 MAC 作为目的 MAC 地址直接向 router 的 external port 端口发送网络帧。查询外网机器的 arp table,即可看到 192.168.1.104 的 MAC 为 qg-3c8d6a68-97 的 MAC 地址。也就是说,外部端口上的所有 IP 的 MAC 地址都相同。这时候,其实 router 充当了一个 ARP Proxy 的角色。

s1@controller:~$ arp
Address HWtype HWaddress Flags Mask Iface
192.168.1.104 ether fa:16:3e:2e:5b:23 C eth0
192.168.1.110 ether fa:16:3e:2e:5b:23 C eth0
192.168.1.111 ether fa:16:3e:2e:5b:23 C eth0
2.2 路由 (Routing)
一个 Virtual Router 连接几个 subnet 就会有几个 virtual interface。每个 interface 的地址是该 subnet 的 gateway 地址。比如:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 ip addr
33: qr-2aa928c9-e8: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default #IP 设为它连接的子网的 Gateway IP
link/ether fa:16:3e:90:e5:50 brd ff:ff:ff:ff:ff:ff
inet 91.1.180.1/24 brd 91.1.180.255 scope global qr-2aa928c9-e8
37: qr-a5c6ed86-c1: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether fa:16:3e:87:40:f3 brd ff:ff:ff:ff:ff:ff
inet 81.1.180.1/24 brd 81.1.180.255 scope global qr-a5c6ed86-c1
48: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97

L3 Agent 在启动时设置如下的路由规则:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 qg-3c8d6a68-97
81.1.180.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-a5c6ed86-c1 #到哪个网段的traffic发到相应的 interface
91.1.180.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-2aa928c9-e8
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 qg-3c8d6a68-97

虚机的 IP 栈在发现数据包的目的虚机的 IP 地址不在自己网段的话,会将其发到 Router 上对应其 subnet 的 Virtual Interface。然后,Virtual Router 根据配置的路由规则和目的IP地址,将包转发到目的端口发出。

2.3 源地址转换 SNAT
2.3.1 Neutron 的 SNAT iptables chains
在没有设置浮动 IP 时,当主机访问外网时,需要将主机的固定 IP 转换成外网网段的 gateway 的 IP 地址,以免暴露内部 IP 地址。其做法是 Neutron 在 iptables 表中增加了 POSTROUTING 链。

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N neutron-l3-agent-OUTPUT #Neutorn 增加的 chain
-N neutron-l3-agent-POSTROUTING #Neutorn 增加的 SNAT chain
-N neutron-l3-agent-PREROUTING
-N neutron-l3-agent-float-snat #Neutorn 增加的 SNAT chain
-N neutron-l3-agent-snat #Neutorn 增加的 SNAT chain
-N neutron-postrouting-bottom #Neutorn 增加的 SNAT chain
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING #(1)将 SNAT chain 转到自定义的 neutron-l3-agent-POSTROUTING
-A POSTROUTING -j neutron-postrouting-bottom #(3)将 SNAT chain 转到自定义的 neutron-postrouting-bottom
-A neutron-l3-agent-POSTROUTING ! -i qg-3c8d6a68-97 ! -o qg-3c8d6a68-97 -m conntrack ! --ctstate DNAT -j ACCEPT #(2)如果出口或者入口不是 qg-3c8d6a68-97 并且状态不是 DNAT 的都接受
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -s 91.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110
-A neutron-l3-agent-snat -s 81.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110
-A neutron-postrouting-bottom -j neutron-l3-agent-snat #(4)再转到 neutron-l3-agent-snat

2.3.2 实验:从虚机 81.1.180.12 ping 外网 192.168.1.15

  1. 在 router 的连接 81.1.180.12 网段 interface 上:
    root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 tcpdump -envi qr-a5c6ed86-c1 -vvv
    tcpdump: listening on qr-a5c6ed86-c1, link-type EN10MB (Ethernet), capture size 65535 bytes
    ^C17:42:48.904820 fa:16:3e:2b:3e:2a > fa:16:3e:87:40:f3, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 28892, offset 0, flags [DF], proto ICMP (1), length 84)
    81.1.180.12 > 192.168.1.15: ICMP echo request, id 32769, seq 0, length 64
    17:42:48.906601 fa:16:3e:87:40:f3 > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 24799, offset 0, flags [none], proto ICMP (1), length 84)
    192.168.1.15 > 81.1.180.12: ICMP echo reply, id 32769, seq 0, length 64
    17:42:49.906238 fa:16:3e:2b:3e:2a > fa:16:3e:87:40:f3, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 28893, offset 0, flags [DF], proto ICMP (1), length 84)

  2. 在 route 的连接 192.168.1.15 网段 interface 上

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 tcpdump -envi qg-3c8d6a68-97 -vvv
tcpdump: listening on qg-3c8d6a68-97, link-type EN10MB (Ethernet), capture size 65535 bytes
^C17:44:47.661916 fa:16:3e:2e:5b:23 > 08:00:27:c7:cf:ca, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 28896, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.110 > 192.168.1.15: ICMP echo request, id 33281, seq 0, length 64
17:44:47.663300 08:00:27:c7:cf:ca > fa:16:3e:2e:5b:23, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 36308, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.15 > 192.168.1.110: ICMP echo reply, id 33281, seq 0, length 64
可见在外网网段的 interface 收到数据包之前,SRC IP 已经被替换成了外网网段的 Gateway IP 了。

2.3.3 关于 SNAT 的一点细节
SNAT 只能用于从内部网络发起的目的是外部网络的连接,而不能相反。

前面分析了内网网络包如何出去,但因为外网主机回复的网络包的目的地址为SNAT 地址,不是真正内网主机的地址,因此回复包如何回到源主机呢?SNAT 的实现会保存一个连接映射表(connection table/mapping table),里面的每条记录保存着该连接的内部(源)address:port和外面(目标)address:port 之间的映射关系。在做 SNAT 时,会往该表中插入一条记录;当回复的包回来以后,会查询该表,找出之前的源也就是现在的目标地址,然后重新构建网络包发给真正的源主机。因为这是地址和端口的组合作为映射的键,如果重复了会怎么办呢?当表中有重复记录时,SNAT 在插入记录时把源端口修改为一个未使用的端口,来保证记录的唯一性。具体可以参考 http://www.commercialventvac.com/finao/DNATs-and-SNATs.html。

2.4 目的地址转换 DNAT
要使外网内的机器能访问虚机,需要设置虚机的浮动IP。浮动 IP 在 Virtual Router 连接的 external network 的 subnet 内分配。注意浮动 IP 只有在 Virtual Router 上配置了 External network subnet gateway 才有意义。

2.4.1 浮动IP分配
创建浮动IP:

root@sun:~# neutron floatingip-create Extnet
Created a new floatingip:
+---------------------+--------------------------------------+
| Field | Value |
+---------------------+--------------------------------------+
| fixed_ip_address | |
| floating_ip_address | 10.8.127.11 |
| floating_network_id | 9c9436d4-2b7c-4787-8535-9835e6d9ac8e |
| id | 7b4cee72-ffcd-4484-a5d8-371b23bb3cc3 |

关联到一个 port:

root@sun:~# neutron port-list | grep 192.168.10.26
| d74c703e-824a-41b1-b4b3-3cd4edfa22b3 | | fa:16:3e:14:ff:6d | {"subnet_id": "ccc80588-2b0d-459b-82e9-972ff4291b79", "ip_address": "192.168.10.26"} |
root@sun:~# neutron floatingip-associate 7b4cee72-ffcd-4484-a5d8-371b23bb3cc3 d74c703e-824a-41b1-b4b3-3cd4edfa22b3
+---------------------+--------------------------------------+
| Field | Value |
+---------------------+--------------------------------------+
| fixed_ip_address | 192.168.10.26 |
| floating_ip_address | 10.8.127.11 |

每个浮动 IP 唯一对应一个 Router:浮动IP -> 关联的 Port -> 所在的 Subnet -> 包含该 subnet 以及 external subnet 的 Router。创建浮动 IP 时,在 Neutron 完成数据库操作来分配浮动IP后,它通过 RPC 来通知该浮动IP对应的 router 去设置该浮动IP对应的 iptables 规则。上面的例子中,固定IP 为 ‘192.168.10.26’ 的虚机可以在外网中使用浮动 IP  ‘10.8.127.11’ 来访问了。

创建浮动 IP 的时候允许指定浮动IP 地址,这么做就能够对同一个虚机使用同一个浮动IP。

root@controller:~/s1# neutron floatingip-create --port-id 97f3ed61-7f9e-4f0a-91af-e95a572acd9c --floating-ip-address 9.115.251.105 5b4daf62-d992-453f-b74d-8c585365a604
Created a new floatingip:
+---------------------+--------------------------------------+
| Field | Value |
+---------------------+--------------------------------------+
| fixed_ip_address | 70.0.0.105 |
| floating_ip_address | 9.115.251.105 |
| floating_network_id | 5b4daf62-d992-453f-b74d-8c585365a604 |
| id | 77935422-e2cf-4dc6-85a3-26a58a493f70 |
| port_id | 97f3ed61-7f9e-4f0a-91af-e95a572acd9c |
| router_id | b94a203d-5317-4d0b-9833-5c65e01bd76f |
| status | DOWN |
| tenant_id | dea8b51d28bf41599e63464828102759 |
+---------------------+--------------------------------------+
默认情况下,只有 admin 才能使用,不过,可以修改 policy.json 文件将其向普通租户开放。

2.4.2 Neturon DNAT Chains
外网访问虚机时,目的 IP 地址为虚机的浮动 IP 地址,因此,必须由 iptables 将其转化为固定 IP 地址,然后再将它路由到虚机。我们需要关注的是 iptables 的 nat 表的 PREOUTING chain:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N neutron-l3-agent-OUTPUT
-N neutron-l3-agent-PREROUTING #neutron 增加的 DNAT chain
-A PREROUTING -j neutron-l3-agent-PREROUTING # DNAT 由 neutron 新增的 chain 负责处理
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A neutron-l3-agent-OUTPUT -d 192.168.1.104/32 -j DNAT --to-destination #本机访问浮动IP 修改为固定 IP

-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697 #将虚机访问 metadata server 的 traffic 端口由 80 改到 9697(由配置项 metadata_port 设置,默认为 9697),那里有 application 在监听。具体内容很深,可以参考这篇文章。

-A neutron-l3-agent-PREROUTING -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14 #到浮动IP的traffic的目的IP 换成虚机的固定 IP

每个浮动 IP,增加三个规则:

-A neutron-l3-agent-PREROUTING -d -j DNAT --to-destination #从本机访问虚机,Dst IP 由浮动IP该为访问固定IP
-A neutron-l3-agent-OUTPUT -d -j DNAT --to #从别的机器上访问虚机,DST IP 由浮动IP改为固定IP
-A neutron-l3-agent-float-snat -s -j SNAT --to #虚机访问外网,将Src IP 由固定IP改为浮动IP
这里可以看到当设置了浮动 IP 以后,SNAT 不在使用 External Gateway 的 IP 了,而是使用浮动 IP 。虽然 entires 依然存在,但是因为 neutron-l3-agent-float-snat 比 neutron-l3-agent-snat 靠前而得到执行。

-A neutron-l3-agent-float-snat -s 91.1.180.14/32 -j SNAT --to-source 192.168.1.104
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -s 91.1.180.0/24 -j SNAT --to-source 192.168.1.110
-A neutron-l3-agent-snat -s 81.1.180.0/24 -j SNAT --to-source 192.168.1.110
-A neutron-postrouting-bottom -j neutron-l3-agent-snat
2.4.3 实验:从外网192.168.1.15 ping虚机 81.1.180.14 的浮动IP 192.168.1.104

  1. 在route 的连接外网网段的interace 上:

17:58:46.116944 08:00:27:c7:cf:ca > fa:16:3e:2e:5b:23, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 25176, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.15 > 192.168.1.104: ICMP echo request, id 24530, seq 4, length 64
17:58:46.117910 fa:16:3e:2e:5b:23 > 08:00:27:c7:cf:ca, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 23128, offset 0, flags [none], proto ICMP (1), length 84)
192.168.1.104 > 192.168.1.15: ICMP echo reply, id 24530, seq 4, length 64
2. 在 router 的连接内网网段的interface上:

root@network:/home/s1# ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 tcpdump -envi qr-2aa928c9-e8 -vvv
tcpdump: listening on qr-2aa928c9-e8, link-type EN10MB (Ethernet), capture size 65535 bytes
^C19:46:12.266739 fa:16:3e:90:e5:50 > fa:16:3e:f3:1e:c0, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 63, id 53299, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.1.15 > 91.1.180.14: ICMP echo request, id 2831, seq 1, length 64
19:46:12.269143 fa:16:3e:f3:1e:c0 > fa:16:3e:90:e5:50, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 23157, offset 0, flags [none], proto ICMP (1), length 84)
91.1.180.14 > 192.168.1.15: ICMP echo reply, id 2831, seq 1, length 64

2.5 L3 Agent iptables 完整流程实验
该实验中使用 “ iptables -t nat -L -nv” 命令来查看每个链上匹配到的数据包数目。

2.4.1 虚机 91.1.180.14 ping 另一个虚机 81.1.180.12

可见:

(1)DNAT 匹配到的是默认的 Policy

(2)SNAT 匹配到 “-A neutron-l3-agent-POSTROUTING ! -i qg-3c8d6a68-97 ! -o qg-3c8d6a68-97 -m conntrack ! --ctstate DNAT -j ACCEPT” 规则后就被 Accept 了。

2.5.1 虚机 91.1.180.14 ping 外网 192.168.1.4

可见:

(1)DNAT 匹配到的是默认的 Policy ACCEPT

(2)SNAT 匹配到 “-A neutron-l3-agent-float-snat -s 91.1.180.14/32 -j SNAT --to-source 192.168.1.104” 规则后做 SNAT。

2.5.2 外网 192.168.1.4 ping 虚机 192.168.1.104(91.1.180.14)

可见:

(1)DNAT 匹配到 “-A neutron-l3-agent-PREROUTING -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14” 然后做 DNAT。

(2)SNAT 匹配到的是默认的 Policy ACCEPT。

为什么结果中显示的 pacakge 数目只是1呢?参考网上文章,对于 SNAT 和 DNAT target 来说,如果一个包被匹配了,那么和它属于同一个流的所有的包都会被自动转换,然后就可以被路由到正确的主机或网络,这么说来,一个流中被匹配的包的数目就是1了。

总结:

  1. Neutron L3 Agent 主要代码结构
    L3 Agent 启动后,它有若干个 Workers 去 MQ 中拿数据,然后将数据放进一个内部的 queue 中。它还会启动一个循环线程去queue 中取数据。当发现有 router 相关的操作发生后,即调用 _process_routers_loop 方法去处理获取的数据。

3.1 L3 Agent 启动
Neutorn L3 Agent 的 binary 是 /usr/bin/neutron-l3-agent,其main 会初始化一个 class Service(n_rpc.Service) 类的实例。在该实例的 start 函数中,它会启动两个周期性任务:

(1).启动 一个线程来执行循环函数 _process_routers_loop 来处理第二个周期性任务添加到 _queue 中的每一个 router 的 action,包括删除、添加和更新。对每一个待处理的 router,最终会调用到 RouterInfo.process 方法。注意 router 的操作都是在其对应的 namespace 中进行的。在 namespace 创建的时候,执行 'sysctl -w net.ipv4.ip_forward=1' 来使得它能够做 IP 路由转发。

def process(self, agent):
"""Process updates to this router
This method is the point where the agent requests that updates be applied to this router.
:param agent: Passes the agent in order to send RPC messages.
"""
self._process_internal_ports()
self.process_external(agent)
# Process static routes for router
self.routes_updated()

    # Update ex_gw_port and enable_snat on the router info cache
    self.ex_gw_port = self.get_ex_gw_port()
    self.snat_ports = self.router.get(
        l3_constants.SNAT_ROUTER_INTF_KEY, [])
    self.enable_snat = self.router.get('enable_snat')

(2)启动一个周期性函数 periodic_sync_routers_task。它负责通过 RPC 获取到 router 列表,然后将需要增加和删除的 router 加入到 _queue 中。

3.2 Router 处理
L3 Agent 的核心是 Router 的处理。

(1)处理 external gateway (比如,external_gateway_added 增加 external gateway:获取该 router 的所有浮动 IP,在agent_conf.external_network_bridge 所指定的外网物理 OVS bridge 上增加一个 tap 设备,名称为 “gq-*”,然后设置其 MAC 地址,MTU 等)

(2)修改路由表 (routes_updated)

(3)在有 external gateway 时,设置 SNAT iptables (_handle_router_snat_rules):先删除当前所有 POSTROUTING 和 snat chains,然后再增加:

增加 SNAT chain

-N neutron-l3-agent-float-snat

为 external gateway

-A neutron-l3-agent-POSTROUTING ! -i qg-3c8d6a68-97 ! -o qg-3c8d6a68-97 -m conntrack ! --ctstate DNAT -j ACCEPT

为每一个子网创建一条 SNAT 规则

-A neutron-l3-agent-snat -s 91.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110
-A neutron-l3-agent-snat -s 81.1.180.0/24 -j SNAT --to-source 192.168.1.110 #(5)将 91.1.180.0/24 网段的包的目的 IP 转为 192.168.1.110

(4)如果有 external gateway 的话,处理浮动 IP 的 SNAT/DNAT iptables (process_snat_dnat_for_fip)

为每一个浮动 IP,以 192.168.1.104 为例

-A neutron-l3-agent-PREROUTING -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14 #DNAT
-A neutron-l3-agent-OUTPUT -d 192.168.1.104/32 -j DNAT --to-destination 91.1.180.14 #本机访问
-A neutron-l3-agent-float-snat -s 91.1.180.14/32 -j SNAT --to-source 192.168.1.104 #SNAT
(5)将浮动IP 配置到 external gateway port (process_floating_ip_addresses -> add_floating_ip -> _add_fip_addr_to_device)

42: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97
valid_lft forever preferred_lft forever
inet 192.168.1.104/32 brd 192.168.1.104 scope global qg-3c8d6a68-97
valid_lft forever preferred_lft forever
(6)除了上述操作,L3 Agent 还在创建/添加 router 的每一个端口(包括internal 的和 external 的)时发出 Gratuitous ARP,通知其所在广播域内的其它机器去更新它的 ARP 表中该 IP 条目。比如:

'ip netns exec qrouter-b49a8032-a676-4ef9-aade-355592949aaa arping -A -I qg-e83aae8d-d2 -c 3 192.168.1.100'
其中,“-A” 表示不需要 ARP 返回包;“-I” 表示通过指定的 port 发出包,这样就算只对有需要的subnet 发出包了;“-c 3” 表示尝试3次。

虚拟路由

没有internel标记说明不是内部口

路由创建后,默认会创建的端口,位于

对应

snat的中的接口都在br-int上

active的路由器在fusion4上,但此时路由器中并未有有效路由,因为没有添加网络接口

fusion4上的snat

改ha-

添加一个私网到路由器,将网关加进来了

添加外网

虚机内ping一个外网ip,可达

转发路径

改端口位于fusion3上。

tcpdump -i vxlan_sys_4789 -vvve -HH

tunnel网卡抓包,发现fusion3封装后发给了fusion4,因为路由器在fusion4上?

ovs-ofctl dump-flows br-int
cookie=0x9a55c7ac0628c73b, duration=5440.615s, table=1, n_packets=0, n_bytes=0, priority=4,dl_vlan=12,dl_dst=fa:16:3e:c7:98:b9 actions=strip_vlan,mod_dl_src:fa:16:3e:a4:d0:cc,output:"tap503fa550-af"

()[root@fusion3 /]# ovs-appctl dpif/dump-flows br-int
recirc_id(0),in_port(30),eth(src=fa:16:3e:c7:98:b9,dst=fa:16:3e:a4:d0:cc),eth_type(0x0800),ipv4(frag=no), packets:7912, bytes:775376, used:0.330s, actions:11

路由的命名空间中的iptables
查看router namespace,发现iptables的NAT talbe中有以下两行规则。

ip netns exec qrouter-fce64ebe-47f0-4846-b3af-9cf764f1ff11 iptables-save

-A neutron-l3-agent-scope -i qr-7adad09d-59 -j MARK --set-xmark 0x4000000/0xffff0000

-A neutron-l3-agent-scope -i qr-6ce5518c-45 -j MARK --set-xmark 0x4000000/0xffff0000

snat的命名空间中的iptables
-A neutron-l3-agent-POSTROUTING ! -i qg-4f9e99f5-10 ! -o qg-4f9e99f5-10 -m conntrack ! --ctstate DNAT -j ACCEPT

-A neutron-l3-agent-snat -o qg-4f9e99f5-10 -j SNAT --to-source 10.19.172.109

-A neutron-l3-agent-snat -m mark ! --mark 0x2/0xffff -m conntrack --ctstate DNAT -j SNAT --to-source 10.19.172.109

因此,从net1或net2向外网发出的网络包,其源IP地址会被修改为10.19.172.109

路由器分布在在这三个节点上,但是vip在只在一个节点上,vrrp路由高可用

出口为一个内部口,怎么出去的呢?

fusion4是vrrp的虚ip所在节点,但不是vm所在的宿主机

下面查看控制节点网络结构的变化,执行 ovs-vsctl show:
br-ex 与 br-int 通过 patch port “phy-br-ex” 和 “int-br-ex” 连接。

ext_net连接到 Neutron 的虚拟路由器,这样 instance 才能访问外网。

Port "ha-deda88f6-76"

169.254.192.6,network:router_ha_interface

Port "qr-6ce5518c-45"

193.1.1.254,network:router_interface_distributed

Port "sg-b06c4bd7-ea"

194.1.1.8,network:router_centralized_snat

router interface 的命名规则如下:
如果 interface 用于连接租户网络,命名格式为 qr-xxx。

如果 interface 用于连接外部网络,命名格式为 qg-xxx。

路由的两个空间
将路由器连接到外部网络后,路由器的SNAT部分将以dvr_snat模式在L3代理程序中进行调度。

qrouter命名空间,与在计算节点上创建的命名空间相同,用于为该计算机上的VM,DHCP或LB端口提供服务,以及用于集中式SNAT服务的“snat”命名空间。

我们可以在SNAT命名空间中这些'sg'设备来自哪里?这些是附加端口,一个用于路由器连接的每个内部网络。这就是为什么路由器现在在每个内部网络中都有两个端口,计算节点上的'qr'设备和SNAT命名空间中的'sg'设备。这些'sg'端口在VM SNAT流量期间用作额外跳跃。

跟踪数据包
当没有浮动IP的VM发送流向外部世界的流量时,它会访问其节点上的qrouter命名空间,从而将消息重定向到SNAT命名空间。

查看 router 的路由表信息:

通过 traceroute 查看一下

当数据包从 router 连接外网的接口 qg-cf54d3ea-6a 发出的时候,会做一次 Source NAT,将包的源地址修改为 router 的接口地址 10.10.10.2,这样就能够保证目的端能够将应答的包发回给 router,然后再转发回源端 instance。

floating IP
通过 SNAT 使得 instance 能够直接访问外网,但外网还不能直接访问 instance。

直接访问 instance 指的是通信连接由外网发起,例如从外网 SSH instance。

如果需要从外网直接访问 instance,可以利用 floating IP。

Open vSwitch driver 环境中 floating IP 的实现是通过在 router 提供网关的外网 interface 上配置 iptables NAT 规则实现。

VxLAN 的 Flow 规则。下面分析控制节点上的 flow rule,计算节点类似。
br-int 的 flow rule

br-int 的 rule 看上去虽然多,其实逻辑很简单,br-int 被当作一个二层交换机,其重要的 rule 是下面这条:

cookie=0xaaa0e760a7848ec3, duration=52798.625s, table=0, n_packets=143, n_bytes=14594, idle_age=9415, priority=0 actions=NORMAL

此规则的含义是:根据 vlan 和 mac 进行转发。

br-tun 的 flow rule

这些才是真正处理 VXLAN 数据包的 rule,流程如下:

上图各方块中的数字对应 rule 中 table 的序号,比如编号为0的方块对应下面三条 rule。

table 0

cookie=0xaaa0e760a7848ec3, duration=76707.867s, table=0, n_packets=70, n_bytes=6600, idle_age=33324, hard_age=65534, priority=1,in_port=1 actions=resubmit(,2)
cookie=0xaaa0e760a7848ec3, duration=76543.287s, table=0, n_packets=56, n_bytes=4948, idle_age=33324, hard_age=65534, priority=1,in_port=2 actions=resubmit(,4)
cookie=0xaaa0e760a7848ec3, duration=76707.867s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop

port 编号:

table的含义
table 0 flow rule 的含义为:
从 port 1(patch-int)进来的包,扔给 table 2 处理:actions=resubmit(,2)
从 port 2(vxlan-a642100b)进来的包,扔给 table 4 处理:actions=resubmit(,4)
即第一条 rule 处理来自内部 br-int(这上面挂载着所有的网络服务,包括路由、DHCP 等)的数据;第二条 rule 处理来自外部 VXLAN 隧道的数据。

table 4

cookie=0xaaa0e760a7848ec3, duration=76647.039s, table=4, n_packets=56, n_bytes=4948, idle_age=33324, hard_age=65534, priority=1,tun_id=0x64 actions=mod_vlan_vid:1,resubmit(,10)

table 4 flow rule 的含义为: 如果数据包的 VXLAN tunnel ID 为 100(tun_id=0x64),action 是添加内部 VLAN ID 1(tag=1),然后扔给 table 10 去学习。

table 10

cookie=0xaaa0e760a7848ec3, duration=76707.865s, table=10, n_packets=56, n_bytes=4948, idle_age=33324, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xaaa0e760a7848ec3,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

table 10 flow rule 的含义为: 学习外部(从 tunnel)进来的包,往 table 20 中添加对返程包的正常转发规则,然后从 port 1(patch-int)扔给 br-int。

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 2

cookie=0xaaa0e760a7848ec3, duration=76707.866s, table=2, n_packets=28, n_bytes=3180, idle_age=33324, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
cookie=0xaaa0e760a7848ec3, duration=76707.866s, table=2, n_packets=42, n_bytes=3420, idle_age=33379, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)

table 2 flow rule 的含义为:

br-int 发过来数据如果是单播包,扔给 table 20 处理:resubmit(,20)
br-int 发过来数据如果是多播或广播包,扔 table 22 处理:resubmit(,22)
table 20

cookie=0xaaa0e760a7848ec3, duration=76543.287s, table=20, n_packets=28, n_bytes=3180, idle_age=33324, hard_age=65534, priority=2,dl_vlan=1,dl_dst=fa:16:3e:fd:8a:ed actions=strip_vlan,set_tunnel:0x64,output:2
cookie=0xaaa0e760a7848ec3, duration=76707.865s, table=20, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=resubmit(,22)

table 20 flow rule 的含义为:

第一条规则就是 table 10 学习来的结果。内部 VLAN 号为 1(tag=1),目标 MAC 是 fa:16:3e:fd:8a:ed(virros-vm2)的数据包,即发送给 virros-vm2 的包,action 是去掉 VLAN 号,添加 VXLAN tunnel ID 100(十六进制 0x64),并从 port 2 (tunnel 端口 vxlan-a642100b) 发出。
对于没学习到规则的数据包,则扔给 table 22 处理。
table 22

cookie=0xaaa0e760a7848ec3, duration=76543.282s, table=22, n_packets=2, n_bytes=84, idle_age=33379, hard_age=65534, dl_vlan=1 actions=strip_vlan,set_tunnel:0x64,output:2
cookie=0xaaa0e760a7848ec3, duration=76707.82s, table=22, n_packets=40, n_bytes=3336, idle_age=65534, hard_age=65534, priority=0 actions=drop

table 22 flow rule 的含义为: 如果数据包的内部 VLAN 号为 1(tag=1),action 是去掉 VLAN 号,添加 VXLAN tunnel ID 100(十六进制 0x64),并从 port 2 (tunnel 端口 vxlan-a642100b) 发出。

重要命令
查看流表
ovs-ofctl dump-flows br-tun

Life is not easy, Let’s go through these flow one by one.

Table0 |--from internal---> Table2 |--unicast --> Table20

   |                           |--broadcast--> Table22

   |

   |--from outside ---> Table4 --tun_id to local_vlan--> Table10(Mac learning)

Table0:

when the traffic comes from br-int, they resubmit the traffic to Table2. when the traffic comes from vxlan, they resubmit the traffic to Table4. In other case, they drop the packet.

Table2:

If packet is unicast, send to Table20, if packet is broadcast, send to Table22:

Table4:

Modify the tun_id to local vlan id, then send to Table10, if tun_id not exist, drop the packet.

Table10:

Learn the TUN_ID, vxlan of_port, tun_id to create flow in Table20.

Table20:

The learned flow will exist 300 sec, if there is learned flow, send to Table22.

Table22:

Change local vlan to tun_id, and broadcast the traffic to all the tun, if the local vlan not exist, just drop the packet.

OpenStack -- arp-spoofing / mac-spoofing
Here is openflow rule sample of br-int on the compute node.

ovs-ofctl dump-flows br-int

After the packet send out from the VM, it will go into br-int. and go through openflow tables from table 0 to table 255. Neutron use table 0, table 24, table 25 in this case. Let’s focus on these tables first.

Rules in table 0(LOCAL_SWITCHING)
Openflow will lookup flow table from high priority to low priority.

priority 10: if the packet is from VM and ARP(ICMP4) or ICMP6 type 136(IPV6), then go to table 24(ARP_SPOOF_TABLE)

priority 9: if the packet is from VM, then go to table 25(MAC_SPOOF_TABLE)

priority 2: if the packet is from other Br(br-ex/br-eth0…), then drop it

priority 0: Normal action.

here is the flows:

Rules in table 24(ARP_SPOOF_TABLE)
Only resubmit to table 25 when the ip is allocated by neutron, drop other packets:

Rules in table 25(MAC_SPOOF_TABLE)
Do normal switch only if the MAC address is allocated in neutron.

Allowed address pairs feature impact
if you add allowed address pairs with MAC, the mac will be allowed here too. Here is a sample.

You can find iptables rules updated in iptables anti ip spoofing chain.

The flow table rules for anti-arp and anti-mac address are also updated.

iptables
netfilter/iptables(简称为iptables)组成 Linux 平台下的包过滤防火墙。其中,iptables 是一个 linux 用户空间(userspace)模块,位于/sbin/iptables,用户可以使用它来操作防火墙表中的规则。真正实现防火墙功能的是 netfilter,它是一个 linux 内核模块,做实际的包过滤。实际上,除了 iptables 以外,还有很多类似的用户空间工具。

Netfilter 是一套数据包过滤框架,在处理 IP 数据包时 hook 了5个关键钩子。通过这5个关键点来实现各种功能,比如firewall/ips。
ip_tables 是真正的内核防火墙模块,通过把自己的函数注入到 Netfilter 的框架中来实现的防火墙功能.
Netfilter 提供了最基本的底层支撑,具体的功能实现只要注册自己的函数就可以了,这样保证了协议栈的纯净与可扩展性.通过上图可以看出 netfilter 与 iptables是分离的.
数据包处理过程:

数据包从左边进入IP协议栈,进行 IP 校验以后,数据包被第一个钩子函数 PRE_ROUTING 处理。
然后就进入路由模块,由其决定该数据包是转发出去还是送给本机。
若该数据包是送给本机的,则要经过钩子函数 LOCAL_IN 处理后传递给本机的上层协议;若该数据包应该被转发,则它将被钩子函数 FORWARD 处理,然后还要经钩子函数 POST_ROUTING 处理后才能传输到网络。
本机进程产生的数据包要先经过钩子函数 LOCAL_OUT 处理后,再进行路由选择处理,然后经过钩子函数POST_ROUTING处理后再发送到网络。
netfilter 使用表(table)和 链(chain)来组织网络包的处理规则(rule)。它默认定义了以下表和链:

表功能

链功能

raw

PREROUTING

OUTPUT
RAW 拥有最高的优先级,它使用PREROUTING和OUTPUT两个链,因此 RAW 可以覆盖所有包。在raw表中支持一个特殊的目标:TRACE,使内核记录下每条匹配该包的对应iptables规则信息。使用raw表内的TRACE target 即可实现对iptables规则的跟踪调试。比如:

iptables -t raw -A OUTPUT -p icmp -j TRACE

ipt ables -t raw -A PREROUTING -p icmp -j TRACE

Filter

包过滤

FORWARD
过滤目的地址和源地址都不是本机的包

INPUT
过滤目的地址是本机的包

OUTPUT
过滤源地址是本机的包

Nat

网络地址转换

PREROUTING
在路由前做地址转换,使得目的地址能够匹配上防火墙的路由表,常用于转换目的地址。

POSTROUTING
在路由后做地址转换。这意味着不需要在路由前修改目的地址。常用语转换源地址。

OUTPUT
对防火墙产生的包做地址转换(很少量地用于 SOHO 环境中)

Mangle

TCP 头修改

PREROUTING
POSTROUTING
OUTPUT
INPUT
FORWARD
在路由器修改 TCP 包的 QoS(很少量地用在 SOHO 环境中)

每个注册的 Hook 函数依次调用各表的链的规则来处理网络包:

PREROUTING Hook 依次调用 Managle 和 Nat 的 PREOUTING 链中的规则来处理网络包
LOCAL_IN Hook 依次调用 MANGLE 和 Filter 的 INPUT 链中的规则来过滤网络包
LOCAL_OUT Hook 依次调用 Mangle,Nat,Filter 表的 Output 链中的规则来过滤网络包
FORWARD Hook 依次调用 Mangle 和 Filter 表的 FORWARD 链中的规则来过滤网络包
POST_ROUTING Hook 依次调用 Managle 和 Nat 表的 POSTROUTING 链中的规则来处理网络包
如果数据包的目的地址是本机,那它被 INPUT 处理。
如果数据包的源地址是本机,那它被 OUTPUT 处理。
如果源地址和目的地址都是别的机器,那它被 FORWARD 链处理。
对 Neutron Virtual Router 所使用的 filter 表来说,它的三个链 INPUT, FORWARD, 和 OUTPUT 是分开的。一个数据包,根据其源和目的地址,只能被其中的某一个处理。
图中的 ”路由判断“ 即判断包的目的地址。如果目的地址不是本机的地址,那它就是需要被路由的包;如果目的地址是本机的,那么被filter 的 INPUT 处理后,被主机的某个程序处理。该程序如果需要发回响应包的话,其源地址肯定是本机的,所有它会被 filter 的 OUTPUT 链处理。该包不一定会出网卡,因为可能会走 loopback,它又会回到本机,重新走封包进入的过程。

1.2.2 iptables
iptables 是一个 CLI 类型的 Linux 用户空间工具,它使得系统管理员能够配置netfile 表(tables)中的链和规则。Linux 使用不同的内核模块和应用来管理不同的网络协议iptable 适用于 ipv4,ip6tables 适用于 ipv6,arptables 适用于 ARP,ebtables 适用于网络帧。iptales 需要管理员权限。

规则(rules)其实就是网络管理员预定义的条件,规则一般的定义为“如果数据包头符合这样的条件,就这样处理这个数据包”。规则存储在内核空间的信息包过滤表中,这些规则分别指定了源地址、目的地址、传输协议(如TCP、UDP、ICMP)和服务类型(如HTTP、FTP和SMTP)等。当数据包与规则匹配时,iptables就根据规则所定义的方法来处理这些数据包,如放行(accept)、拒绝(reject)和丢弃(drop)等。配置防火墙的主要工作就是添加、修改和删除这些规则。

操作 iptables 服务:

/etc/init.d/iptables start/stop/restart

iptables 各命令选项:

-p, --protocol protocol: tcp, udp, udplite, icmp, esp, ah, sctp 之一
-s, --source address[/mask] a network name, a hostname, a network IP address (with /mask), or a plain IP address.
-d, --destination address[/mask][,...]:同 -s
-j, --jump target:match 后的 target。
-g, --goto chain
[!] -i, --in-interface name:连接进来的 interface 名称。!表示否。
[!] -o, --out-interface name
其中 -j:

当数据包进入后,会依次比照 iptables 中的每条规则,直到有一条规则可以对该报文进行匹配,这时该报文将被执行"ACCEPT","DORP","REJECT" 或者其它动作,除 REDIRECT 外,执行完后停止跟余下的 iptables 规则匹配。

-ACCEPT: 将封包放行,进行完此处理动作后,将不再比对其它规则,直接跳往下一个规则链。
-REJECT: 拦阻该封包,并传送封包通知对方。
-DROP: 丢弃封包不予处理,进行完此处理动作后,将不再比对其它规则,直接中断过滤程序。
-DNAT:DNAT 改写封包目的地 IP 为某特定 IP 或 IP 范围,可以指定 port 对应的范围,进行完此处理动作后,将会直接跳往下一个规则链。
-REDIRECT: 将封包重新导向到另一个端口(PNAT),进行完此处理动作后,将会继续比对其它规则。
-SNAT: 改写封包来源 IP 为某特定 IP 或 IP 范围,可以指定 port 对应的范围,进行完此处理动作后,将直接跳往下一个规则链。
-RETURN:中断当前链,返回调用链或者默认的policy。
一些例子:

iptables -A INPUT -s 10.10.10.10 -j DROP #丢弃从 10.10.10.10 主机来的所有包
iptables -A INPUT -s 10.10.10.0/24 -j DROP #丢弃从 10.10.10.0/24 网段进来所有包
iptables -A INPUT -p tcp --dport ssh -s 10.10.10.10 -j DROP # 如果协议是 tcp,目标端口是 ssh 端口,源IP 为 10.10.10.10,那么丢弃它
iptables -A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT #接受从 virbr0 进来的所有目标端口 53 的 udp 包
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT #接受 RELEASED 和 ESTABLISHED 状态的连接。Linux 3.7 以后,--state 被替换成了 --conntrack
iptables -A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED,ESTABLISHED -j ACCEPT #转发时接受这些包
iptables -A FORWARD -p icmp -j ACCEPT #转发时接受所有 ICMP 路由包。
iptables -A INPUT -i lo -j ACCEPT #使用 -i 过滤从 lo 设备进来的包
iptables -A INPUT -i eth0 -j ACCEPT #使用 -i 过滤从网卡 eth0 进来的包。不指定网卡的话表示所有网卡。
封包过滤实现的是针对安全方面的策略,通常我们遵循“凡是没有明确允许的都是禁止的”这样的原则来设计安全策略:首先禁止所有的东西,然后根据需要再开启必要的部分。

Neutron 主要用到 filter 表和 nat 表,其中, filter 用来实现安全组(Security Group)和 防火墙(FWaas);nat 主要用来实现 router。

1.2.3 NAT 的实现
可以使用 iptables nat 表来实现网络地址转换(NAT)。NAT 包括 SNAT (源地址转换)和 DNAT (目的地址转换)。两者的区别在于做地址转换是在路由前还是路由后:

(1)SNAT:路由 - 转换 - 发出

数据经过时, 源地址发生改变,目的地址不变。SNAT 的具体数据流向:

封包先经过 PREROUTING,检查目的 IP 是不是本网段的地址。是的话,走路径A。
如果不是,则开始查询路由表,查找到相应路由条目后(查找路由的过程在 PREROUTING 和 FORWARD 之间),经过 FORWARD 链进行转发,再通过 postrouting 时进行NAT转换。
从这里可以看出,SNAT转换的步骤在 POSTROUTING 链上实现, PREROUTING 只是用来做路由选择。因此,要做 SNAT 的话,需要添加 POSTROUTING 规则,使用 “-j SNAT -to-source”。比如:

iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT -to-source 100.100.100.1
(2)DNAT:转换 - 路由- 发出

DNAT 的功能正好和 SNAT 相反,源地址不变,目的地址发生改变。DNAT 可以用作 PNAT,可以将一个 IP 的端口转换成另一个IP的另外一个端口号,经常用于内网服务器映射到公网,用来隐藏服务器的真实地址。DNAT 的具体数据流向:

在 DNAT 中,NAT 是在 PREROUTING 上做的。在数据进入主机后,路由选择过程是在 PREROUTING 和 FORWARD 之间的,所以应该先做地址转换之后,再进行路由选择,而后经过 FORWARD,最后从 POSTROUTING 出去。
因此,要做 DNAT,需要添加 PREROUTING 规则,使用 “-j DNAT --to-destination”。比如:
iptables -t nat -A PREROUTING -d 100.100.100.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.252.1

有一类比较特殊的 DNAT 是使用 “-j REDIRECT” 做端口号转换:

Send incoming port-80 web traffic to our squid (transparent) proxy

iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80-j REDIRECT --to-port 3128

1.3 route (Linux 路由表)
Linux 系统的 route 命令用于显示和操作 IP 路由表,它的主要作用是创建一个静态路由来指定一个主机或者一个网络通过一个网络接口,如eth0。

route [-CFvnee]

route [-v] [-A family] add [-net|-host] target [netmask Nm] [gw Gw] [metric N] [mss M] [window W] [irtt I] [reject] [mod] [dyn] [rein-
state] [[dev] If]

route [-v] [-A family] del [-net|-host] target [gw Gw] [netmask Nm] [metric N] [[dev] If]
例子:

route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 #增加一条经过 eth0 到达 244.0.0.0 的路由
route add -net 224.0.0.0 netmask 240.0.0.0 reject #增加一条屏蔽的路由,目的地址为224.x.x.x将被拒绝。
route del -net 224.0.0.0 netmask 240.0.0.0
route del -net 224.0.0.0 netmask 240.0.0.0 reject
route del default gw 192.168.120.240
route add default gw 192.168.120.240
1.4 路由器的辅助(Secondary) IP
先来看一个 Virutal Router 的 interface 的 ip 配置:

42: qg-3c8d6a68-97: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether fa:16:3e:2e:5b:23 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.110/24 brd 192.168.1.255 scope global qg-3c8d6a68-97
valid_lft forever preferred_lft forever
inet 192.168.1.104/32 brd 192.168.1.104 scope global qg-3c8d6a68-97
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe2e:5b23/64 scope link
valid_lft forever preferred_lft forever

<BROADCAST,UP,LOWER_UP>:端口的各种状态

UP: device is functioning (enabled 状态,可通过 ip * up/down 设置。)
BROADCAST: device can send traffic to all hosts on the link (能够发广播)
MULTICAST: device can perform and receive multicast packets (能够发多播)
ALLMULTI: device receives all multicast packets on the link (能够接收多播)
PROMISC: device receives all traffic on the link (接收所有的traffic)
LOWER_UP:the state of the Ethernet link(表示线已接上)
inet/brd/scope:IP 地址及子网掩码,广播地址,作用域

scope:

global:valid everywhere
site:valid only within this site (IPv6)
link:valid only on this device
host:valid only inside this host (machine)
注意到这个interface有两个静态 IP 地址。第一个是主要的(primary)IP,第二个是辅助的( secondary) 的 IP。当一个网卡配置了静态IP后,你可以添加secondary IP 给它。这样它就拥有了多个 IP 地址了。Secondary IP 不可以通过 DHCP 分配。它所有的IP 地址都关联到它唯一的一个 MAC 地址上。那为什么需要 secondary IP 地址呢? 路由器有个 Secondary IP 的概念,这个特性可以创建逻辑子网,也就是说在一个物理网口上连接两个子网,比如这个网口接到一台交换机上,如 果这个网口没有配置Secondary IP的话,那么这台交换机只能连接一个网段的主机,比如 192.168.1.1/24,但是,如果它配置了Secondary IP,那么就可以连接两个网段的主机,比如 192.168.1.1/24 和 10.0.0.1/24。更详细的解释可以看这里 和 这里。

命令:

增加 secondary IP

ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 ip a add dev qg-3c8d6a68-97 192.168.1.105/32 brd 192.168.1.105

删除 secondar IP

ip netns exec qrouter-e438bebe-6795-4b68-a613-ec0df38d3064 ip addr del 192.168.1.104/32 dev qg-3c8d6a68-97
1.5 Gratuitous ARP
Gratuitous ARP也称为 免费ARP,无故ARP。Gratuitous ARP不同于一般的ARP请求,它并非期待得到IP对应的MAC地址,而是当主机启动的时候,将发送一个Gratuitous arp请求,即请求自己的IP地址的MAC地址。它常用于三个用途:

Change of L2 address:通告自己改变了 MAC 地址。以 ARP Response 的形式发送广播,它通常只是为了把自己的ARP信息通告/更新给局域网全体,这种Response不需要别人请求,是自己主动发送的通告。报文结构如下当一个网络设备的 MAC 地址发生改变时,发送该设备的 Gratuitous ARP,通知所在广播域内的已经包含该 IP 地址在其 ARP 表中的机器去更新它的 ARP 条目。

Duplicate address detection:重复 MAC 地址检测。以 ARP Request的形式发送广播,请求自己的MAC地址,目的是探测局域网中是否有跟自己IP地址相同的主机,也就是常说的IP冲突。发送主机并不需要一定收到此请求的回答。如果收到一个回答,表示网络中存在与自身IP相同的主机。如果没有收到应答,则表示本机所使用的IP与网络中其它主机并不冲突。

(注意:这两种 ARP 帧虽然都是广播发送的,但目的不同,从帧结构上来说,前者注重的是Target Internet Address,而后者注重的是Sender Hardware Address和Sender Inteernet Address。)

Virtual IP:用于一组服务器做 failover 时通知周围的机器新生效的 IP 地址的 MAC。

ovs
简单来说,openvswitch就是软件交换机。

OpenvSwitch的架构与基本概念
OVS构成
ovs的构成非常简单,每个部件负责各自的职责.

ovs-vswtiched

openvswitch的守护进程.

ovsdb-server

openvswitch的数据库服务,保存相关配置信息,非常轻量级

kernel Datapath

Datapath是流的一个缓存,会把流的match结果cache起来,避免下一次流继续到用户空间进行flow match

controller

这个不是ovs自身的部件,而是一个抽象的概念,指的是ovs的控制者

基本概念

Packet

网络转发的最小数据单元,每个包都来自某个端口,最终会被发往一个或多个目标端口,转发数据包的过程就是网络的唯一功能。

Bridge

OpenvSwitch中的网桥对应物理交换机,其功能是根据一定流规则,把从端口收到的数据包转发到另一个或多个端口。

Port

端口是收发数据包的单元。OpenvSwitch中,每个端口都属于一个特定的网桥。端口收到的数据包会经过流规则的处理,发往其他端口;也会把其他端口来的数据包发送出去.主要有

类型

说明

Normal

用户可以把操作系统中的网卡绑定到ovs上,ovs会生成一个普通端口处理这块网卡进出的数据包。

Internal

端口类型为internal时,ovs会创建一块虚拟网卡,端口收到的所有数据包都会交给该网卡,发出的包会通过该端口交给ovs。当ovs创建一个新网桥时,默认会创建一个与网桥同名的Internal Port.

Patch

当机器中有多个ovs网桥时,可以使用Patch Port把两个网桥连起来。Patch Port总是成对出现,分别连接在两个网桥上,在两个网桥之间交换数据。

Tunnel

隧道端口是一种虚拟端口,支持使用gre或vxlan等隧道技术与位于网络上其他位置的远程端口通讯。

Interface

接口是ovs与外部交换数据包的组件。一个接口就是操作系统的一块网卡,这块网卡可能是ovs生成的虚拟网卡,也可能是物理网卡挂载在ovs上,也可能是操作系统的虚拟网卡(TUN/TAP)挂载在ovs上。

FlowTable

流定义了端口之间数据包的交换规则.下面会对FlowTable做了详细的说明.

流表(FlowTable)
流表定义

流表是交换机进行转发策略控制的核心数据结构。交换机芯片通过查找流表表项来决策进入交换机网络流量采取合适的行为. 流表中包括一些流表项,每个表项包含若干个域:包头域,活动计数器,0个或多个执行行动.

包头域: 执行规则的条件,主要是数据包各层协议的指定,当数据包满足该条件时进行匹配.
计数器: 用来统计流量的一些信息.比如存活时间,错误,发包数等.
行动: 定义了对匹配规则的包的处理方式.可以drop,转发,修改等.
上面是openflow v1.0对flowtable的定义,openflow目前已经发布多个版本,之间会有所差别,但是整体上是一致的的。

流表的匹配

按照从小到大的顺序匹配流表
同表内的表项按照优先级顺序匹配
下面简单查看下流表规则(neutron gre网络br-tun流表),体会一下就会明白。

ovs-ofctl dump-flows br-tun

NXST_FLOW reply (xid=0x4):

从port1进来的包转到表1处理

cookie=0x0, duration=10970.064s, table=0, n_packets=189, n_bytes=16232, idle_age=16, priority=1,in_port=1 actions=resubmit(,1)

从port2进来的包转到表2处理

cookie=0x0, duration=10906.954s, table=0, n_packets=29, n_bytes=5736, idle_age=16, priority=1,in_port=2 actions=resubmit(,2)

不匹配上面两条则drop cookie=0x0, duration=10969.922s, table=0, n_packets=3, n_bytes=230, idle_age=1

ovs-*相关命令快速指南
ovs命令行挺复杂的,提供多个命令,每个命令又有多个不同的子命令,刚刚接触会觉得一团糟,但仔细理解ovs架构和基本概念后会发现命令使用起来非常简单.

ovs对每个组件都会单独提供一个命令行,所以命令行的功能都非常的专一,理解了组件的用途,也就记住了命令行的用途和大概的子命令.
命令行的子命令其实就是对数据库的CURD,而数据库表其实就是OVS的核心概念,如Bridge,Ports,FlowTables等.

这是一张详细一点的架构图.颜色深一点的线是数据包的处理过程,而浅颜色的是Management Workflow.可以很清晰的看到每个组件都会有专门的client与其进行交互.

client与组件对应关系

ovs-dpctl

datapath控制器,可以创建删除DP,控制DP中的FlowTables,最常使用show命令,其他很少手动操作

ovs-ofctl

流表控制器,控制bridge上的流表,查看端口统计信息等

ovsdb-tool

专门管理ovsdb的client

ovs-vsctl

最常用的命令,通过操作ovsdb去管理相关的bridge,ports什么的

ovs-appctl

这个可以直接与openvswitch daemon进行交互,上图中没有列出来,这么命令较少使用

常用子命令说明
ovs-dpctl show -s
ovs-ofctl show, dump-ports, dump-flows, add-flow, mod-flows, del-flows
ovsdb-tools show-log -m
ovs-vsctl
show 显示数据库内容
关于桥的操作 add-br, list-br, del-br, br-exists.
关于port的操作 list-ports, add-port, del-port, add-bond, port-to-br.
关于interface的操作 list-ifaces, iface-to-br
ovs-vsctl list/set/get/add/remove/clear/destroy table record column [value], 常见的表有bridge, controller,interface,mirror,netflow,open_vswitch,port,qos,queue,ssl,sflow.
ovs-appctl list-commands, fdb/show, qos/show

查看bridge,ports,interfaces以及相互之间的对应关系
ovs-vsctl show查看整体信息,这个命令会把相关信息都列出来,信息量大的时候就不太方便了.

查看bridge中的流表
查看某个bridge的flowtables

root@l-network-1:# ovs-ofctl dump-flows br-ex

查看port,interface属于哪个bridge,xxx-to-br即可.
root@l-network-1:~# ovs-vsctl port-to-br vxlan-ac1c0509

br-tun

root@l-network-1:~# ovs-vsctl iface-to-br vxlan-ac1c0509

br-tun

查看有哪些桥,桥中有哪些ports,哪些interfaces
root@l-network-1:~# ovs-vsctl list-br

root@l-network-1:~# ovs-vsctl list-ports br-tun

root@l-network-1:~# ovs-vsctl list-ifaces br-tun

查看隐藏的流表规则,很少使用
root@l-network-1:# ovs-appctl bridge/dump-flows br-ex

流表规则中往往使用portid来指定相关的port,可以使用show命令来对应port name与port id.
root@l-network-1:# ovs-ofctl show br-ex OFPT_FEATURES_REPLY (xid=0x2): dpid:00002c44fd8a32ce n_tables:254, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(qg-e275cd74-7d): addr:fa:16:3e:6b:0b:f3 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max 2(qg-7b2a2e0d-c0): addr:fa:16:3e:45:c7:c5 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max 3(qg-73dbbd87-ed): addr:00:00:00:00:00:00 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max

查看bridge的转发表
root@l-network-1:# ovs-appctl fdb/show br-ex

查看port的统计信息.这个命令优势是可以指定port.
ovs-ofctl dump-ports br [port]也可以查看port的统计信息.这个命令优势是可以指定port.

查看一些有用的统计信息
查看datapath统计信息.主要关注lost数值.hit表示datapath命中数,missed未命中,lost表示没有传递到用户空间就丢弃了. 主要关注lost值是否上升,如果上升说明存在问题了.该命令可以使用-s选项,会将每个port的统计信息也显示出来.

调试技巧
查看流规则的匹配

watch -d -n 1 "ovs-ofctl dump-flows "
查看统计信息
ovs-dpctl show –s
ovs-ofctl dump-ports br-int tap81b1d32f-f3
使用tcpdump抓包,需要设置端口镜像

ip link add name snooper0 type dummy
ip link set dev snooper0 up
ovs-vsctl add-port br-int snooper0

ovs-vsctl -- set Bridge br-int mirrors=@m -- --id=@snooper0 \

get Port snooper0 -- --id=@patch-tun get Port patch-tun
-- --id=@m create Mirror name=mymirror select-dst-port=@patch-tun
select-src-port=@patch-tun output-port=@snooper0 select_all=1

tcpdump -i snooper0

ovs-vsctl clear Bridge br-int mirrors
ovs-vsctl del-port br-int snooper0
ip link delete dev snooper0
查看日志

cat /var/log/openvswitch/*.log

ovsdb-tool show-log -m

record 0: "Open_vSwitch" schema, version="7.12.1", cksum="2211824403 22535"

record 1: 2015-10-20 03:41:17.728 "ovs-vsctl: ovs-vsctl --no-wait -- init -- set Open_vSwitch . db-version=7.12.1"
table Open_vSwitch insert row 909bb8ed:

record 2: 2015-10-20 03:41:17.740 "ovs-vsctl: ovs-vsctl --no-wait set Open_vSwitch . ovs-version=2.4.0 "external-ids:system-id="26c0e58d-8754-41f0-92c5-6863a2525456"" "system-type="Ubuntu"" "system-version="14.04-trusty""" table Open_vSwitch row 909bb8ed (909bb8ed): record 3: 2015-10-20 03:41:17.753 table Open_vSwitch row 909bb8ed (909bb8ed): record 4: 2015-10-20 03:42:51.586 "ovs-vsctl: ovs-vsctl add-br br-ex" table Interface insert row "br-ex" (a8df640b): table Port insert row "br-ex" (a042a3da): table Bridge insert row "br-ex" (b465bc4d): table Open_vSwitch row 909bb8ed (909bb8ed): record 5: 2015-10-20 03:42:51.683 table Interface row "br-ex" (a8df640b): table Open_vSwitch row 909bb8ed (909bb8ed):
使用ovs-appctl ofproto/trace k1=v1,k2=v2测试流匹配
root@l-network-1:~# ovs-appctl ofproto/trace br-ex in_port=10,dl_src=66:4e:cc:ae:4d:20,dl_dst=46:54:8a:95:dd:f8 -generate
Bridge: br-ex
Flow: in_port=10,vlan_tci=0x0000,dl_src=66:4e:cc:ae:4d:20,dl_dst=46:54:8a:95:dd:f8,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=NORMAL
no learned MAC for destination, flooding

Final flow: in_port=10,vlan_tci=0x0000,dl_src=66:4e:cc:ae:4d:20,dl_dst=46:54:8a:95:dd:f8,dl_type=0x0000
Megaflow: in_port=10,vlan_tci=0x0000/0x1fff,dl_src=66:4e:cc:ae:4d:20,dl_dst=46:54:8a:95:dd:f8,dl_type=0x0000
Datapath actions: 27,30,12,14,15,17,20,37,9,28,47,48,54,57,29,25,61,21
几条重要的iptables

SR-IOV
SR-IOV简介
SR-IOV,一种硬件角度出发的虚拟化解决方案

一句话解释SR-IOV

SR-IOV通过将PF分为多个VF为上层虚拟机使用,相当于虚拟机绕过VI直接使用PCIe 设备处理I/O和传输数据。

值得一提的是,物理主机启动时不能简单的扫描SR-IOV设备并列举出所有VF,因为VF没有完整的PCIe配置空间。可以用Linux PCI热插拔API动态为物理主机增加VF,然后分配给虚拟机使用。

SR-IOV实现的价值

传统虚拟化系统中大量的资源和时间损耗在Hypervisor(或者VMM)软件层面,PCIe设备的性能优势因此无法彻底发挥。而SR-IOV的价值在于消除这一软件瓶颈,助力多个虚拟机实现物理资源共享,同时使得虚拟机可以使用到NVMe SSD的高性能。

在此我们可以总结得出SR-IOV优势:

实现SR-IOV之后,VMM把中断交给虚拟机处理,而不是VMM处理I/O,提高了性能;

虚拟机直接和PCIe设备交互减轻物理主机CPU负担,使之有能力承载更多虚拟机;

SR-IOV虚拟化技术可以减少客户所需PCIe设备数量,进而节省PCIe插槽;

SR-IOV可以与其他的I/O虚拟化技术进行结合提供一个更加完整的兼具高性能和安全性的解决方案。

PCIe驱动以及NVMe驱动的修改。驱动是连接系统和SSD的关键,这里需要修改PCIe Driver对 VF BAR空间地址的分配机制以及修改NVMe Driver对VF I/O超时处理的机制

SR-IOV(PCI-SIG Single Root I/O Virtualization and Sharing)。SR-IOV 采用直接I/O 技术,绕过虚拟机监视器直接发送和接收I/O 数据。同时SR-IOV 还利用IOMMU(I/OMemory Management Unit)高效完成内存访问授权和内存地址转换。一个SR-IOV 设备具有一个或多个物理设备(Physical Function,或PF),PF 是标准的PCIe 设备。每一个PF可以创建多个虚拟设备(Virtual Function,或VF),VF 是“轻量级”的PCIe 设备。该规范的目的是:通过为虚拟机提供独立的内存地址、中断和DMA流而避免VMM的介入。SR-IOV允许一个PCI设备提供多个VFs。VMM将一个或者多个VF分配给一个虚机。一个VF同时只能被分配一个虚机。而虚拟机感知不到这个网卡是被VF的还是普通的物理网卡。

Physical Function (PF) : 网卡上的每个实体端口具有至少有一个PF。在某些情况下,网卡上的每个端口可以被分割为四个端口。例如每个端口可成分为四个PFs,或是在双端口的网络卡上能分割为总共有八个PFs。关键点是在于PF拥有完整的设置能力,它们可以被hypervisor当成是实体端口来管理。

Virtual Function (VF) : VF是和VM关联的,它被限制为处理I/O流,基本上是移动数据。它们不支持对实体端口的管理。所支持的VF数不同每一个VF 拥有收发数据包的关键资源,如收发队列、DMA 通道等;而与其他VF 共享其他大部分非关键的设备资源。因此每一个VF 都有独立收发数据包的能力。若把一个VF 分配给一台虚拟机,该虚拟机就具备了直接使用该VF 进行数据包发送和接收的能力。最为重要的是,虚拟机通过VF 进行I/O 操作无需虚拟机监视器的干涉,这正是直接I/O 技术的优势之一。

VF(Virtual Function),PF虚拟出来的功能。一个或者多个VF共享一个PF,其驱动装在虚拟机上,当VF分配给虚拟机以后,虚拟机就能像使用普通PCIe设备一样初始化和配置VF。如果PF代表的是一张物理网卡,那么VF则是一个虚拟机可以看见和使用的虚拟网卡。
一句话解释SR-IOV:SR-IOV通过将PF分为多个VF为上层虚拟机使用,相当于虚拟机绕过VI直接使用PCIe 设备处理I/O和传输数据。
SR-IOV实现的价值
传统虚拟化系统中大量的资源和时间损耗在Hypervisor(或者VMM)软件层面,PCIe设备的性能优势因此无法彻底发挥。而SR-IOV的价值在于消除这一软件瓶颈,助力多个虚拟机实现物理资源共享,同时使得虚拟机可以使用到NVMe SSD的高性能。
在此我们可以总结得出SR-IOV优势:
实现SR-IOV之后,VMM把中断交给虚拟机处理,而不是VMM处理I/O,提高了性能;
虚拟机直接和PCIe设备交互减轻物理主机CPU负担,使之有能力承载更多虚拟机;
SR-IOV虚拟化技术可以减少客户所需PCIe设备数量,进而节省PCIe插槽;
SR-IOV可以与其他的I/O虚拟化技术进行结合提供一个更加完整的兼具高性能和安全性的解决方案。
以NVMe SSD为例,今天的一块NVMe SSD容量可以达到十几TB,而IOPS冲到了100万,同时有着微秒级的延迟。SR-IOV可以使NVMe SSD直接被上层多个VM所用,SSD的性能优势也可以直接被上层应用感知到。
SR-IOV引入了两个PCIe的function types
PFs:包括管理SR-IOV功能在内的所有PCIe function。

VFs:一部分轻量级的PCIe function,只能进行必要的数据操作和配置。

SR-IOV工作流程中有三个角色

1.PCIe的SR-IOV机制:提供独立可配置的多个VFs,每一个VFs具有独立的PCIe配置空间。

2.VMM:则把VFs分配给虚拟机。

3.VT-x和VT-d:通过硬件辅助技术提供和虚拟机之间的直接DMA数据映射传输,跳过VMM的干预。

SR-IOV原理

SR-IOV 对虚拟网络的支持
从 RHEL OpenStack Platform 6 开始,扩展了SR-IOV(single root I/O virtualization - 单引导 I/O 虚拟化)对虚拟机网络的支持。这意味着,OpenStack 可以不再需要虚拟网桥,而是把物理 NIC 的功能直接扩展到实例中。另外,通过对 IEEE 802.1br 的支持,虚拟 NIC 可以与物理交换机进行集成,并由它进行管理。

20.1. 在 RHEL OpenStack Platform 中配置 SR-IOV
本章包含配置 SR-IOV 以将物理 NIC 传递到虚拟实例的过程。这些步骤假设系统包括了一个 Controller 节点、一个 OpenStack Networking(neutron) 节点和多个 Compute(nova)节点。请注意:当已存在了适当的 L2 配置(例如,flat 或 VLAN),使用 SR-IOV 端口的实例与使用普通端口(如连接到 Open vSwitch 网桥)的实例间就可以彼此进行通讯。当前,在相同 Compute 节点上的使用 SR-IOV 端口的实例与使用普通 vSwitch 端口的实例间的通讯有一个限制:如果它们在网卡上共享相同的 PF(Physical Function),则无法进行通讯。

20.2. 在 Compute 节点上创建虚拟功能
在带有支持硬件的所有 Compute 节点上执行这些步骤。

这个步骤会配置一个系统来传递一个 Intel 82576 网络设备。同时,虚拟功能(Virtual Functions)也会被创建,实例可以使用它们来进行 SR-IOV 到设备的访问。

  1. 确认 Intel VT-d 或 AMD IOMMU 已在系统的 BIOS 中启用。请参阅机器的 BIOS 配置菜单,或其它相关信息。

  2. 确保在操作系统中启用 Intel VT-d 或 AMD IOMMU:

  3. 运行 lspci 命令确保网络设备可以被系统识别:

  4. 执行以下步骤来在 Compute 节点上激活 Virtual Functions:

4a.删除内核模块。在下一步中会对模块进行重新配置:
[root@compute ~]# modprobe -r igb
请注意:在第 4 步中,需要使用支持 SRIOV 的 NIC 所使用的模块,而不是使用其它 NIC 的 igb(如 ixgbe 或 mlx4_core)。运行 ethtool 命令来确认驱动。在这个例子中,em1 是我们需要使用的 PF(Physical Function):

[root@compute ~]# ethtool -i em1 | grep ^driver
4b. 启动模块时 max_vfs 的值为 7(或不多于被支持的最多数量)。

[root@compute ~]# modprobe igb max_vfs=7
4c. 使 Virtual Functions 有持久性:

[root@compute ~]# echo "options igb max_vfs=7" >>/etc/modprobe.d/igb.conf
请注意: 对于 Red Hat Enterprise Linux 7,为了使配置具有持久性,在进行完第 4 步后根据 rebuild the initial ramdisk image 中介绍的内容进行相关操作。

请注意:在 4c. 和 4d. 中进行的设置持久性如下:modprobe 命令在使用相同内核模块的所有 NIC 上启用 Virtual Function,并在系统重新引导后仍然可以保持这个设置。虽然可以只在特定 NIC 上启用 VF,但这可能会造成一些问题。例如,使用以下命令来在 enp4s0f1 接口上启用 VF:

echo 7 > /sys/class/net/enp4s0f1/device/sriov_numvfs

当系统重启后,这个设备将不会被保留。解决这个问题的一个方法是,把这个设置添加到 rc.local 中,但这个方法仍然有以下限制:

chmod +x /etc/rc.d/rc.local

echo "echo 7 > /sys/class/net/enp4s0f1/device/sriov_numvfs" >> /etc/rc.local

请注意:因为额外的 systemd,Red Hat Enterprise Linux 将会并行启动服务,而不是依次启动它们。这意味着,rc.local 在引导过程中被执行的位置是不固定的。因此,一些不可预见的情况可能会出现,我们不推荐使用这个方法。

4d. 把 intel_iommu=pt 和 igb.max_vfs=7 参数添加到内核命令行来在内核中激活 Intel VT-d。如果您希望一直使用这个方式引导内核,可以修改当前的设置;或者,您可以创建一个带有这些参数的自定义菜单,这样,您的系统就可以在默认情况下以这种方式启动,并且可以在需要时,不使用这些参数启动内核。

• 要修改当前内核命令行参数,运行以下命令:

[root@compute ~]# grubby --update-kernel=ALL --args="intel_iommu=pt igb.max_vfs=7"
如需了解更多使用 grubby 的信息,请参阅系统管理指南中的 Configuring GRUB 2 Using the grubby Tool。

请注意:如果使用 Dell Power Edge R630 节点,则需要使用 intel_iommu=on 而不是 intel_iommu=pt。您可以通过 grubby 启用它:

grubby --update-kernel=ALL --args="intel_iommu=on"

• 创建一个自定义菜单项:

i. 在 grub 中找到默认的项:

[root@compute ~]# grub2-editenv list
saved_entry=Red Hat Enterprise Linux Server (3.10.0-123.9.2.el7.x86_64) 7.0 (Maipo)
ii. a. 把所需的 menuentry 项的内容(以 "menuentry" 开始,以 "}" 结束。它在开始部分包括了前一步命令显示的 saved_entry 的值)从 /boot/grub2/grub.cfg 复制到 /etc/grub.d/40_custom。b. 在 menuentry 后修改标题 c. 在以 linux16 开始的行的最后添加 intel_iommu=on。

iii. 更新 grub.cfg 以使配置文件中的修改生效:

[root@compute ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
iv. 修改默认的项:

[root@compute ~]# grub2-set-default 'Red Hat Enterprise Linux Server, with Linux 3.10.0-123.el7.x86_64 - SRIOV'
v. 创建 dist.conf 配置文件。

请注意:在执行这个步骤前,请重新查看描述 allow_unsafe_interrupts 的段落:检查 allow_unsafe_interrupts 的设置。

[root@compute ~]# echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/dist.conf
6. 查看 Compute 节点上的 SR-IOV 内核模块。运行 lsmod 以确认模块已被加载:

[root@compute ~]# lsmod |grep igb
这个经过过滤的结果应该包括所需的模块:

igb 87592 0
dca 6708 1 igb
7. 记录下您的网卡的 PCI 厂商 ID(格式是 vendor_id:product_id)。运行带有 -nn 选项的 lspci 命令可以得到这个值。例如:

[root@compute ~]# lspci -nn | grep -i 82576
05:00.0 Ethernet controller [0200]: Intel Corporation 82576 Gigabit Network Connection [8086:10c9] (rev 01)
05:10.0 Ethernet controller [0200]: Intel Corporation 82576 Virtual Function [8086:10ca] (rev 01)
请注意:根据您的网卡硬件的具体情况,这个参数可能会不同。

  1. 使用 lspci 命令查看新创建的 VF:
    [root@ncpu-ndb0 ~]# lspci | grep Ethernet

01:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)

01:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)

03:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)

03:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)

03:10.1 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)

03:10.5 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)

03:11.1 Ethernet controller: Intel Corporation I350 Ethernet Controller Virtual Function (rev 01)

[root@ncpu-ndb0 ~]# ip link show ens1f1

5: ens1f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master ovs-system state UP mode DEFAULT qlen 1000

link/ether a0:36:9f:f3:49:f3 brd ff:ff:ff:ff:ff:ff

vf 0 MAC 00:00:00:00:00:00, spoof checking on, link-state auto

vf 1 MAC 0a:c4:20:65:1d:18, spoof checking on, link-state auto

vf 2 MAC fe:cf:24:ac:fc:b2, spoof checking on, link-state auto

vf 3 MAC fa:16:3e:f4:1e:43, spoof checking on, link-state auto

vf 4 MAC fa:16:3e:01:6d:dd, spoof checking on, link-state auto

vf 5 MAC fa:16:3e:d4:d6:e3, spoof checking on, link-state auto

vf 6 MAC fa:16:3e:8b:e8:00, spoof checking on, link-state auto

[root@ncpu-ndb0 ~]#

[root@ncpu-ndb0 ~]# ls -all /sys/class/net/p2p1/device/driver/module

ls: cannot access /sys/class/net/p2p1/device/driver/module: No such file or directory

[root@ncpu-ndb0 ~]# ls -all /sys/class/net/ens1f1/device/driver/module

lrwxrwxrwx 1 root root 0 May 28 18:36 /sys/class/net/ens1f1/device/driver/module -> ../../../../module/igb

20.3. 在 Network 节点上配置 SR-IOV
OpenStack Networking(neutron)使用 ML2 机制驱动来支持 SR-IOV。在 Network 网络上执行这些步骤来配置 SR-IOV 驱动。

  1. 在 /etc/neutron/plugins/ml2/openvswitch_agent.ini 文件中启用 sriovnicswitch。例如,这个配置启用了 SR-IOV 机制驱动和 Open vSwitch。
    请注意:sriovnicswitch 不支持 DHCP Agent 的当前接口驱动,因此在使用 sriovnicswitch 时需要 openvswitch(或其它支持 VLAN 的机制驱动)。
    [ml2]
    tenant_network_types = vlan
    type_drivers = vlan
    mechanism_drivers = openvswitch, sriovnicswitch
    [ml2_type_vlan]
    network_vlan_ranges = physnet1:15:20
    请注意:sriovnicswitch 当前只支持 flat 和 vlan 驱动。

  2. 可选 - 如果您需要 VF 连接状态和 admin 状态管理,而且您的厂商支持这些功能,请在 /etc/neutron/plugins/ml2/sriov_agent.ini 文件中启用这个选项:

id - id 的值可以是通配符(*),或一个有效的 device/product id。您可以使用 lspci 列出有效的设备名。
address - address 的格式与使用带有 -s 选项的 lspci 命令输出中的格式相同。
devname - devname 是一个有效的 PCI 设备名称。您可以使用 ifconfig -a 列出全部有效的名称。这个项需要和与一个 vNIC 相关联的 PF 或 VF 值相对应。如果设备由代表一个 SR-IOV PF 的地址或 devname 来定义,则 PF 下的所有 VF 必须和这个项匹配。另外,一个项可以关联0 个或多个标签。
physical_network - 在使用 SR-IOV 网络时,"physical_network" 被用来指定设备附加到的物理网络。
您可以为每个主机配置多个 whitelist 项。device_id、product_id、以及 address 或 devname的值将会和查询 libvirt 所返回的 PCI 设备进行匹配。

20.6. 启用 OpenStack Networking SR-IOV agent
可选的 OpenStack Networking SR-IOV agent 启用了对 admin_state 端口的管理。这个 agent 与网络适配器进行集成,允许管理员打开/关闭 VF 的管理状态。

另外,如果 agent_required=True 已在 OpenStack Networking(neutron)服务器上配置,则需要在每个 Compute 节点上都运行 OpenStack Networking SR-IOV Agent。

  1. 在 /etc/neutron/plugins/ml2/openvswitch_agent.ini 文件中启用 NoopFirewallDriver:

[root@compute ~]# openstack-config --set /etc/neutron/plugins/ml2/openvswitch_agent.ini securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
3. 在 /etc/neutron/plugins/ml2/sriov_agent.ini 文件中添加映射信息。在这个例子中,physnet1 是物理网络、enp4s0f1 是 physical function。把 exclude_devices 设为空来允许 agent 管理所有相关的 VF。

  1. 可选操作 - 为了在 agent 配置中排除特定的 VF,在 sriov_nic 项中列出要排除的 VF。例如:

  2. 配置 neutron-sriov-nic-agent.service 来使用 ml2_conf_sriov.ini 文件。例如:

  3. 在 web 网络中创建端口

[root@network ~]# neutron port-create web --name sr-iov --binding:vnic-type direct
3. 使用新端口创建一个实例。创建一个名为 webserver01 的新实例,配置它来使用新端口(端口 ID 是前一个输出中的 id 项中的值):

请注意:您可以使用 glance image-list 命令来获得有效镜像的列表,以及它们的 UUID。

[root@compute ~]# nova boot --flavor m1.tiny --image 59a66200-45d2-4b21-982b-d06bc26ff2d0 --nic port-id=a2122b4d-c9a9-4a40-9b67-ca514ea10a1b webserver01
新实例 webserver01 被创建,并配置为使用 SR-IOV 端口。

20.8. 检查 allow_unsafe_interrupts 设置
为了完全把带有分配设备的客户机与主机分离,需要平台对中断重映射功能的支持。如果不支持这个功能,主机可能会受到来自于恶意客户机上的中断注入攻击(interrupt injection attack)。而在一个客户端可以被完全信任的环境中,管理员可能会允许使用 allow_unsafe_interrupts 选项进行 PCI 设备的分配。您需要根据具体情况检查是否在主机上启用 allow_unsafe_interrupts。如果主机上的 IOMMU 支持中断重映射功能,则不需要启用这个选项。

  1. 使用 dmesg 检查您的主机是否支持 IOMMU 中断重映射功能:

[root@compute ~]# dmesg |grep ecap
当 ecap (0xf020ff → …​1111) 的第 3 位是 1 时,意味着 IOMMU 支持中断重映射。

  1. 确认 IRQ 重映射是否已被启用:

[root@compute ~]# dmesg |grep "Enabled IRQ"
[ 0.033413] Enabled IRQ remapping in x2apic mode
请注意:如需手工禁用 "IRQ remapping",把 intremap=off 添加到 grub.conf 文件中。

  1. 如果主机的 IOMMU 不支持中断重映射,您需要在 kvm 模块中启用 allow_unsafe_assigned_interrupts=1。

20.9. 额外需要考虑的因素
在选择 vNIC 类型时请注意,当前还不支持 vnic_type=macvtap。
不支持带有附加了 SR-IOV 实例的 VM 迁移。
当前,安全组不能在启用了 SR-IOV 的端口中使用。
SR-IOV的优缺点
SR-IOV相对与软件模拟IO虚拟化的优点:

1.降低了IO延迟和对CPU的占用,获得了接近原生的IO性能,因为虚拟机直接使用VFs,没有了VMM的陷入处理。

2.数据更加安全,因为每个VF属于一个IOMMU Group,共享IOMMU Group的设备不能分配给不同的虚拟机,而每个IOMMU Group又有独立的内存。

SR-IOV相对与Device assignment的优点:

没有了一个PCI设备只能给一个虚拟机的尴尬,SR-IOV下多个虚拟机可通过独占VFs的方式共享一个PCI设备。

SR-IOV的缺点:

使用了VFs的虚拟机不能在线迁移。
启用 OpenStack Networking SR-IOV agent
可选的 OpenStack Networking SR-IOV agent 启用了对 admin_state 端口的管理。这个 agent 与网络适配器进行集成,允许管理员打开/关闭 VF 的管理状态。

另外,如果 agent_required=True 已在 OpenStack Networking(neutron)服务器上配置,则需要在每个 Compute 节点上都运行 OpenStack Networking SR-IOV Agent。

请注意:当前,不是所有网卡厂商都支持使用这个 agent 管理端口状态。

执行以下步骤来在 Compute 节点上激活 Virtual Functions:

4a.删除内核模块。在下一步中会对模块进行重新配置:

[root@compute ~]# modprobe -r igb
请注意:在第 4 步中,需要使用支持 SRIOV 的 NIC 所使用的模块,而不是使用其它 NIC 的 igb(如 ixgbe 或 mlx4_core)。运行 ethtool 命令来确认驱动。在这个例子中,em1 是我们需要使用的 PF(Physical Function):

[root@compute ~]# ethtool -i em1 | grep ^driver
4b. 启动模块时 max_vfs 的值为 7(或不多于被支持的最多数量)。

[root@compute ~]# modprobe igb max_vfs=7
4c. 使 Virtual Functions 有持久性:

[root@compute ~]# echo "options igb max_vfs=7" >>/etc/modprobe.d/igb.conf
请注意: 对于 Red Hat Enterprise Linux 7,为了使配置具有持久性,在进行完第 4 步后根据 rebuild the initial ramdisk image 中介绍的内容进行相关操作。

请注意:在 4c. 和 4d. 中进行的设置持久性如下:modprobe 命令在使用相同内核模块的所有 NIC 上启用 Virtual Function,并在系统重新引导后仍然可以保持这个设置。虽然可以只在特定 NIC 上启用 VF,但这可能会造成一些问题。例如,使用以下命令来在 enp4s0f1 接口上启用 VF:

echo 7 > /sys/class/net/enp4s0f1/device/sriov_numvfs

当系统重启后,这个设备将不会被保留。解决这个问题的一个方法是,把这个设置添加到 rc.local 中,但这个方法仍然有以下限制:

chmod +x /etc/rc.d/rc.local

echo "echo 7 > /sys/class/net/enp4s0f1/device/sriov_numvfs" >> /etc/rc.local

请注意:因为额外的 systemd,Red Hat Enterprise Linux 将会并行启动服务,而不是依次启动它们。这意味着,rc.local 在引导过程中被执行的位置是不固定的。因此,一些不可预见的情况可能会出现,我们不推荐使用这个方法。

4d. 把 intel_iommu=pt 和 igb.max_vfs=7 参数添加到内核命令行来在内核中激活 Intel VT-d。如果您希望一直使用这个方式引导内核,可以修改当前的设置;或者,您可以创建一个带有这些参数的自定义菜单,这样,您的系统就可以在默认情况下以这种方式启动,并且可以在需要时,不使用这些参数启动内核。

• 要修改当前内核命令行参数,运行以下命令:

[root@compute ~]# grubby --update-kernel=ALL --args="intel_iommu=pt igb.max_vfs=7"
如需了解更多使用 grubby 的信息,请参阅系统管理指南中的 Configuring GRUB 2 Using the grubby Tool。

请注意:如果使用 Dell Power Edge R630 节点,则需要使用 intel_iommu=on 而不是 intel_iommu=pt。您可以通过 grubby 启用它:

grubby --update-kernel=ALL --args="intel_iommu=on"

• 创建一个自定义菜单项:

i. 在 grub 中找到默认的项:

[root@compute ~]# grub2-editenv list
saved_entry=Red Hat Enterprise Linux Server (3.10.0-123.9.2.el7.x86_64) 7.0 (Maipo)
ii. a. 把所需的 menuentry 项的内容(以 "menuentry" 开始,以 "}" 结束。它在开始部分包括了前一步命令显示的 saved_entry 的值)从 /boot/grub2/grub.cfg 复制到 /etc/grub.d/40_custom。b. 在 menuentry 后修改标题 c. 在以 linux16 开始的行的最后添加 intel_iommu=on。

iii. 更新 grub.cfg 以使配置文件中的修改生效:
[root@compute ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
iv. 修改默认的项:
[root@compute ~]# grub2-set-default 'Red Hat Enterprise Linux Server, with Linux 3.10.0-123.el7.x86_64 - SRIOV'
v. 创建 dist.conf 配置文件。
请注意:在执行这个步骤前,请重新查看描述 allow_unsafe_interrupts 的段落:检查 allow_unsafe_interrupts 的设置。

[root@compute ~]# echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/dist.conf
6. 查看 Compute 节点上的 SR-IOV 内核模块。运行 lsmod 以确认模块已被加载:

[root@compute ~]# lsmod |grep igb
这个经过过滤的结果应该包括所需的模块:

igb 87592 0
dca 6708 1 igb
7. 记录下您的网卡的 PCI 厂商 ID(格式是 vendor_id:product_id)。运行带有 -nn 选项的 lspci 命令可以得到这个值。例如:

请注意:根据您的网卡硬件的具体情况,这个参数可能会不同。

  1. 使用 lspci 命令查看新创建的 VF:

这个结果会包括设备和 Virtual Function:

[root@node9 ~]# lspci | grep Ethernet

3d:00.0 Ethernet controller: Intel Corporation Ethernet Connection X722 for 10GbE SFP+ (rev 09)

3d:00.1 Ethernet controller: Intel Corporation Ethernet Connection X722 for 10GbE SFP+ (rev 09)

3d:00.2 Ethernet controller: Intel Corporation Ethernet Connection X722 for 1GbE (rev 09)

3d:00.3 Ethernet controller: Intel Corporation Ethernet Connection X722 for 1GbE (rev 09)

3d:06.0 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 09)

3d:06.1 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 09)

20.3. 在 Network 节点上配置 SR-IOV
OpenStack Networking(neutron)使用 ML2 机制驱动来支持 SR-IOV。在 Network 网络上执行这些步骤来配置 SR-IOV 驱动。

  1. 在 /etc/neutron/plugins/ml2/openvswitch_agent.ini 文件中启用 sriovnicswitch。例如,这个配置启用了 SR-IOV 机制驱动和 Open vSwitch。

请注意:sriovnicswitch 不支持 DHCP Agent 的当前接口驱动,因此在使用 sriovnicswitch 时需要 openvswitch(或其它支持 VLAN 的机制驱动)。

[ml2]
tenant_network_types = vlan
type_drivers = vlan
mechanism_drivers = openvswitch, sriovnicswitch
[ml2_type_vlan]
network_vlan_ranges = physnet1:15:20
network_vlan_ranges - 在这个例子中,physnet1 被作为网络标签使用,VLAN 的范围是 15-20。
请注意:sriovnicswitch 当前只支持 flat 和 vlan 驱动。

  1. 可选 - 如果您需要 VF 连接状态和 admin 状态管理,而且您的厂商支持这些功能,请在 /etc/neutron/plugins/ml2/sriov_agent.ini 文件中启用这个选项:

[root@network ~]# openstack-config --set /etc/neutron/plugins/ml2/sriov_agent.ini ml2_sriov agent_required True
3. 可选 - 支持的 vendor_id/product_id 组合是 15b3:1004, 8086:10ca。如果网卡厂商的产品 ID 和这个不同,则需要指定它们。例如:

[ml2_sriov]
supported_pci_vendor_devs = 15b3:1004,8086:10ca
4. 配置 neutron-server.service 来使用 ml2_conf_sriov.ini 文件。例如:

[root@network ~]# vi /usr/lib/systemd/system/neutron-server.service
[Service]
Type=notify
User=neutron
ExecStart=/usr/bin/neutron-server --config-file /usr/share/neutron/neutron-dist.conf --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugins/ml2/openvswitch_agent.ini --config-file /etc/neutron/plugins/ml2/sriov_agent.ini --log-file /var/log/neutron/server.log
5. 重启 neutron-server 服务来使配置生效:

[root@network ~]# systemctl restart neutron-server.service
20.4. 在 Controller 节点上配置 SR-IOV

  1. 为了正确调度 SR-IOV 设备,Compute 调度程序需要使用带有 PciPassthroughFilter 过滤的 FilterScheduler。在 Controller 节点上的 nova.conf 文件中应用这个配置。例如:

scheduler_available_filters=nova.scheduler.filters.all_filters
scheduler_default_filters=RetryFilter,AvailabilityZoneFilter,RamFilter,ComputeFilter,ComputeCapabilitiesFilter,ImagePropertiesFilter,CoreFilter, PciPassthroughFilter
2. 重启 Compute 调度器以使修改生效:

[root@compute ~]# systemctl restart openstack-nova-scheduler.service
20.5. 在 Compute 节点上配置 SR-IOV
在所有 Compute 节点上,为每个物理网络关联有效的 VF:

  1. 在 nova.conf 文件中定义这些项。在这个例子中,把设备名为 enp5s0f1 的 VF 网络与网络标签为 physnet1 的物理网络相关联。其中由 physical_network 所指定的网络标签是以前在 network_vlan_ranges 中配置的。

pci_passthrough_whitelist={"devname": "enp5s0f1", "physical_network":"physnet1"}
下面的配置会把厂商 ID 为 8086 的 PF 网络与网络标签为 physnet1(由 physical_network 指定)的物理网络进行关联:~ pci_passthrough_whitelist = {"vendor_id": "8086","product_id": "10ac", "physical_network":"physnet1"} ~

PCI passthrough whitelist 项的格式如下:

["device_id": "",] ["product_id": "",]
["address": "[[[[]:]]:][][.[]]" |
"devname": "Ethernet Interface Name",]
"physical_network":"Network label string"
id - id 的值可以是通配符(*),或一个有效的 device/product id。您可以使用 lspci 列出有效的设备名。
address - address 的格式与使用带有 -s 选项的 lspci 命令输出中的格式相同。
devname - devname 是一个有效的 PCI 设备名称。您可以使用 ifconfig -a 列出全部有效的名称。这个项需要和与一个 vNIC 相关联的 PF 或 VF 值相对应。如果设备由代表一个 SR-IOV PF 的地址或 devname 来定义,则 PF 下的所有 VF 必须和这个项匹配。另外,一个项可以关联0 个或多个标签。
physical_network - 在使用 SR-IOV 网络时,"physical_network" 被用来指定设备附加到的物理网络。
您可以为每个主机配置多个 whitelist 项。device_id、product_id、以及 address 或 devname的值将会和查询 libvirt 所返回的 PCI 设备进行匹配。

  1. 重启 nova-compute 服务以使所做的改变生效:

[root@compute ~]# systemctl restart openstack-nova-compute
20.6. 启用 OpenStack Networking SR-IOV agent
可选的 OpenStack Networking SR-IOV agent 启用了对 admin_state 端口的管理。这个 agent 与网络适配器进行集成,允许管理员打开/关闭 VF 的管理状态。

另外,如果 agent_required=True 已在 OpenStack Networking(neutron)服务器上配置,则需要在每个 Compute 节点上都运行 OpenStack Networking SR-IOV Agent。

请注意:当前,不是所有网卡厂商都支持使用这个 agent 管理端口状态。

  1. 安装 sriov-nic-agent 软件包以便进行以下步骤:

[root@compute ~]# yum install openstack-neutron-sriov-nic-agent
2. 在 /etc/neutron/plugins/ml2/openvswitch_agent.ini 文件中启用 NoopFirewallDriver:

[root@compute ~]# openstack-config --set /etc/neutron/plugins/ml2/openvswitch_agent.ini securitygroup firewall_driver neutron.agent.firewall.NoopFirewallDriver
3. 在 /etc/neutron/plugins/ml2/sriov_agent.ini 文件中添加映射信息。在这个例子中,physnet1 是物理网络、enp4s0f1 是 physical function。把 exclude_devices 设为空来允许 agent 管理所有相关的 VF。

[sriov_nic]

physical_device_mappings = physnet1:enp4s0f1

exclude_devices =

  1. 可选操作 - 为了在 agent 配置中排除特定的 VF,在 sriov_nic 项中列出要排除的 VF。例如:

exclude_devices = eth1:0000:07:00.2; 0000:07:00.3, eth2:0000:05:00.1; 0000:05:00.2

  1. 配置 neutron-sriov-nic-agent.service 来使用 ml2_conf_sriov.ini 文件。例如:

[root@compute ~]# vi /usr/lib/systemd/system/neutron-sriov-nic-agent.service
[Service]
Type=simple
User=neutron
ExecStart=/usr/bin/neutron-sriov-nic-agent --config-file /usr/share/neutron/neutron-dist.conf --config-file /etc/neutron/neutron.conf --log-file /var/log/neutron/sriov-nic-agent.log --config-file /etc/neutron/plugins/ml2/sriov_agent.ini
20.8. 检查 allow_unsafe_interrupts 设置
为了完全把带有分配设备的客户机与主机分离,需要平台对中断重映射功能的支持。如果不支持这个功能,主机可能会受到来自于恶意客户机上的中断注入攻击(interrupt injection attack)。而在一个客户端可以被完全信任的环境中,管理员可能会允许使用 allow_unsafe_interrupts 选项进行 PCI 设备的分配。您需要根据具体情况检查是否在主机上启用 allow_unsafe_interrupts。如果主机上的 IOMMU 支持中断重映射功能,则不需要启用这个选项。

  1. 使用 dmesg 检查您的主机是否支持 IOMMU 中断重映射功能:

[root@compute ~]# dmesg |grep ecap
当 ecap (0xf020ff → …​1111) 的第 3 位是 1 时,意味着 IOMMU 支持中断重映射。

  1. 确认 IRQ 重映射是否已被启用:

[root@compute ~]# dmesg |grep "Enabled IRQ"
[ 0.033413] Enabled IRQ remapping in x2apic mode
请注意:如需手工禁用 "IRQ remapping",把 intremap=off 添加到 grub.conf 文件中。

  1. 如果主机的 IOMMU 不支持中断重映射,您需要在 kvm 模块中启用 allow_unsafe_assigned_interrupts=1。

20.9. 额外需要考虑的因素
在选择 vNIC 类型时请注意,当前还不支持 vnic_type=macvtap。
不支持带有附加了 SR-IOV 实例的 VM 迁移。
当前,安全组不能在启用了 SR-IOV 的端口中使用。
PCI直通
硬件直通是指将物理主机的硬件资源直接挂载给虚拟机使用,不需要经过hypervisor的模拟和过滤,虚拟机发的命令直接送到物理设备。

通过直通技术可以向虚拟机提供接近原生硬件的访问性能,对于I/O性能的提升达到了15%左右,同时还解决了数据库集群、加密狗类和图像处理类应用支持的问题。

SR-IOV
SR-IOV是在硬件直通技术的增强,可以在网卡数量有限的情况先仍然能够利用到直通技术的性能。该技术可以将一张网卡在硬件层面虚拟成多张网卡,被多台虚拟机直通使用。利用该技术不但可以保持原生硬件的性能同时可以将网卡的吞吐量提升约4%左右。

[root@ncpu-ndb0 ~]# lspci | grep Ethernet

[root@ncpu-ndb0 ~]# ls -all /sys/class/net/ens1f1/device/driver/module
lrwxrwxrwx 1 root root 0 May 28 18:36 /sys/class/net/ens1f1/device/driver/module -> ../../../../module/igb

Openstack环境中使用SR-IOV的局限及碰到的问题
不支持热迁移,只支持冷迁移
不支持虚拟机的qos
不支持安全组
请注意:当已存在了适当的 L2 配置(例如,flat 或 VLAN),使用 SR-IOV 端口的实例与使用普通端口(如连接到 Open vSwitch 网桥)的实例间就可以彼此进行通讯。当前,在相同 Compute 节点上的使用 SR-IOV 端口的实例与使用普通 vSwitch 端口的实例间的通讯有一个限制:如果它们在网卡上共享相同的 PF(Physical Function),则无法进行通讯。

SR-IOV
对于SR-IOV来说,则更加彻底,它通过创建不同虚拟功能(VF)的方式,呈现给虚拟机的就是独立的网卡,因此,虚拟机直接跟网卡通信,不需要经过软件交换机。

VF和VM之间通过DMA进行高速数据传输。

SR-IOV的性能是最好的,但是需要一系列的支持,包括网卡、主板、VMM等。

vf命令

  1. 设置VF的mac地址:

ip link set dev p5p1 vf 0 mac aa:bb:cc:dd:ee:00
2. 对指定的VF添加信任:

ip link set dev p5p1 vf 0 trust on
3. 对指定的VF是否开启packet spoof检测:

ip link set dev p5p1 vf 0 spoof checking off
4. 还有一些常用配置如下:

mac LLADDRESS: change the station address for the specified VF. Thevfparameter must be specified.

vlan VLANID: change the assigned VLAN for the specified VF. When specified, all trafficsent from the VF will be tagged with the specified VLAN ID. Incoming trafficwill be filtered for the specified VLAN ID, and will have all VLAN tagsstripped before being passed to the VF. Setting this parameter to 0 disablesVLAN tagging and filtering. Thevfparameter must be specified.

qos VLAN-QOS: assign VLAN QOS (priority) bits for the VLAN tag. When specified, all VLANtags transmitted by the VF will include the specified priority bits in theVLAN tag. If not specified, the value is assumed to be 0. Both thevfandvlanparameters must be specified. Setting bothvlanandqosas 0 disables VLAN tagging and filtering for the VF.

rate TXRATE: change the allowed transmit bandwidth, in Mbps, for the specified VF.Setting this parameter to 0 disables rate limiting.vfparameter must be specified.Please use new APImax_tx_rateoption instead.

max_tx_rate TXRATE: change the allowed maximum transmit bandwidth, in Mbps, for the specified VF.vfparameter must be specified.

min_tx_rate TXRATE: change the allowed minimum transmit bandwidth, in Mbps, for the specified VF.Minimum TXRATE should be always <= Maximum TXRATE.vfparameter must be specified.

state auto|enable|disable: set the virtual link state as seen by the specified VF. Setting to auto means areflection of the PF link state, enable lets the VF to communicate with other VFs onthis host even if the PF link state is down, disable causes the HW to drop any packetssent by the VF.

VXLAN in OpenStack Neutron

肖宏辉 • 18-05-22 •2424 人围观

作者简介:肖宏辉,毕业于中科院研究生院,思科认证网络互连专家(CCIE),8年的工作经验,其中6年云计算开发经验,关注网络,OpenStack,SDN,NFV等技术,OpenStack和ONAP开源社区活跃开发者。本文所有观点仅代表作者个人观点,与作者现在或者之前所在的公司无关。

传统二层网络工作方式
传统二层网络通过交换机内的MAC地址表实现转发。如下图所示。

比如A要发送数据给E。因为A与左边的交换机直连, A先将以太网数据帧发给左边的交换机。左边的交换机收到数据帧之后,查找自身的MAC地址表,从自己的端口4发出,发到了右边的交换机。右边的交换机收到数据帧之后,也是通过查找自身的MAC地址表,从自己的端口2发出。因为端口2与E直连,所以以太网数据帧发到了E。
可以看出MAC地址表是交换机完成转发的核心。这里的MAC地址表是一种控制层信息,因为它决定了网络数据帧的转发。传统网络里面,MAC地址表是通过数据层学习获得的。所有经过交换机的以太网数据帧,交换机会读取源MAC地址,并且记录这个数据帧来自哪个交换机端口。这样交换机就知道MAC地址和交换机端口的对应关系,下次要转发的时候可以根据MAC地址找到对应的交换机端口,进而完成转发。这些对应关系就是交换机的MAC地址表。
既然MAC地址表是学习生成的,那它就不能保证掌握了所有的MAC地址信息。可能还没学到,可能学到了又老化删除了。对于MAC地址表里没有的信息,交换机没办法按照常规的方式完成转发。这个时候,交换机会将数据帧发往所有相关的端口(Trunk口和同一个VLAN下的所有Access端口),这样总能发到目的主机。如果目的主机返回了数据,还是会通过交换机来做转发。交换机收到这个返回的数据帧,就能学习到缺失的信息。这个过程就是常说的flood-learn,发送到所有相关的端口就是flood,从返回的数据学习就是learn。通过flood-learn,交换机学习到缺失的信息,下一次转发同一个目的MAC地址的数据帧时,就不需要再flood。
可以看出,传统的二层网络,并不需要一个独立的“控制层”,只是通过数据层的学习,就能获取相应的转发信息(MAC地址表)。这样的设计,设备相对独立,真正做到插上线就走。

传统VXLAN网络的工作方式
所谓的传统的VXLAN网络,是指RFC7348[1]定义的VXLAN的工作方式。
VXLAN将以太网数据帧封装在UDP里面,进而在三层网络传输。VXLAN数据的封装和解封装发生在VTEP(VXLAN Tunnel EndPoint)。VXLAN可以看成是建立在VTEP之间的L2VPN。
VTEP与传统交换机类似,也是基于MAC地址表工作。VTEP的示意图如下,在VTEP里面,可以认为存在两个表:一个是VLAN和VXLAN的对应关系表;另一个是MAC地址表,里面包含了MAC 地址,VXLAN ID和远端VTEP IP地址的对应关系。VTEP向下连接多个主机,为了区分多个租户网络,不同的租户网络会用VLAN Tag做区分。VTEP收到下连主机的网络数据帧时,会先根据VLAN,查第一个表获得对应的VXLAN ID,之后根据VXLAN ID和目的MAC地址,查MAC地址表获取远端VTEP的IP地址。最后,VTEP会剥离VLAN Tag,按照VXLAN格式封装数据帧,发往远端的VTEP。

VTEP里VLAN和VXLAN的对应关系表是固定的。MAC地址表是VTEP的核心。这里的MAC地址表也是一种控制层信息,因为它决定了网络数据帧的转发。与传统的二层网络类似,传统的VXLAN网络也是通过数据层的学习来更新MAC地址表。VTEP收到的所有的VXLAN数据,VTEP会记录内层报文的源MAC地址,VXLAN ID和远端VTEP的IP地址,进而更新自己的MAC地址表。在这里,与传统的二层网络相比,VLAN ID变成了VXLAN ID,交换机端口变成了远端VTEP IP地址,不过最核心的东西没有变。
既然与传统二层网络类似,那也同样面临MAC地址表信息不全的问题。VTEP如果不能在自己的MAC地址表里面找到对应目的MAC地址的记录,也会有个flood-learn的过程。VTEP会将数据帧封装成VXLAN数据,发送给所有相关的远端VTEP,这样总能发到目的主机。一旦远端VTEP返回数据,那当前VTEP就能学习到缺失的MAC地址信息。
这里有个新的问题,传统二层网络里面,可以根据VLAN来识别flood的范围。可是在VXLAN网络里面,根据什么来识别flood的范围?VXLAN ID是不行的,这个只在VTEP能识别,出了VTEP就没人认识了。传统的VXLAN网络借助了IP组播来识别flood的范围。对于每一个VXLAN ID对应的网络,所有关联的VTEP都预先配置在一个组播组里面。VTEP需要flood的时候,只需要将VXLAN数据的外层IP地址设置成组播地址,这样,所有关联的VTEP都能收到flood。具体的过程如下图所示,224.0.0.1就是一个组播地址。

可以看出,传统的VXLAN网络与传统的二层网络还是很像的。最主要的是,传统的VXLAN网络,也不需要一个特定的“控制层”,只通过数据层的学习,就能获取相应的转发信息(MAC地址表)。这样的设计,使得VXLAN不必依赖某个特定的控制层,兼容性更好,便于早期的协议推广。

OpenStack Neutron
OpenStack Neutron作为OpenStack的网络项目,负责在整个OpenStack环境里面编排和管理虚拟网络环境。OpenStack Neutron支持多种底层,其中应用最广的是基于OpenVSwitch的实现。基于OpenVSwitch的Neutron,支持VXLAN网络,但是在实现上与传统的VXLAN网络有点不一样。

基于软件和OpenFlow的VTEP
VTEP是VXLAN网络的核心,Neutron通过软件(OpenVSwitch)和OpenFlow实现VTEP。下图是OpenStack在计算节点上的虚拟交换机的连接示意图。br-int和br-tun都是OpenVSwitch虚拟交换机。

br-int负责连接虚拟机,并且给不同租户的网络数据打上不同的VLAN Tag,从而实现同一个宿主机上不同租户网络之间的隔离。带VLAN Tag的数据帧被送到br-tun。VTEP在br-tun上实现。前面说过VTEP里面有两张表,一张是VLAN和VXLAN的对应关系表,一张是MAC地址表。在OpenStack Neutron,这两张表通过br-tun上的OpenFlow流表实现。对于VLAN和VXLAN的对应关系,在入方向上是在br-tun的table4实现的。

ovs-ofctl dump-flows br-tun table=4
1

2

3

$ sudo ovs-ofctl dump-flows br-tun table=4

table=4, priority=1,tun_id=0x3 actions=mod_vlan_vid:4,resubmit(,9)

table=4, priority=1,tun_id=0x1c actions=mod_vlan_vid:5,resubmit(,9)

这里直接将VXLAN ID 3与本地VLAN 4对应,VXLAN ID 28(0x1C)与本地VLAN 5对应。

MAC地址表,默认情况下,仍然是通过数据层的学习,来进行更新。学习发生在table10。

sudo ovs-ofctl dump-flows br-tun table=10

1

2

$ sudo ovs-ofctl dump-flows br-tun table=10

table=10, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xbdfa8adb40ce98c5,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:OXM_OF_IN_PORT[]),output:"patch-int"

这条流表乍一看很复杂,但是如果理解了传统网络里面的自学习过程,理解它并不难。br-tun作为VTEP,收到VXLAN数据,解析之后,下一步肯定是要发给br-int,进而送到虚拟机。这条流表在数据帧发往br-int之前(output:”patch-int”),先在table 20里面学习生成一条OpenFlow流表(learn(table=20)。这条流表有固定的hard_time,priority,cookie,并且匹配的VLAN ID来自于当前被学习的数据帧(NXM_OF_VLAN_TCI[0..11]),匹配的目的MAC地址来自于当前被学习的数据帧的源MAC地址(NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[])。匹配完之后,对应的动作有:剥离VLAN(load:0->NXM_OF_VLAN_TCI[]);设置VXLAN ID为当前数据帧的VXLAN ID(load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[]);从当前被学习数据帧的入端口送出(output:OXM_OF_IN_PORT[])。
为什么数据帧里面既有VLAN ID,又有VXLAN ID?因为VTEP(br-tun)收到VXLAN数据解封装之后,VXLAN ID作为数据帧的元数据,与数据帧一起送到了OpenFlow流表处理。至于VLAN ID,是在table 4根据VXLAN ID对应过来的。

我们来看一下在table 20,通过学习生成的流表。

sudo ovs-ofctl dump-flows br-tun table=20

1

2

$ sudo ovs-ofctl dump-flows br-tun table=20

table=20, hard_timeout=300, priority=1,vlan_tci=0x0005/0x0fff,dl_dst=fa:16:3e:10:51:07 actions=load:0->NXM_OF_VLAN_TCI[],load:0x1c->NXM_NX_TUN_ID[],output:"vxlan-c0a81f27"

得到的结果与刚刚介绍的学习的过程一模一样,匹配VLAN ID,目的MAC地址,之后剥离VLAN,设置VXLAN ID,再从某一个端口送出。这个端口,就是之前学习的数据帧的入端口。这条流表有个参数是hard_timeout,这个是流表的老化时间。这个参数表明300秒以后,这条流表会被自动删除。

br-tun的不同端口上配置了不同的远端VTEP IP地址,从br-tun的某个端口送出,实际就是送到了这个端口对应的远端VTEP。例如下面的输出里面,vxlan-c0a81f27对应的远端VTEP的IP地址就是192.168.31.39。

sudo ovs-vsctl show

1

2

3

4

5

6

$ sudo ovs-vsctl show

… ...

Port "vxlan-c0a81f27"

        Interface "vxlan-c0a81f27"

            type: vxlan

            options: {df_default="true", in_key=flow, local_ip="192.168.31.110", out_key=flow, remote_ip="192.168.31.39"}

OpenStack Neutron用软件和OpenFlow实现了VTEP,进而实现VXLAN网络。VTEP的MAC地址表,VLAN/VXLAN对应关系表,都以OpenFlow的形式,存在于br-tun上。不过默认情况下,主要的控制层信息—MAC地址表,还是通过数据层的学习获得。

基于源拷贝的BUM
前面介绍的传统VXLAN网络,是通过underlay网络的IP组播实现flood-learn的。其实不只flood-learn,BUM(Broadcast,Unknown Unicast,Multicast)都是通过IP组播实现的。IP组播用起来不太方便,因为一是需要硬件支持,二是需要额外的配置。
Arista作为VXLAN协议的制定者之一,提出采用源拷贝的方式来实现VXLAN的BUM,从而摆脱对IP组播的依赖[2]。具体来说,在原先需要做BUM的场合,源VTEP将数据帧在源端复制多份,再一一发送给相应的所有远端VTEP。这样既达到了BUM的效果,又不需要特殊的underlay网络的支持。OpenStack Neutron也采用了这种方式实现BUM。
首先在br-tun的table 2,识别单播(Unicast),广播(Broadcast)和组播(Multicast)数据帧。

1

2

3

$ sudo ovs-ofctl dump-flows br-tun table=2

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

table=2, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)

因为广播的目的MAC地址是FF:FF:FF:FF:FF:FF(所有bit都是1),组播的目的MAC地址第一个字节的最低bit必然是1(IEEE 802.1D)[3],单播的目的MAC地址第一个字节的最低bit必然是0,所以单播被送到table 20,广播和组播被送到送到table22。先来看table20,刚刚说过,这是VTEP的MAC地址表所在的table。VTEP在做转发的时候,如果在MAC地址表里找不到目的MAC地址对应的信息,那么这个单播包对于VTEP来说就是未知单播(Unknown Unicast),接下来就要泛洪(flood)。在table20,有一条优先级最低的OpenFlow流表,这条流表什么也不做,只是将数据转到table22。因为优先级最低,如果走到这条流表,那说明table 20里面没有其他的流表能匹配目的MAC地址,当前的数据帧是一个未知单播。

1

2

$ sudo ovs-ofctl dump-flows br-tun table=20

table=20, priority=0 actions=resubmit(,22)

就这样,虽然路径不太一样,但是BUM数据帧,最后都走到了table 22。在table22,会做源拷贝,将BUM送到所有相关的远端VTEP。

1

2

3

$ sudo ovs-ofctl dump-flows br-tun table=22

table=22, priority=1,dl_vlan=4 actions=strip_vlan,load:0x3->NXM_NX_TUN_ID[],output:"vxlan-c0a81f27",output:"vxlan-cae3027f"

table=22, priority=1,dl_vlan=5 actions=strip_vlan,load:0x1c->NXM_NX_TUN_ID[],output:"vxlan-c0a81f27",output:”vxlan-e98a9e17"

所以,OpenStack Neutron区别于传统的VXLAN网络,没有基于IP组播来实现VXLAN的BUM,而是基于源拷贝的方式来实现BUM。但是这里还有一个问题,table 22里的OpenFlow流表怎么知道相关的远端VTEP是哪些?这里就需要Neutron去计算、更新并下发相应的VTEP信息。直观的说,当有新的VTEP加入或者离开VXLAN网络,需要Neutron去更新table22里的流表。这里的OpenFlow流表也是控制层信息,因为它决定了数据帧的转发。但是这里使用了一个独立于数据层的组件,也就是OpenStack Neutron来更新控制信息。这里已经有点控制层,数据层分离的意思了。

L2 Population

传统的网络下,都是通过数据层学习,来更新作为“控制层信息”的MAC地址表。这种方式本身没有问题,但是如果网络规模变大,相应的flood也会变多。如果把网络通信比喻成两个人说话,那flood就是大喊着说话,让所有人都听见。试想一下,如果flood变多,那整个网络环境就会像是菜市场一样,正常的谈话必然会受到影响。针对这个问题,OpenStack Neutron提供了L2 Population。当打开L2 Population的时候,OpenStack Neutron会根据自己掌握的信息,将所有可能的MAC地址,预先下发到VTEP(br-tun)的MAC地址表(OpenFlow table 20)里面。这样,VTEP在转发数据帧的时候,总是能查到MAC地址对应的信息,也就不用泛洪(flood)了。
打开L2 Population之后,br-tun上的table 20就会新增下面的OpenFlow流表。这个流表与前面学习到的流表,虽然看起来略有不同,但是实际效果是一模一样的。匹配VLAN ID,目的MAC地址,之后剥离VLAN,设置VXLAN ID,再从某一个端口送出。

1

2

$ sudo ovs-ofctl dump-flows br-tun table=20

table=20, priority=2,dl_vlan=5,dl_dst=fa:16:3e:10:51:07 actions=strip_vlan,load:0x1c->NXM_NX_TUN_ID[],output:"vxlan-c0a81f27"

唯一的区别是,这条流表没有老化时间,会一直存在。如果对应的MAC地址失效了,例如虚机被删除了,需要Neutron会去删除这条流表。现在,作为控制层信息的MAC地址表不再依赖数据层,完全由OpenStack Neutron来控制。

L2 Population可以抑制未知单播的泛洪。与L2 Population相关的一个功能是ARP responder。后者只有在前者打开的前提下才能工作。ARP responder有时也叫做ARP proxy,它在本地代答ARP查询,从而抑制ARP广播。因为与VXLAN关系不大,就不多说了。

打开L2 Population的OpenStack Neutron与传统的VXLAN网络已经很不一样了。打开L2 Population之后,数据层只需要传递数据。控制层信息由OpenStack Neutron下发。但是这种方式就一定是完美的吗?传统方式下,通过数据层学习来更新MAC地址表,每个VTEP只需要维护自己需要通信的MAC地址信息,甚至一段时间不用的MAC地址信息,会自动老化删除。这样,每个VTEP,只需要维护一个相对较小的MAC地址表。MAC地址表都是存在内存里面,所以,这种情况下,对内存的消耗较小。而采用L2 Population,每个VTEP会被预先下发所有可能的目的MAC地址信息,虽然免去了flood-learn,但是VTEP的MAC地址表一直是最大的状态,相应的对内存的消耗也要更大。这两种方式各有利弊,没有说哪个一定优于另一个,正因为如此,OpenStack Neutron才保留了两种方式,将选择权交给用户。

[1] https://tools.ietf.org/html/rfc7348#page-8
[2] https://www.arista.com/en/company/news/press-release/21-company/press-release/1016-pr-20141022
[3] http://standards.ieee.org/develop/regauth/tut/macgrp.pdf

Neutron架构

Neutron项目共由约1千多个文件构成(k版)。

tree -l 1 neutron/

313 directories, 1224 files
一切,让我们先从Neutron架构图说起走吧,如下所示:

分析 1)位于最上层的Neutron Server充当一个门派中的“掌门人”角色(RESTful Server),负责接受来自外部门派(项目)的API请求,比如Nova API创建网络的请求。 2)位于中间层的Neutron plugin充当一个门派中的“信使”角色,负责传达最高层指令给下面的人。 3)位于下层的Neutron Agent充当一个门派中“干活”角色,负责执行一些具体的任务和操作。

类似于各个计算、存储节点被虚拟化为计算、存储资源池,Openstack所在的整个物理网络在Neutron中也被虚拟化为网络资源池。通过对网络资源的划分和可扩展性,Neutron能够为每个租户提供独立的虚拟网络环境。

Neutron分别提供了二层(L2)vSwitch交换和三层(L3)Router路由抽象的功能,对应于物理网络环境中的交换机和路由器实现。具体实现了如下功能:

Router:为租户提供路由、NAT等服务。

Network:对应于一个真实物理网络中的二层局域网(VLAN),从租户的的角度而言,是租户私有的。

Subnet:为网络中的三层概念,指定一段IPV4或IPV6地址并描述其相关的配置信息。它附加在一个二层Network上,指明属于这个network的虚拟机可使用的IP地址范围。

vpc
VTEP负责原始以太报文的VXLAN封装和解封装,可以是虚拟交换机,也可以是物理交换机. 在openstack中, 通常是ovs提供该功能。

VXLAN GW除了具备VTEP的功能外,还负责VLAN报文与VXLAN报文之间的映射和转发,主要以物理交换机为主。

VXLAN IP GW具有VXLAN GW的所有功能,此外,还负责处理不同VXLAN之间的报文通信,同时也是数据中心内部服务向往发布业务的出口,主要以高性能物理交换机为主。

  1. VM1以广播的形式发送ARP请求;

  2. VTEP1封装报文。打上VXLAN标识为100,外层IP头DA为IP多播组(239.119.1.1),SA为IP_VTEP1.

  3. VTEP1在多播组内进行多播;

  4. VTEP2解析接收到多播报文。填写流表(VNI, 内层mac地址,外层Ip地址),并在本地VXLAN标识为100的范围内

广播(是VXLAN的用武之地)。

  1. VM2对接收到的ARP请求进行响应;

(3) ARP应答

  1. VM2准备ARP响应报文后向VM1发送响应报文

2)VTEP2接收到VM2的响应报文后把它封装在ip单播报文中(VXLAN标识依然为100),然 后向VM1发送单播

3)VTEP1接收到单播报文后,学习内层MAC到外层ip地址的映射,解封装并根据被封装内容的目的MAC地址转发给VM1

4)VM1接收到ARP应答报文,ARP交互结束

四:数据传输

(1) ARP请求应答之后,VM1知道了VM2的Mac地址,并且要向VM2通信(注意,VM1是以TCP的方法向VM2发送数据的)。

VTEP1 收到VM1发送数据包,用MAC地址从流表中检查VM1与VM2是否属于用一个VNI。两个VM不但位于同一个VNI中(不在同一个VNI中出网关),并且VTEP1已经知道了VM2的所有地址信息(MAC和VTEP2_IP)。VTEP1封装新的数据包。然后交给上联交换机。

(2) 上联交换机收到服务器发来的UDP包,对比目的IP地址和自己的路由表,然后将数据报转发给相应的端口。

(3) 目的VTEP收到数据包后检查器VNI,如果UDP报中VNI与VM2的VNI一致,则将数据包解封装后交给VM2进一步处理。至此一个数据包传输完成。整个Vxlan相关的行为(可能穿越多个网关)对虚拟机来说是透明的,虚拟机不会感受传输的过程。虽然VM1与VM2之间启动了TCP来传输数据,但数据包一路上实际是以UDP的形式被转发,两端的VTEP并不会检查数据是否正确或者顺序是否完整,所有的这些工作都是在VM1和VM2在接收到解封装的TCP包后完成的。也就是说如果说如果被UDP封装的是TCP连接,那么UDP和TCP将做为两个独立的协议栈各自工作,相互之间没有交互。

如果需要VXLAN网络和非VXLAN网络连接,必须使用VXLAN网关才能把VXLAN网络和外部网络进行桥接和完成VXLAN ID和VLAN ID之间的映射和路由,和VLAN一样,VXLAN网络之间的通信也需要三层设备的支持,即VXLAN路由的支持。同样VXLAN网关可由硬件和软件来实现。从封装的结构上来看,VXLAN提供了将二层网络overlay在三层网络上的能力,VXLAN Header中的VNI有24个bit,数量远远大于4096,并且UDP的封装可以穿越三层网络,比VLAN有更好的扩展性。

三台虚拟机,它们之间通信的 IP 地址(也就是 underlay 网络)为:

192.168.8.100
192.168.8.101
192.168.8.102
而要创建的 overlay 网络网段为 10.20.1.0/24,实验目的就是 vxlan 能够通过 overlay IP 互相连通。

字段的意思?????????????????????????

Dump flows
Dumps OpenFlow flows 不含 hidden flows (常用)
ovs-ofctl dump-flows br-tun
Dumps OpenFlow flows 包含 hidden flows
ovs-appctl bridge/dump-flows ovs-br
Dump 特定 bridge 的 datapath flows 不論任何 type
ovs-appctl dpif/dump-flows br-int

recirc_id(0),in_port(10),eth(src=fa:16:3e:a7:51:fd,dst=fa:16:3e:94:5e:20),eth_type(0x0800),ipv4(frag=no), packets:242, bytes:23716, used:0.822s, actions:9

recirc_id(0),in_port(9),eth(src=fa:16:3e:94:5e:20,dst=fa:16:3e:a7:51:fd),eth_type(0x0800),ipv4(frag=no), packets:242, bytes:23716, used:0.822s, actions:10

recirc_id(0),in_port(10),eth(src=fa:16:3e:a7:51:fd,dst=fa:16:3e:aa:84:41),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:150, bytes:20480, used:6.516s, flags:SFP., actions:set(tunnel(tun_id=0x64,src=172.30.196.9,dst=172.30.196.2,ttl=64,tp_dst=4789,flags(df|key))),7

Dump 在 Linux kernel 里datapath flow table (常用)
ovs-dpctl dump-flows [dp]

Top like behavior for ovs-dpctl dump-flows
ovs-dpctl-top

查詢 log level list ovs-appctl vlog/list 設定 log level (以 stp 設定 file 為 dbg level 為例) ovs-appctl vlog/set stp:file:dbg ovs-appctl vlog/set {module name}:{console, syslog, file}:{off, emer, err, warn, info, dbg}

關於 sFlow
查詢
ovs-vsctl list sflow
關於 NetFlow
查詢
ovs-vsctl list netflow
關於 VXLAN
參考 rascov - Bridge Remote Mininets using VXLAN

建立 VXLAN Network ID (VNI) 和指定的 OpenFlow port number, eg: VNI=5566, OF_PORT=9
ovs-vsctl set interface vxlan type=vxlan option:remote_ip=x.x.x.x option:key=5566 ofport_request=9

VNI flow by flow
ovs-vsctl set interface vxlan type=vxlan option:remote_ip=140.113.215.200 option:key=flow ofport_request=9

設定 VXLAN tunnel id
ovs-ofctl add-flow ovs-br in_port=1,actions=set_field:5566->tun_id,output:2
ovs-ofctl add-flow s1 in_port=2,tun_id=5566,actions=output:1
OpenFlow Trace
ovs-appctl ofproto/trace ovs-br in_port=1,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 -generate
ovs-ofctl dump-flows br-tun

查表过程
Open vSwitch 概述
在 OVS 中, 有几个非常重要的概念:
Bridge: Bridge 代表一个以太网交换机(Switch),一个主机中可以创建一个或者多个 Bridge 设备。

Port: 端口与物理交换机的端口概念类似,每个 Port 都隶属于一个 Bridge。

Interface: 连接到 Port 的网络接口设备。在通常情况下,Port 和 Interface 是一对一的关系, 只有在配置 Port 为 bond 模式后,Port 和 Interface 是一对多的关系。

Controller: OpenFlow 控制器。OVS 可以同时接受一个或者多个 OpenFlow 控制器的管理。

datapath: 在 OVS 中,datapath 负责执行数据交换,也就是把从接收端口收到的数据包在流表中进行匹配,并执行匹配到的动作。

Flow table: 每个 datapath 都和一个“flow table”关联,当 datapath 接收到数据之后, OVS 会在 flow table 中查找可以匹配的 flow,执行对应的操作, 例如转发数据到另外的端口。

ovs-ofctl
则是 OVS 提供的命令行工具。在没有配置 OpenFlow 控制器的模式下,用户可以使用 ovs-ofctl 命令通过 OpenFlow 协议去连接 OVS,创建、修改或删除 OVS 中的流表项,并对 OVS 的运行状况进行动态监控。

Flow 语法说明
在 OpenFlow 的白皮书中,Flow 被定义为某个特定的网络流量。例如,一个 TCP 连接就是一个 Flow,或者从某个 IP 地址发出来的数据包,都可以被认为是一个 Flow。支持 OpenFlow 协议的交换机应该包括一个或者多个流表,流表中的条目包含:数据包头的信息、匹配成功后要执行的指令和统计信息。

当数据包进入 OVS 后,会将数据包和流表中的流表项进行匹配,如果发现了匹配的流表项,则执行该流表项中的指令集。相反,如果数据包在流表中没有发现任何匹配,OVS 会通过控制通道把数据包发到 OpenFlow 控制器中。

在 OVS 中,流表项作为 ovs-ofctl 的参数,采用如下的格式:字段=值。如果有多个字段,可以用逗号或者空格分开。一些常用的字段列举如下:

表 1. 流表常用字段
表 1. 流表常用字段
字段名称

说明

in_port=port

传递数据包的端口的 OpenFlow 端口编号

dl_vlan=vlan

数据包的 VLAN Tag 值,范围是 0-4095,0xffff 代表不包含 VLAN Tag 的数据包

dl_src=
dl_dst=

匹配源或者目标的 MAC 地址
01:00:00:00:00:00/01:00:00:00:00:00 代表广播地址
00:00:00:00:00:00/01:00:00:00:00:00 代表单播地址

dl_type=ethertype

匹配以太网协议类型,其中:
dl_type=0x0800 代表 IPv4 协议
dl_type=0x086dd 代表 IPv6 协议
dl_type=0x0806 代表 ARP 协议
完整的的类型列表可以参见以太网协议类型列表

nw_src=ip[/netmask]
nw_dst=ip[/netmask]

当 dl_typ=0x0800 时,匹配源或者目标的 IPv4 地址,可以使 IP 地址或者域名

nw_proto=proto

和 dl_type 字段协同使用。
当 dl_type=0x0800 时,匹配 IP 协议编号
当 dl_type=0x086dd 代表 IPv6 协议编号
完整的 IP 协议编号可以参见IP 协议编号列表

table=number

指定要使用的流表的编号,范围是 0-254。在不指定的情况下,默认值为 0。通过使用流表编号,可以创建或者修改多个 Table 中的 Flow

reg=value[/mask]

交换机中的寄存器的值。当一个数据包进入交换机时,所有的寄存器都被清零,用户可以通过 Action 的指令修改寄存器中的值

对于 add−flow,add−flows 和 mod−flows 这三个命令,还需要指定要执行的动作:actions=[target][,target...]

一个流规则中可能有多个动作,按照指定的先后顺序执行。

常见的操作有:
output:port: 输出数据包到指定的端口。port 是指端口的 OpenFlow 端口编号

mod_vlan_vid: 修改数据包中的 VLAN tag

strip_vlan: 移除数据包中的 VLAN tag

mod_dl_src/ mod_dl_dest: 修改源或者目标的 MAC 地址信息

mod_nw_src/mod_nw_dst: 修改源或者目标的 IPv4 地址信息

resubmit:port: 替换流表的 in_port 字段,并重新进行匹配

load:value−>dst[start..end]: 写数据到指定的字段

使用 ovs-ofctl 创建并测试 OpenFlow 命令
查看 Open vSwitch 中的端口信息。从输出结果中,可以获得交换机对应的 datapath ID (dpid),以及每个端口的 OpenFlow 端口编号,端口名称,当前状态等等

ovs-ofctl show br-tun

如果想获得网络接口的 OpenFlow 编号,也可以在 OVS 的数据库中查询
ovs-vsctl get Interface vxlan-ac1e2402 ofport

查看 datapath 的信息
ovs-dpctl show

现在由于端口 p0 和 p1 属于不同的 VLAN,它们之间无法进行数据交换。我们使用 ovs-appctl ofproto/trace 生成一个从端口 p0 发送到端口 p1 的数据包,这个数据包不包含任何 VLAN tag,并观察 OVS 的处理过程

ovs-appctl ofproto/trace ovs-switch in_port=100,dl_src=66:4e:cc:ae:4d:20,

dl_dst=46:54:8a:95:dd:f8 -generate

其他 OpenFlow 常用的操作
查看交换机中的所有 Table
ovs-ofctl dump-tables br-tun

查看交换机中的所有流表项
ovs-ofctl dump-flows br-tun

Dump br0上匹配xx的flow:
ovs-ofctl dump-flows br-tun xx

ovs-ofctl dump-flows br-tun table=22

查看交换机上的端口信息
ovs-ofctl show br-tun

流表的语法和格式
ovs-ofctl的Flow语法由一系列field=value形式的键值对组成,用英文逗号和空格隔开。

在描述Flow时,必须要遵循 TCP/IP 协议栈。比如说,在flow中使用了L3(网络层)的字段时,必须也要指明L2(数据链路层)所用的协议。使用了L4(传输层)的字段时,必须也要指明L2和L3所用的协议。

in_port

匹配从OpenFlow port id 进入的数据包。

in_port=

dl_vlan

匹配IEEE 802.1Q VLAN tag为 的数据包。 的值应该在[0-4095]这个区间。

为0xffff表示匹配没有VLAN tag的包:

dl_vlan=

dl_vlan_pcp

匹配IEEE 802.1Q Priority Code Point(PCP, 优先级代码点)为的数据包,改值取值区间为[0-7]。数字越大,表示优先级越高。可以用于QoS。

dl_src 和 dl_dst

例如:dl_dst=01:00:00:00:00:00/01:00:00:00:00:00

匹配源和目的MAC地址。其中”/”后面的为掩码。

比如说,如果掩码是:

  • 01:00:00:00:00:00 只匹配组播MAC (第一个字节LSB为1的MAC地址为MAC组播地址)

  • fe:ff:ff:ff:ff:ff 匹配其他所有MAC,除了组播MAC

  • ff:ff:ff:ff:ff:ff 完全匹配掩码前的MAC,和省掉掩码的效果一样

  • 00:00:00:00:00:00 完全通配,相当于(dl_dst=*)

dl_type

匹配L2(Data Link ,数据链路层) header中的协议类型,该字段描述L3的类型。有效值区间[0, 65535],可以是十进制数或者是以”0x”开头的十六进制数。比如:

dl_type=0x0800 匹配IP数据包

dl_type=0x0806 匹配ARP数据包

dl_type=0x8035 匹配RARP数据包

需要说明的是 dltype=0x0800 可以用关键字 ip 代替。 而 dltype=0x0806 可以用关键字 arp 代替。dl_type=0x8035 可以用关键字 rarp 代替。

nw_src 和 nw_dst

当使用了 dl_type=0x0800 或者关键字 ip 或 tcp 时,nw_src 和 nw_dst分配匹配IP头中的源IP地址和目的IP地址。其中netmask可以是255.255.255.0这样的(dotted quad)形式,也可以是数字 /24 这样的(CIDR)形式

当使用了 dl_type=0x0806 或者关键字 arp 时,nw_src 匹配 ARP 头中的 arspa(Sender Protocol Address)字段。nw_dst 匹配ARP头中的 artpa(Target Protocol Address)字段

当使用了 dl_type=0x8035 或者关键字 rarp 时,nw_src匹配 RARP 头中的 arspa(Sender Protocol Address)字段。nw_dst匹配RARP头中的 artpa(Target Protocol Address)字段

当 dl_type 使用了通配符或者除了0x0800, 0x0806, 0x8035以外的值,则nw_src, nw_dst的值会被忽略

tp_src 和tp_dst

传输层协议如TCP/UDP协议的源端口号、目的端口号

nwproto 和 ipproto

要和 dl_type 字段配合使用。

当dl_type=0x0800或使用了关键字ip时,匹配IP头中的proto字段,取值区间[0, 255],比如为1时可以匹配ICMP数据包,为6时匹配TCP数据包。

当dl_type=0x86dd或使用了关键字ipv6是,匹配IPv6头中的proto字段,取值区间[0, 255],比如为58时匹配ICMPv6数据包,为6时匹配TCP数据包

当dl_type=0x0806或者使用了关键字arp时,匹配ARP opcode的低8位,ARP opcode大于255时,与等于0效果一样

当dl_type=0x8035或者使用了关键字rarp时,匹配ARP opcode的低8位, ARP opcode大于255时,与等于0效果一样

当dl_type使用了通配符或这除了0x0800, 0x0806, 0x8035以外的值,则nw_proto的值会被忽略

nw_tos

匹配IP ToS字段或IPv6的traffic class字段取值为的数据包。取值区间[0, 255],需要注意的是最低2位会被忽略。当dl_type使用除0x800(IP)和0x86dd(IPv6)以外的数值时,该字段被忽略。

nw_ttl

匹配IP和IPv6的TTL字段为的数据包。取值区间[0, 255]。当dl_type使用除0x800(IP)和0x86dd(IPv6)以为的数值时,该字段被忽略。

icmp_type 和 icmp_code

icmp_type=

icmp_code=

当 dl_type 和 nw_proto 确定数据包为 ICMP 或 ICMPv6 时,匹配 ICMP type 和 code。取值区间都为[0, 255]。如果 dl_type 和 mw_proto 使用了其他值时,该字段忽略。

协议关键字

协议关键字相当于alias,对应关系如下:

ip = dl_type=0x0800

ipv6 = dl_type=0x86dd

icmp = dl_type=0x0800,nw_proto=1

icmp6 = dl_type=0x86dd,nw_proto=58

tcp = dl_type=0x0800,nw_proto=6

tcp6 = dl_type=0x86dd,nw_proto=6

udp = dl_type=0x0800,nw_proto=17

udp6 = dl_type=0x86dd,nw_proto=17

sctp = dl_type=0x0800,nw_proto=132

sctp6 = dl_type=0x86dd,nw_proto=132

arp = dl_type=0x0806

rarp = dl_type=0x8035

mpls = dl_type=0x8847

mplsm = dl_type=0x8848

cookie

cookie=

或cookie=<value/mask>

cookie 字段可以用于以下命令 :add-flows和 mod-flows

actions

ovs-ofctl的 add-flow 以及 mod-flows 命令都需要 actions 字段,描述对匹配的数据包执行的动作

在上述的三条命令中,actions字段是flow的一部分,actions字段中可以有多个 action,它们之间用逗号隔开,一个flow的完整语法如下:

=,[=]...,actions=[,...]

可使用的action非常多。如下:

output

output:

将数据包输出到OpenFlow port

group

group:<group_id>

将数据包输出到OpenFlow group

normal

按照设备的常规L2/L3处理流程来处理数据包。这通常是OVS默认flow中的action。要注意的是,并不是所有的OpenFlow switch都支持这个action。

flood

将数据包输出到所有物理端口,除了该数据包的输入口以及不可被flooding的端口。

all

将数据包输出到所有物理端口,除了该数据包的输入口。

local

将数据包输出到local port(与bridge同名的端口)

in_port

in_port

将数据包输出到其输入口

controller

controller(=…)

将数据包以”packet in”消息的形式发送到OpenFlow控制器。其中=键值对可以是:

max_len= 只将数据包的个字节发送到控制器。默认发送这个数据包。

reson= 指明”pakcet in”消息中的reason字段。默认reason为action,还可以是no_match, invalid_ttl。

id= 指明控制器id,16位整数。表示要发送给那个控制器。默认使用的id是0.

controller

controller[:nbytes]

分别是controller()和controller(max_len=)的简略写法。

enqueue

enqueue(,)

将数据包放到端口的队列中。其中必须是OpenFlow端口号或关键字(如”LOCAL”)。不同交换机支持的队列数不同,有些OpenFlow实现根本不支持队列。

drop

drop

丢掉该数据包。

vlan

mod_vlan_vid:<vlan_vid>

修改数据包的VLAN id为。如果数据包没有VLAN tag则添加VLAN id为的VLAN tag。如果数据包VLAN id已经为,则将其VLAN 优先级priority设为0.

mod_vlan_pcp:<vlan_pcp>

修改数据包的VLAN 优先级priority为。如果数据包没有VLAN tag则添加VLAN priority为的VLAN tag。合法值区间为[0, 7],数字越大优先级越高。如果数据包VLAN priority已经为,则将其VLAN id设为0.

strip_vlan

如果数据包有VLAN tag,则剥去VLAN tag。

datalink header mod

mod_dl_src:

mod_dl_dst:

设置数据包的源或目的MAC地址

network header mod

mod_nw_src:

mod_nw_dst:

设置数据包的源或目的IP地址

mod_nw_tos:

设置IPv4头ToS/DSCP或IPv6头traffic class field中DSCP比特位设置成,数值必须是4的倍数,且在[0, 255]区间。这个action并不会修改ToS中的低2位(2 LSB)。

mod_nw_ecn:

设置IPv4头ToS或IPv6头traffic class field中ECN比特位为,数值区间为[0, 3]。这个action并不会修改高6位(6 MSB)。

需要OpenFLow 1.1以上。

mod_nw_ttl:

修改IPv4 TTL或IPv6 hop limit为,取值区间为[0, 255]。

需要OpenFlow 1.1以上。

这里写代码片transport header mod

mod_tp_src:

mod_tp_dst:

将数据包的TCP/UDP/SCTP源或目的端口设置成

trace 一条flow
ovs-appctl ofproto/trace br0 tcp,tcp_dst=22

OVS架构解析--dpdk datapath数据通路
整体架构
OVS(openvswitch)是开源的虚拟交换机。也是当前市场上云环境中部署份额最大的交换机。支持 openflow协议,ovsdb协议管理。

一个OVS实例包括,ovsdb-server、ovs-vswitchd、datapath快转模块(linux内核中实现,可选的。dpdk模式是在用户态实现快转,并不需要内核态的datapath模块)。

ovsdb-server:作用是对ovsdb操作。

ovs-vswitchd:核心模块,作用是实现OpenFlow交换机、和controller通信、和db通信、实现用户态转发、和内核态快转路径通信。

datapath:在内核空间实现报文快速转发。

ovsdb配置管理方式:管理者通过OVSDB管理协议管理OVS交换机。OVSDB管理协议的规范文件是RFC 7047(The Open vSwitch Database Management Protocol)。RFC7047主要包括,定义OVSDB的结构、交互协议(JSON-RPC)、DB的操作类型。

openflow流表控制方式:controller控制器通过OpenFlow协议给OVS交换机下发流表,控制交换机的转发行为。controller不通过OVSDB,而是直接向OVS交换机下发流表。

ovsdb-server和ovs-vswitchd之间是通过socket交互信息。

ovs-vswitchd通过netlink和内核态快转模块通信。

比如通过ovs-vsctl命令增加ovs交换机接口,ovs-vsctl会通过ovsdb-server向ovsdb更新数据,ovs-vswitchd监测到ovsdb变化时,会更新交换机配置,比如添加接口。

dpdk方式的ovs除了不使用内核模块外,架构和图中相同。

内部模块及其关联关系
vswitchd和ovsdb通信
IDL:接口描述语言。IDL是用来描述软件组件接口的一种计算机语言。IDL通过一种中立的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信交流。也就是说,用一种通用的格式,比如下面数据就是描述一个Open_vSwitch表,最后生成C语言描述的表操作.c文件

使用ovsschema格式定义数据库表,IDL解析器会把该文件内容解析成对数据库初始化、操作等函数,生成ovsdb-idl.c文件。

建立连接

ovsdb_idl_create建立一个ovsdb的连接session,形如:idl =ovsdb_idl_create(remote, &ovsrec_idl_class, true, true)。

remote类似:unix:/var/run/openvswitch/db.sock。

后续使用该idl(struct ovsdb_idl)和ovsdb通信,更新数据。

dpdk加速处理

3.1. ovs dpdk加速配置

在使用dpdk 类型的datapath加速之前,需要设置dpdk-init=true启动参数。

设置方法:

ovs-vsctl --no-wait set Open_vSwitch .other_config:dpdk-init=true

设置dpdk的相关参数,都通过other_config选项完成。

主要的参数有:

dpdk-init

指定ovs是否初始化和支持dpdk端口。

dpdk-lcore-mask

指明dpdk使用的逻辑核。同dpdk的-c参数。

dpdk-socket-mem

指明不同numa节点提前申请的大页内存。同dpdk的--socket-mem参数。

dpdk-hugepage-dir

大页文件系统mount的目录。同dpdk的--huge-dir参数。

vhost-sock-dir

设置vhost-user 套接字的路径。

dpdk-extra

其他的dpdk配置参数。

3.2. dpdk初始化

主要包括:

  1. dpdk eal初始化。

  2. 启动dpdk接口状态监控线程(使用dpdk的库函数),如果状态发送变化,则更新netdev设备的变化标记。

  3. 注册dpdk类型的netdev class。其中包括dpdk设备类型,ring类型、vhost、vhost client类型。
    

与代码学习相关的文章
https://blog.csdn.net/chen98765432101/article/details/79835435

ovs-ofctl
OVS - ofctl [选项]命令[交换机名] [参数... ]
Dump-ports switch转储表交换机

在控制台输出交换机使用流表的统计量。

dump−ports switch [netdev] 转储端口切换[ NETDEV ]

在控制台输出与交换机相关联的网络设备控制台的统计数据。如果指定NETDEV,只有与该设备相关的统计信息将被打印出来。 NETDEV可以是OpenFlow的分配的端口号或设备名,如:为eth0 。

dump−ports−desc switch 转储端口 - 递减交换机

ovs-ofctl get-frags br-tun

输出交换机的片段处理模式。参考 set-frags,下面是对于每个片段处理模式的说明。
show命令也打印片段处理方式及其他输出中。

Set-frags switch frag_mode

配置交换机的IPv4和IPv6。对于frag_mode的选择是:

normal

片段通过像non-fragmented packets的流表。在TCP端口,UDP端口和ICMP类型和代码字段始终设置为0 ,即使是信息可用的分片(与碎片偏移量为0 ) 。这是OpenFlow的交换机默认的片段处理模式。

drop

片段将被丢弃而不通过流表。

Reassemble

交换机在通过流量表传递它们之前将分片重新组合成完整的IP数据包。Open vSwitch没有实现这个片段的处理模式。

NX-match

片段像非碎片包一样通过流表。在TCP端口,UDP端口和ICMP类型和代码字段可用于匹配与碎片偏移量为0 ,并与非零偏移片段设置为0 。这个模式是一个Nicira延伸。

见ip_frag的说明,下面,换一种方式来匹配一个包是否是一个片段还是片段的偏移。

默认情况下, OVS - ofctl输出的与交换机发送它们的条目顺序相同,这可能不太直观或一致。参考options中, −−sort and −−rsort影响显示顺序。

ovs-ofctl dump-aggregate br-tun [flows] 自卸总交换机[流量]

在控制台输出与交换机流的表流匹配的流的汇总统计。如果流量被省略,统计交换机的流表中的所有流量。参考下面流语法,对于流语法。输出格式在表项输出中描述。

dump−aggregate switch [flows] 队列状态切换[口[队列] ]

在控制台输出交换机的指定端口队列的统计数据。如果流量被省略,统计数据交换机的流量表中的所有流量汇总。

queue−stats switch [port [queue]] 队列状态切换[口[队列]]

在控制台输出交换机的指定端口队列的统计数据。端口可以是OpenFlow的端口号或名称,关键字LOCAL(首选方法指的是OpenFlow的本地端口),或关键字ALL。任一端口或队列或两者可被省略(或等价的关键字ALL)。如果两者都省略了,统计数据打印的所有端口上的所有队列。如果省略只有队列,统计所有端口的队列;如果省略唯一的端口,统计每个端口的打印队列。

vxlan&ovs&neutron
ovs-ofctl dump-flows 的table id:

-Tunnel bridge (tun_br)

Various tables for tunneling flows

DVR_PROCESS = 1

PATCH_LV_TO_TUN = 2

GRE_TUN_TO_LV = 3

VXLAN_TUN_TO_LV = 4

GENEVE_TUN_TO_LV = 6

DVR_NOT_LEARN = 9

LEARN_FROM_TUN = 10

UCAST_TO_TUN = 20

ARP_RESPONDER = 21

FLOOD_TO_TUN = 22

-Physical Bridges (phys_brs)

Various tables for DVR use of physical bridge flows

DVR_PROCESS_VLAN = 1

LOCAL_VLAN_TRANSLATION = 2

DVR_NOT_LEARN_VLAN = 3

OVS 的数据流向都是由 Flow 规则控制的,分析 VxLAN 的 Flow 规则。
提个醒:这可能是本教程最烧脑的一节,let's rock it !

下面分析控制节点上的 flow rule,计算节点类似。
br-int 的 flow rule
br-int 的 rule 看上去虽然多,其实逻辑很简单,br-int 被当作一个二层交换机,其重要的 rule 是下面这条:

cookie=0xaaa0e760a7848ec3, duration=52798.625s, table=0, n_packets=143, n_bytes=14594, idle_age=9415, priority=0 actions=NORMAL

cookie=0x98438c21296c7719, duration=67226.215s, table=0, n_packets=0, n_bytes=0, priority=0 actions=NORMAL

cookie=0x98438c21296c7719, duration=67220.064s, table=0, n_packets=822410, n_bytes=55948105, priority=1 actions=NORMAL

此规则的含义是:根据 vlan 和 mac 进行转发。

br-tun 的 flow rule

这些才是真正处理 VXLAN 数据包的 rule,流程如下:

上图各方块中的数字对应 rule 中 table 的序号,比如编号为0的方块对应下面三条 rule。

table 0

cookie=0xaaa0e760a7848ec3, duration=76707.867s, table=0, n_packets=70, n_bytes=6600, idle_age=33324, hard_age=65534, priority=1,in_port=1 actions=resubmit(,2)
cookie=0xaaa0e760a7848ec3, duration=76543.287s, table=0, n_packets=56, n_bytes=4948, idle_age=33324, hard_age=65534, priority=1,in_port=2 actions=resubmit(,4)
cookie=0xaaa0e760a7848ec3, duration=76707.867s, table=0, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=drop

结合如下 port 编号:

table 0 flow rule 的含义为:

从 port 1(patch-int)进来的包,扔给 table 2 处理:actions=resubmit(,2)
从 port 2(vxlan-a642100b)进来的包,扔给 table 4 处理:actions=resubmit(,4)
即第一条 rule 处理来自内部 br-int(这上面挂载着所有的网络服务,包括路由、DHCP 等)的数据;第二条 rule 处理来自外部 VXLAN 隧道的数据。

table 4

cookie=0xaaa0e760a7848ec3, duration=76647.039s, table=4, n_packets=56, n_bytes=4948, idle_age=33324, hard_age=65534, priority=1,tun_id=0x64 actions=mod_vlan_vid:1,resubmit(,10)

table 4 flow rule 的含义为: 如果数据包的 VXLAN tunnel ID 为 100(tun_id=0x64),action 是添加内部 VLAN ID 1(tag=1),然后扔给 table 10 去学习。

table 10

cookie=0xaaa0e760a7848ec3, duration=76707.865s, table=10, n_packets=56, n_bytes=4948, idle_age=33324, hard_age=65534, priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xaaa0e760a7848ec3,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

table 10 flow rule 的含义为: 学习外部(从 tunnel)进来的包,往 table 20 中添加对返程包的正常转发规则,然后从 port 1(patch-int)扔给 br-int。

rule 中下面的内容为学习规则,这里就不详细讨论了。

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 2

cookie=0xaaa0e760a7848ec3, duration=76707.866s, table=2, n_packets=28, n_bytes=3180, idle_age=33324, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
cookie=0xaaa0e760a7848ec3, duration=76707.866s, table=2, n_packets=42, n_bytes=3420, idle_age=33379, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)

table 2 flow rule 的含义为:

br-int 发过来数据如果是单播包,扔给 table 20 处理:resubmit(,20)
br-int 发过来数据如果是多播或广播包,扔 table 22 处理:resubmit(,22)
table 20

cookie=0xaaa0e760a7848ec3, duration=76543.287s, table=20, n_packets=28, n_bytes=3180, idle_age=33324, hard_age=65534, priority=2,dl_vlan=1,dl_dst=fa:16:3e:fd:8a:ed actions=strip_vlan,set_tunnel:0x64,output:2
cookie=0xaaa0e760a7848ec3, duration=76707.865s, table=20, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0 actions=resubmit(,22)

table 20 flow rule 的含义为:

第一条规则就是 table 10 学习来的结果。内部 VLAN 号为 1(tag=1),目标 MAC 是 fa:16:3e:fd:8a:ed(virros-vm2)的数据包,即发送给 virros-vm2 的包,action 是去掉 VLAN 号,添加 VXLAN tunnel ID 100(十六进制 0x64),并从 port 2 (tunnel 端口 vxlan-a642100b) 发出。
对于没学习到规则的数据包,则扔给 table 22 处理。
table 22

cookie=0xaaa0e760a7848ec3, duration=76543.282s, table=22, n_packets=2, n_bytes=84, idle_age=33379, hard_age=65534, dl_vlan=1 actions=strip_vlan,set_tunnel:0x64,output:2
cookie=0xaaa0e760a7848ec3, duration=76707.82s, table=22, n_packets=40, n_bytes=3336, idle_age=65534, hard_age=65534, priority=0 actions=drop

table 22 flow rule 的含义为: 如果数据包的内部 VLAN 号为 1(tag=1),action 是去掉 VLAN 号,添加 VXLAN tunnel ID 100(十六进制 0x64),并从 port 2 (tunnel 端口 vxlan-a642100b) 发出。

VXLAN 的路由和 floating IP 支持
OVS流表分析
(一)L2基本连接的实现

复原一下通信场景,其中的网络基础请参考“OpenStack网络基础”一小节。图中某租户有两个网段,分别用橙红色和蓝色表示,网段间的互通要经过网络节点中的Router,网段内的通信不需要经过网络节点中的Router。网段间的互通可分为3步:橙红色网段通过网段内通信找到Router,Router进行路由,Router通过蓝色网段的网段内通信转发给目的地。Router上的路由是linux内核实现的,我们不去关心,因此可以说租户内部通信都是基于网段内通信实现的。

以Overlay组网模型对OVS的工作机制进行介绍,具体分为两个角度:OVS实现L2的基本连接,OVS对连接机制的优化。

Overlay模型中,网段内通信涉及ovs br-int和ovs br-tun,计算节点和网络节点中两类网桥的实现没有什么区别。概括地说,br-int负责在节点本地的网段内通信,br-tun则负责节点间的网段内通信。

在本节的场景内br-int实现为普通的二层交换机,即完成VLAN标签的增删和正常的二层自学习与转发,没有必要进行过多的解释,其代码实现请参考“Neutron的软件实现”中agent部分。

Br-tun采用多级流表实现节点间的网段内通信,下面直接通过图示来看br-tun中多级流表的设计。图中流表的序号不是固定的,可在neutron.plugins.openvswitch.agent.common目录下的constants.py文件中修改。

OVS中arp响应的流表的实现
总结:

1、br-int 流表总体是按照Normal 的方式,即常规的交换机的转发方式进行转发。而br-tun 交换机则主要按照流表的方式进行转发。

2、一般情况下,VM发出的ARP请求,会在该VM的所有邻居进行广播发送和查找,大量浪费带宽。当neutron开启了l2 pop后(二次注入功能),

计算节点会学习别的主机发送的免费ARP, 从而在本地存在ARP表项。

3、当本地的VM发出ARP请求时,br-tun交换机会优先查找本地的ARP表项,从而对报文进行ARP应答。

这样的话,就不用发出ARP请求的广播报文。如果br-tun交换机查找不到对应的ARP响应流表,则按照普通的隧道广播的方式进行正常广播发送。

4、ARP响应流表的匹配条件和动作分别如下:

ARP响应流表的匹配条件是:
ARP响应流表的动作如下:
br.add_flow(table=constants.ARP_RESPONDER,

        priority=1,

        proto='arp',

        dl_vlan=vlan,

        nw_dst='%s' % ip,

        actions=actions)

ARP_RESPONDER_ACTIONS = ('move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],'

                     'mod_dl_src:%(mac)s,'

                     '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:%(mac)#x->NXM_NX_ARP_SHA[],'

                     'load:%(ip)#x->NXM_OF_ARP_SPA[],'

                     'in_port')

5、对匹配条件和动作的解释如下:
self.tun_br.add_flow(table=constants.ARP_RESPONDER, – Add this new flow to the ARP_RESPONDER table

                             priority=1, – With a priority of 1 (Another, default flow with the lower priority of 0 is added elsewhere in the code)

                             proto=‘arp’, – Match only on ARP messages

                             dl_vlan=lvid, – Match only if the destination VLAN (The message has been locally VLAN tagged by now) matches the VLAN ID / network of the remote VM

                             nw_dst=‘%s‘ % ip, – Match on the IP address of the remote VM in question

                             actions=actions)

actions = (‘move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],’ – Place the source MAC address of the request (The requesting VM) as the new reply’s destination MAC address

                   ‘mod_dl_src:%(mac)s,’ – Put the requested MAC address of the remote VM as this message’s source MAC address

                   ‘load:0x2->NXM_OF_ARP_OP[],’ – Put an 0x2 code as the type of the ARP message. 0x2 is an ARP response.

                   ‘move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],’ – Place the ARP request’s source hardware address (MAC) as this new message’s ARP target / destination hardware address

                   ‘move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],’ – Place the ARP request’s source protocol / IP address as the new message’s ARP destination IP address

                   ‘load:%(mac)#x->NXM_NX_ARP_SHA[],’ – Place the requested VM’s MAC address as the source MAC address of the ARP reply

                   ‘load:%(ip)#x->NXM_OF_ARP_SPA[],’ – Place the requested VM’s IP address as the source IP address of the ARP reply

                   ‘in_port’ % {‘mac’: mac, ‘ip’: ip}) – Forward the message back to the port it came in on

6、参考blog: OVS ARP Responder – Theory and Practice
https://assafmuller.com/2014/05/21/ovs-arp-responder-theory-and-practice/

ovs-vsctl list bridge br-int

ovs-ofctl dump-flows 的table id

-Tunnel bridge (tun_br)

Various tables for tunneling flows

DVR_PROCESS = 1
PATCH_LV_TO_TUN = 2
GRE_TUN_TO_LV = 3
VXLAN_TUN_TO_LV = 4
GENEVE_TUN_TO_LV = 6
DVR_NOT_LEARN = 9
LEARN_FROM_TUN = 10
UCAST_TO_TUN = 20

ARP_RESPONDER = 21
FLOOD_TO_TUN = 22

-Physical Bridges (phys_brs)

Various tables for DVR use of physical bridge flows

DVR_PROCESS_VLAN = 1
LOCAL_VLAN_TRANSLATION = 2
DVR_NOT_LEARN_VLAN = 3

我要成功了
追踪ovs对特定数据报文的流表匹配与处理结果的实例
主要步骤如下:

ovs-appctl dpif/dump-flows br-tun

ovs-appctl dpif/dump-flows br-int

1、确认你需要跟踪的数据包的各项参数;

2、将其转化成openflow的match域的描述;

3、使用openvswitch提供的ofproto/trace功能跟踪流表匹配情况;

查看端口发送的报文特征
()[root@node1 /]# tcpdump -i tap522cd56e-e7 icmp -vvne

tcpdump: listening on tap522cd56e-e7, link-type EN10MB (Ethernet), capture size 262144 bytes

17:26:56.870610 fa:16:3e:69:b8:57 > fa:16:3e:94:5e:20, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 13878, offset 0, flags [DF], proto ICMP (1), length 84)

193.1.1.2 > 193.1.1.3: ICMP echo request, id 5238, seq 2819, length 64

获得该端口的ofport
ovs-vsctl get Interface tap522cd56e-e7 ofport

或者ovs-ofctl show br-int

获得该端口的ofport是5,所以要加上 in_port=5:(不要与ovs-dpctl show的端口混淆)

ofproto/trace功能跟踪流表匹配情况
ovs-appctl ofproto/trace br-int in_port=5,dl_src=fa:16:3e:69:b8:57,dl_dst=fa:16:3e:94:5e:20,ipv4,nw_src=193.1.1.2,nw_dst=193.1.1.2,nw_proto=0 -generate

()[root@node1 /]# ovs-appctl ofproto/trace br-int in_port=5,dl_src=fa:16:3e:69:b8:57,dl_dst=fa:16:3e:94:5e:20,ipv4,nw_src=193.1.1.2,nw_dst=193.1.1.2,nw_proto=0 -generate

Flow: ip,in_port=5,vlan_tci=0x0000,dl_src=fa:16:3e:69:b8:57,dl_dst=fa:16:3e:94:5e:20,nw_src=193.1.1.2,nw_dst=193.1.1.2,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0

bridge("br-int")


  1. priority 1, cookie 0x917ec44f9e18277d

    NORMAL

    -> forwarding to learned port

bridge("br-tun")


  1. in_port=1, priority 1, cookie 0x88f6a531447d8b21

    goto_table:1

  2. priority 0, cookie 0x88f6a531447d8b21

    goto_table:2

  3. dl_dst=00:00:00:00:00:00/01:00:00:00:00:00, priority 0, cookie 0x88f6a531447d8b21

    goto_table:20

  4. dl_vlan=3,dl_dst=fa:16:3e:94:5e:20, priority 2, cookie 0x88f6a531447d8b21

    pop_vlan

    set_field:0x64->tun_id

    output:4

    -> output to kernel tunnel

Final flow: unchanged

Megaflow: recirc_id=0,eth,ip,in_port=5,vlan_tci=0x0000/0x1fff,dl_src=fa:16:3e:69:b8:57,dl_dst=fa:16:3e:94:5e:20,nw_ecn=0,nw_frag=no

Datapath actions: set(tunnel(tun_id=0x64,src=172.30.196.4,dst=172.30.196.9,ttl=64,tp_dst=4789,flags(df|key))),6

查看命中
ovs-appctl ofproto/trace br-int

ovs-appctl dpif/dump-flows br-tun

查看报文特征:
tcpdump抓包或者ovs-appctl dpif/dump-flows br-tun

()[root@node2 /]# tcpdump -i ens2f2 udp and port 4789 and host 172.30.196.4 -vvne

tcpdump: listening on ens2f2, link-type EN10MB (Ethernet), capture size 262144 bytes

18:58:13.672929 e8:61:1f:26:ca:82 > e8:61:1f:1e:e3:ce, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 21188, offset 0, flags [DF], proto UDP (17), length 134)

172.30.196.4.45749 > 172.30.196.9.4789: [no cksum] VXLAN, flags [I] (0x08), vni 100

fa:16:3e:69:b8:57 > fa:16:3e:94:5e:20, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 43484, offset 0, flags [DF], proto ICMP (1), length 84)

193.1.1.2 > 193.1.1.3: ICMP echo request, id 5238, seq 8288, length 64

18:58:13.673107 e8:61:1f:1e:e3:ce > e8:61:1f:26:ca:82, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 60452, offset 0, flags [DF], proto UDP (17), length 134)

172.30.196.9.49526 > 172.30.196.4.4789: [no cksum] VXLAN, flags [I] (0x08), vni 100

fa:16:3e:94:5e:20 > fa:16:3e:69:b8:57, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 38967, offset 0, flags [none], proto ICMP (1), length 84)

193.1.1.3 > 193.1.1.2: ICMP echo reply, id 5238, seq 8288, length 64

查看入端口

ovs-ofctl show br-tun

tun上流量追踪
()[root@node2 /]# ovs-appctl ofproto/trace br-tun in_port=2,dl_src=e8:61:1f:26:ca:82,dl_dst=e8:61:1f:1e:e3:ce,ipv4,nw_src=172.30.196.4,nw_dst=172.30.196.9,nw_proto=17,tun_id=100 -generate

Flow: udp,tun_id=0x64,in_port=2,vlan_tci=0x0000,dl_src=e8:61:1f:26:ca:82,dl_dst=e8:61:1f:1e:e3:ce,nw_src=172.30.196.4,nw_dst=172.30.196.9,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=0,tp_dst=0

bridge("br-tun")


  1. in_port=2, priority 1, cookie 0xb5fe257879954264

    goto_table:4

  2. tun_id=0x64, priority 1, cookie 0xb5fe257879954264

    push_vlan:0x8100

    set_field:4098->vlan_vid

    goto_table:9

  3. priority 0, cookie 0xb5fe257879954264

    goto_table:10

  4. priority 1, cookie 0xb5fe257879954264

    learn(table=20,hard_timeout=300,priority=1,cookie=0xb5fe257879954264,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:OXM_OF_IN_PORT[])

    -> table=20 vlan_tci=0x0002/0x0fff,dl_dst=e8:61:1f:26:ca:82 priority=1 cookie=0xb5fe257879954264 hard=300 actions=load:0->NXM_OF_VLAN_TCI[],load:0x64->NXM_NX_TUN_ID[],output:2

    output:1

bridge("br-int")


  1. priority 1, cookie 0x85bda8b3876e423a

    NORMAL

    -> learned that e8:61:1f:26:ca:82 is on port patch-tun in VLAN 2

    -> no learned MAC for destination, flooding

bridge("br-ex")


  1. in_port=2, priority 2, cookie 0x8611f8a8aaf077c9

    goto_table:1

  2. priority 0, cookie 0x8611f8a8aaf077c9

    goto_table:2

  3. in_port=2, priority 2, cookie 0x8611f8a8aaf077c9

    drop

Final flow: udp,tun_id=0x64,in_port=2,dl_vlan=2,dl_vlan_pcp=0,vlan_tci1=0x0000,dl_src=e8:61:1f:26:ca:82,dl_dst=e8:61:1f:1e:e3:ce,nw_src=172.30.196.4,nw_dst=172.30.196.9,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=0,tp_dst=0

Megaflow: recirc_id=0,eth,ip,tun_id=0x64,in_port=2,vlan_tci=0x0000,dl_src=e8:61:1f:26:ca:82,dl_dst=e8:61:1f:1e:e3:ce,nw_frag=no

Datapath actions: push_vlan(vid=2,pcp=0),3,pop_vlan,9,10

Br-tun采用多级流表实现节点间的网段内通信,下面直接通过图示来看br-tun中多级流表的设计。图中流表的序号不是固定的,可在neutron.plugins.openvswitch.agent.common目录下的constants.py文件中修改。

流表的语法和格式
ovs-ofctl的Flow语法由一系列field=value形式的键值对组成,用英文逗号和空格隔开。

在描述Flow时,必须要遵循 TCP/IP 协议栈。比如说,在flow中使用了L3(网络层)的字段时,必须也要指明L2(数据链路层)所用的协议。使用了L4(传输层)的字段时,必须也要指明L2和L3所用的协议。

in_port匹配从OpenFlow port id 进入的数据包。
in_port=

dl_vlan匹配IEEE 802.1Q VLAN tag为 的数据包。 的值应该在[0-4095]这个区间。
为0xffff表示匹配没有VLAN tag的包:

dl_vlan=

dl_vlan_pcp匹配IEEE 802.1Q Priority Code Point(PCP, 优先级代码点)为的数据包,改值取值区间为[0-7]。数字越大,表示优先级越高。可以用于QoS。

dl_src 和 dl_dst
例如:dl_dst=01:00:00:00:00:00/01:00:00:00:00:00

匹配源和目的MAC地址。其中”/”后面的为掩码。

比如说,如果掩码是:

  • 01:00:00:00:00:00 只匹配组播MAC (第一个字节LSB为1的MAC地址为MAC组播地址)

  • fe:ff:ff:ff:ff:ff 匹配其他所有MAC,除了组播MAC

  • ff:ff:ff:ff:ff:ff 完全匹配掩码前的MAC,和省掉掩码的效果一样

  • 00:00:00:00:00:00 完全通配,相当于(dl_dst=*)

dl_type
匹配L2(Data Link ,数据链路层) header中的协议类型,该字段描述L3的类型。有效值区间[0, 65535],可以是十进制数或者是以”0x”开头的十六进制数。比如:

dl_type=0x0800 匹配IP数据包

dl_type=0x0806 匹配ARP数据包

dl_type=0x8035 匹配RARP数据包

需要说明的是 dltype=0x0800 可以用关键字 ip 代替。 而 dltype=0x0806 可以用关键字 arp 代替。dl_type=0x8035 可以用关键字 rarp 代替。

nw_src 和 nw_dst
当使用了 dl_type=0x0800 或者关键字 ip 或 tcp 时,nw_src 和 nw_dst分配匹配IP头中的源IP地址和目的IP地址。其中netmask可以是255.255.255.0这样的(dotted quad)形式,也可以是数字 /24 这样的(CIDR)形式

当使用了 dl_type=0x0806 或者关键字 arp 时,nw_src 匹配 ARP 头中的 arspa(Sender Protocol Address)字段。nw_dst 匹配ARP头中的 artpa(Target Protocol Address)字段

当使用了 dl_type=0x8035 或者关键字 rarp 时,nw_src匹配 RARP 头中的 arspa(Sender Protocol Address)字段。nw_dst匹配RARP头中的 artpa(Target Protocol Address)字段

当 dl_type 使用了通配符或者除了0x0800, 0x0806, 0x8035以外的值,则nw_src, nw_dst的值会被忽略

tp_src 和tp_dst
传输层协议如TCP/UDP协议的源端口号、目的端口号

nwproto 和 ipproto
要和 dl_type 字段配合使用。

当dl_type=0x0800或使用了关键字ip时,匹配IP头中的proto字段,取值区间[0, 255],比如为1时可以匹配ICMP数据包,为6时匹配TCP数据包。

当dl_type=0x86dd或使用了关键字ipv6是,匹配IPv6头中的proto字段,取值区间[0, 255],比如为58时匹配ICMPv6数据包,为6时匹配TCP数据包

当dl_type=0x0806或者使用了关键字arp时,匹配ARP opcode的低8位,ARP opcode大于255时,与等于0效果一样

当dl_type=0x8035或者使用了关键字rarp时,匹配ARP opcode的低8位, ARP opcode大于255时,与等于0效果一样

当dl_type使用了通配符或这除了0x0800, 0x0806, 0x8035以外的值,则nw_proto的值会被忽略

nw_tos
匹配IP ToS字段或IPv6的traffic class字段取值为的数据包。取值区间[0, 255],需要注意的是最低2位会被忽略。当dl_type使用除0x800(IP)和0x86dd(IPv6)以外的数值时,该字段被忽略。

nw_ttl
匹配IP和IPv6的TTL字段为的数据包。取值区间[0, 255]。当dl_type使用除0x800(IP)和0x86dd(IPv6)以为的数值时,该字段被忽略。

icmp_type 和 icmp_code
icmp_type=

icmp_code=

当 dl_type 和 nw_proto 确定数据包为 ICMP 或 ICMPv6 时,匹配 ICMP type 和 code。取值区间都为[0, 255]。如果 dl_type 和 mw_proto 使用了其他值时,该字段忽略。

协议关键字
协议关键字相当于alias,对应关系如下:

ip = dl_type=0x0800

ipv6 = dl_type=0x86dd

icmp = dl_type=0x0800,nw_proto=1

icmp6 = dl_type=0x86dd,nw_proto=58

tcp = dl_type=0x0800,nw_proto=6

tcp6 = dl_type=0x86dd,nw_proto=6

udp = dl_type=0x0800,nw_proto=17

udp6 = dl_type=0x86dd,nw_proto=17

sctp = dl_type=0x0800,nw_proto=132

sctp6 = dl_type=0x86dd,nw_proto=132

arp = dl_type=0x0806

rarp = dl_type=0x8035

mpls = dl_type=0x8847

mplsm = dl_type=0x8848

cookie
cookie= 或cookie=<value/mask>

cookie 字段可以用于以下命令 :add-flows和 mod-flows

actions
ovs-ofctl的 add-flow 以及 mod-flows 命令都需要 actions 字段,描述对匹配的数据包执行的动作

在上述的三条命令中,actions字段是flow的一部分,actions字段中可以有多个 action,它们之间用逗号隔开,一个flow的完整语法如下:

=,[=]...,actions=[,...]

可使用的action非常多。如下:

output
output:

将数据包输出到OpenFlow port

group
group:<group_id>

将数据包输出到OpenFlow group

normal
按照设备的常规L2/L3处理流程来处理数据包。这通常是OVS默认flow中的action。要注意的是,并不是所有的OpenFlow switch都支持这个action。

flood
将数据包输出到所有物理端口,除了该数据包的输入口以及不可被flooding的端口。

all
将数据包输出到所有物理端口,除了该数据包的输入口。

local
将数据包输出到local port(与bridge同名的端口)

in_port
in_port 将数据包输出到其输入口

controller
controller(=…)

将数据包以”packet in”消息的形式发送到OpenFlow控制器。其中=键值对可以是:

max_len= 只将数据包的个字节发送到控制器。默认发送这个数据包。

reson= 指明”pakcet in”消息中的reason字段。默认reason为action,还可以是no_match, invalid_ttl。

id= 指明控制器id,16位整数。表示要发送给那个控制器。默认使用的id是0.

controller
controller[:nbytes]

分别是controller()和controller(max_len=)的简略写法。

enqueue
enqueue(,)

将数据包放到端口的队列中。其中必须是OpenFlow端口号或关键字(如”LOCAL”)。不同交换机支持的队列数不同,有些OpenFlow实现根本不支持队列。

drop
丢掉该数据包。

vlan
mod_vlan_vid:<vlan_vid>

修改数据包的VLAN id为。如果数据包没有VLAN tag则添加VLAN id为的VLAN tag。如果数据包VLAN id已经为,则将其VLAN 优先级priority设为0.

mod_vlan_pcp:<vlan_pcp>

修改数据包的VLAN 优先级priority为。如果数据包没有VLAN tag则添加VLAN priority为的VLAN tag。合法值区间为[0, 7],数字越大优先级越高。如果数据包VLAN priority已经为,则将其VLAN id设为0.

strip_vlan
如果数据包有VLAN tag,则剥去VLAN tag。

datalink header mod
mod_dl_src:

mod_dl_dst:

设置数据包的源或目的MAC地址

network header mod
mod_nw_src:

mod_nw_dst:

设置数据包的源或目的IP地址

mod_nw_tos:

设置IPv4头ToS/DSCP或IPv6头traffic class field中DSCP比特位设置成,数值必须是4的倍数,且在[0, 255]区间。这个action并不会修改ToS中的低2位(2 LSB)。

mod_nw_ecn:

设置IPv4头ToS或IPv6头traffic class field中ECN比特位为,数值区间为[0, 3]。这个action并不会修改高6位(6 MSB)。

需要OpenFLow 1.1以上。

mod_nw_ttl:

修改IPv4 TTL或IPv6 hop limit为,取值区间为[0, 255]。需要OpenFlow 1.1以上。

这里写代码片transport header mod
mod_tp_src:

mod_tp_dst:

将数据包的TCP/UDP/SCTP源或目的端口设置成

()[root@node2 ljx]# ovs-ofctl show br-int

root@node2 ljx]# ovs-ofctl show br-int

同一个租户,不能创建两个vpc选择同一个

DVR
路由器中的接口

可以看到该 router 分布:

创建了 SNAT network namespace。该 netns 中,对router 的每一个网络,都有一个 qg 或者 sg interface:

两个宿主机上的接口名称一致

对应 qg-xxx,连接外网

对应 sg-xxx,连接内网

iptables -t nat -S
()[root@node1 /]# ip netns exec snat-6b5d11e6-be9d-4060-a4f7-01d85d0788bd iptables -t nat -S

以及 qrouter network namespace:
以下两个接口没有绑定到host上,接口名称一致,但是slave上的接口是down的

ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 iptables -t nat -S

tracepath
194.1.1.2 tracepath

193.1.1.2 tracepath

绑定floatingip前后差异

()[root@node2 /]# ip netns exec qrouter-6b5d11e6-be9d-4060-a4f7-01d85d0788bd iptables -t nat -S

-P PREROUTING ACCEPT

-P INPUT ACCEPT

-P OUTPUT ACCEPT

-P POSTROUTING ACCEPT

-N neutron-l3-agent-OUTPUT

-N neutron-l3-agent-POSTROUTING

-N neutron-l3-agent-PREROUTING #neutron 增加的 DNAT chain

-N neutron-l3-agent-float-snat

-N neutron-l3-agent-snat

-N neutron-postrouting-bottom

-A PREROUTING -j neutron-l3-agent-PREROUTING # DNAT 由 neutron 新增的 chain 负责处理

-A OUTPUT -j neutron-l3-agent-OUTPUT

-A POSTROUTING -j neutron-l3-agent-POSTROUTING

-A POSTROUTING -j neutron-postrouting-bottom

-A neutron-l3-agent-POSTROUTING ! -i rfp-6b5d11e6-b ! -o rfp-6b5d11e6-b -m conntrack ! --ctstate DNAT -j ACCEPT

-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697#将虚机访问 metadata server 的 traffic 端口由 80 改到 9697(由配置项 metadata_port 设置,默认为 9697),那里有 application 在监听。
-A neutron-l3-agent-PREROUTING -d 10.19.172.196/32 -i rfp-6b5d11e6-b -j DNAT --to-destination 194.1.1.2#到浮动IP的traffic的目的IP 换成虚机的固定 IP
-A neutron-l3-agent-float-snat -s 194.1.1.2/32 -j SNAT --to-source 10.19.172.196

-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat

-A neutron-postrouting-bottom -m comment --comment "Perform source NAT on outgoing traffic." -j neutron-l3-agent-snat

每个浮动 IP,增加三个规则:
-A neutron-l3-agent-PREROUTING -d -j DNAT --to-destination #从本机访问虚机,Dst IP 由浮动IP该为访问固定IP

-A neutron-l3-agent-OUTPUT -d -j DNAT --to #从别的机器上访问虚机,DST IP 由浮动IP改为固定IP

-A neutron-l3-agent-float-snat -s -j SNAT --to #虚机访问外网,将Src IP 由固定IP改为浮动IP

网络包走向分析
不同网段内的虚机之间或者虚机和计算机之间的网络流向可以分为几类:

3.2.1 SNAT:当数据包离开 rouer 的 external device 时改变它的源 IP 地址。这在没有浮动IP 时虚机访问外网的情况下使用。

3.2.2/3 FIP:有时候也称为 DNAT。当虚机的固定 IP分配了浮动 IP 的时候,虚机和外网中的虚机通信的时候使用。

Neutron中Router实现同一租户不同网段的虚拟机间的通信,属于东西向流量,可分为:

同一个物理节点上不同网段内的虚机之间的通信;

不同物理节点上不同网段内的虚机之间的通信。

Router实现虚拟机与Internet间的流量,属于南北向流量,可分为:

虚拟机访问Internet的流量,通常需要经过SNAT处理;

Internet访问虚拟机的流量,可能需要经过DNAT处理。

在没有设置浮动 IP (SNAT)的情况下,虚机访问外网时,虚机发出的网络包首先经过其所在的服务器上的 qrouter 做路由选择,然后再经过 neutron network 节点上的 snat network namespace出去到外网。

neutron涉及到的Linux相关技术栈
bridge:网桥,Libr-int:bridge-integration,综合网桥,常用于表示实现主要内部网络功能的网桥。
br-ex:bridge-external,外部网桥,通常表示负责跟外部网络通信的网桥。

br-int

br-tun

tap设备:模拟一个二层的网络设备,可以接受和发送二层网包。
tun设备:模拟一个三层的网络设备,可以接受和发送三层网包。
veth:虚拟ethernet接口,通常以pair的方式出现,一端发出的网包,会被另一端接收,可以形成两个网桥之间的通道。
qvb:neutron veth, Linux Bridge-side
qvo:neutron veth, OVS-side
iptables:Linux 上常见的实现安全策略的防火墙软件。

namespace:用来实现隔离的一套机制,不同 namespace 中的资源之间彼此不可见。

Vlan:虚拟 Lan,同一个物理 Lan 下用标签实现隔离,可用标号为1-4094。

GRE:General Routing Encapsulation,一种通过封装来实现隧道的方式。在openstack中一般是基于L3的gre,即original pkt/GRE/IP/Ethernet
VXLAN:一套利用 UDP 协议作为底层传输协议的 Overlay 实现。一般认为作为 VLan 技术的延伸或替代者。
nux中用于表示一个能连接不同网络设备的虚拟设备,linux中传统实现的网桥类似一个hub设备,而ovs管理的网桥一般类似交换机。

Neutron二层网络服务实现原理
Neutron network 可分为:

VLAN network:基于物理 VLAN 网络实现的虚拟网络。共享同一个物理网络的多个 VLAN 网络是相互隔离的,甚至可以使用重叠的 IP 地址空间。

​Flat network:基于不使用 VLAN 的物理网络实现的虚拟网络。每个物理网络最多只能实现一个虚拟网络。

​GRE network (通用路由封装网络):一个使用 GRE 封装网络包的虚拟网络。GRE 封装的数据包基于 IP 路由表来进行路由,因此 GRE network 不和具体的物理网络绑定。
​VXLAN network(虚拟可扩展网络):基于 VXLAN 实现的虚拟网络。同 GRE network 一样, VXLAN network 中 IP 包的路由也基于 IP 路由表,也不和具体的物理网络绑定。

local network(本地网络):一个只允许在本服务器内通信的虚拟网络,不知道跨服务器的通信。主要用于单节点上测试。

net: 隔离的 L2 域,可以是虚拟、逻辑或交换,同一个网络中的主机彼此 L2 可见。

subnet: IP 地址块,其中每个虚拟机有一个 IP,同一个子网的主机彼此 L3 可见。

port: 网络上虚拟、逻辑或交换端口。

GRE网络, Vlan网络, Vxlan网络
举例GRE网络

​ 在 VM1 中,虚拟机的网卡实际上连接到了物理机的一个 TAP 设备(即 A,常见名称如 tap-XXX)上,A 则进一步通过 VETH pair(A-B)连接到网桥 qbr-XXX 的端口 vnet0(端口 B)上,之后再通过 VETH pair(C-D)连到 br-int 网桥上。一般 C 的名字格式为 qvb-XXX,而 D的名字格式为 qvo-XXX。注意它们的名称除了前缀外,后面的 id 都是一样的,表示位于同一个虚拟机网络到物理机网络的连接上。

br-tun转发逻辑:
​有一条规则,基于 learn 行动来创建反向(内部网包从 gre 端口发出去)的规则。如下所示:

learn 行动并非标准的 openflow 行动,是 openvswitch 自身的扩展行动,这个行动可以根据流内容动态来修改流表内容。这条规则首先创建了一条新的流(该流对应 vm 从 br-tun 的 gre 端口发出的规则):

举例VLAN网络:
路由是L3 agent来实现,每个子网在br-int上有一个端口(qr-YYY和qr-ZZZ,已配置IP,分别是各自内部子网的网关),L3 agent绑定到上面。要访问外部的公共网络,需要通过L3 agent发出,而不是经过int-br-ex到phy-br-ex(实际上并没有网包从这个veth pair传输)。如果要使用外部可见的floating IP,L3 agent仍然需要通过iptables来进行NAT。

在多租户情况下

我们查看br-tun的流表如下(开启了l2 population) :

​当数据包由in_port = 1(也就是由br-int发出)到达table 0时候,会转给table 2;如果是arp广播请求,因为开启l2 population,也就是arp_responder之后,会提交给 table 21。此时table21有条规则会处理这个请求,这条规则专门由l2 population发来的 entry 来更新 。

​table 21 的更新过程并非网上所讲的那么简单,实际过程如下:假如我们创建了网络A和网络B ,有2个计算机点和1个网络节点。创建一台虚拟机aa加入网络A,同时落地在计算节点1时候,本计算节点的table21只有dhcp的arp的回应记录。再创建一虚拟机bb加入网络B,同时落地在计算节点2时候,本计算节点的table21只有dhcp的arp的回应记录,计算节点1的table21并没有bb的arp的回应记录。创建第三台虚拟机加入网络A,如果落地在计算节点1时候,两个计算节点的table21没有任何更新。当创建第四台虚拟机加入网络A,同时落地在计算机点2时候,计算机点2的table 21更新了所有在计算节点1的同一网络的虚拟机的arp信息,同时table 20记录了所有到达计算机点1同一网络的虚拟机的规则。计算节点1的table21也更新了计算节点2的同网络的虚拟机的arp回应,table20也记录了到达其的规则。

L2 Population
​L2 Population 是用来提高 VXLAN 网络 Scalability 的,减少广播风暴。

​这是一个包含 5 个节点的 VXLAN 网络,每个节点上运行了若干 VM。现在假设 Host 1 上的 VM A 想与 Host 4 上的 VM G 通信,VM A 要做的第一步是获知 VM G 的 MAC 地址。 于是 VM A 需要在整个 VXLAN 网络中广播 APR 报文:“VM G 的 MAC 地址是多少?” 如果没有L2poluation,情况会如下:

​L2 Population 的作用是在 VTEP 上提供 Porxy ARP 功能,使得 VTEP 能够预先获知 VXLAN 网络中,包括VM IP – MAC 对应关系和 VM – VTEP 的对应关系。当 Host 1 上的 同网段的VM A 想与 Host 4 上的同网段的 VM G 通信时,Host 1 上的 VTEP 直接响应 VM A 的 APR 请求即可(为什么HOST1 上会响应,参看上文),告诉VM G的MAC地址。当Host 1 上的VMA想与不同网段的VM G 通信时,首先Host 1 上的 VTEP 响应 VMA所在网络的网关的arp信息(当路由器创建并关联网络时候,L2 Population会使得该网络所跨的所有HOST的VTEP更新该路由器的arp回应信息和如何到达的该路由器的流表,说具体就是table21和table20的更新),数据包会发送给路由器,到达路由器之后,如果路由器同时关联着VM G 所在的网络,它就会发送arp广播请求VM G 的mac,此时网络节点的table21会响应(L2 Population的功能)VM G的信息,路由器知道了VM G的mac地址后,发送单播,由table0到table2,再由table20匹配相关流表,tunnel发出。数据包到达VM G所在的HOST之后,首先匹配table0,然后转给table4,table4去tunnel转给table10, table10 学习完存到table 20之后从patch-int口仍给br-int。br-int相当于一个普通的交换机,将数据包转发给VM G。

另外我还发现如果开启了l2population之后,table10的学习可以去掉。

Neutron三层网络服务实现原理
Neutron 对虚拟三层网络的实现是通过其 L3 Agent (neutron-l3-agent)。该 Agent 利用 Linux IP 栈、route 和 iptables 来实现内网内不同网络内的虚机之间的网络流量,以及虚机和外网之间网络流量的路由和转发。为了在同一个Linux 系统上支持可能的 IP 地址空间重叠,它使用了 Linux network namespace 来提供隔离的转发上下文。

NameSpace技术
​二层网络上,VLAN可将一个物理交换机分割成几个独立的虚拟交换机。三层网络上,Linux network namespace(netns)可将一个物理三层网络分割成几个独立的虚拟三层网络。

​每个netns 拥有独立的(virtual)network devices, IP addresses, IP routing tables, /proc/net directory, ports 等等。新创建的netns默认只包含 loopback device。除了这个设备,每个 network device,不管是物理的还是虚拟的网卡还是网桥等,都只能存在于一个netns。而且,连接物理硬件的物理设备只能存在于root netns。其它普通的网络设备可以被创建和添加到某个netns。

相关常用命令
ip netns add nstest

ip netns list

ip netns delete

ip netns exec

添加 virtual interfaces 到 network namespace

ip link add veth-a type veth peer name veth-b #创建一对虚拟网卡veth-a 和 veth-b,两者由一根虚拟网线连接

将 veth-b 添加到 network namespace

ip link set veth-b netns nstest

设置 vi 的 IP 地址

ip netns exec nstest ip addr add 10.0.0.2/24 dev veth-b

ip netns exec nstest ip link set dev veth-b up

设置默认namespace的vi地址

ip addr add 10.0.0.1/24 dev veth-a

ip link set dev veth-a up

Iptables
​ Neutron 主要用到 filter 表和 nat 表,其中, filter 用来实现安全组(Security Group)和 防火墙(FWaas);nat 主要用来实现 router。

​iptables作为client,去管理FWaas。相关的请求最后会发送相关内核模块,如ip_tables。ip_tables内核模块主要用于组织iptables使用的表,链,规则。netfilter是一套技术框架,ip_tables依托于netfilter来注册各种hooks实现对数据包的具体控制。

NEW,ESTABLISHED,RELATED,INVALID状态

NEW: conntrack模块看到的某个连接第一个包,它即将被匹配了。比如,我们看到一个SYN包,是我们所留意的连接的第一个包,就要匹配它。第一个包也可能不是SYN包,但它仍会被认为是NEW状态。
ESTABLISHED: 已经注意到两个方向上的数据传输,而且会继续匹配这个连接的包。处于ESTABLISHED状态的连接是非常容易理解的。只要发送并接到应答,连接就是ESTABLISHED的了。一个连接要从NEW变为ESTABLISHED,只需要接到应答包即可,不管这个包是发往防火墙的,还是要由防火墙转发的。ICMP的错误和重定向等信息包也被看作是ESTABLISHED,只要它们是我们所发出的信息的应答。
RELATED 当一个连接和某个已处于ESTABLISHED状态的连接有关系时,就被认为是RELATED的了。换句话说,一个连接要想是RELATED的,首先要有一个ESTABLISHED的连接。这个ESTABLISHED连接再产生一个主连接之外的连接,这个新的连接就是RELATED的了,比如ftp的父子链接。
INVALID 非以上状态的包。

iptables自定义连

如果想自行定义规则链,可以通过-N参数,然后通过-j/–jump跳转过来就可以了。

下面我们查看openstack中的router是如何使用iptables的。

首先当路由器创建完成并加入网络(9.9.9.0/24)之后,我们查看一下它iptable信息。

输出是分段的,每个段落一个表,*开头后面跟着表名。
:开头的行是对链匹配次数的总结,后面跟着统计信息,[数据包:字节数]。
后面跟着的是具体规则。
最后跟着COMMIT表示一个表的结束。

然后我们将该路由器关联一个外部网络(10.0.99.0/24),我们发现该网络的所有虚拟机访问外网时候,会将凡是到达外网络的数据都进行SNAT为其外部地址10.0.99.2,但是没有相应的表项导致外网中的虚拟机是无法访问内部虚拟机的。

root@netagent:~# ip netns exec qrouter-71d8932f-3782-470f-b3a2-f80203f96885 iptables-save

Floating Ip
我们将9.9.9.0/24网络中的虚拟机(9.9.9.3)分配一个floatingIP(10.0.99.3),查看router的iptables-save。

root@netagent:~# ip netns exec qrouter-71d8932f-3782-470f-b3a2-f80203f96885 iptables-save

-A neutron-l3-agent-float-snat -s 9.9.9.3/32 -j SNAT –to-source 10.0.99.3,决定了该虚拟机访问外网会做SNAT为10.0.99.3,而其他虚拟机依然按照原来方式访问外网。

-A neutron-l3-agent-PREROUTING -d 10.0.99.3/32 -j DNAT –to-destination 9.9.9.3

Linux IP 栈
Secondary IP
我们看一个路由器的ip信息qg-37790cfc-43接口绑定了浮动ip。

ip netns exec qrouter-71d8932f-3782-470f-b3a2-f80203f96885 ip addr

这个interface有两个静态 IP 地址。第一个是主要的(primary)IP,第二个是辅助的( secondary) 的 IP。当一个网卡配置了静态IP后,你可以添加secondary IP 给它。这样它就拥有了多个 IP 地址了。Secondary IP 不可以通过 DHCP 分配。它所有的IP 地址都关联到它唯一的一个 MAC 地址上。那为什么需要 secondary IP 地址呢? 路由器有个 Secondary IP 的概念,这个特性可以创建逻辑子网,也就是说在一个物理网口上连接两个子网,比如这个网口接到一台交换机上,如果这个网口没有配置Secondary IP的话,那么这台交换机只能连接一个网段的主机,比如 192.168.1.1/24,但是,如果它配置了Secondary IP,那么就可以连接两个网段的主机,比如 192.168.1.1/24 和 10.0.0.1/24。

Gratuitous ARP
​ ARP的基本功能就是在以太网环境中,通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。但由于ARP的广播、动态学习等特性注定了它不是一种安全的协议,所以在实际应用中,会由于各种各样的原因使ARP学习失败,从而影响网络的互通性,并进而影响用户的业务稳定运行。整个ARP的体系里基本上就是由ARP Request和Response组成的,Request就是告知对方“我要什么”,而Response是回答“我是什么”。但有些时候也会例外,他们虽然从形式上还是Request和Response的,但它们通常不会不是一问一答的,而是只有其中的一部分,所以通常被称为免费ARP或无为ARP(Gratuitous ARP)。

Gratuitous ARP在主机启动的时候,请求自己的IP地址对应的MAC地址。

它常用于三个用途:

​ Change of L2 address:通告自己改变了 MAC 地址。以 ARP Response 的形式发送广播,它通常只是为了把自己的ARP信息通告/更新给局域网全体,这种Response不需要别人请求,是自己主动发送的通告。当一个网络设备的 MAC 地址发生改变时,发送该设备的 Gratuitous ARP,通知所在广播域内的已经包含该 IP 地址在其 ARP 表中的机器去更新它的 ARP 条目。

​ Duplicate address detection:重复 MAC 地址检测。以 ARP Request的形式发送广播,请求自己的MAC地址,目的是探测局域网中是否有跟自己IP地址相同的主机,也就是常说的IP冲突。发送主机并不需要一定收到此请求的回答。如果收到一个回答,表示网络中存在与自身IP相同的主机。如果没有收到应答,则表示本机所使用的IP与网络中其它主机并不冲突。

Virtual IP:用于一组服务器做 failover 时通知周围的机器新生效的 IP 地址的 MAC.

Route (Linux 路由表)
​route命令用来显示并设置Linux内核中的网络路由表,route命令设置的路由主要是静态路由。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。

route add -net 10.0.0.0 netmask 255.0.0.0 dev eth0 #增加一条经过 eth0 到达 10.0.0.0 的路由

route add -net 10.0.0.0 netmask 255.0.0.0 reject #增加一条屏蔽的路由,目的地址为10.X.X.X将被拒绝。

route del -net 10.0.0.0 netmask 255.0.0.0

route del -net 10.0.0.0 netmask 255.0.0 reject

route del default gw 10.0.36.254

route add default gw 10.0.36.254

基于VRRP的路由高可用
什么是VRRP?
​Virtual Redundent Routing Protocol 虚拟冗余路由协议,是一种可以解决这种问题的容错协议。VRRP协议的目的就是为了解决路由单点故障问题,VRRP通过竞选(election)协议来动态的将路由任务交给LAN中虚拟路由器中的某台VRRP路由器。它的原理如下:

相关术语:

虚拟路由器:由一个 Master 路由器和多个 Backup 路由器组成。主机将虚拟路由器当作默认网关。

VRID:虚拟路由器的标识,有相同 VRID 的一组路由器构成一个虚拟路由器。通常用(0-255)标识

Master路由器:虚拟路由器中承担报文转发任务的路由器。

Backup路由器: Master路由器出现故障时能够代替 Master路由器工作的路由器。

虚拟 IP:地址虚拟路由器的 IP 地址,一个虚拟路由器可以拥有一个或多个IP 地址。

IP 地址拥有者:接口IP地址与虚拟IP地址相同的路由器被称为 IP 地址拥有者。

虚拟 MAC 地址:一个虚拟路由器拥有一个虚拟 MAC 地址。虚拟 MAC 地址的格式为 00-00-5E-00-01-{VRID}。通常情况下虚拟路由器回应 ARP 请求使用的是虚拟 MAC 地址,只有虚拟路由器做特殊配置的时候才回应接口的真实 MAC 地址。

priority优先级:VRRP 根据优先级来确定虚拟路由器中每台路由器的地位。用0-255来表示,数字越小优先级越低。VRRP优先级的取值范围为0到255(数值越大表明优先级越高),可配置的范围是1到254,优先级0为系统保留给路由器放弃Master位置时候使用,255则是系统保留给IP地址拥有者使用。当路由器为IP地址拥有者时,其优先级始终为255。因此,当虚拟路由器内存在IP地址拥有者时,只要其工作正常,则为Master路由器。

抢占方式:默认,如果 Backup 路由器工作在抢占方式下,当它收到 VRRP 报文后会将自己的优先级与通告报文中的优先级进行比较。如果自己的优先级比当前的 Master 路由器的优先级高就会主动抢占成为 Master 路由器否则将保持 Backup 状态。

非抢占方式:如果 Backup 路由器工作在非抢占方式下则只要 Master 路由器没有出现故障Backup 路由器即使随后被配置了更高的优先级也不会成为Master 路由器。

VRRP协议报文:封装在IP报文中,发送到分配给 VRRP 的 IP 组播地址。在IP报文头中,源地址为发送报文接口的主 IP 地址(不是虚拟IP地址),目的地址是224.0.0.18,TTL是255,协议号是112。目前,VRRP协议包括两个版本:VRRPv2和VRRPv3,VRRPv2仅适用于IPv4网路,VRRPv3适用于IPv4和IPv6两种网络。

VRRP 节点三种状态:初始状态(Initialize)、活动状态(Master)、备份状态(Backup)。其中,只有处于Master状态的设备才可以转发那些发送到虚拟IP地址的报文。

然后我们结合上图来描述一下VRRP的机制。

Device A 和 B 组成一个 VRRP 组,它的虚拟 IP(VIP) 为 10.0.0.3/24。其中,通过选举机制,A 是 Master Router,B 是 Backup Router。一个 VRRP 组内可以由多个设备,但是只有一个是 Master 设备。注意 Device A 和 B 可以由自己的 IP 地址,VIP 可以和其中的某 IP 相同,也可以不同。

当前,Router A 作为 Master router 向局域网内的机器提供路由服务,Router B 作为 Backup router。它的任务是周期性地接受 A 发出的心跳。在规定的时间段内,如果都没有收到 A 发出的心跳,则启动一个选举过程,重新选出 Master。

局域网内的机器将虚拟路由器当作默认网关,它们仅仅知道这个虚拟路由器的IP 地址 10.0.0.3,而并不知道具体的 Master 路由器的 IP 地址以及 Backup 路由器的IP 地址。它们将自己的缺省路由下一跳地址设置为10.0.0.3。于是,网络内的主机就通过这个虚拟的路由器来与其它网络进行通信。如果 Master 路由器坏掉,Backup 路由器将会通过选举策略选出一个新的 Master 路由器,继续向网络内的主机提供路由服务。从而实现网络内的主机不间断地与外部网络进行通信。

它的优势:

操作简单:它不需要改变组网情况,也不需要在主机上做任何配置,只需要在相关路由器上配置极少的几条命令,就能实现下一跳网关的备份,并且不会给主机带来任何负担。和其他方法比较起来,VRRP更加能够满足用户的需求。

简化网络管理:在具有多播或广播能力的局域网(如以太网)中,借助 VRRP 能在某台设备出现故障时仍然提供高可靠的缺省链路,有效避免单一链路发生故障后网络中断的问题,而无需修改动态路由协议、路由发现协议等配置信息,也无需修改主机的默认网关配置。

适应性强:VRRP报文封装在IP报文中,支持各种上层协议。

网络开销小:VRRP只定义了一种报文——VRRP通告报文,并且只有处于Master状态的路由器可以发送VRRP报文。

什么是keepalived ?
​Keepalived 是 VRRP 的一个非常好的开源实现,它是一个基于 VRRP 协议来实现的 WEB 服务高可用方案,可以利用其来避免单点故障。在neutron的路由高可用中,需要安装keepalived 即可。

Keepalived组件如下:

keepalived是模块化设计,不同模块负责不同的功能:

core:keepalived的核心;负责主进程的启动和维护全局配置文件的加载解析等

check:负责healthchecker(健康检查)包括了各种健康检查方式以及对应的配置文件的解析

vrrp VRRPD:子进程用来实现VRRP协议

libipfwc iptables(ipchains)库配置LVS

libipvs*:配置LVS

keepalived进程如下:

Keepalived 使用三个进程,其中 Watchdog 是控制进程,VRRP Stack and Checkers 是它的两个子进程。

Watchdog 通过心跳机制来确保子进程处于运行状态。

Checkers:负责真实服务器的健康检测,用于负载均衡。

VRRP Stack:实现 VRRP 协议,提供 HA。

neutron如何实现三层网络的高可用?
QR 和 QG 分别连接内网和外网,这是 router 本来就有的端口;HA 端口是 HA router 才特有的。一个非 HA router,只会存在于一个 Neutron L3 agent 所在的 Network node 中。而一个 HA router 会在多个 Neutron L3 agent 所在的 Network node 里创建 instance,这包括创建相应的 namespace 和端口。每个 HA router instance 里面的 QR 和 QG 都有相同的设备名和 MAC 地址(Media Access Control Address)。

root@netagent10038219:~# ip netns exec qrouter-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b ip addr

Slave 和 Master router instance 的区别在于 Master router instance 的网络端口上是有 IP 地址的。这些 IP 实际上是 VIP,它们只会存在于 Master router instance 上。每个 router instance 上都有一个 HA 端口,这些端口都有不同的 IP。这些端口是用来进行 VRRP 广播通信的。每个 router instance 里面都运行着一个 keepalived 进程。Keepalived 是一个实现了 VRRP 的软件工具,Neutron 利用 keepalived 实现的 L3 HA的。

对于Master 我们查看一下它发的VRRP广播

root@netagent10038219:~# ip netns exec qrouter-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b tcpdump -i ha-65bc01fe-af

HA 端口向广播地址 224.0.0.18 发送信息。这是由 VRRP 协议规定的。如果 Master router instance 出现故障,不能发出广播信息,导致 Slave router instance 未在一定的时间内收到广播信息。剩下的 Slave router instance 会选取出一个来作为新的 Master router instance。这个 instance 会获取 VIP,从而为 OpenStack 提供三层网络。虽然提供服务的 router instance 变了,但是 IP 没有改变,所以从使用者的角度来看,网络服务没有发生改变。

vrid 是 HA router 对应的 id,每个 tenant 下面,不同的 router 对应不同的 vrid。需要注意的是,由于 VRRP 协议的限制,vrid 是一个 8 bit 数据。因此每个 tenant 下面,最多只能创建 255 个 HA router。prio 是 instance 的优先级,这个目前是不可配置的,每个 instance 的优先级一样。authtype 是 instance 之间通信的认证方式,默认是不需要认证。intvl 是广播之间的间隔时间,默认是 2 秒,这个可以配置。

每个 HA router 一旦被创建,Neutron L3 agent 会为每个 router instance 创建一个 keepalived 进程,在这之前会生成一个 keepalived 的配置文件供 keepalived 的进程使用。

root@netagent10038219:~# ps -aux | grep '23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b'

总的来说,要使用 HA router,首先必须有多个 Neutron L3 agent 在不同的 Network node 上运行。一旦创建了一个 HA router,Neutron 会通过多个 L3 agent 创建相应的 namespace 和端口,这样就有了这个 HA router 的多个 instance。同时,L3 agent 还会创建 keepalived 进程,每个 keepalived 进程都有 router 的全部信息。这样,每个 Network node 上都有 HA router 的一个 instance 和管理这个 router instance 的 keepalived 进程。

当一切就绪之后,这些 router instance 会进行一次选举,胜出的作为 Master instance,剩下的作为 Slave instance。由于所有的 router instance 的优先级都是一样的,选举的结果是随机的。也就是说,如果创建多个 HA router,这些 router 的 Master instance 可能分布在多个 Network node 上。

选举结束后,Master router instance 负责提供 L3 网络服务,并同时向其他的 Slave router instance 发广播报告自身的状况。一旦由于各种原因,广播中断,Slave router instance 会重新选举出新的 Master router instance,继续提供 L3 网络服务,并同时发送广播报告自身的状况。

DVR分布式路由的实现
DVR的架构
网络层:

服务层:

架构:

重要信息:
当虚拟机创建落在计算机点后,才会在该计算机节点出现qrouter的namespace

当router设置网关后,才会出现snat namespace

当关联floating ip 才会在计算机点出现fixed ip 的namespace

创建路由器

如果我们选择了外部网络后,在网络节点就会出现snat namespace

fixed IP的南北向流量
创建内部网络6.6.6.0/24和虚拟机6.6.6.5,虚拟机落在计算节点2,我们看到在这个节点出现了qrouter-namespace,同时网络节点也出现了qrouter-namespace,这两个网络接口信息一样,同时snat-namespace也多了一个网口。

在虚机没有floating ip的情况下访问外网,从虚机发出的包会首先到计算节点的qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a,丢给网关6.6.6.1/24,然后匹配table 101058049,我们看到会交给6.6.6.3的地址,它正是snat-namespace新出现的网口sg-d722c249-c5地址。

[root@com2 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip rule

总之,虚拟机通过计算节点的qrouter-namespace再到网络节点的snat-namespace,而外网返回的数据会通过网络节点的snat-namespace和qrouter-namespace到计算节点的虚拟机。

floatingip的南北向流量
我们对虚拟机6.6.6.5绑定一个floatingip如下:

我们看到虚拟机所在的节点多出了fip-namespace,同时我们看到qrouter-namespace有很多变化。虚拟机6.6.6.5ping外网时候,它的数据的首先查找主表main,没有匹配项后,就会匹配from 6.6.6.5 lookup 16,而不会匹配from 6.6.6.1/24 lookup 101058049 。在table16,我们看到数据想通过qrouter-namespace的rfp-851e3ae1-1到达169.254.106.115,数据转发到rfp-851e3ae1-1接口后,qrouter-namespace对其作了SNAT,neutron-l3-agent-float-snat -s 6.6.6.5/32 -j SNAT –to-source 123.4.4.16,也就是数据从rfp-851e3ae1-1发出后的源地址为123.4.4.16。qrouter-namespace的rfp-851e3ae1-1与fip-namespace的fpr-851e3ae1-1是veth对,此时数据就到达了fip-namespace。我们看一下fip-namespace路由表,数据会转发到fg-9b5bb134-fa接口到达外网。

外网数据返回时候,以 123.4.4.16/32作为目的地址发给fip-namespace的fg-9b5bb134-fa,根据路由表会通过fpr-851e3ae1-1发给网关169.254.106.114,也就是qrouter-namespace的rfp-851e3ae1-1接口地址,到达该接口,qrouter-namespace作DNAT,将123.4.4.16/32变为6.6.6.5/32。neutron-l3-agent-PREROUTING -d 123.4.4.16/32 -j DNAT –to-destination 6.6.6.5,然后查主表如何到达6.6.6.5,根据路由表,我们发现数据到达了 qr-b452f7b9-bc发出,之后就会转发到br-int再到虚机。

东西流量
我们又创建了一个子网192.168.1.0/24,虚拟机192.168.1.5在计算节点1,在计算机点1也会出现qrouter-namespace

我们用虚拟机192.168.1.5 ping 6.6.6.5。首先会查询路由表,将包发送到网关192.168.1.1。那么会首先会发送192.168.1.1的arp请求。arp请求会发送到br-int上,192.168.1.5 的portid为b452f7b9-bc4d-464d-8dca-fa2396bfba85,对应的br-int上是Port “qvob452f7b9-bc”

而端口qvo45b153f7-40是属于vlan 2的,qr-b452f7b9-bc也是属于vlan2的,arp广播包会转发到”qr-b452f7b9-bc”和”patch-tun”。而”qr-b452f7b9-bc”是com2的qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a路由接口,拥有网关地址“192.168.1.1”

对于”patch-tun”,之后会发送到“br-tun”,我们看看“br-tun”

cookie=0xbe2941571871a27c, duration=13079.754s, table=1, n_packets=1, n_bytes=42, idle_age=13078, priority=3,arp,dl_vlan=2,arp_tpa=192.168.1.1 actions=drop

cookie=0xbe2941571871a27c, duration=13079.552s, table=1, n_packets=1, n_bytes=42, idle_age=13078, priority=3,arp,dl_vlan=5,arp_tpa=6.6.6.1 actions=drop

发现数据包会由table=0,转给table=1,对于arp采取丢弃的方式。

虚拟机192.168.1..5 ,当获取到了192.168.1.1的MAC地址后,会发出如下的包:

Dest IP: 6.6.6.5
Souce IP: 192.168.1.5
Dest MAC: MAC of 192.168.1.1
Source MAC: MAC of 192.168.1.5

执行命令查看ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip rule
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
查看main
ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip route list table main

6.6.6.0/24 dev qr-b452f7b9-bc proto kernel scope link src 6.6.6.1
192.168.1.0/24 dev qr-ff78f72b-98 proto kernel scope link src 192.168.1.1
在main表中满足以下路由:
6.6.6.0/24 dev qr-b452f7b9-bc proto kernel scope link src 6.6.6.1
因此会从qr-b452f7b9-bc 转发出去。

之后需要去查询6.6.6.5 的MAC地址, MAC是由neutron使用静态ARP的方式设定的:
ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip nei
6.6.6.5 dev qr-ff78f72b-98 lladdr fa:16:3e:23:77:1c PERMANENT
由于Neutron知道所有虚机的信息,因此他可以事先设定好静态ARP。
至此,我们的ICMP包会变成以下形式从qr-b452f7b9-bc 转发出去:
Dest IP: 6.6.6.5
Souce IP: 192.168.1.5
Dest MAC: MAC of 6.6.6.5
Source MAC: MAC of 192.168.1.5

当包转发到”br-tun”后,开始查询openflow表。
ovs-ofctl show br-tun 可以查看端口状态
首先我们看一下br-tun的flowtable,首先会进入table 0,由于包是从br-int发过来的,因此in_port是patch-int(1),之后会查询表1,这张表中会丢弃目标地址是interface_distributed接口的ARP和目的MAC是interface_distributed的包。以防止虚机发送给本地路由器的包被转发到网络中。
我们的ICMP包会命中一下flow,它会把源MAC地址改为全局唯一和计算节点绑定的MAC(fa:16:3f:88:30:58):

cookie=0xbe2941571871a27c, duration=13079.754s, table=1, n_packets=1, n_bytes=42, idle_age=13078, priority=3,arp,dl_vlan=2,arp_tpa=192.168.1.1 actions=drop

cookie=0xbe2941571871a27c, duration=13079.552s, table=1, n_packets=1, n_bytes=42, idle_age=13078, priority=3,arp,dl_vlan=5,arp_tpa=6.6.6.1 actions=drop

cookie=0xbae18ea66f2bd595, duration=69678.476s, table=1, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=2,dl_vlan=2,dl_dst=fa:16:3e:99:3c:29 actions=drop
cookie=0xbae18ea66f2bd595, duration=16219.966s, table=1, n_packets=0, n_bytes=0, idle_age=16219, priority=2,dl_vlan=5,dl_dst=fa:16:3e:a8:f8:fa actions=drop

cookie=0xbae18ea66f2bd595, duration=69678.454s, table=1, n_packets=4, n_bytes=440, idle_age=65534, hard_age=65534, priority=1,dl_vlan=2,dl_src=fa:16:3e:99:3c:29 actions=mod_dl_src:fa:16:3f:88:30:58,resubmit(,2)
cookie=0xbae18ea66f2bd595, duration=16219.961s, table=1, n_packets=4, n_bytes=440, idle_age=16218, priority=1,dl_vlan=5,dl_src=fa:16:3e:a8:f8:fa actions=mod_dl_src:fa:16:3f:88:30:58,resubmit(,2)

继续查询流表2,表2是VXLAN表,如果是广播包就会查询表22,如果是单播包就查询表20:

cookie=0xbae18ea66f2bd595, duration=79697.638s, table=2, n_packets=0, n_bytes=0, idle_age=65534, hard_age=65534, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)
cookie=0xbae18ea66f2bd595, duration=79697.637s, table=2, n_packets=120088, n_bytes=5057540, idle_age=1, hard_age=65534, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)

ICMP包是单播包,因此会查询表20,由于开启了L2 pop功能,在表20中会事先学习到应该转发到哪个VTEP

cookie=0xbae18ea66f2bd595, duration=15286.730s, table=20, n_packets=0, n_bytes=0, idle_age=15286, priority=2,dl_vlan=2,dl_dst= fa:16:3e:23:77:1c actions=strip_vlan,set_tunnel:0x62,output:2

此时包会变成如下形式:

Dest IP: 6.6.6.5

Souce IP: 192.168.1.5

Dest MAC: MAC of 6.6.6.5

Source MAC: 16:3f:88:30:58

之后包会从port 2发出。

数据包到达com2上查询br-tun的流表:
ovs-ofctl show br-tun 可以查看端口状态
在table0中可以看到,如果包是从外部发来的就会去查询表4:
ovs-ofctl dump-flows br-tun | grep “table=0”
cookie=0x83d99e19c90a76fc, duration=10376.919s, table=0, n_packets=6, n_bytes=580, idle_age=6185, priority=1,in_port=2 actions=resubmit(,4)

在表4中,会将tun_id对应的改为本地vlan id,之后查询表9:

cookie=0x83d99e19c90a76fc, duration=5190.158s, table=4, n_packets=0, n_bytes=0, idle_age=5190, priority=1,tun_id=0x42 actions=mod_vlan_vid:5,resubmit(,9)

在表9中,如果发现包的源地址是全局唯一并与计算节点绑定的MAC地址,就将其转发到br-int:

cookie=0x83d99e19c90a76fc, duration=21043.043s, table=9, n_packets=0, n_bytes=0, idle_age=21043, priority=1,dl_src=16:3f:88:30:58 actions=output:1

由于我们的源MAC为16:3f:88:30:58,我们的ICMP包就被转发到了br-int,之后查询br-int的流表:

ovs-ofctl dump-flows br-int
cookie=0xa0e2bd57839cefda, duration=21258.165s, table=0, n_packets=0, n_bytes=0, idle_age=21258, priority=2,in_port=1,dl_src=16:3f:88:30:58actions=resubmit(,1)
在表1中,事先设定好了flow,如果目的MAC是发送给虚拟机6.6.6.5,就将源MAC改为network 2的网关MAC地址:
cookie=0xa0e2bd57839cefda, duration=5401.439s, table=1, n_packets=0, n_bytes=0, idle_age=5401, priority=4,dl_vlan=3,dl_dst=16:3f:88:30:58actions=strip_vlan,mod_dl_src:fa:16:3e:99:3c:29,output:15
经过br-int的流表后,包会变成如下形式:
Dest IP: 6.6.6.5
Souce IP: 192.168.1.5
Dest MAC: MAC of 6.6.6.5
Source MAC:fa:16:3e:99:3c:29(6.6.6.0/24的网关MAC地址)

DHCP服务
neutron dhcp为租户网络提供DHCP服务,即IP地址动态分配,另外还会提供metadata请求服务。
3个主要的部件:
DHCP agent scheduler:负责DHCP agent与network的调度。
DHCP agent:为租户网络提供DHCP的功能,提供metadata request服务。
DHCP driver:即dnsmasq,用于管理DHCP server。

Dnsmasq 是被 Neutron 用来提供 DHCP 和 DNS 服务的一个开源程序,即DHCP driver部分。它提供 DNS 缓存和 DHCP 服务功能。作为域名解析服务器(DNS),dnsmasq可以通过缓存 DNS 请求来提高对访问过的网址的连接速度。作为DHCP 服务器,dnsmasq 可以为局域网电脑提供内网ip地址和路由。DNS和DHCP两个功能可以同时或分别单独实现。dnsmasq轻量且易配置,适用于主机较少的网络。

根据整个dhcp处理的流程,dhcp模块主要由Neutron api、core plugin(如linux bridge plugin,ovs plugin等)、dhcp agent scheduler、dhcp agent、dhcp driver(dnsmasq)构成。

对应架构图中数字,有以下几个接口:
1.network/subnet/port的操作
2.agent management/agent scheduler的操作
3.network/subnet/port操作会发送rpc请求到dhcp agent。
4.agentscheduler db发送rpc请求到dhcp agent。
5.dhcp agent通过DhcpPluginApi发送rpc请求到core plugin,操作相应的数据库。
6.dhcp agent调用dhcp driver进行dhcp相关操作。

虚机获取固定IP (Fixed IP)主要分为两个步骤:

​ 在创建虚机过程中,Neutron 随机生成 MAC 和 从配置数据中分配一个固定IP 地址,并保存到 Dnsmasq 的 hosts 文件中,让 Dnsmasq 做好准备。

​ 虚机在启动时向 Dnsmasq 获取 IP 地址

红色为创建虚拟机的数据流

绿色为虚拟机启动的数据流

创建虚机时的数据流

Controller 节点上的 Neutron Server 接到该请求后,会开始下面的过程:

步骤 2 ~ 6:Neutron Server 生成 MAC 和 IP。 其中 MAC 是由任意数组成的;Fixed IP 是从保存在数据库中的管理员配置的网络和地址数据生成的。

步骤 7 ~ 10: 调用 L3 Agent 和 OVS 执行一些操作。

步骤 12 ~ 14:通过 AMQP 的 cast 消息给 Neutron 节点上的 DHCP Agent,告诉它 Port 创建结束以及 新分配的 Port 的数据。

步骤 13:返回Port 给 nova-compute。

步骤 15:Neturon 节点上的 DHCP Agent 根据接收到的 Port 创建完成通知,重新生成 Dnsmasq 的 hosts 文件,然后让 Dnsmasq 重新加载该文件。Nova 拿到 Port 的数据后,会写入虚机的 libvirt.xml 文件。

根据"my-public" network的能力,用户可以创建很多这样的IP。将浮动IP与虚拟机关联,可以通过命令行或者GUI完成。下图是GUI的例子:

在router namespace中我们可以看到,新增加了3跳iptabales规则:

-A neutron-l3-agent-OUTPUT -d 180.180.180.3/32 -j DNAT --to-destination 20.20.20.2

-A neutron-l3-agent-PREROUTING -d 180.180.180.3/32 -j DNAT --to-destination 20.20.20.2

-A neutron-l3-agent-float-snat -s 20.20.20.2/32 -j SNAT --to-source 180.180.180.3

这些规则主要是对Floating IP进行NAT操作。对于router收到的目的地址为180.180.180.3的请求,会被转换成目标地址为20.20.20.2。反之亦然。

绑定Floating IP后,我们可以连接到虚拟机。需要确认安全组规则已经被设置,从而允许这样连接:

nova secgroup-add-rule default icmp -1 -1 0.0.0.0/0

nova secgroup-add-rule default tcp 22 22 0.0.0.0/0

这两条规则,允许ping和ssh。

QR 和 QG 分别连接内网和外网,这是 router 本来就有的端口;HA 端口是 HA router 才特有的。一个非 HA router,只会存在于一个 Neutron L3 agent 所在的 Network node 中。而一个 HA router 会在多个 Neutron L3 agent 所在的 Network node 里创建 instance,这包括创建相应的 namespace 和端口。每个 HA router instance 里面的 QR 和 QG 都有相同的设备名和 MAC 地址(Media Access Control Address)。

HA 端口向广播地址 224.0.0.18 发送信息。这是由 VRRP 协议规定的。如果 Master router instance 出现故障,不能发出广播信息,导致 Slave router instance 未在一定的时间内收到广播信息。剩下的 Slave router instance 会选取出一个来作为新的 Master router instance。这个 instance 会获取 VIP,从而为 OpenStack 提供三层网络。虽然提供服务的 router instance 变了,但是 IP 没有改变,所以从使用者的角度来看,网络服务没有发生改变。

vrid 是 HA router 对应的 id,每个 tenant 下面,不同的 router 对应不同的 vrid。需要注意的是,由于 VRRP 协议的限制,vrid 是一个 8 bit 数据。因此每个 tenant 下面,最多只能创建 255 个 HA router。prio 是 instance 的优先级,这个目前是不可配置的,每个 instance 的优先级一样。authtype 是 instance 之间通信的认证方式,默认是不需要认证。intvl 是广播之间的间隔时间,默认是 2 秒,这个可以配置。

每个 HA router 一旦被创建,Neutron L3 agent 会为每个 router instance 创建一个 keepalived 进程,在这之前会生成一个 keepalived 的配置文件供 keepalived 的进程使用。

DVR和DVR-snat的区别试验

  1. 所有节点的L3层通信,在流量出节点之前都会把源MAC替换成各节点都全局唯一的DVR host MAC。

  2. 从实质上讲,neutron的DVR模式并没有使用任何颠覆性的技术手段,可以说也就是把原有的VRouter给Distribute了。

  3. 网络节点只是在L3层服务配置了dvr_snat模式以及在命名空间上多了一个snat-XXX的专门处理来自计算节点的无FIP的南北向流量。

openvswitch的原理和常用命令
Openvswitch工作原理
  openvSwitch是一个高质量的、多层虚拟交换机,使用开源Apache2.0许可协议,由 Nicira Networks开发,主要实现代码为可移植的C代码。它的目的是让大规模网络自动化可以通过编程扩展,同时仍然支持标准的管理接口和协议(例如NetFlow, sFlow, SPAN, RSPAN, CLI, LACP, 802.1ag)。此外,它被设计位支持跨越多个物理服务器的分布式环境,类似于VMware的vNetwork分布式vswitch或Cisco Nexus 1000 V。Open vSwitch支持多种linux 虚拟化技术,包括Xen/XenServer, KVM和VirtualBox。

  openvswitch是一个虚拟交换软件,主要用于虚拟机VM环境,作为一个虚拟交换机,支持Xen/XenServer,KVM以及virtualBox多种虚拟化技术。在这种虚拟化的环境中,一个虚拟交换机主要有两个作用:传递虚拟机之间的流量,以及实现虚拟机和外界网络的通信。

  内核模块实现了多个“数据路径”(类似于网桥),每个都可以有多个“vports”(类似于桥内的端口)。每个数据路径也通过关联一下流表(flow table)来设置操作,而这些流表中的流都是用户空间在报文头和元数据的基础上映射的关键信息,一般的操作都是将数据包转发到另一个vport。当一个数据包到达一个vport,内核模块所做的处理是提取其流的关键信息并在流表中查找这些关键信息。当有一个匹配的流时它执行对应的操作。如果没有匹配,它会将数据包送到用户空间的处理队列中(作为处理的一部分,用户空间可能会设置一个流用于以后碰到相同类型的数据包可以在内核中执行操作)。

1.OpenvSwitch的组成
 ovs的主要组成模块如下图所示:
ovs-vswitchd:OVS守护进程是,OVS的核心部件,实现交换功能,和Linux内核兼容模块一起,实现基于流的交换(flow-based switching)。它和上层 controller 通信遵从 OPENFLOW 协议,它与 ovsdb-server 通信使用 OVSDB 协议,它和内核模块通过netlink通信,它支持多个独立的 datapath(网桥),它通过更改flow table 实现了绑定和VLAN等功能。

ovsdb-server:轻量级的数据库服务,主要保存了整个OVS 的配置信息,包括接口啊,交换内容,VLAN啊等等。ovs-vswitchd 会根据数据库中的配置信息工作。它于 manager 和 ovs-vswitchd 交换信息使用了OVSDB(JSON-RPC)的方式。

ovs-dpctl:一个工具,用来配置交换机内核模块,可以控制转发规则。  

ovs-vsctl:主要是获取或者更改ovs-vswitchd 的配置信息,此工具操作的时候会更新ovsdb-server 中的数据库。  

ovs-appctl:主要是向OVS 守护进程发送命令的,一般用不上。

ovsdbmonitor:GUI 工具来显示ovsdb-server 中数据信息。  

ovs-controller:一个简单的OpenFlow 控制器  

ovs-ofctl:用来控制OVS 作为OpenFlow 交换机工作时候的流表内容。

  1. OpenvSwitch的工作流程
      通信流程如下:

  1.VM实例 instance 产生一个数据包并发送至实例内的虚拟网络接口 VNIC,图中就是 instance 中的 eth0.

  2.这个数据包会传送到物理机上的VNIC接口,如图就是vnet接口.

  3.数据包从 vnet NIC 出来,到达桥(虚拟交换机) br100 上.

  4.数据包经过交换机的处理,从物理节点上的物理接口发出,如图中物理机上的 eth0 .

  5.数据包从 eth0 出去的时候,是按照物理节点上的路由以及默认网关操作的,这个时候该数 据包其实已经不受你的控制了.

  注:一般 L2 switch 连接 eth0 的这个口是一个 trunk 口, 因为虚拟机对应的 VNET 往往会设置 VLAN TAG, 可以通过对虚拟机对应的 vnet 打 VALN TAG 来控制虚拟机的网络广播域. 如果跑多个虚拟机的话, 多个虚拟机对应的 vnet 可以设置不同的 vlan tag, 那么这些虚拟机的数据包从 eth0(4)出去的时候, 会带上TAG标记. 这样也就必须是 trunk 口才行。

3.OpenvSwitch简单应用实例
通过以下命令即可实现:

ovs-vsctl add-br br0

ovs-vsctl add-port br0 eth0

ovs-vsctl add-port br0 eth1

4.Openvswitch常见操作

添加网桥:

ovs-vsctl add-br br0

列出所有网桥:

ovs-vsctl list-br

判断网桥是否存在:

ovs-vsctl br-exists br0

将物理网卡挂载到网桥上:

ovs-vsctl add-port br0 eth0

列出网桥中的所有端口:

ovs-vsctl list-ports br0

列出所有挂载到网卡的网桥:

ovs-vsctl port-to-br eth0

查看ovs的网络状态:

ovs-vsctl show

删除网桥上已经挂载的网口:

ovs-vsctl del-port br0 eth0

删除网桥:

ovs-vsctl del-br br0

设置控制器:

ovs-vsctl set-controller br0 tcp:ip:6633

删除控制器:

ovs-vsctl del-controller br0

设置支持OpenFlow Version 1.3:

ovs-vsctl set bridge br0 protocols=OpenFlow13

删除OpenFlow支持设置:

ovs-vsctl clear bridge br0 protocols

设置vlan标签:

ovs-vsctl add-port br0 vlan3 tag=3 -- set interface vlan3 type=internal

删除vlan标签:

ovs-vsctl del-port br0 vlan3

查询 VLAN:

ovs-vsctl show

ifconfig vlan3

查看网桥上所有交换机端口的状态:

ovs-ofctl dump-ports br0

查看网桥上所有的流规则:

ovs-ofctl dump-flows br0

查看ovs的版本:

ovs-ofctl -V

给端口配置tag

ovs-vsctl set port br-ex tag=101

openflow对应的代码

--OpenFlow table IDs

-Integration bridge (int_br)

LOCAL_SWITCHING = 0

Various tables for DVR use of integration bridge flows

DVR_TO_SRC_MAC = 1

DVR_TO_SRC_MAC_VLAN = 2

CANARY_TABLE = 23

Table for ARP poison/spoofing prevention rules

ARP_SPOOF_TABLE = 24

Table for MAC spoof filtering

MAC_SPOOF_TABLE = 25

Tables used for ovs firewall

BASE_EGRESS_TABLE = 71

RULES_EGRESS_TABLE = 72

ACCEPT_OR_INGRESS_TABLE = 73

BASE_INGRESS_TABLE = 81

RULES_INGRESS_TABLE = 82

OVS_FIREWALL_TABLES = (

BASE_EGRESS_TABLE,

RULES_EGRESS_TABLE,

ACCEPT_OR_INGRESS_TABLE,

BASE_INGRESS_TABLE,

RULES_INGRESS_TABLE,

)

-Tunnel bridge (tun_br)

Various tables for tunneling flows

DVR_PROCESS = 1

PATCH_LV_TO_TUN = 2

GRE_TUN_TO_LV = 3

VXLAN_TUN_TO_LV = 4

GENEVE_TUN_TO_LV = 6

DVR_NOT_LEARN = 9

LEARN_FROM_TUN = 10

UCAST_TO_TUN = 20

ARP_RESPONDER = 21

FLOOD_TO_TUN = 22

-Physical Bridges (phys_brs)

Various tables for DVR use of physical bridge flows

DVR_PROCESS_VLAN = 1

LOCAL_VLAN_TRANSLATION = 2

DVR_NOT_LEARN_VLAN = 3

二.Neutron使用openvswitch网络通信的基本原理
  Openstack在创建虚拟机进行网络配置的时候大致分为两个步骤:
  1、Nova-compute通过调度在主机侧创建虚拟机,并且创建好linux bridge,是否创建linux网桥取决于是否把安全组的功能打开,创建好bridge和veth类型的点对点端口,连接bridge设备和br-int网桥。

  2、Neutron-ovs-agent周期任务扫描到网桥上的端口发送rpc请求到neutron-server侧,获取端口的详细信息,进行网络配置,当然,不同类型的网络会进行不同的处理,OVS当前支持,vlan、vxlan、flat、gre类型的网络。

  具体虚拟机通信分为以下两种情况:

同板虚拟机通信

  在报文入口方向打上vlan,在br-int网桥上进行二层的隔离,对neutron-ovs-agent来说,这个是一个内部vlan,因此,很显然,对于这个主机使用的network来说,在主机侧neutron-ovs-agent都会维护一个内部vlan,并且是一一对应的,用于不同network的虚拟机在板上的互相隔离,由于板内虚拟机通信不经过物理网口,因此,也不会受到网口带宽和物理交换机性能的影响。

跨板虚拟机通信:

这里以vlan类型网络举例,network的segment_id为100,即vlan为100,虚拟机出来的报文在进入br-int网桥上被打上内部的vlan,举例来说打上vlan 3,下行的流量在经过对应的网络平面后,vlan会进行对应的修改,通过ovs的flow table把vlan修改成真实的vlan值100;上行流量在br-int网桥上通过flow table把vlan 100修改成内部的vlan 3,flat网络原理类似,下行流量会在br-eth通过flow table strip_vlan送出网口,vxlan类型的网络稍有不同,不过原理也是类似。

Neutron 所实现的网络虚拟化
https://superuser.openstack.org/articles/provisioning-neutron-networks-with-the-nsx-v-plugin/

https://www.cnblogs.com/sammyliu/p/4622563.html

为什么要网络虚拟化?
个人认为,这里主要有两个需求:一个是数据中心的现有网络不能满足云计算的物理需求;另一个是数据中心的现有网络不能满足云计算的软件化即SDN要求。

1.1 现有物理网络不能满足云计算的需求
(1)大容量的MAC表项和ARP表项

(2)4K VLAN Trunk问题

(3)4K VLAN上限问题

(4)虚拟机迁移网络依赖问题

1.2 云计算的 SDN 要求
数据中心(Data Center)中的物理网络是固定的、需要手工配置的、单一的、没有多租户隔离的网络。这是一个物理网络局部实例:

TOR:Top On Rack 交换机。图中的一个服务器有三块网卡,分别连到连接数据网络和管理网络的交换机。

而云架构往往是多租户架构,这意味着多个客户会共享单一的物理网络。因此,除了提供基本的网络连接能力以外,云还需要提供网络在租户之间的隔离能力;同时云是自服务的,这意味着租户可以通过云提供的 API 来使用虚拟出的网络组建来设计,构建和部署各种他们需要的网络。

物理网络和逻辑网络的映射

Neutron 项目在物理网络环境之上提供满足多租户要求的虚拟网络和服务。Neutron 提供的网络虚拟化能力包括:

(1)二层到七层网络的虚拟化:L2(virtual switch)、L3(virtual Router 和 LB)、L4-7(virtual Firewall )等

(2)网络连通性:二层网络和三层网络

(3)租户隔离性

(4)网络安全性

(4)网络扩展性

(5)REST API

(6)更高级的服务,包括 LBaaS,FWaaS,VPNaaS 等。

  1. Neutron 网络虚拟化
    在实际的数据中心中,网络可以分为三层:OpenStack Cloud network,机房intranet (external network),以及真正的外部网络即 Internet。External 网络和Internet 之间是数据中心的 Uplink 路由器,它负责通过 NAT 来进行两个网络之间的IP地址(即 floating IP 和 Internet/Public IP)转换,因此,这部分的控制不在 OpenStack 控制之下,也不在本文的主要探讨范围之内。

OpenSack Cloud network:OpenStack 所管理的网络。

External network:数据中心所管理的的公司网(Intranet) ,虚机使用的 Floating IP 是这个网络的地址的一部分。

Internet:由各大电信运营商所管理的公共网络,使用公共IP。

这是 RedHat 提供的一个 OpenStack Cloud network 网络架构:

大概地分类的话,该网络管理网络 和 数据网络,数据网络中关键的是租户网络,用于租户虚机之间的通信。这部分也是 Neutron 所实现的网络虚拟化的核心。在目前的Neutron 实现中,Neutron 向租户提供虚拟的网络(network)、子网(subnet)、端口 (port)、交换机(switch)、路由器(router)等网络组件。下图显示了虚拟网络和物理网络的映射关系:

2.1 网络(L2 network)
网络(network)是一个隔离的二层网段,类似于物理网络世界中的虚拟 LAN (VLAN)。更具体来讲,它是为创建它的租户而保留的一个广播域,或者被显式配置为共享网段。端口和子网始终被分配给某个特定的网络。这里所谓的隔离,可以理解为几个含义:

跨网络的子网之间的流量必须走 L3 Virtual Router
每个网络使用自己的 DHCP Agent,每个 DHCP Agent 在一个 Network namespace 内
不同网络内的IP地址可以重复(overlapping)
根据创建网络的用户的权限,Neutron network 可以分为:
Provider network:管理员创建的和物理网络有直接映射关系的虚拟网络。

Tenant network:租户普通用户创建的网络,物理网络对创建者透明,其配置由 Neutron根据管理员在系统中的配置决定。

虚机可以直接挂接到 provider network 或者 tenant network 上 “VMs can attach directly to both tenant and provider networks, and to networks with any provider:network_type value, assuming their tenant owns the network or its shared.”。

根据网络的类型,Neutron network 可以分为:
VLAN network(虚拟局域网) :基于物理 VLAN 网络实现的虚拟网络。共享同一个物理网络的多个 VLAN 网络是相互隔离的,甚至可以使用重叠的 IP 地址空间。每个支持 VLAN network 的物理网络可以被视为一个分离的 VLAN trunk,它使用一组独占的 VLAN ID。有效的 VLAN ID 范围是 1 到 4094。

Flat network:基于不使用 VLAN 的物理网络实现的虚拟网络。每个物理网络最多只能实现一个虚拟网络。

local network(本地网络):一个只允许在本服务器内通信的虚拟网络,不知道跨服务器的通信。主要用于单节点上测试。

GRE network (通用路由封装网络):一个使用GRE封装网络包的虚拟网络。GRE封装的数据包基于IP路由表来进行路由,因此GRE network不和具体的物理网络绑定。

VXLAN network(虚拟可扩展网络):基于VXLAN实现的虚拟网络。同GRE network一样,VXLAN network中IP包的路由也基于IP路由表,也不和具体的物理网络绑定。

注:在AWS中,该概念对应 VPC 概念。AWS 对 VPC 的数目有一定的限制,比如每个账户在每个 region 上默认最多只能创建 5 个VPC,通过特别的要求最多可以创建 100 个。

2.1.1 Provider network
Provider Network 是由 OpenStack 管理员创建的,直接对应于数据中心的已有物理网络的一个网段。这种网络有三个和物理网络有关属性:

provider:network_type (网络类型,包括 vxlan, gre, vlan, flat, local)
provider:segmentation_id (网段 ID, 比如 VLAN 的 802.1q tag, GRE 网络的 Tunnel ID, VXLAN 网络的 VNI)
provider:physical_network (物理网络的逻辑名称,比如 physnet1, ph-eth1, etc)
这种网络是可以在多个租户之间共享的。这种网络通过计算和网络节点上指定的 bridge 直接接入物理网络,所以默认的情况下它们是可以路由的,因此也不需要接入 Neutron Virtual Router,也不需要使用 L3 agent。使用这种网络,必须预先在各计算和网络节点上配置指定的网桥。

虽然可以创建 GRE 和 VXLAN 类型的 Provider network, 但是(个人认为)Provider network 只对 Flat 和 VLAN 类型的网络才有意义,因为 Provider network 的一个重要属性是 provider:physical_network,而这个参数对其他网络类型没有有意义。

创建 provider network:
l local 类型的:neutron net-create NAME --provider:network_type local

l flat 类型的:neutron net-create NAME --provider:network_type flat --provider:physical_network PHYS_NET_NAME

l vlan 类型的:neutron net-create NAME --provider:network_type vlan --provider:physical_network PHYS_NET_NAME --provider:segmentation_id VID

l gre 类型的:neutron net-create NAME --provider:network_type gre --provider:segmentation_id TUNNEL_ID

l vxlan 类型的:neutron net-create NAME --provider:network_type vxlan --provider:segmentation_id TUNNEL_ID

2.1.3 Tenant network
Tenant network 是由 tenant 的普通用户创建的网络。默认情况下,这类用户不能创建共享的 tenant network(因此 Nuetron Server 的policy 设置了"create_network:shared": "rule:admin_only"。),因此这种网络是完全隔离的,也不可以被别的 tenant 共享。

Tenant network 也有 local,flat,vlan,gre 和 vxlan 等类型。但是,tenant 普通用户创建的 Flat 和 VLAN tenant network 实际上还是 Provider network,所以真正有意义的是 GRE 和 VXLAN 类型,这种网络和物理网络没有绑定关系。

创建 tenant network 的过程:
(1) 管理员在 neutron 配置文件中配置 tenant_network_types,其值可以设为一个所支持的网络类型列表,比如 “vlan,gre,vxlan”。其默认值为 “local“,因此需要改变。该值表明该 OpenStack 云中允许被创建的 tenant network 类型。

(2) 运行命令 neutron net-create <net_name>

(3) neutron server 逐一根据该配置项尝试创建 network segment,成功则立即返回。

2.1.4 Provider network 和 Tenant network 的区别

几个区别:

Provider network 是由 Admin 用户创建的,而 Tenant network 是由 tenant 普通用户创建的。

Provider network 和物理网络的某段直接映射,比如对应某个 VLAN,因此需要预先在物理网络中做相应的配置。而 tenant network 是虚拟化的网络,Neutron 需要负责其路由等三层功能。

对 Flat 和 VLAN 类型的网络来说,只有 Provider network 才有意义。即使是这种类型的 tenant network,其本质上也是对应于一个实际的物理段。

对 GRE 和 VXLAN 类型的网络来说,只有 tenant network 才有意义,因为它本身不依赖于具体的物理网络,只是需要物理网络提供 IP 和 组播即可。

Provider network 根据 admin 用户输入的物理网络参数创建;而 tenant work 由 tenant 普通用户创建,Neutron 根据其网络配置来选择具体的配置,包括网络类型,物理网络和 segmentation_id。

创建 Provider network 时允许使用不在配置项范围内的 segmentation_id。

2.1.5 多网段 Provider network (multi-segment provider network)
默认情况下,用户只能创建单网段 provider network。这个 Blueprint 使得 Neutron 能够支持创建多网段网络。这种网络由相同或者不同网络类型的多个网段(network segments)组成一个广播域。一个 Port 只能在一个 segment 中分配。

2.1.6 Mirantis 描述的网络
概念扫盲:Mirantis OpenStack

https://blog.csdn.net/lynn_kong/article/details/16866687

Mirantis 将逻辑网络分为以下三大类:
public network (公共网络)
虚机通过pulic network 访问 internet。它分配外部的IP地址给网络节点,以及在使用DVR时分配给计算节点,然后虚机通过SNAT来访问外网。

它也向 public endpoints 提供虚拟IP,用于外网访问 openstack service api。

管理员制定一个相邻的地址区间给浮动IP使用。

需要将 public network 与其它网络隔离。

internal network (内部网络)tunnel的那张网卡呢?
internal network 是出了 public network 和 provate network 的其他网络的统称,包括存储,管理,PXE 网络等。

内部网络 - PXE 网络,用于环境部署

存储网络 - 用于存储访问

管理网络 - 包括数据库,MQ,HA 服务,以及计算和存储节点之间的 iSCSI 网络。

内部网络将各节点连接起来。openstack 环境中的各个模块之间的交互通过内部网络进行。

不要将 internal network 和 private network 混淆,private network 只用于虚机之间的通信。

出于安全考虑,需要将内部网络从 public 和 private network 隔离出来。

Private network (私有网络)
用于虚机之间的通信,包括 VLAN, GRE/VXLAN 等类型的虚拟网络都会运行在私有网络之上。

当 Neutron使用 VLAN 模式时候的网卡分配方案:

三网卡方案:

eth0 - PXE 网络
eth1 - 公共网络(untagged),管理网络(tag=102),存储网络(tag=103)
eth2 - 私有网络

四网卡方案

eth0 - PXE 网络
eth1 - 公共网络(untagged),管理网络(tag=102)
eth2 - 私有网络
eth3 - 存储网络

2.2 子网 (subnet)
子网是一组 IPv4 或 IPv6 地址以及与其有关联的配置。它是一个地址池,OpenStack 可从中向虚拟机 (VM) 分配 IP 地址。每个子网指定为一个无类别域间路由 (Classless Inter-Domain Routing) 范围,必须与一个网络相关联。除了子网之外,租户还可以指定一个网关、一个域名系统 (DNS) 名称服务器列表,以及一组主机路由。这个子网上的 VM 实例随后会自动继承该配置。

2.3 端口 (Port)
一个 Port 代表虚拟网络交换机(logical network switch)上的一个虚机交换端口(virtual switch port)。虚机的网卡(VIF - Virtual Interface)会被连接到 port 上。当虚机的 VIF 连接到 Port 后,这个 vNIC 就会拥有 MAC 地址和 IP 地址。Port 的 IP 地址是从 subnet 中分配的。

2.4 虚机交换机 (Virtual switch)
Neutron 默认采用开源的 Open vSwitch 作为其虚机交换机,同时还支持使用 Linux bridge。

2.5 虚拟路由器 (Virtual router)
一个 Virtual router 提供不同网段之间的 IP 包路由功能,由 Nuetron L3 agent 负责管理。

2.6 各组件之间的关系
OpenStack 实际上并未增加网络功能。路由、交换和名称解析是由底层的网络基础架构处理的。OpenStack 的作用是将这些组件的管理捆绑在一起,并将它们连接到计算工作负载。

  1. Neutron中的网络连通性
    如上面所述,一个标准 OpenStack 环境中的物理网络配置往往包括:

Internet(Pulic network):传统意义上的公共网络,使用往往由电信运营商提供的公共IP。

外部网络(External network):数据中心 Intranet,从这里分配浮动IP地址。

OpenStack 内部网络:

管理网络(management network):提供 OpenStack 各个组件之间的内部通信,以及 API 访问端点(Endpoint)。为安全考虑,该网络必须限制在数据中心之内。

API 网络:其实这不是一个单独的网络,而是包含在外部和内部网络中。API 的 Endpoint 包括 publicurl 和 internalurl,其中,publicurl 包含的是 externa network 的 IP 地址,internal network 包含的是 management network IP 地址。为了简单起见,提供给内外网络访问的API的 publicurl 和 internalurl 相同,而只给内部网络访问的 API 只使用 internalurl。

数据网络(data network):除管理网络以外的其它网络,往往还可以细分为下面几种。它们可以合为一种,也可以从性能方面考虑分离出一种或几种作为单独的网络。

租户网络(Tenant network):提供虚机在计算节点之间,以及计算节点和网络节点之间的通信。同样这也是数据中心的内部网络。

存储访问网络(storage access network):访问存储的网络。

存储后端网络(storage backend network):比如 Ceph 和 Swift 集群用于后端数据复制的网络。

除了以上网络外,往往还有各种功能网络,包括 IPMI 网络,PXE 网络,监控网络等等。这部分就略去不谈了。

这几种网络,在物理交换机上,往往都使用 VLAN 来做网络隔离。现在讨论的只是租户网络即虚机之间通信的网络,在 Neutron 的实现看来,该网络的连通性包括几个层次:

同主机和不同主机上一个网段内的虚机之间的连接性:虚拟二层网络,走物理二层(VLAN)或者三层(GRE/VxLAN)网络。
不同网段内的虚机之间的连通性:经过物理(VLAN)或者 Neutron Virtual router
虚机和外部网络之间的连通性:经过物理路由器(给 VLAN 虚拟网络实用的物理交换机连接的路由器)或者 Neutron Virtual router
为了支持这些网络连通性,Neutron 需要实现跨主机的二层网络和虚拟路由器。可以参考 学习OpenStack之(5):在Mac上部署Juno版本OpenStack 四节点环境。

3.1 虚拟二层网络的实现
所谓虚拟二层网络,就是提供给虚机的一种虚拟网络,使得虚机可以和同网段中的其它虚机就像在物理二层网络一样在网络二层直接通信,不管目的虚机处于什么物理位置。也就是说,对虚机来说,物理三层网络对它是透明的。

3.1.1 使用 VLAN 实现虚拟二层网络

3.1.2 基于 GRE/VxLAN 实现的二层网络 (L2)
除了 VLAN 方式的物理的二层网络,另一种方式使用Tunnel/Overlay 方式实现虚机二层网络。那Overlay如何应对云计算网络的挑战呢?

(1)首先,Overlay的核心是重新构建一个逻辑网络平面,其技术手段的关键是采用隧道技术实现L2oIP的封装。通过隧道实现各虚拟机之间的二层直连。这样网络只看见Overlay边缘节点的MAC地址,物理网络学习到的MAC表项非常有限,现有接入交换机32K的MAC表项足以满足需求(如下图所示)。对应的Overlay边缘节点实现基于会话的地址学习机制,也就是说只学习有交互流量的虚拟机MAC地址。这样也严格限制了边缘节点的地址表项。

Overlay网络如何应对云计算网络的挑战

(2)其次,Overlay网络仅仅是一个逻辑上的二层直连网络。其依赖的物理网络,是一个传统的路由网络。这个路由网络是一个三层到边缘的网络。也就是说二层广播域被缩小到极致。这样,网络风暴潜在的风险大幅度降低。同时,对于一些需要依赖二层广播的协议报文,例如:ARP报文,Overlay网络通过ARP代理等方式实现协议的内容透传,不会影响协议报文的正常运作。

(3)再次,针对4K VLAN上限问题,Overlay网络通过L2oIP的封装字段,提供24bits长度的隔离ID,最大可以支持16M租户或业务。

(4)最后,针对网络虚拟化问题。Overlay网络本身就是一个从物理网络中抽离的逻辑网络,通过名址分离使得内层IP地址完全作为一个寻址标签,不再具备路由功能,可以实现不同subnet之间二层互通,保证二层网络的连通性不受IP地址段的限制。

(资料来源)

在 Neutron 中,Neutron 将虚机发出的数据帧打包,走三层物理网络到达目的虚机的主机,解包给虚机。这种实现使得两个虚机的通信看起来是二层网络通信。

3.2 虚拟路由器 (virtual router)
跨子网的通信需要走虚拟路由器。同物理路由器一样,虚拟路由器由租户创建,拥有多个 virtual interface,连接一个租户的子网,以及外部网络。它具有以下特性:

一个 VR 只属于创建它的租户,只用于该租户的子网之间和子网与外网的路由
同一网络内的若干子网可以挂在一个 VR 上
同一租户的不同网络的没有 IP 地址重叠的子网可以挂在一个 VR 上
不同租户之间的内网之间是不能使用 VR 的
同一租户的不同网络内的有 IP 地址重叠的两个子网不能使用同一个 VR(添加子网到 VR 时会报错)
在网络节点上,一个 VR 运行在一个 Network namespace 内,该namespace 的名称包含该 VR 的 UUID
3.3 DHCP 服务
DHCP 服务是网络环境中必须有的。Neutron 提供基于 Dnamasq 实现的虚机 DHCP 服务,向租户网络内的虚机动态分配固定 IP 地址。它具有以下特性:

一个网络可以有多个运行在不同物理网络节点上的 DHCP Agent,同时向网络内的虚机提供服务
一个 DHCP Agent 只属于一个网络,在网络节点上运行在一个 network namespace 内
网络内的子网共享该 DHCP Agent
4. Neutron 租户网络的隔离性(isolation of tenant network)
Neutron 实现了不同层次的租户网络隔离性:

租户之间的网络是三层隔离的,连通过 VR 做路由都不行,实在要连通的话,需要走物理网络
一个租户内的不同网络之间二层隔离的,需要通过 VR 做三层连通
一个网络内的不同子网也是二层隔离的,需要通过 VR 做三层连通
Neutron 对每个租户网络(tenant network)都分配一个 segmentation_id,其特点包括:

每个 tenant network 都有一个这种 ID
每个租户网络的 ID 在全部的租户范围内都是唯一的
一个 ID 代表一个广播域
一个 ID 使得同一网络内的两个虚机之间好像建立了一个虚拟通道(tunnel)一样
不同 ID 的网络 tunnel 之间是互相隔离的
根据物理实现不同,该ID被实现为几种不同的形式:
VLAN ID
GRE Tunnel ID
VxLAN VNI
(1)计算节点的 br-int 上,neutron 为每个虚机连接 OVS 的 access port 分配了内部的 VLAN Tag。这种 tag 限制了网络流量只能在 tenant network 之内。
(2)计算节点的 br-tun 上,neutron 将内部的 VLAN Tag 转化为 GRE Tunnel ID,是的不同 network 的流量走不通的 tunnel。
(3)网络节点的 br-tun 上,neutron 将 GRE Tunnel ID 转发了一一对应的 内部 VLAN Tag,使得网络流被不同的服务处理。
(4)网络节点的 br-int 上连接的 DHCP 和 L3 agent 使用 Linux network namespace 进行隔离。
Neutron 租户网络的安全性(security)
除了租户的隔离性以外,

Neutron 还提供数据网络与外部网络的隔离性。默认情况下,所有虚机通往外网的流量全部走网络节点上的 L3 agent。在这里,内部的固定 IP 被转化为外部的浮动 IP 地址。这种做法一方面保证了网络包能够回来,另一方面也隐藏了内部的 IP 地址。
Neutron 还是用 Linux iptables 特性,实现其 Security Group 特性,从而保证访问虚机的安全性。
Neutron利用网络控制节点上的 network namespace 中的 iptables,实现了进出租户网络的网络包防火墙,从而保证了进出租户网络的安全性。
Neutron 租户网络的可用性(HA)和扩展性(Scalability)
OpenStack 云中可能用于成千上万台虚机,成千上万个租户,因此,Neutron 的数据网络的可用性和扩展性非常重要。Neutron 中,这些特性包括几个层次:

(1)软件架构上,Neutron 实现OpenStack 标准的去中心化架构和插件机制,有效地保证了其扩展性。

(2)Neutron 为每个 provider/tenant network 分配一个唯一的 segmentation_id。该 ID 的数目是影响可扩展性的因素之一。在规模不大的私有云中,往往是用 VLAN 模式,简单、可靠、能够满足规模要求;而在大型的私有云或者公有云中,往往使用 VxLAN。

网络类型

ID 数目

说明

flat

1

通常不用于数据网络,因为其不具备租户隔离型。

vlan

4094

802.1Q 定义了 VLAN ID 占 12-bit

GRE

16777216

RFC 2637 定义的 GRE 的 ID 占 24-bit

VxLAN

16777216

RFC 7348 定义的 VxLAN 的 ID 占 24-bit

(3)分布式 Virtual Router (DVR) 和 分布式 DHCP agent

默认情况下,L3 agent 和 DHCP agent 部署在网络节点上,这在大规模的云环境中可能会存在性能问题。这是 Blueprint。

|

(左图-图15,未使用DVR。右图-图16,使用了DVR-分布式虚拟路由器。来源。)

通过使用 DVR,L3 转发和 NAT 会被分布在计算节点上,这使得计算节点变成了网络节点,这样集中式的网络节点的负载就被分担了。这个 Blueprint 实现了分布式的 DHCP Agent,这进一步释放了集中式网络节点的压力。

(4)L2 Population 和 ARP Responder:这两个功能大大减少了网络的复杂性,提交了网络效率,从而促进了扩展性。

(4)高可用:虽然 Neutron 在高可用实现方面走在了 OpenStack 所有组件的前列,目前已经实现了多种 L3 Agent 的 HA 方案,但是,这些方案目前还不是很成熟,大都是在 Juno 版本中添加的,而且现在还在持续优化中,离正式进入生产环境还有一些距离。但是,我们可以相信,再经过两三个版本,这些功能就能够进入生产环境。

请参考这个文章:

(4)Neutron OVS OpenFlow 流表 和 L2 Population

(11)Neutron DVR

(12)Neutron VRRP

(13)High Availability (HA)

  1. Neutron 扩展服务(extension service)
    在实际的网络中,除了网络的核心功能以外,还有一些普遍应用的网络服务,比如 VPN, Load Balancing 和 Firewall 等。Neutron 针对这些普遍的服务,也提供了参考实现。需要注意的是,目前这些实现更多类似于一种原型实现(PoC),其目的是向给产品实现者提供一种参考架构,因此,这些功能往往还不能直接应用到生产系统。正是考虑到这些特点,目前 Neutron 项目组将这些代码挪出了 Neutron的主代码库。

具体请参考:

(7)Neutron LBaas

(9)Neutron FWaas 和 Nova Security Group

(10)Neutron VPNaas

  1. Neutron REST API
    Neutron 的 neutron-server 模块提供 REST API 来操作上文提到的各种网络组件,包括 network,subnet,port,router 等。可参考上图13。具体的 API 可以参考 这里和这里。

  2. Neutron 的实现框架
    Neutron 中包含了大量内容。那Neutron 是如何实现这么多概念的呢?

9.1 服务分类
neutron 采用『分而治之』策略,同时采取插件式框架。首先Neutron把服务分类。

它把所提供的服务分为两大类:

第一类是核心服务,通过 core plugin 实现。在 /etc/neutron/neutron.conf 中的 core_plugin 配置项中指定。从这个配置项名称看,一个 Neutron 环境应该只能支持一个核心服务。现在主要支持的核心服务见下面列表。从列表可以看出来,除了 ML2 是Neutron 社区实现的,其它都是各个厂家自己实现的。因此,neutron 的默认 core plugin 就这是 ml2。ml2 是 neutron的核心插件,所有neutron内置API请求都会被它处理。它主要实现了最核心的 network、subnet、port等概念,实现了这几种资源的操作API。

Short name

Class name

bigswitch

neutron.plugins.bigswitch.plugin:NeutronRestProxyV2

brocade

neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2

cisco

neutron.plugins.cisco.network_plugin:PluginV2

embrane

neutron.plugins.embrane.plugins.embrane_ovs_plugin:EmbraneOvsPlugin

hyperv

neutron.plugins.hyperv.hyperv_neutron_plugin:HyperVNeutronPlugin

midonet

neutron.plugins.midonet.plugin:MidonetPluginV2

ml2

neutron.plugins.ml2.plugin:Ml2Plugin

mlnx

neutron.plugins.mlnx.mlnx_plugin:MellanoxEswitchPlugin

nec

neutron.plugins.nec.nec_plugin:NECPluginV2

nicira

neutron.plugins.nicira.NeutronPlugin:NvpPluginV2

plumgrid

neutron.plugins.plumgrid.plumgrid_plugin.plumgrid_plugin:NeutronPluginPLUMgridV2

ryu

neutron.plugins.ryu.ryu_neutron_plugin:RyuNeutronPluginV2

第二类是非核心服务。这类服务通过 service plugin 实现。 service plugin 主要是用来支持除 core plugin 之外的其他资源的操作API的。neutron核心资源上面已经讲过,就包含很少的几个(network、subnet、port等),其他资源如 qos、router、floatingips、fwaas、lbaas、vpnaas 等的操作API都需要通过相应的plugin来支持,有部分老版本的plugin的实现是在neutron项目中(services目录下),而新版本的资源一般都是在各自独立的项目中,如neutron_lbaas、neutron_fwaas、neutron_vpnaas,减少与neutron主项目的耦合,便于各自独立快速的发展更新。当前支持的主要 service plugin 包括:

Short name

Class name

dummy

neutron.tests.unit.dummy_plugin:DummyServicePlugin

router

neutron.services.l3_router.l3_router_plugin:L3RouterPlugin

firewall

neutron.services.firewall.fwaas_plugin:FirewallPlugin

lbaas

neutron.services.loadbalancer.plugin:LoadBalancerPlugin

metering

neutron.services.metering.metering_plugin:MeteringPlugin

vpnaas

neutron_vpnaas.services.vpn.plugin:VPNDriverPlugin

9.2 ML2
因为 ML2 是 Neutron 的核心,因此要重点讲一下。它的配置文件是 /etc/neutron/plugins/ml2/ml2_conf.ini。它主要实现了两种驱动:

类型驱动:Neutron 支持的每一种网络类型都有实现一个类型驱动。包括:
Flat:这种类型只能作为 Provider network type
VLAN
GRE
VXLAN
机制驱动: 机制驱动是实现某个网络类型所采用的机制即网络组件。主要包括:
Arista
Cisco
Nexus
Hyper-V Agent
L2 Population
Linuxbridge
Open vSwitch
OpenDaylight
OpenContrail
各厂家自己的
因此,可以简单认为,ML2 的主要工作就是实现这几种网络组件支持的网络。要注意,类型驱动和机制驱动不是完全对应关系。下面是它们之间的对照表:

备注:L2 population 是一种特殊的机制驱动,它旨在优化 VXLAN 和 GRE 环境中的 BUM(广播,未知目的地址,多播)网络包。它必须和 linux bridge 或者 OVS 一起工作,不能单独使用。

9.3 Agent 代理
前面讲的是neutorn 要实现的目标,也就是要提供的网络服务。对所有的服务,都需要在控制、网络、计算等节点上执行相应的操作,比如创建网桥、创建命名空间、创建端口、创建 iptables 规则等。这些操作是通过称为 Agent 的多种组件来完成的。它们要么接收别人发过来的指令,要么自己通过扫描等行为,来触发具体行动,完成所需执行的内容。

Agent 也分为两大类:

L2 agent:对应于核心服务。这是完成 ML2 所需操作的agent。基本上每种机制驱动都有对应一个 L2 agent。每个agent 通常会支持一种机制驱动,但也可能支持多种。L2 Agent 运行在网络和计算节点上。

L2 agent 名称

配置文件

安装包名称

作用

所支持的机制驱动

Open vSwitch agent

openvswitch_agent.ini

neutron-openvswitch-agent

通过配置 Open vSwitch 来实现 L2 网络

Open vSwitch;

L2 population

Linux bridge agent

linuxbridge_agent.ini

neutron-linuxbridge-agent

通过配置 Linux bridge 来实现 L2 网络

Linux bridge;L2 population

SRIOV Nic Switch agent

sriov_agent.ini

neutron-sriov-nic-agent

通过配置 PCI virtual function 来实现 L2 网络

SRIOV

MacVTap agent

macvtap_agent.ini

neutron-macvtap-agent

通过配置 MacVTap 设备来实现 L2 网络

MacVTap

其它驱动:对应于非核心服务。这些驱动运行在网络节点上。

Agent名称

安装包

配置文件

支持的servie plugin

配置参考页面

L3 Agent

neutron-l3-agent

/etc/neutron/l3_agent.ini

router

https://docs.openstack.org/neutron/pike/configuration/l3-agent.html​

firewall

Metadata agent

neutron-metadata-agent

/etc/neutron/metadata_agent.ini

https://docs.openstack.org/kilo/config-reference/content/networking-options-metadata.html

DHCP Agent

neutron-dhcp-agent

/etc/neutron/dhcp_agent.ini

https://docs.openstack.org/neutron/pike/configuration/dhcp-agent.html

LBaaS Agent

neutron-lbaas-agent

/etc/neutron/lbaas_agent.ini

lbaas

https://docs.openstack.org/mitaka/config-reference/networking/lbaas.html

VPN agent

neutron-vpn-agent

/etc/neutron/vpn_agent.ini

vpnaas

https://docs.openstack.org/newton/config-reference/networking/samples/vpn_agent.ini.html

L3 metering agent

neutron-metering-agent

/etc/neutron/metering_agent.ini

metering

https://docs.openstack.org/neutron/pike/admin/archives/adv-config.html

看下组件部署示意图:

可以使用 neutron 命令 agent-list 来查看所有 agent:

9.4 neutron 之内以及与其它组件间通信
openstack 的核心是虚拟机,neutron,nova 以及其它模块都是为虚拟服务的。在虚机启动时,nova 和 neutron 需要紧密配合。简单的nova 和 neutron交互示意图如下:

这里面的通信机制包括直接API调用(nova 调用 neutron api),网络对象扫描(L2 agent 在计算节点上持续扫描nova 创建的 tap 设备,然后配置 br-int 和 br-tun、安全组规则、和neutorn server 交互等)。关于虚机创建过程,请参阅 OpenStack 创建虚机过程简要汇总。

9.4.1 neutron server 通知 L2 和 L3 agent
L2 agent:主要包括安全组规则更新和port update。

L3 agent:主要是 router 更新。

9.4.2 l2 agent 主动扫描 tap 设备
当扫描到有添加的端口时,

当扫描到有端口删除时,

9.4.3 跨组件通知 notification
另外一个组件间通信机制是 notification,即通过RPC发送通知。neutron_server.ini 中能配置的几种 notification 包括:

notify_nova_on_port_data_changes:当neutron 将绑定到一个虚机的浮动IP在不释放的情况下关联到一个新虚机时,需要给nova 发送原来关联的虚机的信息,以使它能修改数据库。代码在这里。
notify_nova_on_port_status_changes:默认值为 true。当 neutron port 状态有变化,在neutron 做数据库修改之前,向 nova 发送通知。代码在这里。
dhcp_agent_notification:默认值为 true。表示允许发送资源操作通知给 dhcp agent。其发送时机包括:
network.create.end
network.update.end
network.delete.end
subnet.create.end
subnet.update.end
subnet.delete.end
port.create.end
port.update.end
port.delete.end
9.5 配置Neutron的大体步骤
确定需要哪些服务,要考虑核心服务以及非核心服务。

确定所要支持的L2网络类型(包括提供者网络和租户网络)(比如是 VLAN 还是 VXLAN),要支持一种还是多种(比如要不要同时支持 VLAN 和 VXLAN);以及实现网络类型的机制(主要是要采用 Open vSwitch 还是 linux bridge)。其实也就是确定 ML2 的类型驱动和机制驱动。

对于非 L2 服务,要确定采用哪种实现方式。比如利用网络命名空间实现 routing,利用 OpenSwan 实现 VPNaaS,利用 HAProxy 或者 Octavia 实现 LBaaS,利用 IPtables 实现 FWaaS。

根据服务所要求的agent 或者软件包,安装并进行配置。比如 VPN 服务可能需要安装 OpenSwan。

部分服务可能还需要做物理网络配置,比如要支持 VLAN 类型的网络。

================================================

ML2
因为 ML2 是 Neutron 的核心,因此要重点讲一下。它的配置文件是 /etc/neutron/plugins/ml2/ml2_conf.ini。它主要实现了两种驱动:

类型驱动:Neutron 支持的每一种网络类型都有实现一个类型驱动。包括:
Flat:这种类型只能作为 Provider network type

VLAN

GRE

VXLAN

机制驱动: 机制驱动是实现某个网络类型所采用的机制即网络组件。主要包括:
Arista

Cisco

Nexus

Hyper-V Agent

L2 Population

Linuxbridge

Open vSwitch

OpenDaylight

OpenContrail

各厂家自己的

ML2 的主要工作就是实现这几种网络组件支持的网络。要注意,类型驱动和机制驱动不是完全对应关系。下面是它们之间的对照表:

备注:L2 population 是一种特殊的机制驱动,它旨在优化 VXLAN 和 GRE 环境中的 BUM(广播,未知目的地址,多播)网络包。它必须和 linux bridge 或者 OVS 一起工作,不能单独使用。

Agent 代理
前面讲的是neutorn 要实现的目标,也就是要提供的网络服务。对所有的服务,都需要在控制、网络、计算等节点上执行相应的操作,比如创建网桥、创建命名空间、创建端口、创建 iptables 规则等。这些操作是通过称为 Agent 的多种组件来完成的。它们要么接收别人发过来的指令,要么自己通过扫描等行为,来触发具体行动,完成所需执行的内容。

Agent 也分为两大类:

L2 agent:对应于核心服务。这是完成 ML2 所需操作的agent。基本上每种机制驱动都有对应一个 L2 agent。每个agent 通常会支持一种机制驱动,但也可能支持多种。L2 Agent 运行在网络和计算节点上。

L2 agent 名称

配置文件

安装包名称

作用

所支持的机制驱动

Open vSwitch agent

openvswitch_agent.ini

neutron-openvswitch-agent

通过配置 Open vSwitch 来实现 L2 网络

Open vSwitch;

L2 population

Linux bridge agent

linuxbridge_agent.ini

neutron-linuxbridge-agent

通过配置 Linux bridge 来实现 L2 网络

Linux bridge;L2 population

SRIOV Nic Switch agent

sriov_agent.ini

neutron-sriov-nic-agent

通过配置 PCI virtual function 来实现 L2 网络

SRIOV

MacVTap agent

macvtap_agent.ini

neutron-macvtap-agent

通过配置 MacVTap 设备来实现 L2 网络

MacVTap

其它驱动:对应于非核心服务。这些驱动运行在网络节点上。

Agent名称

安装包

配置文件

支持的servie plugin

配置参考页面

L3 Agent

neutron-l3-agent

/etc/neutron/l3_agent.ini

router

https://docs.openstack.org/neutron/pike/configuration/l3-agent.html​

firewall

Metadata agent

neutron-metadata-agent

/etc/neutron/metadata_agent.ini

https://docs.openstack.org/kilo/config-reference/content/networking-options-metadata.html

DHCP Agent

neutron-dhcp-agent

/etc/neutron/dhcp_agent.ini

https://docs.openstack.org/neutron/pike/configuration/dhcp-agent.html

LBaaS Agent

neutron-lbaas-agent

/etc/neutron/lbaas_agent.ini

lbaas

https://docs.openstack.org/mitaka/config-reference/networking/lbaas.html

VPN agent

neutron-vpn-agent

/etc/neutron/vpn_agent.ini

vpnaas

https://docs.openstack.org/newton/config-reference/networking/samples/vpn_agent.ini.html

L3 metering agent

neutron-metering-agent

/etc/neutron/metering_agent.ini

metering

https://docs.openstack.org/neutron/pike/admin/archives/adv-config.html

看下组件部署示意图:

可以使用 neutron 命令 agent-list 来查看所有 agent:

neutron 之内以及与其它组件间通信
openstack 的核心是虚拟机,neutron,nova 以及其它模块都是为虚拟服务的。在虚机启动时,nova 和 neutron 需要紧密配合。简单的nova 和 neutron交互示意图如下:

这里面的通信机制包括直接API调用(nova 调用 neutron api),网络对象扫描(L2 agent 在计算节点上持续扫描nova 创建的 tap 设备,然后配置 br-int 和 br-tun、安全组规则、和neutorn server 交互等)。关于虚机创建过程,请参阅 OpenStack 创建虚机过程简要汇总。

9.4.1 neutron server 通知 L2 和 L3 agent
L2 agent:主要包括安全组规则更新和port update。

L3 agent:主要是 router 更新。

9.4.2 l2 agent 主动扫描 tap 设备
当扫描到有添加的端口时,

当扫描到有端口删除时,

9.4.3 跨组件通知 notification
另外一个组件间通信机制是 notification,即通过RPC发送通知。neutron_server.ini 中能配置的几种 notification 包括:

notify_nova_on_port_data_changes:当neutron 将绑定到一个虚机的浮动IP在不释放的情况下关联到一个新虚机时,需要给nova 发送原来关联的虚机的信息,以使它能修改数据库。代码在这里。
notify_nova_on_port_status_changes:默认值为 true。当 neutron port 状态有变化,在neutron 做数据库修改之前,向 nova 发送通知。代码在这里。
dhcp_agent_notification:默认值为 true。表示允许发送资源操作通知给 dhcp agent。其发送时机包括:
network.create.end
network.update.end
network.delete.end
subnet.create.end
subnet.update.end
subnet.delete.end
port.create.end
port.update.end
port.delete.end
ARP Responder
arp_responder 的原理不复杂。Neutorn DB 中保存了所有的端口的 MAC 和 IP 地址数据。而 ARP 就是一个虚机要根据另一个虚机的 IP 地址查询它的 MAC。因此,只需要 Neutron server 通过 RPC 告诉每个计算节点上的 ML2 agent 所有活动端口的 MAC 和 IP,那么就可以将 br-tun 变成一个供本机适用的 ARP Proxy,这样本机上的虚机的 ARP 请求的响应就可以由 br-tun 在本地解决。Assaf Meller 有篇文章来阐述 ARP Responder。

使用 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--网络实现--GRE 模式
问题导读
1.什么是VETH、qvb、qvo?
2.qbr的存在的作用是什么?
3.router服务的作用是什么?
如果不具有Linux网络基础,比如防火墙如何过滤ip、端口或则对openstack ovs了解不多,下面比较难懂一些。

概述
Neutron 的设计目标是实现“网络即服务”,为了达到这一目标,在设计上遵循了基于“软件定义网络”实现网络虚拟化的原则,在实现上充分利用了 Linux 系统上的各种网络相关的技术。

理解了 Linux 系统上的这些概念将有利于快速理解 Neutron 的原理和实现。

涉及的 Linux 网络技术

bridge:网桥,Linux中用于表示一个能连接不同网络设备的虚拟设备,linux中传统实现的网桥类似一个hub设备,而ovs管理的网桥一般类似交换机。
br-int:bridge-integration,综合网桥,常用于表示实现主要内部网络功能的网桥。
br-ex:bridge-external,外部网桥,通常表示负责跟外部网络通信的网桥。
GRE:General Routing Encapsulation,一种通过封装来实现隧道的方式。在openstack中一般是基于L3的gre,即original pkt/GRE/IP/Ethernet
VETH:虚拟ethernet接口,通常以pair的方式出现,一端发出的网包,会被另一端接收,可以形成两个网桥之间的通道。
qvb:neutron veth, Linux Bridge-side
qvo:neutron veth, OVS-side
TAP设备:模拟一个二层的网络设备,可以接受和发送二层网包。
TUN设备:模拟一个三层的网络设备,可以接受和发送三层网包。
iptables:Linux 上常见的实现安全策略的防火墙软件。
Vlan:虚拟 Lan,同一个物理 Lan 下用标签实现隔离,可用标号为1-4094。
VXLAN:一套利用 UDP 协议作为底层传输协议的 Overlay 实现。一般认为作为 VLan 技术的延伸或替代者。
namespace:用来实现隔离的一套机制,不同 namespace 中的资源之间彼此不可见。
基本概念

Neutron管理下面的实体:

网络:隔离的 L2 域,可以是虚拟、逻辑或交换,同一个网络中的主机彼此 L2 可见。
子网:隔离的 L3 域,IP 地址块。其中每个机器有一个 IP,同一个子网的主机彼此 L3 可见。
端口:网络上虚拟、逻辑或交换端口。 所有这些实体都是虚拟的,拥有自动生成的唯一标示id,支持CRUD功能,并在数据库中跟踪记录状态。

网络

隔离的 L2 广播域,一般是创建它的用户所有。用户可以拥有多个网络。网络是最基础的,子网和端口都需要关联到网络上。

网络上可以有多个子网。同一个网络上的主机一般可以通过交换机或路由器连通起来。

子网

隔离的 L3 域,子网代表了一组分配了 IP 的虚拟机。每个子网必须有一个 CIDR 和关联到一个网络。IP 可以从 CIDR 或者用户指定池中选取。

子网可能会有一个网关、一组 DNS 和主机路由。不同子网之间 L2 是互相不可见的,必须通过一个三层网关(即路由器)经过 L3 上进行通信。

端口

可以进出流量的接口,往往绑定上若干 MAC 地址和 IP 地址,以进行寻址。一般为虚拟交换机上的虚拟接口。

虚拟机挂载网卡到端口上,通过端口访问网络。当端口有 IP 的时候,意味着它属于某个子网。

抽象系统架构

无论哪种具体的网络虚拟化实现,在启用 DVR 特性(J 版本以后支持)之前,所有流量(东西向、南北向)都需要经过网络节点的转发;DVR 特性则允许东西向流量和带有 Floating IP 的南北向流量不经过网络节点的转发,直接从计算节点的外部网络出去。

GRE 模式

下图给出了在OpenStack中网络实现的一个简化的架构示意。

一般的,OpenStack中网络实现包括vlan、gre、vxlan 等模式,此处以gre模式为例。

在OpenStack中,所有网络有关的逻辑管理均在Network节点中实现,例如DNS、DHCP以及路由等。Compute节点上只需要对所部属的虚拟机提供基本的网络功能支持,包括隔离不同租户的虚拟机和进行一些基本的安全策略管理(即security group)。

计算节点

Compute节点上包括两台虚拟机VM1和VM2,分别经过一个网桥(如qbr-XXX)连接到 br-int 网桥上。br-int 网桥再经过 br-tun 网桥(物理网络是 GRE 实现)连接到物理主机外部网络。

对于物理网络通过vlan来隔离的情况,则一般会存在一个br-eth网桥,替代 br-tun 网桥。

qbr

在VM1中,虚拟机的网卡实际上连接到了物理机的一个TAP设备(即A,常见名称如tap-XXX)上,A则进一步通过VETH pair(A-B)连接到网桥qbr-XXX的端口vnet0(端口B)上,之后再通过VETH pair(C-D)连到br-int网桥上。一般C的名字格式为qvb-XXX,而D的名字格式为qvo-XXX。注意它们的名称除了前缀外,后面的id都是一样的,表示位于同一个虚拟机网络到物理机网络的连接上。

之所以TAP设备A没有直接连接到网桥br-int上,是因为OpenStack需要通过iptables实现security group的安全策略功能。目前openvswitch并不支持应用iptables规则的Tap设备。

因为qbr的存在主要是为了辅助iptables来实现security group功能,有时候也被称为firewall bridge。详见security group部分的分析【后面篇章会给出】。

br-int

一个典型的br-int的端口如下所示:

[Shell] 纯文本查看 复制代码

01

ovs-vsctl show

02

Bridge br-int

03

Port "qvo-XXX"

04

    tag: 1

05

    Interface "qvo-XXX"

06

Port patch-tun

07

    Interface patch-tun

08

        type: patch

09

        options: {peer=patch-int}

10

Port br-int

11

    Interface br-int

12

        type: internal

其中br-int为内部端口。

端口patch-tun(即端口E,端口号为1)连接到br-tun上,实现到外部网络的隧道。 端口qvo-XXX(即端口D,端口号为2)带有tag1,说明这个口是一个1号vlan的access端口。虚拟机发出的从该端口到达br-int的网包将被自动带上vlan tag 1,而其他带有vlan tag 1的网包则可以在去掉vlan tag后从该端口发出(具体请查询vlan access端口)。这个vlan tag是用来实现不同网络相互隔离的,比如租户创建一个网络(neutron net-create),则会被分配一个唯一的vlan tag。

br-int在GRE模式中作为一个NORMAL交换机使用,因此有效规则只有一条正常转发。如果两个在同一主机上的vm属于同一个tenant的(同一个vlan tag),则它们之间的通信只需要经过br-int即可。

[Shell] 纯文本查看 复制代码

01

ovs-ofctl dump-flows br-int

02

NXST_FLOW reply (xid=0x4):

03

cookie=0x0, duration=10727.864s, table=0, n_packets=198, n_bytes=17288, idle_age=13, priority=1 actions=NORMAL

br-tun

一个典型的br-tun上的端口类似:

[Shell] 纯文本查看 复制代码

01

Bridge br-tun

02

    Port patch-int

03

        Interface patch-int

04

            type: patch

05

            options: {peer=patch-tun}

06

    Port "gre-1"

07

        Interface "gre-1"

08

            type: gre

09

            options: {in_key=flow, local_ip="10.0.0.101", out_key=flow, remote_ip="10.0.0.100"}

10

    Port br-tun

11

        Interface br-tun

12

            type: internal

其中patch-int(即端口F,端口号为1)是连接到br-int上的veth pair的端口,gre-1口(即端口G,端口号为2)对应vm到外面的隧道。

gre-1端口是虚拟gre端口,当网包发送到这个端口的时候,会经过内核封包,然后从10.0.0.101发送到10.0.0.100,即从本地的物理网卡(10.0.0.101)发出。

br-tun将带有vlan tag的vm跟外部通信的流量转换到对应的gre隧道,这上面要实现主要的转换逻辑,规则要复杂,一般通过多张表来实现。

典型的转发规则为:

[Shell] 纯文本查看 复制代码

01

ovs-ofctl dump-flows br-tun

02

NXST_FLOW reply (xid=0x4):

03

cookie=0x0, duration=10970.064s, table=0, n_packets=189, n_bytes=16232, idle_age=16, priority=1,in_port=1 actions=resubmit(,1)

04

cookie=0x0, duration=10906.954s, table=0, n_packets=29, n_bytes=5736, idle_age=16, priority=1,in_port=2 actions=resubmit(,2)

05

cookie=0x0, duration=10969.922s, table=0, n_packets=3, n_bytes=230, idle_age=10962, priority=0 actions=drop

06

cookie=0x0, duration=10969.777s, table=1, n_packets=26, n_bytes=5266, idle_age=16, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)

07

cookie=0x0, duration=10969.631s, table=1, n_packets=163, n_bytes=10966, idle_age=21, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,21)

08

cookie=0x0, duration=688.456s, table=2, n_packets=29, n_bytes=5736, idle_age=16, priority=1,tun_id=0x1 actions=mod_vlan_vid:1,resubmit(,10)

09

cookie=0x0, duration=10969.488s, table=2, n_packets=0, n_bytes=0, idle_age=10969, priority=0 actions=drop

10

cookie=0x0, duration=10969.343s, table=3, n_packets=0, n_bytes=0, idle_age=10969, priority=0 actions=drop

11

cookie=0x0, duration=10969.2s, table=10, n_packets=29, n_bytes=5736, idle_age=16, 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

12

cookie=0x0, duration=682.603s, table=20, n_packets=26, n_bytes=5266, hard_timeout=300, idle_age=16, hard_age=16, priority=1,vlan_tci=0x0001/0x0fff,dl_dst=fa:16:3e:32:0d:db actions=load:0->NXM_OF_VLAN_TCI[],load:0x1->NXM_NX_TUN_ID[],output:2

13

cookie=0x0, duration=10969.057s, table=20, n_packets=0, n_bytes=0, idle_age=10969, priority=0 actions=resubmit(,21)

14

cookie=0x0, duration=688.6s, table=21, n_packets=161, n_bytes=10818, idle_age=21, priority=1,dl_vlan=1 actions=strip_vlan,set_tunnel:0x1,output:2

15

cookie=0x0, duration=10968.912s, table=21, n_packets=2, n_bytes=148, idle_age=689, priority=0 actions=drop

其中,表0中有3条规则:从端口1(即patch-int)来的,扔到表1,从端口2(即gre-1)来的,扔到表2。

[Shell] 纯文本查看 复制代码

01

cookie=0x0, duration=10970.064s, table=0, n_packets=189, n_bytes=16232, idle_age=16, priority=1,in_port=1 actions=resubmit(,1)

02

cookie=0x0, duration=10906.954s, table=0, n_packets=29, n_bytes=5736, idle_age=16, priority=1,in_port=2 actions=resubmit(,2)

03

cookie=0x0, duration=10969.922s, table=0, n_packets=3, n_bytes=230, idle_age=10962, priority=0 actions=drop

表1有2条规则:如果是单播(00:00:00:00:00:00/01:00:00:00:00:00),则扔到表20;如果是多播等(01:00:00:00:00:00/01:00:00:00:00:00),则扔到表21。

[Shell] 纯文本查看 复制代码

01

cookie=0x0, duration=10969.777s, table=1, n_packets=26, n_bytes=5266, idle_age=16, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)

02

cookie=0x0, duration=10969.631s, table=1, n_packets=163, n_bytes=10966, idle_age=21, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,21)

表2有2条规则:如果是tunnel 1的网包,则修改其vlan id为1,并扔到表10;非tunnel 1的网包,则丢弃。

[Shell] 纯文本查看 复制代码

01

cookie=0x0, duration=688.456s, table=2, n_packets=29, n_bytes=5736, idle_age=16, priority=1,tun_id=0x1 actions=mod_vlan_vid:1,resubmit(,10)

02

cookie=0x0, duration=10969.488s, table=2, n_packets=0, n_bytes=0, idle_age=10969, priority=0 actions=drop

表3只有1条规则:丢弃。

表10有一条规则,基于learn行动来创建反向(从gre端口抵达,且目标是到vm的网包)的规则。learn行动并非标准的openflow行动,是openvswitch自身的扩展行动,这个行动可以根据流内容动态来修改流表内容。这条规则首先创建了一条新的流(该流对应vm从br-tun的gre端口发出的规则):其中table=20表示规则添加在表20;NXM_OF_VLAN_TCI[0..11]表示匹配包自带的vlan id;NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]表示L2目标地址需要匹配包的L2源地址;load:0->NXM_OF_VLAN_TCI[],去掉vlan,load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],添加tunnel号为原始tunnel号;output:NXM_OF_IN_PORT[],发出端口为原始包抵达的端口。最后规则将匹配的网包从端口1(即patch-int)发出。

[Shell] 纯文本查看 复制代码

01

cookie=0x0, duration=10969.2s, table=10, n_packets=29, n_bytes=5736, idle_age=16, 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

表20中有两条规则,其中第一条即表10中规则利用learn行动创建的流表项,第2条提交其他流到表21。

[Shell] 纯文本查看 复制代码

01

cookie=0x0, duration=682.603s, table=20, n_packets=26, n_bytes=5266, hard_timeout=300, idle_age=16, hard_age=16, priority=1,vlan_tci=0x0001/0x0fff,dl_dst=fa:16:3e:32:0d:db actions=load:0->NXM_OF_VLAN_TCI[],load:0x1->NXM_NX_TUN_ID[],output:2

02

cookie=0x0, duration=10969.057s, table=20, n_packets=0, n_bytes=0, idle_age=10969, priority=0 actions=resubmit(,21)

表21有2条规则,第一条是匹配所有目标vlan为1的网包,去掉vlan,然后从端口2(gre端口)发出。第二条是丢弃。

[Shell] 纯文本查看 复制代码

01

cookie=0x0, duration=688.6s, table=21, n_packets=161, n_bytes=10818, idle_age=21, priority=1,dl_vlan=1 actions=strip_vlan,set_tunnel:0x1,output:2

02

cookie=0x0, duration=10968.912s, table=21, n_packets=2, n_bytes=148, idle_age=689, priority=0 actions=drop

这些规则所组成的整体转发逻辑如下图所示。

网络节点
br-tun

[Shell] 纯文本查看 复制代码

01

Bridge br-tun

02

    Port br-tun

03

        Interface br-tun

04

            type: internal

05

    Port patch-int

06

        Interface patch-int

07

            type: patch

08

            options: {peer=patch-tun}

09

    Port "gre-2"

10

        Interface "gre-2"

11

            type: gre

12

            options: {in_key=flow, local_ip="10.0.0.100", out_key=flow, remote_ip="10.0.0.101"}

Compute节点上发往GRE隧道的网包最终抵达Network节点上的br-tun,该网桥的规则包括:

[Bash shell] 纯文本查看 复制代码

01

ovs-ofctl dump-flows br-tun

02

NXST_FLOW reply (xid=0x4):

03

cookie=0x0, duration=19596.862s, table=0, n_packets=344, n_bytes=66762, idle_age=4, priority=1,in_port=1 actions=resubmit(,1)

04

cookie=0x0, duration=19537.588s, table=0, n_packets=625, n_bytes=125972, idle_age=4, priority=1,in_port=2 actions=resubmit(,2)

05

cookie=0x0, duration=19596.602s, table=0, n_packets=2, n_bytes=140, idle_age=19590, priority=0 actions=drop

06

cookie=0x0, duration=19596.343s, table=1, n_packets=323, n_bytes=65252, idle_age=4, priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)

07

cookie=0x0, duration=19596.082s, table=1, n_packets=21, n_bytes=1510, idle_age=5027, priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,21)

08

cookie=0x0, duration=9356.289s, table=2, n_packets=625, n_bytes=125972, idle_age=4, priority=1,tun_id=0x1 actions=mod_vlan_vid:1,resubmit(,10)

09

cookie=0x0, duration=19595.821s, table=2, n_packets=0, n_bytes=0, idle_age=19595, priority=0 actions=drop

10

cookie=0x0, duration=19595.554s, table=3, n_packets=0, n_bytes=0, idle_age=19595, priority=0 actions=drop

11

cookie=0x0, duration=19595.292s, table=10, n_packets=625, n_bytes=125972, idle_age=4, 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

12

cookie=0x0, duration=9314.338s, table=20, n_packets=323, n_bytes=65252, hard_timeout=300, idle_age=4, hard_age=3, priority=1,vlan_tci=0x0001/0x0fff,dl_dst=fa:16:3e:cb:11:f6 actions=load:0->NXM_OF_VLAN_TCI[],load:0x1->NXM_NX_TUN_ID[],output:2

13

cookie=0x0, duration=19595.026s, table=20, n_packets=0, n_bytes=0, idle_age=19595, priority=0 actions=resubmit(,21)

14

cookie=0x0, duration=9356.592s, table=21, n_packets=9, n_bytes=586, idle_age=5027, priority=1,dl_vlan=1 actions=strip_vlan,set_tunnel:0x1,output:2

15

cookie=0x0, duration=19594.759s, table=21, n_packets=12, n_bytes=924, idle_age=5057, priority=0 actions=drop

这些规则跟Compute节点上br-tun的规则相似,完成tunnel跟vlan之间的转换。

br-int

[Bash shell] 纯文本查看 复制代码

01

Bridge br-int

02

    Port "qr-ff19a58b-3d"

03

        tag: 1

04

        Interface "qr-ff19a58b-3d"

05

            type: internal

06

    Port br-int

07

        Interface br-int

08

            type: internal

09

    Port patch-tun

10

        Interface patch-tun

11

            type: patch

12

            options: {peer=patch-int}

13

    Port "tap4385f950-8b"

14

        tag: 1

15

        Interface "tap4385f950-8b"

16

            type: internal

该集成网桥上挂载了很多进程来提供网络服务,包括路由器、DHCP服务器等。这些进程不同的租户可能都需要,彼此的地址空间可能冲突,也可能跟物理网络的地址空间冲突,因此都运行在独立的网络名字空间中。 规则跟computer节点的br-int规则一致,表现为一个正常交换机。

[Bash shell] 纯文本查看 复制代码

01

ovs-ofctl dump-flows br-int

02

NXST_FLOW reply (xid=0x4):

03

cookie=0x0, duration=18198.244s, table=0, n_packets=849, n_bytes=164654, idle_age=43, priority=1 actions=NORMAL

网络名字空间

在linux中,网络名字空间可以被认为是隔离的拥有单独网络栈(网卡、路由转发表、iptables)的环境。网络名字空间经常用来隔离网络设备和服务,只有拥有同样网络名字空间的设备,才能看到彼此。 可以用ip netns list命令来查看已经存在的名字空间。

[Bash shell] 纯文本查看 复制代码

01

ip netns

02

qdhcp-88b1609c-68e0-49ca-a658-f1edff54a264

03

qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f

qdhcp开头的名字空间是dhcp服务器使用的,qrouter开头的则是router服务使用的。 可以通过 ip netns exec namespaceid command 来在指定的网络名字空间中执行网络命令,例如

[Bash shell] 纯文本查看 复制代码

01

ip netns exec qdhcp-88b1609c-68e0-49ca-a658-f1edff54a264 ip addr

02

71: ns-f14c598d-98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

03

link/ether fa:16:3e:10:2f:03 brd ff:ff:ff:ff:ff:ff

04

inet 10.1.0.3/24 brd 10.1.0.255 scope global ns-f14c598d-98

05

inet6 fe80::f816:3eff:fe10:2f03/64 scope link

06

   valid_lft forever preferred_lft forever

可以看到,dhcp服务的网络名字空间中只有一个网络接口“ns-f14c598d-98”,它连接到br-int的tapf14c598d-98接口上。

dhcp 服务

dhcp服务是通过dnsmasq进程(轻量级服务器,可以提供dns、dhcp、tftp等服务)来实现的,该进程绑定到dhcp名字空间中的br-int的接口上。可以查看相关的进程。

[Bash shell] 纯文本查看 复制代码

01

ps -fe | grep 88b1609c-68e0-49ca-a658-f1edff54a264

02

nobody 23195 1 0 Oct26 ? 00:00:00 dnsmasq --no-hosts --no-resolv --strict-order --bind-interfaces --interface=ns-f14c598d-98 --except-interface=lo --pid-file=/var/lib/neutron/dhcp/88b1609c-68e0-49ca-a658-f1edff54a264/pid --dhcp-hostsfile=/var/lib/neutron/dhcp/88b1609c-68e0-49ca-a658-f1edff54a264/host --dhcp-optsfile=/var/lib/neutron/dhcp/88b1609c-68e0-49ca-a658-f1edff54a264/opts --dhcp-script=/usr/bin/neutron-dhcp-agent-dnsmasq-lease-update --leasefile-ro --dhcp-range=tag0,10.1.0.0,static,120s --conf-file= --domain=openstacklocal

03

root 23196 23195 0 Oct26 ? 00:00:00 dnsmasq --no-hosts --no-resolv --strict-order --bind-interfaces --interface=ns-f14c598d-98 --except-interface=lo --pid-file=/var/lib/neutron/dhcp/88b1609c-68e0-49ca-a658-f1edff54a264/pid --dhcp-hostsfile=/var/lib/neutron/dhcp/88b1609c-68e0-49ca-a658-f1edff54a264/host --dhcp-optsfile=/var/lib/neutron/dhcp/88b1609c-68e0-49ca-a658-f1edff54a264/opts --dhcp-script=/usr/bin/neutron-dhcp-agent-dnsmasq-lease-update --leasefile-ro --dhcp-range=tag0,10.1.0.0,static,120s --conf-file= --domain=openstacklocal

router服务

首先,要理解什么是router,router是提供跨subnet的互联功能的。比如用户的内部网络中主机想要访问外部互联网的地址,就需要router来转发(因此,所有跟外部网络的流量都必须经过router)。目前router的实现是通过iptables进行的。 同样的,router服务也运行在自己的名字空间中,可以通过如下命令查看:

[Bash shell] 纯文本查看 复制代码

01

ip netns exec qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f ip addr

02

66: qg-d48b49e0-aa: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

03

link/ether fa:16:3e:5c:a2:ac brd ff:ff:ff:ff:ff:ff

04

inet 172.24.4.227/28 brd 172.24.4.239 scope global qg-d48b49e0-aa

05

inet 172.24.4.228/32 brd 172.24.4.228 scope global qg-d48b49e0-aa

06

inet6 fe80::f816:3eff:fe5c:a2ac/64 scope link

07

   valid_lft forever preferred_lft forever

08

68: qr-c2d7dd02-56: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

09

link/ether fa:16:3e:ea:64:6e brd ff:ff:ff:ff:ff:ff

10

inet 10.1.0.1/24 brd 10.1.0.255 scope global qr-c2d7dd02-56

11

inet6 fe80::f816:3eff:feea:646e/64 scope link

12

   valid_lft forever preferred_lft forever

可以看出,该名字空间中包括两个网络接口。

第一个接口qg-d48b49e0-aa(即K)是外部接口(qg=q gateway),将路由器的网关指向默认网关(通过router-gateway-set命令指定),这个接口连接到br-ex上的tapd48b49e0-aa(即L)。

第二个接口qr-c2d7dd02-56(即N,qr=q bridge)跟br-int上的tapc2d7dd02-56口(即M)相连,将router进程连接到集成网桥上。

查看该名字空间中的路由表:

[Bash shell] 纯文本查看 复制代码

01

ip netns exec qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f ip route

02

172.24.4.224/28 dev qg-d48b49e0-aa proto kernel scope link src 172.24.4.227

03

10.1.0.0/24 dev qr-c2d7dd02-56 proto kernel scope link src 10.1.0.1

04

default via 172.24.4.225 dev qg-d48b49e0-aa

其中,第一条规则是将到172.24.4.224/28段的访问都从网卡qg-d48b49e0-aa(即K)发出。

第二条规则是将到10.1.0.0/24段的访问都从网卡qr-c2d7dd02-56(即N)发出。 最后一条是默认路由,所有的通过qg-d48b49e0-aa网卡(即K)发出。 floating ip服务同样在路由器名字空间中实现,例如如果绑定了外部的floating ip 172.24.4.228到某个虚拟机10.1.0.2,则nat表中规则为:

[Bash shell] 纯文本查看 复制代码

01

ip netns exec qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f iptables -t nat -S

02

-P PREROUTING ACCEPT

03

-P POSTROUTING ACCEPT

04

-P OUTPUT ACCEPT

05

-N neutron-l3-agent-OUTPUT

06

-N neutron-l3-agent-POSTROUTING

07

-N neutron-l3-agent-PREROUTING

08

-N neutron-l3-agent-float-snat

09

-N neutron-l3-agent-snat

10

-N neutron-postrouting-bottom

11

-A PREROUTING -j neutron-l3-agent-PREROUTING

12

-A POSTROUTING -j neutron-l3-agent-POSTROUTING

13

-A POSTROUTING -j neutron-postrouting-bottom

14

-A OUTPUT -j neutron-l3-agent-OUTPUT

15

-A neutron-l3-agent-OUTPUT -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2

16

-A neutron-l3-agent-POSTROUTING ! -i qg-d48b49e0-aa ! -o qg-d48b49e0-aa -m conntrack ! --ctstate DNAT -j ACCEPT

17

-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697

18

-A neutron-l3-agent-PREROUTING -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2

19

-A neutron-l3-agent-float-snat -s 10.1.0.2/32 -j SNAT --to-source 172.24.4.228

20

-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat

21

-A neutron-l3-agent-snat -s 10.1.0.0/24 -j SNAT --to-source 172.24.4.227

22

-A neutron-postrouting-bottom -j neutron-l3-agent-snat

其中SNAT和DNAT规则完成外部floating ip到内部ip的映射:

[Bash shell] 纯文本查看 复制代码

01

-A neutron-l3-agent-OUTPUT -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2

02

-A neutron-l3-agent-PREROUTING -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2

03

-A neutron-l3-agent-float-snat -s 10.1.0.2/32 -j SNAT --to-source 172.24.4.228

另外有一条SNAT规则把所有其他的内部IP出来的流量都映射到外部IP 172.24.4.227。这样即使在内部虚拟机没有外部IP的情况下,也可以发起对外网的访问。

[Bash shell] 纯文本查看 复制代码

01

-A neutron-l3-agent-snat -s 10.1.0.0/24 -j SNAT --to-source 172.24.4.227

br-ex

[Bash shell] 纯文本查看 复制代码

01

Bridge br-ex

02

    Port "eth1"

03

        Interface "eth1"

04

    Port br-ex

05

        Interface br-ex

06

            type: internal

07

    Port "qg-1c3627de-1b"

08

        Interface "qg-1c3627de-1b"

09

            type: internal

br-ex上直接连接到外部物理网络,一般情况下网关在物理网络中已经存在,则直接转发即可。

ovs-ofctl dump-flows br-exNXST_FLOW reply (xid=0x4): cookie=0x0, duration=23431.091s, table=0, n_packets=893539, n_bytes=504805376, idle_age=0, priority=0 actions=NORMAL

如果对外部网络的网关地址配置到了br-ex(即br-ex作为一个网关):

[Bash shell] 纯文本查看 复制代码

01

ip addr add 172.24.4.225/28 dev br-ex

需要将内部虚拟机发出的流量进行SNAT,之后发出。

[Bash shell] 纯文本查看 复制代码

01

iptables -A FORWARD -d 172.24.4.224/28 -j ACCEPT

02

iptables -A FORWARD -s 172.24.4.224/28 -j ACCEPT

03

iptables -t nat -I POSTROUTING 1 -s 172.24.4.224/28 -j MASQUERADE

Netruon OVS OpenFlow tables + L2 Population
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 复杂得多。

基础知识
1.1 OpenFlow 结构、流表和数据包处理
下面左图是 Open vSwitch 中流表的结构。右图这个流程图详细描述了数据包流通过一个 OpenFlow 交换机的过程。

ARP Proxy
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 

优点:

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

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

缺点:
  1.增加了某一网段上 ARP 流量
  2.主机需要更大的 ARP table 来处理IP地址到MAC地址的映射
  3.安全问题,比如 ARP 欺骗(spoofing)
  4.不会为不使用 ARP 来解析地址的网络工作
  5.不能够概括和推广网络拓扑

不使用 ARP Responder 和 DVR 时 br-tun 中的流表(flow tables)
OpenStack 中,Neutron 作为 OVS 的 Controller,向 OVS 发出管理 tunnel port 的指令,以及提供流表。

2.1 流表分析
其中比较有意思的是:

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

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

2.2 MAC 地址学习
学习规则:

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🆎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。
这里可以看到,通过 MAC 地址学习机制,Neutron 可以一定程度地优化网络流向,但是这种机制需要等待从别的节点的流量进来,只能算是一种被动的机制,效率不高。而且,这种机制只对单播帧有效,而对于多播和组播依然无效。其结果是网络成本依然很高。下图中,A 的广播包其实只对 3 和 4 有用,但是 2 和 5 也收到了。
ARP Responder
arp_responder 的原理不复杂。Neutorn DB 中保存了所有的端口的 MAC 和 IP 地址数据。而 ARP 就是一个虚机要根据另一个虚机的 IP 地址查询它的 MAC。因此,只需要 Neutron server 通过 RPC 告诉每个计算节点上的 ML2 agent 所有活动端口的 MAC 和 IP,那么就可以将 br-tun 变成一个供本机适用的 ARP Proxy,这样本机上的虚机的 ARP 请求的响应就可以由 br-tun 在本地解决。Assaf Meller 有篇文章来阐述 ARP Responder。

使用 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。

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

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

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

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

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

增加的 flow tables 在红色部分:

 因此,通过使用 l2-pop mechanism driver 和 OVS 2.1, Neutorn 可以在本地回答虚机的 ARP 请求,从而避免了昂贵的 ARP 广播。这个功能给 GRE 和  VXLAN 的实现是在 Juno 版本中完成的。 这个 blueprint 似乎在支持VLAN 中的这个功能,但是看起来没有完成。

L2 population
根据这篇文档,l2pop 目前支持 VXLAN with Linux bridge 和 GRE/VXLAN with OVS,其 blueprint 在这里。

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

{'status': 'DOWN/BUILD/ACTIVE', 'binding:host_id': u'compute1', 'allowed_address_pairs': [], 'extra_dhcp_opts': [], 'device_owner': u'compute:nova', 'binding:profile': {}, 'fixed_ips': [{'subnet_id': u'4ec65731-35a5-4637-a59b-a9f2932099f1', 'ip_address': u'81.1.180.15'}], 'id': u'1167e9ac-e10f-4cf5-bd09-6649eab38b32', 'security_groups': [u'f5377a66-803d-481b-b4c3-a6631e8ab456'], 'device_id': u'30580ea7-c456-416b-a01e-0fe645edf5dc', 'name': u'', 'admin_state_up': True, 'network_id': u'86c0d29b-4880-4739-bd68-eb3c392f5099', 'tenant_id': u'74c8ada23a3449f888d9e19b76d13aab', 'binding:vif_details': {u'port_filter': True, u'ovs_hybrid_plug': True}, 'binding:vnic_type': u'normal', 'binding:vif_type': u'ovs', 'mac_address': u'fa:16:3e:4f:59:9d'}
在某些状态变化下:

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 的效果:

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

def init(self, topic=topics.AGENT):
super(L2populationAgentNotifyAPI, self).init(
topic=topic, default_version=self.BASE_RPC_API_VERSION)
self.topic_l2pop_update = topics.get_topic_name(topic, topics.L2POPULATION, topics.UPDATE)

def _notification_fanout(self, context, method, fdb_entries):
    self.fanout_cast(context, self.make_msg(method, fdb_entries=fdb_entries), topic=self.topic_l2pop_update)

def _notification_host(self, context, method, fdb_entries, host):
    self.cast(context, self.make_msg(method, fdb_entries=fdb_entries), topic='%s.%s' % (self.topic_l2pop_update, host))

def add_fdb_entries(self, context, fdb_entries, host=None):
    if fdb_entries:
        if host:
            self._notification_host(context, 'add_fdb_entries',fdb_entries, host) #cast 给指定 host
        else: 
            self._notification_fanout(context, 'add_fdb_entries', fdb_entries) #fanout 给所有计算和网络节点


这段代码是说,l2pop 采用的 MQ topic 是 “L2POPULATION”,消息通知采用 fanout 或者 cast 机制。如果是 fanout 的话,消息将发到所有的 ML2 agent 节点。这样的话,其覆盖面就有些过于广泛了,就这个问题有人提了一个 ticket,官方答复是 work as design,要改的话只能是添加 new feature 了。

4.3.2 大规模网络环境中节点上的 OpenFlow flows 过多
应用高并发—TCP半连接队列和全连接队列
http://rdknown.hikvision.com/sc/doc/experience/preview?id=31f61a12-48f5-407d-bb10-7c67f801907c&classname=com.hikvision.case

2.查看firewall规则与状态

  1. 查看默认防火墙状态(关闭后显示notrunning,开启后显示running)
    firewall-cmd --state

  2. 查看防火墙规则(只显示/etc/firewalld/zones/public.xml中防火墙策略)
    firewall-cmd --list-all

  3. 查看所有的防火墙策略(即显示/etc/firewalld/zones/下的所有策略)
    firewall-cmd --list-all-zones

  4. 重新加载配置文件
    firewall-cmd --reload

网络地址分类
  B类IP地址

  一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,地址范围128.1.0.1-191.255.255.254(二进制表示为:10000000 00000001 00000000 00000001 - 10111111 11111111 11111111 11111110)。可用的B类网络有16384个,每个网络能容纳65534主机 。

  C类IP地址

  一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”。范围192.0.1.1-223.255.255.254(二进制表示为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110)。C类网络可达2097152个,每个网络能容纳254个主机。

私有网络相关
把以下资料中的知识点整理到自己的文档中

深入理解 Neutron -- OpenStack 网络实现:VXLAN 模式

https://blog.csdn.net/qq_15437629/article/details/78702829

私有网络,社区资料
https://docs.openstack.org/mitaka/zh_CN/install-guide-rdo/launch-instance-networks-selfservice.html

https://docs.openstack.org/zh_CN/user-guide/cli-create-and-manage-networks.html

https://www.cnblogs.com/embedded-linux/p/10200831.html

管理外部网络和浮动IP地址

https://www.ibm.com/support/knowledgecenter/en/SST55W_4.2.0/liaca/liaca_manage_floating_ip.html

Configuring Floating IP addresses for Networking in OpenStack Public and Private Clouds
https://www.mirantis.com/blog/configuring-floating-ip-addresses-networking-openstack-public-private-clouds/

OpenStack网络基础知识: OpenvSwitch使用指南

http://fishcried.com/2016-02-09/openvswitch-ops-guide/

基于 Open vSwitch 的 OpenFlow 实践

https://www.ibm.com/developerworks/cn/cloud/library/1401_zhaoyi_openswitch/index.html

https://docs.openstack.org/python-openstackclient/pike/cli/command-objects/floating-ip.html

https://docs.openstack.org/newton/networking-guide/deploy-ovs-ha-dvr.html

/etc/neutron/l3_agent.ini:3:external_network_bridge =

openswitch_agent.ini

https://wiki.openstack.org/wiki/Ovs-flow-logic#OVS_flows_logic_with_local_ARP_responder

posted on 2019-09-05 14:44  wddblog  阅读(1007)  评论(0编辑  收藏  举报