]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
luajit: add flowint support
authorVictor Julien <victor@inliniac.net>
Mon, 22 Apr 2013 15:50:43 +0000 (17:50 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 22 May 2013 13:05:51 +0000 (15:05 +0200)
Expose ScFlowintGet and ScFlowintSet functions to luajit. These set
flowints in real time, regardless of rule and/or script match.

Example:

function init (args)
    local needs = {}
    needs["http.request_headers"] = tostring(true)
    needs["flowint"] = {"cnt"}
    return needs
end

function match(args)
    a = ScFlowintGet(0);
    if a then
        ScFlowintSet(0, a + 1)
    else
        ScFlowintSet(0, 1)
    end

    a = ScFlowintGet(0);
    if a == 23 then
        return 1
    end

    return 0
end

return 0

Script's init call first registers "cnt" at id 0, then 0 is used to use
this var.

src/detect-luajit-extensions.c
src/detect-luajit.c
src/detect-luajit.h

index bc2db10a74f8ff00862d93f48ca5253a3565cdab..1f8bfea1baf2a96c50e119bf7c570de99e333c68 100644 (file)
@@ -266,6 +266,180 @@ int LuajitSetFlowvar(lua_State *luastate) {
     return 0;
 }
 
+static int LuajitGetFlowint(lua_State *luastate) {
+    uint16_t idx;
+    int id;
+    Flow *f;
+    FlowVar *fv;
+    DetectLuajitData *ld;
+    int need_flow_lock = 0;
+    uint32_t number;
+
+    /* need luajit data for id -> idx conversion */
+    lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+    lua_gettable(luastate, LUA_REGISTRYINDEX);
+    ld = lua_touserdata(luastate, -1);
+    SCLogDebug("ld %p", ld);
+    if (ld == NULL) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "internal error: no ld");
+        return 2;
+    }
+
+    /* need flow */
+    lua_pushlightuserdata(luastate, (void *)&luaext_key_flow);
+    lua_gettable(luastate, LUA_REGISTRYINDEX);
+    f = lua_touserdata(luastate, -1);
+    SCLogDebug("f %p", f);
+    if (f == NULL) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "no flow");
+        return 2;
+    }
+
+    /* need flow lock hint */
+    lua_pushlightuserdata(luastate, (void *)&luaext_key_need_flow_lock);
+    lua_gettable(luastate, LUA_REGISTRYINDEX);
+    need_flow_lock = lua_toboolean(luastate, -1);
+
+    /* need flowvar idx */
+    if (!lua_isnumber(luastate, 1)) {
+        SCLogDebug("1st arg not a number");
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "1st arg not a number");
+        return 2;
+    }
+    id = lua_tonumber(luastate, 1);
+    if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) {
+        SCLogDebug("id %d", id);
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "flowvar id out of range");
+        return 2;
+    }
+    idx = ld->flowint[id];
+    if (idx == 0) {
+        SCLogDebug("idx %u", idx);
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "flowvar id uninitialized");
+        return 2;
+    }
+
+    /* lookup var */
+    if (need_flow_lock)
+        FLOWLOCK_RDLOCK(f);
+
+    fv = FlowVarGet(f, idx);
+    if (fv == NULL) {
+        SCLogDebug("fv NULL");
+        if (need_flow_lock)
+            FLOWLOCK_UNLOCK(f);
+
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "no flow var");
+        return 2;
+    }
+    number = fv->data.fv_int.value;
+
+    if (need_flow_lock)
+        FLOWLOCK_UNLOCK(f);
+
+    /* return value through luastate, as a luastring */
+    lua_pushnumber(luastate, (lua_Number)number);
+    SCLogDebug("retrieved flow:%p idx:%u value:%u", f, idx, number);
+
+    return 1;
+
+}
+
+int LuajitSetFlowint(lua_State *luastate) {
+    uint16_t idx;
+    int id;
+    Flow *f;
+    DetectEngineThreadCtx *det_ctx;
+    DetectLuajitData *ld;
+    int need_flow_lock = 0;
+    uint32_t number;
+    lua_Number luanumber;
+
+    /* need luajit data for id -> idx conversion */
+    lua_pushlightuserdata(luastate, (void *)&luaext_key_ld);
+    lua_gettable(luastate, LUA_REGISTRYINDEX);
+    ld = lua_touserdata(luastate, -1);
+    SCLogDebug("ld %p", ld);
+    if (ld == NULL) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "internal error: no ld");
+        return 2;
+    }
+
+    /* need det_ctx */
+    lua_pushlightuserdata(luastate, (void *)&luaext_key_det_ctx);
+    lua_gettable(luastate, LUA_REGISTRYINDEX);
+    det_ctx = lua_touserdata(luastate, -1);
+    SCLogDebug("det_ctx %p", det_ctx);
+    if (det_ctx == NULL) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "internal error: no det_ctx");
+        return 2;
+    }
+
+    /* need flow */
+    lua_pushlightuserdata(luastate, (void *)&luaext_key_flow);
+    lua_gettable(luastate, LUA_REGISTRYINDEX);
+    f = lua_touserdata(luastate, -1);
+    SCLogDebug("f %p", f);
+    if (f == NULL) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "no flow");
+        return 2;
+    }
+    /* need flow lock hint */
+    lua_pushlightuserdata(luastate, (void *)&luaext_key_need_flow_lock);
+    lua_gettable(luastate, LUA_REGISTRYINDEX);
+    need_flow_lock = lua_toboolean(luastate, -1);
+
+    /* need flowvar idx */
+    if (!lua_isnumber(luastate, 1)) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "1st arg not a number");
+        return 2;
+    }
+    id = lua_tonumber(luastate, 1);
+    if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWVARS) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "flowvar id out of range");
+        return 2;
+    }
+
+    if (!lua_isnumber(luastate, 2)) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "2nd arg not a number");
+        return 2;
+    }
+    luanumber = lua_tonumber(luastate, 2);
+    if (luanumber < 0 || id > (double)UINT_MAX) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "value out of range, value must be unsigned 32bit int");
+        return 2;
+    }
+    number = (uint32_t)luanumber;
+
+    idx = ld->flowint[id];
+    if (idx == 0) {
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "flowint id uninitialized");
+        return 2;
+    }
+
+    if (need_flow_lock)
+        FlowVarAddInt(f, idx, number);
+    else
+        FlowVarAddIntNoLock(f, idx, number);
+
+    SCLogDebug("stored flow:%p idx:%u value:%u", f, idx, number);
+    return 0;
+}
+
 void LuajitExtensionsMatchSetup(lua_State *lua_state, DetectLuajitData *ld, DetectEngineThreadCtx *det_ctx, Flow *f, int need_flow_lock) {
     SCLogDebug("det_ctx %p, f %p", det_ctx, f);
 
@@ -299,6 +473,12 @@ int LuajitRegisterExtensions(lua_State *lua_state) {
 
     lua_pushcfunction(lua_state, LuajitSetFlowvar);
     lua_setglobal(lua_state, "ScFlowvarSet");
+
+    lua_pushcfunction(lua_state, LuajitGetFlowint);
+    lua_setglobal(lua_state, "ScFlowintGet");
+
+    lua_pushcfunction(lua_state, LuajitSetFlowint);
+    lua_setglobal(lua_state, "ScFlowintSet");
     return 0;
 }
 
index 26c1e6e3ad5fb2c85e17ffd9a54dc820d9d9f1f3..0299fce5199b27a9e64a2d1fd9d50e605a679db4 100644 (file)
@@ -674,6 +674,28 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuajitData *ld) {
             }
             lua_pop(luastate, 1);
             continue;
