再谈UDP GSO和GRO

shixudong@163.com

一、UDP/GSO再认识

近期本人收到一位网友私信:“tx-udp-segmentation = on时,发送UDP报文, 发包端抓包发现协议栈仍然分片,这是为什么?”在回答这个问题之前,先简要梳理一下UDP/GSO和TCP/GSO的主要区别:

1、对于TCP,只要开启TSO,GSO总是开启(不受GSO开关控制),只有TSO关闭时,才能通过GSO开关控制GSO开启与否。然而,自内核v4.17起,GSO总是开启,无论TSO开启与否,再也不受GSO开关影响。

2、对于UDP,没有对应的GSO开关,并且对网络应用不透明,需要应用程序设置相应的socket选项显式启用。UDP有类似TSO的硬件卸载开关tx-udp-segmentation,但同样需要应用程序设置相应的socket选项才能发挥tx-udp-segmentation的作用。

所以针对开头的问题,实际上就是传统的UDP应用程序根本无法利用UDP/GSO特性(包括硬件卸载tx-udp-segmentation)。事实上,由于当时没找到简易的验证工具,在《也谈UDP GSO和GRO》一文中,关于抓包工具验证UDP/GSO效果的描述不太全面,在该文中提到,由于较新内核调整了抓包位置,导致无法验证TCP/GSO。但对于UDP/GSO来说,虽然其同样使用了TSO/GSO的底层框架和机制,然而由于UDP/GSO在传输层对网络应用不透明的特性,UDP/GSO的直观效果是可以通过抓包工具验证的。
受到《TPROXY与Wireguard》文末小插曲的启发,可以利用管道串接dd和socat构造UDP大包,再通过socat的setsockopt参数启用UDP/GSO。网卡不支持tx-udp-segmentation时,使用dd if=/dev/zero bs=1024 count=8 |socat -b65536 - UDP-send:192.168.4.68:8888,setsockopt=17:103:1472,可以抓到UDP分段包(除最后一包外每包长度1514)。网卡支持tx-udp-segmentation时,使用前述命令可以抓到长度为8234(8*1024+28+14)的UDP大包。如不带setsockopt参数,则无论网卡是否支持tx-udp-segmentation,使用dd if=/dev/zero bs=1024 count=8 |socat -b65536 - UDP-send:192.168.4.68:8888,只能抓到IP分片包(Fragmented IP)。
UDP/GSO硬件卸载特性tx-udp-segmentation自内核v4.18引入,支持该特性的逻辑网卡在《也谈UDP GSO和GRO》一文有提到,包括内核wireguard、bridge/bond/team等。自内核v6.2起, virtio_net、tap/tun也开始支持tx-udp-segmentation,与前面那些逻辑网卡不同的是,虚拟机virtio_net不仅需要主机tap的配合,还需要QEMU8.0的加持,所以guest和host内核都需要升级到v6.2。此外低版本QEMU升级到QEMU8.0后,还需要额外修改xml文件使得guest的机器类型和QEMU8.0一致。上述逻辑/虚拟网卡中,virtio_net需要手工开启tx-udp-segmentation,其他网卡则默认开启。

二、UDP/GRO再认识

