-- -- (C) 2013-15 - ntop.org -- top_rrds = { ["bytes.rrd"] = "Traffic", ["packets.rrd"] = "Packets", ["drops.rrd"] = "Packet Drops", ["num_flows.rrd"] = "Active Flows", ["num_hosts.rrd"] = "Active Hosts", ["num_http_hosts.rrd"] = "Active HTTP Servers" } -- ######################################################## if(ntop.isPro()) then package.path = dirs.installdir .. "/pro/scripts/lua/modules/?.lua;" .. package.path require "nv_graph_utils" end -- ######################################################## function getProtoVolume(ifName, start_time, end_time) ifId = getInterfaceId(ifName) path = fixPath(dirs.workingdir .. "/" .. ifId .. "/rrd/") rrds = ntop.readdir(path) ret = { } for rrdFile,v in pairs(rrds) do if((string.ends(rrdFile, ".rrd")) and (top_rrds[rrdFile] == nil)) then rrdname = getRRDName(ifId, nil, rrdFile) if(ntop.notEmptyFile(rrdname)) then local fstart, fstep, fnames, fdata = ntop.rrd_fetch(rrdname, 'AVERAGE', start_time, end_time) local num_points_found = table.getn(fdata) accumulated = 0 for i, v in ipairs(fdata) do for _, w in ipairs(v) do if(w ~= w) then -- This is a NaN v = 0 else --io.write(w.."\n") v = tonumber(w) if(v < 0) then v = 0 end end end accumulated = accumulated + v end if(accumulated > 0) then rrdFile = string.sub(rrdFile, 1, string.len(rrdFile)-4) ret[rrdFile] = accumulated end end end end return(ret) end -- ######################################################## function navigatedir(url, label, base, path, go_deep, print_html) local shown = false local to_skip = false local ret = { } local do_debug = false local printed = false rrds = ntop.readdir(path) table.sort(rrds) for k,v in pairsByKeys(rrds, asc) do if(v ~= nil) then p = fixPath(path .. "/" .. v) if(ntop.isdir(p)) then if(go_deep) then r = navigatedir(url, label.."/"..v, base, p, print_html) for k,v in pairs(r) do ret[k] = v if(do_debug) then print(v.."
\n") end end end else if(top_rrds[v] == nil) then if(label == "*") then to_skip = true else if(not(shown) and not(to_skip)) then if(print_html) then if(not(printed)) then print('
  • \n') printed = true end print('\n') end end return(ret) end -- ######################################################## function breakdownBar(sent, sentLabel, rcvd, rcvdLabel) if((sent+rcvd) > 0) then sent2rcvd = round((sent * 100) / (sent+rcvd), 0) print('
    '..sentLabel) print('
    ' .. rcvdLabel .. '
    ') else print(' ') end end -- ######################################################## function percentageBar(total, value, valueLabel) if((total ~= nil) and (total > 0)) then pctg = round((value * 100) / total, 0) print('
    '..valueLabel) print('
    ') else print(' ') end end -- ######################################################## function getRRDName(ifid, host, rrdFile) rrdname = fixPath(dirs.workingdir .. "/" .. ifid .. "/rrd/") if(host ~= nil) then rrdname = rrdname .. getPathFromKey(host) .. "/" end return(rrdname .. rrdFile) end -- ######################################################## zoom_vals = { { "1m", "now-60s", 60 }, { "5m", "now-300s", 60*5 }, { "10m", "now-600s", 60*10 }, { "1h", "now-1h", 60*60*1 }, { "3h", "now-3h", 60*60*3 }, { "6h", "now-6h", 60*60*6 }, { "12h", "now-12h", 60*60*12 }, { "1d", "now-1d", 60*60*24 }, { "1w", "now-1w", 60*60*24*7 }, { "2w", "now-2w", 60*60*24*14 }, { "1M", "now-1mon", 60*60*24*31 }, { "6M", "now-6mon", 60*60*24*31*6 }, { "1Y", "now-1y", 60*60*24*366 } } function getZoomAtPos(cur_zoom, pos_offset) local pos = 1 local new_zoom_level = cur_zoom for k,v in pairs(zoom_vals) do if(zoom_vals[k][1] == cur_zoom) then if (pos+pos_offset >= 1 and pos+pos_offset < 13) then new_zoom_level = zoom_vals[pos+pos_offset][1] break end end pos = pos + 1 end return new_zoom_level end -- ######################################################## function getZoomDuration(cur_zoom) for k,v in pairs(zoom_vals) do if(zoom_vals[k][1] == cur_zoom) then return(zoom_vals[k][3]) end end return(180) end -- ######################################################## function drawPeity(ifid, host, rrdFile, zoomLevel, selectedEpoch) rrdname = getRRDName(ifid, host, rrdFile) if(zoomLevel == nil) then zoomLevel = "1h" end nextZoomLevel = zoomLevel; epoch = tonumber(selectedEpoch); for k,v in ipairs(zoom_vals) do if(zoom_vals[k][1] == zoomLevel) then if(k > 1) then nextZoomLevel = zoom_vals[k-1][1] end if(epoch) then start_time = epoch - zoom_vals[k][3]/2 end_time = epoch + zoom_vals[k][3]/2 else start_time = zoom_vals[k][2] end_time = "now" end end end --print("=> Found "..rrdname.."

    \n") if(ntop.notEmptyFile(rrdname)) then --io.write("=> Found ".. start_time .. "|" .. end_time .. "

    \n") local fstart, fstep, fnames, fdata = ntop.rrd_fetch(rrdname, 'AVERAGE', start_time..", end_time..") local max_num_points = 512 -- This is to avoid having too many points and thus a fat graph local num_points_found = table.getn(fdata) local sample_rate = round(num_points_found / max_num_points) local num_points = 0 local step = 1 local series = {} if(sample_rate < 1) then sample_rate = 1 end -- print("=> "..num_points_found.."[".. sample_rate .."]["..fstart.."]

    ") id = 0 num = 0 total = 0 sample_rate = sample_rate-1 points = {} for i, v in ipairs(fdata) do timestamp = fstart + (i-1)*fstep num_points = num_points + 1 local elemId = 1 for _, w in ipairs(v) do if(w ~= w) then -- This is a NaN v = 0 else v = tonumber(w) if(v < 0) then v = 0 end end value = v*8 -- bps total = total + value if(id == sample_rate) then points[num] = round(value).."" num = num+1 id = 0 else id = id + 1 end elemId = elemId + 1 end end end print(""..round(total).." ") for i=0,10 do if(i > 0) then print(",") end print(points[i]) end print("\n") end -- ######################################################## function drawRRD(ifid, host, rrdFile, zoomLevel, baseurl, show_timeseries, selectedEpoch, selected_epoch_sanitized, topArray) local debug_rrd = false if(zoomLevel == nil) then zoomLevel = "1h" end if((selectedEpoch == nil) or (selectedEpoch == "")) then -- Refresh the page every minute unless a specific epoch has been selected print("\n"); end if(ntop.isPro()) then drawProGraph(ifid, host, rrdFile, zoomLevel, baseurl, show_timeseries, selectedEpoch, selected_epoch_sanitized, topArray) return end dirs = ntop.getDirs() rrdname = getRRDName(ifid, host, rrdFile) names = {} series = {} if(zoomLevel == nil) then zoomLevel = "1h" end nextZoomLevel = zoomLevel; epoch = tonumber(selectedEpoch); for k,v in ipairs(zoom_vals) do if(zoom_vals[k][1] == zoomLevel) then if(k > 1) then nextZoomLevel = zoom_vals[k-1][1] end if(epoch) then start_time = epoch - zoom_vals[k][3]/2 end_time = epoch + zoom_vals[k][3]/2 else start_time = zoom_vals[k][2] end_time = "now" end end end local maxval_bits_time = 0 local maxval_bits = 0 local minval_bits = 0 local minval_bits_time = 0 local lastval_bits = 0 local lastval_bits_time = 0 local total_bytes = 0 local num_points = 0 local step = 1 prefixLabel = l4Label(string.gsub(rrdFile, ".rrd", "")) -- io.write(prefixLabel.."\n") if(prefixLabel == "Bytes") then prefixLabel = "Traffic" end if(ntop.notEmptyFile(rrdname)) then -- print("=> Found "..rrdname.."

    \n") -- print("=> "..rrdname) -- io.write("=> *** ".. start_time .. "|" .. end_time .. "

    \n") local fstart, fstep, fnames, fdata = ntop.rrd_fetch(rrdname, 'AVERAGE', start_time, end_time) --print("=> here we go") local max_num_points = 600 -- This is to avoid having too many points and thus a fat graph local num_points_found = table.getn(fdata) local sample_rate = round(num_points_found / max_num_points) if(sample_rate < 1) then sample_rate = 1 end -- DEBUG --tprint(fdata, 1) step = fstep num = 0 names_cache = {} for i, n in ipairs(fnames) do -- handle duplicates if (names_cache[n] == nil) then names_cache[n] = true names[num] = prefixLabel if(host ~= nil) then names[num] = names[num] .. " (" .. firstToUpper(n)..")" end num = num + 1 --io.write(prefixLabel.."\n") --print(num.."\n") end end id = 0 sampling = 0 --sample_rate = 1 sample_rate = sample_rate-1 accumulated = 0 for i, v in ipairs(fdata) do s = {} s[0] = fstart + (i-1)*fstep num_points = num_points + 1 local elemId = 1 for _, w in ipairs(v) do if(w ~= w) then -- This is a NaN v = 0 else --io.write(w.."\n") v = tonumber(w) if(v < 0) then v = 0 end end if(v > 0) then lastval_bits_time = s[0] lastval_bits = v end s[elemId] = v*8 -- bps --if(s[elemId] > 0) then io.write("[".. elemId .. "]=" .. s[elemId] .."\n") end elemId = elemId + 1 end total_bytes = total_bytes + v*fstep --if((v*fstep) > 0) then io.write(" | " .. (v*fstep) .." | [sampling: ".. sampling .. "/" .. sample_rate.."]\n") end if(sampling == sample_rate) then if(sample_rate > 0) then s[1] = accumulated / sample_rate end series[id] = s id = id + 1 sampling = 0 accumulated = 0 else accumulated = accumulated + s[1] sampling = sampling + 1 end end for key, value in pairs(series) do local t = 0 for elemId=0,(num-1) do --io.write(key.."="..value[elemId+1].. "\n") t = t + value[elemId+1] -- bps end t = t * step if(((minval_bits_time == 0) or (minval_bits >= t)) and (value[0] < lastval_bits_time)) then --io.write(value[0].."\t".. t .. "\t".. lastval_bits_time .. "\n") minval_bits_time = value[0] minval_bits = t end if((maxval_bits_time == 0) or (maxval_bits <= t)) then maxval_bits_time = value[0] maxval_bits = t end end print [[

    ]] if(show_timeseries == 1) then print [[
    ]] end -- show_timeseries == 1 print(' Timeframe:
    \n') for k,v in ipairs(zoom_vals) do print('\n') end print [[

    NOTE: Click on the graph to zoom.

    ]] if(string.contains(rrdFile, "num_")) then formatter_fctn = "fint" else formatter_fctn = "fpackets" end if (topArray ~= nil) then print [[ ]] print(' \n') if(string.contains(rrdFile, "num_") or string.contains(rrdFile, "packets") or string.contains(rrdFile, "drops")) then print(' \n') print(' \n') print(' \n') print(' \n') print(' \n') else formatter_fctn = "fbits" print(' \n') print(' \n') print(' \n') print(' \n') print(' \n') end print(' \n') print(' \n') print [[
     TimeValue
    Min' .. os.date("%x %X", minval_bits_time) .. '' .. formatValue(round(minval_bits/step), 1) .. '
    Max' .. os.date("%x %X", maxval_bits_time) .. '' .. formatValue(round(maxval_bits/step), 1) .. '
    Last' .. os.date("%x %X", last_time) .. '' .. formatValue(round(lastval_bits/step), 1) .. '
    Average' .. formatValue(round(total_bytes*8/(step*num_points), 2)) .. '
    Total Number' .. formatValue(round(total_bytes)) .. '
    Min' .. os.date("%x %X", minval_bits_time) .. '' .. bitsToSize(minval_bits/step) .. '
    Max' .. os.date("%x %X", maxval_bits_time) .. '' .. bitsToSize(maxval_bits/step) .. '
    Last' .. os.date("%x %X", last_time) .. '' .. bitsToSize(lastval_bits/step) .. '
    Average' .. bitsToSize(total_bytes*8/(step*num_points)) .. '
    Total Traffic' .. bytesToSize(total_bytes) .. '
    Selection Time
    Minute
    Top Talkers
    ]] end -- topArray ~= nil print[[

    ]] printGraphTopFlows(ifid, (host or ''), _GET["epoch"], zoomLevel, rrdFile) print [[ ]] else print("

    File "..rrdname.." cannot be found
    ") end end -- ######################################################## function create_rrd(name, step, ds) if(not(ntop.exists(name))) then if(enable_second_debug == 1) then io.write('Creating RRD ', name, '\n') end local prefs = ntop.getPrefs() ntop.rrd_create( name, step, -- step 'DS:' .. ds .. ':DERIVE:5:U:U', 'RRA:AVERAGE:0.5:1:'..tostring(prefs.intf_rrd_raw_days*24*60*60), -- raw: 1 day = 86400 'RRA:AVERAGE:0.5:60:'..tostring(prefs.intf_rrd_1min_days*24*60), -- 1 min resolution = 1 month 'RRA:AVERAGE:0.5:3600:'..tostring(prefs.intf_rrd_1h_days*24), -- 1h resolution (3600 points) 2400 hours = 100 days 'RRA:AVERAGE:0.5:86400:'..tostring(prefs.intf_rrd_1d_days) -- 1d resolution (86400 points) 365 days -- 'RRA:HWPREDICT:1440:0.1:0.0035:20' ) end end function create_rrd_num(name, ds) if(not(ntop.exists(name))) then if(enable_second_debug == 1) then io.write('Creating RRD ', name, '\n') end local prefs = ntop.getPrefs() ntop.rrd_create( name, 1, -- step 'DS:' .. ds .. ':GAUGE:5:0:U', 'RRA:AVERAGE:0.5:1:'..tostring(prefs.intf_rrd_raw_days*24*60*60), -- raw: 1 day = 86400 'RRA:AVERAGE:0.5:3600:'..tostring(prefs.intf_rrd_1h_days*24), -- 1h resolution (3600 points) 2400 hours = 100 days 'RRA:AVERAGE:0.5:86400:'..tostring(prefs.intf_rrd_1d_days) -- 1d resolution (86400 points) 365 days -- 'RRA:HWPREDICT:1440:0.1:0.0035:20' ) end end function makeRRD(basedir, ifname, rrdname, step, value) name = fixPath(basedir .. "/" .. rrdname .. ".rrd") if(string.contains(rrdname, "num_")) then create_rrd_num(name, rrdname) else create_rrd(name, 1, rrdname) end ntop.rrd_update(name, "N:".. toint(value)) if(enable_second_debug == 1) then io.write('Updating RRD ['.. ifname..'] '.. name .. " " .. value ..'\n') end end function createRRDcounter(path, step, verbose) if(not(ntop.exists(path))) then if(verbose) then print('Creating RRD ', path, '\n') end local prefs = ntop.getPrefs() ntop.rrd_create( path, step, -- step 'DS:sent:DERIVE:600:U:U', 'DS:rcvd:DERIVE:600:U:U', 'RRA:AVERAGE:0.5:1:'..tostring(prefs.other_rrd_raw_days*24*300), -- raw: 1 day = 1 * 24 = 24 * 300 sec = 7200 'RRA:AVERAGE:0.5:12:'..tostring(prefs.other_rrd_1h_days*24), -- 1h resolution (12 points) 2400 hours = 100 days 'RRA:AVERAGE:0.5:288:'..tostring(prefs.other_rrd_1d_days) -- 1d resolution (288 points) 365 days --'RRA:HWPREDICT:1440:0.1:0.0035:20' ) end end -- ######################################################## function createSingleRRDcounter(path, verbose) if(not(ntop.exists(path))) then if(verbose) then print('Creating RRD ', path, '\n') end local prefs = ntop.getPrefs() ntop.rrd_create( path, 300, -- step 'DS:num:DERIVE:600:U:U', 'RRA:AVERAGE:0.5:1:'..tostring(prefs.other_rrd_raw_days*24*300), -- raw: 1 day = 1 * 24 = 24 * 300 sec = 7200 'RRA:AVERAGE:0.5:12:'..tostring(prefs.other_rrd_1h_days*24), -- 1h resolution (12 points) 2400 hours = 100 days 'RRA:AVERAGE:0.5:288:'..tostring(prefs.other_rrd_1d_days), -- 1d resolution (288 points) 365 days 'RRA:HWPREDICT:1440:0.1:0.0035:20') end end -- ######################################################## function dumpSingleTreeCounters(basedir, label, host, verbose) what = host[label] if(what ~= nil) then for k,v in pairs(what) do for k1,v1 in pairs(v) do -- print("-->"..k1.."/".. type(v1).."<--\n") if(type(v1) == "table") then for k2,v2 in pairs(v1) do dname = fixPath(basedir.."/"..label.."/"..k.."/"..k1) if(not(ntop.exists(dname))) then ntop.mkdir(dname) end fname = dname..fixPath("/"..k2..".rrd") createSingleRRDcounter(fname, verbose) ntop.rrd_update(fname, "N:"..toint(v2)) if(verbose) then print("\t"..fname.."\n") end end else dname = fixPath(basedir.."/"..label.."/"..k) if(not(ntop.exists(dname))) then ntop.mkdir(dname) end fname = dname..fixPath("/"..k1..".rrd") createSingleRRDcounter(fname, verbose) ntop.rrd_update(fname, "N:"..toint(v1)) if(verbose) then print("\t"..fname.."\n") end end end end end end function printGraphTopFlows(ifId, host, epoch, zoomLevel, l7proto) -- Check if the DB is enabled rsp = interface.execSQLQuery("show tables") if(rsp == nil) then return end if((epoch == nil) or (epoch == "")) then epoch = os.time() end local d = getZoomDuration(zoomLevel)/2 epoch_end = epoch+d epoch_begin = epoch-d printTopFlows(ifId, host, epoch_begin, epoch_end, l7proto, '', '', '', 5, 5) end function printTopFlows(ifId, host, epoch_begin, epoch_end, l7proto, l4proto, port, info, limitv4, limitv6) url_update = "/lua/get_db_flows.lua?ifId="..ifId.. "&host="..(host or '') .. "&epoch_begin="..epoch_begin.."&epoch_end="..epoch_end.."&l4proto="..l4proto.."&port="..port.."&info="..info if(l7proto ~= "") then if(not(isnumber(l7proto))) then local id -- io.write(l7proto.."\n") l7proto = string.gsub(l7proto, "%.rrd", "") if(string.ends(l7proto, ".rrd")) then l7proto = string.sub(l7proto, 1, -5) end id = interface.getnDPIProtoId(l7proto) if(id ~= -1) then l7proto = id title = "Top "..l7proto.." Flows" else l7proto = "" end end if(l7proto ~= "") then url_update = url_update.."&l7proto="..l7proto end end if((host == "") and (l4proto == "") and (port == "")) then title = "Top Flows ["..formatEpoch(epoch_begin).." - "..formatEpoch(epoch_end).."]" else title = "" end print [[
    ]] if(not((limitv4 == nil) or (limitv4 == "") or (limitv4 == "0"))) then print [[
    ]] end if(not((limitv6 == nil) or (limitv6 == "") or (limitv6 == "0"))) then if(selected == false) then print('
    \n') else print('
    \n') end print('
    ') end print [[
    ") return end print [[ var url_update6 = "]] print(url_update.."&limit="..limitv6) print [[&version=6"; var graph_options6 = { url: url_update6, perPage: 5, ]] if(title ~= "") then print('title: "IPv6 '..title..'",\n') else print("title: '',\n") end print [[ showFilter: true, showPagination: true, sort: [ [ "BYTES","desc"] ], columns: [ { title: "Key", field: "idx", hidden: true, }, ]] if(ntop.isPro()) then print [[ { title: "", field: "FLOW_URL", sortable: false, css: { textAlign: 'center' } }, ]] end print [[ { title: "Application", field: "L7_PROTO", sortable: true, css: { textAlign: 'center' } }, { title: "L4 Proto", field: "PROTOCOL", sortable: true, css: { textAlign: 'center' } }, { title: "Client", field: "CLIENT", sortable: false, }, { title: "Server", field: "SERVER", sortable: false, }, { title: "Begin", field: "FIRST_SWITCHED", sortable: true, css: { textAlign: 'center' } }, { title: "End", field: "LAST_SWITCHED", sortable: true, css: { textAlign: 'center' } }, { title: "Bytes", field: "BYTES", sortable: true, css: { textAlign: 'right' } }, { title: "Avg Thpt", field: "AVG_THROUGHPUT", sortable: false, css: { textAlign: 'right' } } ] }; var table6 = $("#table-flows6").datatable(graph_options6); ]] end