当前位置: 首页 > 工具软件 > Zeek > 使用案例 >

Zeek学习(四) —— IP协议解析

司徒斌
2023-12-01

IP

IP层的解析主要位于src/packet_analysis/ip/IP.ccIPAnalyzer::AnalyzePacket中,其涉及结构体主要包括:

  • Packet:包信息
  • struct ip:netinet/ip.h中定义的IP头部
  • struct ip6_hdr:netinet/ip6.h中定义的IPV6头部
  • IP_Hdr:自定义的IP头部类
  • detail::PacketFilter:包过滤
  • zeek::detail::Discarder:报是否需要丢弃
  • detail::FragmentManager
// 参数输入
// len 数据长度(即除ether层的数据长度)
// data 数据
// packet Packet对象
bool IPAnalyzer::AnalyzePacket(size_t len, const uint8_t* data, Packet* packet)
{
    // 首先对长度进行校验,如果长度小于IP的头部则代表这个包是非法的报文直接返回
    if ( len < sizeof(struct ip) )
    {
        Weird("truncated_IP", packet);
        return false;
    }
    
    // 获取头部数据大小
    int32_t hdr_size = static_cast<int32_t>(data - packet->data);
    
    // 强转成IP头部
    auto ip = (const struct ip*)data;
    // 协议
    uint32_t protocol = ip->ip_v;
    
    
    if ( protocol == 4 )
    {   
        // 将ip头部转化成IP_Hdr对象
        packet->ip_hdr = std::make_shared<IP_Hdr>(ip, false);
        packet->l3_proto = L3_IPV4;
    }
    else if ( protocol == 6 )
    {
        
        if ( len < sizeof(struct ip6_hdr) )
        {
            Weird("truncated_IP", packet);
            return false;
        }
        // 将ip头部转化成IP_Hdr对象
        packet->ip_hdr = std::make_shared<IP_Hdr>((const struct ip6_hdr*)data, false, len);
        packet->l3_proto = L3_IPV6;
    }
    else
    {
        Weird("unknown_ip_version", packet);
        return false;
    }
    
    //如果在这个包中有一个封装栈,意味着这个包是链的一部分隧道,确保将IP头存储在堆栈的最后一个流中,
    //以便它可以之前的分析器在我们返回链时使用。
    if ( packet->encap )
    {
        if ( auto* ec = packet->encap->Last() )
            ec->ip_hdr = packet->ip_hdr;
    }
    
    const struct ip* ip4 = packet->ip_hdr->IP4_Hdr();
    
    //获取总长度,一般和len一样
    uint32_t total_len = packet->ip_hdr->TotalLen();
    if ( total_len == 0 )
    {
       // 处理解析出来的总长度大小为0的情况,一般都不会进入到这里面
        Weird("ip_hdr_len_zero", packet);
        
        if ( detail::ignore_checksums )
            total_len = packet->cap_len - hdr_size;
        else
            return false;
    }
    
    // 截断IPV6?不太明白
    if ( packet->len < total_len + hdr_size )
    {
        Weird("truncated_IPv6", packet);
        return false;
    }
   
    // 获取IP头部
    uint16_t ip_hdr_len = packet->ip_hdr->HdrLen();
    // 如果头部大小大于总长度,包无效
    if ( ip_hdr_len > total_len )
    {
        Weird("invalid_IP_header_size", packet);
        return false;
    }
    // 如果头部大小大于长度,包无效
    if ( ip_hdr_len > len )
    {
        Weird("internally_truncated_header", packet);
        return false;
    }
    
    // 根据协议来校验相应的头部
    if ( packet->ip_hdr->IP4_Hdr() )
    {
        if ( ip_hdr_len < sizeof(struct ip) )
        {
            Weird("IPv4_min_header_size", packet);
            return false;
        }
    }
    else
    {
        if ( ip_hdr_len < sizeof(struct ip6_hdr) )
        {
            Weird("IPv6_min_header_size", packet);
            return false;
        }
    }
    
    // 包过滤
    detail::PacketFilter* packet_filter = packet_mgr->GetPacketFilter(false);
    if ( packet_filter && packet_filter->Match(packet->ip_hdr, total_len, len) )
        return false;
    
    if ( ! packet->l2_checksummed && ! detail::ignore_checksums && ip4 &&
        ! IPBasedAnalyzer::GetIgnoreChecksumsNets()->Contains(packet->ip_hdr->IPHeaderSrcAddr()) &&
        detail::in_cksum(reinterpret_cast<const uint8_t*>(ip4), ip_hdr_len) != 0xffff )
    {
        Weird("bad_IP_checksum", packet);
        return false;
    }
    
    // 是否丢弃该报文
    if ( discarder && discarder->NextPacket(packet->ip_hdr, total_len, len) )
        return false;
    
    detail::FragReassembler* f = nullptr;
    
    // 对于报文分段的处理
    if ( packet->ip_hdr->IsFragment() )
    {
        packet->dump_packet = true; // always record fragments
        
        if ( len < total_len )
        {
            Weird("incompletely_captured_fragment", packet);
            /// 不完整的分片
            if ( packet->ip_hdr->FragOffset() != 0 )
                return false;
        }
        else
        {
            // 添加到分段管理器中
            f = detail::fragment_mgr->NextFragment(run_state::processing_start_time, packet->ip_hdr,
                                                   packet->data + hdr_size);
            
            std::shared_ptr<IP_Hdr> ih = f->ReassembledPkt();
            
            if ( ! ih )
                // 如果不是重组包则直接返回,一般走不到这里
                return true;
            
            ip4 = ih->IP4_Hdr();
            
            // Switch the stored ip header over to the one from the
            // fragmented packet.
            packet->ip_hdr = std::move(ih);
            
            len = total_len = packet->ip_hdr->TotalLen();
            ip_hdr_len = packet->ip_hdr->HdrLen();
            packet->cap_len = total_len + hdr_size;
            
            if ( ip_hdr_len > total_len )
            {
                Weird("invalid_IP_header_size", packet);
                return false;
            }
        }
    }
    
    detail::FragReassemblerTracker frt(f);
    
    // 停止构建IPV6链
    if ( packet->ip_hdr->LastHeader() == IPPROTO_ESP )
    {
        packet->dump_packet = true;
        if ( esp_packet )
            event_mgr.Enqueue(esp_packet, packet->ip_hdr->ToPktHdrVal());
        
        // Can't do more since upper-layer payloads are going to be encrypted.
        return true;
    }
    
    // We stop building the chain when seeing IPPROTO_MOBILITY so it's always
    // last if present.
    if ( packet->ip_hdr->LastHeader() == IPPROTO_MOBILITY )
    {
        packet->dump_packet = true;
        
        if ( ! detail::ignore_checksums &&
            mobility_header_checksum(packet->ip_hdr.get()) != 0xffff )
        {
            Weird("bad_MH_checksum", packet);
            return false;
        }
        
        if ( mobile_ipv6_message )
            event_mgr.Enqueue(mobile_ipv6_message, packet->ip_hdr->ToPktHdrVal());
        
        if ( packet->ip_hdr->NextProto() != IPPROTO_NONE )
            Weird("mobility_piggyback", packet);
        
        return true;
    }
    
    data = packet->ip_hdr->Payload();
    len -= ip_hdr_len;
    
    bool return_val = true;
    int proto = packet->ip_hdr->NextProto();
    
    packet->proto = proto;
    
    // Double check the lengths one more time before forwarding this on.
    if ( total_len < packet->ip_hdr->HdrLen() )
    {
        Weird("bogus_IP_header_lengths", packet);
        return false;
    }
    
    switch ( proto )
    {
        case IPPROTO_NONE:
            if ( ! (packet->encap && packet->encap->LastType() == BifEnum::Tunnel::TEREDO) )
            {
                Weird("ipv6_no_next", packet);
                return_val = false;
            }
            break;
        default:
            packet->proto = proto;
            
           // 传递给下一个包处理器
            return_val = ForwardPacket(len, data, packet, proto);
            break;
    }
    
    // 删除重组定时器
    if ( f )
        f->DeleteTimer();
    
    return return_val;
	}

