]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
luajit/flowint: add ScFlowintIncr & ScFlowintDecr
authorVictor Julien <victor@inliniac.net>
Tue, 23 Apr 2013 09:58:49 +0000 (11:58 +0200)
committerVictor Julien <victor@inliniac.net>
Wed, 22 May 2013 13:47:27 +0000 (15:47 +0200)
Add flowint lua functions for incrementing and decrementing flowints.

First use creates the var and inits to 0. So a call:

    a = ScFlowintIncr(0)

Results in a == 1.

If the var reached UINT_MAX (2^32), it's not further incremented. If the
var reaches 0 it's not decremented further.

Calling ScFlowintDecr on a uninitialized var will init it to 0.

Example script:

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

    function match(args)
        a = ScFlowintIncr(0);
        if a == 23 then
            return 1
        end

        return 0
    end
    return 0

This script matches the 23rd time it's invoked on a flow.

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

index 74b0637cc44f7c01a3b5d51c2181007c6f10b839..ffa708d62481c2fab3d8ddff2dbab7ce5e3b80fa 100644 (file)
@@ -304,7 +304,7 @@ static int LuajitGetFlowint(lua_State *luastate) {
     lua_gettable(luastate, LUA_REGISTRYINDEX);
     need_flow_lock = lua_toboolean(luastate, -1);
 
-    /* need flowvar idx */
+    /* need flowint idx */
     if (!lua_isnumber(luastate, 1)) {
         SCLogDebug("1st arg not a number");
         lua_pushnil(luastate);
@@ -315,14 +315,14 @@ static int LuajitGetFlowint(lua_State *luastate) {
     if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWINTS) {
         SCLogDebug("id %d", id);
         lua_pushnil(luastate);
-        lua_pushstring(luastate, "flowvar id out of range");
+        lua_pushstring(luastate, "flowint 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");
+        lua_pushstring(luastate, "flowint id uninitialized");
         return 2;
     }
 
@@ -345,7 +345,7 @@ static int LuajitGetFlowint(lua_State *luastate) {
     if (need_flow_lock)
         FLOWLOCK_UNLOCK(f);
 
-    /* return value through luastate, as a luastring */
+    /* return value through luastate, as a luanumber */
     lua_pushnumber(luastate, (lua_Number)number);
     SCLogDebug("retrieved flow:%p idx:%u value:%u", f, idx, number);
 
@@ -400,7 +400,7 @@ int LuajitSetFlowint(lua_State *luastate) {
     lua_gettable(luastate, LUA_REGISTRYINDEX);
     need_flow_lock = lua_toboolean(luastate, -1);
 
-    /* need flowvar idx */
+    /* need flowint idx */
     if (!lua_isnumber(luastate, 1)) {
         lua_pushnil(luastate);
         lua_pushstring(luastate, "1st arg not a number");
@@ -409,7 +409,7 @@ int LuajitSetFlowint(lua_State *luastate) {
     id = lua_tonumber(luastate, 1);
     if (id < 0 || id >= DETECT_LUAJIT_MAX_FLOWVARS) {
         lua_pushnil(luastate);
-        lua_pushstring(luastate, "flowvar id out of range");
+        lua_pushstring(luastate, "flowint id out of range");
         return 2;
     }
 
@@ -442,6 +442,172 @@ int LuajitSetFlowint(lua_State *luastate) {
     return 0;
 }
 
+static int LuajitIncrFlowint(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 flowint 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, "flowint id out of range");
+        return 2;
+    }
+    idx = ld->flowint[id];
+    if (idx == 0) {
+        SCLogDebug("idx %u", idx);
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "flowint id uninitialized");
+        return 2;
+    }
+
+    /* lookup var */
+    if (need_flow_lock)
+        FLOWLOCK_RDLOCK(f);
+
+    fv = FlowVarGet(f, idx);
+    if (fv == NULL) {
+        number = 1;
+    } else {
+        number = fv->data.fv_int.value;
+        if (number < UINT_MAX)
+            number++;
+    }
+    FlowVarAddIntNoLock(f, idx, number);
+
+    if (need_flow_lock)
+        FLOWLOCK_UNLOCK(f);
+
+    /* return value through luastate, as a luanumber */
+    lua_pushnumber(luastate, (lua_Number)number);
+    SCLogDebug("incremented flow:%p idx:%u value:%u", f, idx, number);
+
+    return 1;
+
+}
+
+static int LuajitDecrFlowint(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 flowint 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, "flowint id out of range");
+        return 2;
+    }
+    idx = ld->flowint[id];
+    if (idx == 0) {
+        SCLogDebug("idx %u", idx);
+        lua_pushnil(luastate);
+        lua_pushstring(luastate, "flowint id uninitialized");
+        return 2;
+    }
+
+    /* lookup var */
+    if (need_flow_lock)
+        FLOWLOCK_RDLOCK(f);
+
+    fv = FlowVarGet(f, idx);
+    if (fv == NULL) {
+        number = 0;
+    } else {
+        number = fv->data.fv_int.value;
+        if (number > 0)
+            number--;
+    }
+    FlowVarAddIntNoLock(f, idx, number);
+
+    if (need_flow_lock)
+        FLOWLOCK_UNLOCK(f);
+
+    /* return value through luastate, as a luanumber */
+    lua_pushnumber(luastate, (lua_Number)number);
+    SCLogDebug("decremented flow:%p idx:%u value:%u", f, idx, number);
+
+    return 1;
+
+}
+
 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);
 
@@ -481,6 +647,13 @@ int LuajitRegisterExtensions(lua_State *lua_state) {
 
     lua_pushcfunction(lua_state, LuajitSetFlowint);
     lua_setglobal(lua_state, "ScFlowintSet");
+
+    lua_pushcfunction(lua_state, LuajitIncrFlowint);
+    lua_setglobal(lua_state, "ScFlowintIncr");
+
+    lua_pushcfunction(lua_state, LuajitDecrFlowint);
+    lua_setglobal(lua_state, "ScFlowintDecr");
+
     return 0;
 }
 
index 0299fce5199b27a9e64a2d1fd9d50e605a679db4..c22ec29da6ddb426f6ac6ef0de67cc31ab909502 100644 (file)
@@ -1475,6 +1475,299 @@ end:
     return result;
 }
 
+/** \test http buffer, flowints */
+static int LuajitMatchTest05(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 = ScFlowintIncr(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;
+}
+
+/** \test http buffer, flowints */
+static int LuajitMatchTest06(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 == nil then\n"
+        "       print \"new var set to 2\""
+        "       ScFlowintSet(0, 2)\n"
+        "   end\n"
+        "   a = ScFlowintDecr(0)\n"
+        "   if a == 0 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 != 0) {
+        printf("%u != %u: ", fv->data.fv_int.value, 0);
+        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) {
@@ -1483,6 +1776,8 @@ void DetectLuajitRegisterTests(void) {
     UtRegisterTest("LuajitMatchTest02", LuajitMatchTest02, 1);
     UtRegisterTest("LuajitMatchTest03", LuajitMatchTest03, 1);
     UtRegisterTest("LuajitMatchTest04", LuajitMatchTest04, 1);
+    UtRegisterTest("LuajitMatchTest05", LuajitMatchTest05, 1);
+    UtRegisterTest("LuajitMatchTest06", LuajitMatchTest06, 1);
 #endif
 }