Luckylau's Blog

Neutron三层网络服务实现原理

​ Neutron 对虚拟三层网络的实现是通过其 L3 Agent (neutron-l3-agent)。该 Agent 利用 Linux IP 栈、route 和 iptables 来实现内网内不同网络内的虚机之间的网络流量,以及虚机和外网之间网络流量的路由和转发。为了在同一个Linux 系统上支持可能的 IP 地址空间重叠,它使用了 Linux network namespace 来提供隔离的转发上下文。

NameSpace技术

​ 在二层网络上,VLAN 可以将一个物理交换机分割成几个独立的虚拟交换机。类似地,在三层网络上,Linux network namespace(netns) 可以将一个物理三层网络分割成几个独立的虚拟三层网络。

​ Network namespace (netns)从 Linux 2.6.24 版本开始添加,直到 2.6.29 添加完成。每个 netns 拥有独立的 (virtual)network devices, IP addresses, IP routing tables, /proc/net directory, ports 等等。新创建的 netns 默认只包含 loopback device。除了这个设备,每个 network device,不管是物理的还是虚拟的网卡还是网桥等,都只能存在于一个 netns。而且,连接物理硬件的物理设备只能存在于 root netns。其它普通的网络设备可以被创建和添加到某个 netns。

添加 network namespace

​ ip netnas add <network namespace name>

​ Example:

​ ip netns add nstest

列表所有 netns

​ ip netns list

删除某 netns

​ ip netns delete <network namespace name>

在 network namespace 中运行命令

​ ip netns exec <network namespace name> <command>

​ Example using the namespace from above:

​ ip netns exec nstest ip addr

添加 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

ping

​ ip netns exec nstest ping 10.0.0.1

​ PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.

​ bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.054 ms

查看路由表和 iptbales

​ ip netns exec nstest route

​ ip netns exec nstest iptables -L

Iptables

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

​ iptables其实是个client,供用户去管理防火墙。相关的请求最后会发送相关内核模块,如ip_tables。ip_tables内核模块主要用于组织iptables使用的表,链,规则。netfilter是一套技术框架,ip_tables依托于netfilter来注册各种hooks实现对数据包的具体控制。一些厂商的防火墙,入侵检测,入侵防御系统什么的基本依托于Netfilter来实现(从事过相关开发)。

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跳转过来就可以了。

1
2
iptables -N 链名
iptables ... -j 链名

下面我们查看openstack中的router是如何使用iptables的。

首先当路由器创建完成并加入网络(9.9.9.0/24)之后,我们查看一下它iptable信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# Generated by iptables-save v1.6.0 on Tue Jul 18 21:49:49 2017
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-snat - [0:0]
:neutron-postrouting-bottom - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A POSTROUTING -j neutron-postrouting-bottom
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697
-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
COMMIT
# Completed on Tue Jul 18 21:49:49 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 21:49:49 2017
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:neutron-filter-top - [0:0]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-local - [0:0]
:neutron-l3-agent-scope - [0:0]
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-filter-top
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-filter-top
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A neutron-filter-top -j neutron-l3-agent-local
-A neutron-l3-agent-FORWARD -j neutron-l3-agent-scope
-A neutron-l3-agent-INPUT -m mark --mark 0x1/0xffff -j ACCEPT
-A neutron-l3-agent-INPUT -p tcp -m tcp --dport 9697 -j DROP
-A neutron-l3-agent-scope -o qr-b111bb2e-84 -m mark ! --mark 0x4000000/0xffff0000 -j DROP
COMMIT
# Completed on Tue Jul 18 21:49:49 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 21:49:49 2017
*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
COMMIT
# Completed on Tue Jul 18 21:49:49 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 21:49:49 2017
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-floatingip - [0:0]
:neutron-l3-agent-mark - [0:0]
:neutron-l3-agent-scope - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-mark
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-scope
-A neutron-l3-agent-PREROUTING -m connmark ! --mark 0x0/0xffff0000 -j CONNMARK --restore-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-floatingip
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x1/0xffff
-A neutron-l3-agent-float-snat -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-scope -i qr-b111bb2e-84 -j MARK --set-xmark 0x4000000/0xffff0000
COMMIT
# Completed on Tue Jul 18 21:49:49 2017

输出是分段的,每个段落一个表,*开头后面跟着表名。
:开头的行是对链匹配次数的总结,后面跟着统计信息,[数据包:字节数]。
后面跟着的是具体规则。
最后跟着COMMIT表示一个表的结束。

