suricata日志模塊

一 說明

suricata的日志先按照packet日志/事務(wù)日志/文件日志/流日志進(jìn)行分類,這個在output.c的OutputRegisterRootLoggers函數(shù)中有注冊,內(nèi)容如下:

void OutputRegisterRootLoggers(void)
{
    OutputPacketLoggerRegister();
    OutputTxLoggerRegister();
    OutputFiledataLoggerRegister();
    OutputFileLoggerRegister();
    OutputStreamingLoggerRegister();
}

以tx的日志為例,看下注冊的代碼:

void OutputTxLoggerRegister (void)
{
    OutputRegisterRootLogger(OutputTxLogThreadInit, OutputTxLogThreadDeinit,
        OutputTxLogExitPrintStats, OutputTxLog);
}

將這種大類的日志注冊到rootLogger上面如下:

void OutputRegisterRootLogger(ThreadInitFunc ThreadInit,
    ThreadDeinitFunc ThreadDeinit,
    ThreadExitPrintStatsFunc ThreadExitPrintStats,
    OutputLogFunc LogFunc)
{
    RootLogger *logger = SCCalloc(1, sizeof(*logger));
    if (logger == NULL) {
        return;
    }
    logger->ThreadInit = ThreadInit;
    logger->ThreadDeinit = ThreadDeinit;
    logger->ThreadExitPrintStats = ThreadExitPrintStats;
    logger->LogFunc = LogFunc;
    TAILQ_INSERT_TAIL(&RootLoggers, logger, entries);
}

RootLoggers就是全局的大類日志,循環(huán)調(diào)用每個大類的日志輸出內(nèi)容如下:

TmEcode OutputLoggerLog(ThreadVars *tv, Packet *p, void *thread_data)
{
    LoggerThreadStore *thread_store = (LoggerThreadStore *)thread_data;
    RootLogger *logger = TAILQ_FIRST(&RootLoggers);
    LoggerThreadStoreNode *thread_store_node = TAILQ_FIRST(thread_store);
    while (logger && thread_store_node) {
        if (logger->LogFunc != NULL) {
            logger->LogFunc(tv, p, thread_store_node->thread_data);
        }
        logger = TAILQ_NEXT(logger, entries);
        thread_store_node = TAILQ_NEXT(thread_store_node, entries);
    }

    return TM_ECODE_OK;
}

那看下PacketLog(這個日志更簡單)這個大類的日志輸出吧:

static TmEcode OutputPacketLog(ThreadVars *tv, Packet *p, void *thread_data)
{
    BUG_ON(thread_data == NULL);

    if (list == NULL) {
        /* No child loggers. */
        return TM_ECODE_OK;
    }

    OutputLoggerThreadData *op_thread_data = (OutputLoggerThreadData *)thread_data;
    OutputPacketLogger *logger = list;
    OutputLoggerThreadStore *store = op_thread_data->store;

    BUG_ON(logger == NULL && store != NULL);
    BUG_ON(logger != NULL && store == NULL);
    BUG_ON(logger == NULL && store == NULL);

    while (logger && store) {
        BUG_ON(logger->LogFunc == NULL || logger->ConditionFunc == NULL);

        if ((logger->ConditionFunc(tv, (const Packet *)p)) == TRUE) {
            PACKET_PROFILING_LOGGER_START(p, logger->logger_id);
            logger->LogFunc(tv, store->thread_data, (const Packet *)p);
            PACKET_PROFILING_LOGGER_END(p, logger->logger_id);
        }

        logger = logger->next;
        store = store->next;

        BUG_ON(logger == NULL && store != NULL);
        BUG_ON(logger != NULL && store == NULL);
    }

    return TM_ECODE_OK;
}

關(guān)鍵是從list這個靜態(tài)鏈表中取日志,然后輸出,那么這個靜態(tài)的鏈表的值在哪里賦值的那?
我們看下非root類的日志注冊:

void OutputRegisterLoggers(void) {
 /* custom format log*/
    LogCustomFormatRegister();

    LuaLogRegister();
    /* fast log */
    AlertFastLogRegister();
    /* debug log */
    AlertDebugLogRegister();
    /* prelue log */
    AlertPreludeRegister();
    /* syslog log */
    AlertSyslogRegister();
    /* unified2 log */
    Unified2AlertRegister();
    /* drop log */
    LogDropLogRegister();
    JsonDropLogRegister();
....
}