IP_Hdr

class IP_Hdr
{
    public:
    // 构造函数
    IP_Hdr(const struct ip* arg_ip4, bool arg_del, bool reassembled = false);
    // 构造函数
    IP_Hdr(const struct ip6_hdr* arg_ip6, bool arg_del, int len, const IPv6_Hdr_Chain* c = nullptr,
           bool reassembled = false);
    //拷贝方法
    IP_Hdr* Copy() const;
    // 析构
    ~IP_Hdr();
    // 获取原始IPV4结构
    const struct ip* IP4_Hdr() const { return ip4; }
    // 获取原始IPV6结构
    const struct ip6_hdr* IP6_Hdr() const { return ip6; }
    
    IPAddr IPHeaderSrcAddr() const;
    IPAddr IPHeaderDstAddr() const;
    IPAddr SrcAddr() const;
    IPAddr DstAddr() const;
    // IP层负载指针
    const u_char* Payload() const;
    // 移动头
    const ip6_mobility* MobilityHeader() const
    // 负载长度,IPV4启用TCP分段时返回0
    uint16_t PayloadLen() const
    //IP报文总长度(header+payload)
    uint32_t TotalLen() const
    // IP头长度
    uint16_t HdrLen() const { return ip4 ? ip4->ip_hl * 4 : ip6_hdrs->TotalLength(); }
    // IPV6的最后一个头部
    uint8_t LastHeader() const
    // 传输层协议类型
    unsigned char NextProto() const
    // TTL
    unsigned char TTL() const { return ip4 ? ip4->ip_ttl : ip6->ip6_hlim; }
    // IP报文是否分段
    bool IsFragment() const
    // 当前分段报文的偏移字节数
    uint16_t FragOffset() const
    // 标识
    uint32_t ID() const { return ip4 ? ntohs(ip4->ip_id) : ip6_hdrs->ID(); }
    // more fragment是否设置
    int MF() const { return ip4 ? (ntohs(ip4->ip_off) & 0x2000) != 0 : ip6_hdrs->MF(); }
    // 不分段是否设置
    int DF() const { return ip4 ? ((ntohs(ip4->ip_off) & 0x4000) != 0) : 0; }
    // IPV6流标签
    uint32_t FlowLabel() const { return ip4 ? 0 : (ntohl(ip6->ip6_flow) & 0x000fffff); }
    // IPV6拓展头的数量
    size_t NumHeaders() const { return ip4 ? 1 : ip6_hdrs->Size(); }
    // 返回头部记录值对象指针
    RecordValPtr ToIPHdrVal() const;
    // 返回报记录值对象指针(包括IP头部和下一层头部)
    RecordValPtr ToPktHdrVal() const;
    // 和上个方法一样
    RecordValPtr ToPktHdrVal(RecordValPtr pkt_hdr, int sindex) const;
    
