在网络通信中如果发送的IP包超过MTU值就会将IP包拆分成多个包发送。那么在suricata中对于这种拆分开得IP包又是如何处理的呢?下面我们一步一步来分析。
判断数据包是不是分片包是在DecedeIPV4这个函数中做的,具体位置是在 数据包解析模块->DecodeEthernet->DecedeIPV4
在DecedeIPV4这个函数中有如下一段代码:
/* If a fragment, pass off for re-assembly. */ if (unlikely(IPV4_GET_IPOFFSET(p) > 0 || IPV4_GET_MF(p) == 1)) { Packet *rp = Defrag(tv, dtv, p, pq); if (rp != NULL) { PacketEnqueue(pq, rp); } p->flags |= PKT_IS_FRAGMENT; return TM_ECODE_OK; }这就是专门用来处理IP分片数据包的了。 IPV4_GET_IPOFFSET(p) > 0 是判断p中的偏移是否大于0(除第一个分片包的offset为0外其余的offset都会大于0)。 IPV4_GET_MF(p) == 1 是判断p中的mf标志是否为1 (除最后一个分片包mf为0外其余的mf都为1)。 用逻辑或来判断,保证了只要是分片包就会进入分片处理的逻辑。
主要的逻辑处里都在Defrag函数中:
/* return a locked tracker or NULL */ tracker = DefragGetTracker(tv, dtv, p); if (tracker == NULL) return NULL; Packet *rp = DefragInsertFrag(tv, dtv, tracker, p, pq);tracker为申请的一块内存,我理解是用来存放分片包的,tracker里面会保存一个原始IP包的所有分片,直到这些分片被重组成功。 DefragInsertFrag这个函数将新收到的p插入刚刚得到的tracker,如果p是最后一个分片,那么会返回一个重组后的packet结构的指针rp,否则返回NULL。
DefragInsertFrag函数的最下方有针对最后一个分片的处理
if (!more_frags) { tracker->seen_last = 1; } if (tracker->seen_last) { if (tracker->af == AF_INET) { r = Defrag4Reassemble(tv, tracker, p); if (r != NULL && tv != NULL && dtv != NULL) { StatsIncr(tv, dtv->counter_defrag_ipv4_reassembled); if (pq && DecodeIPV4(tv, dtv, r, (void *)r->ip4h, IPV4_GET_IPLEN(r), pq) != TM_ECODE_OK) { UNSET_TUNNEL_PKT(r); r->root = NULL; TmqhOutputPacketpool(tv, r); r = NULL; } else { PacketDefragPktSetupParent(p); } } } else if (tracker->af == AF_INET6) { r = Defrag6Reassemble(tv, tracker, p); if (r != NULL && tv != NULL && dtv != NULL) { StatsIncr(tv, dtv->counter_defrag_ipv6_reassembled); if (pq && DecodeIPV6(tv, dtv, r, (uint8_t *)r->ip6h, IPV6_GET_PLEN(r) + IPV6_HEADER_LEN, pq) != TM_ECODE_OK) { UNSET_TUNNEL_PKT(r); r->root = NULL; TmqhOutputPacketpool(tv, r); r = NULL; } else { PacketDefragPktSetupParent(p); } } } }more_frags获取的是p的mf的值。 我们这里只看IPV4的处理,V6的逻辑一样。 Defrag4Reassemble重组了tracker中与p的identify相同的所有分片,并返回一个重组后的包rp 调用DecodeIPV4,传入rp,重走一遍没有分包的正常逻辑。
总结: suricata会在分片数据包被重组完成后才会调用后续的处理流程解传输层的头信息。包重组实在receive线程完成的,这一过程对后续的worker线程是透明的。