很多,看下具體一個注冊到底干了什么事情?

void JsonDropLogRegister (void)
{
    OutputRegisterPacketModule(LOGGER_JSON_DROP, MODULE_NAME, "drop-json-log",
        JsonDropLogInitCtx, JsonDropLogger, JsonDropLogCondition,
        JsonDropLogThreadInit, JsonDropLogThreadDeinit, NULL);
    OutputRegisterPacketSubModule(LOGGER_JSON_DROP, "eve-log", MODULE_NAME,
        "eve-log.drop", JsonDropLogInitCtxSub, JsonDropLogger,
        JsonDropLogCondition, JsonDropLogThreadInit, JsonDropLogThreadDeinit,
        NULL);
}

在進(jìn)一步看下怎么注冊的:

void OutputRegisterPacketModule(LoggerId id, const char *name,
    const char *conf_name, OutputInitFunc InitFunc,
    PacketLogger PacketLogFunc, PacketLogCondition PacketConditionFunc,
    ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit,
    ThreadExitPrintStatsFunc ThreadExitPrintStats)
{
    if (unlikely(PacketLogFunc == NULL || PacketConditionFunc == NULL)) {
        goto error;
    }

    OutputModule *module = SCCalloc(1, sizeof(*module));
    if (unlikely(module == NULL)) {
        goto error;
    }

    module->logger_id = id;
    module->name = name;
    module->conf_name = conf_name;
    module->InitFunc = InitFunc;
    module->PacketLogFunc = PacketLogFunc;
    module->PacketConditionFunc = PacketConditionFunc;
    module->ThreadInit = ThreadInit;
    module->ThreadDeinit = ThreadDeinit;
    module->ThreadExitPrintStats = ThreadExitPrintStats;
    TAILQ_INSERT_TAIL(&output_modules, module, entries);

    SCLogDebug("Packet logger \"%s\" registered.", name);
    return;
error:
    SCLogError(SC_ERR_FATAL, "Fatal error encountered. Exiting...");
    exit(EXIT_FAILURE);
}

申請一個日志模塊,然后加入到全局變量:output_modules 里面。
這個地方有點蒙了,為什么循環(huán)用的是list,這里面是日志模塊,這兩者是怎么關(guān)聯(lián)的。
通過循環(huán)獲取output_modules內(nèi)容填充到list中去。runmodes.c里面的函數(shù)如下:

static void RunModeInitializeEveOutput(ConfNode *conf, OutputCtx *parent_ctx)
{
    ConfNode *types = ConfNodeLookupChild(conf, "types");
    SCLogDebug("types %p", types);
    if (types == NULL) {
        return;
    }

    ConfNode *type = NULL;
    TAILQ_FOREACH(type, &types->head, next) {
        SCLogConfig("enabling 'eve-log' module '%s'", type->val);

        int sub_count = 0;
        char subname[256];
        snprintf(subname, sizeof(subname), "eve-log.%s", type->val);

        /* Now setup all registers logger of this name. */
        OutputModule *sub_module;
        TAILQ_FOREACH(sub_module, &output_modules, entries) {
            if (strcmp(subname, sub_module->conf_name) == 0) {
                sub_count++;

                if (sub_module->parent_name == NULL ||
                        strcmp(sub_module->parent_name, "eve-log") != 0) {
                    FatalError(SC_ERR_INVALID_ARGUMENT,
                            "bad parent for %s", subname);
                }
                if (sub_module->InitSubFunc == NULL) {
                    FatalError(SC_ERR_INVALID_ARGUMENT,
                            "bad sub-module for %s", subname);
                }
                ConfNode *sub_output_config =
                    ConfNodeLookupChild(type, type->val);
                // sub_output_config may be NULL if no config

                /* pass on parent output_ctx */
                OutputInitResult result =
                    sub_module->InitSubFunc(sub_output_config, parent_ctx);
                if (!result.ok || result.ctx == NULL) {
                    continue;
                }

                AddOutputToFreeList(sub_module, result.ctx);
                SetupOutput(sub_module->name, sub_module,
                        result.ctx);
            }
        }

        /* Error is no registered loggers with this name
         * were found .*/
        if (!sub_count) {
            FatalErrorOnInit(SC_ERR_INVALID_ARGUMENT,
                    "No output module named %s", subname);
            continue;
        }
    }
}

