From 4842f9cb0dfc3ddd105315cd522766d96874078d Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Thu, 21 Apr 2022 10:26:02 -0400 Subject: [PATCH] moved packet constructors and fixes to comms namespace references in plc comms code --- reactor-plc/plc.lua | 84 +++--------------------- reactor-plc/startup.lua | 2 +- rtu/rtu.lua | 2 +- rtu/startup.lua | 2 +- scada-common/comms.lua | 140 ++++++++++++++++++++++++++++++++++++++++ scada-common/modbus.lua | 62 +----------------- 6 files changed, 153 insertions(+), 139 deletions(-) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 6843303..3389ae7 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -1,6 +1,10 @@ -- #REQUIRES comms.lua -- #REQUIRES ppm.lua +local PROTOCOLS = comms.PROTOCOLS +local RPLC_TYPES = comms.RPLC_TYPES +local RPLC_LINKING = comms.RPLC_LINKING + -- Internal Safety System -- identifies dangerous states and SCRAMs reactor if warranted -- autonomous from main SCADA supervisor/coordinator control @@ -193,78 +197,6 @@ function iss_init(reactor) } 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 function comms_init(id, modem, local_port, server_port, reactor, iss) local self = { @@ -432,13 +364,13 @@ function comms_init(id, modem, local_port, server_port, reactor, iss) if s_pkt.is_valid() then -- get as RPLC packet if s_pkt.protocol() == PROTOCOLS.RPLC then - local rplc_pkt = rplc_packet() + local rplc_pkt = comms.rplc_packet() if rplc_pkt.decode(s_pkt) then pkt = rplc_pkt.get() end -- get as SCADA management packet elseif s_pkt.protocol() == PROTOCOLS.SCADA_MGMT then - local mgmt_pkt = mgmt_packet() + local mgmt_pkt = comms.mgmt_packet() if mgmt_pkt.decode(s_pkt) then pkt = mgmt_packet.get() end @@ -478,11 +410,11 @@ function comms_init(id, modem, local_port, server_port, reactor, iss) send_status() log._debug("re-sent initial status data") elseif link_ack == RPLC_LINKING.DENY then - -- @todo: make sure this doesn't become an MITM security risk + -- @todo: make sure this doesn't become a MITM security risk print_ts("received unsolicited link denial, unlinking\n") log._debug("unsolicited rplc link request denied") elseif link_ack == RPLC_LINKING.COLLISION then - -- @todo: make sure this doesn't become an MITM security risk + -- @todo: make sure this doesn't become a MITM security risk print_ts("received unsolicited link collision, unlinking\n") log._warning("unsolicited rplc link request collision") else diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 4d13e74..80cb626 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -10,7 +10,7 @@ os.loadAPI("scada-common/comms.lua") os.loadAPI("config.lua") os.loadAPI("plc.lua") -local R_PLC_VERSION = "alpha-v0.2.0" +local R_PLC_VERSION = "alpha-v0.2.1" local print = util.print local println = util.println diff --git a/rtu/rtu.lua b/rtu/rtu.lua index efa31f2..23ace96 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -153,7 +153,7 @@ function rtu_comms(modem, local_port, server_port) if s_pkt.is_valid() then -- get as MODBUS TCP packet if s_pkt.protocol() == PROTOCOLS.MODBUS_TCP then - local m_pkt = modbus.packet() + local m_pkt = comms.modbus_packet() if m_pkt.decode(s_pkt) then pkt = m_pkt.get() end diff --git a/rtu/startup.lua b/rtu/startup.lua index 576dd47..bb7e3e1 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -17,7 +17,7 @@ os.loadAPI("dev/boiler_rtu.lua") os.loadAPI("dev/imatrix_rtu.lua") os.loadAPI("dev/turbine_rtu.lua") -local RTU_VERSION = "alpha-v0.2.0" +local RTU_VERSION = "alpha-v0.2.1" local print = util.print local println = util.println diff --git a/scada-common/comms.lua b/scada-common/comms.lua index ac0c3c5..4b77fb1 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -88,6 +88,9 @@ function scada_packet() local modem_event = function () return self.modem_msg_in end local raw = function () return self.raw end + local sender = function () return self.s_port end + local receiver = function () return self.r_port end + local is_valid = function () return self.valid end local seq_num = function () return self.seq_num end @@ -107,6 +110,8 @@ function scada_packet() receive = receive, modem_event = modem_event, raw = raw, + sender = sender, + receiver = receiver, is_valid = is_valid, seq_num = seq_num, protocol = protocol, @@ -115,6 +120,141 @@ function scada_packet() } end +-- MODBUS packet +function modbus_packet() + local self = { + frame = nil, + txn_id = txn_id, + protocol = protocol, + length = length, + unit_id = unit_id, + func_code = func_code, + data = data + } + + -- make a MODBUS packet + local make = function (txn_id, protocol, length, unit_id, func_code, data) + self.txn_id = txn_id + self.protocol = protocol + self.length = length + self.unit_id = unit_id + self.func_code = func_code + self.data = data + end + + -- decode a MODBUS packet from a SCADA frame + local decode = function (frame) + if frame then + self.frame = frame + + local data = frame.data() + 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 + + -- get this packet + local get = function () + return { + scada_frame = self.frame, + txn_id = self.txn_id, + protocol = self.protocol, + length = self.length, + unit_id = self.unit_id, + func_code = self.func_code, + data = self.data + } + end + + return { + make = make, + decode = decode, + get = get + } +end + +-- reactor PLC packet +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 + +-- SCADA management packet function mgmt_packet() local self = { frame = nil, diff --git a/scada-common/modbus.lua b/scada-common/modbus.lua index 8a4137f..a8148b7 100644 --- a/scada-common/modbus.lua +++ b/scada-common/modbus.lua @@ -1,3 +1,5 @@ +-- #REQUIRES comms.lua + -- modbus function codes local MODBUS_FCODE = { READ_COILS = 0x01, @@ -263,63 +265,3 @@ function new(rtu_dev) handle_packet = handle_packet } end - -function packet() - local self = { - frame = nil, - txn_id = txn_id, - protocol = protocol, - length = length, - unit_id = unit_id, - func_code = func_code, - data = data - } - - -- make a MODBUS packet - local make = function (txn_id, protocol, length, unit_id, func_code, data) - self.txn_id = txn_id - self.protocol = protocol - self.length = length - self.unit_id = unit_id - self.func_code = func_code - self.data = data - end - - -- decode a MODBUS packet from a SCADA frame - local decode = function (frame) - if frame then - self.frame = frame - - local data = frame.data() - 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 - - -- get this packet - local get = function () - return { - scada_frame = self.frame, - txn_id = self.txn_id, - protocol = self.protocol, - length = self.length, - unit_id = self.unit_id, - func_code = self.func_code, - data = self.data - } - end - - return { - make = make, - decode = decode, - get = get - } -end