UDP 接收段合并卸载 (URO)

从 Windows 11 版本 24H2 开始,UDP 接收段合并卸载 (URO) 使网络接口卡 (NIC) 能够合并 UDP 接收段。 NIC 可以将与一组规则匹配的同一流中的 UDP 数据报合并为逻辑连续缓冲区。 然后将这些组合的数据报作为单个大型数据包指示给 Windows 网络堆栈。

合并 UDP 数据报可降低在高带宽流中处理数据包的 CPU 成本,从而提高吞吐量并减少每字节的周期数。

以下部分介绍了合并 UDP 数据包的规则以及如何编写 URO 微型端口驱动程序。

合并 UDP 数据包的规则

只能在满足以下所有条件的数据包上尝试 URO 合并:

  • 对于所有数据包,IpHeader.Version 相同。
  • 对于所有数据包,IpHeader.SourceAddressIpHeader.DestinationAddress 相同。
  • 对于所有数据包,UdpHeader.SourcePortUdpHeader.DestinationPort 相同。
  • UdpHeader.Length 除了最后一个数据包可能较小之外,所有数据包都是相同的。
  • UdpHeader.Length 必须为非零。
  • UdpHeader.Checksum,如果非零,则必须在所有数据包上正确。 这意味着接收检查和卸载必须验证数据包。
  • 对于所有数据包,Layer 2 headers 必须相同。

如果数据包为 IPv4,它们还必须满足以下条件:

  • 对于所有数据包,IPv4Header.Protocol == 17 (UDP)。
  • EthernetHeader.EtherType == 0x0800 适用于所有数据包。
  • 接收的数据包上的 IPv4Header.HeaderChecksum 必须正确。 这意味着接收检查和卸载必须验证报头。
  • 对于所有数据包,IPv4Header.HeaderLength == 5(无 IPv4 选项标头)。
  • 对于所有数据包,IPv4Header.ToS 相同。
  • 对于所有数据包,IPv4Header.ECN 相同。
  • 对于所有数据包,IPv4Header.DontFragment 相同。
  • 对于所有数据包,IPv4Header.TTL 相同。
  • IPv4Header.TotalLength == UdpHeader.Length* + length(IPv4Header) 适用于所有数据包。

如果数据包为 IPv6,它们还必须满足以下条件:

  • 对于所有数据包,IPv6Header.NextHeader == 17 (UDP) (无扩展标头)。
  • 对于所有数据包,EthernetHeader.EtherType == 0x86dd (IPv6)。
  • 对于所有数据包,IPv6Header.TrafficClassIPv6Header.ECN 相同。
  • 对于所有数据包,IPv6Header.FlowLabel 相同。
  • 对于所有数据包,IPv6Header.HopLimit 相同。
  • IPv6Header.PayloadLength == UdpHeader.Length 适用于所有数据包。

URO 数据包结构

生成的单合并单元 (SCU) 必须具有单个 IP 标头和 UDP 标头,后跟所有合并的数据报的 UDP 有效负载连接在一起。

URO 指示必须将 IPv4Header.TotalLength 字段设置为 SCU 的总长度,或将 IPv6Header.PayloadLength 字段设置为 UDP 有效负载的长度,并将 UdpHeader.Length 字段设置为合并有效负载的长度。

如果第 2 层 (L2) 标头存在于合并的数据报中,则 SCU 必须包含有效的 L2 标头。 SCU 中的 L2 标头必须与合并的数据报的 L2 标头类似。

校验和验证与指示

URO 指示必须将 IPv4Header.HeaderChecksumUdpHeader.Checksum 字段设置为零,并填写 SCU 上的校验和卸载带外信息,指示 IPv4 和 UDP 校验和成功。

匹配所有合并条件但校验和验证失败的数据包必须单独指示。 在它之后收到的数据包不得与在它之前收到的数据包合并。

例如,假设从同一流接收到数据包 1、2、3、4 和 5,但是数据包 3 未通过校验和检查。 数据包 1 和 2 可以合并在一起,数据包 4 和 5 可以合并在一起,但数据包 3 不得与任一 SCU 合并。 数据包 1 和 2 不得与数据包 4 和 5 合并。 数据包 2 是 SCU 中的最后一个数据包,数据包 4 启动新的 SCU。 此外,在指示数据包 3 之前,必须指示包含数据包 1 和 2 的 SCU,并且必须在包含数据包 4 和 5 的 SCU 之前指示数据包 3。

数据包合并和流分离

多个流的数据包可以并行合并,因为硬件和内存允许。 来自不同流的数据包不得合并在一起。

来自多个接收交错的数据包可以被分离,并与其各自的流合并。 例如,如果数据包按 A、A、B、C、B、A 的顺序到达,则 A 流中的数据包可以合并为 AAA,B 流中的数据包合并为 BB,而 C 流中的数据包可以正常显示,也可以与流 C 中挂起的 SCU 合并。

