EVOLUTION-MANAGER
Edit File: discover_utils.lua
-- -- (C) 2017-24 - ntop.org -- local json = require "dkjson" local page_utils = require "page_utils" require "locales_utils" require "xmlSimple" local discover = {} discover.debug = false discover.progress_string = "discovery.progess" discover.osinfo = { [0] = {"Unknown", ''}, [1] = {"Linux", '<i class=\'fab fa-linux fa-lg\'></i>'}, [2] = {"Windows", '<i class=\'fab fa-windows fa-lg\'></i>'}, [3] = {"MacOS", '<i class=\'fab fa-apple fa-lg\'></i>'}, [4] = {"iOS", '<i class=\'fab fa-apple fa-lg\'></i>'}, [5] = {"Android", '<i class=\'fab fa-android fa-lg\'></i>'}, [6] = {"LaserJET", 'LasetJET'}, [7] = {"AppleAirport", 'Apple Airport'} -- NOTE: keep in sync with OperatingSystem in ntop_typedefs.h } -- ################################# function discover.getOsName(id) local info = discover.osinfo[tonumber(id)] if (info) then return (info[1]) end return ("") end -- ################################# function discover.getOsIcon(id) local info = discover.osinfo[tonumber(id)] if (info) then return (info[2] .. " ") end return ("") end -- ################################# function discover.getOsAndIcon(id) local name = discover.getOsName(id) if (isEmptyString(name)) then return (id) else return (discover.getOsIcon(id) .. name) end end -- ################################# discover.apple_osx_versions = { ['4'] = 'Mac OS X 10.0 (Cheetah)', ['5'] = 'Mac OS X 10.1 (Puma)', ['6'] = 'Mac OS X 10.2 (Jaguar)', ['7'] = 'Mac OS X 10.3 (Panther)', ['8'] = 'Mac OS X 10.4 (Tiger)', ['9'] = 'Mac OS X 10.5 (Leopard)', ['10'] = 'Mac OS X 10.6 (Snow Leopard)', ['11'] = 'Mac OS X 10.7 (Lion)', ['12'] = 'OS X 10.8 (Mountain Lion)', ['13'] = 'OS X 10.9 (Mavericks)', ['14'] = 'OS X 10.10 (Yosemite)', ['15'] = 'OS X 10.11 (El Capitan)', ['16'] = 'OS X 10.12 (Sierra)', ['17'] = 'OS X 10.13 (High Sierra)', ['18'] = 'macOS 10.14 (Mojave)', ['19'] = 'macOS 10.15 (Catalina)' } discover.apple_products = { ['Macmini5,3'] = 'Mac mini "Core i7" 2.0 (Mid-2011/Server)', ['Macmini5,2'] = 'Mac mini "Core i7" 2.7 (Mid-2011)', ['Macmini5,1'] = 'Mac mini "Core i5" 2.3 (Mid-2011)', ['MacPro4,1'] = 'Mac Pro "Eight Core" 2.93 (2009/Nehalem)', ['iMac16,2'] = 'iMac "Core i7" 3.3 21.5-Inch (4K, Late 2015)', ['iMac16,1'] = 'iMac "Core i5" 1.6 21.5-Inch (Late 2015)', ['iMac5,1'] = 'iMac "Core 2 Duo" 2.33 20-Inch', ['MacBookPro7,1'] = 'MacBook Pro "Core 2 Duo" 2.66 13" Mid-2010', ['MacPro2,1'] = 'Mac Pro "Eight Core" 3.0 (2,1)', ['MacBook10,1'] = 'MacBook "Core i7" 1.4 12" (Mid-2017-18)', ['Macmini1,1'] = 'Mac mini "Core Duo" 1.83', ['iMac12,2'] = 'iMac "Core i7" 3.4 27-Inch (Mid-2011)', ['iMac6,1'] = 'iMac "Core 2 Duo" 2.33 24-Inch', ['MacBookPro5,1'] = 'MacBook Pro "Core 2 Duo" 2.93 15" (Unibody)', ['MacBookPro11,5'] = 'MacBook Pro "Core i7" 2.8 15" Mid-2015 (DG)', ['MacBookPro11,4'] = 'MacBook Pro "Core i7" 2.8 15" Mid-2015 (IG)', ['MacBookPro11,3'] = 'MacBook Pro "Core i7" 2.8 15" Mid-2014 (DG)', ['MacBookPro11,2'] = 'MacBook Pro "Core i7" 2.8 15" Mid-2014 (IG)', ['MacBookPro11,1'] = 'MacBook Pro "Core i7" 3.0 13" Mid-2014', ['MacBookPro10,2'] = 'MacBook Pro "Core i7" 3.0 13" Early 2013', ['MacBookPro10,1'] = 'MacBook Pro "Core i7" 2.8 15" Early 2013', ['MacBookPro5,5'] = 'MacBook Pro "Core 2 Duo" 2.53 13" (SD/FW)', ['MacBookAir7,1'] = 'MacBook Air "Core i7" 2.2 11" (Early 2015)', ['MacBookAir7,2'] = 'MacBook Air "Core i7" 2.2 13" (Early 2015)', ['iMac17,1'] = 'iMac "Core i7" 4.0 27-Inch (5K, Late 2015)', ['MacBookPro8,1'] = 'MacBook Pro "Core i7" 2.8 13" Late 2011', ['MacBookPro8,2'] = 'MacBook Pro "Core i7" 2.5 15" Late 2011', ['MacBookPro8,3'] = 'MacBook Pro "Core i7" 2.5 17" Late 2011', ['MacBook6,1'] = 'MacBook "Core 2 Duo" 2.26 13" (Uni/Late 09)', ['MacBookPro4,1'] = 'MacBook Pro "Core 2 Duo" 2.6 17" (08)', ['Macmini4,1'] = 'Mac mini "Core 2 Duo" 2.66 (Server)', ['PowerMac10,2'] = 'Mac mini G4/1.5', ['PowerMac10,1'] = 'Mac mini G4/1.42', ['iMac13,2'] = 'iMac "Core i7" 3.4 27-Inch (Late 2012)', ['iMac13,1'] = 'iMac "Core i3" 3.3 21.5-Inch (Early 2013)', ['iMac9,1'] = 'iMac "Core 2 Duo" 2.26 20-Inch (Mid-2009)', ['Macmini3,1'] = 'Mac mini "Core 2 Duo" 2.53 (Server)', ['iMac5,2'] = 'iMac "Core 2 Duo" 1.83 17-Inch (IG)', ['MacBook2,1'] = 'MacBook "Core 2 Duo" 2.16 13" (Black)', ['MacBook1,1'] = 'MacBook "Core Duo" 2.0 13" (Black)', ['iMac14,4'] = 'iMac "Core i5" 1.4 21.5-Inch (Mid-2014)', ['iMac14,1'] = 'iMac "Core i5" 2.7 21.5-Inch (Late 2013)', ['iMac14,3'] = 'iMac "Core i7" 3.1 21.5-Inch (Late 2013)', ['iMac14,2'] = 'iMac "Core i7" 3.5 27-Inch (Late 2013)', ['MacBookPro2,2'] = 'MacBook Pro "Core 2 Duo" 2.33 15"', ['MacBookAir3,2'] = 'MacBook Air "Core 2 Duo" 2.13 13" (Late 2010)', ['MacBookPro13,1'] = 'MacBook Pro "Core i7" 2.4 13" Late 2016', ['MacBookPro13,3'] = 'MacBook Pro "Core i7" 2.9 15" Touch/Late 2016', ['MacBookPro13,2'] = 'MacBook Pro "Core i7" 3.3 13" Touch/Late 2016', ['MacBook9,1'] = 'MacBook "Core m7" 1.3 12" (Early 2016)', ['MacBookAir6,1'] = 'MacBook Air "Core i7" 1.7 11" (Early 2014)', ['MacBookAir6,2'] = 'MacBook Air "Core i7" 1.7 13" (Early 2014)', ['MacBookPro9,1'] = 'MacBook Pro "Core i7" 2.7 15" Mid-2012', ['MacBookPro9,2'] = 'MacBook Pro "Core i7" 2.9 13" Mid-2012', ['MacBook3,1'] = 'MacBook "Core 2 Duo" 2.2 13" (Black-SR)', ['MacPro6,1'] = 'Mac Pro "Twelve Core" 2.7 (Late 2013)', ['iMac10,1'] = 'iMac "Core 2 Duo" 3.33 27-Inch (Late 2009)', ['MacBookPro1,1'] = 'MacBook Pro "Core Duo" 2.16 15"', ['MacBookPro5,3'] = 'MacBook Pro "Core 2 Duo" 3.06 15" (SD)', ['MacBookPro5,2'] = 'MacBook Pro "Core 2 Duo" 3.06 17" Mid-2009', ['iMac8,1'] = 'iMac "Core 2 Duo" 3.06 24-Inch (Early 2008)', ['MacBookPro5,4'] = 'MacBook Pro "Core 2 Duo" 2.53 15" (SD)', ['Macmini2,1'] = 'Mac mini "Core 2 Duo" 2.0', ['MacBookAir3,1'] = 'MacBook Air "Core 2 Duo" 1.6 11" (Late 2010)', ['Macmini6,1'] = 'Mac mini "Core i5" 2.5 (Late 2012)', ['MacBookPro1,2'] = 'MacBook Pro "Core Duo" 2.16 17"', ['iMac4,1'] = 'iMac "Core Duo" 2.0 20-Inch', ['iMac4,2'] = 'iMac "Core Duo" 1.83 17-Inch (IG)', ['Macmini7,1'] = 'Mac mini "Core i7" 3.0 (Late 2014)', ['MacBookPro2,1'] = 'MacBook Pro "Core 2 Duo" 2.33 17"', ['MacBook5,1'] = 'MacBook "Core 2 Duo" 2.4 13" (Unibody)', ['MacBook5,2'] = 'MacBook "Core 2 Duo" 2.13 13" (White-09)', ['MacBookPro14,2'] = 'MacBook Pro "Core i7" 3.5 13" Touch/Mid-2017-18', ['MacBookPro14,3'] = 'MacBook Pro "Core i7" 3.1 15" Touch/Mid-2017-18', ['MacPro1,1*'] = 'Mac Pro "Quad Core" 3.0 (Original)', ['MacBookPro14,1'] = 'MacBook Pro "Core i7" 2.5 13" Mid-2017-18', ['MacBookPro12,1'] = 'MacBook Pro "Core i7" 3.1 13" Early 2015', ['MacBook8,1'] = 'MacBook "Core M" 1.3 12" (Early 2015)', ['iMac15,1'] = 'iMac "Core i5" 3.3 27-Inch (5K, Mid-2015)', ['MacBookAir1,1'] = 'MacBook Air "Core 2 Duo" 1.8 13" (Original)', ['MacBookAir2,1'] = 'MacBook Air "Core 2 Duo" 2.13 13" (Mid-09)', ['iMac7,1'] = 'iMac "Core 2 Extreme" 2.8 24-Inch (Al)', ['MacBookAir5,2'] = 'MacBook Air "Core i7" 2.0 13" (Mid-2012)', ['MacBook4,1'] = 'MacBook "Core 2 Duo" 2.4 13" (Black-08)', ['MacBookAir5,1'] = 'MacBook Air "Core i7" 2.0 11" (Mid-2012)', ['MacBookPro3,1'] = 'MacBook Pro "Core 2 Duo" 2.6 17" (SR)', ['iMac11,1'] = 'iMac "Core i7" 2.8 27-Inch (Late 2009)', ['iMac11,2'] = 'iMac "Core i5" 3.6 21.5-Inch (Mid-2010)', ['iMac11,3'] = 'iMac "Core i7" 2.93 27-Inch (Mid-2010)', ['MacBook7,1'] = 'MacBook "Core 2 Duo" 2.4 13" (Mid-2010)', ['Macmini6,2'] = 'Mac mini "Core i7" 2.6 (Late 2012/Server)', ['MacPro5,1'] = 'Mac Pro "Twelve Core" 3.06 (Server 2012)', ['MacBookPro6,2'] = 'MacBook Pro "Core i7" 2.8 15" Mid-2010', ['MacBookPro6,1'] = 'MacBook Pro "Core i7" 2.8 17" Mid-2010', ['iMac18,1'] = 'iMac "Core i5" 2.3 21.5-Inch (Mid-2017-18)', ['iMac18,3'] = 'iMac "Core i7" 4.2 27-Inch (5K, Mid-2017-18)', ['iMac18,2'] = 'iMac "Core i7" 3.6 21.5-Inch (4K, Mid-2017-18)', ['iMac12,1'] = 'iMac "Core i3" 3.1 21.5-Inch (Late 2011)', ['MacBookAir4,2'] = 'MacBook Air "Core i5" 1.6 13" (Edu Only)', ['MacBookAir4,1'] = 'MacBook Air "Core i7" 1.8 11" (Mid-2011)', ['MacPro3,1'] = 'Mac Pro "Eight Core" 3.2 (2008)' } discover.asset_icons = { ['unknown'] = '', ['printer'] = '<i class="fas fa-print fa-lg devtype-icon" aria-hidden="true"></i>', -- 1 ['video'] = '<i class="fas fa-video fa-lg devtype-icon" aria-hidden="true"></i>', -- 2 ['workstation'] = '<i class="fas fa-desktop fa-lg devtype-icon" aria-hidden="true"></i>', -- ... and so on ['laptop'] = '<i class="fas fa-laptop fa-lg devtype-icon" aria-hidden="true"></i>', ['tablet'] = '<i class="fas fa-tablet fa-lg devtype-icon" aria-hidden="true"></i>', ['phone'] = '<i class="fas fa-mobile fa-lg devtype-icon" aria-hidden="true"></i>', ['tv'] = '<i class="fas fa-tv fa-lg devtype-icon" aria-hidden="true"></i>', ['networking'] = '<i class="fas fa-arrows-alt fa-lg devtype-icon" aria-hidden="true"></i>', ['wifi'] = '<i class="fas fa-wifi fa-lg devtype-icon" aria-hidden="true"></i>', ['nas'] = '<i class="fas fa-database fa-lg devtype-icon" aria-hidden="true"></i>', ['multimedia'] = '<i class="fas fa-music fa-lg devtype-icon" aria-hidden="true"></i>', ['iot'] = '<i class="fas fa-thermometer fa-lg devtype-icon" aria-hidden="true"></i>' -- IMPORTANT: please keep in sync asset_icons with id2label } discover.extra_asset_icons = { ['lightbulb'] = '<i class="fas fa-lightbulb fa-lg devtype-icon" aria-hidden="true"></i>', ['phone'] = '<i class="fas fa-phone fa-lg devtype-icon" aria-hidden="true"></i>', ['health'] = '<i class="fas fa-medkit fa-lg devtype-icon" aria-hidden="true"></i>' } local id2label = { -- ID string_id label special_purpose_device [0] = {'unknown', i18n("device_types.unknown")}, [1] = {'printer', i18n("device_types.printer"), true}, [2] = {'video', i18n("device_types.video"), true}, [3] = {'workstation', i18n("device_types.workstation")}, [4] = {'laptop', i18n("device_types.laptop")}, [5] = {'tablet', i18n("device_types.tablet")}, [6] = {'phone', i18n("device_types.phone")}, [7] = {'tv', i18n("device_types.tv"), true}, [8] = {'networking', i18n("device_types.networking"), true}, [9] = {'wifi', i18n("device_types.wifi"), true}, [10] = {'nas', i18n("device_types.nas"), true}, [11] = {'multimedia', i18n("device_types.multimedia"), true}, [12] = {'iot', i18n("device_types.iot"), true} -- IMPORTANT: please keep in sync asset_icons with id2label } discover.ghost_icon = '<i class="fas fa-ghost fa-lg" aria-hidden="true"></i>' discover.android_icon = '<i class="fab fa-android fa-lg" aria-hidden="true"></i>' discover.apple_icon = '<i class="fab fa-apple fa-lg" aria-hidden="true"></i>' discover.MAX_DISCOVERED_DEVICES = 8192 -- ################################################################################ local discover_progress_key = "ntop.discovery_progress" function setDiscoveryProgress(progress) -- io.write(progress.."\n") ntop.setCache(discover_progress_key, progress, 60) end function discover.getDiscoveryProgress() return (ntop.getCache(discover_progress_key) or "") end -- ################################################################################ local function device_label_sort_fn(a, b) return asc_insensitive(a[2], b[2]) end -- ################################################################################ function discover.sortedDeviceTypeLabels() return pairsByValues(id2label, device_label_sort_fn) end -- ################################################################################ local function getCachedDiscoveryKey(interface_name) return "ntopng.cache.ifid_" .. getInterfaceId(interface_name) .. ".device_discovery_status" end -- ################################################################################ local function getCachedDiscoveredDevicesKey(interface_name) return "ntopng.cache.ifid_" .. getInterfaceId(interface_name) .. ".discovered_devices" end -- ################################################################################ function discover.getInterfaceNetworkDiscoveryEnabledKey(ifid) return "ntopng.prefs.ifid_" .. ifid .. ".interface_network_discovery" end -- ################################################################################ function discover.interfaceNetworkDiscoveryEnabled(ifid) -- must be discoverable and must have not been disabled by the interface preferences return interface.isDiscoverableInterface() and not (ntop.getPref(discover.getInterfaceNetworkDiscoveryEnabledKey(ifid)) == "false") end -- ################################################################################ local function getRequestNetworkDiscoveryKey(ifid) return "ntopng.cache.ifid_" .. ifid .. ".network_discovery_requested" end -- ################################################################################ function discover.requestNetworkDiscovery(ifid) ntop.setCache(getRequestNetworkDiscoveryKey(ifid), "true") end -- ################################################################################ function discover.clearNetworkDiscovery(ifid) ntop.delCache(getRequestNetworkDiscoveryKey(ifid)) end -- ################################################################################ function discover.networkDiscoveryRequested(ifid) return ntop.getCache(getRequestNetworkDiscoveryKey(ifid)) == "true" end -- ################################################################################ function isPrinterMDNS(mdns) if (mdns == nil) then return false end if (discover.debug) then tprint(mdns) io.write(debug.traceback()) end if ((mdns["_printer._tcp.local"] ~= nil) or (mdns["_scanner._tcp.local"] ~= nil) or (mdns["_pdl-datastream._tcp.local"] ~= nil)) then return true end return false end -- ################################################################################ function isNASMDNS(mdns) if (mdns == nil) then return false end if ((mdns["_edcp._udp.local"] ~= nil) or (mdns["_afpovertcp._tcp.local"] ~= nil) or (mdns["_smb._tcp.local"] ~= nil)) then if (mdns["_companion-link._tcp.local"] == nil) then return true end end return false end -- ################################################################################ function isGHomeMDNS(mdns) if (mdns == nil) then return false end if (discover.debug) then tprint(mdns) io.write(debug.traceback()) end if ((mdns["_googlecast._tcp.local"] ~= nil) or (mdns["_googlezone._tcp.local"] ~= nil)) then return true end return false end -- ################################################################################ function discover.printDeviceTypeSelectorOptions(device_type, skip_unknown) device_type = tonumber(device_type) for typeid, info in discover.sortedDeviceTypeLabels() do local devtype = info[1] local label = info[2] if not skip_unknown or devtype ~= "unknown" then print("<option value=\"" .. typeid .. "\"") if (typeid == device_type) then print(" selected") end print(">" .. label .. "</option>") end end end -- ################################################################################ function discover.printDeviceTypeSelector(device_type, field_name) print [[<div class="form-group mb-3"><select name="]] print(field_name) print [[" class="form-select">\ <option value="0"></option>]] discover.printDeviceTypeSelectorOptions(device_type, true) print [[</select></div>]] end -- ################################################################################ function discover.devtype2icon(devtype) local label = id2label[tonumber(devtype) or 0] if (label == nil) then label = 'unknown' else label = label[1] end return (discover.asset_icons[label]) end -- ################################################################################ function discover.isValidDevtype(devtype) for k, v in pairs(id2label) do if (v[1] == devtype) then return (true) end end return (false) end -- ################################################################################ function discover.devtype2id(devtype) for k, v in pairs(id2label) do if (v[1] == devtype) then return k end end return (0) -- unknown end -- ################################################################################ function discover.id2devtype(id) for k, v in pairs(id2label) do if (k == id) then return v[1] end end return ("") -- unknown end -- ################################################################################ function discover.devtype2string(devtype) devtype = tonumber(devtype) for k, v in pairs(id2label) do if (k == devtype) then return v[2] end end return ("") -- unknown end -- ################################################################################ function discover.isSpecialPurposeDevice(id) local v = id2label[id] if (v) then return (v[3] or false) end return (false) end -- ################################################################################ function discover.getGeneralPurposeDevicesList() local rv = {} for k, v in pairsByField(id2label, 2, asc) do if not v[3] then rv[#rv + 1] = v[2] end end return rv end -- ################################################################################ function discover.getSpecialPurposeDevicesList() local rv = {} for k, v in pairsByField(id2label, 2, asc) do if v[3] then rv[#rv + 1] = v[2] end end return rv end -- ################################################################################ local function getbaseURL(url) local name = url:match("([^/]+)$") if ((name == "") or (name == nil)) then return (url) else return (string.sub(url, 1, string.len(url) - string.len(name) - 1)) end end -- ################################################################################ local function probeSSH(ip) local ssh_rsp = ntop.tcpProbe(ip, 22, 1) if (ssh_rsp ~= nil) then local _ssh_rsp = string.gsub(ssh_rsp, "\n", "") local elems local osvers = nil local use_last = false -- tprint(_ssh_rsp) elems = string.split(_ssh_rsp, ' ') if (elems == nil) then osvers = _ssh_rsp use_last = true else osvers = elems[2] if (osvers == nil) then osvers = elems[1] end end -- tprint(osvers) if (osvers ~= nil) then local _osvers = string.split(osvers, "-") if (_osvers ~= nil) then local n if (use_last) then n = #_osvers else n = 1 end osvers = _osvers[n] -- tprint(_osvers) return (trimSpace(osvers)) end end end return (trimSpace(ssh_rsp)) end local function appendSSHOS(mac, ip) local r = probeSSH(ip) if ((r ~= nil) and (r ~= "")) then if (string.contains(r, "Debian") or string.contains(r, "Raspbian") or string.contains(r, "dropbear") or string.contains(r, "Ubuntu")) then interface.setHostOperatingSystem(ip, 1) -- 1 = Linux elseif (string.contains(r, "MS")) then interface.setHostOperatingSystem(ip, 2) -- 2 = windows end return (" (" .. r .. ")") else return ("") end end -- ################################################################################ local function findDevice(ip, mac, manufacturer, _mdns, ssdp_str, ssdp_entries, names, snmpName, snmpDescr, osx, symName) local mdns = {} local ssdp = {} local str local friendlyName = "" if (snmpName ~= nil) then ntop.setHashCache("ntopng.prefs.snmp_devices", ip, ntop.getPref("ntopng.prefs.default_snmp_community")) end if ((ssdp_entries ~= nil) and (ssdp_entries.friendlyName ~= nil)) then friendlyName = ssdp_entries["friendlyName"] end if ((names == nil) or (names[ip] == nil)) then hostname = "" else hostname = string.lower(names[ip]) end if (manufacturer == nil) then manufacturer = "" else manufacturer = string.lower(manufacturer) end if (symName == nil) then symName = "" else symName = string.lower(symName) end if (_mdns ~= nil) then if (discover.debug) then io.write(mac .. " /" .. manufacturer .. " / " .. _mdns .. "\n") end local mdns_items = string.split(_mdns, ";") if (mdns_items == nil) then mdns[_mdns] = 1 else for _, v in pairs(mdns_items) do mdns[v] = 1 end end end if (ssdp_str ~= nil) then if (discover.debug) then io.write(mac .. " /" .. manufacturer .. " / " .. ssdp_str .. "\n") end local ssdp_items = string.split(ssdp_str, ";") if (ssdp_items == nil) then ssdp[ssdp_str] = 1 else for _, v in pairs(ssdp_items) do ssdp[v] = 1 end end end if (osx ~= nil) then -- model=iMac11,3;osxvers=16 local elems = string.split(osx, ';') if ((elems == nil) and string.contains(osx, "model=")) then elems = {} table.insert(elems, osx) end if (elems ~= nil) then local model = string.split(elems[1], '=') local osxvers = nil if (discover.apple_products[model[2]] ~= nil) then model = discover.apple_products[model[2]] if (model == nil) then model = "" end else model = model[2] end if (elems[2] ~= nil) then local osxvers = string.split(elems[2], '=') if (discover.apple_osx_versions[osxvers[2]] ~= nil) then osxvers = discover.apple_osx_versions[osxvers[2]] if (osxvers == nil) then osxvers = "" end else osxvers = osxvers[2] end end osx = "<br>" .. model if (osxvers ~= nil) then osx = osx .. "<br>" .. osxvers end end end if (mdns["_ssh._tcp.local"] ~= nil) then local icon = 'workstation' local ret if (osx ~= nil) then if (string.contains(osx, "MacBook")) then icon = 'laptop' end end ret = '</i>' .. discover.asset_icons[icon] .. ' ' .. discover.apple_icon if (osx ~= nil) then ret = ret .. osx end interface.setHostOperatingSystem(ip, 3) -- 3 = OSX if (discover.debug) then io.write(debug.traceback()) end return icon, ret, nil elseif (mdns["_nvstream_dbd._tcp.local"] ~= nil) then interface.setHostOperatingSystem(ip, 2) -- 2 = windows if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. ' (Windows)', nil elseif (isGHomeMDNS(mdns)) then -- Google Home if (discover.debug) then io.write(debug.traceback()) end return 'multimedia', discover.asset_icons['multimedia'], nil elseif (mdns["_workstation._tcp.local"] ~= nil) then interface.setHostOperatingSystem(ip, 1) -- 1 = Linux if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. ' (Linux)', nil else interface.setHostOperatingSystem(ip, 0) -- Unknown end if (string.contains(friendlyName, "TV")) then if (discover.debug) then io.write(debug.traceback()) end return 'tv', discover.asset_icons['tv'], nil end if ((ssdp["urn:upnp-org:serviceId:AVTransport"] ~= nil) or (ssdp["urn:upnp-org:serviceId:RenderingControl"] ~= nil) or (ssdp["urn:upnp-org:serviceId:MusicServices"] ~= nil) or (mdns["_airplay._tcp.local"] ~= nil)) then if (discover.debug) then io.write(debug.traceback()) end return 'multimedia', discover.asset_icons['multimedia'], nil end if (ssdp["_airport._tcp.local"] ~= nil) then if (discover.debug) then io.write(debug.traceback()) end return 'wifi', discover.asset_icons['wifi'], nil end if (ssdp_entries and ssdp_entries["modelDescription"]) then local descr = string.lower(ssdp_entries["modelDescription"]) if (string.contains(descr, "camera")) then if (discover.debug) then io.write(debug.traceback()) end return 'video', discover.asset_icons['video'], nil elseif (string.contains(descr, "router")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil end end if (discover.debug) then io.write("[manufacturer] " .. manufacturer .. "\n") end if ((discover.debug) and (snmpName ~= nil)) then io.write("[snmpName] " .. snmpName .. "\n") end if (string.contains(manufacturer, "oki electric") and (snmpName ~= nil)) then if (discover.debug) then io.write(debug.traceback()) end return 'printer', discover.asset_icons['printer'] .. ' (' .. snmpName .. ')', snmpName elseif (string.contains(manufacturer, "lexmark")) then if (discover.debug) then io.write(debug.traceback()) end return 'printer', discover.asset_icons['printer'], nil elseif (string.contains(manufacturer, "hikvision")) then if (discover.debug) then io.write(debug.traceback()) end return 'video', discover.asset_icons['video'], nil elseif (string.contains(manufacturer, "synology")) then if (discover.debug) then io.write(debug.traceback()) end return 'nas', discover.asset_icons['nas'], nil elseif (string.contains(manufacturer, "sonos")) then if (discover.debug) then io.write(debug.traceback()) end return 'multimedia', discover.asset_icons['multimedia'], nil elseif (string.contains(manufacturer, "super micro")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(manufacturer, "quanta computer Inc")) then -- Often Dell DRACK if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(manufacturer, "fujitsu technology solutions")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(manufacturer, "asustek computer")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['laptop'], nil elseif (string.contains(manufacturer, "raspberry")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(manufacturer, "juniper networks")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil elseif (string.contains(manufacturer, "brocade communications")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil elseif (string.contains(manufacturer, "cisco")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil elseif (string.contains(manufacturer, "routerboard") or string.contains(manufacturer, "netgear") -- or string.contains(manufacturer, "tp-link")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil elseif (string.contains(manufacturer, "3com")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil elseif (string.contains(manufacturer, "gigaset")) then if (discover.debug) then io.write(debug.traceback()) end return 'phone', discover.extra_asset_icons['phone'], nil elseif (string.contains(manufacturer, "palo alto networks")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil elseif (string.contains(manufacturer, "liteon technology")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(manufacturer, "realtek")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(manufacturer, 'tp%-link')) then -- % is the escape char in Lua if (discover.debug) then io.write(debug.traceback()) end return 'wifi', discover.asset_icons['wifi'], nil elseif (string.contains(manufacturer, 'broadband')) then -- % is the escape char in Lua if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil elseif (string.contains(manufacturer, 'nest labs') or string.contains(manufacturer, 'netatmo')) then if (discover.debug) then io.write(debug.traceback()) end return 'iot', discover.asset_icons['iot'], nil elseif (string.contains(manufacturer, "samsung electronics") or string.contains(manufacturer, "samsung electro") or string.contains(manufacturer, "htc corporation") or string.contains(manufacturer, "huawei") or (string.contains(manufacturer, "xiaomi communications") or string.starts(mac, "44:D1:96")) or string.contains(manufacturer, "oneplus") or string.contains(manufacturer, "mobile communications") -- LG Electronics (Mobile Communications) ) then if (discover.debug) then io.write(debug.traceback()) end interface.setHostOperatingSystem(ip, 5) -- 5 = Android return 'phone', discover.asset_icons['phone'] .. ' ' .. discover.android_icon, nil elseif ((string.contains(manufacturer, "hewlett packard") or string.contains(manufacturer, "hon hai")) and (snmpName ~= nil)) then local _snmpName = string.lower(snmpName) local _snmpDescr -- io.write("IP "..ip.." has descr (".. _snmpName ..")\n") if (snmpDescr == nil) then -- io.write("IP "..ip.." has empty descr (".. _snmpName ..")\n") _snmpDescr = _snmpName else _snmpDescr = string.lower(snmpDescr) end if (string.contains(_snmpDescr, "jet") -- JetDirect, LaserJet, InkJet, DeskJet or string.contains(_snmpDescr, "fax")) then if (discover.debug) then io.write(debug.traceback()) end return 'printer', discover.asset_icons['printer'] .. ' (' .. snmpName .. ')', snmpName elseif (string.contains(_snmpDescr, "curve")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'] .. ' (' .. snmpName .. ')', snmpName else -- return 'unknown', snmpName, nil end elseif (string.contains(manufacturer, "vmware") or string.contains(manufacturer, "qemu") or string.contains(manufacturer, "xen") or string.contains(manufacturer, "parallel")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. " (VM)", nil elseif (string.contains(manufacturer, "xerox") or string.contains(manufacturer, "ricoh")) then local l = "" if (snmpName ~= nil) then l = ' (' .. snmpName .. ')' end if (discover.debug) then io.write(debug.traceback()) end return 'printer', discover.asset_icons['printer'] .. l, snmpName elseif (string.contains(manufacturer, "apple")) then if (string.contains(hostname, "iphone") or string.contains(symName, "iphone")) then interface.setHostOperatingSystem(ip, 4) -- 4 = iOS if (discover.debug) then io.write(debug.traceback()) end return 'phone', discover.asset_icons['phone'] .. ' (' .. discover.apple_icon .. ' iPhone)', nil elseif (string.contains(hostname, "ipad") or string.contains(symName, "ipad")) then interface.setHostOperatingSystem(ip, 4) -- 4 = iOS if (discover.debug) then io.write(debug.traceback()) end return 'tablet', discover.asset_icons['tablet'] .. ' (' .. discover.apple_icon .. 'iPad)', nil elseif (string.contains(hostname, "ipod") or string.contains(symName, "ipod")) then interface.setHostOperatingSystem(ip, 4) -- 4 = iOS if (discover.debug) then io.write(debug.traceback()) end return 'phone', discover.asset_icons['phone'] .. ' (' .. discover.apple_icon .. 'iPod)', nil else local ret = '</i> ' .. discover.asset_icons['workstation'] .. ' ' .. discover.apple_icon local what = 'workstation' if (((snmpName ~= nil) and string.contains(snmpName, "capsul")) or string.contains(symName, "capsule") or string.contains(hostname, "capsule") or isNASMDNS(mdns)) then ret = '</i> ' .. discover.asset_icons['nas'], nil what = 'nas' elseif (string.contains(symName, "book") or string.contains(hostname, "book")) then ret = '</i> ' .. discover.asset_icons['laptop'] .. ' ' .. discover.apple_icon, nil what = 'laptop' elseif (string.contains(symName, "airport")) then ret = '</i> ' .. discover.asset_icons['wifi'] .. ' ' .. discover.apple_icon, nil what = 'laptop' end if (snmpName ~= nil) then ret = ret .. " [" .. snmpName .. "]" end interface.setHostOperatingSystem(ip, 3) -- 3 = OSX if (discover.debug) then io.write(debug.traceback()) end return what, ret, snmpName end end -- Amazon devices if (string.contains(mac, "F0:4F:7C") and string.contains(hostname, "kindle-")) then if (discover.debug) then io.write(debug.traceback()) end return 'tablet', discover.asset_icons['tablet'] .. ' (Kindle)', "Kindle" elseif (string.contains(mac, "40:B4:CD") -- and string.contains(hostname, "amazon-") ) then if (discover.debug) then io.write(debug.traceback()) end return 'multimedia', discover.asset_icons['multimedia'], "Alexa" -- Alexa end -- io.write("==>"..mac .. " /" .. manufacturer .. " / ".. friendlyName.. "/"..discover.extra_asset_icons['lightbulb'] .."\n") -- Philips Hue if (string.contains(mac, "00:17:88") and string.contains(friendlyName, "hue")) then if (discover.debug) then io.write(debug.traceback()) end return 'iot', discover.extra_asset_icons['lightbulb'], nil end -- Withings (Nokia Health) if (string.contains(mac, "00:24:E4")) then if (discover.debug) then io.write(debug.traceback()) end return 'iot', discover.extra_asset_icons['health'], nil end -- Logitech if (string.contains(manufacturer, "logitech")) then if (string.contains(friendlyName, "Harmony")) then if (discover.debug) then io.write(debug.traceback()) end return 'iot', discover.asset_icons['iot'], nil else if (discover.debug) then io.write(debug.traceback()) end return 'multimedia', discover.asset_icons['multimedia'], nil end end if (names["gateway.local"] == ip) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'], nil end if (string.starts(hostname, "desktop-") or string.starts(symName, "desktop-")) then interface.setHostOperatingSystem(ip, 2) -- 2 = windows if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. ' (Windows)', nil elseif (string.contains(hostname, "thinkpad") or string.contains(symName, "thinkpad")) then if (discover.debug) then io.write(debug.traceback()) end return 'laptop', discover.asset_icons['laptop'], nil elseif (string.contains(hostname, "android") or string.contains(symName, "android")) then interface.setHostOperatingSystem(ip, 5) -- 5 = Android if (discover.debug) then io.write(debug.traceback()) end return 'phone', discover.asset_icons['phone'] .. ' ' .. discover.android_icon, nil elseif (string.contains(hostname, "%-NAS") or string.contains(symName, "%-NAS")) then if (discover.debug) then io.write(debug.traceback()) end return 'nas', discover.asset_icons['nas'], nil end if (snmpName ~= nil) then if (string.contains(snmpName, "router") or string.contains(snmpName, "switch")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'] .. ' (' .. snmpName .. ')', snmpName elseif (string.contains(snmpName, "air")) then if (discover.debug) then io.write(debug.traceback()) end return 'wifi', discover.asset_icons['wifi'] .. ' (' .. snmpName .. ')', snmpName end end if (snmpName == nil) then snmpName = "" else snmpName = ' (' .. snmpName .. ')' end if (string.contains(manufacturer, "ubiquiti")) then if (discover.debug) then io.write(debug.traceback()) end return 'networking', discover.asset_icons['networking'] .. snmpName, nil end if (isPrinterMDNS(mdns)) then if (discover.debug) then io.write(debug.traceback()) end return 'printer', discover.asset_icons['printer'] .. snmpName, nil end if (isNASMDNS(mdns)) then if (discover.debug) then io.write(debug.traceback()) end return 'nas', discover.asset_icons['nas'] .. snmpName, nil end if (discover.debug) then tprint(ssdp) tprint(mdns) end -- Let's try SSH ssh_rsp = probeSSH(ip) -- Last resort is HTTP http_rsp = ntop.httpGet("http://" .. ip, nil, nil, 1) if ((http_rsp ~= nil) and (http_rsp.HTTP_HEADER ~= nil)) then local server = http_rsp.HTTP_HEADER["server"] if (server ~= nil) then if (discover.debug) then io.write("[SSH] " .. server) end if (string.contains(server, "Ubuntu") or string.contains(server, "Debian") or string.contains(server, "Linux")) then interface.setHostOperatingSystem(ip, 1) -- 1 = Linux if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. ' (Linux)', nil elseif (string.contains(server, "Apache")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(server, "GoAhead")) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), nil elseif (string.contains(server, "Microsoft")) then if (discover.debug) then io.write(debug.traceback()) end interface.setHostOperatingSystem(ip, 2) -- 2 = windows return 'workstation', discover.asset_icons['workstation'] .. ' (Windows)', nil elseif (string.contains(server, "Virata%-EmWeb") or string.contains(server, "HP%-ChaiSOE") -- Usually LaserJet or string.contains(server, "EWS%-NIC5") -- Xerox or string.contains(server, "RomPager") -- Xerox or string.contains(server, "eHTTP") -- Aruba or string.contains(server, "Web-Server") -- Ricoh ) then if (discover.debug) then io.write(debug.traceback()) end return 'printer', discover.asset_icons['printer'], nil end end -- io.write(ip.."\n") -- tprint(http_rsp.HTTP_HEADER) end if (ssh_rsp ~= nil) then if (discover.debug) then io.write(debug.traceback()) end return 'workstation', discover.asset_icons['workstation'] .. appendSSHOS(mac, ip), ssh_rsp end if (discover.debug) then io.write(debug.traceback()) end return 'unknown', "", nil end local function remove_xml_comments(xml) -- Remove comments using gsub with pattern matching return xml:gsub("<!%-%-.-%-%->", "") end -- ############################################################################# local function analyzeSSDP(ssdp) local rsp = {} -- Cleanup to avoid SSRF: -- - Don't be tricked into issuing queries to ourselves -- - Only query the same IP of the one that has been discovered for url, host in pairs(ssdp) do if ntop.isLocalInterfaceAddress(host) then -- No queries to ourselves ssdp[url] = nil end if not url:starts("http://" .. host) and not url:starts("https://" .. host) then -- Not using HTTP or HTTPs, or the host is telling us to contact -- another host different from itself. Skip to avoid SSRF ssdp[url] = nil end end -- NOTE: use page_utils.safe_html to protect agains malicious SSDP responses trying to inject code for url, host in pairs(ssdp) do url = ntop.httpPurifyParam(url) -- Cleanup against XSS local hresp = ntop.httpGet(url, "", "", 3 --[[ seconds ]] ) local manufacturer = "" local modelDescription = "" local modelName = "" local icon = "" local base_url = getbaseURL(url) local services = {} local friendlyName = "" if (hresp ~= nil) and (hresp["CONTENT"] ~= nil) then local xml = newParser() local r = xml:ParseXmlText(remove_xml_comments(hresp["CONTENT"])) if (r.root ~= nil) then if (r.root.device ~= nil) then if (r.root.device.friendlyName ~= nil) then friendlyName = page_utils.safe_html(r.root.device.friendlyName:value()) end if (r.root.device.modelName ~= nil) then modelName = page_utils.safe_html(r.root.device.modelName:value()) end if (r.root.device.modelDescription ~= nil) then modelDescription = page_utils.safe_html(r.root.device.modelDescription:value()) end end end if (r.root ~= nil) then if (r.root.device ~= nil) then if (r.root.device.manufacturer ~= nil) then manufacturer = page_utils.safe_html(r.root.device.manufacturer:value()) end if (r.root.device.serviceList ~= nil) then local k, v local serviceList = r.root.device.serviceList:children() for k, v in pairs(serviceList) do if (v.serviceId ~= nil) then local value = page_utils.safe_html(v.serviceId:value()) if (value ~= nil) then if (discover.debug) then io.write(value .. "\n") end table.insert(services, value) end end end end if (r.root.device.iconList ~= nil) then local k, v local iconList = r.root.device.iconList:children() local lastwidth = 999 for k, v in pairs(iconList) do if ((v.mimetype ~= nil) and (v.width ~= nil) and (v.url ~= nil)) then local mime = v.mimetype:value() local width = tonumber(v.width:value()) if (width <= lastwidth) then if ((mime == "image/jpeg") or (mime == "image/png") or (mime == "image/gif")) then if (string.sub(v.url:value(), 1, 1) == "/") then local slash = string.split(base_url, "/") if (slash ~= nil) then base_url = "" for i, v in pairs(slash) do -- io.write(i.." =>"..v.." ["..base_url.."]\n") if (i < 4) then if (i > 1) then base_url = base_url .. "/" end base_url = base_url .. v end end end end if ((string.sub(base_url, -1) ~= "/") and (string.sub(v.url:value(), 1, 1) ~= "/")) then base_url = base_url .. "/" end icon = "<img src=" .. ntop.httpPurifyParam(base_url .. v.url:value()) .. ">" -- io.write(icon.."\n") lastwidth = width -- Pick the smallest icon end end end end end end end if (discover.debug) then io.write(hresp["CONTENT"] .. "\n") end end if (rsp[host] ~= nil) then rsp[host].url = rsp[host].url .. "<br><A HREF=" .. url .. ">" .. friendlyName .. "</A>" for _, v in ipairs(services) do table.insert(rsp[host].services, v) end else rsp[host] = { ["icon"] = icon, ["manufacturer"] = manufacturer, ["url"] = "<A HREF=" .. url .. ">" .. friendlyName .. "</A>", ["services"] = services, ["modelName"] = modelName, ["modelDescription"] = modelDescription, ["friendlyName"] = friendlyName } end if (discover.debug) then io.write(rsp[host].icon .. " / " .. rsp[host].manufacturer .. " / " .. rsp[host].url .. " / " .. "\n") end end return rsp end -- ################################################################################ local function discoverStatus(code, message) return { code = code or '', message = message or '' } end -- ############################################################################# local function discoverARP() if (discover.debug) then io.write("Starting ARP discovery...\n") end local status = discoverStatus("OK") local res = {} local ghost_macs = {} local ghost_found = false local arp_mdns = interface.arpScanHosts() -- List of hosts that reply to ARP local now = os.time() if (discover.debug) then io.write("Completed ARP discovery...\n") end if (arp_mdns == nil) then status = discoverStatus("ERROR", i18n("discover.err_unable_to_arp_discovery")) else -- Add the known macs to the list local known_macs = interface.getMacsInfo(nil, 999, 0, false, true, nil, nil, nil, nil, nil) or {} for _, hmac in pairs(known_macs.macs) do if ((hmac["bytes.sent"] > 0) and ((now - hmac["seen.last"]) < 60) and (hmac["location"] == "lan")) then -- Skip silent/wan hosts if (arp_mdns[hmac.mac] == nil) then -- This is a known host that has *NOT* been observed via ARP scan local ips = interface.findHostByMac(hmac.mac) or {} if (discover.debug) then io.write("Missing MAC " .. hmac.mac .. "\n") end for k, v in pairs(ips) do arp_mdns[hmac.mac] = k ghost_macs[hmac.mac] = k ghost_found = true end end end end end return { status = status, ghost_macs = ghost_macs, ghost_found = ghost_found, arp_mdns = arp_mdns } end -- ############################################################################# local function discovery2config(interface_name) local cached = ntop.getCache(getCachedDiscoveryKey(interface_name)) local disc = json.decode(cached) if isEmptyString(cached) or not disc then return nil end disc["devices"] = ntop.lrangeCache(getCachedDiscoveredDevicesKey(interface_name), 0, -1) or {} for i = 1, #disc["devices"] do local discovered_device = json.decode(disc["devices"][i]) local mac_info = interface.getMacInfo(discovered_device.mac) if mac_info ~= nil then if not isEmptyString(mac_info.devtype) and mac_info.devtype ~= 0 then discovered_device["device_label"] = discover.devtype2icon(mac_info.devtype) discovered_device["device_type"] = mac_info.devtype end end disc["devices"][i] = discovered_device end if (disc and false) then for _, dev in pairs(disc.devices) do if (dev.device_type .. "" ~= "unknown") then -- print(dev.mac .. " = " .. dev.device_type .. "\n") end end end return disc end -- ############################################################################# function discover.discover2table(interface_name, recache) local snmp_community = ntop.getPref("ntopng.prefs.default_snmp_community") if isEmptyString(snmp_community) then snmp_community = "public" end interface.select(tostring(interface_name)) local cached = discovery2config(interface_name) if not recache then if cached then return cached -- or { status = discoverStatus("ERROR", i18n("discover.error_unable_to_decode_json")) } else return { status = discoverStatus("NOCACHE", i18n("discover.error_no_discovery_cached")) } end end setDiscoveryProgress("[0 %]") -- ARP local arp_d = discoverARP() if arp_d["status"]["code"] ~= "OK" then return { status = arp_d["status"] } end setDiscoveryProgress("[10 %]") local arp_mdns = arp_d["arp_mdns"] or {} local ghost_macs = arp_d["ghost_macs"] local ghost_found = arp_d["ghost_found"] -- SSDP, MDNS and SNMP if (discover.debug) then io.write("Starting SSDP discovery...\n") end local ssdp = interface.discoverHosts(3) local osx_devices = {} for mac, ip in pairsByValues(arp_mdns, asc) do if ((ip == "0.0.0.0") or (ip == "255.255.255.255")) then -- This does not look like a good IP/MAC combination elseif (string.find(mac, ":") ~= nil) then local manufacturer = get_manufacturer_mac(mac) -- This is an ARP entry if (discover.debug) then io.write("Attempting to resolve " .. ip .. "\n") end local sym = ntop.getResolvedName(ip) -- placeholder resolution just to fill-up the cache interface.mdnsQueueNameToResolve(ip) if interface.snmpGetBatch then interface.snmpGetBatch(ip, snmp_community, "1.3.6.1.2.1.1.5.0", 0) if (string.contains(manufacturer, "HP") or string.contains(manufacturer, "Hewlett Packard") or string.contains(manufacturer, "Hon Hai")) then -- Query printer model interface.snmpGetBatch(ip, snmp_community, "1.3.6.1.2.1.25.3.2.1.3.1", 0) end end else local ip_addr = mac local mdns_services = ip if (discover.debug) then io.write("[MDNS Services] '" .. ip_addr .. "' = '" .. mdns_services .. "'\n") end if (string.contains(mdns_services, '_sftp')) then osx_devices[ip_addr] = 1 end ntop.resolveName(ip) -- Force address resolution end end setDiscoveryProgress("[30 %]") if (discover.debug) then io.write("Analyzing SSDP...\n") end ssdp = analyzeSSDP(ssdp) local show_services = false setDiscoveryProgress("[40 %]") if (discover.debug) then io.write("Collecting MDNS responses\n") end local mdns = interface.mdnsReadQueuedResponses() if (discover.debug) then for ip, rsp in pairsByValues(mdns, asc) do io.write("[MDNS Resolver] " .. ip .. " = " .. rsp .. "\n") end end for ip, _ in pairs(osx_devices) do if (discover.debug) then io.write("[MDNS OSX] Querying " .. ip .. "\n") end interface.mdnsQueueAnyQuery(ip, "_sftp-ssh._tcp.local") end setDiscoveryProgress("[50 %]") if (discover.debug) then io.write("Collecting SNMP responses\n") end local snmp = {} if interface.snmpReadResponses then snmp = interface.snmpReadResponses() or {} end -- Query sysDescr for the hosts that have replied for ip, rsp in pairsByValues(snmp, asc) do -- io.write("Requesting sysDescr for "..ip.."\n") interface.snmpGetBatch(ip, snmp_community, "1.3.6.1.2.1.1.1.0", 0) end setDiscoveryProgress("[60 %]") if (discover.debug) then io.write("Collecting MDNS OSX responses\n") end osx_devices = interface.mdnsReadQueuedResponses() if (discover.debug) then io.write("Collected MDNS OSX responses\n") end if (discover.debug) then for a, b in pairs(osx_devices) do io.write("[MDNS OSX] " .. a .. " / " .. b .. "\n") end end setDiscoveryProgress("[70 %]") local snmpSysDescr = {} if interface.snmpReadResponses then snmpSysDescr = interface.snmpReadResponses() end if (discover.debug) then for ip, rsp in pairsByValues(snmpSysDescr, asc) do io.write("[SNMP Descr] " .. ip .. " OK\n") end end if (discover.debug) then for ip, rsp in pairsByValues(snmp, asc) do io.write("[SNMP] " .. ip .. " = [" .. rsp .. "][") if (snmpSysDescr[i] ~= nil) then io.write(snmpSysDescr[i]) end io.write("]\n") end end setDiscoveryProgress("[80 %]") -- Time to pack the results in a table... local status = discoverStatus("OK") local res = {} local too_many_devices_discovered = false for mac, ip in pairsByValues(arp_mdns, asc) do if ((string.find(mac, ":") == nil) or (ip == "0.0.0.0") or (ip == "255.255.255.255")) then goto continue end local entry = { ip = ip, mac = mac, ghost = false, information = {} } local host = interface.getHostInfo(ip, 0) -- no VLAN local sym, device_type, device_label, device_info local manufacturer local services = "" local symIP = mdns[ip] if (host ~= nil) then sym = host["name"] else sym = ntop.getResolvedName(ip) end if not isEmptyString(sym) and sym ~= ip then entry["sym"] = sym end if not isEmptyString(symIP) and symIP ~= ip then entry["symIP"] = symIP end if not isEmptyString(arp_mdns[ip]) then entry["information"] = table.merge(entry["information"], string.split(arp_mdns[ip], ";")) end if ssdp[ip] then if ssdp[ip].icon then entry["icon"] = ssdp[ip].icon end if ssdp[ip].modelName then entry["modelName"] = ssdp[ip].modelName end if ssdp[ip].url then entry["url"] = ssdp[ip].url end if ssdp[ip].manufacturer then entry["manufacturer"] = ssdp[ip].manufacturer end if ssdp[ip].services then entry["information"] = table.merge(entry["information"], ssdp[ip].services) for i, name in ipairs(ssdp[ip].services) do services = services .. ";" .. name end end end if (ghost_macs[mac] ~= nil) then entry["ghost"] = true end device_type, device_label, device_info = findDevice(ip, mac, entry["manufacturer"] or get_manufacturer_mac(mac), arp_mdns[ip], services, ssdp[ip], mdns, snmp[ip], snmpSysDescr[ip], osx_devices[ip], sym) local mac_info = interface.getMacInfo(mac) if isEmptyString(device_label) then entry["device_label_noicon"] = device_label if mac_info ~= nil then device_label = discover.devtype2icon(mac_info.devtype) end end if mac_info ~= nil then entry["os_type"] = mac_info.operatingSystem end ntop.setMacDeviceType(mac, discover.devtype2id(device_type), false) -- false means don't overwrite if already set to ~= unknown entry["device_type"] = device_type entry["device_label"] = device_label if (device_info ~= nil) then entry["device_info"] = device_info end if (discover.debug) then io.write("======================\n") tprint(entry) io.write("======================\n") end res[#res + 1] = entry if #res >= discover.MAX_DISCOVERED_DEVICES then too_many_devices_discovered = true break end ::continue:: end setDiscoveryProgress("") -- Cleanup old data ntop.delCache(getCachedDiscoveryKey(interface_name)) ntop.delCache(getCachedDiscoveredDevicesKey(interface_name)) -- Save the discovery status local response = { status = status, ghost_found = ghost_found, discovery_timestamp = os.time(), too_many_devices_discovered = too_many_devices_discovered } ntop.setCache(getCachedDiscoveryKey(interface_name), json.encode(response)) -- Save the actually discovered devices local records_cache = getCachedDiscoveredDevicesKey(interface_name) -- res is already trimmed at MAX_DISCOVERED_DEVICES, no need to check it for i = 1, #res do ntop.lpushCache(records_cache, json.encode(res[i])) end return response end -- ################################################################################ function discover.discovery_function(ifname, ifstats) if not interface.isDiscoverableInterface() then return end ntop.setPref("ntopng.prefs.is_periodic_network_discovery_running.ifid_" .. interface.getId(), "1") ntop.setCache("ntopng.cache.network_discovery_executed.ifid_" .. interface.getId(), "1", 300) traceError(TRACE_INFO,TRACE_CONSOLE, "[Discover] Started periodic discovery on interface "..ifname) local res = discover.discover2table(ifname, true --[[ recache --]]) traceError(TRACE_INFO,TRACE_CONSOLE, "[Discover] Completed periodic discovery on interface "..ifname) discover.clearNetworkDiscovery(ifstats.id) ntop.setPref("ntopng.prefs.is_periodic_network_discovery_running.ifid_" .. interface.getId(), "0") end -- ################################################################################ return discover