    bool Reassembled() const { return reassembled; }
    
    private:
    const struct ip* ip4 = nullptr;
    const struct ip6_hdr* ip6 = nullptr;
    const IPv6_Hdr_Chain* ip6_hdrs = nullptr;
    bool del = false;
    bool reassembled = false;
	};

IP分片

  1. 首先将源地址、目的地址、段ID使用make_tuple形成key
  2. 然后查找此key是否存在,不存在就创建一个并添加到map中
  3. 然后再把当前包添加到重组对象中
FragReassembler* FragmentManager::NextFragment(double t, const std::shared_ptr<IP_Hdr>& ip,
                                               const u_char* pkt)
	{
	uint32_t frag_id = ip->ID();
	FragReassemblerKey key = std::make_tuple(ip->SrcAddr(), ip->DstAddr(), frag_id);

	FragReassembler* f = nullptr;
	auto it = fragments.find(key);
	if ( it != fragments.end() )
		f = it->second;

	if ( ! f )
		{
		f = new FragReassembler(session_mgr, ip, pkt, key, t);
		fragments[key] = f;
		if ( fragments.size() > max_fragments )
			max_fragments = fragments.size();
		return f;
		}

	f->AddFragment(t, ip, pkt);
	return f;
	}

FragManager

class FragmentManager
{
public:
    FragmentManager() = default;
    ~FragmentManager();
    // 添加分片
    FragReassembler* NextFragment(double t, const std::shared_ptr<IP_Hdr>& ip, const u_char* pkt);
    // 清空分片
    void Clear();
    // 移除分片
    void Remove(detail::FragReassembler* f);
    
