问题导读:
1.Neutron的代码分析?
2.Neutron 其它服务有哪些?
3.后续版本中 DVR 开发?
4. 代码分析
- DVR Router network namespace 的创建和删除
- DVR Router 相关的 flows
- DVR Router 的 ARP 表
4.1 DVR Router 相关 network namespace 的创建和删除
4.1.1 qrouter 在计算和网络节点上的删除和创建
1. 当一个子网 subnet 被加入到一个 DVR Router 时,DVR Router 会被分布到所有包含在该子网内的虚机的计算节点上。
- 计算节点上的 L3 Agent 会收到一个通知,它会配置 router
- OVS Agent 会将 router 的端口 plug 到 OVS Bridge 上,并且配置 flows
2. 当一个虚机被创建,而且虚机所在的计算节点上不存在该虚机所在 subnet 连接的 DVR Router 时。
3. 当与 DVR Router 相关的最后一个虚机被删除时,router namespace 会被从虚机所在的计算节点上删除。
4.1.2 snat 在网络节点上的创建和删除
删除:当删除 router 的 external gateway 时
4.1.3 fip 在 计算节点上的创建和删除
删除:(1)当计算节点上最后一个使用浮动 IP 的虚机被删除后 (2)所有虚机的浮动 IP 被删除后
4.2 DVR MAC 地址
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 4 5 6 7 8 |
MariaDB [neutron] > select * from dvr_host_macs;
+ ----------+-------------------+
| host | mac_address |
+ ----------+-------------------+
| network | fa : 16 : 3 f : 12 : a 3 : 38 |
| compute 1 | fa : 16 : 3 f : b 2 : 34 : 82 |
| compute 2 | fa : 16 : 3 f : db : 6 f : 73 |
+ ----------+-------------------+
|
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 4 |
root@compute 1 : / home / s 1 # ovs-ofctl dump-flows br-tun | grep mod_dl_src
cookie = 0 x 0 , duration = 6989.3 94 s , table = 1 , n_packets = 6405 , n_bytes = 627690 , idle_age = 510 , priority = 1 , dl_vlan = 1 , dl_src = fa : 16 : 3 e : ec : f 3 : dd actions = mod_dl_src : fa : 16 : 3 f : b 2 : 34 : 82 , resubmit ( , 2 )
cookie = 0 x 0 , duration = 8055.1 65 s , table = 1 , n_packets = 10 , n_bytes = 980 , idle_age = 4814 , priority = 1 , dl_vlan = 2 , dl_src = fa : 16 : 3 e : 63 : 3 b : 4 c actions = mod_dl_src : fa : 16 : 3 f : b 2 : 34 : 82 , resubmit ( , 2 )
cookie = 0 x 0 , duration = 8059.9 47 s , table = 1 , n_packets = 635 , n_bytes = 26950 , idle_age = 4843 , priority = 1 , dl_vlan = 1 , dl_src = fa : 16 : 3 e : a 9 : da : b 5 actions = mod_dl_src : fa : 16 : 3 f : b 2 : 34 : 82 , resubmit ( , 2 )
|
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 4 5 6 |
s 1 @controller : ~$ neutron port - list | grep fa : 16 : 3 e : ec : f 3 : dd
| f 849 ae 46 -4819 -45 f 5 - a 805 -5970 d 4 e 31951 | | fa : 16 : 3 e : ec : f 3 : dd | { "subnet_id" : "f8841500-b392-4053-bda1-acf419f4a86e" , "ip_address" : "90.1.180.1" }
s 1 @controller : ~$ neutron port - list | grep fa : 16 : 3 e : 63 : 3 b : 4 c
| 517 bdba 3 - b 117 -43 ce -851 b - bb 1 d 039879 dc | | fa : 16 : 3 e : 63 : 3 b : 4 c | { "subnet_id" : "4ec65731-35a5-4637-a59b-a9f2932099f1" , "ip_address" : "81.1.180.1" }
s 1 @controller : ~$ neutron port - list | grep fa : 16 : 3 e : a 9 : da : b 5
| e 47 fca 31 - dbf 6 -47 e 5 -9 ccb -0950557 a 55 e 3 | | fa : 16 : 3 e : a 9 : da : b 5 | { "subnet_id" : "13888749-12b3-462e-9afe-c527bd0a297e" , "ip_address" : "91.1.180.1" }
|
DVR-MAC-ADDRESS 的更新是 neutron server 通过 RPC Notifier 做的。每当一个新的地址被分配后,它通知所有的 L3 Agent 节点做处理。
4.3 DVR OVS flows
4.3.1 br-int flows 的主要修改
table 0:LOCAL_SWITCHING
[AppleScript] 纯文本查看 复制代码 ?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 |
root@compute 1 : / home / s 1 # ovs-ofctl dump-flows br-int
NXST_FLOW reply ( xid = 0 x 4 ) :
#获取所有的 DVR_MAC_ADDRESS,然后
# 从 patch-tun 进入的 src mac 为 network 节点的 DVR HOST MAC 的网络帧(也就是从 network 节点来的帧),重新提交 table 1 处理
table = 0 , n_packets = 0 , n_bytes = 0 , idle_age = 9734 , priority = 2 , in_port = 5 , dl_src = fa : 16 : 3 f : 12 : a 3 : 38 actions = resubmit ( , 1 )
# 从 compute 2 节点来的网络帧,提交到 table 1
table = 0 , n_packets = 2321 , n_bytes = 227458 , idle_age = 0 , priority = 2 , in_port = 5 , dl_src = fa : 16 : 3 f : db : 6 f : 73 actions = resubmit ( , 1 )
#本机上的虚机产生的网络帧,走常规的转发模式发到虚机或者 br-tun
table = 0 , n_packets = 22797 , n_bytes = 2176300 , idle_age = 0 , priority = 1 actions = NORMAL
#目标网段是 81.1.180.0/24 网段的帧,去掉 vlan tag,修改 src mac 为 qrouter 的 81.1.180.1 interface 的 mac 地址,发到虚机
table = 1 , n_packets = 0 , n_bytes = 0 , idle_age = 9725 , priority = 2 , ip , dl_vlan = 2 , nw_dst = 81.1 . 180.0 / 24 actions = strip_vlan , mod_dl_src : fa : 16 : 3 e : 63 : 3 b : 4 c , output : 4
#目标网段是 90.1.180.0/24 网段的帧,去掉 vlan tag,修改 src mac 为qrouter 的 90.1.180.1 interface 的 mac 地址,发到虚机
table = 1 , n_packets = 0 , n_bytes = 0 , idle_age = 2268 , priority = 2 , ip , dl_vlan = 1 , nw_dst = 90.1 . 180.0 / 24 actions = strip_vlan , mod_dl_src : fa : 16 : 3 e : ec : f 3 : dd , output : 8
#dst mac 地址为 vm1,去掉 vlan tag,修改 src mac,发到虚机table=1, n_packets=2321, n_bytes=227458, idle_age=0, priority=4,dl_vlan=2,dl_dst=fa:16:3e:30:ee:23 actions=strip_vlan,mod_dl_src:fa:16:3e:63:3b:4c,output:4
#dst mac 为 vm2,则去掉 vlan tag,修改 src mac,发到虚机table=1, n_packets=0, n_bytes=0, idle_age=2268, priority=4,dl_vlan=1,dl_dst=fa:16:3e:4a:22:ff actions=strip_vlan,mod_dl_src:fa:16:3e:ec:f3:dd,output:8table=1, n_packets=0, n_bytes=0, idle_age=9734, priority=1 actions=drop
table = 23 , n_packets = 0 , n_bytes = 0 , idle_age = 9734 , priority = 0 actions = drop
|
<ignore_js_op>
4.3.2 br-tun flows 主要的修改
<ignore_js_op>
对于将要离开本机的网络帧:
- Table 1 (DVR process Table): 如果网络帧的 src mac 是本机 qrouter 上的 interface 的 mac 地址(dvr-router-intf-mac),将其修改为 DVR-compute-node-unique-mac,然后交给table 2 处理;其它的帧,交给 table 2.
[AppleScript] 纯文本查看 复制代码 ?
1 |
table = 1 , n_packets = 8655 , n_bytes = 848190 , idle_age = 1 , priority = 1 , dl_vlan = 1 , dl_src = fa : 16 : 3 e : ec : f 3 : dd actions = mod_dl_src : fa : 16 : 3 f : b 2 : 34 : 82 , resubmit ( , 2 ) table = 1 , n_packets = 10 , n_bytes = 980 , idle_age = 7987 , priority = 1 , dl_vlan = 2 , dl_src = fa : 16 : 3 e : 63 : 3 b : 4 c actions = mod_dl_src : fa : 16 : 3 f : b 2 : 34 : 82 , resubmit ( , 2 ) table = 1 , n_packets = 635 , n_bytes = 26950 , idle_age = 8015 , priority = 1 , dl_vlan = 1 , dl_src = fa : 16 : 3 e : a 9 : da : b 5 actions = mod_dl_src : fa : 16 : 3 f : b 2 : 34 : 82 , resubmit ( , 2 )
|
- table 2:单播帧转 table 20;多播帧转 table 22
[AppleScript] 纯文本查看 复制代码 ?
1 2 |
table = 2 , n_packets = 8673 , n_bytes = 849786 , idle_age = 1 , priority = 0 , dl_dst = 00 : 00 : 00 : 00 : 00 : 00 / 01 : 00 : 00 : 00 : 00 : 00 actions = resubmit ( , 20 )
table = 2 , n_packets = 675 , n_bytes = 30798 , idle_age = 3727 , priority = 0 , dl_dst = 01 : 00 : 00 : 00 : 00 : 00 / 01 : 00 : 00 : 00 : 00 : 00 actions = resubmit ( , 22 )
|
- table 20:将 vlan id 转化为 tunnel id,并根据处理进入本机的网络帧的时候学习到的 mac 地址和 tunnel port,查找网络帧的出口 tunnel port
[AppleScript] 纯文本查看 复制代码 ?
1 |
table = 20 , n_packets = 0 , n_bytes = 0 , idle_age = 10156 , priority = 2 , dl_vlan = 1 , dl_dst = fa : 16 : 3 e : 54 : f 8 : b 8 actions = strip_vlan , set_tunnel : 0 x 4 , output : 3 table = 20 , n_packets = 4012 , n_bytes = 393176 , idle_age = 1 , priority = 2 , dl_vlan = 1 , dl_dst = fa : 16 : 3 e : 13 : 93 : 0 d actions = strip_vlan , set_tunnel : 0 x 4 , output : 2
|
- table 22:将 vlan id 转化为 tunnel id,并泛洪到所有的 tunnel 端口
[AppleScript] 纯文本查看 复制代码 ?
1 |
table = 22 , n_packets = 8 , n_bytes = 1126 , idle_age = 11018 , hard_age = 5501 , dl_vlan = 2 actions = strip_vlan , set_tunnel : 0 x 6 , output : 3 , output : 2 table = 22 , n_packets = 642 , n_bytes = 27866 , idle_age = 3727 , hard_age = 5469 , dl_vlan = 1 actions = strip_vlan , set_tunnel : 0 x 4 , output : 3 , output : 2 table = 22 , n_packets = 25 , n_bytes = 1806 , idle_age = 3772 , priority = 0 actions = drop
|
对于进入本机的网络帧:
- table 0:交给 table 3 处理
- table 3:只允许目的网络为本机上的虚机所在的网络的网络帧,修改其 vlan id,转 table 9
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 |
table = 3 , n_packets = 98 , n_bytes = 6514 , idle_age = 3731 , priority = 1 , tun_id = 0 x 4 actions = mod_vlan_vid : 1 , resubmit ( , 9 ) # tunnel id 转换为 vlan id
table = 3 , n_packets = 3830 , n_bytes = 375768 , idle_age = 1 , priority = 1 , tun_id = 0 x 6 actions = mod_vlan_vid : 2 , resubmit ( , 9 ) #tunnel id 转换为 vlan id
table = 3 , n_packets = 0 , n_bytes = 0 , idle_age = 11238 , priority = 0 actions = drop
|
- Table 9 (DVR Learning blocker):如果 src mac 是 DVR-Unique-MAC,不做 mac 学习,转发到 patch-int;否则,转到 table 10 做 mac 地址学习
[AppleScript] 纯文本查看 复制代码 ?
1 |
table = 9 , n_packets = 0 , n_bytes = 0 , idle_age = 11236 , priority = 1 , dl_src = fa : 16 : 3 f : 12 : a 3 : 38 actions = output : 1 #从 network node 过来的帧,发到 br-inttable=9, n_packets=3821, n_bytes=374458, idle_age=1, priority=1,dl_src=fa:16:3f:db:6f:73 actions=output:1 #从 compute 2 node 过来的帧,发到 br-inttable=9, n_packets=107, n_bytes=7824, idle_age=3731, priority=0 actions=resubmit(,10) #其余提交 table 10 处理,进行地址学习
|
- table 10:mac 地址学习,结果存到 table 20
[AppleScript] 纯文本查看 复制代码 ?
1 |
table = 10 , n_packets = 107 , n_bytes = 7824 , idle_age = 3731 , 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
|
注意:在 table 20 中,除了自己通过 mac 地址学习学到的 mac 地址外,还需要借助 l2population 。这就是为什么 DVR 依赖于 l2population 的缘故。没有使用的话,网络包无法发到正确的 tunnel interface。
具体设置 OVS flows 的代码在 setup_dvr_flows_on_integ_tun_br 函数中。更详细的说明可以参考官方文章。
4.4 qrouter 中的ARP 表
1. 当目标计算机(vm2)不在其同一个网段时,它需要获取默认网关的 mac 地址,这个将由 qrouter 直接相应 arp 请求。 2. 当目标计算机(vm2)在其同一个网段时,它需要直接获取 vm2 的 mac 地址。这个应该仍然是通过 ARP 广播获得。简单的做法是使用 arp responder。
qrouter 在做完 vm1 的网络包的路由后,将网络包从 vm2 所在网段的 interface 上发出前,需要获取 vm2 的 mac 地址。而这个是通过它查询自身的 ARP table 获得的。这是 compute 1 上 qrouter netns 中的 ip neighbour 表:
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 4 |
root@compute 1 : / home / s 1 # ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip neigh
90.1 . 180.6 dev qr - f 849 ae 46 -48 lladdr fa : 16 : 3 e : 13 : 93 : 0 d PERMANENT
81.1 . 180.1 0 dev qr -517 bdba 3 - b 1 lladdr fa : 16 : 3 e : c 0 : 8 f : 2 c PERMANENT
90.1 . 180.3 dev qr - e 47 fca 31 - db lladdr fa : 16 : 3 e : 69 : 92 : 30 PERMANENT
|
通过该由 L3 Agent 动态维护的 ARP 表,qrouter 就能直接查到它要通信的 interface 的 MAC 地址了,而不需要通过广播的方式去被动获取。具体原因是:
Why all of this complexity? Remember that the router’s MAC addresses are present on all hosts and cannot leave the host. If a router scheduled on host 1 generated an ARP request for a port scheduled on host 2, host 2 would receive the message and its virtual switches would suddenly re-learn a MAC they supposedly already know to be connected to br-int. They would see it coming from a tunnel connected to br-tun!
大致的更新过程为:
- 每个L2 Agent 进程循环检查其管理的 port 的状态
- 当 port 状态由 down 变为 up 时,它通过 RPC 通知 neutron server 该变化,neutron server 然后发出 fanout 通知其他的 L2 agent 去添加 arp entry (add_arp_entry),再调用 ip neigh replace方法在 qrouter network namespace 中 增加一个 arp entry
- 当 port 状态由 up 变为 down 时,它通过 RPC 通知 neutron server 该变化,neutron server 然后发出 fanout 通知其他的 L2 agent 去添加 delete entry (del_arp_entry),再调用 ip neigh del 方法在 qrouter network namespace 中 删除该 arp entry
4.5 ip rule 和 route 操作
4.5.1 增加一个 internal subnet 时
(1)计算该 subnet cidr(81.1.180.1/24)的 index 1359066113,作为新增 ip rule 的优先级和路由表的名称。 (2)增加 default gateway,运行 ['ip route replace default via 81.1.180.17 dev qr-517bdba3-b1 table 1359066113]。这里的 81.1.180.17 正是 snat namespace 的 IP。 (3)增加 ip rule, 允许 [ip rule add from 81.1.180.1/24 lookup 1359066113 priority 1359066113]。这样就将该 subnet 中的虚机的网络帧转到 route table (4)执行 ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 sysctl -w net.ipv4.conf.qr-517bdba3-b1.send_redirects=0
效果如下:
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 4 5 6 7 |
root@compute 1 : / home / s 1 # ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
0 : from all lookup local
32766 : from all lookup main
32767 : from all lookup default
1359066113 : from 81.1 . 180.1 / 24 lookup 1359066113
root@compute 1 : / home / s 1 # ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route list table 1359066113
default via 81.1 . 180.1 7 dev qr -517 bdba 3 - b 1
|
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 4 5 6 7 |
root@compute 1 : / home / s 1 # ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
0 : from all lookup local
32766 : from all lookup main
32767 : from all lookup default
1359066113 : from 81.1 . 180.1 / 24 lookup 1359066113
1510061057 : from 90.1 . 180.1 / 24 lookup 1510061057
1526838273 : from 91.1 . 180.1 / 24 lookup 1526838273
|
4.5.2 给虚机绑定浮动 IP 时
(1)增加 ip rule,通过运行 ['add', 'from', u'81.1.180.18', 'lookup', 16, 'priority', 32768],其中,ID 16 为写死的,其优先级是从 32768 开始到 36768 这个区间内依次分配。 (2)在路由表 16 中添加路由项 default via 169.254.31.239 dev rfp-e8f12f7a-6。这使得虚机访问外网的网络包会通过 rfp-e8f12f7a-6 发到 169.254.31.239。而这个 IP 正是 fip 上 pfr 端口的IP。
在 fip namespace 中:
(1)增加 route:192.168.1.0/24 dev fg-6b744484-88 proto kernel scope link src 192.168.1.119。这使得访问外网机器的网络包能从 fg-6b744484-88 出去。 (2)增加 route:192.168.1.116 via 169.254.31.238 dev fpr-e8f12f7a-6。使得访问虚机的网络包会发给 169.254.31.238,进入 qrouter。这个 router 上的每个浮动 IP 有这么一条 route。
配置了两个浮动 IP 的情况下是这样的结果:
[AppleScript] 纯文本查看 复制代码 ?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 |
root@compute 1 : / home / s 1 # ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
32768 : from 81.1 . 180.1 8 lookup 16 #去 fip 的
32769 : from 90.1 . 180.8 lookup 16 #去 fip 的
1359066113 : from 81.1 . 180.1 / 24 lookup 1359066113 #去 snat 的
1510061057 : from 90.1 . 180.1 / 24 lookup 1510061057 #去 snat 的
root@compute 1 : / home / s 1 # ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route list table 16
default via 169.2 54.3 1.2 39 dev rfp - e 8 f 12 f 7 a -6 # route 是一样的
root@compute 1 : / home / s 1 # ip netns exec fip-557e9f0c-9c66-46da-b289-218d49c218d2 ip route
default via 192.1 68.1 . 1 dev fg -6 b 744484 -88
169.2 54.3 1.2 38 / 31 dev fpr - e 8 f 12 f 7 a -6 proto kernel scope link src 169.2 54.3 1.2 39
192.1 68.1 . 0 / 24 dev fg -6 b 744484 -88 proto kernel scope link src 192.1 68.1 . 119
192.1 68.1 . 104 via 169.2 54.3 1.2 38 dev fpr - e 8 f 12 f 7 a -6 #每个浮动 IP 一个 route item
192.1 68.1 . 116 via 169.2 54.3 1.2 38 dev fpr - e 8 f 12 f 7 a -6
|
这里能看到 qroute 的 ip rule 上,针对一个虚机/子网,有两条 rule,一条查路由表 16 到 fip,另一条查表到 snat。但是,在有浮动 IP 的情况下,前一条策略的优先级数值将小于后后一条的,这就决定了查路由表 16,数据包走 fip。
4.5.3 qrouter 的 main 路由表
[AppleScript] 纯文本查看 复制代码 ?
1 2 3 4 5 |
root@compute 1 : / home / s 1 # ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route
81.1 . 180.0 / 24 dev qr -517 bdba 3 - b 1 proto kernel scope link src 81.1 . 180.1 #为到子网 1 中的虚机的网络包做路由
90.1 . 180.0 / 24 dev qr - f 849 ae 46 -48 proto kernel scope link src 90.1 . 180.1 #为到子网 2 中的虚机的网络包做路由
91.1 . 180.0 / 24 dev qr - e 47 fca 31 - db proto kernel scope link src 91.1 . 180.1
169.2 54.3 1.2 38 / 31 dev rfp - e 8 f 12 f 7 a -6 proto kernel scope link src 169.2 54.3 1.2 38 #为从 fip 进来的外网访问内部虚机的网络包做路由
|
5. Neutron 其它服务与 DVR
5.1 FWaas DVR
DVR 实现后,FWaas 需要做相应的修改。
官方文档在这里:
https://wiki.openstack.org/wiki/Neutron/FWaaS/FWaaS-DVR
Spec:https://review.openstack.org/#/c/106225/9/specs/juno/neutron-dvr-fwaas.rst
目标:FWaas 保持对 南-北流量做防火墙,而不影响东-西流量。
做法:Neutron 网络节点上的 FWaas Agent 安装在 SNAT network namespace 中;计算和网络节点上的 FWaas Agent 安装在 qrouter network namespace 中。
5.2 VPNaas DVR
5.3 LBaas 与 DVR
总体情况:
<ignore_js_op>
6. 后续版本中 DVR 开发
6.1 Kilo 版本中
- VPNaaS 对 DVR 的支持
- 从传统 router 迁移到 DVR router
- 网络节点上 HA + DVR 支持
- VLAN 支持
6.2 Liberty 版本中
- L3 Agent 重构
- 分布式 DHCP
- 性能调优
- 分布式 SNAT
从第五和第六两个章节也可以看出,Juno 版本才添加的 DVR 功能还很不完善,难以满足生产环境的使用要求,主要是因为它还不支持目前实际部署中应该很广泛的 VLAN 组网模式,以及无法解决 HA 和 DVR 共存的问题。可喜的是这两个主要问题会在 K 版本中解决,因此 K 版本中的 DVR 至少可以用来做测试用了。等到 L 版本,实现分布式 DHCP 和 SNAT,以及性能优化以后,离生产环境的要求基本就差不多了。