Suricata之源代碼(三)

Yaml文件的構造

        準備說下面代碼所幹的事情之前,我準備介紹一下suricata.yaml文件。介紹引用自百度百科Yaml

概念

    YAML(IPA: /ˈjæməl/,尾音類似camel駱駝)是一個可讀性高,用來表達資料序列的編程語言,
    YAML是一種很簡單的類似於XML的數據描述語言,語法比XML簡單很多。

誕生

    YAML參考了其他多種語言,包括:XMLC語言PythonPerl以及電子郵件格式RFC2822。
    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操作。
    其讓人最容易上手的特色是巧妙避開各種封閉符號,如:引號、各種括號等,這些符號在巢狀結構時會變得複雜而難以辨認。

Suricata.yaml

    suricata的整個配置都是通過Yaml來配置的,在我認爲它也就是一種key-value的形式。通過不同的縮進來區分孩子。
classification-file: /etc/suricata/classfication.config  //這是對classification.config的配置路徑
reference-config-file: /etc/suricata/reference.config    //這是對reference.config的配置路徑
magic-file: /usr/share/file/magic                       //這是對magic文件的配置路徑

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.

詳細的suricata.yaml

Yaml文件的加載

 /** \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);
        }

這裏主要函數是ConfYamlLoadFile(conf_filename),該函數是將變量conf_filename加載到內存中。
/**
 * \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;
}
    函數ConfYamlLoadFile使用yaml庫中的方法進行解析,主要的函數是ConfYamlParse。通過該函數對suricata.yaml全部的選項進行解析。

/**
 * \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;
}
    該函數會通過yaml_parser_parse(parser, &event)得到event變量。再次通過event.type的類型,將所有的配置都存入ConfNode的變量中,然後suricata通過key的值搜索value的值,從而得到配置文件中的值。 

ConfNode結構是這樣的:

/**
 * 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;





發佈了28 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章