基于Linux內(nèi)核新特性的網(wǎng)關(guān)設(shè)計(jì)實(shí)踐
UCloud 外網(wǎng)網(wǎng)關(guān)是為了承載外網(wǎng)IP、負(fù)載均衡等產(chǎn)品的外網(wǎng)出入向流量,當(dāng)前基于 Linux 內(nèi)核的 OVS/GRE 隧道/netns/iptables 等實(shí)現(xiàn),很好地支撐了現(xiàn)有業(yè)務(wù)。同時(shí),我們也在不斷跟蹤 Linux 內(nèi)核社區(qū)的新技術(shù)發(fā)展,并將之用于下一代外網(wǎng)網(wǎng)關(guān)的設(shè)計(jì)。這些新特性可將系統(tǒng)性能和管理能力再提上一檔,滿(mǎn)足未來(lái)幾年的需求。在方案設(shè)計(jì)研發(fā)過(guò)程中發(fā)現(xiàn),新特性存在不少缺陷和 Bug,為此我們向 Linux 內(nèi)核社區(qū)回饋了 10 多個(gè)補(bǔ)丁,并融入到 Linux 內(nèi)核 5.0 版本中,幫助完善內(nèi)核功能并提升穩(wěn)定性。
當(dāng)前業(yè)界的多租戶(hù)外網(wǎng)網(wǎng)關(guān)很多都是基于 OpenFlow 的 OpenvSwitch(OVS)方案,然而隨著內(nèi)核路由轉(zhuǎn)發(fā)功能的不斷完善,利用內(nèi)核原生路由轉(zhuǎn)發(fā)方式進(jìn)行設(shè)計(jì)多租戶(hù)外網(wǎng)網(wǎng)關(guān)系統(tǒng)成為一種可能。在這種方式下能有效的使用傳統(tǒng) iproute2 路由工具以及 iptables、nftables 等防火墻工具,并且隨著 SwitchDev 技術(shù)的興起,未來(lái)將網(wǎng)關(guān)系統(tǒng)遷移到 Linux Switch 上也成為一種可能。
現(xiàn)有 Linux 內(nèi)核 3.x 的不足
當(dāng)前廣泛使用的內(nèi)核版本為 3.x 系列,例如 CentOS 7 全系列標(biāo)準(zhǔn)支持的內(nèi)核為 3.10 版本,F(xiàn)edora/Ubuntu 等 Linux 發(fā)行版也有大量使用。在 3.x 系列內(nèi)核下存在著 IP 隧道管理復(fù)雜、租戶(hù)隔離性能損耗等問(wèn)題。
- IP 隧道管理復(fù)雜
Linux 內(nèi)核創(chuàng)建 IP 隧道設(shè)備來(lái)建立點(diǎn)對(duì)點(diǎn)的隧道連接,創(chuàng)建時(shí)需指定隧道目標(biāo)和隧道密鑰。因?yàn)樗拗鳈C(jī)之間兩兩建立連接,面向宿主機(jī)的目的地址眾多,這樣就會(huì)導(dǎo)致網(wǎng)關(guān)節(jié)點(diǎn)上需要?jiǎng)?chuàng)建成千上萬(wàn)的隧道設(shè)備,在大規(guī)模業(yè)務(wù)環(huán)境下,隧道的管理將變得及其復(fù)雜。 - 多租戶(hù)隔離導(dǎo)致的性能下降
- 公有云需要實(shí)現(xiàn)多租戶(hù)隔離以確保用戶(hù)間的安全和隱私
由于 VPC 網(wǎng)絡(luò)下不同租戶(hù)的內(nèi)網(wǎng)地址可以重合,導(dǎo)致路由也有重合的可能性,此時(shí)需要通過(guò)大量的策略路由去隔離租戶(hù)的路由規(guī)則,由于策略路由的鏈表屬性,性能會(huì)隨著鏈表長(zhǎng)度的增加而急劇下降。 - 由于防火墻和 NAT 的實(shí)現(xiàn)基于同樣鏈?zhǔn)降?iptables,性能損耗同樣可觀。
- netns 帶來(lái)性能開(kāi)銷(xiāo)
通過(guò) netns 實(shí)現(xiàn)租戶(hù)路由和防火墻規(guī)則的隔離,但是 netns 會(huì)引入虛擬網(wǎng)卡和協(xié)議棧重入開(kāi)銷(xiāo),使整體性能下降 20% 左右。
三項(xiàng)內(nèi)核新技術(shù)
為了解決原有方案存在的困擾,我們調(diào)研了大量行業(yè)主流方案和內(nèi)核上游的新動(dòng)向,發(fā)現(xiàn)輕量級(jí)隧道(簡(jiǎn)稱(chēng) lwtunnel)、虛擬路由轉(zhuǎn)發(fā)(簡(jiǎn)稱(chēng) VRF)以及 nftable & netfilter 流卸載三項(xiàng)內(nèi)核新技術(shù)的特性,可以幫助規(guī)避原方案存在的缺陷。
1、輕量級(jí)隧道
Linux 內(nèi)核在 4.3 版本中引入了輕量級(jí)隧道,它提供了通過(guò)路由方式設(shè)置隧道屬性的方法,這樣可以避免管理大量的隧道設(shè)備。
創(chuàng)建隧道設(shè)備時(shí)指定 external
模式,利用路由設(shè)置的輕量級(jí)隧道通過(guò) tun
設(shè)備發(fā)送報(bào)文。
# 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、虛擬路由轉(zhuǎn)發(fā)
Linux 內(nèi)核在 4.3 版本中引入了 VRF 的初步支持,并在 4.8 版本形成完備版本。虛擬路由轉(zhuǎn)發(fā)可以將一臺(tái) Linux Box 的物理路由器當(dāng)多臺(tái)虛擬路由器使用,能很好的解決租戶(hù)路由隔離問(wèn)題,避免直接使用策略路由。因此,可以將不同租戶(hù)的網(wǎng)卡加入租戶(hù)所屬的虛擬路由器中來(lái)實(shí)現(xiàn)多租戶(hù)的虛擬路由。
# 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 是一種新的數(shù)據(jù)包分類(lèi)框架,旨在替代現(xiàn)存的 {ip,ip6,arp,eb}_tables。在 nftables 中,大部分工作是在用戶(hù)態(tài)完成的,內(nèi)核只知道一些基本指令(過(guò)濾是用偽狀態(tài)機(jī)實(shí)現(xiàn)的)。nftables 的一個(gè)高級(jí)特性就是映射,可以使用不同類(lèi)型的數(shù)據(jù)并映射它們。例如,我們可以映射 iif 設(shè)備到專(zhuān)用的規(guī)則集合(之前創(chuàng)建的存儲(chǔ)在一個(gè)鏈中)。由于是哈希映射的方式,可以***的避免鏈?zhǔn)揭?guī)則跳轉(zhuǎn)的性能開(kāi)銷(xiāo)。
Linux 內(nèi)核在版本 4.16 引入了流卸載功能,它為 IP 轉(zhuǎn)發(fā)提供了基于流的卸載功能。當(dāng)一條新建連接完成首回合原方向和反方向的報(bào)文時(shí),完成路由,防火墻和 NAT 工作后,在處理反方向首報(bào)文的 forward
鉤子,根據(jù)報(bào)文路由、NAT 等信息創(chuàng)建可卸載流到接收網(wǎng)卡 ingress
鉤子上。后續(xù)的報(bào)文可以在接收 ingress
鉤子上直接轉(zhuǎn)發(fā),不需要再進(jìn)入 IP 棧處理。此外,將來(lái)流卸載還將支持硬件卸載模式,這將極大提高系統(tǒng)轉(zhuǎn)發(fā)性能。
# 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
方案設(shè)計(jì)與優(yōu)化實(shí)踐
通過(guò)對(duì)上述三項(xiàng)新技術(shù)的研究,我們發(fā)現(xiàn)可以嘗試設(shè)計(jì)一套基于路由的方式,實(shí)現(xiàn)多租戶(hù)層疊網(wǎng)絡(luò)的外網(wǎng)網(wǎng)關(guān)。在方案設(shè)計(jì)過(guò)程中,我們也碰到了諸如 lwtunnel 和流卸載功能不足,以及 VRF 和流卸載不能一起有效的工作等問(wèn)題。最終我們都設(shè)法解決了,并針對(duì)這些內(nèi)核的不足提交補(bǔ)丁給 Linux 內(nèi)核社區(qū)。
1、lwtunnel 發(fā)送報(bào)文 tunnel_key 丟失
問(wèn)題描述:我們利用 lwtunnel 路由方式發(fā)送報(bào)文時(shí),創(chuàng)建了一個(gè) external 類(lèi)型的 gretap 隧道,我們將命令設(shè)置了 id 為 1000,但是發(fā)送成功報(bào)文中沒(méi)有 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
問(wèn)題定位:我們研究 iproute2 代碼,發(fā)現(xiàn)由于 TUNNEL_KEY
的標(biāo)志并沒(méi)有開(kāi)放給用戶(hù)態(tài),所以 iproute2 工具并沒(méi)有對(duì) lwtunnel 路由設(shè)置 TUNNEL_KEY
,導(dǎo)致報(bào)文不會(huì)創(chuàng)建 tunnel_key
字段。
提交補(bǔ)?。何覀兘o內(nèi)核和用戶(hù)態(tài) iproute2 分別提交補(bǔ)丁來(lái)解決這一問(wèn)題:
提交補(bǔ)丁后,可以通過(guò)以下方式設(shè)置路由:
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 對(duì)指定密鑰的 IP 隧道無(wú)效
問(wèn)題發(fā)現(xiàn):為了能有效隔離租戶(hù)路由,我們給每個(gè)租戶(hù)創(chuàng)建一個(gè)基于 tunnel_key
的 gretap 隧道設(shè)備。如下圖,創(chuàng)建一個(gè) tunnel_key
1000 的 gretap 隧道設(shè)備,把隧道設(shè)備加入租戶(hù)所屬 VRF,隧道設(shè)備能有效地接收?qǐng)?bào)文,但并不能發(fā)送報(bào)文。
# 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
問(wèn)題定位:研究?jī)?nèi)核發(fā)現(xiàn),IP 隧道在非外部模式下即使指定了輕量級(jí)隧道路由,發(fā)送報(bào)文也沒(méi)有使用它,導(dǎo)致報(bào)文路由錯(cuò)誤被丟棄。
提交補(bǔ)?。?/p>
提交補(bǔ)丁后,在未指定 tunnel_dst 的非外部模式 IP 隧道下,能使用輕量級(jí)隧道路由進(jìn)行發(fā)送報(bào)文。
3、外部 IP 隧道 ARP 無(wú)法正常運(yùn)行
問(wèn)題描述:鄰居 IP 隧道進(jìn)行了 ARP 請(qǐng)求,但是本端的 ARP 回應(yīng)報(bào)文的隧道頭中并沒(méi)帶 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
問(wèn)題定位:研究代碼發(fā)現(xiàn),隧道收到了對(duì)端的 ARP 請(qǐng)求,在發(fā)送報(bào)文 ARP 回復(fù)的時(shí)候會(huì)復(fù)制請(qǐng)求報(bào)文的隧道信息,但是遺漏了所有 tun_flags。
提交補(bǔ)?。?/p>
4、流卸載不能與 DNAT 有效工作
問(wèn)題描述:防火墻創(chuàng)建規(guī)則從 eth0 收到目的地址 2.2.2.11 的報(bào)文,DNAT 為 10.0.0.7, 流卸載無(wú)法工作。
問(wèn)題定位:分析發(fā)現(xiàn),客戶(hù)端 1.1.1.7 -> 2.2.2.7 DNAT 到服務(wù)器 10.0.0.7,***個(gè)回復(fù)的反向報(bào)文(syc+ack)使用了錯(cuò)的目的地址獲取反向路由。
daddr = ct->tuplehash[!dir].tuple.dst.u3.ip
此時(shí) dir
為反方向,所以 daddr
獲取為原方向的目的地址,這個(gè)值是 2.2.2.7,但是由于被 DNAT過(guò),真正的路由不應(yīng)該通過(guò) 2.2.2.7 去獲取,而是應(yīng)該根據(jù) 10.0.0.7 這個(gè)值去獲取。
addr = ct->tuplehash[dir].tuple.src.u3.ip
提交補(bǔ)?。?/p>
5、流卸載不能與 VRF 有效工作
問(wèn)題描述:將網(wǎng)卡 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
問(wèn)題定位:查看代碼發(fā)現(xiàn),原方向和反方向首報(bào)文進(jìn)入?yún)f(xié)議堆棧后 skb->dev 會(huì)設(shè)置為 vrf device user1,創(chuàng)建流卸載規(guī)則的 iif
就是 user1。但是卸載規(guī)則下發(fā)在 eth0 和 eth1 的 ingress 鉤子上,所以后續(xù)報(bào)文在 eth0 和 eth1 的 ingress 鉤子上不能匹配流規(guī)則規(guī)則。
提交補(bǔ)丁:
最終,我們根據(jù)兩個(gè)方向查找路由的結(jié)果,設(shè)置流卸載規(guī)則的 iif
和 oif
信息來(lái)解決此問(wèn)題。
6、VRF PREROUTING 鉤子重入問(wèn)題
問(wèn)題描述:配置網(wǎng)卡加入 VRF,防火墻 ingress 方向規(guī)則為接收目的地址 2.2.2.11 、TCP 目的端口 22 的報(bào)文,egress 方向規(guī)則為丟棄 TCP 目的端口 22 的報(bào)文。出現(xiàn)異常結(jié)果: 收到目的地址 2.2.2.11 TCP 22 目的端口的報(bào)文卻被丟棄。
問(wèn)題定位:研究發(fā)現(xiàn)網(wǎng)卡加入 VRF 后收到的報(bào)文會(huì)兩次進(jìn)入 PREROUTING 鉤子,因?yàn)樵谶M(jìn)入 IP 棧時(shí)會(huì)進(jìn)***次PREROUTING 鉤子,然后被 VRF 設(shè)備接管后會(huì)再次進(jìn)入 PREROUTING 鉤子。上述規(guī)則***次在 rule-1000-ingress chain中 dst nat 為 10.0.0.7,第二次由于報(bào)文被 DNAT 后會(huì)錯(cuò)誤的進(jìn)入 rule-1000-egress,導(dǎo)致報(bào)文被丟棄。
提交補(bǔ)?。何覀兘o內(nèi)核加了一個(gè)支持判斷網(wǎng)卡類(lèi)型的 match 項(xiàng)目,讓用戶(hù)態(tài)避免可知的第二次無(wú)效重入,內(nèi)核態(tài)和用戶(hù)態(tài) nftables 分別提交了如下的補(bǔ)丁:
使用方法:
nft add rule firewall rules-all meta iifkind "vrf" counter accept
原型驗(yàn)證
最終,我們成功地利用 lwtunnel、VRF 和流卸載實(shí)現(xiàn)多租戶(hù)外網(wǎng)網(wǎng)關(guān)的原型驗(yàn)證。驗(yàn)證過(guò)程如下:
1、首先創(chuàng)建原型環(huán)境
a. netns cl 模擬外網(wǎng)客戶(hù)端,地址為 1.1.1.7,隧道源地址 172.168.0.7,配置發(fā)送路由;
b. netns ns1 模擬租戶(hù) 1,內(nèi)網(wǎng)地址為 10.0.0.7,外網(wǎng)地址為 2.2.2.11,隧道源地址 172.168.0.11 tunnel_key 1000,配置發(fā)送路由;
c. netns ns2 模擬租戶(hù) 2,內(nèi)網(wǎng)地址為 10.0.0.7,外網(wǎng)地址為 2.2.2.12,隧道源地址 172.168.0.12 tunnel_key 2000,配置發(fā)送路由;
d. Host 模擬外網(wǎng)網(wǎng)關(guān),隧道源地址 172.168.0.1,創(chuàng)建租戶(hù) VRF user1 和 use2,創(chuàng)建租戶(hù) IP 隧道 tun1 和 tun2,配置轉(zhuǎn)發(fā)路由。
原型環(huán)境圖如下:
2、創(chuàng)建防火墻規(guī)則
a. 租戶(hù) 1 入向允許 TCP 目的端口 22 和 ICMP 訪問(wèn),出向禁止訪問(wèn)外部 TCP 22 目的端口;
b. 租戶(hù) 2 入向允許 TCP 端口 23 和 ICMP 訪問(wèn),出向禁止訪問(wèn)外部 TCP 23 目的端口;
c. 在租戶(hù) tun1 和 tun2 設(shè)備上支持流卸載。
最終,客戶(hù)端可以通過(guò) 2.2.2.11 成功訪問(wèn) user1 tcp 22 端口服務(wù),user1 不能訪問(wèn)客戶(hù)端 tcp 22 端口服務(wù);客戶(hù)端可以通過(guò) 2.2.2.12 成功訪問(wèn) user2 tcp 23 端口服務(wù),user1 不能訪問(wèn)客戶(hù)端 tcp 23 端口服務(wù)。
在后續(xù)硬件功能完善以及網(wǎng)卡廠商支持后,我們還會(huì)做進(jìn)一步的開(kāi)發(fā)驗(yàn)證。
寫(xiě)在***
以上是本項(xiàng)目涉及的部分核心問(wèn)題,這些補(bǔ)丁特性都可以在 Linux 內(nèi)核 5.0 版本里獲取。我們把這期間為 Linux 內(nèi)核社區(qū)貢獻(xiàn)的補(bǔ)丁整理成了一份列表,希望能為開(kāi)發(fā)者提供幫助,讀者可以點(diǎn)擊閱覽。
Linux 作為成熟的開(kāi)源套件,一直是云廠商使用的主流操作系統(tǒng),但在技術(shù)的更新迭代過(guò)程中,一些新特性在實(shí)際應(yīng)用上也會(huì)存在穩(wěn)定性、兼容性等方面的問(wèn)題。我們?cè)谘芯渴褂蒙嫌渭夹g(shù)的同時(shí),也一直積極探索、豐富開(kāi)源技術(shù)功能,幫助提高開(kāi)源技術(shù)穩(wěn)定性。并將產(chǎn)出持續(xù)回饋給社區(qū),與社區(qū)共同構(gòu)建一個(gè)繁榮的開(kāi)源生態(tài)。