suricata源码之tag

tag是suricata提供的一个规则关键字,但是在suricata的说明文档,这里并没有给出关键字的解释,因为他是为了兼容snort规则而支持的一个关键字。

tag的字面含义就是标签的意思。通常来说当一个规则命中的时候,往往会去记录命中该规则报文的信息,如果想要按照个人需求记录更多的信息,比如记录该规则命中之后10秒内该条流上所有的报文信息或者命中之后10个上行报文信息,该怎么办呢?这其实就是tag关键字所能够提供的能力,即标记规则命中之后的指定流量信息,用于指导日志对这些流量进行输出,方便分析。

例如:

 alert tcp any any -> any 53 (sid:100; tag:session,10,seconds;)

上述规则表示的就sid为100的这条规则在命中之后,该条TCP流上10秒内的流量都需要记录。

tag关键字的基本格式如下:

tag:type, count, metric, direction;

type有如下两种:

  • 1,session,表示的是tag标记的是一个具体的流。
  • 2,host,表示tag标记的是IP地址,也就是跟当前匹配报文具有相同IP地址的报文上。

count表示基数。metric表示单位,可以是packets ,seconds ,bytes 。两者配合使用,即规则触发之后记录多长时间或者多少个报文,再或者是多少流量的数据。

direction可以是src ,dst ,exclusive 。其中当type为host类型的时候使用src,dst表示的是标记源,目的IP地址的报文。当type为session的时候使用exclusive,表示仅标记第一个匹配该规则的流,其他的流不标记。

下面从源码的角度看看tag的功能是如何实现的,包括tag关键字的解析,打标签,去标签以及在实现tag功能过程中用到的数据结构和方法思路。

tag规则解析

对应的函数为DetectTagSetup,最终会将解析之后的结果放入DetectTagData结构中,对应的就是前面tag格式中的几个字段内容。真正的解析函数为DetectTagParse,使用的是pcre,在suricata中多数关键字的解析都是这样实现的。有一点需要注意的是,SigMatchAppendSMToList在解析之后的规则加入的是DETECT_SM_LIST_TMATCH这个sm list。一般的非字符串匹配的规则加入的是DETECT_SM_LIST_MATCH中,通常是传输层以下的匹配,字符串匹配的规则加入的是DETECT_SM_LIST_PMATCH,通常是传输层以上的匹配,后置规则的匹配加入的是DETECT_SM_LIST_POSTMATCHDETECT_SM_LIST_TMATCH这个sm list专门用于tag的匹配。

tag打标签

tag作用的type要么是流要么是一类的IP地址。标签中的数据包括要匹配的流量条件以及当前已经匹配的内容,即DetectTagDataEntry结构的内容。标签数据要和流或者报文进行关联,由于tag的内容并不是flow或则和packet的必选内容,而且完全依赖于规则。规则中有了tag规则,命中该规则之后才会有tag的数据与流或者packet进行关联,没有则则不需要关联。其出现的概率不大,直接放在flow或者packet结构中会浪费内存。因此suricata对于这类可可选的资源信息进行统一的抽象,将其命名为storage。这一块存储可以是tag信息,也可以是其他的信息。将这个storage与流或者报文进行关联即可。来看看tag是如何实现的:

在流表管理这篇文章中,提到在每条流申请的时候,每条流结构内存之后额外申请了一些指针的空间用于指向与这条流有关的数据内容。如果这条流命中的规则中包含tag,那么就会有一个指针指向tag数据的内存。流程如下:
1,数据包的后置匹配调用为DetectRunPostRules->PacketAlertFinalize->DetectTagMatch

由于DETECT_SM_LIST_TMATCH这个sm list只用于tag关键字,因此简单的搜索一下,可以发现tag规则的匹配是在DetectRunPostRules->PacketAlertFinalize即后置匹配函数中进行调用,如下代码:

if (s->sm_arrays[DETECT_SM_LIST_TMATCH] != NULL) {
                KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_TMATCH);
                SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_TMATCH];
                while (1) {
                    /* tags are set only for alerts */
                    KEYWORD_PROFILING_START;
                    sigmatch_table[smd->type].Match(NULL, det_ctx, p, (Signature *)s, smd->ctx);
                    KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
                    if (smd->is_last)
                        break;
                    smd++;
                }
            }

其中match的匹配函数是DetectTagMatch

2,要明白的是tag关键字的目的并不是去匹配,而是给流或者packet包打上标志,以流为例说明打标签的过程。

DetectTagMatch->TagFlowAdd中:

  • 1,FlowGetStorageById查看该流后面有没有tag数据。
  • 2,如果有就更新与这条流有关的tag存储的数据,如果该条流有关的tag有多个,需要遍历。
  • 3,如果是刚刚匹配上,这个时候还没有tag数据,则需要DetectTagDataCopy新建这样的tag数据,并通过FlowSetStorageById函数让流结构体后面的指针指向此处。这样同一条流上的下个报文来的时候就能通过流中的指针指向找到tag数据并更新。

3,type为session的类型的tag打在流上,那么type为host的标签打在哪个上面呢?

因为流是全局的hash表,当packet过来的时候很容易根据五元组找到该流的内存位置,然后找到tag。同样的当packet过来的时候,要想找到host地址对应的tag数据,则也需要一个全局的host表,根据packet的源或者目的IP找到host的内存位置,然后找到tag。那么host表完全可以参照flow表的设计,用hash表来组织,用IP地址来进行索引。如果你看过我前面文章对于流表的解释,相信host表也很容易理解。host表的目的也很简单,为了支持对于像tag这类作用在IP地址上的规则,而额外建立的用于索引IP地址相关数据之用。采用同样设计的这种全局表还包括Defrag表,IPPair表等,这几个全局表都存在着时效性的问题,都需要老化,老化的操作是在FlowManager之中,这几个表将在后续单独的分析。

tag去标签

由于tag标签也是具有时效性的,要么是时间多少秒,要么是多少个packets包,要么是多少个字节的数据。因此在这些条件达到之后,tag数据也就失去了意义,需要进行回收处理,该回收处理的过程就是PacketAlertFinalize->TagHandlePacket。可以看到无论在TagHandlePacketFlow流tag的回收还是TagHandlePacketHost IP tag的回收。都是在达到限定条件之后,将tag的内存数据进行释放。

本文为CSDN村中少年原创文章,转载记得加上原创出处,博主链接这里

猜你喜欢

转载自blog.csdn.net/javajiawei/article/details/106883526
tag