我想知道如何解析 IPv6 标头并获取 TCP 标头及其有效负载的偏移量。
我知道 IPv6 具有固定的基本标头。我还知道 IPv6 中有多个扩展标头,其中包括:路由、片段、目标选项、ESP 等。
我得到的是指向以太网帧缓冲区的指针。现在我可以找到 IPv6 报头的开头,但在有 ESP 报头的情况下无法计算 TCP 报头及其有效负载的开头。有一些 rfc2406 描述了 ESP,但我不清楚。我无法计算 ESP 报头及其数据的大小,所以我无法找出 TCP 的偏移量。
我的问题是:在 IPv6 的情况下,如何计算 ESP 扩展头的长度?
答案1
一般来说,每个扩展头以两个字节开头,指示下一个头的类型和当前头的长度。
不过,你不能假设这通常都是正确的。这取决于你正在解析的扩展头的类型,该类型由上一个头中的下一个头字段指示。
与其他 IPv6 扩展标头相比,IPSec 中长度字段的解释略有不同。
对于普通的 IPv6 扩展头,您可以先将字节值加 1,然后乘以 8,得到头的长度。
对于 IPSec 标头,您可以先将字节值加 2,然后乘以 4,得到标头的长度。
为什么要这样设计
数据包的接收方需要在处理数据包时验证数据包的长度字段。IPv6 要求所有扩展报头的长度均为 8 字节的倍数,以便能够在 64 位 CPU 上高效实现。
扩展头的最小长度为 8 个字节,虽然扩展头没有最大长度,但是如果扩展头声称超出其所嵌入的数据包的末尾,则是无效的。
任何一项验证错误都极有可能导致严重的安全问题。例如,如果接收方盲目接受声称长度为 0 字节的扩展头,则会导致解析假设下一个扩展头从完全相同的地址开始。这可能会导致无限循环,接收方会不断解析相同的扩展头。如果收到这样的损坏数据包,这很可能会导致操作系统立即冻结。
但是通过要求接收方加 1 并乘以 8,就不再需要进行两次验证,因为无论您将此计算应用于 0 到 255 范围内的哪个字节值,它都会产生至少为 8 且是 8 的倍数的结果。作为额外的好处,它将可能的扩展头大小从 255 字节增加到 2048 字节。
接收方仍需验证最大长度,这是无法回避的。任何具有有效扩展报头的数据包都可能因将主报头中的有效载荷长度字段减小到对于扩展报头来说太短而受到破坏。
IPSec 为何与众不同
IPSec 可用于 IPv4 和 IPv6。但 IPv4 通常仅需要 4 字节对齐(针对 32 位 CPU 进行了优化),而 IPv6 需要 8 字节对齐(针对 64 位 CPU 进行了优化)。因此,在这种情况下乘数仅为 4。不过,报头的最小长度仍为 8 字节。
这意味着当在 IPv6 上使用 IPSec 时,接收方必须验证长度字段是否为偶数字节值。但是,假设 CPU 架构能够访问未对齐的值(性能会降低),那么如果开发人员忘记了此特定验证,风险就会很小。最坏的情况是,损坏的数据包将需要更多 CPU 周期来处理。
使用它来查找有效载荷头
以下伪代码显示如何处理标题。
Initialize a pointer to the first byte of the IPv6 header.
Initialize the header type to 41.
While the header type is a known IP or extension header:
Compute current header length
Process current header if applicable
Copy next header field from current header
Move pointer forward by current header length
在该算法中,报头类型 4 和 41 是特殊的,因为它们表示 IPv4 报头和 IPv6 报头。