ntopng/scripts/lua/modules/llm/tools/get_live_flows_summary.lua
2026-06-23 16:41:48 +02:00

113 lines
4.4 KiB
Lua

--
-- (C) 2013-26 - ntop.org
--
return {
name = "get_live_flows_summary",
description = "Return an aggregated summary of active flows on the current interface. " ..
"Optional arguments: ip (string, filter by host), " ..
"role ('any'|'client'|'server', default 'any'), " ..
"group_by ('country'|'destination'|'application'|'protocol'|'source', default 'country'), " ..
"alerted_only (boolean, default false), max_flows (integer, default 500).",
handler = function(args)
local host_ip = args and args.ip
local role = ((args and args.role) or "any"):lower()
local group_by = ((args and args.group_by) or "country"):lower()
local alerted_only = args and args.alerted_only and true or false
local max_hits = (args and tonumber(args.max_flows)) or 500
local host_key = host_ip
if host_ip and args and args.vlan and tonumber(args.vlan) > 0 then
host_key = host_ip .. "@" .. tostring(args.vlan)
end
local pag = { maxHits = max_hits, sortColumn = "column_bytes", a2zSortOrder = false }
if alerted_only then pag.alertedFlows = true end
local flows_data
if host_key then
if role == "client" then
flows_data = interface.getFlowsInfo(nil, pag, nil, host_key)
elseif role == "server" then
flows_data = interface.getFlowsInfo(nil, pag, nil, nil, host_key)
else
flows_data = interface.getFlowsInfo(host_key, pag)
end
else
flows_data = interface.getFlowsInfo(nil, pag)
end
if not flows_data or not flows_data.flows then
return "No active flows found.", nil
end
local groups = {}
local total = { flows = 0, alerted = 0, bytes = 0, score = 0 }
for _, f in ipairs(flows_data.flows) do
local bytes = (f["cli2srv.bytes"] or 0) + (f["srv2cli.bytes"] or 0)
local pkts = f["packets"] or 0
local score = f["flow_score"] or (f.score and f.score.flow_score) or 0
local is_alerted = f["flow.alerted"] and 1 or 0
total.flows = total.flows + 1
total.alerted = total.alerted + is_alerted
total.bytes = total.bytes + bytes
total.score = total.score + score
local srv_country = f["srv.country"] or "??"
local cli_country = f["cli.country"] or "??"
local l7 = f["proto.ndpi"] or f["application"] or "Unknown"
local l4 = f["proto.l4"] or "Unknown"
local srv_ip = f["srv.ip"] or ""
local cli_ip = f["cli.ip"] or ""
local key
if group_by == "country" then
key = srv_country
elseif group_by == "destination" then
key = srv_ip
elseif group_by == "application" then
key = l7
elseif group_by == "protocol" then
key = l4
elseif group_by == "source" then
key = cli_country
else
key = srv_country
end
if not groups[key] then
groups[key] = { bytes = 0, packets = 0, num_flows = 0, num_alerted = 0, score = 0 }
end
local g = groups[key]
g.bytes = g.bytes + bytes
g.packets = g.packets + pkts
g.num_flows = g.num_flows + 1
g.num_alerted = g.num_alerted + is_alerted
g.score = g.score + score
end
local sorted = {}
for k, v in pairs(groups) do
sorted[#sorted + 1] = { key = k, bytes = v.bytes, packets = v.packets,
num_flows = v.num_flows, num_alerted = v.num_alerted, score = v.score }
end
table.sort(sorted, function(a, b) return a.bytes > b.bytes end)
local ctx = host_ip and ("host=" .. host_ip .. " role=" .. role) or "all hosts"
local filter_str = alerted_only and " [alerted only]" or ""
local hdr_line = string.format(
"Summary for %s%s | group_by=%s | total_flows=%d total_alerted=%d total_bytes=%d total_score=%d",
ctx, filter_str, group_by, total.flows, total.alerted, total.bytes, total.score)
local rows = { group_by .. ",num_flows,num_alerted,bytes,packets,score" }
for _, r in ipairs(sorted) do
rows[#rows + 1] = string.format("%s,%d,%d,%d,%d,%d",
r.key, r.num_flows, r.num_alerted, r.bytes, r.packets, r.score)
end
return hdr_line .. "\n" .. table.concat(rows, "\n"), nil
end,
opts = { read_only = true }
}