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
git.kernel.org/pub/scm/lin…? id=1875a9ab01dfa96b06cb6649cb1ce56efa86c7cb
iproute: Set ip/ip6 lwtunnel flags
git.kernel.org/pub/scm/net… 提交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
git.kernel.org/pub/scm/lin… 提交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
git.kernel.org/pub/scm/lin… 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
git.kernel.org/pub/scm/lin… 最终,咱们根据两个方向查找路由的结果,设置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
git.kernel.org/pub/scm/lin… meta: add iifkind and oifkind support
git.netfilter.org/nftables/co…
使用方法:
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做为成熟的开源套件,一直是云厂商使用的主流操做系统,但在技术的更新迭代过程当中,一些新特性在实际应用上也会存在稳定性、兼容性等方面的问题。咱们在研究使用上游技术的同时,也一直积极探索、丰富开源技术功能,帮助提升开源技术稳定性。并将产出持续回馈给社区,与社区共同构建一个繁荣的开源生态。