由于当时对网卡的两个特性tx-scatter-gather和tx-scatter-gather-fraglist认识不够深入,网上资料也不全面,导致《也谈UDP GSO和GRO》一文中,对GRO大包转发出口处的抓包分析有瑕疵,对UDP/GRO有关的两个特性rx-gro-list与rx-udp-gro-forwarding认识也不够深入。经过对Linux有关GRO卸载技术源代码的再次学习,对GRO相关底层实现有了进一步的理解,特记录于此。
tx-scatter-gather和tx-scatter-gather-fraglist可以分别对应到内核sk_buff的frags数组和frag_list 链表。在本机发送环节,TCP/GSO和UDP/GSO只使用frags方式发送数据,且网卡必须支持tx-scatter-gather特性(事实上目前基本上所有网卡都已支持tx-scatter-gather);未启用UDP/GSO时,本机发送的UDP大包和ping大包由__ip_make_skb函数转换成frag_list链表,并在IP层被ip_do_fragment拆分成IP分片包(根据以上分析,实际上发送环节的frag_list链表与tx-scatter-gather-fraglist不会发生任何关系。常规情况下,即使发送环节产生了frag_list 链表,也会被ip_do_fragment拆分)。在网卡接收环节,基本上所有硬件网卡也都是使用frags方式接收数据,个别硬件网卡使用frag_list方式接收数据,然后两者都交给内核GRO做进一步的合并处理。
在内核TCP/GRO处理环节,早期内核GRO不支持frags和frag_list组合使用,由于大部分网卡只支持使用frags方式收包,且frags数组最多只能包含MAX_SKB_FRAGS(内核硬编码,取值17)个成员,而且收包不像发包,每个frags只能容纳一个帧(1448字节),导致GRO最多只能合并17*1448+1*1514=26130字节的大包。内核v3.13起GRO已支持frags和frag_list组合使用,最多可合并17*1448(frags)+27*1448(frag_list)+1*1514=65226字节的大包,合并效率较早期内核GRO大大提高。然而该效率提高却并不完全适用于转发环节,虽然转发环节在IP层不对前述大包做拆分与合并动作,但由于硬件网卡基本上都不支持tx-scatter-gather-fraglist,导致带有frag_list属性的大包(包含其frags部分)在继续转发出去前只能由GSO重新拆分为小包。对于长度大于26130字节的大包,显然浪费了不必要的GRO合并和GSO拆分开销。通过抓包也能发现,接收网卡能收到最大65226字节的大包,而转发网卡最大只能发送纯frags方式的26130字节的大包。针对这一不足,自内核v6.4起,引入了CONFIG_MAX_SKB_FRAGS(取值17和45之间,默认还是17,修改需要重新编译内核),MAX_SKB_FRAGS取值45后,内核GRO合并时无需使用frag_list也能合并44*1448(frags)+1*1514=65226字节的大包,转发时再也无需经由GSO重新拆分小包,显著提高了转发效率。
对于UDP来说,目前有三种方法可以接收UDP/GRO包:
1、应用程序显式启用相应的socket选项接收UDP/GRO(内核v5.0);
2、开启rx-gro-list接收和转发UDP/GRO(内核v5.6);
3、开启rx-udp-gro-forwarding接收和转发UDP/GRO(内核v5.12)。
方法3接收和转发UDP/GRO时,采用了TCP/GRO同样的底层框架(合并函数都是skb_gro_receive),存在TCP/GRO同样的问题,即UDP/GRO合并后有部分大包同时采用了frags和frag_list组合。如转发网卡不支持tx-udp-segmentation,包括纯frags方式在内的所有大包都将由GSO重新拆分为小包,显著降低了转发效率。如转发网卡支持tx-udp-segmentation,则只有frag_list属性的大包(包含其frags部分)才需要由GSO重新拆分为小包,相较前者,还能适度平衡转发效率。
方法2接收和转发UDP/GRO时,GRO底层引入了新的合并函数skb_gro_receive_list,UDP/GRO合并大包采用纯frag_list方式,即使转发网卡支持tx-udp-segmentation,但依然不会支持tx-scatter-gather-fraglist,所有大包都将由GSO重新拆分为小包。
显然在转发网卡支持tx-udp-segmentation时,方法3总体转发效率要优于方法2。在转发网卡不支持tx-udp-segmentation时,由于方法2后续GSO拆分frag_list大包较方法3同时拆分frags/frag_list组合方式大包效率有改善,此时方法2转发效率要优于方法3。
基于同样的原因,对于TCP来说,如果转发网卡不支持TSO(如PPPOE、wifi),鉴于先前TCP/GRO不支持纯frag_list方式合并大包,导致后续TCP/GSO拆分frags/frag_list组合方式GRO大包时效率较低。针对这一不足,自内核v6.10起,新增了纯frag_list方式的TCP fraglist GRO support,底层共用UDP/GRO引入的合并函数skb_gro_receive_list。在转发网卡不支持TSO特性时,通过设置入口网卡rx-gro-list=on改用TCP fraglist GRO(仅适用于转发,本机接收仍使用传统方式),可有效改善转发效率
根据源码和官方资料,如果同时开启方法2和方法3,方法2优先,在转发网卡支持tx-udp-segmentation时,考虑到方法2转发效率不如方法3,正确的操作姿势应该是只开启方法3。
方法1只能用于本机接收,GRO底层框架和方法3一致(合并函数为skb_gro_receive),方法2和3既能用于转发,也能用于本机接收。上述三种方法用于本机接收时,考虑到纯flag_list方式涉及到更多的skb申请/释放操作,相对来说,方法1和方法3的效率更佳。然而方法3有一个小瑕疵,本机接收无法用于内核类udp隧道的底层UDP,比如内核wireguard使用的底层UDP。
三、rx-gro-list和rx-udp-gro-forwarding再认识
《也谈UDP GSO和GRO》一文中提到,将wireguard网卡用于转发出口网卡时,为了充分利用其tx-udp-segmentation特性,入口网卡应只启用rx-udp-gro-forwarding。本文则针对wireguard网卡用作入口网卡进行展开分析。
将内核wireguard网卡用作入口网卡接收UDP/GRO大包时,建议同时开启wg网卡以及底层网卡的UDP/GRO功能,分别对应上下两层UDP。由于rx-udp-gro-forwarding实现时的小瑕疵,本机接收无法用于wg网卡的底层UDP,因此底层网卡只能使用rx-gro-list。wg网卡建议使用rx-udp-gro-forwarding,无论后续转发还是自己接收,效率都要高于使用rx-gro-list(详见前面分析过程)。
至于将最新版的用户空间wireguard-go用作入口网卡,如内核版本已为v6.2,wg网卡以及底层网卡的UDP/GRO功能均已自动启用,如内核版本在v5.0和v6.2之间,则底层网卡的UDP/GRO功能自动启用,上层wg网卡则无法支持UDP/GRO功能(相关控制开关本来就没有实际作用)。此外,由于wireguard-go的底层UDP/GRO功能采用前述方法1实现,rx-gro-list和rx-udp-gro-forwarding开关对其压根没有意义,但会受gro开关控制,在切换gro开关后,还需要重新启动wg网卡。
四、关于tx-gso-list和UDP GRO转发
v5.6引入的UDP fraglist GRO/GSO同时新增了两个ethtool开关,即rx-gro-list和tx-gso-list,rx-gro-list已经在《也谈UDP GSO和GRO》和本文做了详细的分析。至于tx-gso-list,在前一篇文章中提到,如入口网卡采用rx-udp-gro-forwarding,出口网卡只要支持tx-udp-segmentation即可卸载或透明转发UDP大包(实际上出口网卡还需要同时支持tx-scatter-gather,只不过因为基本上所有网卡都已具备该特性,默认就不提这一茬了);如入口网卡采用rx-gro-list,出口网卡需要同时支持tx-udp-segmentation、tx-gso-list和tx-scatter-gather-fraglist三种特性,才能卸载或透明转发UDP大包。
前面分析过,本机发送环节,TCP/GSO和UDP/GSO只使用frags方式发送数据,所以用不到网卡的tx-scatter-gather-fraglist特性,事实上目前似乎也没有物理网卡支持该特性。然而在转发环节,取决于入口网卡GRO合并大包所采用方式,转发数据可能包含frags方式或frag_list方式、以及两者的组合。如转发数据为纯frags方式(对应rx-udp-gro-forwarding=on且MAX_SKB_FRAGS=45),出口网卡只需支持tx-udp-segmentation即可;如转发数据为两者组合方式(对应rx-udp-gro-forwarding=on),出口网卡需增加第二个特性tx-scatter-gather-fraglist;如转发数据为纯frag_list方式(对应rx-gro-list=on),出口网卡还需再增加第三个特性tx-gso-list。

