consistent packet constructors/receiving

This commit is contained in:
Mikayla Fischler
2022-04-02 08:28:43 -04:00
parent 013656bc4d
commit 36fb4587a1
4 changed files with 182 additions and 47 deletions

View File

@ -172,6 +172,78 @@ function iss_init(reactor)
} }
end end
function rplc_packet()
local self = {
frame = nil,
id = nil,
type = nil,
length = nil,
body = nil
}
local _rplc_type_valid = function ()
return self.type == RPLC_TYPES.KEEP_ALIVE or
self.type == RPLC_TYPES.LINK_REQ or
self.type == RPLC_TYPES.STATUS or
self.type == RPLC_TYPES.MEK_STRUCT or
self.type == RPLC_TYPES.MEK_SCRAM or
self.type == RPLC_TYPES.MEK_ENABLE or
self.type == RPLC_TYPES.MEK_BURN_RATE or
self.type == RPLC_TYPES.ISS_ALARM or
self.type == RPLC_TYPES.ISS_GET or
self.type == RPLC_TYPES.ISS_CLEAR
end
-- make an RPLC packet
local make = function (id, packet_type, length, data)
self.id = id
self.type = packet_type
self.length = length
self.data = data
end
-- decode an RPLC packet from a SCADA frame
local decode = function (frame)
if frame then
self.frame = frame
if frame.protocol() == comms.PROTOCOLS.RPLC then
local data = frame.data()
local ok = #data > 2
if ok then
make(data[1], data[2], data[3], { table.unpack(data, 4, #data) })
ok = _rplc_type_valid()
end
return ok
else
log._debug("attempted RPLC parse of incorrect protocol " .. frame.protocol(), true)
return false
end
else
log._debug("nil frame encountered", true)
return false
end
end
local get = function ()
return {
scada_frame = self.frame,
id = self.id,
type = self.type,
length = self.length,
data = self.data
}
end
return {
make = make,
decode = decode,
get = get
}
end
-- reactor PLC communications -- reactor PLC communications
function rplc_comms(id, modem, local_port, server_port, reactor) function rplc_comms(id, modem, local_port, server_port, reactor)
local self = { local self = {
@ -249,17 +321,21 @@ function rplc_comms(id, modem, local_port, server_port, reactor)
-- parse packet as generic SCADA packet -- parse packet as generic SCADA packet
s_pkt.recieve(side, sender, reply_to, message, distance) s_pkt.recieve(side, sender, reply_to, message, distance)
-- get using RPLC protocol format if s_pkt.is_valid() then
if s_pkt.is_valid() and s_pkt.protocol() == PROTOCOLS.RPLC then -- get as RPLC packet
local body = s_pkt.data() if s_pkt.protocol() == PROTOCOLS.RPLC then
if #body > 2 then local rplc_pkt = rplc_packet()
pkt = { if rplc_pkt.decode(s_pkt) then
scada_frame = s_pkt, pkt = rplc_pkt.get()
id = body[1], end
type = body[2], -- get as SCADA management packet
length = #body - 2, elseif s_pkt.protocol() == PROTOCOLS.SCADA_MGMT then
body = { table.unpack(body, 3, 2 + #body) } local mgmt_pkt = mgmt_packet()
} if mgmt_pkt.decode(s_pkt) then
pkt = mgmt_packet.get()
end
else
log._error("illegal packet type " .. s_pkt.protocol(), true)
end end
end end

View File

@ -121,34 +121,17 @@ function rtu_comms(modem, local_port, server_port)
-- get as MODBUS TCP packet -- get as MODBUS TCP packet
if s_pkt.protocol() == PROTOCOLS.MODBUS_TCP then if s_pkt.protocol() == PROTOCOLS.MODBUS_TCP then
local m_pkt = modbus_packet() local m_pkt = modbus_packet()
m_pkt.receive(s_pkt.data()) if m_pkt.decode(s_pkt) then
pkt = m_pkt.get()
pkt = { end
scada_frame = s_pkt,
modbus_frame = m_pkt
}
-- get as SCADA management packet -- get as SCADA management packet
elseif s_pkt.protocol() == PROTOCOLS.SCADA_MGMT then elseif s_pkt.protocol() == PROTOCOLS.SCADA_MGMT then
local body = s_pkt.data() local mgmt_pkt = mgmt_packet()
if #body > 1 then if mgmt_pkt.decode(s_pkt) then
pkt = { pkt = mgmt_packet.get()
scada_frame = s_pkt,
type = body[1],
length = #body - 1,
body = { table.unpack(body, 2, 1 + #body) }
}
elseif #body == 1 then
pkt = {
scada_frame = s_pkt,
type = body[1],
length = #body - 1,
body = nil
}
else
log._error("Malformed SCADA packet has no length field")
end end
else else
log._error("Illegal packet type " .. s_pkt.protocol(), true) log._error("illegal packet type " .. s_pkt.protocol(), true)
end end
end end
@ -161,8 +144,9 @@ function rtu_comms(modem, local_port, server_port)
if protocol == PROTOCOLS.MODBUS_TCP then if protocol == PROTOCOLS.MODBUS_TCP then
-- MODBUS instruction -- MODBUS instruction
if packet.modbus_frame.unit_id <= #units then if packet.unit_id <= #units then
local return_code, response = units.modbus_io.handle_packet(packet.modbus_frame) local unit = units[packet.unit_id]
local return_code, response = unit.modbus_io.handle_packet(packet)
_send(response, PROTOCOLS.MODBUS_TCP) _send(response, PROTOCOLS.MODBUS_TCP)
if not return_code then if not return_code then
@ -186,7 +170,7 @@ function rtu_comms(modem, local_port, server_port)
end end
else else
-- should be unreachable assuming packet is from parse_packet() -- should be unreachable assuming packet is from parse_packet()
log._error("Illegal packet type " .. protocol, true) log._error("illegal packet type " .. protocol, true)
end end
end end
end end

View File

@ -114,3 +114,67 @@ function scada_packet()
data = data data = data
} }
end end
function mgmt_packet()
local self = {
frame = nil,
type = nil,
length = nil,
data = nil
}
local _scada_type_valid = function ()
return self.type == SCADA_MGMT_TYPES.PING or
self.type == SCADA_MGMT_TYPES.SV_HEARTBEAT or
self.type == SCADA_MGMT_TYPES.REMOTE_LINKED or
self.type == SCADA_MGMT_TYPES.RTU_ADVERT or
self.type == SCADA_MGMT_TYPES.RTU_HEARTBEAT
end
-- make a SCADA management packet
local make = function (packet_type, length, data)
self.type = packet_type
self.length = length
self.data = data
end
-- decode a SCADA management packet from a SCADA frame
local decode = function (frame)
if frame then
self.frame = frame
if frame.protocol() == comms.PROTOCOLS.SCADA_MGMT then
local data = frame.data()
local ok = #data > 1
if ok then
make(data[1], data[2], { table.unpack(data, 3, #data) })
ok = _scada_type_valid()
end
return ok
else
log._debug("attempted SCADA_MGMT parse of incorrect protocol " .. frame.protocol(), true)
return false
end
else
log._debug("nil frame encountered", true)
return false
end
end
local get = function ()
return {
scada_frame = self.frame,
type = self.type,
length = self.length,
data = self.data
}
end
return {
make = make,
decode = decode,
get = get
}
end

View File

@ -176,9 +176,8 @@ function modbus_init(rtu_dev)
end end
function modbus_packet() function modbus_packet()
local MODBUS_TCP = 0
local self = { local self = {
frame = nil,
txn_id = txn_id, txn_id = txn_id,
protocol = protocol, protocol = protocol,
length = length, length = length,
@ -187,6 +186,7 @@ function modbus_packet()
data = data data = data
} }
-- make a MODBUS packet
local make = function (txn_id, protocol, length, unit_id, func_code, data) local make = function (txn_id, protocol, length, unit_id, func_code, data)
self.txn_id = txn_id self.txn_id = txn_id
self.protocol = protocol self.protocol = protocol
@ -196,18 +196,29 @@ function modbus_packet()
self.data = data self.data = data
end end
local receive = function (raw) -- decode a MODBUS packet from a SCADA frame
local size_ok = #raw ~= 6 local decode = function (frame)
if frame then
self.frame = frame
if size_ok then local data = frame.data()
make(raw[1], raw[2], raw[3], raw[4], raw[5], raw[6]) local size_ok = #data ~= 6
if size_ok then
make(data[1], data[2], data[3], data[4], data[5], data[6])
end
return size_ok and self.protocol == comms.PROTOCOLS.MODBUS_TCP
else
log._debug("nil frame encountered", true)
return false
end end
return size_ok and self.protocol == MODBUS_TCP
end end
-- get this packet
local get = function () local get = function ()
return { return {
scada_frame = self.frame,
txn_id = self.txn_id, txn_id = self.txn_id,
protocol = self.protocol, protocol = self.protocol,
length = self.length, length = self.length,
@ -219,7 +230,7 @@ function modbus_packet()
return { return {
make = make, make = make,
receive = receive, decode = decode,
get = get get = get
} }
end end