Suricata lua continued

Today I improved the lua jit support in Suricata further. The scripts will now need to express their “needs” through an “init” function in the script that is called only at Suricata startup.

The “init” function fills a lua table. This will allow the user to indicate what buffers the script needs to inspect. The script will then only be invoked when these buffers are actually available, so the script won’t have to worry about whether or not some data is unavailable or not. Also, only these buffers are passed to the script, so safing the overhead of copying unnecessary buffers.

The earlier example extended:

function init (args)
    local needs = {}
    needs["packet"] = tostring(true)
    needs["payload"] = tostring(true)
    return needs
end

-- match if packet and payload both contain HTTP
function match(args)
    pkt = 0
    pay = 0
    
    for k,v in pairs(args) do
        if tostring(k) == "packet" then
            a = tostring(v)
            if #a > 0 then
                if a:find("HTTP") then
                    pkt = 1
                end
            end
        elseif tostring(k) == "payload" then
            a = tostring(v)
            if #a > 0 then
                if a:find("HTTP") then
                    pay = 1
                end
            end
        end

        if pay == 1 and pkt == 1 then
            return 1
        end
    end

    return 0
end

return 0

The new part is:

function init (args)
    local needs = {}
    needs["packet"] = tostring(true)
    needs["payload"] = tostring(true)
    return needs
end

Currently only “packet”, which is the complete raw packet including protocol headers such as ethernet, and “payload” are available. I will extend this to include “stream”, “http.uri”, “http.headers” and so on. Also, I was thinking that the script could also require a flowvar or flowint.

Another change is that the user can also return a table instead of just a 1 or 0. In that table “retval” is used as the return code. The idea here is that later we’ll be able to return flowvars and flowints as well. Example:

function init (args)
    local needs = {}
    needs["packet"] = tostring(true)
    return needs
end

-- return match via table
function match(args)
    local result = {}
    result["retval"] = tostring(1)
    return result
end

Here the the script always matches, and passes the match back to Suricata through a table.

So far mostly internal changes and no extra detection features, but it’s getting some more shape. Looking forward to feedback! 🙂

6 thoughts on “Suricata lua continued

    • If you need it to be accessable by other flows I think right now you will have to write something to disk. There is no API that supports storing it inside Suricata currently.

  1. Hello, when I use lua script the performance of my suricata is very bad. My cpu utilization always exceeds 300%. How can I improve the performance? Thanks in advance!

    • Couple of things to try: use LuaJIT if you aren’t already.
      Also, add a ‘prefilter’ test. For example add ‘content:”somepattern”;’ before the lua script, so that it’s not called as often.

      Of course it also greatly depends on what the script does.

  2. Thanks for the great post! I had no idea this blog existed; it’s providing just the type of detailed information about suricata features that I’ve been hunting for.

    Is it possible to extract the data of args[“packet”]? I see that it is in some kind of binary format, and would love to be able to extract the L2 and L3 header information, especially the addressing.

Comments are closed.