ntopng/scripts/lua/modules/lua_utils.lua
emanuele-f a10d6c4f7d Fix login issue with special characters.
The special characters were URL encoded during HTTP request and saved in URL encoded form.
The login used the unencoded password instead, and this resulted in a password hash mismatch which prevented user login.

Now the password hash is calculated on the unencoded password.
2017-04-14 18:20:36 +02:00

3036 lines
88 KiB
Lua

--
-- (C) 2014-17 - ntop.org
--
dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?/init.lua;" .. package.path
require "lua_trace"
i18n = require "i18n"
local locale = "en" -- FIX make it configurable
i18n.loadFile(dirs.installdir..'/scripts/locales/'..locale..'.lua')
-- ##############################################
function getInterfaceName(interface_id)
local ifnames = interface.getIfNames()
interface_id = tonumber(interface_id)
for _,if_name in pairs(ifnames) do
--io.write(if_name.."\n")
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["ifid"]
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", 6 },
{ "UDP", "udp", 17 },
{ "ICMP", "icmp", 1 },
{ "Other IP", "other ip", -1 }
}
function __FILE__() return debug.getinfo(2,'S').source end
function __LINE__() return debug.getinfo(2, 'l').currentline end
-- ##############################################
function sendHTTPHeaderIfName(mime, ifname, maxage, content_disposition)
info = ntop.getInfo(false)
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')
if(content_disposition ~= nil) then print('Content-Disposition: '..content_disposition..'\r\n') end
print('Last-Modified: '..os.date("!%a, %m %B %Y %X %Z").."\r\n")
print('\r\n')
end
-- ##############################################
function sendHTTPHeaderLogout(mime, content_disposition)
sendHTTPHeaderIfName(mime, nil, 0, content_disposition)
end
-- ##############################################
function sendHTTPHeader(mime, content_disposition)
sendHTTPHeaderIfName(mime, nil, 3600, content_disposition)
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
-- ##############################################
-- Simplified checker
function isIPv6Address(ip)
if(string.find(ip, ":") ~= nil) then
return true
end
return false
end
-- ##############################################
function isMacAddress(address)
if(string.match(address, "^%x%x:%x%x:%x%x:%x%x:%x%x:%x%x$") ~= nil) then
return true
end
return false
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
-- ##############################################
function string.contains(String,Start)
if type(String) ~= 'string' or type(Start) ~= 'string' then
return false
end
return(string.find(String,Start,1) ~= nil)
end
-- ##############################################
function string.starts(String,Start)
if type(String) ~= 'string' or type(Start) ~= 'string' then
return false
end
return string.sub(String,1,string.len(Start))==Start
end
-- ##############################################
function string.ends(String,End)
if type(String) ~= 'string' or type(End) ~= 'string' then
return false
end
return End=='' or string.sub(String,-string.len(End))==End
end
-- ##############################################
function printASN(asn, asname)
asname = asname:gsub('"','')
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 getPageUrl(base_url, params)
for _,_ in pairs(params or {}) do
return base_url .. "?" .. table.tconcat(params, "=", "&")
end
return base_url
end
-- ##############################################
function printIpVersionDropdown(base_url, page_params)
local ipversion = _GET["version"]
local ipversion_filter
if not isEmptyString(ipversion) then
ipversion_filter = '<span class="glyphicon glyphicon-filter"></span>'
else
ipversion_filter = ''
end
local ipversion_params = table.clone(page_params)
ipversion_params["version"] = nil
print[[\
<button class="btn btn-link dropdown-toggle" data-toggle="dropdown">IP Version]] print(ipversion_filter) print[[<span class="caret"></span></button>\
<ul class="dropdown-menu" role="menu" id="flow_dropdown">\
<li><a href="]] print(getPageUrl(base_url, ipversion_params)) print[[">All Versions</a></li>\
<li]] if ipversion == "4" then print(' class="active"') end print[[><a href="]] ipversion_params["version"] = "4"; print(getPageUrl(base_url, ipversion_params)); print[[">IPv4 Only</a></li>\
<li]] if ipversion == "6" then print(' class="active"') end print[[><a href="]] ipversion_params["version"] = "6"; print(getPageUrl(base_url, ipversion_params)); print[[">IPv6 Only</a></li>\
</ul>]]
end
-- ##############################################
function shortenString(name, max_len)
max_len = max_len or 24
if(string.len(name) < max_len) then
return(name)
else
return(string.sub(name, 1, max_len).."...")
end
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
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
-- ##############################################
function l4Label(proto)
return(_handleArray(l4_keys, proto))
end
-- Alerts (see ntop_typedefs.h)
-- each table entry is an array as:
-- {"alert html string", "alert C enum value", "plain string"}
alert_level_keys = {
{ "<span class='label label-info'>Info</span>", 0, "info" },
{ "<span class='label label-warning'>Warning</span>", 1, "warning" },
{ "<span class='label label-danger'>Error</span>", 2, "error" }
}
alert_type_keys = {
{ "<i class='fa fa-tint'></i> TCP SYN Flood", 0, "tcp_syn_flood" },
{ "<i class='fa fa-tint'></i> Flows Flood", 1, "flows_flood" },
{ "<i class='fa fa-arrow-circle-up'></i> Threshold Cross", 2, "threshold_cross" },
{ "<i class='fa fa-frown-o'></i> Blacklist Host", 3, "blacklist_host" },
{ "<i class='fa fa-clock-o'></i> Periodic Activity", 4, "periodic_activity" },
{ "<i class='fa fa-sort-asc'></i> Quota Exceeded", 5, "quota_exceeded" },
{ "<i class='fa fa-ban'></i> Malware Detected", 6, "malware_detected" },
{ "<i class='fa fa-bomb'></i> Ongoing Attacker", 7, "ongoing_attacker" },
{ "<i class='fa fa-bomb'></i> Under Attack", 8, "under_attack" },
{ "<i class='fa fa-exclamation'></i> Misconfigured App", 9, "misconfigured_app" },
{ "<i class='fa fa-exclamation'></i> Suspicious Activity", 10, "suspicious_activity" },
{ "<i class='fa fa-exclamation'></i> Too Many Alerts", 11, "too_many_alerts" },
{ "<i class='fa fa-exclamation'></i> MySQL open_files_limit too small", 12, "open_files_limit_too_small" },
{ "<i class='fa fa-exclamation'></i> Interface Alerted", 13, "interface_alerted" },
{ "<i class='fa fa-exclamation'></i> Flow Misbehaviour", 14, "flow_misbehaviour" },
}
alert_entity_keys = {
{ "Interface", 0, "interface" },
{ "Host", 1, "host" },
{ "Network", 2, "network" },
{ "SNMP device", 3, "snmp_device" },
{ "Flow", 4, "flow" }
}
alert_functions_description = {
["active"] = "Active host time (seconds)",
["bytes"] = "Layer 2 bytes delta (sent + received)",
["dns"] = "Layer 2 bytes delta (sent + received) for DNS detected traffic",
["idle"] = "Idle time since last packet sent (seconds)",
["packets"] = "Packets delta (sent + received)",
["p2p"] = "Layer 2 bytes delta (sent + received) for peer-to-peer detected traffic",
["throughput"] = "Average throughput (sent + received) [Mbps]",
}
network_alert_functions_description = {
["ingress"] = "Ingress Bytes delta",
["egress"] = "Egress Bytes delta",
["inner"] = "Inner Bytes delta",
}
function noHtml(s)
if s == nil then return nil end
return s:gsub("<.->(.-)</.->","%1"):gsub("^%s*(.-)%s*$", "%1")
end
function alertSeverityLabel(v, nohtml)
local res = _handleArray(alert_level_keys, tonumber(v))
if res ~= nil and nohtml == true then res = noHtml(res) end
return res
end
function alertSeverity(v)
local severity_table = {}
for i, t in ipairs(alert_level_keys) do
severity_table[#severity_table + 1] = {t[2], t[3]}
end
return(_handleArray(severity_table, v))
end
function alertSeverityRaw(sev_idx)
sev_idx = sev_idx + 1
if sev_idx <= #alert_level_keys then
return alert_level_keys[sev_idx][3]
end
return nil
end
function alertTypeLabel(v, nohtml)
local res = _handleArray(alert_type_keys, tonumber(v))
if res ~= nil and nohtml == true then res = noHtml(res) end
return res
end
function alertType(v)
local typetable = {}
for i, t in ipairs(alert_type_keys) do
typetable[#typetable + 1] = {t[2], t[3]}
end
return(_handleArray(typetable, v))
end
function alertTypeRaw(alert_idx)
alert_idx = alert_idx + 1
if alert_idx <= #alert_type_keys then
return alert_type_keys[alert_idx][3]
end
return nil
end
function alertEntityLabel(v, nothml)
local res = _handleArray(alert_entity_keys, tonumber(v))
if res ~= nil and nohtml == true then res = noHtml(res) end
return res
end
function alertEntity(v)
local typetable = {}
for i, t in ipairs(alert_entity_keys) do
typetable[#typetable + 1] = {t[2], t[3]}
end
return(_handleArray(typetable, v))
end
function alertEntityRaw(entity_idx)
entity_idx = entity_idx + 1
if entity_idx <= #alert_entity_keys then
return alert_entity_keys[entity_idx][3]
end
return nil
end
function areAlertsEnabled()
return ((not ntop.getPrefs().has_cmdl_disable_alerts) and
(ntop.getPref("ntopng.prefs.disable_alerts_generation") ~= "1"))
end
function firstToUpper(str)
str = tostring(str)
return (str:gsub("^%l", string.upper))
end
function pairsByKeys(t, f)
local a = {}
-- io.write(debug.traceback().."\n")
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
--function round(num) return math.floor(num+.5) 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", 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) .. " Bytes";
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) .. ' Bytes';
end
end
end
-- Convert bits to human readable format
function bitsToSizeMultiplier(bits, multiplier)
precision = 2
kilobit = 1000;
megabit = kilobit * multiplier;
gigabit = megabit * multiplier;
terabit = gigabit * multiplier;
if((bits >= kilobit) and (bits < megabit)) then
return round(bits / kilobit, precision) .. ' kbit/s';
elseif((bits >= megabit) and (bits < gigabit)) then
return round(bits / megabit, precision) .. ' Mbit/s';
elseif((bits >= gigabit) and (bits < terabit)) then
return round(bits / gigabit, precision) .. ' Gbit/s';
elseif(bits >= terabit) then
return round(bits / terabit, precision) .. ' Tbit/s';
else
return round(bits, precision) .. ' bit/s';
end
end
function bitsToSize(bits)
return(bitsToSizeMultiplier(bits, 1000))
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
-- returns the MAXIMUM value found in a table t, together with the corresponding
-- index argmax. a pair argmax, max is returned.
function tmax(t)
local argmx, mx = nil, nil
if (type(t) ~= "table") then return nil, nil end
for k, v in pairs(t) do
-- first iteration
if mx == nil and argmx == nil then
mx = v
argmx = k
elseif (v == mx and k > argmx) or v > mx then
-- if there is a tie, prefer the greatest argument
-- otherwise grab the maximum
argmx = k
mx = v
end
end
return argmx, mx
end
-- returns the MINIMUM value found in a table t, together with the corresponding
-- index argmin. a pair argmin, min is returned.
function tmin(t)
local argmn, mn = nil, nil
if (type(t) ~= "table") then return nil, nil end
for k, v in pairs(t) do
-- first iteration
if mn == nil and argmn == nil then
mn = v
argmn = k
elseif (v == mn and k > argmn) or v < mn then
-- if there is a tie, prefer the greatest argument
-- otherwise grab the minimum
argmn = k
mn = v
end
end
return argmn, mn
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
local days = math.floor(seconds / 86400)
local hours = math.floor((seconds / 3600) - (days * 24))
local minutes = math.floor((seconds / 60) - (days * 1440) - (hours * 60))
local sec = seconds % 60
local 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
end
if(days > 0) then
if(string.len(msg) > 0) then msg = msg .. ", " end
msg = msg .. days .. " day"
if(days > 1) then msg = msg .. "s" end
end
end
if(hours > 0) then
if(string.len(msg) > 0) then msg = msg .. ", " end
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
end
if(minutes > 0) then
if(string.len(msg) > 0) then msg = msg .. ", " end
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 msToTime(ms)
if(ms > 1000) then
return secondsToTime(ms/1000)
else
if(ms < 1) then
return("< 1 ms")
else
return(round(ms, 4).." ms")
end
end
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
-- #################################################################
-- NOTE keep in sync with Flashstart::initMapping()
host_categories = {
["freetime"] = "FreeTime",
["chat"] = "Chat",
["onlineauctions"] = "Auctions",
["onlinegames"] = "Online Games",
["pets"] = "Animals",
["porn"] = "Porn",
["religion"] = "Religion",
["phishing"] = "Phishing",
["sexuality"] = "Sex",
["games"] = "Games",
["socialnetworking"] = "SocialNetwork",
["jobsearch"] = "JobSearch",
["mail"] = "Webmail",
["news"] = "News",
["proxy"] = "AnonymousProxy",
["publicite"] = "Advertisement",
["sports"] = "Sport",
["vacation"] = "Travel",
["ecommerce"] = "E-commerce",
["instantmessaging"] = "InstantMessaging",
["kidstimewasting"] = "KidGames",
["audio-video"] = "AudioVideo",
["books"] = "Books",
["government"] = "Government",
["malware"] = "Malware",
["medical"] = "Medicine",
["ann"] = "Ads",
["drugs"] = "Drugs",
["dating"] = "OnlineDating",
["desktopsillies"] = "DesktopImages",
["filehosting"] = "FileHosting",
["filesharing"] = "FileSharing",
["gambling"] = "Gambling",
["warez"] = "CracksWarez",
["radio"] = "Radio",
["updatesites"] = "Updates",
["financial"] = "FinanceBanking",
["adult"] = "Adults",
["fashion"] = "Fashion",
["showbiz"] = "Showbiz",
["ict"] = "ICT",
["company"] = "Business",
["education"] = "EducationSchool",
["searchengines"] = "SearchEngines",
["blog"] = "Blog",
["association"] = "Associations",
["music"] = "Musica",
["legal"] = "Legal",
["photo"] = "Photo",
["stats"] = "Webstat",
["content"] = "ContentServer",
["domainforsale"] = "DomainForSale",
["weapons"] = "Guns",
["generic"] = "Generic"
}
-- #################################################################
function getCategoryLabel(cat)
if((cat == "") or (cat == nil) or (cat == "???")) then
return("")
end
for c,v in pairs(host_categories) do
if(c == cat) then
return(v)
end
end
return(cat)
end
function getCategoryIcon(what, cat)
if((cat == "") or (cat == nil) or (cat == "???")) then
return("")
end
ret = ""
for c,_ in pairs(cat) do
if(host_categories[c] ~= nil) then
ret = ret .. " <span class='label label-info'>"..host_categories[c].."</span>"
else
ret = ret .. " <span class='label label-info'>"..c.."</span>"
end
end
return(ret)
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 isIPv4(ip)
if string.find(ip, "%.") then
return true
end
return false
end
function isIPv6(ip)
if((string.find(ip, ":")) and (not isMacAddress(ip))) then
return true
end
return false
end
function addGoogleMapsScript()
local g_maps_key = ntop.getCache('ntopng.prefs.google_apis_browser_key')
if g_maps_key ~= nil and g_maps_key~= "" then
g_maps_key = "&key="..g_maps_key
else
g_maps_key = ""
end
print("<script src=\"https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"..g_maps_key.."\"></script>\n")
end
function addLogoSvg()
print [[
<svg width="103px" height="50px" viewBox="0 0 103 50" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#fbfbfb" d=" M 0.00 0.00 L 103.00 0.00 L 103.00 50.00 L 0.00 50.00 L 0.00 0.00 Z" />
<path fill="#e0dfdf" d=" M 4.85 1.48 C 6.22 1.31 7.60 1.22 8.98 1.21 C 37.00 1.18 65.02 1.25 93.03 1.19 C 95.53 1.45 98.91 1.02 100.31 3.63 C 101.57 5.50 101.41 7.84 101.46 9.98 C 101.41 20.66 101.49 31.34 101.42 42.02 C 101.47 44.44 100.55 47.12 98.30 48.31 C 95.68 49.45 92.76 48.78 90.01 48.81 C 65.02 48.80 40.02 48.83 15.02 48.80 C 11.70 48.82 8.35 49.28 5.04 48.72 L 5.39 47.64 C 8.24 47.83 11.09 48.07 13.95 48.13 C 39.31 48.15 64.66 48.12 90.02 48.15 C 92.91 48.15 95.79 47.82 98.63 47.31 C 98.05 46.60 97.37 45.99 96.61 45.49 L 95.99 45.06 C 96.80 44.80 98.40 44.30 99.21 44.04 L 99.36 44.03 C 99.50 44.03 99.79 44.02 99.93 44.01 C 100.48 31.44 100.36 18.75 100.14 6.16 C 99.85 5.82 99.28 5.16 99.00 4.83 L 98.05 4.18 C 97.44 3.75 96.83 3.33 96.22 2.91 C 95.08 1.78 93.46 1.86 91.98 1.84 C 65.31 2.00 38.64 1.81 11.97 1.92 C 9.59 1.93 7.19 1.95 4.85 1.48 Z" />
<path fill="#fdc28e" d=" M 2.40 4.64 C 3.22 4.02 4.05 3.42 4.88 2.81 C 35.32 2.80 65.78 2.61 96.22 2.91 C 96.83 3.33 97.44 3.75 98.05 4.18 C 97.94 10.99 97.98 17.80 98.03 24.61 C 94.33 24.55 90.63 24.55 86.93 24.61 C 87.03 20.61 85.58 16.14 81.83 14.17 C 79.15 12.57 75.83 13.14 73.29 14.71 C 72.19 14.27 71.09 13.86 69.98 13.48 C 68.39 13.87 67.26 15.25 67.53 16.92 C 67.45 19.53 67.54 22.13 67.52 24.74 C 67.13 24.69 66.35 24.59 65.97 24.55 C 65.86 21.70 65.76 18.50 63.66 16.31 C 60.13 12.15 52.85 12.14 49.37 16.37 C 47.35 18.58 46.81 21.64 46.81 24.53 C 45.41 24.56 44.01 24.59 42.61 24.62 C 42.59 22.97 42.57 21.32 42.60 19.68 C 43.90 19.29 45.26 19.07 46.53 18.57 C 47.29 17.40 47.41 15.65 46.54 14.51 C 45.32 14.03 44.05 13.72 42.73 13.58 C 42.78 10.95 42.74 6.67 38.89 7.30 C 36.49 8.54 37.22 11.38 36.92 13.57 C 34.23 13.50 32.07 16.35 34.12 18.59 C 34.83 18.84 36.25 19.33 36.97 19.58 C 37.12 21.25 37.06 22.92 37.06 24.59 C 35.75 24.59 34.44 24.59 33.14 24.61 C 32.96 21.36 33.89 17.35 31.15 14.90 C 28.35 12.51 24.15 12.89 21.14 14.66 C 19.28 13.73 16.40 12.75 15.43 15.37 C 15.10 18.44 15.36 21.53 15.28 24.61 C 11.08 24.54 6.88 24.58 2.69 24.58 C 2.10 17.97 2.69 11.28 2.40 4.64 Z" />
<path fill="#fde2cb" d=" M 98.05 4.18 L 99.00 4.83 C 99.13 17.89 98.72 31.00 99.36 44.03 L 99.21 44.04 L 97.99 43.66 C 98.18 37.32 98.14 30.96 98.03 24.61 C 97.98 17.80 97.94 10.99 98.05 4.18 Z" />
<path fill="#919191" d=" M 99.00 4.83 C 99.28 5.16 99.85 5.82 100.14 6.16 C 100.36 18.75 100.48 31.44 99.93 44.01 C 99.79 44.02 99.50 44.03 99.36 44.03 C 98.72 31.00 99.13 17.89 99.00 4.83 Z" />
<path fill="#ffffff" d=" M 36.92 13.57 C 37.22 11.38 36.49 8.54 38.89 7.30 C 42.74 6.67 42.78 10.95 42.73 13.58 C 44.05 13.72 45.32 14.03 46.54 14.51 C 47.41 15.65 47.29 17.40 46.53 18.57 C 45.26 19.07 43.90 19.29 42.60 19.68 C 42.57 21.32 42.59 22.97 42.61 24.62 C 42.16 27.60 44.14 33.15 39.86 33.86 C 35.40 33.43 37.57 27.54 37.06 24.59 C 37.06 22.92 37.12 21.25 36.97 19.58 C 36.25 19.33 34.83 18.84 34.12 18.59 C 32.07 16.35 34.23 13.50 36.92 13.57 Z" />
<path fill="#ffffff" d=" M 21.14 14.66 C 24.15 12.89 28.35 12.51 31.15 14.90 C 33.89 17.35 32.96 21.36 33.14 24.61 C 33.05 27.07 33.29 29.54 32.95 31.99 C 32.16 34.62 27.92 34.40 27.34 31.72 C 27.19 29.35 27.31 26.98 27.29 24.61 C 27.51 22.14 27.48 18.11 24.05 18.23 C 20.81 18.41 20.90 22.23 21.02 24.62 C 20.99 27.08 21.12 29.55 20.95 32.01 C 20.07 34.59 15.97 34.42 15.33 31.76 C 15.20 29.38 15.32 26.99 15.28 24.61 C 15.36 21.53 15.10 18.44 15.43 15.37 C 16.40 12.75 19.28 13.73 21.14 14.66 Z" />
<path fill="#ffffff" d=" M 49.37 16.37 C 52.85 12.14 60.13 12.15 63.66 16.31 C 65.76 18.50 65.86 21.70 65.97 24.55 C 66.06 29.16 62.74 33.71 57.98 34.13 C 52.20 35.30 46.89 30.21 46.81 24.53 C 46.81 21.64 47.35 18.58 49.37 16.37 Z" />
<path fill="#ffffff" d=" M 73.29 14.71 C 75.83 13.14 79.15 12.57 81.83 14.17 C 85.58 16.14 87.03 20.61 86.93 24.61 C 86.64 28.43 84.48 32.37 80.78 33.78 C 78.26 34.69 75.60 34.08 73.23 33.05 C 73.25 35.33 73.62 37.68 73.06 39.92 C 72.06 42.49 67.59 41.88 67.55 39.02 C 67.45 34.26 67.54 29.50 67.52 24.74 C 67.54 22.13 67.45 19.53 67.53 16.92 C 67.26 15.25 68.39 13.87 69.98 13.48 C 71.09 13.86 72.19 14.27 73.29 14.71 Z" />
<path fill="#fdc28e" d=" M 21.02 24.62 C 20.90 22.23 20.81 18.41 24.05 18.23 C 27.48 18.11 27.51 22.14 27.29 24.61 C 25.20 24.57 23.11 24.57 21.02 24.62 Z" />
<path fill="#fdc28e" d=" M 52.65 24.61 C 52.24 21.99 53.91 18.42 57.03 18.94 C 59.66 19.25 60.63 22.31 60.35 24.61 C 57.79 24.56 55.22 24.56 52.65 24.61 Z" />
<path fill="#fdc28e" d=" M 72.96 25.45 C 73.30 22.95 73.64 19.11 76.92 18.92 C 80.68 18.62 81.33 23.07 81.11 25.88 L 80.87 25.48 L 80.33 24.57 C 78.09 24.72 75.61 24.01 73.55 25.17 L 72.96 25.45 Z" />
<path fill="#fc8a21" d=" M 2.69 24.58 C 6.88 24.58 11.08 24.54 15.28 24.61 C 15.32 26.99 15.20 29.38 15.33 31.76 C 15.97 34.42 20.07 34.59 20.95 32.01 C 21.12 29.55 20.99 27.08 21.02 24.62 C 23.11 24.57 25.20 24.57 27.29 24.61 C 27.31 26.98 27.19 29.35 27.34 31.72 C 27.92 34.40 32.16 34.62 32.95 31.99 C 33.29 29.54 33.05 27.07 33.14 24.61 C 34.44 24.59 35.75 24.59 37.06 24.59 C 37.57 27.54 35.40 33.43 39.86 33.86 C 44.14 33.15 42.16 27.60 42.61 24.62 C 44.01 24.59 45.41 24.56 46.81 24.53 C 46.89 30.21 52.20 35.30 57.98 34.13 C 62.74 33.71 66.06 29.16 65.97 24.55 C 66.35 24.59 67.13 24.69 67.52 24.74 C 67.54 29.50 67.45 34.26 67.55 39.02 C 67.59 41.88 72.06 42.49 73.06 39.92 C 73.62 37.68 73.25 35.33 73.23 33.05 C 75.60 34.08 78.26 34.69 80.78 33.78 C 84.48 32.37 86.64 28.43 86.93 24.61 C 90.63 24.55 94.33 24.55 98.03 24.61 C 98.14 30.96 98.18 37.32 97.99 43.66 L 99.21 44.04 C 98.40 44.30 96.80 44.80 95.99 45.06 C 65.83 44.97 35.66 44.94 5.50 45.07 L 4.31 45.05 C 3.90 44.64 3.08 43.80 2.67 43.38 C 2.79 37.12 2.76 30.84 2.69 24.58 Z" />
<path fill="#fc8a21" d=" M 52.65 24.61 C 55.22 24.56 57.79 24.56 60.35 24.61 C 60.25 27.09 57.56 29.61 55.11 28.23 C 53.54 27.78 53.08 25.97 52.65 24.61 Z" />
<path fill="#fc8a21" d=" M 73.55 25.17 C 75.61 24.01 78.09 24.72 80.33 24.57 L 80.87 25.48 C 80.22 29.65 73.76 29.42 73.55 25.17 Z" />
<path fill="#fdb26d" d=" M 5.50 45.07 C 35.66 44.94 65.83 44.97 95.99 45.06 L 96.61 45.49 C 94.20 46.38 91.57 45.86 89.06 46.00 C 60.87 45.86 32.65 46.28 4.48 45.79 L 5.50 45.07 Z" />
<path fill="#a2a2a2" d=" M 2.04 44.37 L 4.48 45.79 C 32.65 46.28 60.87 45.86 89.06 46.00 C 91.57 45.86 94.20 46.38 96.61 45.49 C 97.37 45.99 98.05 46.60 98.63 47.31 C 95.79 47.82 92.91 48.15 90.02 48.15 C 64.66 48.12 39.31 48.15 13.95 48.13 C 11.09 48.07 8.24 47.83 5.39 47.64 C 4.55 46.82 2.87 45.19 2.04 44.37 Z" />
</svg>
]]
end
function addGauge(name, url, maxValue, width, height)
if(url ~= nil) then print('<A HREF="'..url..'">') end
print [[
<div class="progress">
<div id="]] print(name) print [[" class="progress-bar progress-bar-warning"></div>
</div>
]]
if(url ~= nil) then print('</A>\n') end
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 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
-- Return the first 'howmany' hosts
function getTopInterfaceHosts(howmany, localHostsOnly)
hosts_stats = interface.getHostsInfo()
hosts_stats = hosts_stats["hosts"]
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)
-- 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
-- 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
-- ##############################################
function splitNetworkPrefix(net)
local prefix = tonumber(net:match("/(.+)"))
local address = net:gsub("/.+","")
return address, prefix
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 getHostAltNamesKey()
return "ntopng.host_labels"
end
-- ##############################################
-- Used to avoid resolving host names too many times
resolved_host_labels_cache = {}
-- host_ip can be a mac. host_mac can be null.
function getHostAltName(host_ip, host_mac)
local alt_name = resolved_host_labels_cache[host_ip]
-- cache hit
if(alt_name ~= nil) then
return(alt_name)
end
alt_name = ntop.getHashCache(getHostAltNamesKey(), host_ip)
if (isEmptyString(alt_name) and (host_mac ~= nil)) then
alt_name = ntop.getHashCache(getHostAltNamesKey(), host_mac)
end
if isEmptyString(alt_name) then
alt_name = host_ip
end
if not isEmptyString(alt_name) then
resolved_host_labels_cache[host_ip] = alt_name
end
return(alt_name)
end
function setHostAltName(host_ip, alt_name)
ntop.setHashCache(getHostAltNamesKey(), host_ip, alt_name)
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 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
if(host_type == "srv") then
if(flow_info["host_server_name"] ~= nil and flow_info["host_server_name"] ~= "") then
return(flow_info["host_server_name"])
end
if(flow_info["protos.ssl.certificate"] ~= nil and flow_info["protos.ssl.certificate"] ~= "") then
return(flow_info["protos.ssl.certificate"])
end
end
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, novlan)
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(novlan == nil) then
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
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", true)
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(false)
new_version = version2int(rsp)
version_elems = split(info["version"], " ");
this_version = version2int(version_elems[1])
if(new_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.
-- You can call it as tprint(mytable)
-- The other two parameters should not be set
function tprint(s, l, i)
l = (l) or 1000; i = i or "";-- default item limit, indent string
if (l<1) then io.write("ERROR: Item limit reached.\n"); return l-1 end;
local ts = type(s);
if (ts ~= "table") then io.write(i..' '..ts..' '..tostring(s)..'\n'); return l-1 end
io.write(i..' '..ts..'\n');
for k,v in pairs(s) do
local indent = ""
if(i ~= "") then
indent = i .. "."
end
indent = indent .. tostring(k)
l = tprint(v, l, indent);
if (l < 0) then break end
end
return l
end
function 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
function table.slice(tbl, first, last, step)
local sliced = {}
for i = first or 1, last or #tbl, step or 1 do
sliced[#sliced+1] = tbl[i]
end
return sliced
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
function getRedisIfacePrefix(ifid)
return "ntopng.prefs.iface_"..tostring(ifid)
end
----- End of Redis Utils ------
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 isLocalPacketdumpEnabled()
local nbox_integration = ntop.getCache("ntopng.prefs.nbox_integration")
if nbox_integration == nil or nbox_integration ~= "1" then
nbox_integration = false
else
nbox_integration = true
end
return isAdministrator() and not nbox_integration and interface.isPacketInterface()
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)
local table_key = getRedisPrefix("ntopng.prefs.table")
local value = nil
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)
local table_key = getRedisPrefix("ntopng.prefs.table")
local value = nil
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)
if(interface_name == nil) then return("") end
-- io.write(debug.traceback().."\n")
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_name)
key = 'ntopng.prefs.'..interface_name..'.name'
custom_name = ntop.getCache(key)
if((custom_name ~= nil) and (custom_name ~= "")) then
return(custom_name)
else
interface.select(interface_name)
_ifstats = interface.getStats()
-- print(interface_name.."=".._ifstats.name)
return(_ifstats.name)
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-o' 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 < 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
function getPasswordInputPattern()
return [[^[\w\$\\!\/\(\)=\?\^\*@_\-\u0000-\u00ff]{1,}$]]
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
-- get_mac_classification
function get_mac_classification(m, extended_name)
local short_extended = ntop.getMacManufacturer(m) or {}
if extended_name then
return short_extended.extended or short_extended.short or m
else
return short_extended.short or m
end
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"
}
function macInfo(mac)
return(' <A HREF="' .. ntop.getHttpPrefix() .. '/lua/mac_details.lua?host='.. mac ..'">'..mac..'</A> ')
end
-- get_symbolic_mac
function get_symbolic_mac(mac_address, only_symbolic)
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
if(only_symbolic == true) then
return(magic_short_macs[m].."_"..t)
else
return(magic_short_macs[m].."_"..t.." ("..macInfo(mac_address)..")")
end
else
local s = get_mac_classification(m)
if(m == s) then
return(get_mac_classification(m)..":"..t)
else
if(only_symbolic == true) then
return(get_mac_classification(m).."_"..t)
else
return(get_mac_classification(m).."_"..t.." ("..macInfo(mac_address)..")")
end
end
end
end
end
function get_manufacturer_mac(mac_address)
local m = string.sub(mac_address, 1, 8)
local ret = get_mac_classification(m, true --[[ extended name --]])
if(ret == m) then ret = "n/a" end
return ret
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
if(ntop.exists(rrdpath)) then return true end
-- Let's now try SNMP counters
rrdpath = dirs.workingdir .. "/" .. ifId .. "/snmpstats/" .. getPathFromKey(host_ip) .. "/" .. rrdname
-- print(rrdpath)
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
-- ###############################################
function tolongint(what)
if(what == nil) then
return(0)
else
return(string.format("%u", what))
end
end
-- ###############################################
function trimSpace(what)
if(what == nil) then return("") end
return(string.gsub(string.gsub(what, "%s+", ""), "+%s", ""))
end
-- ###############################################
-- TODO: improve this function
function jsonencode(what)
what = string.gsub(what, '"', "'")
-- everything but all ASCII characters from the space to the tilde
what = string.gsub(what, "[^ -~]", " ")
-- cleanup line feeds and carriage returns
what = string.gsub(what, "\n", " ")
what = string.gsub(what, "\r", " ")
-- escape all the remaining backslashes
what = string.gsub(what, "\\", "\\\\")
-- max 1 sequential whitespace
what = string.gsub(what, " +"," ")
return(what)
end
-- ###############################################
function formatWebSite(site)
return("<A target=\"_blank\" HREF=\"http://"..site.."\">"..site.."</A> <i class=\"fa fa-external-link\"></i></th>")
end
-- Update Utils::flowstatus2str
function getFlowStatus(status)
if(status == 0) then return("<font color=green>Normal</font>")
elseif(status == 1) then return("<font color=orange>Slow TCP Connection</font>")
elseif(status == 2) then return("<font color=orange>Slow Application Header</font>")
elseif(status == 3) then return("<font color=orange>Slow Data Exchange (Slowloris?)</font>")
elseif(status == 4) then return("<font color=orange>Low Goodput</font>")
elseif(status == 5) then return("<font color=orange>Suspicious TCP SYN Probing (or server port down)</font>")
elseif(status == 6) then return("<font color=orange>TCP Connection Issues (Retransmission, OOO, Lost)</font>")
elseif(status == 7) then return("<font color=orange>Suspicious TCP Probing</font>")
elseif(status == 8) then return("<font color=orange>Flow emitted when network interface was alerted</font>")
elseif(status == 9) then return("<font color=orange>TCP connection refused</font>")
elseif(status == 10) then return("<font color=orange>SSL Certificate Mismatch</font>")
else return("<font color=orange>Unknown status ("..status..")</font>")
end
end
-- prints purged information for hosts / flows
function purgedErrorString()
return 'Very likely it is expired and ntopng has purged it from memory. You can set purge idle timeout settings from the <A HREF="'..ntop.getHttpPrefix()..'/lua/admin/prefs.lua?tab=in_memory"><i class="fa fa-flask"></i> Preferences</A>.'
end
-- print TCP flags
function printTCPFlags(flags)
if(hasbit(flags,0x01)) then print('<span class="label label-info">FIN</span> ') end
if(hasbit(flags,0x02)) then print('<span class="label label-info">SYN</span> ') end
if(hasbit(flags,0x04)) then print('<span class="label label-danger">RST</span> ') end
if(hasbit(flags,0x08)) then print('<span class="label label-info">PUSH</span> ') end
if(hasbit(flags,0x10)) then print('<span class="label label-info">ACK</span> ') end
if(hasbit(flags,0x20)) then print('<span class="label label-info">URG</span> ') end
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}
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
return res
end
-- ##########################################
function historicalProtoHostHref(ifId, host, l4_proto, ndpi_proto_id, info)
if ntop.isPro() and ntop.getPrefs().is_dump_flows_to_mysql_enabled == true then
local hist_url = ntop.getHttpPrefix().."/lua/pro/db_explorer.lua?search=true&ifid="..ifId
local now = os.time()
local ago1h = now - 3600
hist_url = hist_url.."&epoch_end="..tostring(now)
if((host ~= nil) and (host ~= "")) then hist_url = hist_url.."&"..hostinfo2url(host) end
if((l4_proto ~= nil) and (l4_proto ~= "")) then
hist_url = hist_url.."&l4proto="..l4_proto
end
if((ndpi_proto_id ~= nil) and (ndpi_proto_id ~= "")) then hist_url = hist_url.."&protocol="..ndpi_proto_id end
if((info ~= nil) and (info ~= "")) then hist_url = hist_url.."&info="..info end
print('&nbsp;')
-- print('<span class="label label-info">')
print('<a href="'..hist_url..'&epoch_begin='..tostring(ago1h)..'" title="Flows seen in the last hour"><i class="fa fa-history fa-lg"></i></a>')
-- print('</span>')
end
end
-- ##########################################
_icmp_types = {
{ 0, 0, "Echo Reply" },
{ 3, 0, "Network Unreachable" },
{ 3, 1, "Host Unreachable" },
{ 3, 2, "Protocol Unreachable" },
{ 3, 3, "Port Unreachable" },
{ 3, 4, "Fragmentation needed but no fragment bit set" },
{ 3, 5, "Source routing failed" },
{ 3, 6, "Destination network unknown" },
{ 3, 7, "Destination host unknown" },
{ 3, 8, "Source host isolated (obsolete)" },
{ 3, 9, "Destination network administratively prohibited" },
{ 3, 10, "Destination host administratively prohibited" },
{ 3, 11, "Network unreachable for TOS" },
{ 3, 12, "Host unreachable for TOS" },
{ 3, 13, "Communication administratively prohibited by filtering" },
{ 3, 14, "Host precedence violation" },
{ 3, 15, "Precedence cutoff in effect" },
{ 4, 0, "Source quench" },
{ 5, 0, "Redirect for network" },
{ 5, 1, "Redirect for host" },
{ 5, 2, "Redirect for TOS and network" },
{ 5, 3, "Redirect for TOS and host" },
{ 8, 0, "Echo request x" },
{ 9, 0, "Router advertisement" },
{ 10, 0, "Route solicitation" },
{ 11, 0, "TTL equals 0 during transit" },
{ 11, 1, "TTL equals 0 during reassembly" },
{ 12, 0, "IP header bad (catchall error)" },
{ 12, 1, "Required options missing" },
{ 13, 0, "Timestamp request (obsolete)" },
{ 14, 0, "Timestamp reply (obsolete)" },
{ 15, 0, "Information request (obsolete)" },
{ 16, 0, "Information reply (obsolete)" },
{ 17, 0, "Address mask request" },
{ 18, 0, "Address mask reply" }
}
-- Code is currently ignored on IVMPv6
_icmpv6_types = {
{ 0, "Reserved" },
{ 1, "Destination Unreachable" },
{ 2, "Packet Too Big" },
{ 3, "Time Exceeded" },
{ 4, "Parameter Problem" },
{ 100, "Private experimentation" },
{ 101, "Private experimentation" },
-- { 102-126, "Unassigned" },
{ 127, "Reserved for expansion of ICMPv6 error messages" },
{ 128, "Echo Request" },
{ 129, "Echo Reply" },
{ 130, "Multicast Listener Query" },
{ 131, "Multicast Listener Report" },
{ 132, "Multicast Listener Done" },
{ 133, "Router Solicitation" },
{ 134, "Router Advertisement" },
{ 135, "Neighbor Solicitation" },
{ 136, "Neighbor Advertisement" },
{ 137, "Redirect Message" },
{ 138, "Router Renumbering" },
{ 139, "ICMP Node Information Query" },
{ 140, "ICMP Node Information Response" },
{ 141, "Inverse Neighbor Discovery Solicitation Message" },
{ 142, "Inverse Neighbor Discovery Advertisement Message" },
{ 143, "Version 2 Multicast Listener Report" },
{ 144, "Home Agent Address Discovery Request Message" },
{ 145, "Home Agent Address Discovery Reply Message" },
{ 146, "Mobile Prefix Solicitation" },
{ 147, "Mobile Prefix Advertisement" },
{ 148, "Certification Path Solicitation Message" },
{ 149, "Certification Path Advertisement Message" },
{ 150, "ICMP messages utilized by experimental mobility protocols" },
{ 151, "Multicast Router Advertisement" },
{ 152, "Multicast Router Solicitation" },
{ 153, "Multicast Router Termination" },
{ 154, "FMIPv6 Messages" },
{ 155, "RPL Control Message" },
{ 156, "ILNPv6 Locator Update Message" },
{ 157, "Duplicate Address Request" },
{ 158, "Duplicate Address Confirmation" },
{ 159, "MPL Control Message" },
-- { 160-199, "Unassigned" },
{ 200, "Private experimentation" },
{ 201, "Private experimentation" },
{ 255, "Reserved for expansion of ICMPv6 informational messages" }
}
-- #############################################
function getICMPV6TypeCode(icmp)
local t = icmp.type
local c = icmp.code
for _, _e in ipairs(_icmpv6_types) do
if(_e[1] == t) then
return(_e[2])
end
end
return(t.."/"..c)
end
-- #############################################
function getICMPTypeCode(icmp)
local t = icmp.type
local c = icmp.code
for _, _e in ipairs(_icmp_types) do
if((_e[1] == t) and (_e[2] == c)) then
return(_e[3])
end
end
return(getICMPV6TypeCode(icmp))
end
-- #############################################
-- Add here the icons you guess based on the Mac address
local guess_icon_keys = {
["dell inc."] = "fa-desktop",
["vmware, inc."] = "fa-desktop",
["xensource, inc."] = "fa-desktop",
["lanner electronics, inc."] = "fa-desktop",
["nexcom international co., ltd."] = "fa-desktop",
["apple, inc."] = "fa-apple",
["cisco systems, inc"] = "fa-arrows",
["juniper networks"] = "fa-arrows",
["brocade communications systems, inc."] = "fa-arrows",
["force10 networks, inc."] = "fa-arrows",
["huawei technologies co.,ltd"] = "fa-arrows",
["alcatel-lucent ipd"] = "fa-arrows",
["arista networks, inc."] = "fa-arrows",
["3com corporation"] = "fa-arrows",
["routerboard.com"] = "fa-arrows",
["extreme networks"] = "fa-arrows",
["xerox corporation"] = "fa-print"
}
function guessHostIcon(key)
local m = string.lower(get_manufacturer_mac(key))
local icon = guess_icon_keys[m]
if((icon ~= nil) and (icon ~= "")) then
return(" <i class='fa "..icon.." fa-lg'></i>")
else
return ""
end
end
-- #############################################
local icon_keys = {
[ ""] = "",
[ "Computer"] = "fa-desktop",
[ "Laptop"] = "fa-laptop",
[ "Phone"] = "fa-mobile",
[ "Tablet"] = "fa-tablet",
[ "TV"] = "fa-television",
[ "Printer"] = "fa-print",
[ "Camera"] = "fa-video-camera",
[ "Router"] = "fa-arrows"
}
function getHostIcons()
return pairsByKeys(icon_keys, asc)
end
function getHostIconName(key)
local hash_key = "ntopng.host_icons"
local icon = ntop.getHashCache(hash_key, key)
if (icon == nil) then
icon = ""
end
return icon
end
function getHostIcon(key)
local icon = getHostIconName(key)
if((icon == nil) or (icon == "")) then
return(guessHostIcon(key))
else
return(" <i class='fa "..icon.." fa-lg'></i>")
end
end
function setHostIcon(key, icon)
ntop.setHashCache("ntopng.host_icons", key, icon)
end
function pickIcon(key, alt_key)
local icon = getHostIconName(key)
if (isEmptyString(icon) and (alt_key ~= nil)) then
icon = getHostIconName(alt_key)
end
print [[<div class="form-group"><select name="custom_icon" class="form-control">]]
for k,v in getHostIcons() do
print("<option value=\"".. v.."\"")
if(v == icon) then print(" selected") end
print(">".. k .."</option>")
end
print [[</select></div>]]
end
-- ####################################################
function tableToJsObject(lua_table)
local json = require("dkjson")
return json.encode(lua_table, nil)
end
-- ####################################################
-- Compute the difference in seconds between local time and UTC.
local function get_timezone()
local now = os.time()
return os.difftime(now, os.time(os.date("!*t", now)))
end
local function get_tzoffset(timezone)
local h, m = math.modf(timezone / 3600)
return string.format("%+.4d", 100 * h + 60 * m)
end
function get_timezone_offset()
local ts = get_tzoffset(get_timezone())
local utcdate = os.date("!*t", ts)
local localdate = os.date("*t", ts)
localdate.isdst = false -- this is the trick
return os.difftime(os.time(localdate), os.time(utcdate))
end
function format_time(timestamp, format, tzoffset)
if tzoffset == "local" then -- calculate local time zone (for the server)
local now = os.time()
local local_t = os.date("*t", now)
local utc_t = os.date("!*t", now)
local delta = (local_t.hour - utc_t.hour)*60 + (local_t.min - utc_t.min)
local h, m = math.modf( delta / 60)
tzoffset = string.format("%+.4d", 100 * h + 60 * m)
end
return os.date(format, timestamp + tzoffset)
end
function makeTimeStamp(d)
local pattern = "(%d+)%/(%d+)%/(%d+) (%d+):(%d+):(%d+)"
local day,month, year, hour, minute, seconds = string.match(d, pattern);
local timestamp = os.time({year=year, month=month, day=day, hour=hour, min=minute, sec=seconds});
return timestamp.."";
end
-- ###########################################
-- IMPORTANT: keep it in sync with sortField (ntop_typedefs.h)
-- AND host_search_walker:NetworkInterface.cpp
-- AND NetworkInterface::getFlows()
looking_glass_criteria = {
-- KEY LABEL Host::lua()-label formatting
{ "uploaders", i18n("uploaders"), "upload", bytesToSize },
{ "downloaders", i18n("downloaders"), "download", bytesToSize },
{ "unknowers", i18n("unknowers"), "unknown", bytesToSize },
{ "incomingflows", i18n("incomingflows"), "incomingflows", formatValue },
{ "outgoingflows", i18n("outgoingflows"), "outgoingflows", formatValue },
}
function criteria2label(criteria)
local id
for id, _ in ipairs(looking_glass_criteria) do
local key = looking_glass_criteria[id][1]
local label = looking_glass_criteria[id][2]
local fnctn = looking_glass_criteria[id][4]
if(key == criteria) then
return label, fnctn
end
end
return criteria, formatValue
end
function label2criteriakey(what)
local id
for id, _ in ipairs(looking_glass_criteria) do
local c = looking_glass_criteria[id][1]
local key = looking_glass_criteria[id][3]
local fnctn = looking_glass_criteria[id][4]
if(what == c) then
return key, fnctn
end
end
return what, formatValue
end
--
-- Concatenates table keys to values with separators
--
-- Parameters
-- keys_values: the table which contains the items
-- kv_sep: a string to be put between a key and a value
-- group_sep: a string to be put between key-value groups
-- last_sep: a string to be put after last value, if table is not empty
-- value_quote: a string to be used to quote values
--
function table.tconcat(keys_values, kv_sep, group_sep, last_sep, value_quote)
local groups = {}
kv_sep = kv_sep or ""
group_sep = group_sep or ""
last_sep = last_sep or ""
value_quote = value_quote or ""
for k, v in pairs(keys_values) do
local parts = {k, kv_sep, value_quote, v, value_quote}
groups[#groups + 1] = table.concat(parts, "")
end
if #groups > 0 then
return table.concat(groups, group_sep) .. last_sep
else
return ""
end
end
function table.merge(a, b)
local merged = {}
for _, t in ipairs({a, b}) do
for k,v in pairs(t) do
merged[k] = v
end
end
return merged
end
function table.clone(t, filter)
local clone = {}
local filter = filter or function (k, v) return true end
for k, v in pairs(t) do
if filter(k, v) then
clone[k] = v
end
end
return clone
end
function toboolean(s)
if s == "true" then
return true
elseif s == "false" then
return false
else
return nil
end
end
--
-- Find the highest divisor which divides input value.
-- val_idx can be used to index divisors values.
-- Returns the highest_idx
--
function highestDivisor(divisors, value, val_idx, iterator_fn)
local highest_idx = nil
local highest_val = nil
iterator_fn = iterator_fn or ipairs
for i, v in iterator_fn(divisors) do
local cmp_v
if val_idx ~= nil then
v = v[val_idx]
end
if((highest_val == nil) or ((v > highest_val) and (value % v == 0))) then
highest_val = v
highest_idx = i
end
end
return highest_idx
end
-- ###########################################
-- Note: the base unit is Kbit/s here
FMT_TO_DATA_RATES_KBPS = {
["k"] = {label="kbit/s", value=1},
["m"] = {label="Mbit/s", value=1000},
["g"] = {label="Gbit/s", 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="Secs", value=1},
["m"] = {label="Mins", value=60},
["h"] = {label="Hours", value=3600},
["d"] = {label="Days", value=3600*24},
}
-- ###########################################
-- Note: use data-min and data-max to setup ranges
function makeResolutionButtons(fmt_to_data, ctrl_id, fmt, value, extra)
local extra = extra or {}
local html_lines = {}
local divisors = {}
-- fill in divisors
if tonumber(value) ~= nil then
-- foreach character in format
string.gsub(fmt, ".", function(k)
local v = fmt_to_data[k]
if v ~= nil then
divisors[#divisors + 1] = {k=k, v=v.value}
end
end)
end
local selected = nil
if tonumber(value) ~= 0 then
selected = highestDivisor(divisors, value, "v")
end
if selected ~= nil then
selected = divisors[selected].k
else
selected = string.sub(fmt, 1, 1)
end
local style = table.merge({display="flex"}, extra.style or {})
html_lines[#html_lines+1] = [[<div class="btn-group ]] .. table.concat(extra.classes or {}, "") .. [[" id="]] .. ctrl_id .. [[" data-toggle="buttons" style="]] .. table.tconcat(style, ":", "; ", ";") .. [[">]]
-- foreach character in format
string.gsub(fmt, ".", function(k)
local v = fmt_to_data[k]
if v ~= nil then
local line = {}
line[#line+1] = [[<label class="btn]]
if selected == k then
line[#line+1] = [[ btn-primary active]]
else
line[#line+1] = [[ btn-default]]
end
line[#line+1] = [[ btn-sm"><input data-resol="]] .. k .. [[" value="]] .. v.value .. [[" title="]] .. v.label .. [[" name="opt_resbt_]] .. k .. [[_]] .. ctrl_id .. [[" autocomplete="off" type="radio"]]
if selected == k then line[#line+1] = [[ checked="checked"]] end
line[#line+1] = [[/>]] .. v.label .. [[</label>]]
html_lines[#html_lines+1] = table.concat(line, "")
end
end)
html_lines[#html_lines+1] = [[</div>]]
-- Note: no // comment below, only /* */
local js_init_code = [[
var _resol_inputs = [];
function resol_selector_get_input(a_button) {
return $("input", $(a_button).closest(".form-group")).last();
}
function resol_selector_get_buttons(an_input) {
return $(".btn-group", $(an_input).closest(".form-group")).first().find("input");
}
/* This function scales values wrt selected resolution */
function resol_selector_reset_input_range(selected) {
var selected = $(selected);
var input = resol_selector_get_input(selected);
var raw = parseInt(input.attr("data-min"));
if (! isNaN(raw))
input.attr("min", Math.sign(raw) * Math.ceil(Math.abs(raw) / selected.val()));
raw = parseInt(input.attr("data-max"));
if (! isNaN(raw))
input.attr("max", Math.sign(raw) * Math.ceil(Math.abs(raw) / selected.val()));
var step = parseInt(input.attr("data-step-" + selected.attr("data-resol")));
if (! isNaN(step)) {
input.attr("step", step);
/* Align value */
input.val(input.val() - input.val() % step);
} else
input.attr("step", "");
resol_recheck_input_range(input);
}
function resol_selector_change_selection(selected) {
selected.attr('checked', 'checked')
.closest("label").removeClass('btn-default').addClass('btn-primary')
.siblings().removeClass('active').removeClass('btn-primary').addClass('btn-default').find("input").removeAttr('checked');
resol_selector_reset_input_range(selected);
}
function resol_recheck_input_range(input) {
var value = input.val();
if (input[0].hasAttribute("min"))
value = Math.max(value, input.attr("min"));
if (input[0].hasAttribute("max"))
value = Math.min(value, input.attr("max"));
var old_val = input.val();
if ((old_val != "") && (old_val != value))
input.val(value);
}
function resol_selector_change_callback(event) {
resol_selector_change_selection($(this));
}
function resol_selector_on_form_submit(event) {
var form = $(this);
if (event.isDefaultPrevented()) /* isDefaultPrevented is true when the form is invalid */
return false;
resol_selector_finalize(form);
return true;
}
/* Helper function to set a selector value by raw value */
function resol_selector_set_value(input_id, its_value) {
var input = $(input_id);
var buttons = resol_selector_get_buttons($(input_id));
var values = [];
buttons.each(function() {
values.push(parseInt($(this).val()));
});
var new_value;
var new_i;
if (its_value > 0) {
/* highest divisor */
var highest_i = 0;
for (var i=1; i<values.length; i++) {
if(((values[i] > values[highest_i]) && (its_value % values[i] == 0)))
highest_i = i;
}
new_value = its_value / values[highest_i];
new_i = highest_i;
} else {
/* smallest value */
new_value = Math.max(its_value, -1);
new_i = values.indexOf(Math.min.apply(Math, values));
}
/* Set */
input.val(new_value);
resol_selector_change_selection($(buttons[new_i]));
/* This must be set manually on initialization */
$(buttons[new_i]).closest("label").addClass("active");
}
function resol_selector_get_raw(input) {
var buttons = resol_selector_get_buttons(input);
var selected = buttons.filter(":checked");
return parseInt(selected.val()) * parseInt(input.val());
}
function resol_selector_finalize(form) {
$.each(_resol_inputs, function(i, elem) {
/* Skip elements which are not part of the form */
if (! $(elem).closest("form").is(form))
return;
var selected = $(elem).find("input[checked]");
var input = resol_selector_get_input(selected);
resol_recheck_input_range(input);
/* transform in raw units */
var new_input = $("<input type=\"hidden\"/>");
new_input.attr("name", input.attr("name"));
input.removeAttr("name");
new_input.val(resol_selector_get_raw(input));
new_input.appendTo(form);
});
/* remove added input names */
$("input[name^=opt_resbt_]", form).removeAttr("name");
}]]
local js_specific_code = [[
$("#]] .. ctrl_id .. [[ input").change(resol_selector_change_callback);
$(function() {
var elemid = "#]] .. ctrl_id .. [[";
_resol_inputs.push(elemid);
var selected = $(elemid + " input[checked]");
resol_selector_reset_input_range(selected);
/* setup the form submit callback (only once) */
var form = selected.closest("form");
if (! form.attr("data-options-handler")) {
form.attr("data-options-handler", 1);
form.submit(resol_selector_on_form_submit);
}
});
]]
-- join strings and strip newlines
local html = string.gsub(table.concat(html_lines, ""), "\n", "")
js_init_code = string.gsub(js_init_code, "\n", "")
js_specific_code = string.gsub(js_specific_code, "\n", "")
if tonumber(value) ~= nil then
-- returns the new value with selected resolution
return {html=html, init=js_init_code, js=js_specific_code, value=tonumber(value) / fmt_to_data[selected].value}
else
return {html=html, init=js_init_code, js=js_specific_code, value=nil}
end
end
-- ###########################################
--
-- 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["bridge.device_a"] ~= nil) and (ifstats["bridge.device_b"] ~= nil)
end
function isCaptivePortalActive(ifstats)
if not ntop.isEnterprise() then
return false
end
local ifstats = ifstats or interface.getStats()
local is_bridge_iface = isBridgeInterface(ifstats)
local is_captive_portal_enabled = ntop.getPrefs()["is_captive_portal_enabled"]
return is_bridge_iface and is_captive_portal_enabled
end
function hasSnmpDevices(ifid)
if (not ntop.isEnterprise()) or (not isAdministrator()) then
return false
end
return has_snmp_devices(ifid)
end
function getTopFlowPeers(hostname_vlan, max_hits, detailed)
local detailed = detailed or false
local paginator_options = {
sortColumn = "column_bytes",
a2zSortOrder = false,
detailedResults = detailed,
maxHits = max_hits,
}
local res = interface.getFlowsInfo(hostname_vlan, paginator_options)
if ((res ~= nil) and (res.flows ~= nil)) then
return res.flows
else
return {}
end
end
function stripVlan(name)
local key = string.split(name, "@")
if(key ~= nil) then
return(key[1])
else
return(name)
end
end
-- ###########################################
--
-- IMPORTANT
-- Leave it at the end so it can use the functions
-- defined in this file
--
http_lint = require "http_lint"