给定流中的数据包不得相互重新排序。 例如,A 流的数据包必须按接收的顺序合并,而不考虑从 B 和 C 流收到的数据包。

用于控制 URO 的 INF 关键字

以下关键字可用于通过注册表项设置启用/禁用 URO:

  • *UdpRsc

枚举标准化 INF 关键字具有以下属性:

  • SubkeyName:必须在 INF 文件中指定且出现在注册表中的关键字的名称。

  • ParamDesc:与 SubkeyName 关联的显示文本。

  • :与列表中的每个选项关联的枚举整数值。 此值存储在 NDI\params\SubkeyName\Value中。

  • EnumDesc:与菜单中显示的每个值关联的显示文本。

  • 默认:菜单的默认值。

SubkeyName ParamDesc EnumDesc
*UdpRsc URO 0 已禁用
1 (默认值) Enabled

有关使用枚举关键字的详细信息,请参阅 枚举关键字

编写 URO 微型端口驱动程序

从 NDIS 6.89 开始,URO 的 NDIS 接口可促进 TCP/IP 与 NDIS 微型端口驱动程序之间的通信。

报表 URO 功能

微型端口驱动程序在 NDIS_OFFLOAD 结构的 UdpRsc 成员中播发对 URO 的支持,并将其传递给 NdisMSetMiniportAttributes 函数。

查询 URO 功能

若要检查微型端口驱动程序是否支持 URO,NDIS 驱动程序和其他应用程序可以查询返回 NDIS_OFFLOAD 结构的 OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES OID。

查询 URO 状态

若要确定当前的 URO 状态,NDIS 驱动程序和其他应用程序可以查询 OID_TCP_OFFLOAD_CURRENT_CONFIG OID 请求。 NDIS 处理此 OID,不会将其传递到微型端口。

更改 URO 状态

可以通过发出 OID_TCP_OFFLOAD_PARAMETERS OID 请求来启用或禁用 URO。 此 OID 使用 NDIS_OFFLOAD_PARAMETERS 结构。 在此结构中,UdpRsc.Enabled 成员可以具有以下值:

意义
NDIS_OFFLOAD_PARAMETERS_UDP_RSC_NO_CHANGE
0
微型端口驱动程序不应更改当前设置。
NDIS_OFFLOAD_PARAMETERS_UDP_RSC_DISABLED
1
URO 已禁用。
NDIS_OFFLOAD_PARAMETERS_UDP_RSC_ENABLED
2
已启用 URO。

当驱动程序使用标志集处理 OID_TCP_OFFLOAD_PARAMETERS OID 请求 NDIS_OFFLOAD_PARAMETERS_UDP_RSC_DISABLED 时,NIC 必须等待完成请求,直到指示所有现有的合并段和未完成的 URO 指示。 这可确保跨 NDIS 组件同步 URO 启用/禁用事件。

微型端口驱动程序处理 OID_TCP_OFFLOAD_PARAMETERS OID 请求后,微型端口驱动程序必须发出具有更新卸载状态的 NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG 状态指示。

NDIS_OFFLOAD_PARAMETERS 中的 NDIS_OFFLOAD_PARAMETERS_SKIP_REGISTRY_UPDATE 标志允许仅禁用 URO 的运行时。 使用此标志所做的更改不会保存到注册表。

在 NDIS 6.89 及更高版本中选择退出 URO

面向 NDIS 6.89 及更高版本的驱动程序应了解 URO 数据包并正常处理它们。 若要选择退出 URO:

此方法可确保不熟悉 URO 的组件不会收到 URO NBL。 如果存在不支持 URO 的 LWF 或协议驱动程序,则 NDIS 在绑定期间在微型端口上禁用 URO。

URO 驱动程序的编程注意事项

实现支持 URO 的微型端口驱动程序时,请考虑以下问题。

Winsock URO API

有关 Winsock URO API 的信息,请参阅 IPPROTO_UDP 套接字选项。 请参阅有关 UDP_RECV_MAX_COALESCED_SIZEUDP_COALESCED_INFO的信息。

Windows TCP/IP 堆栈更新

Microsoft TCP/IP 传输在与 NDIS 绑定时会启用 URO,除非配置阻止此操作。

WFP 标注可以使用 FWPS_CALLOUT2 中的 FWP_CALLOUT_FLAG_ALLOW_URO 来宣传其对 URO 的支持。 如果在 URO 敏感层注册了不兼容的 WFP 回调函式,则在回调函式注册期间,操作系统会禁用 URO。

如果套接字选择加入 URO,其最大合并大小大于或等于硬件卸载大小,则堆栈会将 NBL 从未经修改的硬件传送到套接字。 如果套接字选择加入较小的最大合并大小,堆栈会将合并接收分解为套接字的较小大小。

如果套接字未选择加入 URO,则堆栈将重新分段该套接字的接收。 如果没有硬件 URO,现有软件 URO 功能将继续可用。