Post

Suricata midstream direction(ai)

Suricata midstream direction(ai)

Suricata Midstream Flow Direction Correction Analysis

在 Suricata 中,针对 midstream(流的中途截获)流量方向判断错误的更正,并没有物理修改 Flow 结构体中的 spdp 字段。相反,它是通过设置一个标志位来进行逻辑上的更正

结论

  • 物理存储Flow 结构体中的 sp (Source Port) 和 dp (Destination Port) 始终保持流初始化时的值,不会被交换。
  • 逻辑更正:通过设置 FLOW_DIR_REVERSED 标志位来标记流的方向需要被视为反转。

详细实现机制

1. 核心更正位置:FlowSwap

当 Suricata 发现流的方向判断错误时,会调用 FlowSwap 函数。

  • 文件路径: suricata/src/flow.c
  • 行为: 它不交换 spdp,而是设置 FLOW_DIR_REVERSED 标志,并交换流内部的统计计数器和协议掩码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// suricata/src/flow.c

void FlowSwap(Flow *f)
{
    // 这里设置了标志位,表示流的方向被反转了
    f->flags |= FLOW_DIR_REVERSED;

    // 交换协议检测掩码
    SWAP_VARS(uint32_t, f->probing_parser_toserver_alproto_masks,
                   f->probing_parser_toclient_alproto_masks);

    // 交换各种标志和计数器,但注意这里没有交换 sp 和 dp
    FlowSwapFlags(f);
    FlowSwapFileFlags(f);
    // ...
}

2. 触发更正的位置:应用层检测

通常是在应用层协议检测阶段发现方向错误的。主要位于 TCPProtoDetect(TCP)或 AppLayerHandleUdp(UDP)。

  • 文件路径: suricata/src/app-layer.c
  • 行为: 当检测到协议特征与当前方向不符,且符合 midstream 条件时,触发翻转。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// suricata/src/app-layer.c

int TCPProtoDetect(ThreadVars *tv, DecodeThreadVars *dtv, Flow *f, TcpSession *ssn, Packet *p)
{
    // ...
    if (reverse_flow &&
            ((ssn->flags & (STREAMTCP_FLAG_MIDSTREAM | STREAMTCP_FLAG_MIDSTREAM_SYNACK)) ==
                    STREAMTCP_FLAG_MIDSTREAM)) {
        
        // 只有当另一方向还未检测出协议时才翻转
        if (*alproto_otherdir == ALPROTO_UNKNOWN) {
            SCLogDebug("reversing flow after proto detect told us so");
            
            // 交换 Packet 的方向标志
            PacketSwap(p);
            
            // 核心:调用 FlowSwap 标记流方向反转
            FlowSwap(f);
            
            // ...
        }
    }
    // ...
}

3. 更正后的影响 (Usage)

由于 spdp 在内存中没变,Suricata 在需要使用端口或 IP 时会检查 FLOW_DIR_REVERSED 标志。

A. 判断包方向 (FlowGetPacketDirection)

  • 文件路径: suricata/src/flow.c
  • 逻辑: 会检查标志位,如果置位,则返回相反的方向(TOSERVERTOCLIENT)。

B. 输出日志 (CreateEveHeaderFromFlow)

  • 文件路径: suricata/src/output-json-flow.c
  • 逻辑: 在生成 EVE JSON 日志时,会根据标志位决定是否交换显示的 IP 和端口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// suricata/src/output-json-flow.c

static SCJsonBuilder *CreateEveHeaderFromFlow(const Flow *f)
{
    // ...
    if ((f->flags & FLOW_DIR_REVERSED) == 0) {
        // 正常情况,直接使用 f->sp, f->dp
        sp = f->sp;
        dp = f->dp;
    } else {
        // 反转情况,输出时交换显示
        // src_ip 显示为 dst, dst_ip 显示为 src
        // sp 显示为 f->dp, dp 显示为 f->sp
        sp = f->dp;
        dp = f->sp;
    }
    // ...
}
This post is licensed under CC BY 4.0 by the author.