UDP/GRO引入tx-gso-list特性的初衷是为了在转发情形下可以独立控制是否由GSO对纯frag_list方式的GRO大包进行软件分段,然而由于纯frag_list方式GRO大包同样受转发网卡tx-scatter-gather-fraglist特性影响,导致tx-gso-list特性不仅显得有点多余,并且增加了纯frag_list方式下UDP/GRO转发的复杂性。

自v5.6引入tx-gso-list特性以来,直到v5.10,只有设置了NETIF_F_GSO_MASK属性的逻辑网卡才支持tx-gso-list和tx-udp-segmentation特性(如bridge/bond/team)。v5.11通过更新NETIF_F_GSO_SOFTWARE定义,使得大部分逻辑网卡也开始支持tx-gso-list和tx-udp-segmentation特性,如wireguard。然而对于bridge/bond/team等设备来说,是否具备NETIF_F_GSO_SOFTWARE特性,完全依赖于下挂网卡。由于:1、支持tx-udp-segmentation特性的硬件网卡依然很少,2、到目前为止,已支持tx-udp-segmentation特性的硬件网卡都不支持tx-gso-list特性。NETIF_F_GSO_SOFTWARE特性的重定义,导致事实上bridge/bond/team不再支持tx-gso-list,并且大部分情形下也不再支持tx-udp-segmentation。v6.2起,虽然virtio_net、tap/tun开始支持tx-udp-segmentation,然而却仍不支持tx-gso-list。
显然,由于网卡(包含逻辑网卡)普遍缺乏对tx-gso-list特性的支持,至少在逻辑网卡层面,纯frag_list方式下UDP GRO大包的转发性能明显低于其他两种方式。
至于v6.10引入的TCP fraglist GRO,因为直接借用了UDP/GRO的rx-gro-list和tx-gso-list特性,也会存在上述tx-gso-list的短板问题。不过由于TCP fraglist GRO的引入,本意就是为了在个别网卡不支持TSO时改善转发效率,实际环境中转发网卡已经不支持TSO,即使支持tx-gso-list也毫无意义,TCP/GRO大包总是需要经由GSO重新拆分。如前所述,纯frag_list方式接收TCP/GRO,只是为了后续转发时改善GSO的拆包效率而已。
v6.10新增的TCP fraglist GRO功能,可以通过抓包观察效果(转发网卡需开启TSO),入口网卡不启用rx-gro-list(off)时,转发网卡上能抓到TCP/GRO大包(物理网卡因为不支持tx-scatter-gather-fraglist,最大包长26130字节;逻辑网卡如支持scatter-gather-fraglist,最大包长可为65226字节)。入口网卡启用rx-gro-list=on时,如前所述,由于物理网卡都不支持tx-scatter-gather-fraglist特性,逻辑网卡不会同时支持tx-scatter-gather-fraglist和tx-gso-list特性,转发网卡只能抓到GSO分段后的小包。

