TCP 重组一直是入侵检测系统中最为重要也是最难的一部分,它涉及到全流量的缓存,因此存储消耗十分巨大,据统计 100 万的会话就要产生 1G~10G 的内存缓存,因此设计一套 TCP 重组优化的算法十分必要,目前优化的办法有两种,一种是尽量不去 TCP 重组减少缓存包括红绿名单,配置,抽样算法,另一种就是将重组下沉到硬件例如 FPGA,减少以软件方式缓存。我横向对比了三种目前流行的入侵检测系统,,看看 TCP 软件重组上这三种 IDS/IPS 系统有什么优化点:
QNSM首页、文档和下载 - 高性能网络安全监控引擎 - OSCHINA - 中文开源技术交流社区
Snort - Network Intrusion Detection & Prevention System
GitHub - OISF/suricata: Suricata git repository maintained by the OISF
QSNM 实现
QSNM 是否进行流重组,以条件编译确定__QNSM_STREAM_REASSEMBLE,默认配置中是不进行 TCP 流重组的
同一个流的 TCP 都会进行流重组,上下行都在一个缓存队列中,最大支持 8 个报文,且不考虑重叠部分
重组方法基于 hashmap + 双向链表
TCP 流缓存删除方式:1. 老化 2. 无需进一步解析 3. 命中规则
/** tcp stream reassemble */
... ...
qnsm_list_for_each_prev_entry(tmp_tcp_data, &que->tcp_que, node)
{
same_dir = (dir == tmp_tcp_data->dir);
sort1 = cur_tcp_data->seq;
if (same_dir)
{
sort2 = tmp_tcp_data->seq;
}
else
{
sort2 = tmp_tcp_data->ack;
}
diff = packet_sequence_diff(sort2, sort1);
/* 根据diff进行链表insert */
... ...
QNSM_LIST_ADD_AFTER(&cur_tcp_data->node, &tmp_tcp_data->node);
que->data_cnt++;
cache->cur_pkt_num++;
}
... ...
复制代码
tcp_queue is null
cur_seg->seq > tmp_seg->seq/ack
cur_seg->seq = tmp_seg->seq/ack
Snort 实现
if ( SEQ_GT(rcv->r_win_base, tdb->seq) )
{
//Received data segment whose seq no is less than already ACKed bytes
if(SEQ_GT(rcv->r_nxt_ack, tdb->seq))
{
//We have already seen the data and this is a retransmission with different packet size
//Add the packet to seglist if the size is more than offset
uint32_t offset = rcv->r_win_base - tdb->seq;
if ( offset < p->dsize )
{
tdb->seq += offset;
p->data += offset;
p->dsize -= (uint16_t)offset;
StreamQueue(rcv, p, tdb, tcpssn);
//Restore the original seq and dsize before the packet is egressed
p->dsize += (uint16_t)offset;
p->data -= offset;
tdb->seq -= offset;
}
}
else
{
//We have NOT seen the data. Add it to stream queue
StreamQueue(rcv, p, tdb, tcpssn);
}
}
else
StreamQueue(rcv, p, tdb, tcpssn);
复制代码
typedef struct _StreamTracker
{
StreamTcpPolicy *tcp_policy;
StreamSegment *seglist; /* first queued segment */
StreamSegment *seglist_tail; /* last queued segment */
/* Local in the context of these variables means the local part
* of the connection. For example, if this particular StreamTracker
* was tracking the client side of a connection, the l_unackd value
* would represent the client side of the connection's last unacked
* sequence number
*/
uint32_t l_unackd; /* local unack'd seq number */
uint32_t l_nxt_seq; /* local next expected sequence */
uint32_t l_window; /* local receive window */
uint32_t r_nxt_ack; /* next expected ack from remote side */
uint32_t r_win_base; /* remote side window base sequence number
* (i.e. the last ack we got) */
uint32_t isn; /* initial sequence number */
uint32_t ts_last; /* last timestamp (for PAWS) */
uint32_t ts_last_pkt; /* last packet timestamp we got */
uint32_t seglist_base_seq; /* seq of first queued segment */
uint32_t seg_count; /* number of current queued segments */
uint32_t seg_bytes_total; /* total bytes currently queued */
uint32_t seg_bytes_logical; /* logical bytes queued (total - overlaps) */
uint32_t total_bytes_queued; /* total bytes queued (life of session) */
uint32_t total_segs_queued; /* number of segments queued (life) */
uint32_t overlap_count; /* overlaps encountered */
uint32_t small_seg_count;
uint16_t reassembly_policy;
} StreamTracker;
typedef struct _StreamSegment
{
uint8_t *data;
uint8_t *payload;
struct _StreamSegment *prev;
struct _StreamSegment *next;
struct timeval tv;
uint32_t caplen;
uint32_t pktlen;
uint32_t ts;
uint32_t seq;
uint16_t orig_dsize;
uint16_t size;
} StreamSegment;
typedef struct _TcpSession
{
StreamTracker client;
StreamTracker server;
...
}
复制代码
SEQ_EQ(tdb->seq, tail->seq + tail->size)
/* segment fit cleanly at the end of the segment list */
SEQ_LEQ(dist_head, dist_tail) /* Start iterating at the head (left) */
handle left overlaps
handle right overlaps
Windows/BSD 倾向于原始报文,除了后续报文起始序列号在原始报文前这种情况。Windows/BSD: <1><1><1><4><4><2><3><3><3><6><6><6><7><7><7><3.3><3.3><3.3><3.4><3.4><3.5><3.5><3.6><11>Linux 倾向于原始报文,除了后续报文起始序列号在原始报文之前,或后续报文起始序列号相同但终止序列号在原始报文后的情况 Linux: <1><1><1><4><4><2><3><3><3><6><6><6><7><7><7><3.3><3.3><3.3><3.4><3.4><3.5><3.5><11><11>
Suricata 实现
Reference
snort_manual.pdf
snort-2.9.16.1 code
qnsm-master code
解析Snort的TCP流重组
suricata5.0 code
suricata 6.0.3 tcp reassemble
评论