Yaml文件的构造
在准备说下面代码所干的事情之前,我准备介绍一下suricata.yaml文件。介绍引用自百度百科Yaml
概念
YAML(IPA: /ˈjæməl/,尾音类似camel骆驼)是一个可读性高,用来表达资料序列的编程语言,
YAML是一种很简单的类似于XML的数据描述语言,语法比XML简单很多。
YAML是一种很简单的类似于XML的数据描述语言,语法比XML简单很多。
诞生
Clark Evans在2001年5月在首次发表了这种语言[2],另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者。
命名
YAML是"YAML Ain't a Markup Language"(YAML不是一种置标语言)的递归缩写。
在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种置标语言),但为了强调这种语言以数据做为中心,而不是以置标语言为重点,而用返璞词重新命名。
功能
YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表,标量等资料形态、。
它使用空白符号缩排和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种设定档、倾印除错内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。
尽管它比较适合用来表达阶层式(hierarchical model)的数据结构,不过也有精致的语法可以表示关联性(relational model)的资料。
由于YAML使用空白字符和分行来分隔资料,使的他特别适合用grep、Python、Perl、Ruby操作。
其让人最容易上手的特色是巧妙避开各种封闭符号,如:引号、各种括号等,这些符号在巢状结构时会变得复杂而难以辨认。
Max-pending-packets(最大包处理数)
用max-pending-packets来设置允许suricata所能同时处理的数据包的个数。这个设置的范围是在一个数据包到上千个数据包之间的数字。
Runmodes(运行模式)
对于rumodes的设置是根据你自己的喜好,你喜欢什么样的模式就在这里设置你所喜欢的模式。在安装了Suricata之后你可以在命令行输入suricata --list-runmodes来查看Suricata支持的所有运行模式。
Suricata.yaml
suricata的整个配置都是通过Yaml来配置的,在我认为它也就是一种key-value的形式。通过不同的缩进来区分孩子。- <span style="font-family:SimHei;font-size:14px;">classification-file: /etc/suricata/classfication.config //这是对classification.config的配置路径
- reference-config-file: /etc/suricata/reference.config //这是对reference.config的配置路径
- magic-file: /usr/share/file/magic //这是对magic文件的配置路径</span>
<span style="font-family:SimHei;font-size:14px;">classification-file: /etc/suricata/classfication.config //这是对classification.config的配置路径
reference-config-file: /etc/suricata/reference.config //这是对reference.config的配置路径
magic-file: /usr/share/file/magic //这是对magic文件的配置路径</span>
Max-pending-packets(最大包处理数)
用max-pending-packets来设置允许suricata所能同时处理的数据包的个数。这个设置的范围是在一个数据包到上千个数据包之间的数字。max-pending-packets: 1024
Runmodes(运行模式)
对于rumodes的设置是根据你自己的喜好,你喜欢什么样的模式就在这里设置你所喜欢的模式。在安装了Suricata之后你可以在命令行输入suricata --list-runmodes来查看Suricata支持的所有运行模式。runmode: autofp
Default-packet-size(默认包的大小)
对于default-packet-size这个选项,主要是设置你网络中的数据包的大小。default-packet-size: 1514
Action-order
1)pass:Suricata允许数据包通过网络,接下来Suricata就不会对它再进行检测。2)drop:这个功能只会存在IPS/inline模式,如果有特征匹配到了,并且这个特征的动作是drop那么这个数据包也不会进行下面的检测,而是丢弃数据包。
3)reject::对错误的ICMP包和TCP的有些错误包产生reject,在IPS模式下reject和drop的含义一样。
4)alert:这种数据包被看作是没有攻击的,这种可以让系统管理员注意到。在Inline/IPS模式中,这种被对待成drop或者是reject。
action-order:
- pass
- drop
- reject
- alert
Outputs
这个模块主要是针对Suricata的输出日志进行配置,可以配置你所感兴趣的日志。-fast: #The log-name.
enabled:yes #This log is enabled. Set to 'no' to disable.//是否要开启这个日志文件
filename: fast.log #The name of the file in the default logging directory.//日志文件的名称
append: yes/no #If this option is set to yes, the last filled fast.log-file will not be
#overwritten while restarting Suricata.
-Unified-log: #The log-name.
enabled: no #This log is not enabled. Set 'yes' to enable.
filename: unified.log #The name of the file in the default logging directory.
limit: 32 #The file size limit in megabytes.//如果日志大小超过就会重新创建下一个日志文件
- http-log: #The log-name.
enabled: yes #This log is enabled. Set 'no' to disable.
filename: http.log #The name of the file in the default logging directory.
append: yes/no #If this option is set to yes, the last filled fast.log-file will not be
# overwritten while restarting Suricata.
detection-engine grouping tree
src Stands for source IP-address.
dst Stands for destination IP-address.
sp Stands for source port.
dp Stands for destination port.
Multi-pattern-matcher(多模匹配)
mpm-algo: b2gc //选择的多模匹配算法
pattern-matcher:
- b2gc:
search_algo: B2gSearchBNDMq
hash_size: low #Determines the size of the hash-table.
bf_size: medium #Determines the size of the bloom- filter.
- b3g:
search_algo: B3gSearchBNDMq
hash_size: low #See hash-size -b2gc.
bf_size: medium #See bf-size -b2gc.
- wumanber:
hash_size: low #See hash-size -b2gc.
bf_size: medium #See bf-size -b2gc.
Yaml文件的加载
- <span style="font-size:12px;">/** \todo we need an api for these */
- /* Load yaml configuration file if provided. */
- if (conf_filename != NULL) {
- #ifdef UNITTESTS
- if (run_mode == RUNMODE_UNITTEST) {
- SCLogError(SC_ERR_CMD_LINE, "should not use a configuration file with unittests");
- exit(EXIT_FAILURE);
- }
- #endif
- if (ConfYamlLoadFile(conf_filename) != 0) {
- /* Error already displayed. */
- exit(EXIT_FAILURE);
- }</span>
<span style="font-size:12px;">/** \todo we need an api for these */
/* Load yaml configuration file if provided. */
if (conf_filename != NULL) {
#ifdef UNITTESTS
if (run_mode == RUNMODE_UNITTEST) {
SCLogError(SC_ERR_CMD_LINE, "should not use a configuration file with unittests");
exit(EXIT_FAILURE);
}
#endif
if (ConfYamlLoadFile(conf_filename) != 0) {
/* Error already displayed. */
exit(EXIT_FAILURE);
}</span>
这里主要函数是ConfYamlLoadFile(conf_filename),该函数是将变量conf_filename加载到内存中。
- <span style="font-size:12px;">/**
- * \brief Load configuration from a YAML file.
- *
- * This function will load a configuration file. On failure -1 will
- * be returned and it is suggested that the program then exit. Any
- * errors while loading the configuration file will have already been
- * logged.
- *
- * \param filename Filename of configuration file to load.
- *
- * \retval 0 on success, -1 on failure.
- */
- int
- ConfYamlLoadFile(const char *filename)
- {
- FILE *infile;
- yaml_parser_t parser;
- int ret;
- ConfNode *root = ConfGetRootNode();
- if (yaml_parser_initialize(&parser) != 1) {
- fprintf(stderr, "Failed to initialize yaml parser.\n");
- return -1;
- }
- infile = fopen(filename, "r");
- if (infile == NULL) {
- fprintf(stderr, "Failed to open file: %s: %s\n", filename,
- strerror(errno));
- yaml_parser_delete(&parser);
- return -1;
- }
- yaml_parser_set_input_file(&parser, infile);
- ret = ConfYamlParse(&parser, root, 0);
- yaml_parser_delete(&parser);
- fclose(infile);
- return ret;
- }</span>
<span style="font-size:12px;">/**
* \brief Load configuration from a YAML file.
*
* This function will load a configuration file. On failure -1 will
* be returned and it is suggested that the program then exit. Any
* errors while loading the configuration file will have already been
* logged.
*
* \param filename Filename of configuration file to load.
*
* \retval 0 on success, -1 on failure.
*/
int
ConfYamlLoadFile(const char *filename)
{
FILE *infile;
yaml_parser_t parser;
int ret;
ConfNode *root = ConfGetRootNode();
if (yaml_parser_initialize(&parser) != 1) {
fprintf(stderr, "Failed to initialize yaml parser.\n");
return -1;
}
infile = fopen(filename, "r");
if (infile == NULL) {
fprintf(stderr, "Failed to open file: %s: %s\n", filename,
strerror(errno));
yaml_parser_delete(&parser);
return -1;
}
yaml_parser_set_input_file(&parser, infile);
ret = ConfYamlParse(&parser, root, 0);
yaml_parser_delete(&parser);
fclose(infile);
return ret;
}</span>
函数ConfYamlLoadFile使用yaml库中的方法进行解析,主要的函数是ConfYamlParse。通过该函数对suricata.yaml全部的选项进行解析。
- <span style="font-size:12px;">/**
- * \brief Parse a YAML layer.
- *
- * \param parser A pointer to an active yaml_parser_t.
- * \param parent The parent configuration node.
- *
- * \retval 0 on success, -1 on failure.
- */
- static int
- ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq)
- {
- ConfNode *node = parent;
- yaml_event_t event;
- int done = 0;
- int state = 0;
- int seq_idx = 0;
- while (!done) {
- if (!yaml_parser_parse(parser, &event)) {
- fprintf(stderr,
- "Failed to parse configuration file at line %" PRIuMAX ": %s\n",
- (uintmax_t)parser->problem_mark.line, parser->problem);
- return -1;
- }
- if (event.type == YAML_DOCUMENT_START_EVENT) {
- /* Verify YAML version - its more likely to be a valid
- * Suricata configuration file if the version is
- * correct. */
- yaml_version_directive_t *ver =
- event.data.document_start.version_directive;
- if (ver == NULL) {
- fprintf(stderr, "ERROR: Invalid configuration file.\n\n");
- fprintf(stderr, "The configuration file must begin with the following two lines:\n\n");
- fprintf(stderr, "%%YAML 1.1\n---\n\n");
- goto fail;
- }
- int major = event.data.document_start.version_directive->major;
- int minor = event.data.document_start.version_directive->minor;
- if (!(major == YAML_VERSION_MAJOR && minor == YAML_VERSION_MINOR)) {
- fprintf(stderr, "ERROR: Invalid YAML version. Must be 1.1\n");
- goto fail;
- }
- }
- else if (event.type == YAML_SCALAR_EVENT) {
- char *value = (char *)event.data.scalar.value;
- SCLogDebug("event.type = YAML_SCALAR_EVENT (%s) inseq=%d",
- value, inseq);
- if (inseq) {
- ConfNode *seq_node = ConfNodeNew();
- seq_node->name = SCCalloc(1, DEFAULT_NAME_LEN);
- if (seq_node->name == NULL)
- return -1;
- snprintf(seq_node->name, DEFAULT_NAME_LEN, "%d", seq_idx++);
- seq_node->val = SCStrdup(value);
- TAILQ_INSERT_TAIL(&parent->head, seq_node, next);
- }
- else {
- if (state == CONF_KEY) {
- if (parent->is_seq) {
- if (parent->val == NULL) {
- parent->val = SCStrdup(value);
- if (parent->val && strchr(parent->val, '_'))
- Mangle(parent->val);
- }
- }
- ConfNode *n0 = ConfNodeLookupChild(parent, value);
- if (n0 != NULL) {
- node = n0;
- }
- else {
- node = ConfNodeNew();
- node->name = SCStrdup(value);
- if (node->name && strchr(node->name, '_')) {
- if (!(parent->name &&
- ((strcmp(parent->name, "address-groups") == 0) ||
- (strcmp(parent->name, "port-groups") == 0)))) {
- Mangle(node->name);
- if (mangle_errors < MANGLE_ERRORS_MAX) {
- SCLogWarning(SC_WARN_DEPRECATED,
- "%s is deprecated. Please use %s on line %"PRIuMAX".",
- value, node->name, (uintmax_t)parser->mark.line+1);
- mangle_errors++;
- if (mangle_errors >= MANGLE_ERRORS_MAX)
- SCLogWarning(SC_WARN_DEPRECATED, "not showing more "
- "parameter name warnings.");
- }
- }
- }
- TAILQ_INSERT_TAIL(&parent->head, node, next);
- }
- state = CONF_VAL;
- }
- else {
- if (node->allow_override) {
- if (node->val != NULL)
- SCFree(node->val);
- node->val = SCStrdup(value);
- }
- state = CONF_KEY;
- }
- }
- }
- else if (event.type == YAML_SEQUENCE_START_EVENT) {
- SCLogDebug("event.type = YAML_SEQUENCE_START_EVENT");
- if (ConfYamlParse(parser, node, 1) != 0)
- goto fail;
- state = CONF_KEY;
- }
- else if (event.type == YAML_SEQUENCE_END_EVENT) {
- SCLogDebug("event.type = YAML_SEQUENCE_END_EVENT");
- return 0;
- }
- else if (event.type == YAML_MAPPING_START_EVENT) {
- SCLogDebug("event.type = YAML_MAPPING_START_EVENT");
- if (inseq) {
- ConfNode *seq_node = ConfNodeNew();
- seq_node->is_seq = 1;
- seq_node->name = SCCalloc(1, DEFAULT_NAME_LEN);
- if (seq_node->name == NULL)
- return -1;
- snprintf(seq_node->name, DEFAULT_NAME_LEN, "%d", seq_idx++);
- TAILQ_INSERT_TAIL(&node->head, seq_node, next);
- if (ConfYamlParse(parser, seq_node, 0) != 0)
- goto fail;
- }
- else {
- if (ConfYamlParse(parser, node, inseq) != 0)
- goto fail;
- }
- state = CONF_KEY;
- }
- else if (event.type == YAML_MAPPING_END_EVENT) {
- SCLogDebug("event.type = YAML_MAPPING_END_EVENT");
- done = 1;
- }
- else if (event.type == YAML_STREAM_END_EVENT) {
- done = 1;
- }
- yaml_event_delete(&event);
- continue;
- fail:
- yaml_event_delete(&event);
- return -1;
- }
- return 0;
- }</span>
<span style="font-size:12px;">/**
* \brief Parse a YAML layer.
*
* \param parser A pointer to an active yaml_parser_t.
* \param parent The parent configuration node.
*
* \retval 0 on success, -1 on failure.
*/
static int
ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq)
{
ConfNode *node = parent;
yaml_event_t event;
int done = 0;
int state = 0;
int seq_idx = 0;
while (!done) {
if (!yaml_parser_parse(parser, &event)) {
fprintf(stderr,
"Failed to parse configuration file at line %" PRIuMAX ": %s\n",
(uintmax_t)parser->problem_mark.line, parser->problem);
return -1;
}
if (event.type == YAML_DOCUMENT_START_EVENT) {
/* Verify YAML version - its more likely to be a valid
* Suricata configuration file if the version is
* correct. */
yaml_version_directive_t *ver =
event.data.document_start.version_directive;
if (ver == NULL) {
fprintf(stderr, "ERROR: Invalid configuration file.\n\n");
fprintf(stderr, "The configuration file must begin with the following two lines:\n\n");
fprintf(stderr, "%%YAML 1.1\n---\n\n");
goto fail;
}
int major = event.data.document_start.version_directive->major;
int minor = event.data.document_start.version_directive->minor;
if (!(major == YAML_VERSION_MAJOR && minor == YAML_VERSION_MINOR)) {
fprintf(stderr, "ERROR: Invalid YAML version. Must be 1.1\n");
goto fail;
}
}
else if (event.type == YAML_SCALAR_EVENT) {
char *value = (char *)event.data.scalar.value;
SCLogDebug("event.type = YAML_SCALAR_EVENT (%s) inseq=%d",
value, inseq);
if (inseq) {
ConfNode *seq_node = ConfNodeNew();
seq_node->name = SCCalloc(1, DEFAULT_NAME_LEN);
if (seq_node->name == NULL)
return -1;
snprintf(seq_node->name, DEFAULT_NAME_LEN, "%d", seq_idx++);
seq_node->val = SCStrdup(value);
TAILQ_INSERT_TAIL(&parent->head, seq_node, next);
}
else {
if (state == CONF_KEY) {
if (parent->is_seq) {
if (parent->val == NULL) {
parent->val = SCStrdup(value);
if (parent->val && strchr(parent->val, '_'))
Mangle(parent->val);
}
}
ConfNode *n0 = ConfNodeLookupChild(parent, value);
if (n0 != NULL) {
node = n0;
}
else {
node = ConfNodeNew();
node->name = SCStrdup(value);
if (node->name && strchr(node->name, '_')) {
if (!(parent->name &&
((strcmp(parent->name, "address-groups") == 0) ||
(strcmp(parent->name, "port-groups") == 0)))) {
Mangle(node->name);
if (mangle_errors < MANGLE_ERRORS_MAX) {
SCLogWarning(SC_WARN_DEPRECATED,
"%s is deprecated. Please use %s on line %"PRIuMAX".",
value, node->name, (uintmax_t)parser->mark.line+1);
mangle_errors++;
if (mangle_errors >= MANGLE_ERRORS_MAX)
SCLogWarning(SC_WARN_DEPRECATED, "not showing more "
"parameter name warnings.");
}
}
}
TAILQ_INSERT_TAIL(&parent->head, node, next);
}
state = CONF_VAL;
}
else {
if (node->allow_override) {
if (node->val != NULL)
SCFree(node->val);
node->val = SCStrdup(value);
}
state = CONF_KEY;
}
}
}
else if (event.type == YAML_SEQUENCE_START_EVENT) {
SCLogDebug("event.type = YAML_SEQUENCE_START_EVENT");
if (ConfYamlParse(parser, node, 1) != 0)
goto fail;
state = CONF_KEY;
}
else if (event.type == YAML_SEQUENCE_END_EVENT) {
SCLogDebug("event.type = YAML_SEQUENCE_END_EVENT");
return 0;
}
else if (event.type == YAML_MAPPING_START_EVENT) {
SCLogDebug("event.type = YAML_MAPPING_START_EVENT");
if (inseq) {
ConfNode *seq_node = ConfNodeNew();
seq_node->is_seq = 1;
seq_node->name = SCCalloc(1, DEFAULT_NAME_LEN);
if (seq_node->name == NULL)
return -1;
snprintf(seq_node->name, DEFAULT_NAME_LEN, "%d", seq_idx++);
TAILQ_INSERT_TAIL(&node->head, seq_node, next);
if (ConfYamlParse(parser, seq_node, 0) != 0)
goto fail;
}
else {
if (ConfYamlParse(parser, node, inseq) != 0)
goto fail;
}
state = CONF_KEY;
}
else if (event.type == YAML_MAPPING_END_EVENT) {
SCLogDebug("event.type = YAML_MAPPING_END_EVENT");
done = 1;
}
else if (event.type == YAML_STREAM_END_EVENT) {
done = 1;
}
yaml_event_delete(&event);
continue;
fail:
yaml_event_delete(&event);
return -1;
}
return 0;
}</span>
该函数会通过yaml_parser_parse(parser, &event)得到event变量。再次通过event.type的类型,将所有的配置都存入ConfNode的变量中,然后suricata通过key的值搜索value的值,从而得到配置文件中的值。
ConfNode结构是这样的:
- <span style="font-size:12px;">/**
- * Structure of a configuration parameter.
- */
- typedef struct ConfNode_ {
- char *name;//key
- char *val;//value
- int is_seq;
- int allow_override;
- struct ConfNode_ *parent;
- TAILQ_HEAD(, ConfNode_) head;
- TAILQ_ENTRY(ConfNode_) next;
- } ConfNode;</span>
<span style="font-size:12px;">/**
* Structure of a configuration parameter.
*/
typedef struct ConfNode_ {
char *name;//key
char *val;//value
int is_seq;
int allow_override;
struct ConfNode_ *parent;
TAILQ_HEAD(, ConfNode_) head;
TAILQ_ENTRY(ConfNode_) next;
} ConfNode;</span>