IP层的解析主要位于src/packet_analysis/ip/IP.cc
的IPAnalyzer::AnalyzePacket
中,其涉及结构体主要包括:
// 参数输入
// 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;
}
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;
};
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;
}
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;
};
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;
};
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);
}
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);
}
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);
}
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;
}
}