tcp协议流量在网络流量中占据着十分重要的地位,无论是检测基于tcp承载的应用层协议,还是检测tcp数据流,都离不开tcp数据段重组,下面我们来看一下suricata是如何实现tcp数据包重组的。
数据重组流程
数据重组入口函数StreamTcpReassembleHandleSegmentHandleData(stream-tcp-reassemble.c)。具体详细处理逻辑如下:
- 获取当前待重组数据帧的tcp协议负载的信息,根据当前数据帧携带数据的长度,计算该数据帧可使用的数据长度size(即:该数据帧的数据在要求的数据范围内的数据长度,具体计算逻辑可参考StreamTcpReassembleCheckDepth函数)。
- 创建TcpSegment对象,初始化seq(当前数据帧的seq)和payload_len(第一步计算出来,符合要求的数据帧的长度size)信息。
- 将新建的tcpsegment对象插入TcpStream对象的seg_tree(该树是红黑树,用于判断该tcpsegment对象是否符合重组条件,参照功能函数DoInsertSegment,若该函数返回0则代表插入成功;返回1则代表插入成功但是和前面已插入的tcpsegment存在部分重合的数据;返回2则代表seg_tree树中已含有相同数据的tcpsegment)。
- 根据当前数据帧的seq及可用数据长度size,计算当前数据帧携带数据的存放位置(data_offset:当前数据帧中可用于tcp流重组的数据的起始位置,stream_offset:当前数据帧放入tcp流重组中数据在已重组数据中的位置)。
1)若TcpStream对象的base_seq小于等于当前数据帧的seq
stream_offset = STREAM_BASE_OFFSET(stream) + (seg->seq - stream->base_seq);
data_offset = 0;
2)若TcpStream对象的base_seq大于当前数据帧的seq
data_offset = stream->base_seq - seg->seq;
stream_offset = STREAM_BASE_OFFSET(stream);
5、将待重组的数据存入StreamingBuffer对象的buf,并更新StreamingBuffer对象的相关信息。(具体逻辑处理功能函数: StreamingBufferInsertAt)
1)计算buf内存空间是否能容纳当前数据帧内容,若不够则自动扩充内存空间
2)将数据存入streambuf中