卸载大型 TCP 数据包的段
NDIS 微型端口驱动程序可以卸载大于网络介质的最大传输单元 (MTU) 的大型 TCP 数据包分段。 支持大型 TCP 数据包分段的 NIC 还必须能够:
计算包含 IP 选项的发送数据包的 IP 校验和。
计算包含 TCP 选项的发送数据包的 TCP 校验和。
NDIS 版本 6.0 及更高版本支持大型发送卸载版本 1 (LSOv1) ,这类似于 NDIS 5 中的大型发送卸载 (LSO) 。x. NDIS 版本 6.0 及更高版本还支持大型发送卸载版本 2 (LSOv2) ,后者提供增强的大型数据包分段服务,包括对 IPv6 的支持。
支持 LSOv2 和 LSOv1 的微型端口驱动程序必须从 NET_BUFFER_LIST 结构 OOB 信息中确定卸载类型。 驱动程序可以使用 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 结构的 Type 成员来确定驱动程序堆栈使用的是 LSOv2 还是 LSOv1,并执行相应的卸载服务。 包含 LSOv1 或 LSOv2 OOB 数据的任何NET_BUFFER_LIST结构也包含单个 NET_BUFFER 结构。 有关NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO的详细信息,请参阅 访问 TCP/IP 卸载NET_BUFFER_LIST信息。
但是,如果微型端口收到 OID_TCP_OFFLOAD_PARAMETERS 关闭微型端口上的 LSO 功能,并且微型端口成功完成 OID 后,微型端口应 删除包含任何 非零 LSOv1 或 LSOv2 OOB 数据的所有NET_BUFFER_LIST, (NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO) 。
TCP/IP 传输仅卸载满足以下条件的大型 TCP 数据包:
数据包是 TCP 数据包。 TCP/IP 传输不会卸载大型 UDP 数据包进行分段。
数据包必须至少按微型端口驱动程序指定的最小段数进行整除。 有关详细信息,请参阅报告 NIC 的 LSOv1 TCP 数据包分段功能和报告 NIC 的 LSOv2 TCP 数据包分段功能。
数据包不是环回数据包。
数据包不会通过隧道发送。
在卸载大型 TCP 数据包进行分段之前,TCP/IP 传输:
- 汇报与NET_BUFFER_LIST结构关联的大数据包分段信息。 此信息是NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO结构,是与NET_BUFFER_LIST结构关联的NET_BUFFER_LIST信息的一部分。 有关 NET_BUFFER_LIST 信息的详细信息,请参阅 访问 TCP/IP 卸载NET_BUFFER_LIST信息。 TCP/IP 传输将 MSS 值设置为 MSS) (最大段大小。
对于 LSOv1,将大型 TCP 数据包的总长度写入数据包的 IP 标头的“总长度”字段。 总长度包括 IP 标头的长度、IP 选项(如果存在)的长度、TCP 标头的长度、TCP 选项的长度(如果存在)以及 TCP 有效负载的长度。 对于 LSOv2,将数据包的 IP 标头的“总长度”字段设置为 0。 微型端口驱动程序应根据NET_BUFFER_LIST结构中第一个NET_BUFFER结构的长度来确定数据包的长度。
计算 TCP 伪标头的补和,并将此总和写入 TCP 标头的校验和字段。 TCP/IP 传输通过伪标头器中的以下字段计算其补总和:源 IP 地址、目标 IP 地址和协议。 TCP/IP 传输提供的伪标头的补总和使 NIC 能够提前开始计算 NIC 派生自大型 TCP 数据包的每个数据包的实际 TCP 校验和,而无需检查 IP 标头。 请注意,RFC 793 规定通过源 IP 地址、目标 IP 地址、协议和 TCP 长度计算伪标头校验和。 (TCP 长度是 TCP 标头的长度加上 TCP 有效负载的长度。TCP 长度不包括伪标头的长度。) 但是,由于基础微型端口驱动程序和 NIC 从 TCP/IP 传输向下传递的大型数据包生成 TCP 段,因此传输不知道每个 TCP 段的 TCP 有效负载的大小,因此无法在伪标头中包含 TCP 长度。 相反,如下所述,NIC 扩展 TCP/IP 传输提供的伪标头校验和,以涵盖每个生成的 TCP 段的 TCP 长度。
将正确的序列号写入 TCP 标头的“序列号”字段。 序列号标识 TCP 有效负载的第一个字节。
微型端口驱动程序在其 MiniportSendNetBufferLists 或 MiniportCoSendNetBufferLists 函数中获取NET_BUFFER_LIST结构后,可以使用 tcpLargeSendNetBufferListInfo_Id调用 NET_BUFFER_LIST_INFO 宏,以获取 TCP/IP 传输写入的 MSS 值。
微型端口驱动程序从数据包的 IP 标头获取大数据包的总长度,并使用 MSS 值将大型 TCP 数据包划分为较小的数据包。 每个较小的数据包都包含 MSS 或更少的用户数据字节。 请注意,只有从分段大数据包创建的最后一个数据包应包含小于 MSS 用户数据字节。 从分段数据包创建的所有其他数据包都应包含 MSS 用户数据字节。 如果不遵循此规则,创建和传输不必要的额外数据包可能会降低性能。
微型端口驱动程序将 MAC、IP 和 TCP 标头固定到派生自大型数据包的每个段。 微型端口驱动程序必须计算这些派生数据包的 IP 和 TCP 校验和。 为了计算派生自大型 TCP 数据包的每个数据包的 TCP 校验和,NIC 计算 TCP 标头和 TCP 有效负载) 的 TCP 校验和 (的变量部分,将此校验和添加到 TCP/IP 传输计算的伪标头的补码之和中,然后计算校验和的 16 位校验和的补数。 有关计算此类校验和的详细信息,请参阅 RFC 793 和 RFC 1122。
下图显示了大型数据包的分段。
大型 TCP 数据包中的 TCP 用户数据的长度应等于或小于微型端口驱动程序分配给 MaxOffLoadSize 值的值。 有关 MaxOffLoadSize 值的详细信息,请参阅报告 NIC 的 LSOv1 TCP 数据包分段功能和报告 NIC 的 LSOv2 TCP 数据包分段功能。
在驱动程序发出状态指示以指示 对 MaxOffLoadSize 值进行更改之后,如果驱动程序收到使用以前的 MaxOffLoadSize 值的 LSO 发送请求,则它不得崩溃。 相反,驱动程序可能会使发送请求失败。
独立发出报告 MaxOffLoadSize 值更改的状态指示的中间驱动程序必须确保未发出状态指示的基础微型端口适配器不会获得大小大于微型端口适配器报告的 MaxOffLoadSize 值的任何数据包。
响应 OID_TCP_OFFLOAD_PARAMETERS 关闭 LSO 服务的微型端口中间驱动程序必须在 LSO 发送请求仍可能到达微型端口驱动程序的一小段时间内做好准备。
段数据包中的 TCP 用户数据的长度必须小于或等于 MSS。 MSS 是 TCP 传输通过使用与NET_BUFFER_LIST结构关联的 LSO NET_BUFFER_LIST信息向下传递 的 ULONG 值。 请注意,只有从分段大数据包创建的最后一个数据包应包含小于 MSS 用户数据字节。 从分段数据包创建的所有其他数据包都应包含 MSS 用户数据字节。 如果不遵循此规则,创建和传输不必要的额外数据包可能会降低性能。
派生自大型 TCP 数据包的段数据包数必须等于或大于微型端口驱动程序指定的 MinSegmentCount 值。 有关 MinSegmentCount 值的详细信息,请参阅报告 NIC 的 LSOv1 TCP 数据包分段功能和报告 NIC 的 LSOv2 TCP 数据包分段功能。
以下假设和限制适用于处理任何支持 LSO 的微型端口驱动程序的 IP 和 TCP 标头,无论版本如何:
不会设置 TCP/IP 传输卸载的大型 TCP 数据包的 IP 标头中的 MF 位,IP 标头中的片段偏移量将为零。
不会设置大型 TCP 数据包的 TCP 标头中的 URG、RST 和 SYN 标志,TCP 标头中 (指针) 的紧急偏移量将为零。
如果设置了大型数据包的 TCP 标头中的 FIN 位,则微型端口驱动程序必须在它从大型 TCP 数据包创建的最后一个数据包的 TCP 标头中设置此位。
如果设置了大型 TCP 数据包的 TCP 标头中的 PSH 位,则微型端口驱动程序必须在它从大型 TCP 数据包创建的最后一个数据包的 TCP 标头中设置此位。
如果设置了大型 TCP 数据包的 TCP 标头中的 CWR 位,微型端口驱动程序必须在它从大型 TCP 数据包创建的第一个数据包的 TCP 标头中设置此位。 微型端口驱动程序可以选择在从大型 TCP 数据包创建的最后一个数据包的 TCP 标头中设置此位,尽管这不太理想。
如果大型 TCP 数据包包含 IP 选项或 TCP 选项 (或两者) ,微型端口驱动程序会将这些选项不更改地复制到派生自大型 TCP 数据包的每个数据包。 具体而言,NIC 不会递增“时间戳”选项。
(以太网、IP、TCP) 的所有数据包标头都将位于数据包的第一个 MDL 中。 标头不会拆分到多个 MDL 中。
提示
启用 LSO 时,此假设有效。 否则,如果未启用 LSO,微型端口驱动程序无法假定 IP 标头与以太网标头位于同一 MDL 中。
微型端口驱动程序必须按照从 TCP/IP 传输接收 NET_BUFFER_LIST结构的顺序 ,以NET_BUFFER_LIST结构发送数据包。
处理大型 TCP 数据包时,微型端口适配器只负责分段数据包,并将 MAC、IP 和 TCP 标头附加到派生自大型 TCP 数据包的数据包。 TCP/IP 传输 (执行所有其他任务,例如根据远程主机的接收窗口大小) 调整发送窗口大小。
在完成大型数据包 (的发送操作(例如 ,使用 NdisMSendNetBufferListsComplete 或 NdisMCoSendNetBufferListsComplete) )之前,微型端口驱动程序会写入 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO值 (NET_BUFFER_LIST 大发送卸载的信息,) 以及从大型 TCP 数据包创建的所有数据包中成功发送的 TCP 用户数据字节总数。
除了之前的 LSO 要求外,支持 LSOv2 的微型端口驱动程序还必须:
支持 IPv4 或 IPv6,或者同时支持 IPv4 和 IPv6。
支持在网络接口卡 (NIC) 生成的每个段数据包中从大型数据包复制 IPv4 选项。
支持在每个 TCP 段数据包中从大型 TCP 数据包复制 IPv6 扩展标头。
支持在微型端口驱动程序生成的每个 TCP 段数据包中复制 TCP 选项。
使用 NET_BUFFER_LIST 结构中的 IP 和 TCP 标头作为模板,为每个段数据包生成 TCP/IP 标头。
使用 IP 标识 (IP ID) 从 0x0000 到 0x7FFF 范围内的值。 (从0x8000到0xFFFF的范围是为支持 TCP 烟囱卸载的设备保留的。) 例如,如果模板 IP 标头以标识字段值0x7FFE开头,则第一个 TCP 段数据包的 IP ID 值必须0x7FFE,后跟0x7FFF、0x0000、0x0001等。
使用 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 的 TcpHeaderOffset 成员中的字节偏移量来确定 TCP 标头的位置,从数据包的第一个字节开始。
将与每个 LSOv2 NET_BUFFER_LIST 结构关联的NET_BUFFER结构数限制 为 1。
注意
这是支持 LSOv2 的微型端口驱动程序的新要求。 虽然建议对 LSOv1 微型端口驱动程序显式强制实施此规则。
根据NET_BUFFER_LIST结构中第一个NET_BUFFER结构的长度确定数据包的总长度。 这不同于 驱动程序用于 LSOv1 的方法。
支持 TCP 选项、IP 选项和 IP 扩展标头。
发送操作完成后,微型端口驱动程序必须将 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 结构的 LsoV2TransmitComplete.Reserved 成员设置为零,并将 LsoV2TransmitComplete.Type 成员设置为NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE。