From b4b9253472d933f530dca5f5157c74a6862416dd Mon Sep 17 00:00:00 2001 From: Matteo Biscosi Date: Thu, 18 Jul 2024 10:24:14 +0200 Subject: [PATCH] Fixes flow exporters issues --- include/NetworkInterface.h | 3 +- include/ViewInterface.h | 3 +- include/ZMQParserInterface.h | 10 +- include/nProbeStats.h | 63 + include/ntop_includes.h | 1 + include/ntop_typedefs.h | 28 - scripts/lua/modules/ntop_utils.lua | 1057 ++++++++--------- .../rest/v2/get/interface/nprobes/data.lua | 97 +- src/LuaEngineInterface.cpp | 2 +- src/ViewInterface.cpp | 18 +- src/ZMQParserInterface.cpp | 126 +- src/nProbeStats.cpp | 35 + 12 files changed, 750 insertions(+), 693 deletions(-) create mode 100644 include/nProbeStats.h create mode 100644 src/nProbeStats.cpp diff --git a/include/NetworkInterface.h b/include/NetworkInterface.h index 7c118812906e..52b094fc038d 100644 --- a/include/NetworkInterface.h +++ b/include/NetworkInterface.h @@ -734,6 +734,7 @@ class NetworkInterface : public NetworkInterfaceAlertableEntity { virtual u_int32_t getFlowMaxIdle(); virtual void lua(lua_State *vm, bool fullStats); + virtual void probeLuaStats(lua_State *vm) { }; void luaScore(lua_State *vm); void luaAlertedFlows(lua_State *vm); void luaAnomalies(lua_State *vm); @@ -995,7 +996,7 @@ class NetworkInterface : public NetworkInterfaceAlertableEntity { #ifdef NTOPNG_PRO void updateBehaviorStats(const struct timeval *tv); - virtual void getFlowDevices(lua_State *vm, bool add_table); + virtual void getFlowDevices(lua_State *vm); virtual void getFlowDeviceInfo(lua_State *vm, u_int32_t deviceIP, bool showAllStats = true) { if (flow_interfaces_stats) { lua_newtable(vm); diff --git a/include/ViewInterface.h b/include/ViewInterface.h index 44a68dc1d200..50122b3bcfc6 100644 --- a/include/ViewInterface.h +++ b/include/ViewInterface.h @@ -92,11 +92,12 @@ class ViewInterface : public NetworkInterface { virtual bool hasSeenVLANTaggedPackets() const; #ifdef NTOPNG_PRO - virtual void getFlowDevices(lua_State *vm, bool add_table); + virtual void getFlowDevices(lua_State *vm); virtual void getFlowDeviceInfo(lua_State *vm, u_int32_t deviceIP, bool showAllStats); #endif virtual void getSFlowDevices(lua_State *vm, bool add_table); virtual void getSFlowDeviceInfo(lua_State *vm, u_int32_t deviceIP); + virtual void lua(lua_State *vm, bool fullStats); virtual u_int32_t getFlowsHashSize(); virtual Flow *findFlowByKeyAndHashId(u_int32_t key, u_int hash_id, diff --git a/include/ZMQParserInterface.h b/include/ZMQParserInterface.h index 13f5d277fd5d..4599cafa5f32 100644 --- a/include/ZMQParserInterface.h +++ b/include/ZMQParserInterface.h @@ -30,7 +30,6 @@ class ZMQParserInterface : public ParserInterface { typedef std::map labels_map_t; typedef std::map descriptions_map_t; typedef std::map counters_map_t; - std::unordered_map exporters_stats; std::unordered_map cloud_flow_exporters; u_int16_t top_vlan_id; std::unordered_map name_to_vlan; @@ -45,8 +44,8 @@ class ZMQParserInterface : public ParserInterface { u_int32_t flow_max_idle, returned_flow_max_idle; u_int64_t zmq_initial_bytes, zmq_initial_pkts, zmq_remote_initial_exported_flows; - std::map source_id_last_zmq_remote_stats; - ZMQ_RemoteStats *zmq_remote_stats, *zmq_remote_stats_shadow; + std::map source_id_last_zmq_remote_stats; + nProbeStats *zmq_remote_stats, *zmq_remote_stats_shadow; u_int32_t remote_lifetime_timeout, remote_idle_timeout; struct timeval last_zmq_remote_stats_update; RwLock lock; @@ -54,7 +53,7 @@ class ZMQParserInterface : public ParserInterface { CustomAppMaps *custom_app_maps; #endif - void exporterLuaStats(lua_State *vm); + void exporterLuaStats(lua_State *vm, nProbeStats *zrs); void loadVLANMappings(); u_int16_t findVLANMapping(std::string name); @@ -131,7 +130,7 @@ class ZMQParserInterface : public ParserInterface { u_int32_t source_id, u_int32_t msg_id, void *data); u_int32_t periodicStatsUpdateFrequency() const; - virtual void setRemoteStats(ZMQ_RemoteStats *zrs); + virtual void setRemoteStats(nProbeStats *zrs); #ifdef NTOPNG_PRO virtual bool getCustomAppDetails(u_int32_t remapped_app_id, u_int32_t *const pen, @@ -142,6 +141,7 @@ class ZMQParserInterface : public ParserInterface { return zmq_remote_stats ? zmq_remote_stats->sflow_pkt_sample_drops : 0; }; virtual void lua(lua_State *vm, bool fullStats); + virtual void probeLuaStats(lua_State *vm); inline u_int32_t getFlowMaxIdle() { return (returned_flow_max_idle); } }; diff --git a/include/nProbeStats.h b/include/nProbeStats.h new file mode 100644 index 000000000000..3caf9fac1a94 --- /dev/null +++ b/include/nProbeStats.h @@ -0,0 +1,63 @@ +/* + * + * (C) 2013-24 - ntop.org + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _NPROBE_STATS_H_ +#define _NPROBE_STATS_H_ + +#include "ntop_includes.h" + +typedef struct { + u_int64_t nf_ipfix_flows; + u_int64_t sflow_samples; +} FlowCollection; + +typedef struct { + time_t time_last_used; + u_int32_t num_sflow_flows; + u_int32_t num_netflow_flows; + u_int32_t num_drops; + u_int32_t unique_source_id; +} ExporterStats; + +class nProbeStats { + public: + char remote_ifname[32], remote_ifaddress[64]; + char remote_probe_address[64], remote_probe_public_address[64], uuid[36]; + char remote_probe_version[64], remote_probe_os[64]; + char remote_probe_license[64], remote_probe_edition[64]; + char remote_probe_maintenance[64]; + u_int32_t source_id, uuid_num, num_exporters; + u_int64_t remote_bytes, remote_pkts, num_flow_exports; + u_int32_t remote_ifspeed, remote_time, local_time, avg_bps, avg_pps; + u_int32_t remote_lifetime_timeout, remote_idle_timeout, + remote_collected_lifetime_timeout; + u_int32_t export_queue_full, too_many_flows, elk_flow_drops, + sflow_pkt_sample_drops, flow_collection_drops, + flow_collection_udp_socket_drops; + FlowCollection flow_collection; + + std::map exportersStats; + public: + nProbeStats(); + ~nProbeStats() {}; +}; + +#endif /* _NPROBE_STATS_H_ */ diff --git a/include/ntop_includes.h b/include/ntop_includes.h index d4c52a26390b..f3804ee47dc7 100644 --- a/include/ntop_includes.h +++ b/include/ntop_includes.h @@ -225,6 +225,7 @@ using namespace std; #include "Cardinality.h" #include "PeerStats.h" #include "IpAddress.h" +#include "nProbeStats.h" #include "Ping.h" #include "JobQueue.h" #include "ContinuousPingStats.h" diff --git a/include/ntop_typedefs.h b/include/ntop_typedefs.h index 646c991a03a7..f922d19066c7 100644 --- a/include/ntop_typedefs.h +++ b/include/ntop_typedefs.h @@ -371,26 +371,6 @@ PACK_ON struct l2tp_header { /* IMPORTANT: whenever the Parsed_FlowSerial is changed, nProbe must be updated * too */ -typedef struct zmq_remote_stats { - char remote_ifname[32], remote_ifaddress[64]; - char remote_probe_address[64], remote_probe_public_address[64], uuid[36]; - char remote_probe_version[64], remote_probe_os[64]; - char remote_probe_license[64], remote_probe_edition[64]; - char remote_probe_maintenance[64]; - u_int32_t source_id, uuid_num, num_exporters; - u_int64_t remote_bytes, remote_pkts, num_flow_exports; - u_int32_t remote_ifspeed, remote_time, local_time, avg_bps, avg_pps; - u_int32_t remote_lifetime_timeout, remote_idle_timeout, - remote_collected_lifetime_timeout; - u_int32_t export_queue_full, too_many_flows, elk_flow_drops, - sflow_pkt_sample_drops, flow_collection_drops, - flow_collection_udp_socket_drops; - struct { - u_int64_t nf_ipfix_flows; - u_int64_t sflow_samples; - } flow_collection; -} ZMQ_RemoteStats; - typedef struct zmq_template { u_int32_t pen, field; const char *format, *name, *descr; @@ -1216,13 +1196,5 @@ typedef enum { ESTABLISHED, CLOSED = 3 } MajorConnectionStates; - -typedef struct { - time_t time_last_used; - u_int32_t num_sflow_flows; - u_int32_t num_netflow_flows; - u_int32_t num_drops; - u_int32_t unique_source_id; -} ExporterStats; #endif /* _NTOP_TYPEDEFS_H_ */ diff --git a/scripts/lua/modules/ntop_utils.lua b/scripts/lua/modules/ntop_utils.lua index 1670a1a5c3a8..4ba74075cfa2 100644 --- a/scripts/lua/modules/ntop_utils.lua +++ b/scripts/lua/modules/ntop_utils.lua @@ -2,13 +2,11 @@ -- (C) 2021-24 - ntop.org -- -- This file contains a small set of utility functions - -- ############################################### - -if(pragma_once_ntop_utils == true) then - -- io.write(debug.traceback().."\n") - -- avoid multiple inclusions - return +if (pragma_once_ntop_utils == true) then + -- io.write(debug.traceback().."\n") + -- avoid multiple inclusions + return end pragma_once_ntop_utils = true @@ -22,45 +20,45 @@ local clock_start = os.clock() -- split function split(s, delimiter) - result = {}; - if(s ~= nil) then - if delimiter == nil then - -- No delimiter, split all characters - for match in s:gmatch"." do - table.insert(result, match); - end - else - -- Split by delimiter - for match in (s..delimiter):gmatch("(.-)"..delimiter) do - table.insert(result, match); - end - end - end - return result; + result = {}; + if (s ~= nil) then + if delimiter == nil then + -- No delimiter, split all characters + for match in s:gmatch "." do + table.insert(result, match); + end + else + -- Split by delimiter + for match in (s .. delimiter):gmatch("(.-)" .. delimiter) do + table.insert(result, match); + end + end + end + return result; end -- ############################################## function ends(String, End) - return End == '' or string.sub(String, -string.len(End)) == End + return End == '' or string.sub(String, -string.len(End)) == End end -- ############################################## function string.starts(String, Start) - if type(String) ~= 'string' or type(Start) ~= 'string' then - return false - end - return string.sub(String, 1, string.len(Start)) == Start + if type(String) ~= 'string' or type(Start) ~= 'string' then + return false + end + return string.sub(String, 1, string.len(Start)) == Start end -- ############################################## function string.ends(String, End) - if type(String) ~= 'string' or type(End) ~= 'string' then - return false - end - return End == '' or string.sub(String, -string.len(End)) == End + if type(String) ~= 'string' or type(End) ~= 'string' then + return false + end + return End == '' or string.sub(String, -string.len(End)) == End end -- ############################################## @@ -70,21 +68,21 @@ end -- @param oid_string_start A string-encoded dotted-decimal SNMP OID prefix -- @return True if `oid_string` starts with `oid_string_start` or false otherwise function string.oid_starts(oid_string, oid_string_start) - if type(oid_string) ~= 'string' or type(oid_string_start) ~= 'string' then - return false - end + if type(oid_string) ~= 'string' or type(oid_string_start) ~= 'string' then + return false + end - -- Make sure both OIDs end with a dot, to avoid - -- considering 1.3.6.1.4.1.99 starting with 1.3.6.1.4.1.9 - if not string.ends(oid_string, ".") then - oid_string = oid_string .. "." - end + -- Make sure both OIDs end with a dot, to avoid + -- considering 1.3.6.1.4.1.99 starting with 1.3.6.1.4.1.9 + if not string.ends(oid_string, ".") then + oid_string = oid_string .. "." + end - if not string.ends(oid_string_start, ".") then - oid_string_start = oid_string_start .. "." - end + if not string.ends(oid_string_start, ".") then + oid_string_start = oid_string_start .. "." + end - return string.sub(oid_string, 1, string.len(oid_string_start)) == oid_string_start + return string.sub(oid_string, 1, string.len(oid_string_start)) == oid_string_start end -- ############################################## @@ -93,46 +91,46 @@ end -- You can call it as tprint(mytable) -- The other two parameters should not be set function tprint(s, l, i) - -- io.write(debug.traceback().."\n") - - l = (l) or 1000; - i = i or ""; -- default item limit, indent string - if (l < 1) then - io.write("ERROR: Item limit reached.\n"); - return l - 1 - end - local ts = type(s); - if (ts ~= "table") then - io.write(i .. ' ' .. ts .. ' ' .. tostring(s) .. '\n'); - return l - 1 - end - io.write(i .. ' ' .. ts .. '\n'); - for k, v in pairs(s) do - local indent = "" - - if (i ~= "") then - indent = i .. "." - end - indent = indent .. tostring(k) - - l = tprint(v, l, indent); - if (l < 0) then - break - end - end - - return l + -- io.write(debug.traceback().."\n") + + l = (l) or 1000; + i = i or ""; -- default item limit, indent string + if (l < 1) then + io.write("ERROR: Item limit reached.\n"); + return l - 1 + end + local ts = type(s); + if (ts ~= "table") then + io.write(i .. ' ' .. ts .. ' ' .. tostring(s) .. '\n'); + return l - 1 + end + io.write(i .. ' ' .. ts .. '\n'); + for k, v in pairs(s) do + local indent = "" + + if (i ~= "") then + indent = i .. "." + end + indent = indent .. tostring(k) + + l = tprint(v, l, indent); + if (l < 0) then + break + end + end + + return l end -- ############################################## function splitNetworkPrefix(net) - if not net then - tprint(debug.traceback()) - end - local prefix = tonumber(net:match("/(.+)")) - local address = net:gsub("/.+","") - return address, prefix + if not net then + tprint(debug.traceback()) + end + local prefix = tonumber(net:match("/(.+)")) + local address = net:gsub("/.+", "") + return address, prefix end -- ############################################## @@ -148,109 +146,109 @@ end -- value_quote: a string to be used to quote values -- function table.tconcat(keys_values, kv_sep, group_sep, last_sep, value_quote) - local groups = {} - kv_sep = kv_sep or "" - group_sep = group_sep or "" - last_sep = last_sep or "" - value_quote = value_quote or "" + local groups = {} + kv_sep = kv_sep or "" + group_sep = group_sep or "" + last_sep = last_sep or "" + value_quote = value_quote or "" - for k, v in pairs(keys_values) do - local parts = {k, kv_sep, value_quote, v, value_quote} - groups[#groups + 1] = table.concat(parts, "") - end + for k, v in pairs(keys_values) do + local parts = {k, kv_sep, value_quote, v, value_quote} + groups[#groups + 1] = table.concat(parts, "") + end - if #groups > 0 then - return table.concat(groups, group_sep) .. last_sep - else - return "" - end + if #groups > 0 then + return table.concat(groups, group_sep) .. last_sep + else + return "" + end end -- ############################################## -- NOTE: on index based tables using #table is much more performant function table.len(tbl) - local count = 0 + local count = 0 - if tbl == nil then - -- io.write("ERROR: table expected, got nil\n") - -- io.write(debug.traceback().."\n") - return 0 - end + if tbl == nil then + -- io.write("ERROR: table expected, got nil\n") + -- io.write(debug.traceback().."\n") + return 0 + end - if type(tbl) ~= "table" then - io.write("ERROR: table expected, got " .. type(tbl) .. "\n") - io.write(debug.traceback() .. "\n") - return 0 - end + if type(tbl) ~= "table" then + io.write("ERROR: table expected, got " .. type(tbl) .. "\n") + io.write(debug.traceback() .. "\n") + return 0 + end - for k, v in pairs(tbl) do - count = count + 1 - end + for k, v in pairs(tbl) do + count = count + 1 + end - return count + return count end -- ############################################## function table.slice(tbl, first, last, step) - local sliced = {} + local sliced = {} - for i = first or 1, last or #tbl, step or 1 do - sliced[#sliced + 1] = tbl[i] - end + for i = first or 1, last or #tbl, step or 1 do + sliced[#sliced + 1] = tbl[i] + end - return sliced + return sliced end -- ############################################## function table.empty(tbl) - if (tbl == nil) then - return true - end + if (tbl == nil) then + return true + end - if next(tbl) == nil then - return true - end + if next(tbl) == nil then + return true + end - return false + return false end -- ############################################## function isIPv4(address) - if(address == nil) then - return false - end + if (address == nil) then + return false + end - -- Reuse the for loop to check the address validity - local checkAddress = (function(chunks) - for _, v in pairs(chunks) do + -- Reuse the for loop to check the address validity + local checkAddress = (function(chunks) + for _, v in pairs(chunks) do if (tonumber(v) < 0) or (tonumber(v) > 255) then - return false + return false end - end - return true - end) + end + return true + end) - local chunks = {address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")} - local chunksWithPort = {address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)%:(%d+)$")} + local chunks = {address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")} + local chunksWithPort = {address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)%:(%d+)$")} - if #chunks == 4 then - return checkAddress(chunks) - elseif #chunksWithPort == 5 then - table.remove(chunksWithPort, 5) - return checkAddress(chunksWithPort) - end + if #chunks == 4 then + return checkAddress(chunks) + elseif #chunksWithPort == 5 then + table.remove(chunksWithPort, 5) + return checkAddress(chunksWithPort) + end - return false + return false end -- ############################################## function isIPv6(ip) - return ((not isEmptyString(ip)) and ntop.isIPv6(ip)) + return ((not isEmptyString(ip)) and ntop.isIPv6(ip)) end -- ############################################## @@ -258,31 +256,31 @@ end -- Check if address is a CIDR -- strict (optional) do not accept subnets without the '/' function isIPv4Network(address, strict) - -- Check for @ VLAN - local parts = split(address, "@") - if #parts == 2 then - address = parts[1] - end + -- Check for @ VLAN + local parts = split(address, "@") + if #parts == 2 then + address = parts[1] + end - -- Parse CIDR - parts = split(address, "/") - if #parts == 2 then - local prefix = tonumber(parts[2]) + -- Parse CIDR + parts = split(address, "/") + if #parts == 2 then + local prefix = tonumber(parts[2]) - if (prefix == nil) or (math.floor(prefix) ~= prefix) or (prefix < 0) or (prefix > 32) then - return false - end + if (prefix == nil) or (math.floor(prefix) ~= prefix) or (prefix < 0) or (prefix > 32) then + return false + end - elseif #parts == 1 and strict then - return false + elseif #parts == 1 and strict then + return false - -- Check empty - elseif #parts ~= 1 then - return false - end + -- Check empty + elseif #parts ~= 1 then + return false + end - -- Check IP - return isIPv4(parts[1]) + -- Check IP + return isIPv4(parts[1]) end -- ############################################## @@ -290,86 +288,86 @@ end -- Check if address is a CIDR -- strict (optional) do not accept subnets without the '/' function isIPv6Network(address, strict) - -- Check for @ VLAN - local parts = split(address, "@") - if #parts == 2 then - address = parts[1] - end + -- Check for @ VLAN + local parts = split(address, "@") + if #parts == 2 then + address = parts[1] + end - -- Parse CIDR - parts = split(address, "/") - if #parts == 2 then - local prefix = tonumber(parts[2]) + -- Parse CIDR + parts = split(address, "/") + if #parts == 2 then + local prefix = tonumber(parts[2]) - if (prefix == nil) or (math.floor(prefix) ~= prefix) or (prefix < 0) or (prefix > 128) then - return false - end + if (prefix == nil) or (math.floor(prefix) ~= prefix) or (prefix < 0) or (prefix > 128) then + return false + end - elseif #parts == 1 and strict then - return false + elseif #parts == 1 and strict then + return false - -- Check empty - elseif #parts ~= 1 then - return false - end + -- Check empty + elseif #parts ~= 1 then + return false + end - -- Check IPv6 - return isIPv6(parts[1]) + -- Check IPv6 + return isIPv6(parts[1]) end -- ############################################## function firstToUpper(str) - str = tostring(str) - return (str:gsub("^%l", string.upper)) + str = tostring(str) + return (str:gsub("^%l", string.upper)) end -- ############################################## function pairsByKeys(t, f) - local a = {} - if t == nil then - io.write(debug.traceback() .. "\n") - end - for n in pairs(t) do - table.insert(a, n) - end - table.sort(a, f) - local i = 0 -- iterator variable - local iter = function() -- iterator function - i = i + 1 - if a[i] == nil then - return nil - else - return a[i], t[a[i]] - end - end - return iter + local a = {} + if t == nil then + io.write(debug.traceback() .. "\n") + end + for n in pairs(t) do + table.insert(a, n) + end + table.sort(a, f) + local i = 0 -- iterator variable + local iter = function() -- iterator function + i = i + 1 + if a[i] == nil then + return nil + else + return a[i], t[a[i]] + end + end + return iter end -- ############################################## function pairsByValues(t, f) - local a = {} - if t == nil then - io.write(debug.traceback() .. "\n") - end - for n in pairs(t) do - table.insert(a, n) - end - table.sort(a, function(x, y) - return f(t[x], t[y]) - end) - local i = 0 -- iterator variable - local iter = function() -- iterator function - i = i + 1 - if a[i] == nil then - return nil - else - return a[i], t[a[i]] - end - end - return iter + local a = {} + if t == nil then + io.write(debug.traceback() .. "\n") + end + for n in pairs(t) do + table.insert(a, n) + end + table.sort(a, function(x, y) + return f(t[x], t[y]) + end) + local i = 0 -- iterator variable + local iter = function() -- iterator function + i = i + 1 + if a[i] == nil then + return nil + else + return a[i], t[a[i]] + end + end + return iter end -- ############################################## @@ -380,307 +378,307 @@ end -- @param f The sort function, either `asc` or `rev` -- @return An iterator function pairsByDottedDecimalKeys(t, f) - local sorter = {} - - -- Build a support array for the actual sorting - for key, value in pairs(t) do - local key_sorter = key:split("%.") or {key} -- An array that will be used to sort - local splitted = key_sorter[#key_sorter]:split("@") or {} - -- This example handles the VLAN, if no VLAN is present, add 0, in case - -- a comparison between an host with VLAN and one without is performed - key_sorter[#key_sorter] = splitted[1] - key_sorter[#key_sorter + 1] = splitted[2] or 0 - - sorter[#sorter + 1] = { - sorter = key_sorter, - key = key, -- Original key - value = value -- Original value - } - end - - table.sort(sorter, function(left, right) - -- The minimum of the two lengths, used to to the comparisons - local len = math.min(#left.sorter, #right.sorter) - - for i = 1, len do - -- Convert elements to numbers - local left_number, right_number = tonumber(left.sorter[i]), tonumber(right.sorter[i]) - - if left_number ~= right_number then - -- If numbers are different, compare them using the sort function - return f(left_number, right_number) - elseif i == len then - -- This is the lat time we do the comparison: - -- When lengths are not equal, legths are used at tie breaker - return f(#left.sorter, #right.sorter) - end - end - end) + local sorter = {} + + -- Build a support array for the actual sorting + for key, value in pairs(t) do + local key_sorter = key:split("%.") or {key} -- An array that will be used to sort + local splitted = key_sorter[#key_sorter]:split("@") or {} + -- This example handles the VLAN, if no VLAN is present, add 0, in case + -- a comparison between an host with VLAN and one without is performed + key_sorter[#key_sorter] = splitted[1] + key_sorter[#key_sorter + 1] = splitted[2] or 0 + + sorter[#sorter + 1] = { + sorter = key_sorter, + key = key, -- Original key + value = value -- Original value + } + end + + table.sort(sorter, function(left, right) + -- The minimum of the two lengths, used to to the comparisons + local len = math.min(#left.sorter, #right.sorter) + + for i = 1, len do + -- Convert elements to numbers + local left_number, right_number = tonumber(left.sorter[i]), tonumber(right.sorter[i]) + + if left_number ~= right_number then + -- If numbers are different, compare them using the sort function + return f(left_number, right_number) + elseif i == len then + -- This is the lat time we do the comparison: + -- When lengths are not equal, legths are used at tie breaker + return f(#left.sorter, #right.sorter) + end + end + end) - local i = 0 - local iter = function() - i = i + 1 + local i = 0 + local iter = function() + i = i + 1 - if sorter[i] == nil then - return - end + if sorter[i] == nil then + return + end - return sorter[i].key, sorter[i].value - end + return sorter[i].key, sorter[i].value + end - return iter + return iter end -- ############################################## function pairsByField(t, field, f) - local a = {} - for n in pairs(t) do - table.insert(a, n) - end + local a = {} + for n in pairs(t) do + table.insert(a, n) + end - table.sort(a, function(x, y) - return f(t[x][field], t[y][field]) - end) - local i = 0 -- iterator variable - local iter = function() -- iterator function - i = i + 1 - if a[i] == nil then - return nil - else - return a[i], t[a[i]] - end - end - return iter + table.sort(a, function(x, y) + return f(t[x][field], t[y][field]) + end) + local i = 0 -- iterator variable + local iter = function() -- iterator function + i = i + 1 + if a[i] == nil then + return nil + else + return a[i], t[a[i]] + end + end + return iter end -- ############################################## function asc(a, b) - if a == nil or b == nil then - return false - elseif type(a) ~= type(b) then - traceError(TRACE_WARNING, TRACE_CONSOLE, - "Bad types in asc(): " .. a .. " (" .. type(a) .. ") vs " .. b .. " (" .. type(b) .. ")") - return false - end + if a == nil or b == nil then + return false + elseif type(a) ~= type(b) then + traceError(TRACE_WARNING, TRACE_CONSOLE, + "Bad types in asc(): " .. a .. " (" .. type(a) .. ") vs " .. b .. " (" .. type(b) .. ")") + return false + end - return (a < b) + return (a < b) end -- ############################################## function rev(a, b) - if a == nil or b == nil then - return false - elseif type(a) ~= type(b) then - traceError(TRACE_WARNING, TRACE_CONSOLE, - "Bad types in rev(): " .. a .. " (" .. type(a) .. ") vs " .. b .. " (" .. type(b) .. ")") - tprint(debug.traceback()) - return false - end + if a == nil or b == nil then + return false + elseif type(a) ~= type(b) then + traceError(TRACE_WARNING, TRACE_CONSOLE, + "Bad types in rev(): " .. a .. " (" .. type(a) .. ") vs " .. b .. " (" .. type(b) .. ")") + tprint(debug.traceback()) + return false + end - return (a > b) + return (a > b) end -- ############################################## function asc_insensitive(a, b) - if type(a) ~= "string" then - return asc(a, b) - end - return (string.lower(a) < string.lower(b)) + if type(a) ~= "string" then + return asc(a, b) + end + return (string.lower(a) < string.lower(b)) end -- ############################################## function rev_insensitive(a, b) - if type(a) ~= "string" then - return rev(a, b) - end - return (string.lower(a) > string.lower(b)) + if type(a) ~= "string" then + return rev(a, b) + end + return (string.lower(a) > string.lower(b)) end -- ############################################## function string.split(s, p) - local temp = {} - local index = 0 - - if s == nil then - io.write(debug.traceback() .. "\n") - end - - local last_index = string.len(s) - - while true do - local i, e = string.find(s, p, index) - - if i and e then - local next_index = e + 1 - local word_bound = i - 1 - table.insert(temp, string.sub(s, index, word_bound)) - index = next_index - else - if index > 0 and index <= last_index then - table.insert(temp, string.sub(s, index, last_index)) - elseif index == 0 then - temp = nil - end - break - end - end + local temp = {} + local index = 0 + + if s == nil then + io.write(debug.traceback() .. "\n") + end + + local last_index = string.len(s) + + while true do + local i, e = string.find(s, p, index) + + if i and e then + local next_index = e + 1 + local word_bound = i - 1 + table.insert(temp, string.sub(s, index, word_bound)) + index = next_index + else + if index > 0 and index <= last_index then + table.insert(temp, string.sub(s, index, last_index)) + elseif index == 0 then + temp = nil + end + break + end + end - return temp + return temp end -- ############################################## function isMacAddress(address) - local v - local addr + local v + local addr - if (address == nil) then - return false - end + if (address == nil) then + return false + end - v = string.split(address, "@") + v = string.split(address, "@") - if (v ~= nil) then - addr = v[1] - else - addr = address - end + if (v ~= nil) then + addr = v[1] + else + addr = address + end - if (string.ends(addr, "_v4") or string.ends(addr, "_v6") or - (string.match(addr, "^%x%x:%x%x:%x%x:%x%x:%x%x:%x%x$") ~= nil) or - (string.match(addr, "^%x%x:%x%x:%x%x:%x%x:%x%x:%x%x%@%d+$") ~= nil)) then - return true - end - return false + if (string.ends(addr, "_v4") or string.ends(addr, "_v6") or + (string.match(addr, "^%x%x:%x%x:%x%x:%x%x:%x%x:%x%x$") ~= nil) or + (string.match(addr, "^%x%x:%x%x:%x%x:%x%x:%x%x:%x%x%@%d+$") ~= nil)) then + return true + end + return false end function isCommunityId(address) - local c - if (address == nil) then - return false - end + local c + if (address == nil) then + return false + end - c = string.split(address, ":") - if (c ~= nil and #c == 2) then - return true - end + c = string.split(address, ":") + if (c ~= nil and #c == 2) then + return true + end - return false + return false end function isJA3(address) - if (address == nil) then - return false - end - if (string.find(address, "%.") or string.find(address, ":")) then - return false - end - return true + if (address == nil) then + return false + end + if (string.find(address, "%.") or string.find(address, ":")) then + return false + end + return true end -- ############################################## function isEmptyString(str) - if ((str == nil) or (str == "") or (str == " ")) then - return true - else - return false - end + if ((str == nil) or (str == "") or (str == " ")) then + return true + else + return false + end end -- ############################################## function ternary(cond, T, F) - if cond then - return T - else - return F - end + if cond then + return T + else + return F + end end -- ############################################## function isAdministrator() - return ntop.isAdministrator() + return ntop.isAdministrator() end -- ############################################## function isNoLoginUser() - return _SESSION["user"] == ntop.getNologinUser() + return _SESSION["user"] == ntop.getNologinUser() end -- ############################################## function getSystemInterfaceId() - -- NOTE: keep in sync with SYSTEM_INTERFACE_ID in ntop_defines.h - -- This must be a string as it is passed in interface.select - return "-1" + -- NOTE: keep in sync with SYSTEM_INTERFACE_ID in ntop_defines.h + -- This must be a string as it is passed in interface.select + return "-1" end -- ############################################## function getSystemInterfaceName() - -- NOTE: keep in sync with SYSTEM_INTERFACE_NAME in ntop_defines.h - return "__system__" + -- NOTE: keep in sync with SYSTEM_INTERFACE_NAME in ntop_defines.h + return "__system__" end -- ########################################### function hasHighResolutionTs() - local active_driver = ntop.getPref("ntopng.prefs.timeseries_driver") + local active_driver = ntop.getPref("ntopng.prefs.timeseries_driver") - -- High resolution timeseries means dumping the host timeseries - -- every 60 seconds instead of 300 seconds. - return ((active_driver == "influxdb") and (ntop.getPref("ntopng.prefs.ts_resolution") ~= "300")) + -- High resolution timeseries means dumping the host timeseries + -- every 60 seconds instead of 300 seconds. + return ((active_driver == "influxdb") and (ntop.getPref("ntopng.prefs.ts_resolution") ~= "300")) end if (trace_script_duration ~= nil) then - io.write(debug.getinfo(1, 'S').source .. " executed in " .. (os.clock() - clock_start) * 1000 .. " ms\n") + io.write(debug.getinfo(1, 'S').source .. " executed in " .. (os.clock() - clock_start) * 1000 .. " ms\n") end -- ############################################## function getInterfaceId(interface_name) - if (interface_name == getSystemInterfaceName()) then - return (getSystemInterfaceId()) - end + if (interface_name == getSystemInterfaceName()) then + return (getSystemInterfaceId()) + end - local ifnames = interface.getIfNames() + local ifnames = interface.getIfNames() - for if_id, if_name in pairs(ifnames) do - if if_name == interface_name then - return tonumber(if_id) - end - end + for if_id, if_name in pairs(ifnames) do + if if_name == interface_name then + return tonumber(if_id) + end + end - return (-1) + return (-1) end -- ########################################### function visualTsKey(tskey) - if ends(tskey, "_v4") or ends(tskey, "_v6") then - local ver = string.sub(tskey, string.len(tskey) - 1, string.len(tskey)) - local address = string.sub(tskey, 1, string.len(tskey) - 3) - local visual_addr + if ends(tskey, "_v4") or ends(tskey, "_v6") then + local ver = string.sub(tskey, string.len(tskey) - 1, string.len(tskey)) + local address = string.sub(tskey, 1, string.len(tskey) - 3) + local visual_addr - if ver == "v4" then - visual_addr = address - else - visual_addr = address .. " (" .. ver .. ")" - end + if ver == "v4" then + visual_addr = address + else + visual_addr = address .. " (" .. ver .. ")" + end - return visual_addr - end + return visual_addr + end - return tskey + return tskey end -- ############################################## @@ -694,35 +692,35 @@ end -- @param skip_first if true, 0 will be returned when no cached value is present -- @return the difference between current and previous value function delta_val(reg, metric_name, granularity, curr_val, skip_first) - -- This require is okay, alert_granularities is just a struct - local alert_granularities = require "alert_granularities" + -- This require is okay, alert_granularities is just a struct + local alert_granularities = require "alert_granularities" - local granularity_num = alert_granularities[granularity] or 0 - if granularity_num ~= 0 and not isEmptyString(granularity_num) then - granularity_num = granularity_num.granularity_id - end + local granularity_num = alert_granularities[granularity] or 0 + if granularity_num ~= 0 and not isEmptyString(granularity_num) then + granularity_num = granularity_num.granularity_id + end - local key = string.format("%s:%d", metric_name, granularity_num) + local key = string.format("%s:%d", metric_name, granularity_num) - -- Read cached value and purify it - local prev_val - local prev = reg.getCachedAlertValue(key, granularity_num) - if prev == nil or type(prev) == "table" then -- Safety check and debug - -- traceError(TRACE_ERROR, TRACE_CONSOLE, "Bad prev val") - -- tprint(prev) - -- tprint(debug.traceback()) - else - prev_val = tonumber(prev) - end + -- Read cached value and purify it + local prev_val + local prev = reg.getCachedAlertValue(key, granularity_num) + if prev == nil or type(prev) == "table" then -- Safety check and debug + -- traceError(TRACE_ERROR, TRACE_CONSOLE, "Bad prev val") + -- tprint(prev) + -- tprint(debug.traceback()) + else + prev_val = tonumber(prev) + end - -- Save the value for the next round - reg.setCachedAlertValue(key, tostring(curr_val), granularity_num) + -- Save the value for the next round + reg.setCachedAlertValue(key, tostring(curr_val), granularity_num) - if ((skip_first == true) and (prev_val == nil)) then - return (0) - else - return (curr_val - (prev_val or 0)) - end + if ((skip_first == true) and (prev_val == nil)) then + return (0) + else + return (curr_val - (prev_val or 0)) + end end -- ############################################## @@ -738,61 +736,61 @@ end -- Check if this is a valid pool member (MAC or CIDR@VLAN) -- @param fix_relaxed Fix relaxed notation (IP without net mask or vlan) function checkPoolMember(member, fix_relaxed) - if isEmptyString(member) then - return nil - end - - if isMacAddress(member) then - return member - end - - -- VLAN - local vlan_id - local vlan_idx = string.find(member, "@") - if vlan_idx == nil then - if fix_relaxed then - vlan_id = 0 -- default vlan: 0 - else - return nil -- no vlan - end - elseif vlan_idx == 1 then - return nil -- bad format - else - local other = string.sub(member, 1, vlan_idx - 1) - vlan_id = tonumber(string.sub(member, vlan_idx + 1)) - if vlan_id == nil or vlan_id < 0 then - return nil - end - member = other - end - - -- prefix is mandatory here - local address, prefix = splitNetworkPrefix(member) - if address == nil then - return nil -- bad format - elseif prefix == nil then - if fix_relaxed then - if isIPv4(address) then - prefix = '32' -- default mask: 32 - elseif isIPv6(address) then - prefix = '128' -- default mask: 128 - else - return nil -- bad format - end - else - return nil -- no mask - end - end - - if isIPv4(address) and (tonumber(prefix) >= 0) and (tonumber(prefix) <= 32) then - -- ok - elseif isIPv6(address) and (tonumber(prefix) >= 0) and (tonumber(prefix) <= 128) then - -- ok - else - return nil -- bad format - end - - return address .. '/' .. prefix .. '@' .. vlan_id + if isEmptyString(member) then + return nil + end + + if isMacAddress(member) then + return member + end + + -- VLAN + local vlan_id + local vlan_idx = string.find(member, "@") + if vlan_idx == nil then + if fix_relaxed then + vlan_id = 0 -- default vlan: 0 + else + return nil -- no vlan + end + elseif vlan_idx == 1 then + return nil -- bad format + else + local other = string.sub(member, 1, vlan_idx - 1) + vlan_id = tonumber(string.sub(member, vlan_idx + 1)) + if vlan_id == nil or vlan_id < 0 then + return nil + end + member = other + end + + -- prefix is mandatory here + local address, prefix = splitNetworkPrefix(member) + if address == nil then + return nil -- bad format + elseif prefix == nil then + if fix_relaxed then + if isIPv4(address) then + prefix = '32' -- default mask: 32 + elseif isIPv6(address) then + prefix = '128' -- default mask: 128 + else + return nil -- bad format + end + else + return nil -- no mask + end + end + + if isIPv4(address) and (tonumber(prefix) >= 0) and (tonumber(prefix) <= 32) then + -- ok + elseif isIPv6(address) and (tonumber(prefix) >= 0) and (tonumber(prefix) <= 128) then + -- ok + else + return nil -- bad format + end + + return address .. '/' .. prefix .. '@' .. vlan_id end -- ############################################## @@ -800,87 +798,86 @@ end -- Check if this is a valid pool member (MAC or CIDR@VLAN) -- @param relaxed Allow relaxed notation (IP without net mask or vlan) function isValidPoolMember(member, relaxed) - if checkPoolMember(member, relaxed) then - return true - else - return false - end + if checkPoolMember(member, relaxed) then + return true + else + return false + end end -- ############################################## function fixPoolMemberFormat(member) - return checkPoolMember(member, true) + return checkPoolMember(member, true) end -- ################################################################# function bit(p) - return 2 ^ (p - 1) -- 1-based indexing + return 2 ^ (p - 1) -- 1-based indexing end -- ############################################## -- Typical call: if hasbit(x, bit(3)) then ... function hasbit(x, p) - return x % (p + p) >= p + return x % (p + p) >= p end -- ############################################## function setbit(x, p) - return hasbit(x, p) and x or x + p + return hasbit(x, p) and x or x + p end -- ############################################## function clearbit(x, p) - return hasbit(x, p) and x - p or x + return hasbit(x, p) and x - p or x end -- ########################################### function getHttpHost() - local ntopng_info = ntop.getInfo() - local ntopng_host_info = ntop.getHostInformation() or {} + local ntopng_info = ntop.getInfo() + local ntopng_host_info = ntop.getHostInformation() or {} - -- Read configured ntopng host name or IP - local ntopng_host_ip = ntop.getPref("ntopng.prefs.ntopng_host_address") - if isEmptyString(ntopng_host_ip) then - -- fallback: managegemt IP - ntopng_host_ip = ntopng_host_info.ip - end - if isEmptyString(ntopng_host_ip) then - -- last resort: dummy IP - ntopng_host_ip = '127.0.0.1' - end + -- Read configured ntopng host name or IP + local ntopng_host_ip = ntop.getPref("ntopng.prefs.ntopng_host_address") + if isEmptyString(ntopng_host_ip) then + -- fallback: managegemt IP + ntopng_host_ip = ntopng_host_info.ip + end + if isEmptyString(ntopng_host_ip) then + -- last resort: dummy IP + ntopng_host_ip = '127.0.0.1' + end - local http_host - if starts(ntopng_host_ip, 'http') then - http_host = ntopng_host_ip - else - -- Computing URL adding protocol and port - local ntopng_protocol = "http://" - local ntopng_port = ntopng_info.http_port + local http_host + if starts(ntopng_host_ip, 'http') then + http_host = ntopng_host_ip + else + -- Computing URL adding protocol and port + local ntopng_protocol = "http://" + local ntopng_port = ntopng_info.http_port - if not ntop.isnEdge() - and ntopng_info.https_port and tonumber(ntopng_info.https_port) ~= 0 then - ntopng_protocol = "https://" - ntopng_port = ntopng_info.https_port - end + if not ntop.isnEdge() and ntopng_info.https_port and tonumber(ntopng_info.https_port) ~= 0 then + ntopng_protocol = "https://" + ntopng_port = ntopng_info.https_port + end - http_host = ntopng_protocol .. ntopng_host_ip .. ":" .. ntopng_port - end + http_host = ntopng_protocol .. ntopng_host_ip .. ":" .. ntopng_port + end - return http_host + return http_host end -- ############################################## -function starts(String,Start) - if((String == nil) or (Start == nil)) then - return(false) - end +function starts(String, Start) + if ((String == nil) or (Start == nil)) then + return (false) + end - return string.sub(String,1,string.len(Start))==Start + return string.sub(String, 1, string.len(Start)) == Start end diff --git a/scripts/lua/rest/v2/get/interface/nprobes/data.lua b/scripts/lua/rest/v2/get/interface/nprobes/data.lua index f2b217ba91bd..23778a274144 100644 --- a/scripts/lua/rest/v2/get/interface/nprobes/data.lua +++ b/scripts/lua/rest/v2/get/interface/nprobes/data.lua @@ -30,70 +30,47 @@ interface.select(ifid) local ifstats = interface.getStats() local probes_stats = ifstats.probes or {} local timeseries_enabled = areFlowdevTimeseriesEnabled() -if table.len(probes_stats) > 0 then - for k, v in pairs(ifstats.probes or {}) do - v.exporters = ifstats.exporters or {} - v.ifid = ifid - probes_stats[k] = v - end - ifstats.probes = probes_stats -end -if interface.isView() then - local zmq_stats = {} - local exporters_stats = {} - for interface_id, _ in pairsByKeys(interface.getIfNames() or {}) do - interface.select(interface_id) - if interface.isViewed() then - local tmp = interface.getStats() - for k, v in pairs(tmp.probes or {}) do - v.exporters = tmp.exporters or {} - v.ifid = interface_id - probes_stats[k] = v + +for interface_id, probes_list in pairs(ifstats.probes or {}) do + for source_id, probe_info in pairs(probes_list or {}) do + local flow_drops = 0 + local exported_flows = 0 + local probe_active = false + local flow_exporters_num = table.len(probe_info.exporters) + if interface.getHostInfo(probe_info["probe.ip"]) then + probe_active = true + end + if table.len(probe_info.exporters) == 0 then + flow_exporters_num = 1 -- Packet exporter + flow_drops = probe_info["drops.elk_flow_drops"] + probe_info["drops.flow_collection_udp_socket_drops"] + + probe_info["drops.export_queue_full"] + probe_info["drops.too_many_flows"] + probe_info["drops.flow_collection_drops"] + + probe_info["drops.sflow_pkt_sample_drops"] + probe_info["drops.elk_flow_drops"] + exported_flows = probe_info["zmq.num_flow_exports"] + else + for _, values in pairs(probe_info.exporters) do + flow_drops = flow_drops + values.num_drops + exported_flows = exported_flows + values.num_netflow_flows + values.num_sflow_flows end end - end - ifstats.probes = probes_stats - interface.select(ifstats.id) -end -for k, v in pairs(ifstats.probes or {}) do - local flow_drops = 0 - local exported_flows = 0 - local probe_active = false - local flow_exporters_num = table.len(v.exporters or {}) - if interface.getHostInfo(v["probe.ip"]) then - probe_active = true - end - if table.len(v.exporters) == 0 then - flow_exporters_num = 1 - flow_drops = v["drops.elk_flow_drops"] + v["drops.flow_collection_udp_socket_drops"] + - v["drops.export_queue_full"] + v["drops.too_many_flows"] + v["drops.flow_collection_drops"] + - v["drops.sflow_pkt_sample_drops"] + v["drops.elk_flow_drops"] - exported_flows = v["zmq.num_flow_exports"] - else - for _, values in pairs(v.exporters) do - flow_drops = flow_drops + values.num_drops - exported_flows = exported_flows + values.num_netflow_flows + values.num_sflow_flows - end + res[#res + 1] = { + probe_interface = ternary((probe_info["remote.name"] ~= "none"), probe_info["remote.name"], + i18n("if_stats_overview.remote_probe_collector_mode")), + probe_version = probe_info["probe.probe_version"], + probe_ip = probe_info["probe.ip"], + probe_uuid = probe_info["probe.uuid"], + probe_public_ip = probe_info["probe.public_ip"], + probe_edition = probe_info["probe.probe_edition"], + probe_license = probe_info["probe.probe_license"] or i18n("if_stats_overview.no_license"), + probe_maintenance = probe_info["probe.probe_maintenance"] or i18n("if_stats_overview.expired_maintenance"), + flow_exporters = flow_exporters_num, + dropped_flows = flow_drops, + exported_flows = exported_flows, + timeseries_enabled = timeseries_enabled, + ifid = interface_id, + is_probe_active = probe_active + } end - - res[#res + 1] = { - probe_interface = ternary((v["remote.name"] ~= "none"), v["remote.name"], - i18n("if_stats_overview.remote_probe_collector_mode")), - probe_version = v["probe.probe_version"], - probe_ip = v["probe.ip"], - probe_uuid = v["probe.uuid"], - probe_public_ip = v["probe.public_ip"], - probe_edition = v["probe.probe_edition"], - probe_license = v["probe.probe_license"] or i18n("if_stats_overview.no_license"), - probe_maintenance = v["probe.probe_maintenance"] or i18n("if_stats_overview.expired_maintenance"), - flow_exporters = flow_exporters_num, - dropped_flows = flow_drops, - exported_flows = exported_flows, - timeseries_enabled = timeseries_enabled, - ifid = v.ifid, - is_probe_active = probe_active - } end rest_utils.answer(rc, res) diff --git a/src/LuaEngineInterface.cpp b/src/LuaEngineInterface.cpp index fa63c68f5420..6022142eb9a3 100644 --- a/src/LuaEngineInterface.cpp +++ b/src/LuaEngineInterface.cpp @@ -2865,7 +2865,7 @@ static int ntop_get_flow_devices(lua_State *vm) { if (!curr_iface) return (ntop_lua_return_value(vm, __FUNCTION__, CONST_LUA_ERROR)); else { - curr_iface->getFlowDevices(vm, false); + curr_iface->getFlowDevices(vm); /* Return a table with key, the interface id and as value, * a table with the IPs of the interface diff --git a/src/ViewInterface.cpp b/src/ViewInterface.cpp index d2f65990d0e5..3e9ebeaae9d2 100644 --- a/src/ViewInterface.cpp +++ b/src/ViewInterface.cpp @@ -817,11 +817,9 @@ void ViewInterface::lua_queues_stats(lua_State *vm) { /* **************************************************** */ #ifdef NTOPNG_PRO -void ViewInterface::getFlowDevices(lua_State *vm, bool add_table) { - if(add_table) lua_newtable(vm); - +void ViewInterface::getFlowDevices(lua_State *vm) { for (int i = 0; i < num_viewed_interfaces; i++) - viewed_interfaces[i]->getFlowDevices(vm, false); + viewed_interfaces[i]->getFlowDevices(vm); } /* **************************************************** */ @@ -849,3 +847,15 @@ void ViewInterface::getSFlowDeviceInfo(lua_State *vm, u_int32_t deviceIP) { } /* **************************************************** */ + +void ViewInterface::lua(lua_State *vm, bool fullStats) { + NetworkInterface::lua(vm, fullStats); + lua_newtable(vm); + for (int i = 0; i < num_viewed_interfaces; i++) + viewed_interfaces[i]->probeLuaStats(vm); + lua_pushstring(vm, "probes"); + lua_insert(vm, -2); + lua_settable(vm, -3); +} + +/* **************************************************** */ diff --git a/src/ZMQParserInterface.cpp b/src/ZMQParserInterface.cpp index 3b47d5cc2330..be41cdc348a9 100644 --- a/src/ZMQParserInterface.cpp +++ b/src/ZMQParserInterface.cpp @@ -207,7 +207,7 @@ ZMQParserInterface::ZMQParserInterface(const char *endpoint, /* **************************************************** */ ZMQParserInterface::~ZMQParserInterface() { - map::iterator it; + map::iterator it; if (zmq_remote_stats) free(zmq_remote_stats); if (zmq_remote_stats_shadow) free(zmq_remote_stats_shadow); @@ -217,7 +217,7 @@ ZMQParserInterface::~ZMQParserInterface() { for (it = source_id_last_zmq_remote_stats.begin(); it != source_id_last_zmq_remote_stats.end(); ++it) - free(it->second); + delete (it->second); source_id_last_zmq_remote_stats.clear(); } @@ -327,13 +327,11 @@ u_int8_t ZMQParserInterface::parseEvent(const char *payload, int payload_size, void *data) { json_object *o; enum json_tokener_error jerr = json_tokener_success; - ZMQ_RemoteStats zrs; + nProbeStats zrs; /* Do not instantiate, automatically cleaned outside of the scope */ const u_int32_t max_timeout = 600, min_timeout = 60; if (polling_start_time == 0) polling_start_time = (u_int32_t)time(NULL); - memset(&zrs, 0, sizeof(zrs)); - //ntop->getTrace()->traceEvent(TRACE_NORMAL, "[msg_id: %u] %s", msg_id, //payload); @@ -495,7 +493,7 @@ u_int8_t ZMQParserInterface::parseEvent(const char *payload, int payload_size, if (json_object_object_get_ex(val, "unique_source_id", &x)) exp_stats.unique_source_id = (u_int32_t)json_object_get_int64(x); - exporters_stats[ip] = exp_stats; + zrs.exportersStats[ip] = exp_stats; } } } @@ -3012,12 +3010,11 @@ u_int8_t ZMQParserInterface::parseOption(const char *payload, int payload_size, /* **************************************** */ u_int32_t ZMQParserInterface::periodicStatsUpdateFrequency() const { - ZMQ_RemoteStats *zrs = zmq_remote_stats; + nProbeStats *zrs = zmq_remote_stats; u_int32_t update_freq; u_int32_t update_freq_min = ntop->getPrefs()->get_housekeeping_frequency(); - if (zrs) - update_freq = + if (zrs) update_freq = min_val(max_val(zrs->remote_lifetime_timeout, zrs->remote_idle_timeout), zrs->remote_collected_lifetime_timeout); else @@ -3028,9 +3025,9 @@ u_int32_t ZMQParserInterface::periodicStatsUpdateFrequency() const { /* **************************************** */ -void ZMQParserInterface::setRemoteStats(ZMQ_RemoteStats *zrs) { - ZMQ_RemoteStats *last_zrs, *cumulative_zrs; - map::iterator it; +void ZMQParserInterface::setRemoteStats(nProbeStats *zrs) { + nProbeStats *last_zrs, *cumulative_zrs; + map::iterator it; u_int32_t last_time = getTimeLastPktRcvdRemote(); struct timeval now; @@ -3042,7 +3039,7 @@ void ZMQParserInterface::setRemoteStats(ZMQ_RemoteStats *zrs) { if (source_id_last_zmq_remote_stats.find(zrs->source_id) == source_id_last_zmq_remote_stats.end()) { - last_zrs = (ZMQ_RemoteStats *)malloc(sizeof(ZMQ_RemoteStats)); + last_zrs = new (std::nothrow) nProbeStats(); if (!last_zrs) { lock.unlock(__FILE__, __LINE__); @@ -3053,7 +3050,7 @@ void ZMQParserInterface::setRemoteStats(ZMQ_RemoteStats *zrs) { } else last_zrs = source_id_last_zmq_remote_stats[zrs->source_id]; - memcpy(last_zrs, zrs, sizeof(ZMQ_RemoteStats)); + *last_zrs = *zrs; lock.unlock(__FILE__, __LINE__); @@ -3065,14 +3062,14 @@ void ZMQParserInterface::setRemoteStats(ZMQ_RemoteStats *zrs) { /* Sum stats from all exporters */ - cumulative_zrs = (ZMQ_RemoteStats *)calloc(1, sizeof(ZMQ_RemoteStats)); + cumulative_zrs = new (std::nothrow) nProbeStats(); if (!cumulative_zrs) return; lock.wrlock(__FILE__, __LINE__); /* Need write lock due to (*) */ for (it = source_id_last_zmq_remote_stats.begin(); it != source_id_last_zmq_remote_stats.end();) { - ZMQ_RemoteStats *zrs_i = it->second; + nProbeStats *zrs_i = it->second; if (last_time > MAX_HASH_ENTRY_IDLE && zrs_i->remote_time < last_time - MAX_HASH_ENTRY_IDLE /* sec */) { @@ -3080,7 +3077,7 @@ void ZMQParserInterface::setRemoteStats(ZMQ_RemoteStats *zrs) { // ntop->getTrace()->traceEvent(TRACE_NORMAL, "Erased %s [local_time: // %u][last_time: %u]", zrs_i->remote_ifname, zrs_i->local_time, // last_time); - free(zrs_i); + delete (zrs_i); source_id_last_zmq_remote_stats.erase(it++); /* (*) */ } else { cumulative_zrs->num_exporters += zrs_i->num_exporters; @@ -3183,21 +3180,15 @@ bool ZMQParserInterface::getCustomAppDetails(u_int32_t remapped_app_id, /* **************************************************** */ -void ZMQParserInterface::lua(lua_State *vm, bool fullStats) { - ZMQ_RemoteStats *zrs = zmq_remote_stats; - std::map::iterator it; - - NetworkInterface::lua(vm, fullStats); - - /* ************************************* */ - +void ZMQParserInterface::probeLuaStats(lua_State *vm) { + std::map::iterator it; lua_newtable(vm); lock.rdlock(__FILE__, __LINE__); for (it = source_id_last_zmq_remote_stats.begin(); it != source_id_last_zmq_remote_stats.end(); ++it) { - ZMQ_RemoteStats *zrs = it->second; + nProbeStats *zrs = it->second; lua_newtable(vm); @@ -3233,59 +3224,69 @@ void ZMQParserInterface::lua(lua_State *vm, bool fullStats) { lua_push_uint64_table_entry(vm, "flow_collection.sflow_samples", zrs->flow_collection.sflow_samples); lua_push_uint64_table_entry(vm, "zmq.num_flow_exports", zrs->num_flow_exports); lua_push_uint64_table_entry(vm, "zmq.num_exporters", zrs->num_exporters); + exporterLuaStats(vm, zrs); + + /* ************************************* */ + + if (zrs) { + lua_push_uint64_table_entry(vm, "probe.remote_time", + zrs->remote_time); /* remote time when last event has been sent */ + lua_push_uint64_table_entry(vm, "probe.local_time", + zrs->local_time); /* local time when last event has been received */ + + lua_push_uint64_table_entry(vm, "zmq.num_flow_exports", + zrs->num_flow_exports - zmq_remote_initial_exported_flows); + lua_push_uint64_table_entry(vm, "zmq.num_exporters", zrs->num_exporters); + + if (zrs->export_queue_full > 0) + lua_push_uint64_table_entry(vm, "zmq.drops.export_queue_full", + zrs->export_queue_full); + if (zrs->flow_collection_drops) + lua_push_uint64_table_entry(vm, "zmq.drops.flow_collection_drops", + zrs->flow_collection_drops); + if (zrs->flow_collection_udp_socket_drops) + lua_push_uint64_table_entry(vm, + "zmq.drops.flow_collection_udp_socket_drops", + zrs->flow_collection_udp_socket_drops); + + lua_push_uint64_table_entry(vm, "timeout.lifetime", + zrs->remote_lifetime_timeout); + lua_push_uint64_table_entry(vm, "timeout.collected_lifetime", + zrs->remote_collected_lifetime_timeout); + lua_push_uint64_table_entry(vm, "timeout.idle", zrs->remote_idle_timeout); + } lua_pushstring(vm, std::to_string(it->first).c_str() /* The source_id as string (can't use integers or Lua will think it's an array ) */); lua_insert(vm, -2); lua_settable(vm, -3); } + lua_rawseti(vm, -2, get_id()); + /* Here the Interface ID is added because in case of View Interfaces + * this field could be the same for different interfaces + */ lock.unlock(__FILE__, __LINE__); +} + +/* **************************************************** */ +void ZMQParserInterface::lua(lua_State *vm, bool fullStats) { + NetworkInterface::lua(vm, fullStats); + lua_newtable(vm); + probeLuaStats(vm); lua_pushstring(vm, "probes"); lua_insert(vm, -2); lua_settable(vm, -3); - - /* ************************************* */ - - if (zrs) { - lua_push_uint64_table_entry(vm, "probe.remote_time", - zrs->remote_time); /* remote time when last event has been sent */ - lua_push_uint64_table_entry(vm, "probe.local_time", - zrs->local_time); /* local time when last event has been received */ - - lua_push_uint64_table_entry(vm, "zmq.num_flow_exports", - zrs->num_flow_exports - zmq_remote_initial_exported_flows); - lua_push_uint64_table_entry(vm, "zmq.num_exporters", zrs->num_exporters); - - if (zrs->export_queue_full > 0) - lua_push_uint64_table_entry(vm, "zmq.drops.export_queue_full", - zrs->export_queue_full); - if (zrs->flow_collection_drops) - lua_push_uint64_table_entry(vm, "zmq.drops.flow_collection_drops", - zrs->flow_collection_drops); - if (zrs->flow_collection_udp_socket_drops) - lua_push_uint64_table_entry(vm, - "zmq.drops.flow_collection_udp_socket_drops", - zrs->flow_collection_udp_socket_drops); - - lua_push_uint64_table_entry(vm, "timeout.lifetime", - zrs->remote_lifetime_timeout); - lua_push_uint64_table_entry(vm, "timeout.collected_lifetime", - zrs->remote_collected_lifetime_timeout); - lua_push_uint64_table_entry(vm, "timeout.idle", zrs->remote_idle_timeout); - exporterLuaStats(vm); - } } /* **************************************************** */ -void ZMQParserInterface::exporterLuaStats(lua_State *vm) { - std::unordered_map::iterator it; +void ZMQParserInterface::exporterLuaStats(lua_State *vm, nProbeStats *zrs) { + std::map::iterator it; lua_newtable(vm); - lock.rdlock(__FILE__, __LINE__); - for (it = exporters_stats.begin(); - it != exporters_stats.end(); ++it) { + for (it = zrs->exportersStats.begin(); + it != zrs->exportersStats.end(); ++it) { lua_newtable(vm); char buf[32], ipb[24]; snprintf(buf, sizeof(buf), "%s", Utils::intoaV4(it->first, ipb, sizeof(ipb))); @@ -3301,7 +3302,6 @@ void ZMQParserInterface::exporterLuaStats(lua_State *vm) { lua_settable(vm, -3); } - lock.unlock(__FILE__, __LINE__); lua_pushstring(vm, "exporters"); lua_insert(vm, -2); lua_settable(vm, -3); diff --git a/src/nProbeStats.cpp b/src/nProbeStats.cpp new file mode 100644 index 000000000000..39b53d41dc16 --- /dev/null +++ b/src/nProbeStats.cpp @@ -0,0 +1,35 @@ +/* + * + * (C) 2013-24 - ntop.org + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "ntop_includes.h" + +nProbeStats::nProbeStats() { + source_id = uuid_num = num_exporters = + remote_ifspeed = remote_time = local_time = avg_bps = avg_pps = + remote_lifetime_timeout = remote_idle_timeout = + remote_collected_lifetime_timeout = export_queue_full = + too_many_flows = elk_flow_drops = sflow_pkt_sample_drops = + flow_collection_drops = flow_collection_udp_socket_drops = 0; + + remote_bytes = remote_pkts = num_flow_exports = 0; +} + +/* *************************************** */ \ No newline at end of file