在进行以上抓包测试时,入口网卡请不要选择主机vnetX或虚拟机virtio_net。virtio_net具备rx_gro_hw特性,无需启用内核GRO就能收取GSO大包,在virtio_net上即使启用rx-gro-list=on,也因为透传的GSO大包无需合并,不会转换成纯frag_list方式。主机上的vnetX,其底层为tun驱动,vnetX也能直接收取虚拟机发出的GSO大包,并且其压根没有GRO功能一说。这两种情形下,TCP fraglist GRO功能针对这些GSO大包无法发挥作用,透传的纯frags方式GSO大包将不受转发网卡tx-scatter-gather-fraglist和tx-gso-list特性影响,转发网卡上仍能抓到TCP/GSO大包,从而无法通过抓包观察TCP fraglist GRO效果。如非要选择virtio_net作为入口网卡进行抓包测试,可关闭其rx_gro_hw特性,其实质是关闭主机上对应vnetX网卡的TSO功能。

<think>首先,用户的问题是关于如何关闭GSO(Generic Segmentation Offload)。用户说:"请问how to disable Generic Segmentation Offload (GSO)",所以我需要提供如何禁用GSO的指导。 从系统级指令中,我有几个引用: - 引用[1]提到了一些可以启用/禁用的特性,包括GSO(generic segmentation offload)。 - 引用[2]讨论了GSO_UDP_L4。 - 引用[3]解释了GSO的全称实现细节。 - 引用[4]显示了代码片段,涉及sk_setup_caps函数,其中设置了GSO相关标志。 用户明确说:"我想用户想要了解如何关闭GSO(Generic Segmentation Offload)的方法。" 所以,我的回答应该专注于禁用GSO的步骤。 回答结构: - 使用中文回答。 - 结构清晰,逐步解决问题。 - 确保回答真实可靠。 - 在引用的段落末尾添加引用标识。 - 生成相关问题。 关键点: 1. **什么是GSO?** 简要解释GSO以提供背景。 - GSO(Generic Segmentation Offload)是一种网络优化技术,允许操作系统将大型数据包的处理卸载到网络硬件上,减少CPU负载[^3]。 2. **为什么禁用GSO?** 用户可能在某些场景下需要禁用GSO,比如调试网络问题或兼容性问题。 3. **如何禁用GSO?** 提供具体步骤。 -Linux系统中,可以通过ethtool命令来禁用GSO- 步骤: a. 检查当前接口的GSO状态:`ethtool -k <interface> | grep gso` b. 禁用GSO:`ethtool -K <interface> gso off` - 需要root权限。 - 例如:`sudo ethtool -K eth0 gso off` - 禁用后,可以使用`ethtool -k eth0`验证。 - 引用[1]提到了一些特性,包括gso,所以可以引用它。 - 引用[3][4]提供背景,但不直接涉及禁用方法。 4. **永久禁用?** 上述命令是临时的;重启后会恢复。要永久禁用,需要修改网络配置文件。 - 例如,在Ubuntu中,编辑`/etc/network/interfaces`或在systemd系统中使用networkd配置文件。 5. **注意事项:** 禁用GSO可能会影响性能,因为CPU需要处理分段。 生成相关问题: - 基于主题,生成2-3个相关问题。 - 例如:1. 禁用GSO后如何验证是否生效?2. GSO与其他卸载特性(如TSO)的关系?3. 在哪些情况下需要禁用GSO? 回答结构: 1. **介绍GSO:** 简要解释什么是GSO及其作用。 2. **禁用步骤:** 提供命令行指令。 3. **永久禁用:** 解释如何使设置永久生效。 4. **验证:** 如何检查GSO是否已禁用。 5. **注意事项:** 提醒潜在的性能影响。 6. **相关问题:** 在末尾生成相关问题。 确保LaTeX格式正确:用户提到行内数学表达式用$...$,独立公式用$$...$$。但在这个上下文中,可能不需要数学公式,除非涉及计算。如果需要,比如解释数据包大小,可以使用。 引用标识:在引用段落末尾添加[^n]。 例如:"GSO(Generic Segmentation Offload)是一种网络优化技术[^3]。" 或者 "通过ethtool命令可以禁用GSO[^1]。" 最终回答: - 开头:直接回应用户问题。 - 主体:逐步指导。 - 结尾:相关问题。</think>### 如何禁用 Generic Segmentation Offload (GSO) GSO(Generic Segmentation Offload)是一种网络优化技术,允许操作系统将大型数据包的分段处理卸载到网络硬件上,从而减少 CPU 负载并提升网络性能[^3]。然而,在某些场景下(如调试网络问题、兼容性测试或性能分析),可能需要临时或永久禁用 GSO。以下我将逐步指导如何在 Linux 系统中禁用 GSO,确保方法真实可靠(基于标准 Linux 工具文档)。操作需要 root 权限,请谨慎执行以避免意外影响网络性能。 #### 步骤 1: 理解 GSO 状态(可选) 在禁用 GSO 前,建议先检查当前网络接口的 GSO 状态。这有助于确认后续操作是否生效。 - 使用 `ethtool` 命令查看接口(如 `eth0`)的卸载特性: ```bash ethtool -k eth0 | grep gso ``` 输出可能类似: ``` generic-segmentation-offload: on ``` 这表示 GSO 已启用[^1]。 #### 步骤 2: 临时禁用 GSO 临时禁用 GSO 会在系统重启后失效,适合快速测试。 - 执行以下命令(以接口 `eth0` 为例): ```bash sudo ethtool -K eth0 gso off ``` - **解释**:`ethtool -K` 用于修改内核卸载特性,`gso off` 指定禁用 GSO[^1]。 - 验证是否禁用成功: ```bash ethtool -k eth0 | grep gso ``` 输出应为: ``` generic-segmentation-offload: off ``` #### 步骤 3: 永久禁用 GSO 要使禁用设置永久生效,需修改网络配置文件。具体方法取决于 Linux 发行版: - **对于 systemd-networkd(Ubuntu/Debian 等)**: 1. 编辑接口配置文件(如 `/etc/systemd/network/50-eth0.network`): ```ini [Match] Name=eth0 [Link] GenericSegmentationOffload=no ``` 2. 重启网络服务: ```bash sudo systemctl restart systemd-networkd ``` - **对于 NetworkManager(CentOS/RHEL 等)**: 1. 创建或修改配置文件(如 `/etc/sysconfig/network-scripts/ifcfg-eth0`),添加: ``` ETHTOOL_OPTS="-K ${DEVICE} gso off" ``` 2. 重启网络: ```bash sudo systemctl restart NetworkManager ``` - **验证永久设置**:重启系统后,再次运行 `ethtool -k eth0 | grep gso` 确认输出为 `off`。 #### 注意事项 - **性能影响**:禁用 GSO 后,CPU 需处理所有数据包分段,可能导致网络吞吐量下降或延迟增加。仅在必要时禁用,并监控系统负载[^3]。 - **兼容性**:GSO 与其他卸载特性(如 TSO 或 GRO)相关。禁用 GSO 可能自动影响相关特性;建议用 `ethtool -k eth0` 全面检查[^1][^4]。 - **硬件限制**:并非所有网卡都支持 GSO 修改。如果命令失败,检查网卡驱动文档或使用 `ethtool -i eth0` 确认支持情况。 #### 引用说明 本指南基于 Linux 网络工具标准文档实践经验。例如,`ethtool` 是管理卸载特性的推荐工具,其参数 `-K gso off` 直接对应内核特性开关[^1]。GSO 的实现细节(如分段逻辑)可参考内核代码,但用户层操作无需深入此层面[^3][^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值