+        } else if (strcmp(k, "flowint") == 0) {
+            if (lua_istable(luastate, -1)) {
+                lua_pushnil(luastate);
+                while (lua_next(luastate, -2) != 0) {
+                    /* value at -1, key is at -2 which we ignore */
+                    const char *value = lua_tostring(luastate, -1);
+                    SCLogDebug("value %s", value);
+                    /* removes 'value'; keeps 'key' for next iteration */
+                    lua_pop(luastate, 1);
+
+                    if (ld->flowints == DETECT_LUAJIT_MAX_FLOWINTS) {
+                        SCLogError(SC_ERR_LUAJIT_ERROR, "too many flowints registered");
+                        goto error;
+                    }
+
+                    uint16_t idx = VariableNameGetIdx(de_ctx, (char *)value, DETECT_FLOWINT);
+                    ld->flowint[ld->flowints++] = idx;
+                    SCLogDebug("script uses flowint %u with script id %u", idx, ld->flowints - 1);
+                }
+            }
+            lua_pop(luastate, 1);
+            continue;
         }
 
         v = lua_tostring(luastate, -1);
@@ -1301,6 +1323,158 @@ end:
     UTHFreePackets(&p2, 1);
     return result;
 }
+
+/** \test http buffer, flowints */
+static int LuajitMatchTest04(void) {
+    const char script[] =
+        "function init (args)\n"
+        "   local needs = {}\n"
+        "   needs[\"http.request_headers\"] = tostring(true)\n"
+        "   needs[\"flowint\"] = {\"cnt\"}\n"
+        "   return needs\n"
+        "end\n"
+        "\n"
+        "function match(args)\n"
+        "   print \"inspecting\""
+        "   a = ScFlowintGet(0)\n"
+        "   if a then\n"
+        "       ScFlowintSet(0, a + 1)\n"
+        "   else\n"
+        "       ScFlowintSet(0, 1)\n"
+        "   end\n"
+        "   \n"
+        "   a = ScFlowintGet(0)\n"
+        "   if a == 2 then\n"
+        "       print \"match\"\n"
+        "       return 1\n"
+        "   end\n"
+        "   return 0\n"
+        "end\n"
+        "return 0\n";
+    char sig[] = "alert http any any -> any any (flow:to_server; luajit:unittest; sid:1;)";
+    int result = 0;
+    uint8_t httpbuf1[] =
+        "POST / HTTP/1.1\r\n"
+        "Host: www.emergingthreats.net\r\n"
+        "User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.13) Gecko/2009080315 Ubuntu/8.10 (intrepid) Firefox/3.0.13\r\n"
+        "Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8\r\n";
+    uint8_t httpbuf2[] =
+        "Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3\r\n"
+        "Accept-Encoding: gzip,deflate\r\n"
+        "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
+        "Date: Tue, 22 Sep 2009 19:24:48 GMT\r\n"
+        "Server: Apache\r\n"
+        "Content-Length: 500\r\n"
+        "\r\n"
+        "<!DOCTYPE html PUBLIC";
+    uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+    uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+    TcpSession ssn;
+    Packet *p1 = NULL;
+    Packet *p2 = NULL;
+    Flow f;
+    Signature *s = NULL;
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx;
+
+    ut_script = script;
+
+    memset(&th_v, 0, sizeof(th_v));
+    memset(&f, 0, sizeof(f));
+    memset(&ssn, 0, sizeof(ssn));
+
+    p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+    p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+
+    FLOW_INITIALIZE(&f);
+    f.protoctx = (void *)&ssn;
+    f.flags |= FLOW_IPV4;
+    f.alproto = ALPROTO_HTTP;
+
+    p1->flow = &f;
+    p1->flowflags |= FLOW_PKT_TOSERVER;
+    p1->flowflags |= FLOW_PKT_ESTABLISHED;
+    p1->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+    p2->flow = &f;
+    p2->flowflags |= FLOW_PKT_TOSERVER;
+    p2->flowflags |= FLOW_PKT_ESTABLISHED;
+    p2->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+
+    StreamTcpInitConfig(TRUE);
+
+    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+    if (de_ctx == NULL) {
+        goto end;
+    }
+    de_ctx->flags |= DE_QUIET;
+
+    s = DetectEngineAppendSig(de_ctx, sig);
+    if (s == NULL) {
+        printf("sig parse failed: ");
+        goto end;
+    }
+
+    SigGroupBuild(de_ctx);
+    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+    int r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+    if (r != 0) {
+        printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+        goto end;
+    }
+    HtpState *http_state = f.alstate;
+    if (http_state == NULL) {
+        printf("no http state: ");
+        goto end;
+    }
+
+    /* do detect for p1 */
+    SCLogInfo("p1");
+    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
+
+    if (PacketAlertCheck(p1, 1)) {
+        printf("sid 1 matched on p1 but should not have: ");
+        goto end;
+    }
+
+    r = AppLayerParse(NULL, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+    if (r != 0) {
+        printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+        goto end;
+    }
+    /* do detect for p2 */
+    SCLogInfo("p2");
+    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
+
+    if (!(PacketAlertCheck(p2, 1))) {
+        printf("sid 1 didn't match on p2 but should have: ");
+        goto end;
+    }
+
+    FlowVar *fv = FlowVarGet(&f, 1);
+    if (fv == NULL) {
+        printf("no flowvar: ");
+        goto end;
+    }
+
+    if (fv->data.fv_int.value != 2) {
+        printf("%u != %u: ", fv->data.fv_int.value, 2);
+        goto end;
+    }
+
+    result = 1;
+end:
+    if (de_ctx != NULL)
+        DetectEngineCtxFree(de_ctx);
+
+    StreamTcpFreeConfig(TRUE);
+    FLOW_DESTROY(&f);
+    UTHFreePackets(&p1, 1);
+    UTHFreePackets(&p2, 1);
+    return result;
+}
+
 #endif
 
 void DetectLuajitRegisterTests(void) {
@@ -1308,6 +1482,7 @@ void DetectLuajitRegisterTests(void) {
     UtRegisterTest("LuajitMatchTest01", LuajitMatchTest01, 1);
     UtRegisterTest("LuajitMatchTest02", LuajitMatchTest02, 1);
     UtRegisterTest("LuajitMatchTest03", LuajitMatchTest03, 1);
+    UtRegisterTest("LuajitMatchTest04", LuajitMatchTest04, 1);
 #endif
 }
 
index ad14d7a3a7ddeb10e44649ca390ef0144dde64b7..6d23452a0946629cd915a6f80b14b78139602033 100644 (file)
@@ -37,6 +37,7 @@ typedef struct DetectLuajitThreadData {
 } DetectLuajitThreadData;
 
 #define DETECT_LUAJIT_MAX_FLOWVARS  15
+#define DETECT_LUAJIT_MAX_FLOWINTS  15
 
 typedef struct DetectLuajitData {
     int thread_ctx_id;
@@ -45,6 +46,8 @@ typedef struct DetectLuajitData {
     uint32_t flags;
     int alproto;
     char *buffername; /* buffer name in case of a single buffer */
+    uint16_t flowint[DETECT_LUAJIT_MAX_FLOWINTS];
+    uint16_t flowints;
     uint16_t flowvar[DETECT_LUAJIT_MAX_FLOWVARS];
     uint16_t flowvars;
 } DetectLuajitData;