当前位置: 首页 > 科技观察

基于Linux内核新特性的网关设计实践

时间:2023-03-13 23:08:01 科技观察

UCloud外网网关是承载外网IP、负载均衡等产品的外网出入流量。目前实现了基于Linux内核的OVS/GREtunnel/netns/iptables。很好地支持了现有的业务。同时,我们也在不断跟踪Linux内核社区新技术的发展,并将其运用到下一代外网网关的设计中。这些新功能将系统性能和可管理性提升到一个新的水平,满足未来几年的需求。在解决方案的设计和开发过程中,我们发现新特性存在很多缺陷和BUG。为此,我们向Linux内核社区反馈了10多个补丁,并将其集成到Linux内核5.0版本中,帮助完善内核功能,提高稳定性。目前业界很多多租户外网网关都是基于OpenFlow的OpenvSwitch(OVS)方案。但随着内核路由转发功能的不断完善,利用内核原生的路由转发方式设计多租户外网网关系统成为可能。这样传统的iproute2路由工具和iptables、nftables等防火墙工具就可以得到有效的利用。随着SwitchDev技术的兴起,未来也有可能将网关系统迁移到LinuxSwitch上。现有Linux内核3.x的不足目前广泛使用的内核版本是3.x系列。比如CentOS7全系列标准支持的内核版本3.10,在Fedora/Ubuntu等Linux发行版中也被广泛使用。3.x系列内核下,存在IP隧道管理复杂、租户隔离性能损失等问题。复杂的IP隧道管理Linux内核创建一个IP隧道设备来建立点对点的隧道连接,创建时需要指定隧道目标和隧道密钥。由于主机是成对连接的,而且主机的目的地址很多,因此需要在网关节点上创建数以千计的隧道设备。在大规模的商业环境中,隧道的管理将变得异常困难。复杂的。多租户隔离导致的性能下降公有云需要实现多租户隔离,以保证用户的安全和隐私。由于VPC网络下不同租户的内网地址可以重叠,所以路由也可能重叠。这个时候需要出台大量的政策。路由隔离租户的路由规则,由于策略路由的链表属性,随着链表长度的增加,性能会急剧下降。由于firewall和NAT的实现都是基于同一个chainediptables,所以性能损失也相当可观。Netns带来了性能开销。租户路由和防火墙规则通过netns隔离,但是netns会引入虚拟网卡和协议栈重入开销,整体性能会降低20%左右。三种新的内核技术为了解决原有方案中存在的问题,我们调研了大量业界主流方案和内核上游的新动向,发现Lightweighttunneling(简称lwtunnel)、VirtualRouting转发(VirtualRoutingForwarding)(简称VRF)和nftable&netfilter流量卸载(flowoffload)是三个内核新技术特性,可以帮助避免原有解决方案的缺陷。1、轻量级隧道Linux内核在4.3版本引入了轻量级隧道,提供了一种通过路由设置隧道属性的方法,可以避免管理大量的隧道设备。在创建隧道设备时指定外部模式,使用路由设置的轻量级隧道通过tun设备发送数据包。#ipladddevtuntypegretapexternal#ifconfigtun1.1.1.7/24up#iprr2.2.2.11via1.1.1.11devtunencapipid1000dst172.168.0.1key2,虚拟路由和转发版本引入了Linux内核4.3增加了对VRF的初步支持,在4.8版本形成了一个完整的版本。虚拟路由转发可以使用一个LinuxBox物理路由器作为多个虚拟路由器,可以解决租户路由隔离问题,避免直接使用策略路由。因此,可以将不同租户的网卡添加到租户所属的虚拟路由器中,实现多租户虚拟路由。#iplinkadduser1typevrftable1#iplinkadduser1typevrftable2#iplsetuser1up#iplsetuser2up#iplsetdeveth1masteruser1#iplsetdeveth1masteruser2#ipradefaultvia192.168.0.1deveth1table1onlink#ipradefaultvia192.168.0.1deveth1table2onlink3.FlowOffloadnftables是一个新的数据包分类框架,旨在取代现有的{ip,ip6,arp,eb}_tables。在nftables中,大部分工作是在userland完成的,内核只知道一些基本指令(过滤是用伪状态机实现的)。nftables的一个高级特性是映射,它可以获取不同类型的数据并将它们映射。例如,我们可以将iif设备映射到专用规则集(之前创建并存储在链中)。由于哈希映射的方式,完全可以避免链式规则跳转的性能开销。Linux内核在4.16版本引入了flowoffload,为IP转发提供基于流的offload。当新的连接完成原方向和反向的第一轮数据包时,在完成路由、防火墙和NAT工作后,处理反向第一数据包的forwardhook创建一个offloadableflow到Receive网卡ingress钩。后续数据包可以直接在接收ingresshook上转发,无需进入IP栈处理。此外,流卸载未来还将支持硬件卸载模式,这将大大提高系统转发性能。#nftaddtablefirewall#nftaddflowtableffb1{hookingresspriority0\;设备={eth0,eth1}\;}#nftaddchainfftb-all{typefilterhookforwardpriority0\;政策接受\;}#nftaddrulefftb-allctzone1ipprotocoltcpflowoffload@fb1方案设计及优化实践通过对以上三种新技术的研究,我们发现可以尝试设计一种基于路由的方法来实现多-租户级联网络外网网关。在设计过程中,我们也遇到了lwtunnel和streamoffloading功能不足,VRF和streamoffloading不能有效协同工作等问题。最终我们都设法解决了这个问题,并向Linux内核社区提交了针对这些内核缺陷的补丁。1、lwtunnel发送消息时的tunnel_key丢失问题:当我们使用lwtunnel路由方式发送消息时,我们创建了一个外部gretap隧道,我们在命令中设置了id为1000,但是里面没有tunnel_key字段成功发送消息。#ipladddevtuntypegretap#ifconfigtun1.1.1.7/24up#iprr2.2.2.11via1.1.1.11devtunencapipid1000dst172.168.0.1问题定位:我们研究了iproute2代码,发现由于TUNNEL_KEYflag没有对用户模式开放,所以iproute2工具没有为lwtunnel路由设置TUNNEL_KEY,所以数据包不会创建tunnel_key字段。提交补丁:我们提交补丁给内核态和用户态iproute2解决这个问题:rr2.2.2.11via1.1.1.11devtunencapipid1000dst172.168.0.1key2,lwtunnel指定key的IP隧道无效问题发现:为了有效隔离租户路由,我们根据tunnel_key创建一个gretap隧道设备对于每个租户。如下图,创建一个tunnel_key为1000的gretaptunnel设备,并将该tunnel设备添加到租户的VRF中。隧道设备可以有效接收报文,但不能发送报文。#ipladddevtuntypegretapkey1000#ifconfigtun1.1.1.7/24up#iprr2.2.2.11via1.1.1.11devtunencapipid1000dst172.168.0.1key问题定位:研究内核发现IPtunnel在非外部模式下,即使指定了轻量级隧道路由,也不会用于发送数据包,导致数据包路由错误被丢弃。提交补丁:ip_tunnel:Makenone-tunnel-dsttunnelportworkwithlwtunnel提交补丁后,可以使用轻量级隧道路由在不指定tunnel_dst的非外部模式IP隧道下发送数据包。3、外部IP隧道的ARP无法正常运行。问题描述:邻居IP隧道发起ARP请求,但本端ARP响应报文的隧道头中没有tunnel_key字段。#ipladddevtuntypegretapexternal#ifconfigtun1.1.1.7/24up#iprr2.2.2.11via1.1.1.11devtunencapipid1000dst172.168.0.1key对于最后的ARP请求,隧道信息发送ARP回复时将复制请求消息的一部分,但省略所有tun_flags。提交补丁:iptunnel:在来自src4的iptunnel_metadata_reply中设置tun_flags。流量卸载无法与DNAT一起有效工作。问题描述:防火墙创建规则接收来自eth0的目的地址为2.2.2.11的数据包,DNAT为10.0.0.7,无法进行流分流。问题定位:分析发现client1.1.1.7->2.2.2.7DNAT到server10.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提交补丁:netfilter:nft_flow_offload:Fixreverseroutelookup5.Flowoffload无法与VRF有效工作问题描述:将网卡eth0和eth1添加到VFR后,流量卸载不起作用。#ipaddradddeveth01.1.1.1/24#ipaddradddeveth11.1.1.1/24#iplinkadduser1typevrftable1#iplsetuser1up#iplsetdeveth0masteruser1#iplsetdeveth1masteruser1问题定位:查看代码发现,原方向和反方向首包进入协议栈后,skb->dev会设置为vrf设备user1,创建流卸载规则的iif为user1。但是由于在eth0和eth1的ingresshook上下发了offloadingrules,所以后续报文无法匹配eth0和eth1的ingresshook上的流规则。提交补丁:netfilter:nft_flow_offload:fixinteractionwithvrfslavedevice最后我们根据两个方向的路由查找结果设置流卸载规则的iif和oif信息来解决这个问题。6.VRFPREROUTINGhook重入问题问题描述:配置网卡加入VRF,防火墙的入口方向规则是接收目的地址为2.2.2.11、TCP目的端口为22的数据包,出口方向规则是discardpacketswithTCPdestinationport22.出现异常结果:目的地址为2.2.2.11TCP22目的端口的报文收到但被丢弃。问题定位:研究发现网卡在加入VRF后收到的数据包会两次进入PREROUTINGhook,因为PREROUTINGhook在进入IP栈时会进入第一个PREROUTINGhook,被取走后再次进入PREROUTINGhook通过VRF设备。对于上面的rule,第一次rule-1000-ingress链中的dstnat是10.0.0.7,第二次因为报文是DNATed,所以会误进入rule-1000-egress,导致要丢弃的消息。提交补丁:我们在内核中加入了匹配项,支持判断网卡类型,这样用户态就可以避免第二次已知无效重入。内核态和用户态nftables分别提交了如下补丁:最终,我们使用lwtunnel、VRF和flowoffload成功实现了多租户外网网关的原型验证。验证过程如下:1.首先创建原型环境a.netnscl模拟外网客户端,地址为1.1.1.7,隧道源地址为172.168.0.7,配置发送路由;b.netnsns1模拟租户1,内网地址为10.0.0.7,外网地址为2.2.2.11,隧道源地址为172.168.0.11tunnel_key1000,配置发送路由;C。netnsns2模拟租户2,内网地址为10.0.0.7,外网地址为2.2.2.12,隧道源地址172.168.0.12tunnel_key2000,配置发送路由;d.主机模拟外网网关,隧道源地址172.168.0.1,创建租户VRFuser1和use2,创建租户IP隧道tun1和tun2,配置转发路由。原型环境图如下:2.创建防火墙规则a.租户1允许入方向访问TCP目的端口22和ICMP,出方向禁止访问外部TCP目的端口22;b.租户2入方向允许TCP23端口和ICMP访问,出方向禁止访问外部TCP23端口访问外部TCP23目的端口;C。支持租户tun1和tun2设备上的流量卸载。最终客户端可以通过2.2.2.11成功访问到user1的tcp22端口服务,user1无法访问客户端的tcp22端口服务;client通过2.2.2.12可以成功访问user2tcpport23服务,user1无法访问clienttcpport23服务。后续硬件功能完善,网卡厂商支持后,我们会做进一步的开发和验证。上面写的***是本项目涉及的一些核心问题,这些补丁特性可以在Linux内核5.0版本中获得。我们整理了一份这段时间贡献给Linux内核社区的补丁列表,希望能为开发者提供帮助,读者可以点击阅读。作为一个成熟的开源套件,Linux一直是云厂商使用的主流操作系统。但在技术更新迭代过程中,一些新特性在实际应用中会出现稳定性和兼容性问题。在研究和使用上游技术的同时,我们也一直在积极探索和丰富开源技术的功能,以帮助提高开源技术的稳定性。并持续将产出回馈社区,与社区一起共建繁荣的开源生态。