    size_t Size() const { return fragments.size(); }
    size_t MaxFragments() const { return max_fragments; }
    [[deprecated("Remove in v5.1. MemoryAllocation() is deprecated and will be removed. See "
                 "GHI-572.")]] uint32_t
        MemoryAllocation() const;
    
private:
    using FragmentMap = std::map<detail::FragReassemblerKey, detail::FragReassembler*>;
    FragmentMap fragments;
    size_t max_fragments = 0;
};

FragReassembler

class FragReassembler : public Reassembler
{
public:
    FragReassembler(session::Manager* s, const std::shared_ptr<IP_Hdr>& ip, const u_char* pkt,
    const FragReassemblerKey& k, double t);
    ~FragReassembler() override;
    
    void AddFragment(double t, const std::shared_ptr<IP_Hdr>& ip, const u_char* pkt);
    
    void Expire(double t);
    void DeleteTimer();
    void ClearTimer() { expire_timer = nullptr; }
    
    std::shared_ptr<IP_Hdr> ReassembledPkt() { return std::move(reassembled_pkt); }
    const FragReassemblerKey& Key() const { return key; }
    
protected:
    void BlockInserted(DataBlockMap::const_iterator it) override;
    void Overlap(const u_char* b1, const u_char* b2, uint64_t n) override;
    void Weird(const char* name) const;
    
    // IP头部,使用memcpy
    u_char* proto_hdr;
    // 
    std::shared_ptr<IP_Hdr> reassembled_pkt;
    session::Manager* s;
    // 完全重组后包的大小
    uint64_t frag_size;
    // key
    FragReassemblerKey key;
     // 第一个IPV6段的下一个协议字段
    uint16_t next_proto;
    // 协议头长度
    uint16_t proto_hdr_len;

    FragTimer* expire_timer;
};

构造函数

  1. 拷贝IP头部信息
  2. 启动定时器
  3. 调用AddFragment
FragReassembler::FragReassembler(session::Manager* arg_s, const std::shared_ptr<IP_Hdr>& ip,
                                 const u_char* pkt, const FragReassemblerKey& k, double t)
    : Reassembler(0, REASSEM_FRAG)
{
        s = arg_s;
        key = k;
        
        const struct ip* ip4 = ip->IP4_Hdr();
        if ( ip4 )
        {
            proto_hdr_len = ip->HdrLen();
            proto_hdr = new u_char[64]; // max IP header + slop
            // Don't do a structure copy - need to pick up options, too.
            memcpy((void*)proto_hdr, (const void*)ip4, proto_hdr_len);
        }
        else
        {
            proto_hdr_len = ip->HdrLen() - 8; // minus length of fragment header
            proto_hdr = new u_char[proto_hdr_len];
            memcpy(proto_hdr, ip->IP6_Hdr(), proto_hdr_len);
        }
        
        reassembled_pkt = nullptr;
        frag_size = 0; // flag meaning "not known"
        next_proto = ip->NextProto();
        
        if ( frag_timeout != 0.0 )
        {
            expire_timer = new FragTimer(this, t + frag_timeout);
            timer_mgr->Add(expire_timer);
        }
        else
            expire_timer = nullptr;
        
        AddFragment(t, ip, pkt);
    }

