3. 分片重组机制:接收端分片重组流程、超时机制与资源管理
IP分片在发送端将大数据包切分为若干小片(Fragment),接收端则必须将这些片重新组装为完整的原始IP数据报。分片重组是网络协议栈中一个计算密集且状态敏感的过程,其效率直接影响大包传输的吞吐量,而超时与资源管理则决定了系统的健壮性与抗攻击能力。
3.1 接收端分片重组流程
Linux内核在IPv4协议栈中通过 ip_defrag() 函数完成分片重组。重组流程遵循以下核心步骤:
- 分片到达:网络设备驱动将数据包送入IP层,
ip_local_deliver()检测到MF标志或片偏移非零时,将分片交给ip_defrag()。 - 查找重组队列:根据
(源IP, 目的IP, 标识符ID, 协议)四元组哈希查找对应的inet_frag_queue队列。若不存在则新建队列。 - 分片插入与排序:将新分片按片偏移量插入队列中的红黑树(rbtree),同时更新已接收数据长度和分片位图。
- 完整性检查:当所有分片(包括首片和尾片)均已到达时,触发重组操作。
- 重组与提交:将各分片的数据载荷按偏移顺序拷贝到连续缓冲区,重建IP首部,然后提交给上层协议(TCP/UDP)。
关键数据结构:
struct inet_frag_queue 是重组队列的核心,内部包含红黑树根节点、已接收长度、分片位图、定时器以及引用计数。每个分片以 struct sk_buff 形式挂载在红黑树上。
性能要点: 红黑树查找与插入的时间复杂度为 O(log n),相比早期内核使用的链表(O(n))在大分片数场景下性能提升显著。对于常见的小分片数(2-5片),红黑树与链表的差异不大,但在极端分片攻击场景下红黑树能有效防止CPU耗尽。
3.2 超时机制
分片重组不能无限等待。Linux内核为每个重组队列维护一个低精度定时器(基于jiffies),默认超时时间为 IP_FRAG_TIME(通常为30秒)。超时机制的设计要点如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
IP_FRAG_TIME |
30 秒 (HZ * 30) | 分片重组队列的最大存活时间 |
sysctl net.ipv4.ipfrag_time |
30 | 用户态可调参数,单位秒 |
sysctl net.ipv4.ipfrag_high_thresh |
262144 (256KB) | 分片内存使用上限(触发回收) |
sysctl net.ipv4.ipfrag_low_thresh |
196608 (192KB) | 分片内存使用下限(停止回收) |
超时处理流程:
- 定时器到期时,调用
inet_frag_evictor()回收该队列。 - 回收操作释放所有已缓存的分片
sk_buff,并销毁队列。 - 若系统内存压力大(分片总内存超过
high_thresh),内核会主动提前回收最老的队列,即使其定时器尚未到期。
安全风险: 分片超时机制是防范 分片攻击(Fragment Attack) 的第一道防线。攻击者发送大量不完整的分片流,消耗系统内存。Linux 通过
ipfrag_high_thresh 和 ipfrag_low_thresh 实现内存水位控制,当超过高水位时强制驱逐旧队列。
3.3 资源管理
分片重组的资源管理涉及内存分配、队列生命周期和哈希表结构三个维度。Linux内核使用 inet_frags 全局结构统一管理所有协议族(IPv4/IPv6)的分片资源。
3.3.1 内存管理策略
- per-net 命名空间隔离:每个网络命名空间拥有独立的分片内存计数器,避免容器间相互影响。
- 两级阈值回收:当已用内存超过
high_thresh时,内核同步回收队列直到内存降至low_thresh以下。 - 分片缓存复用:使用
skb的frag_list链直接链接分片,避免大块内存拷贝(仅在重组最终阶段执行一次拷贝)。
3.3.2 哈希表与队列结构
分片队列通过哈希表组织,哈希键为 (src, dst, id, proto) 四元组。哈希表大小由 sysctl net.ipv4.ipfrag_hash_bits 控制(默认17,即131072个桶)。
3.4 性能调优关键参数
| sysctl 参数 | 默认值 | 调优建议 |
|---|---|---|
net.ipv4.ipfrag_time |
30 | 高延迟链路(如卫星)可增大到60-120;低延迟数据中心可减小到5-10 |
net.ipv4.ipfrag_high_thresh |
262144 | 大并发分片场景可增大到1MB以上;注意与系统总内存的平衡 |
net.ipv4.ipfrag_low_thresh |
196608 | 通常为 high_thresh 的 75% |
net.ipv4.ipfrag_hash_bits |
17 | 大流量场景可增大到19-20,减少哈希冲突 |
调优原则:
- 减少分片发生:优先通过路径MTU发现(PMTUD)避免分片,分片重组始终是性能损耗点。
- 匹配网络延迟:
ipfrag_time应略大于网络最大RTT,避免正常分片因超时被丢弃。 - 内存与性能平衡:增大
high_thresh可减少因内存压力导致的队列驱逐,但会增加内存占用。 - 哈希表大小:
hash_bits每增加1,桶数翻倍,内存开销约增加8KB(64位系统),但能显著降低链式冲突。
3.5 分片重组与GRO的交互
在启用GRO(Generic Receive Offload)的网卡上,分片重组发生在GRO之后、IP层之前。GRO会尝试合并同一流的小包,但不会合并分片——分片必须保持独立直到IP层重组。这种设计避免了GRO层处理分片逻辑的复杂性,但也意味着分片无法享受GRO的合并收益。
实践建议: 在高速网络(10Gbps+)中,应尽量避免IP分片。如果必须使用分片(如IPsec隧道场景),建议增大
ipfrag_high_thresh 并配合RPS(Receive Packet Steering)将分片处理分散到多个CPU核心,避免单核瓶颈。