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 稳定维护者 

相关文章
相关标签/搜索