mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 07:29:32 +00:00
578 lines
18 KiB
Lua
578 lines
18 KiB
Lua
--
|
|
-- (C) 2014-18 - ntop.org
|
|
--
|
|
local dirs = ntop.getDirs()
|
|
local ts_utils = require("ts_utils")
|
|
local os_utils = require("os_utils")
|
|
local tracker = require "tracker"
|
|
|
|
local delete_data_utils = {}
|
|
local dry_run = false
|
|
|
|
local ALL_INTERFACES_HASH_KEYS = "ntopng.prefs.iface_id"
|
|
local ACTIVE_INTERFACES_DELETE_HASH = "ntopng.prefs.delete_active_interfaces_data"
|
|
local PCAP_DUMP_INTERFACES_DELETE_HASH = "ntopng.prefs.delete_pcap_dump_interfaces_data"
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.status_to_i18n(err)
|
|
local map = {
|
|
ERR_NO_HOST_FS_DATA = "delete_data.msg_err_no_fs_data",
|
|
ERR_INVALID_HOST = "delete_data.msg_err_invalid_host",
|
|
ERR_TS_DELETE = "delete_data.msg_err_unable_to_delete_ts_data",
|
|
ERR_UNABLE_TO_DELETE_DIR = "delete_data.msg_err_unable_to_delete_dir",
|
|
}
|
|
|
|
return map[err] or 'delete_data.msg_err_unknown'
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_host_timeseries_data(interface_id, host_info)
|
|
local status = "OK"
|
|
local is_mac = isMacAddress(host_info["host"])
|
|
|
|
if not is_mac and not isIPv4(host_info["host"]) and not isIPv6(host_info["host"]) then
|
|
status = "ERR_INVALID_HOST"
|
|
else
|
|
local to_delete
|
|
local delete_tags
|
|
local value = hostinfo2hostkey(host_info)
|
|
|
|
if is_mac then
|
|
to_delete = "mac"
|
|
delete_tags = {ifid=interface_id, mac=value}
|
|
else
|
|
to_delete = "host"
|
|
delete_tags = {ifid=interface_id, host=value}
|
|
end
|
|
|
|
if not dry_run then
|
|
if not ts_utils.delete(to_delete, delete_tags) then
|
|
status = "ERR_TS_DELETE"
|
|
end
|
|
end
|
|
end
|
|
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
-- TODO delete HOST_V4_BY_MAC_SERIALIZED_KEY and HOST_V6_BY_MAC_SERIALIZED_KEY as well
|
|
local function delete_host_redis_keys(interface_id, host_info)
|
|
local status = "OK"
|
|
local hostkey = hostinfo2hostkey(host_info, nil, true)
|
|
local serialized_k, dns_k, devnames_k, devtypes_k, drop_k, label_k, dhcp_k
|
|
|
|
if not isMacAddress(host_info["host"]) then
|
|
-- this is an IP address, see HOST_SERIALIZED_KEY (ntop_defines.h)
|
|
serialized_k = string.format("ntopng.serialized_hosts.ifid_%u__%s@%d", interface_id, host_info["host"], host_info["vlan"] or "0")
|
|
dns_k = string.format("ntopng.dns.cache.%s", host_info["host"]) -- neither vlan nor ifid implemented for the dns cache
|
|
drop_k = "ntopng.prefs.drop_host_traffic"
|
|
label_k = "ntopng.host_labels"
|
|
else
|
|
-- is a mac address, see MAC_SERIALIED_KEY (see ntop_defines.h)
|
|
serialized_k = string.format("ntopng.serialized_macs.ifid_%u__%s", interface_id, host_info["host"])
|
|
devnames_k = string.format("ntopng.cache.devnames.%s", host_info["host"])
|
|
devtypes_k = string.format("ntopng.prefs.device_types.%s", host_info["host"])
|
|
dhcp_k = getDhcpNamesKey(interface_id)
|
|
end
|
|
|
|
if not dry_run then
|
|
if serialized_k then ntop.delCache(serialized_k) end
|
|
if devnames_k then ntop.delCache(devnames_k) end
|
|
if devtypes_k then ntop.delCache(devtypes_k) end
|
|
if dns_k then ntop.delCache(dns_k) end
|
|
if dhcp_k then ntop.delHashCache(dhcp_k, host_info["host"]) end
|
|
if drop_k then ntop.delHashCache(drop_k, hostkey) end
|
|
if label_k then ntop.delHashCache(label_k, hostkey) end
|
|
end
|
|
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_host_mysql_flows(interface_id, host_info)
|
|
local status = "OK"
|
|
|
|
if ntop.getPrefs()["is_dump_flows_to_mysql_enabled"] then
|
|
local addr = host_info["host"]
|
|
local vlan = host_info["vlan"] or 0
|
|
local q
|
|
|
|
if isIPv4(addr) then
|
|
q = string.format("DELETE FROM %s WHERE (IP_SRC_ADDR = INET_ATON('%s') OR IP_DST_ADDR = INET_ATON('%s')) AND VLAN_ID = %u and INTERFACE_ID = %u",
|
|
"flowsv4", addr, addr, vlan, interface_id)
|
|
elseif isIPv6(addr) then
|
|
q = string.format("DELETE FROM %s WHERE (IP_SRC_ADDR = '%s' OR IP_DST_ADDR = '%s') AND VLAN_ID = %u AND INTERFACE_ID = %u",
|
|
"flowsv6", addr, addr, vlan, interface_id)
|
|
end
|
|
|
|
if not dry_run and q then
|
|
interface.execSQLQuery(q)
|
|
end
|
|
end
|
|
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function _delete_host(interface_id, host_info)
|
|
local old_ifname = interface.getStats().name
|
|
local is_mac = isMacAddress(host_info["host"])
|
|
interface.select(getInterfaceName(interface_id))
|
|
|
|
if is_mac then
|
|
interface.deleteMacData(host_info["host"])
|
|
else
|
|
interface.deleteHostData(hostinfo2hostkey(host_info))
|
|
end
|
|
|
|
local h_ts = delete_host_timeseries_data(interface_id, host_info)
|
|
local h_rk = delete_host_redis_keys(interface_id, host_info)
|
|
local h_db = delete_host_mysql_flows(interface_id, host_info)
|
|
|
|
interface.select(old_ifname)
|
|
return {delete_host_timeseries_data = h_ts, delete_host_redis_keys = h_rk, delete_host_mysql_flows = h_db}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.delete_host(interface_id, host_info)
|
|
-- TRACKER HOOK
|
|
tracker.log('delete_host', { hostinfo2hostkey(host_info) })
|
|
|
|
return _delete_host(interface_id, host_info)
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.delete_network(interface_id, netaddr, mask, vlan)
|
|
mask = tonumber(mask)
|
|
|
|
-- TRACKER HOOK
|
|
tracker.log('delete_network', { hostinfo2hostkey({host=netaddr.."/"..mask, vlan=vlan}) })
|
|
|
|
if not isIPv4(netaddr) then
|
|
return {delete_network = {status = "ERR_INVALID_IPV4_NETWORK"}}
|
|
end
|
|
|
|
if (mask < 24) or (mask > 32) then
|
|
return {delete_network = {status = "ERR_NETWORK_TOO_BIG"}}
|
|
end
|
|
|
|
local prefix = ntop.networkPrefix(netaddr, tonumber(mask))
|
|
local parts = string.split(prefix, "%.")
|
|
local start = tonumber(parts[4])
|
|
|
|
local size = 1 << (32-mask)
|
|
|
|
for i=0,size-1 do
|
|
parts[4] = tostring(start + i)
|
|
|
|
local ip = table.concat(parts, ".")
|
|
local status = _delete_host(interface_id, {host=ip, vlan=vlan})
|
|
|
|
for what, what_res in pairs(status) do
|
|
if what_res["status"] ~= "OK" then
|
|
return status
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Delete network ts data
|
|
if not ts_utils.delete("subnet", {ifid=interface_id, subnet=prefix.."/"..mask}) then
|
|
return {delete_network_timeseries = {status = "ERR_TS_DELETE"}}
|
|
end
|
|
|
|
return {delete_network = {status = "OK"}}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_keys_patterns(keys_patterns, preserve_prefs)
|
|
for _, pattern in pairs(keys_patterns) do
|
|
local matching_keys = ntop.getKeysCache(pattern)
|
|
|
|
for matching_key, _ in pairs(matching_keys or {}) do
|
|
if((not preserve_prefs) or
|
|
((not starts(matching_key, "ntopng.prefs.")) and
|
|
(not starts(matching_key, "ntopng.user.")))) then
|
|
if not dry_run then
|
|
ntop.delCache(matching_key)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_system_interface_redis(preserve_prefs)
|
|
local keys_patterns = {
|
|
"ntopng.prefs.snmp_devices*",
|
|
"ntopng.prefs.system_rtt_hosts*",
|
|
}
|
|
|
|
delete_keys_patterns(keys_patterns, preserve_prefs)
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_interfaces_redis_keys(interfaces_list, preserve_prefs)
|
|
local pref_prefix = "ntopng.prefs"
|
|
local status = "OK"
|
|
|
|
for if_id, if_name in pairs(interfaces_list) do
|
|
-- let's match some patterns here (don't write an hexahustive list of keys
|
|
-- as it will become unmanageable)
|
|
|
|
local keys_patterns = {
|
|
-- examples:
|
|
-- ntopng.prefs.0.host_pools.pool_ids
|
|
-- ntopng.prefs.0.host_pools.details.0
|
|
string.format("%s.%u.*", pref_prefix, if_id),
|
|
-- examples:
|
|
-- ntopng.profiles_counters.ifid_0
|
|
-- ntopng.serialized_host_pools.ifid_0
|
|
string.format("ntopng.*ifid_%u", if_id),
|
|
-- examples:
|
|
-- ntopng.serialized_macs.ifid_0__52:54:00:3B:CB:B3
|
|
-- ntopng.serialized_hosts.ifid_0__192.168.2.131@0
|
|
string.format("*.ifid_%u_*", if_id),
|
|
-- examples:
|
|
-- ntopng.cache.engaged_alerts_cache_ifid_4_5mins
|
|
-- ntopng.cache.engaged_alerts_cache_ifid_4_min
|
|
string.format("ntopng.*_ifid_%u_*", if_id),
|
|
-- examples:
|
|
-- ntopng.prefs.ifid_0.custom_nDPI_proto_categories
|
|
-- ntopng.prefs.ifid_0.is_traffic_mirrored
|
|
string.format("*.ifid_%u.*", if_id),
|
|
-- examples:
|
|
-- ntopng.prefs.iface_2.packet_drops_alert
|
|
-- ntopng.prefs.iface_3.scaling_factor
|
|
string.format("%s.iface_%u.*", pref_prefix, if_id),
|
|
-- examples:
|
|
-- ntopng.prefs.enp1s0f0.xxx
|
|
string.format("%s.%s.*", pref_prefix, if_name),
|
|
-- examples:
|
|
-- ntopng.prefs.enp2s0f0_not_idle
|
|
string.format("%s.%s_*", pref_prefix, if_name),
|
|
}
|
|
|
|
delete_keys_patterns(keys_patterns, preserve_prefs)
|
|
|
|
if(if_id == getSystemInterfaceId()) then
|
|
delete_system_interface_redis(preserve_prefs)
|
|
end
|
|
end
|
|
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_interfaces_data(interfaces_list)
|
|
local status = "OK"
|
|
local data_dir = ntop.getDirs()["workingdir"]
|
|
|
|
for if_id, if_name in pairs(interfaces_list) do
|
|
local if_dir = os_utils.fixPath(string.format("%s/%u/", data_dir, if_id))
|
|
|
|
if not dry_run then
|
|
if not ts_utils.delete("" --[[ all schemas ]], {ifid=if_id}) then
|
|
status = "ERR_TS_DELETE"
|
|
break
|
|
end
|
|
|
|
-- Delete additional data
|
|
if ntop.exists(if_dir) and not ntop.rmdir(if_dir) then
|
|
status = "ERR_UNABLE_TO_DELETE_DIR"
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_interfaces_influx_data(interfaces_list)
|
|
local status = "OK"
|
|
-- TODO
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_interfaces_db_flows(interfaces_list)
|
|
local db_utils = require "db_utils"
|
|
local status = "OK"
|
|
local prefs = ntop.getPrefs()
|
|
|
|
for if_id, if_name in pairs(interfaces_list) do
|
|
-- this deletes MySQL
|
|
if prefs.is_dump_flows_to_mysql_enabled == true and not dry_run then
|
|
db_utils.harverstExpiredMySQLFlows(if_id, os.time() + 86400 --[[ go 1d in the future to make sure everything is deleted --]])
|
|
end
|
|
-- TODO: add delete for nIndex
|
|
end
|
|
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_interfaces_ids(interfaces_list)
|
|
local status = "OK"
|
|
|
|
for if_id, if_name in pairs(interfaces_list) do
|
|
-- delete the interface from the all interfaces hash
|
|
-- this will cause the id to be re-used
|
|
if not dry_run then
|
|
ntop.delHashCache(ALL_INTERFACES_HASH_KEYS, if_name)
|
|
ntop.delHashCache(ALL_INTERFACES_HASH_KEYS, if_id)
|
|
end
|
|
end
|
|
|
|
return {status = status}
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function list_interfaces(inactive_interfaces_only)
|
|
local res = {}
|
|
local active_interfaces = interface.getIfNames()
|
|
local all_interfaces = ntop.getHashAllCache(ALL_INTERFACES_HASH_KEYS)
|
|
|
|
for k, v in pairs(all_interfaces) do
|
|
if tonumber(k) ~= nil then
|
|
-- assumes this in an interface integer id
|
|
-- this check is necessary as function Utils::ifname2id
|
|
-- inserts in the same hash table both the ids and the interface
|
|
-- names. So for example interface eno1 with id 20 has two entries in the
|
|
-- hash table, namely eno1: 20 and 20: eno1
|
|
goto continue
|
|
end
|
|
|
|
local if_name = k
|
|
local if_id = v
|
|
|
|
if inactive_interfaces_only and active_interfaces[if_id] then
|
|
-- the interface is active
|
|
goto continue
|
|
end
|
|
|
|
-- add the interface to the list of inactive interfaces
|
|
res[if_id] = if_name
|
|
::continue::
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.list_inactive_interfaces()
|
|
return list_interfaces(true --[[ inactive interfaces only --]])
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.list_all_interfaces()
|
|
return list_interfaces(false --[[ all interfaces, active and inactive --]])
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
local function delete_interfaces_from_list(interfaces_list, preserve_interface_ids, preserve_redis_keys)
|
|
local if_dt = delete_interfaces_data(interfaces_list)
|
|
local if_db = delete_interfaces_db_flows(interfaces_list)
|
|
local preserve_prefs = ternary(ntop.isnEdge(), true, false)
|
|
|
|
local if_rk
|
|
if not preserve_redis_keys then
|
|
if_rk = delete_interfaces_redis_keys(interfaces_list, preserve_prefs)
|
|
end
|
|
|
|
-- last step is to also free the ids that can thus be recycled
|
|
-- if everything was OK.
|
|
local if_in
|
|
if not preserve_interface_ids then
|
|
if not preserve_redis_keys or if_rk["status"] == "OK" then
|
|
if if_dt["status"] == "OK" then
|
|
if if_db["status"] == "OK" then
|
|
if_in = delete_interfaces_ids(interfaces_list)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local res = {delete_if_data = if_dt, delete_if_redis_keys = if_rk, delete_if_db = if_db, delete_if_ids = if_in}
|
|
|
|
for op, op_res in pairs(res or {}) do
|
|
local trace_level = TRACE_NORMAL
|
|
local status = op_res["status"]
|
|
|
|
if status ~= "OK" then
|
|
trace_level = TRACE_ERROR
|
|
end
|
|
|
|
traceError(trace_level, TRACE_CONSOLE, string.format("Deleting data [%s][%s]", op, status))
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.delete_all_interfaces_data()
|
|
-- Deleting all interfaces can be a risky operation as it includes active interfaces.
|
|
-- Currently we are using this only in boot.lua (that is, before interfaces registration)
|
|
-- and only in nEdge. Use it carefully.
|
|
|
|
if not ntop.isnEdge() then
|
|
return
|
|
end
|
|
|
|
local if_list = delete_data_utils.list_all_interfaces()
|
|
|
|
return delete_interfaces_from_list(if_list)
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.delete_active_interfaces_data()
|
|
local if_list = ntop.getHashAllCache(ACTIVE_INTERFACES_DELETE_HASH)
|
|
|
|
return delete_interfaces_from_list(if_list, true --[[ preserve ids --]])
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.request_delete_active_interface_data(if_id)
|
|
local if_name = getInterfaceName(if_name)
|
|
ntop.setHashCache(ACTIVE_INTERFACES_DELETE_HASH, tostring(if_id), if_name)
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.clear_request_delete_active_interface_data()
|
|
ntop.delCache(ACTIVE_INTERFACES_DELETE_HASH)
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.delete_active_interface_data_requested(if_name)
|
|
if not isEmptyString(if_name) then
|
|
-- Check if a delete has been requested for a particular interface
|
|
local if_id = getInterfaceId(if_name)
|
|
|
|
if tonumber(if_id) >= 0 then
|
|
local req = ntop.getHashCache(ACTIVE_INTERFACES_DELETE_HASH, tostring(if_id))
|
|
if not isEmptyString(req) then
|
|
return true
|
|
end
|
|
end
|
|
else
|
|
-- Check if there's at least a data delete request
|
|
return (ntop.getHashAllCache(ACTIVE_INTERFACES_DELETE_HASH) ~= nil)
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.delete_pcap_dump_interfaces_data()
|
|
local if_list = ntop.getHashAllCache(PCAP_DUMP_INTERFACES_DELETE_HASH)
|
|
local res = {}
|
|
|
|
if if_list and table.len(if_list) > 0 then
|
|
res = delete_interfaces_from_list(if_list)
|
|
|
|
ntop.delCache(PCAP_DUMP_INTERFACES_DELETE_HASH)
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
function delete_data_utils.delete_inactive_interfaces()
|
|
delete_data_utils.delete_pcap_dump_interfaces_data()
|
|
|
|
local inactive_if_list = delete_data_utils.list_inactive_interfaces()
|
|
|
|
local res = delete_interfaces_from_list(inactive_if_list)
|
|
|
|
for if_id, _ in pairs(inactive_if_list) do
|
|
ntop.delHashCache(PCAP_DUMP_INTERFACES_DELETE_HASH, if_id)
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
-- NOTE: this has 1 day accuracy
|
|
function delete_data_utils.harvestDateBasedDirTree(dir, retention, now)
|
|
for year in pairs(ntop.readdir(dir) or {}) do
|
|
local year_path = os_utils.fixPath(dir .. "/" .. year)
|
|
local num_deleted_months = 0
|
|
local tot_months = 0
|
|
|
|
for month in pairs(ntop.readdir(year_path) or {}) do
|
|
local month_path = os_utils.fixPath(year_path .. "/" .. month)
|
|
local num_deleted_days = 0
|
|
local tot_days = 0
|
|
|
|
for day in pairs(ntop.readdir(month_path) or {}) do
|
|
if(tonumber(day) ~= nil) then
|
|
local tstamp = os.time({day=tonumber(day), month=tonumber(month), year=tonumber(year), hour=0, min=0, sec=0})
|
|
local days_diff = (now - tstamp) / 86400
|
|
|
|
if(days_diff > retention) then
|
|
local day_path = os_utils.fixPath(month_path .. "/" .. day)
|
|
--tprint(os.date('PURGE %Y-%m-%d %H:%M:%S', tstamp))
|
|
ntop.rmdir(day_path)
|
|
num_deleted_days = num_deleted_days + 1
|
|
end
|
|
|
|
tot_days = tot_days + 1
|
|
end
|
|
end
|
|
|
|
if num_deleted_days == tot_days then
|
|
--tprint("PURGE month: ".. month .."/" .. year)
|
|
ntop.rmdir(month_path)
|
|
num_deleted_months = num_deleted_months + 1
|
|
end
|
|
|
|
tot_months = tot_months + 1
|
|
end
|
|
|
|
if num_deleted_months == tot_months then
|
|
--tprint("PURGE year " .. year)
|
|
ntop.rmdir(year_path)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ################################################################
|
|
|
|
-- TRACKER HOOK
|
|
|
|
tracker.track(delete_data_utils, 'delete_all_interfaces_data')
|
|
tracker.track(delete_data_utils, 'delete_inactive_interfaces')
|
|
tracker.track(delete_data_utils, 'request_delete_active_interface_data')
|
|
|
|
-- ################################################################
|
|
|
|
return delete_data_utils
|
|
|
|
|