Linux flow offload提高路由转发效率

Linux flow offload提高路由转发效率凡是正确的东西 该来的最终还是会来的 当然了 经理可能也有同感 来看看几年前我写的文章 利用 nf conntrack 机制存储路由 省去每包路由查找 https blog csdn net dog250 article details 在 Linux 的连接跟踪 nf conntrack 中缓存私有数据省去每次查找 https blog csdn net dog

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

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

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

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

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

  • 系统在网卡ingress建立flow cache,flow元组命中的将会直接被转发,不必再进行路由查找。
  • 网卡收包进ingress。
  • ingress匹配flow table:
    • 命中:
      1. 获取flow entry项里的路由项。
      2. 解析路由项里的出口网卡dev_out,MAC地址等。
      3. 递减TTL。
      4. 直接dev_out转发。
    • 未命中:
      1. 转交给慢速路径,标准Linux内核处理。
      2. ip_forward在FORWARD hook点将flow entry注入flow table(软件或硬件)。

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


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

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

  1. nf_flow_table
  2. nft_flow_offload

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

root@zhaoya-VirtualBox:~/# modinfo nft_flow_offload |grep depends depends: 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被打折扣呢?也许吧。

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

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

是时候演示怎么玩了。

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

# bypass.conf flush ruleset table 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.conf root@zhaoya-VirtualBox:~/monitor# nft list flowtables table ip filter { 
       flowtable f { 
       hook ingress priority filter devices = { 
       enp0s9, enp0s10 } } } root@zhaoya-VirtualBox:~/monitor# 

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

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

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

 

查看iptables的统计数据:

root@zhaoya-VirtualBox:~/monitor# iptables -L FORWARD -v Chain FORWARD (policy ACCEPT 2 packets, 120 bytes) pkts bytes target prot opt in out source destination 2 120 all -- any any anywhere anywhere root@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 -F root@zhaoya-VirtualBox:~/monitor# iptables -A FORWARD -j DROP root@zhaoya-VirtualBox:~/monitor# root@zhaoya-VirtualBox:~/monitor# tcpdump -i enp0s9 -n tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on enp0s9, link-type EN10MB (Ethernet), capture size  bytes 18:42:17. IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ... 18:42:17. IP 192.168.58.7.60987 > 192.168.57.7.22: Flags [.], ... 18:42:18. IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ... 18:42:18. IP 192.168.58.7.60987 > 192.168.57.7.22: Flags [.], ... 

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

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

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

root@zhaoya-VirtualBox:~/monitor# nft flush ruleset root@zhaoya-VirtualBox:~/monitor# tcpdump -i enp0s9 -n tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on enp0s9, link-type EN10MB (Ethernet), capture size  bytes 18:46:23. 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. IP 192.168.57.7.22 > 192.168.58.7.60987: Flags [P.], ... 18:46:35. 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了:

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

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

此外:






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

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

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

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


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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/214832.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午3:20
下一篇 2026年3月18日 下午3:21


相关推荐

  • qt qtreewidgetitem_qtwidgets

    qt qtreewidgetitem_qtwidgetsQTreeWidget在添加了数据之后点击扩展箭头展开和收缩的时候会显得卡顿,这个是qt内核的一个bug,正常情况下应该是立即响应的,既然是qt的bug,就只能换种方式实现TreeWidget来解决这个bug,具体办法大致可以有一下几种,具体看需求来决定用哪一种方法更好:如果需求要求实现的TreeWidget不要求是完全的TreeWidget,扩展的层次是有限制的,就是只有2层,3层或者…

    2022年10月1日
    5
  • pytest测试框架常用功能_unittest批量加载用例

    pytest测试框架常用功能_unittest批量加载用例目录前言###文章内容有配套的学习视频和笔记都放在了文章末尾###1、什么是单元测试框架2、单元测试框架主要做什么3、单元测试框架和自动化测试框架有什么关系4、Pytest测试框架说明5、Pytest框架和Unittest框架区别重点:配套学习资料和视频教学前言大家好我是测试达人,最近我会更新一系列pytest的框架全套教程,不比你在培训机构花的几千块买的ppt教程好吗?==白嫖真香!!###文章内容有配套的学习视频和笔记都放在了文章末尾###1、什么是单

    2022年10月14日
    7
  • gimp教程:gimp界面介绍「建议收藏」

    gimp教程:gimp界面介绍「建议收藏」GIMP(跨平台图像处理程序)是一个开发源代码的光栅与图像编辑的先进功能,关于GIMP的界面,初学者都了解吗?下面是小编整理的关于gimp教程中gimp界面介绍,快来分享吧!gimp界面介绍:gimp图像窗口Gimp图像窗口是打开图形图像文件时图像显示的窗口,关闭窗口右上角的关闭按钮的话程序也将随之关闭。如下图所示,其窗口中包含下面几个元素:A、居于最上面的标题栏,最左面是Gimp图标(icons),中间是图像名,如果是刚开始打开无图像的话显示”GNUImageManipulatio..

    2022年6月15日
    37
  • arp命令linux上,linux arp命令的使用

    arp命令linux上,linux arp命令的使用arp 命令处理系统的 ARP 缓存 可以清除缓存中的地址映射 建立新的地址映射 语法 arp v n Htype iif a hostname arp v iif dhostname pub arp v Htype iif shostnamehw addr temp arp v Htype iif sho

    2026年3月17日
    2
  • heapdump.phd分析工具_dump文件分析工具

    heapdump.phd分析工具_dump文件分析工具heapdump分析工具是一款强大的数据分析工具,它可以用图表的形式来展现相应的分析结果,在使用heapdump分析工具之前请先安装JDK1.6。运行环境1.运行环境要求JDK1.6或以上如果JDK版本过低,报错如下:Exceptioninthread”main”java.lang.NoClassDefFoundError:java/util/regex/PatternSyntaxEx…

    2022年10月2日
    4
  • c#使用SerialPort串口控件

    c#使用SerialPort串口控件

    2021年6月5日
    215

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号