From b60e28e1a492bd9f49d44cdb5fa34373624421cd Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 20 Feb 2014 09:36:16 +0100 Subject: [PATCH] output-lua: packet logger support Through 'needs' the script init function can indicate it wants to see packets and select a condition function. Currently only alerts is an option: function init (args) local needs = {} needs["type"] = "packet" needs["filter"] = "alerts" return needs end --- src/output-lua.c | 189 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 151 insertions(+), 38 deletions(-) diff --git a/src/output-lua.c b/src/output-lua.c index f166113cda..fc579991ed 100644 --- a/src/output-lua.c +++ b/src/output-lua.c @@ -69,6 +69,40 @@ typedef struct LogLuaThreadCtx_ { const char lualog_ext_key_tx[] = "suricata:lualog:tx:ptr"; +/** \brief dump stack from lua state to screen */ +void LuaPrintStack(lua_State *state) { + int size = lua_gettop(state); + int i; + + for (i = 1; i <= size; i++) { + int type = lua_type(state, i); + printf("Stack size=%d, level=%d, type=%d, ", size, i, type); + + switch (type) { + case LUA_TFUNCTION: + printf("function %s", lua_tostring(state, i) ? "true" : "false"); + break; + case LUA_TBOOLEAN: + printf("bool %s", lua_toboolean(state, i) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf("number %g", lua_tonumber(state, i)); + break; + case LUA_TSTRING: + printf("string `%s'", lua_tostring(state, i)); + break; + case LUA_TTABLE: + printf("table `%s'", lua_tostring(state, i)); + break; + default: + printf("other %s", lua_typename(state, type)); + break; + + } + printf("\n"); + } +} + static int LuaTxLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id) { SCEnter(); @@ -98,49 +132,114 @@ static int LuaTxLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow SCReturnInt(0); } -/** \brief dump stack from lua state to screen */ -void LuaPrintStack(lua_State *state) { - int size = lua_gettop(state); - int i; +void LogLuaPushTableKeyValueInt(lua_State *luastate, const char *key, int value) +{ + lua_pushstring(luastate, key); + lua_pushnumber(luastate, value); + lua_settable(luastate, -3); +} - for (i = 1; i <= size; i++) { - int type = lua_type(state, i); - printf("Stack size=%d, level=%d, type=%d, ", size, i, type); +void LogLuaPushTableKeyValueString(lua_State *luastate, const char *key, const char *value) +{ + lua_pushstring(luastate, key); + lua_pushstring(luastate, value ? value : "(null)"); + lua_settable(luastate, -3); +} - switch (type) { - case LUA_TFUNCTION: - printf("function %s", lua_tostring(state, i) ? "true" : "false"); - break; - case LUA_TBOOLEAN: - printf("bool %s", lua_toboolean(state, i) ? "true" : "false"); - break; - case LUA_TNUMBER: - printf("number %g", lua_tonumber(state, i)); - break; - case LUA_TSTRING: - printf("string `%s'", lua_tostring(state, i)); - break; - case LUA_TTABLE: - printf("table `%s'", lua_tostring(state, i)); - break; - default: - printf("other %s", lua_typename(state, type)); - break; +static int LuaPacketLogger(ThreadVars *tv, void *thread_data, const Packet *p) +{ + LogLuaThreadCtx *td = (LogLuaThreadCtx *)thread_data; + char timebuf[64]; + CreateTimeString(&p->ts, timebuf, sizeof(timebuf)); + + char srcip[46], dstip[46]; + if (PKT_IS_IPV4(p)) { + PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); + } else if (PKT_IS_IPV6(p)) { + PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); + } else { + /* decoder event */ + goto not_supported; + } + + char proto[16] = ""; + if (SCProtoNameValid(IP_GET_IPPROTO(p)) == TRUE) { + strlcpy(proto, known_proto[IP_GET_IPPROTO(p)], sizeof(proto)); + } else { + snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IP_GET_IPPROTO(p)); + } + + SCMutexLock(&td->lua_ctx->m); + uint16_t cnt; + for (cnt = 0; cnt < p->alerts.cnt; cnt++) { + const PacketAlert *pa = &p->alerts.alerts[cnt]; + if (unlikely(pa->s == NULL)) { + continue; + } + + lua_getglobal(td->lua_ctx->luastate, "log"); + //if (lua_type(td->lua_ctx->luastate, -1) != LUA_TFUNCTION) { + // SCLogError(SC_ERR_LUAJIT_ERROR, "no log function in script"); + // goto error; + //} + + /* prepare data to pass to script */ + lua_newtable(td->lua_ctx->luastate); + + LogLuaPushTableKeyValueInt(td->lua_ctx->luastate, "sid", pa->s->id); + LogLuaPushTableKeyValueInt(td->lua_ctx->luastate, "gid", pa->s->gid); + LogLuaPushTableKeyValueInt(td->lua_ctx->luastate, "rev", pa->s->rev); + LogLuaPushTableKeyValueInt(td->lua_ctx->luastate, "priority", pa->s->prio); + + if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP) { + LogLuaPushTableKeyValueInt(td->lua_ctx->luastate, "sp", p->sp); + LogLuaPushTableKeyValueInt(td->lua_ctx->luastate, "dp", p->dp); + } + + LogLuaPushTableKeyValueString(td->lua_ctx->luastate, "msg", pa->s->msg); + LogLuaPushTableKeyValueString(td->lua_ctx->luastate, "srcip", srcip); + LogLuaPushTableKeyValueString(td->lua_ctx->luastate, "dstip", dstip); + LogLuaPushTableKeyValueString(td->lua_ctx->luastate, "ts", timebuf); + LogLuaPushTableKeyValueString(td->lua_ctx->luastate, "ipproto", proto); + LogLuaPushTableKeyValueString(td->lua_ctx->luastate, "class", pa->s->class_msg); + + //LuaPrintStack(td->lua_ctx->luastate); + int retval = lua_pcall(td->lua_ctx->luastate, 1, 0, 0); + if (retval != 0) { + SCLogInfo("failed to run script: %s", lua_tostring(td->lua_ctx->luastate, -1)); } - printf("\n"); } +//error: + SCMutexUnlock(&td->lua_ctx->m); +not_supported: + SCReturnInt(0); } +static int LuaPacketConditionAlerts(ThreadVars *tv, const Packet *p) +{ + if (p->alerts.cnt > 0) + return TRUE; + return FALSE; +} + +typedef struct LogLuaScriptOptions_ { + AppProto alproto; + int packet; + int alerts; + int file; +} LogLuaScriptOptions; + /** \brief load and evaluate the script * * This function parses the script, checks if all the required functions * are defined and runs the 'init' function. The init function will inform * us what the scripts needs are. */ -static int LuaScriptInit(const char *filename) { +static int LuaScriptInit(const char *filename, LogLuaScriptOptions *options) { int status; -// AppProto alproto = ALPROTO_UNKNOWN; lua_State *luastate = luaL_newstate(); if (luastate == NULL) @@ -217,8 +316,14 @@ static int LuaScriptInit(const char *filename) { SCLogDebug("k='%s', v='%s'", k, v); -// if (strcmp(k,"protocol") == 0 && strcmp(v, "http") == 0) -// alproto = ALPROTO_HTTP; + if (strcmp(k,"protocol") == 0 && strcmp(v, "http") == 0) + options->alproto = ALPROTO_HTTP; + else if (strcmp(k, "type") == 0 && strcmp(v, "packet") == 0) + options->packet = 1; + else if (strcmp(k, "filter") == 0 && strcmp(v, "alerts") == 0) + options->alerts = 1; + else + SCLogInfo("unknown key and/or value: k='%s', v='%s'", k, v); } //SCLogInfo("alproto %u", alproto); @@ -371,25 +476,33 @@ static OutputCtx *OutputLuaLogInit(ConfNode *conf) ConfNode *script; TAILQ_FOREACH(script, &scripts->head, next) { SCLogInfo("script %s", script->val); + LogLuaScriptOptions opts; + memset(&opts, 0x00, sizeof(opts)); - int r = LuaScriptInit(script->val); + int r = LuaScriptInit(script->val, &opts); if (r != 0) { SCLogInfo("script init failed (%d)", r); continue; } - + /* create an OutputModule for this script, based + * on it's needs. */ OutputModule *om = SCCalloc(1, sizeof(*om)); BUG_ON(om == NULL); //TODO - if (1) { // TODO, logger type selection logic + om->name = MODULE_NAME; + om->conf_name = script->val; + om->InitSubFunc = OutputLuaLogInitSub; + + if (opts.alproto == ALPROTO_HTTP) { om->TxLogFunc = LuaTxLogger; om->alproto = ALPROTO_HTTP; - om->name = MODULE_NAME; - om->conf_name = script->val; - om->InitSubFunc = OutputLuaLogInitSub; - TAILQ_INSERT_TAIL(&output_ctx->submodules, om, entries); + } else if (opts.packet && opts.alerts) { + om->PacketLogFunc = LuaPacketLogger; + om->PacketConditionFunc = LuaPacketConditionAlerts; } + + TAILQ_INSERT_TAIL(&output_ctx->submodules, om, entries); } return output_ctx;