UCloud基於Linux內核新特性的Conntrack卸載設計與優化

近期,Netfilter 和 Mellanox 聯合開發了flowtable hardware offload 功能,使flowtable成爲一種標準的conntrack卸載方案,並實現了Linux標準的Netfilter 硬件offload接口。做爲一個新功能,還存在一些缺陷和不完善的地方。linux

對此,咱們作了大量的硬件卸載開發工做,進行問題修復、功能優化,幫助完善kernel功能並提高性能,後續也計劃將該功能合併至UCloud外網網關,進一步提高系統性能和管理能力。git

優化後的性能飛躍

首先,來看一組簡單粗暴的數據。api

在全部硬件卸載開發工做完成後,咱們基於flowtable的conntrack offload,從bps、pps、cpu使用率等維度分別進行了系列性能對比測試,測試結果以下:網絡

  • 非硬件卸載模式:

①單條流帶寬測試爲12GBps,耗費2個host CPU;tcp

②多條流小包包量測試爲1Mpps,耗費8個host CPU; 性能

  • 硬件卸載模式:

①單條流帶寬測試爲23GBps,耗費 0 host CPU;測試

②多條流小包包量測試爲8Mpps,耗費 0 host CPU; 優化

能夠看到在硬件卸載模式下bps、pps性能提高顯著,不只作到了CPU無損耗,性能還分別提高1倍、8倍之多。不過對於新建鏈接率cps並無太顯著的提高,分析緣由爲該性能由conntrack在軟件協議棧中完成,與硬件無關。在已有層面,一旦該特性應用於產品上,預估將帶來極大的性能飛躍。spa

固然,性能提高的背後離不開每個細微技術的打磨:咱們對conntrack offload與NAT功能分別進行了問題修復與優化,並在此基礎上,還與Netfilter、Mellanox合做開發了tunnel offload的新特性。 接下來,就來詳細聊聊咱們所作的工做。設計

Flowtable offload背景簡介

Linux內核在版本4.16引入了flow offload功能,它爲IP forward提供了基於流的卸載功能。當一條新建鏈接完成首回合原方向和反方向的報文時,完成路由,Firewall和NAT工做後,在處理反方向首報文的forward hook,根據報文路由、NAT等信息建立可卸載flow到接收網卡ingress hook上。後續的報文能夠在接收ingress hook上直接轉發,不須要再進入IP stack處理。 這種模式實現了完成創建鏈接的conntrack軟件offload,不過目前flowtable offload只支持TCP和UDP協議的卸載。 

圖:Flowtable offload圖示 

當前內核中硬件支持offload的標準只有Network TC subsystem的tc flower接口。

_Pablo Neira Ayuso_首先將tc flower相應的offload接口進行公共化爲network子系統flow offload,而後在flowtable offload中支持HW的flow offload接口。這樣即可在驅動層面上徹底統一使用flow offload接口。 