runmodes.c中有個SetupOut函數(shù):

static void SetupOutput(const char *name, OutputModule *module, OutputCtx *output_ctx)
{
    /* flow logger doesn't run in the packet path */
    if (module->FlowLogFunc) {
        OutputRegisterFlowLogger(module->name, module->FlowLogFunc,
            output_ctx, module->ThreadInit, module->ThreadDeinit,
            module->ThreadExitPrintStats);
        return;
    }
    /* stats logger doesn't run in the packet path */
    if (module->StatsLogFunc) {
        OutputRegisterStatsLogger(module->name, module->StatsLogFunc,
            output_ctx,module->ThreadInit, module->ThreadDeinit,
            module->ThreadExitPrintStats);
        return;
    }

    if (module->logger_id == LOGGER_ALERT_DEBUG) {
        debuglog_enabled = 1;
    }

    if (module->PacketLogFunc) {
        SCLogDebug("%s is a packet logger", module->name);
        OutputRegisterPacketLogger(module->logger_id, module->name,
            module->PacketLogFunc, module->PacketConditionFunc, output_ctx,
            module->ThreadInit, module->ThreadDeinit,
            module->ThreadExitPrintStats);
    } else if (module->TxLogFunc) {
        SCLogDebug("%s is a tx logger", module->name);
        OutputRegisterTxLogger(module->logger_id, module->name, module->alproto,
                module->TxLogFunc, output_ctx, module->tc_log_progress,
                module->ts_log_progress, module->TxLogCondition,
                module->ThreadInit, module->ThreadDeinit,
                module->ThreadExitPrintStats);
        logger_bits[module->alproto] |= (1<<module->logger_id);
    } else if (module->FiledataLogFunc) {
        SCLogDebug("%s is a filedata logger", module->name);
        OutputRegisterFiledataLogger(module->logger_id, module->name,
            module->FiledataLogFunc, output_ctx, module->ThreadInit,
            module->ThreadDeinit, module->ThreadExitPrintStats);
        filedata_logger_count++;
    } else if (module->FileLogFunc) {
        SCLogDebug("%s is a file logger", module->name);
        OutputRegisterFileLogger(module->logger_id, module->name,
            module->FileLogFunc, output_ctx, module->ThreadInit,
            module->ThreadDeinit, module->ThreadExitPrintStats);
        file_logger_count++;
    } else if (module->StreamingLogFunc) {
        SCLogDebug("%s is a streaming logger", module->name);
        OutputRegisterStreamingLogger(module->logger_id, module->name,
            module->StreamingLogFunc, output_ctx, module->stream_type,
            module->ThreadInit, module->ThreadDeinit,
            module->ThreadExitPrintStats);
    } else {
        SCLogError(SC_ERR_INVALID_ARGUMENT, "Unknown logger type: name=%s",
            module->name);
    }
}

二 整理下邏輯

2.1 初始化思路:

1)首先通過rootlogger的注冊,注冊幾個基本的大日志,注冊到RootLoggers里面。
注冊的循環(huán)函數(shù)是對一個大類里面的list做循環(huán)調(diào)用打印日志。
2)通過非rootLogger的日志注冊,注冊具體的日志到output_modules模塊中。
3)在初始化的時候會將output_modules 變成串到相關(guān)大類的list里面去。

2.3 調(diào)用思路

1)通過flow-worker.c中的OutputLoggerLog 函數(shù)調(diào)用。
2)上面函數(shù)遍歷:RootLoggers 調(diào)用相關(guān)大類的日志打印函數(shù)。
3)日志打印函數(shù)遍歷內(nèi)部的list循環(huán)打印。
4)list的循環(huán)其實是注冊具體日志的具體日志模塊轉(zhuǎn)變得到的大類日志。
5)實際輸出日志內(nèi)容。

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容