AddFragment

  1. 对协议和头部长度进行检查
  2. 对协议的DF进行检查
  3. 对头部和总长度进行检查
  4. 判断是否有更多分片MF,如果没有设置总大小
void FragReassembler::AddFragment(double t, const std::shared_ptr<IP_Hdr>& ip, const u_char* pkt)
{
    const struct ip* ip4 = ip->IP4_Hdr();
    // 对协议和头部长度进行检查
    if ( ip4 )
    {
        if ( ip4->ip_p != ((const struct ip*)proto_hdr)->ip_p ||
            ip4->ip_hl != ((const struct ip*)proto_hdr)->ip_hl )
            // || ip4->ip_tos != proto_hdr->ip_tos
            // don't check TOS, there's at least one stack that actually
            // uses different values, and it's hard to see an associated
            // attack.
            s->Weird("fragment_protocol_inconsistency", ip.get());
    }
    else
    {
        if ( ip->NextProto() != next_proto || ip->HdrLen() - 8 != proto_hdr_len )
            s->Weird("fragment_protocol_inconsistency", ip.get());
        // TODO: more detailed unfrag header consistency checks?
    }
    // 对DF进行检查
    if ( ip->DF() )
        // Linux MTU discovery for UDP can do this, for example.
        s->Weird("fragment_with_DF", ip.get());
    
    uint16_t offset = ip->FragOffset();
    uint32_t len = ip->TotalLen();
    uint16_t hdr_len = ip->HdrLen();
    // 对头部和总长度进行检查
    if ( len < hdr_len )
    {
        s->Weird("fragment_protocol_inconsistency", ip.get());
        return;
    }
    // 计算总带下 = 当前分片偏移值+当前IP报总长度 - 当前IP报头部长度
    uint64_t upper_seq = offset + len - hdr_len;
    // IPV6
    if ( ! offset )
        // Make sure to use the first fragment header's next field.
        next_proto = ip->NextProto();
    // 判断是否有更多分片MF
    if ( ! ip->MF() )
    {
        // Last fragment.
        if ( frag_size == 0 )
            // 设置总大小
            frag_size = upper_seq;
        
        else if ( upper_seq != frag_size )
        {
            s->Weird("fragment_size_inconsistency", ip.get());
            
            if ( upper_seq > frag_size )
                frag_size = upper_seq;
        }
    }
    
    else if ( len < MIN_ACCEPTABLE_FRAG_SIZE )
        s->Weird("excessively_small_fragment", ip.get());
    
    if ( upper_seq > MAX_ACCEPTABLE_FRAG_SIZE )
        s->Weird("excessively_large_fragment", ip.get());
    
    if ( frag_size && upper_seq > frag_size )
    {
        // This can happen if we receive a fragment that's *not*
        // the last fragment, but still imputes a size that's
        // larger than the size we derived from a previously-seen
        // "last fragment".
        
        s->Weird("fragment_size_inconsistency", ip.get());
        frag_size = upper_seq;
    }
    
    // Do we need to check for consistent options?  That's tricky
    // for things like LSRR that get modified in route.
    
    // Remove header.
    pkt += hdr_len;
    len -= hdr_len;
    
    NewBlock(run_state::network_time, offset, len, pkt);
}

NewBlock

seq: offset
len: 当前IP报文 - 头部长度
data:当前IP报文的负载
void Reassembler::NewBlock(double t, uint64_t seq, uint64_t len, const u_char* data)
{
    if ( len == 0 )
        return;
    // 当前负载长度/总长度    
    uint64_t upper_seq = seq + len;
    
    // 第一次的包直接返回
    CheckOverlap(old_block_list, seq, len, data);
    
    if ( upper_seq <= trim_seq )
        // Old data, don't do any work for it.
        return;
    
    // 第一次的包直接返回
    CheckOverlap(block_list, seq, len, data);
    
    // 部分旧数据,保存旧数据
    if ( seq < trim_seq )
    { // Partially old data, just keep the good stuff.
        uint64_t amount_old = trim_seq - seq;
        
        data += amount_old;
        seq += amount_old;
        len -= amount_old;
    }
    
    auto it = block_list.Insert(seq, upper_seq, data);
    ;
    BlockInserted(it);
}