_Paul Blakey_將flowtable offload指定到TC_SETUP_FT域,實如今驅動層來區分不一樣的subsystem。

 在全部基本功能完善後,咱們在review commit的代碼後發現flowtable HW offload的創建並無指定到正確的TC_SETUP_FT類型。對此,咱們進行了簡單的修復,並將patch提交至社區:

 ① Netfilter:nf_flow_table_offload:Fix block setup as TC_SETUP_FT cmd(https://git.kernel.org/pub/sc...

②Netfilter:nf_flow_table_offload:Fix block_cb tc_setup_type as TC_SETUP_CLSFLOWER(https://git.kernel.org/pub/sc...

Conntrack offload測試優化實踐

接着,咱們對conntrack offload卸載功能進行了實操測試,發現其中存在部分卸載規則問題,對其進行了修復優化。

|  測試復現

建立一臺虛擬機,配置虛擬機地址爲10.0.0.75。host建立user1的vrf,將虛擬機對應的VF representor mlx_pf0vf0以及PF mlx_p0加入到user vrf。PF對端鏈接物理機地址爲10.0.1.241。Host做爲虛擬機和對端物理機之間的虛擬機路由器。 

建立firewall規則,容許訪問虛擬機的icmp和tcp 5001端口,而且把VFrepresentor port和PF port加入到flowtable作offload。 

開啓netperf測試,10.0.1.241訪問10.0.0.75:5001。發現鏈接能成功創建,可是後續帶寬跑不上來。

|  問題定位

在Host上的確抓不到相應的報文,說明卸載成功,而且在虛擬機中抓包發現收報文的收到和回覆的報文都是正確的。可是在對端物理機上抓包卻發現dst_mac全爲0。 咱們猜想應該是卸載規則出現了問題,爲驗證結論,咱們查看相關源碼並進行分析:

經過源碼能夠發現dst_neigh正確獲取,可是因爲dst_neigh_lookup會爲沒有neighbor項的地址建立一個新的neighbor,致使值爲0。(因爲整個測試環境剛部署好,沒有進行過任何通訊,因此兩端的neighbor是沒有的) 

仔細分析下offload規則下發的時機,第一個original方向的SYN報文從10.0.1.241->10.0.0.75:5001經過協議棧能轉入到虛擬機,虛擬機發出reply方向的SYN+ACK報文10.0.0.75:5001->10.0.1.241,同時在Host的netfilter forward表會創建offload規則下發給硬件。這時連接兩個方向的報文都進行了鑑權,conntrack項也變成established,造成完整的轉發規則。 

可是reply方向的ip_dst 10.0.1.241的neighbor仍是沒有,緣由在於協議棧上的報文還沒真正發送觸發arp流程。 

針對上訴分析,因爲flowtable offload並不支持icmp協議,咱們先進行了ping探測讓兩端的neighbor項創建成功,而後再繼續測試。此時發現,正式化硬件offload能夠正常的工做。

|  提交patch:

針對conntrack offload 中neighbor無效致使卸載錯誤的狀況,咱們提交patch進行了修復。簡單總結爲:在得到無效neighbor的時候先不進行卸載:

Netfilter: nf_flow_table_offload: checkthe status of dst_neigh(https://git.kernel.org/pub/sc...

NAT功能測試優化實踐

接着,咱們繼續對flowtableoffload中NAT功能項進行了實操測試,對部分卸載規則問題進行了優化改善。

|  測試復現:  

建立一臺虛擬機,配置虛擬機地址爲10.0.0.75,具備外部地址2.2.2.11。host建立user1的vrf,將虛擬機對應的VF representor mlx_pf0vf0以及PF mlx_p0加入到user vrf。PF對端鏈接物理機地址爲1.1.1.7。Host就做爲虛擬機和對端物理機之間的虛擬nat網關。

一樣建立firewall規則,容許訪問虛擬機的icmp和tcp 5001端口,而且把VFrepresentor port和PF port加入到flowtable作offload。

接着建立nat轉發規則,dst ip address 2.2.2.11 dnat to 10.0.0.75, src ip address 10.0.0.75snat to 2.2.2.11。

開啓netperf測試,1.1.1.7訪問至2.2.2.11:5001。發現鏈接能成功創建,可是後續帶寬值很低。

|  問題定位:

在Host上的確抓不到相應的報文,說明卸載成功,在虛擬機中抓包發現收報文的dst mac不正確,值一樣也爲0(測試代碼沒合入,無效neighbor的時候先不進行卸載的patch)。 

因爲original方向的SYN報文1.1.1.7訪問2.2.2.11:5001轉換成1.1.1.7到10.0.0.75:5001成功發送給了虛擬機,這時10.0.0.75對應的neighbor是存在的,不該該出現獲取不到的狀況。 

咱們猜想應該仍是卸載規則出現了問題,查看源碼進行分析: 

經過源碼可知,在NAT場景下original方向的tuple->dst_cache的device確實是mlx_pf0vf0,可是tuple->dst_v4卻仍舊是原來的2.2.2.11,不是10.0.0.75。10.0.0.75應該經過remote_tuple->src_v4獲取

解決該問題後,咱們再次開啓netperf測試,1.1.1.7:32315訪問2.2.2.11:5001。發現鏈接依然能成功創建,可是帶寬仍是跑不上來。 

在虛擬機上抓包發現接收報文dst port異常爲32315。即1.1.1.7: 32315到10.0.0.75: 32315。咱們判斷卸載規則仍存在其餘問題,查看源碼進行分析:

能夠看到在DNAT場景下會將original方向的dst port改成reply方向的dstport即32315, 將reply方向的dst port改成original方向的source port。這個邏輯明顯有誤。

一樣在SNAT下也存在相似的轉換問題, 會將original方向的srcport改成reply方向的dst port, 將reply方向的src port改成original方向的source port。 

舉個例子,好比10.0.0.75:32200訪問1.1.1.75:5001會將reply的報文改成1.1.1.75: 32200到2.2.2.11:32200。 因此NAT模式下的portmangle存在問題,正確的邏輯應該是: 

  • DNAT: 

original方向的dst port應該改成reply方向的src port;

reply方向的src port應改成original 方向的dst port。 

  • SNAT:

Original方向的src port應該改成reply方向的dstprt;

Reply方向的dst port應該改成original方向的src port。

|  提交patch: 

最終,針對NAT測試問題,咱們也提交了相關patch進行修復:

① Netfilter: nf_flow_table_offload: fix incorrect ethernet dst address(https://git.kernel.org/pub/sc...

② Netfilter: nf_flow_table_offload: fix the nat port mangle.(https://git.kernel.org/pub/sc...

新特性開發:SDN網絡下的Tunnel offload

咱們發現flowtable hardware offload沒法支持tunnel設備。對於SDN網絡而言,tunnel是一項關鍵性的指標。 最終,咱們從conntrack offload與NAT功能的優化修復過程當中獲得啓發,在新思考的推進下,與 Netfilter 維護者Pablo NeiraAyuso 和 Mellanox 驅動工程師Paul Blakey通力合做,共同開發出了tunnel 卸載的新特性。 

如下是開發細節的詳細介紹。 

一、在Flowtable上設置tunnel device實現卸載 

在tc flower softwarefilter規則中, 全部的tunnel meta match、tunnel decap action均設置在tunnel device的ingress qdisc上,只有在tunnel device 接收入口才能獲取meta信息。 

所以tc flower offload模式下的卸載規則也會follow這個邏輯,下發到tunnel device上。可是tunnel device都是虛擬的邏輯device,須要找到lower hardware device進行設置卸載——即採用indirect block的方式來實現。 

首先驅動程序註冊監聽tunnel建立的事件。當tunnel被建立,驅動會給對應tunnel device設置一個tc flower規則的callback。經過這種indirect block的方式來實現tunnel device與hardware device的關聯。

|  提交patch: 

咱們先提交了series(http://patchwork.ozlabs.org/c... )——在flow offload 中支持indirect block方式,主要包括如下patch:

① cls_api:modify the tc_indr_block_ing_cmd parameters.

② cls_api:remove the tcf_block cache

③ cls_api:add flow_indr_block_call function

④ flow_offload:move tc indirect block to flow offload

⑤ flow_offload:support get multi-subsystem block

 引入上面的series後,flowtable就能經過indirect block卸載tunnel device。

①Netfilter: flowtable: addnf_flow_table_block_offload_init()(https://git.kernel.org/pub/sc...

②Netfilter: flowtable: addindr block setup support(https://git.kernel.org/pub/sc...) 

二、Tunnel 信息匹配及Action 設計 

完成第一步後,flowtable即可以成功的將卸載規則設置到tunnel device上,可是此時還存在一個問題:沒法支持tunnel信息匹配以及decap、encap操做。 

針對這一問題,首先咱們想到Linux內核在4.3版本中引入的輕量級隧道Lightweight tunneling,它提供了經過route方式設置tunnel屬性的方法:建立隧道設備時指定external模式,利用路由設置的輕量級隧道經過tun設備發送報文。

Flowtable規則設置也是徹底基於route信息去完成,沿着這一思路:tunnel信息也能夠經過route信息獲取。

|  提交patch:

咱們提交了flowtable中tunnelmatch和encap/decap action的offloadpatches:

①Netfilter: flowtable: add tunnel match offload support(https://git.kernel.org/pub/sc...

②Netfilter: flowtable: add tunnel encap/decap actionoffload support(https://git.kernel.org/pub/sc...

三、Mlx5e在TC_SETUP_FT中支持Indirect block 

接着,咱們又發現了第3個問題:mlx5e驅動中flowtable offload的規則被指定到TC_SETUP_FT域中,但該驅動中並不支持indirect block

|  提交patch: 

發現這一點後,咱們向社區提交了patch實現該功能的完善:

① net/mlx5e:refactor indr setup block(https://git.kernel.org/pub/sc...

②net/mlx5e:add mlx5e_rep_indr_setup_ft_cb support(https://git.kernel.org/pub/sc...) 

四、Tunnel offload 功能驗證 

完成上述開發步驟後,咱們也按照前文的測試思路進行了功能驗證,確認功能的完善與準確性。 

|  測試驗證: 

建立一臺虛擬機,配置虛擬機地址爲10.0.0.75,具備外部地址2.2.2.11。host建立user1的vrf,建立key 1000的gretap tunnel device tun1。將虛擬機對應的VF representor mlx_pf0vf0和tun1加入到user1 vrf。PF mlx_p0配置underlay地址172.168.152.75/24。PF對端鏈接物理機網卡配置地址172.168.152.208/24, 建立gretap tunnel tun,  tun設備的remote爲172.168.152.75,key爲1000,設置地址爲1.1.1.7。 首先,虛擬機與對端物理機tun設備經過tunnel相互訪問:

接着,建立firewall規則,容許訪問虛擬機的icmp和tcp 5001端口,而且把VF representor port和tun 1隧道設備加入到flowtable作offload。 最後,建立NAT轉發規則:dst ip address 2.2.2.11 dnat to 10.0.0.7五、src ip address 10.0.0.75 snat to 2.2.2.11。

測試後最終確認各個功能完整正常,至此,咱們實現了SDN網絡下tunnel offload卸載的能力。

寫在最後

以上是本項目涉及的部分核心問題,咱們把這期間貢獻的patch整理成了一份列表,但願能爲開發者提供幫助,讀者能夠點擊「閱讀原文」閱覽完整patch list。 

Flow Offload開源技術爲行業內帶來了革命性的思路,但在技術的更新迭代過程當中,一些新特性在實際應用上也會存在穩定性、兼容性等方面的問題。做爲開源技術的同行者,UCloud一直積極探索、豐富開源技術功能,幫助提升開源技術穩定性。

做者簡介:文旭,UCloud虛擬網絡平臺研發工程師,Linux kernel network 活躍開發者,Netfilter flowtable offload 穩定維護者 

相關文章
相關標籤/搜索