mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Add features/fixes to multicast-profinet-discovery. Closes #1846
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
local coroutine = require "coroutine"
|
local coroutine = require "coroutine"
|
||||||
|
local math = require "math"
|
||||||
local nmap = require "nmap"
|
local nmap = require "nmap"
|
||||||
local stdnse = require "stdnse"
|
local stdnse = require "stdnse"
|
||||||
local string = require "string"
|
local string = require "string"
|
||||||
local table = require "table"
|
local table = require "table"
|
||||||
|
local target = require "target"
|
||||||
local packet = require "packet"
|
local packet = require "packet"
|
||||||
local ipOps = require "ipOps"
|
local ipOps = require "ipOps"
|
||||||
|
|
||||||
|
|
||||||
description = [[
|
description = [[
|
||||||
Sends a multicast PROFINET DCP Identify All message and prints the responses.
|
Sends a multicast PROFINET DCP Identify All message and prints the responses.
|
||||||
|
|
||||||
@@ -16,27 +17,36 @@ Reference:
|
|||||||
|
|
||||||
---@output
|
---@output
|
||||||
--multicast-profinet-discovery:
|
--multicast-profinet-discovery:
|
||||||
--| devices:
|
--| 00:0E:8C:C9:41:15:
|
||||||
|
--| Interface: eth0
|
||||||
|
--| IP:
|
||||||
|
--| ip_info: IP set
|
||||||
|
--| ip_addr: 10.253.81.37
|
||||||
|
--| subnetmask: 255.255.255.0
|
||||||
|
--| gateway: 10.253.81.1
|
||||||
|
--| Device:
|
||||||
|
--| vendorId: 002A
|
||||||
|
--| deviceId: 0105
|
||||||
|
--| vendorValue: S7-300
|
||||||
|
--| deviceRole: 0x00 (None)
|
||||||
|
--| nameOfStation: pn-io
|
||||||
|
--| instance: low: 0, high: 100
|
||||||
--|
|
--|
|
||||||
--| ip_addr: 10.253.81.37
|
--| AC:64:17:2C:C9:46:
|
||||||
--| mac_addr: 00:0E:8C:C9:41:15
|
--| Interface: eth0
|
||||||
--| subnetmask: 255.255.255.0
|
--| IP:
|
||||||
--| vendorId: 002A
|
--| ip_info: IP set
|
||||||
--| deviceId: 0105
|
--| ip_addr: 10.253.81.26
|
||||||
--| vendorvalue: S7-300
|
--| subnetmask: 255.255.255.0
|
||||||
--| deviceRole: 00
|
--| gateway: 10.253.81.1
|
||||||
--| nameOfStation: pn-io
|
--| Device:
|
||||||
--|
|
--| vendorId: 002A
|
||||||
--| ip_addr: 10.253.81.26
|
--| deviceId: 0404
|
||||||
--| mac_addr: AC:64:17:2C:C9:46
|
--| vendorValue: SIMATIC-HMI
|
||||||
--| subnetmask: 255.255.255.0
|
--| deviceRole: 0x01 (IO-Device)
|
||||||
--| vendorId: 002A
|
--|_ nameOfStation: xd134xbvisu.profinetxaschnittstellexb103b2
|
||||||
--| deviceId: 0404
|
|
||||||
--| vendorvalue: SIMATIC-HMI
|
|
||||||
--| deviceRole: 00
|
|
||||||
--|_ nameOfStation: xd134xbvisu.profinetxaschnittstellexb103b2
|
|
||||||
|
|
||||||
author = "Stefan Eiwanger, DINA-community"
|
author = {"Stefan Eiwanger, DINA-community", "Andreas Galauner"}
|
||||||
license = "BSD-2-Clause Plus Patent License. For further details, please refer https://spdx.org/licenses/BSD-2-Clause-Patent.html"
|
license = "BSD-2-Clause Plus Patent License. For further details, please refer https://spdx.org/licenses/BSD-2-Clause-Patent.html"
|
||||||
categories = {"discovery","info", "safe", "broadcast"}
|
categories = {"discovery","info", "safe", "broadcast"}
|
||||||
|
|
||||||
@@ -59,274 +69,212 @@ build_eth_frame= function(iface)
|
|||||||
|
|
||||||
stdnse.debug(1, "Build packet for dcp identify all call.")
|
stdnse.debug(1, "Build packet for dcp identify all call.")
|
||||||
stdnse.debug(1, "Interface: " .. iface.device)
|
stdnse.debug(1, "Interface: " .. iface.device)
|
||||||
local pn_dcp_size = 46 -- min size of ethernet packet
|
local eth_packet = packet.Frame:new()
|
||||||
local eth_packet
|
eth_packet.mac_src = iface.mac
|
||||||
local src_mac = iface.mac
|
|
||||||
|
|
||||||
|
|
||||||
local dest_mac = packet.mactobin(pn_dcp_multicast)
|
eth_packet.mac_dst = packet.mactobin(pn_dcp_multicast)
|
||||||
local eth_proto = string.pack("I2", 0x9288)
|
eth_packet.ether_type = 0x8892
|
||||||
|
|
||||||
-- pn-dcp request frame : [FrameID | ServiceID | ServiceType | Xid | ResponseDelay | DCPDataLength | Option | Suboption ]
|
-- pn-dcp request frame : [FrameID | ServiceID | ServiceType | Xid | ResponseDelay | DCPDataLength | Option | Suboption ]
|
||||||
local blockData = string.pack("I2BBI4I2I2BB", 0xfefe, 0x05,0x00,0x10000010, 0x0400, 0x0400,0xff, 0xff)
|
eth_packet.buf = string.pack(">I2BBI4I2I2BBI2",
|
||||||
local padbyte = string.pack("B", 0x00)
|
0xfefe, -- Frame ID
|
||||||
|
0x05, -- Service ID: 5 = Identify
|
||||||
|
0x00, -- Service Type: 0 = Request
|
||||||
|
math.random(0xffffffff), -- Xid (transaction ID)
|
||||||
|
math.random(9), -- Response delay * 10ms
|
||||||
|
0x0004, -- DCP Data length (length of following data)
|
||||||
|
0xff, -- Option: 0xff = all
|
||||||
|
0xff, -- Suboption: 0xff = all
|
||||||
|
0x0000 -- Length of following block data: 0
|
||||||
|
)
|
||||||
|
|
||||||
-- build the packet
|
-- build the packet
|
||||||
eth_packet = dest_mac .. src_mac .. eth_proto .. blockData
|
eth_packet:build_ether_frame()
|
||||||
local length = string.len(eth_packet)
|
|
||||||
|
|
||||||
-- fill the rest of the packet with 0x00 till ethernet min size is reached
|
-- fill the rest of the packet with 0x00 till ethernet min size is reached
|
||||||
local padding = string.rep(padbyte, (pn_dcp_size-length))
|
return eth_packet.frame_buf
|
||||||
eth_packet = eth_packet .. padding
|
|
||||||
return eth_packet
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local PNDCP_IP_INFO = {
|
||||||
|
[0] = "No IP set",
|
||||||
|
[1] = "IP set",
|
||||||
|
[2] = "IP set via DHCP",
|
||||||
|
}
|
||||||
|
|
||||||
|
local PNDCP_DEVICE_ROLES = {
|
||||||
|
[0x01] = "IO-Device",
|
||||||
|
[0x02] = "IO-Controller",
|
||||||
|
[0x04] = "IO-Multidevice",
|
||||||
|
[0x08] = "PN-Supervisor",
|
||||||
|
}
|
||||||
|
|
||||||
|
local function parse_string (block)
|
||||||
|
-- skip 2-byte block info
|
||||||
|
return block:sub(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_parser (parsefunc, label)
|
||||||
|
return function (block, results)
|
||||||
|
results[label] = parsefunc(block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local parser = {
|
||||||
|
-- Option IP
|
||||||
|
['\x01\x01'] = function (block, results)
|
||||||
|
local _, mac = string.unpack(">I2 c6")
|
||||||
|
results.mac_addr = stdnse.format_mac(mac)
|
||||||
|
end,
|
||||||
|
['\x01\x02'] = function (block, results)
|
||||||
|
local block_info, ipdw, netdw, gwdw = string.unpack(">I2 I4 I4 I4", block)
|
||||||
|
|
||||||
|
local ipinfo = PNDCP_IP_INFO[block_info & 0xf]
|
||||||
|
if block_info & 0x80 > 0 then
|
||||||
|
ipinfo = ipinfo .. " (conflict)"
|
||||||
|
end
|
||||||
|
results.ip_info = ipinfo
|
||||||
|
|
||||||
|
if ipdw > 0 then
|
||||||
|
results.ip_addr = ipOps.fromdword(ipdw)
|
||||||
|
end
|
||||||
|
if netdw > 0 then
|
||||||
|
results.subnetmask = ipOps.fromdword(netdw)
|
||||||
|
end
|
||||||
|
if gwdw > 0 then
|
||||||
|
results.gateway = ipOps.fromdword(gwdw)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
-- device properties
|
||||||
|
['\x02\x01'] = function (block, results)
|
||||||
|
results.vendorValue = block:sub(3)
|
||||||
|
end,
|
||||||
|
['\x02\x02'] = function (block, results)
|
||||||
|
results.nameOfStation = block:sub(3)
|
||||||
|
end,
|
||||||
|
['\x02\x03'] = function (block, results)
|
||||||
|
local vendorid, deviceid = string.unpack(">xx I2 I2", block)
|
||||||
|
results.vendorId = ("0x%04x"):format(vendorid)
|
||||||
|
results.deviceId = ("0x%04x"):format(deviceid)
|
||||||
|
end,
|
||||||
|
['\x02\x04'] = function (block, results)
|
||||||
|
local deviceRole = string.unpack(">xxBx", block)
|
||||||
|
|
||||||
|
-- device role
|
||||||
|
local device_role_strings = {}
|
||||||
|
if deviceRole == 0x00 then
|
||||||
|
table.insert(device_role_strings, "None")
|
||||||
|
else
|
||||||
|
for flag, name in pairs(PNDCP_DEVICE_ROLES) do
|
||||||
|
if deviceRole & flag ~= 0 then
|
||||||
|
table.insert(device_role_strings, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
results.deviceRole = ("0x%02x (%s)"):format(deviceRole,
|
||||||
|
table.concat(device_role_strings, ", "))
|
||||||
|
end,
|
||||||
|
--['\x02\x05'] device options?
|
||||||
|
['\x02\x06'] = function (block, results)
|
||||||
|
results.alias = block:sub(3)
|
||||||
|
end,
|
||||||
|
['\x02\x07'] = function (block, results)
|
||||||
|
local low, high = string.unpack(">xx BB", block)
|
||||||
|
results.instance = ("low: %d, high: %d"):format(low, high)
|
||||||
|
end,
|
||||||
|
['\x02\x08'] = function (block, results)
|
||||||
|
local vendorid, deviceid = string.unpack(">xx I2 I2", block)
|
||||||
|
results.OEMvendorId = ("0x%04x"):format(vendorid)
|
||||||
|
results.OEMdeviceId = ("0x%04x"):format(deviceid)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ensure any option can be used without crashing
|
||||||
|
setmetatable(parser, {
|
||||||
|
__index = function(self, key)
|
||||||
|
local option, suboption = string.byte(key, 1, 2)
|
||||||
|
stdnse.debug(1, "Unknown option/suboption %d/%d", option, suboption)
|
||||||
|
return function () end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
-- extract data from incoming dcp packets and store them into a table
|
-- extract data from incoming dcp packets and store them into a table
|
||||||
--@param eth_data ethernet part of the recieved packet
|
|
||||||
--@param pn_data profinet part of the recieved packet == ethernet packetload
|
--@param pn_data profinet part of the recieved packet == ethernet packetload
|
||||||
--@return device table with all extraced data from the pn_dcp
|
--@return device table with all extraced data from the pn_dcp
|
||||||
parse_pndcp = function(eth_data, pn_data)
|
parse_pndcp = function(pn_data)
|
||||||
stdnse.debug(1, "Start parsing of answer")
|
stdnse.debug(1, "Start parsing of answer")
|
||||||
local pos = 7 -- start after the destination mac address (host)
|
|
||||||
local deviceMacAddress
|
|
||||||
local deviceRoleInterpretation = {}
|
|
||||||
deviceRoleInterpretation [0] = "PNIO Device"
|
|
||||||
deviceRoleInterpretation [1] = "PNIO Controller"
|
|
||||||
deviceRoleInterpretation [2] = "PNIO Multidevice"
|
|
||||||
deviceRoleInterpretation [3] = "PNIO Supervisor"
|
|
||||||
|
|
||||||
-- extract device mac address
|
|
||||||
local mac
|
|
||||||
mac, pos = string.unpack("c6", eth_data, pos)
|
|
||||||
deviceMacAddress = stdnse.format_mac(mac)
|
|
||||||
|
|
||||||
stdnse.debug(1, "Device MAC address: %s", deviceMacAddress)
|
|
||||||
|
|
||||||
-- check if the packet is a request
|
-- check if the packet is a request
|
||||||
local serviceType
|
local dcp_header_format = ">I2 B B xxxx xx xx" -- skip Xid, delay, length
|
||||||
serviceType= string.unpack("B", pn_data, 4)
|
if #pn_data < dcp_header_format:packsize() then
|
||||||
stdnse.debug(1, "Servicetype %x", serviceType)
|
return nil
|
||||||
if (serviceType == 0) then return end
|
end
|
||||||
|
local frame_id, service_id, service_type, pos = string.unpack(dcp_header_format, pn_data)
|
||||||
|
if frame_id ~= 0xfeff or service_id ~= 5 or service_type ~= 1 then
|
||||||
-- start extrating data from pn_dcp_response -- start with 1
|
return nil
|
||||||
pos = 11
|
end
|
||||||
|
|
||||||
local gesDCPDataLength = ""
|
|
||||||
gesDCPDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
stdnse.debug(1,"DCP Datalength of full packet: %d", gesDCPDataLength)
|
|
||||||
|
|
||||||
-- extract data from DCP block
|
-- extract data from DCP block
|
||||||
local option, suboption
|
local result = {}
|
||||||
local IP, deviceVendorValue, deviceRole, deviceId, nameofstation, dcpDatalength, subnetmask, standardGateway, vendorId = "", "", "", "", "", "", "", "", ""
|
while(pos < #pn_data) do
|
||||||
stdnse.debug(1, "Start extracting data from DCP block")
|
|
||||||
while(pos < gesDCPDataLength) do
|
|
||||||
|
|
||||||
-- Option IP, suboption IP
|
local option, block, p = string.unpack("!2 c2 >s2", pn_data, pos)
|
||||||
option, suboption, pos = string.unpack("BB", pn_data, pos)
|
parser[option](block, result)
|
||||||
|
|
||||||
local dcpDataLength, _
|
|
||||||
if option == 1 then -- IP
|
|
||||||
if(suboption == 2) then
|
|
||||||
stdnse.debug(1, "Option IP, suboption IP")
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1,"* DCP Datalength of IP/IP %d", dcpDataLength)
|
|
||||||
|
|
||||||
-- block info
|
|
||||||
_, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
|
|
||||||
local dword = ""
|
|
||||||
-- IP
|
|
||||||
dword, pos = string.unpack(">I4", pn_data, pos)
|
|
||||||
IP = ipOps.fromdword(dword)
|
|
||||||
stdnse.debug(1, "* IP address: %s", IP)
|
|
||||||
|
|
||||||
-- subnetmask
|
|
||||||
dword, pos = string.unpack(">I4", pn_data, pos)
|
|
||||||
subnetmask = ipOps.fromdword(dword)
|
|
||||||
stdnse.debug(1, "* Subnetmask: %s", subnetmask)
|
|
||||||
|
|
||||||
-- standard gateway
|
|
||||||
dword, pos = string.unpack(">I4", pn_data, pos)
|
|
||||||
standardGateway = ipOps.fromdword(dword)
|
|
||||||
stdnse.debug(1, "* Default gateway: %s", standardGateway)
|
|
||||||
|
|
||||||
--[[if dcpDataLength%2 ~= 0 then
|
|
||||||
pos = pos +1 -- add padding
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
else
|
|
||||||
stdnse.debug(1, "Option IP, suboption something else: %d", suboption)
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1, "* DCP datalength of IP/else: %d", dcpDataLength)
|
|
||||||
|
|
||||||
if dcpDataLength%2 ~= 0 then
|
|
||||||
pos = pos +1 -- add padding
|
|
||||||
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
elseif option == 2 then -- device properties
|
|
||||||
if suboption == 1 then-- deviceVendorValue manufacturer specific option
|
|
||||||
stdnse.debug(1, "Option device properties, suboption manufacturer specific")
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1,"* DCP Datalength of device properties/manufacturer specific %d", dcpDataLength)
|
|
||||||
|
|
||||||
-- block info
|
|
||||||
_, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
|
|
||||||
-- device vendor
|
|
||||||
deviceVendorValue, pos = string.unpack("c" .. (dcpDataLength - 2) ,pn_data, pos)
|
|
||||||
stdnse.debug(1, "* Device Vendor: %s", deviceVendorValue)
|
|
||||||
|
|
||||||
if dcpDataLength%2 ~= 0 then
|
|
||||||
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
|
||||||
pos = pos +1 -- add padding
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif suboption == 2 then -- nameofstation
|
|
||||||
stdnse.debug(1, "Option device properties, suboption name of station")
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1,"* DCP Datalength of device properties/name of station %d", dcpDataLength)
|
|
||||||
|
|
||||||
-- block info
|
|
||||||
_, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
|
|
||||||
-- name of station
|
|
||||||
nameofstation, pos = string.unpack("c" .. (dcpDataLength - 2) ,pn_data, pos)
|
|
||||||
stdnse.debug(1, "* Name Of Station: %s", nameofstation)
|
|
||||||
|
|
||||||
if dcpDataLength%2 ~= 0 then
|
|
||||||
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
|
||||||
pos = pos +1 -- add padding
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif suboption == 3 then -- device id, vendor Id
|
|
||||||
stdnse.debug(1, "Option device properties, suboption device ID")
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1,"* DCP Datalength of device properties/device ID %d", dcpDataLength)
|
|
||||||
|
|
||||||
-- block info
|
|
||||||
_, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
|
|
||||||
-- vendor ID
|
|
||||||
local tmpvendorId, tmpdeviceId = "", ""
|
|
||||||
tmpvendorId, pos = string.unpack("c2", pn_data, pos)
|
|
||||||
vendorId = stdnse.tohex(tmpvendorId)
|
|
||||||
vendorId = "0x" .. vendorId
|
|
||||||
stdnse.debug(1, "* Vendor ID: %s", vendorId)
|
|
||||||
|
|
||||||
-- device ID
|
|
||||||
tmpdeviceId, pos = string.unpack("c2", pn_data, pos)
|
|
||||||
deviceId = stdnse.tohex(tmpdeviceId)
|
|
||||||
deviceId = "0x" .. deviceId
|
|
||||||
stdnse.debug(1, "* Device ID: %s", deviceId)
|
|
||||||
|
|
||||||
elseif suboption == 4 then -- device role
|
|
||||||
stdnse.debug(1, "Option device properties, suboption device role")
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1,"* DCP Datalength of device properties/device role %d", dcpDataLength)
|
|
||||||
|
|
||||||
-- block info
|
|
||||||
_, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
|
|
||||||
-- device role
|
|
||||||
deviceRole, pos = string.unpack("B", pn_data, pos)
|
|
||||||
deviceRole = deviceRoleInterpretation[deviceRole] .. ' 0x0' .. deviceRole
|
|
||||||
stdnse.debug(1, "* Device Role: %s", deviceRole)
|
|
||||||
|
|
||||||
-- reserved
|
|
||||||
_, pos = string.unpack("B", pn_data, pos)
|
|
||||||
else
|
|
||||||
stdnse.debug(1, "Option device properties, suboption something else: %d", suboption)
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1,"* DCP Datalength of device properties/device role %d", dcpDataLength)
|
|
||||||
|
|
||||||
pos = pos + dcpDataLength
|
|
||||||
if dcpDataLength%2 ~= 0 then
|
|
||||||
stdnse.debug(2, "dcpDatalength was odd, add padding +1 to pos")
|
|
||||||
pos = pos +1 -- add padding
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
else
|
|
||||||
stdnse.debug(1, "Option something else: %d", option)
|
|
||||||
|
|
||||||
-- DCP block length
|
|
||||||
dcpDataLength, pos = string.unpack(">I2", pn_data, pos)
|
|
||||||
--stdnse.debug(1,"* DCP Datalength of device properties/device role %d", dcpDataLength)
|
|
||||||
|
|
||||||
pos = pos + dcpDataLength
|
|
||||||
if dcpDataLength%2 ~= 0 then
|
|
||||||
stdnse.debug(1, "dcpDatalength was odd, add padding +1 to pos")
|
|
||||||
pos = pos +1 -- add padding
|
|
||||||
end
|
|
||||||
|
|
||||||
end -- close if
|
|
||||||
|
|
||||||
end -- close while
|
end -- close while
|
||||||
|
|
||||||
-- store data into table
|
return result
|
||||||
local device = stdnse.output_table()
|
|
||||||
device.ip_addr = IP
|
|
||||||
device.mac_addr = deviceMacAddress
|
|
||||||
device.subnetmask = subnetmask
|
|
||||||
device.vendorId = vendorId
|
|
||||||
device.deviceId = deviceId
|
|
||||||
device.vendorvalue = deviceVendorValue
|
|
||||||
device.deviceRole = deviceRole
|
|
||||||
device.nameOfStation = nameofstation
|
|
||||||
|
|
||||||
stdnse.debug(1, "End of parsing\n")
|
|
||||||
|
|
||||||
return device
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- helpfunction for thread call
|
-- helpfunction for thread call
|
||||||
--@param iface interface table
|
--@param iface interface table
|
||||||
|
--@param to_ms timeout in ms to wait for responses
|
||||||
--@param pn_dcp ethernet dcp packet to send
|
--@param pn_dcp ethernet dcp packet to send
|
||||||
--@param devices table for results
|
--@param devices table for results
|
||||||
--@return devices, table with devices which answered to the dcp identify all call
|
--@return devices, table with devices which answered to the dcp identify all call
|
||||||
discoverThread = function(iface, pn_dcp, devices)
|
discoverThread = function(iface, to_ms, pn_dcp, devices)
|
||||||
local condvar = nmap.condvar(devices)
|
local condvar = nmap.condvar(devices)
|
||||||
local dnet = nmap.new_dnet()
|
local dnet = nmap.new_dnet()
|
||||||
local pcap_s = nmap.new_socket()
|
local pcap_s = nmap.new_socket()
|
||||||
pcap_s:set_timeout(2000)
|
pcap_s:set_timeout(100)
|
||||||
dnet:ethernet_open(iface.device)
|
dnet:ethernet_open(iface.device)
|
||||||
pcap_s:pcap_open(iface.device, 256, false, "ether proto 0x8892")
|
pcap_s:pcap_open(iface.device, 256, false, "ether proto 0x8892")
|
||||||
|
|
||||||
local status, ethData, length, pn_data
|
|
||||||
|
|
||||||
dnet:ethernet_send(pn_dcp) -- send the frame
|
dnet:ethernet_send(pn_dcp) -- send the frame
|
||||||
|
dnet:ethernet_close(); -- close the sender
|
||||||
|
|
||||||
status = true
|
local start = nmap.clock_ms()
|
||||||
while status do
|
while (nmap.clock_ms() - start) < to_ms do
|
||||||
status, length, ethData, pn_data = pcap_s:pcap_receive()
|
local status, length, ethData, pn_data = pcap_s:pcap_receive()
|
||||||
|
|
||||||
if(status) then
|
if(status) then
|
||||||
devices[#devices + 1] = parse_pndcp(ethData, pn_data)
|
local dev = parse_pndcp(pn_data)
|
||||||
|
if dev then
|
||||||
|
local out = stdnse.output_table()
|
||||||
|
out.Interface = iface.device
|
||||||
|
out.IP = stdnse.output_table()
|
||||||
|
if dev.ip_addr then
|
||||||
|
-- Add new target if desired
|
||||||
|
target.add(dev.ip_addr)
|
||||||
|
out.IP.ip_addr = dev.ip_addr
|
||||||
|
end
|
||||||
|
out.IP.ip_info = dev.ip_info
|
||||||
|
out.IP.subnetmask = dev.subnetmask
|
||||||
|
out.IP.gateway = dev.gateway
|
||||||
|
out.Device = stdnse.output_table()
|
||||||
|
out.Device.vendorId = dev.vendorId
|
||||||
|
out.Device.deviceId = dev.deviceId
|
||||||
|
out.Device.vendorValue = dev.vendorValue
|
||||||
|
out.Device.deviceRole = dev.deviceRole
|
||||||
|
out.Device.nameOfStation = dev.nameOfStation
|
||||||
|
-- extract device mac address
|
||||||
|
local mac = string.unpack("c6", ethData, 7)
|
||||||
|
devices[stdnse.format_mac(mac)] = out
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dnet:ethernet_close(iface.device); -- close the sender
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pcap_s:close(iface.device)
|
pcap_s:close(iface.device)
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
@@ -334,19 +282,13 @@ discoverThread = function(iface, pn_dcp, devices)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- main fuction
|
-- main fuction
|
||||||
--@return 0 if no devices were found
|
|
||||||
--@return output_tab table for nmap to show the gathered information
|
--@return output_tab table for nmap to show the gathered information
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
local output_tab = stdnse.output_table()
|
local output_tab = stdnse.output_table()
|
||||||
output_tab.devices = {}
|
|
||||||
|
|
||||||
-- check interface parameter
|
-- check interface parameter
|
||||||
|
|
||||||
local dnet = nmap.new_dnet()
|
|
||||||
local pcap_s = nmap.new_socket()
|
|
||||||
pcap_s:set_timeout(4000)
|
|
||||||
|
|
||||||
local macs = {}
|
local macs = {}
|
||||||
local filter_interfaces = function (iface)
|
local filter_interfaces = function (iface)
|
||||||
if iface.link == "ethernet" and iface.up == "up" and
|
if iface.link == "ethernet" and iface.up == "up" and
|
||||||
@@ -360,22 +302,22 @@ action = function()
|
|||||||
-- check if at least one interface is available
|
-- check if at least one interface is available
|
||||||
if #interfaces == 0 then
|
if #interfaces == 0 then
|
||||||
print("No interfaces found")
|
print("No interfaces found")
|
||||||
return false
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get the frame we want to send
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
|
local to_ms = (timeout or 2) * 1000
|
||||||
|
|
||||||
local threads = {}
|
local threads = {}
|
||||||
|
|
||||||
local condvar = nmap.condvar(output_tab.devices)
|
local condvar = nmap.condvar(output_tab)
|
||||||
|
|
||||||
|
|
||||||
for _, iface in ipairs(interfaces) do
|
for _, iface in ipairs(interfaces) do
|
||||||
local pn_dcp = build_eth_frame(iface)
|
local pn_dcp = build_eth_frame(iface)
|
||||||
--print(iface.device)
|
--print(iface.device)
|
||||||
|
|
||||||
local co = stdnse.new_thread(discoverThread, iface, pn_dcp, output_tab.devices)
|
local co = stdnse.new_thread(discoverThread, iface, to_ms, pn_dcp, output_tab)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -392,9 +334,9 @@ action = function()
|
|||||||
until next(threads) == nil
|
until next(threads) == nil
|
||||||
|
|
||||||
-- check the output if something is doubled there
|
-- check the output if something is doubled there
|
||||||
if #output_tab.devices == 0 then
|
if #output_tab == 0 then
|
||||||
print("No profinet devices in the subnet")
|
print("No profinet devices in the subnet")
|
||||||
return 0
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user