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
评论