seq: offset
len: 当前IP报文 - 头部长度
data:当前IP报文的负载
void Reassembler::CheckOverlap(const DataBlockList& list, uint64_t seq, uint64_t len,
                               const u_char* data)
{
    if ( list.Empty() )
        return;
    
    const auto& last = list.LastBlock();
    
    if ( seq == last.upper )
        // Special case check for common case of appending to the end.
        return;
    
    uint64_t upper = (seq + len);
    
    auto it = list.FirstBlockAtOrBefore(seq);
    
    if ( it == list.End() )
        it = list.Begin();
    
    for ( ; it != list.End(); ++it )
    {
        const auto& b = it->second;
        uint64_t nseq = seq;
        uint64_t nupper = upper;
        const u_char* ndata = data;
        
        if ( nupper <= b.seq )
            break;
        
        if ( nseq >= b.upper )
            continue;
        
        if ( nseq < b.seq )
        {
            ndata += (b.seq - seq);
            nseq = b.seq;
        }
        
        if ( nupper > b.upper )
            nupper = b.upper;
        
        uint64_t overlap_offset = (nseq - b.seq);
        uint64_t overlap_len = (nupper - nseq);
        
        if ( overlap_len )
            Overlap(&b.block[overlap_offset], ndata, overlap_len);
    }
}

DataBlockMap::const_iterator DataBlockList::Insert(uint64_t seq, uint64_t upper, const u_char* data,
                                                   DataBlockMap::const_iterator hint)
	{
	auto size = upper - seq;
	auto rval = block_map.emplace_hint(hint, seq, DataBlock(data, size, seq));

	total_data_size += size;
	Reassembler::sizes[reassembler->rtype] += size + sizeof(DataBlock);
	Reassembler::total_size += size + sizeof(DataBlock);

	return rval;
	}
seq: offset
uppper:seq + len 当前负载长度/总长度
data:当前IP报文的负载
DataBlockMap::const_iterator DataBlockList::Insert(uint64_t seq, uint64_t upper, const u_char* data,
                                                   DataBlockMap::const_iterator* hint)
	{
	// Empty list.
	if ( block_map.empty() )
		return Insert(seq, upper, data, block_map.end());

	const auto& last = block_map.rbegin()->second;

	// Special check for the common case of appending to the end.
	if ( seq == last.upper )
		return Insert(seq, upper, data, block_map.end());

	// Find the first block that doesn't come completely before the new data.
	DataBlockMap::const_iterator it;

	if ( hint )
		it = *hint;
	else
		{
		it = FirstBlockAtOrBefore(seq);

		if ( it == block_map.end() )
			it = block_map.begin();
		}

	while ( std::next(it) != block_map.end() && it->second.upper <= seq )
		++it;

	const auto& b = it->second;

	if ( b.upper <= seq )
		// b is the last block, and it comes completely before the new block.
		return Insert(seq, upper, data, block_map.end());

	if ( upper <= b.seq )
		// The new block comes completely before b.
		return Insert(seq, upper, data, it);

	DataBlockMap::const_iterator rval;

	// The blocks overlap.
	if ( seq < b.seq )
		{
		// The new block has a prefix that comes before b.
		uint64_t prefix_len = b.seq - seq;

		rval = Insert(seq, seq + prefix_len, data, it);

		data += prefix_len;
		seq += prefix_len;
		}
	else
		rval = it;

	uint64_t overlap_start = seq;
	uint64_t overlap_offset = overlap_start - b.seq;
	uint64_t new_b_len = upper - seq;
	uint64_t b_len = b.upper - overlap_start;
	uint64_t overlap_len = min(new_b_len, b_len);

	if ( overlap_len < new_b_len )
		{
		// Recurse to resolve remainder of the new data.
		data += overlap_len;
		seq += overlap_len;

		auto r = Insert(seq, upper, data, &it);

		if ( rval == it )
			rval = r;
		}

	return rval;
	}

