首页 > 编程知识 正文

Linux flow offload提高路由转发效率,linux防火墙路由转发

时间:2023-05-03 10:13:34 阅读:264722 作者:620

凡是正确的东西,该来的最终还是会来的。 (当然了,经理可能也有同感。)

来看看几年前我写的文章:

利用nf_conntrack机制存储路由,省去每包路由查找: https://blog.csdn.net/dog250/article/details/24101425在Linux的连接跟踪(nf_conntrack)中缓存私有数据省去每次查找: https://blog.csdn.net/dog250/article/details/42814563使用multi zone的nf conntrack来缓存路由和socket构建高性能处理: https://blog.csdn.net/dog250/article/details/43370449

很多年前我写了很多这种模块,各种协议栈短路的设计,并且在上海的很多机房里跑了很久,也用这些东西成功面试了很多家大公司,也用这些东西面试过很多应聘者…

一晃好多年过去了,优化conntrack并置入e1000e驱动的周末恍如昨日,如今,Linux 4.18内核之后,使用标准内核,路由转发终于可以正儿八经地被offload了:

系统在网卡ingress建立flow cache,flow元组命中的将会直接被转发,不必再进行路由查找。

关于flow offload的详细信息参见下面的文章:
https://lwn.net/Articles/738214/

其原理图如下:

你觉得flow offload仅仅就是在Netfilter上增加了个HOOK吗?如果这样,我早就能做到了,而且在上海市的很多机房跑了好多年了。

flow offload真正厉害的地方是它迎合了云计算虚拟化智能网卡offload网络处理逻辑的风潮,实现了Linux标准的Netfilter API的硬件offload接口!它的意义在于下图显示的:

再来总结一下数据包的路径:

网卡收包进ingress。ingress匹配flow table: 命中: 获取flow entry项里的路由项。解析路由项里的出口网卡dev_out,MAC地址等。递减TTL。直接dev_out转发。 未命中: 转交给慢速路径,标准Linux内核处理。ip_forward在FORWARD hook点将flow entry注入flow table(软件或硬件)。

迄至Linux 5.3版本的内核,硬件offload依然处于pending状态,关于硬件offload的patch,可以参考下面链接的patch:
https://www.mail-archive.com/netdev@vger.kernel.org/msg198158.html
https://www.mail-archive.com/netdev@vger.kernel.org/msg198155.html
https://www.mail-archive.com/netdev@vger.kernel.org/msg198157.html
https://www.mail-archive.com/netdev@vger.kernel.org/msg198156.html
https://www.mail-archive.com/netdev@vger.kernel.org/msg198159.html

本质上来讲,硬件offload其实就是调用了一个网卡驱动程序的接口,实现操作网卡流表的目的。

在演示怎么玩之前,必须澄清一个问题,即flow offload和nf_conntrack的关系。

首先,flow offload依赖两个内核模块:

nf_flow_tablenft_flow_offload

当modinfo它们时,一个很令人遗憾的消息出现了:

root@zhaoya-VirtualBox:~/# modinfo nft_flow_offload |grep dependsdepends: nf_tables,nf_flow_table,nf_conntrack

它们依赖nf_conntrack这么一个饱受诟病的东西。在我五六年前实现conntrack rtcache的时候,我还特意优化了nf_conntrack,增加了一个hot cache机制,从而提升了性能,当时我还使用的是2.6.32这个古老的版本。

虽然一直到5.3版本,nf_conntrack一直在迭代优化,但是它本质上还是避免不了lock,依然不能被彻底洗白,遗憾的是,flow offload依赖了nf_conntrack。

那么flow offload的性能收益会不会因为nf_conntrack被打折扣呢?也许吧。

但是,这里要从三方面看这件事:

承认性能损失,毕竟nf_conntrack也带来了很多功能性收益。修改flow offload,接触对nf_conntrack的依赖,同时也丧失了与NAT等功能的联动机制。向前看,flow offload的主场在硬件offload,Netfilter的软flow offload就是个Demo,谁来在乎nf_conntrack呢。

是时候演示怎么玩了。

搭建下面的拓扑:

我们希望的效果是在中间R机器上配置flow offload,从A访问B的流量将被offload,不再需要经过R机器的路由查找。

登录到中间的R机器,打开ip_forward,我是用nftables来配置flow offload的,编写以下的nft配置:

# bypass.confflush rulesettable ip filter {flowtable ft {hook ingress priority 0;devices = {enp0s9, enp0s10};}chain forward {type filter hook forward priority 0;ip protocol tcp flow offload @ft}}

注意⚠️:本文不讲nft,请自行翻阅其手册,非常好玩。

执行nft命令加载它:

root@zhaoya-VirtualBox:~/monitor# nft -f ./bypass.confroot@zhaoya-VirtualBox:~/monitor# nft list flowtablestable ip filter {flowtable f {hook ingress priority filterdevices = { enp0s9, enp0s10 }}}root@zhaoya-VirtualBox:~/monitor#

同时,为了验证流量确实被offload了,我们只需要证明一个flow除了第一个包之外的后续流量没有经过FORWARD链即可,我们添加一条iptables空规则,然后查看确认它的统计数据:

root@zhaoya-VirtualBox:~/monitor# iptables -L FORWARD -vChain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 all -- any any anywhere anywhereroot@zhaoya-VirtualBox:~/monitor#

此时,在机器B上发起到A的ssh连接并随便执行个命令产生数据输出,也就是模拟一个双向通信的TCP流:

查看iptables的统计数据:

root@zhaoya-VirtualBox:~/monitor# iptables -L FORWARD -vChain FORWARD (policy ACCEPT 2 packets, 120 bytes) pkts bytes target prot opt in out source destination 2 120 all -- any any anywhere anywhereroot@zhaoya-VirtualBox:~/monitor#

可见,仅仅有2个包通过了FORWARD链。后续的包均被offload到了网卡的ingress。

那么,为什么是2个包触发了flow offload呢?如果确实是2个包触发flow offload,具体是哪两个包呢?

以下是触发flow offload项被创建添加进flow table的代码:

static void nft_flow_offload_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt){...// 必须关联一个被confirm的conntrack才可被视为一条完整的可offload的流if (!nf_ct_is_confirmed(ct)) goto out;...flow = flow_offload_alloc(ct, &route);...ret = flow_offload_add(flowtable, flow);

可见,可offload的flow的条件是其conntrack必须被confirm,即 完整地经过Netfiler路径的flow才可被offload ,这是合理的,毕竟被当前Box给DROP掉的流不是完整的流,offload它没有任何意义。

因此,一个TCP流的双向包均被FORWARD链 “看到” 是该流被offload的必要条件,所以,我们在iptables FORWARD的统计数据中看到了2个包。

现在,我们已经知道,主机B发起的经由主机R到达主机A的TCP流已经被主机R的flow offload机制给卸载了,如果我们在主机R上DROP掉FORWARD数据包,会怎样呢?

让我们试一下:

root@zhaoya-VirtualBox:~/monitor# iptables -Froot@zhaoya-VirtualBox:~/monitor# iptables -A FORWARD -j DROProot@zhaoya-VirtualBox:~/monitor#root@zhaoya-VirtualBox:~/monitor# tcpdump -i enp0s9 -ntcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on enp0s9, link-type EN10MB (Ethernet), capture size 262144 bytes18:42:17.775319 IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ...18:42:17.775734 IP 192.168.58.7.60987 > 192.168.57.7.22: Flags [.], ...18:42:18.775997 IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ...18:42:18.776370 IP 192.168.58.7.60987 > 192.168.57.7.22: Flags [.], ...

依然可以看到双向的TCP包被转发,这说明了两个事实:

能抓到包说明我们用的flow offload是基于Netfilter的软实现。没有被iptables的FORWARD DROP规则丢包说明iptables没有同步给flowtable。

此时,我们flush掉nftables的flow offload规则,再抓包:

root@zhaoya-VirtualBox:~/monitor# nft flush rulesetroot@zhaoya-VirtualBox:~/monitor# tcpdump -i enp0s9 -ntcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on enp0s9, link-type EN10MB (Ethernet), capture size 262144 bytes18:46:23.304633 IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ...18:46:25.035613 IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ...18:46:28.502334 IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ...18:46:35.427412 IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ...^C

很明显,双向通信被iptables的DROP规则阻滞了,只剩下TCP重传了,看前面的重传间隔时间戳,符合TCP的RTO退避规则。

完成了一个简单的验证实验之后,我们是不是要祈求一个iptables的target呢?这对于大多数熟悉iptables却不熟悉nftables的我们十分有意义,比如如下设置规则即可实现以上类似的flow offload:

iptables -A FORWARD -p tcp -j FLOWOFFLOAD

嗯,有擦掌欲试的想法了,这对我而言并不难,曾经对xtables-addons各种高级玩。

实现这个target的过程顺便还可以把flow offload对nf_conntrack的那个依赖砍掉,实现一个 更加纯粹(不带那些个花里胡哨的NAT支持) 的flow offload机制,换句话说,我将自己定义flowtable结构体,除了元组信息之外,去除其它花里胡哨的东西。

然而,似乎没有必要,因为OpenWRT已经实现了一个xt_FLOWOFFLOAD target了:

netfilter: add a xt_FLOWOFFLOAD target for NAT/routing offload support

This makes it possible to add an iptables rule that offloads routing/NAT
packet processing to a software fast path. This fast path is much
quicker than running packets through the regular tables/chains.

Requires Linux 4.14

Signed-off-by: Felix Fietkau nbd@nbd.name

代码来自其github:
https://github.com/openwrt/openwrt/commit/820f03099894bd48638fb5be326b5c551f0f2b98

似乎我也就不需要再做什么了…

插曲:
有人问我写那么多模块和路由转发优化的文章,为什么却不给社区提交patch呢?很简单,因为我对Linux内核社区这个熟人名利场没有兴趣,整这些东西可以提高自己的知名度,也会极大的削弱兴趣以及降低手艺人(而不是工程师)的效率,规范和协议是为了多人协作的意义存在的,一个人干手艺活儿当然是怎么方便怎么来了,我完全不需要关注一行多少字符,也可以随便命名函数和变量,当然引入很多魔术字也很方便。不过,经理可能不这么想。

此外:

这些模块是给公司写的。这些思路都比较简单,没什么技术含量,整理成套话会比代码好些。

说回flow offload,我觉得整体来看,这里有三方面的内容:

对于转发的包,offload其路由的查找过程,即在FORWARD链cache它的路由项到flowtable。对于本地处理的包,offload其socket的查找过程,即在传输层cache它的socket地址到flowtable。上述1和2,如果网卡支持(可编程SmartNIC),那么将cache置于硬件,即HW offload。

说来说去,还是我几年前想做的那些事,只是一直没有条件玩智能网卡,因为太贵吧。当然,经理并不一定这么认为。

浙江温州皮鞋湿,下雨进水不会胖。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。