一 概述

snort插件在做协议解析业务时需要知道查询名和相关联的协议ID,以便将协议解析结果和协议名称,协议ID都关联保存起来。总的业务流程如下:

向snort插件导入协议名和协议_结点

 

1.snort主进程在启动时调用LoadProtocolAppName从数据库读取所有的appname和appid,调用LoadProtocolReference=》AddProtocolReferenceExtern将appname和appid插入哈希表;

2.snort插件如modbus插件等在启动时调用findProtocolReference从哈希表查询appname和appid。

二 SFTargetProtocolReference结构

typedef struct _SFTargetProtocolReference
{
char name[STD_BUF];
int16_t ordinal; // 序数
}SFTargetProtocolReference;

存入哈希表的数据是SFTargetProtocolReference结构,name是协议名,ordinal是协议ID,每存入一个SFTargetProtocolReference对象,ordinal都会递增加1,其上限是MAX_PROTOCOL_ORDINAL(8192)。

三 哈希表结构sfghash

向snort插件导入协议名和协议_协议解析_02

 

sfghash是链式哈希表结构,每个Bucket对应一个双向链表,链表里并不存储实际数据,而是存储数据的指针,如下所示:

typedef struct _sfghash_node
{
struct _sfghash_node * next, * prev;
const void * key; // 对应appname
void *data; // 对应SFTargetProtocolReference
};

这里的key和data都是指针,这样会节省哈希表的存储空间,但需要通过释放函数释放相关结点存储,即:

typedef struct _sfghash
{
SFHASHFCN *sfhashfcn; // 哈希函数
SFGHASH_NODE **table; // 哈希表 相当一个指针数组
...
void (*userfree)(void *); // 用户传入释放data的函数指针
...
} SFGHASH, SFDOCT;

1.创建哈希表sfghash_new:

(1)创建一个大于等于传入参数最大素数的hash table;

(2)创建哈希函数;

(3)保存user_free等对象

2.加入哈希表sfghash_add

(1)支持传入字符串的key或者二进制的key,在传入二进制的key时,需要带入key的字节数;

(2)通过sfhashfcn→hash_fcn算出key对应的哈希值,并对nrows取模找到bucket位置(index);

(3)从相应的bucket找出key,如果找到,则返回已查询到,否则新生成一个hash node插入bucket最开始位置,方便之后查询。

3.哈希表查询sfghash_find_node

(1)根据key算出哈希值;

(2)找到bucket位置;

(3)从bucket查询key,如果查询到,使用movetofront将key的结点移动到bucket最开始,方便之后查询。

4.哈希表删除sfghash_delete

遍历整个hash table,并调用userfree释放data空间。

四 snort调用sfghash

代码:target-based/sftarget_protocol_reference.c,函数调用链:

AddProtocolReference=>InitializeProtocolReferenceTable=>sfghash_add

AddProtocolReferenceExtern=>InitializeProtocolReferenceTable=>sfghash_find=>sfghash_add

FindProtocolReference=>InitializeProtocolReferenceTable=>sfghash_find