DataBlock::DataBlock(const u_char* data, uint64_t size, uint64_t arg_seq)
	{
	seq = arg_seq;
	upper = seq + size;
	block = new u_char[size];
	memcpy(block, data, size);
	}

BlockInserted

void FragReassembler::BlockInserted(DataBlockMap::const_iterator /* it */)
	{
	auto it = block_list.Begin();

	if ( it->second.seq > 0 || ! frag_size )
		// For sure don't have it all yet.
		return;

	auto next = std::next(it);

	
    // 校验是否所有分片都存在
	while ( next != block_list.End() )
		{
		if ( it->second.upper != next->second.seq )
			break;

		++it;
		++next;
		}

	const auto& last = block_list.LastBlock();

	if ( next != block_list.End() )
		{
		// We have a hole.
		if ( it->second.upper >= frag_size )
			{
			// We're stuck.  The point where we stopped is
			// contiguous up through the expected end of
			// the fragment, but there's more stuff still
			// beyond it, which is not contiguous.  This
			// can happen for benign reasons when we're
			// intermingling parts of two fragmented packets.
			Weird("fragment_size_inconsistency");

			// We decide to analyze the contiguous portion now.
			// Extend the fragment up through the end of what
			// we have.
			frag_size = it->second.upper;
			}
		else
			return;
		}

	else if ( last.upper > frag_size )
		{
		Weird("fragment_size_inconsistency");
		frag_size = last.upper;
		}

	else if ( last.upper < frag_size )
		// Missing the tail.
		return;

	// We have it all.  Compute the expected size of the fragment.
	uint64_t n = proto_hdr_len + frag_size;

	// It's possible that we have blocks associated with this fragment
	// that exceed this size, if we saw MF fragments (which don't lead
	// to us setting frag_size) that went beyond the size indicated by
	// the final, non-MF fragment.  This can happen for benign reasons
	// due to intermingling of fragments from an older datagram with those
	// for a more recent one.

	u_char* pkt = new u_char[n];
	memcpy((void*)pkt, (const void*)proto_hdr, proto_hdr_len);

	u_char* pkt_start = pkt;

	pkt += proto_hdr_len;

	for ( it = block_list.Begin(); it != block_list.End(); ++it )
		{
		const auto& b = it->second;

		if ( it != block_list.Begin() )
			{
			const auto& prev = std::prev(it)->second;

			// If we're above a hole, stop.  This can happen because
			// the logic above regarding a hole that's above the
			// expected fragment size.
			if ( prev.upper < b.seq )
				break;
			}

		if ( b.upper > n )
			{
			reporter->InternalWarning("bad fragment reassembly");
			DeleteTimer();
			Expire(run_state::network_time);
			delete[] pkt_start;
			return;
			}

		memcpy(&pkt[b.seq], b.block, b.upper - b.seq);
		}

	reassembled_pkt.reset();

	unsigned int version = ((const struct ip*)pkt_start)->ip_v;

	if ( version == 4 )
		{
		struct ip* reassem4 = (struct ip*)pkt_start;
		reassem4->ip_len = htons(frag_size + proto_hdr_len);
		reassembled_pkt = std::make_shared<IP_Hdr>(reassem4, true, true);
		DeleteTimer();
		}

	else if ( version == 6 )
		{
		struct ip6_hdr* reassem6 = (struct ip6_hdr*)pkt_start;
		reassem6->ip6_plen = htons(frag_size + proto_hdr_len - 40);
		const IPv6_Hdr_Chain* chain = new IPv6_Hdr_Chain(reassem6, next_proto, n);
		reassembled_pkt = std::make_shared<IP_Hdr>(reassem6, true, n, chain, true);
		DeleteTimer();
		}

	else
		{
		reporter->InternalWarning("bad IP version in fragment reassembly: %d", version);
		delete[] pkt_start;
		}
	}

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习]

 类似资料: