UCloud外網網關是爲了承載外網IP、負載均衡等產品的外網出入向流量,當前基於Linux內核的OVS/GRE tunnel/netns/iptables等實現,很好地支撐了現有業務。同時,咱們也在不斷跟蹤開源社區的新技術發展,並將之用於下一代外網網關的設計。這些新特性可將系統性能和管理能力再提上一檔,知足將來幾年的需求。在方案設計研發過程當中發現,新特性存在很多缺陷和Bug,爲此咱們向開源社區回饋了10多個patch,並融入到kernel 5.0版本中,幫助完善kernel功能並提高穩定性。linux
當前業界的多租戶外網網關不少都是基於OpenFlow的OpenvSwitch(OVS)方案,然而隨着內核路由轉發功能的不斷完善,利用內核原生路由轉發方式進行設計多租戶外網網關係統成爲一種可能。在這種方式下能有效的使用傳統iproute2路由工具以及iptables、nftables等Firewall工具,而且隨着SwitchDev技術的興起,將來將網關係統遷移到Linux Switch上也成爲一種可能。git
現有kernel 3.x的不足api
當前普遍使用的內核版本爲3.x系列,例如CentOS 7全系列標準支持的內核爲3.10版本,Fedora/Ubuntu等Linux發行版也有大量使用。在3.x系列內核下存在着IP tunnel管理複雜、租戶隔離性能損耗等問題。安全
1. IP tunnel管理複雜網絡
Linux內核建立IP tunnel設備來創建點對點的隧道鏈接,建立時需指定tunnel dst和 tunnel key。由於宿主機之間兩兩創建鏈接,面向宿主機的目的地址衆多,這樣就會致使網關節點上須要建立成千上萬的tunnel設備,在大規模業務環境下,tunnel的管理將變得及其複雜。負載均衡
2. 多租戶隔離致使的性能降低框架
a. 公有云須要實現多租戶隔離以確保用戶間的安全和隱私。因爲VPC網絡下不一樣租戶的內網地址能夠重合,致使路由也有重合的可能性,此時須要經過大量的策略路由去隔離租戶的路由規則,因爲策略路由的鏈表屬性,性能會隨着鏈表長度的增長而急劇降低。tcp
b. 因爲Firewall和NAT的實現基於一樣鏈式的iptables,性能損耗一樣可觀。工具
3. netns帶來性能開銷性能
經過netns實現租戶路由和Firewall規則的隔離,可是netns會引入虛擬網卡和協議棧重入開銷,使總體性能降低20%左右。
三項內核新技術
爲了解決原有方案存在的困擾,咱們調研了大量行業主流方案和內核上游的新動向,發現Lightweight tunneling(輕量級隧道,簡稱lwtunnel)、Virtual Routing Forwarding(虛擬路由轉發,簡稱VRF)以及nftable & netfilter flow offload(流卸載)三項內核新技術的特性,能夠幫助規避原方案存在的缺陷。
1. Lightweight tunneling
Linux內核在4.3版本中引入了輕量級隧道Lightweight tunneling,它提供了經過route方式設置tunnel屬性的方法,這樣能夠避免管理大量的tunnel設備。
建立隧道設備時指定external模式,利用路由設置的輕量級隧道經過tun設備發送報文。
2. Virtual Routing Forwarding
Linux內核在4.3版本中引入了VRF的初步支持,並在4.8版本造成完備版本。Virtual Routing Forwarding虛擬路由轉發,能夠將一臺Linux Box的物理路由器當多臺虛擬路由器使用,能很好的解決租戶路由隔離問題,避免直接使用策略路由。所以,能夠將不一樣租戶的網卡加入租戶所屬的虛擬路由器中來實現多租戶的虛擬路由。
3. flow offload
Nftables是一種新的數據包分類框架,旨在替代現存的{ip,ip6,arp,eb}_tables。在nftables中,大部分工做是在用戶態完成的,內核只知道一些基本指令(過濾是用僞狀態機實現的)。nftables的一個高級特性就是映射,可使用不一樣類型的數據並映射它們。例如,咱們能夠映射iif device到專用的規則集合(以前建立的存儲在一個鏈中)。因爲是hash映射的方式,能夠完美的避免鏈式規則跳轉的性能開銷。
Linux內核在版本4.16引入了flow offload功能,它爲IP forward提供了基於流的卸載功能。當一條新建鏈接完成首回合原方向和反方向的報文時,完成路由,Firewall和NAT工做後,在處理反方向首報文的forward hook,根據報文路由、NAT等信息建立可卸載flow到接收網卡ingress hook上。後續的報文能夠在接收ingress hook上直接轉發,不須要再進入IP stack處理。此外,未來flow offload還將支持hardware offload模式,這將極大提升系統轉發性能。
方案設計與優化實踐
經過對上述三項新技術的研究,咱們發現能夠嘗試設計一套基於路由的方式,實現多租戶overlay網絡的外網網關。在方案設計過程當中,咱們也碰到了諸如lwtunnel和flow offload功能不足,以及VRF和flow offload不能一塊兒有效的工做等問題。最終咱們都設法解決了,並針對這些內核的不足提交patch給Linux開源社區。
1. lwtunnel發送報文tunnel_key丟失
**問題描述:**咱們利用lwtunnel路由方式發送報文時,建立了一個external類型的gretap tunnel,咱們將命令設置了id爲1000,可是發送成功報文中沒有tunnel_key字段。
**問題定位:**咱們研究iproute2代碼,發現因爲TUNNEL_KEY flag並無開放給用戶態,因此iproute2工具並無對lwtunnel路由設置TUNNEL_KEY,致使報文不會建立tunnel_key字段。
提交patch: 咱們給內核和用戶態iproute2分別提交patch來解決這一問題:
iptunnel: make TUNNEL_FLAGS available in uapi
https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git/commit/? id=1875a9ab01dfa96b06cb6649cb1ce56efa86c7cb
iproute: Set ip/ip6 lwtunnel flags
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=3d65cefbefc86a53877f1e6461a9461e5b8fd7b3 提交patch後,能夠經過如下方式設置路由。
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對指定key的IP tunnel無效
**問題發現:**爲了能有效隔離租戶路由,咱們給每一個租戶建立一個基於tunnel_key的gretap tunnel設備。以下圖,建立一個tunnel_key 1000的gretap tunnel設備,把tunnel設備加入租戶所屬VRF,tunnel設備能有效地接收報文,但並不能發送報文。
問題定位:研究內核發現,IP tunnel在非external模式下即便指定了輕量級隧道路由,發送報文也沒有使用它,致使報文路由錯誤被丟棄。
提交patch:
ip_tunnel: Make none-tunnel-dst tunnel port work with lwtunnel
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d71b57532d70c03f4671dd04e84157ac6bf021b0 提交patch後,在未指定tunnel_dst的非external模式IP tunnel下,能使用輕量級隧道路由進行發送報文。
3. external IP tunnel ARP沒法正常運行
**問題描述:**鄰居IP tunnel進行了ARP請求,可是本端的ARP迴應報文的隧道頭中並沒帶tunnel_key字段。
**問題定位:**研究代碼發現,tunnel收到了對端的ARP 請求,在發送報文ARP回覆的時候會複製請求報文的tunnel信息,可是遺漏了全部tun_flags。
提交patch:
iptunnel: Set tun_flags in the iptunnel_metadata_reply from src
4. Flow offload不能與DNAT有效工做
**問題描述:**Firewall建立規則從eth0收到目的地址2.2.2.11的報文,DNAT爲10.0.0.7, flow offload沒法工做。
**問題定位:**分析發現,客戶端1.1.1.7 —> 2.2.2.7 DNAT到server 10.0.0.7,第一個reply反向報文(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
提交patch:
netfilter: nft_flow_offload: Fix reverse route lookup
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a799aea0988ea0d1b1f263e996fdad2f6133c680 5. Flow offload不能與VRF有效工做
問題描述: 將網卡eth0和eth1加入VFR後,flow offload不起做用。
**問題定位:**查看代碼發現,原方向和反方向首報文進入協議堆棧後skb->dev會設置爲vrf device user1,建立flow offload規則的iif就是user1。可是offload規則下發在eth0和eth1的ingress hook上,因此後續報文在eth0和eth1的ingress hook上不能匹配flow規則。
提交patch:
netfilter: nft_flow_offload: fix interaction with vrf slave device
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=10f4e765879e514e1ce7f52ed26603047af196e2 最終,咱們根據兩個方向查找路由的結果,設置flow offload規則的iif和oif信息來解決此問題。
6. VRF PREROUTING hook重入問題
**問題描述:**配置網卡加入VRF,firewall ingress方向規則爲接收目的地址2.2.2.11 、TCP 目的端口22的報文,egress方向規則爲丟棄TCP 目的端口 22的報文。出現異常結果: 收到目的地址2.2.2.11 TCP 22目的端口的報文卻被丟棄。
**問題定位:**研究發現網卡加入VRF後收到的報文會兩次進入PREROUTING hook,由於在進入IP stack時會進第一次PREROUTING hook,而後被VRF設備接管後會再次進入PREROUTING hook。上述規則第一次在rule-1000-ingress chain中dst nat爲10.0.0.7,第二次因爲報文被DNAT後會錯誤的進入rule-1000-egress,致使報文被丟棄。
**提交patch:**咱們給內核加了一個支持判斷網卡類型的match項目,讓用戶態避免可知的第二次無效重入,內核態和用戶態nftables分別提交了以下的patch:
netfilter: nft_meta: Add NFT_META_I/OIFKIND meta type
https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git/commit/?id=0fb4d21956f4a9af225594a46857ccf29bd747bc meta: add iifkind and oifkind support
http://git.netfilter.org/nftables/commit/?id=512795a673f999fb04b84dbbbe41174e9c581430
使用方法:
nft add rule firewall rules-all meta iifkind "vrf" counter accept
原型驗證
最終,咱們成功地利用lwtunnel、VRF和flow offload實現多租戶外網網關的原型驗證。驗證過程以下:
1. 首先建立原型環境。
a. netns cl模擬外網client, 地址爲1.1.1.7,tunnel src 172.168.0.7,配置發送路由;
b. netns ns1模擬租戶1,內網地址爲10.0.0.7,外網地址爲 2.2.2.11,tunnel src 172.168.0.11 tunnel_key 1000,配置發送路由;
c. netns ns2模擬租戶2,內網地址爲10.0.0.7,外網地址爲 2.2.2.12,tunnel src 172.168.0.12 tunnel_key 2000,配置發送路由;
d. Host模擬外網網關,tunnel src 172.168.0.1,建立租戶VRF user1和use2,建立租戶IP tunnel tun1和tun2,配置轉發路由。
原型環境圖以下:
2. 建立firewall規則:
a. 租戶1入向容許TCP目的端口22和ICMP訪問,出向禁止訪問外部TCP 22目的端口;
b. 租戶2入向容許TCP端口23和ICMP訪問,出向禁止訪問外部TCP 23目的端口;
c. 在租戶tun1和tun2設備上支持flow offload。
最終,client能夠經過2.2.2.11成功訪問user1 tcp 22端口服務,user1不能訪問client tcp 22端口服務;client能夠經過2.2.2.12成功訪問user2 tcp 23端口服務,user1不能訪問client tcp 23端口服務。
待後續hardware offload功能完善以及網卡廠商支持後,咱們會作進一步的開發驗證。
寫在最後
以上是本項目涉及的部分核心問題,這些patch特性均可以在Linux kernel 5.0版本里獲取。咱們把這期間爲Linux kernel社區貢獻的patch整理成了一份列表,但願能爲開發者提供幫助,讀者能夠點擊「閱讀原文」閱覽完整patch list。
Linux做爲成熟的開源套件,一直是雲廠商使用的主流操做系統,但在技術的更新迭代過程當中,一些新特性在實際應用上也會存在穩定性、兼容性等方面的問題。咱們在研究使用上游技術的同時,也一直積極探索、豐富開源技術功能,幫助提升開源技術穩定性。並將產出持續回饋給社區,與社區共同構建一個繁榮的開源生態。