Migrate C flow status alerts to Lua user scripts

- Alerts and flow status cleanup
- Community flow user scripts migration
- Implement scripts filters by l7 proto and packet interface only
- Migrate flow2statusinfojson
- Lower flow periodic update to 30 seconds if there is flow activity
- Display flow scripts without a gui section
This commit is contained in:
emanuele-f 2019-10-17 17:37:15 +02:00
parent bfdf5773d3
commit b3a8c6d49a
37 changed files with 1071 additions and 1287 deletions

View file

@ -15,6 +15,7 @@ require "alert_utils"
local user_scripts = require("user_scripts")
local alert_consts = require("alert_consts")
local flow_consts = require("flow_consts")
local json = require("dkjson")
if ntop.isPro() then
package.path = dirs.installdir .. "/pro/scripts/lua/modules/?.lua;" .. package.path
@ -27,9 +28,16 @@ local do_trace = false -- Trace lua calls
local available_modules = nil
local benchmarks = {}
-- Keeps information about the current predominant status
local predominant_status = nil
local predominant_status_msg = nil
-- Keeps information about the current predominant alerted status
local alerted_status
local alerted_status_msg
local alerted_custom_severity
local predominant_status
local recalculate_predominant_status
-- Save them as they are overridden
local c_flow_set_status = flow.setStatus
local c_flow_clear_status = flow.clearStatus
-- #################################################################
@ -92,7 +100,25 @@ function teardown()
print("flow.lua:teardown() called\n")
end
user_scripts.teardown(available_modules, do_benchmark, do_print_benchmark)
if(available_modules ~= nil) then
user_scripts.teardown(available_modules, do_benchmark, do_print_benchmark)
end
end
-- #################################################################
-- @brief Store more information into the flow status. Such information
-- does not depend the specific flow status being triggered
-- @param flow_info as returned by flow.getInfo()
-- @param flow_status the status table to augument
local function augumentFlowStatusInfo(flow_info, flow_status)
flow_status["ntopng.key"] = flow.getKey()
flow_status["hash_entry_id"] = flow.getHashEntryId()
if(flow_info["proto.ndpi"] == "ICMP") then
-- NOTE: this information is parsed by getFlowStatusInfo()
flow_status["icmp"] = flow.getICMPStatusInfo()
end
end
-- #################################################################
@ -103,12 +129,21 @@ end
-- @param mod_fn the callback to call
-- @return true if some module was called, false otherwise
local function call_modules(l4_proto, mod_fn)
if not(available_modules) then
return
end
local all_modules = available_modules.modules
local hooks = available_modules.l4_hooks[l4_proto]
local rv = false
local prev_predominant_status = flow_consts.getStatusInfo(flow.getPredominantStatus())
-- Reset predominant status information
predominant_status = nil
predominant_status_msg = nil
alerted_status = nil
alerted_status_msg = nil
alerted_custom_severity = nil
recalculate_predominant_status = false
predominant_status = prev_predominant_status
if(hooks ~= nil) then
hooks = hooks[mod_fn]
@ -119,28 +154,69 @@ local function call_modules(l4_proto, mod_fn)
return(false)
end
-- TODO too expensive, remove
local info = flow.getFullInfo()
if(do_trace) then print(string.format("%s()[START]: bitmap=0x%x predominant=%d", mod_fn, flow.getStatus(), prev_predominant_status.status_id)) end
-- NOTE: this information is required by many modules, provide it to all of them
local info = flow.getInfo()
local params = {
-- Flow specific information
flow_info = info,
now = os.time(),
}
for mod_key, hook_fn in pairs(hooks) do
local script = all_modules[mod_key]
if(script.l7_proto ~= nil) then
-- Check if the L7 protocol correspond
local flow_proto = info["proto.ndpi"]
if(string.find(flow_proto, script.l7_proto) == nil) then
if do_trace then print(string.format("%s() [check: %s]: skipping flow with proto=%s (wants %s)\n", mod_fn, mod_key, flow_proto, script.l7_proto)) end
goto continue
end
end
if do_trace then print(string.format("%s() [check: %s]: %s\n", mod_fn, mod_key, shortFlowLabel(info))) end
hook_fn(params)
rv = true
::continue::
end
if(predominant_status ~= nil) then
if(recalculate_predominant_status) then
-- The predominant status has changed and we've lost track of it
-- This is the worst case, it must be recalculated manually
predominant_status = flow_consts.getPredominantStatus(flow.getStatus())
end
if(do_trace) then print(string.format("%s()[END]: bitmap=0x%x predominant=%d", mod_fn, flow.getStatus(), predominant_status.status_id)) end
if(prev_predominant_status ~= predominant_status) then
-- The predominant status has changed, updated the flow
flow.setPredominantStatus(predominant_status.status_id)
end
if(alerted_status ~= nil) then
if do_trace then
traceError(TRACE_NORMAL, TRACE_CONSOLE, string.format("flow.triggerAlert(type=%s, severity=%s)",
alertTypeRaw(predominant_status.alert_type.alert_id), alertSeverityRaw(predominant_status.alert_severity.severity_id)))
alertTypeRaw(alerted_status.alert_type.alert_id), alertSeverityRaw(alerted_status.alert_severity.severity_id)))
end
flow.triggerAlert(predominant_status.status_id, predominant_status.alert_type.alert_id, predominant_status.alert_severity.severity_id, predominant_status_msg)
-- The message can be either a table or a localized string message.
-- When using tables the status can possibly be augumented with augumentFlowStatusInfo
alerted_status_msg = alerted_status_msg or {}
if(type(alerted_status_msg) == "table") then
augumentFlowStatusInfo(info, alerted_status_msg)
-- Need to convert to JSON
alerted_status_msg = json.encode(alerted_status_msg)
end
flow.triggerAlert(alerted_status.status_id, alerted_status.alert_type.alert_id,
alerted_custom_severity or alerted_status.alert_severity.severity_id, alerted_status_msg)
end
return(rv)
@ -151,21 +227,51 @@ end
-- @brief This provides an API that flow user_scripts can call in order to
-- set a flow status bit. The status_json of the predominant status is
-- saved for later use.
function flow.triggerStatus(status_id, status_json)
function flow.triggerStatus(status_id, status_json, custom_severity)
local new_status = flow_consts.getStatusInfo(status_id)
if((predominant_status == nil) or (new_status.prio > predominant_status.prio)) then
-- The new status as an higher priority
predominant_status = new_status
predominant_status_msg = status_json
if((alerted_status == nil) or (new_status.prio > alerted_status.prio)) then
-- The new alerted status as an higher priority
alerted_status = new_status
alerted_status_msg = status_json
alerted_custom_severity = custom_severity -- possibly nil
end
-- Set the status bit in the flow status bitmap
-- Call the function below to handle the predominant status and update
-- the flow status
flow.setStatus(status_id)
end
-- #################################################################
-- NOTE: overrides the C flow.setStatus (now saved in c_flow_set_status)
function flow.setStatus(status_id)
if c_flow_set_status(status_id) then
-- The status has actually changed
local new_status = flow_consts.getStatusInfo(status_id)
if(new_status.prio > predominant_status.prio) then
-- The new status as an higher priority
predominant_status = new_status
end
end
end
-- #################################################################
-- NOTE: overrides the C flow.clearStatus (now saved in c_flow_clear_status)
function flow.clearStatus(status_id)
if c_flow_clear_status(status_id) then
-- The status has actually changed
if(predominant_status.id == status_id) then
-- The predominant status has been cleared, need to recalculate it
recalculate_predominant_status = true
end
end
end
-- #################################################################
-- Given an L4 protocol, we must call both the hooks registered for that protocol and
-- the hooks registered for any L4 protocol (id 255)
function protocolDetected(l4_proto)