网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
{
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



**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618658159)**
= 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
[外链图片转存中…(img-AWc1uAya-1715745198857)]
[外链图片转存中…(img-aK4va4Cq-1715745198858)]
[外链图片转存中…(img-pny5v9gQ-1715745198858)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新