-- -- (C) 2014-22 - ntop.org -- -- Hack to avoid include loops if(pragma_once_lua_utils == true) then -- avoid multiple inclusions return end pragma_once_lua_utils = true -- ############################################### local clock_start = os.clock() dirs = ntop.getDirs() package.path = dirs.installdir .. "/scripts/lua/modules/i18n/?.lua;" .. package.path package.path = dirs.installdir .. "/scripts/lua/modules/timeseries/?.lua;" .. package.path package.path = dirs.installdir .. "/scripts/lua/modules/pools/?.lua;" .. package.path require "lua_trace" require "lua_utils_generic" require "ntop_utils" require "locales_utils" local os_utils = require "os_utils" local format_utils = require "format_utils" -- TODO: replace those globals with locals everywhere secondsToTime = format_utils.secondsToTime msToTime = format_utils.msToTime bytesToSize = format_utils.bytesToSize formatPackets = format_utils.formatPackets formatFlows = format_utils.formatFlows formatValue = format_utils.formatValue pktsToSize = format_utils.pktsToSize bitsToSize = format_utils.bitsToSize round = format_utils.round bitsToSizeMultiplier = format_utils.bitsToSizeMultiplier -- ############################################## function isAllowedSystemInterface() return ntop.isAllowedInterface(tonumber(getSystemInterfaceId())) end -- ############################################## local cached_allowed_networks_set = nil function hasAllowedNetworksSet() if(cached_allowed_networks_set == nil) then local nets = ntop.getAllowedNetworks() local allowed_nets = string.split(nets, ",") or {nets} cached_allowed_networks_set = false for _, net in pairs(allowed_nets) do if((not isEmptyString(net)) and (net ~= "0.0.0.0/0") and (net ~= "::/0")) then cached_allowed_networks_set = true break end end end return(cached_allowed_networks_set) end -- ############################################## function hasSoftwareUpdatesSupport() return (not ntop.isOffline() and isAdministrator() and ntop.isPackage() and not ntop.isWindows()) end -- See Utils::l4proto2name() l4_keys = { { "IP", "ip", 0 }, { "ICMP", "icmp", 1 }, { "IGMP", "igmp", 2 }, { "TCP", "tcp", 6 }, { "UDP", "udp", 17 }, { "IPv6", "ipv6", 41 }, { "RSVP", "rsvp", 46 }, { "GRE", "gre", 47 }, { "ESP", "esp", 50 }, { "IPv6-ICMP", "ipv6icmp", 58 }, { "EIGRP", "eigrp", 88 }, { "OSPF", "ospf", 89 }, { "PIM", "pim", 103 }, { "VRRP", "vrrp", 112 }, { "HIP", "hip", 139 }, { "SCTP", "sctp", 132 }, { "ICMPv6", "icmpv6", 58 }, { "IGMP", "igmp", 2 }, { "Other IP", "other_ip", -1 } } function __FILE__() return debug.getinfo(2,'S').source end function __LINE__() return debug.getinfo(2, 'l').currentline end -- ############################################## function findString(str, tofind) if(str == nil) then return(nil) end if(tofind == nil) then return(nil) end str1 = string.lower(string.gsub(str, "-", "_")) tofind1 = string.lower(string.gsub(tofind, "-", "_")) return(string.find(str1, tofind1, 1)) end -- ############################################## function findStringArray(str, tofind) if(str == nil) then return(nil) end if(tofind == nil) then return(nil) end local rsp = false for k,v in pairs(tofind) do str1 = string.gsub(str, "-", "_") tofind1 = string.gsub(v, "-", "_") if(str1 == tofind1) then rsp = true end end return(rsp) end -- ############################################## -- -- Returns indexes to be used for string shortening. The portion of to_shorten between -- middle_start and middle_end will be inside the bounds. -- -- to_shorten: string to be shorten -- middle_start: middle part begin index -- middle_end: middle part begin index -- maxlen: maximum length -- function shortenInTheMiddle(to_shorten, middle_start, middle_end, maxlen) local maxlen = maxlen - (middle_end - middle_start) if maxlen <= 0 then return 0, string.len(to_shorten) end local left_slice = math.max(middle_start - math.floor(maxlen / 2), 1) maxlen = maxlen - (middle_start - left_slice - 1) local right_slice = math.min(middle_end + maxlen, string.len(to_shorten)) return left_slice, right_slice end -- ############################################## function shortHostName(name) local chunks = {name:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")} if(#chunks == 4) then return(name) else local max_len = ntop.getPref("ntopng.prefs.max_ui_strlen") max_len = tonumber(max_len) if(max_len == nil) then max_len = 24 end chunks = {name:match("%w+:%w+:%w+:%w+:%w+:%w+")} --io.write(#chunks.."\n") if(#chunks == 1) then return(name) end if(string.len(name) < max_len) then return(name) else tot = 0 n = 0 ret = "" for token in string.gmatch(name, "([%w-]+).") do if(tot < max_len) then if(n > 0) then ret = ret .. "." end ret = ret .. token tot = tot+string.len(token) n = n + 1 end end return(ret .. "...") end end return(name) end -- ############################################## function _handleArray(name, sev) local id for id, _ in ipairs(name) do local l = name[id][1] local key = name[id][2] if(string.upper(key) == string.upper(sev)) then return(l) end end return(firstToUpper(sev)) end -- ############################################## function l4Label(proto) return(_handleArray(l4_keys, proto)) end -- ############################################## function l4_proto_to_id(proto_name) for _, proto in pairs(l4_keys) do if proto[1] == proto_name or proto[2] == proto_name then return(proto[3]) end end end -- ############################################## function l4_proto_to_string(proto_id) if not proto_id then return "" end if isEmptyString(proto_id) then return "" end proto_id_num = tonumber(proto_id) if proto_id_num == nil then -- Already string? return proto_id end for _, proto in pairs(l4_keys) do if proto[3] == proto_id_num then return proto[1], proto[2] end end return string.format("%d", proto_id_num) end -- ############################################## -- Return the list of L4 proto (key = name, value = id) function l4_proto_list() local list = {} for _, proto in pairs(l4_keys) do -- add L4 proto only if proto[2] ~= 'ip' and proto[2] ~= 'ipv6' then list[proto[1]] = proto[3] end end return list end -- ############################################## function areAlertsEnabled() if(__alert_enabled == nil) then -- Not too nice as changes will be read periodically as new VMs are reloaded -- but at least we avoid breaking up the performance __alert_enabled = (ntop.getPref("ntopng.prefs.disable_alerts_generation") ~= "1") end return (__alert_enabled) end -- ############################################## function isScoreEnabled() return(ntop.isEnterpriseM() or ntop.isnEdgeEnterprise()) end -- ############################################## function hasTrafficReport() local ts_utils = require("ts_utils_core") local is_pcap_dump = interface.isPcapDumpInterface() return((not is_pcap_dump) and (ts_utils.getDriverName() == "rrd") and ntop.isEnterpriseM()) end function hasAlertsDisabled() _POST = _POST or {} return ((_POST["disable_alerts_generation"] ~= nil) and (_POST["disable_alerts_generation"] == "1")) or ((_POST["disable_alerts_generation"] == nil) and (ntop.getPref("ntopng.prefs.disable_alerts_generation") == "1")) end -- ############################################## function hasClickHouseSupport() local auth = require "auth" if not (ntop.isPro() or ntop.isnEdgeEnterprise()) or ntop.isWindows() then return false end -- Don't allow nIndex for unauthorized users if not auth.has_capability(auth.capabilities.historical_flows) then return false end -- TODO optimize if prefs == nil then prefs = ntop.getPrefs() end if prefs.is_dump_flows_to_clickhouse_enabled then return true end return false end -- ############################################## -- NOTE: global nindex support may be enabled but some disable on some interfaces function interfaceHasClickHouseSupport() return(hasClickHouseSupport() and ntop.getPrefs()["is_dump_flows_to_clickhouse_enabled"]) end -- ############################################## function truncate(x) if(x == nil) then tprint(debug.traceback()) end return x<0 and math.ceil(x) or math.floor(x) end -- ############################################## -- Note that the function below returns a string as returning a number -- would not help as a new float would be returned function toint(num) return string.format("%u", truncate(num)) end -- ############################################## function capitalize(str) return (str:gsub("^%l", string.upper)) end -- ############################################## local function starstring(len) local s = "" while(len > 0) do s = s .."*" len = len -1 end return(s) end -- ############################################## function obfuscate(str) local len = string.len(str) local in_clear = 2 if(len <= in_clear) then return(starstring(len)) else return(string.sub(str, 0, in_clear)..starstring(len-in_clear)) end end -- ############################################## function isnumber(str) if((str ~= nil) and (string.len(str) > 0) and (tonumber(str) ~= nil)) then return(true) else return(false) end end -- ############################################## -- 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; end -- ############################################## function formatEpoch(epoch, full_time) return(format_utils.formatEpoch(epoch, full_time)) end -- ############################################## function starts(String,Start) if((String == nil) or (Start == nil)) then return(false) end return string.sub(String,1,string.len(Start))==Start end -- ############################################## function ends(String,End) return End=='' or string.sub(String,-string.len(End))==End end -- ################################################################# function bit(p) 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 end -- ############################################## function setbit(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 end -- ############################################## function isBroadMulticast(ip) if(ip == "0.0.0.0") then return true end -- print(ip) t = string.split(ip, "%.") -- print(table.concat(t, "\n")) if(t == nil) then return false -- Might be an IPv6 address else if(tonumber(t[1]) >= 224) then return true end end return false end -- ############################################## function isBroadcastMulticast(ip) local ainfo = interface.getAddressInfo(ip) if(ainfo.is_multicast or ainfo.is_broadcast) then return true else return false end end -- ############################################## -- -- Fix member format (IP address to /32 CIDR and VLAN to default 0) -- E.g. 192.168.1.10 -> 192.168.1.10/32@0 -- function fixPoolMemberFormat(member) 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 vlan_id = 0 elseif vlan_idx == 1 then return nil 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 elseif prefix == nil then prefix = '32' 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 end return address .. '/' .. prefix .. '@' .. vlan_id end -- ############################################## -- -- Members supported format -- 192.168.1.10/32@10 -- 00:11:22:33:44:55 -- function isValidPoolMember(member) if isEmptyString(member) then return false end if isMacAddress(member) then return true end -- vlan is mandatory here local vlan_idx = string.find(member, "@") if ((vlan_idx == nil) or (vlan_idx == 1)) then return false end local other = string.sub(member, 1, vlan_idx-1) local vlan = tonumber(string.sub(member, vlan_idx+1)) if (vlan == nil) or (vlan < 0) then return false end -- prefix is mandatory here local address, prefix = splitNetworkPrefix(other) if prefix == nil then return false end if isIPv4(address) and (tonumber(prefix) >= 0) and (tonumber(prefix) <= 32) then return true elseif isIPv6(address) and (tonumber(prefix) >= 0) and (tonumber(prefix) <= 128) then return true end return false end -- ############################################## function host2member(ip, vlan, prefix) if prefix == nil then if isIPv4(ip) then prefix = 32 else prefix = 128 end end return ip .. "/" .. tostring(prefix) .. "@" .. tostring(vlan) end -- ############################################## function isLocal(host_ip) host = interface.getHostInfo(host_ip) if((host == nil) or (host['localhost'] ~= true)) then return(false) else return(true) end end -- ############################################## -- Windows fixes for interfaces with "uncommon chars" function purifyInterfaceName(interface_name) -- io.write(debug.traceback().."\n") interface_name = string.gsub(interface_name, "@", "_") interface_name = string.gsub(interface_name, ":", "_") interface_name = string.gsub(interface_name, "/", "_") return(interface_name) end -- ############################################## -- See datatype AggregationType in ntop_typedefs.h function aggregation2String(value) if(value == 0) then return("Client Name") elseif(value == 1) then return("Server Name") elseif(value == 2) then return("Domain Name") elseif(value == 3) then return("Operating System") elseif(value == 4) then return("Registrar Name") else return(value) end end -- ################################# -- Aggregates items below some edge -- edge: minimum percentage value to create collision -- min_col: minimum collision groups to aggregate function aggregatePie(values, values_sum, edge, min_col) local edge = edge or 0.09 min_col = min_col or 2 local aggr = {} local other = i18n("other") local below_edge = {} -- Initial lookup for k,v in pairs(values) do if v / values_sum <= edge then -- too small below_edge[#below_edge + 1] = k else aggr[k] = v end end -- Decide if to aggregate for _,k in pairs(below_edge) do if #below_edge >= min_col then -- aggregate aggr[other] = aggr[other] or 0 aggr[other] = aggr[other] + values[k] else -- do not aggregate aggr[k] = values[k] end end return aggr end -- ################################# -- This function actively resolves an host if there is not information about it. -- NOTE: prefer the host2name on this function function resolveAddress(hostinfo, allow_empty, shorten_len) local alt_name = ip2label(hostinfo["host"], hostinfo["vlan"], shorten_len) if(not isEmptyString(alt_name) and (alt_name ~= hostinfo["host"])) then -- The host label has priority return(alt_name) end local hostname = ntop.resolveName(hostinfo["host"]) if isEmptyString(hostname) then -- Not resolved if allow_empty == true then return hostname else -- this function will take care of formatting the IP return hostinfo2label(hostinfo, true, shorten_len) end end return hostinfo2label(hostinfo, true, shorten_len) end -- ########################################### function computeL7Stats(stats, show_breed, show_ndpi_category) local _ifstats = {} if(show_breed) then local breed_stats = {} for key, value in pairs(stats["ndpi"]) do local b = stats["ndpi"][key]["breed"] local traffic = stats["ndpi"][key]["bytes.sent"] + stats["ndpi"][key]["bytes.rcvd"] if(breed_stats[b] == nil) then breed_stats[b] = traffic else breed_stats[b] = breed_stats[b] + traffic end end for key, value in pairs(breed_stats) do _ifstats[key] = value end elseif(show_ndpi_category) then local ndpi_category_stats = {} for key, value in pairs(stats["ndpi_categories"]) do key = getCategoryLabel(key, value.category) local traffic = value["bytes"] if(ndpi_category_stats[key] == nil) then ndpi_category_stats[key] = traffic else ndpi_category_stats[key] = ndpi_category_stats[key] + traffic end end for key, value in pairs(ndpi_category_stats) do _ifstats[key] = value end else -- Add ARP to stats local arpBytes = 0 if(stats["eth"] ~= nil) then arpBytes = stats["eth"]["ARP_bytes"] if(arpBytes > 0) then _ifstats["ARP"] = arpBytes end end for key, value in pairs(stats["ndpi"]) do local traffic = value["bytes.sent"] + value["bytes.rcvd"] if(key == "Unknown") then traffic = traffic - arpBytes end if(traffic > 0) then if(show_breed) then _ifstats[value["breed"]] = traffic else _ifstats[key] = traffic end end end end return _ifstats end -- ############################################## function splitNetworkPrefix(net) if not net then tprint(debug.traceback()) end local prefix = tonumber(net:match("/(.+)")) local address = net:gsub("/.+","") return address, prefix end -- ############################################## function splitNetworkWithVLANPrefix(net_mask_vlan) local vlan = tonumber(net_mask_vlan:match("@(.+)")) local net_mask = net_mask_vlan:gsub("@.+","") local prefix = tonumber(net_mask:match("/(.+)")) local address = net_mask:gsub("/.+","") return address, prefix, vlan end -- ############################################## function splitProtocol(proto_string) local parts = string.split(proto_string, "%.") local app_proto local master_proto if parts == nil then master_proto = proto_string app_proto = nil else master_proto = parts[1] app_proto = parts[2] end return master_proto, app_proto end -- ############################################## function setHostAltName(host_info, alt_name) local host_key if type(host_info) == "table" then -- Note: we are not using hostinfo2hostkey which includes the -- vlan for backward compatibility, compatibility with -- the backend, and compatibility with the vpn scripts host_key = host_info["host"] -- hostinfo2hostkey(host_info) else host_key = host_info end local key = getHostAltNamesKey(host_key) if isEmptyString(alt_name) then ntop.delCache(key) else ntop.setCache(key, alt_name) end end -- ############################################## function setHostNotes(host_info, notes) local host_key if type(host_info) == "table" then -- Note: we are not using hostinfo2hostkey which includes the -- vlan for backward compatibility, compatibility with -- the backend, and compatibility with the vpn scripts host_key = host_info["host"] -- hostinfo2hostkey(host_info) else host_key = host_info end ntop.setCache(getHostNotesKey(host_key), notes) end -- ############################################## local function label2formattedlabel(alt_name, host_info, show_vlan, shorten_len) if not isEmptyString(alt_name) then local ip = host_info["ip"] or host_info["host"] -- Make it shorter local res = alt_name -- Special shorting function for IP addresses if res ~= ip then if (not shorten_len) or (shorten_len == false) then -- Don't touch the string, requested as-is without shortening elseif tonumber(shorten_len) then -- Shorten according to the specified length res = shortenString(res, shorten_len) else -- Use the default system-wide setting for the shortening res = shortenString(res) end end -- Adding the vlan if requested if show_vlan then local vlan = tonumber(host_info["vlan"]) if vlan and vlan > 0 then local full_vlan_name = getFullVlanName(vlan, true --[[ Compact --]]) res = string.format("%s@%s", res, full_vlan_name) end end return res end -- Fallback: just the IP and VLAN return(hostinfo2hostkey(host_info, true)) end -- ############################################## -- Attempt at retrieving an host label from an host_info, using local caches and DNS resolution. -- This can be more expensive if compared to only using information found inside host_info. local function hostinfo2label_resolved(host_info, show_vlan, shorten_len, skip_resolution) local ip = host_info["ip"] or host_info["host"] local res -- If local broadcast domain host and DHCP, try to get the label associated -- to the MAC address if host_info["mac"] and (host_info["broadcast_domain_host"] or host_info["dhcpHost"]) then res = getHostAltName(host_info["mac"]) end -- In case no resolution is requested, directly skip this part if (isEmptyString(res)) and (not skip_resolution) then -- Try and get the resolved name res = ntop.getResolvedName(ip) if not isEmptyString(res) then res = string.lower(res) else -- Nothing found, just fallback to the IP address res = ip end end return label2formattedlabel(res, host_info, show_vlan, shorten_len) end -- ############################################## -- Retrieve an host label from an host_info. The minimum fields of -- the host_info are "host" and "vlan", however a full JSON from Host::lua -- is needed to provide an accurate result. -- -- The following order is used to determine the label: -- MAC label (LBD hosts only), IP label, MDNS/DHCP name from C, resolved IP -- -- NOTE: The function attempt at labelling an host only using information found in host_info. -- In case host_info is not enough to label the host, then local caches and DNS resolution kick in -- to find a label (at the expense of extra time). function hostinfo2label(host_info, show_vlan, shorten_len, skip_resolution) local alt_name = nil local ip = host_info["ip"] or host_info["host"] -- Take the label as found in the host structure local res = host_info.label if isEmptyString(res) then -- Use any user-configured custom name -- This goes first as a label set by the user MUST take precedance over any other possibly available label res = getHostAltName(ip) if isEmptyString(res) then -- Read what is found inside host `name`, e.g., name as found by dissected traffic such as DHCP res = host_info["name"] if isEmptyString(res) then return hostinfo2label_resolved(host_info, show_vlan, shorten_len, skip_resolution) end end end return label2formattedlabel(res, host_info, show_vlan, shorten_len) end -- ############################################## -- Just a convenience function for hostinfo2label with only IP and VLAN function ip2label(ip, vlan, shorten_len) return hostinfo2label({host = ip, vlan = (vlan or 0)}, true, shorten_len) end -- ############################################## function mac2label(mac) local alt_name = getHostAltName(mac) if not isEmptyString(alt_name) and (alt_name ~= mac) then return(alt_name) end alt_name = ntop.getCache(getDhcpNameKey(interface.getId(), mac)) if not isEmptyString(alt_name) and (alt_name ~= mac) then return(alt_name) end -- Fallback: just the MAC return(mac) end -- ############################################## -- Mac Addresses -- local specialMACs = { "01:00:0C", "01:80:C2", "01:00:5E", "01:0C:CD", "01:1B:19", "FF:FF", "33:33" } function isSpecialMac(mac) for _,key in pairs(specialMACs) do if(string.contains(mac, key)) then return true end end return false end -- ############################################## -- Flow Utils -- function flowinfo2hostname(flow_info, host_type, alerts_view, add_hostname) local name local orig_name if (alerts_view and not hasClickHouseSupport()) or (add_hostname ~= nil and add_hostname == false) then -- do not return resolved name as it will hide the IP address return(flow_info[host_type..".ip"]) end if(flow_info == nil) then return("") end if(host_type == "srv") then if flow_info["host_server_name"] ~= nil and flow_info["host_server_name"] ~= "" and flow_info["host_server_name"]:match("%w") and not isIPv4(flow_info["host_server_name"]) and not isIPv6(flow_info["host_server_name"]) then -- remove possible ports from the name return(flow_info["host_server_name"]:gsub(":%d+$", "")) end if(flow_info["protos.tls.certificate"] ~= nil and flow_info["protos.tls.certificate"] ~= "") then return(flow_info["protos.tls.certificate"]) end end local hostinfo = { host = flow_info[host_type..".ip"], label = flow_info[host_type..".host"], mac = flow_info[host_type..".mac"], dhcpHost = flow_info[host_type..".dhcpHost"], broadcast_domain_host = flow_info[host_type..".broadcast_domain_host"], vlan = flow_info["vlan"], } return(hostinfo2label(hostinfo)) end -- ############################################## -- This function set the interface alias, return true if the -- alias is set, false otherwise function setInterfaceAlias(iface, alias) local ok = true if(isEmptyString(iface)) then ok = false end if(ok and (iface ~= alias) and not isEmptyString(alias)) then ntop.setCache(getInterfaceAliasKey(iface), alias) else ok = false end return ok end -- ############################################## function setLocalNetworkAlias(network, alias) if((network ~= alias) or isEmptyString(alias)) then ntop.setHashCache(getLocalNetworkAliasKey(), network, alias) else ntop.delHashCache(getLocalNetworkAliasKey(), network) end end -- ############################################## function setVlanAlias(vlan_id, alias) if((vlan_id ~= alias) or isEmptyString(alias)) then ntop.setHashCache(getVlanAliasKey(), vlan_id, alias) else ntop.delHashCache(getVlanAliasKey(), vlan_id) end end -- ############################################## function flow2hostinfo(host_info, host_type) local host_name local res = interface.getHostMinInfo(host_info[host_type .. ".ip"]) if((res == nil) or (res["name"] == nil)) then host_name = host_info[host_type .. ".ip"] else host_name = res["name"] end return({host = host_info[host_type .. ".ip"], vlan = host_info[host_type .. ".vlan"], name = host_name}) end -- ############################################## -- URL Util -- -- -- Split the host key (ip@vlan) creating a new lua table. -- Example: -- info = hostkey2hostinfo(key) -- ip = info["host"] -- vlan = info["vlan"] -- function hostkey2hostinfo(key) local host = {} local info = split(key,"@") if(info[1] ~= nil) then host["host"] = info[1] end if(info[2] ~= nil) then host["vlan"] = tonumber(info[2]) else host["vlan"] = 0 end return host end -- ############################################## function isHostKey(key) local info = split(key,"@") -- Check format if not info or #info < 1 or #info > 2 then return false end -- Check IP format if isEmptyString(info[1]) or (not isIPv4(info[1]) and not isIPv6(info[1])) then return false end -- Check VLAN format (if any) if not isEmptyString(info[2]) and tonumber(info[2]) == nil then return false end -- Ok return true end -- ############################################## -- -- Analyze the host_info table and return the host key. -- Example: -- host_info = interface.getHostInfo("127.0.0.1",0) -- key = hostinfo2hostkey(host_info) -- function hostinfo2hostkey(host_info, host_type, show_vlan) local rsp = "" if(host_type == "cli") then local cli_ip = host_info["cli.ip"] or host_info["cli_ip"] if cli_ip then rsp = rsp..cli_ip end elseif(host_type == "srv") then local srv_ip = host_info["srv.ip"] or host_info["srv_ip"] if srv_ip then rsp = rsp..srv_ip end else if(host_info["ip"] ~= nil) then rsp = rsp..host_info["ip"] elseif(host_info["mac"] ~= nil) then rsp = rsp..host_info["mac"] elseif(host_info["host"] ~= nil) then rsp = rsp..host_info["host"] elseif(host_info["name"] ~= nil) then rsp = rsp..host_info["name"] end end local vlan_id = tonumber(host_info["vlan"] or host_info["vlan_id"] or 0) if vlan_id ~= 0 or show_vlan then rsp = rsp..'@'..tostring(vlan_id) end if(debug_host) then traceError(TRACE_DEBUG,TRACE_CONSOLE,"HOST2URL => ".. rsp .. "\n") end return rsp end -- ############################################## function member2visual(member) local info = hostkey2hostinfo(member) local host = info.host local hlen = string.len(host) if string.ends(host, "/32") and isIPv4(string.sub(host, 1, hlen-3)) then host = string.sub(host, 1, hlen-3) elseif string.ends(host, "/128") and isIPv6(string.sub(host, 1, hlen-4)) then host = string.sub(host, 1, hlen-4) end return hostinfo2hostkey({host=host, vlan=info.vlan}) end -- ############################################## -- -- Catch the main information about an host from the host_info table and return the corresponding json. -- Example: -- hostinfo2json(host[key]), return a json string based on the host value -- hostinfo2json(flow[key],"cli"), return a json string based on the client host information in the flow table -- hostinfo2json(flow[key],"srv"), return a json string based on the server host information in the flow table -- function hostinfo2json(host_info,host_type) local rsp = '' if(host_type == "cli") then if(host_info["cli.ip"] ~= nil) then rsp = rsp..'host: "'..host_info["cli.ip"]..'"' end elseif(host_type == "srv") then if(host_info["srv.ip"] ~= nil) then rsp = rsp..'host: "'..host_info["srv.ip"]..'"' end else if((type(host_info) ~= "table") and (string.find(host_info,"@"))) then host_info = hostkey2hostinfo(host_info) end if(host_info["host"] ~= nil) then rsp = rsp..'host: "'..host_info["host"]..'"' elseif(host_info["ip"] ~= nil) then rsp = rsp..'host: "'..host_info["ip"]..'"' elseif(host_info["name"] ~= nil) then rsp = rsp..'host: "'..host_info["name"] ..'"' elseif(host_info["mac"] ~= nil) then rsp = rsp..'host: "'..host_info["mac"] ..'"' end end if((host_info["vlan"] ~= nil) and (host_info["vlan"] ~= 0)) then rsp = rsp..', vlan: "'..tostring(host_info["vlan"]) .. '"' end if(debug_host) then traceError(TRACE_DEBUG,TRACE_CONSOLE,"HOST2JSON => ".. rsp .. "\n") end return rsp end -- ############################################## -- -- Catch the main information about an host from the host_info table and return the corresponding jqueryid. -- Example: host 192.168.1.254, vlan0 ==> 1921681254_0 function hostinfo2jqueryid(host_info,host_type) local rsp = '' if(host_type == "cli") then if(host_info["cli.ip"] ~= nil) then rsp = rsp..''..host_info["cli.ip"] end elseif(host_type == "srv") then if(host_info["srv.ip"] ~= nil) then rsp = rsp..''..host_info["srv.ip"] end else if((type(host_info) ~= "table") and (string.find(host_info,"@"))) then host_info = hostkey2hostinfo(host_info) end if(host_info["host"] ~= nil) then rsp = rsp..''..host_info["host"] elseif(host_info["ip"] ~= nil) then rsp = rsp..''..host_info["ip"] elseif(host_info["name"] ~= nil) then rsp = rsp..''..host_info["name"] elseif(host_info["mac"] ~= nil) then rsp = rsp..''..host_info["mac"] end end if((host_info["vlan"] ~= nil) and (host_info["vlan"] ~= 0)) then rsp = rsp..'@'..tostring(host_info["vlan"]) end rsp = string.gsub(rsp, "%.", "__") rsp = string.gsub(rsp, "/", "___") rsp = string.gsub(rsp, ":", "____") if(debug_host) then traceError(TRACE_DEBUG,TRACE_CONSOLE,"HOST2KEY => ".. rsp .. "\n") end return rsp end -- ############################################## function isPausedInterface(current_ifname) if(not isEmptyString(_POST["toggle_local"])) then return(_POST["toggle_local"] == "0") end state = ntop.getCache("ntopng.prefs.ifid_"..tostring(interface.name2id(current_ifname)).."_not_idle") if(state == "0") then return true else return false end end -- ############################################## function tablePreferences(key, value, force_set) if not _SESSION then -- Not in a user session, ignore preferences return end table_key = getRedisPrefix("ntopng.sort.table") if((value == nil) or (value == "")) and (force_set ~= true) then -- Get preferences return ntop.getHashCache(table_key, key) else -- Set preferences ntop.setHashCache(table_key, key, value) return(value) end end -- ############################################## function setInterfaceRegreshRate(ifid, refreshrate) local key = "ntopng.prefs.ifid_"..tostring(ifid)..".refresh_rate" if isEmptyString(refreshrate) then ntop.delCache(key) else ntop.setCache(key, tostring(refreshrate)) end end -- ############################################## -- "Some Very Long String" -> "Some Ver...g String" function shortenCollapse(s, max_len) local replacement = "..." local r_len = string.len(replacement) local s_len = string.len(s) if max_len == nil then max_len = ntop.getPref("ntopng.prefs.max_ui_strlen") max_len = tonumber(max_len) if(max_len == nil) then max_len = 24 end end if max_len <= r_len then return replacement end if s_len > max_len then local half = math.floor((max_len-r_len) / 2) return string.sub(s, 1, half) .. replacement .. string.sub(s, s_len-half+1) end return s end -- ############################################## function harvestUnusedDir(path, min_epoch) local files = ntop.readdir(path) -- print("Reading "..path.."
\n") for k,v in pairs(files) do if(v ~= nil) then local p = os_utils.fixPath(path .. "/" .. v) if(ntop.isdir(p)) then harvestUnusedDir(p, min_epoch) else local when = ntop.fileLastChange(path) if((when ~= -1) and (when < min_epoch)) then os.remove(p) end end end end end -- ############################################## function harvestJSONTopTalkers(days) local when = os.time() - 86400 * days ifnames = interface.getIfNames() for _,ifname in pairs(ifnames) do interface.select(ifname) local _ifstats = interface.getStats() local dirs = ntop.getDirs() local basedir = os_utils.fixPath(dirs.workingdir .. "/" .. _ifstats.id) harvestUnusedDir(os_utils.fixPath(basedir .. "/top_talkers"), when) harvestUnusedDir(os_utils.fixPath(basedir .. "/flows"), when) end end -- ############################################### -- prints purged information for hosts / flows function purgedErrorString() local info = ntop.getInfo(false) return i18n("purged_error_message",{url=ntop.getHttpPrefix()..'/lua/admin/prefs.lua?tab=in_memory', product=info["product"]}) end -- print TCP flags function formatTCPFlags(flags) local out = '' if(hasbit(flags,0x02)) then out = out .. 'S ' end if(hasbit(flags,0x10)) then out = out .. 'A ' end if(hasbit(flags,0x01)) then out = out .. 'F ' end if(hasbit(flags,0x08)) then out = out .. 'P ' end if(hasbit(flags,0x04)) then out = out .. 'R ' end if(hasbit(flags,0x20)) then out = out .. 'U ' end if(hasbit(flags,0x40)) then out = out .. 'E ' end if(hasbit(flags,0x80)) then out = out .. 'C ' end return out end -- convert the integer carrying TCP flags in a more convenient lua table function TCPFlags2table(flags) local res = { ["FIN"] = 0, ["SYN"] = 0, ["RST"] = 0, ["PSH"] = 0, ["ACK"] = 0, ["URG"] = 0, ["ECE"] = 0, ["CWR"] = 0, } if(hasbit(flags,0x01)) then res["FIN"] = 1 end if(hasbit(flags,0x02)) then res["SYN"] = 1 end if(hasbit(flags,0x04)) then res["RST"] = 1 end if(hasbit(flags,0x08)) then res["PSH"] = 1 end if(hasbit(flags,0x10)) then res["ACK"] = 1 end if(hasbit(flags,0x20)) then res["URG"] = 1 end if(hasbit(flags,0x40)) then res["ECE"] = 1 end if(hasbit(flags,0x80)) then res["CWR"] = 1 end return res end -- ########################################## function historicalProtoHostHref(ifId, host, l4_proto, ndpi_proto_id, info, vlan, no_print) if ntop.isEnterpriseM() then local now = os.time() local ago1h = now - 3600 local prefs = ntop.getPrefs() if prefs.is_dump_flows_to_clickhouse_enabled then local hist_url = ntop.getHttpPrefix().."/lua/pro/db_search.lua?" local params = {epoch_end = now, epoch_begin = ago1h, ifid = ifId} if host then local host_k = hostinfo2hostkey(host) if isEmptyString(host_k) then host_k = host end params["ip"] = host_k..";eq" end if l4_proto then params["l4proto"] = l4_proto..";eq" end if ndpi_proto_id then params["l7proto"] = ndpi_proto_id..";eq" end if vlan and vlan ~= 0 then params["vlan_id"] = vlan..";eq" end if info then params["info"] = info..";in" end local url_params = table.tconcat(params, "=", "&") if not no_print then print(' ') -- print('') print('') -- print('') else return '' end end end end -- #################################################### function tableToJsObject(lua_table) local json = require("dkjson") return json.encode(lua_table, nil) end -- #################################################### -- @brief The difference, in seconds, between the local time of this instance and GMT local server_timezone_diff_seconds -- #################################################### -- @brief Converts a datetime string into an epoch, adjusted with the client time function makeTimeStamp(d) local pattern = "(%d+)%/(%d+)%/(%d+) (%d+):(%d+):(%d+)" local day, month, year, hour, minute, seconds = string.match(d, pattern); -- Get the epoch out of d. The epoch gets adjusted by os.time in the server timezone, that is, in -- the timezone of this running ntopng instance -- See https://www.lua.org/pil/22.1.html local server_epoch = os.time({year = year, month = month, day = day, hour = hour, min = minute, sec = seconds}); -- Convert the server_epoch into a gmt_epoch which is adjusted to GMT local gmt_epoch = server_epoch + get_server_timezone_diff_seconds() -- Finally, compute a client_epoch by adding the seconds of getFrontendTzSeconds() to the GMT epoch just computed local client_epoch = gmt_epoch + getFrontendTzSeconds() -- Now we can compute the deltas to know the extact number of seconds between the server and the client timezone local server_to_gmt_delta = gmt_epoch - server_epoch local gmt_to_client_delta = client_epoch - gmt_epoch local server_to_client_delta = client_epoch - server_epoch -- Make sure everything is OK... assert(server_to_client_delta == server_to_gmt_delta + gmt_to_client_delta) -- tprint({ -- server_ts = server_epoch, -- gmt_ts = gmt_epoch, -- server_to_gmt_delta = (server_to_gmt_delta) / 60 / 60, -- gmt_to_client_delta = (gmt_to_client_delta) / 60 / 60, -- server_to_client_delta = (server_to_client_delta) / 60 / 60 -- }) -- Return the epoch in the client timezone return string.format("%u", server_epoch - server_to_client_delta) end -- ########################################### -- Note: the base unit is Kbps here FMT_TO_DATA_RATES_KBPS = { ["k"] = {label="Kbps", value=1}, ["m"] = {label="Mbps", value=1000}, ["g"] = {label="Gbps", value=1000*1000}, } FMT_TO_DATA_BYTES = { ["b"] = {label="B", value=1}, ["k"] = {label="KB", value=1024}, ["m"] = {label="MB", value=1024*1024}, ["g"] = {label="GB", value=1024*1024*1024}, } FMT_TO_DATA_TIME = { ["s"] = {label=i18n("metrics.secs"), value=1}, ["m"] = {label=i18n("metrics.mins"), value=60}, ["h"] = {label=i18n("metrics.hours"), value=3600}, ["d"] = {label=i18n("metrics.days"), value=3600*24}, } -- ########################################### -- -- Extracts parameters from a lua table. -- This function performs the inverse conversion of javascript paramsPairsEncode. -- -- Note: plain parameters (not encoded with paramsPairsEncode) remain unchanged only -- when strict mode is *not* enabled -- function paramsPairsDecode(params, strict_mode) local res = {} for k,v in pairs(params) do local sp = split(k, "key_") if #sp == 2 then local keyid = sp[2] local value = "val_"..keyid if params[value] then res[v] = params[value] end end if((not strict_mode) and (res[v] == nil)) then -- this is a plain parameter res[k] = v end end return res end function isBridgeInterface(ifstats) return ifstats.inline end function hasSnmpDevices(ifid) if (not ntop.isEnterpriseM()) or (not isAdministrator()) then return false end return has_snmp_devices(ifid) end function stripVlan(name) local key = string.split(name, "@") if((key ~= nil) and (#key == 2)) then -- Verify that the host is actually an IP address and the VLAN actually -- a number to avoid stripping things that are not vlans (e.g. part of an host name) local addr = key[1] if((tonumber(key[2]) ~= nil) and (isIPv6(addr) or isIPv4(addr))) then return(addr) end end return(name) end -- ########################################### function swapKeysValues(tbl) local new_tbl = {} for k, v in pairs(tbl or {}) do new_tbl[v] = k end return new_tbl end -- ########################################### function tsQueryToTags(query) local tags = {} for _, part in pairs(split(query, ",")) do local sep_pos = string.find(part, ":") if sep_pos then local k = string.sub(part, 1, sep_pos-1) local v = string.sub(part, sep_pos+1) tags[k] = v end end return tags end function tsTagsToQuery(tags) return table.tconcat(tags, ":", ",") end -- ########################################### -- Compares IPv4 / IPv6 addresses function ip_address_asc(a, b) return(ntop.ipCmp(a, b) < 0) end function ip_address_rev(a, b) return(ntop.ipCmp(a, b) > 0) end -- ########################################### -- @brief Deletes all the cache/prefs keys matching the pattern function deleteCachePattern(pattern) local keys = ntop.getKeysCache(pattern) for key in pairs(keys or {}) do ntop.delCache(key) end end -- ########################################### -- NOTE: '~= "0"' is used for prefs which are enabled by default function areInterfaceTimeseriesEnabled(ifid) return((ntop.getPref("ntopng.prefs.interface_rrd_creation") ~= "0")) end function areInterfaceL7TimeseriesEnabled(ifid) return(areInterfaceTimeseriesEnabled(ifid) and (ntop.getPref("ntopng.prefs.interface_ndpi_timeseries_creation") ~= "per_category")) end function areInterfaceCategoriesTimeseriesEnabled(ifid) local rv = ntop.getPref("ntopng.prefs.interface_ndpi_timeseries_creation") -- note: categories are disabled by default return(areInterfaceTimeseriesEnabled(ifid) and ((rv == "per_category") or (rv == "both"))) end function areHostTimeseriesEnabled(ifid) local rv = ntop.getPref("ntopng.prefs.hosts_ts_creation") if isEmptyString(rv) then rv = "light" end return((rv == "light") or (rv == "full")) end function areHostL7TimeseriesEnabled(ifid) local rv = ntop.getPref("ntopng.prefs.host_ndpi_timeseries_creation") -- note: host protocols are disabled by default return((ntop.getPref("ntopng.prefs.hosts_ts_creation") == "full") and ((rv == "per_protocol") or (rv == "both"))) end function areHostCategoriesTimeseriesEnabled(ifid) local rv = ntop.getPref("ntopng.prefs.host_ndpi_timeseries_creation") -- note: host protocols are disabled by default return((ntop.getPref("ntopng.prefs.hosts_ts_creation") == "full") and ((rv == "per_category") or (rv == "both"))) end function areSystemTimeseriesEnabled() return(ntop.getPref("ntopng.prefs.system_probes_timeseries") ~= "0") end function areHostPoolsTimeseriesEnabled(ifid) return(ntop.isPro() and (ntop.getPref("ntopng.prefs.host_pools_rrd_creation") == "1")) end function areASTimeseriesEnabled(ifid) return(ntop.getPref("ntopng.prefs.asn_rrd_creation") == "1") end function areInternalTimeseriesEnabled(ifid) -- NOTE: no separate preference so far return(areSystemTimeseriesEnabled()) end function areCountryTimeseriesEnabled(ifid) return((ntop.getPref("ntopng.prefs.country_rrd_creation") == "1")) end function areOSTimeseriesEnabled(ifid) return((ntop.getPref("ntopng.prefs.os_rrd_creation") == "1")) end function areVlanTimeseriesEnabled(ifid) return(ntop.getPref("ntopng.prefs.vlan_rrd_creation") == "1") end function areMacsTimeseriesEnabled(ifid) return(ntop.getPref("ntopng.prefs.l2_device_rrd_creation") == "1") end function areContainersTimeseriesEnabled(ifid) -- NOTE: no separate preference so far return(true) end function areSnmpTimeseriesEnabled(device, port_idx) return(ntop.getPref("ntopng.prefs.snmp_devices_rrd_creation") == "1") end function areFlowdevTimeseriesEnabled(ifid, device) return(ntop.getPref("ntopng.prefs.flow_device_port_rrd_creation") == "1") end -- ########################################### -- version is major.minor.veryminor function version2int(v) if(v == nil) then return(0) end e = string.split(v, "%."); if(e ~= nil) then major = e[1] minor = e[2] veryminor = e[3] if(major == nil or tonumber(major) == nil or type(major) ~= "string") then major = 0 end if(minor == nil or tonumber(minor) == nil or type(minor) ~= "string") then minor = 0 end if(veryminor == nil or tonumber(veryminor) == nil or type(veryminor) ~= "string") then veryminor = 0 end version = tonumber(major)*1000 + tonumber(minor)*100 -- + tonumber(veryminor) return(version) else return(0) end end --- Check if there is a new major release --- @return string message If there is a new major release then return a non-nil string --- containing the update message. function check_latest_major_release() if ntop.isOffline() then return nil end -- get the latest major release local latest_version = ntop.getCache("ntopng.cache.major_release") -- tprint(debug.traceback()) if isEmptyString(latest_version) then local rsp = ntop.httpGet("https://www.ntop.org/ntopng.version", "", "", 10 --[[ seconds ]]) if (not isEmptyString(rsp)) and (not isEmptyString(rsp["CONTENT"])) then latest_version = trimSpace(string.gsub(rsp["CONTENT"], "\n", "")) else -- a value that won't trigger an update message latest_version = "0.0.0" end ntop.setCache("ntopng.cache.major_release", latest_version, 86400 --[[ recheck interval]]) end return get_version_update_msg(info, latest_version) end -- ########################################### -- To be called inside the flows tableCallback function initFlowsRefreshRows() print[[ datatableInitRefreshRows($("#table-flows"), "key_and_hash", 10000, { /* List of rows with trend icons */ "column_thpt": ]] print(ternary(getThroughputType() ~= "bps", "NtopUtils.fpackets", "NtopUtils.bitsToSize")) print[[, "column_bytes": NtopUtils.bytesToSize, }); $("#dt-bottom-details > .float-left > p").first().append('. ]] print(i18n('flows_page.idle_flows_not_listed')) print[[');]] end -- ########################################### function canRestoreHost(ifid, ip, vlan) local ip_to_mac = string.format("ntopng.ip_to_mac.ifid_%u__%s@%d", ifid, ip, vlan) local key_to_check -- Check if there is a MAC address associated local mac = ntop.getCache(ip_to_mac) if not isEmptyString(mac) then key_to_check = string.format("ntopng.serialized_hostsbymac.ifid_%u__%s_%s", ifid, mac, ternary(isIPv4(ip), "v4", "v6")) else key_to_check = string.format("ntopng.serialized_hosts.ifid_%u__%s@%d", ifid, ip, vlan) end return(not table.empty(ntop.getKeysCache(key_to_check))) end -- ########################################### function create_ndpi_proto_name(v) local app = "" if v["proto.ndpi"] then app = getApplicationLabel(v["proto.ndpi"]) else local master_proto = interface.getnDPIProtoName(tonumber(v["l7_master_proto"])) local app_proto = interface.getnDPIProtoName(tonumber(v["l7_proto"])) if master_proto == app_proto then app = app_proto elseif master_proto == "Unknown" then app = app_proto else app = master_proto if app_proto ~= "Unknown" then app = app .. "." .. app_proto end end app = getApplicationLabel(app) end return app end -- ############################################## function setObsPointAlias(observation_point_id, alias) if((observation_point_id ~= alias) and not isEmptyString(alias)) then ntop.setHashCache(getObsPointAliasKey(), observation_point_id, alias) else ntop.delHashCache(getObsPointAliasKey(), observation_point_id) end end -- ############################################## function setFlowDevAlias(flowdev_ip, alias) if((flowdev_ip ~= alias) and not isEmptyString(alias)) then ntop.setHashCache(getFlowDevAliasKey(), flowdev_ip, alias) else ntop.delHashCache(getFlowDevAliasKey(), flowdev_ip) end end -- ############################################## function addScoreToAlertDescr(msg, score) return (msg .. string.format(" [%s: %s]", i18n("score"), format_utils.formatValue(score))) end -- ############################################## function addHTTPInfoToAlertDescr(msg, alert_json, url_only) if ((alert_json) and (table.len(alert_json["proto"] or {}) > 0) and (table.len(alert_json["proto"]["http"] or {}) > 0)) then local http_info = format_http_info({ http_info = alert_json["proto"]["http"]["last_method"], last_return_code = alert_json["proto"]["http"]["last_return_code"], last_user_agent = alert_json["proto"]["http"]["last_user_agent"], last_url = alert_json["proto"]["http"]["last_url"] }) if http_info["last_method"] then msg = msg .. string.format(" [ %s: %s ]", i18n("db_explorer.http_method"), http_info["last_method"]) end if http_info["last_return_code"] then msg = msg .. string.format(" [ %s: %s ]", i18n("last_response_status_code"), http_info["last_return_code"]) end if http_info["last_user_agent"] then msg = msg .. string.format(" [ %s: %s ]", i18n("last_user_agent"), http_info["last_user_agent"]) end if http_info["last_url"] then msg = msg .. string.format(" [ %s: %s ]", i18n("last_url"), http_info["last_url"]) end end return msg end -- ############################################## function addDNSInfoToAlertDescr(msg, alert_json) if ((alert_json) and (table.len(alert_json["proto"] or {}) > 0) and (table.len(alert_json["proto"]["dns"] or {}) > 0)) then local dns_info = format_dns_query_info({ last_query_type = alert_json["proto"]["dns"]["last_query_type"], last_return_code = alert_json["proto"]["dns"]["last_return_code"], last_query = alert_json["proto"]["dns"]["last_query"] }) if dns_info["last_query_type"] then msg = msg .. string.format(" [ %s: %s ]", i18n("last_query_type"), dns_info["last_query_type"]) end if dns_info["last_return_code"] then msg = msg .. string.format(" [ %s: %s ]", i18n("last_return_code"), dns_info["last_return_code"]) end if dns_info["last_query"] then msg = msg .. string.format(" [ %s: %s ]", i18n("last_url"), dns_info["last_query"]) end end return msg end -- ############################################## function addTLSInfoToAlertDescr(msg, alert_json) if ((alert_json) and (table.len(alert_json["proto"] or {}) > 0) and (table.len(alert_json["proto"]["tls"] or {}) > 0)) then local tls_info = format_tls_info({ notBefore = alert_json["proto"]["tls"]["notBefore"], notAfter = alert_json["proto"]["tls"]["notAfter"], client_requested_server_name = alert_json["proto"]["tls"]["client_requested_server_name"], ['ja3.server_unsafe_cipher'] = alert_json["proto"]["tls"]["ja3.server_unsafe_cipher"] }) if tls_info["tls_certificate_validity"] then msg = msg .. string.format(" [ %s: %s ]", i18n("tls_certificate_validity"), tls_info["tls_certificate_validity"]) end if tls_info["ja3.server_unsafe_cipher"] then msg = msg .. string.format(" [ %s: %s ]", i18n("ja3.server_unsafe_cipher"), tls_info["ja3.server_unsafe_cipher"]) end if tls_info["client_requested_server_name"] then msg = msg .. string.format(" [ %s: %s ]", i18n("client_requested_server_name"), tls_info["client_requested_server_name"]) end end return msg end -- ############################################## function addICMPInfoToAlertDescr(msg, alert_json) if ((alert_json) and (table.len(alert_json["proto"] or {}) > 0) and (table.len(alert_json["proto"]["icmp"] or {}) > 0)) then local icmp_info = format_icmp_info({ code = alert_json["proto"]["icmp"]["code"], type = alert_json["proto"]["icmp"]["type"] }) -- Already formatted by the function if icmp_info["type"] then msg = msg .. string.format(" [ %s: %s ]", i18n("icmp_type"), icmp_info["type"]) end if icmp_info["code"] then msg = msg .. string.format(" [ %s: %s ]", i18n("icmp_code"), icmp_info["code"]) end end return msg end -- ############################################## function addBytesInfoToAlertDescr(msg, value) msg = string.format("%s [ %s: %s | %s: %s ]", msg, i18n("server_traffic"), bytesToSize(value["srv2cli_bytes"] or 0), i18n("client_traffic"), bytesToSize(value["cli2srv_bytes"] or 0)) return msg end -- ############################################## function addExtraFlowInfo(alert_json, value) local msg = "" msg = addHTTPInfoToAlertDescr(msg, alert_json) msg = addDNSInfoToAlertDescr(msg, alert_json) msg = addTLSInfoToAlertDescr(msg, alert_json) msg = addICMPInfoToAlertDescr(msg, alert_json) msg = addBytesInfoToAlertDescr(msg, value) return msg end -- ############################################## function hostnameIsDomain(name) if not isEmptyString(name) then local parts = string.split(name, "%.") if parts and #parts > 1 then local last = parts[#parts] if string.len(last) > 0 then return true end end end return false end -- ############################################## local iec104_typeids = { M_SP_NA_1=0x01, M_SP_TA_1=0x02, M_DP_NA_1=0x03, M_DP_TA_1=0x04, M_ST_NA_1=0x05, M_ST_TA_1=0x06, M_BO_NA_1=0x07, M_BO_TA_1=0x08, M_ME_NA_1=0x09, M_ME_TA_1=0x0A, M_ME_NB_1=0x0B, M_ME_TB_1=0x0C, M_ME_NC_1=0x0D, M_ME_TC_1=0x0E, M_IT_NA_1=0x0F, M_IT_TA_1=0x10, M_EP_TA_1=0x11, M_EP_TB_1=0x12, M_EP_TC_1=0x13, M_PS_NA_1=0x14, M_ME_ND_1=0x15, M_SP_TB_1=30, M_DP_TB_1=31, M_ST_TB_1=32, M_BO_TB_1=33, M_ME_TD_1=34, M_ME_TE_1=35, M_ME_TF_1=36, M_IT_TB_1=37, M_EP_TD_1=38, M_EP_TE_1=39, M_EP_TF_1=40, ASDU_TYPE_41=41, ASDU_TYPE_42=42, ASDU_TYPE_43=43, ASDU_TYPE_44=44, C_SC_NA_1=45, C_DC_NA_1=46, C_RC_NA_1=47, C_SE_NA_1=48, C_SE_NB_1=49, C_SE_NC_1=50, C_BO_NA_1=51, C_SC_TA_1=58, C_DC_TA_1=59, C_RC_TA_1=60, C_SE_TA_1=61, C_SE_TB_1=62, C_SE_TC_1=63, C_BO_TA_1=64, M_EI_NA_1=70, C_IC_NA_1=100, C_CI_NA_1=101, C_RD_NA_1=102, C_CS_NA_1=103, C_TS_NA_1=104, C_RP_NA_1=105, C_CD_NA_1=106, C_TS_TA_1=107, P_ME_NA_1=110, P_ME_NB_1=111, P_ME_NC_1=112, P_AC_NA_1=113, F_FR_NA_1=120, F_SR_NA_1=121, F_SC_NA_1=122, F_LS_NA_1=123, F_FA_NA_1=124, F_SG_NA_1=125, F_DR_TA_1=126, } function iec104_typeids2str(c) if(c == nil) then return end for s, v in pairs(iec104_typeids) do if(v == tonumber(c)) then return(s.." (".. v ..")") end end return(c) end -- ############################################## function format_device_name(device_ip, short_version) local device_name = device_ip if ntop.isPro() then device_name = hostinfo2label(hostkey2hostinfo(device_ip)) if device_name ~= device_ip then if short_version then device_name = shortenString(device_name, 32) end device_name = string.format('%s [%s]', device_ip, device_name) end end return device_name end -- ############################################## -- This function, given a record and a name return a standard formatted value -- and if the value is 0, an empty string is returned -- e.g. 1000 -> 1,000 | 0 -> function format_high_num_value_for_tables(record, name) local formatted_record = format_utils.formatValue(record[name] or 0) if formatted_record == '0' then formatted_record = '' end return formatted_record end -- ############################################## require "lua_utils_print" require "lua_utils_get" require "lua_utils_gui" if(trace_script_duration ~= nil) then io.write(debug.getinfo(1,'S').source .." executed in ".. (os.clock()-clock_start)*1000 .. " ms\n") end