1. 代碼分析
DVR 代碼修改包括幾部分:
- DVR Router network namespace 的建立和删除
- DVR Router 相關的 flows
- DVR Router 的 ARP 表
1.1 DVR Router 相關 network namespace 的建立和删除
1.1.1 qrouter 在計算和網絡節點上的删除和建立
- 對于每一個 DVR Router,在每個分布了和 router 連接配接的網段内的虛機的計算節點上,都會有一個 qrouter 執行個體。兩種情況下會将一個 DVR Router 部署到一個 L3 Agent 上:
- 當一個子網 subnet 被加入到一個 DVR Router 時,DVR Router 會被分布到所有包含在該子網内的虛機的計算節點上。
-
計算節點上的 L3 Agent 會收到一個通知,它會配置 router
OVS Agent 會将 router 的端口 plug 到 OVS Bridge 上,并且配置 flows
- 當一個虛機被建立,而且虛機所在的計算節點上不存在該虛機所在 subnet 連接配接的 DVR Router 時。
- 當與 DVR Router 相關的最後一個虛機被删除時,router namespace 會被從虛機所在的計算節點上删除。
1.1.2 snat 在網絡節點上的建立和删除
建立:當設定 router 的 external gateway 時
删除:當删除 router 的 external gateway 時
1.1.3 fip 在 計算節點上的建立和删除
建立:當一個浮動 IP 被配置設定給一個虛機的時候,如果虛機所在的計算節點上 fip namespace 不存在,則建立它
删除:(1)當計算節點上最後一個使用浮動 IP 的虛機被删除後 (2)所有虛機的浮動 IP 被删除後
1.2 DVR MAC 位址
前面提到過,分布到多個計算節點上的 qrouter 的interface 的 MAC 位址都相同。這在傳統的網絡中是不允許的,在 neutron 網絡中某些時候也會導緻一些問題。Neutron的做法是會向每個計算節點配置設定一個唯一的 DVR Host MAC 位址。當使用了 DVR 的 OVS Agent 啟動的時候,它通過 RPC 去從 neutron server 上申請該 MAC 位址。該 MAC 位址會被儲存在 DB 中,與該計算節點強綁定。比如:
MariaDB [neutron]> select * from dvr_host_macs;
+----------+-------------------+
| host | mac_address |
+----------+-------------------+
| network | fa:16:3f:12:a3:38 |
| compute1 | fa:16:3f:b2:34:82 |
| compute2 | fa:16:3f:db:6f:73 |
+----------+-------------------+
當資料包離開 DVR Router 經過 br-tun 時,OVS flows 會将 DVR Router interface 的源 MAC 位址替換成該 MAC 位址。
root@compute1:/home/s1# ovs-ofctl dump-flows br-tun | grep mod_dl_src
cookie=, duration=s, table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:ec:f3:dd actions=mod_dl_src:fa::f:b2::,resubmit(,)
cookie=, duration=s, table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e::b:c actions=mod_dl_src:fa::f:b2::,resubmit(,)
cookie=, duration=s, table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:a9:da:b5 actions=mod_dl_src:fa::f:b2::,resubmit(,)
而 src mac 位址分别是 qrouter 上的作為預設各網段的預設網關的 mac 位址:
s1@controller:~$ neutron port-list | grep fa::e:ec:f3:dd
| f849ae46--f5-a805-d4e31951 | | fa::e:ec:f3:dd | {"subnet_id": "f8841500-b392-4053-bda1-acf419f4a86e", "ip_address": "90.1.180.1"}
s1@controller:~$ neutron port-list | grep fa::e::b:c
| bdba3-b117-ce-b-bb1d039879dc | | fa::e::b:c | {"subnet_id": "4ec65731-35a5-4637-a59b-a9f2932099f1", "ip_address": "81.1.180.1"}
s1@controller:~$ neutron port-list | grep fa::e:a9:da:b5
| e47fca31-dbf6-e5-ccb-0950557a55e3 | | fa::e:a9:da:b5 | {"subnet_id": "13888749-12b3-462e-9afe-c527bd0a297e", "ip_address": "91.1.180.1"}
是以,這裡假設你不會将多個 DVR Router 連接配接到一個 subnet。當資料包達到該計算節點時,OVS flows 會将其源 MAC 位址替換成 VM gateway 的 MAC 位址。
DVR-MAC-ADDRESS 的更新是 neutron server 通過 RPC Notifier 做的。每當一個新的位址被配置設定後,它通知所有的 L3 Agent 節點做處理。
1.3 DVR OVS flows
使用 DVR Router 的計算節點上,br-int 和 br-tun 中的 flows 會有修改。具體請參見上文的 3.2.4 部分。
1.3.1 br-int flows 的主要修改
table 1: DVR_TO_SRC_MAC
table 0:LOCAL_SWITCHING
[email protected]:/home/s1# ovs-ofctl dump-flows br-int
NXST_FLOW reply (xid=):
#擷取所有的 DVR_MAC_ADDRESS,然後
# 從 patch-tun 進入的 src mac 為 network 節點的 DVR HOST MAC 的網絡幀(也就是從 network 節點來的幀),重新送出 table 1 處理
table=, n_packets=, n_bytes=, idle_age=, priority=,in_port=,dl_src=fa::f::a3: actions=resubmit(,)
# 從 compute 2 節點來的網絡幀,送出到 table 1
table=, n_packets=, n_bytes=, idle_age=, priority=,in_port=,dl_src=fa::f:db:f: actions=resubmit(,)
#本機上的虛機産生的網絡幀,走正常的轉發模式發到虛機或者 br-tun
table=, n_packets=, n_bytes=, idle_age=, priority= actions=NORMAL
#目标網段是 81.1.180.0/24 網段的幀,去掉 vlan tag,修改 src mac 為 qrouter 的 81.1.180.1 interface 的 mac 位址,發到虛機
table=, n_packets=, n_bytes=, idle_age=, priority=,ip,dl_vlan=,nw_dst=/ actions=strip_vlan,mod_dl_src:fa::e::b:c,output:
#目标網段是 90.1.180.0/24 網段的幀,去掉 vlan tag,修改 src mac 為qrouter 的 90.1.180.1 interface 的 mac 位址,發到虛機
table=, n_packets=, n_bytes=, idle_age=, priority=,ip,dl_vlan=,nw_dst=/ actions=strip_vlan,mod_dl_src:fa::e:ec:f3:dd,output:
#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=, n_packets=, n_bytes=, idle_age=, priority= actions=drop
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISO0EjN0ATN4ATNyUDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
1.3.2 br-tun flows 主要的修改
br-tun flows 的主要修改是增加了 table 1 和 9.
對于将要離開本機的網絡幀:
- Table 1 (DVR process Table): 如果網絡幀的 src mac 是本機 qrouter 上的 interface 的 mac 位址(dvr-router-intf-mac),将其修改為 DVR-compute-node-unique-mac,然後交給table 2 處理;其它的幀,交給 table 2.
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:ec:f3:dd actions=mod_dl_src:fa::f:b2::,resubmit(,)
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e::b:c actions=mod_dl_src:fa::f:b2::,resubmit(,)
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_src=fa::e:a9:da:b5 actions=mod_dl_src:fa::f:b2::,resubmit(,)
- table 2:單點傳播幀轉 table 20;多點傳播幀轉 table 22
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_dst=:::::/::::: actions=resubmit(,)
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_dst=:::::/::::: actions=resubmit(,)
- table 20:将 vlan id 轉化為 tunnel id,并根據處理進入本機的網絡幀的時候學習到的 mac 位址和 tunnel port,查找網絡幀的出口 tunnel port
table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_dst=fa::e::f8:b8 actions=strip_vlan,set_tunnel:x4,output:table=, n_packets=, n_bytes=, idle_age=, priority=,dl_vlan=,dl_dst=fa::e:::d actions=strip_vlan,set_tunnel:x4,output:
- table 22:将 vlan id 轉化為 tunnel id,并泛洪到所有的 tunnel 端口
table=, n_packets=, n_bytes=, idle_age=, hard_age=, dl_vlan= actions=strip_vlan,set_tunnel:,output:,output:table=, n_packets=, n_bytes=, idle_age=, hard_age=, dl_vlan= actions=strip_vlan,set_tunnel:,output:,output:table=, n_packets=, n_bytes=, idle_age=, priority= actions=drop
對于進入本機的網絡幀:
- table 0:交給 table 3 處理
- table 3:隻允許目的網絡為本機上的虛機所在的網絡的網絡幀,修改其 vlan id,轉 table 9
table=, n_packets=, n_bytes=, idle_age=, priority=,tun_id= actions=mod_vlan_vid:,resubmit(,) # tunnel id 轉換為 vlan id
table=, n_packets=, n_bytes=, idle_age=, priority=,tun_id= actions=mod_vlan_vid:,resubmit(,) #tunnel id 轉換為 vlan id
table=, n_packets=, n_bytes=, idle_age=, priority= actions=drop
- Table 9 (DVR Learning blocker):如果 src mac 是 DVR-Unique-MAC,不做 mac 學習,轉發到 patch-int;否則,轉到 table 10 做 mac 位址學習
table 10:mac 位址學習,結果存到 table 20
table=, n_packets=, n_bytes=, idle_age=, priority= actions=learn(table=,hard_timeout=,priority=,NXM_OF_VLAN_TCI[],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:NXM_OF_IN_PORT[]),output:
注意:在 table 20 中,除了自己通過 mac 位址學習學到的 mac 位址外,還需要借助 l2population 。這就是為什麼 DVR 依賴于 l2population 的緣故。沒有使用的話,網絡包無法發到正确的 tunnel interface。
具體設定 OVS flows 的代碼在 setup_dvr_flows_on_integ_tun_br 函數中。更詳細的說明可以參考官方文章。
1.4 qrouter 中的ARP 表
虛機 vm1 需要通過 ARP 擷取兩種 mac 位址:
- 當目标計算機(vm2)不在其同一個網段時,它需要擷取預設網關的 mac 位址,這個将由 qrouter 直接相應 arp 請求。
- 當目标計算機(vm2)在其同一個網段時,它需要直接擷取 vm2 的 mac 位址。這個應該仍然是通過 ARP 廣播獲得。簡單的做法是使用 arp responder。
qrouter 在做完 vm1 的網絡包的路由後,将網絡包從 vm2 所在網段的 interface 上發出前,需要擷取 vm2 的 mac 位址。而這個是通過它查詢自身的 ARP table 獲得的。這是 compute 1 上 qrouter netns 中的 ip neighbour 表:
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip neigh
. dev qr-f849ae46- lladdr fa::e:::0d PERMANENT
. dev qr-bdba3-b1 lladdr fa::e:c0:f:c PERMANENT
. dev qr-e47fca31-db lladdr fa::e::: PERMANENT
這裡面可以看到大量的 PERMANENT MAC 位址。這是因為,L3 Agent 配置 DVR Router 的時候,它通過 RPC 從 neturon server 擷取該 router 各 interface 的 subnet 中擷取所有虛機的 MAC 位址。當一個 subnet 被加到 DVR Router 的時候,每個相關的 L3 Agent 都會被通知到,然後它通過 RPC 擷取各 MAC 位址。當一個新的 port 被建立,或者 port 的 MAC 有更新的時候,所有相關的 L3 Agent 會被通知到去更新 ARP 表。
通過該由 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
1.5 ip rule 和 route 操作
1.5.1 增加一個 internal subnet 時
在 qrouter namespace 上:
(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
效果如下:
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
: from all lookup local
: from all lookup main
: from all lookup default
: from ./ lookup
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route list table 1359066113
default via . dev qr-bdba3-b1
這樣,當虛機還沒有配置浮動IP時,通路外網的話,網絡幀的路線為:vm —- qrouter subnet 1 interface — SNAT —- external port —– pc
是以,當 router 上連接配接有多個 subnet 時,qrouter 中也有相應數量的 ip rule 和 routing table:
[email protected]:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
: from all lookup local
: from all lookup main
: from all lookup default
: from / lookup
: from / lookup
: from / lookup
1.5.2 給虛機綁定浮動 IP 時
在 qrouter namespace 中:
(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 的情況下是這樣的結果:
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip rule
: from . lookup #去 fip 的
: from . lookup #去 fip 的
: from ./ lookup #去 snat 的
: from ./ lookup #去 snat 的
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route list table 16
default via . dev rfp-e8f12f7a- # route 是一樣的
root@compute1:/home/s1# ip netns exec fip-557e9f0c-9c66-46da-b289-218d49c218d2 ip route
default via . dev fg-b744484-
./ dev fpr-e8f12f7a- proto kernel scope link src .
./ dev fg-b744484- proto kernel scope link src .
. via . dev fpr-e8f12f7a- #每個浮動 IP 一個 route item
. via . dev fpr-e8f12f7a-
這裡能看到 qroute 的 ip rule 上,針對一個虛機/子網,有兩條 rule,一條查路由表 16 到 fip,另一條查表到 snat。但是,在有浮動 IP 的情況下,前一條政策的優先級數值将小于後後一條的,這就決定了查路由表 16,資料包走 fip。
1.5.3 qrouter 的 main 路由表
main 路由表是為虛拟子網服務的,每個 subnet 對應一條路由規則,使得目的為每個 subnet 的網絡包從指定的 qrouter 的 qr interface 上發出。
root@compute1:/home/s1# ip netns exec qrouter-e8f12f7a-6938-4e65-88c4-97e4cb211b27 ip route
./ dev qr-bdba3-b1 proto kernel scope link src . #為到子網 1 中的虛機的網絡包做路由
./ dev qr-f849ae46- proto kernel scope link src . #為到子網 2 中的虛機的網絡包做路由
./ dev qr-e47fca31-db proto kernel scope link src .
./ dev rfp-e8f12f7a- proto kernel scope link src . #為從 fip 進來的外網通路内部虛機的網絡包做路由
2. Neutron 其它服務與 DVR
2.1 FWaas DVR
DVR 與傳統的 FWaas 不相容,因為它作用于neuron 網絡節點上的 virtual router,過濾進出租戶網絡的網絡包。傳統的 FWaas 可以參考我的另一篇文章。
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 中。
2.2 VPNaas DVR
Juno 版本中 VPNaas 不支援 DVR,隻支援傳統的 router。Kilo 版本中會實作 VPNaas 對 DVR 的支援。新的 VPN 服務隻會在 dvr_snat 節點上的 snat namespace 上運作。
2.3 LBaas 與 DVR
兩者之間沒有互相依賴關系,是以 DVR 對 LBaas 沒有影響。
總體情況:
3. 後續版本中 DVR 開發
3.1 Kilo 版本中
- VPNaaS 對 DVR 的支援
- 從傳統 router 遷移到 DVR router
- 網絡節點上 HA + DVR 支援
- VLAN 支援
3.2 Liberty 版本中
- L3 Agent 重構
- 分布式 DHCP
- 性能調優
- 分布式 SNAT
從前兩個章節也可以看出,Juno 版本才添加的 DVR 功能還很不完善,難以滿足生産環境的使用要求,主要是因為它還不支援目前實際部署中應該很廣泛的 VLAN 組網模式,以及無法解決 HA 和 DVR 共存的問題。可喜的是這兩個主要問題會在 K 版本中解決,是以 K 版本中的 DVR 至少可以用來做測試用了。到了 L 版本,實作分布式 DHCP 和 SNAT,以及性能優化以後,離生産環境的要求基本就差不多了。
劉世民, 雲架構師,十餘年IT行業從業經驗,在電信軟體、企業軟體、存儲以及雲計算等領域從事過研發、管理和架構設計等方面的工作。從2012年開始從事 OpenStack 研發工作,對其核心子產品、相關開源技術和産品,以及行業等都有着較深的了解。
責編:陳晨 聯系請添加微信:violace95 備注公司 職位 姓名。尋求報道或投稿,請請發送至郵箱:[email protected]