int DecodeTCP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq)
{
StatsIncr(tv, dtv->counter_tcp);
if (unlikely(DecodeTCPPacket(tv, p,pkt,len) < 0)) {
//调用DecodeTCPPacket完成实际的解码
SCLogDebug("invalid TCP packet");
p->tcph = NULL;
return TM_ECODE_FAILED;
}
#ifdef DEBUG
SCLogDebug("TCP sp: %" PRIu32 " -> dp: %" PRIu32 " - HLEN: %" PRIu32 " LEN: %" PRIu32 " %s%s%s%s%s",
GET_TCP_SRC_PORT(p), GET_TCP_DST_PORT(p), TCP_GET_HLEN(p), len,
TCP_HAS_SACKOK(p) ? "SACKOK " : "", TCP_HAS_SACK(p) ? "SACK " : "",
TCP_HAS_WSCALE(p) ? "WS " : "", TCP_HAS_TS(p) ? "TS " : "",
TCP_HAS_MSS(p) ? "MSS " : "");
#endif
FlowSetupPacket(p);
/*设置包的flag为PKT_WANTS_FLOW,并调用FlowGetHash获得包的flow_hash*/
return TM_ECODE_OK;
}
static int DecodeTCPPacket(ThreadVars *tv, Packet *p, uint8_t *pkt, uint16_t len)
{
if (unlikely(len < TCP_HEADER_LEN)) {
ENGINE_SET_INVALID_EVENT(p, TCP_PKT_TOO_SMALL);
return -1;
//若len小于TCP_HEADER_LEN(20),添加TCP_PKT_TOO_SMALL事件并返回
}
p->tcph = (TCPHdr *)pkt;
//设置tcph指针(TCPHdr *类型)
uint8_t hlen = TCP_GET_HLEN(p);
if (unlikely(len < hlen)) {
ENGINE_SET_INVALID_EVENT(p, TCP_HLEN_TOO_SMALL);
return -1;
//获取TCP头部长度hlen,若len小于hlen,则添加TCP_HLEN_TOO_SMALL事件并返回
}
uint8_t tcp_opt_len = hlen - TCP_HEADER_LEN;
if (unlikely(tcp_opt_len > TCP_OPTLENMAX)) {
ENGINE_SET_INVALID_EVENT(p, TCP_INVALID_OPTLEN);
return -1;
/*计算TCP选项长度(hlen – 20)tcp_opt_len,若大于TCP_OPTLENMAX(40),
则添加TCP_INVALID_OPTLEN事件并返回。*/
}
if (likely(tcp_opt_len > 0)) {
DecodeTCPOptions(p, pkt + TCP_HEADER_LEN, tcp_opt_len);
//若tcp_opt_len大于0,则调用DecodeTCPOptions对选项进行解码。
}
SET_TCP_SRC_PORT(p,&p->sp);
SET_TCP_DST_PORT(p,&p->dp);
p->proto = IPPROTO_TCP;
p->payload = pkt + hlen;
p->payload_len = len - hlen;
//设置Packet的sp、dp、proto、payload、payload_len。
return 0;
}
Suricata中用于表示TCP选项的数据结构为TCPVars:
字段 | 含义 |
---|---|
tcp_opts[TCP_OPTMAX] | TCP选项数组,类型为TCPOpt,TCP_OPTMAX定义为20。 |
tcp_opt_cnt | TCP选项个数 |
ts、sack、sackok、ws、mss | 特定选项指针 |
选项由TCPOpt结构体表示:(可参考TCP头部选项 )
typedef struct TCPOpt_ {
uint8_t type; /* 选项类型 */0
uint8_t len; /* 选项长度 */
uint8_t data; / 内容指针 */
} TCPOpt;
其中type有以下几种(参考TCP头部选项功能详解):
类型 | 含义 |
---|---|
TCP_OPT_EOL | 选项表结束。 |
TCP_OPT_NOP | 空操作,一般用于将TCP选项的总长度填充为4字节的整数倍。 |
TCP_OPT_MSS | 最大报文段长度选项,一般设置为MTU-40,关于MTU可参考:以太网最大帧和最小帧、MTU。 |
TCP_OPT_WS | 窗口扩大选项,用于增加TCP窗口值(16位,最大64KB),提高吞吐量。 |
TCP_OPT_SACKOK | 选择性确认(Selective Acknowledgements )选项,用在连接初始化时,表示是否支持SACK技术。 |
TCP_OPT_SACKSACK | 实际工作的选项,可让发送端只重新发送丢失的TCP报文段。 |
TCP_OPT_TS | 时间戳选项,用于计算RTT,从而为TCP流量控制提供重要信息。 |
DecodeTCPOptions流程为:
1. 与处理IPv4选项类似,循环处理每一个选项。
2. 先看是否为单字节选项:对于EOL,直接退出;对于NOP,前进。
3. 再看是否为多字节选项:填充tcp_opts[tcp_opt_cnt],并设置特定选项指针。