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! 🙂
I need save temporary varible in memory. Which a accessed by many differernt flow. I want count POST request by domain. Why can i do it?
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.
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.
Thanks! I will try it!
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.