深入理解 Neutron -- OpenStack 网络实现(1):GRE
模式
概述
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的端口如下所示:
Bridge br-int Port "qvo-XXX" tag: 1 Interface "qvo-XXX" Port patch-tun Interface patch-tun type: patch options: {peer=patch-int} Port br-int Interface br-int 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即可。
# ovs-ofctl dump-flows br-int NXST_FLOW reply (xid=0x4): cookie=0x0, duration=10727.864s, table=0, n_packets=198, n_bytes=17288, idle_age=13, priority=1 actions=NORMAL |
br-tun
一个典型的br-tun上的端口类似:
Bridge br-tun Port patch-int Interface patch-int type: patch options: {peer=patch-tun} Port "gre-1" Interface "gre-1" type: gre options: {in_key=flow, local_ip="10.0.0.101", out_key=flow, remote_ip="10.0.0.100"} Port br-tun Interface br-tun 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隧道,这上面要实现主要的转换逻辑,规则要复杂,一般通过多张表来实现。
典型的转发规则为:
# ovs-ofctl dump-flows br-tun NXST_FLOW reply (xid=0x4): cookie=0x0, duration=10970.064s, table=0, n_packets=189,
n_bytes=16232, idle_age=16, priority=1,in_port=1 actions=resubmit(,1) cookie=0x0, duration=10906.954s, table=0, n_packets=29, n_bytes=5736, idle_age=16,
priority=1,in_port=2 actions=resubmit(,2) cookie=0x0, duration=10969.922s, table=0, n_packets=3, n_bytes=230, idle_age=10962,
priority=0 actions=drop 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) 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) 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) cookie=0x0, duration=10969.488s, table=2, n_packets=0, n_bytes=0, idle_age=10969, priority=0 actions=drop cookie=0x0, duration=10969.343s, table=3, n_packets=0, n_bytes=0, idle_age=10969, priority=0 actions=drop 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 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 cookie=0x0, duration=10969.057s, table=20, n_packets=0, n_bytes=0,
idle_age=10969, priority=0 actions=resubmit(,21) 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 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。
cookie=0x0, duration=10970.064s, table=0, n_packets=189, n_bytes=16232,
idle_age=16, priority=1,in_port=1 actions=resubmit(,1) cookie=0x0, duration=10906.954s, table=0, n_packets=29,
n_bytes=5736, idle_age=16, priority=1,in_port=2 actions=resubmit(,2) 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。
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) 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的网包,则丢弃。
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) 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)发出。
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。
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 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端口)发出。第二条是丢弃。
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 cookie=0x0, duration=10968.912s, table=21, n_packets=2, n_bytes=148,
idle_age=689, priority=0 actions=drop |
这些规则所组成的整体转发逻辑如下图所示。
网络节点
br-tun
Bridge br-tun Port br-tun Interface br-tun type: internal Port patch-int Interface patch-int type: patch options: {peer=patch-tun} Port "gre-2" Interface "gre-2" type: gre options: {in_key=flow, local_ip="10.0.0.100", out_key=flow, remote_ip="10.0.0.101"} |
Compute节点上发往GRE隧道的网包最终抵达Network节点上的br-tun,该网桥的规则包括:
# ovs-ofctl dump-flows br-tun NXST_FLOW reply (xid=0x4): cookie=0x0, duration=19596.862s, table=0, n_packets=344, n_bytes=66762,
idle_age=4, priority=1,in_port=1 actions=resubmit(,1) cookie=0x0, duration=19537.588s, table=0, n_packets=625, n_bytes=125972,
idle_age=4, priority=1,in_port=2 actions=resubmit(,2) cookie=0x0, duration=19596.602s, table=0, n_packets=2, n_bytes=140,
idle_age=19590, priority=0 actions=drop 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) 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) 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) cookie=0x0, duration=19595.821s, table=2, n_packets=0, n_bytes=0,
idle_age=19595, priority=0 actions=drop cookie=0x0, duration=19595.554s, table=3, n_packets=0, n_bytes=0, idle_age=19595, priority=0 actions=drop 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 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 cookie=0x0, duration=19595.026s, table=20, n_packets=0, n_bytes=0,
idle_age=19595, priority=0 actions=resubmit(,21) 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 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
Bridge br-int Port "qr-ff19a58b-3d" tag: 1 Interface "qr-ff19a58b-3d" type: internal Port br-int Interface br-int type: internal Port patch-tun Interface patch-tun type: patch options: {peer=patch-int} Port "tap4385f950-8b" tag: 1 Interface "tap4385f950-8b" type: internal |
该集成网桥上挂载了很多进程来提供网络服务,包括路由器、DHCP服务器等。这些进程不同的租户可能都需要,彼此的地址空间可能冲突,也可能跟物理网络的地址空间冲突,因此都运行在独立的网络名字空间中。
规则跟computer节点的br-int规则一致,表现为一个正常交换机。
# ovs-ofctl dump-flows br-int NXST_FLOW reply (xid=0x4): 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命令来查看已经存在的名字空间。
# ip netns qdhcp-88b1609c-68e0-49ca-a658-f1edff54a264 qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f |
qdhcp开头的名字空间是dhcp服务器使用的,qrouter开头的则是router服务使用的。 可以通过
ip netns exec namespaceid command 来在指定的网络名字空间中执行网络命令,例如
# ip netns exec qdhcp-88b1609c-68e0-49ca-a658-f1edff54a264 ip addr 71: ns-f14c598d-98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether fa:16:3e:10:2f:03 brd ff:ff:ff:ff:ff:ff inet 10.1.0.3/24 brd 10.1.0.255 scope global ns-f14c598d-98 inet6 fe80::f816:3eff:fe10:2f03/64 scope link valid_lft forever preferred_lft forever |
可以看到,dhcp服务的网络名字空间中只有一个网络接口“ns-f14c598d-98”,它连接到br-int的tapf14c598d-98接口上。
dhcp 服务
dhcp服务是通过dnsmasq进程(轻量级服务器,可以提供dns、dhcp、tftp等服务)来实现的,该进程绑定到dhcp名字空间中的br-int的接口上。可以查看相关的进程。
# ps -fe | grep 88b1609c-68e0-49ca-a658-f1edff54a264 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 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服务也运行在自己的名字空间中,可以通过如下命令查看:
# ip netns exec qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f ip addr 66: qg-d48b49e0-aa: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether fa:16:3e:5c:a2:ac brd ff:ff:ff:ff:ff:ff inet 172.24.4.227/28 brd 172.24.4.239 scope global qg-d48b49e0-aa inet 172.24.4.228/32 brd 172.24.4.228 scope global qg-d48b49e0-aa inet6 fe80::f816:3eff:fe5c:a2ac/64 scope link valid_lft forever preferred_lft forever 68: qr-c2d7dd02-56: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether fa:16:3e:ea:64:6e brd ff:ff:ff:ff:ff:ff inet 10.1.0.1/24 brd 10.1.0.255 scope global qr-c2d7dd02-56 inet6 fe80::f816:3eff:feea:646e/64 scope link 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进程连接到集成网桥上。
查看该名字空间中的路由表:
# ip netns exec qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f ip route 172.24.4.224/28 dev qg-d48b49e0-aa proto kernel scope link src 172.24.4.227 10.1.0.0/24 dev qr-c2d7dd02-56 proto kernel scope link src 10.1.0.1 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表中规则为:
# ip netns exec qrouter-2d214fde-293c-4d64-8062-797f80ae2d8f iptables -t nat -S -P PREROUTING ACCEPT -P POSTROUTING ACCEPT -P OUTPUT ACCEPT -N neutron-l3-agent-OUTPUT -N neutron-l3-agent-POSTROUTING -N neutron-l3-agent-PREROUTING -N neutron-l3-agent-float-snat -N neutron-l3-agent-snat -N neutron-postrouting-bottom -A PREROUTING -j neutron-l3-agent-PREROUTING -A POSTROUTING -j neutron-l3-agent-POSTROUTING -A POSTROUTING -j neutron-postrouting-bottom -A OUTPUT -j neutron-l3-agent-OUTPUT -A neutron-l3-agent-OUTPUT -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2 -A neutron-l3-agent-POSTROUTING ! -i qg-d48b49e0-aa !
-o qg-d48b49e0-aa -m conntrack ! --ctstate DNAT -j ACCEPT -A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697 -A neutron-l3-agent-PREROUTING -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2 -A neutron-l3-agent-float-snat -s 10.1.0.2/32 -j SNAT --to-source 172.24.4.228 -A neutron-l3-agent-snat -j neutron-l3-agent-float-snat -A neutron-l3-agent-snat -s 10.1.0.0/24 -j SNAT --to-source 172.24.4.227 -A neutron-postrouting-bottom -j neutron-l3-agent-snat |
其中SNAT和DNAT规则完成外部floating ip到内部ip的映射:
-A neutron-l3-agent-OUTPUT -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2 -A neutron-l3-agent-PREROUTING -d 172.24.4.228/32 -j DNAT --to-destination 10.1.0.2 -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的情况下,也可以发起对外网的访问。
-A neutron-l3-agent-snat -s 10.1.0.0/24 -j SNAT --to-source 172.24.4.227 |
br-ex
Bridge br-ex Port "eth1" Interface "eth1" Port br-ex Interface br-ex type: internal Port "qg-1c3627de-1b" Interface "qg-1c3627de-1b" 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作为一个网关):
# ip addr add 172.24.4.225/28 dev br-ex |
需要将内部虚拟机发出的流量进行SNAT,之后发出。
# iptables -A FORWARD -d 172.24.4.224/28 -j ACCEPT # iptables -A FORWARD -s 172.24.4.224/28 -j ACCEPT # iptables -t nat -I POSTROUTING 1 -s 172.24.4.224/28 -j MASQUERADE |
深入理解 Neutron -- OpenStack 网络实现(2):VLAN
模式
Vlan模式下的系统架构跟GRE模式下类似,如下图所示。
需要注意的是,在vlan模式下,vlan tag的转换需要在br-int和br-ethx两个网桥上进行相互配合。即br-int负责从int-br-ethX过来的包(带外部vlan)转换为内部vlan,而br-ethx负责从phy-br-ethx过来的包(带内部vlan)转化为外部的vlan。
(个人对vlan tag的理解,应该是对vlan的一个人为标记,起到识别的作用)
下面进行一些细节的补充讨论,以Vlan作为物理网络隔离的实现。假如要实现同一个租户下两个子网,如下图所示:
计算节点
查看网桥信息,主要包括两个网桥:br-int和br-eth1:
[root@Compute ~]# ovs-vsctl show f758a8b8-2fd0-4a47-ab2d-c49d48304f82 Bridge "br-eth1" Port "phy-br-eth1" Interface "phy-br-eth1" Port "br-eth1" Interface "br-eth1" type: internal Port "eth1" Interface "eth1" Bridge br-int Port "qvoXXX" tag: 1 Interface "qvoXXX" Port "qvoYYY" tag: 1 Interface "qvoYYY" Port "qvoZZZ" tag: 2 Interface "qvoZZZ" Port "qvoWWW" tag: 2 Interface "qvoWWW" Port "int-br-eth1" Interface "int-br-eth1" Port br-int Interface br-int type: internal |
类似GRE模式下,br-int负责租户隔离,br-eth1负责跟计算节点外的网络通信。 在Vlan模式下,租户的流量隔离是通过vlan来进行的,因此此时包括两种vlan,虚拟机在Compute
Node内流量带有的local vlan和在Compute Node之外物理网络上隔离不同租户的vlan。
br-int和br-eth1分别对从端口int-br-eth1和phy-br-eth1上到达的网包进行vlan
tag的处理。此处有两个网,分别带有两个vlan tag(内部tag1对应外部tag101,内部tag2对应外部tag102)。
其中,安全组策略仍然在qbr相关的iptables上实现。
br-int
与GRE模式不同的是,br-int完成从br-eth1上过来流量(从口int-br-eth1到达)的vlan
tag转换,可能的规则为
#ovs-ofctl dump-flows br-int cookie=0x0, duration=100.795s, table=0, n_packets=6,
n_bytes=468, idle_age=90, priority=2,in_port=3 actions=drop cookie=0x0, duration=97.069s, table=0, n_packets=22, n_bytes=6622, idle_age=31,
priority=3,in_port=3,dl_vlan=101 actions=mod_vlan_vid:1,NORMAL cookie=0x0, duration=95.781s, table=0, n_packets=8, n_bytes=1165, idle_age=11,
priority=3,in_port=3,dl_vlan=102 actions=mod_vlan_vid:2,NORMAL cookie=0x0, duration=103.626s, table=0, n_packets=47,
n_bytes=13400, idle_age=11, priority=1 actions=NORMAL |
br-eth1
br-eth1上负责从br-int上过来的流量(从口phy-br-eth1到达),实现local vlan到外部vlan的转换。
#ovs-ofctl dump-flows br-eth0 NXST_FLOW reply (xid=0x4): cookie=0x0, duration=73.461s, table=0, n_packets=51, n_bytes=32403,
idle_age=2, hard_age=65534, priority=4,in_port=4,dl_vlan=1 actions=mod_vlan_vid:101,NORMAL cookie=0x0, duration=83.461s, table=0, n_packets=51, n_bytes=32403,
idle_age=2, hard_age=65534, priority=4,in_port=4,dl_vlan=2 actions=mod_vlan_vid:102,NORMAL cookie=0x0, duration=651.538s, table=0, n_packets=72, n_bytes=3908,
idle_age=2574, hard_age=65534, priority=2,in_port=4 actions=drop cookie=0x0, duration=654.002s, table=0, n_packets=31733, n_bytes=6505880,
idle_age=2, hard_age=65534, priority=1 actions=NORMAL |
网络节点
类似GRE模式下,br-eth1收到到达的网包,int-br-eth1和phy-br-eth1上分别进行vlan转换,保证到达br-int上的网包都是带有内部vlan
tag,到达br-eth1上的都是带有外部vlan tag。br-ex则完成到OpenStack以外网络的连接。
查看网桥信息,包括三个网桥,br-eth1、br-int和br-ex。
#ovs 3bd78da8-d3b5-4112-a766-79506a7e2801 Bridge br-ex Port "qg-VVV" Interface "qg-VVV" type: internal Port br-ex Interface br-ex type: internal Port "eth0" Interface "eth0" Bridge br-int Port br-int Interface br-int type: internal Port "int-br-eth1" Interface "int-br-eth0" Port "tapXXX" tag: 1 Interface "tapXXX" type: internal Port "tapWWW" tag: 2 Interface "tapWWW" type: internal Port "qr-YYY" tag: 1 Interface "qr-YYY" type: internal Port "qr-ZZZ" tag: 2 Interface "qr-ZZZ" type: internal Bridge "br-eth1" Port "phy-br-eth1" Interface "phy-br-eth1" Port "br-eth1" Interface "br-eth1" type: internal Port "eth1" Interface "eth1" |
br-eth1
br-eth1主要负责把物理网络上外部vlan转化为local vlan。
#ovs-ofctl dump-flows br-eth1 NXST_FLOW reply (xid=0x4): cookie=0x0, duration=144.33s, table=0, n_packets=13, n_bytes=28404, idle_age=24,
hard_age=65534, priority=4,in_port=5,dl_vlan=101 actions=mod_vlan_vid:1,NORMAL cookie=0x0, duration=144.33s, table=0, n_packets=13, n_bytes=28404, idle_age=24,
hard_age=65534, priority=4,in_port=5,dl_vlan=102 actions=mod_vlan_vid:2,NORMAL cookie=0x0, duration=608.373s, table=0, n_packets=23, n_bytes=1706,
idle_age=65534, hard_age=65534, priority=2,in_port=5 actions=drop cookie=0x0, duration=675.373s, table=0, n_packets=58,
n_bytes=10625, idle_age=24, hard_age=65534, priority=1 actions=NORMAL |
br-int
br-int上挂载了大量的agent来提供各种网络服务,另外负责对发往br-eth1的流量,实现local
vlan转化为外部vlan。
#ovs-ofctl dump-flows br-int NXST_FLOW reply (xid=0x4): cookie=0x0, duration=147294.121s, table=0, n_packets=224, n_bytes=33961,
idle_age=13, hard_age=65534, priority=3,in_port=4,dl_vlan=1 actions=mod_vlan_vid:101,NORMAL cookie=0x0, duration=603538.84s, table=0, n_packets=19, n_bytes=2234,
idle_age=18963, hard_age=65534, priority=2,in_port=4 actions=drop cookie=0x0, duration=603547.134s, table=0, n_packets=31901,
n_bytes=6419756, idle_age=13, hard_age=65534, priority=1 actions=NORMAL |
dnsmasq负责提供DHCP服务,绑定到某个特定的名字空间上,每个需要DHCP服务的租户网络有自己专属隔离的DHCP服务(图中的tapXXX和tapWWW上各自监听了一个dnsmasq)。
路由是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。
每个L3 agent或dnsmasq都在各自独立的名字空间中,如下图所示,其中同一租户的两个子网都使用了同一个路由器。
对于子网使用不同路由器的情况,多个路由器会在自己独立的名字空间中。例如要实现两个租户的两个子网的情况,如下图所示。
这种情况下,网络节点上的名字空间如下图所示。
br-ex
br-ex要做的事情很简单,只需要正常转发即可。
#ovs-ofctl dump-flows br-ex NXST_FLOW reply (xid=0x4): cookie=0x0, duration=6770.969s, table=0, n_packets=5411, n_bytes=306944,
idle_age=0, hard_age=65534, priority=0 actions=NORMAL |
|