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

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

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

優化後的性能飛躍

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

在所有硬件卸載開發工作完成後,我們基於flowtable的conntrack offload,從bps、pps、cpu使用率等維度分別進行了系列性能對比測試,測試結果如下:

  • 非硬件卸載模式:

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

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

  • 硬件卸載模式:

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

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

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

當然,性能提升的背後離不開每一個細微技術的打磨:我們對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/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ee1bcfe01251e8958b53de45d2b0c85e09dd2719)

②Netfilter:nf_flow_table_offload:Fix block_cb tc_setup_type as TC_SETUP_CLSFLOWER(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e052901991ae21e15851bfc89c682bfcb39a4dcf)

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/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f31ad71c44c17e55d9a6fa24f8249a15365cf8b2)

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/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1b67e50601fabc9589022e6b5e79fd8596c2338e)

② Netfilter: nf_flow_table_offload: fix the nat port mangle.(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=73327d47d2c04214f23217b982a004c22a493c78)

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

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

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

1、在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/cover/1143094/ )——在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/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4679877921cd6fb2545ffecbaa3cdbbb74245aab)

②Netfilter: flowtable: addindr block setup support(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b5140a36da7876bc084a2c680c8dbc7438db2051) 

2、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/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cfab6dbd0ecf342fc904952d8565c8d80c741a63)

②Netfilter: flowtable: add tunnel encap/decap actionoffload support(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=88bf6e4114d5af685c11c02ce95efa7c7494e940)

3、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/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5a37a8df809b14f682f7b5c863b4672132e2ffc9)

②net/mlx5e:add mlx5e_rep_indr_setup_ft_cb support(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=07c264ab8e6c76efbc2eb06839f8dcd9fd0c3ebd) 

4、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.75、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 穩定維護者 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章