-- -- (C) 2013-26 - ntop.org -- local dirs = ntop.getDirs() package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path require "lua_utils" local json = require("dkjson") local interface_utils = {} function interface_utils.get_pingable_interfaces() local res = {} local interfaces = ntop.getPingIfNames() for id, ifname in pairs(interfaces) do local custom_name = getHumanReadableInterfaceName(ifname) if isEmptyString(custom_name) then custom_name = nil end -- Note: returning in a format compatible with /lua/rest/v2/get/ntopng/interfaces.lua res[#res + 1] = { ifid = tonumber(id), ifname = ifname, name = custom_name or ifname, is_packet_interface = true, is_pcap_interface = false, is_zmq_interface = false, } end return res end -- ############################################## local function override_stats(stats, overr_stats) -- override stats with the values calculated from the latest user reset for k, v in pairs(overr_stats or {}) do stats[k] = v end return stats end -- ############################################## -- Lightview mode has no backend support, merging stats from all interfaces here. -- Used by if_stats.lua and rest/v2/get/interface/lightview/data.lua -- Returns ifstats and info with: -- ifaces: list of interfaces -- probes_stats: nProbe instances -- drops: dropped flows (collector) or packets -- update_freq: min stats update frequency (sec) of interfaces function interface_utils.getLightViewStats(ifstats) local prev_ifid = interface.getId() local info = { ifaces = {}, probes_stats = {}, drops = 0, update_freq = nil } local zmq_stats = {} local exporters_stats = {} local agg_stats = { bytes = 0, packets = 0, drops = 0, num_deduplicated_flows = 0 } local agg_discarded_probing = nil local agg_db_stats = { flows = 0, dropped_flows = 0 } local agg_export_stats = {} local agg_anomalies = { num_local_hosts_anomalies = 0, num_remote_hosts_anomalies = 0, tot_num_anomalies = { local_hosts = 0, remote_hosts = 0 } } local agg_traffic = { tx = 0, rx = 0, tx_pkts = 0, rx_pkts = 0 } local has_traffic_directions = false local agg_alerts = { engaged = 0, dropped = 0 } local agg_tcp = { retransmissions = 0, out_of_order = 0, lost = 0 } local agg_eth = { ingress = { bytes = 0, packets = 0 }, egress = { bytes = 0, packets = 0 } } local agg_remote = { bps = 0, pps = 0 } local agg_zmq_counters = { ["zmq.num_flow_exports"] = 0, ["zmq.drops.export_queue_full"] = 0, ["zmq.drops.flow_collection_drops"] = 0, ["zmq.drops.flow_collection_udp_socket_drops"] = 0 } local exporters_drops = 0 local packet_drops = 0 for interface_name, _ in pairsByKeys(interface.getIfNames() or {}) do interface.select(interface_name) local tmp = interface.getStats() local is_collector = (tmp.zmqRecvStats ~= nil and table.len(tmp.zmqRecvStats) > 0) info.ifaces[#info.ifaces + 1] = { id = interface.getId(), name = getHumanReadableInterfaceName(interface_name) } local freq = interface.getStatsUpdateFreq(tmp.id) if freq and (not info.update_freq or freq < info.update_freq) then info.update_freq = freq end if tmp.stats and tmp.stats_since_reset then tmp.stats = override_stats(tmp.stats, tmp.stats_since_reset) end if tmp.zmqRecvStats and tmp.zmqRecvStats_since_reset then tmp.zmqRecvStats = override_stats(tmp.zmqRecvStats, tmp.zmqRecvStats_since_reset) end for k, v in pairs(tmp.probes or {}) do info.probes_stats[k] = v end for k, v in pairs(tmp.exporters or {}) do if not exporters_stats[k] then exporters_stats[k] = {} end for key_stat, value_stat in pairs(v) do exporters_stats[k][key_stat] = value_stat + (exporters_stats[k][key_stat] or 0) end exporters_drops = exporters_drops + (v["num_drops"] or 0) end for k, v in pairs(tmp.zmqRecvStats or {}) do zmq_stats[k] = (zmq_stats[k] or 0) + v end if tmp.stats then agg_stats.bytes = agg_stats.bytes + (tmp.stats.bytes or 0) agg_stats.packets = agg_stats.packets + (tmp.stats.packets or 0) agg_stats.drops = agg_stats.drops + (tmp.stats.drops or 0) agg_stats.num_deduplicated_flows = agg_stats.num_deduplicated_flows + (tmp.stats.num_deduplicated_flows or 0) if not is_collector then packet_drops = packet_drops + (tmp.stats.drops or 0) end if tmp.stats.discarded_probing_packets then agg_discarded_probing = agg_discarded_probing or { packets = 0, bytes = 0 } agg_discarded_probing.packets = agg_discarded_probing.packets + (tmp.stats.discarded_probing_packets or 0) agg_discarded_probing.bytes = agg_discarded_probing.bytes + (tmp.stats.discarded_probing_bytes or 0) end end if tmp.dbStats then agg_db_stats.flows = agg_db_stats.flows + (tmp.dbStats.flows or 0) agg_db_stats.dropped_flows = agg_db_stats.dropped_flows + (tmp.dbStats.dropped_flows or 0) end for _, db_type in ipairs({ "db", "es", "kafka", "syslog" }) do local s = tmp.stats_since_reset and tmp.stats_since_reset[db_type] if s then agg_export_stats[db_type] = agg_export_stats[db_type] or { flow_export_count = 0, flow_export_rate = 0, flow_export_drops = 0 } agg_export_stats[db_type].flow_export_count = agg_export_stats[db_type].flow_export_count + (s.flow_export_count or 0) agg_export_stats[db_type].flow_export_rate = agg_export_stats[db_type].flow_export_rate + (s.flow_export_rate or 0) agg_export_stats[db_type].flow_export_drops = agg_export_stats[db_type].flow_export_drops + (s.flow_export_drops or 0) end end if tmp.anomalies then agg_anomalies.num_local_hosts_anomalies = agg_anomalies.num_local_hosts_anomalies + (tmp.anomalies.num_local_hosts_anomalies or 0) agg_anomalies.num_remote_hosts_anomalies = agg_anomalies.num_remote_hosts_anomalies + (tmp.anomalies.num_remote_hosts_anomalies or 0) local an = tmp.anomalies.tot_num_anomalies or {} agg_anomalies.tot_num_anomalies.local_hosts = agg_anomalies.tot_num_anomalies.local_hosts + (an.local_hosts or 0) agg_anomalies.tot_num_anomalies.remote_hosts = agg_anomalies.tot_num_anomalies.remote_hosts + (an.remote_hosts or 0) end if tmp.has_traffic_directions then has_traffic_directions = true agg_traffic.tx = agg_traffic.tx + (tmp.traffic_sent_since_reset or 0) agg_traffic.rx = agg_traffic.rx + (tmp.traffic_rcvd_since_reset or 0) agg_traffic.tx_pkts = agg_traffic.tx_pkts + (tmp.packets_sent_since_reset or 0) agg_traffic.rx_pkts = agg_traffic.rx_pkts + (tmp.packets_rcvd_since_reset or 0) end agg_alerts.engaged = agg_alerts.engaged + (tmp.num_alerts_engaged or 0) agg_alerts.dropped = agg_alerts.dropped + (tmp.num_dropped_alerts or 0) if tmp.tcpPacketStats then agg_tcp.retransmissions = agg_tcp.retransmissions + (tmp.tcpPacketStats.retransmissions or 0) agg_tcp.out_of_order = agg_tcp.out_of_order + (tmp.tcpPacketStats.out_of_order or 0) agg_tcp.lost = agg_tcp.lost + (tmp.tcpPacketStats.lost or 0) end if tmp.eth then for _, dir in ipairs({ "ingress", "egress" }) do if tmp.eth[dir] then agg_eth[dir].bytes = agg_eth[dir].bytes + (tmp.eth[dir].bytes or 0) agg_eth[dir].packets = agg_eth[dir].packets + (tmp.eth[dir].packets or 0) end end end agg_remote.bps = agg_remote.bps + (tonumber(tmp.remote_bps) or 0) agg_remote.pps = agg_remote.pps + (tonumber(tmp.remote_pps) or 0) for k, _ in pairs(agg_zmq_counters) do agg_zmq_counters[k] = agg_zmq_counters[k] + (tonumber(tmp[k]) or 0) end end interface.select(tostring(prev_ifid)) ifstats.zmqRecvStats = zmq_stats ifstats.exporters = exporters_stats ifstats.probes = info.probes_stats ifstats.stats.bytes = agg_stats.bytes ifstats.stats.packets = agg_stats.packets ifstats.stats.drops = agg_stats.drops ifstats.stats.num_deduplicated_flows = agg_stats.num_deduplicated_flows ifstats.stats_since_reset.drops = agg_stats.drops ifstats.stats.discarded_probing_packets = agg_discarded_probing and agg_discarded_probing.packets or nil ifstats.stats.discarded_probing_bytes = agg_discarded_probing and agg_discarded_probing.bytes or nil if agg_db_stats.flows > 0 or agg_db_stats.dropped_flows > 0 then ifstats.dbStats = agg_db_stats end for db_type, s in pairs(agg_export_stats) do ifstats.stats_since_reset[db_type] = s end ifstats.anomalies = agg_anomalies ifstats.has_traffic_directions = has_traffic_directions ifstats.traffic_sent_since_reset = agg_traffic.tx ifstats.traffic_rcvd_since_reset = agg_traffic.rx ifstats.packets_sent_since_reset = agg_traffic.tx_pkts ifstats.packets_rcvd_since_reset = agg_traffic.rx_pkts ifstats.num_alerts_engaged = agg_alerts.engaged ifstats.num_dropped_alerts = agg_alerts.dropped ifstats.tcpPacketStats = agg_tcp ifstats.eth = ifstats.eth or {} ifstats.eth.ingress = ifstats.eth.ingress or {} ifstats.eth.egress = ifstats.eth.egress or {} ifstats.eth.ingress.bytes = agg_eth.ingress.bytes ifstats.eth.ingress.packets = agg_eth.ingress.packets ifstats.eth.egress.bytes = agg_eth.egress.bytes ifstats.eth.egress.packets = agg_eth.egress.packets ifstats.remote_bps = agg_remote.bps ifstats.remote_pps = agg_remote.pps for k, v in pairs(agg_zmq_counters) do ifstats[k] = v end -- flows when collecting, packets otherwise if table.len(zmq_stats) > 0 then info.drops = exporters_drops else info.drops = packet_drops end return ifstats, info end -- ############################################## return interface_utils