然后我们将该路由器关联一个外部网络(10.0.99.0/24),我们发现该网络的所有虚拟机访问外网时候,会将凡是到达外网络的数据都进行SNAT为其外部地址10.0.99.2,但是没有相应的表项导致外网中的虚拟机是无法访问内部虚拟机的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
root@netagent:~# ip netns exec qrouter-71d8932f-3782-470f-b3a2-f80203f96885 iptables-save
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:14:04 2017
*nat
:PREROUTING ACCEPT [98:22227]
:INPUT ACCEPT [1:325]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-snat - [0:0]
:neutron-postrouting-bottom - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-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 qg-37790cfc-43 ! -o qg-37790cfc-43 -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
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -o qg-37790cfc-43 -j SNAT --to-source 10.0.99.2
-A neutron-l3-agent-snat -m mark ! --mark 0x2/0xffff -m conntrack --ctstate DNAT -j SNAT --to-source 10.0.99.2
-A neutron-postrouting-bottom -m comment --comment "Perform source NAT on outgoing traffic." -j neutron-l3-agent-snat
COMMIT
# Completed on Tue Jul 18 22:14:04 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:14:04 2017
*filter
:INPUT ACCEPT [26:8892]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:neutron-filter-top - [0:0]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-local - [0:0]
:neutron-l3-agent-scope - [0:0]
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-filter-top
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-filter-top
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A neutron-filter-top -j neutron-l3-agent-local
-A neutron-l3-agent-FORWARD -j neutron-l3-agent-scope
-A neutron-l3-agent-INPUT -m mark --mark 0x1/0xffff -j ACCEPT
-A neutron-l3-agent-INPUT -p tcp -m tcp --dport 9697 -j DROP
-A neutron-l3-agent-scope -o qr-b111bb2e-84 -m mark ! --mark 0x4000000/0xffff0000 -j DROP
COMMIT
# Completed on Tue Jul 18 22:14:04 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:14:04 2017
*raw
:PREROUTING ACCEPT [123:30794]
:OUTPUT ACCEPT [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
COMMIT
# Completed on Tue Jul 18 22:14:04 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:14:04 2017
*mangle
:PREROUTING ACCEPT [118:29354]
:INPUT ACCEPT [23:7908]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-floatingip - [0:0]
:neutron-l3-agent-mark - [0:0]
:neutron-l3-agent-scope - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A neutron-l3-agent-POSTROUTING -o qg-37790cfc-43 -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-mark
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-scope
-A neutron-l3-agent-PREROUTING -m connmark ! --mark 0x0/0xffff0000 -j CONNMARK --restore-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-floatingip
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x1/0xffff
-A neutron-l3-agent-float-snat -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-mark -i qg-37790cfc-43 -j MARK --set-xmark 0x2/0xffff
-A neutron-l3-agent-scope -i qg-37790cfc-43 -j MARK --set-xmark 0x4000000/0xffff0000
-A neutron-l3-agent-scope -i qr-b111bb2e-84 -j MARK --set-xmark 0x4000000/0xffff0000
COMMIT
# Completed on Tue Jul 18 22:14:04 2017

Floating Ip

我们将9.9.9.0/24网络中的虚拟机(9.9.9.3)分配一个floatingIP(10.0.99.3),查看router的iptables-save。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
root@netagent:~# ip netns exec qrouter-71d8932f-3782-470f-b3a2-f80203f96885 iptables-save
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:23:42 2017
*nat
:PREROUTING ACCEPT [48:12000]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-snat - [0:0]
:neutron-postrouting-bottom - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A POSTROUTING -j neutron-postrouting-bottom
-A neutron-l3-agent-OUTPUT -d 10.0.99.3/32 -j DNAT --to-destination 9.9.9.3
-A neutron-l3-agent-POSTROUTING ! -i qg-37790cfc-43 ! -o qg-37790cfc-43 -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
-A neutron-l3-agent-PREROUTING -d 10.0.99.3/32 -j DNAT --to-destination 9.9.9.3
-A neutron-l3-agent-float-snat -s 9.9.9.3/32 -j SNAT --to-source 10.0.99.3
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -o qg-37790cfc-43 -j SNAT --to-source 10.0.99.2
-A neutron-l3-agent-snat -m mark ! --mark 0x2/0xffff -m conntrack --ctstate DNAT -j SNAT --to-source 10.0.99.2
-A neutron-postrouting-bottom -m comment --comment "Perform source NAT on outgoing traffic." -j neutron-l3-agent-snat
COMMIT
# Completed on Tue Jul 18 22:23:42 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:23:42 2017
*filter
:INPUT ACCEPT [608:211608]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:neutron-filter-top - [0:0]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-local - [0:0]
:neutron-l3-agent-scope - [0:0]
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-filter-top
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-filter-top
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A neutron-filter-top -j neutron-l3-agent-local
-A neutron-l3-agent-FORWARD -j neutron-l3-agent-scope
-A neutron-l3-agent-INPUT -m mark --mark 0x1/0xffff -j ACCEPT
-A neutron-l3-agent-INPUT -p tcp -m tcp --dport 9697 -j DROP
-A neutron-l3-agent-scope -o qr-b111bb2e-84 -m mark ! --mark 0x4000000/0xffff0000 -j DROP
COMMIT
# Completed on Tue Jul 18 22:23:42 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:23:42 2017
*raw
:PREROUTING ACCEPT [3155:832499]
:OUTPUT ACCEPT [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
COMMIT
# Completed on Tue Jul 18 22:23:42 2017
# Generated by iptables-save v1.6.0 on Tue Jul 18 22:23:42 2017
*mangle
:PREROUTING ACCEPT [3150:831059]
:INPUT ACCEPT [605:210624]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-floatingip - [0:0]
:neutron-l3-agent-mark - [0:0]
:neutron-l3-agent-scope - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A neutron-l3-agent-POSTROUTING -o qg-37790cfc-43 -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-mark
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-scope
-A neutron-l3-agent-PREROUTING -m connmark ! --mark 0x0/0xffff0000 -j CONNMARK --restore-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-floatingip
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x1/0xffff
-A neutron-l3-agent-float-snat -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-mark -i qg-37790cfc-43 -j MARK --set-xmark 0x2/0xffff
-A neutron-l3-agent-scope -i qg-37790cfc-43 -j MARK --set-xmark 0x4000000/0xffff0000
-A neutron-l3-agent-scope -i qr-b111bb2e-84 -j MARK --set-xmark 0x4000000/0xffff0000
COMMIT
# Completed on Tue Jul 18 22:23:42 2017

-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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ip netns exec qrouter-71d8932f-3782-470f-b3a2-f80203f96885 ip addr
16: qg-37790cfc-43: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1
link/ether fa:16:3e:51:15:05 brd ff:ff:ff:ff:ff:ff
inet 10.0.99.2/24 brd 10.0.99.255 scope global qg-37790cfc-43
valid_lft forever preferred_lft forever
inet 10.0.99.3/32 brd 10.0.99.3 scope global qg-37790cfc-43
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe51:1505/64 scope link
valid_lft forever preferred_lft forever
#解释
<BROADCAST,MULTICAST,UP,LOWER_UP>:端口的各种状态
BROADCAST: device can send traffic to all hosts on the link (能够发广播)
MULTICAST: device can perform and receive multicast packets (能够发多播)
UP: device is functioning (enabled 状态,可通过 ip * up/down 设置。)
LOWER_UP:the state of the Ethernet link(表示线已接上)
inet/brd/scope:IP 地址及子网掩码,广播地址,作用域
scope:global:valid everywhere

​ 这个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如何实现?

Neutron L3 HA 的提出就是为了保证 OpenStack 环境三层网络的高可用性。

​ 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)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#Master的信息
root@netagent10038219:~# ip netns exec qrouter-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
28: ha-65bc01fe-af: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8950 qdisc noqueue state UNKNOWN group default qlen 1
link/ether fa:16:3e:1f:d2:bd brd ff:ff:ff:ff:ff:ff
inet 169.254.192.1/18 brd 169.254.255.255 scope global ha-65bc01fe-af
valid_lft forever preferred_lft forever
inet 169.254.0.1/24 scope global ha-65bc01fe-af
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe1f:d2bd/64 scope link
valid_lft forever preferred_lft forever
30: qr-fe09d52e-c6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8950 qdisc noqueue state UNKNOWN group default qlen 1
link/ether fa:16:3e:58:13:ad brd ff:ff:ff:ff:ff:ff
inet 1.1.1.1/24 scope global qr-fe09d52e-c6
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe58:13ad/64 scope link nodad
valid_lft forever preferred_lft forever
32: qg-21bed7aa-c6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1
link/ether fa:16:3e:a9:28:e7 brd ff:ff:ff:ff:ff:ff
inet 10.99.1.101/32 scope global qg-21bed7aa-c6
valid_lft forever preferred_lft forever
inet 10.99.1.102/32 scope global qg-21bed7aa-c6
valid_lft forever preferred_lft forever
inet 10.99.1.103/24 scope global qg-21bed7aa-c6
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fea9:28e7/64 scope link nodad
valid_lft forever preferred_lft forever
#Slave信息
root@netagent10038220:~# ip netns exec qrouter-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
26: ha-557abd6c-48: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8950 qdisc noqueue state UNKNOWN group default qlen 1
link/ether fa:16:3e:3f:75:8d brd ff:ff:ff:ff:ff:ff
inet 169.254.192.2/18 brd 169.254.255.255 scope global ha-557abd6c-48
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe3f:758d/64 scope link
valid_lft forever preferred_lft forever
29: qr-fe09d52e-c6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8950 qdisc noqueue state UNKNOWN group default qlen 1
link/ether fa:16:3e:58:13:ad brd ff:ff:ff:ff:ff:ff
32: qg-21bed7aa-c6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1
link/ether fa:16:3e:a9:28:e7 brd ff:ff:ff:ff:ff:ff

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广播

1
2
3
4
5
6
root@netagent10038219:~# ip netns exec qrouter-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b tcpdump -i ha-65bc01fe-af
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ha-65bc01fe-af, link-type EN10MB (Ethernet), capture size 262144 bytes
05:01:11.336576 IP 169.254.192.1 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 50, authtype none, intvl 2s, length 20
05:01:13.337818 IP 169.254.192.1 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 50, authtype none, intvl 2s, length 20
05:01:15.339083 IP 169.254.192.1 > 224.0.0.18: VRRPv2, Advertisement, vrid 1, prio 50, authtype none, intvl 2s, length 20

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 的进程使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
root@netagent10038219:~# ps -aux | grep '23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b'
root 4310 0.0 0.8 155892 73080 ? S Mar17 0:05 /usr/bin/python /usr/local/bin/neutron-keepalived-state-change --router_id=23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b --namespace=qrouter-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b --conf_dir=/var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b --monitor_interface=ha-65bc01fe-af --monitor_cidr=169.254.0.1/24 --pid_file=/var/lib/neutron/external/pids/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.monitor.pid --state_path=/var/lib/neutron --user=0 --group=0
root 4810 0.0 0.0 54252 564 ? Ss Mar17 0:59 keepalived -P -f /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b/keepalived.conf -p /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid -r /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid-vrrp
root 4811 0.0 0.0 56496 3856 ? S Mar17 2:31 keepalived -P -f /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b/keepalived.conf -p /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid -r /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid-vrrp
root 6330 0.0 0.8 140156 71188 ? S Mar28 0:02 /usr/bin/python /usr/local/bin/neutron-ns-metadata-proxy --pid_file=/var/lib/neutron/external/pids/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid --metadata_proxy_socket=/var/lib/neutron/metadata_proxy --router_id=23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b --state_path=/var/lib/neutron --metadata_port=9697 --metadata_proxy_user=0 --metadata_proxy_group=0 --log-file=neutron-ns-metadata-proxy-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.log --log-dir=/var/log/neutron
root 28706 0.0 0.0 13080 2544 pts/1 S+ 05:47 0:00 grep --color=auto 23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b
root@netagent10038219:/var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b# ls
keepalived.conf neutron-keepalived-state-change.log state
vrrp_instance VR_1 {
state BACKUP
interface ha-65bc01fe-af
virtual_router_id 1
priority 50
garp_master_delay 60
nopreempt
advert_int 2
track_interface {
ha-65bc01fe-af
}
virtual_ipaddress {
169.254.0.1/24 dev ha-65bc01fe-af
}
virtual_ipaddress_excluded {
1.1.1.1/24 dev qr-fe09d52e-c6
10.99.1.101/32 dev qg-21bed7aa-c6
10.99.1.102/32 dev qg-21bed7aa-c6
10.99.1.103/24 dev qg-21bed7aa-c6
fe80::f816:3eff:fe58:13ad/64 dev qr-fe09d52e-c6 scope link
fe80::f816:3eff:fea9:28e7/64 dev qg-21bed7aa-c6 scope link
}
virtual_routes {
0.0.0.0/0 via 10.99.1.1 dev qg-21bed7aa-c6
}
}
========================================================================================
root@netagent10038220:~# ps -aux | grep '23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b'
root 4330 0.0 0.8 155896 72840 ? S Mar17 0:04 /usr/bin/python /usr/local/bin/neutron-keepalived-state-change --router_id=23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b --namespace=qrouter-23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b --conf_dir=/var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b --monitor_interface=ha-557abd6c-48 --monitor_cidr=169.254.0.1/24 --pid_file=/var/lib/neutron/external/pids/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.monitor.pid --state_path=/var/lib/neutron --user=0 --group=0
root 4882 0.0 0.0 54252 568 ? Ss Mar17 0:59 keepalived -P -f /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b/keepalived.conf -p /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid -r /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid-vrrp
root 4883 0.0 0.0 56540 4304 ? S Mar17 1:46 keepalived -P -f /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b/keepalived.conf -p /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid -r /var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b.pid-vrrp
root 25801 0.0 0.0 13080 2548 pts/0 S+ 05:48 0:00 grep --color=auto 23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b
root@netagent10038220:/var/lib/neutron/ha_confs/23d7334d-a55f-4b4b-9dfa-4d1ee4b3080b# ls
keepalived.conf neutron-keepalived-state-change.log state
vrrp_instance VR_1 {
state BACKUP
interface ha-557abd6c-48
virtual_router_id 1
priority 50
garp_master_delay 60
nopreempt
advert_int 2
track_interface {
ha-557abd6c-48
}
virtual_ipaddress {
169.254.0.1/24 dev ha-557abd6c-48
}
virtual_ipaddress_excluded {
1.1.1.1/24 dev qr-fe09d52e-c6
10.99.1.101/32 dev qg-21bed7aa-c6
10.99.1.102/32 dev qg-21bed7aa-c6
10.99.1.103/24 dev qg-21bed7aa-c6
fe80::f816:3eff:fe58:13ad/64 dev qr-fe09d52e-c6 scope link
fe80::f816:3eff:fea9:28e7/64 dev qg-21bed7aa-c6 scope link
}
virtual_routes {
0.0.0.0/0 via 10.99.1.1 dev qg-21bed7aa-c6
}
}

​ 总的来说,要使用 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的架构

网络层:

服务层:

架构:

DVR的配置与试验

上图是我搭建的部署环境(你可以将Network Server与Controller Node放在一起),对于DVR来说我们需要操作如下:

控制节点:

1
2
3
neutron.conf
[DEFAULT]
router_distributed = True

网络节点:

1
2
3
4
5
6
openvswitch_agent.ini
[agent]
enable_distributed_routing = True
l3_agent.ini
[DEFAULT]
agent_mode = dvr_snat

计算节点:

1
2
3
4
5
6
7
8
9
yum install openstack-neutron
systemctl enable neutron-l3-agent.service
openvswitch_agent.ini
[agent]
enable_distributed_routing = True
l3_agent.ini
[DEFAULT]
interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver
agent_mode = dvr

然后重启以下服务:

1
2
3
4
5
控制节点:systemctl restart neutron-server
计算节点:systemctl restart neutron-openvswitch-agent
systemctl start neutron-l3-agent
网络节点:systemctl restart neutron-openvswitch-agent
systemctl restart neutron-l3-agent

为了演示DVR我们创建外部网络

1
2
3
[root@controller ~]# neutron net-create --shared --provider:physical_network external --provider:network_type flat external_net
[root@controller ~]# neutron subnet-create --name external_net --allocation-pool start=123.4.4.10,end=123.4.4.50 --dns-nameserver 123.4.4.1 --gateway 123.4.4.1 external_net 123.4.4.0/24
[root@controller ~]# neutron net-update external_net --router:external

在下面实现之前要注意:

当虚拟机创建落在计算机点后,才会在该计算机节点出现qrouter的namespace

当router设置网关后,才会出现snat namespace

当关联floating ip 才会在计算机点出现fixed ip 的namespace

创建路由器

如果我们选择了外部网络后,在网络节点就会出现snat namespace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@netagent ~]# ip netns list
snat-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a
[root@netagent ~]# ip netns exec snat-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 0 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
qg-b683d547-5f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 123.4.4.14 netmask 255.255.255.0 broadcast 123.4.4.255
inet6 fe80::f816:3eff:fe88:bdc7 prefixlen 64 scopeid 0x20<link>
ether fa:16:3e:88:bd:c7 txqueuelen 1000 (Ethernet)
RX packets 5 bytes 332 (332.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 13 bytes 1074 (1.0 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
fixed IP的南北向流量

创建内部网络6.6.6.0/24和虚拟机6.6.6.5,虚拟机落在计算节点2,我们看到在这个节点出现了qrouter-namespace,同时网络节点也出现了qrouter-namespace,这两个网络接口信息一样,同时snat-namespace也多了一个网口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@com2 ~]# ip netns list
qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a
[root@com2 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 0 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
qr-b452f7b9-bc: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 6.6.6.1 netmask 255.255.255.0 broadcast 6.6.6.255
inet6 fe80::f816:3eff:fe99:3c29 prefixlen 64 scopeid 0x20<link>
ether fa:16:3e:99:3c:29 txqueuelen 1000 (Ethernet)
RX packets 10 bytes 1076 (1.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10 bytes 864 (864.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@netagent ~]# ip netns list
snat-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a
qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a
[root@netagent ~]# ip netns exec snat-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 0 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
qg-b683d547-5f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 123.4.4.14 netmask 255.255.255.0 broadcast 123.4.4.255
inet6 fe80::f816:3eff:fe88:bdc7 prefixlen 64 scopeid 0x20<link>
ether fa:16:3e:88:bd:c7 txqueuelen 1000 (Ethernet)
RX packets 19 bytes 1324 (1.2 KiB)
RX errors 0 dropped 1 overruns 0 frame 0
TX packets 13 bytes 1074 (1.0 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
sg-d722c249-c5: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 6.6.6.3 netmask 255.255.255.0 broadcast 6.6.6.255
inet6 fe80::f816:3eff:fef5:93ba prefixlen 64 scopeid 0x20<link>
ether fa:16:3e:f5:93:ba txqueuelen 1000 (Ethernet)
RX packets 22 bytes 1500 (1.4 KiB)
RX errors 0 dropped 2 overruns 0 frame 0
TX packets 15 bytes 1210 (1.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

在虚机没有floating ip的情况下访问外网,从虚机发出的包会首先到计算节点的qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a,丢给网关6.6.6.1/24,然后匹配table 101058049,我们看到会交给6.6.6.3的地址,它正是snat-namespace新出现的网口sg-d722c249-c5地址。

1
2
3
4
5
6
7
8
9
10
11
[root@com2 ~]# 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
101058049: from 6.6.6.1/24 lookup 101058049
[root@com2 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip route list table 101058049
default via 6.6.6.3 dev qr-b452f7b9-bc
[root@netagent ~]# ip netns exec snat-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a iptables -nvL -t nat
Chain neutron-l3-agent-snat (1 references)
pkts bytes target prot opt in out source destination
0 0 SNAT all -- * qg-b683d547-5f 0.0.0.0/0 0.0.0.0/0 to:123.4.4.14

总之,虚拟机通过计算节点的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再到虚机。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
[root@com2 ~]# ip netns list
fip-49ca86bb-afe9-4c6f-a67f-5399f57b851d
qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a
[root@com2 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: rfp-851e3ae1-1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether ce:61:44:bd:66:1e brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 169.254.106.114/31 scope global rfp-851e3ae1-1
valid_lft forever preferred_lft forever
inet 123.4.4.16/32 brd 123.4.4.16 scope global rfp-851e3ae1-1
valid_lft forever preferred_lft forever
inet6 fe80::cc61:44ff:febd:661e/64 scope link
valid_lft forever preferred_lft forever
15: qr-b452f7b9-bc: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc pfifo_fast state UNKNOWN qlen 1000
link/ether fa:16:3e:99:3c:29 brd ff:ff:ff:ff:ff:ff
inet 6.6.6.1/24 brd 6.6.6.255 scope global qr-b452f7b9-bc
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe99:3c29/64 scope link
valid_lft forever preferred_lft forever
[root@com2 ~]# 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
57481: from 6.6.6.5 lookup 16
101058049: from 6.6.6.1/24 lookup 101058049
[root@com2 ~]# 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
169.254.106.114/31 dev rfp-851e3ae1-1 proto kernel scope link src 169.254.106.114
[root@com2 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
6.6.6.0 0.0.0.0 255.255.255.0 U 0 0 0 qr-b452f7b9-bc
169.254.106.114 0.0.0.0 255.255.255.254 U 0 0 0 rfp-851e3ae1-1
[root@com2 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip route list table 16
default via 169.254.106.115 dev rfp-851e3ae1-1
[root@com2 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a iptables-save
.....
-A neutron-l3-agent-OUTPUT -d 123.4.4.16/32 -j DNAT --to-destination 6.6.6.5
-A neutron-l3-agent-POSTROUTING ! -i rfp-851e3ae1-1 ! -o rfp-851e3ae1-1 -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
-A neutron-l3-agent-PREROUTING -d 123.4.4.16/32 -j DNAT --to-destination 6.6.6.5
-A neutron-l3-agent-float-snat -s 6.6.6.5/32 -j SNAT --to-source 123.4.4.16
-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
COMMIT
[root@com2 ~]# ip netns exec fip-49ca86bb-afe9-4c6f-a67f-5399f57b851d ifconfig
fg-9b5bb134-fa: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 123.4.4.17 netmask 255.255.255.0 broadcast 123.4.4.255
inet6 fe80::f816:3eff:fe5d:43b7 prefixlen 64 scopeid 0x20<link>
ether fa:16:3e:5d:43:b7 txqueuelen 1000 (Ethernet)
RX packets 3 bytes 84 (84.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10 bytes 864 (864.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
fpr-851e3ae1-1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 169.254.106.115 netmask 255.255.255.254 broadcast 0.0.0.0
inet6 fe80::b8fe:64ff:fe79:1608 prefixlen 64 scopeid 0x20<link>
ether ba:fe:64:79:16:08 txqueuelen 1000 (Ethernet)
RX packets 7 bytes 738 (738.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 7 bytes 738 (738.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 0 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@com2 ~]# ip netns exec fip-49ca86bb-afe9-4c6f-a67f-5399f57b851d route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 123.4.4.1 0.0.0.0 UG 0 0 0 fg-9b5bb134-fa
123.4.4.0 0.0.0.0 255.255.255.0 U 0 0 0 fg-9b5bb134-fa
123.4.4.16 169.254.106.114 255.255.255.255 UGH 0 0 0 fpr-851e3ae1-1
169.254.106.114 0.0.0.0 255.255.255.254 U 0 0 0 fpr-851e3ae1-1
[root@com2 ~]# ip netns exec fip-49ca86bb-afe9-4c6f-a67f-5399f57b851d ip route list table main
default via 123.4.4.1 dev fg-9b5bb134-fa
123.4.4.0/24 dev fg-9b5bb134-fa proto kernel scope link src 123.4.4.17
123.4.4.16 via 169.254.106.114 dev fpr-851e3ae1-1
169.254.106.114/31 dev fpr-851e3ae1-1 proto kernel scope link src 169.254.106.115

东西流量

我们又创建了一个子网192.168.1.0/24,虚拟机192.168.1.5在计算节点1,在计算机点1也会出现qrouter-namespace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@com1 ~]# ip netns list
qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a
[root@com1 ~]# ip netns exec qrouter-851e3ae1-1b10-41ed-b215-f0e5dfd7a46a ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
15: qr-b452f7b9-bc: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc pfifo_fast state UNKNOWN qlen 1000
link/ether fa:16:3e:99:3c:29 brd ff:ff:ff:ff:ff:ff
inet 6.6.6.1/24 brd 6.6.6.255 scope global qr-b452f7b9-bc
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fe99:3c29/64 scope link
valid_lft forever preferred_lft forever
18: qr-ff78f72b-98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc pfifo_fast state UNKNOWN qlen 1000
link/ether fa:16:3e:a8:f8:fa brd ff:ff:ff:ff:ff:ff
inet 192.168.1.1/24 brd 192.168.1.255 scope global qr-ff78f72b-98
valid_lft forever preferred_lft forever
inet6 fe80::f816:3eff:fea8:f8fa/64 scope link
valid_lft forever preferred_lft forever

我们用虚拟机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:58 actions=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:58 actions=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 文件。

参考:

http://man.linuxde.net/route

http://fishcried.com/2016-02-19/iptables/

http://www.cnblogs.com/sammyliu/p/4636091.html

http://www.embeddedlinux.org.cn/linux_net/0596002556/understandlni-CHP-28-SECT-3.html

https://www.ibm.com/developerworks/cn/cloud/library/1506_xiaohh_openstackl3/

Luckylau wechat
如果对您有价值,看官可以打赏的!