UCloud 外網閘道器是為了承載外網IP、負載均衡等產品的外網出入向流量,當前基於 Linux 內核的 OVS/GRE 隧道/netns/iptables 等實現,很好地支撐了現有業務。同時,我們也在不斷跟蹤 Linux 核心社群的新技術發展,並將之用於下一代外網閘道器的設計。這些新特性可將系統效能和管理能力再提上一檔,滿足未來幾年的需求。在方案設計研發過程中發現,新特性存在不少缺陷和 Bug,為此我們向 Linux 核心社群回饋了 10 多個補丁,並融入到 Linux 核心 5.0 版本中,幫助完善核心功能並提升穩定性。
當前業界的多租戶外網閘道器很多都是基於 OpenFlow 的 OpenvSwitch(OVS)方案,然而隨著核心路由轉發功能的不斷完善,利用核心原生路由轉發方式進行設計多租戶外網閘道器係統成為一種可能。在這種方式下能有效的使用傳統 iproute2 路由工具以及 iptables、nftables 等防火牆工具,並且隨著 SwitchDev 技術的興起,未來將閘道器係統遷移到 Linux Switch 上也成為一種可能。
現有 Linux 核心 3.x 的不足
當前廣泛使用的核心版本為 3.x 系列,例如 CentOS 7 全系列標準支援的核心為 3.10 版本,Fedora/Ubuntu 等 Linux 發行版也有大量使用。在 3.x 系列內核下存在著 IP 隧道管理複雜、租戶隔離效能損耗等問題。
Linux 核心建立 IP 隧道裝置來建立點對點的隧道連線,建立時需指定隧道標的和隧道金鑰。因為宿主機之間兩兩建立連線,面向宿主機的目的地址眾多,這樣就會導致閘道器節點上需要建立成千上萬的隧道裝置,在大規模業務環境下,隧道的管理將變得及其複雜。
由於 VPC 網路下不同租戶的內網地址可以重合,導致路由也有重合的可能性,此時需要透過大量的策略路由去隔離租戶的路由規則,由於策略路由的連結串列屬性,效能會隨著連結串列長度的增加而急劇下降。
透過 netns 實現租戶路由和防火牆規則的隔離,但是 netns 會引入虛擬網絡卡和協議棧重入開銷,使整體效能下降 20% 左右。
三項核心新技術
為瞭解決原有方案存在的困擾,我們調研了大量行業主流方案和核心上游的新動向,發現輕量級隧道(簡稱 lwtunnel)、虛擬路由轉發(簡稱 VRF)以及 nftable & netfilter 流解除安裝三項核心新技術的特性,可以幫助規避原方案存在的缺陷。
1、輕量級隧道
Linux 核心在 4.3 版本中引入了輕量級隧道,它提供了透過路由方式設定隧道屬性的方法,這樣可以避免管理大量的隧道裝置。
建立隧道裝置時指定 external
樣式,利用路由設定的輕量級隧道透過 tun
裝置傳送報文。
-
# ip l add dev tun type gretap external
-
# ifconfig tun 1.1.1.7/24 up
-
# ip r r 2.2.2.11 via 1.1.1.11 dev tun encap ip id 1000 dst 172.168.0.1 key
2、虛擬路由轉發
Linux 核心在 4.3 版本中引入了 VRF 的初步支援,併在 4.8 版本形成完備版本。虛擬路由轉發可以將一臺 Linux Box 的物理路由器當多臺虛擬路由器使用,能很好的解決租戶路由隔離問題,避免直接使用策略路由。因此,可以將不同租戶的網絡卡加入租戶所屬的虛擬路由器中來實現多租戶的虛擬路由。
-
# ip link add user1 type vrf table1
-
# ip link add user1 type vrf table2
-
# ip l set user1 up
-
# ip l set user2 up
-
# ip l set dev eth1 master user1
-
# ip l set dev eth1 master user2
-
# ip r a default via 192.168.0.1 dev eth1 table 1 onlink
-
# ip r a default via 192.168.0.1 dev eth1 table 2 onlink
3、流解除安裝
nftables 是一種新的資料包分類框架,旨在替代現存的 {ip,ip6,arp,eb}_tables。在 nftables 中,大部分工作是在使用者態完成的,核心只知道一些基本指令(過濾是用偽狀態機實現的)。nftables 的一個高階特性就是對映,可以使用不同型別的資料並對映它們。例如,我們可以對映 iif 裝置到專用的規則集合(之前建立的儲存在一個鏈中)。由於是雜湊對映的方式,可以完美的避免鏈式規則跳轉的效能開銷。
Linux 核心在版本 4.16 引入了流解除安裝功能,它為 IP 轉發提供了基於流的解除安裝功能。當一條新建連線完成首回合原方向和反方向的報文時,完成路由,防火牆和 NAT 工作後,在處理反方向首報文的 forward
鉤子,根據報文路由、NAT 等資訊建立可解除安裝流到接收網絡卡 ingress
鉤子上。後續的報文可以在接收 ingress
鉤子上直接轉發,不需要再進入 IP 棧處理。此外,將來流解除安裝還將支援硬體解除安裝樣式,這將極大提高系統轉發效能。
-
# nft add table firewall
-
# nft add flowtable f fb1 { hook ingress priority 0 \; devices = { eth0, eth1 } \; }
-
# nft add chain f ftb-all {type filter hook forward priority 0 \; policy accept \; }
-
# nft add rule f ftb-all ct zone 1 ip protocol tcp flow offload @fb1
方案設計與最佳化實踐
透過對上述三項新技術的研究,我們發現可以嘗試設計一套基於路由的方式,實現多租戶層疊網路的外網閘道器。在方案設計過程中,我們也碰到了諸如 lwtunnel 和流解除安裝功能不足,以及 VRF 和流解除安裝不能一起有效的工作等問題。最終我們都設法解決了,並針對這些內核的不足提交補丁給 Linux 核心社群。
1、lwtunnel 傳送報文 tunnel_key 丟失
問題描述:我們利用 lwtunnel 路由方式傳送報文時,建立了一個 external 型別的 gretap 隧道,我們將命令設定了 id 為 1000,但是傳送成功報文中沒有 tunnel_key
欄位。
-
# ip l add dev tun type gretap
-
# ifconfig tun 1.1.1.7/24 up
-
# ip r r 2.2.2.11 via 1.1.1.11 dev tun encap ip id 1000 dst 172.168.0.1
問題定位:我們研究 iproute2 程式碼,發現由於 TUNNEL_KEY
的標誌並沒有開放給使用者態,所以 iproute2 工具並沒有對 lwtunnel 路由設定 TUNNEL_KEY
,導致報文不會建立 tunnel_key
欄位。
提交補丁:我們給內核和使用者態 iproute2 分別提交補丁來解決這一問題:
提交補丁後,可以透過以下方式設定路由:
-
ip r r 2.2.2.11 via 1.1.1.11 dev tun encap ip id 1000 dst 172.168.0.1 key
2、lwtunnel 對指定金鑰的 IP 隧道無效
問題發現:為了能有效隔離租戶路由,我們給每個租戶建立一個基於 tunnel_key
的 gretap 隧道裝置。如下圖,建立一個 tunnel_key
1000 的 gretap 隧道裝置,把隧道裝置加入租戶所屬 VRF,隧道裝置能有效地接收報文,但並不能傳送報文。
-
# ip l add dev tun type gretap key 1000
-
# ifconfig tun 1.1.1.7/24 up
-
# ip r r 2.2.2.11 via 1.1.1.11 dev tun encap ip id 1000 dst 172.168.0.1 key
問題定位:研究核心發現,IP 隧道在非外部樣式下即使指定了輕量級隧道路由,傳送報文也沒有使用它,導致報文路由錯誤被丟棄。
提交補丁:
提交補丁後,在未指定 tunnel_dst 的非外部樣式 IP 隧道下,能使用輕量級隧道路由進行傳送報文。
3、外部 IP 隧道 ARP 無法正常執行
問題描述:鄰居 IP 隧道進行了 ARP 請求,但是本端的 ARP 回應報文的隧道頭中並沒帶 tunnel_key
欄位。
-
# ip l add dev tun type gretap external
-
# ifconfig tun 1.1.1.7/24 up
-
# ip r r 2.2.2.11 via 1.1.1.11 dev tun encap ip id 1000 dst 172.168.0.1 key
問題定位:研究程式碼發現,隧道收到了對端的 ARP 請求,在傳送報文 ARP 回覆的時候會複製請求報文的隧道資訊,但是遺漏了所有 tun_flags。
提交補丁:
4、流解除安裝不能與 DNAT 有效工作
問題描述:防火牆建立規則從 eth0 收到目的地址 2.2.2.11 的報文,DNAT 為 10.0.0.7, 流解除安裝無法工作。
問題定位:分析發現,客戶端 1.1.1.7 -> 2.2.2.7 DNAT 到伺服器 10.0.0.7,第一個回覆的反向報文(syc+ack)使用了錯的目的地址獲取反向路由。
-
daddr = ct->tuplehash[!dir].tuple.dst.u3.ip
此時 dir
為反方向,所以 daddr
獲取為原方向的目的地址,這個值是 2.2.2.7,但是由於被 DNAT過,真正的路由不應該透過 2.2.2.7 去獲取,而是應該根據 10.0.0.7 這個值去獲取。
-
addr = ct->tuplehash[dir].tuple.src.u3.ip
提交補丁:
5、流解除安裝不能與 VRF 有效工作
問題描述:將網絡卡 eth0 和 eth1 加入 VFR 後,流解除安裝不起作用。
-
# ip addr add dev eth0 1.1.1.1/24
-
# ip addr add dev eth1 1.1.1.1/24
-
# ip link add user1 type vrf table 1
-
# ip l set user1 up
-
# ip l set dev eth0 master user1
-
# ip l set dev eth1 master user1
問題定位:檢視程式碼發現,原方向和反方向首報文進入協議堆疊後 skb->dev 會設定為 vrf device user1,建立流解除安裝規則的 iif
就是 user1。但是解除安裝規則下發在 eth0 和 eth1 的 ingress 鉤子上,所以後續報文在 eth0 和 eth1 的 ingress 鉤子上不能匹配流規則規則。
提交補丁:
最終,我們根據兩個方向查詢路由的結果,設定流解除安裝規則的 iif
和 oif
資訊來解決此問題。
6、VRF PREROUTING 鉤子重入問題
問題描述:配置網絡卡加入 VRF,防火牆 ingress 方向規則為接收目的地址 2.2.2.11 、TCP 目的埠 22 的報文,egress 方向規則為丟棄 TCP 目的埠 22 的報文。出現異常結果: 收到目的地址 2.2.2.11 TCP 22 目的埠的報文卻被丟棄。
問題定位:研究發現網絡卡加入 VRF 後收到的報文會兩次進入 PREROUTING 鉤子,因為在進入 IP 棧時會進第一次PREROUTING 鉤子,然後被 VRF 裝置接管後會再次進入 PREROUTING 鉤子。上述規則第一次在 rule-1000-ingress chain中 dst nat 為 10.0.0.7,第二次由於報文被 DNAT 後會錯誤的進入 rule-1000-egress,導致報文被丟棄。
提交補丁:我們給核心加了一個支援判斷網絡卡型別的 match 專案,讓使用者態避免可知的第二次無效重入,核心態和使用者態 nftables 分別提交瞭如下的補丁:
使用方法:
-
nft add rule firewall rules-all meta iifkind "vrf" counter accept
原型驗證
最終,我們成功地利用 lwtunnel、VRF 和流解除安裝實現多租戶外網閘道器的原型驗證。驗證過程如下:
1、首先建立原型環境
a. netns cl 模擬外網客戶端,地址為 1.1.1.7,隧道源地址 172.168.0.7,配置傳送路由;
b. netns ns1 模擬租戶 1,內網地址為 10.0.0.7,外網地址為 2.2.2.11,隧道源地址 172.168.0.11 tunnel_key 1000,配置傳送路由;
c. netns ns2 模擬租戶 2,內網地址為 10.0.0.7,外網地址為 2.2.2.12,隧道源地址 172.168.0.12 tunnel_key 2000,配置傳送路由;
d. Host 模擬外網閘道器,隧道源地址 172.168.0.1,建立租戶 VRF user1 和 use2,建立租戶 IP 隧道 tun1 和 tun2,配置轉發路由。
原型環境圖如下:
2、建立防火牆規則
a. 租戶 1 入向允許 TCP 目的埠 22 和 ICMP 訪問,出向禁止訪問外部 TCP 22 目的埠;
b. 租戶 2 入向允許 TCP 埠 23 和 ICMP 訪問,出向禁止訪問外部 TCP 23 目的埠;
c. 在租戶 tun1 和 tun2 裝置上支援流解除安裝。
最終,客戶端可以透過 2.2.2.11 成功訪問 user1 tcp 22 埠服務,user1 不能訪問客戶端 tcp 22 埠服務;客戶端可以透過 2.2.2.12 成功訪問 user2 tcp 23 埠服務,user1 不能訪問客戶端 tcp 23 埠服務。
在後續硬體功能完善以及網絡卡廠商支援後,我們還會做進一步的開發驗證。
寫在最後
以上是本專案涉及的部分核心問題,這些補丁特性都可以在 Linux 核心 5.0 版本里獲取。我們把這期間為 Linux 核心社群貢獻的補丁整理成了一份串列[9],希望能為開發者提供幫助,讀者可以點選閱覽。
Linux 作為成熟的開源套件,一直是雲廠商使用的主流作業系統,但在技術的更新迭代過程中,一些新特性在實際應用上也會存在穩定性、相容性等方面的問題。我們在研究使用上游技術的同時,也一直積極探索、豐富開源技術功能,幫助提高開源技術穩定性。並將產出持續回饋給社群,與社群共同構建一個繁榮的開源生態。