ntopng/scripts/lua/modules/lua_utils.lua
Luca Deri 4bf10f4fce Added user role in About page
Fixed SQL when exploring subnets
Modified preferences to specify that when nDPI RRDs are created, they do for both local hosts and networks
2015-09-14 14:16:11 +02:00

1790 lines
No EOL
46 KiB
Lua

--
-- (C) 2014-15 - ntop.org
--
require "lua_trace"
function getInterfaceName(interface_id)
local ifnames = interface.getIfNames()
interface_id = tonumber(interface_id)
for _,if_name in pairs(ifnames) do
interface.select(if_name)
_ifstats = interface.getStats()
if(_ifstats.id == interface_id) then
return(_ifstats.name)
end
end
return("")
end
function getInterfaceId(interface_name)
return(interface.name2id(interface_name))
end
-- Note that ifname can be set by Lua.cpp so don't touch it if already defined
if((ifname == nil) and (_GET ~= nil)) then
ifname = _GET["ifname"]
if(ifname ~= nil) then
if(ifname.."" == tostring(tonumber(ifname)).."") then
-- ifname does not contain the interface name but rather the interface id
ifname = getInterfaceName(ifname)
if(ifname == "") then ifname = nil end
end
end
if(debug_session) then traceError(TRACE_DEBUG,TRACE_CONSOLE, "Session => Session:".._SESSION["session"]) end
if((ifname == nil) and (_SESSION ~= nil)) then
if(debug_session) then traceError(TRACE_DEBUG,TRACE_CONSOLE, "Session => set ifname by _SESSION value") end
ifname = _SESSION["ifname"]
if(debug_session) then traceError(TRACE_DEBUG,TRACE_CONSOLE, "Session => ifname:"..ifname) end
else
if(debug_session) then traceError(TRACE_DEBUG,TRACE_CONSOLE, "Session => set ifname by _GET value") end
end
end
--print("(((("..ifname.."))))")
l4_keys = {
{ "TCP", "tcp" },
{ "UDP", "udp" },
{ "ICMP", "icmp" },
{ "Other IP", "other ip" }
}
function __FILE__() return debug.getinfo(2,'S').source end
function __LINE__() return debug.getinfo(2, 'l').currentline end
function sendHTTPHeaderIfName(mime, ifname, maxage)
info = ntop.getInfo()
print('HTTP/1.1 200 OK\r\n')
print('Cache-Control: max-age=0, no-cache, no-store\r\n')
print('Server: ntopng '..info["version"]..' ['.. info["platform"]..']\r\n')
print('Pragma: no-cache\r\n')
print('X-Frame-Options: DENY\r\n')
print('X-Content-Type-Options: nosniff\r\n')
if(_SESSION ~= nil) then print('Set-Cookie: session='.._SESSION["session"]..'; max-age=' .. maxage .. '; path=/; HttpOnly\r\n') end
if(ifname ~= nil) then print('Set-Cookie: ifname=' .. ifname .. '; path=/\r\n') end
print('Content-Type: '.. mime ..'\r\n')
print('Last-Modified: '..os.date("!%a, %m %B %Y %X %Z").."\r\n")
print('\r\n')
end
function sendHTTPHeaderLogout(mime)
sendHTTPHeaderIfName(mime, nil, 0)
end
function sendHTTPHeader(mime)
sendHTTPHeaderIfName(mime, nil, 3600)
end
function printGETParameters(get)
for key, value in pairs(get) do
io.write(key.."="..value.."\n")
end
end
function isEmptyString(str)
-- io.write(str..'\n')
if((str == nil) or (str == "")) then
return true
else
return false
end
end
function findString(str, tofind)
local upper_lower = true
if(str == nil) then return(nil) end
if(tofind == nil) then return(nil) end
str1 = string.gsub(str, "-", "_")
tofind1 = string.gsub(tofind, "-", "_")
rsp = string.find(str1, tofind1, 1)
if(upper_lower) then
if(rsp == nil) then
-- Lowercase
str1 = string.lower(str1)
tofind1 = string.lower(tofind1)
rsp = string.find(str1, tofind1, 1)
end
if(rsp == nil) then
-- Uppercase
str1 = string.upper(str1)
tofind1 = string.upper(tofind1)
rsp = string.find(str1, tofind1, 1)
end
end
--print(str1 .. "/" .. tofind1.."\n")
--print(rsp)
--print("\n")
return(rsp)
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
function string.contains(String,Start)
return(string.find(String,Start,1) ~= nil)
end
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
end
function string.ends(String,End)
return End=='' or string.sub(String,-string.len(End))==End
end
function printASN(asn, asname)
if(asn > 0) then
return("<A HREF='http://as.robtex.com/as"..asn..".html' title='"..asname.."'>"..asname.."</A> <i class='fa fa-external-link fa-lg'></i>")
else
return(asname)
end
end
function shortenString(name)
max_len = 24
if(string.len(name) < max_len) then
return(name)
else
return(string.sub(name, 1, max_len).."...")
end
end
function shortHostName(name)
local chunks = {name:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")}
if(#chunks == 4) then
return(name)
else
max_len = 24
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
epp_rrd_names = {
{ "Positive Replies Number", "num_replies_ok.rrd" },
{ "Error Replies Number", "num_replies_error.rrd" },
{ "Query Number", "num_queries.rrd" },
{ "domain-create", "num_cmd_1.rrd" },
{ "domain-update", "num_cmd_2.rrd" },
{ "domain-delete", "num_cmd_3.rrd" },
{ "domain-restore", "num_cmd_4.rrd" },
{ "domain-transfer", "num_cmd_5.rrd" },
{ "domain-transfer-trade", "num_cmd_6.rrd" },
{ "domain-transfer-request", "num_cmd_7.rrd" },
{ "domain-transfer-trade-request", "num_cmd_8.rrd" },
{ "domain-transfer-cancel", "num_cmd_9.rrd" },
{ "domain-transfer-approve", "num_cmd_10.rrd" },
{ "domain-transfer-reject", "num_cmd_11.rrd" },
{ "contact-create", "num_cmd_12.rrd" },
{ "contact-update", "num_cmd_13.rrd" },
{ "contact-delete", "num_cmd_14.rrd" },
{ "domain-update-hosts", "num_cmd_15.rrd" },
{ "domain-update-statuses", "num_cmd_16.rrd" },
{ "domain-update-contacts", "num_cmd_17.rrd" },
{ "domain-trade", "num_cmd_18.rrd" },
{ "domain-update-simple", "num_cmd_19.rrd" },
{ "domain-info", "num_cmd_20.rrd" },
{ "contact-info", "num_cmd_21.rrd" },
{ "domain-check", "num_cmd_22.rrd" },
{ "contact-check", "num_cmd_23.rrd" },
{ "poll-req", "num_cmd_24.rrd" },
{ "domain-transfer-trade-cancel", "num_cmd_25.rrd" },
{ "domain-transfer-trade-approve", "num_cmd_26.rrd" },
{ "domain-transfer-trade-reject", "num_cmd_27.rrd" },
{ "domain-transfer-query", "num_cmd_28.rrd" },
{ "login", "num_cmd_29.rrd" },
{ "login-chg-pwd", "num_cmd_30.rrd" },
{ "logout", "num_cmd_31.rrd" },
{ "poll-ack", "num_cmd_32.rrd" },
{ "hello", "num_cmd_33.rrd" },
{ "unknown-command", "num_cmd_34.rrd" }
}
function l4Label(proto)
return(_handleArray(l4_keys, proto))
end
function mapEppRRDName(name)
return(_handleArray(epp_rrd_names, name))
end
-- Alerts (see ntop_typedefs.h)
alert_level_keys = {
{ "<span class='label label-info'>Info</span>", 0 },
{ "<span class='label label-warning'>Warning</span>", 1 },
{ "<span class='label label-danger'>Error</span>", 2 }
}
alert_type_keys = {
{ "<i class='fa fa-tint'></i> TCP SYN Flood", 0 },
{ "<i class='fa fa-tint'></i> Flows Flood", 1 },
{ "<i class='fa fa-arrow-circle-up'></i> Threshold Cross", 2 },
{ "<i class='fa fa-frown-o'></i> Blacklist Host", 3 },
{ "<i class='fa fa-clock-o'></i> Periodic Activity", 4 },
{ "<i class='fa fa-sort-asc'></i> Quota Exceeded", 5 },
{ "<i class='fa fa-ban'></i> Malware Detected", 6 }
}
function alertSeverityLabel(v)
return(_handleArray(alert_level_keys, tonumber(v)))
end
function alertTypeLabel(v)
return(_handleArray(alert_type_keys, tonumber(v)))
end
function firstToUpper(str)
str = tostring(str)
return (str:gsub("^%l", string.upper))
end
function pairsByKeys(t, f)
local a = {}
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 = {}
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
function asc(a,b)
return (a < b)
end
function rev(a,b)
return (a > b)
end
--for _key, _value in pairsByKeys(vals, rev) do
-- print(_key .. "=" .. _value .. "\n")
--end
function round(num, idp)
return tonumber(string.format("%." .. (idp or 0) .. "f", num))
end
-- Note that the function below returns a string as returnong a number
-- would not help as a new float would be returned
function toint(num)
return string.format("%u", num)
end
-- Convert bytes to human readable format
function bytesToSize(bytes)
if(bytes == nil) then
return("0")
else
precision = 2
kilobyte = 1024;
megabyte = kilobyte * 1024;
gigabyte = megabyte * 1024;
terabyte = gigabyte * 1024;
bytes = tonumber(bytes)
if((bytes >= 0) and (bytes < kilobyte)) then
return round(bytes, precision) .. " B";
elseif((bytes >= kilobyte) and (bytes < megabyte)) then
return round(bytes / kilobyte, precision) .. ' KB';
elseif((bytes >= megabyte) and (bytes < gigabyte)) then
return round(bytes / megabyte, precision) .. ' MB';
elseif((bytes >= gigabyte) and (bytes < terabyte)) then
return round(bytes / gigabyte, precision) .. ' GB';
elseif(bytes >= terabyte) then
return round(bytes / terabyte, precision) .. ' TB';
else
return round(bytes, precision) .. ' B';
end
end
end
-- Convert bits to human readable format
function bitsToSize(bits)
precision = 2
kilobit = 1000;
megabit = kilobit * 1000;
gigabit = megabit * 1000;
terabit = gigabit * 1000;
if((bits >= kilobit) and (bits < megabit)) then
return round(bits / kilobit, precision) .. ' Kbit';
elseif((bits >= megabit) and (bits < gigabit)) then
return round(bits / megabit, precision) .. ' Mbit';
elseif((bits >= gigabit) and (bits < terabit)) then
return round(bits / gigabit, precision) .. ' Gbit';
elseif(bits >= terabit) then
return round(bits / terabit, precision) .. ' Tbit';
else
return round(bits, precision) .. ' bps';
end
end
-- Convert packets to pps readable format
function pktsToSize(pkts)
precision = 2
if (pkts >= 1000000) then return round(pkts/1000000, precision)..' Mpps';
elseif(pkts >= 1000) then return round(pkts/ 1000, precision)..' Kpps';
else return round(pkts , precision)..' pps';
end
end
function formatValue(amount)
local formatted = amount
if(formatted == nil) then return(0) end
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if(k==0) then
break
end
end
return formatted
end
function formatPackets(amount)
return formatValue(tonumber(amount)).." Pkts"
end
function capitalize(str)
return (str:gsub("^%l", string.upper))
end
function isnumber(str)
if((str ~= nil) and (string.len(str) > 0) and (tonumber(str) ~= nil)) then
return(true)
else
return(false)
end
end
function split(pString, pPattern)
local Table = {} -- NOTE: use {n = 0} in Lua-5.0
local fpat = "(.-)" .. pPattern
local last_end = 1
local s, e, cap = pString:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(Table,cap)
end
last_end = e+1
s, e, cap = pString:find(fpat, last_end)
end
if last_end <= #pString then
cap = pString:sub(last_end)
table.insert(Table, cap)
end
return Table
end
string.split = function(s, p)
local temp = {}
local index = 0
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
end
function formatEpoch(epoch)
return(os.date("%d/%m/%Y %X", epoch))
end
function secondsToTime(seconds)
if(seconds == nil) then return "" end
if(seconds < 1) then
return("< 1 sec")
end
days = math.floor(seconds / 86400)
hours = math.floor((seconds / 3600) - (days * 24))
minutes = math.floor((seconds / 60) - (days * 1440) - (hours * 60))
sec = seconds % 60
msg = ""
if(days > 0) then
years = math.floor(days/365)
if(years > 0) then
days = days % 365
msg = years .. " year"
if(years > 1) then msg = msg .. "s" end
msg = msg .. ", "
end
if(days > 0) then
msg = msg .. days .. " day"
if(days > 1) then msg = msg .. "s" end
msg = msg .. ", "
end
end
if(hours > 0) then
msg = msg .. string.format("%d ", hours)
if(hours > 1) then
msg = msg .. "h"
else
msg = msg .. "h"
end
--if(hours > 1) then msg = msg .. "s" end
msg = msg .. ", "
end
if(minutes > 0) then
msg = msg .. string.format("%d min", minutes)
end
if(sec > 0) then
if((string.len(msg) > 0) and (minutes > 0)) then msg = msg .. ", " end
msg = msg .. string.format("%d sec", sec);
end
return msg
end
function starts(String,Start)
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 getCategoryIcon(what, cat)
if((cat == "") or (cat == nil)) then
return("")
elseif(cat == "safe") then
return("<A HREF=http://google.com/safebrowsing/diagnostic?site="..what.."&hl=en-us><font color=green><i class=\'fa fa-check\'></i></font></A>")
elseif(cat == "malware") then
return("<A HREF=http://google.com/safebrowsing/diagnostic?site="..what.."&hl=en-us><font color=red><i class=\'fa fa-ban\'></i></font></A>")
else
return(cat)
end
end
function abbreviateString(str, len)
if(str == nil) then
return("")
else
if(string.len(str) < len) then
return(str)
else
return(string.sub(str, 1, 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)
-- check NoIP
if(ip == "0.0.0.0") then
return true
end
-- check IPv6
t = string.split(ip, "%.")
if(t ~= nil) then
-- check Multicast / Broadcast
if(tonumber(t[1]) >= 224) then
return true
end
end
return false
end
function addGauge(name, url, maxValue, width, height)
if(url ~= nil) then print('<A HREF="'..url..'">') end
print('<canvas id="'..name..'" height="'..height..'" width="'..width..'"></canvas>\n')
-- print('<div id="'..name..'-text" style="font-size: 12px;"></div>\n')
if(url ~= nil) then print('</A>') end
print [[
<script type="text/javascript">
var opts = {
fontSize: 40,
lines: 12, // The number of lines to draw
angle: 0.15, // The length of each line
lineWidth: 0.44, // The line thickness
pointer: {
length: 0.85, // The radius of the inner circle
strokeWidth: 0.051, // The rotation offset
color: '#000000' // Fill color
},
limitMax: 'false', // If true, the pointer will not go past the end of the gauge
colorStart: '#6FADCF', // Colors
colorStop: '#8FC0DA', // just experiment with them
strokeColor: '#E0E0E0', // to see which ones work best for you
generateGradient: true
};
]]
print('var target = document.getElementById("'..name..'"); // your canvas element\n')
print('var '..name..' = new Gauge(target).setOptions(opts);\n')
--print(name..'.setTextField(document.getElementById("'..name..'-text"));\n')
print(name..'.maxValue = '..maxValue..'; // set max gauge value\n')
print("</script>\n")
end
-- Compute the difference in seconds between local time and UTC.
function get_timezone()
local now = os.time()
return os.difftime(now, os.time(os.date("!*t", now)))
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
-- Return the first 'howmany' hosts
function getTopInterfaceHosts(howmany, localHostsOnly)
hosts_stats = interface.getHostsInfo()
ret = {}
sortTable = {}
n = 0
for k,v in pairs(hosts_stats) do
if((not localHostsOnly) or ((v["localhost"] == true) and (v["ip"] ~= nil))) then
sortTable[v["bytes.sent"]+v["bytes.rcvd"]+n] = k
n = n +0.01
end
end
n = 0
for _v,k in pairsByKeys(sortTable, rev) do
if(n < howmany) then
ret[k] = hosts_stats[k]
n = n+1
else
break
end
end
return(ret)
end
function http_escape(s)
s = string.gsub(s, "([&=+%c])", function (c)
return string.format("%%%02X", string.byte(c))
end)
s = string.gsub(s, " ", "+")
return s
end
function getInterfaceId(interface_name)
ifnames = interface.getIfNames()
for _,if_name in pairs(ifnames) do
interface.select(if_name)
_ifstats = interface.getStats()
if(_ifstats.name == interface_name) then return(_ifstats.id) end
end
return(-1)
end
-- Windows fixes for interfaces with "uncommon chars"
function purifyInterfaceName(interface_name)
interface_name = string.gsub(interface_name, "@", "_")
interface_name = string.gsub(interface_name, ":", "_")
return(interface_name)
end
-- Fix path format Unix <-> Windows
function fixPath(path)
if(ntop.isWindows() and (string.len(path) > 2)) then
path = string.gsub(path, "/", "\\")
-- Avoid changing c:\.... into c_\....
path = string.sub(path, 1, 2) .. string.gsub(string.sub(path, 3), ":", "_")
-- io.write("->"..path.."\n")
end
return(path)
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
function getOSIcon(name)
icon = ""
if(findString(name, "Linux") or findString(name, "Ubuntu")) then icon = '<i class=\'fa fa-linux fa-lg\'></i> '
elseif(findString(name, "Android")) then icon = '<i class=\'fa fa-android fa-lg\'></i> '
elseif(findString(name, "Windows") or findString(name, "Win32") or findString(name, "MSIE")) then icon = '<i class=\'fa fa-windows fa-lg\'></i> '
elseif(findString(name, "iPhone") or findString(name, "iPad") or findString(name, "OS X") ) then icon = '<i class=\'fa fa-apple fa-lg\'></i> '
end
return(icon)
end
function getApplicationLabel(name)
icon = ""
if(name == nil) then name = "" end
if(findString(name, "Skype")) then icon = '<i class=\'fa fa-skype fa-lg\'></i>'
elseif(findString(name, "Unknown")) then icon = '<i class=\'fa fa-question fa-lg\'></i>'
elseif(findString(name, "Twitter")) then icon = '<i class=\'fa fa-twitter fa-lg\'></i>'
elseif(findString(name, "DropBox")) then icon = '<i class=\'fa fa-dropbox fa-lg\'></i>'
elseif(findString(name, "Spotify")) then icon = '<i class=\'fa fa-spotify fa-lg\'></i>'
elseif(findString(name, "Apple")) then icon = '<i class=\'fa fa-apple fa-lg\'></i>'
elseif(findString(name, "Google") or
findString(name, "Chrome")) then icon = '<i class=\'fa fa-google-plus fa-lg\'></i>'
elseif(findString(name, "FaceBook")) then icon = '<i class=\'fa fa-facebook-square fa-lg\'></i>'
elseif(findString(name, "Youtube")) then icon = '<i class=\'fa fa-youtube-square fa-lg\'></i>'
elseif(findString(name, "thunderbird")) then icon = '<i class=\'fa fa-paper-plane fa-lg\'></i>'
end
name = name:gsub("^%l", string.upper)
return(icon.." "..name)
end
function mapOS2Icon(name)
if(name == nil) then
return("")
else
return(getOSIcon(name) .. name)
end
end
function getItemsNumber(n)
tot = 0
for k,v in pairs(n) do
--io.write(k.."\n")
tot = tot + 1
end
--io.write(tot.."\n")
return(tot)
end
function getHostCommaSeparatedList(p_hosts)
hosts = {}
hosts_size = 0
for i,host in pairs(split(p_hosts, ",")) do
hosts[i] = host
hosts_size = hosts_size + 1
end
return hosts,hosts_size
end
-- ##############################################
-- Used to avoid resolving host names too many times
resolved_host_labels_cache = {}
function getHostAltName(host_ip)
local alt_name = resolved_host_labels_cache[host_ip]
if(alt_name ~= nil) then
return(alt_name)
end
alt_name = ntop.getHashCache("ntopng.host_labels", host_ip)
if((alt_name == nil) or (alt_name == "")) then
alt_name = host_ip
end
resolved_host_labels_cache[host_ip] = alt_name
return(alt_name)
end
function setHostAltName(host_ip, alt_name)
ntop.setHashCache("ntopng.host_labels", host_ip, alt_name)
end
-- Flow Utils --
function host2name(name, vlan)
local orig_name = name
vlan = tonumber(vlan or "0")
name = getHostAltName(name)
if(name == orig_name) then
rname = ntop.getResolvedAddress(name)
if((rname ~= nil) and (rname ~= "")) then
name = rname
end
end
if(vlan > 0) then
name = name .. '@' .. vlan
end
return name
end
function flowinfo2hostname(flow_info, host_type, vlan)
local name
local orig_name
name = flow_info[host_type..".host"]
if((name == "") or (name == nil)) then
name = flow_info[host_type..".ip"]
end
return(host2name(name, flow_info["vlan"]))
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
--
-- 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
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(host_info["host"] ~= nil) then
rsp = rsp..host_info["host"]
elseif(host_info["name"] ~= nil) then
rsp = rsp..host_info["name"]
elseif(host_info["ip"] ~= nil) then
rsp = rsp..host_info["ip"]
elseif(host_info["mac"] ~= nil) then
rsp = rsp..host_info["mac"]
end
end
if(((host_info["vlan"] ~= nil) and (host_info["vlan"] ~= 0))
or ((show_vlan ~= nil) and show_vlan)) then
rsp = rsp..'@'..tostring(host_info["vlan"])
end
if(debug_host) then traceError(TRACE_DEBUG,TRACE_CONSOLE,"HOST2URL => ".. rsp .. "\n") end
return rsp
end
--
-- Analyze the get_info and return a new table containing the url information about an host.
-- Example: url2host(_GET)
--
function url2hostinfo(get_info)
local host = {}
-- Catch when the host key is using as host url parameter
if((get_info["host"] ~= nil) and (string.find(get_info["host"],"@"))) then
get_info = hostkey2hostinfo(get_info["host"])
end
if(get_info["host"] ~= nil) then
host["host"] = get_info["host"]
if(debug_host) then traceError(TRACE_DEBUG,TRACE_CONSOLE,"URL2HOST => Host:"..get_info["host"].."\n") end
end
if(get_info["vlan"] ~= nil) then
host["vlan"] = tonumber(get_info["vlan"])
if(debug_host) then traceError(TRACE_DEBUG,TRACE_CONSOLE,"URL2HOST => Vlan:"..get_info["vlan"].."\n") end
else
host["vlan"] = 0
end
return host
end
--
-- Catch the main information about an host from the host_info table and return the corresponding url.
-- Example:
-- hostinfo2url(host_key), return an url based on the host_key
-- hostinfo2url(host[key]), return an url based on the host value
-- hostinfo2url(flow[key],"cli"), return an url based on the client host information in the flow table
-- hostinfo2url(flow[key],"srv"), return an url based on the server host information in the flow table
--
function hostinfo2url(host_info,host_type)
local rsp = ''
-- local version = 0
local version = 1
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")) 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
if(version == 0) then
rsp = rsp..'&vlan='..tostring(host_info["vlan"])
elseif(version == 1) then
rsp = rsp..'@'..tostring(host_info["vlan"])
end
end
if(debug_host) then traceError(TRACE_DEBUG,TRACE_CONSOLE,"HOST2URL => ".. rsp .. "\n") end
return rsp
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
-- 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
function ntop_version_check()
_rsp = ntop.getCache("ntopng.version")
if((_rsp == nil) or (_rsp == "")) then
_rsp = ntop.httpGet("http://www.ntop.org/ntopng.version", "", "", 10)
if((_rsp == nil) or (_rsp["CONTENT"] == nil)) then rsp = "0.0.0" else rsp = _rsp["CONTENT"] end
ntop.setCache("ntopng.version", rsp, 86400)
else
rsp = _rsp
end
if(rsp ~= nil) then
info = ntop.getInfo()
stable_version = version2int(rsp)
version_elems = split(info["version"], " ");
this_version = version2int(version_elems[1])
if(stable_version > this_version) then
print("<p><div class=\"alert alert-warning\"><font color=red><i class=\"fa fa-cloud-download fa-lg\"></i> A new "..info["product"].." (v." .. rsp .. ") is available for <A HREF=http://www.ntop.org/get-started/download/>download</A>: please upgrade.</font></div></p>")
end
end
end
-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
function tprint (tbl, indent)
if not indent then indent = 0 end
if(tbl ~= nil) then
for k, v in pairs(tbl) do
formatting = string.rep(" ", indent) .. k .. ": "
if type(v) == "table" then
io.write(formatting)
tprint(v, indent+1)
elseif type(v) == 'boolean' then
io.write(formatting .. tostring(v))
else
io.write(formatting .. v)
end
end
io.write("\n")
end
end
function table.empty(table)
if(table == nil) then return true end
if next(table) == nil then
return true
end
return false
end
function table.len(table)
local count = 0
if(table == nil) then return(0) end
for k,v in pairs(table) do
count = count + 1
end
return count
end
-- ############################################
-- Redis Utils
-- ############################################
-- Inpur: General prefix (i.e ntopng.pref)
-- Output: User based prefix, if it exists
--
-- Examples:
-- With user: ntopng.pref.user_name
-- Without: ntopng.pref
function getRedisPrefix(str)
if not (isEmptyString(_SESSION["user"] )) then
-- Login enabled
return (str .. '.' .. _SESSION["user"])
else
-- Login disabled
return (str)
end
end
function getPathFromKey(key)
local path = string.gsub(key, "%.", "/")
path = string.gsub(path, ":", "_")
return fixPath(path)
end
----- End of Redis Utils ------
-- ############################################
-- Runtime preference
function prefsInputField(label, comment, key, value)
if(_GET[key] ~= nil) then
k = "ntopng.prefs."..key
v_s = _GET[key]
v = tonumber(v_s)
if(v ~= nil and (v > 0) and (v < 86400)) then
-- print(k.."="..v)
ntop.setCache(k, tostring(v))
value = v
elseif (v_s ~= nil) then
ntop.setCache(k, v_s)
value = v_s
end
end
print('<tr><td width=50%><strong>'..label..'</strong><p><small>'..comment..'</small></td>')
print [[
<td class="input-group col-lg-3" align=right><form class="navbar-form navbar-right">]]
print('<input id="csrf" name="csrf" type="hidden" value="'..ntop.getRandomCSRFValue()..'" />\n')
print [[
<div class="input-group" >
<input type="text" class="form-control" name="]] print(key) print [[" value="]] print(value.."") print [[">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">Save</button>
</span>
</div><!-- /input-group -->
</form></td></tr>
]]
end
function toggleTableButton(label, comment, on_label, on_value, on_color , off_label, off_value, off_color, submit_field, redis_key, disabled)
if(_GET[submit_field] ~= nil) then
ntop.setCache(redis_key, _GET[submit_field])
value = _GET[submit_field]
else
value = ntop.getCache(redis_key)
end
if (disabled == true) then
disabled = 'disabled = ""'
else
disabled = ""
end
-- Read it anyway to
if(value == off_value) then
rev_value = on_value
on_active = "btn-default"
off_active = "btn-"..off_color.." active"
else
rev_value = off_value
on_active = "btn-"..on_color.." active"
off_active = "btn-default"
end
if(label ~= "") then print('<tr><td width=50%><strong>'..label..'</strong><p><small>'..comment..'</small></td><td align=right>\n') end
print('<form>\n<div class="btn-group btn-toggle">')
print('<input id="csrf" name="csrf" type="hidden" value="'..ntop.getRandomCSRFValue()..'" />\n')
print('<input type=hidden name='..submit_field..' value='..rev_value..'>\n')
print('<button type="submit" '..disabled..' class="btn btn-sm '..on_active..'">'..on_label..'</button>')
print('<button '..disabled..' class="btn btn-sm '..off_active..'">'..off_label..'</button></div>\n')
print('</form>\n')
if(label ~= "") then print('</td></tr>') end
return(value)
end
function isPausedInterface(current_ifname)
state = ntop.getCache("ntopng.prefs."..current_ifname.."_not_idle")
if(state == "0") then return true else return false end
end
function getThroughputType()
throughput_type = ntop.getCache("ntopng.prefs.thpt_content")
if(throughput_type == "") then
throughput_type = "bps"
end
return throughput_type
end
function isLoopback(name)
if((name == "lo") or (name == "lo0")) then
return(true)
else
return(false)
end
end
function processColor(proc)
if(proc == nil) then
return("")
elseif(proc["average_cpu_load"] < 33) then
return("<font color=green>"..proc["name"].."</font>")
elseif(proc["average_cpu_load"] < 66) then
return("<font color=orange>"..proc["name"].."</font>")
else
return("<font color=red>"..proc["name"].."</font>")
end
end
-- Table preferences
function getDefaultTableSort(table_type)
table_key = getRedisPrefix("ntopng.prefs.table")
if(table_type ~= nil) then
value = ntop.getHashCache(table_key, "sort_"..table_type)
end
if((value == nil) or (value == "")) then value = 'column_' end
return(value)
end
function getDefaultTableSortOrder(table_type)
table_key = getRedisPrefix("ntopng.prefs.table")
if(table_type ~= nil) then
value = ntop.getHashCache(table_key, "sort_order_"..table_type)
end
if((value == nil) or (value == "")) then value = 'desc' end
return(value)
end
function getDefaultTableSize()
table_key = getRedisPrefix("ntopng.prefs.table")
value = ntop.getHashCache(table_key, "rows_number")
if((value == nil) or (value == "")) then value = 10 end
return(tonumber(value))
end
function tablePreferences(key, value)
table_key = getRedisPrefix("ntopng.prefs.table")
if((value == nil) or (value == "")) then
-- Get preferences
return ntop.getHashCache(table_key, key)
else
-- Set preferences
ntop.setHashCache(table_key, key, value)
return(value)
end
end
function getInterfaceNameAlias(interface_name)
label = ntop.getCache('ntopng.prefs.'..interface_name..'.name')
if((label == nil) or (label == "")) then
return(interface_name)
else
return(label)
end
end
function getHumanReadableInterfaceName(interface_id)
key = 'ntopng.prefs.'..interface_id..'.name'
custom_name = ntop.getCache(key)
if((custom_name ~= nil) and (custom_name ~= "")) then
return(custom_name)
else
interface.select(interface_id)
_ifstats = interface.getStats()
-- print(interface_id.."=".._ifstats.name)
if((interface_id ~= _ifstats.description) and (_ifstats.description ~= "PF_RING")) then
return(_ifstats.description)
else
return(_ifstats.name)
end
end
end
function escapeHTML(s)
s = string.gsub(s, "([&=+%c])", function (c)
return string.format("%%%02X", string.byte(c))
end)
s = string.gsub(s, " ", "+")
return s
end
function unescapeHTML (s)
s = string.gsub(s, "+", " ")
s = string.gsub(s, "%%(%x%x)", function (h)
return string.char(tonumber(h, 16))
end)
return s
end
-- ##############################################
function harvestUnusedDir(path, min_epoch)
local files = ntop.readdir(path)
-- print("Reading "..path.."<br>\n")
for k,v in pairs(files) do
if(v ~= nil) then
local p = 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 = fixPath(dirs.workingdir .. "/" .. _ifstats.id)
harvestUnusedDir(fixPath(basedir .. "/top_talkers"), when)
harvestUnusedDir(fixPath(basedir .. "/flows"), when)
end
end
-- ##############################################
function isAdministrator()
local user_group = ntop.getUserGroup()
if(user_group == "administrator") then
return(true)
else
return(false)
end
end
-- ##############################################
function haveAdminPrivileges()
if(isAdministrator) then
return(true)
else
ntop.dumpFile(dirs.installdir .. "/httpdocs/inc/header.inc")
dofile(dirs.installdir .. "/scripts/lua/inc/menu.lua")
print("<div class=\"alert alert-danger\"><img src=".. ntop.getHttpPrefix() .. "/img/warning.png> Access forbidden</div>")
return(false)
end
end
-- ##############################################
function getKeysSortedByValue(tbl, sortFunction)
local keys = {}
for key in pairs(tbl) do
table.insert(keys, key)
end
table.sort(keys, function(a, b)
return sortFunction(tbl[a], tbl[b])
end)
return keys
end
function getKeys(t, col)
local keys = {}
for k,v in pairs(t) do keys[tonumber(v[col])] = k end
return keys
end
-- ##############################################
function formatBreed(breed)
if(breed == "Safe") then
return("<i class='fa fa-lock' alt='Safe Protocol'></i>")
elseif(breed == "Acceptable") then
return("<i class='fa fa-thumbs-o-up' alt='Acceptable Protocol'></i>")
elseif(breed == "Fun") then
return("<i class='fa fa-smile' alt='Fun Protocol'></i>")
elseif(breed == "Unsafe") then
return("<i class='fa fa-thumbs-o-down'></i>")
elseif(breed == "Dangerous") then
return("<i class='fa fa-warning'></i>")
else
return("")
end
end
function getFlag(country)
if((country == nil) or (country == "")) then
return("")
else
return(" <A HREF=" .. ntop.getHttpPrefix() .. "/lua/hosts_stats.lua?country=".. country .."><img src='".. ntop.getHttpPrefix() .. "/img/blank.gif' class='flag flag-".. string.lower(country) .."'></A> ")
end
end
-- GENERIC UTILS
-- ternary
function ternary(cond, T, F)
if cond then return T else return F end
end
-- split
function split(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
-- startswith
function startswith(s, char)
return string.sub(s, 1, string.len(s)) == char
end
-- strsplit
function strsplit(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
if(match ~= "") then result[match] = true end
end
return result;
end
-- isempty
function isempty(array)
local count = 0
for _,__ in pairs(array) do
count = count + 1
end
return (count == 0)
end
-- isin
function isin(s, array)
if (s == nil or s == "" or array == nil or isempty(array)) then return false end
for _, v in pairs(array) do
if (s == v) then return true end
end
return false
end
-- hasKey
function hasKey(key, theTable)
if((theTable == nil) or (theTable[key] == nil)) then
return(false)
else
return(true)
end
end
-- maxRateToString
function maxRateToString(max_rate)
if((max_rate == nil) or (max_rate == "")) then max_rate = -1 end
max_rate = tonumber(max_rate)
if(max_rate == -1) then
return("No Limit")
else
if(max_rate == 0) then
return("Drop All Traffic")
else
if(max_rate < 1000) then
return(max_rate.." Kbit/s")
else
local mr
mr = round(max_rate / 1000, 2)
if(mr < 1000) then
return(mr.." Mbit/s")
else
gbit = mr /1000
return(gbit.." Gbit/s")
end
end
end
end
end
-- makeTopStatsScriptsArray
function makeTopStatsScriptsArray()
path = dirs.installdir .. "/scripts/lua/modules/top_scripts"
path = fixPath(path)
local files = ntop.readdir(path)
topArray = {}
for k,v in pairs(files) do
if(string.ends(k, ".lua")) then
if(v ~= nil) then
value = {}
fn,ext = v:match("([^.]+).([^.]+)")
mod = require("top_scripts."..fn)
if(type(mod) ~= type(true)) then
value["name"] = mod.name
value["script"] = mod.infoScript
value["key"] = mod.infoScriptKey
value["levels"] = mod.numLevels
topArray[fn] = value
end
end
end
end
return(topArray)
end
local mac_cache = { }
-- get_mac_classification
function get_mac_classification(m)
local path = fixPath(dirs.installdir.."/httpdocs/other/EtherOUI.txt")
local file_mac
if(string.len(m) > 8) then m = string.sub(m, 1, 8) end
if(mac_cache[m] ~= nil) then
-- io.write("Cached "..m.."\n")
return(mac_cache[m])
end
file_mac = io.open(path)
if (file_mac == nil) then return m end
local mac_line = file_mac:read("*l")
while (mac_line ~= nil) do
if (not startswith(mac_line, "#") and mac_line ~= "") then
b = string.sub(mac_line, 1, 8)
if (m == b) then
t = split(mac_line, "\t")
file_mac.close()
rsp = split(t[2], " ")[1]
mac_cache[m] = rsp
return rsp
end
end
mac_line = file_mac:read("*l")
end
file_mac.close()
return m
end
local magic_macs = {
["FF:FF:FF:FF:FF:FF"] = "Broadcast",
["01:00:0C:CC:CC:CC"] = "CDP",
["01:00:0C:CC:CC:CD"] = "CiscoSTP",
["01:80:C2:00:00:00"] = "STP",
["01:80:C2:00:00:00"] = "LLDP",
["01:80:C2:00:00:03"] = "LLDP",
["01:80:C2:00:00:0E"] = "LLDP",
["01:80:C2:00:00:08"] = "STP",
["01:1B:19:00:00:00"] = "PTP",
["01:80:C2:00:00:0E"] = "PTP"
}
local magic_short_macs = {
["01:00:5E"] = "IPv4mcast",
["33:33:"] = "IPv6mcast"
}
-- get_symbolic_mac
function get_symbolic_mac(mac_address)
if(magic_macs[mac_address] ~= nil) then
return(magic_macs[mac_address])
else
local m = string.sub(mac_address, 1, 8)
local t = string.sub(mac_address, 10, 17)
if(magic_short_macs[m] ~= nil) then
return(magic_short_macs[m].."_"..t.." ("..mac_address..")")
else
local s = get_mac_classification(m)
if(m == s) then
return(get_mac_classification(m)..":"..t)
else
return(get_mac_classification(m).."_"..t.." ("..mac_address..")")
end
end
end
end
-- rrd_exists
function rrd_exists(host_ip, rrdname)
if(host_ip == nil) then return false end
dirs = ntop.getDirs()
rrdpath = dirs.workingdir .. "/" .. ifId .. "/rrd/" .. getPathFromKey(host_ip) .. "/" .. rrdname
return ntop.exists(rrdpath)
end
-- getservbyport
function getservbyport(port_num, proto)
if(proto == nil) then proto = "TCP" end
port_num = tonumber(port_num)
proto = string.lower(proto)
-- io.write(port_num.."@"..proto.."\n")
return(ntop.getservbyport(port_num, proto))
end
-- getSpeedMax
function getSpeedMax(ifname)
if(ifname == nil) then
return -1
end
if(ifname ~= "eth0") then
return -1
end
ifname = tostring(ifname)
return(ntop.getSpeedMax(ifname))
end
function intToIPv4(num)
return(math.floor(num / 2^24).. "." ..math.floor((num % 2^24) / 2^16).. "." ..math.floor((num % 2^16) / 2^8).. "." ..num % 2^8)
end
function getFlowMaxRate(cli_max_rate, srv_max_rate)
cli_max_rate = tonumber(cli_max_rate)
srv_max_rate = tonumber(srv_max_rate)
if((cli_max_rate == 0) or (srv_max_rate == 0)) then
max_rate = 0
elseif((cli_max_rate == -1) and (srv_max_rate > 0)) then
max_rate = srv_max_rate
elseif((cli_max_rate > 0) and (srv_max_rate == -1)) then
max_rate = cli_max_rate
else
max_rate = math.min(cli_max_rate, srv_max_rate)
end
return(max_rate)
end