解析完一条规则后,会调用FinishPortListRule函数将该规则进行归类。归类的原则是
Finish adding the rule to the port tables
1) find the table this rule should belong to (src/dst/any-any tcp,udp,icmp,ip or nocontent)
2) find an index for the sid:gid pair
3) add all no content rules to a single no content port object, the ports are irrelevant so
make it a any-any port object.
4) if it's an any-any rule with content, add to an any-any port object
5) find if we have a port object with these ports defined, if so get it, otherwise create it.
a)do this for src and dst port
b)add the rule index/id to the portobject(s)
c)if the rule is bidir add the rule and port-object to both src and dst tables
完成将规则添加到端口表(每条规则都有规则头,还有协议,根据他们进行归类)
1)找到此规则应属于的表(src / dst / any-any tcp,udp,icmp,ip或nocontent)
2)找到sid:gid键值对的索引
3)将所有无content选项规则添加到单个无content选项端口对象,端口无关紧要,因此将其放入any-any port object。
4)如果它是包含content的any-any(源和目的端口都是any)规则,则添加到any-any port object
5)查找我们是否有定义了这些端口的端口对象,如果是这样,则获取它,否则创建它。
a)为src和dst端口执行此操作
b)将规则索引/ id添加到portobject(s)
c)如果规则是双向的,则将规则和端口对象添加到src和dst表中
数据结构
/* 端口对象: 二者的前三个成员一样, 这样设计,可以重用部分处理函数 PortObjectAppend,还有其他的处理也比较方便*/
typedef struct {
char * name; /* user name - always use strdup or malloc for this*/
int id; /* internal tracking - compiling sets this value */
SF_LIST * item_list; /* list of port and port-range items */
SF_LIST * rule_list; /* list of rules */
void * data; /* user data, PORT_GROUP based on rule_list - only used by any-any ports */
void (*data_free)(void *);
}PortObject;
typedef struct {
char * name; /* user name - always use strdup or malloc for this*/
int id; /* internal tracking - compiling sets this value */
SF_LIST * item_list; /* list of port and port-range items */
SFGHASH * rule_hash; /* hash of rule (rule-indexes) in use */
int port_cnt; /* count of ports using this object */
BITOP * bitop; /* for collecting ports that use this object */
void * data; /* user data, PORT_GROUP based on rule_hash */
void (*data_free)(void *);
}PortObject2;
/*端口表 : 将规则按端口进行归类*/
typedef struct _PortTable_s {
/* turns on group optimization, better speed-but more memory
* otherwise a single merged rule group is used.
*/
int pt_optimize;
/* save the users input port objects in this list
* rules may be added after creation of a port object
* but the ports are not modified.
*/
SF_LIST * pt_polist;
int pt_poid;
/*
* Array of lists of PortObject pointers to unique PortObjects,
* the associated rule lists are stored in Data elements in rh,
* the keys are the address of the PortObjects
*/
SF_LIST * pt_port_lists[SFPO_MAX_PORTS];
/* Compiled / merged port object hash table */
SFGHASH * pt_mpo_hash;
SFGHASH * pt_mpxo_hash;
SF_LIST * pt_plx_list;
/* a single rule list with all rules merged together */
SF_LIST * pt_merged_rule_list;
/*
* Final Port/Rule Groupings, one port object per port, or null
*/
PortObject2 * pt_port_object[SFPO_MAX_PORTS];
int pt_lrc; /* large rule count, this many rules is a large group */
/* Stats */
int single_merges; /* single PortObject on a port */
int small_merges; /* small port objects merged into a bigger object */
int large_single_merges; /* 1 large + some small objects */
int large_multi_merges; /* >1 large object merged + some small objects */
int non_opt_merges;
}PortTable;
/* 用于管理所有端口表的数据结构*/
typedef struct {
PortTable * tcp_src, * tcp_dst;/* */
PortTable * udp_src, * udp_dst;
PortTable * icmp_src,* icmp_dst;
PortTable * ip_src, * ip_dst;
PortObject * tcp_anyany;
PortObject * udp_anyany;
PortObject * icmp_anyany;
PortObject * ip_anyany;
PortObject * tcp_nocontent;
PortObject * udp_nocontent;
PortObject * icmp_nocontent;
PortObject * ip_nocontent;
}rule_port_tables_t;
上面的函数会进行去重处理、合并规则所属的类别等,处理完之后,调用fpCreateFastPacketDetection函数进行生成检测引擎。
内部调用fpCreatePortGroups,再在里面调用fpCreatePortTablePortGroups进行创建检测引擎需要的数据结构,并将规则选项中的模式串加入检测引擎,编译规则生成引擎。
int fpCreateFastPacketDetection(SnortConfig *sc)
{
rule_port_tables_t *port_tables;
FastPatternConfig *fp;
/* This is somewhat necessary because of how the detection option trees
* are added via a callback from the pattern matcher */
if(!rule_count || (sc == NULL))
return 0;
port_tables = sc->port_tables;
fp = sc->fast_pattern_config;
...
if (fpCreatePortGroups(sc, port_tables))
FatalError("Could not create PortGroup objects for PortObjects\n");
...
if (fpCreateRuleMaps(sc, port_tables))
FatalError("Could not create rule maps\n");
...
return 0;
}
/*
* 对所有的端口表创建端口组对象
* 注意 any-any ports使用PortObjects表示,这个函数会将其转化为PortObject2,为后面的创建端口组函数做准备
*/
static int fpCreatePortGroups(SnortConfig *sc, rule_port_tables_t *p)
{
PortObject2 *po2, *add_any_any = NULL;
FastPatternConfig *fp = sc->fast_pattern_config;
if (!rule_count)
return 0 ;
/* 进行转换 */
po2 = PortObject2Dup(p->tcp_anyany);
if (po2 == NULL)
FatalError("Could not create a PortObject version 2 for tcp-any-any rules\n!");
...
if (fpCreatePortTablePortGroups(sc, p->tcp_src, add_any_any))
{
LogMessage("fpCreatePorTablePortGroups failed-tcp_src\n");
return -1;
}
...
/* 转换后的po2进行处理,通过po2创建PortGroup。里面会基于content和uricontent构建多模式状态机的第一步*/
if (fpCreatePortObject2PortGroup(sc, po2, 0))
{
LogMessage("fpCreatePorTablePortGroups failed-tcp any-any\n");
return -1;
}
...
}
/*
* 内部遍历p中的pt_mpo_hash哈希表调用fpCreatePortObject2PortGroup函数进行构建多模式状态机
*/
static int fpCreatePortTablePortGroups(SnortConfig *sc, PortTable *p, PortObject2 *poaa)
{
...
for (node=sfghash_findfirst(p->pt_mpo_hash); //p->pt_mpxo_hash
node;
node=sfghash_findnext(p->pt_mpo_hash) ) //p->pt->mpxo_hash
{
PortObject2 * po;
po = (PortObject2*)node->data;
...
if (fpCreatePortObject2PortGroup(sc, po, poaa))
{
LogMessage("fpCreatePortObject2PortGroup() failed\n");
return -1;
}
...
}
}
static int fpCreatePortObject2PortGroup(SnortConfig *sc, PortObject2 *po, PortObject2 *poaa)
{
SFGHASH_NODE *node;
unsigned sid, gid;
OptTreeNode * otn;
PORT_GROUP * pg;
PortObject2 *pox;
FastPatternConfig *fp = sc->fast_pattern_config;
/* verify we have a port object */
if (po == NULL)
return 0;
po->data = 0;
...
/* rule_hash中保存了多有与该端口相关的所有规则选项的索引,通过它们快速获取到对应的规则选项 */
if (po->rule_hash == NULL)
return 0;
/* 初始化:创建port_group */
pg = (PORT_GROUP *)SnortAlloc(sizeof(PORT_GROUP));
/* 创建多模式状态机需要的对象,每一种类别都会分配内存*/
if (fpAllocPms(sc, pg, fp) != 0)
{
free(pg);
return -1;
}
/*
* 遍历PortObject中的规则,并将它们添加到PORT_GROUP 多模式状态机和 port group RULE_NODE
* 的list中(这个list在某些情况下会用到,比如fpEvalHeader函数中,在遍历规则进行检测的时候会
* 用到)
* po 源端口、目的端口:content/uri and nocontent
* poaa 任意端口:content/uri and nocontent
* each PG has
* 每个PG 都有src or dst contents, generic-contents, and no-contents
*/
pox = po;
while (pox != NULL)
{
/* 遍历规则索引的哈希表*/
for (node = sfghash_findfirst(pox->rule_hash);
node;
node = sfghash_findnext(pox->rule_hash))
{
int *prindex = (int *)node->data;
/* be safe - no rule index, ignore it */
if (prindex == NULL)
continue;
/* 通过规则索引找到对应的 gid:sid */
gid = RuleIndexMapGid(ruleIndexMap, *prindex);
sid = RuleIndexMapSid(ruleIndexMap, *prindex);
/*使用 gid:sid 定位 otn */
otn = OtnLookup(sc->otn_map, gid, sid);
if (otn == NULL)
{
...
continue;
}
...
/* 1:将otn中的content、uricontent等模式串添加到pg中对应的pg的链表中:Content List、No-Content List、 Uri-Content List
* 2: 调用mpseAddPatternWithSnortConfig往pg的pgPms数组中对应的模式状态机中放入最长的模式串,为后面编译状态机做准备
*/
if (fpAddPortGroupRule(sc, pg, otn, fp) != 0)
continue;
}
...
if (pox == poaa)
break;
pox = poaa;
}
/* 调用mpsePrepPatternsWithSnortConf编译模式状态机 */
if (fpFinishPortGroup(sc, pg, fp) != 0)
return 0;
po->data = pg;
po->data_free = fpDeletePortGroup;
return 0;
}